☰
基于Nordic BLE SoC的洗手液监测器:低功耗智能卫生方案
2026/9/25 15:06:02 网站建设 项目流程

洗手液监测器:基于 Nordic BLE SoC 的智能卫生方案实操记录

疫情期间大家应该都见过这类场景:医院走廊、商场入口、学校食堂门口摆放着一瓶瓶洗手液,但到底有多少人真的用了?用了多久?有没有按时补充?这些问题靠人眼盯是不现实的。我最近做了一个基于 Nordic Semiconductor BLE SoC 的洗手液监测器,专门回答这三个问题,而且整个设备用的是纽扣电池,一颗可以跑一年以上。这篇文章把整个项目的思路、硬件选型、固件开发、数据链路和踩坑记录都梳理一遍,给打算做类似 BLE 低功耗传感节点的朋友一个可复用的参考。

这个项目本质上是一个 BLE 信标 + 接近感应的组合体:通过红外反射传感器检测人手伸向洗手液瓶的动作,利用 Nordic 的 BLE SoC 把事件广播出去,再由网关汇总数据上传云端。它的核心价值不是“检测有没有洗手液”,而是“统计洗手行为发生的频率和时刻”,这就引出了一串技术点:低功耗状态机怎么设计、广播包怎么编码、数据在网关侧怎么去重、电池续航怎么压进“年”这个量级。

如果你正打算做智能楼宇、智慧医疗、资产管理类的 BLE 节点,这篇文章会很有参考价值。它不涉及复杂的云端平台,所有代码逻辑都围绕“传感器采集 + BLE 上报 + 低功耗”,硬件成本控制在 30 元左右,适合小批量原型验证,也适合直接升级成量产方案。

1. 先搞清楚核心问题:监测器到底该做什么

1.1 手卫生依从性这个老大难问题

医疗领域有个很出名的统计:医院内感染(HAI)的发生率大约在 5% 到 10%,而正确的手卫生行为能显著降低这个比例。但“正确洗手”做不到实时监督,护士站可以盯着,走廊里的洗手液机没人盯。早年的方案是人工巡查,每天记录洗手液消耗量,这个办法数据滞后、精度低、还费人力。

所以这类监测器的核心需求其实是两个维度:

  • 行为事件维度:有人在某个时间点按压或靠近了洗手液机,这是一个离散事件。
  • 设备状态维度:洗手液是否快用完了,是否需要补充。

这两个维度决定了硬件必须要有感知能力和通信能力。感知靠传感器,通信靠无线。在 WiFi、Zigbee、BLE 这几个选项里,BLE 因为功耗低、手机直接能扫到、网关成本低,成了最合理的选择。

1.2 为什么选 Nordic 的 BLE SoC,而不是 ESP32 或 nRF24L01+MCU

项目初期我对比过三套方案,这个环节值得展开说说,因为很多人会纠结“用 ESP32 不也能做吗”。确实能,但要看场景。ESP32 性能强、WiFi/BLE 双模、开发简单,可它的功耗在物联网场景里就是个灾难,深度睡眠还要 10 微安以上,WiFi 连接时峰值电流能到几百毫安。对于一节纽扣电池供电、要跑一年的设备,这个功耗完全不可接受。

第二套方案是 STM32 + nRF24L01+ 这种经典的 2.4G 射频组合。它的问题在于协议栈是私有的,手机没法直接连接,需要额外配一个专用网关,而且整体物料成本并不低,还要自己处理协议封装。

最终选了 Nordic 的单芯片方案,nRF52 系列把 Cortex-M4F 内核、BLE 协议栈、射频前端集成在一颗芯片里。J-Link 直接调试,协议栈由 Nordic 官方维护,开发时不用关心链路层的时序,专注业务逻辑就好。开发板加芯片的成本在量产级别可以做到 20 元以内,几乎不可能找到更低功耗的成熟方案。

1.3 单节点到系统:端到端的链路设计

