3. BLE广播与扫描:广播信道与数据信道、广播PDU格式、扫描请求与响应、主动扫描与被动扫描

好,咱们今天聊聊BLE里最基础、也最容易踩坑的部分——广播与扫描。

你想想看,两个蓝牙设备要通信,第一步是什么?不是握手,不是配对,而是“发现对方”。怎么发现?靠的就是广播。我经常跟团队里新人说:广播是BLE的敲门砖,门都敲不开,后面全是白搭

3.1 广播信道与数据信道

BLE在2.4GHz频段里干活,一共划了40个信道,编号0到39。但有意思的是,这40个信道不是一视同仁的。

广播信道有3个:37、38、39。对应的中心频率分别是2402MHz、2426MHz、2480MHz。

数据信道有37个:0到36。

为什么广播信道只有3个?我刚开始学的时候也纳闷。后来做项目才明白——这是为了避开Wi-Fi的干扰。Wi-Fi常用的信道是1、6、11,而BLE的37、38、39正好卡在Wi-Fi信道的间隙里。说白了,就是故意躲着Wi-Fi走。

核心要点:广播只在37/38/39三个信道上轮流发,数据通信则在37个数据信道上跳频。

我记得有一次做室内定位项目,设备老是连不上。排查了半天,发现是Wi-Fi路由器正好把信道6开满了,而BLE的广播信道38被压得死死的。后来我把广播信道配置改成只用37和39,问题就解决了。嗯,这种坑,你不实际碰一回,光看书真想不到。

3.2 广播PDU格式

广播包长什么样?咱们直接看结构。

广播PDU格式(不含前导码和接入地址):
+--------+--------+------------------+
| 报头   | 长度   | 有效载荷         |
| 2字节  | 1字节  | 0~37字节         |
+--------+--------+------------------+

报头里藏着不少信息:PDU类型、发送地址类型、接收地址类型、是否包含完整的名称等等。我个人习惯把报头里的PDU类型记牢,因为调试时经常要看。

常见的PDU类型:

  • ADV_IND:可连接的非定向广播。最常用,手机扫到的就是它。
  • ADV_DIRECT_IND:定向广播,指定给某个设备。省电,但只有特定设备能收到。
  • ADV_NONCONN_IND:不可连接的非定向广播。适合只发数据、不建立连接的场景,比如信标。
  • ADV_SCAN_IND:可扫描的非定向广播。允许扫描请求,但不允许连接。
  • SCAN_REQ / SCAN_RSP:扫描请求和扫描响应,后面细说。

小技巧:如果你在做低功耗设备,广播间隔设得越大越省电。但代价是别人发现你的时间变长。我一般建议广播间隔在100ms~1s之间调,具体看应用场景。

3.3 扫描请求与响应

广播包能带的数据有限,最多31字节。那如果我想多传点信息怎么办?比如设备名称、服务UUID、厂商自定义数据……这时候就需要扫描请求与响应了。

流程是这样的:

  1. 广播者发一个ADV_IND或ADV_SCAN_IND。
  2. 扫描者收到后,发一个SCAN_REQ(扫描请求)。
  3. 广播者收到SCAN_REQ后,回复一个SCAN_RSP(扫描响应)。

SCAN_RSP也是最多31字节。所以广播数据 + 扫描响应数据,总共可以传62字节。够用了吧?

注意:SCAN_REQ和SCAN_RSP都是在广播信道上传输的,不是数据信道。而且SCAN_REQ里包含了扫描者的设备地址,所以广播者是知道谁在扫它的。我曾经遇到过一个客户,担心隐私泄露,要求设备不响应任何扫描请求。嗯,那就只能用ADV_NONCONN_IND了。

3.4 主动扫描与被动扫描

这两个概念,我当年也迷糊过一阵子。其实说白了很简单:

  • 被动扫描:扫描者只监听广播包,不发任何请求。收到什么算什么。
  • 主动扫描:扫描者除了监听,还会主动发SCAN_REQ,去要更多的数据。

你想想看,被动扫描就像你在路边站着,听别人喊话。主动扫描呢,是你听到有人喊,你跑过去问“你叫什么名字?你干啥的?”

在实际项目中,我一般这样选:

  • 如果只是要发现设备的存在,被动扫描就够了,省电。
  • 如果需要获取设备的详细信息(比如名称、服务列表),那就用主动扫描。

但要注意:主动扫描会占用更多的空中时间,也更容易产生冲突。我在一个高密度设备场景(比如展会里几百个信标)里吃过亏——主动扫描太多,广播信道直接堵死了。后来改成被动扫描 + 延长扫描窗口,才稳定下来。

3.5 广播与扫描的完整流程

咱们用一张图把整个流程串起来。我特意画了个SVG,方便你理解。

BLE广播与扫描流程 广播者 (Advertiser) 扫描者 (Scanner) 发送 ADV_IND 发送 SCAN_REQ 发送 SCAN_RSP 广播事件 扫描请求

这张图展示的是主动扫描的完整流程。如果是被动扫描,那就只有第一步——广播者发ADV_IND,扫描者默默收,后面两步都没有。

3.6 避坑指南

最后,我把自己踩过的几个坑分享给你:

  • 广播间隔别设太短:我曾经为了“响应快”,把广播间隔设成20ms。结果三个广播信道轮流发,空中全是广播包,数据信道反而没机会用了。后来改成100ms,功耗降了,稳定性反而上去了。
  • 扫描窗口和扫描间隔要匹配:扫描窗口设得比间隔还大?那等于一直在扫,功耗高得吓人。我一般建议扫描窗口不超过扫描间隔的50%。
  • 注意广播数据的长度:31字节看着不多,但如果你塞满了,广播间隔又短,信道占用率会飙升。我习惯把核心信息放广播里,次要信息放扫描响应里。
  • 主动扫描不是万能的:有些设备(比如苹果的某些外设)对SCAN_REQ的响应策略很特殊,不一定每次都回SCAN_RSP。别指望主动扫描一定能拿到完整数据。

嗯,广播和扫描这部分,说白了就是“怎么让别人找到你”和“怎么找到别人”。看似简单,但实际项目中坑不少。多动手调一调,比看十遍书都管用。


公众号:蓝海资料掘金营,微信deep3321