广播与扫描:BLE设备如何“打招呼”
咱们做蓝牙开发,第一步要搞明白的就是广播和扫描。说白了,这就是设备之间互相“打招呼”的过程。一个设备在喊:“我在这儿,我叫XX,我能干这些事!”另一个设备在听:“嗯,我看看周围都有谁。”
这个过程看似简单,但里面门道不少。我刚开始做BLE项目时,就因为在广播参数上没调好,导致设备死活连不上,折腾了两天。后来才发现,是广播间隔设得太短,把扫描设备的缓冲区撑爆了。嗯,咱们今天就把这块彻底讲透。
广播信道与数据信道
BLE在2.4GHz频段工作,一共划分了40个信道,编号从0到39。但有意思的是,这40个信道被分成了两类:广播信道和数据信道。
| 信道类型 | 信道编号 | 用途 |
|---|---|---|
| 广播信道 | 37、38、39 | 设备发现、广播数据、建立连接 |
| 数据信道 | 0-36(共37个) | 连接后的数据传输 |
为什么只选3个信道做广播?你想想看,2.4GHz频段上还有Wi-Fi、微波炉这些干扰源。如果只用1个信道,万一被Wi-Fi占了,设备就找不到了。所以BLE选了3个分散的信道——37(2402MHz)、38(2426MHz)、39(2480MHz)——它们巧妙地避开了Wi-Fi常用的1、6、11信道。
关键点:广播信道只用于广播和连接请求。一旦连接建立,双方就跳到数据信道上通信。数据信道有37个,支持跳频,抗干扰能力很强。
我在项目中遇到过一个问题:客户说他们的设备在办公室环境里经常搜不到。我排查后发现,是广播信道38正好被隔壁的Wi-Fi热点覆盖了。解决方案很简单——调整广播信道的使用策略,或者干脆让设备在3个信道上轮流广播。BLE协议栈默认就是这么干的,但有些低成本的芯片实现得不够好。
广播PDU类型
广播包的核心是PDU(Protocol Data Unit,协议数据单元)。它就像一封信,信封上写着“这是谁发的、发给谁、什么类型”。PDU类型决定了这封信的用途。
咱们先看最常见的几种:
- ADV_IND:可连接的非定向广播。这是最常用的类型。设备在广播,任何扫描设备都能收到,并且可以发起连接。我90%的项目都用这个。
- ADV_DIRECT_IND:定向广播。这封信是写给特定设备的,只有目标设备能处理。优点是连接速度快,缺点是其他设备不知道它的存在。
- ADV_NONCONN_IND:不可连接的非定向广播。只广播数据,不允许连接。常用于信标(Beacon)场景,比如室内定位。
- ADV_SCAN_IND:可扫描的非定向广播。允许扫描请求,但不允许连接。这种类型比较少见,我几乎没用过。
| PDU类型 | 可连接 | 可扫描 | 典型场景 |
|---|---|---|---|
| ADV_IND | 是 | 是 | 通用设备发现、连接 |
| ADV_DIRECT_IND | 是 | 否 | 快速重连、已知设备 |
| ADV_NONCONN_IND | 否 | 否 | 信标、广播数据 |
| ADV_SCAN_IND | 否 | 是 | 需要额外扫描响应的广播 |
我的建议:如果你不确定用哪种,就用ADV_IND。它最通用,兼容性最好。定向广播虽然快,但有个坑——如果目标设备不在范围内,广播设备会一直发,直到超时,白白浪费电。
扫描请求与响应
扫描设备收到广播包后,如果觉得“这个设备有点意思”,可以主动发一个扫描请求(SCAN_REQ)。广播设备收到后,会回复一个扫描响应(SCAN_RSP)。
为什么要多此一举?因为广播包的数据长度有限制——最多31字节。有时候31字节不够用,比如你要广播设备名称、服务UUID、还有自定义数据。这时候就可以把一部分数据放在扫描响应里。
我曾经做过一个温湿度传感器项目,广播包里只放了设备ID和温湿度数据,设备名称放在扫描响应里。这样扫描设备可以先快速判断“这是不是我关心的设备”,再决定要不要发扫描请求获取更多信息。既省电又高效。
注意:扫描请求和响应是在广播信道上完成的,不是数据信道。所以它们也会占用广播资源。如果周围设备太多,频繁的扫描请求会导致广播信道拥塞。我曾经在一个展会上遇到过——几十个设备同时广播,扫描设备根本处理不过来,丢包率超过50%。
主动扫描与被动扫描
扫描设备有两种工作模式:主动扫描和被动扫描。区别很简单——主动扫描会发扫描请求,被动扫描不会。
- 被动扫描:只听不说。扫描设备只接收广播包,不发送任何数据。优点是省电、不干扰信道。缺点是只能拿到广播包里的31字节数据。
- 主动扫描:听完广播后,主动发扫描请求,获取扫描响应。优点是能拿到更多数据。缺点是费电、增加信道负载。
你可能会问:那我该用哪种?
我的经验是:移动端(手机)用主动扫描,因为手机电量相对充足,而且需要获取完整的设备信息。嵌入式设备用被动扫描,尤其是电池供电的传感器节点,能省一点是一点。
我记得有一次帮客户优化一个门锁项目。门锁作为扫描设备,每隔几秒扫描一次周围的手机。一开始用的主动扫描,结果电池撑不到一个月。改成被动扫描后,只解析广播包里的设备ID,电池续航直接翻倍。嗯,有时候少做比多做更有效。
广播与扫描的完整流程
咱们用一张图把整个过程串起来:
这张图把整个流程说清楚了。被动扫描只收广播包,主动扫描会多两步交互。如果需要连接,扫描设备会发CONNECT_REQ,然后双方跳到数据信道上通信。
避坑指南
最后,分享几个我踩过的坑:
- 广播间隔别设太短:20ms的广播间隔虽然能让设备快速被发现,但会严重挤占信道。我建议非紧急场景用100ms以上。
- 注意广播数据长度:广播包和扫描响应各31字节,加起来62字节。别超了,超了协议栈会静默丢弃。
- 定向广播的坑:ADV_DIRECT_IND只能持续1.28秒(高占空比模式)或更短。如果目标设备没回应,广播设备就沉默了。你得自己实现重试逻辑。
- 扫描窗口和扫描间隔:扫描设备也有参数。扫描窗口越大,越容易收到广播,但越费电。我一般设扫描窗口=扫描间隔的一半,比如30ms窗口、60ms间隔。
一个小技巧:调试广播和扫描时,用抓包工具(比如Ellisys或nRF Sniffer)看看实际空中的报文。很多时候,你以为设备在广播,其实它根本没发出来。亲眼看到报文,比看代码日志靠谱得多。
广播和扫描是BLE的基石。搞懂了这部分,后面的连接、数据传输、功耗优化才能顺理成章。嗯,今天就到这儿,咱们下个章节见。