My ultimate guide to the Raspberry pi audio server I wanted — Bluetooth

Published: 2020-04-19

Part 1 — Bluetooth

Given my prior experience, I wanted to start by the bluetooth sink. It’s my first priority to connect my amplifier and I felt it would be easier to configure the rest of my requirements according to bluetooth constraints. I found an excellent guide for the bluetooth setup, which really covered all my bluetooth needs:

#EDIT 2025: This can also be achieved with PipeWire to replace PulseAudio, which I highly recommend today. With the following you can skip 1. Install PulseAudio. But from 2. bluetooth configuration everything still applies, which is nice.

$ sudo apt install pipewire pipewire-pulse wireplumber libspa-0.2-bluetooth  
$ systemctl --user enable --now pipewire.service pipewire-pulse.service wireplumber.service  
$ sudo usermod -a -G bluetooth pi

#END EDIT 2025

1. Install PulseAudio

First thing we have to install Pulseaudio and its bluetooth module

$ sudo apt-get install pulseaudio pulseaudio-module-bluetooth

We will be running pulse audio as a normal user as recommended, so we need to add our user to the bluetooth group

$ sudo usermod -a -G bluetooth pi  
$ systemctl --user enable pulseaudio   
Created symlink /home/pi/.config/systemd/user/default.target.wants/pulseaudio.service → /usr/lib/systemd/user/pulseaudio.service.   
Created symlink /home/pi/.config/systemd/user/sockets.target.wants/pulseaudio.socket → /usr/lib/systemd/user/pulseaudio.socket.

2. Bluetooth configuration

Lets modify our bluetooth configuration to declare it as audio sink
/etc/bluetooth/main.conf

[General]   
Name = Ampli-BT   
Class = 0x200428  
DiscoverableTimeout = 0 #Always discoverable   

[Policy]   
AutoEnable=true

We use raspi-config to do the autologin part

$ sudo raspi-config

activate autologin for user “pi” by selecting

Close raspi-config and then reboot
Now let’s login again. Pulseaudio and bluetooth should be started:

$ systemctl status --user pulseaudio  
● pulseaudio.service - Sound Service  
Loaded: loaded (/usr/lib/systemd/user/pulseaudio.service; enabled; vendor preset: enabled)  
Active: active (running) since Sat 2020–04–18 01:26:23 CEST; 15h ago  
Main PID: 426 (pulseaudio)  
CGroup: /user.slice/user-1000.slice/user@1000.service/pulseaudio.service  
 └─426 /usr/bin/pulseaudio --daemonize=no  

Apr 18 01:25:56 Ampli-BT systemd\[413\]: Starting Sound Service…  
Apr 18 01:26:23 Ampli-BT systemd\[413\]: Started Sound Service.  

$ sudo systemctl status bluetooth  
● bluetooth.service - Bluetooth service  
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)  
Active: active (running) since Sat 2020–04–18 01:25:32 CEST; 15h ago  
Docs: man:bluetoothd(8)  
Main PID: 281 (bluetoothd)  
Status: "Running"  
Memory: 4.1M  
CGroup: /system.slice/bluetooth.service  
 └─281 /usr/lib/bluetooth/bluetoothd  

 Apr 18 01:25:30 Ampli-BT systemd\[1\]: Starting Bluetooth service…  
 Apr 18 01:25:32 Ampli-BT bluetoothd\[281\]: Bluetooth daemon 5.50  
 Apr 18 01:25:32 Ampli-BT systemd\[1\]: Started Bluetooth service.  
 Apr 18 01:25:32 Ampli-BT bluetoothd\[281\]: Starting SDP server  
 Apr 18 01:25:32 Ampli-BT bluetoothd\[281\]: Bluetooth management interface 1.14 initialized  
 Apr 18 01:25:32 Ampli-BT bluetoothd\[281\]: Sap driver initialization failed.  
 Apr 18 01:25:32 Ampli-BT bluetoothd\[281\]: sap-server: Operation not permitted (1)  
 Apr 18 01:26:23 Ampli-BT bluetoothd\[281\]: Endpoint registered: sender=:1.15 path=/MediaEndpoint/A2DPSource  
 Apr 18 01:26:23 Ampli-BT bluetoothd\[281\]: Endpoint registered: sender=:1.15 path=/MediaEndpoint/A2DPSink

Now using bluetoothctl we’ll be able to pair our phone with the pi audio sink.

$ bluetoothctl  
 [NEW] Controller XX:XX:XX:XX:XX:XX Ampli-BT [default]  
 [bluetooth]# power on  
 Changing power on succeeded  
 [bluetooth]# discoverable on  
 Changing discoverable on succeeded  
 [CHG] Controller XX:XX:XX:XX:XX:XX Discoverable: yes  
 [bluetooth]# pairable on  
 Changing pairable on succeeded  
 [bluetooth]# agent on  
 Agent registered  
 [bluetooth]# default-agent  
 Default agent request successful