整个系统分为三层:

  • 感知层:若干个洗手液监测节点,每个节点是一个 nRF52 设备,跑 BLE 广播模式。
  • 汇聚层:一个 BLE 网关(我用的是 nRF52840 dongle 配合树莓派,也可以用手机),扫描周围节点的广播包,解析后通过 WiFi/4G 上传。
  • 应用层:云端接收数据,做清洗、统计和可视化。

这里的关键设计决策是:节点不进连接、不配对,只做单向广播。这样节点的功耗可以被压到极低,同时省去连接管理的复杂度。代价是广播是单向的,没法远程改配置,但对于这种一次部署不再改参的设备来说完全够用。真需要升级固件时,可以用 Nordic 的 DFU 机制通过网关下发改固件,但那是另一套工程,后面单独说。

2. 硬件设计要点:传感器、SoC 与供电方案

2.1 传感器选型:对射式还是反射式

检测人手伸向洗手液瓶的动作,可选方案有这几种:

方案原理优点缺点
红外反射传感器发射红外光,物体反射后由接收管检测成本低、功耗可控距离近(10cm以内)、易受环境光干扰
对射式红外发射管和接收管分两侧,物体经过时遮挡光路检测距离远、判定准确需要安装支架,结构复杂
超声波测距通过飞行时间算距离变化距离远、不受颜色影响体积大、功耗高、成本高于红外
电容感应感应人手接近引起的电容变化无需开孔、美观对安装环境敏感,标定麻烦

在洗手液机这个场景里,人手距瓶身喷嘴一般在 5cm 以内,而且我们只需要检测“有没有手,而不是测量精确距离”,所以红外反射式传感器是性价比最高的选择。硬件上用一颗 IR LED + 一颗光电二极管,或者直接买集成的反射式传感器模块,比如 RPR-0521RS 这类自带环境光补偿的 I2C 传感器,代码写起来更省心,还能顺带利用它的 ALS(环境光)通道来做光照补偿。

2.2 主芯片选型:nRF52810 还是 nRF52832

Nordic 的 nRF52 系列里,最常用的是 nRF52810、nRF52832 和 nRF52840。它们之间的差别主要在 Flash/RAM 大小、IO 数量、是否支持 NFC/长距离。

我这个项目用的是 nRF52832,实际上如果你只需要一个简单的传感器节点,nRF52810 就行,它价格更低,功耗特性和 52832 基本一致。选 52832 的原因是我想在初期调测试代码时留有足够空间,而且 52832 支持 S132 协议栈(全功能 BLE 外设),后续如果想把节点改成可连接的温湿度计或者血糖仪这类双向设备,引脚和外设资源不用重新设计。

不过这里有个坑需要提醒:nRF52810 不支持 S132,只能用 S112,它只支持外设模式且连接数少。如果你的产品规划里明确只要广播和单连接,那 52810 没问题;但想留扩展余地,建议直接从 52832 起步,开发调试少受平台限制。

2.3 供电设计:一节 CR2032 跑一年的关键参数

整个设备只有两种供电方案可选:锂电池+升压 DCDC,或者一次性纽扣电池。从成本、体积、可靠性角度看,CR2032 最合适,3V 电压直接喂给 nRF52 的 VDD,不需要额外的 LDO。这样电路极简,也少一个静态功耗源。

要估算续航,先列出各状态的电流消耗:

  • 深度睡眠(System OFF 模式):nRF52832 典型 0.3 µA,连电池自放电都比它大。
  • 传感器检测周期(每 1 秒唤醒一次,读取 ADC/GPIO):每次耗时约 2ms,电流约 5 mA,等效平均电流约 10 µA。
  • BLE 广播(每 500ms 广播一次,每次约 3 个广播包):广播时峰值电流约 5.5 mA,单次广播占用 2.25ms,等效平均电流约 25 µA。

如果每 5 秒检测一次手部事件,每次事件触发后广播 10 个包,那么平均电流可以做到 50 µA 以下。一颗 CR2032 的额定容量大约 220 mAh,可用容量按 70% 算(实际容量受自放电和脉冲放电影响,降额必须考虑),折合可用容量 150 mAh,也就是 150 mAh / 0.05 mA = 3000 小时,约 125 天。

等等,这个算出来才 4 个月?问题出在广播间隔和唤醒检测频率上。实际项目的优化方案是:平时每 2 秒唤醒采集一次传感器数据,广播间隔放到 1 秒。另外触发事件后不是连续广播 10 包,而是发完 3 包立刻回到睡眠。这样平均电流可以控制在 25 µA 左右,续航大约 250 天,接近一年。如果你能接受事件上报延迟 2 秒,把广播间隔进一步拉长到 2 秒,平均电流可以压到 15 µA 以下,续航超过 400 天。

这里要强调一个理解上的误区:BLE 广播的功耗并不由发射功率主导,而是由“唤醒次数 × 单次唤醒时长”主导。一个广播包大概只有 300 µs 的射频开启时间,但协议栈要预热、要准备数据,整个流程大约 2 到 4 ms。所以算法上尽量减少唤醒次数,比调低发射功率更有效。

2.4 硬件布局的几个实际问题

硬件上走过的弯路不少,挑三个最有价值的说一说。

第一,红外发射管和接收管之间的光学隔离。因为发射管功率较大,如果两者正面相对安装,接收端会被发射端直射的红外光直接打饱和,极端情况下会造成持续误触发。解决办法是在安装结构里加一个不透明的隔板,物理隔离发射和接收光路。

第二,传感器朝向。洗手液瓶大多是圆柱或者方瓶,传感器要安装在瓶身侧面的支架上,朝向人手伸过来的方向。如果朝上装,会检测到天花板或灯光反射造成误触发;如果朝下装,又会检测到台面。需要在现场做一次“空场景测试”,记录 5 分钟的原始 ADC 值波动范围,作为判定阈值的设计依据。

第三,蓝牙天线的净空区。很多新手画的 PCB 上天线底下走了地线或者铺了铜,导致辐射效率大幅下降。nRF52 参考设计里天线下方的 PCB 需要净空,这个区域不能有走线和铜皮。近场调试可能看不出问题,但到了现场,隔着 5 米就收不到广播包了。

3. 固件开发:广播、传感器读取与低功耗状态机

3.1 广播包设计与编码

节点采用非连接广播(Non-connectable Advertising),这样手机和网关都能收到数据,同时不允许任何设备连接,避免了多设备连接占满连接槽位的问题。

广播包的内容设计如下:

字段长度内容说明
Flags1 字节0x06,LE General Discoverable + BR/EDR Not Supported
Manufacturer Specific Data6-8 字节厂商 ID + 设备类型 + 电池电压 + 事件计数
Local Name可变“HWSAN-{设备ID}”,方便开发时识别设备

其中事件计数是关键字段。网关需要用它来做去重判断。如果一个节点在一次洗手动作中广播了 3 个相同包,网关收到 3 次但事件计数没变,就只上报一次。计数到 255 回绕,网关侧需要容忍这种回绕。

广播间隔的选择直接影响功耗和上报实时性。事件触发时我会临时切换到 100ms 高速广播,持续 1 秒,这样网关能在 1 秒内收到至少 5 个包。空闲时广播间隔是 1 秒,只广播电池电压和设备状态,方便维护巡检。这里用到一个 nRF52 的特性:可以动态修改广播参数,不需要重新初始化协议栈。

3.2 红外传感器的读取与判定算法

传感器读取的核心是避免误判。原始 ADC 值在无人状态下也不是完全稳定的,环境光变化、洗手液瓶的材质反光、空调出风口的气流扰动都会造成波动。

我采用的判定逻辑是:

  1. 每 2 秒读取一次传感器 ADC 值,记为raw。
  2. 每次读完后与上次值做差,得到delta。
  3. 如果delta大于设定的阈值(比如基线值的 30%),则判定为“检测到事件”,进入事件确认状态。
  4. 连续 3 次检测都超过阈值,才真正触发事件并进入广播状态。

这种“连续确认”机制可以过滤掉单次随机波动造成的误触发。但要注意,连续确认的代价是事件检测延迟增加了最多 4 秒。如果你要求更快的响应(比如检测到人手后马上播放提示音),就得缩短确认次数或者加长检测间隔。

另外一个细节:红外传感器的使用时间长了,发射管的光强会衰减,接收管的灵敏度也会下降。设计时应在固件里留一个电容电压校准参数的存储位置,每次更换电池时通过 BLE 广播电池电压,网关侧如果看到电压异常低,可以提醒维护人员更换电池,顺便校准传感器阈值。

3.3 低功耗状态机:从“永远在跑”改成“该睡就睡”

整个固件的核心是一个只有四个状态的状态机:

  • INIT:上电初始化,配置 GPIO、传感器、协议栈。
  • MEASURE:唤醒,采集传感器 ADC 值,判断是否有人手接近。
  • EVENT:确认触发事件,发送广播包。
  • SLEEP:进入 System ON 睡眠模式,等待 RTC 唤醒。

状态之间的切换完全由定时器和 ADC 比较结果驱动。System ON 睡眠模式下,内核停止运行,外设中的 RTC 和 GPIO 唤醒功能仍然工作,电流在微安级别。

实现上要注意的坑:nRF52 的 UART、SPI、TWI 等外设在进入睡眠前必须全部 deinit,否则它们会持续耗电。另外,定时器的分辨率要选够。我用的是 RTC1(32.768 kHz 晶振),精度比内部 RC 振荡器好,而且 RTC 的定时时间不受芯片温度影响,这直接避免了“冬天走得快、夏天走得慢”这种问题。

3.4 DFU 升级能力:没它你会哭

虽然节点是纯广播模式,没有连接,但总会有“改一下阈值”“修一个 bug”的需求。之前我吃过亏,一批设备部署到现场后发现问题,只能全部拆回来升级,又费时间又尴尬。

后来给节点加了 DFU 支持。nRF52 的 DFU 流程是:设备先以 DFU 模式广播,网关或者手机连接后,把新的固件包通过 BLE 发给设备,设备写进内部 Flash,然后跳转重启。

但广播模式的节点要进入 DFU 模式,得有一个触发机制。我的做法是:在传感器检测到连续 5 次异常(比如 ADC 值持续超上限),判断为“维护人员故意触发了特殊的维护动作”,然后节点进入 DFU 广播模式,持续一分钟等待连接。虽然这个机制不太优雅,但在不拆壳的情况下已经是最实用的方案了。

4. 网关、云端与前端可视化

4.1 网关选型:能收到广播就行,但别太乐观

节点的数据通过 BLE 广播出来,就必须有设备在听。网关的硬件选型有几个层次:

  • 手机:适合开发和演示,装个 Nordic nRF Connect 或者自己写个 Android App,扫描广播包,把数据转发到云端。
  • 树莓派 + BLE USB Dongle:适合小规模部署,便宜灵活,可以用 Python 或 Node.js 写扫描脚本。
  • 专用 BLE 网关:适合大规模部署,比如 Nordic 的 nRF52840 Dongle 配合 Zephyr 做多协议处理,或者市面上成熟的 LoRa/BLE 网关产品,支持以太网/PoE 供电,部署方便。

我的测试环境用树莓派 4B + nRF52840 Dongle。树莓派通过 USB 连接 dongle,运行一个 bluez 编写扫描程序,抓取广播包后解析出厂商数据字段,再通过 MQTT 发给本地 Broker。

