4、扫描机制详解:被动扫描与主动扫描、扫描请求与扫描响应、扫描参数配置

扫描,说白了就是接收机在听广播。但听也有不同的听法——你是只安静地听,还是听完之后主动问一句“你还有更多信息吗?”这就是被动扫描和主动扫描的核心区别。

我刚开始做 BLE 项目时,总觉得扫描就是打开接收机等着就行。后来发现,参数没配好,要么漏包,要么功耗爆炸。嗯,这里面的门道不少。

4.1 被动扫描:只听不问

被动扫描是最基础的模式。扫描设备只打开接收窗口,监听广播信道上的数据包。它不会发送任何额外数据。

  • 工作流程:扫描设备在每个扫描窗口内监听广播信道(37/38/39)。收到广播包后,直接上报给主机。
  • 数据量:只能获取广播包本身的数据(最多 31 字节)。
  • 功耗:最低。因为只收不发。
  • 典型场景:信标(Beacon)应用、低功耗传感器广播。
我的经验:做室内定位信标时,我习惯用被动扫描。信标只发 31 字节的 iBeacon 帧,扫描端不需要额外交互,省电又简单。

4.2 主动扫描:问清楚再走

主动扫描就不一样了。扫描设备收到广播包后,如果发现广播包里的 SCAN_REQ 位被置 1(表示广播设备支持扫描请求),就会主动发送一个 扫描请求(SCAN_REQ)。

广播设备收到后,会回复一个 扫描响应(SCAN_RSP),携带额外数据。

  • 工作流程:收到广播包 → 判断是否可扫描 → 发送 SCAN_REQ → 等待 SCAN_RSP → 上报完整数据。
  • 数据量:广播包(31 字节)+ 扫描响应(31 字节)= 最多 62 字节。
  • 功耗:较高。因为多了一次发送和接收。
  • 典型场景:设备发现、名称获取、服务信息查询。
注意:主动扫描会增加空中的碰撞概率。如果周围设备很多,SCAN_REQ 和 SCAN_RSP 可能会互相干扰。我曾经在一个展会上调试,几十台设备同时扫描,结果丢包率飙升到 30% 以上。

4.3 扫描请求与扫描响应:一问一答

这两个帧是主动扫描的核心。我们来看看它们的结构。

帧类型 PDU 类型 方向 数据内容
SCAN_REQ 0x03 扫描设备 → 广播设备 发送设备地址 + 目标设备地址
SCAN_RSP 0x04 广播设备 → 扫描设备 广播设备地址 + 额外 AD Data

你想想看,SCAN_REQ 其实不携带应用数据,它只是告诉广播设备:“嘿,我在这里,给我更多信息。”广播设备收到后,如果愿意响应,就回复 SCAN_RSP。

这里有个坑:广播设备可以拒绝响应。如果广播包里的 SCAN_REQ 位为 0,扫描设备就不该发请求。但有些实现不检查这个位,强行发送,结果广播设备直接忽略,白白浪费了功耗。

避坑指南:我曾经调试过一个项目,扫描端功耗异常高。抓空口日志发现,扫描设备每收到一个包就发 SCAN_REQ,不管对方支不支持。后来加上 SCAN_REQ 位检查,功耗降了 40%。

4.4 扫描参数配置:细节决定成败

扫描参数直接影响发现速度、功耗和可靠性。核心参数就几个,但配错了很要命。

4.4.1 扫描窗口与扫描间隔

  • 扫描窗口(Scan Window):每次扫描持续的时间,单位 0.625 ms。
  • 扫描间隔(Scan Interval):两次扫描开始之间的时间间隔,单位 0.625 ms。

这两个参数的关系很简单:窗口 ≤ 间隔。如果窗口等于间隔,就是连续扫描;如果窗口小于间隔,就是间歇扫描。

配置 窗口(ms) 间隔(ms) 占空比 功耗 发现速度
连续扫描 10 10 100% 高 最快
中等扫描 10 30 33% 中 中等
低功耗扫描 5 100 5% 低 慢
我的建议:产品开发初期,先用连续扫描确保功能正确。后期优化功耗时,再逐步拉大间隔。我习惯留一个调试接口,可以在运行时动态调整扫描参数。

4.4.2 扫描类型

  • 被动扫描(0x00):只接收,不发 SCAN_REQ。
  • 主动扫描(0x01):接收后发 SCAN_REQ,等待 SCAN_RSP。

这个参数在 HCI 命令 LE Set Scan Parameters 中设置。嗯,别搞反了。

4.4.3 扫描过滤策略

BLE 支持两种过滤策略:

  • 过滤重复(Filter Duplicates):如果同一个设备地址的广播包重复出现,是否上报给主机。开启后可以减轻主机处理负担。
  • 白名单过滤(White List):只扫描白名单中的设备。适合已知设备场景。
注意:开启过滤重复后,如果广播设备更新了广播数据(比如电量变化),扫描端可能不会收到新数据。我遇到过用户抱怨设备状态不更新,查了半天发现是过滤重复导致的。

4.5 扫描状态机:从开始到结束

扫描不是一个瞬间动作,而是一个状态机。我画了一张图,帮你理清流程。

BLE 扫描状态机 BLE 扫描状态机 空闲 扫描初始化 扫描进行中 收到广播包 发送 SCAN_REQ HCI 扫描使能 参数配置完成 广播事件 主动扫描 等待下一个扫描窗口 等待下一个扫描窗口 HCI 扫描禁用 说明:主动扫描在收到广播包后多一步 SCAN_REQ/SCAN_RSP 交互,被动扫描则直接回到等待状态

4.6 实际项目中的参数选择

我整理了一个参数选择指南,帮你快速决策。

场景 扫描类型 窗口(ms) 间隔(ms) 过滤重复
信标扫描 被动 10 100 开启
设备发现 主动 10 30 关闭
低功耗传感器 被动 5 200 开启
高密度环境 主动 5 50 开启
核心原则:扫描窗口至少要覆盖一个广播事件(约 3 个广播包的发送时间,约 3-5 ms)。窗口太短,可能错过整个广播事件。我习惯设 10 ms 起步。

4.7 常见问题与调试技巧

  • 扫描不到设备:检查扫描窗口是否足够大,广播信道是否匹配(默认三个信道都要扫)。
  • 功耗过高:拉大扫描间隔,开启过滤重复,使用被动扫描。
  • 丢包严重:减少扫描窗口,避免连续扫描,降低碰撞概率。
  • 数据不更新:检查过滤重复是否开启,广播设备是否更新了数据。
调试工具:我习惯用抓空口日志的工具(如 Ellisys、Frontline)观察 SCAN_REQ 和 SCAN_RSP 的交互。如果看不到 SCAN_RSP,要么广播设备不支持,要么信道冲突。

扫描机制,说白了就是平衡发现速度、数据完整性和功耗。没有万能配置,只有最适合你场景的参数。嗯,多试几次,你就能找到感觉。