bluetooth --- 底层蓝牙

本模块提供了访问板载蓝牙控制器的接口。它支持低功耗蓝牙(BLE),可担任 Central(中心)、Peripheral(外围)、Broadcaster(广播者)和 Observer(观察者)角色,同时支持 GATT 服务端与客户端以及面向连接的 L2CAP 通道。设备可以同时担任多种角色。此外还支持配对与绑定。

本 API 旨在与底层蓝牙协议保持一致,并为更高层的抽象(例如特定设备类型)提供构建模块。

小技巧

对于大多数应用,建议优先使用更高层的 aioble 库,它为本模块提供了基于 asyncio 的封装。请参阅 aioble --- 异步 BLE

class BLE

class bluetooth.BLE

返回单例 BLE 对象。

配置

active(active: bool | None = None, /) bool

可选地更改 BLE 射频的激活状态,并返回当前状态。

在使用本类的任何其他方法之前,必须先激活射频。

config(param: str, /) Any
config(*, **kwargs: Any) None

获取或设置 BLE 接口的配置值。要获取某个值,参数名应以字符串形式加引号传入,且一次只能查询一个参数。要设置值则使用关键字语法,一次可以设置一个或多个参数。

目前支持的值有:

  • 'mac':当前正在使用的地址,取决于当前的地址模式。返回一个 (addr_type, addr) 元组。

    有关地址类型的详细信息,请参阅 gap_scan

    此项只能在接口当前处于激活状态时查询。

  • 'addr_mode':设置地址模式。可选值为:

    名称

    行为

    0x00

    PUBLIC

    使用控制器的公共地址。

    0x01

    RANDOM

    使用生成的静态地址。

    0x02

    RPA

    使用可解析私有地址。

    0x03

    NRPA

    使用不可解析私有地址。

    默认情况下,接口在可用时会使用 PUBLIC 地址,否则使用 RANDOM 地址。

  • 'gap_name':获取/设置由 Generic Access 服务(UUID 0x1800)的 Device Name 特征(UUID 0x2a00)使用的 GAP 设备名称。该名称可在任意时刻设置并多次更改。

  • 'rxbuf':获取/设置用于存储传入事件的内部缓冲区大小(字节)。该缓冲区对整个 BLE 驱动是全局的,因此它处理所有事件的传入数据,包括所有特征。增大它可以更好地处理突发性的传入数据(例如扫描结果),并能够接收更大的特征值。

  • 'mtu':获取/设置在 ATT MTU 交换过程中使用的 MTU。最终的 MTU 将取此值与远程设备 MTU 中的较小者。ATT MTU 交换不会自动发生(除非远程设备发起),必须通过 gattc_exchange_mtu 手动发起。使用 _IRQ_MTU_EXCHANGED 事件来获取某个连接的 MTU。

  • 'bond':设置在配对期间是否启用绑定。启用后,配对请求将设置 "bond" 标志,且双方设备都会存储密钥。

  • 'mitm':设置配对是否需要 MITM 防护。

  • 'io':设置本设备的 I/O 能力。

    可用选项有:

    常量

    能力

    _IO_CAPABILITY_DISPLAY_ONLY

    0

    仅显示

    _IO_CAPABILITY_DISPLAY_YESNO

    1

    显示并支持是/否输入

    _IO_CAPABILITY_KEYBOARD_ONLY

    2

    仅键盘

    _IO_CAPABILITY_NO_INPUT_OUTPUT

    3

    无输入或输出

    _IO_CAPABILITY_KEYBOARD_DISPLAY

    4

    键盘和显示

  • 'le_secure':设置是否需要 "LE Secure"(LE 安全连接)配对。默认为 false(即允许 "Legacy Pairing" 传统配对)。

事件处理

irq(handler: Callable[[int, Tuple], Any | None], /) None

为来自 BLE 协议栈的事件注册回调。handler 接受两个参数:event(将是下文列出的代码之一)和 data(一个与事件相关的值元组)。

注意: 作为防止不必要内存分配的优化,元组中的 addradv_datachar_datanotify_datauuid 项是指向 bluetooth 内部环形缓冲区的只读 memoryview 实例,仅在 IRQ 处理函数被调用期间有效。如果你的程序需要保存其中某个值以便在 IRQ 处理函数返回后访问(例如保存到类实例或全局变量中),则需要复制该数据,可以使用 bytes()bluetooth.UUID(),如下所示:

