4、广播数据包结构:AD Structure、AD Type、完整广播数据示例解析

广播数据包,说白了就是设备在喊:「我在这儿,我叫啥,我能干啥」。你想想看,两个 BLE 设备要建立连接,总得先打个招呼吧?这个打招呼的过程,就是通过广播数据包完成的。

我刚开始做 BLE 开发时,总觉得广播数据就是随便塞几个字节就行。直到有一次,产品在手机端死活搜不到服务——后来一查,是广播包里的 AD Type 配错了。嗯,从那以后我再也不敢小看这个结构了。

4.1 AD Structure:广播数据的最小单元

广播数据包的结构其实很简单。它由若干个 AD Structure 拼接而成。每个 AD Structure 遵循一个固定的格式:

字段 长度 说明
Length 1 字节 表示后续 Data 的长度(不包含自身)
AD Type 1 字节 标识数据的类型(如设备名、UUID 等)
AD Data Length - 1 字节 真正的数据内容

举个例子:0x02 0x01 0x06 就是一个完整的 AD Structure。

  • 0x02 表示后面有 2 个字节的数据
  • 0x01 是 AD Type,代表「Flags」
  • 0x06 是 Flags 的值,表示 LE General Discoverable Mode + BR/EDR Not Supported

我个人习惯把 AD Structure 想象成「积木块」。每个积木块都告诉接收方一条信息。多个积木块拼在一起,就构成了完整的广播数据。

4.2 AD Type:这些类型你必须掌握

AD Type 是广播数据包的灵魂。BLE 协议栈定义了非常多的类型,但实际开发中常用的就那么几个。我整理了一张表,你可以直接拿来参考:

AD Type 值 名称 用途 长度说明
0x01 Flags 广播模式、BR/EDR 支持情况 固定 1 字节
0x02 / 0x03 16-bit Service UUID (不完全/完全) 声明支持的 16 位服务 UUID 每个 UUID 占 2 字节
0x06 / 0x07 128-bit Service UUID (不完全/完全) 声明支持的 128 位服务 UUID 每个 UUID 占 16 字节
0x08 / 0x09 Local Name (缩短/完整) 设备名称 可变长度
0x0A Tx Power Level 发射功率(用于 RSSI 校准) 固定 1 字节(有符号)
0xFF Manufacturer Specific Data 厂商自定义数据 可变长度
我的经验:Flags 字段几乎是必加的。如果不加,有些手机扫描端会无法正确识别设备的广播模式。我曾经踩过这个坑——设备广播了,但 iPhone 就是扫不到,最后发现是 Flags 没配。

4.3 广播数据包的整体结构

一个完整的 BLE 广播信道 PDU,其实包含两个部分:报头 和 有效载荷。有效载荷里又分为 AdvA(广播设备地址)和 AdvData(广播数据)。

我们关心的广播数据,就放在 AdvData 里。它的最大长度是 31 字节。这 31 个字节要容纳所有的 AD Structure,所以你得精打细算。

关键约束:广播数据总长度不能超过 31 字节。如果信息太多,可以考虑使用 Scan Response(扫描响应)来补充。

我画了一张图,帮你理清整个广播数据包的层级关系:

BLE 广播数据包结构 广播信道 PDU(37 字节) 报头(2 字节) + 有效载荷(最多 35 字节) 有效载荷(35 字节) AdvA(6 字节) + AdvData(最多 29 字节) AdvData(最多 31 字节) 由多个 AD Structure 拼接而成 AD Structure 1 Length + Type + Data AD Structure 2 Length + Type + Data AD Structure 3 Length + Type + Data ... 直到 31 字节

4.4 完整广播数据示例解析

光讲理论太枯燥。我们直接看一个真实的广播数据包。假设我抓到了一个设备的广播数据,十六进制如下:

02 01 06 03 02 0A 18 05 09 42 4C 45 5F 44 65 76

我们来逐字节拆解:

原始数据 Length AD Type AD Data 含义
02 01 06 0x02 0x01 (Flags) 0x06 LE General Discoverable, BR/EDR Not Supported
03 02 0A 18 0x03 0x02 (16-bit UUID 不完全列表) 0x0A18 包含服务 UUID = 0x180A (Device Information Service)
05 09 42 4C 45 5F 44 65 76 0x05 0x09 (完整设备名) 0x42 0x4C 0x45 0x5F 0x44 0x65 0x76 设备名称为 "BLE_Dev"

你看,这个设备只用了 12 个字节就完成了广播。它告诉扫描者:

  • 我支持 BLE,不支持经典蓝牙
  • 我有一个 Device Information Service
  • 我的名字叫 BLE_Dev
注意:我曾经遇到过一个问题——设备名太长,超过了 31 字节的广播数据限制。结果手机端只能看到截断的名字。后来我改用缩短的设备名(AD Type 0x08),把完整名字放在 Scan Response 里,问题就解决了。

4.5 广播数据的填充顺序建议

虽然协议没有强制规定 AD Structure 的顺序,但我个人建议按照以下优先级来填充:

  1. Flags(0x01) — 放在最前面,扫描器最先解析
  2. UUID 列表 — 让扫描器快速判断是否是自己关心的设备
  3. Local Name — 方便用户识别设备
  4. Tx Power Level — 用于距离估算
  5. Manufacturer Specific Data — 自定义数据,放在最后

你想想看,扫描器收到广播包后,通常是按顺序解析的。把最重要的信息放在前面,能提高解析效率。我在做低功耗优化时,就利用这个顺序,让扫描端在解析到不关心的 UUID 后直接丢弃,省电效果很明显。

4.6 一个完整的广播数据构造示例

假设我们要构造一个广播数据包,包含以下信息:

  • Flags: LE General Discoverable, BR/EDR Not Supported
  • 完整 16-bit UUID: 0x180F (Battery Service)
  • 完整设备名: "MySensor"
  • Tx Power Level: -4 dBm

对应的十六进制数据如下:

02 01 06          // Flags: Length=2, Type=0x01, Data=0x06
03 02 0F 18       // UUID: Length=3, Type=0x02, Data=0x180F
09 09 4D 79 53 65 6E 73 6F 72  // Name: Length=9, Type=0x09, Data="MySensor"
02 0A FC          // Tx Power: Length=2, Type=0x0A, Data=0xFC (-4 dBm 的补码表示)

总长度 = 2 + 3 + 9 + 2 = 16 字节,远小于 31 字节的限制。还有空间可以加厂商自定义数据。

小技巧:Tx Power Level 的值是有符号的。0xFC 换算成十进制是 -4。我习惯用这个公式:实际功率(dBm) = 有符号字节值。比如 0xFC = -4 dBm,0x04 = +4 dBm。