链路层:状态机、数据包与地址策略
链路层,说白了就是 BLE 协议栈里最核心的那一层。我经常跟团队里的新人说,你搞懂了链路层,BLE 通信的底子就算打牢了。这一层负责设备之间怎么建立连接、怎么收发数据、怎么省电,还有怎么保护隐私。
咱们今天重点聊三个东西:状态机怎么跳转、数据包长什么样、白名单和私有地址怎么用。嗯,都是实战中绕不开的硬骨头。
链路层状态机
BLE 链路层有 5 个状态,设备在任一时刻只能处于其中一个状态。这 5 个状态分别是:待机(Standby)、广播(Advertising)、扫描(Scanning)、发起连接(Initiating)、已连接(Connection)。
状态之间的跳转有严格规则,不是你想跳就能跳的。我画了一张图,你看一眼就明白了。
你看,待机是起点也是终点。从待机可以跳到广播、扫描或发起连接。但广播和扫描之间不能直接跳转,必须先回到待机。这个设计我一开始也觉得有点绕,后来做项目时才明白——这是为了防止状态冲突,保证链路层行为可预测。
我的经验:调试连接失败问题时,先检查状态机是否卡在某个状态。我曾经遇到一个设备始终无法连接,折腾了两天,最后发现是广播超时后没有正确回到待机,导致状态机死锁。复位一下就好了。
链路层数据包格式
BLE 链路层的数据包结构很简洁,一共就几个字段。但每个字段都有讲究,咱们一个一个看。
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 前导码(Preamble) | 1 | 用于接收端同步时钟,固定为 0xAA 或 0x55 |
| 访问地址(Access Address) | 4 | 广播包固定为 0x8E89BED6;数据包为随机生成 |
| 协议数据单元(PDU) | 2~257 | 包含报头、载荷和可能的消息完整性校验 |
| 循环冗余校验(CRC) | 3 | 24 位 CRC,用于检测传输错误 |
前导码只有 1 个字节,但作用很大。接收端靠它来锁定比特时钟。访问地址在广播信道是固定的,在数据信道则是连接建立时协商的。我建议你记住那个广播访问地址 0x8E89BED6,调试时经常要用到。
PDU 是数据包的核心,它又分两种:广播信道 PDU 和数据信道 PDU。广播信道 PDU 的报头包含 PDU 类型、发送地址类型和长度等信息。数据信道 PDU 的报头则包含逻辑链路标识(LLID)、序列号和更多控制位。
广播信道 PDU 类型:
- ADV_IND:可连接的非定向广播,最常用
- ADV_DIRECT_IND:定向广播,针对特定设备
- ADV_NONCONN_IND:不可连接的非定向广播,用于信标
- ADV_SCAN_IND:可扫描的非定向广播
- SCAN_REQ / SCAN_RSP:扫描请求与响应
- CONNECT_REQ:连接请求,包含连接参数
数据信道 PDU 的 LLID 字段决定了这个包是数据包还是控制包。LLID 为 0x01 表示数据包续帧,0x02 表示数据包起始帧或完整帧,0x03 表示链路层控制包。
注意:CRC 校验失败是无线通信中最常见的错误之一。我曾经在产线上遇到过一批设备丢包率异常高,排查后发现是天线匹配电路虚焊导致信号质量差,CRC 错误率飙升。所以,当你看到大量 CRC 错误时,先检查物理层,别急着怀疑协议栈。
白名单与私有地址
白名单和私有地址,这两个东西经常一起用。白名单用来过滤设备,私有地址用来保护隐私。咱们分开说。
白名单
白名单就是一张允许通信的设备地址列表。链路层可以配置为只接受白名单中的设备发来的包。这在广播、扫描和发起连接时都有效。
举个例子,你做一个智能门锁,总不能随便一个手机都能连吧?把允许开锁的手机地址加到白名单里,其他设备发来的连接请求直接忽略。这样既省电又安全。
白名单存储在链路层控制器中,最多可以存几十个地址。我建议你根据产品需求合理规划白名单大小,别贪多,否则查找效率会下降。
私有地址
私有地址是 BLE 4.0 引入的隐私保护机制。设备地址可以定期变化,防止被恶意跟踪。私有地址分两种:可解析私有地址(RPA)和不可解析私有地址(NRPA)。
| 类型 | 生成方式 | 能否被解析 | 典型用途 |
|---|---|---|---|
| 可解析私有地址(RPA) | 基于 IRK 和随机数生成 | 能,配对设备可解析 | 已配对设备间的连接 |
| 不可解析私有地址(NRPA) | 完全随机生成 | 不能 | 低安全性广播,如信标 |
RPA 的生成过程是这样的:设备用本地 IRK(身份解析密钥)和一个随机数,通过 AES-128 加密生成一个哈希值,再和随机数拼接成地址。配对过的设备收到这个地址后,用自己存储的 IRK 去尝试解析,能解开就知道是谁了。
避坑指南:我曾经在一个项目中用了 NRPA 做连接,结果发现每次重连都要重新配对,用户体验很差。后来改成 RPA,配合白名单使用,既保护了隐私,又保持了连接体验。记住:需要长期连接的场景,用 RPA;一次性广播场景,用 NRPA 就够了。
白名单 + 私有地址的配合
白名单可以存储两种地址:公共地址和私有地址。对于私有地址,白名单里存的是设备的 IRK,而不是具体的地址。因为地址会变,但 IRK 不变。链路层收到一个 RPA 后,会用白名单里的每个 IRK 去尝试解析,匹配上了就放行。
这个机制在实际产品中非常实用。比如你家的智能灯泡,第一次配对时把手机的白名单和 IRK 存下来。之后手机每次连接都用新的 RPA,灯泡通过白名单里的 IRK 解析出是同一个手机,直接放行。整个过程对用户完全透明。
总结一下链路层的核心要点:
- 状态机有 5 个状态,跳转有严格规则,广播和扫描不能直接互跳
- 数据包结构简单,前导码 + 访问地址 + PDU + CRC,广播访问地址固定为 0x8E89BED6
- 白名单用于设备过滤,私有地址用于隐私保护,两者配合效果最佳
- RPA 可被配对设备解析,NRPA 完全随机,按场景选择
链路层的内容就这些。你把这些搞清楚了,后面讲连接建立和数据传输就会轻松很多。嗯,咱们继续往下走。