4. 广播数据包格式:AD Structure、AD Type
广播包说白了就是一堆小数据块拼起来的。每个小数据块叫 AD Structure,它们一个接一个地塞在广播信道里。我刚开始啃协议栈的时候,总觉得这玩意儿很神秘,后来拆了几个包才发现——其实挺简单的。
4.1 AD Structure 的基本结构
每个 AD Structure 就三样东西:长度、类型、数据。你想想看,这不就跟我们写简历一样,先写标题再写内容嘛。
| 字段 | 长度 | 说明 |
|---|---|---|
| Length | 1 字节 | 表示 Type + Data 的总长度(不包含 Length 自身) |
| Type | 1 字节 | AD Type,告诉接收方这段数据是什么 |
| Data | 0~29 字节 | 具体的数据内容 |
举个例子:02 01 06 这个三字节序列,Length=02,Type=01(Flags),Data=06。嗯,这里要注意,Length 不包含自己那个字节,只算 Type 和 Data。
核心原则:广播包的总长度不能超过 31 字节(AdvData 上限)。所有 AD Structure 加起来必须 ≤ 31 字节。我见过不少新手把数据塞爆了,结果手机死活扫不到。
4.2 常见的 AD Type
协议栈里定义了上百种 AD Type,但实际项目中常用的就那么几个。我挑几个最关键的说说。
4.2.1 Flags (Type = 0x01)
Flags 几乎是每个广播包都会带的。它告诉接收方:我这个设备支持什么模式?能不能被连接?
| Bit | 含义 | 说明 |
|---|---|---|
| Bit 0 | LE Limited Discoverable Mode | 有限可发现模式(只持续一段时间) |
| Bit 1 | LE General Discoverable Mode | 通用可发现模式(一直可被发现) |
| Bit 2 | BR/EDR Not Supported | 不支持经典蓝牙(纯 BLE 设备必须置 1) |
| Bit 3 | Simultaneous LE and BR/EDR | 同时支持 BLE 和经典蓝牙 |
| Bit 4 | Simultaneous LE and BR/EDR (Host) | 主机端同时支持 |
最常见的值是 0x06(Bit 1 + Bit 2),表示通用可发现 + 纯 BLE。我习惯在调试时先确认 Flags 对不对,因为很多扫描不到的问题都是 Flags 设错了。
避坑:我曾经遇到一个案子,设备广播了但手机就是搜不到。查了半天,发现 Flags 里 Bit 2 没置 1,手机以为它是经典蓝牙设备,直接忽略了。所以纯 BLE 设备,Bit 2 必须为 1。
4.2.2 TX Power Level (Type = 0x0A)
这个字段告诉接收方:我的发射功率是多少 dBm。扫描端拿到 RSSI 后,结合这个值就能估算距离。
格式很简单:1 字节有符号整数,单位 dBm。比如 0x14 表示 +20 dBm,0xF6 表示 -10 dBm。
小技巧:如果你在做室内定位或接近检测,TX Power Level 一定要带上。没有它,RSSI 就是个相对值,没法换算成距离。我一般在广播包里固定写 0x00(0 dBm),方便调试。
4.2.3 Local Name (Type = 0x08 / 0x09)
设备名字。Type 0x08 是短名字,0x09 是完整名字。扫描端拿到这个名字,就能在界面上显示给用户看。
名字长度有限制——广播包总共才 31 字节,名字太长就塞不下了。我建议名字控制在 8 个字符以内,留点空间给其他 AD Structure。
// 示例:广播包包含名字 "MyTag"
// 0x06 0x09 4D 79 54 61 67
// Length=06, Type=0x09 (Complete Local Name), Data="MyTag"
4.2.4 UUID (Type = 0x02 / 0x03 / 0x04 / 0x05 / 0x06 / 0x07)
UUID 用来标识设备支持的服务。比如你的设备是个心率计,就广播心率服务的 UUID。扫描端看到这个 UUID,就知道该不该连你。
| Type | 含义 | UUID 长度 |
|---|---|---|
| 0x02 | Incomplete List of 16-bit UUIDs | 2 字节 |
| 0x03 | Complete List of 16-bit UUIDs | 2 字节 |
| 0x04 | Incomplete List of 32-bit UUIDs | 4 字节 |
| 0x05 | Complete List of 32-bit UUIDs | 4 字节 |
| 0x06 | Incomplete List of 128-bit UUIDs | 16 字节 |
| 0x07 | Complete List of 128-bit UUIDs | 16 字节 |
16-bit UUID 是蓝牙 SIG 定义的,比如心率服务是 0x180D。128-bit UUID 是自定义的,自己随便写。我个人建议能用 16-bit 就别用 128-bit,省空间。
4.3 广播包结构总览
下面这张图把广播包的完整结构画出来了。从 PDU 到 AD Structure,一层层拆开看,你就知道数据是怎么组织的了。
4.4 实战:拼一个完整的广播包
假设我们要做一个温度标签,名字叫 "Temp",广播温度服务的 UUID(0x1809),发射功率 0 dBm。广播包长这样:
// 广播数据 (AdvData) 共 14 字节
// Flags: 02 01 06
// TX Power: 02 0A 00
// UUID: 03 03 09 18
// Name: 05 09 54 65 6D 70
// 完整广播包:
// 02 01 06 02 0A 00 03 03 09 18 05 09 54 65 6D 70
你想想看,总共 14 字节,离 31 字节的上限还远着呢。如果以后要加自定义数据,空间完全够用。
调试建议:用 nRF Connect 或 LightBlue 抓包,直接看广播数据的十六进制。我习惯先看 Flags,再看 UUID,最后看名字。顺序对了,问题就少一半。
4.5 常见问题与避坑
- 广播包超长:31 字节是硬上限,超了设备直接不广播。我曾经被坑过一次,名字写了 20 个字符,结果广播包死活发不出来。
- AD Structure 顺序:协议没规定顺序,但业界习惯把 Flags 放第一个。扫描端解析时一般按顺序处理,所以重要的放前面。
- Type 重复:同一个 Type 可以出现多次,但接收方通常只取第一个。所以别重复放,浪费空间。
- 名字截断:如果名字太长放不下,协议允许只放短名字(Type 0x08)。扫描端看到短名字,知道不完整,但至少能显示。
嗯,广播包格式其实不复杂。你只要记住:每个 AD Structure 都是 Length + Type + Data,拼起来不超过 31 字节,就够用了。剩下的,多抓几次包自然就熟了。