2. BLE广播与扫描实战:广播类型、数据格式与抓包分析
各位好,欢迎来到第二章。上一章我们聊了蓝牙协议栈的宏观架构,这一章咱们直接上手——把BLE的广播与扫描彻底吃透。
广播,说白了就是蓝牙设备在“喊话”。它不停地对外说:“我在这儿,我叫某某,我能干这些事。”而扫描,就是另一台设备在“听”。听谁在喊,喊了什么内容。
听起来简单?嗯,但这里面的门道可不少。我在项目里见过太多人栽在广播配置上——要么设备连不上,要么扫描不到,要么功耗高得离谱。今天咱们就把这些坑一个个填平。
2.1 广播类型:三种核心模式
BLE广播不是只有一种。根据设备是否需要被连接、是否需要主动回应,分为三种类型。我习惯用一张表来对比,清晰明了:
| 广播类型 | 可连接性 | 扫描响应 | 典型场景 |
|---|---|---|---|
| 可连接广播 | 是 | 支持 | 蓝牙耳机、手环、智能锁 |
| 不可连接广播 | 否 | 不支持 | 信标(Beacon)、温度传感器 |
| 可扫描广播 | 否 | 支持 | 需要广播额外数据但不连接 |
关键点:可连接广播和可扫描广播的区别在于——前者允许对方发起连接请求,后者只允许对方发送扫描请求来获取更多数据。我刚开始做BLE时,把可扫描广播配成了可连接,结果设备一直被人连上,功耗直接翻倍。
2.2 广播数据格式:AD Structure 与 AD Type
广播包里的数据不是随便塞的。BLE协议规定了一套严格的格式——AD Structure(Advertising Data Structure)。
每个AD Structure由三部分组成:
- Length(1字节):表示后面AD Type + AD Data的总长度
- AD Type(1字节):表示数据类型,比如设备名、UUID、Tx Power等
- AD Data(N字节):具体的数据内容
举个例子,一个包含“设备名”的广播包,它的AD Structure长这样:
0x05 // Length = 5(后面5个字节)
0x09 // AD Type = Complete Local Name
0x42 // 'B'
0x4C // 'L'
0x45 // 'E'
0x00 // 结束符(实际项目中通常没有,这里仅为示意)
常见的AD Type有哪些?我整理了一份常用清单:
| AD Type值 | 含义 | 说明 |
|---|---|---|
| 0x01 | Flags | 广播模式、是否支持BR/EDR等 |
| 0x02/0x03 | 16-bit Service UUID | 不完全/完整的16位UUID列表 |
| 0x06/0x07 | 128-bit Service UUID | 不完全/完整的128位UUID列表 |
| 0x08/0x09 | Local Name | 缩短/完整的设备名称 |
| 0x0A | Tx Power Level | 发射功率,用于距离估算 |
| 0xFF | Manufacturer Specific Data | 厂商自定义数据 |
我的经验:Flags(0x01)这个AD Type几乎每个广播包都必须有。它告诉接收端这个设备是否支持经典蓝牙、是否处于可连接模式。我曾经漏配了这个字段,结果手机死活扫不到设备——折腾了两天才发现。
2.3 扫描请求与扫描响应
这里有个有意思的机制。广播包本身只有31字节的有效载荷(在BLE 4.x中),放不下太多信息。那如果设备想广播更多数据怎么办?
答案是:扫描请求 + 扫描响应。
流程是这样的:
- 广播设备发送可扫描广播(包含基本信息)
- 扫描设备收到后,发送扫描请求(SCAN_REQ)
- 广播设备收到请求后,发送扫描响应(SCAN_RSP),包含额外数据
扫描响应同样遵循AD Structure格式,只是它是在收到请求后才发送的。这样做的好处是:平时广播包只带必要信息,省电;需要更多数据时再响应,灵活。
注意:不可连接广播不支持扫描响应。如果你用iBeacon或Eddystone这类信标,它们广播的数据必须全部塞进31字节的广播包里。我见过有人把信标数据配到扫描响应里,结果手机App永远读不到——因为信标根本不会响应扫描请求。
2.4 使用nRF Connect抓包分析广播包
理论说完了,咱们来点实战。我个人最常用的工具是nRF Connect(手机App)和LightBlue。今天以nRF Connect为例,带大家抓一个广播包看看。
操作步骤:
- 打开nRF Connect App,点击“SCANNER”进入扫描界面
- 点击右上角的“...”菜单,选择“Show all”显示所有设备
- 找一个正在广播的设备,点击它
- 在“RAW ADVERTISING DATA”区域,你会看到一串十六进制数据
比如我抓到一个真实的广播包数据:
02 01 06 03 02 0D 18 0A 09 42 4C 45 5F 44 65 76 69 63 65
咱们来解析一下:
02 01 06:Length=2, AD Type=0x01 (Flags), Data=0x06(表示可连接、不支持BR/EDR)03 02 0D 18:Length=3, AD Type=0x02 (16-bit UUID不完全列表), UUID=0x180D(心率服务)0A 09 42 4C 45 5F 44 65 76 69 63 65:Length=10, AD Type=0x09 (完整设备名), Name="BLE_Device"
你看,每个AD Structure都是自描述的——先告诉你长度,再告诉你类型,最后是数据。解析起来非常规整。
避坑指南:我曾经在调试一个心率手环时,发现广播包里没有包含心率服务的UUID。手机App扫描到设备后,不知道它能干什么,直接把它过滤掉了。后来我在广播包里加上了0x180D这个UUID,问题立刻解决。记住:广播包里的Service UUID是告诉外界“我能做什么”的关键信息。
2.5 广播与扫描的核心流程
为了让大家更直观地理解整个广播与扫描的交互过程,我画了一张流程图:
这张图把整个交互过程串起来了。你想想看,从广播到扫描请求,再到扫描响应,最后到连接请求——每一步都有对应的PDU类型。搞清楚了这些,调试蓝牙连接问题时你就能快速定位是哪个环节出了岔子。
2.6 本章小结
这一章我们覆盖了:
- 三种广播类型的区别与选型
- AD Structure的格式与解析方法
- 扫描请求与扫描响应的配合机制
- 用nRF Connect实际抓包分析
嗯,内容不少。但这些都是BLE开发的基本功。下一章我们会深入连接过程的细节——包括连接参数、连接间隔、延迟等。到时候你会发现,今天学的广播知识是理解连接的基础。
好了,拿起你的手机和开发板,打开nRF Connect,去抓几个真实的广播包练练手吧。只有亲手解析过原始数据,你才能真正理解BLE广播的精髓。