Skip to content
CxmrykkPublic

About

Use Android Auto compatible head units as an external display (ft. Raspberry Pi)

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

381 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BerryAuto

BerryAuto turns a car's Android Auto head unit into an external touchscreen display for a Linux single-board computer (SBC).

It does this by emulating the phone side of the Android Auto Protocol (AAP). The SBC connects to the car's Android Auto USB port as a USB gadget and presents itself to the head unit as an Android phone. Once the session is established, BerryAuto streams the SBC's desktop to the car as H.264/HEVC video and turns the car's touchscreen into a virtual touchscreen on the SBC.

BerryAuto is not an Android Auto receiver. It does not let a phone project onto your SBC, and no phone is involved at any point. The car's head unit is the display, and the SBC takes the place of the phone.

The Raspberry Pi 4 is a common target, but any Linux device with a USB Device Controller (UDC) that supports gadget mode, a supported video encoder, and DKMS support should work.

How It Works

  1. Accessory handshake. The car is the USB host. BerryAuto exposes a FunctionFS USB gadget and answers the Android Open Accessory (AOA) requests that a head unit sends to switch a phone into accessory mode. After the AOA start request, the daemon exits with status 42 so the runner script can re-enumerate the gadget in accessory mode.
  2. AAP session. BerryAuto answers the head unit's version request, completes the TLS handshake as the phone side, and requests service discovery. The head unit's response describes its supported resolutions, frame rates, codecs, and touchscreen size. BerryAuto then opens the video, input, and sensor channels.
  3. Virtual display. An EVDI virtual monitor is attached to the SBC, so the desktop environment sees a real display and extends or resizes onto it.
  4. Video. Frames from the virtual monitor are encoded (hardware encoders preferred) and streamed to the head unit, with flow control driven by the head unit's acknowledgements.
  5. Touch. Touch events from the car's screen are injected into the SBC through a uinput multitouch device.

BerryAuto does not stream audio and does not use the car's microphone. It leaves the head unit's audio and microphone channels closed, so the car keeps its normal audio routing (for example, Bluetooth).

Features

  • USB gadget mode: Uses Linux FunctionFS to act as an Android Open Accessory (AOA) device towards the head unit.
  • Kernel-level virtual display (EVDI): Appears to the OS as a physical plug-and-play monitor, so there are no Wayland/X11 screen-capture restrictions, permission prompts, or headless-display workarounds.
  • Automatic display mode: Picks the head unit's preferred video configuration and injects a matching EDID into the virtual monitor so the desktop resizes itself. The EDID is chosen from three built-in 60 Hz modes (1920x1080, 1280x720, 800x480) based on the negotiated width; frames are scaled to the negotiated stream size if the two differ.
  • Hardware encoder discovery: Queries FFmpeg for hardware encoders (v4l2m2m, omx, rpi, vaapi, nvenc, qsv, amf) and falls back to software encoders (libx264/libx265) only if none can be opened.
  • Multitouch input: Creates a virtual touchscreen via uinput and forwards the car's touch events to the local display server.

Requirements

Hardware

  • A Linux SBC with a USB Device Controller (UDC) capable of gadget/OTG mode (e.g., Raspberry Pi 4 via the USB-C port, Orange Pi, Rock Pi).
  • A car or aftermarket head unit with wired Android Auto support.
  • A USB cable from the SBC's OTG port to the head unit's Android Auto USB port (the port you would normally plug a phone into).

Power: On the Raspberry Pi 4, the USB-C port is both the OTG data port and the power input. Most car USB ports cannot supply enough current to run a Pi 4 reliably, so plan to power the SBC separately (for example, with a USB-C power/data splitter).

OS Configuration

Enable the dwc2 USB driver to allow the device to act as a USB gadget.
(On Raspberry Pi, add dtoverlay=dwc2 to /boot/firmware/config.txt or /boot/config.txt.)

Add the required modules to /etc/modules:

dwc2
libcomposite
uinput
evdi

Reboot your device after applying these changes.

Dependencies

Install the required build tools, kernel headers for your running kernel, and graphics development libraries (Debian/Ubuntu):

sudo apt update
sudo apt install -y build-essential cmake pkg-config git \
    libssl-dev libprotobuf-dev protobuf-compiler \
    libavcodec-dev libavutil-dev libswscale-dev \
    linux-headers-$(uname -r) dkms libdrm-dev

Compiling EVDI from Source

Distribution packages of EVDI are often outdated, so build it from the official DisplayLink repository.

