4、BLE广播包类型:ADV_IND、ADV_DIRECT_IND、ADV_NONCONN_IND、SCAN_REQ/RSP

做蓝牙开发这么多年,我见过不少新手在广播包类型上栽跟头。说白了,BLE的广播包就那么几种,但每种都有自己的脾气。你选错了类型,设备可能连不上,或者功耗高得吓人。

今天咱们就把这四种核心广播包类型掰开揉碎了讲。嗯,先从最常见的说起。

4.1 ADV_IND:通用广播包

ADV_IND 的全称是 Connectable Undirected Advertising。这是最通用的广播包,也是我项目里用得最多的。

特点:

  • 可被扫描(SCAN_REQ/RSP)
  • 可被连接(CONNECT_REQ)
  • 不指定目标设备,谁都能收到

说白了,这就是个「喊话」模式。你的设备在广播,周围所有扫描设备都能听到,并且可以发起连接。

我的习惯: 做智能灯泡、传感器这类需要被任意手机连接的设备,我首选 ADV_IND。它兼容性最好,几乎所有 BLE 设备都支持。

我记得有一次做智能门锁项目,客户说手机有时候搜不到设备。排查了半天,发现是广播间隔设得太长,加上 ADV_IND 的发射功率没调好。后来把间隔从 100ms 调到 50ms,问题就解决了。

4.2 ADV_DIRECT_IND:定向广播包

ADV_DIRECT_IND 是 Connectable Directed Advertising。这个包有点特殊——它只发给一个指定的设备。

核心特征:

  • 包含目标设备的 MAC 地址
  • 只能被目标设备接收
  • 不可被扫描(不能发 SCAN_REQ)
  • 连接速度快

你想想看,为什么需要这种包?场景很明确:你已经知道要连哪个设备了,不想让其他设备打扰。

实际案例: 蓝牙耳机回连。手机和耳机之前配对过,耳机开机后直接发 ADV_DIRECT_IND 给手机,手机收到后立刻发起连接。整个过程不到 100ms,用户体验非常好。

我曾经踩过一个坑:ADV_DIRECT_IND 的广播间隔不能太长,否则目标设备可能错过。规范里建议用 3.75ms 的间隔,连续发 1.28 秒。如果没连上,再切回 ADV_IND 继续广播。

注意: ADV_DIRECT_IND 不能用于广播数据。它只包含两个 MAC 地址(发起者和目标者),没有空间放自定义数据。如果你需要广播数据,别选这个类型。

4.3 ADV_NONCONN_IND:不可连接广播包

ADV_NONCONN_IND 是 Non-connectable Undirected Advertising。听名字就知道——只能广播,不能连接。

适用场景:

  • 信标(Beacon)设备
  • 广播传感器数据(温度、湿度等)
  • 不需要建立连接的场景

我做过一个温湿度监测项目,每个房间放一个传感器,用 ADV_NONCONN_IND 广播数据。网关只负责接收,不建立连接。这样传感器功耗极低,两节 AA 电池能用一年多。

为什么功耗低?因为 ADV_NONCONN_IND 不需要监听 SCAN_REQ 和 CONNECT_REQ,广播完就休眠。你想想看,省了多少监听窗口的时间。

广播类型 可扫描 可连接 典型应用
ADV_IND 是 是 通用设备、传感器
ADV_DIRECT_IND 否 是 快速回连、耳机
ADV_NONCONN_IND 否 否 信标、单向广播

4.4 SCAN_REQ 与 SCAN_RSP:扫描请求与响应

这两个不是广播包,而是扫描过程中的控制包。但我把它们放在一起讲,因为它们是广播数据解析里绕不开的环节。

SCAN_REQ: 扫描设备发起的请求,询问广播设备「你还有更多数据吗?」

SCAN_RSP: 广播设备回复的响应,包含额外的广播数据。

为什么要分两次?因为广播包(ADV_IND)最多只能放 31 字节数据。如果不够用,可以把一部分数据放到 SCAN_RSP 里。

我的做法: 把关键数据(设备名称、服务 UUID)放 ADV_IND 里,次要数据(厂商自定义数据、电量)放 SCAN_RSP 里。这样扫描设备可以快速判断是否需要继续交互。

我曾经遇到一个兼容性问题:某款手机在收到 ADV_IND 后,不发送 SCAN_REQ,直接发起连接。结果设备把关键配置信息放在 SCAN_RSP 里,手机没收到,导致连接后功能异常。后来我把所有必要数据都塞进了 ADV_IND,问题解决。

4.5 四种包类型的交互流程

下面这张图展示了广播者和扫描者之间的完整交互流程。我建议你仔细看看,理解每个包在什么时机出现。

BLE 广播与扫描交互流程 广播者 (Advertiser) 扫描者 (Scanner) ADV_IND / ADV_NONCONN_IND SCAN_REQ SCAN_RSP CONNECT_REQ (仅对 ADV_IND) 说明: • ADV_DIRECT_IND 不参与扫描交互,直接等待 CONNECT_REQ • ADV_NONCONN_IND 收到 SCAN_REQ 后不响应 • SCAN_REQ/RSP 只在 ADV_IND 和 ADV_SCAN_IND 模式下发生

4.6 实战选择建议

说了这么多,到底怎么选?我总结了几条经验:

  1. 需要连接 + 需要广播数据 → ADV_IND。这是最通用的选择,90% 的场景都适用。
  2. 需要快速连接 + 已知目标 → ADV_DIRECT_IND。回连场景首选,但注意它不能带数据。
  3. 只广播不连接 → ADV_NONCONN_IND。信标、传感器数据广播,功耗最低。
  4. 数据超过 31 字节 → 把部分数据放 SCAN_RSP。但要注意,不是所有扫描设备都会发 SCAN_REQ。
避坑指南: 我曾经在量产项目里用了 ADV_SCAN_IND(可扫描但不可连接),结果客户反馈设备经常连不上。后来发现是扫描设备兼容性问题,部分手机不支持对这种包发起连接。从那以后,除非有特殊需求,我基本只用 ADV_IND。

好了,这四种广播包类型就讲到这里。记住一点:没有最好的类型,只有最适合场景的类型。多在实际项目里试试,你自然就摸清它们的脾气了。