geenweb
Posts: 3
Joined: Sat Jan 20, 2018 3:12 pm

Bluetooth Pairing Headless Pi Zero W

Sat Jan 20, 2018 3:30 pm

I'm working on a project where I need to pair my Pi Zero W to my Linux laptop. The Pi is a headless embedded device. While I can ssh into it if needed, its primary method of communicating to the laptop in normal operation is via Bluetooth. I wanted a script to run on the Pi at power up that ensures it is paired. I realize that once it is paired, it should stay paired, but stuff always happens. ;) Getting this to work was a real pain, and a lot more trial and error than I would have expected. At this point I feel that this is a robust bash shell script, and I thought I'd share it here to help others who may be trying to do something similar. This pairs consistently when run from cron on my Pi Zero W running Raspbian GNU/Linux 9.1 (stretch), bluetoothctl Version 5.43, and empty version 0.6.20b. I'm also running a simple script on the laptop that executes the following bluetoothctl commands to ensure it is in a pairable state:
  • power on
  • agent on
  • default-agent
  • pairable on
  • discoverable on
If anyone has any additional suggestions feel free to add.

Code: Select all

#!/bin/bash
# Gene Weber - February 3rd 2018
#
# Uses bluetoothctl and empty-expect to check for and establish bluetooth device pairing.
# sudo apt install empty-expect
#
# Add the line: @reboot /home/pi/filename.sh to the cron system daemon using
# crontab -u pi -e
#
# This script can also be run on the Linux PC to be paired by changing only the Device_ID.
#
# "paired-devices" and "discoverable on" do not work using empty on Pi Zero W in cron,
# so this script works around these issues.

# Bluetooth ID of device to be paired.
DEVICE_ID=AA:11:A1:BB:2B:C3
PAIR_STRING="Device "$DEVICE_ID

rm empty.log
rm paired.log

# Delay until bluetooth service is running after power up.
RETURN_STATUS=1
until [ $RETURN_STATUS -eq 0 ]
do
  sleep 1
  sudo service bluetooth status | grep 'Status: "Running"' >empty.log
  RETURN_STATUS=$?
done

PASS=1
PAIRED=1
while [ $PAIRED -ne 0 ]
do

  echo -e "This is pairing attempt "$PASS".\n" >paired.log
 
  # Write list of paired devices to log file.
  bluetoothctl << EOF 2>&1 >>paired.log
  paired-devices
  exit
EOF
 
  # Check if device ID is in the list of paired devices.
  grep ^"$PAIR_STRING" paired.log >>empty.log
  PAIRED=$?
 
  # If device ID isn't paired, set controller to proper state and pair devices.
  if [ $PAIRED -ne 0 ]
  then
    # Fork a bluetoothctl process. Capture PID and log.
    # BTC.in and BTC.out are fifos for inter-process communication.
    empty -f -i BTC.in -o BTC.out -p empty.pid -L empty.log bluetoothctl
 
    # Allow some start up time for bluetoothctl
    sleep 2
 
    # Send power on command.
    empty -s -o BTC.in 'power on\n'
 
    # Watch for return of "Changing power on succeeded", then send agent on command.
    empty -w -i BTC.out -o BTC.in succeeded 'agent on\n'
 
    # Watch for return of "Agent registered", then send default-agent command.
    empty -w -i BTC.out -o BTC.in registered 'default-agent\n'
 
    # Watch for return of "Default agent request successful", then send pairable on command.
    empty -w -i BTC.out -o BTC.in successful 'pairable on\n'
 
    # Watch for return of "Changing pairable on succeeded", then trust Device.
    empty -w -i BTC.out -o BTC.in succeeded 'trust '$DEVICE_ID'\n'
 
    # Watch for return of "trust succeeded" or "not available", then attempt pairing.
    # If Device was not availble to trust, it won't be available to pair, but that's OK.
    empty -w -i BTC.out -o BTC.in succeeded 'pair '$DEVICE_ID'\n' available 'pair '$DEVICE_ID'\n'
 
    # Watch for "Pairing successful", "not available" or "org.bluez.Error.ConnectionAttemptFailed".
    # Start scanning of not available, else exit.
    empty -w -i BTC.out -o BTC.in successful 'exit\n'  available 'scan on\n' ConnectionAttemptFailed 'exit\n'
 
    # Allow time for exit and PID removal.
    sleep 2
 
    # If PID still exists then Device was not availble and scan was started.
    if [ -f "empty.pid" ]
    then
      # Allow up to a 3 minutes to find the desired Device ID, turn scan off if found.
      empty -t 180 -w -i BTC.out -o BTC.in $DEVICE_ID 'scan off\n'
 
      # Allow time after device is found before attempting next command.
      sleep 3

      # Watch for "Discovery stopped", then trust Device.
      empty -w -i BTC.out -o BTC.in stopped 'trust '$DEVICE_ID'\n'
 
      # Watch for "trust succeeded", then attempt pairing.
      empty -w -i BTC.out -o BTC.in succeeded 'pair '$DEVICE_ID'\n'
 
      # Watch for "Pairing successful", "not available" or "org.bluez.Error.ConnectionAttemptFailed".
      # Then send exit command.
      empty -w -i BTC.out -o BTC.in successful 'exit\n' Failed 'exit\n' ConnectionAttemptFailed 'exit\n'
    fi
 
  fi
 
  # Ensure that the empty process gets terminated if script fails for some reason.
  sleep 2
  if [ -f "empty.pid" ]
  then
    empty -k `cat ./empty.pid`
    echo -e "\nPairing script failed.\n" >> empty.log
  fi
 
  # Allow 5 tries to pair before giving up
  PASS=$((PASS + 1))
  if [ $PASS -eq 6 ]
  then
    PAIRED=0
  fi
done

# Make bluetooth discoverable in case the laptop needs to do the pairing.
bluetoothctl << EOF2 2>&1 >>paired.log
discoverable on
exit
EOF2
Last edited by geenweb on Sat Feb 03, 2018 4:51 pm, edited 1 time in total.

geenweb
Posts: 3
Joined: Sat Jan 20, 2018 3:12 pm

Re: Bluetooth Pairing Headless Pi Zero W

Sun Jan 21, 2018 7:32 pm

Note that in this process I discovered that a bluetoothctl commands return "Invalid command" when executed with empty when the script is launched from nohup, cron, or /etc/rc.local. They work fine if the script is launched from the command line. This happens with the commands: "devices", "paired-devices", and "discoverable on". It is 100% repeatable. These errors do not occur if the script is run nohup on my laptop. The script simply works around these issues.

geenweb
Posts: 3
Joined: Sat Jan 20, 2018 3:12 pm

Re: Bluetooth Pairing Headless Pi Zero W

Sat Feb 03, 2018 4:53 pm

The Bluetooth pairing script and my previous posts have been updated.

Return to “Networking and servers”