
1. 项目背景与硬件方案选型思考1.1 宠物健康追踪设备的真实需求拆解这几年宠物经济越来越火身边做智能硬件的朋友不少都在往宠物赛道看。宠物健康追踪器这类设备核心要解决的问题其实很明确让主人不用去宠物医院就能日常掌握宠物的活动量、心率、体温、睡眠质量这些关键健康指标同时及时发现异常行为趋势比如长时间不活动、心率持续偏高等。我当时做这个项目最开始的时候需求清单列了这么几条记录宠物每天的活动量区分走路、跑跳、休息状态光学心率监测能看静息心率的变化趋势体温采集发热能第一时间提醒续航至少两周以上最好能做到一个月主人手机端实时查看数据历史趋势要能存能查支持后续固件升级不能每次改功能都让用户换设备这里最关键的一个约束就是功耗。宠物不会配合你充电设备挂在项圈上或者藏在胸背带里主人大概率不会天天记得摘下来充电。所以整个方案选型的第一优先级就是“低功耗”这三个字。1.2 为什么最终选了Nordic的BLE SoC市面上做低功耗无线方案的选择其实不少我当时对比了好几个方向方案优势劣势适不适合这个项目Nordic nRF52832/nRF52840协议栈成熟、文档全、生态好、功耗极低价格偏高、Flash/RAM相比MCU方案不算大非常适合首选ESP32-S3WiFiBLE双协议、算力强、内存大功耗偏大、睡眠电流较高、启动逻辑复杂不适合纯电池设备TI CC2640/CC2642射频性能优秀、功耗低工具链比较老、社区资料相对少可以做但是开发效率不如NordicST BlueNRG封装小、价格低BLE协议栈问题排查难度大有经验可以新手慎选Dialog DA14531成本极低Flash资源紧张不适合做复杂应用适合极简产品不适合多功能追踪器最终我选的是Nordic nRF52832。原因有这么几个一个是BLE协议栈SoftDevice非常成熟而且和应用程序代码做了隔离应用崩了不会把协议栈搞挂这在调试期极其重要。另一个是还有配套的DFU固件升级方案从Bootloader到手机端工具链都现成后期维护省了大力气。还有一个是我实际测试下来nRF52832在System OFF模式下功耗不到1uASystem ON定时唤醒的运行时平均电流也能控制在10uA级别这对一款需要长时间续航的宠物穿戴设备来说是完全合格的。如果预算更充足、需要更多Flash和RAM来跑算法可以考虑nRF52840。它的Flash有1MBRAM 256KB还多了一些外设比如USB支持BLE 5.0的长距离和高吞吐模式天线调好之后实际吞吐可以到1.3Mbps左右。但就宠物健康追踪器来说nRF52832的512KB Flash 64KB RAM完全够用没有必要多花钱。1.3 关于SoC芯片启动流程和选型时的认识做硬件的人可能对“SoC”这个词不陌生但真正接触射频SoCRF SoC之后你会发现手机里的应用处理器和这种BLE单芯片的启动逻辑完全是两回事。BLE SoC内部集成了射频收发前端、基带控制器、协议栈、应用MCU、电源管理单元启动的时候有一套明确的顺序上电或者复位后芯片内部ROM里的固化引导代码先执行引导代码加载SoftDevice协议栈镜像到RAM协议栈初始化完成后再跳转到应用程序入口应用程序调用协议栈提供的API完成BLE协议栈使能和广播配置这个流程看起来简单但如果你直接操作寄存器、跳过协议栈的初始化很可能会发现蓝牙根本起不来或者空中包乱飞。所以在nRF5 SDK上做开发不管用Keil、IAR还是GCC工程配置里都一定要保证SoftDevice在前、Application在后链接脚本的起始地址也要对齐协议栈占用的Flash区域。这个属于新人最容易踩的坑。2. 硬件电路设计与电源系统的实际经验2.1 供电拓扑与DC-DC的选择nRF52832的工作电压范围是1.8V到3.6V典型用一颗锂聚合物电池或者两节纽扣电池供电。宠物追踪器体积限制比较严格我没法用太大的电池最终选的是90mAh的聚合物锂电池整机目标平均电流控制在100uA以内这样才能做到三周以上续航。供电链路我做了两级。第一级是电池直接进nRF52832的VDD但这里有一个非常重要的设计点nRF52系列内部其实是有一个可选的DC-DC降压模式的。开启这个模式之后芯片内部的高频RF PA供电由DC-DC来提供发射电流能从原来的十几毫安降到几毫安效果非常明显。具体做法是在VDD与DEC引脚之间接一个10uH的电感然后在SoftDevice初始化时调用sd_power_dcdc_mode_set(1)来使能。如果不加这个电感、不用DC-DC模式整机电流会明显偏高。我们用实际测试数据算过一笔账同样每100ms广播一次、连接间隔50ms的情况下纯LDO模式平均电流大约250uA而开启DC-DC后能降到170uA左右相当于续航直接多了快一半。这一百多微安看着不多但放大到一个月的时间维度上差别就是“一周一充”和“两周一充”的差别。2.2 射频链路与天线匹配的调试心得做蓝牙BLE设备射频调试是整个硬件环节里最考验耐心的部分。nRF52832的射频输出引脚ANT出来之后一般接一个π型匹配网络再加天线。对于量产产品直接用芯片参考设计里的元件值是能起来的但谐振点和回波损耗不会刚好落在最优位置需要用网络分析仪实测后调整。我当时做的是PCB天线。体积限制下PCB天线比如F型倒F天线比陶瓷天线更适合成本也低但设计上要注意净空区天线底下不要铺铜、周围不要走无关的线、地平面的范围要足够。S11回波损耗我调到最好的时候能到-25dB以下一般量产只要保证在-10dB以下就够用了。这里有一个特别容易被忽略的点天线匹配网络的电容电感精度是NP0/C0G档和绕线电感才会有稳定的性能。如果用X5R陶瓷电容或者叠层电感温漂和偏压特性会让匹配网络在高低温下飘移直接影响辐射功率和接收灵敏度。我踩过一次坑低温测试时发现信号差了一截后来换掉匹配电容就好了。2.3 电源纹波对蓝牙射频和模拟采集的影响热词里有一条提到“rf soc器件gen3 adc电源纹波”这个点在我们这种射频SoC加传感器的方案里同样非常关键。nRF52832内部有12位SAR ADC可以用来采集电池电压、NTC体温、传感器模拟输出但ADC的参考电压来自内部VDD如果VDD上叠加了纹波采集结果会跟着一起跳。实测下来蓝牙发射瞬间大概2ms-4ms的窗口电流会突然拉高电源轨上会出现几十毫伏的纹波。这种瞬态纹波对数字电路没影响但对模拟采集就是灾难。我的解决办法是软件上避开射频活动窗口在ADC采样前加一个时间同步等BLE事件结束之后再过几个毫秒才开始采样硬件上加大VDD的旁路电容比如4.7uF陶瓷电容并联0.1uF高频电容同时走线上把模拟采样地和射频地做好单点隔离。如果你后续想做更准确的电池电量百分比可以考虑用卡尔曼滤波EKF来处理电压数据结合库仑计做容量校正。这个在热词里也有体现但现实项目中电池SOC的估算第一版往往用OCV查表法就够了不要一上来就上EKF加了状态矩阵的调试成本会翻倍。3. BLE协议栈与固件开发的关键细节3.1 nRF5 SDK与nRF Connect SDK怎么选做Nordic开发面前有两条技术路线老的nRF5 SDK基于SoftDevice协议栈比如S132和新的nRF Connect SDK基于Zephyr RTOS。这个选择直接决定你后续的开发体验和可维护性。我的建议是如果项目要求快速出原型团队熟悉传统嵌入式开发用nRF5 SDK更稳。因为资料多、示例全、踩坑记录丰富SoftDevice的API设计也相对直观。如果项目生命周期长、后续可能要加Matter/Thread/蓝牙Mesh或者需要用到较新芯片比如nRF54系列就选nRF Connect SDK。我做宠物追踪器的时候用的是nRF5 SDK 17.1.0 S132协议栈支持CentralPeripheral但只用Peripheral。当时Zephyr版本还没有现在这么稳定开发效率上nRF5 SDK完胜。但如果你现在才起步可以仔细评估一下nRF Connect SDKNordic现在新出的示例基本都是基于Zephyr了再往后nRF5 SDK的更新只会越来越少。3.2 GATT服务结构的定义与数据格式设计宠物健康追踪器里我定义了这么几个服务和特征值服务UUID特征值说明数据类型0x180D心率测量标准心率服务Notify方式8bit BPM 可选RR间期0x180A设备信息硬件版本、固件版本、序列号字符串0x180F电池电量标准电池服务Notify低电量8bit 百分比自定义Service (UUID 0xFF01)活动量数据自定义服务Notify方式每半小时累计步数/活动强度自定义Service (UUID 0xFF01)体温数据自定义服务Notify方式16bit 温度值0.01°C精度自定义Service (UUID 0xFF01)历史数据读取自定义服务Read方式分段返回历史记录自定义Service (UUID 0xFF01)设备控制自定义服务Write方式同步时间、配置上报间隔这里有一个经验一律先考虑标准服务。心率、电量、设备信息这些iOS和Android系统层面有默认的处理逻辑用标准UUID可以省掉很多App端适配工作。自定义部分才用私有UUID而且UUID不要拍脑袋编一个最好自己定义一个固定的Base UUID比如 6E400001-B5A3-F393-E0A9-E50E24DCCA9E 这种格式后3位递增。活动量数据我用的是“每半小时一条记录每一条是一个结构化字节数组”。第0字节是状态位表示当前是走路/跑跳/休息第1-2字节是步数第3字节是活动强度等级。这样一包数据20字节能传40条记录App端收到后直接解析存数据库。3.3 广播参数、连接参数与低功耗计算BLE设备的功耗大头有三个广播、连接事件、传感器采样。广播间隔和连接间隔这两个参数在设计时就要算清楚。我当时的广播参数是广播间隔 100ms广播数据包含设备名称8字节 厂商自定义数据6字节含设备ID和电池电量发射功率 0dBm。这个配置下广播平均电流大约是 100uA 左右占空比不高手机端扫描也能稳定搜到设备。连接之后的参数我设置了连接间隔 30ms15个1.25ms单位从机延迟Slave Latency8个事件监督超时 5s。这样连接状态下从机只在每第9个连接事件里收发一次平均电流大幅下降。来算一下每个连接事件上从机要唤醒接收主机的包、然后回一个包这段射频活动时间大约 2ms按峰值电流 10mA开DC-DC时约 5mA算单个连接事件功耗约 20uA·ms0.02mAs。连接间隔30ms、从机延迟8意味每270ms才有一个事件平均电流 0.02mAs / 0.27s ≈ 74uA。再加上传感器采样的功耗整体可以控制在 120uA-150uA 以内。这里有个容易忽略的坑从机延迟越大手机端收到数据的实时性就越差。如果主人打开App想立即刷新数据而设备还在8个连接事件后才应答体感会很卡。所以我在代码里实现了一个动态调整逻辑默认空闲时用长连接间隔App发一个“开始同步”的命令后设备马上把连接参数临时切到短间隔比如 15ms同步完成后再切回来。用Nordic的sd_ble_gap_conn_param_update()可以很方便地做到。3.4 iBeacon、PAwR这些新概念该怎么看热词里出现了“ble的ibeacon”和“ble pawr”顺便聊一下。iBeacon本质上是BLE广播数据里面的一种厂商自定义格式里面放了一个UUID、Major、Minor和发射功率。宠物追踪器中如果要做室内定位或者“离主人近/远”的判断是可以参考iBeacon的思路的但我实测下来iBeacon在空旷室外能跑 50m 左右在室内穿墙后就非常不稳定做“防丢”提醒不如直接用连接RSSI阈值判断来得实在。PAwRPeriodic Advertising with Responses是BLE 5.4里引入的本质上是一种一对多的广播通信机制可以让一个主设备周期性地广播数据多个从设备在指定的时隙回传。这个技术做电子价签这种海量设备场景非常适合但宠物健康追踪这种“一个主人对一只宠物”的场景其实用不上了解一下即可。4. DFU固件升级的实现从Nordic底到手机端4.1 为什么DFU在宠物设备里是必备功能宠物健康追踪器挂在宠物脖子上你不可能让用户拿线刷机。固件有Bug、算法要优化、心率算法要调参都需要OTA升级。而且宠物医院和主人不太可能跑到售后网点去刷机所以从出生开始设备就必须支持手机端无线升级。Nordic的DFUDevice Firmware Update思路是Bootloader SoftDevice Application三个镜像分开存放升级的时候Bootloader负责接收新固件、校验、擦除和写入。nRF52832的Flash是512KB我当时做了分区区域起始地址大小内容SoftDevice0x00000152KBS132协议栈Bootloader0x2600032KBDFU引导程序Application0x28000200KB应用代码用户数据区之后剩余历史数据存储DFU时手机App把固件分包比如每包 20 字节发给设备设备写入临时区域校验CRC无误后Bootloader再执行替换和重启。4.2 手机端DFU原生App、nRF Connect还是小程序Nordic官方提供了nRF Connect App内置DFU功能研发阶段足够用。但给用户交付的话必须把DFU能力集成到我们自己的App里。我自己实际做的过程中先是在自有App里集成了Nordic官方的Android/iOS DFU库。Android端用io.runtime.mcumgr或者Nordic的Device Firmware Update库都可以后者更贴近nRF5 SDK的协议实现。iOS端用NordicDFU这个Pod支持ZIP包升级模式里面包含Bootloader、SoftDevice、Application三个镜像的合并包。这里有一个值得注意的点热词里有人问“只安装shiny.bluetoothle可以实现BLE蓝牙通信吗”这个问题背后反映的是很多人对BLE库的依赖关系不太清楚。如果你做的是 .NET 环境下的蓝牙通信单纯装一个shiny.bluetoothle是不够的它只是一层封装底层还需要依赖对应的平台原生蓝牙实现。在Windows上还需要底层蓝牙栈驱动支持。跨平台框架能帮你的只是API统一管不了系统底层。4.3 微信小程序实现设备DFU的经验设备量大了之后用户未必愿意专门下载一个App。把DFU能力塞进微信小程序理论上可以降低用户升级门槛但实际动手会发现不少问题。小程序蓝牙API有几个硬限制一次writeBLECharacteristicValue最多写 20 字节MTU默认23上传一个大固件包你需要非常小心地分包发送、每包间隔处理。我当时做了这样的逻辑先读取设备当前的MTU值如果设备支持MTU协商比如nRF52上可以协商到 247就先把MTU提到 247这样每一包可以写到 244 字节分包数量直接少了十几倍升级速度从原来的10分钟缩短到2分钟以内。另外iOS上微信小程序的BLE实现比较“黑盒”。扫描、连接、缓存很多系统行为不可控尤其是旧版本微信对蓝牙缓存处理不好经常出现调试时改了广播包、手机还是连不上。我这边是让用户把蓝牙关闭再打开才恢复的后来在微信官方社区查到这属于iOS系统蓝牙缓存问题解决方案是在扫描前加一段延迟再加上设备端广播地址变化用sd_ble_gap_address_set随机化地址能明显减少缓存命中的概率。如果你在微信小程序里做DFU我强烈建议固件包先走网络下载到手机本地再通过蓝牙传给设备不要在小程序代码包里直接塞固件因为小程序包有体积和更新时限的限制。另外升级过程中界面要显示清晰进度条并提示用户“升级期间请不要远离设备”否则中途断开设备变砖的风险会让售后压力很大。5. 手机App连接与数据解析的实操记录5.1 Android BLE工程的核心步骤做一个Android BLE连接工程流程基本是固定的获取蓝牙权限 → 扫描设备 → 连接GATT Server → 协商MTU → 发现服务 → 订阅通知 → 读写特征值。权限这块Android 12API 31之前要在Manifest里声明BLUETOOTH、BLUETOOTH_ADMIN而且要ACCESS_FINE_LOCATION才能拿到扫描结果。Android 12之后引入了BLUETOOTH_SCAN、BLUETOOTH_CONNECT这两个运行时权限逻辑跟之前的定位权限又不一样了。如果你的App targetSdkVersion比较高这两套权限都要处理好否则会出现“明明蓝牙开着却扫不到设备”的经典问题。代码层面有一个常见的坑直接在UI线程调用startLeScan回调里做刷新。实际上扫描回调可能在几十毫秒内被频繁触发如果不做节流UI会卡成PPT。我一般是用一个HashMap缓存扫描到的设备覆盖更新RSSI每500ms刷新一次列表。连接之后要记得调requestMtu(247)。很多第三方设备默认MTU只有23字节如果你不协商读写大一点的特征值比如历史数据就会一直报GATT_INVALID_ATTRIBUTE_LENGTH。5.2 C#环境做BLE通信的选项热词里问“winforms项目对于net framework4.7.2实现ble蓝牙通信可以用的第三方库”我实际做过类似的工程这里直接把经验放出来。如果你在 .NET Framework 4.7.2 WinForms 环境里做BLE本质上是有两条路用Windows.Devices.Bluetooth命名空间。这个API是UWP时代的产物但在WinForms项目里也能用只要项目目标平台是Windows 10及以上并且引用了System.Runtime.WindowsRuntime相关程序集。优点是系统原生、不用装额外驱动。缺点是API是异步模型需要大量async/awaitWinForms里的消息泵配合起来有时要小心死锁。用第三方库。比如InTheHand.Net.Bluetooth这个老牌库它在Windows平台上是基于系统蓝牙协议栈封装支持传统蓝牙和BLE用起来直观。另外一个就是shiny.bluetoothle但正如前面说过的它依赖平台原生蓝牙栈而且对.NET Framework的支持并不算好建议直接用 .NET 6/8 再考虑它。如果时间允许最省事的方案还是直接写一个小的 UWP 后台任务或者 WinRT 组件把BLE功能包成一个exe/serviceWinForms只负责UI展示这样蓝牙协议栈的复杂性被隔离出去。5.3 数据解析与可视化App端拿到设备Notify上来的数据后我做了两层解析。第一层是通用协议解析把各个特征值按照约定好的字段格式拆开变成结构化对象。第二层是业务逻辑处理比如心率数据要算滑动平均、活动量数据要按小时聚合、体温数据要记录异常阈值。可视化方面我在第一版用的曲线是自己用Canvas画的曲线比较简陋缩放和滑动体验也不是很好。后来换成了 MPAndroidChart效果提升明显。图表核心指标是24小时活动量柱状图、心率趋势折线图、体温异常标注。如果你的项目需要更强的交互可以看看EChartsAndroid网页版集成或者AAChartKitiOS。这里有个实用的建议BLE设备传上来的数据一定要带时间戳而且App端收到后要重新校准时间。因为设备离线期间可能没有时钟校准数据顺序错乱会严重影响心率趋势的准确性。我当时在设备端存了“累计秒数 本地时间修正值”App端收到后统一转成“Unix时间戳”这样即使断连过、跨越时区数据还是能按时间正确排列。6. 常见问题排查与避坑实录6.1 蓝牙连不上或者掉线的排查顺序我把做项目过程中遇到的典型问题整理成一个速查表有类似问题的可以按着这个顺序排查现象可能原因解决方案手机扫描不到设备广播间隔太长或者设备处于System OFF状态看电流判断设备是否活着扫描时要靠近、延长扫描时间扫描到了但连接不上上一台手机还保持着连接设备端做多个Central管理或者设置连接白名单旧连接被踢掉后才能新连连接后马上断开监督超时时间设置太短conn_sup_timeout不小于连接间隔 × (1 slave_latency) × 3连接一段时间后掉线手机蓝牙栈自动清理不活跃连接定期有数据交互比如每5秒更新一次RSSI解锁手机时掉线iOS的后台连接策略App端申请后台蓝牙模式设备端缩短监督超时以快速重连印象最深刻的是有一次设备长时间无故不在线测电流又发现睡眠模式正常。后来抓包才发现是室内宠物走到金属家具附近多径衰弱导致丢包次数多了设备端重传逻辑又在极短的监督超时范围内没来得及抢回连接。把监督超时从4秒调到10秒后问题基本消失。但注意监督超时不能太长否则真的断开后重连会很慢这是一个需要平衡的参数。6.2 功耗异常的几个隐蔽原因如果实测整机电流比预想高很多时候不是射频的问题而是这些小细节GPIO浮空。传感器、LED、按键如果有GPIO悬空未定义方向漏电流可能到几十甚至上百微安。所有不用的GPIO一定config成输出低或者输入上拉不能悬空。定时器没有关闭。nRF52的RTC1/定时器在进入System ON之前如果还在跑是不会睡眠的电流直接飙高。进入System ON前要检查所有外设是否停止。外部传感器供电未切断。比如光学心率传感器MAX30102正常工作电流能到几百微安甚至毫安级采样完一定要把它电源关断。SoftDevice没有进入低功耗模式。在System ON空闲时要调用sd_app_evt_wait()让协议栈进入省电状态否则CPU一直在跑while(1)电流降不下来。功耗调试最直接的办法是串一个10欧姆采样电阻用示波器抓电阻两端压降波形识别各个阶段的电流峰值和时间占比。抓几次之后心里大概就有谱了。6.3 DFU升级失败最典型的两种场景第一种是升级包过大导致Flash写满。nRF52832的Application区只有200KB左右如果固件编译出来超过这个尺寸Bootloader会直接拒绝。我这边把优化等级从 -O0 调到 -Os再裁剪掉不需要的日志输出之后固件从 220KB 压到 150KB才放下了。第二种是签名校验失败。Nordic的Secure DFU默认是要求所有固件包带上签名密钥的。如果手机端用了错误的密钥签名设备端校验时就直接进入错误状态表现就是“升级进度走到了 99%然后设备重启固件版本还是旧的”。检查DFU包签名和公钥是不是匹配是调试这个问题的首选。另外提醒一句DFU过程中如果设备没电比蓝牙断开的危害更大。设备进入DFU后会关闭应用逻辑只剩Bootloader在跑不充电的话电池耗尽就直接变砖。所以我量产前特意在DFU模式下把射频发射功率调低到了-20dBm同时把Bootloader的广播间隔拉到50ms费电但容易被发现多少能缩短一些升级窗口的时间。量产版还在外包装和App里都提示“升级前确保设备电量高于50%”。6.4 关于BLE连接策略的一些额外思考宠物健康追踪器与主人的手机并不是一直保持着连接状态。宠物在家跑来跑去手机蓝牙经常处于“断连-重连”的循环里。这里我用的是白名单广播连接策略设备一直广播但只接受已配对手机的连接请求。这样未配对手机即使扫到设备也无法连接安全性多了一层保障同时也避免无意中干扰到邻居的蓝牙设备。配对信息Bond信息存在Flash的系统区域DFU升级后这些信息是不会丢的但如果你在调试阶段反复擦写整片Flash就要重新配对。这也是一个“设备突然连不上”的排查方向。7. 写在最后的几个建议如果你正打算做类似的宠物健康追踪设备我把个人觉得最有价值的几个经验总结成以下几点不一定全面但都是真金白银踩出来的第一方案评估阶段一定要把“升级”当成必备功能来设计不是可选功能。宠物设备挂在活物身上一旦部署出去你就没法到现场改代码了所有Bug修复和算法迭代都靠OTA撑着。硬件上预留Bootloader空间、软件上定好固件分区方案要在第一天就想清楚后面再改分区是极其痛苦的。第二BLE的功耗优化是一个系统工程硬件、协议栈、应用、手机端四层都要配合。别指望光靠一个低功耗芯片就能做到长续航。传感器选型、采样策略、发射功率、连接参数、手机App的连接行为都会影响最终续航数据。第三做BLE开发尤其要注意“手机差异性”。同样的设备iPhone和不同品牌的Android手机连接稳定性、MTU协商结果、扫描速度表现完全不一样。不要把“在一台手机上测试通过”当作完成了测试。有条件的话找尽量多的手机做兼容性验证尤其是老款的Android机和不同版本的iOS系统。第四如果你只是做技术验证或者学习直接买一块Nordic官方开发板nRF52840 DK或者nRF52832 DK先把官方示例跑通再一点一点改成自己的业务逻辑。不要一上来就自己画板子项目里的Bug十有八九是软硬件问题混在一起很难定位。宠物健康追踪这个方向在我看来还远没有到天花板心率算法的精度、活动识别的智能程度、续航的进一步拉长这些都有很多可以做深的地方。希望这篇文章能给准备入坑或者正在调试的朋友一些参考少走一些我走过的弯路。