
做过物联网硬件项目的朋友应该都有同感选通信模组这件事最能决定项目的生死。尤其是做电池供电的温度监测设备既要无线连接稳定又要把功耗压到微安级别还得考虑产线调试、手机直连、成本控制……每一项都是坑。我最近把一个温度监测产品从方案调研做到小批量试产最终核心器件敲定了U-blox的BLE模块这个决策过程踩了不少弯路也积累了不少一手经验。今天把这个项目完整拆一遍从选型逻辑、硬件设计、BLE协议细节到固件调优再到Android、C#上位机对接把能直接参考的东西全写出来。先说清楚这个项目是干什么的一款长时间在野外或冷链环境中工作的温度记录仪内部用电池供电需要定时采集温度数据通过BLE广播或连接方式把数据传给手机App或者上传到附近的网关转发到云端。设备的典型工作场景包括药品冷链运输、机房温湿度监测、农业大棚、实验室环境记录等。核心诉求有三个一是功耗必须可控一颗纽扣电池能撑几个月到一年二是无线连接要可靠不能出现在冷库里面连不上手机的情况三是整体方案要方便量产天线、晶振、协议栈这些不能出幺蛾子。U-blox的BLE模块正好在这几个维度上都比较均衡所以最终成为首选。下面从整个项目的完整链路来复盘。1. 项目背景与整体方案选型1.1 核心需求解析这个项目到底要解决什么问题温度监测设备看起来简单无非就是传感器加无线模块但实际落地时要考虑的东西非常多。首先是供电约束。设备要长时间无人值守如果像手机一样天天充电客户根本不会买单。我们当时的产品定义是采用两节AA电池或者一颗CR2032纽扣电池供电目标至少连续运行6个月理想情况是1年以上。这就意味着整机平均电流得控制在几十微安以内峰值电流也要尽量压低无线模块的待机电流、广播电流、连接电流、发射功率每一项都得掰开算。其次是通信方式的选择。市面上能做的无线方案其实很多WiFi、蓝牙BLE、LoRa、ZigBee、NB-IoT、Sub-1G私有协议等各有各的适用场景。蓝牙BLE的优势在于手机生态的直接支持用户拿手机就能直接读取数据不需要专门配网或者购买网关。对于冷链运输这种场景来说运输人员、仓储人员、质检人员手里都有手机App一扫就能看到温度曲线这个体验是LoRa和NB-IoT给不了的。而且BLE在低功耗设计上非常成熟广播模式、连接模式、休眠模式之间的切换很灵活非常适合电池设备。再加上U-blox本身在GPS领域积累的品牌信任度它在射频性能和协议栈稳定性上比很多小厂模块靠谱得多所以BLE成了确定方向。1.2 方案选型对比U-blox BLE模块凭什么胜出选型阶段我们也对比了不少方案包括Nordic原厂的nRF52系列方案、国产的某BLE模块、TI的CC2540/CC2640系列以及U-blox的NINA和ANNA系列。对比的核心维度是功耗参数、射频性能、协议栈稳定性、开发资源、量产可获取性、价格和认证情况。表格看一眼就明白对比维度U-blox NINA-B系列Nordic原厂方案国产低成本模块峰值发射电流5.3mA 0dBm典型类似视具体芯片普遍偏高需实测休眠电流1.4uARTC唤醒1.9uA左右经常标称很低但实测翻车蓝牙协议栈成熟稳定支持BLE 5成熟但需自己移植SDK参差不齐部分有坑射频一致性批量一致性好依赖自己layout波动大需要逐批抽检天线设计内置PCB天线或U.FL需自己设计天线部分留有天线底座FCC/CE认证模块已过认证需要整机自己去过不一定有完整认证技术支持原厂FAE响应及时社区资源多基本靠自求多福单价中高中但开发成本高便宜但隐性成本高选U-blox的核心逻辑其实是我们团队人手有限不想花大量精力在射频调优和认证上面。模块已经过了FCC和CE认证只要按参考设计画板子产线的射频一致性就有保障。这相当于把硬件设计里风险最高的环节外包给了U-blox去兜底。对于一款要量产几百上千台的产品来说省下的认证费用和技术支持成本远比模块本身的差价要高。尤其是BLE这种射频敏感度很高的技术自己layout天线稍有不慎信号强度和稳定度就会出问题量产时返工就非常难受。1.3 系统架构总览传感器到云端的三层链路整个系统的硬件架构可以分成三层来看。第一层是感知层也就是温度传感器部分。我们最终用的是数字温度传感器SHT30I2C接口精度正负0.3摄氏度分辨率0.01摄氏度相比NTC热敏电阻的方案省掉了ADC采样和线性校准的麻烦采样值直接读寄存器就能用。第二层是主控和通信层这是关键节点U-blox BLE模块在这里同时充当了主控和无线传输的角色模块内部有一颗Arm Cortex-M4的处理器以NINA-B1为例温度数据的采集、存储、协议处理、广播和连接管理全部在模块里完成相当于一颗芯片干了两颗芯片的活。第三层是应用层包括手机App、PC上位机或者边缘网关。选择用模块内部MCU而不是外挂一颗STM32是经过考虑的。外挂MCU的方案灵活性高可以选更便宜的传感器和存储芯片但代价是系统复杂度上升、功耗控制难度加大、代码量翻倍。而BLE模块集成了MCU、射频前端、协议栈所有关键功能都在一个器件里完成硬件设计大幅简化调试时只需要面对一个编译工具链。对于温度监测这种业务逻辑相对固定、不需要复杂算力的产品来说U-blox模块的集成方案明显更合适。它留出了充足的GPIO未来如果要扩展湿度、气压传感器也还能腾出引脚来接。2. U-blox BLE模块选型与核心参数解读2.1 模块家族梳理NINA和ANNA怎么选U-blox当前主流的BLE模块主要有三个系列NINA-B系列、ANNA-B系列和NORA-B系列。NINA-B系列主打通用型体积稍大但引脚资源丰富适合做主控ANNA-B系列是超小封装面积只有NINA-B的一半不到适合空间极紧凑的可穿戴设备NORA-B系列则是基于nRF5340芯片支持BLE Audio等新特性性能更强劲但价格也更高。我们最后用的是NINA-B1它基于Nordic nRF52832芯片内置64MHz Cortex-M4512KB Flash和64KB RAM对于温度记录仪的需要来说完全够用。如果只是做最简单的透传方案也可以选ANNA-B2加上外部MCU的架构但这样功耗优化的难度就会大很多。BLE低功耗的精髓是在无线模块不工作的时候让整个系统进入深度休眠在需要上报数据的时候用尽量短的时间完成采集和发送然后继续睡。如果外部MCU和BLE模块是两个芯片两者之间的唤醒时序、电平状态、通信握手都会增加额外开销而且两颗芯片的静态功耗加在一起本来就比一颗芯片高。所以NINA-B1这种单芯片主控方案在低功耗设计上天生就有优势。2.2 模块功耗指标实测每个数字都要较真选型的时候看的是数据手册真正要信的是自己实测。U-blox官方给NINA-B1的典型参数是System Off模式下电流约1.4uASystem On且RTC运行时约1.9uA主动接收模式约3.6mA0dBm发射约5.3mA。这些数字在实验室里我们逐一验证过和手册基本吻合。但要注意实际系统的总功耗不是模块单独说了算还要加上传感器的工作电流、DC-DC的转换损耗、LED指示灯的漏电等。温度监测设备的数据上报策略我们最终定为每30秒采集一次温度每2秒发送一次广播包App连接状态下每1秒更新一次实时温度。在这种策略下整机的平均电流实测大约在35uA左右。听起来很小但用一颗220mAh的CR2032纽扣电池来算理论上可以跑7000多小时约合300天。实际考虑到电池自放电、低温环境容量下降、广播失败重试等情况保守估计能用8到9个月基本达到产品定义要求。如果把采集间隔拉长到1分钟一次平均电流还能降到20uA以内那就能轻松突破一年。2.3 天线选型与layout注意点射频不是玄学天线是整个BLE项目里最容易翻车的环节。NINA-B1提供了一个引脚可以外接U.FL座子来使用外置天线也有内置PCB天线的版本。我们为了保证整机的结构密封性温度监测设备经常要放在冷库、室外等潮湿环境选了内置PCB天线的版本这样整机外壳可以完全密封不用担心天线馈线的防水问题。layout上有几个要注意的点都是经验教训模块周围要留出净空区天线下方的所有层都不能铺铜这个区域一般至少要5mm乘10mm左右具体参照模块的硬件集成手册天线区域附近不要走高速信号线更不能有金属外壳遮挡模块的地引脚要尽量多点过孔连接到主地平面保证射频参考地完整。我遇到过的情况是第一次打板时为了节省面积把天线区域附近放了一颗电源芯片结果实测辐射距离从20米直接掉到7米后来把电源芯片挪走、天线区域清空距离才恢复正常。测温设备很多都是挂在金属货架上的金属靠近天线会造成频偏和吸收这个在结构设计阶段就要做好隔离。3. BLE通信协议与数据链路设计3.1 GATT服务结构定义温度数据怎么组织蓝牙BLE通信的核心是GATTGeneric Attribute Profile协议。设备端被称为GATT Server手机App或网关被称为GATT Client。我们需要在Server端定义若干Service服务每个Service下面有若干个Characteristic特征值。手机通过读写这些特征值来获取数据或者下发指令。我们定义的服务结构如下Temperature Service服务UUID0x1809这是蓝牙标准定义的Health Thermometer服务里面包含温度测量特征值。我们自定义扩展了实时温度值、历史温度记录两个特征值。实际使用中发现标准服务的兼容性最好iOS和Android都能直接识别所以尽量用标准UUID。Battery Service服务UUID0x180F上报电池剩余电量手机App可以根据此数值提醒用户换电池。Device Information Service服务UUID0x180A包含设备序列号、固件版本号、硬件版本号。产线调试和售后排查全靠它。Configuration Service自定义UUID写入采样间隔、广播间隔、温度单位等参数。这个服务用自定义UUID避免和标准服务冲突。每次温度测量完成后模块会主动通过Notification通知方式把数据推送给已连接的手机端。Notification的好处是手机不需要轮询数据到了模块就主动推过去功耗和实时性都更优。而历史数据因为数据量比较大我们用Read读方式手机主动发起读取请求。3.2 广播包设计不连接也能看温度对于温度监测设备来说广播模式是比连接模式更常用的工作方式。因为实际使用中很多场景只需要快速看一个当前温度打开手机App一扫描就能看到根本不需要建立连接。所以我们在广播包的数据字段里直接塞入了当前温度值、电池电量和设备状态手机端通过解析广播数据就能直接显示温度不用连接极大降低了使用门槛。广播包的格式是ADV_IND可连接广播广播间隔设为100ms到500ms之间可配置。广播间隔越短手机扫描到设备的速度越快但功耗也越高。因为我们的温度展示不需要超低延迟100ms足够了在需要省电的模式下可以拉长到1秒。广播数据里有一个坑广播包的最大负载是31字节去掉固定的Flags2字节和UUID列表若干字节留给厂商自定义数据的空间并不多。我们用了16位的自定义Service UUID加温度数据刚好能塞下设计时要精打细算。另外U-blox模块也支持iBeacon广播格式如果客户需要苹果设备在锁屏状态下也能通过“ Nearby”方式弹出温度提示可以直接切换到iBeacon模式。但这个功能本质上只是把广播数据改成苹果规定的格式数据解析还是我们自己在App里完成。3.3 连接参数优化连接间隔与从机延迟的权衡当手机App需要持续读取温度曲线时设备会进入连接模式。连接模式的功耗主要取决于三个参数连接间隔、从机延迟Slave Latency和超时时间。连接间隔是主机与从机之间通信的周期每隔这个时间从机就要起来监听一次主机的数据包。间隔越短数据实时性越高但功耗越大间隔越长功耗越低但数据延迟越大。我们配置的连接间隔为30ms到50ms可自适应从机延迟设为4意思是当从机没有数据要发时可以连续跳过4次连接事件去睡觉这样功耗能降到原来的五分之一左右同时又不影响温度数据的实时性。这里有一个经验iOS和Android对连接参数的接受范围不一样iOS要求连接间隔必须在30ms以上的范围而且必须是15ms的倍数Android相对宽松。如果参数设置超出系统限制手机会拒绝连接请求或者直接把参数改掉所以在固件里要充分测试两个平台的兼容性。3.4 与手机端和网关的对接实践项目里还涉及和手机端、网关的对接。这块我用三个平台分别说Android端工程结构上就是标准的BLE Central流程申请蓝牙权限、扫描设备、连接GATT、发现服务、读写特征值。有几个关键细节Android 6.0以上要动态申请定位权限因为蓝牙扫描被系统归类为定位相关操作Android 12以上还需要申请BLUETOOTH_SCAN和BLUETOOTH_CONNECT权限权限模型改得很严格。扫描回调中要根据广播数据直接解析温度如果扫描到设备之后还要进一步走连接流程建议把扫描到的设备地址保存下来stopScan之后再发起连接避免连接过程中继续扫描导致系统资源混乱。C#上位机这块主要是WinForms项目基于.NET Framework 4.7.2怎么实现BLE通信的问题。正经答案是.NET Framework 4.7.2没有内置的BLE API建议升级到.NET 5及以上的版本用Windows.Devices.Bluetooth命名空间直接操作。如果你的项目实在不能升级框架有两个可行方案一是引用Microsoft.Windows.SDK.Contracts包通过它调用WinRT的API二是借助第三方库比如InTheHand.Net.Bluetooth它对经典蓝牙和BLE都有支持但商业授权需要确认。我们最终选择了升级到.NET 6的方案因为WinRT的API在WinForms里用起来比较顺手事件模型和异步方法都很完善BLE扫描、连接、读写的代码写起来很清晰。ESP32-S3网关这个方向主要是给没有手机App使用习惯的大客户准备的。ESP32-S3集成了WiFi和BLE可以用BLE和NINA-B1模块组网然后通过WiFi把温度数据上传到云平台。这里有个天然的兼容性问题ESP32-S3的BLE协议栈和U-blox模块的协议栈都是基于标准的BLE协议理论上互操作没问题但如果有人在海思芯片或者乐鑫旧版SDK上做开发可能会遇到MTU协商、数据分片等兼容性问题。我们用ESP32-S3做网关时把MTU协商设为247字节一次能传完整的历史温度数据包减少了拆包和组包的复杂度。4. 固件开发与调试实录4.1 开发环境搭建与工程结构U-blox NINA-B1模块的软件开发目前主流的路线有两条一条是直接用Nordic的nRF5 SDK用C语言开发从裸机到协议栈全自己控制另一条是用Zephyr RTOS用设备树描述硬件代码风格更现代驱动也更齐全。我们选了nRF5 SDK主要是考虑到技术团队对Nordic的生态更熟悉而且U-blox的硬件抽象层和板级支持包做得相当完善直接基于他们的示例工程改就能跑通广播。工程结构上关键的几个文件是main.c负责应用主逻辑和传感器采集调度ble_service.c负责蓝牙服务和特征值的定义sensor_driver.c负责SHT30的I2C读写power_manager.c负责进入System Off模式的时机和唤醒源管理nfc_pairing.c是U-blox模块的一个特色NINA-B1支持NFC配对手机一贴就能自动完成BLE配对这个功能在我们后续的版本里做进了包装盒客户开箱体验好了很多。4.2 核心实现传感器采集与低功耗调度温度采集流程的代码逻辑可以简单描述为模块从System Off模式被RTC闹钟唤醒初始化I2C、读取SHT30的温度值、检查是否需要保存历史数据、更新广播数据、然后立刻回到System Off模式等待下一个唤醒周期。整个唤醒到再次休眠的过程实测大约耗时4ms其中I2C读取约2ms数据更新和运行时间约1.5ms剩余的时间是协议栈处理和系统恢复时间。有一个很容易被忽略的细节SHT30上电后默认是Sleep模式要发送命令才能唤醒进入单次测量模式。如果你在初始化之后立即读取数据需要预留足够的转换时间SHT30在最高精度下需要约15ms的转换时间。如果用默认的周期采集模式传感器会自动以设定频率测量但这样传感器自身在每个周期都会耗电拉高了整体平均功耗。所以我们专门写了一个函数用单次触发模式唤醒 - 发送测量命令 - 等待15ms转换 - 读取结果 - 传感器进入休眠。这样传感器只在测量瞬间耗电其余时间完全休息电流消耗可以忽略不计。4.3 固件联调和日志输出串口打印的高级用法调试阶段串口打印是救命的。U-blox模块的UART口就是标准的日志输出通道可以打印蓝牙栈的错误码、连接事件、RSSI值等。但这里有个坑如果你用UART打印日志模块就不能同时用UART做透传或数据通信引脚冲突。我们的方案是调试阶段专门留出一个UART口做日志输出量产版本通过配置宏关掉日志打印把UART口释放出来以备未来外接其他传感器。日志输出时建议把BLE连接状态的每个回调都打印出来连接成功、连接断开、MTU更新、数据发送完成、加密状态变化等。BLE的诡异问题往往藏在事件回调的某个角落比如连接后一段时间自动断开很可能是因为连接超时参数配置问题不是射频问题。打印日志能帮你快速定位是协议栈问题还是应用逻辑问题。4.4 功耗调优流程实测数据与电池寿命计算功耗是整个项目最磨人的部分。我们先用J-Link的功耗测量工具测了整机在不同模式下的电流曲线然后逐项优化第一版整机平均电流是87uA明显偏高。排查后发现一个重要元凶GPIO浮空漏电。I2C的两个引脚在没接传感器的时候处于浮空状态会有几百纳安的漏电累计起来不可忽略。解决方法是把I2C引脚在休眠前全部设置为输出低电平唤醒后再恢复为开漏模式。这一项优化平均电流降了约8uA。第二版又把LED指示灯去掉了。原本设计了一个状态指示灯每5秒闪一下LED峰值电流20mA虽然只闪3ms但平均电流增加了4uA左右。对于不依赖视觉反馈的IoT设备LED能省就省可以通过手机App查看设备状态。最终整机平均电流稳定在35uA左右。电池寿命的计算方式是电池容量 / 平均电流 理论续航时间再乘一个0.7到0.8的安全系数因为电池的自放电、冷库低温下容量衰减、广播重试增加、RSSI波动导致的发送功率升高等因素都会缩短实际寿命。CR2032标称容量220mAh除以35uA约等于6285小时也就是262天再乘0.75实际预估约197天。AA电池可用2400mAh的碱性电池同样的平均电流能跑4年多所以最终产品默认使用两节AA电池供电。5. 常见问题与排查技巧实录5.1 射频表现不达标天线区域和电源解耦的教训第一次整机测试时无遮挡环境下的通信距离只有6米远远低于预期的20米。排查过程是先用另一块U-blox官方开发板做对照排除是模块本身的问题然后把我们的板子和开发板放在同一个位置逐步移开外壳、电池、金属支架发现外壳一盖上信号立刻变差3到4米怀疑是外壳材质或内部金属件的问题最后拆开检查发现天线区域的正上方正好是外壳的一块金属加强筋这是结构设计时为了增加外壳强度加进去的但完全遮挡了天线。处理方式是结构上把金属加强筋改成塑料筋同时在天线投影区域的外壳上做了一块凹槽增加天线周围的空间高度。改完再测无遮挡距离恢复到22米。这个教训很值得记下来结构设计和硬件设计必须同步评审尤其是天线位置每一版结构件都要自己先看一下天线的净空和遮挡情况不要等打样回来再发现。5.2 连接不稳定、频繁断开连接参数与干扰排查在客户现场出现过温度记录仪和手机之间连接两三分钟就自动断开的现象。排查思路先看是否所有手机都复现——如果只有特定品牌复现多半是手机系统对BLE连接参数有限制再从固件侧抓连接事件回调日志看断开的错误码是0x08超时还是0x13远程断开。我们的情况是0x08超时说明连接参数协商出了问题手机和模块之间的连接间隔或从机延迟设置超出了手机支持范围。解决方法把连接间隔配置改为自适应先按30ms请求如果手机协商失败就自动放宽到50ms同时增加Supervision Timeout到4秒这样即使偶尔丢包也不会立刻断开。另外还要注意2.4GHz频段的干扰蓝牙和WiFi同频段工作在机房、地铁、商场这种人流密集的地方干扰会导致频繁重传功耗和稳定性都会变差。我们最终在广播模式里增加了跳频算法的依赖——BLE协议栈本身就支持跳频但如果用的信道正好被WiFi占死还是要靠时间规避。用户手册里建议客户在温度监测区域内尽量部署5GHz频段的WiFi路由器减少同频段的WiFi干扰。5.3 手机扫描不到设备兼容性排查清单Android手机扫描不到设备先说一个常见锅Android的蓝牙扫描是批量返回结果的不是每发现一个设备就立即回调一次而是扫描窗口结束之后统一回调。如果你在回调里处理的时间太长会导致后续结果丢失表现为“明明模块在广播但手机就是扫不到”。解决方法是把扫描结果处理放到独立线程不要阻塞回调。另一个很容易忽略的点BLE广播数据和扫描响应数据里有厂商自定义字段时有些Android机型对厂商ID校验很严格只接受已注册的厂商ID不接受我们自定义的厂商ID区段。可以把厂商ID换成蓝牙SIG分配的公共ID或者干脆把温度数据放在扫描响应的Service UUID列表里。我们最后选择了用标准服务UUID的方式来宣告设备存在兼容性好了很多。5.4 温度数据跳变和漂移传感器布局与滤波算法温度数据偶尔会有跳变从25度瞬间跳到28度再跳回来这大概率是传感器布局问题不是BLE通信问题。SHT30传感器放在PCB边缘、靠近发热器件比如MCU的DC-DC电源、模块的射频功放时会受到局部热源影响。我们把这些热源和传感器之间拉开距离同时在传感器周围的地平面上加了一些热隔离槽数据就稳定了。如果是在冷库等低温场景使用温度传感器的精度会受到环境湿度影响。SHT30的说明书中写了在湿度较高的环境中PCB表面可能会发生凝结导致温度测量值短暂偏离。这个目前没法完全消除只能靠涂三防漆和增加传感器贴装高度来缓解。软件侧我们加了一个简单的滑动平均滤波对连续读取的5次温度值取中位数跳变问题基本就消除了代价是响应速度慢了一秒钟但在温度监测场景中完全可接受。5.5 产线测试与校准流程量产时每台设备都要过一道测试工序。我们做的是通过工装的UART口发送测试指令让模块进入连续广播模式产测电脑连接一个BLE USB dongle扫描并校验广播数据中的温度值是否在一个合理范围内同时用一台安装好的标准温度计和待测设备放在同一环境里比对。一个关键细节是产测时测试环境温度要保持稳定最好用恒温箱否则校准出来的基准温度本身就有偏差。BLE设备的MAC地址是芯片自带的不需要额外烧录每颗芯片有全球唯一的MAC。但U-blox模块在出厂时默认的MAC地址范围可以追溯这个信息要保存到用户手册里方便售后排查。如果后续客户要求设备信息可追溯可以在module的Flash里写入自定义的序列号和包装盒上的二维码对应起来。最后分享几点实操心得做这个项目最大的体会是选BLE模块不只是选一颗芯片而是选一套完整的生态支持体系。U-blox模块虽然贵一点但它把射频设计和认证问题都兜底了让我们能把精力集中在应用逻辑和功耗优化上这省下的隐形成本非常可观。再分享一个小技巧在U-blox模块的RTC闹钟唤醒周期里把广播间隔和采集频率做成可动态调整的根据环境温度和电池电压动态变化。比如电池电压降到2.0V以下时自动把采集间隔从30秒拉长到5分钟能多撑好几周。这个策略在很多客户现场非常受欢迎。实际测试数据上这套方案最终小批量试产的100台设备连续运行3个月无故障通信成功率在冷藏库环境中达到99.8%。后续扩展方向还可以加入湿度传感器、气压传感器、加速度计甚至事件记录的云同步功能U-blox的NINA-B系列模块的引脚资源和Flash空间都还有富余未来扩展的空间还很大。