Skip to content

Instantly share code, notes, and snippets.

@vschroeter
Last active January 27, 2025 10:59
Show Gist options
  • Save vschroeter/2fba1ca3dae52992d919dbf83411ebbb to your computer and use it in GitHub Desktop.
Save vschroeter/2fba1ca3dae52992d919dbf83411ebbb to your computer and use it in GitHub Desktop.
Setup bluetooth on Ubuntu 24.04 with Piperwire (on Raspberry Pi 5)

Setup bluetooth on Ubuntu 24.04 with Piperwire (on Raspberry Pi 5)

I've had a hard time bringing bluetooth to work on a Raspberry Pi 5 with Ubuntu 24.04 using Pipewire as audio backend. Maybe you run into the same situation and find this helpful.

With the common tutorials on how to enable a bluetooth speaker connection on Ubuntu, there were the following problems for my combination of Ubuntu 24.04 on a Raspberry Pi 5 and using PipeWire (as of July 2024):

  • either the audio quality was fine but the connection automatically disconnects after a few seconds
  • or the connection does not disconnect, but the audio begins to stutter extremely after 60 to 90 seconds of audio played

Since Ubuntu 22.10, PipeWire is the default soundserver on Ubuntu, so trying to use Bluetooth with PulseAudio as soundserver would mean to uninstall the recommended default and reconfigure the system. So PipeWire it is!

After trying a lot of different stuff, the solution contains:

  • setup PipeWire correctly
  • compiling / installing a current version of bluez
  • setting the correct bluetooth parameters

This gist is based on (Debian Wiki)[https://wiki.debian.org/PipeWire#Debian_12] and inspired by this gist for Bluetooth on Ubuntu 22.04

Installation

PipeWire

WirePlumber as session manager:

# Install WirePlumber as session manager while removing pipewire-media-sessions as the old session manager
sudo apt install wireplumber pipewire-media-session-
#  Enable WirePlumber in "systemd" (running as user, not as root)
systemctl --user --now enable wireplumber.service

Setting up Alsa and PipeWire as substitute for PulseAudio

# Configure PipeWire to activate its PulseAudio replacement daemon
# Configure PipeWire to activate its ALSA Plugin
# Also install pulseaudio and alsa utils
sudo apt install pipewire-pulse pipewire-alsa pulseaudio-utils alsa-utils

# Reboot and check, if pipewire-pulse is working correctly
sudo reboot now

# This should return something like:
# "Server Name: PulseAudio (on PipeWire 1.X.XX)" 
LANG=C pactl info | grep '^Server Name'

Bluetooth / BlueZ

# Minimum requirement for bluetooth
sudo apt install libspa-0.2-bluetooth
# If still installed, remove pulseaudio-module-bluetooth 
sudo apt remove pulseaudio-module-bluetooth

We need to install bluez either by source or taking the available apt version.

Trying the available version

sudo apt install bluez 

If this version is working, you're fine! At the time of writing, I had to install a newer version of bluez (upgrading from apt version 5.72 to 5.77)

(Optional) Installing bluez by source

Installation instructions taken from https://www.makeuseof.com/install-bluez-latest-version-on-ubuntu/

# If you have an old version installed, remove it first
sudo apt autoremove bluez
# Install necessary build packages
sudo apt install build-essential libreadline-dev libical-dev libdbus-1-dev libudev-dev libglib2.0-dev python3-docutils

# Set the current bluez version
# Check https://github.com/bluez/bluez/releases for the most recent version
BLUEZ_VERSION=5.77
wget http://www.kernel.org/pub/linux/bluetooth/bluez-$BLUEZ_VERSION.tar.xz

# Download, unzip, build and install the bluez version
tar -xf bluez-$BLUEZ_VERSION.tar.xz && cd bluez-$BLUEZ_VERSION 
./configure 
make 
sudo make install

For me, this new version of bluez also solved a extremely verbous console output when being inside the bluetoothctl terminal.

Configure bluetooth

If you have compiled a newer version of bluez, allowing experimental features might help.

# Modify /lib/systemd/system/bluetooth.service, e.g. with nano
sudo nano /lib/systemd/system/bluetooth.service

# Search for the ExecStart line and add a "--experimental" at the end, so that you have the following:
# ExecStart=/usr/local/libexec/bluetooth/bluetoothd --experimental

Set the correct controller mode:

# Modify /etc/bluetooth/main.cfg
sudo nano /etc/bluetooth/main.cfg

# Uncomment / set both lines:
# Name = BlueZ
# ControllerMode = dual

Restart bluetooth / your system

sudo systemctl restart bluetooth
# or sudo reboot now

Sources for the bluetooth config:

Connect to your device

These are the common bluetoothctl commands to find and connect to your device:

bluetoothctl power on
bluetoothctl agent on
bluetoothctl discoverable on
bluetoothctl pairable on
bluetoothctl scan on

bluetoothctl trust DEVICE:MAC
bluetoothctl pair DEVICE:MAC
bluetoothctl connect DEVICE:MAC

Collection of further methods to tackle problems

Check if bluetooth is blocked

sudo rfkill list
# If bluetooth is marked as blocked, run:
sudo rfkill unblock all

Check if bluetooth is even running

# Check the status of bluetooth, there should be something like "running" / "active"
systemctl status bluetooth

# If not, start bluetooth with one of the following
systemctl start bluetooth
bluetoothctl power on

# You can also check and unblock again
rfkill unblock all

Choppy audio on systems with high load

Based on https://wiki.debian.org/PipeWire

PipeWire's lower latency compared to PulseAudio can lead to choppy audio on systems with high load.

First view the quantum using the pw-top command and then increase the quantum value using this command, until the audio becomes smoother.

# Call this while playling audio.
# Experiment with different quantum values.
pw-metadata -n settings 0 clock.force-quantum 2048

Once you find the right quantum value for your situation, you can make the value permanent by creating a config file ~/.config/pipewire/pipewire.conf.d/choppy-under-load.conf with the following content and restart pipewire related daemons.

context.properties = {
   default.clock.quantum = 2048
   default.clock.min-quantum = 2048
}

Reduce Latency

Based on this gist for Ubuntu 22.

Find necessary info about the bluetooth device (while it is connected!)

pactl list | grep -Pzo '.*bluez_card(.*\n)*'

The output should be something like

    Name: bluez_card.28_11_A5_84_B6_F9
    Driver: module-bluez5-device.c
    ...
    Ports:
    speaker-output: Speaker (priority: 0, latency offset: 0 usec, available)
        Part of profile(s): a2dp_sink, headset_head_unit
    speaker-input: Bluetooth Input (priority: 0, latency offset: 0 usec, not available)
        Part of profile(s): headset_head_unit

We see that the buffers have currently 0 latency. In the next step, you will need the NAME and PORT of your output. In this example, these are bluez_card.28_11_A5_84_B6_F9 and speaker-output, respectively.

Set the buffer size (latency) of your card to a suitable value with this command pattern:

pactl set-port-latency-offset <NAME> <PORT> <BUFFER_SIZE_MICROSECONDS>

The latency unit of the following command is microseconds, so I'm using a 50 millisecond buffer for my command here:

pactl set-port-latency-offset bluez_card.28_11_A5_84_B6_F9 speaker-output 50000

Restart your bluetooth service to apply your change

sudo service bluetooth restart

As there is usually no documentation about this, you may have to experiment with higher or lower buffer values. Many people people posted their working latencies in the comments to this answer. Check them out for guidance on the latency value.

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