ubluepy --- Bluetooth LE 外设与中心设备

ubluepy 模块是随 MicroPython nRF 移植版附带的旧版 Bluetooth LE API。它大致模仿了 Linux 上的 bluepy Python 库,并直接构建于 Nordic SoftDevice 之上 --- 没有可移植的后端,因此该模块仅在 Nordic 目标平台上可用(在 OpenMV 的产品线中即 Arduino Nano 33 BLE Sense)。较新的 bluetooth / aioble API 在本构建中启用,因此 ubluepy 是驱动片上无线电的唯一方式。

可用的功能集取决于固件所烧录的 SoftDevice:

  • s140(Nano 33 BLE Sense)--- 同时支持外设中心(扫描器)角色。这正是 OpenMV 固件所附带的版本。

  • s132 --- 同时支持外设和中心。

  • s110 --- 仅支持外设;扫描器 / 连接相关方法被编译排除。

外设示例

作为 Bluetooth LE 外设进行广播,提供单个环境感知服务,并在每次写入其客户端特征配置描述符(CCCD)时通知一个温度特征:

from ubluepy import Service, Characteristic, UUID, Peripheral, constants
from machine import LED

notif_enabled = False

def event_handler(event_id, handle, data):
    global notif_enabled
    if event_id == constants.EVT_GAP_CONNECTED:
        LED("LED_GREEN").on()
    elif event_id == constants.EVT_GAP_DISCONNECTED:
        LED("LED_GREEN").off()
        periph.advertise(device_name="Nano 33", services=[svc])
    elif event_id == constants.EVT_GATTS_WRITE:
        notif_enabled = bool(data[0])

svc = Service(UUID("181A"))            # Environmental Sensing
char = Characteristic(UUID("2A6E"),
                      props=Characteristic.PROP_NOTIFY | Characteristic.PROP_READ,
                      attrs=Characteristic.ATTR_CCCD)
svc.addCharacteristic(char)

periph = Peripheral()
periph.addService(svc)
periph.setConnectionHandler(event_handler)
periph.advertise(device_name="Nano 33", services=[svc])

中心设备示例

扫描附近的广播设备 100 ms,并解码每个 ScanEntry 的广播数据:

from ubluepy import Scanner, constants

s = Scanner()
for entry in s.scan(100):
    print(entry.addr(), entry.rssi(), "dBm")
    for ad_type, name, value in entry.getScanData():
        print(" ", ad_type, name, bytes(value))

模块内容

class ubluepy.UUID(value)

构造一个 16 位或 128 位的 Bluetooth UUID。

value

可为以下之一:

  • int --- 一个 16 位数值 UUID(UUID(0x180A))。

  • 6 字符的 "0xXXXX" 字符串 --- 一个 16 位 UUID,例如 UUID("0x181A")

  • 36 字符的 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 字符串 --- 一个完整的 128 位 UUID。其中厂商专用部分会在构造时注册到 SoftDevice。

  • 另一个 UUID 实例 --- 执行复制。

其他任何长度都会引发 ValueError("Invalid UUID string length")

binVal() int

int 形式返回 UUID 的低 16 位。对于 128 位 UUID,仅返回嵌入在厂商专用 UUID 内的 16 位字段(完整的 128 位值只能通过 SoftDevice 的厂商专用索引访问)。

class ubluepy.Service(uuid: UUID, type: int = Service.PRIMARY)

定义一个 GATT 服务,当其被添加到 Peripheral 时将注册到 SoftDevice。

uuid

一个 UUID 实例。传入非 UUID 对象会引发 ValueError

type

Service.PRIMARY(默认)或 Service.SECONDARY 之一。其他值会引发 ValueError

uuid() UUID

返回该服务的 UUID 实例。

addCharacteristic(characteristic: Characteristic) None

向服务注册一个 Characteristic。该特征的 GATT 句柄在此调用期间被分配。

getCharacteristic(uuid: UUID) Characteristic | None

按 UUID 查找先前已添加的 Characteristic。返回该特征实例,若未找到匹配项则返回 None

getCharacteristics() list

返回已添加到该服务的所有特征的列表。

PRIMARY: int

主服务的服务类型常量(1)。

SECONDARY: int

辅助服务的服务类型常量(2)。

class ubluepy.Characteristic(uuid: UUID, *, props: int = PROP_READ | PROP_WRITE, attrs: int = 0)

定义一个 GATT 特征。在父级 Peripheral 开始广播之前,使用 Service.addCharacteristic() 将其添加到 Service 中。

uuid

一个 UUID 实例。

props(仅限关键字)