connected_addr = bytes(addr)  # equivalently: adv_data, char_data, or notify_data
matched_uuid = bluetooth.UUID(uuid)

例如,扫描结果的 IRQ 处理函数可能会检查 adv_data 来判断它是否为正确的设备,然后才复制地址数据以供程序中其他地方使用。要在 IRQ 处理函数内打印数据,则需要使用 print(bytes(addr))

处理函数通常根据事件代码进行分派,并解包与事件相关的载荷元组:

def bt_irq(event, data):
    if event == _IRQ_CENTRAL_CONNECT:
        conn_handle, addr_type, addr = data
        ...
    elif event == _IRQ_SCAN_RESULT:
        addr_type, addr, adv_type, rssi, adv_data = data
        ...

下文列出了每个事件代码、它所传递的载荷以及简短的说明。对于提及 status 字段的事件,status 在成功时为 0,失败时为某个由实现决定的非零值。

常量

事件

载荷元组

_IRQ_CENTRAL_CONNECT

1

一个 central 已连接到本 peripheral。

(conn_handle, addr_type, addr)

_IRQ_CENTRAL_DISCONNECT

2

一个 central 已从本 peripheral 断开连接。

(conn_handle, addr_type, addr)

_IRQ_GATTS_WRITE

3

一个已连接的客户端写入了本地特征或描述符。使用 gatts_read 来获取新值。

(conn_handle, attr_handle)

_IRQ_GATTS_READ_REQUEST

4

一个已连接的客户端发起了一次读取。从下表中返回非零错误代码以拒绝该读取,或返回 0 / None 以接受它。

(conn_handle, attr_handle)

_IRQ_SCAN_RESULT

5

在主动扫描期间收到了单个广播数据包。

(addr_type, addr, adv_type, rssi, adv_data)

_IRQ_SCAN_DONE

6

当前扫描已结束,原因可能是配置的持续时间已到,或是调用了 gap_scan(None)

()

_IRQ_PERIPHERAL_CONNECT

7

先前发起的 gap_connect 已成功。

(conn_handle, addr_type, addr)

_IRQ_PERIPHERAL_DISCONNECT

8

一个已连接的 peripheral 已断开连接。

(conn_handle, addr_type, addr)

_IRQ_GATTC_SERVICE_RESULT

9

gattc_discover_services 发现了一个服务。

(conn_handle, start_handle, end_handle, uuid)

_IRQ_GATTC_SERVICE_DONE

10

服务发现已完成。

(conn_handle, status)

_IRQ_GATTC_CHARACTERISTIC_RESULT

11

gattc_discover_characteristics 发现了一个特征。

(conn_handle, end_handle, value_handle, properties, uuid)

_IRQ_GATTC_CHARACTERISTIC_DONE

12

特征发现已完成。

(conn_handle, status)

_IRQ_GATTC_DESCRIPTOR_RESULT

13

gattc_discover_descriptors 发现了一个描述符。

(conn_handle, dsc_handle, uuid)

_IRQ_GATTC_DESCRIPTOR_DONE

14

描述符发现已完成。

(conn_handle, status)

_IRQ_GATTC_READ_RESULT

15

先前发起的 gattc_read 已返回数据。

(conn_handle, value_handle, char_data)

_IRQ_GATTC_READ_DONE

16

先前发起的 gattc_read 已完成。

(conn_handle, value_handle, status)

_IRQ_GATTC_WRITE_DONE

17

先前发起的 gattc_write 已被确认。

(conn_handle, value_handle, status)

_IRQ_GATTC_NOTIFY

18

远程服务端发送了一个(无需确认的)通知。

(conn_handle, value_handle, notify_data)

_IRQ_GATTC_INDICATE

19

远程服务端发送了一个(需确认的)指示。

(conn_handle, value_handle, notify_data)

_IRQ_GATTS_INDICATE_DONE

20

先前发送的指示已被客户端确认(或已超时)。

(conn_handle, value_handle, status)

_IRQ_MTU_EXCHANGED

21

一次 ATT MTU 交换已完成(由任一方发起)。

(conn_handle, mtu)

_IRQ_L2CAP_ACCEPT

22

远程设备请求在本设备正在监听的某个 PSM 上建立 L2CAP 连接。返回非零整数以拒绝,或返回 0 / None 以接受。

(conn_handle, cid, psm, our_mtu, peer_mtu)

_IRQ_L2CAP_CONNECT

23

一个 L2CAP 通道现已建立,原因可能是接受了一个传入请求,或是完成了一次外发的 l2cap_connect

(conn_handle, cid, psm, our_mtu, peer_mtu)

_IRQ_L2CAP_DISCONNECT

24

一个 L2CAP 通道已断开。status0 表示正常断开,非零则表示外发连接尝试失败。

(conn_handle, cid, psm, status)

_IRQ_L2CAP_RECV

25

数据已到达 L2CAP 通道。调用 l2cap_recvinto 来读取它。

(conn_handle, cid)

_IRQ_L2CAP_SEND_READY

26

先前返回 Falsel2cap_send 已清空缓冲区,通道再次就绪。非零的 status 表示发送缓冲区溢出,应用程序必须重新发送数据。

(conn_handle, cid, status)

_IRQ_CONNECTION_UPDATE

27

远程设备已更新连接参数(间隔、延迟、监督超时)。

(conn_handle, conn_interval, conn_latency, supervision_timeout, status)

_IRQ_ENCRYPTION_UPDATE

28

连接的加密状态已更改,通常发生在配对或绑定完成之后。

(conn_handle, encrypted, authenticated, bonded, key_size)

_IRQ_GET_SECRET

29

协议栈正在请求一个已存储的绑定密钥。如果 keyNone,则返回 sec_type 类型中第 index 个存储的值;否则返回与给定的 (sec_type, key) 关联的值。如果没有存储任何内容,则返回 None

(sec_type, index, key)

_IRQ_SET_SECRET

30

协议栈要求应用程序持久化一个绑定密钥。存储完成后返回 True

(sec_type, key, value)

_IRQ_PASSKEY_ACTION

31

作为配对的一部分,需要执行一项 passkey 操作。使用 gap_passkey 进行响应;可能的操作请参阅下文的 passkey 操作表。

(conn_handle, action, passkey)

对于 _IRQ_GATTS_READ_REQUEST 事件,可用的返回代码有:

常量

含义

_GATTS_NO_ERROR

0x00

接受该读取。

_GATTS_ERROR_READ_NOT_PERMITTED

0x02

不允许读取。

_GATTS_ERROR_WRITE_NOT_PERMITTED

0x03

不允许写入。

_GATTS_ERROR_INSUFFICIENT_AUTHENTICATION

0x05

客户端未通过身份验证。

_GATTS_ERROR_INSUFFICIENT_AUTHORIZATION

0x08

客户端未获授权。

_GATTS_ERROR_INSUFFICIENT_ENCRYPTION

0x0f

链路未加密。

对于 _IRQ_PASSKEY_ACTION 事件,可用的操作有:

常量

含义

_PASSKEY_ACTION_NONE

0

无需操作。

_PASSKEY_ACTION_INPUT

2

提示用户输入远程设备上显示的 passkey。

_PASSKEY_ACTION_DISPLAY

3

显示一个 6 位 passkey 供远程设备输入。

_PASSKEY_ACTION_NUMERIC_COMPARISON

4

确认该 passkey 与远程设备上显示的一致。

为了节省固件空间,这些常量并未包含在 bluetooth 模块中。请将上面列表中你需要的常量添加到你的程序里。

Broadcaster(广播者)角色

gap_advertise(interval_us: int | None, adv_data: bytes | None = None, *, resp_data: bytes | None = None, connectable: bool = True) None

以指定的间隔(单位:微秒)开始广播。该间隔将向下取整到最接近的 625us。要停止广播,将 interval_us 设置为 None

adv_dataresp_data 可以是任何实现了缓冲区协议的类型(例如 bytesbytearraystr)。adv_data 包含在所有广播中,而 resp_data 则在响应主动扫描时发送。

注意: 如果 adv_data(或 resp_data)为 None,则会复用上一次调用 gap_advertise 时传入的数据。这使得广播者只需调用 gap_advertise(interval_us) 即可恢复广播。要清除广播载荷,传入一个空的 bytes,即 b''

Observer(观察者)角色(扫描器)

gap_scan(duration_ms: int | None, interval_us: int = 1280000, window_us: int = 11250, active: bool = False, /) None

运行一次扫描操作,持续指定的时长(单位:毫秒)。

要无限期扫描,将 duration_ms 设置为 0

要停止扫描,将 duration_ms 设置为 None

使用 interval_uswindow_us 可选地配置占空比。扫描器将每隔 interval_us 微秒运行 window_us 微秒,总共持续 duration_ms 毫秒。默认的间隔和窗口分别为 1.28 秒和 11.25 毫秒(后台扫描)。

对于每个扫描结果,都会触发 _IRQ_SCAN_RESULT 事件,事件数据为 (addr_type, addr, adv_type, rssi, adv_data)

addr_type 的值表示公共地址或随机地址:

名称

含义

0x00

PUBLIC

公共设备地址。

0x01

RANDOM

随机地址(静态、RPA 或 NRPA;类型编码在地址本身之中)。

adv_type 的值对应蓝牙规范:

名称

含义

0x00

ADV_IND

可连接且可扫描的非定向广播。

0x01

ADV_DIRECT_IND

可连接的定向广播。

0x02

ADV_SCAN_IND

可扫描的非定向广播。

0x03

ADV_NONCONN_IND

不可连接的非定向广播。

0x04

SCAN_RSP

扫描响应。

如果你希望在结果中接收扫描响应,可将 active 设置为 True

当扫描停止时(无论是因为持续时间结束还是被显式停止),都会触发 _IRQ_SCAN_DONE 事件。

Central(中心)角色

中心设备可以连接到它通过观察者角色发现的 peripheral(参阅 gap_scan),或通过已知地址进行连接。

gap_connect(addr_type: int | None, addr: bytes | None = None, scan_duration_ms: int = 2000, min_conn_interval_us: int | None = None, max_conn_interval_us: int | None = None, /) None

连接到一个 peripheral。

有关地址类型的详细信息,请参阅 gap_scan

要提前取消一个未完成的连接尝试,调用 gap_connect(None)

成功时会触发 _IRQ_PERIPHERAL_CONNECT 事件。如果取消连接尝试,则会触发 _IRQ_PERIPHERAL_DISCONNECT 事件。

设备将最多等待 scan_duration_ms 以接收来自目标设备的广播载荷。

连接间隔可以使用 min_conn_interval_usmax_conn_interval_us 之一或两者以微秒为单位进行配置。否则将选择一个默认间隔,通常介于 30000 到 50000 微秒之间。较短的间隔会提升吞吐量,但代价是更高的功耗。

Peripheral(外围)角色

外围设备应当发送可连接的广播(参阅 gap_advertise)。它通常会担任 GATT 服务端的角色,且需先使用 gatts_register_services 注册服务和特征。

当一个 central 连接时,会触发 _IRQ_CENTRAL_CONNECT 事件。

Central(中心)与 Peripheral(外围)角色

gap_disconnect(conn_handle: int, /) bool

断开指定的连接句柄。这可以是已连接到本设备的 central(如果本设备担任 peripheral),也可以是本设备先前连接到的 peripheral(如果本设备担任 central)。

成功时会触发 _IRQ_PERIPHERAL_DISCONNECT_IRQ_CENTRAL_DISCONNECT 事件。

如果该连接句柄未处于连接状态,则返回 False,否则返回 True

GATT 服务端

GATT 服务端拥有一组已注册的服务。每个服务可以包含若干特征,每个特征都有一个值。特征还可以包含描述符,描述符本身也有值。

这些值存储在本地,并通过其在服务注册期间生成的 "value handle"(值句柄)进行访问。它们也可以被远程客户端设备读取或写入。此外,服务端可以通过连接句柄向已连接的客户端 "通知" 某个特征。

担任 central 或 peripheral 角色的设备都可以充当 GATT 服务端,不过在大多数情况下,由 peripheral 设备充当服务端更为常见。

特征和描述符的默认最大大小为 20 字节(默认的 23 字节 ATT MTU 减去 3 字节的 ATT 头部;协商出更大的 MTU 本身并不会提高此上限)。客户端写入它们的任何内容都将被截断到此长度。但是,任何本地写入都会增大最大大小,因此如果你想允许客户端对某个特征进行更大的写入,请在注册后使用 gatts_write。例如 gatts_write(char_handle, bytes(100))

gatts_register_services(services_definition: Sequence[Sequence], /) Sequence[Sequence[int]]

