Skip to content

Instantly share code, notes, and snippets.

@dandanio
Created June 7, 2021 02:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dandanio/705447323c621969894b9a2cd27f7fc8 to your computer and use it in GitHub Desktop.
Save dandanio/705447323c621969894b9a2cd27f7fc8 to your computer and use it in GitHub Desktop.
Headless A2DP Audio Streaming on Raspbian Stretch with a Raspberry Pi Zero W and a HiFiBerry DAC+ Zero

Headless A2DP Audio Streaming on Raspbian Stretch with a Raspberry Pi Zero W and a HiFiBerry DAC+ Zero

About

This gist will show how to setup Raspberry Pi OS Buster running on a Raspberry Pi Zero HW with a HiFiBerry DAC+ Zero, connected to a regular set of computer speakers for a DIY Streaming Bluetooth Speaker. You can use any device with a A2DP capability like a phone, a tablet or your laptop to stream audio. Towards the bottom, I added some bonus features like low latency optimization so audio played has minimal lag.

Motivation

There are a million and one guides and gists out there but none of them actually does what I wanted to do "out of the box" - there are always bigger or smaller hacks involved. I wanted a turnkey solution that would not relay on any external scripts of extra code. It is up to date as of June 2021.

All examples below are from a Mac OS X laptop.

Hardware Prerequisites

  • A Raspberry Pi Zero HW (the one with WiFi and headers) (you can use a Zero W and either solder or hammer the headers to it)
  • HiFiBerry DAC+ Zero HAT (for audio output)
  • Min. 4GB micro SD card
    • Technically, you can get by with 2GB card since the image is less than 1.9GB in size, but you won't be able to, for example, update the install.
  • A PC (Windows, Mac or Linux)

Software preparation

  1. Download and install Raspberry Pi OS Lite to a micro SD card using balenaEtcher or similar software.

  2. After a successful install, mount the boot partition and prepare the OS to work headlessly (without a keyboard or a screen):

    • Open a terminal window and cd into the mounted /boot partition (cd /Volumes/boot) and configure wifi networking:
    $ cat <<EOF > wpa_supplicant.conf
    country=US
    ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
    update_config=1
    
    network={
        ssid="YOUR_SSID"
        psk="YOUR_SSID_PASSWORD"
    }
    EOF
    • Create an empty file ssh to enable ssh at boot:
    $ touch ssh
    • While there, we might as well enable the HiFiBerry DAC+ Zero HAT: inside config.txt
    [all]
    ...
    start_x=0
    gpu_mem=16
    dtoverlay=hifiberry-dac
    ...

    or just use the script (for Mac OS X use gsed instead):

    $ sed -i '/\[all\]/a start_x=0\ngpu_mem=16\ndtoverlay=hifiberry-dac\n' config.txt
    • Unmount and remove the card, put it into the Raspberry Pi and boot.

Initial Setup

  1. Find out the zero's IP address and ssh to it with a user pi and password raspberry.

  2. Change the default password!

    $ passwd
  3. Make sure the system is up to date using the following commands.

    $ sudo apt update && sudo apt upgrade
  4. Reboot.

  5. Log back in and configure your fresh install with raspi config:

    $ sudo raspi-config
  6. Reboot as required.

  7. Check if your HiFiBerry DAC+ Zero is properly seen by the system:

    $ aplay -l
    **** List of PLAYBACK Hardware Devices ****
    card 0: sndrpihifiberry [snd_rpi_hifiberry_dac], device 0: HifiBerry DAC HiFi pcm5102a-hifi-0 [HifiBerry DAC HiFi pcm5102a-hifi-0]
    Subdevices: 1/1
    Subdevice #0: subdevice #0
  8. OPTIONAL Disable WiFi powersave to increase performance and decrease latency:

    $ sudo systemctl --full --force edit wifi_powersave@.service

    In the empty editor insert these statements, save them and quit the editor:

        [Unit]
        Description=Set WiFi power save %i
        After=sys-subsystem-net-devices-wlan0.device
    
        [Service]
        Type=oneshot
        RemainAfterExit=yes
        ExecStart=/sbin/iw dev wlan0 set power_save %i
    
        [Install]
        WantedBy=sys-subsystem-net-devices-wlan0.device
    

    Now enable just what you want on boot up:

    $ sudo systemctl disable wifi_powersave@on.service
    $ sudo systemctl enable wifi_powersave@off.service
  9. OPTIONAL Use zram as a swap space to save the card. One way is to use this repo.

    $ sudo systemctl stop dphys-swapfile.service
    $ sudo systemctl disable dphys-swapfile.service
    $ sudo rm /var/swap
  10. Reboot

  11. Finally, log back in again and install the required packages.

    $ sudo apt install bluealsa bluez-tools --no-install-recommends

