11.2. BLE 协议栈¶
与网络类似,低功耗蓝牙(Bluetooth Low Energy)被构建为一个分层堆栈,每一层解决单一问题,并向上层暴露一个清晰的抽象。下面的名称直接来自蓝牙核心规范,并出现在 aioble 和 bluetooth API 中。
低功耗蓝牙协议栈。每一层向上一层递交一个更清晰的抽象,与网络协议栈采用的模式相同。¶
物理层。 在两个设备之间通过 2.4 GHz 无线电搬运比特。信道选择、调制、发射功率。摄像头的任务就是保持通电;其余的交由芯片完成。
链路层。 在两个同意相互通信的设备之间搬运数据包。它添加设备地址,使每个数据包能够瞄准某一个邻居;调度构成一次连接的周期性无线电事件;并处理接收方未确认的任何数据包的重传。
通用访问规范(GAP)。 发现与连接层。它定义了四种角色——广播者、观察者、外围设备、中心设备——以及让两个设备最初得以相互发现的广播 / 扫描协议,外加在它们之间打开和关闭连接的流程。地址、广播负载、连接参数和配对都存在于这一层。
通用属性规范(GATT)。 数据层。它位于一条已打开的 GAP 连接之上,暴露一个小型键/值数据库:一方承载一棵由称为特征的命名值组成的树,另一方对它们进行读取、写入或订阅。实际的应用字节就在这一层流动。
应用层。 摄像头与对端约定这些字节所代表的任何含义。蓝牙 SIG 发布了一些标准规范文件(profile)——心率、电池电量、环境感测——它们定义了常用特征,使互不相关的设备能够互操作,但任何应用程序也可以自由定义自己的规范文件。
11.2.1. 各层在运行时如何堆叠¶
这一模式与网络协议栈相吻合:
应用程序的字节进入一个特征值。
GATT 用一个标识这些字节属于哪个特征的报头将其包装起来。
GAP 保持一条已打开的连接处于运行状态,以便 GATT 有地方发送数据。
链路层将这一切包装进一个数据包中,寻址到对端的设备地址,并调度一次无线电事件来发送它。
物理层将该数据包转换为一段短暂的 2.4 GHz 无线电突发。
物理层和链路层从 Python 角度看几乎是不可见的——芯片和无线电固件处理它们。从 GAP 往上,摄像头的 Python 代码就有了更多发言权。
11.2.2. 面向用户的 API 悄然跳过的两个层¶
蓝牙规范还命名了另外两个位于链路层与 GAP/GATT 之间的层:主机控制器接口(HCI)——主机 CPU 用来驱动独立无线电芯片的协议——以及 L2CAP——将一条链路层连接拆分为多条逻辑信道的多路复用器。
二者在 aioble API 中都不可见,但二者都没有消失。HCI 位于 BLE 移植层内部,除非用到自定义构建的 MicroPython,否则它是不可见的;而 L2CAP 是 GATT 在其之上运行的承载层。想要原始字节流的应用程序可以申请自己的 L2CAP 信道(L2CAP 通道)。