使用指定的服务配置服务端,替换任何现有的服务。

services_definition 是一个 service 的列表,其中每个 service 是一个包含 UUID 和 characteristic 列表的二元组。

每个 characteristic 是一个包含 UUID、一个 flags 值以及可选的 descriptor 列表的二元或三元组。

每个 descriptor 是一个包含 UUID 和一个 flags 值的二元组。

flags 是下文定义的各标志的按位或组合。它们同时设置特征(或描述符)的行为以及安全和隐私要求。

返回值是一个列表(每个服务对应一个元素),其中每个元素是一个元组(每个元素为一个值句柄)。特征句柄和描述符句柄按它们定义的顺序平铺到同一个元组中。

下面的示例注册了两个服务(Heart Rate 心率服务和 Nordic UART):

bt = bluetooth.BLE()
bt.active(True)

# Heart Rate service: one Heart Rate Measurement characteristic.
HR_SERVICE = (
    bluetooth.UUID(0x180D),
    (
        (bluetooth.UUID(0x2A37),
         bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY),
    ),
)

# Nordic UART service: a TX characteristic the client subscribes
# to for notifications, and an RX characteristic it writes to.
UART_SERVICE = (
    bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E'),
    (
        (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'),
         bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY),
        (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'),
         bluetooth.FLAG_WRITE),
    ),
)

((hr,), (tx, rx)) = bt.gatts_register_services(
    (HR_SERVICE, UART_SERVICE),
)

这三个值句柄(hrtxrx)可与 gatts_readgatts_writegatts_notifygatts_indicate 配合使用。

注意: 在注册服务之前必须先停止广播。

特征和描述符的可用标志有:

常量

含义

_FLAG_BROADCAST

0x0001

特征可被广播。

_FLAG_READ

0x0002

客户端可读取该值。

_FLAG_WRITE_NO_RESPONSE

0x0004

客户端可写入而不期望响应。

_FLAG_WRITE

0x0008

客户端可写入并带有确认响应。

_FLAG_NOTIFY

0x0010

服务端可发送通知(无需确认)。

_FLAG_INDICATE

0x0020

服务端可发送指示(需确认)。

_FLAG_AUTHENTICATED_SIGNED_WRITE

0x0040

客户端可发起签名写入。

_FLAG_AUX_WRITE

0x0100

扩展属性:允许排队/可靠写入。

_FLAG_READ_ENCRYPTED

0x0200

读取需要加密链路。

_FLAG_READ_AUTHENTICATED

0x0400

读取需要经过身份验证(MITM 防护)的链路。

_FLAG_READ_AUTHORIZED

0x0800

读取需要应用层级的授权。

_FLAG_WRITE_ENCRYPTED

0x1000

写入需要加密链路。

_FLAG_WRITE_AUTHENTICATED

0x2000

写入需要经过身份验证(MITM 防护)的链路。

_FLAG_WRITE_AUTHORIZED

0x4000

写入需要应用层级的授权。

与上面的事件常量一样,这些标志也未由 bluetooth 模块提供;请将你需要的标志复制到你的程序中。

gatts_read(value_handle: int, /) bytes

读取此句柄的本地值(该值可能由 gatts_write 写入,也可能由远程客户端写入)。

gatts_write(value_handle: int, data: bytes, send_update: bool = False, /) None

写入此句柄的本地值,客户端可读取该值。

如果 send_updateTrue,则任何已订阅的客户端将就此次写入收到通知(或指示,具体取决于它们订阅了什么以及该特征支持哪些操作)。

gatts_notify(conn_handle: int, value_handle: int, data: bytes | None = None, /) None

向已连接的客户端发送通知请求。

如果 dataNone(默认值),则将发送当前的本地值(由 gatts_write 设置)。

否则,如果 data 不为 None,则该值将作为通知的一部分发送给客户端。本地值不会被修改。

注意: 无论客户端是否订阅了该特征,通知都会被发送。

gatts_indicate(conn_handle: int, value_handle: int, data: bytes | None = None, /) None

向已连接的客户端发送指示请求。

如果 dataNone(默认值),则将发送当前的本地值(由 gatts_write 设置)。

否则,如果 data 不为 None,则该值将作为指示的一部分发送给客户端。本地值不会被修改。

