链路层状态机与连接建立
BLE 链路层(Link Layer)是蓝牙核心协议栈中直接控制射频收发、状态转换和连接维护的实体。本章围绕链路层的五种基本状态、广播与扫描的时序机制、连接参数协商流程以及通过 HCI 命令对连接进行精确控制展开,为后续实现本地持久化中的连接管理与数据同步打下基础。
链路层角色与状态机
BLE 链路层定义了五种互斥状态:待机(Standby)、广播(Advertising)、扫描(Scanning)、发起(Initiating)和连接(Connection)。每个时刻链路层只能处于其中一种状态,状态转换由主机通过 HCI 命令或内部定时事件触发。
| 状态 | 角色 | 说明 |
|---|---|---|
| Standby | 空闲 | 不发送或接收任何数据包,最低功耗 |
| Advertising | 广播者(Advertiser) | 周期性发送广播包,可携带扫描响应数据 |
| Scanning | 扫描者(Scanner) | 监听广播信道,接收广播包与扫描响应 |
| Initiating | 发起者(Initiator) | 针对特定广播者发送连接请求,建立连接 |
| Connection | 主设备/从设备 | 在已建立的连接中按锚点时间收发数据 |
状态转换的典型路径:Standby → Advertising(开启广播),Standby → Scanning(开始扫描),Scanning → Initiating(匹配到目标后发起连接),Initiating → Connection(收到连接响应后进入连接态)。
广播与扫描过程
广播和扫描是连接建立的前置步骤。广播者在三个广播信道(37/38/39)上交替发送 ADV_IND 或 ADV_DIRECT_IND 等 PDU;扫描者在相同信道上监听并接收广播包,可选择发送扫描请求以获取额外数据。
广播时序参数
| 参数 | 范围 | 说明 |
|---|---|---|
advInterval | 20 ms – 10.24 s | 连续广播事件间的间隔,步进 0.625 ms |
advDelay | 0 – 10 ms | 每个广播事件前的随机延迟,防碰撞 |
advChannelMap | bit 0-2 | 启用哪些广播信道(37/38/39) |
扫描时序参数
| 参数 | 范围 | 说明 |
|---|---|---|
scanInterval | 2.5 ms – 10.24 s | 两次扫描间的间隔 |
scanWindow | 2.5 ms – scanInterval | 每次扫描的监听时长 |
scanType | 被动 / 主动 | 被动只接收;主动可发送扫描请求 |
连接参数协商
当发起者发送 CONNECT_REQ PDU 后,连接参数在连接请求中一次性确定,随后可通过 连接参数更新请求(LL_CONNECTION_PARAM_REQ)在连接建立后进行协商。核心参数包括:
| 参数 | 典型范围 | 说明 |
|---|---|---|
connInterval | 7.5 ms – 4 s | 两个连接事件之间的间隔 |
connLatency | 0 – 499 | 从设备可跳过的最大连接事件数 |
supervisionTimeout | 100 ms – 32 s | 连接超时时间,超过未收到数据则判定断开 |
协商流程通常由从设备发起(例如需要更低的延迟传输数据):
- 从设备发送 LL_CONNECTION_PARAM_REQ,包含期望的参数范围。
- 主设备收到后,根据自身能力与当前负载决定接受或拒绝。
- 若接受,主设备回复 LL_CONNECTION_PARAM_RSP;若拒绝,可提出替代值。
- 双方应用新参数从下一个连接事件开始生效。
使用 HCI 命令控制连接
主机(Host)通过 Host-Controller Interface(HCI)向链路层发送命令。建立连接最常用的 HCI 命令序列如下:
1. 设置广播参数并开启广播
# HCI 命令示例 (使用 hcitool/btmgmt 或直接发送 HCI 数据包)
# 设置广播参数:间隔 100ms,类型为可连接广播
hcitool -i hci0 cmd 0x08 0x0006 0xA0 0x00 0xA0 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x07 0x00
# 开启广播
hcitool -i hci0 cmd 0x08 0x000A 0x01
2. 设置扫描参数并开启扫描
# 设置扫描间隔 60ms,扫描窗口 60ms,主动扫描
hcitool -i hci0 cmd 0x08 0x000C 0x60 0x00 0x60 0x00 0x01 0x00
# 开启扫描
hcitool -i hci0 cmd 0x08 0x000D 0x01 0x00
3. 发起连接(指定目标 MAC)
# 发起连接:目标地址 11:22:33:44:55:66,连接间隔 30ms,延迟 0,超时 4s
hcitool -i hci0 cmd 0x08 0x000D 0x00 0x60 0x00 0x60 0x00 0x00 0x00 0x11 0x22 0x33 0x44 0x55 0x66 0x00 0x00 0x30 0x00 0x00 0x00 0x10 0x00
实际开发中通常使用 BlueZ 提供的 hcitool lecc 或 bluetoothctl connect 等高层工具,但底层均映射为上述 HCI 命令。在嵌入式环境中(如 Zephyr、NimBLE),则直接调用 HCI API:
/* Zephyr 中发起连接示例 */
struct bt_le_conn_param *param = BT_LE_CONN_PARAM(24, 40, 0, 400);
struct bt_conn *conn;
int err = bt_conn_le_create(&peer_addr, BT_CONN_LE_CREATE_CONN, param, &conn);
if (err) {
printk("连接创建失败: %d\n", err);
}
常见错误与修复
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 扫描不到广播包 | 广播信道被禁用;扫描窗过短;射频冲突 | 检查 advChannelMap;增大 scanWindow;检查天线匹配 |
| 连接请求无响应 | 目标设备已进入连接态;广播类型不可连接 | 确认广播类型为 ADV_IND 或 ADV_DIRECT_IND;重置目标状态 |
| 连接频繁断开 | supervisionTimeout 过小;连接间隔不匹配 | 增大超时值;确保主从设备连接参数兼容 |
| 参数更新被拒绝 | 请求参数超出主设备能力范围 | 读取主设备支持的参数范围,调整至合理区间 |
调试时建议开启蓝牙芯片的 HCI 日志(如 Zephyr 的 CONFIG_BT_HCI_RAW_LOG),实时观察 HCI 命令与事件流:
# 开启 btmon 监控 HCI 流量(Linux)
btmon -w hci_log.pklg
# 实时查看
btmon -r
性能与稳定性优化
- 广播拥塞控制:在广播密集型场景(如数十个设备同时广播),将
advDelay随机化范围适当增大到 5–10 ms,降低碰撞概率。 - 扫描窗口适配:若扫描功耗敏感,可采用 间歇扫描 策略:扫描 100 ms、休眠 900 ms,而非持续全速扫描。
- 连接参数自适应:在本地持久化场景中,批量数据传输时使用短间隔(15–30 ms),空闲时切换回长间隔(200–500 ms)并增大 latency,降低功耗。
- 超时冗余:设置
supervisionTimeout为连接间隔的 10–20 倍以上,防止瞬时射频干扰导致意外断开。 - 信道规避:在 2.4 GHz 干扰严重的环境中,通过
advChannelMap避开拥堵信道(如仅使用 38、39 信道)。