cd ~
git clone https://github.com/DisplayLink/evdi.git
cd evdi

# 1. Build and install the userspace library (libevdi)
cd library
make
sudo make install
sudo cp evdi_lib.h /usr/local/include/ # The EVDI Makefile does not install the header
sudo ldconfig # Refresh the linker cache so BerryAuto can find the new library
cd ..

# 2. Build and install the kernel module via DKMS
# Extract the current version from dkms.conf
EVDI_VER=$(grep PACKAGE_VERSION module/dkms.conf | cut -d'=' -f2 | tr -d '"')

# Create the DKMS source directory and copy the module files into it
sudo mkdir -p /usr/src/evdi-$EVDI_VER
sudo cp -a module/. /usr/src/evdi-$EVDI_VER/

# Build and install the module
sudo dkms add -m evdi -v $EVDI_VER
sudo dkms build -m evdi -v $EVDI_VER
sudo dkms install -m evdi -v $EVDI_VER

# Load the module into the running kernel
sudo modprobe evdi

Building BerryAuto

The build embeds a TLS certificate and private key into the binary. Make sure berryautod/certs/android_auto.crt and berryautod/certs/android_auto.key exist before running CMake; the build will fail without them.

cd ~
git clone https://github.com/Cxmrykk/BerryAuto.git
cd BerryAuto/berryautod
mkdir build
cd build
cmake ..
make -j$(nproc)

Protocol sources are generated from berryautod/proto/ when CMake configures the project. If you change any .proto file, re-run cmake .. (or delete the build directory and rebuild).

Installation & Setup

The helper scripts must be on your system path:

cd ~/BerryAuto
sudo ln -sf "$(pwd)/scripts/setup_opengal_gadget.sh" /usr/local/bin/
sudo chmod +x /usr/local/bin/setup_opengal_gadget.sh

Add your user to the video group to access hardware encoders:

sudo usermod -aG video $(whoami)

The runner script reconfigures the USB gadget with sudo each time the head unit reconnects, so it needs to run without a password prompt:

echo "$(whoami) ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/$(whoami)

Note that this grants your user passwordless sudo for every command. If you prefer, restrict the rule to only the commands the runner script needs.

Configuration

BerryAuto works without any configuration. It picks the best video configuration the head unit offers and searches your system for a usable hardware encoder.

To override the encoder, bitrate, or resolution, create a configuration file at ~/.config/berryauto.conf or /etc/berryauto.conf. See CONFIG.md for all options and an example.

Running BerryAuto

  1. Connect the SBC's OTG port to the car's Android Auto USB port.
  2. Start the runner script:
./scripts/run_berryauto.sh

When the head unit starts an Android Auto session, BerryAuto creates the virtual display, your desktop extends or resizes onto it, and video starts streaming to the car's screen. There are no screen-sharing prompts to accept.

Autostart on Boot (systemd Service)

To start BerryAuto on boot and have it recover automatically whenever the head unit connects or disconnects, set it up as a systemd service.

From the project root directory (~/BerryAuto), run:

# Enable systemd linger for the current user so user-level systemd-run commands execute correctly
loginctl enable-linger $USER

# Get your user's UID
USER_UID=$(id -u $USER)

# Create the service file
sudo tee /etc/systemd/system/berryauto.service > /dev/null <<EOF
[Unit]
Description=BerryAuto Daemon Runner
After=graphical.target systemd-modules-load.service user@${USER_UID}.service
Requires=user@${USER_UID}.service
Wants=graphical.target

[Service]
Type=simple
User=$USER
Group=$(id -g -n)
Environment="XDG_RUNTIME_DIR=/run/user/${USER_UID}"
WorkingDirectory=$PWD
ExecStart=$PWD/scripts/run_berryauto.sh

# Prevent systemd from permanently giving up if the script restarts quickly
Restart=always
RestartSec=3
StartLimitIntervalSec=0

[Install]
WantedBy=graphical.target
EOF

# Reload systemd and enable the service
sudo systemctl daemon-reload
sudo systemctl enable --now berryauto.service

Check the status of the service at any time with:

systemctl status berryauto.service

Safety

BerryAuto does not implement Android Auto's driving restrictions. Whatever is on the SBC's desktop is shown on the car's screen and remains fully interactive while the vehicle is moving. Don't use it in a way that distracts the driver, and check your local laws regarding in-vehicle displays.

About

Use Android Auto compatible head units as an external display (ft. Raspberry Pi)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages