A simple Bluetooth Speaker Daemon for the Raspberry Pi 3
Branch: master
Clone or download
Latest commit 3f49801 Feb 12, 2018
Type Name Latest commit message Commit time
Failed to load latest commit information.
bt_manager Release fd on disconnect to fix 100% cpu load issue Jul 16, 2017
codecs [Add] BTSpeaker Jan 3, 2017
hooks.default wifi option (todo) Feb 12, 2018
.gitignore [Add] pychache Jan 8, 2017
LICENSE [Add] BTSpeaker Jan 3, 2017
README.md typo Feb 12, 2018
bt_speaker.py comment Feb 12, 2018
bt_speaker.service [Add] Run as btspeaker for security Apr 23, 2017
config.ini.default cleaner hooks/more options/etc Feb 12, 2018
install.sh cleaner hooks/more options/etc Feb 12, 2018
librtpsbc.so [Add] BTSpeaker Jan 3, 2017



A simple Bluetooth Speaker Daemon designed for the Raspberry Pi 3.


Quick Installation for Raspbian:

sudo -i
bash <(curl -s https://raw.githubusercontent.com/lukasjapan/bt-speaker/master/install.sh)

For details refer to the comments in the install script.

Depending on your application, you might also want to send all audio to the headphone jack. This can be done by raspi-config:

Advanced Options -> Audio -> Force 3.5mm ('headphone') jack

Note: Bt-speaker has been made with the default raspbian audio configuration in mind. If you are using external sound cards or have installed a sound daemon (like PulseAudio or Jack) you might need to adjust the config file accordingly.


The BT-Speaker daemon does not behave like a typical bluetooth device. Once a client disconnects, the speaker will immediately allow other clients to connect. This means that the quickest device may claim the speaker and no real bluetooth pairing occurs. The bright side of this logic is that no button for unpairing is needed.

The speakers name will default to the hostname of your Raspberry Pi. BT-Speaker does not manage this value. You are advised to change the hostname according to your needs.


The default settings of BT-Speaker will be copied and can be overridden in /etc/bt_speaker/config.ini.

Section Key Default Value Description
bt_speaker play_command aplay -f cd - The raw audio in CD Format (16bit little endian, 44100Hz, stereo) is piped to this command.
bt_speaker connect_command /etc/bt_speaker/hooks/connect Command that is called when an audio device connects to BT-Speaker
bt_speaker disconnect_command /etc/bt_speaker/hooks/disconnect Command that is called when an audio device disconnects from BT-Speaker
bluez device_path /org/bluez/hci0 The DBUS path where BT-Speaker can find the bluetooth device
bluez discoverable yes Specifies if the raspberry pi should advertise itself if no client is connected.
alsa enabled yes Enables volume control via alsamixer
alsa mixer PCM The volume of this mixer will be set from AVRCP messages (Remote volume control)
alsa id 0 The alsa id of the mixer control
alsa cardindex 0 The alsa cardindex of the soundcard

The settings in the alsa section specify on which alsa mixer (more info here) volume changes are applied. You need to adjust these settings if you are using an external sound card.

Details of Implementation

The BT-Speaker daemon has been written in Python and works with Bluez5. It talks to the Bluez daemon via the Bluez DBUS interface.

Bluetooth profiles

BT-Speaker will register itself as an A2DP capable device and route the received audio fully decoded to ALSAs aplay command.

Changes in volume are detected via messages from the AVRCP profile and are applied directly to the ALSA master volume.

Bluetooth device class

Some devices may filter out BT-Speaker and require the bluetooth device class to be expicitly set. Although BT-Speaker does not support to change the device class itself, you can change it manually after launching BT-Speaker.

pi@raspberrypi:~ $ sudo hciconfig hci0 class 0x240408

More about Bluetooth device classes can be found (here)

Partial Bluez5 port of BT-Manager

The great BT-Manager library does (currently) only work with Bluez4. Changes in the Bluez DBUS API from version 4 to 5 were huge and fully porting BT-Manager would have been a too heavy task. So instead, I extracted all relevant parts and ported them to Bluez5 as good as I could. Documentation and probably lots of other parts there have yet to be adjusted, so refer to that code with caution.

About the audio stream

The following describes some internals of the audio stream that is transferred via bluetooth.


The Light of Dawn blog describes the format very accurate:

As it turns out, the audio data is compressed with SBC codec. But I can't just use "sbcdec" tool from SBC package to decode it, as the audio data is encapsulated in A2DP packets, not naked SBC-compressed audio data. A2DP packets are RTP packets (referenced by A2DP specification, and detailed in this IETF draft) containing A2DP Media Payload. We need to extract the SBC audio data, pass it through SBC decompressor, and only then we get raw audio data that can be sent to ALSA.

Unfortunately there is no media player (or at least I didn't find any) that could handle this 'SBC in RTP' format natively. However, BT-Manager already provided a C library that takes care of the decoding process. The decoded output is raw audio data in CD format (16 bit little endian, 44100Hz, stereo) and can be piped to ALSA as mentioned in the blog.


The C library for the decoding process is located in the codecs folder. Its functions are called via Python CFFI. BT-Speaker provides the binary for ARM already, so there is no need to compile the codec manually.

However, if you need to do so for some reason, please be aware that the Makefile has been adjusted by the following:

  1. The default PLATFORM setting has been changed to armv6
  2. The -O3 flag has been added