2. 广播与扫描原理:广播者与观察者角色、广播信道与数据信道、广播PDU结构解析
这一节,我们聊聊BLE最核心的机制——广播与扫描。说白了,就是设备之间怎么“打招呼”和“听招呼”。
我刚接触BLE时,总觉得广播就是设备不停地往外喊“我在这儿”。后来踩过坑才明白,这里面门道不少。广播信道怎么选、数据怎么打包、扫描窗口设多大,都直接影响功耗和连接成功率。
2.1 广播者与观察者:两种基础角色
BLE设备在广播阶段,只有两种角色:
- 广播者(Advertiser):主动往外发数据包的角色。比如一个温度传感器,每隔100ms广播一次当前温度。
- 观察者(Observer):被动接收广播包的角色。比如手机上的蓝牙扫描器,只收不发。
我习惯把广播者比作“喊话的人”,观察者比作“听广播的人”。两者之间没有连接,只是单向通信。
关键点:广播者不需要知道谁在听,观察者也不需要回应。这种“无连接”模式,是BLE低功耗的基础。
在实际项目中,我遇到过一个问题:设备作为广播者,广播间隔设得太短(比如20ms),结果电池两天就耗光了。后来我把间隔调到200ms,功耗降了80%。你想想看,广播间隔直接影响功耗,这个参数必须根据应用场景来调。
2.2 广播信道与数据信道:为什么是37个?
BLE在2.4GHz频段工作,一共划分了40个信道,编号0到39。但这40个信道不是随便用的:
| 信道类型 | 信道编号 | 数量 | 用途 |
|---|---|---|---|
| 广播信道 | 37、38、39 | 3个 | 设备发现、广播数据、连接请求 |
| 数据信道 | 0~36(共37个) | 37个 | 连接后的数据传输 |
为什么广播信道只有3个?我刚开始也觉得奇怪。后来查了资料才明白:这3个信道特意避开了Wi-Fi常用的1、6、11信道,减少了干扰。说白了,就是“挑不堵的路走”。
避坑指南:我曾经在一个Wi-Fi密集的办公环境做测试,广播信道37(2402MHz)正好和Wi-Fi信道1重叠,导致设备扫描成功率只有60%。后来我强制设备只在信道38和39上广播,成功率提升到95%。
数据信道有37个,用于连接后的数据传输。这里有个关键机制叫跳频(Frequency Hopping):连接建立后,两个设备会按照一个伪随机序列,在37个数据信道之间跳来跳去。这样做的好处是抗干扰能力强——就算某个信道被Wi-Fi占了,跳过去就是了。
2.3 广播PDU结构解析:数据包长什么样?
广播包(Advertising PDU)是BLE通信的最小数据单元。我习惯把它拆成三部分来看:
广播PDU结构:
+----------------+----------------+------------------+
| 前导码 (1字节) | 接入地址 (4字节)| PDU (2~39字节) |
+----------------+----------------+------------------+
↓
PDU内部结构:
+--------+--------+--------+
| 报头 | 载荷 | CRC |
| (2字节)| (0~37B)| (3字节)|
+--------+--------+--------+
嗯,这里要注意:前导码和接入地址是物理层固定的,我们开发者主要关心PDU部分。
2.3.1 报头(Header)
报头占2字节,包含以下关键字段:
| 字段 | 位数 | 说明 |
|---|---|---|
| PDU类型 | 4位 | 指示广播包类型(可连接、可扫描、不可连接等) |
| RFU | 2位 | 保留位,必须为0 |
| 信道选择 | 1位 | 指示是否使用信道选择算法#2 |
| TxAdd | 1位 | 发送地址类型(0=公共地址,1=随机地址) |
| RxAdd | 1位 | 接收地址类型(仅用于扫描响应) |
| 长度 | 6位 | 载荷长度(0~37字节) |
| RFU | 1位 | 保留位 |
PDU类型有几种常见的:
- ADV_IND:可连接、可扫描的广播包。最常用,比如手机广播。
- ADV_DIRECT_IND:定向广播包,指定给某个设备。连接速度快,但功耗高。
- ADV_NONCONN_IND:不可连接的广播包。适合只发数据、不建立连接的场景,比如信标。
- ADV_SCAN_IND:可扫描但不可连接的广播包。观察者可以请求更多数据。
- SCAN_REQ / SCAN_RSP:扫描请求和扫描响应,用于主动获取更多广播数据。
注意:ADV_DIRECT_IND包不能携带用户数据,只能携带目标设备的地址。我见过有人想在这个包里塞数据,结果设备根本不广播——白费功夫。
2.3.2 载荷(Payload)
载荷部分最多37字节,包含广播数据。数据格式是AD Structure(Advertising Data Structure):
AD Structure格式:
+--------+--------+--------+--------+
| 长度 | AD类型 | 数据 | ... |
| (1字节)| (1字节)| (N字节) | |
+--------+--------+--------+--------+
示例:广播设备名称
长度: 0x05 (表示后面有5字节)
AD类型: 0x09 (完整设备名称)
数据: "Beacon" (5字节)
常见的AD类型有:
- 0x01:Flags(标志位,指示设备能力)
- 0x08 / 0x09:设备名称(缩短/完整)
- 0x0A:Tx Power Level(发射功率,用于距离估算)
- 0xFF:Manufacturer Specific Data(厂商自定义数据)
我个人习惯把Flags放在第一个AD Structure,因为很多扫描器会先解析它。Flags字段告诉观察者:这个设备是否支持经典蓝牙、是否处于可连接状态等。
2.3.3 CRC(循环冗余校验)
CRC占3字节,用于校验PDU的完整性。如果CRC校验失败,接收方会直接丢弃这个包。嗯,这里不用太纠结算法细节,知道它是“防错码”就行。
2.4 广播与扫描的交互流程
广播者和观察者之间是怎么交互的?我画了一张图来说明:
流程是这样的:
- 广播者在3个广播信道(37、38、39)上轮流发送广播包。每个信道上发一次,然后切换到下一个信道。
- 观察者以固定的扫描窗口和扫描间隔在3个信道上监听。
- 如果广播包恰好落在观察者的扫描窗口内,观察者就能收到这个包。
- 观察者可以发送扫描请求(SCAN_REQ),广播者回复扫描响应(SCAN_RSP),获取更多数据。
经验之谈:我曾经调试一个门禁系统,发现手机扫描不到门禁的广播。后来用抓包工具一看,门禁的广播间隔是100ms,但手机的扫描窗口只有50ms,扫描间隔是200ms。也就是说,手机每200ms才打开50ms的窗口,漏掉了不少广播包。我把扫描窗口调到100ms,问题就解决了。
2.5 广播参数与功耗权衡
广播参数直接影响功耗和发现速度。我整理了一个对比表:
| 参数 | 推荐值 | 功耗影响 | 发现速度 |
|---|---|---|---|
| 广播间隔 | 100ms~1s | 间隔越小,功耗越高 | 间隔越小,发现越快 |
| 广播时长 | 30~60s(可连接) | 时长越长,功耗越高 | 时长越长,发现概率越大 |
| 扫描窗口 | 30~100ms | 窗口越大,功耗越高 | 窗口越大,接收概率越大 |
| 扫描间隔 | 100ms~1s | 间隔越小,功耗越高 | 间隔越小,发现越快 |
嗯,这里有个经验公式:广播间隔 × 扫描窗口 / 扫描间隔 决定了发现概率。举个例子,广播间隔100ms,扫描窗口50ms,扫描间隔200ms,那么发现概率大约是25%。如果扫描窗口调到100ms,发现概率就升到50%。
避坑指南:我曾经在一个项目中,把广播间隔设为20ms,结果设备发热严重,电池半天就没电了。后来查了BLE规范才发现,广播间隔最小是20ms,但实际应用中建议不低于100ms。除非你特别需要快速发现,否则别用这么小的间隔。
2.6 小结
这一节我们聊了广播与扫描的核心原理:
- 广播者在3个广播信道上发数据,观察者在扫描窗口内收数据。
- 广播PDU由报头、载荷、CRC三部分组成,载荷最多37字节。
- 广播参数(间隔、窗口、时长)直接影响功耗和发现速度,需要根据场景权衡。