Skip to content

Instantly share code, notes, and snippets.

@ssipos90
Last active April 9, 2022 13:02
Show Gist options
  • Save ssipos90/6bd31be375b84b536b8954ea7c266b37 to your computer and use it in GitHub Desktop.
Save ssipos90/6bd31be375b84b536b8954ea7c266b37 to your computer and use it in GitHub Desktop.
Raspberry PI 3 bluealsa a2dp sink

This tutorial is for a2dp only. I've moved to pulseaudio bluetooth meanwhile to use other features automatically.

Compilation instructions for bluealsa a2dp are on the repo page.

Make sure user PI is in the right groups for udev, pulse access, etc (audio, bluetooth, pulse-access, plugdev)

Disable onboard sound, it sucks (comment out/remove the line dtparam=audio=on in /boot/config.txt) and use an external USB soundcard (I just got this one https://www.aliexpress.com/item/Ugreen-USB-2-0-to-3-5mm-Audio-External-Sound-Card-Microphone-Earphone-Speaker-Adapter-for/32507625943.html and it works fine. Previously I used LX-3000's soundcard I had around).

autobt.py goes in /home/pi/autobt.. Technically, this is the place you can integrate stuff like on line #4 where you can send commands, but you need threads or a way to interrupt and listen to commands (didn't research, don't know python). You also need to download https://raw.githubusercontent.com/pauloborges/bluez/master/test/bluezutils.py in the same dir. If other dependencies are missing, they should be available using apt install ... This is used to auto-trust any connected device (phone etc). My android 8 phone (OnePlus 5) doesn't allow playing without trusting the device first and disconnects after a while otherwise. It sets the pin to 0000 if your device requires a pin pairing.

The x.service files go in /etc/systemd/system

Edit /etc/bluetooth/audio.conf and make it look like (you can set your own class here):

[General]
Class=0x20041C
Enable=Source,Sink,Media,Socket

Edit /etc/bluetooth/main.conf and change:

  • Name to whatever you want (I don't think this is the right place, but it doesn't hurt)
  • DiscoverableTimeout=0 (unless you do something fancy in a script)
  • At the bottom, set AutoEnable=true

Edit /var/lib/bluetooth/[your-bt-addr-here-or-use-tab-tab]/settings and add (I think this goes in one of the confs above, not sure where though - audio.conf or main.conf)

[General]
Discoverable=true

Edit /etc/asound.conf and add this but change with your BT address

defaults.bluealsa.interface "hci0"
defaults.bluealsa.device "B8:27:EB:52:72:77"
defaults.bluealsa.profile "a2dp"
defaults.bluealsa.delay 10000

Run systemctl daemon-reload and for each services run systemctl enable x.service and systemctl start x.service. A reboot shouldn't hurt...

Also, for better performance overall, try to remove the burden on your pi (if you plan use on PiZeroW) by unused services like samba...

#!/usr/bin/python
#dbus-send --system --print-reply --type=method_call --dest='org.bluez' /org/bluez/hci0/dev_94_65_2D_7A_28_55/player0 org.bluez.MediaPlayer1.Play
from __future__ import absolute_import, print_function, unicode_literals
from optparse import OptionParser
import sys
import dbus
import dbus.service
import dbus.mainloop.glib
try:
from gi.repository import GObject
except ImportError:
import gobject as GObject
import bluezutils
BUS_NAME = 'org.bluez'
AGENT_INTERFACE = 'org.bluez.Agent1'
AGENT_PATH = "/test/agent"
bus = None
device_obj = None
dev_path = None
def ask(prompt):
try:
return raw_input(prompt)
except:
return input(prompt)
def set_trusted(path):
props = dbus.Interface(bus.get_object("org.bluez", path),
"org.freedesktop.DBus.Properties")
props.Set("org.bluez.Device1", "Trusted", True)
def dev_connect(path):
dev = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device1")
dev.Connect()
class Rejected(dbus.DBusException):
_dbus_error_name = "org.bluez.Error.Rejected"
class Agent(dbus.service.Object):
exit_on_release = True
def set_exit_on_release(self, exit_on_release):
self.exit_on_release = exit_on_release
@dbus.service.method(AGENT_INTERFACE,
in_signature="", out_signature="")
def Release(self):
print("Release")
if self.exit_on_release:
mainloop.quit()
@dbus.service.method(AGENT_INTERFACE,
in_signature="os", out_signature="")
def AuthorizeService(self, device, uuid):
return
@dbus.service.method(AGENT_INTERFACE,
in_signature="o", out_signature="s")
def RequestPinCode(self, device):
set_trusted(device)
return "0000"
@dbus.service.method(AGENT_INTERFACE,
in_signature="o", out_signature="u")
def RequestPasskey(self, device):
set_trusted(device)
return dbus.UInt32("0000")
@dbus.service.method(AGENT_INTERFACE,
in_signature="ouq", out_signature="")
def DisplayPasskey(self, device, passkey, entered):
print("DisplayPasskey (%s, %06u entered %u)" %
(device, passkey, entered))
@dbus.service.method(AGENT_INTERFACE,
in_signature="os", out_signature="")
def DisplayPinCode(self, device, pincode):
print("DisplayPinCode (%s, %s)" % (device, pincode))
@dbus.service.method(AGENT_INTERFACE,
in_signature="ou", out_signature="")
def RequestConfirmation(self, device, passkey):
set_trusted(device)
return
@dbus.service.method(AGENT_INTERFACE,
in_signature="o", out_signature="")
def RequestAuthorization(self, device):
return
@dbus.service.method(AGENT_INTERFACE,
in_signature="", out_signature="")
def Cancel(self):
print("Cancel")
def pair_reply():
print("Device paired")
set_trusted(dev_path)
dev_connect(dev_path)
mainloop.quit()
def pair_error(error):
err_name = error.get_dbus_name()
if err_name == "org.freedesktop.DBus.Error.NoReply" and device_obj:
print("Timed out. Cancelling pairing")
device_obj.CancelPairing()
else:
print("Creating device failed: %s" % (error))
mainloop.quit()
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
capability = "NoInputNoOutput"
parser = OptionParser()
parser.add_option("-i", "--adapter", action="store",
type="string",
dest="adapter_pattern",
default=None)
parser.add_option("-c", "--capability", action="store",
type="string", dest="capability")
parser.add_option("-t", "--timeout", action="store",
type="int", dest="timeout",
default=60000)
(options, args) = parser.parse_args()
if options.capability:
capability = options.capability
path = "/test/agent"
agent = Agent(bus, path)
mainloop = GObject.MainLoop()
obj = bus.get_object(BUS_NAME, "/org/bluez");
manager = dbus.Interface(obj, "org.bluez.AgentManager1")
manager.RegisterAgent(path, capability)
print("Agent registered")
# Fix-up old style invocation (BlueZ 4)
if len(args) > 0 and args[0].startswith("hci"):
options.adapter_pattern = args[0]
del args[:1]
if len(args) > 0:
device = bluezutils.find_device(args[0],
options.adapter_pattern)
dev_path = device.object_path
agent.set_exit_on_release(False)
device.Pair(reply_handler=pair_reply, error_handler=pair_error,
timeout=60000)
device_obj = device
else:
manager.RequestDefaultAgent(path)
mainloop.run()
#adapter.UnregisterAgent(path)
#print("Agent unregistered")
[Unit]
Description=Bluetooth Auto Agent
After=bluealsa-aplay.service
StartLimitIntervalSec=0
[Service]
User=pi
Type=simple
Restart=always
RestartSec=1
ExecStart=/usr/bin/python /home/pi/autobt/autobt.py
[Install]
WantedBy=multi-user.target
[Unit]
Description=BlueAlsa Aplay
After=bluealsa.service
StartLimitIntervalSec=0
[Service]
User=pi
Type=simple
Restart=always
RestartSec=1
ExecStart=/usr/bin/bluealsa-aplay -dhw:0,0 00:00:00:00:00:00 --profile-a2dp
[Install]
WantedBy=multi-user.target
[Unit]
Description=BlueAlsa
After=hciuart.service
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
ExecStart=/usr/bin/bluealsa --a2dp-volume -p a2dp-sink
[Install]
WantedBy=multi-user.target
@ssipos90
Copy link
Author

@besi do try to implement soon because I will wipe my bench RPi soon to start a new project - that is if you need any help

@besi
Copy link

besi commented Nov 3, 2018

@ssipos90: Thanks so much for this write down. I am tinkering with my UB-Boom right now.

@besi
Copy link

besi commented Nov 3, 2018

Quick question: The /var/lib/bluetooth/ADDRESS is this the BT adapter. It's a bit weird, because there I have two addresses and none of wich is my BT speaker.

@besi
Copy link

besi commented Nov 3, 2018

Okay I've been tinkering for hours now. What is weird is that I can connect to my BT headphones but not the UEBoom. There I can connect via bluetoothctl but some seconds later the connection is dropped again.

So once I playback I get this error:

ALSA lib dlmisc.c:254:(snd1_dlobj_cache_get) Cannot open shared library /usr/lib/arm-linux-gnueabihf/alsa-lib/libasound_module_pcm_bluetooth.so
aplay: main:788: audio open error: No such device or address

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment