链路层:状态机、数据包与地址策略

链路层,说白了就是 BLE 协议栈里最核心的那一层。我经常跟团队里的新人说,你搞懂了链路层,BLE 通信的底子就算打牢了。这一层负责设备之间怎么建立连接、怎么收发数据、怎么省电,还有怎么保护隐私。

咱们今天重点聊三个东西:状态机怎么跳转、数据包长什么样、白名单和私有地址怎么用。嗯,都是实战中绕不开的硬骨头。

链路层状态机

BLE 链路层有 5 个状态,设备在任一时刻只能处于其中一个状态。这 5 个状态分别是:待机(Standby)、广播(Advertising)、扫描(Scanning)、发起连接(Initiating)、已连接(Connection)。

状态之间的跳转有严格规则,不是你想跳就能跳的。我画了一张图,你看一眼就明白了。

BLE 链路层状态机 待机 广播 扫描 发起连接 已连接 启动广播 启动扫描 发起连接 停止广播 停止扫描 连接失败 收到连接请求 连接成功 断开连接 关键规则 广播→扫描:不允许 扫描→广播:不允许 已连接→广播:不允许

你看,待机是起点也是终点。从待机可以跳到广播、扫描或发起连接。但广播和扫描之间不能直接跳转,必须先回到待机。这个设计我一开始也觉得有点绕,后来做项目时才明白——这是为了防止状态冲突,保证链路层行为可预测。

我的经验:调试连接失败问题时,先检查状态机是否卡在某个状态。我曾经遇到一个设备始终无法连接,折腾了两天,最后发现是广播超时后没有正确回到待机,导致状态机死锁。复位一下就好了。

链路层数据包格式

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 完全随机,按场景选择

链路层的内容就这些。你把这些搞清楚了,后面讲连接建立和数据传输就会轻松很多。嗯,咱们继续往下走。