Reading Multiple Bluetooth Beacons

The official PYBD running MicroPython, and its accessories.
Target audience: Users with a PYBD
Post Reply
User avatar
devnull
Posts: 473
Joined: Sat Jan 07, 2017 1:52 am
Location: Singapore / Cornwall
Contact:

Reading Multiple Bluetooth Beacons

Post by devnull » Mon Nov 04, 2019 2:30 am

Have really been struggling with multiple connections to multiple devices and being able to asynchronously read the data from each beacon and return that data to other code for further processing.

Dealing with a single connection and a single read is pretty simple, but when you have multiple devices to read (with multiple open connections) and need to read multiple values from each device asynchronously it becomes very messy when using a global interrupt.

I guess that everyone will say that uasyncio is probably the way, but having to deal with a single interrupt for all connections is still going to need some workarounds !

And I don't really need for my code to do anything else while waiting for this data, as the data is crucial to the next step !

What is the recommended approach for this ??

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Reading Multiple Bluetooth Beacons

Post by jimmo » Mon Nov 04, 2019 4:56 am

So your beacons need you to connect to them? They're not just using advertising data?

I think the answer to this is fairly application specific, but the basic idea is that you'd make a Beacon class to track each beacon and route the irq method to the right instance based on address / conn_handle.

Here's a rough sketch of what that might look like?

Code: Select all

class Peripheral:
  def __init__(self, central, addr_type, addr):
    self._central = central
    self._addr_type = addr_type
    self._addr = addr
    self._conn_handle = None
    self._central._connecting_peripherals[(addr_type, addr)] = self
    self._central._ble.connect(addr_type, addr)

  def connected(self):
   pass

  def irq(self, event, data):
    pass


class Central:
  def __init__(self, ble):
    self._connecting_peripherals = {}
    self._connected_peripherals = {}
    self._ble = ble
    self._ble.irq(handler=self.irq, trigger=0xffff)

  def irq(self, event, data):
    if event == _IRQ_SCAN_RESULT:
      addr_type, addr, connectable, rssi, adv_data = data
      # e.g. create a Peripheral(self, addr_type, addr)
    elif event == _IRQ_SCAN_COMPLETE:
      pass
    elif event == _IRQ_PERIPHERAL_CONNECT:
      conn_handle, addr_type, addr = data
      key = (addr_type, addr,)
      if key in self._connecting_peripherals:
        p = self._connecting_peripherals[key]
        p._conn_handle = conn_handle
        p.connected()
        del self._connecting_peripherals[key]
        self._connected_peripherals[conn_handle] = p
    else if data[0] in self._connected_peripherals:
        # All other events, data[0] is conn_handle.
        self._connected_peripherals[data[0]].irq(event, data)

User avatar
devnull
Posts: 473
Joined: Sat Jan 07, 2017 1:52 am
Location: Singapore / Cornwall
Contact:

Re: Reading Multiple Bluetooth Beacons

Post by devnull » Mon Nov 04, 2019 11:08 pm

Thanks Jimmo;

That works really well, I have expanded on it and fixed a couple of typos but it pretty much works out of the box.

But how can I disconnect a peripheral and destroy it's class instance, or allow disconnect & reconnect of an instance ?

Code: Select all

  def connect(self):
    self._central._connecting_peripherals[(self._addr_type, self._addr)] = self
    self._central._ble.gap_connect(self._addr_type, self._addr)      
  
  def disconnect(self):
    self._central._ble.gap_disconnect(self._conn_handle)
I have added these into the peripheral class, disconnect works but I can never reconnect again.
Last edited by devnull on Mon Nov 04, 2019 11:19 pm, edited 1 time in total.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Reading Multiple Bluetooth Beacons

Post by jimmo » Mon Nov 04, 2019 11:19 pm

After calling p.irq(), check if the event is "disconnected" and del from the connected dict.

Post Reply