2、GAP层避坑:广播参数配置、连接间隔协商、设备发现与配对绑定

GAP 层,说白了就是 BLE 设备的“社交礼仪”。

它决定了你的设备怎么让别人发现、怎么建立连接、怎么交换密钥。很多刚入门的工程师觉得 GAP 很简单,不就是配几个参数嘛。嗯,我当年也这么想,直到产品在产线上批量出问题……

这一章,我把 GAP 层最常踩的坑,一个一个掰开讲。

2.1 广播参数配置:别让设备“隐身”或“吵死人”

广播是 BLE 设备的第一印象。配置不好,要么别人搜不到你,要么你一直在空中“大喊大叫”,把整个频段都占满了。

2.1.1 广播间隔与占空比

广播间隔(Advertising Interval)决定了设备多久发一次广播包。单位是 0.625ms,取值范围 20ms ~ 10.24s。

场景 推荐间隔 注意
快速发现(如配网) 20ms ~ 100ms 功耗高,适合短暂使用
常规广播(如信标) 100ms ~ 1s 平衡功耗与发现速度
低功耗后台广播 1s ~ 10s 发现慢,但省电
⚠ 我曾经踩过的坑: 把广播间隔设成 20ms 用于长期广播,结果设备电池两天就耗光了。后来才意识到,广播时发射机是一直开着的,功耗比连接状态还高。

2.1.2 广播类型选择

BLE 有四种广播类型,选错了直接导致连接失败。

  • 可连接非定向广播(Connectable Undirected):最常用,任何扫描者都能连接。
  • 可连接定向广播(Connectable Directed):只允许指定设备连接,速度快,但容易丢。
  • 不可连接非定向广播(Non-connectable Undirected):只广播,不接收连接请求,适合信标。
  • 可扫描非定向广播(Scannable Undirected):允许扫描者读取扫描响应数据,但不建立连接。

我习惯在配网阶段用可连接非定向广播,连接成功后立即停止广播。这样既保证发现效率,又避免不必要的功耗。

2.1.3 广播数据与扫描响应

广播包最多 31 字节,扫描响应也是 31 字节。很多人把数据全塞进广播包,结果放不下。

💡 我的做法: 把设备名称、服务 UUID 等关键信息放广播包,把制造商自定义数据放扫描响应。这样扫描者可以先快速过滤,再通过扫描响应获取详细信息。

2.2 连接间隔协商:别让双方“吵架”

连接间隔(Connection Interval)是主从设备之间交换数据的周期。单位是 1.25ms,范围 7.5ms ~ 4s。

连接间隔的协商,其实是主设备和从设备之间的“讨价还价”。从设备可以提要求,但最终决定权在主设备手里。

2.2.1 连接参数请求流程

  1. 从设备发送 LL_CONNECTION_PARAM_REQ 或 L2CAP Connection Parameter Update Request。
  2. 主设备检查参数是否在可接受范围内。
  3. 主设备回复接受或拒绝。

为什么主设备会拒绝?因为连接间隔太短,主设备要频繁处理中断,CPU 负载会飙升。

核心原则: 从设备提的连接间隔,必须满足主设备的 latency 和 supervision timeout 约束。否则,主设备会直接忽略你的请求。

2.2.2 连接间隔与吞吐量的关系

连接间隔越短,吞吐量越高,但功耗也越大。我整理了一个经验表:

连接间隔 理论吞吐量(1M PHY) 典型功耗(从设备)
7.5ms ~236 kbps 高(~15mA)
30ms ~59 kbps 中(~5mA)
100ms ~18 kbps 低(~1mA)
⚠ 注意: 实际吞吐量还受 MTU 大小、PDU 长度、连接事件内数据包数量影响。别只看连接间隔。

2.2.3 连接参数更新失败的处理

我遇到过最头疼的问题:从设备请求更新连接参数,主设备一直不响应。结果从设备卡在等待状态,无法进行后续操作。

解决方案:

  • 设置超时机制,比如 5 秒内没收到响应,就断开重连。
  • 在广播阶段就把连接参数写进广播数据,让主设备一开始就用合适的参数。
  • 使用 GAP_SetParam 或类似 API 提前配置好默认参数。

2.3 设备发现与扫描:别让设备“视而不见”

设备发现是 GAP 层的核心功能之一。扫描参数配置不当,会导致设备明明在广播,却怎么也搜不到。

2.3.1 扫描窗口与扫描间隔

扫描窗口(Scan Window)和扫描间隔(Scan Interval)决定了扫描器的“监听”频率。

  • 扫描窗口:每次监听持续的时间。
  • 扫描间隔:两次监听开始之间的时间。

