adafruit / adafruit/Adafruit_CircuitPython_BLE

BLE connection hangs after 25 seconds

Open
#163 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
138
Forks
58
PR merge metrics
No merged PRs in 30d

Description

The LED Glasses driver nRF52840 using CircuitPython connects to a BLE light bulb and controls it well for 25 seconds, but after 25 seconds it seems to hang the next time a characteristic is accessed. There isn't any Exception, but the code stops running and Ctrl-C doesn't give a REPL prompt. The only way to recover is to reset the CPU.

If after 25 seconds, I call connect() again before accessing a characteristic, it usually works properly.

In case it helps to note, there's a hack in my pair() below to force a call to _discover_remote(). I imagine there's a more proper procedure.

The MAC address of the peripheral is hard-coded (but redacted) into the central's code below.

The code in the BLE peripheral is closed source, so I can't change it.

Are there any further experiments that could help narrow down the cause of this? I'd like to be able to connect to multiple peripherals (with the same UUID), but the pairing fails if I keep re-executing connect() on different peripherals.

```
import time
from time import sleep

import board

from _bleio import Address
from adafruit_ble import BLERadio, BLEConnection
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement,SolicitServicesAdvertisement, Advertisement
from adafruit_ble.uuid import VendorUUID, UUID
from adafruit_ble.services import Service
from adafruit_ble.characteristics import Characteristic
from adafruit_ble.characteristics.int import Uint8Characteristic, Uint16Characteristic
from adafruit_ble.attributes import Attribute
from adafruit_ble.uuid import VendorUUID, UUID

class HueService(Service):
"""Service that allows for setting the power, brightness and color"""

uuid = VendorUUID("932c32bd-0000-47a2-835a-a8d455b859dd")
#rwproperties=Characteristic.READ|Characteristic.WRITE|Characteristic.NOTIFY|Characteristic.WRITE_NO_RESPONSE,
brightness = Uint8Characteristic(
uuid=VendorUUID("932c32bd-0003-47a2-835a-a8d455b859dd"),
properties=Characteristic.READ|Characteristic.WRITE,
read_perm = Attribute.OPEN,
write_perm=Attribute.LESC_ENCRYPT_WITH_MITM)

class HueLight():
HUEserviceUUID = VendorUUID("932c32bd-0000-47a2-835a-a8d455b859dd")
#Basic note: variables defined here are shared by all instances
#scanning happens on the one BLERadio(), but connections are per instance
ble = BLERadio()
address_list = []

def __init__(self,address):
self.light_connection = None #BLEConnection()
#convert an int to a byte array
if isinstance(address,int): #address is integer
self.address_bytes = address.to_bytes(6,"little")
else:
self.address_bytes = bytes(address)
#print("init address",self.address_bytes)
#keep track of all lights
self.address_list.append(self.address_bytes)
self.index=len(self.address_list)-1
#self.connect()

def connect(self,pair=True):
#peer_address=Address(int(self.address_bytes).to_bytes(6,"little"))
if self.light_connection is not None:
print("Warning: light_connection is",self.light_connection)
self.light_connection = self.ble.connect(Address(self.address_bytes,1))
#TODO try without pairing after initial pair
if pair:
print("Checking pairing")
if not self.light_connection.paired:
self.pair()
else:
print("Already paired")
print("Connected?",self.light_connection.connected)
print("bonded?",self.light_connection.paired)

def connect_to_advertisement(self):
print("Index:",self.index)
if not self.light_connection or not self.light_connection.paired:
print("Scanning for {}...".format(self.address_bytes))
advertisements=SolicitServicesAdvertisement()
advertisements.solicited_services.append(HueService)
for adv in self.ble.start_scan(timeout=60):
if adv.address.address_bytes == self.address_bytes:
self.light_connection = self.ble.connect(adv)
if not self.light_connection.paired:
self.pair()
else:
print("Already paired")
self.ble.stop_scan()
break
self.ble.stop_scan()

def pair(self):
#this uuid is arbitrary
uuid = VendorUUID("932c32bd-0002-47a2-835a-a8d455b859dd")
#just need to run __contains__ which runs _discover_remote()
if uuid in self.light_connection:
pass
#self.light_connection._discover_remote(uuid)
print("Bonding...")
try:
self.light_connection.pair()
except Exception as err:
print("Error in pairing: {}".format(err))
if self.light_connection.paired:
print("Paired")

def connected(self):
return self.light_connection.connected

def disconnect(self):
self.light_connection.disconnect()

def get_brightness(self):
return self.light_connection[HueService].brightness

def set_brightness(self,brightness):
self.light_connection[HueService].brightness=brightness

ambient2=HueLight(0xfedcba098765) #set to real MAC address of peripheral

ambient2.connect_to_advertisement()

read_brightness=ambient2.get_brightness()
new_brightness=((read_brightness+100) % 230) +20
ambient2.set_brightness(new_brightness)
print("Set to",new_brightness)
sleep(29)
print("Connected?",ambient2.connected())

#Try uncommending the line below to avoid system hanging <-----------------------------------
#ambient2.connect()

print("If connect() is not called after the sleep, the system will hang here.")
read_brightness=ambient2.get_brightness()
print("Got brightness")
new_brightness=((read_brightness+100) % 230) +20
ambient2.set_brightness(new_brightness)
print("Set to",new_brightness)
sleep(1)
read_brightness=ambient2.get_brightness()
print("Current brightness",read_brightness)
#ambient2.connect()
ambient2.disconnect()
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by running the reproduction script on the nRF52840 with the BLE light bulb, focusing on BLERadio.connect(), connect_to_advertisement(), pair(), and characteristic access after the 29-second sleep. Compare behavior with and without the extra connect() call and record whether the connection hangs. Done means the cause is narrowed down and the connection flow supports repeated access and multiple peripherals without requiring a CPU reset.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
embedded-iot, networking
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.