I (like others) have multiple disconnects between a RPi4 central(client) and ESP32 BLE peripheral(server). Using the "nRF Connect" app on a android phone, the connection to the ESP32 is robust. However, RPi4 - ESP32 BLE communication is VERY unstable. This finding implies the fault is with the RPi and/or code. The initial BLE connection occurs faithfully but the connection inevitably drops after a random number of successful reads (usually 1-50 reads). I am using BluePy 1.3.0 with a new Raspbian image on a RPI4. I attached skeleton code and the error message produced after a random number of successful Reads.
import time
from bluepy.btle import Peripheral
peripheral_address = "8c:aa:b5:85:20:1e"
service_uuid = "537e7010-9928-4595-89dc-46b495862dc6"
characteristic_uuid = "3778ceab-0974-4eb0-9da5-26c3a69cc742" # Read from peripheral
p = Peripheral(peripheral_address, "public") #random does not work!!
Service=p.getServiceByUUID(service_uuid)
Characterization=Service.getCharacteristics(characteristic_uuid)[0]
print("Got characterization")
time.sleep(1)
while True:
value = Characterization.read()
print(value)
time.sleep(0.1)
Traceback (most recent call last):
File "/home/pi/Desktop/BLETest/bleRead.py", line 16, in <module>
value = Characterization.read()
File "/usr/local/lib/python3.7/dist-packages/bluepy/btle.py", line 197, in read
return self.peripheral.readCharacteristic(self.valHandle)
File "/usr/local/lib/python3.7/dist-packages/bluepy/btle.py", line 530, in readCharacteristic
resp = self._getResp('rd')
File "/usr/local/lib/python3.7/dist-packages/bluepy/btle.py", line 407, in _getResp
resp = self._waitResp(wantType + ['ntfy', 'ind'], timeout)
File "/usr/local/lib/python3.7/dist-packages/bluepy/btle.py", line 362, in _waitResp
raise BTLEDisconnectError("Device disconnected", resp)
bluepy.btle.BTLEDisconnectError: Device disconnected
Note: Adding code to catch the disconnect exception has been unsuccessful thus far resulting in additional raised errors and loss of data secondary to the time it takes to reconnect.
I am very interested to hear from anyone who has robust BLE communication with a RPi client? Any and all help is appreciated. Thank you.
There are a large number of things it could be but there a couple areas that I would investigate.
Firstly there is a bit churn going on with Bluetooth firmware on the RPi at the moment as you can see from this thread: https://github.com/RPi-Distro/firmware-nonfree/issues/8
So I would check that you are up to date with with those.
BluePy I believe has a bluepy-helper
module which is based on Bluez version 5.47 which is behind what the RPi is using now. Might be worth trying a different library to see if the issue persists.
Here is an example of reading your characteristic with the BlueZ D-Bus API directly using pydbus for the python D-Bus bindings:
from time import sleep
import pydbus
from gi.repository import GLib
peripheral_address = "8C:AA:B5:85:20:1E"
service_uuid = "537e7010-9928-4595-89dc-46b495862dc6"
characteristic_uuid = "3778ceab-0974-4eb0-9da5-26c3a69cc742" # Read from peripheral
# DBus object paths
BLUEZ_SERVICE = 'org.bluez'
ADAPTER_PATH = '/org/bluez/hci0'
device_path = f"{ADAPTER_PATH}/dev_{peripheral_address.replace(':', '_')}"
# setup dbus
bus = pydbus.SystemBus()
mngr = bus.get(BLUEZ_SERVICE, '/')
adapter = bus.get(BLUEZ_SERVICE, ADAPTER_PATH)
device = bus.get(BLUEZ_SERVICE, device_path)
device.Connect()
while not device.ServicesResolved:
sleep(0.5)
def get_characteristic_path(dev_path, uuid):
"""Look up DBus path for characteristic UUID"""
mng_objs = mngr.GetManagedObjects()
for path in mng_objs:
chr_uuid = mng_objs[path].get('org.bluez.GattCharacteristic1', {}).get('UUID')
if path.startswith(dev_path) and chr_uuid == uuid.casefold():
return path
# Characteristic DBus information
char_path = get_characteristic_path(device._path, characteristic_uuid)
characterization = bus.get(BLUEZ_SERVICE, char_path)
# Read characteristic without event loop notifications
while True:
print(characterization.ReadValue({}))
time.sleep(0.1)
Does the characteristic you are reading from support notifications? If it does then that is a more efficient way to use the Bluetooth link. The above while
loop can be replaced with:
# Enable eventloop for notifications
def notify_handler(iface, prop_changed, prop_removed):
"""Notify event handler for characteristic"""
if 'Value' in prop_changed:
new_value = prop_changed['Value']
print(f"Received: {new_value}")
mainloop = GLib.MainLoop()
characterization.onPropertiesChanged = notify_handler
characterization.StartNotify()
try:
mainloop.run()
except KeyboardInterrupt:
mainloop.quit()
characterization.StopNotify()
device.Disconnect()