在收到确认(或失败,例如超时)时,会触发 _IRQ_GATTS_INDICATE_DONE 事件。

注意: 无论客户端是否订阅了该特征,指示都会被发送。

gatts_set_buffer(value_handle: int, len: int, append: bool = False, /) None

设置某个值的内部缓冲区大小(字节)。这将限制可接收的最大写入量。默认为 20 字节(默认的 23 字节 ATT MTU 减去 3 字节的 ATT 头部)。

append 设置为 True 会使所有远程写入追加到当前值之后,而不是替换它。以这种方式最多可缓冲 len 字节。当你使用 gatts_read 时,读取后该值将被清空。此功能在实现类似 Nordic UART 服务的东西时很有用。

GATT 客户端

GATT 客户端可以发现远程 GATT 服务端上的特征并对其进行读写。

更常见的是由 central 角色的设备充当 GATT 客户端,不过 peripheral 也可以充当客户端,以便发现连接到它的 central 的相关信息(例如从设备信息服务中读取设备名称)。

gattc_discover_services(conn_handle: int, uuid: UUID | None = None, /) None

向已连接的服务端查询其服务。

可选地指定服务 uuid 以仅查询该服务。

对于每个发现的服务,都会触发 _IRQ_GATTC_SERVICE_RESULT 事件,完成时会触发 _IRQ_GATTC_SERVICE_DONE

gattc_discover_characteristics(conn_handle: int, start_handle: int, end_handle: int, uuid: UUID | None = None, /) None

向已连接的服务端查询指定范围内的特征。

可选地指定特征 uuid 以仅查询该特征。

传入 start_handle=1end_handle=0xffff 可覆盖完整的 GATT 属性句柄范围,因此这种组合实际上会搜索远程设备上的每一个服务。

对于每个发现的特征,都会触发 _IRQ_GATTC_CHARACTERISTIC_RESULT 事件,完成时会触发 _IRQ_GATTC_CHARACTERISTIC_DONE

gattc_discover_descriptors(conn_handle: int, start_handle: int, end_handle: int, /) None

向已连接的服务端查询指定范围内的描述符。

对于每个发现的描述符,都会触发 _IRQ_GATTC_DESCRIPTOR_RESULT 事件,完成时会触发 _IRQ_GATTC_DESCRIPTOR_DONE

gattc_read(conn_handle: int, value_handle: int, /) None

针对指定的特征或描述符句柄,向已连接的服务端发起一次远程读取。

当某个值可用时,会触发 _IRQ_GATTC_READ_RESULT 事件,完成时会触发 _IRQ_GATTC_READ_DONE

gattc_write(conn_handle: int, value_handle: int, data: bytes, mode: int = 0, /) None

针对指定的特征或描述符句柄,向已连接的服务端发起一次远程写入。

参数 mode 指定写入行为,目前支持的值有:

  • mode=0(默认)是无响应写入:写入将发送到远程服务端,但不会返回任何确认,也不会触发任何事件。

  • mode=1 是有响应写入:请求远程服务端发送一个响应/确认,表示它收到了数据。

如果收到来自远程服务端的响应,则会触发 _IRQ_GATTC_WRITE_DONE 事件。

gattc_exchange_mtu(conn_handle: int, /) None

使用通过 BLE.config(mtu=value) 设置的首选 MTU,与已连接的服务端发起 MTU 交换。

当 MTU 交换完成时,会触发 _IRQ_MTU_EXCHANGED 事件。

MTU 交换通常由 central 发起;NimBLE 支持这两种角色。

L2CAP 面向连接的通道

此功能允许在两个 BLE 设备之间进行类似 socket 的数据交换。一旦设备通过 GAP 连接,任一设备都可以在某个数字 PSM(Protocol/Service Multiplexer,协议/服务复用器)上监听对方的连接。

同一时间只能有一个 L2CAP 通道处于活动状态(即不能在监听时进行连接)。

活动的 L2CAP 通道由其建立时所基于的连接句柄和一个 CID(通道 ID)来标识。

面向连接的通道内置了基于信用额度的流量控制。与 ATT(设备协商共享 MTU)不同,监听设备和连接设备各自设置一个独立的 MTU,该 MTU 限制了远程设备在数据被 l2cap_recvinto 完全消费之前可发送的最大未处理数据量。

l2cap_listen(psm: int, mtu: int, /) None

