4. 广播与扫描工程实践
BLE 设备在未建立连接前,所有可发现性、可连接性都依赖广播信道完成。nRF52 上广播由 SoftDevice 或 Nordic 协议栈的广播模块驱动,工程上真正需要掌握的不是“能发广播”,而是广播包里放了什么、用哪种广播类型、对端如何扫描和响应、抓包时如何判断行为是否符合预期。本节围绕这四点展开,并给出可直接对照抓包结果的判断依据。
一、广播包格式:31 字节如何被组织
传统 BLE 广播(Legacy Advertising)每个广播事件在 37/38/39 三个信道轮流发送同一份 PDU。广播 PDU 由 2 字节 Header 和最多 37 字节 Payload 组成,其中 Payload 前 6 字节是广播地址(AdvA),剩余最多 31 字节才是用户可填的广播数据(AdvData)。这 31 字节不是自由文本,而是由若干 AD Structure 串联:每个结构为 Length(1B) + AD Type(1B) + AD Data(nB),Length 表示“Type + Data”的总长度,因此单个结构最多 30 字节数据。
常用 AD Type 包括:0x01 Flags、0x09 Complete Local Name、0x03 Complete List of 16-bit Service UUIDs、0x06 Incomplete List of 128-bit UUIDs、0x0A TX Power Level、0xFF Manufacturer Specific Data。Flags 通常放在最前,用于声明 LE General Discoverable、BR/EDR Not Supported 等能力位。工程上要牢记:31 字节是硬上限,名称、UUID、厂商数据会互相挤占;放不下时要么改用扫描响应承载,要么使用扩展广播(Extended Advertising,nRF52 部分型号支持)。
// 示例:nRF5 SDK 广播数据(非实测,仅结构示意)
static uint8_t adv_data[] = {
0x02, 0x01, 0x06, // Flags: LE General Disc + BR/EDR Not Supported
0x09, 'N', 'R', 'F', '_', 'D', 'E', 'V', // Complete Local Name
0x03, 0x03, 0x0D, 0x18, // 16-bit UUID: 0x180D (Heart Rate)
0x05, 0xFF, 0x01, 0x02, 0x03, 0x04 // Manufacturer Specific Data
};
// 总长 = 3 + 9 + 4 + 6 = 22 字节,剩余 9 字节可继续扩展
二、广播类型与可连接性
广播类型决定了设备是否可被发现、是否可被连接、是否允许主动扫描响应。常见类型如下:
- ADV_IND:可连接、可扫描的非定向广播,最常用,任何中心设备都可发起连接或扫描请求。
- ADV_DIRECT_IND:定向可连接广播,PDU 中同时包含 AdvA 和 TargetA(目标中心地址),只有该目标能连接,广播间隔通常很短,用于快速重连。
- ADV_NONCONN_IND:不可连接、不可扫描的非定向广播,常用于 Beacon(如 iBeacon/Eddystone),只广播数据,不接受连接和扫描请求。
- ADV_SCAN_IND:可扫描但不可连接,允许中心设备发 SCAN_REQ 获取扫描响应,但不接受连接。
选择依据很直接:需要被手机连接并交互,用 ADV_IND;只做信标或传感器广播,用 ADV_NONCONN_IND;需要在不连接的前提下让对端读取更多信息,用 ADV_SCAN_IND + SCAN_RSP;已知目标地址且要快速重连,用 ADV_DIRECT_IND。nRF5 SDK 中通过 ble_gap_adv_params_t.type 设置,例如 BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED 对应 ADV_IND。
三、扫描响应:31 字节的第二次机会
当中心设备收到 ADV_IND 或 ADV_SCAN_IND 后,可发送 SCAN_REQ,外设回 SCAN_RSP。SCAN_RSP 同样最多 31 字节,格式与 AdvData 一致,但不包含 Flags,通常用于放完整名称、更多服务 UUID 或厂商数据。工程上常见做法是:AdvData 放 Flags + 短名称 + 关键 UUID,SCAN_RSP 放完整名称 + 剩余 UUID + 厂商数据,从而突破单包 31 字节限制。
注意:ADV_NONCONN_IND 和 ADV_DIRECT_IND 不支持 SCAN_RSP。若抓包时看到设备声明了可扫描但从未回应 SCAN_REQ,应检查广播类型是否设置正确,以及协议栈是否注册了扫描响应数据。
四、实际抓包分析:从空口看行为
抓包工具推荐 nRF Sniffer + Wireshark。nRF Sniffer 固件烧录到 nRF52 DK 后,Wireshark 通过 extcap 接口捕获空口包,可看到 ADV_IND、SCAN_REQ、SCAN_RSP、CONNECT_IND 等。分析时按以下顺序判断:
- 确认广播类型:Wireshark 的 Info 列会显示 “ADV_IND”“ADV_NONCONN_IND”“ADV_DIRECT_IND” 等。若显示 ADV_DIRECT_IND,展开 PDU 可看到 TargetA,核对是否为目标中心地址。
- 检查 AdvData 解析:展开 “Bluetooth Low Energy Link Layer” → “Advertising Data”,Wireshark 会逐条列出 AD Structure。若出现 “Malformed” 或长度不匹配,通常是 Length 字段写错或数据越界。
- 观察扫描交互:若中心发 SCAN_REQ 后外设回 SCAN_RSP,说明广播类型支持扫描且已注册扫描响应数据;若只有 SCAN_REQ 没有 SCAN_RSP,检查广播类型是否为 ADV_NONCONN_IND 或协议栈是否配置了扫描响应。
- 判断可连接性:只有 ADV_IND 和 ADV_DIRECT_IND 能触发 CONNECT_IND。若抓包中始终没有 CONNECT_IND,先确认广播类型,再检查中心是否在扫描窗口内、地址是否匹配。
- 核对广播间隔:Wireshark 时间列可估算相邻同信道广播包间隔,与
ble_gap_adv_params_t.interval设置对比。间隔过短会增加功耗,过长会降低被发现速度。
五、工程配置要点与常见问题
在 nRF5 SDK 中,广播初始化通常分两步:ble_advertising_init() 注册 AdvData 和 SCAN_RSP 数据,ble_advertising_start() 指定广播类型和参数。定向广播需要额外调用 ble_gap_adv_set_opt() 或使用 BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE 并设置 peer 地址。常见问题包括:
- AdvData 超过 31 字节导致
ble_advertising_init()返回NRF_ERROR_DATA_SIZE,需拆分到 SCAN_RSP 或精简字段。 - 使用 ADV_DIRECT_IND 时未设置 peer 地址,协议栈返回
NRF_ERROR_INVALID_PARAM。 - SCAN_RSP 未注册却使用可扫描广播类型,中心发 SCAN_REQ 后无响应,表现为“可发现但读不到完整名称”。
- 广播间隔与连接间隔混淆:广播间隔只影响被发现速度,连接后由连接参数决定通信节奏。
实际调试时,可先用手机 nRF Connect 扫描,观察设备名称、UUID、厂商数据是否与设计一致;再用 nRF Sniffer 抓包确认空口行为。两者结合,能快速定位是数据格式问题、广播类型问题还是扫描响应缺失问题。对于量产产品,建议在产测工位固定使用同一抓包脚本,核对广播包中厂商数据、设备名称和 Flags 是否符合出厂规范,避免因广播配置错误导致设备无法被目标 App 发现。