由一个或多个 Characteristic.PROP_* 值组成的位掩码,用于描述该特征支持哪些操作。

attrs(仅限关键字)

附加 GATT 属性的位掩码。使用 Characteristic.ATTR_CCCD 来附加一个客户端特征配置描述符 --- 这是使 PROP_NOTIFY / PROP_INDICATE 特征正常工作所必需的。

uuid() UUID

返回该特征的 UUID 实例。

properties() int

返回构造时设置的 props 位掩码。

read() bytearray

仅限中心角色。 从已连接的对端读取该特征的值。返回包含最新值的 bytearray。在外设上这是一个空操作,返回 None

write(data, *, with_response: bool = False) None

写入该特征。

  • 外设上,如果该特征的属性中设置了 PROP_NOTIFY,则该值会作为 GATT 通知发送给已连接的中心设备;否则更新本地属性值。

  • 中心设备上,该值会被写入远程对端。设置 with_response=True 可发出写请求并等待对端确认,而非发送写命令。

data 是任何符合缓冲区协议的对象(bytesbytearraymemoryview)。

PROP_BROADCAST: int

特征可以广播其值(0x01)。

PROP_READ: int

特征支持读取(0x02)。

PROP_WRITE_WO_RESP: int

特征支持无响应写入(0x04)。

PROP_WRITE: int

特征支持有响应写入(0x08)。

PROP_NOTIFY: int

特征可以向已订阅的中心设备推送通知(0x10)。

PROP_INDICATE: int

特征可以向已订阅的中心设备推送指示(需确认的通知)(0x20)。

PROP_AUTH_SIGNED_WR: int

特征支持经认证的签名写入(0x40)。

ATTR_CCCD: int

向该特征添加一个客户端特征配置描述符(0x01)。这是客户端能够订阅通知/指示所必需的。

class ubluepy.Descriptor(uuid: UUID)

用于表示 GATT 描述符的桩类。当前实现仅存储 UUID 且不暴露任何方法 --- 提供它是为了与本模块未来版本保持前向兼容。

class ubluepy.Peripheral

本地 Bluetooth LE 设备。同一个类用于外设和中心两种角色;角色由你调用的方法选定(advertise() 选择外设,connect() 选择中心)。

addService(service: Service) None

将一个 Service(及其先前添加的所有特征)注册到本地 GATT 服务器。

getServices() list

返回当前注册到此 Peripheral 的服务列表。

advertise(*, device_name: str | None = None, services: list | None = None, data: bytes | None = None, connectable: bool = True) None

以外设角色开始广播。

device_name

在 GAP 负载中广播的完整本地名称。

services

要广播的 Service 实例列表。每个服务的 UUID 都会被包含在广播中。

data

可选的原始广播负载(bytes / bytearray),追加到自动生成的头部之后。可用于厂商专用或信标负载,例如 Eddystone。

connectable

当为 True(默认)时,作为可连接设备进行广播,并注册 GAP / GATTS 事件处理程序,使得已配置的 setConnectionHandler() 回调在连接、断开连接以及 CCCD 写入时触发。当为 False 时,作为信标进行广播 --- 不附加任何处理程序,且该设备无法被连接。

advertise_stop() None

停止任何正在进行的广播。

setConnectionHandler(func) None

注册一个在 GAP 和 GATTS 事件上被调用的回调。该回调以 func(event_id, conn_handle, data) 形式调用,其中 event_idconstants.EVT_GAP_CONNECTEDconstants.EVT_GAP_DISCONNECTEDconstants.EVT_GATTS_WRITE 值之一,conn_handle 是 SoftDevice 连接句柄(对于 GATTS 写入则为属性句柄),data 是以 bytearray 形式表示的原始事件负载(连接 / 断开连接时为 None)。

setNotificationHandler(func) None

注册一个用于在中心角色下接收通知事件的回调。

withDelegate(delegate: DefaultDelegate) None

附加一个 DefaultDelegate 实例以接收已解码的 GATT 事件。

disconnect() None

断开活动连接(在本构建中目前是一个空操作桩)。

connect(addr, *, addr_type: int = constants.ADDR_TYPE_PUBLIC) None

仅限中心角色。 连接到具有给定地址的对端,并同步发现其主服务和特征。在连接建立且发现完成之前会阻塞;发现的服务随后可通过 getServices() 获取。

addr

对端地址,为 17 字符的 "xx:xx:xx:xx:xx:xx" 字符串(例如取自 ScanEntry.addr())。

addr_type(仅限关键字)

constants.ADDR_TYPE_PUBLIC(默认)或 constants.ADDR_TYPE_RANDOM_STATIC 之一。

