广播与扫描机制:BLE设备如何“打招呼”
广播和扫描,是BLE通信的第一步。说白了,就是设备怎么让别人发现自己,以及怎么发现别人。
我刚接触BLE时,总觉得广播就是简单发个包。后来做多设备连接项目,才发现广播类型选错、数据格式写错,设备之间根本“聊”不起来。今天咱们就把这块彻底理清楚。
广播类型:四种“打招呼”方式
BLE广播有四种类型。每种类型决定了设备能不能被连接、能不能响应扫描。我习惯把它们分成两组来看:
| 广播类型 | 可连接 | 可扫描 | 典型场景 |
|---|---|---|---|
| 可连接不可扫描广播 | ✅ | ❌ | 传统蓝牙配对 |
| 可连接可扫描广播 | ✅ | ✅ | BLE外设(心率计、温度计) |
| 不可连接可扫描广播 | ❌ | ✅ | 信标(Beacon) |
| 不可连接不可扫描广播 | ❌ | ❌ | 单向广播(如广告推送) |
关键点:可连接广播必须同时支持扫描响应。这是BLE协议规定的,不是你想改就能改。
我在项目中遇到过一个问题:设备明明在广播,手机就是扫不到。查了半天,发现广播类型配成了“不可连接不可扫描”。嗯,这种低级错误,犯过一次就记住了。
广播数据格式:AD Structure 详解
广播数据不是随便塞的。它遵循AD Structure格式。每个AD Structure由三部分组成:
- Length(1字节):表示AD Type + AD Data的总长度
- AD Type(1字节):标识数据类型(如设备名、UUID、Tx Power等)
- AD Data(N字节):具体数据内容
举个例子,一个包含设备名和UUID的广播包,结构是这样的:
// 广播数据示例(十六进制)
// 设备名:"BLE_Dev"
// 服务UUID:0x180D(心率服务)
0x08 0x09 42 4C 45 5F 44 65 76 // AD Structure 1: 设备名
↑ ↑ ↑
Length Type Data("BLE_Dev")
0x03 0x02 0x0D 0x18 // AD Structure 2: 16位UUID
↑ ↑ ↑
Length Type Data(0x180D小端序)
小技巧:我习惯用nRF Connect或LightBlue抓包看广播数据。看到原始十六进制,对照AD Type表,很快就能定位问题。
常见的AD Type值:
| AD Type值 | 含义 | 数据长度 |
|---|---|---|
| 0x01 | Flags(广播标志) | 1字节 |
| 0x02/0x03 | 16位服务UUID(完整/部分) | 2字节 |
| 0x06/0x07 | 128位服务UUID(完整/部分) | 16字节 |
| 0x08/0x09 | 设备名(缩短/完整) | 可变 |
| 0x0A | Tx Power Level | 1字节 |
| 0xFF | 制造商自定义数据 | 可变 |
注意:广播包最大31字节。你塞的数据越多,留给扫描响应的空间就越少。我一般把核心信息放广播包,扩展信息放扫描响应。
扫描请求与扫描响应:一问一答
扫描请求(Scan Request)和扫描响应(Scan Response),是BLE里一个很巧妙的设计。
你想想看:广播包只有31字节,不够用怎么办?扫描响应就是解决方案。
- 扫描请求:扫描设备发给广播设备,说“嘿,你还有更多信息吗?”
- 扫描响应:广播设备回复,把额外数据发过去
这个过程只在主动扫描模式下发生。被动扫描不会发扫描请求,也就拿不到扫描响应数据。
我曾经调试一个多设备连接项目,发现某些设备扫描响应数据总是不完整。后来定位到是广播设备处理扫描请求太慢,导致响应超时。解决办法是优化广播间隔,给扫描响应留足时间窗口。
主动扫描 vs 被动扫描
这两种扫描方式,区别在于扫描设备是否主动“搭话”。
| 特性 | 主动扫描 | 被动扫描 |
|---|---|---|
| 发送扫描请求 | ✅ 是 | ❌ 否 |
| 获取扫描响应 | ✅ 可以 | ❌ 不能 |
| 功耗 | 较高 | 较低 |
| 数据完整性 | 更完整 | 仅广播包 |
| 典型场景 | 设备发现、连接建立 | 信标检测、低功耗监听 |
说白了,主动扫描就是“主动问”,被动扫描就是“只听不说”。
我建议:做多设备连接时,用主动扫描。因为你需要完整的设备信息(设备名、服务UUID等)来做连接决策。如果只是做信标检测,被动扫描就够了,省电。
知识体系总览
下面这张图,把广播与扫描的核心逻辑串起来了。我画图时习惯把流程和关键参数都标清楚,方便你对照理解。
我的经验:画这种流程图时,把广播类型和扫描方式放在一起看,能帮你快速理解设备之间的交互逻辑。我每次做新项目,都会先画一张类似的图,确认流程没问题再写代码。
避坑指南
做广播和扫描开发,有几个坑我踩过,分享给你:
- 广播间隔别设太短:我曾经为了快速发现设备,把广播间隔设到20ms。结果功耗飙升,设备发热。后来改成100ms,效果一样好。
- 扫描窗口和扫描间隔要匹配:扫描窗口设得比扫描间隔还大,会导致扫描周期错乱。我一般设扫描窗口为扫描间隔的50%。
- 广播数据别超31字节:这个限制是硬性的。超了协议栈会直接报错,或者静默截断。我习惯先算好长度再填充。
- 扫描响应数据也要算在31字节内:广播包和扫描响应各自独立,但每个都是31字节上限。别搞混了。
重要提醒:广播和扫描的参数配置,直接影响连接成功率。我建议你在开发初期就用抓包工具验证广播数据格式,别等到联调时才发现问题。
好了,广播与扫描机制就讲到这里。记住:广播是设备“说话”的方式,扫描是设备“听”的方式。选对类型、配好数据、理解流程,多设备连接的第一步就走稳了。