简体   繁体   中英

How to register to be notify of changes in a BLE peripheral?

I have a BLE peripheral device (Arduino nano) with a single service and a single characteristic. The characteristic holds an 8-bit uint which is set to 1 when the switch is on, 0 when the switch is off. The characteristic supports READ and NOTIFY.

Using nRF Connect I can see the NOTIFY part is working as the value is being updated when the state of the switch changes.

But what I really want to do is use a Raspberry Pi as the central device using Adafruit CircuitPython BLE.

Following the examples in the Adafruit CircuitPython BLE repository, I created a simple program below:

observe_ble_switch.py

#!/usr/bin/env python3

import asyncio
import time
from switch_service import SwitchService

from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import Advertisement

ble = BLERadio()

async def main():
    device_name = "My Arduino"
    device_found = False
    ble_device_connection = None

    print("Scanning for %r" % device_name)

    while not device_found:
        print("...")

        for adv in ble.start_scan(Advertisement, timeout=5):
            name = adv.complete_name
            if not name:
                continue

            if name.strip("\x00") == device_name:
                ble.stop_scan()
                device_found = True
                print("%r found!" % name)
                ble_device_connection = ble.connect(adv)
                break

    if ble_device_connection and ble_device_connection.connected:
        print("Connected to %r!" % name)

        if SwitchService in ble_device_connection:
            print("switch service available")
            switch_service = ble_device_connection[SwitchService]

            while ble_device_connection.connected:
                print("status %r" % switch_service.read_status())
                time.sleep(0.3)
        else:
            print("switch service not available")

if __name__ == "__main__":
    asyncio.run(main())

switch_service.py

from adafruit_ble.uuid import VendorUUID
from adafruit_ble import Service
from adafruit_ble.characteristics.int import Uint8Characteristic

class SwitchService(Service):
    """
    """

    uuid = VendorUUID('8158b2fd-94e4-4ff5-a99d-9a7980e998d7')

    switch_characteristic = Uint8Characteristic(
        uuid=VendorUUID("8158b2fe-94e4-4ff5-a99d-9a7980e998d7")
    )

    def __init__(self, service=None):
        super().__init__(service=service)
        self.status = self.switch_characteristic

    def read_status(self):
        return self.status

The problem I am having is that read_status() will always return whatever the state of the BLE switch is when the program first ran. It doesn't get notified of subsequent states changes of the BLE switch. My thought was that what I am missing is registering with the BLE switch to be notified of changes. I am struggling to find examples or reference to do this.

Thanks.

Thanks to ukBaz for pointing out Bleak. The following code uses Bleak which was awesome to work with.

import asyncio

from bleak import BleakScanner
from bleak.backends.bluezdbus.client import BleakClientBlueZDBus

device_name = "My Arduino"
switch_status_char_uuid = "8158b2fe-94e4-4ff5-a99d-9a7980e998d7"


def notification_handler(sender, data):
    print("Switch is active: {}".format(bool(data[0])))


async def run():
    client = None
    external_heartbeat_received = False
    device = await BleakScanner.find_device_by_filter(
        lambda d, ad: d.name and d.name.lower() == device_name.lower()
    )

    if device is None:
        print("{} not found".format(device_name))
    else:
        print("{} found".format(device))

    client = BleakClientBlueZDBus(device)

    try:
        timer = 60  # seconds

        while timer != 0 or external_heartbeat_received:
            if not client.is_connected:
                if await client.connect():
                    print("Connected to {}".format(device_name))
                    await client.start_notify(switch_status_char_uuid, notification_handler)
            await asyncio.sleep(1)
            timer -= 1

            # If timer expired and we received a heartbeat, restart timer and carry on.
            if timer == 0:
                if external_heartbeat_received:
                    timer = 60
                    external_heartbeat_received = False
    except:
        print("Connected to {} failed".format(device_name))

    if client is not None and client.is_connected:
        await client.disconnect()
        print("Disconnected from {}".format(device_name))

loop = asyncio.get_event_loop()
loop.run_until_complete(run())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM