骑行编队出去拉练,最怕的不是腿不行,而是码表上的心率、踏频、速度一路飘红掉线。车队十几个人,每个人身上三四个蓝牙传感器,前前后后拉开一两百米,设备一断连,数据全乱。之前用HC-05、JDY-31这类经典蓝牙模块做过一版监测终端,结果只要距离拉开超过五十米就开始丢数据,市区路段更是频繁掉线。这次换上了Arad Connectivity的MN54L模块(基于Nordic nRF54L-15芯片),专门做了一轮“队列脱队监测”实验,重点对比1M和125K两种PHY下的表现。这篇就把整个测试思路、环境搭建、固件配置、实测数据和踩坑过程完整记录下来,给同样在做骑行编队蓝牙监测或者低速远距离BLE应用的朋友一些参考。
先说结论:125K PHY(也就是LE Coded PHY里的S=8编码)在开阔直道上的脱队监测距离能到300米以上,而1M PHY在同样条件下只有120米左右。但125K不是白给的,延迟、功耗、空中包时长都明显增加,而且它对天线方向更敏感。后面我把每个环节拆开讲。
1. 项目背景:骑行的“脱队监测”到底要解决什么问题
1.1 骑行场景里的蓝牙连接痛点
骑行车队的通信和监测需求和室内场景完全不一样。室内蓝牙设备通常距离近、遮挡少、设备固定,而骑行编队是动态的:领队和收尾之间可能拉开一两百米,中间还隔着其他骑手的人体和自行车金属车架,路边有树木、护栏、桥墩,甚至还要过隧道。这些因素反映到BLE链路上,就是RSSI跳崖式下跌、重传率飙升、最终连接超时。
所谓“队列脱队监测”,我在这套系统里定义成两层含义。第一层是物理连接层的脱队:某位骑手的蓝牙设备(心率带、踏频计、码表、尾灯雷达)和监测终端之间的BLE连接断开,或者广播信号消失;第二层是队列位置层的脱队:骑手和设备明明还连着,但距离监测终端太远,RSSI低于阈值,从整个骑行队列的拓扑结构上看已经“掉出队尾”了。这两层合起来,才是完整的脱队监测。
用老模块做这件事很难受。HC-05是蓝牙2.0 SPP,没有BLE,距离短、功耗高;JDY-31虽然是BLE 4.0,但只能做透传,PHY固定为1M,也没有办法在连接过程中动态切换编码方式。它们能在桌面和手机之间传个小数据,但想在几百米的动态骑行环境里持续跟踪十几个设备,完全带不动。所以关键不在于“能不能连上”,而在于“在弱信号、多径、动态遮挡条件下能不能稳住并给出预警”,这就需要新一代BLE芯片的支持。
1.2 为什么选MN54L / nRF54L-15
Arad Connectivity的MN54L是一款基于Nordic nRF54L-15的贴片式BLE模块。nRF54L-15是Nordic新一代低功耗蓝牙SoC,支持BLE 5.4,1M、2M、500K Coded、125K Coded四种PHY,接收灵敏度在125K Coded模式下比1M模式有很明显的提升。它内部是Arm Cortex-M33加一个RISC-V辅助核心,Flash 1.5MB,RAM 256KB,跑Zephyr RTOS(nRF Connect SDK)。
选它的原因有几个。第一,nRF54L-15的无线电前端动态范围和抗干扰能力比nRF52840那一代强了不少,这在户外多设备环境下很关键。第二,它支持连接态下的PHY切换,也就是说可以在运行中根据RSSI表现自动从1M切到125K,或者反过来,这给脱队监测提供了很灵活的机制。第三,Arad Connectivity的模块把晶振、电感、天线匹配都做好了,有完整的参考设计,直接贴着PCB天线就能用,不用自己折腾射频部分。对于做骑行监测终端的人来说,模块自带天线比外置天线在车把上安装更方便,也不容易被雨水和振动影响。
这里也顺带说明一个容易混淆的概念:BLE里的PHY和以太网里的PHY芯片不是一回事。以太网PHY处理的是UTP线缆上的电气信号,调试时经常要面对MDIO接口、link status、serdes lane这些概念;而BLE PHY是2.4GHz射频信号的调制编码方式,不存在MDIO这种寄存器接口。所以网上搜“PHY芯片”“linux phy 不使用mdio”这些内容时,要先把上下文分清,别把两套东西搅在一起。
1.3 测试目标与判定指标
这次实验的目标很明确:评估MN54L在真实骑行队列场景下的脱队监测能力,并为后续固件里的自动PHY切换策略提供数据依据。我定了几个可量化的指标:
| 指标 | 定义 | 阈值 |
|---|---|---|
| 连接脱队距离 | 终端与远端设备之间断链时拉开的最大距离 | 1M和125K分别记录 |
| 信号脱队距离 | RSSI低于-95dBm且持续3秒以上的距离 | -95dBm为警告线 |
| 丢包率 | 连接态下每10秒统计一次的重传/丢失数据包比例 | 超过20%视为链路劣化 |
| 误报率 | 没有离开队列但被判定为脱队的次数 | 越低越好 |
| 功耗 | 连续监测状态下模块平均电流 | 记录对比 |
为什么用RSSI加持续时间的双重判定,而不是只看连接状态?因为BLE在临界距离上会反复“断开-重连”,直接看连接状态会得到一堆抖动。后来实际测试也证明,加入持续时间和重传统计之后,误报率从肉眼可见的频繁抖动降到了可以接受的水平。
2. 硬件与测试环境准备
2.1 MN54L模块硬件特性速览
MN54L模块本身很小,适合做到骑行码表、车把支架或者头盔尾灯内部。我在实验里用的是Arad Connectivity官方的评估板,板上已经把nRF54L-15的最小系统、天线、编程调试口都引出来了,直接插杜邦线就能接串口和供电。主要硬件参数如下:
- 主控:nRF54L-15,双核(Cortex-M33 + RISC-V协处理器)
- 工作频段:2.4GHz,支持BLE 5.4所有PHY
- 发射功率:+8dBm(实际测试用+4dBm和+8dBm两档)
- 接收灵敏度:1M模式下约-97dBm,125K Coded模式下约-105dBm(模块数据手册口径)
- 供电:1.8V到3.6V,内置DC-DC,评估板上用3.3V LDO
- 接口:UART、SPI、I2C、GPIO等
考虑到骑行场景是户外、可能淋雨、有连续振动,我没有把测试设备固定在车上,而是放在双肩背包的胸带位置。这个位置离人体最近,天线朝向基本朝前,和骑手实际安装码表、心率带的位置也比较接近,能代表真实使用情况。
2.2 测试场地、路线与设备挂载
实验选在一条城市近郊的封闭骑行绿道,路面平坦,两侧是农田和少量树木,周边2.4GHz干扰源较少。整条路线分为三段:
| 路段 | 特征 | 测试意义 |
|---|---|---|
| A段:开阔直道 | 约800米,无遮挡,两侧无金属围栏 | 测试极限距离和RSSI下降曲线 |
| B段:弯道林荫道 | 连续弯道,两侧有树木和护栏 | 测试多径和人体遮挡影响 |
| C段:桥下通道 | 约100米,顶部和两侧是混凝土结构 | 测试遮挡和反射极限 |
参与测试的设备包括:一个MN54L终端(装在背包胸带),三个远端信标节点(每30ms广播一次,载波为iBeacon格式的32字节载荷),一对连接态数据采集设备(模拟真实码表传感器,通过UART把RSSI和重传统计打印出来)。所有设备的MAC地址固定,便于在扫描结果里区分。
这里有一个非常重要的安装细节:远端设备的天线方向要和终端保持基本平行。蓝牙天线在PCB上通常有明确的方向性,竖直和水平放置的增益差可以达到6-10dB。我第一次测试时把信标竖着放进骑行服后袋,结果距离只有平放的一半,后面在5.1节还会细说。
2.3 固件初始化:PHY切换与扫描参数配置
固件基于nRF Connect SDK(Zephyr RTOS)环境,Nordic新SDK对nRF54L-15的支持已经比较完善,但有些API还是延续了nRF52840时代的风格。最关键的是PHY配置,连接建立后可以主动发起PHY更新:
static void set_coded_phy(struct bt_conn *conn) { const struct bt_conn_le_phy_param phy_param = { .options = BT_CONN_LE_PHY_OPT_CODED, .tx_phy = BT_CONN_LE_PHY_CODED, .rx_phy = BT_CONN_LE_PHY_CODED, }; int err = bt_conn_le_phy_update(conn, &phy_param); if (err) { printk("PHY update failed: %d\n", err); } }注意PHY更新不是一次性成功的。对端设备如果也支持Coded PHY,通常会在几个连接事件内完成协商;如果对端是旧设备(比如只支持1M的传感器),更新会直接失败,因此固件里要做回退机制,不能因为一次失败就把连接标记为脱队。我在初始化回调里对PHY更新失败做了计数,连续失败超过3次才调整监测策略。
扫描参数方面,脱队监测需要兼顾发现新设备和保持现有连接。对广播信标的扫描,我采用了中等间隔:
struct bt_le_scan_param scan_params = { .type = BT_LE_SCAN_TYPE_ACTIVE, .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, .interval = 100, /* 100 x 0.625ms = 62.5ms */ .window = 50, /* 50 x 0.625ms = 31.25ms */ };这个配置在静止测试中表现很好,但在骑行场景下有个问题:扫描窗口和连接事件会抢占无线电,导致RSSI采样不稳定。后面在4.2节和5.2节会给出调整后的方案。
3. 脱队监测机制:从“断连”到“脱队”的判定
3.1 什么是脱队监测:不是简单看断连
很多人在做设备监测时,只盯着connection disconnected回调,一旦断开就上报脱队。这在骑行队列场景下不够用,原因有三。
第一,BLE连接断开前通常有长达几秒的重传和超时过程,等到回调触发,骑手已经骑出去几十米了,预警意义大打折扣。第二,有些场景下连接没有断开,但信号已经弱到数据包基本传不回来——丢包率超过一半,RSSI低于-100dBm,这种“伪连接”状态对监测毫无价值。第三,广播型信标(比如心率带的广播模式)不存在连接断开这回事,只能用“最近一次收到广播的时间间隔”来判断是否失去联系。
所以我把脱队监测做成一个状态机,输入是RSSI采样值、重传次数、断连事件,输出是三级状态:正常(Normal)、警告(Warning)、脱队(Lost)。警告状态下终端会通过串口输出提醒,同时尝试主动切换PHY或提高重传优先级;Lost状态下才真正上报“脱队事件”。
3.2 状态机与RSSI阈值设计
状态机最核心的是阈值设计。我用的两组阈值分别是:
- 警告阈值:RSSI低于-85dBm,或者连续10秒丢包率超过20%
- 脱队阈值:RSSI低于-95dBm持续3秒,或者连续5次扫描没收到广播,或者连接超时事件触发
为什么选-85和-95这两个数?从实测看,1M PHY在开阔直道上的RSSI随距离衰减曲线大概是:50米处约-70dBm,100米处约-85dBm,130米处接近-95dBm。如果把警告线设成-80dBm,会在80米左右就频繁触发,太保守;如果设成-90dBm,留给骑手的反应距离又太少。125K PHY因为编码增益,同样距离下RSSI读数会比1M高3-6dB(注意不同芯片对Coded PHY信号强度上报方式的差异),所以需要按PHY分别设定阈值。
状态机实现上我用了一个简单的滑动窗口。每收到一个连接事件的数据包就记一次RSSI,窗口长度20次,取中位数而不是平均数。平均数容易被瞬时尖峰带偏,中位数抗抖性更好。这个看起来很小的细节,在实际骑行测试里效果差距很大,不做中位滤波的话弯道路段几乎全程都在误报。
3.3 1M和125K PHY的监测策略差异
两种PHY的监测策略其实应该有区别,不能拿一套逻辑硬套。
1M PHY适合近距离快速响应。它的每个数据包在空中占用时间短,连接事件可以很密集地发生,RSSI采样率高,所以监测逻辑可以激进一点,比如连续3次RSSI低于-95dBm就可以上报Lost。车队在编队训练、市区穿行时,距离一般不超过100米,用1M PHY完全够,而且延迟低、功耗低。
125K PHY则是一个双刃剑。它的接收灵敏度高,理论上能覆盖300米以上的距离,但代价是每个数据包在空中占用时间变成1M模式的8倍(S=8编码),连接间隔里的可用时隙变少,RSSI采样频率也跟着下降。我还发现,在Coded PHY下,距离远了以后虽然能解出部分数据包,但误码率高的区域会出现“断续收到广播”的现象——一会儿收到一个-100dBm的包,一会儿又完全静默。如果监测逻辑只看“最近一次收到广播的时间”,很容易误判。因此125K模式下的Lost判定,我改成了“连续10次扫描窗口内未收到任何有效广播,且无连接数据”,比1M模式的判定宽松得多。
另外,PHY切换策略上,我在同一场景做了测速对比:当RSSI下降到-85dBm时自动发起1M到125K的切换,初次切换时间平均需要约350ms,这个过程中会有一小段数据空洞。在骑行监测里可以接受,但如果做实时心率显示就不行了,所以切换条件还需要根据应用场景权衡。
4. 实验过程与数据对比
4.1 开阔直道:1M和125K的极限距离对比
A段测试是在无风、无干扰的清晨做的。远端信标固定在骑手后背,终端放在跟随车辆副驾,两车同速行驶,间距从30米逐步拉开到500米。每拉开20米记录一次RSSI,拉到连接断链或广播丢失为止。
1M PHY的实测结果是:距离80米以内,RSSI基本稳定在-65到-75dBm之间,丢包率低于5%;拉到120米时,RSSI进入-90到-95dBm区间,丢包率快速增长到30%上下;到140米左右连接彻底断开。也就是说1M PHY在这个场景下的“可靠监测距离”大约是100米,超过就开始报警。
125K PHY的结果让人眼前一亮。同样条件下,200米时RSSI仍能稳定在-90dBm左右,丢包率只有6%;拉到320米时才出现第一次超过5秒的静默;350米后广播几乎完全收不到。这个距离表现符合Coded PHY的预期,但已经明显超出普通低功耗蓝牙传感器的覆盖需求。对于一条十几人的骑行编队来说,300米意味着哪怕收尾骑手掉到队尾,终端仍然能感知到他的信号,预警时间非常充裕。
| 场景 | PHY | 可靠信号距离 | 脱队判定距离 | 丢包率拐点 |
|---|---|---|---|---|
| 开阔直道 | 1M | 100米 | 140米 | 120米 |
| 开阔直道 | 125K | 250米 | 350米 | 280米 |
| 弯道林荫道 | 1M | 45米 | 80米 | 60米 |
| 弯道林荫道 | 125K | 90米 | 150米 | 110米 |
| 桥下通道 | 1M | 20米 | 50米 | 35米 |
| 桥下通道 | 125K | 40米 | 80米 | 60米 |
4.2 弯道、林荫道与桥下:多径和人体遮挡才是真杀手
B段弯道测试把两种PHY的差距拉得更明显。林荫道的两侧树木和弯道本身造成严重多径衰落,终端在弯道外侧时,直射路径频繁被树干和护栏阻断。1M PHY在45米左右就开始频繁丢包,RSSI跳变超过20dB;125K PHY虽然也受多径影响,但编码增益让数据包在同等信号强度下更容易被解出,把可靠距离推到了90米。不过两种PHY都有一个现象:在弯道内侧,也就是远端设备被弯道墙挡住的时候,RSSI会瞬间跌到-100dBm以下,然后下个弯道又恢复。这种情况下如果监测逻辑只看瞬时RSSI,误报率会非常高。我的处理方式是引入“方向记忆”,也就是上一次稳定RSSI的值保留5秒,在这个窗口内即使瞬时RSSI掉到底,也只记Warning不记Lost。
C段桥下通道是另一个极端。混凝土结构对2.4GHz信号的反射和吸收很厉害,两种PHY进入通道后RSSI都急剧下跌。1M PHY在通道入口处就基本失联,125K PHY还能在入口附近维持极弱信号,但进入通道中段也断了。这说明脱队监测在遮挡环境下不能全指望PHY,物理层的极限摆在那里。真要解决桥下、隧道场景,只能加基站节点或者利用骑手之间的中继转发,这也是我后面想扩展的方向之一。
4.3 125K PHY的代价:延迟与功耗实测
125K PHY提升距离不是没有代价的。我单独测了两种PHY在连接状态下的数据延迟和模块功耗,这对实际产品的电池寿命影响很大。
数据延迟方面,1M PHY下,连接间隔设为30ms时,端到端延迟大约35ms;切到125K PHY后,同样连接间隔下延迟飙升到120ms以上,因为每个包在空中要占更长时间,一个连接事件里能处理的数据量变小了。如果连接间隔放宽到50ms,125K PHY的延迟会接近200ms。这个水平做设备状态监测可以,做语音或实时控制就不行。
功耗方面,我用一个电流探头夹在MN54L评估板的3.3V输入端,记录1分钟平均电流。1M PHY连接状态,发射功率+8dBm,连接间隔30ms,广播接收开启,实测平均电流6.8mA;切到125K PHY后平均电流升到9.5mA。换算下来,一块500mAh的锂电池,1M模式下能撑约70小时,125K模式约50小时。考虑到125K模式主要用在脱队预警和低速远程监测,实际固件应该设计成“平时用1M,RSSI下降后临时切125K”,而不是全程固定在125K。
5. 实测中踩到的坑与排查记录
5.1 天线方向性造成的“假脱队”
这是整个测试里最容易误导人的坑。第一轮测试我把远端信标竖着塞进骑行服后背口袋,终端装在背包胸带,结果1M PHY在60米外就开始报警,明显不符合预期。排查时把设备从车上拿下来,放在三脚架上同一个高度测,距离又恢复正常。问题出在天线极化方向上:PCB天线是垂直极化设计,竖放时辐射方向图和水平放置差异巨大,骑手身体遮挡加上天线失配,实际增益掉了将近10dB。
解决方式是统一设备的天线朝向,并做固定夹具。之后所有距离测试都在信标水平放置、终端天线平面面向骑手的前提下进行。这个教训也提醒我:任何无线测试,第一先控变量,第二别相信“贴着人体就测不准”这种模糊说法,要用可重复的安装方式把变量固定下来。
5.2 2.4G干扰与扫描窗口博弈
骑行绿道虽然相对干净,但测试时正好有环卫工人用对讲机在附近,还遇到一次疑似蓝牙音箱的干扰。干扰最明显的表现是:终端在150米开外仍然能收到广播,但RSSI读数从-85dBm突然跳到-105dBm,而且不带任何过渡。这其实是信道阻塞导致重传内爆,干扰源把某些信道上的射频底噪抬高了,BLE自动跳频到被干扰的信道后,重传次数激增,RSSI统计被重传包稀释。
这段时间我做了两组实验。一组把扫描窗口从31.25ms改到62.5ms,发现RSSI波动略有改善,但功耗上升了约1.2mA;另一组把扫描间隔从62.5ms改到100ms,RSSI采样率下降,脱队误报时间延迟了约1秒。权衡之下,扫描间隔保持100、窗口50是当前场景下的折中点。如果是市区强干扰环境,还需要结合信道分类(Channel Classification)和周期广播(PA)事件做更精细的调度。
5.3 nRF54L-15相关配置注意事项
用nRF54L-15做这套东西,有几个坑值得单独拿出来说。
第一个是DC-DC配置。nRF54L-15支持DC-DC和LDO两种供电模式,默认Zephyr工程如果没有把DC-DC开关打开,射频发射时电流会明显偏高。我一开始用评估板默认配置,发射电流比标称多了3mA,后来在prj.conf里打开CONFIG_PM_DEVICE_DPMS_ENABLE相关的DC-DC配置才正常。模块数据手册上的功耗数据基本都是基于DC-DC模式测的,切记核对。
第二个是RISC-V辅助核心不参与射频调度,但会影响系统时钟。如果你在nRF54L-15上开了协处理器跑传感器采集,主核的无线电事件延迟可能会因为总线竞争而变大。我在早一版固件里让协处理器每5ms采集一次IMU数据并写共享内存,结果BLE广播时间戳抖动达到几十毫秒,排查了很久才发现是总线冲突。最终把协处理器采集频率降到100Hz,抖动才降下来。
第三个是调试串口和射频共存。评估板如果通过UART输出调试日志,在高波特率(比如921600)下,UART中断会抢占BLE协议栈的线程,导致连接事件偶尔错过。实测中把串口波特率降到115200,并在扫描回调里不打印任何日志、只记录状态标志位,脱队监测的稳定性明显上升。这个经验对所有用Zephyr做BLE的嵌入式项目都适用:不要在中断上下文和协议栈回调里做耗时打印。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方式 |
|---|---|---|
| 60米内RSSI突然掉到-100dBm以下 | 天线方向不对、人体遮挡 | 固定设备朝向再复测 |
| 连接频繁断开但广播正常 | PHY切换失败导致重传超时 | 打印PHY更新返回码,加回退逻辑 |
| 125K PHY下偶尔收不到广播 | 空中包太长、扫描窗口重叠概率下降 | 增大扫描窗口或改用定期广播 |
| 终端耗电比预期高很多 | DC-DC未开启 | 核对prj.conf和模块手册参考电路 |
| RSSI读数剧烈抖动 | 强干扰源或扫描窗口过短 | 开启信道分类,调整扫描参数 |
| 桥下/隧道必掉线 | 物理遮挡和多径反射 | 增加中继节点,物理层攻坚不现实 |
6. 实测体会与后续可以玩的方向
一圈测试下来,我对MN54L这款模块和nRF54L-15芯片的定位有了比较清楚的认识。它最大的价值不在于单点通信距离有多远,而在于把“远距离感知”和“低功耗常驻监测”这两件事揉在了一起。骑行编队脱队监测这种需求,过去用老蓝牙模块做不到,用手机APP做又太傻太耗电,用MN54L做成了一个小终端,就能在不干扰骑手的情况下实时盯住整个车队的信号地图。
我个人在实际操作中的体会是:不要迷信115K PHY的标称灵敏度,Coded PHY在真实移动场景下依然逃不开多径衰退,它的优势是给系统增加了冗余余量。最稳妥的设计是“1M为主,125K为辅”的动态策略——RSSI好就全速跑,RSSI一旦跌到警告线,立刻临时切Coded模式争取额外的时间窗口。这个逻辑在这个测试里已经验证有效,误报率比固定单一PHY低很多。
最后再分享一个小技巧:测试时给每个远端设备设置不同的蓝牙MAC地址,并用固定间隔广播,这样终端在扫描到之后可以根据MAC和时间戳计算“最后一次收到广播的时间”,进而判断脱队。这个方法不需要建立连接,功耗也低,适合大量设备的队列监测场景。后续我打算在固件里把连接态监测和广播态监测分开,让终端同时管理若干连接设备和大批广播信标,做一版完整的“骑行队列雷达”,到时候再回来更新数据。