class ubluepy.Scanner

用于发现附近广播设备的 GAP 观察者。仅当固件针对支持中心功能的 SoftDevice(s132 / s140)构建时可用。

scan(timeout: int) list

运行一次被动扫描 timeout 毫秒,并返回 ScanEntry 实例列表 --- 该窗口内每收到一个广播报告对应一个实例。

class ubluepy.ScanEntry

Scanner.scan() 捕获的单个广播报告。实例由扫描器返回 --- 没有公共构造函数。

addr() str

以 17 字符的 "xx:xx:xx:xx:xx:xx" 字符串形式返回对端地址。

addr_type() int

返回对端地址类型(constants.ADDR_TYPE_PUBLICconstants.ADDR_TYPE_RANDOM_STATIC)。

rssi() int

以 dBm 为单位返回信号强度指示。

getScanData() list

将广播负载解码为 (ad_type, description, value) 元组的列表。ad_type 是数值型 AD 类型字节(参见 constants.ad_types),description 是匹配常量名称的字符串(若类型未知则为 None),value 是以 bytearray 形式表示的 AD 记录主体。

class ubluepy.DefaultDelegate

传递给 Peripheral.withDelegate() 的对象的基类。对其进行子类化并重写 handleConnection() / handleNotification() 以响应 GATT 事件。

handleConnection() None

在 GAP 连接 / 断开连接事件上被调用。默认实现为空。

handleNotification() None

在收到 GATT 通知时被调用。默认实现为空。

常量

模块的 constants 属性是一个命名空间,包含 GAP/GATT 事件标识符、地址类型值以及嵌套的 ad_types 命名空间。

ubluepy.constants: type

暴露下列常量的容器。

constants.EVT_GAP_CONNECTED: int

用于 GAP 连接的 Peripheral 连接处理程序 event_id 值(16)。

constants.EVT_GAP_DISCONNECTED: int

用于 GAP 断开连接的 Peripheral 连接处理程序 event_id 值(17)。

constants.EVT_GATTS_WRITE: int

用于写入本地 GATT 属性(包括启用/禁用通知的 CCCD 写入)的 Peripheral 连接处理程序 event_id 值(80)。

constants.UUID_CCCD: int

客户端特征配置描述符的标准 Bluetooth UUID(0x2902)。

constants.ADDR_TYPE_PUBLIC: int

公共 Bluetooth 设备地址(0)。

constants.ADDR_TYPE_RANDOM_STATIC: int

静态随机 Bluetooth 设备地址(1)。

constants.ad_types: type

来自 Bluetooth Core Specification Supplement 的广播数据 AD 类型常量命名空间。每个名称映射到相应的 1 字节 AD 类型:

名称

AD_TYPE_FLAGS

0x01

AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE

0x02

AD_TYPE_16BIT_SERVICE_UUID_COMPLETE

0x03

AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE

0x04

AD_TYPE_32BIT_SERVICE_UUID_COMPLETE

0x05

AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE

0x06

AD_TYPE_128BIT_SERVICE_UUID_COMPLETE

0x07

AD_TYPE_SHORT_LOCAL_NAME

0x08

AD_TYPE_COMPLETE_LOCAL_NAME

0x09

AD_TYPE_TX_POWER_LEVEL

0x0A

AD_TYPE_CLASS_OF_DEVICE

0x0D

AD_TYPE_SIMPLE_PAIRING_HASH_C

0x0E

AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R

0x0F

AD_TYPE_SECURITY_MANAGER_TK_VALUE

0x10

AD_TYPE_SECURITY_MANAGER_OOB_FLAGS

0x11

AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE

0x12

AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT

0x14

AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT

0x15

AD_TYPE_SERVICE_DATA

0x16

AD_TYPE_PUBLIC_TARGET_ADDRESS

0x17

AD_TYPE_RANDOM_TARGET_ADDRESS

0x18

AD_TYPE_APPEARANCE

0x19

AD_TYPE_ADVERTISING_INTERVAL

0x1A

AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS

0x1B

AD_TYPE_LE_ROLE

0x1C

AD_TYPE_SIMPLE_PAIRING_HASH_C256

0x1D

AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256

0x1E

AD_TYPE_SERVICE_DATA_32BIT_UUID

0x20

AD_TYPE_SERVICE_DATA_128BIT_UUID

0x21

AD_TYPE_URI

0x24

AD_TYPE_3D_INFORMATION_DATA

0x3D

AD_TYPE_MANUFACTURER_SPECIFIC_DATA

0xFF