Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLE connection hangs after 25 seconds #163

Open
cpblegh opened this issue May 10, 2022 · 0 comments
Open

BLE connection hangs after 25 seconds #163

cpblegh opened this issue May 10, 2022 · 0 comments

Comments

@cpblegh
Copy link

cpblegh commented May 10, 2022

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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant