
去年年底在做一个 BLE 网关项目主控侧用的是 ST 的 BlueNRG-LP SoC负责扫描周围传感器节点通过 BLE 5 扩展广播上报的状态数据。按说这个芯片支持蓝牙 5.2扫描扩展广播应该是基本功结果一接真实节点就翻车了手机 nRF Connect 里能清楚看到传感器在发扩展广播BlueNRG-LP 这边却什么都扫不到。更麻烦的是这个问题不是百分百必现把扫描间隔调大之后偶尔能抓到一两个包但只要并发节点一多基本全军覆没。这篇文章就把整个排查过程、根因和最终代码改法记录下来遇到类似“BlueNRG-LP 扫描不到扩展广播包”的朋友可以参考少走弯路。1. 先把这个坑讲清楚BlueNRG-LP 收不到扩展广播包1.1 一个典型的故障现场先说我的环境网关板子上用的是 BlueNRG-LPSDK 是基于 ST 官方 ble_lib.a 的方式做应用开发MCU 侧通过 UART/HCI 接口和协议栈交互。传感器节点是第三方模块配置成 BLE 5 扩展广播广播周期 100ms数据长度大概 80 字节里面带着温湿度、电量这些业务数据。刚开始调试的时候我发现一个非常迷惑的现象手机上的 nRF Connect 能正常显示这个传感器节点的广播名称、厂商数据、信号强度全都有说明发射端没有问题。而同一时间BlueNRG-LP 的运行日志里什么都没有既没有普通广播的回调也没有任何扫描结果。后来我试着把扫描间隔从 30ms 调到 100ms偶尔能收到一两个包但数据是零碎的根本拼不出完整的业务数据。这个现象本身就很有价值它不是“完全收不到”而是“绝大多数时候收不到偶尔能抓到一点”。这说明射频链路大概率是通的问题更可能出在协议栈对扩展广播的处理逻辑上。如果你也遇到类似情况先别急着怀疑硬件天线和匹配电路重点看看扫描配置和事件解析。1.2 扩展广播和传统广播差在哪主信道与辅助信道的区别要理解为什么“扩展广播扫描不到”比传统广播更容易出现得先知道 BLE 5 扩展广播的传输机制。传统广播Legacy Advertising只在 37、38、39 三个主广播信道上发送一个广播包最多 31 字节接收端只要在这三个信道上扫描就能拿到全部数据。扩展广播完全不同。它做了一个拆分的动作广播器先在主广播信道上发一个 ADV_EXT_IND这个包本身很短不带实际业务数据只包含一个“AuxPtr”指针告诉接收端“真正的广播数据在哪个辅助信道、偏移多少、用什么 PHY 发”。接收端要拿到真正的数据必须跟踪这个指针跳到 0-36 号辅助信道上的 AUX_ADV_IND甚至还要继续接收 AUX_CHAIN_IND 链包才能拼出完整数据。打个比方传统广播就像一个人在楼下喊一嗓子把信息喊完扩展广播则是先在一楼贴一张纸条告诉你快递放在 15 楼 3 号柜你得跟着纸条爬上去拿。如果你的接收端只是个“站在楼下听喊声”的装置那它最多看到那张纸条永远拿不到真正的包裹。BlueNRG-LP 从硬件上是支持 BLE 5.2 扩展广播的但硬件支持不代表软件默认开启。扫描器如果没有正确配置扩展扫描参数或者没有处理扩展广播事件结果就是什么都收不到或者只能在主信道上看到一些无法解析的指示包。2. 扫描不到扩展广播常见原因基本就这四类2.1 扫描 PHY 和广播 PHY 没对上最常见也最隐蔽展开调试之后我发现第一类原因是扫描 PHY 不匹配。扩展广播的广播器可以在 1M PHY、2M PHY 或 Coded PHY 上发送辅助信道数据而扫描器必须提前配置好自己要在哪些 PHY 上扫描。如果两边没对上主信道上的 ADV_EXT_IND 可能还能收到但跳到辅助信道后就因为不支持对应 PHY 而把包丢掉。我当时遇到的场景就是典型例子传感器模块配置的是 LE Coded PHY也就是长距离模式125kbps 编码速率。而我的网关扫描代码里固定写的是scan_phys 0x01只扫描 1M PHY。结果就是主信道指示能收到辅助信道的数据全部丢失。手机上之所以能看到是因为手机端的扫描器为了兼容性默认会同时扫 1M 和 Coded 两个 PHY。这个问题隐蔽就隐蔽在它不会让你完全收不到任何东西而是给你一条“几乎不可用”的链路。你把扫描间隔调大瞎猫碰到死耗子偶尔能拿到一小段数据但这并不是稳定接收反而更容易误导你往信号强度、天线匹配方向去排查。2.2 只开启了 legacy 扫描没有真正进入扩展扫描第二类原因是代码里根本没有进入扩展扫描模式。很多 BlueNRG-LP 的示例工程是从 BLE 4.x 时代的代码移植过来的扫描流程用的还是老一套 API只监听三个主信道不解析 ADV_EXT_IND 的 AuxPtr。如果协议栈没有开启扩展扫描那主信道上的扩展广播 PDU 会被当成一个未知类型包要么直接丢弃要么上报一个没有数据的空事件。上层应用一看数据长度为 0顺手就过滤掉了表面上就是“扫描不到扩展广播”。这种问题在 SDK 版本差异很大的工程里特别容易出现。比如你在老版本上开发过拿到新 SDK 之后没有重新看头文件直接沿用旧的扫描配置结构体字段个数都不一样配置写进去也是白写。我建议排查时直接打开 SDK 的 ble_gap.h 或者 hci_le.h确认你调用的接口是不是带scan_phys、scan_enable这些扩展扫描参数的版本。2.3 过滤策略和重复包过滤把结果挡掉了第三类原因跟扫描过滤策略有关。BLE 扫描配置里有扫描过滤策略scanning filter policy有白名单、定向广播处理等逻辑。如果这个值被设置成只接受白名单设备而目标设备根本没有加进白名单那扫描结果自然不会上报。另外还有一个容易忽略的参数是重复包过滤filter duplicates。它的作用是同一个广播地址的重复广播只上报一次减少事件数量。这个参数本身不会导致“完全扫不到”但如果你调试时开着重启广播器或者广播器每次上电换了随机地址重复包过滤的表现可能和你预期不一致导致某些事件被吞掉。我遇到过一种情况广播器的地址类型是 Random Static但每次广播端重启后地址不变扫描端开启了重复过滤所以第一包上报后后续的广播全部被过滤。你在日志里看到的就是“只有零星几条记录”非常像扫描不稳定的问题。排查时先把重复包过滤关掉再分析是不是过滤策略的问题。2.4 事件类型判断写死扩展广播被识别成“不认识的包”第四类原因不在扫描参数上而在应用层的事件解析上。这也是最容易栽跟头的地方。传统广播的事件类型是一个枚举值比如ADV_IND、ADV_NONCONN_IND判断逻辑很简单。但扩展广播的事件类型字段是 16 位的位掩码可连接、可扫描、定向、扫描响应、Legacy 标志、数据完整状态分别占不同的 bit。如果你从老代码里拷贝了一段判断逻辑只处理event_type 0x0000这样的分支那扩展广播十有八九会被当成“不认识的包”丢掉。更隐蔽的是扩展广播报告里的数据字段不是一定完整的。由于扩展广播可以分包一个完整广播事件的数据可能跨多个通道包如果扫描窗口不够你可能收到一个Data Status标记为“不完整”的报告。如果你的应用层只处理完整数据包那这些不完整报告也会被丢弃表现出来同样是“扫不到”。3. 从现象到根因一套能照抄的排查流程3.1 先用第三方工具确认广播源别让接收端背锅调试这类问题我第一步永远是先确认发射端是否正常。别觉得这一步多余现实中真的有很多项目最后发现是广播端配置错了扫描端累死累活也修不好。我习惯拿一台手机装好 nRF Connect打开扫描页。重点看三样东西广播类型是不是 Extended辅助信道 PHY 是什么广播数据长度大概多少。nRF Connect 的扫描结果里会把这些字段标出来如果是 Coded PHY 的设备还会显示CODED字样。这样你在改 BlueNRG-LP 代码之前心里已经有一个明确的验证目标比如“这台设备用 Coded PHY 在 125kbps 下广播数据长度 80 字节”。如果你手头没有手机也可以用 BlueNRG-LP 自己写一个最小扫描工程来验证发射端。但那样会把自己绕进去因为你还没排除接收端问题拿一个不确定的接收端去验证发射端很难判断是谁的锅。手机作为标准接收端最可靠。3.2 打开协议栈事件日志区分“没收到”和“收到了但没上报”确认发射端正常之后下一步就是在 BlueNRG-LP 上开日志判断协议栈底层到底有没有收到扩展广播事件。BlueNRG-LP 的协议栈通过事件回调向上层上报数据最底层的事件类型是 HCI 层的EVT_LE_META_EVENT如果是扩展广播子事件是HCI_LE_EXTENDED_ADVERTISING_REPORT。我在回调函数里加了一个打印把所有 HCI 事件和子事件代码打出来不做任何过滤。这一步能帮你直接定位问题层级如果完全没有任何扩展广播报告事件说明协议栈底层就没切到扩展扫描模式重点查扫描参数。如果有扩展广播报告事件但你的业务代码没有输出说明问题出在上层过滤或事件解析。如果有事件且有数据但数据内容不对那可能是分片重组、扫描窗口或者重复过滤的问题。我在实际项目里就是靠这一步确认了协议栈确实没有上报扩展广播事件才把排查方向从射频转向扫描参数。3.3 最小工程复现逐项替换参数定位根因拿到底层日志之后不要直接在完整业务代码里改配置那样变量太多很难判断哪个参数的改动起了作用。我建议到这一步直接建一个最小工程只保留扫描任务和串口日志然后把扫描参数逐个替换。我的替换顺序一般是先确认scanning_phys是否包含目标广播的 PHY。如果你不确定目标广播用哪个 PHY直接改成0x07也就是 1M、2M、Coded 三个 PHY 一起扫。把scanning_filter_policy改成NO_FILTER先不过滤任何设备。把filter_duplicates改成DUPLICATES_DISABLE确保每包都上报。把扫描间隔和窗口改成连续扫描也就是scan_interval scan_window确保扫描占空比 100%。这三个参数都改完之后如果最小工程能稳定收到扩展广播事件那基本可以确认就是配置问题。接下来再把参数逐个往回改找到哪个参数是“罪魁祸首”。这比一次性把所有可疑代码都改掉要清晰得多。3.4 周期广播是另一个容易被绕进去的坑排查到一半的时候我突然想到一个容易混淆的概念周期广播。很多同学把“扩展广播”和“周期广播”混为一谈但如果发射端配置的是 Periodic Advertising那普通扫描永远扫不到数据。周期广播和扩展广播的差别在于扩展广播是“先指示后发送”的单次事件扫描器跟踪 AuxPtr 拿到数据周期广播则是在一个固定的时间间隔内反复发送广播事件接收端需要先调用LE Periodic Advertising Create Sync命令和广播器建立同步之后才能收到周期广播报告。如果你在 nRF Connect 里看到广告类型带Periodic字样或者广播配置里开了 Periodic Advertising那你的 BlueNRG-LP 扫描代码需要额外写同步逻辑不能只依赖普通扫描。很多设备在广播扩展广播的同时也会发周期广播但你真正要的业务数据可能在周期广播里而不是在普通扩展广播里。这个方向如果搞错了配置调得再对也没用。4. 正确配置扩展扫描代码级修复方案4.1 扫描参数初始化PHY、过滤、间隔全部显式设置确认根因之后修复方式其实很简单。下面这段代码是我在 BlueNRG-LP 上跑通的扩展扫描初始化逻辑重点就是三个 PHY 都扫描、不过滤重复包、不设白名单过滤。需要注意的是不同 SDK 版本的接口名称和参数个数可能略有差异以你的 SDK 头文件为准但思路是一样的。tBleStatus ret; uint8_t own_addr_type PUBLIC_ADDR; le_scan_parameters_t scan_params[3]; // 1M PHY 扫描参数 scan_params[0].scan_type PASSIVE_SCAN; scan_params[0].scan_interval 0x0030; // 30 ms scan_params[0].scan_window 0x0030; // 30 ms和 interval 相等表示连续扫描 scan_params[0].scanning_filter_policy NO_FILTER; // 2M PHY 扫描参数 scan_params[1].scan_type PASSIVE_SCAN; scan_params[1].scan_interval 0x0030; scan_params[1].scan_window 0x0030; scan_params[1].scanning_filter_policy NO_FILTER; // Coded PHY 扫描参数 scan_params[2].scan_type PASSIVE_SCAN; scan_params[2].scan_interval 0x0030; scan_params[2].scan_window 0x0030; scan_params[2].scanning_filter_policy NO_FILTER; // scanning_phys: bit0 表示 1Mbit1 表示 2Mbit2 表示 Coded ret hci_le_set_extended_scan_parameters( own_addr_type, NO_FILTER, 0x07, scan_params, DUPLICATES_DISABLE); if (ret ! BLE_STATUS_SUCCESS) { // 打印错误并处理 return; } ret hci_le_set_extended_scan_enable(ENABLE, DUPLICATES_DISABLE, 0);这里有个关键点scan_interval和scan_window的单位是 0.625ms0x0030就是 30ms。调试阶段建议把两者设成相等也就是 100% 占空比的连续扫描这样最容易复现和验证。等确认扩展广播能稳定收到之后再根据实际功耗要求把扫描窗口缩短。如果你用的是 ST 提供的 GAP 高层 API而不是直接调 HCI 层那就去找类似aci_gap_set_scan_configuration的接口把scanning_phys参数同样设成0x07。本质上是一样的只是封装层级不同。4.2 扩展广播事件解析要点按位读 Event_Type配置好扫描之后还要确认应用层正确解析扩展广播报告事件。这里最容易出错的就是Event_Type字段它不是一个简单枚举而是位掩码。我贴一段解析逻辑的骨架注意判断条件用的是位运算不是直接比较等于某个值void HCI_Event_CB(void *pData) { hci_event_pckt *event (hci_event_pckt *)pData; if (event-evt ! HCI_LE_META_EVT) { return; } evt_le_meta_event *meta (evt_le_meta_event *)event-data; if (meta-subevent HCI_LE_EXTENDED_ADVERTISING_REPORT) { evt_le_extended_advertising_report_rp0 *rp (evt_le_extended_advertising_report_rp0 *)meta-data; // Event_Type 是 16 位位掩码不能用 判断 uint16_t event_type rp-Event_Type; // bit4 为 1 表示 Legacy PDU如果只关心扩展广播可以跳过 Legacy if (event_type 0x0010) { return; // Legacy 广播走其他处理 } // bit5-6 表示 Data Status0 完整1 不完整2 截断 uint8_t data_status (event_type 5) 0x03; // 业务数据在 rp-Data 里长度是 rp-Data_Length // 根据 data_status 决定是否继续拼包 ProcessExtAdvReport(rp-Address, rp-RSSI, rp-Primary_PHY, rp-Secondary_PHY, rp-Data_Length, rp-Data); } }很多老代码里会用event_type 0x0000来判断普通可连接广播这个习惯在扩展广播上报场景下一定要改掉。设备上报的扩展广播事件类型即使不是 Legacy也可能可连接或可扫描严格按照位掩码去解析。另外再看一眼Primary_PHY和Secondary_PHY两个字段0x01表示 1M0x02表示 2M0x03表示 Coded。如果你的广播源在 Coded PHY 上发送这里能直接看到日志里也多打一个 PHY后续问题定位会更方便。4.3 多 PHY 同扫的取舍和功耗提醒把scanning_phys设成0x07三个 PHY 一起扫在调试阶段没问题但正式产品里要根据实际情况收敛。因为多 PHY 扫描意味着接收机要在更多物理信道上工作扫描功耗会比单 PHY 明显增加。BlueNRG-LP 本身功耗很低但扫描期间接收机是持续工作的这部分功耗省不掉。如果你的应用场景明确知道广播端只用 1M PHY那就只扫 1M没有必要为了“保险”把 2M 和 Coded 全开着。如果广播端用的是 Coded PHY那你只需要在 Coded 对应的扫描参数里设置合理的扫描间隔和窗口其他 PHY 不配或者不使能功耗能降下来不少。另外一个细节是扫描窗口和广播周期的配合。广播端如果 100ms 发一次扩展广播你的扫描窗口哪怕只有 10ms理论上也有概率抓到但抓包率会很低。某些业务场景要求快速发现设备那就必须提高扫描占空比甚至连续扫描。另一些场景只要求 2 秒内发现一次那扫描周期就可以压得很低。这个参数没有绝对最优解一定要结合广播周期和业务时延要求去定。5. 问题速查表和调试经验5.1 从现象到候选原因的速查表我把这次排障碰到的几个典型现象整理成一张表后续再遇到类似问题可以直接对照省得从头查。现象可能原因排查方向完全没收到任何扫描事件扫描未使能 / PHY 配置错误 / 过滤策略过于严格检查扫描使能、scanning_phys、filter policy偶尔能收到一两包无法稳定复现扫描窗口太小 / 重复包过滤 / 广播周期太长先做连续扫描关闭重复包过滤上层能收到事件但数据长度不对扩展广播分包未重组 / Data Status 不完整检查分片处理逻辑按位解析 data_status手机能收到BlueNRG-LP 不能广播使用了 Coded PHY 而扫描端只配了 1M确认广播 PHY把对应 PHY 加进扫描参数广告类型带 Periodic 但扫不到把周期广播当扩展广播处理改用周期广播同步命令不是普通扫描事件类型判断后数据全部丢弃应用层用 判断 Event_Type改为按位掩码解析这张表不是万能的但基本覆盖了这次项目里和大半年来社区里遇到最多的几类问题。遇到故障时先对号入座再结合日志去验证效率会高很多。5.2 调试扩展广播时好用的工具清单调试过程中工具选对能省很多时间。我这次用到的工具组合如下手机 nRF Connect第一道验证确认广播源存在、广播类型、PHY、数据长度。免费移动端都有。串口日志在 BlueNRG-LP 的协议栈回调里打印事件代码和关键字段这一条应该算是最重要的调试手段。BLE 抓包器如果你手头有 USB dongle 抓包器可以直接看空中的广播 PDU能确认主信道 ADV_EXT_IND 和辅助信道 AUX_ADV_IND 是否都在发以及 PHY 是否和你预期一致。ST 官方工具ST 的 BLE Toolbox 配合官方开发板也能做扫描。但我个人更习惯直接用第三方手机 App 验证发射端再用自己的代码验证接收端。逻辑分析仪或示波器这一步一般不用只有当怀疑射频硬件问题时才需要。先把软件层配置排除干净再动硬件。这组工具组合下来大多数扩展广播扫描问题都能在半小时内定位到具体环节。5.3 这次排障后我养成的三个习惯最后分享三个这次排障之后我一直在用的习惯。第一个习惯是拿到新 SDK 或新芯片先跑一遍最小扫描例程并且在例程里把扫描 PHY 打印出来。很多时候不是你不会写代码而是新旧接口之间的差异被忽略了。先确认例程能不能扫到扩展广播再把业务代码往上叠加问题范围会小很多。第二个习惯是事件回调里先打日志再做逻辑判断。以前我总喜欢在回调里先过滤掉“无效事件”结果过滤条件写错了反而把有效事件全部丢掉。现在我是所有事件先落日志让日志告诉我发生了什么再由解析逻辑决定怎么处理。排查问题的时候信息永远比“看起来干净”的日志更有价值。第三个习惯是区分扩展广播和周期广播。拿到一个新的广播源设备先花一分钟在手机 App 上看清楚广播类型再决定用普通扫描还是周期同步。这次项目里浪费的最长一段时间就是因为把周期广播当成了普通扩展广播来处理。方向错了后面的努力全是白费。BlueNRG-LP 本身是一款很稳定的 BLE 5 SoC扩展广播扫不到大概率不是芯片问题。把 PHY、过滤策略、事件解析这三个点逐一理清问题基本都能解决。如果你也刚好卡在扩展广播这一步希望这份笔记能帮你省下那一个下午。