We should now be able to pair with our phone, but we’ll need to trust the device from the pi before being able to play music

[NEW] Device YY:YY:YY:YY:YY:YY <your smartphone>  
[bluetooth]# trust YY:YY:YY:YY:YY:YY   
Changing YY:YY:YY:YY:YY:YY trust succeeded   
[bluetooth]# quit   
[DEL] Controller XX:XX:XX:XX:XX:XX Ampli-BT [default]

From your phone you should now be able to play music and hear it from the speakers connected to your pi

3. Pin Authentication

Pretty nice, but nobody wants to start a computer to run bluetoothctl commands when playing music from a new device. So we’ll add PIN authentication to make pairing and trusting headless. We’ll need a new package including bt-agent to help with that

$ sudo apt-get install bluez-tools

Create /etc/bluetooth/pin.conf WITH a trailing line

* 1234  

And set permissions as needed.

$ sudo chown root:root /etc/bluetooth/pin.conf  
$ sudo chmod 600 /etc/bluetooth/pin.conf

* represents all phone, we could give a different PIN for different bluetooth MAC address, but that’s not interesting here.

We need a new systemd unit to automate bt-agent /etc/systemd/system/bt-agent.service

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

[Service]   
Type=simple   
ExecStart=/usr/bin/bt-agent -c NoInputNoOutput -p /etc/bluetooth/pin.conf  
ExecStartPost=/bin/sleep 1   
ExecStartPost=/bin/hciconfig hci0 sspmode 0  

[Install]   
WantedBy=bluetooth.target

and automates this at boot

$ sudo systemctl daemon-reload  
$ sudo systemctl enable bt-agent  
$ sudo systemctl restart bt-agent  
$ sudo systemctl status bt-agent.service   
● bt-agent.service - Bluetooth Auth Agent  
Loaded: loaded (/etc/systemd/system/bt-agent.service; enabled; vendor preset: enabled)  
Active: active (running) since Sat 2020–04–18 01:25:33 CEST; 16h ago  
Main PID: 303 (bt-agent)  
Memory: 2.5M  
CGroup: /system.slice/bt-agent.service  
 └─303 /usr/bin/bt-agent -c NoInputNoOutput -p /etc/bluetooth/pin.conf  

Apr 18 01:25:32 Ampli-BT systemd\[1\]: Starting Bluetooth Auth Agent…  
Apr 18 01:25:33 Ampli-BT bt-agent\[303\]: Agent registered  
Apr 18 01:25:33 Ampli-BT bt-agent\[303\]: Default agent requested  
Apr 18 01:25:33 Ampli-BT systemd\[1\]: Started Bluetooth Auth Agent.

Our headless bluetooth speaker is now working as expected.

Playing sound when bluetooth connects

I also wanted to play a sound when a device connects to bluetooth to get audio feedback on the connection. I used udev for this. Udev helps you executes script based on filtered kernel events. Here we’ll look for an add event in the bluetooth subsystem.

# Edit: I found a better udev rule. An input device is also added later in the bluetooth connection process, so sound is now only played after PIN code authentication has happened, which makes more sense.

/etc/udev/rules.d/99-local.conf

# I kept 1st version as as reminder  
#ACTION=="add", SUBSYSTEM=="bluetooth", RUN+="/usr/local/bin/bt-connection.sh"  
# This stopped working after a kernel upgrade  
#ACTION=="add", KERNEL=="input[0–9]*", SUBSYSTEM=="input", ATTR{name}=="\"*:*:*:*:*:*\"", RUN+="/usr/local/bin/bt-connection.sh"  
# This stopped working after a kernel upgrade  

ACTION=="add", KERNEL=="input[0–9]*", SUBSYSTEM=="input", ENV{PHYS}=="\"*:*:*:*:*:*\"", ENV{ID\_BUS}="bluetooth", RUN+="/usr/local/bin/bt-connection.sh"  
ACTION=="add", SUBSYSTEM=="bluetooth", RUN+="/usr/local/bin/bt-connection.sh"

/usr/local/bin/bt-connection.sh

#!/bin/sh  
if [[ $(whoami) == "root" ]]; then  
 /bin/su - pi -c 'pactl play-sample success'  
else  
 pactl play-sample success  
fi  
echo "$(date): ${NAME} successfully connected"

We now need to restart udev to apply this new rule

$ sudo udevadm control --reload-rules

A sound is now indeed played when a device bluetooth connects. But there ̶a̶r̶e̶ was a few anoying things about it:

$ mpg123 -w /usr/local/share/sounds/success.wav /usr/local/share/sounds/success.mp3  
$ pactl upload-sample /usr/local/share/sounds/success.wav  
$ sudo apt remove mpg123

So I’ll look for a more precise udev rule, and a way of playing sound from mpc, MPD default command line interface some day.
# Edit: More precise udev rule found.
# Edit: mpg123 removed
# Edit: fixed udev rule after kernel upgrade

Part 2 : Pulseaudio TCP