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中),放不下太多信息。那如果设备想广播更多数据怎么办?

答案是:扫描请求 + 扫描响应。

流程是这样的:

  1. 广播设备发送可扫描广播(包含基本信息)
  2. 扫描设备收到后,发送扫描请求(SCAN_REQ)
  3. 广播设备收到请求后,发送扫描响应(SCAN_RSP),包含额外数据

扫描响应同样遵循AD Structure格式,只是它是在收到请求后才发送的。这样做的好处是:平时广播包只带必要信息,省电;需要更多数据时再响应,灵活。

注意:不可连接广播不支持扫描响应。如果你用iBeacon或Eddystone这类信标,它们广播的数据必须全部塞进31字节的广播包里。我见过有人把信标数据配到扫描响应里,结果手机App永远读不到——因为信标根本不会响应扫描请求。

2.4 使用nRF Connect抓包分析广播包

理论说完了,咱们来点实战。我个人最常用的工具是nRF Connect(手机App)和LightBlue。今天以nRF Connect为例,带大家抓一个广播包看看。

操作步骤:

  1. 打开nRF Connect App,点击“SCANNER”进入扫描界面
  2. 点击右上角的“...”菜单,选择“Show all”显示所有设备
  3. 找一个正在广播的设备,点击它
  4. 在“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 广播与扫描的核心流程

为了让大家更直观地理解整个广播与扫描的交互过程,我画了一张流程图:

BLE广播与扫描交互流程 广播设备 (Advertiser) 扫描设备 (Scanner) 1. 发送广播包 ADV_IND / ADV_NONCONN_IND 2. 发送扫描请求 SCAN_REQ 3. 发送扫描响应 SCAN_RSP 4. 发送连接请求 CONNECT_REQ(仅可连接广播) 说明:不可连接广播跳过步骤2-4,可扫描广播跳过步骤4

这张图把整个交互过程串起来了。你想想看,从广播到扫描请求,再到扫描响应,最后到连接请求——每一步都有对应的PDU类型。搞清楚了这些,调试蓝牙连接问题时你就能快速定位是哪个环节出了岔子。

2.6 本章小结

这一章我们覆盖了:

  • 三种广播类型的区别与选型
  • AD Structure的格式与解析方法
  • 扫描请求与扫描响应的配合机制
  • 用nRF Connect实际抓包分析

嗯,内容不少。但这些都是BLE开发的基本功。下一章我们会深入连接过程的细节——包括连接参数、连接间隔、延迟等。到时候你会发现,今天学的广播知识是理解连接的基础。

好了,拿起你的手机和开发板,打开nRF Connect,去抓几个真实的广播包练练手吧。只有亲手解析过原始数据,你才能真正理解BLE广播的精髓。