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

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.

I’ve submitted this patch to the PulseAudio maintainers, it was merged and will be in the next release.

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-firmware alsa-lib alsa-plugins
# gpasswd -a <your-username> audio
$ alsamixer # set the volume to max
$ speaker-test # You should hear sound!

Next PulseAudio.

# pacman -S pulseaudio pulseaudio-alsa # Or install from the custom build I used above
# useradd pulse
# gpasswd -a pulse lp # this is needed later
# 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 download pulseaudio.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]
# 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

17 Comments »

  1. 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.

    Comment by cw — February 15, 2014 @ 1:50 pm

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

    Comment by delx — February 15, 2014 @ 10:02 pm

  3. 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

    Comment by Soren — February 26, 2014 @ 8:15 am

  4. @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.

    Comment by delx — March 7, 2014 @ 3:44 pm

  5. 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.

    Comment by bruint — March 14, 2014 @ 11:19 am

  6. @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?

    Comment by delx — March 15, 2014 @ 12:11 am

  7. 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

    Comment by lsvx — March 24, 2014 @ 3:53 pm

  8. @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.

    Comment by delx — March 24, 2014 @ 4:16 pm

  9. @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 0×0419 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!

    Comment by lsvx — March 24, 2014 @ 11:53 pm

  10. @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!!

    Comment by lsvx — March 25, 2014 @ 12:27 am

  11. @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!

    Comment by lsvx — March 25, 2014 @ 1:05 am

  12. 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?

    Comment by icepower — May 18, 2014 @ 7:32 pm

  13. 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.

    Comment by Catani — May 20, 2014 @ 7:31 am

  14. 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?

    Comment by hasso — June 25, 2014 @ 5:47 am

  15. @hasso,

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

    Comment by delx — June 29, 2014 @ 2:01 pm

  16. 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

    Comment by Florian — July 5, 2014 @ 12:00 am

  17. @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.

    Comment by delx — July 5, 2014 @ 10:37 pm

RSS feed for comments on this post.

Leave a comment