Raspberry Pi Zero - Bluetooth Configuration

This guide provides step-by-step instructions for configuring a Raspberry Pi Zero W (or other Pi models) to automatically connect to a Bluetooth speaker at boot, with a watchdog service that will reconnect if the speaker is turned off and back on later.

Prerequisites

  • Raspberry Pi Zero W running Raspberry Pi OS Bookworm (or other Pi model)
  • Bluetooth speaker
  • SSH access to the Pi

Step 1: Install Required Packages

First, we need to install the necessary Bluetooth packages including the audio utilities:

sudo apt update
sudo apt upgrade -y
sudo apt install -y bluetooth bluez bluez-alsa-utils

The bluez-alsa-utils package is critical for the audio profiles to work correctly.

Step 2: Pair and Trust Your Speaker

Start the Bluetooth service and configure your speaker:

# Start Bluetooth service
sudo systemctl start bluetooth
sudo systemctl enable bluetooth

# Enter Bluetooth control
sudo bluetoothctl

In the bluetoothctl interface, run these commands:

agent on
default-agent
scan on
# Wait for your speaker to appear, then replace XX:XX:XX:XX:XX:XX with your speaker's MAC
pair XX:XX:XX:XX:XX:XX
trust XX:XX:XX:XX:XX:XX
connect XX:XX:XX:XX:XX:XX
exit

If you're having trouble connecting, try removing the device first:

remove XX:XX:XX:XX:XX:XX
scan on
# Then pair again

Step 3: Update Bluetooth Configuration

Edit the main Bluetooth configuration file:

sudo nano /etc/bluetooth/main.conf

