The pipewire
solution:
I think I have this working finally...
NOTE: This project began on a bullseye
'Lite' RPi 3A, and wound up on the 64-bit bookworm
'Lite' in an effort to improve reliability. I hope this answer is generally useful to others, but based on past experience with Bluetooth on Raspberry Pi that is probably too much to hope for... :(
Before starting this exercise, I read in this Debian wiki that for Debian 11 (aka bullseye
) pipewire
was "experimental" and "unsupported". This led me to the decision that I should use pulseaudio
to play sound through my old Flip 5 BT speaker. AIUI (not very well at all!) the alternative to pulseaudio
was to deal more-or-less directly with the Bluetooth stack via ALSA. I've tried the ALSA route, but found it... x-confusing. OTOH, I had been successful using pulseaudio
on another RPi under the buster
version of RPi OS.
I still don't know what I did wrong with pulseaudio
. I couldn't really troubleshoot this because I could not find any documentation - none that I was able to understand. There has been tons of stuff written on the subject, but everything I tried led to a dead-end or failure.
Through groping around in the dark, I eventually came to realize that using systemctl
to check the status of the bluetooth.service was a reasonably good indicator as to how things were going to go. The lines in the listing below re:
Sap driver initialization failed
, and
Failed to set privacy: Rejected
made no sense to me, but I found they were present at each failure.
$ systemctl status bluetooth.service
● bluetooth.service - Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2024-04-08 04:08:13 UTC; 7s ago
Docs: man:bluetoothd(8)
Process: 1543 ExecStartPost=/usr/bin/sdptool add SP (code=exited, status=0/SUCCESS)
Main PID: 1542 (bluetoothd)
Status: "Running"
Tasks: 1 (limit: 407)
CPU: 191ms
CGroup: /system.slice/bluetooth.service
└─1542 /usr/libexec/bluetooth/bluetoothd -C
Apr 08 04:08:13 rpi3a bluetoothd[1542]: Bluetooth daemon 5.55
Apr 08 04:08:13 rpi3a bluetoothd[1542]: Starting SDP server
Apr 08 04:08:13 rpi3a bluetoothd[1542]: Bluetooth management interface 1.22 initialized
Apr 08 04:08:13 rpi3a bluetoothd[1542]: profiles/sap/server.c:sap_server_register() Sap driver initialization failed.
Apr 08 04:08:13 rpi3a bluetoothd[1542]: sap-server: Operation not permitted (1)
Apr 08 04:08:13 rpi3a bluetoothd[1542]: Failed to set privacy: Rejected (0x0b)
Apr 08 04:08:13 rpi3a sdptool[1543]: Serial Port service registered
Apr 08 04:08:13 rpi3a systemd[1]: Started Bluetooth service.
Apr 08 04:08:13 rpi3a bluetoothd[1542]: Endpoint registered: sender=:1.31 path=/MediaEndpoint/A2DPSink/sbc
Apr 08 04:08:13 rpi3a bluetoothd[1542]: Endpoint registered: sender=:1.31 path=/MediaEndpoint/A2DPSource/sbc
This repeated failure led me to reconsider my earlier decision to use pulseaudio
instead of pipewire
. However, I wanted to give pipewire
the best chance of success - which meant using a more current version than the one available in bullseye
.
I decided to check the "bullseye backports" branch of the repo for a later version. I was in luck: ver 0.3.65
(same as in Debian 12) was available. For help on how to set up backports, please see this Q&A.. I also relied on this page for pipewire
installation instructions - as there are no instructions in the RPi documentation site.
The other thing that must be done to get Bluetooth audio working is to make sure that the RPi BT stack: 1. can "see" the speaker, 2. the speaker can be "paired" w/ the RPi's BT controller, and 3. the speaker is declared "trusted" by you. The bluetoothctl
app can do all of this. You can run it interactively by simply entering bluetoothctl
at the command prompt. In interactive mode, you will see a prompt [bluetooth]#
. You can type help
at the prompt to see all (most?) of the available commands.
Following is a sample dialog:
$ bluetoothctl
Agent registered
[CHG] Controller B8:27:EB:F3:8A:58 Pairable: yes
[bluetooth]# power on
Changing power on succeeded
[bluetooth]# scan on
Discovery started
...
# unfortunately, you'll have to review all of these results to identify your speaker...
# and you'll also need a lot of patience as this may take a while!
# eventually, you may see something like this:
...
[NEW] Device B8:F6:53:AE:13:F1 JBL Flip 5
[bluetooth]# scan off
[bluetooth]# devices
Device 5C:XX:XX:XX:XX:BF fumier
Device 00:XX:XX:XX:XX:5F SZG DW2450
Device B8:F6:53:AE:13:F1 JBL Flip 5
Device DC:XX:XX:XX:XX:97 rpi4b
[bluetooth]# pair B8:F6:53:AE:13:F1
Attempting to pair with B8:F6:53:AE:13:F1
[CHG] Device B8:F6:53:AE:13:F1 Paired: yes
...
Pairing successful
[bluetooth]# trust B8:F6:53:AE:13:F1
[CHG] Device B8:F6:53:AE:13:F1 Trusted: yes
Changing B8:F6:53:AE:13:F1 trust succeeded
[bluetooth]# connect B8:F6:53:AE:13:F1
Hopefully, you get a message that indicates the connection was successful. You may then leave bluetoothctl
by typing exit
at the prompt. It gets lost in the dialog above, but the sequence of commands in the interactive bluetoothctl
session is as follows:
power on
scan on
scan off (following discovery of your target device/speaker
devices (to list everything "discovered")
pair
trust
connect
help (to list available commands)
Again, I found it useful to check w/ systemctl
to get status of the bluetooth service before moving forward:
$ systemctl status bluetooth.service
$ systemctl status bluetooth.service
● bluetooth.service - Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2024-04-08 09:40:37 UTC; 17h ago
Docs: man:bluetoothd(8)
Main PID: 509 (bluetoothd)
Status: "Running"
Tasks: 1 (limit: 407)
CPU: 176ms
CGroup: /system.slice/bluetooth.service
└─509 /usr/libexec/bluetooth/bluetoothd
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSource/aptx_ll_0
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSource/aptx_ll_duplex_1
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSource/aptx_ll_duplex_0
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSource/faststream
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSource/faststream_duplex
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSink/opus_05
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSource/opus_05
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSink/opus_05_duplex
Apr 08 09:45:06 rpi3a bluetoothd[509]: Endpoint registered: sender=:1.18 path=/MediaEndpoint/A2DPSource/opus_05_duplex
Apr 08 23:47:20 rpi3a bluetoothd[509]: /org/bluez/hci0/dev_B8_F6_53_9B_1A_97/fd1: fd(44)
ready
ready
- no bitching about SAP drivers, etc. We are lookin' good! :)
And so the moment of truth has arrived, and I start my player on a soundtrack glommed from YouTube:
$ nohup mpg123 --loop -1 rainstorm.mp3 &
[2] 28719
$
YES! I can hear it... the sound of falling rain!
Conclusion: I am beginning to believe some of the very positive things I've read about pipewire
. G'night...
Epilogue & Conclusions:
9 Apr '24: FWIW:
This isn't a particularly helpful remark, but it may be useful for setting expectations. If you spend a week or so chasing Bluetooth problems on a Linux system, you begin to wonder: "Does Bluetooth on Linux just suck?" Unfortunately, I think the answer may be, "Yes, it does suck." I've played audio thru a BT speaker using my iPhone6 as the music/control source for days without an interrupt; the setup/connection process takes maybe 30 seconds. Using Linux/RPi as the music/control source I can occasionally manage a few hours of continuous play, while spending hours futzing with arcane programs to accomplish the setup. BT under Linux just sucks - you're going have to deal with that.
12 Apr '24:
One of the annoying things that you may be forced to deal with using pipewire/wireplumber
is that the associated systemd
service runs under user ownership. "What does this mean?", you may ask. It means that if you logout
, your music stops.
As a workaround to this logout issue, I thought that I could use agetty
to create a password-less console login for user pi
, started from the root crontab
. I was unable to get that idea working, but I found a solution that did work in the Debian forum.
However... after a few days the pipewire/wireplumber
solution began to wear on me. I was not keen on the opacity of pipewire
, and it proved to be less reliable than I hoped. After a couple of days, I tossed 'bullseye' and installed 'bookworm' on the RPi. Unfortunately that resulted in almost no improvement. When the music stopped, the only solution I could find to re-start it was to reboot the system. And finally, pipewire
is probably gross overkill for my use-case: a Raspberry Pi and a single £75 Bluetooth speaker :)
10 Oct '24 (FINAL UPDATE):
Please note this will be the final update for this answer...
This has turned into a saga. I'm still experimenting - and still learning - but I've been posting the results to my GitHub account.
After expressing some misgivings re the pipewire
solution, I've flip-flopped again. Completing a 1,500-hour reliability test with pipewire
had much to do with that! Additionally, I am now also using pipewire
on an RPi 3A+, where I use cmus
as the player. It has been remarkably reliable there also. If you'd like more details, feel free to follow the saga on GitHub :)
sudo su && tar cf bluePi3B+Buster.tar /var/lib/bluetooth
and use this to setup other images. This is a kludge but it works.