实际测试中发现一个问题:BLE 广播的信道只有 37/38/39 三个,当环境里蓝牙设备很多时,广播冲突概率很高,网关收到丢包率可能达到 30%。解决办法是在发送端做冗余:每个事件连发 3 个广播包,时间上稍微错开,这样即使某两个包冲突,剩下的也能被网关收到。这个简单策略能把实际丢包率降到 5% 以下。

4.2 数据链路:从原始广播包到结构化事件

网关收到原始广播包,需要做这几件事:

  1. 检查广播包类型,过滤非目标设备(通过厂商 ID 和本地名称前缀)。
  2. 解析出设备 ID、事件计数、电池电压、传感器状态字段。
  3. 用“设备 ID + 事件计数”作为去重键,避免同一事件重复上报。
  4. 打上时间戳(网关本地时间,建议做 NTP 同步),推送到 MQTT。

MQTT 主题按层级设计,比如hw_sanitizer/{device_id}/event和hw_sanitizer/{device_id}/telemetry。事件主题里放一次洗手动作的触发时间和计数值;遥测主题里放周期性的电池电压、设备状态。这样下游的数据消费者(比如一个 Node-RED 流程)可以单独订阅事件流,而不用接收所有遥测包。

云端我用了简单的 MariaDB 做存储,然后接 Grafana 做展示。Grafana 可以直接查数据库,绘出每个洗手点位的日使用量曲线、每个点位的电池剩余趋势。这个链路最大的好处是每个环节都通用,不会绑定某一家云厂商。

4.3 可视化要看什么:这决定了你数据的意义

数据可视化不是做一堆花哨图表就完事。我在设计 dashboard 时,确立了三个核心视图:

  • 日使用量柱状图:每个洗手液点位一天被触发的次数,按小时聚合,用于发现高峰低谷。
  • 设备健康度列表:每个节点的电池电压、最近一次心跳时间、信号 RSSI 均值,用于主动维护。
  • 依从率趋势图:如果有多个点位(比如手术室入口、走廊、护士站),可以按“实际触发次数 / 理论期望次数”算依从率。这个比例比原始次数更能说明问题。

后端的分析脚本用 Python 写,定期从数据库拉数据,计算阈值和趋势,把异常设备自动标记出来。这部分不复杂,但很实用。

5. 我踩过的坑与调试心得

5.1 传感器误触发的排查:到底是谁在碰它

项目上线后不到一周,就有一个点位出现了异常高的使用量,一天触发了两千多次。排查过程让我印象很深。

先看原始数据报:事件计数的增长速度和触发时刻完全不像是人手操作,几乎是均匀分布。这就排除了人手的行为特征。然后看传感器 ADC 值,发现空闲状态下的基线在早晨和傍晚有大幅波动。那两天恰好是阴天转晴,环境光变化剧烈,红外传感器受到阳光中红外成分的干扰,误触发了。

解决方法是:利用传感器内置的环境光通道,当环境光强度超过某个阈值时,动态抬高判定阈值。同时增加一个低通滤波,对 ADC 值做滑动平均,减小瞬时噪声的影响。

调完后的效果非常明显,一天的误触发次数从两千降到了个位数。这里得到的教训是:做检测类产品,尤其是户外或者窗口位置,考虑环境光变化是一个必修课,不能只在实验室里测。

5.2 电池续航与功耗实测:和理论差的不是一星半点

理论算过能跑一年,但实测续航只到了 7 个月左右,差了快一半。用 Nordic 的在线功耗分析工具 PPK2 抓电流曲线后,发现两个问题。

第一个问题是 TWI(I2C)总线在每次读取后没有完全释放。传感器模块是 I2C 接口的,每次读完寄存器,如果 SDA 线保持在高电平,外设不会完全关断,会导致大约 5 µA 的额外电流。解决办法是在读取完最后一位后,发送一个 STOP 条件并拉低 SDA,等待一个短暂延时,然后切换到 GPIO 输入模式并上拉,彻底释放总线。