Add or modify these lines (if they don't exist, add them at the end):

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

This setting ensures the audio profiles are properly enabled.

Step 4: Create Connection Manager Script

Create a script that will handle connecting to your Bluetooth speaker:

sudo nano /usr/local/bin/bluetooth-speaker-manager.sh

Paste this script (replace XX:XX:XX:XX:XX

with your speaker's MAC):

#!/bin/bash
SPEAKER_MAC="XX:XX:XX:XX:XX:XX"
LOG_FILE="/tmp/bluetooth-connect.log"
CONNECTED_FLAG="/tmp/bt_speaker_connected"
EVENT_SCRIPT="/usr/local/bin/speaker-connected.sh"
MAX_ATTEMPTS=3

# Function to check if already connected
check_connection() {
  if bluetoothctl info $SPEAKER_MAC | grep -q "Connected: yes"; then
    return 0 # Connected
  else
    return 1 # Not connected
  fi
}

# Log with timestamp
log_message() {
  echo "$(date) - $1" >> $LOG_FILE
}

log_message "Bluetooth speaker manager starting"

# Check if already running
if [ -f /tmp/bt_manager_running ]; then
  PID=$(cat /tmp/bt_manager_running)
  if ps -p $PID > /dev/null; then
    log_message "Already running with PID $PID, exiting"
    exit 0
  fi
fi

# Set running flag
echo $$ > /tmp/bt_manager_running

# Initial check
if check_connection; then
  log_message "Speaker already connected"
  # Only run the event script once per connection
  if [ ! -f "$CONNECTED_FLAG" ]; then
    log_message "Running connected event script"
    touch "$CONNECTED_FLAG"
    [ -x "$EVENT_SCRIPT" ] && "$EVENT_SCRIPT" &
  fi
  exit 0
else
  # Not connected, remove the flag
  rm -f "$CONNECTED_FLAG"
fi

# Make sure Bluetooth is running
if ! systemctl is-active bluetooth >> $LOG_FILE 2>&1; then
  log_message "Starting Bluetooth service"
  systemctl start bluetooth
  sleep 5
fi

# Ensure Bluetooth audio profiles are available
log_message "Ensuring audio profiles are loaded"
sudo hciconfig hci0 class 0x41C
sleep 2

# Reset Bluetooth adapter
log_message "Resetting Bluetooth adapter"
bluetoothctl power off >> $LOG_FILE 2>&1
sleep 3
bluetoothctl power on >> $LOG_FILE 2>&1
sleep 8

# Try to connect - multiple attempts
for attempt in $(seq 1 $MAX_ATTEMPTS); do
  log_message "Connection attempt $attempt of $MAX_ATTEMPTS"
  
  # Try to connect
  (
  echo "power on"
  sleep 2
  echo "agent on"
  sleep 1
  echo "default-agent"
  sleep 1
  echo "connect $SPEAKER_MAC"
  sleep 10
  echo "quit"
  ) | bluetoothctl >> $LOG_FILE 2>&1
  
  # Check if successful
  if check_connection; then
    log_message "Successfully connected to speaker"
    touch $CONNECTED_FLAG
    # Run the event script when connected
    [ -x "$EVENT_SCRIPT" ] && "$EVENT_SCRIPT" &
    exit 0
  fi
  
  log_message "Attempt $attempt failed, retrying..."
  sleep 3
done

log_message "Failed to connect to Bluetooth speaker after $MAX_ATTEMPTS attempts"
rm -f $CONNECTED_FLAG
exit 1

Make the script executable:

sudo chmod +x /usr/local/bin/bluetooth-speaker-manager.sh

Step 5: Create Event Script

Create a script that will run when the speaker connects:

sudo nano /usr/local/bin/speaker-connected.sh

Paste this content:

#!/bin/bash

# This script runs when the Bluetooth speaker connects
# You can customize it to run any audio application you want

LOG_FILE="/tmp/bluetooth-connect.log"

echo "$(date) - Speaker connected event running" >> "$LOG_FILE"

# Example: Play a notification sound to confirm connection
if [ -f /usr/share/sounds/alsa/Front_Center.wav ]; then
  aplay /usr/share/sounds/alsa/Front_Center.wav
elif [ -f /usr/share/sounds/freedesktop/stereo/complete.oga ]; then
  # Try another common sound file
  aplay /usr/share/sounds/freedesktop/stereo/complete.oga
fi

# Example: Start your audio application
# mpg123 /path/to/your/music.mp3 &

# Add any other commands you want to run when connected

exit 0

Make it executable:

sudo chmod +x /usr/local/bin/speaker-connected.sh

Step 6: Create Watchdog Service

Create a systemd service that will continuously check if the speaker is connected and attempt to reconnect if needed:

sudo nano /etc/systemd/system/bluetooth-speaker-watchdog.service

Paste this service definition:

[Unit]
Description=Bluetooth Speaker Connection Watchdog
After=bluetooth.target network.target
Wants=bluetooth.target
Requires=bluetooth.service

[Service]
Type=simple
ExecStartPre=/bin/sleep 30
ExecStart=/bin/bash -c 'while true; do if ! bluetoothctl info XX:XX:XX:XX:XX:XX | grep -q "Connected: yes"; then /usr/local/bin/bluetooth-speaker-manager.sh; fi; sleep 30; done'
Restart=on-failure
RestartSec=30
StandardOutput=journal

[Install]
WantedBy=multi-user.target

Be sure to replace XX:XX:XX:XX:XX

with your speaker's MAC address.

Step 7: Enable and Start the Service

sudo systemctl daemon-reload
sudo systemctl enable bluetooth-speaker-watchdog
sudo systemctl start bluetooth-speaker-watchdog

Step 8: Test and Troubleshoot

# Check service status
sudo systemctl status bluetooth-speaker-watchdog

# Check speaker connection
bluetoothctl info XX:XX:XX:XX:XX:XX

# View connection logs
tail -f /tmp/bluetooth-connect.log

Common Issues and Solutions

1. Profile unavailable errors

If you see "Failed to connect: org.bluez.Error.Failed br-connection-profile-unavailable", make sure you've:

  • Installed bluez-alsa-utils
  • Set the correct Bluetooth class in /etc/bluetooth/main.conf
  • Removed and re-paired the device

2. Timeout errors

If you see "Failed to connect: org.bluez.Error.Failed br-connection-page-timeout", try:

  • Increasing the sleep times in the script
  • Making sure the speaker is in pairing mode
  • Checking the speaker's battery level

3. Speaker disconnects randomly

  • The watchdog service should reconnect automatically within 30 seconds
  • Check for power saving settings on your speaker
  • Try a powered USB hub if your Pi can't provide enough power

4. Service not starting at boot

  • Check logs with journalctl -u bluetooth-speaker-watchdog
  • Verify service is enabled with systemctl is-enabled bluetooth-speaker-watchdog
  • Increase the initial sleep time (ExecStartPre=/bin/sleep 60)

5. Script runs but no sound

  • Check audio routing with aplay -l
  • Test audio with aplay -D bluealsa:HCI=hci0,DEV=XX:XX:XX:XX:XX:XX /usr/share/sounds/alsa/Front_Center.wav
  • Make sure your speaker volume is turned up

Customizing the Solution

You can customize the speaker-connected.sh script to do anything you want when the speaker connects, such as:

  • Starting a music player
  • Running a web radio application
  • Starting your own custom audio application

Conclusion

This setup provides a reliable way to automatically connect to a Bluetooth speaker at boot and reconnect if the connection is lost. The watchdog service ensures the speaker will always be connected when available, and the event script allows you to run custom actions when the connection is established.

Great! You’ve successfully signed up.

Welcome back! You've successfully signed in.

You've successfully subscribed to Enigmatic Ideas.

Success! Check your email for magic link to sign-in.

Success! Your billing info has been updated.

Your billing was not updated.