如果扫描窗口等于扫描间隔,就是连续扫描,功耗高但发现快。如果扫描窗口远小于扫描间隔,就是间歇扫描,省电但可能漏包。

💡 我个人的习惯: 在配网阶段用连续扫描(窗口=间隔),连接成功后切换到间歇扫描。这样既保证配网速度,又降低日常功耗。

2.3.2 主动扫描 vs 被动扫描

扫描类型 行为 适用场景
被动扫描 只监听广播包,不发送扫描请求 省电,适合信标接收
主动扫描 监听广播包后,发送扫描请求获取扫描响应 需要更多设备信息时

主动扫描会多一次空中交互,功耗更高,但能获取到扫描响应里的额外数据。我建议在需要过滤设备时用主动扫描,否则用被动扫描就够了。

2.3.3 过滤策略

很多芯片支持白名单过滤。白名单里只有指定设备的 MAC 地址,扫描器只处理这些设备的广播包。

这样做的好处:

  • 减少 CPU 中断次数。
  • 降低功耗。
  • 避免被无关设备干扰。
⚠ 我曾经踩过的坑: 白名单配置错误,导致设备无法被扫描到。后来发现是 MAC 地址的字节序搞反了。BLE 用的是小端序,但有些芯片的 API 要求大端序,一定要看 datasheet。

2.4 配对与绑定:别让密钥“丢了”

配对是建立安全连接的过程,绑定是存储密钥以便下次快速重连。很多人把这两个概念混为一谈,结果密钥管理一塌糊涂。

2.4.1 配对流程

BLE 配对分为三个阶段:

  1. 配对特征交换:双方交换 IO 能力、认证要求、密钥大小等信息。
  2. 短期密钥(STK)生成:根据配对方式生成临时密钥。
  3. 传输特定密钥:交换长期密钥(LTK)、身份解析密钥(IRK)等。

配对方式有四种:Just Works、Passkey Entry、Numeric Comparison、Out of Band。选哪种取决于你的设备有没有显示屏、键盘等 IO 能力。

配对方式 IO 能力要求 安全性
Just Works 无 低(易受中间人攻击)
Passkey Entry 一方有输入,一方有显示 中
Numeric Comparison 双方都有显示和确认 高
Out of Band NFC 等带外通道 高

2.4.2 绑定与密钥存储

绑定成功后,设备会存储 LTK、IRK、CSRK 等密钥。下次重连时,双方可以直接用存储的密钥加密通信,跳过配对过程。

这里有个坑:密钥存储的位置。有些芯片把密钥放在 RAM 里,掉电就丢了。有些放在 Flash 里,但擦写次数有限。

💡 我的做法: 把密钥存储在外部 EEPROM 或 Flash 的专用分区,并加上 CRC 校验。每次启动时检查密钥是否完整,不完整就重新配对。

2.4.3 绑定信息丢失的处理

如果从设备绑定了主设备,但主设备换了(比如用户换了手机),从设备还拿着旧的密钥,就会导致连接失败。

解决方案:

  • 在从设备上实现“清除绑定信息”的功能,比如长按按键 5 秒。
  • 在配对时加入“白名单”机制,只允许已知设备连接。
  • 如果连接失败,自动回退到未绑定状态,重新开始配对。
⚠ 我曾经踩过的坑: 从设备绑定了手机,手机丢了。用户换新手机后,从设备一直拒绝连接。最后只能通过串口命令清除绑定信息,用户体验极差。后来我加了一个“恢复出厂设置”的硬件按键,才解决这个问题。

2.5 GAP 层核心流程总览

下面这张图,把 GAP 层的主要流程串起来了。从广播开始,到扫描、连接、配对、绑定,每一步都有对应的参数和注意事项。

GAP 层核心流程 GAP 层核心流程 广播 扫描 连接 连接参数协商 配对 绑定 广播间隔、广播类型 广播数据、扫描响应 扫描窗口、扫描间隔 主动/被动扫描、白名单 连接建立、连接事件 主从角色 连接间隔、Latency Supervision Timeout 配对方式、IO 能力 密钥生成 密钥存储、绑定信息 重连与恢复

GAP 层看似简单,但每个环节都有隐藏的坑。广播参数配不好,设备就“隐身”;连接间隔没协商好,双方就“吵架”;配对绑定没处理好,密钥就“丢了”。

我个人习惯在项目初期就把 GAP 层的参数配置写成一个配置文件,每个参数都加上注释说明为什么选这个值。这样后期调试时,一眼就能看出问题出在哪。

嗯,这一章的内容就到这里。记住,GAP 层是 BLE 设备的“脸面”,把脸面收拾好了,后面的工作才能顺利。