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、厂商自定义数据……这时候就需要扫描请求与响应了。
流程是这样的:
- 广播者发一个ADV_IND或ADV_SCAN_IND。
- 扫描者收到后,发一个SCAN_REQ(扫描请求)。
- 广播者收到SCAN_REQ后,回复一个SCAN_RSP(扫描响应)。
SCAN_RSP也是最多31字节。所以广播数据 + 扫描响应数据,总共可以传62字节。够用了吧?
注意:SCAN_REQ和SCAN_RSP都是在广播信道上传输的,不是数据信道。而且SCAN_REQ里包含了扫描者的设备地址,所以广播者是知道谁在扫它的。我曾经遇到过一个客户,担心隐私泄露,要求设备不响应任何扫描请求。嗯,那就只能用ADV_NONCONN_IND了。
3.4 主动扫描与被动扫描
这两个概念,我当年也迷糊过一阵子。其实说白了很简单:
- 被动扫描:扫描者只监听广播包,不发任何请求。收到什么算什么。
- 主动扫描:扫描者除了监听,还会主动发SCAN_REQ,去要更多的数据。
你想想看,被动扫描就像你在路边站着,听别人喊话。主动扫描呢,是你听到有人喊,你跑过去问“你叫什么名字?你干啥的?”
在实际项目中,我一般这样选:
- 如果只是要发现设备的存在,被动扫描就够了,省电。
- 如果需要获取设备的详细信息(比如名称、服务列表),那就用主动扫描。
但要注意:主动扫描会占用更多的空中时间,也更容易产生冲突。我在一个高密度设备场景(比如展会里几百个信标)里吃过亏——主动扫描太多,广播信道直接堵死了。后来改成被动扫描 + 延长扫描窗口,才稳定下来。
3.5 广播与扫描的完整流程
咱们用一张图把整个流程串起来。我特意画了个SVG,方便你理解。
这张图展示的是主动扫描的完整流程。如果是被动扫描,那就只有第一步——广播者发ADV_IND,扫描者默默收,后面两步都没有。
3.6 避坑指南
最后,我把自己踩过的几个坑分享给你:
- 广播间隔别设太短:我曾经为了“响应快”,把广播间隔设成20ms。结果三个广播信道轮流发,空中全是广播包,数据信道反而没机会用了。后来改成100ms,功耗降了,稳定性反而上去了。
- 扫描窗口和扫描间隔要匹配:扫描窗口设得比间隔还大?那等于一直在扫,功耗高得吓人。我一般建议扫描窗口不超过扫描间隔的50%。
- 注意广播数据的长度:31字节看着不多,但如果你塞满了,广播间隔又短,信道占用率会飙升。我习惯把核心信息放广播里,次要信息放扫描响应里。
- 主动扫描不是万能的:有些设备(比如苹果的某些外设)对SCAN_REQ的响应策略很特殊,不一定每次都回SCAN_RSP。别指望主动扫描一定能拿到完整数据。
嗯,广播和扫描这部分,说白了就是“怎么让别人找到你”和“怎么找到别人”。看似简单,但实际项目中坑不少。多动手调一调,比看十遍书都管用。
公众号:蓝海资料掘金营,微信deep3321