Make bluetooth discoverable

Normally a Bluetooth device is only discoverable for a limited amount of time. Since this is a headless setup we want the device to always be discoverable.

  1. Set the DiscoverableTimeout in /etc/bluetooth/main.conf to 0

    # How long to stay in discoverable mode before going back to non-discoverable
    # The value is in seconds. Default is 180, i.e. 3 minutes.
    # 0 = disable timer, i.e. stay discoverable forever
    DiscoverableTimeout = 0
    
  2. Restart bluetooth service:

    $ sudo systemctl restart bluetooth.service`
  3. Enable discovery on the Bluetooth controller

    $ sudo bluetoothctl
    power on
    discoverable on
    exit

Install and configure the bt-agent service for a pin-less authentication

Create, enable and start bt-agent service:

$ sudo systemctl --full --force edit bt-agent.service

add:

[Unit]
Description=Bluetooth Auth Agent
After=bluetooth.service
PartOf=bluetooth.service

[Service]
Type=simple
ExecStart=/usr/bin/bt-agent -c NoInputNoOutput

[Install]
WantedBy=bluetooth.target
$ sudo systemctl enable bt-agent.service
$ sudo systemctl start bt-agent.service

Configure bluealsa service

Run:

$ sudo systemctl --full --force edit bluealsa.service

and add -p a2dp-sink to the ExecStart line, so it looks like this:

...
ExecStart=/usr/bin/bluealsa -p a2dp-sink
...

Configure the bluealsa-aplay service

Run:

$ sudo systemctl --full --force edit bluealsa-aplay.service

and add this to the file:

[Unit]
Description=BlueALSA aplay service
After=bluetooth.service
Requires=bluetooth.service

[Service]
ExecStart=/usr/bin/bluealsa-aplay -vv --profile-a2dp --pcm-buffer-time=24000 --pcm-period-time=6000 00:00:00:00:00:00

[Install]
WantedBy=multi-user.target

Then enable the service:

$ sudo systemctl enable bluealsa-aplay.service

Reboot.

Testing

Before testing with a device, log in to the Raspberry Pi and make sure all services started properly.

Run:

$ sudo ps auxw | grep bluealsa
root       309  0.0  0.6  15536  3276 ?        Ssl  20:41   0:03 /usr/bin/bluealsa-aplay -vv --profile-a2dp --pcm-buffer-time=24000 --pcm-period-time=6000 00:00:00:00:00:00
root       404  0.2  1.0  35012  5012 ?        Ssl  20:41   0:16 /usr/bin/bluealsa -p a2dp-sink

If you see the above processes, there is a big chance there will be sound!

Start a tail on /var/log/syslog like that:

$ sudo tail -f /var/log/syslog

and try to search for your device in the bluetooth pairing section. Once connected and paired, try streaming!

Troubleshooting

It might happen that the you won't be able to adjust the volume from your device. Try this:

$ sudo systemctl --full --force edit bluetooth.service

and add --noplugin=avrcp to the systemd service file so it looks like this:

[Unit]
...
[Service]
...
ExecStart=/usr/lib/bluetooth/bluetoothd --noplugin=avrcp
...
[Install]
...

Restart your bluetooth.service and try again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment