January 16, 2014

Bluetooth Audio A2DP Receiver with Raspberry Pi

Filed under: Technical — Tags: , , , , , — James Bunton @ 10:34 am

My latest project was to have wireless audio streaming from my Symbian mobile phone over Bluetooth to a a new set of speakers. I used PulseAudio, Bluez5 and Arch Linux running on a Raspberry Pi. It all works really well. I can connect/disconnect from the phone and everything is automatically started on boot. I’m enjoying it now as I type this.

Thanks to Greg & Helen for the Christmas present! :)

Installing Arch Linux on ARM

I’ve been trying out Arch Linux recently and so far I quite like it. For an experienced Linux user it’s easy to get up and running and to maintain. All the packages are always up to date, which is awesome. Systemd makes managing your own services really simple. Writing custom packages with ABS is a breeze compared to using to my previous experiences with dpkg/apt, RPM or Fink.

So Arch Linux only officially supports the i686 and x86_64 architectures. Fortunately there’s an Arch Linux ARM project which rebuilds all the packages for various ARM architectures. This includes a disk image for Raspberry Pi. Follow the instructions there to get your RasPi up and running with Arch. You can use the excellent Arch Wiki from the main project for the Wifi/Network setup guides.

Make sure your system is up to date. Remember Arch doesn’t support partial upgrades, so always do both the repository update and package upgrade at the same time.

# pacman -Syu

I also chose to use the latest Linux kernel instead of the stable branch with Arch uses by default. If you want this too:

# pacman -S linux-raspberrypi-latest

Basic tools

This is a selection of basic tools that I like to have everywhere.

# pacman -S \
    sudo python bash-completion base-devel openssh \
    git screen vim mercurial openbsd-netcat rsync unzip

Hacks!!

There are two bugs that I had to work around to make this setup functional.

Kernel OOPS with bluetooth & audio at the same time

Firstly using the builtin audio at the same time as the Bluetooth adapter caused a kernel panic! See the GitHub issues RasPi #465, RasPi #482, RasPi #491 and RasPi #499. I worked around this by using a USB audio adapter and blacklisting the internal audio.

$ cat /etc/modprobe.d/use-usb-audio.conf 
blacklist snd_bcm2835
options snd-usb-audio nrpacks=1

There is a reported work-around to use the builtin audio. Try adding this to /boot/cmdline.txt

dwc_otg.fiq_split_enable=0

PulseAudio too picky when decoding SBC

Update! This patch was merged into Pulseaudio. It is no longer necessary for PulseAudio >= 6.

Secondly PulseAudio seems to be very picky about the SBC data it receives from the phone. After 5-10 mins of playing I’ll see “SBC Decoding Error (-3)”, after which PulseAudio will terminate playback & clean up the Bluetooth connection. I wrote a quick patch to fix this, see commit 4a5f48e7a42f997793db76e2001b7c252f8d93fe. If you want this you can download my custom pulseaudio package and install it with pacman -U instead of using the version from the repository. If you want to build this yourself using ABS then you can download the PKGBUILD and other files.

Audio

I’m using a USB audio adapter because of the bug mentioned above, and also the builtin analogue audio is not great quality anyway.

$ lsusb|grep Audio
Bus 001 Device 006: ID 0d8c:000e C-Media Electronics, Inc. \
  Audio Adapter (Planet UP-100, Genius G-Talk)

I bought it from Adelong computers for $6.50. It sounds great! Make sure you use a powered hub though, the Raspberry Pi is not able to supply too much energy through its USB ports.

Next lets get the audio software set up:

# pacman -S alsa-utils alsa-lib
# gpasswd -a <your-username> audio
$ alsamixer # set the volume to max
$ speaker-test # You should hear sound!

Next PulseAudio.

# pacman -S pulseaudio pulseaudio-alsa pulseaudio-bluetooth
# useradd pulse
# gpasswd -a pulse audio

Now try switching to the newly created pulse and running pulseaudio -v. You can ignore the DBUS warnings. The error about bluez4 also doesn’t matter because we’re using bluez5. Try playing some wav file audio with paplay as a test.

Finally lets get Pulse running automatically on startup. Systemd makes this so easy! First create pulseaudio.service:

[Unit]
Description=pulseaudio service
Requires=bluetooth.target

[Service]
User=pulse
ExecStart=/usr/bin/pulseaudio -v
Restart=always
LimitRTPRIO=99
LimitNICE=40
LimitMEMLOCK=40000

[Install]
WantedBy=multi-user.target

Next enable the service:

# cp pulseaudio.service /etc/systemd/system/pulseaudio.service
# systemctl enable pulseaudio
# systemctl start pulseaudio
# systemctl status pulseaudio # it should be running

Bluetooth

Installation

Now install and set up Bluetooth. Check that your Bluetooth adapter shows up in the output from lsusb before proceeding. I had better luck plugging the Bluetooth directly into the RasPi rather than through a hub, so try that.

# pacman -S bluez bluez-libs bluez-utils
# gpasswd -a <your-username> lp # gives access to bluetooth [1]
# gpasswd -a pulse lp
# systemctl enable bluetooth
# systemctl start bluetooth
# hciconfig hci0 up
# hciconfig hci0 class 0x200420 # Pretend to be a car sound system [2]

[1] See the policy in /etc/dbus-1/system.d/bluetooth.conf
[2] Bluetooth Class of Device/Service (CoD) Generator

Make it persistent

We want to enable the bluetooth device on boot as well as set the class persistently even if the bluetooth adapter changes.

# echo > /etc/udev/rules.d/10-bluetooth.rules \
  'ACTION=="add", KERNEL=="hci0", RUN+="/usr/bin/hciconfig hci0 up"'

Also edit /etc/bluetooth/main.conf. You should uncomment “Class” and set it to the same value as above, ‘0x200420‘. I also like to uncomment “Name” and just set it to ‘%h‘.

Pair your device

Follow this process whenever you want to pair a new device with your RasPi.

$ bluetoothctl
[bluetooth]# agent KeyboardOnly
[bluetooth]# default-agent
[bluetooth]# discoverable on
[bluetooth]# pairable on

Now scan for the RasPi from your phone or other device and pair with it. You should be able to connect from your phone and stream audio! To allow the device to connect in the future you must use the trust command.

I should simplify this last step, but since I don’t do it often I have not bothered :)

Enjoy!

Things to check

systemd Journal

The Raspberry Pi uses an SD card for the root file system, this means that your /var/log is quite likely very slow. I would recommend changing /etc/systemd/journald.conf to store log output in memory rather than on the slow flash disk, set Storage=volatile. This means your logs will instead be written to /run/log.

Pulseaudio Priority

By default pulseaudio will have permission to start threads with realtime priority scheduling. This is needed for glitch-free playback, especially on low-powered hardware like the Raspberry Pi. If you use the systemd unit file I provided above then the LimitRTPRIO and LimitNICE options will be set to allow this. You can use top to make sure pulseaudio is running with these priorities.

Sample Rate

Sample rate conversion is CPU-intensive and significantly decreases the audio quality. Most music is distributed in 44.1kHz, so make sure your audio hardware supports this. While playing audio you can inspect /proc/asound/card0/pcm0p/sub0/hw_params to see what sample rate your hardware is using. Pulseaudio will try to configure your hardware to match the sample rate of the incoming audio. If they do not match then something went wrong, or you have cheap USB audio hardware that perhaps only supports 48kHz. In this case you should get better hardware.

Glitchy Sound

I found that if I tried to use my phone too much while playing audio then it would glitch badly. This didn’t happen with my car’s bluetooth setup so I figured it must be something to do with bluez and/or pulseaudio. I fixed it by editing /etc/pulse/daemon.conf and setting:

default-fragments = 10

22 comments

cw says:

Trying it out now. Noticed a few issues when following the instructions.

gpasswd -a audio #for noobs like myself, I presume a username needs to be specified?
pulseaudio #should this command be systemctl instead?

Do share which USB Audio Adapter you are using as well. Tks.

delx says:

@cw, thanks! I’ve updated the post with those fixes as well as adding a link to the usb audio hardware I’m using.

Soren says:

Hello..

Thanks for a great guide everything was working and pretty easy to understand and setup :)

I only got one issue, i can’t get my phone to automatically connect to the Rpi on boot. I tried looking through the arch wiki and google, but i didn’t find any clues on how to automatically connect on Rpi boot.

Of course i can connect from the bluetooth menu on my iPhone, and from the bluetoothclt with connect. But i use this on my car, and automatically join would be nice.

Have you tried this?

Thx

Best Regards
Soren

delx says:

@Soren
You should be able to trigger the connection from the Raspberry Pi using a command like this:

$ dbus-send \
  --print-reply \
  --system \
  --dest=org.bluez \
  /org/bluez/$(pidof bluetoothd)/hci0/dev_01_23_45_67_89_AF \
  org.bluez.AudioSource.Connect

If you get that to work then sticking it into a systemd unit to run on startup shouldn’t be too hard.

bruint says:

Hi There,
Been trying to implement this but not having a huge amount of luck.

It looks as though the audio.conf file may have been removed from BlueZ.
http://permalink.gmane.org/gmane.linux.bluez.kernel/37261

Any ideas/thoughts?

Cheers, Tom.

delx says:

@bruint You’d have to provide more detail. What exactly does not work? What have you tried? What OS and software versions are you using?

lsvx says:

Delx,

This is really cool! I had experimented with trying to get this same thing working on my arch pi a few months ago but gave up after way too many hours. Thanks a lot for putting this together!

I started with a fresh install and following your directions I was able to pair my phone with the pi and play music out of the native audio. You’re right, the native audio sounds pretty bad haha.

This works great but I am having the following issue: every time I reboot my pi, I have to retype the hciconfig commands to turn on the Bluetooth device and set the class and then enter the interactive bluetoothctl environment to make the pi discoverable and bootable. Additionally, once this is done, I must accept the pairing request for my phone in the bluetoothctl environment in order for it to connect even though I have already trusted it using the trust command. Is this expected? Is there any way to automate this so that I don’t have to have a monitor and keyboard connected to the pi in order to pair devices after a reboot?

I wrote a short udev rule that runs `hciconfig hci0 up` and `hciconfig hci0 piscan` when the Bluetooth device is loaded on boot; this makes the pi discoverable on my phone, but any attempt to pair with it fails. Do you have any ideas?

Thanks again,
lsvx

delx says:

@Isvx, glad you got it to work! :)
I’ve added a new section “Making it persistent” to the post. I’d forgotten about the udev rule that I added and the change to /etc/bluetooth/main.conf to set the class.

Did you remember to trust your device in bluetoothctl? After accepting the pairing you should be able to use the trust once, it gets remembered somewhere in /var/lib/bluetooth.

lsvx says:

@delx,
Thanks for taking a look. I had actually already trusted my phone and I can verify that it is trusted as running `info ` within the bluetoothctl shell shows `Trusted: yes`. After making the changes you suggested I am still getting similar issues:

Whenever I reboot, my device shows it is “up” but not discoverable or pairable; I still have to use the interactive shell to set discoverable and pairable to “on” for the pi to show up in my phone’s list of devices. Once this is set, I can once again try connecting (oddly the adapter’s name is always ‘alarmpi’ even after setting the name in the config, however the class does get applied) but I’m finding that the terminal is returning some errors and never successfully connects:

Bluetooth: hci0 corrupted ACL packet
Bluetooth: hci0 ACL packet for unknown connection handle 11
Bluetooth: hci0 ACL packet for unknown connection handle 10
Bluetooth: hci0 command 0x0419 tx timeout

I am starting to suspect that it maybe an issue with the particular Bluetooth dongle I am using but do not want to give up on it yet since I was able to successfully pair once.

Have you run into any of this before or have any suggestions?

Thanks for your time!

lsvx says:

@delx

After powercycling my pi a few time it looks like the situation has improved ie sorcery!

Whenever I reboot my pi, my device DOES come up as discoverable and pairable, though with the wrong name and correct class. When I try to pair with my phone, the pairing fails. I opened up the bluetoothctl shell to see what the interactive output would be and I am seeing that when I try to pair my phone I get:

[CHG] Device Connected: yes
[CHG] Device Connected: no
[CHG] Device Connected: yes
[CHG] Device Connected: no

It feels so close!!

lsvx says:

@delx

I finally found the issue!

I checked my bluetoothctl logs and found than when the pairing was failing I was getting:

Mar 24 08:38:13 alarmpi bluetoothd[160]: No agent available for request type 2
Mar 24 08:38:13 alarmpi bluetoothd[160]: device_confirm_passkey: Operation not permitted
Mar 24 08:38:13 alarmpi bluetoothd[160]: No agent available for request type 2
Mar 24 08:38:13 alarmpi bluetoothd[160]: device_confirm_passkey: Operation not permitted
Mar 24 08:41:12 alarmpi bluetoothd[160]: No agent available for request type 2

This mean that there was no agent available to negotiate the device pairing. To correct this, I ran:
# bluetoothctl
[bluetooth]# agent on
[bluetooth]# default-agent

The pi now has an agent available to negotiate requests and I can pair to my heart’s desire.

Thanks again for your great writeup and sorry for spamming your comments!

icepower says:

Hi,

I have issue, that I cannot connect my bluetooth device after a reboot of the Rasberry Pi. I always have to manually run pulseaudio –start (as root). Then it works perfectly. It is probably an issue with the permissions or something. That is what I found, but have no clues to resolve the issue:

—–wiki.archlinux.org——–
Pairing works, but connecting does not

You might see the following error in bluetoothctl:

[bluetooth]# connect 00:1D:43:6D:03:26
Attempting to connect to 00:1D:43:6D:03:26
Failed to connect: org.bluez.Error.Failed

To further investigate, have a look at the log via one of the following commands:

# systemctl status bluetooth
# journalctl -n 20

You might see a message like this:

bluetoothd[5556]: a2dp-sink profile connect failed for 00:1D:43:6D:03:26: Protocol not available

The problem in this case is that pulseaudio is not catching up. A common solution to this problem is to restart pulseaudio. Note that it is perfectly fine to run bluetoothctl as root while pulseaudio runs as user. After restarting pulseaudio, retry to connect. It is not necessary to repeat the pairing.

How can I restart pulseaudio after start?

Catani says:

I can’t get Pulse 5.0~11 and Bluez 5.18 to work together.
After pairing and connecting the phone to raspberry, the pulseaudio server crashes without warning (I’m running it with verbose arg: -vvvvvv):

Here is the log:

D: [pulseaudio] module-udev-detect.c: /dev/snd/controlC0 is accessible: yes
D: [pulseaudio] module-udev-detect.c: Resuming all sinks and sources of card alsa_card.platform-bcm2835_AUD0.0.
D: [pulseaudio] bluez5-util.c: Properties changed in adapter /org/bluez/hci0
D: [pulseaudio] bluez4-util.c: dbus: interface=org.freedesktop.DBus.Properties, path=/org/bluez/hci0, member=PropertiesChanged
D: [pulseaudio] bluez5-util.c: Properties changed in adapter /org/bluez/hci0
D: [pulseaudio] bluez4-util.c: dbus: interface=org.freedesktop.DBus.Properties, path=/org/bluez/hci0, member=PropertiesChanged
D: [pulseaudio] bluez5-util.c: Properties changed in device /org/bluez/hci0/dev_14_89_FD_9D_EE_DF
D: [pulseaudio] bluez4-util.c: dbus: interface=org.freedesktop.DBus.Properties, path=/org/bluez/hci0/dev_14_89_FD_9D_EE_DF, member=PropertiesChanged
D: [pulseaudio] bluez5-util.c: Unknown interface org.freedesktop.DBus.Introspectable found, skipping
D: [pulseaudio] bluez5-util.c: Unknown interface org.bluez.MediaTransport1 found, skipping
D: [pulseaudio] bluez5-util.c: Unknown interface org.freedesktop.DBus.Properties found, skipping
D: [pulseaudio] bluez4-util.c: dbus: interface=org.freedesktop.DBus.ObjectManager, path=/, member=InterfacesAdded
D: [pulseaudio] bluez4-util.c: dbus: interface=org.bluez.MediaEndpoint1, path=/MediaEndpoint/A2DPSink, member=SetConfiguration
D: [pulseaudio] bluez5-util.c: dbus: path=/MediaEndpoint/A2DPSink, interface=org.bluez.MediaEndpoint1, member=SetConfiguration
D: [pulseaudio] bluez5-util.c: Transport /org/bluez/hci0/dev_14_89_FD_9D_EE_DF/fd0 state changed from disconnected to idle
D: [pulseaudio] module-bluez5-discover.c: Loading module-bluez5-device path=/org/bluez/hci0/dev_14_89_FD_9D_EE_DF

It seems it crashes right after trying to load module-bluez5-discover.
For a second, the phone says “Connected to media audio” then pulseaudio crashes silently.

Any hints?

PS: I’m runing raspbian.

hasso says:

This worked first time, although I needed to create the /home/pulse/.config/pulse directory and chown to pulse.

However… I can’t adjust the volume remotely through my iPhone. It comes on full blast! Muting from the iPhone works, but the sound is all or nothing. Any ideas as to how I can make the volume adjustable?

delx says:

@hasso,

No, sorry. I don’t think that pulseaudio/bluez support the necessary volume control protocol.

Florian says:

Hi there,
at first, thank you for the tutorial.
It is the best i found so far for doing this.

But there is one issue I am unable to solve:
The pulsaudio.service refuses to start. Anything

systemctl status pulseaudio.service

says is:
systemd[1]: Starting pulseaudio service…
systemd[1]: pulseaudio.service start request repeated too quickly, refusing to start.
systemd[1]: Failed to start pulseaudio service.

So far I could not find any helpful hints on the web, as most of them say that pulseaudio cannot be run as system daemon.
I tried your version and the repository version as well.

Am I missing anything? Do you have any idea what to do?

Many thanks in advance,
Florian

delx says:

@Florian

So pulseaudio definitely can be run as a system daemon, but it’s not recommended. My tutorial does not run it as a system daemon but as a regular user.

I think you need to look at the logs a little more to understand your problem. Try opening two shells. In the first one ‘tail’ the systemd journal with sudo journalctl -f. In the second restart pulseaudio. You should see what is causing it to fail to start.

Saulius says:

Had problems with pulseaudio service not starting, solved them by replacing “useradd pulse” with “useradd -m pulse”.
Also used this nice trick about hot plugging USB sound card:
https://wiki.archlinux.org/index.php/Advanced_Linux_Sound_Architecture#Hot-plugging_a_USB_sound_card
Btw, works on beaglebone black, too! Thanks for the tutorial.

Alex Groleau says:

I also had to load the bluez5 modules in my pulseaudio configs. Here is the relevant bluetooth section:

### Automatically load driver modules for Bluetooth hardware
.ifexists module-bluetooth-policy.so
load-module module-bluetooth-policy
.endif

.ifexists module-bluetooth-discover.so
load-module module-bluetooth-discover
.endif

.ifexists module-bluez5-device.so
load-module module-bluez5-device
.endif

.ifexists module-bluez5-discover.so
load-module module-bluez5-discover
.endif

Robert says:

This is a great recipe. When I tried to set this up in Raspbian it ended up installing CUPS, SANE etc etc packages. Arch only seemed to install what I needed :) I managed to get this to work almost first time on Arch. Maybe your bluetoothctl commands should have “trust” listed at the bottom – it took me a while to realise that I had to do this.

I haven’t been able to get the audio output coming out of the HDMI port (the sound quality of the 3.5 jack is awful) – I’ve tried “amixer cset numid=3 2” and “hdmi_drive=2” in config.txt. Does something need to be set up in pulseaudio?

I’m using a RPi B+, if that makes any difference.

Macario says:

Thanks for your post!

After a bit of struggle and checking other sources I could get stream audio from my phone.

I had to install the package pulseaudio-bluetooth and I added “Enable=Source,Sink,Media,Socket” to /etc/bluetooth/main.conf

Nonetheless I still have a few problems:

– Even though I added the udev rule I still have to manually run `hciconfig hci0 up`. I’ve noticed the class doesn’t seem to make a difference.

– I have to initiate the connection to the phone from bluetoothctl, otherwise the connection is not maintained as @lsvx faced:
[CHG] Device Connected: yes
[CHG] Device Connected: no
[CHG] Device Connected: yes
[CHG] Device Connected: no

James Bunton says:

@Macario, thanks. I’ve updated the post to reflect the changes to Arch Linux’s pulseaudio packages.

What phone OS are you using? I’ve tested this with clients running Android 4/5, Symbian, Linux and OSX. They all are able to connect and maintain a connection without any bluetoothctl commands. Are you sure you did the initial pairing correctly?

Leave a Reply

Your email address will not be published. Required fields are marked *