开始在指定的 psm 上监听传入的 L2CAP 通道请求,本地 MTU 设置为 mtu

当远程设备发起连接时,会触发 _IRQ_L2CAP_ACCEPT 事件,这给了监听服务端拒绝传入连接的机会(通过返回非零整数)。

连接被接受后,会触发 _IRQ_L2CAP_CONNECT 事件,使服务端能够获取通道 ID(CID)以及本地和远程的 MTU。

注意: 目前无法停止监听。

l2cap_connect(conn_handle: int, psm: int, mtu: int, /) None

连接到指定 psm 上的一个监听端,本地 MTU 设置为 mtu

连接成功时,会触发 _IRQ_L2CAP_CONNECT 事件,使客户端能够获取 CID 以及本地和远程(对端)的 MTU。

连接失败将触发带有非零状态的 _IRQ_L2CAP_DISCONNECT 事件。

l2cap_disconnect(conn_handle: int, cid: int, /) None

断开具有指定 conn_handlecid 的活动 L2CAP 通道。

l2cap_send(conn_handle: int, cid: int, buf: bytes, /) bool

在由 conn_handlecid 标识的 L2CAP 通道上发送指定的 buf(必须支持缓冲区协议)。

缓冲区必须同时满足两个限制:它不能超过远程(对端)MTU,也不能超过本地 MTU 的两倍。

如果通道现在 "停滞",则将返回 False,这意味着在收到 _IRQ_L2CAP_SEND_READY 事件之前不得再次调用 l2cap_send(该事件会在远程设备授予更多信用额度时发生,通常发生在它接收并处理完数据之后)。

l2cap_recvinto(conn_handle: int, cid: int, buf: Any | None, /) int

从指定的 conn_handlecid 接收数据到提供的 buf 中(必须支持缓冲区协议,例如 bytearray 或 memoryview)。

返回从通道读取的字节数。

如果 bufNone,则返回可用的字节数。

注意: 在收到 _IRQ_L2CAP_RECV 事件后,应用程序应继续调用 l2cap_recvinto,直到接收缓冲区中不再有可用字节(通常最多达到远程(对端)MTU 的大小)。

在接收缓冲区清空之前,远程设备不会被授予更多的通道信用额度,将无法再发送任何数据。

配对与绑定

配对允许通过交换密钥对连接进行加密和身份验证(并可选地通过 passkey 身份验证提供 MITM 防护)。

绑定是将这些密钥存储到非易失性存储中的过程。绑定后,设备能够根据存储的身份解析密钥(IRK)解析来自另一设备的可解析私有地址(RPA)。要支持绑定,应用程序必须实现 _IRQ_GET_SECRET_IRQ_SET_SECRET 事件。

gap_pair(conn_handle: int, /) None

与远程设备发起配对。

在调用此方法之前,请确保已设置 iomitmle_securebond 配置选项(通过 config)。

配对成功时,会触发 _IRQ_ENCRYPTION_UPDATE 事件。

gap_passkey(conn_handle: int, action: int, passkey: int, /) None

针对指定的 conn_handleaction 响应一个 _IRQ_PASSKEY_ACTION 事件。passkey 的含义取决于 action(而 action 又取决于所配置的 I/O 能力):

操作

所需的 passkey 响应

_PASSKEY_ACTION_INPUT

用户从远程设备上读取的 passkey。

_PASSKEY_ACTION_DISPLAY

本地生成并显示给用户的随机 6 位 passkey。

_PASSKEY_ACTION_NUMERIC_COMPARISON

1 表示接受 _IRQ_PASSKEY_ACTION 事件中显示的 passkey,或 0 表示取消配对。

class UUID

class bluetooth.UUID(value: int | bytes | str, /)

使用指定的 value 创建一个 UUID 实例。蓝牙使用三种 UUID 宽度;UUID 接受其中任意一种:

UUID 宽度

可接受的 value 类型

示例

16 位

int 或一个 2 字节缓冲区(小端序)

UUID(0x2908)UUID(b'\x08\x29')

32 位

4 字节缓冲区(小端序)

UUID(b'\x08\x29\x00\x00')

128 位

16 字节缓冲区或一个带连字符的字符串

UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')

16 位和 32 位 UUID 通常是 SIG 分配的标识符(参阅 蓝牙分配编号);128 位 UUID 通常由厂商自定义。