第二个问题是广播包数量比预想的多。我在事件触发后设置了“连发 10 包”,但没考虑到协议栈在后台还会自动补发一些 ACK 包或者重试包。后来优化策略:把广播间隔改成一包一发模式,事件确认后发 3 包就停。改完之后,实测电流曲线干净了很多。

实际功耗测试中,我建议每个使用 nRF52 系列的朋友都搞一个 PPK2 或者至少用万用表串联测平均电流。否则你根本不知道系统里到底哪里在漏电。

5.3 信号覆盖问题的处理:一个网关到底能管多少点位

在一个 200 平米的开放空间里,一个网关能覆盖 20 个左右的洗手液节点,但问题不在于覆盖半径,而在于并发冲突。

多个节点同时广播时,由于广播信道只有 3 个,信道拥塞导致丢包率上升。特别是早晚高峰,食堂门口有 6 个节点同时触发,网关收到的事件往往不足 60%。

缓解方案有两条路:一是从发送端降低广播时长,每个事件从发 3 包减到发 2 包,发包间隔从 100ms 改成 80ms,减少在信道上的占用时间;二是从网关端增加扫描窗口,在 Linux 上用 Wireshark 抓包分析,确认冲突发生在哪些信道,然后调整发射功率,尽量减小节点间的相互干扰。

实际效果最好的方式还是将洗手液点位分组,每组错开广播时刻。比如 A 组奇数秒广播,B 组偶数秒广播,通过固件里设置一个 500ms 相位偏移来实现。这个优化没有增加任何硬件成本,但显著提升了并发场景下的接收成功概率。

5.4 洗手液对 PCB 的腐蚀问题:别小看那一层水雾

最后说一个不太起眼但容易踩的坑。洗手液机的周围,尤其是出液口附近,湿度长期偏高,而且洗手液泡沫可能会溅到 PCB 表面。如果 PCB 不做三防漆处理,一段时间后可能出现短路或腐蚀。

第一次打样没有做三防,结果一个月后一张板子就出问题了。拆下后发现传感器引脚之间有明显白色残留物。解决办法很简单:PCB 出厂时加一道三防漆工艺,成本几乎可以忽略,但稳定性和寿命显著提升。

如果条件不允许用三防漆,也要在结构设计时让 PCB 垂直安装,避免水平朝上,减少液体在板面积聚。传感器的窗口要用密封垫圈和机壳贴合,防止水汽从缝隙渗入。

6. 项目复盘与可复用的经验

动手做这个项目之前,我其实对 BLE 广播的具体功耗数字并没有太准确的把握,很多数据是边做边测出来的。但做完之后,整个链路里哪些地方是重点、哪些地方可以放手,思路就很清晰了。

  • 传感器方案的选择要跟着“检测什么”走,不要求功能多,只求稳定。
  • Nordic 的 BLE SoC 在低功耗和开发效率之间取得了很好的平衡,官方提供的 SDK 和协议栈文档质量很高,只要你按参考设计画板,基本没有射频翻车的问题。
  • 广播模式的节点虽然没有握手环节,但正因为无连接,部署和维护极其简单。这适合大量一次性部署的传感器场景。
  • 功耗设计要贯穿软硬件始终,任何一个角落的漏电都会侵蚀你的电池寿命。

如果后续要做类似的 BLE 传感节点,我的建议是先定功耗预算,再做功能。先画一个电流分配表,把每个模块的静态电流、唤醒电流和唤醒时间列出来,乘上使用频率,算出总平均电流,再决定电池容量和传感器方案。这个习惯能避免很多后期返工。

洗手液监测器只是一个很小的应用。同样的硬件架构,换个传感器,就能做门磁、占用检测、资产追踪、冷链监测,核心逻辑都是“超低功耗 + BLE 广播 + 网关汇聚 + 云端分析”。这个架构的通用性在我实测中验证得相当扎实,以后有新项目也打算继续沿用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询