4、广播PDU结构:广播信道PDU格式详解、PDU类型与Header/Payload字段
好,咱们今天来聊聊广播PDU的结构。说实话,这个知识点是BLE开发里最基础也最容易踩坑的地方。我记得刚入行那会儿,拿着抓包工具看了一整天,愣是没搞明白那些十六进制数据到底在说什么。后来才明白,PDU结构搞不清楚,后面做广播优化、做扫描响应,全都是空中楼阁。
4.1 广播信道PDU格式详解
先看整体结构。广播信道PDU,说白了就是一段固定格式的数据包。它由两部分组成:Header(头部)和Payload(有效载荷)。
嗯,这里要注意,广播信道PDU和后面要讲的数据信道PDU,结构上是有区别的。广播信道PDU的格式更简单,因为它不需要处理那么多链路层控制逻辑。
具体格式是这样的:
广播信道PDU = Header (16 bits) + Payload (可变长度)
Header固定占2个字节,Payload的长度范围是0到37个字节。为什么是37?因为广播信道PDU的最大长度是39字节,减去2字节的Header,剩下37字节给Payload。这个我在项目里验证过,确实如此。
核心要点:广播信道PDU的最大长度是39字节,这是由BLE 5.0规范定义的。超过这个长度,接收端会直接丢弃。
4.2 PDU类型详解
Header里最重要的字段就是PDU类型。它占4个bit,决定了这个广播包是干什么用的。我给大家列个表,一目了然:
| PDU类型 | 名称 | 说明 | 典型场景 |
|---|---|---|---|
| 0000 | ADV_IND | 可连接的非定向广播 | 最常见的广播类型,手机扫描设备时用的就是它 |
| 0001 | ADV_DIRECT_IND | 可连接的定向广播 | 快速连接场景,比如蓝牙耳机回连 |
| 0010 | ADV_NONCONN_IND | 不可连接的非定向广播 | 信标广播,比如iBeacon、Eddystone |
| 0011 | ADV_SCAN_IND | 可扫描的非定向广播 | 允许扫描请求,但不允许连接 |
| 0100 | ADV_EXT_IND | 扩展广播(BLE 5.0新增) | 大数据量广播,比如音频数据 |
| 0101-0111 | 保留 | 预留未来使用 | - |
| 1000 | SCAN_REQ | 扫描请求 | 主动扫描时发送 |
| 1001 | SCAN_RSP | 扫描响应 | 回复扫描请求 |
| 1010 | CONNECT_IND | 连接请求 | 发起连接时发送 |
我个人习惯把PDU类型分成两类:广播包(ADV_xxx)和响应包(SCAN_REQ、SCAN_RSP、CONNECT_IND)。广播包是设备主动发出来的,响应包是收到广播包之后回复的。
小技巧:如果你在做低功耗设备,比如温湿度传感器,建议用ADV_NONCONN_IND。因为它不需要监听扫描请求和连接请求,能省不少电。我在一个智能家居项目里就是这么干的,电池续航从3个月延长到了8个月。
4.3 Header字段深度解析
Header的16个bit,每个bit都有它的用途。咱们一个一个来看:
Header (16 bits) = PDU类型(4 bits) + RFU(1 bit) + ChSel(1 bit) + TxAdd(1 bit) + RxAdd(1 bit) + 长度(8 bits)
嗯,这里要注意,RFU是Reserved for Future Use,也就是留给未来用的。目前必须填0,否则有些老设备可能会不认。
各字段说明:
- PDU类型(4 bits):上面表格已经讲过了,决定广播包的行为。
- RFU(1 bit):保留位,填0。
- ChSel(1 bit):信道选择标志。0表示使用标准信道选择算法,1表示使用改进算法。BLE 5.0推荐用改进算法,能减少干扰。
- TxAdd(1 bit):发送地址类型。0表示公共地址,1表示随机地址。这个在隐私保护里很重要。
- RxAdd(1 bit):接收地址类型。只在定向广播和连接请求里有用。
- 长度(8 bits):Payload的字节数。范围是0到37。
避坑指南:我曾经在一个项目里,因为TxAdd填错了,导致手机死活扫描不到设备。后来查了半天才发现,设备用的是随机地址,但TxAdd填成了0(公共地址)。这个问题在调试时特别容易忽略,大家一定要检查。
4.4 Payload字段详解
Payload是广播包的核心内容,它由若干个AD Structure(AD结构体)组成。每个AD Structure的格式是固定的:
AD Structure = Length (1 byte) + AD Type (1 byte) + AD Data (可变长度)
Length:表示AD Type + AD Data的总长度。注意,它不包含自身这个字节。
AD Type:标识数据的类型。比如0x01表示Flags,0x09表示Complete Local Name,0xFF表示Manufacturer Specific Data。
AD Data:具体的数据内容。
举个例子,一个完整的广播包Payload可能是这样的:
02 01 06 0A 09 42 6C 75 65 54 6F 6F 74 68 03 02 0A 00
咱们来解析一下:
- 02 01 06:Length=2,AD Type=0x01(Flags),Data=0x06(表示LE General Discoverable Mode + BR/EDR Not Supported)
- 0A 09 42 6C 75 65 54 6F 6F 74 68:Length=10,AD Type=0x09(Complete Local Name),Data="BlueTooth"
- 03 02 0A 00:Length=3,AD Type=0x02(Incomplete List of 16-bit Service UUIDs),Data=0x000A(Heart Rate Service)
你想想看,这个结构是不是很清晰?每个AD Structure都是自描述的,接收端只要按顺序解析就行了。
核心要点:Payload的总长度不能超过37字节。如果你的广播数据超过了这个限制,要么用扩展广播(ADV_EXT_IND),要么把数据拆到扫描响应里。
4.5 实战经验分享
最后,我分享一个实际项目里的经验。有一次我在做蓝牙门锁,需要广播设备名称和服务UUID。一开始我把所有数据都塞进广播包,结果发现Payload长度超过了37字节,设备根本发不出去。
后来我用了两个技巧:
- 缩短设备名称:把"Smart Lock Pro Max"改成了"SLPM",省了10个字节。
- 使用扫描响应:把服务UUID放到扫描响应里,广播包只放Flags和缩短后的设备名。
这样一来,广播包只有15字节,完全没问题。扫描响应里再放完整的服务UUID和其他数据。这个方案在量产了10万台设备后,没有出现过兼容性问题。
小技巧:如果你不确定自己的广播包格式对不对,可以用nRF Connect或者LightBlue这些工具抓包看看。我每次调试新设备,第一件事就是打开抓包工具,确认广播包的结构和内容。
好了,广播PDU结构就讲到这里。下一章咱们聊聊扫描相关的技术,包括主动扫描和被动扫描的区别,以及扫描窗口和扫描间隔的配置技巧。到时候我会分享一个我在智能穿戴项目里遇到的扫描功耗优化案例,挺有意思的。