MicroPython中提供了ubluetooth模块作为蓝牙BLE(Bluetooth Low Energy)的操作接口,其主要提供了蓝牙控制器层面的操作, 匹配低阶蓝牙BLE协议,但未集成各种特定设备类型配置文件(Profile),仅提供基础实现模块以供高阶层抽象,故称作低阶蓝牙BLE接口。
在蓝牙BLE中,一个设备可能需要以多个角色进行工作。当前,其能够支持中心(Central)、外设(Peripheral)、广播者(Broadcaster)和观察者(Observer)角色,还能够支持GATT服务器、GATT客户端和L2CAP面向连接通道的工作模式。目前,仅有部分MicroPython移植版本能够支持配对和绑定操作。
注意:该模块仍在开发中,其涉及的类、函数、方法和常量定义均有可能发生变化。
GATT服务器
GATT服务器含有一系列经注册的服务。每项服务均可包含带有属性值的特性,特性还可以包含描述符,描述符也有自己的属性值。
这些属性值均存储在本地,能够通过服务注册时生成的“属性句柄”(handle)进行存取,亦可以供远程设备读取。另外,服务器还能在特定连接中将特性的属性值上报给所连接的客户端设备。
中心设备和外设设备均能够工作于服务器模式,然而,大部分情况下,外设设备工作于服务器模式更为常见。
特性(Characteristic)和描述符(Descriptor)默认具有最大20字节的长度,远程客户端写入的任何值会被截短到该长度。然而,任何本地的写入操作均可以增大该长度,所以,若需要某个特性能够被远程客户端以更大长度写入,可以在服务注册之后,使用gatts_write()首先进行一次大长度的写入操作,比如gatts_write(char_handle,bytes(100))。
BLE.gatts_register_services(services_definition, /)
配置服务器特定服务选项,该调用会替代掉现有选项值。
services_definition是一个服务列表,其中每项服务均是一个包含两个元素的元组,该元组由一个UUID和一个特性列表组成。每个特性是一个两元素或者三元素的元组,其中包含一个UUID,一个标志值,和一个可选的的描述符列表。而每个描述符又是一个两元素的元组,其包含一个UUID和一个标志值。
这些标志值是下面各种标志的位或组合,既可以设定特性或者描述符的行为,也可以设定所需的安全性和隐私性。
该函数的返回值是一个元组列表,其中每项服务对应一个元组,每个元组的元素均是其所对应特性和描述符的句柄。特性和描述符的句柄在同一元组中以其定义时的顺序排列。
下面示例中注册了两项服务(心率服务和Nordic UART透传服务):
HR_UUID=bluetooth.UUID(0x180D)
HR_CHAR=(bluetooth.UUID(0x2A37),bluetooth.FLAG_READ| luetooth.FLAG_NOTIFY,)
HR_SERVICE=(HR_UUID, (HR_CHAR,),)
UART_UUID=bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
UART_TX=(bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'),
bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
UART_RX=(bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'),
bluetooth.FLAG_WRITE,)
UART_SERVICE=(UART_UUID, (UART_TX, UART_RX,),)
SERVICES=(HR_SERVICE, UART_SERVICE,)
((hr,),(tx,rx,),) = bt.gatts_register_services(SERVICES)
hr,tx,rx这三个特性句柄能够被用于gatts_read(),gatts_write(),gatts_notify()和gatts_indicate()以进行相应操作。
注意:在注册服务之前必须先停止广播。
特性和描述符可用标志如下:
from micropython import const
_FLAG_BROADCAST = const(0x0001)
_FLAG_READ = const(0x0002)
_FLAG_WRITE_NO_RESPONSE = const(0x0004)
_FLAG_WRITE = const(0x0008)
_FLAG_NOTIFY = const(0x0010)
_FLAG_INDICATE = const(0x0020)
_FLAG_AUTHENTICATED_SIGNED_WRITE = const(0x0040)
_FLAG_AUX_WRITE = const(0x0100)
_FLAG_READ_ENCRYPTED = const(0x0200)
_FLAG_READ_AUTHENTICATED = const(0x0400)
_FLAG_READ_AUTHORIZED = const(0x0800)
_FLAG_WRITE_ENCRYPTED = const(0x1000)
_FLAG_WRITE_AUTHENTICATED = const(0x2000)
_FLAG_WRITE_AUTHORIZED = const(0x4000)
若要使用上述常量定义,需要自行添加到你的Python代码中。
BLE.gatts_read(value_handle, /)
读取value_handle所指定的本地值,该值可能由gatts_write()本地写入,也可能由远程设备写入。
BLE.gatts_write(value_handle, data, /)
写入value_handle所指定的值,以供客户端读取。
BLE.gatts_notify(conn_handle, value_handle, data=None, /)
向已连接的客户端发送上报请求。如果data参数不为None,其会作为上报请求的负载发向客户端,Value_handle指定的本地值并不会被改变。而如果data为None,当前本地值将会被发送过去。(本地值需经由gatts_write()设置过)
BLE.gatts_indicate(conn_handle, value_handle, /)
向已连接的客户端发送通知请求。
注意,该调用不支持发送自定义值,其总是发送value_handle指定的本地值(本地址需经由gatts_write()设置过)。作为确认(无论失败还是超时),_IRQ_GATTS_INDICATE_DONE事件会被触发。
BLE.gatts_set_buffer(value_handle,len,append=False, /)
以字节为单位设置特定值的内部缓冲区大小。该缓冲区会限制远程写入请求的最大长度,默认为20字节。
若将append参数设为True将使得所有远程的写入操作均以添加,而不是替代,的形式更新当前值。该方式最多能够接受len参数所指定长度的字节数。当调用gatts_read()时,该值会在读取后被清空。此特性对于实现一些类似Nodic UART透传服务十分有效。
GATT客户端
GATT客户端能够发现和读/写远程GATT服务器上的特性值。
通常,中心设备工作于GATT客户端模式,然而,外设设备也可能工作于GATT客户端模式以发现其所连接的中心设备的信息(比如,从设备信息服务中读取设备名)。
BLE.gattc_discover_services(conn_handle, uuid=None, /)
查询已连接服务器所支持的服务。可选的,可通过服务uuid参数指定只查询某项服务。
对于每项所发现的服务,_IRQ_GATTC_SERVICE_RESULT事件会被首先触发,接着,_IRQ_GATTC_SERVICE_DONE会被最后触发。
BLE.gattc_discover_characteristics(conn_handle,start_handle,end_handle,uuid=None, /)
查询已连接服务器一定范围内的特性。可选的,可通过特性uuid参数指定只查询某个特性。可以使用start_handle=1,end_handle=0xffff来搜寻所有服务里的特性。
对于每个所发现的特性,_IRQ_GATTC_CHARACTERISTIC_RESULT事件会被首先触发,接着,_IRQ_GATTC_CHARACTERISTIC_DONE会被最后触发。
BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle, /)
查询已连接服务器一定范围内的描述符。
对于每个所发现的描述符,_IRQ_GATTC_DESCRIPTOR_RESULT事件会被首先触发,接着,_IRQ_GATTC_DESCRIPTOR_DONE会被最后触发。
BLE.gattc_read(conn_handle, value_handle, /)
发起向已连接服务器的指定特性或描述符的读取操作。
当该值可用时,_IRQ_GATTC_READ_RESULT事件会被首先触发,接着,_IRQ_GATTC_READ_DONE事件会被最后触发。
BLE.gattc_write(conn_handle, value_handle, data, mode=0, /)
发起向已连接服务器指定特性或描述符的写入动作。其中,参数mode设定写入动作的参数,目前所支持的模式如下:
- mode=0无确认写入(默认模式):该写入操作会被发送到远程服务器上,但是没有确认回复,也没有事件会被触发;
- mode=1带确认写入:该模式下,要求远程服务器在收到数据后发送回复确认。
如果从远程服务器接收到回复,_IRQ_GATTC_WRITE_DONE事件会被触发。
BLE.gattc_exchange_mtu(conn_handle, /)
发起和已连接服务器的MTU(MTU:Maximum transmission unit,指在一个协议数据单元中能够传输的最大数据量)交换协商过程。其中,MTU值需首先使用BLE.config(mtu=value)设定完毕。MTU交换完成后,_IRQ_MTU_EXCHANGE事件会被触发。
注意:MTU交换通常由中心设备发起。当中心设备使用BlueKitchen协议栈时,不支持远程外设发起MTU交换,而对于NimBLE协议栈,中心设备和外设设备均可以发起该交换过程。
L2CAP(Logical Link Control and Adaptation Protocol)面向连接通道
该功能特性允许两个蓝牙BLE设备进行类似socket形式的数据交换。一旦设备通过通用存取规范GAP(Generic Access Profile)建立连接以后,任何一方设备均能够通过数字协议服务复用器PSM(Protocol/Service Multiplexer)监听另一方的连接。
注意:该功能特性目前仅在STM32平台和Unix平台上的NimBLE协议栈中得以支持(ESP32平台不支持)。在某一时间内,只能有一路L2CAP通道处于激活状态(例如,不能在监听的同时进行连接)。
多个L2CAP通道可以建立在同一CID(通道ID)上,活动L2CAP通道可以用连接句柄唯一标识。
面向连接通道具有内置的基于信任的流控机制。不像ATT(Attribute protocol),设备间需要协商一个共享的MTU(Maximum transmission unit),面向连接通道中的监听方设备设置了独立的MTU以限制其用l2cap_recvinto()接收到但还未消费完的最大数据量,连接方设备也设置了独立的MTU以限制其发送给远程设备的最大数据量。
BLE.l2cap_listen(psm, mtu, /)
开始在特定psm上监听可能的L2CAP通道请求,本地MTU设定为mtu参数所指定值。
当远程设备发起连接后,_IRQ_L2CAP_ACCEPT事件会被触发,在该事件响应函数中,可以通过返回非零整数而拒绝该连接请求。一旦接受了该连接请求,_IRQ_L2CAP_CONNECT事件会被触发,可以在该事件的响应函数中获取CID(通道ID)、本地和远程的MTU。
注意:当前时刻不允许停止监听。
BLE.l2cap_connect(conn_handle, psm, mtu, /)
在给定psm上连接处于监听态的设备,本地MTU通过mtu参数进行设置。
连接成功后,_IRQ_L2CAP_CONNECT事件会被触发,可以在该事件的响应函数中获取CID(通道ID)、本地和远程的MTU。连接失败时,会触发带有非零状态值的_IRQ_L2CAP_DISCONNECT事件。
BLE.l2cap_disconnect(conn_handle, cid, /)
断开conn_handle和cid所指定的已激活的L2CAP通道。
BLE.l2cap_send(conn_handle, cid, buf, /)
在conn_handle和cid所指定的L2CAP通道上发送buf中内容。buf所指定的缓冲区长度不能大于远程设备的MTU,也不能大于两倍的本地MTU。
如果该通道当前已经“阻塞”,该函数会返回False,此时,直到_IRQ_L2CAP_SEND_READY事件被触发,l2cap_send函数不应该被再次调用。(该事件通常在远程设备接收并已经处理了数据之后被会触发)
BLE.l2cap_recvinto(conn_handle, cid, buf, /)
从conn_handle和cid所指定通道上接收数据并存储到buf参数所指定的缓冲区(该buf参数必须是有缓冲意义的数据类型,比如bytearray或者memoryview)。
该函数返回从通道中读取到的字节数。如果buf参数为None,仅返回其中可用的字节数。
注意:在接收到_IRQ_L2CAP_RECV事件后,应用程序应该持续调用l2cap_recvinto()直到接收缓冲区中没有数据(例如,已接收了远程设备所支持的MTU长度数据)。
直到接收方接收缓冲区为空,该通道上的远程设备不应该再次发送数据。
配对和绑定
配对允许设备双方通过交换秘钥而对连接进行加密和授权。可选的,配对时可进行配对秘钥认证(passkey authentication)以进行中间人攻击防护(MITM protection)。
绑定是将秘钥存储到非易失性存储器的过程。在进行绑定时,设备能够基于其所存储的IRK(identity resolving key)解析另一设备的RPA(resolvable private address)。为支持绑定,应用必须实现_IRQ_GET_SECRET事件和_IRQ_SET_SECRET事件的响应函数。
注意:该功能特性目前仅在STM32平台和Unix平台上的NimBLE协议栈上得以支持。(ESP32上不支持)
BLE.gap_pair(conn_handle, /)
发起和远程设备的配对过程。
在调用该函数之前,需确保io,mitm,le_secure和bond配置选项已通过config()进行过正确设置。配对成功后,_IRQ_ENCRYPTION_UPDATE事件会被触发。
BLE.gap_passkey(conn_handle, action, passkey, /)
用于响应特定于conn_handle和action的_IRQ_PASSKEY_ACTION事件。
配对秘钥passkey为一个数字值,设备I/O能力不同则对应的action不同,passkey亦不同:(1) 当action为_PASSKEY_ACTION_INPUT时,应用程序应该弹出输入界面以便用户输入远程设备上的passkey;(2) 当action为_PASSKEY_ACTION_DISPLAY时,应用程序应该生成一个随机的六位passkey并展示给用户;(3) 当action为_PASSKEY_ACTION_NUMERIC_COMPARISON时,应用程序应该显示_IRQ_PASSKEY_ACTION事件所提供的passkey,然后,要么返回0以代表取消配对,要么返回1以代表接受配对。
class UUID
class ubluetooth.UUID(value, /)
用特定值生成UUID实例。该值可以为如下之一:
- 十六位整数,比如0x2908;
- 128位UUID字符串,比如 '6E400001-B5A3-F393-E0A9-E50E24DCCA9E'
注:点关注,不迷路,关注公众号: eeemaker小王子 获取更多内容及pdf完整版本