
1. 从一个假报警说起为什么断线检测比漏水检测更棘手做过水浸报警器的人大概都遇到过这种尴尬半夜手机突然收到告警爬起来检查一圈发现地面干得能起灰。问题出在哪传感器线被老鼠咬断了或者接头氧化松脱报警器把开路当成了有水。用户被折腾几次之后直接把这个设备扔进抽屉再也不会信任它。这个场景在工业现场更常见。某机房的空调冷凝水管附近装了几个水浸探头运维人员最怕的不是真漏水而是探头本身失效导致的误报和漏报。真漏水没报出来是事故没漏水天天报是骚扰两种都会让整套监测系统失去意义。所以一个靠谱的泄漏报警器核心能力其实有两层第一层是准确检测到液体存在第二层是准确判断传感器本身是否还在正常工作。第二层往往被忽略但它才是决定这个设备能不能长期无人值守运行的关键。这篇内容就围绕一个基于 ESP32 的泄漏报警器展开重点讲清楚传感器断线检测这件事怎么落地——包括电路上的考量、固件里的判断逻辑、阈值怎么定、以及实测中那些文档里不会写的坑。适合的读者是做过 ESP32 基础项目、想把它用到实际监测场景的开发者正在做水浸/液漏报警产品、被误报问题困扰的硬件工程师以及任何对如何让一个传感器系统自己知道自己坏了这个问题感兴趣的人。不需要你精通模拟电路但需要你能看懂基本的 GPIO 操作和一点电阻分压的概念。2. 泄漏检测的物理原理决定了断线检测必须另起炉灶2.1 导电式水浸传感本质是一个可变电阻市面上最常见的低成本水浸传感器结构非常简单两块梳齿状的导电电极中间留有空隙。干燥时两极之间是空气电阻极高理论上可以到兆欧级别一旦有水尤其是含杂质的水覆盖水中的离子提供导电通路两极之间的电阻迅速下降到几十千欧甚至更低。ESP32 读取这个传感器的典型做法是把电极当成一个电阻接入分压电路一端接 3.3V另一端通过一个固定电阻接地电极和固定电阻的连接点接到 ADC 引脚。干燥时电极电阻大分压点接近 0V有水时电极电阻小分压点电压升高。固件读 ADC 值超过某个阈值就判定为有水。这个方案便宜、简单但它有一个天生的盲区断线状态和干燥状态在电气上几乎无法区分。线断了电极相当于从电路里消失分压点电压和干燥时一样低。固件看到低电压只能判断没水完全不知道传感器已经不在线了。2.2 为什么不能靠读数为零来判断断线有人会想那我在固件里判断如果 ADC 读数一直是 0就认为是断线。这个思路在实验室里可能凑合放到现场立刻出问题。第一ADC 读数永远为 0 的情况也可能是电极被完全绝缘的污垢覆盖或者探头被浸泡在纯水里纯水几乎不导电。这些情况下传感器是在线的只是暂时测不到。第二ESP32 的 ADC 本身有噪声和偏移读数不会精确等于 0你设一个小于某值算断线的阈值又会和干燥状态重叠。第三如果传感器线很长线缆本身的分布电容和干扰会让读数漂移你根本没法用一个固定值来区分。所以结论很明确要可靠地检测断线必须在电路层面给传感器加一个身份标识让在线和离线在电气上产生本质区别。这是整个方案的设计出发点也是后面所有电路和固件逻辑的根基。2.3 三种可行的断线检测思路对比在动手之前我把常见的几种方案列出来对比一下方便你根据自己的场景选。方案原理优点缺点适用场景末端并联标识电阻传感器末端并一个已知阻值电阻断线后分压点电压跳到特定值成本极低只需一个电阻需要占用一个 ADC 通道阈值需标定大多数低成本场景四线制/回路电流检测用恒流源驱动检测回路电流是否在范围内抗干扰强可检测线缆电阻变化电路复杂成本高工业长线缆场景数字传感器心跳传感器自带 MCU定期发送心跳判断最可靠可传更多信息传感器成本高需通信协议多点组网场景对于个人项目和小型监测场景第一种方案性价比最高。它的核心思想是在传感器探头的末端跨接一个阻值远大于有水电阻、但远小于干燥电阻的电阻。这样三种状态在分压点上产生三个不同的电压区间固件就能区分有水干燥断线三种情况。3. 标识电阻怎么选一次把分压计算讲透3.1 先确定三个状态的电阻范围假设我们用的水浸探头实测数据大致是完全浸没在自来水中的电阻约 5kΩ 到 30kΩ干燥时电阻大于 1MΩ线缆正常时导通良好。标识电阻 R_id 要满足两个条件一是远大于有水时的电阻避免有水时被它短路影响检测二是远小于干燥时的电阻这样断线后分压点电压会明显抬升。取 R_id 100kΩ 是一个比较稳妥的中间值。它比有水的 30kΩ 大比干燥的 1MΩ 小落在中间偏上的位置。3.2 分压电路的具体计算电路这样接3.3V 接传感器一端传感器另一端接分压电阻 R_div 到地分压点接 ADC。同时在传感器两端也就是探头末端并联 R_id。有水时探头电阻 R_wet 与 R_id 并联等效电阻约等于 R_wet因为 R_id 大得多。取 R_wet 10kΩR_div 10kΩ则分压点电压 3.3 × 10/(1010) 1.65V。干燥时探头电阻 R_dry 与 R_id 并联等效电阻约等于 R_id 100kΩ。分压点电压 3.3 × 10/(10100) 0.3V。断线时探头消失只剩 R_id 通过线缆另一端……这里要注意如果断线发生在探头和 R_id 之间那 R_id 也一起断了分压点电压 0V。如果断线发生在 R_id 之后靠近主板一侧那 R_id 还在电压 0.3V和干燥状态一样。这就是关键细节标识电阻必须放在探头末端而不是主板端。放在主板端的话断线后电阻还在你根本区分不出来。放在探头末端断线后电阻随探头一起消失分压点电压掉到 0V和干燥的 0.3V 拉开差距。3.3 阈值怎么定留足余量根据上面的计算三个状态的 ADC 电压大致是有水 1.65V干燥 0.3V断线 0V。ESP32 的 ADC 是 12 位参考电压 3.3V所以 1V 对应约 1240 个计数。有水约 2048 计数干燥约 372 计数断线约 0 计数阈值可以这样设大于 1200 判有水200 到 1200 之间判干燥小于 200 判断线。200 这个阈值对应约 0.16V给 ADC 噪声和线缆压降留了余量。注意实际电阻值会有偏差探头在不同水质下电阻变化也很大。建议先用万用表实测你的探头在自来水、雨水、纯水中的电阻再代入计算。不要直接抄上面的数字。3.4 一个容易被忽略的点线缆电阻如果传感器线缆很长比如 10 米以上线缆本身的电阻不能忽略。普通铜线每米约 0.02Ω10 米双线来回 0.4Ω相对于 10kΩ 可以忽略。但如果用的是劣质细线或者接头氧化接触电阻可能到几十欧甚至上百欧这会影响有水时的分压计算。更麻烦的是接触电阻不稳定会导致读数跳动。我的做法是线缆尽量用双绞线接头处用热缩管密封并且在固件里对 ADC 读数做滑动平均滤波滤掉接触不良带来的尖峰。如果现场干扰特别大可以在 ADC 引脚和地之间并一个 0.1uF 电容硬件层面先滤一道。4. 固件里的状态机把三种状态管清楚4.1 为什么用状态机而不是 if-else如果只是简单判断有水报警几个 if-else 就够了。但加上断线检测之后状态变多了而且状态之间还有转换关系比如从干燥变成断线要报警从有水变成断线也要报警但从干燥变成有水是正常触发。用一堆嵌套 if-else 写逻辑很快就会乱。状态机的好处是把当前处于什么状态和状态之间怎么迁移分开。代码读起来清晰后期加功能比如加一个传感器恢复的通知也容易。4.2 状态定义与迁移条件我定义四个状态STATE_DRY干燥、STATE_WET有水、STATE_DISCONNECTED断线、STATE_UNKNOWN上电初始或异常。迁移逻辑上电后先进入STATE_UNKNOWN连续采样 10 次如果读数稳定在某个区间再进入对应状态。这样避免上电瞬间的抖动误判。STATE_DRY下读数持续大于 1200 超过 500ms迁移到STATE_WET触发漏水告警。STATE_WET下读数持续小于 1200 超过 5 秒迁移回STATE_DRY。这里用 5 秒而不是 500ms是为了避免水面波动导致状态反复跳变。任何状态下读数持续小于 200 超过 1 秒迁移到STATE_DISCONNECTED触发断线告警。STATE_DISCONNECTED下读数恢复到 200 以上并稳定 3 秒迁移回STATE_DRY或STATE_WET根据读数判断并发送传感器恢复通知。4.3 去抖动与滤波的具体实现ADC 读数一定要滤波。我用的方法是每次采样读 16 次去掉最大和最小的各 4 个剩下 8 个取平均。这样一次滤波后的值就比较稳了。然后在状态判断时再要求连续 N 次满足条件才迁移这就是时间上的去抖动。代码上我习惯用一个环形缓冲区存最近的采样值状态判断函数只读缓冲区里的统计结果。这样采样和判断解耦主循环不会被阻塞。#define SAMPLE_BUF_SIZE 16 #define FILTER_DROP 4 int sample_buf[SAMPLE_BUF_SIZE]; int buf_index 0; int read_filtered_adc() { for (int i 0; i SAMPLE_BUF_SIZE; i) { sample_buf[i] analogRead(ADC_PIN); delay(2); } // 复制并排序去掉最大最小各4个 int sorted[SAMPLE_BUF_SIZE]; memcpy(sorted, sample_buf, sizeof(sorted)); // 简单插入排序 for (int i 1; i SAMPLE_BUF_SIZE; i) { int key sorted[i]; int j i - 1; while (j 0 sorted[j] key) { sorted[j 1] sorted[j]; j--; } sorted[j 1] key; } int sum 0; for (int i FILTER_DROP; i SAMPLE_BUF_SIZE - FILTER_DROP; i) { sum sorted[i]; } return sum / (SAMPLE_BUF_SIZE - 2 * FILTER_DROP); }这段代码在 ESP32 Arduino 环境下可以直接用。analogRead默认是 12 位返回 0 到 4095。如果你用的是 ESP-IDF换成adc1_get_raw并配置好衰减即可。4.4 告警去重与恢复通知断线告警不能每次循环都发否则手机会被轰炸。我的做法是状态迁移时才发一次通知状态不变时不发。同时记录一个上次通知时间如果同一状态持续超过 1 小时再发一次提醒可选看场景。恢复通知同样重要。传感器断线后重新接好应该告诉用户已恢复否则用户不知道问题解决了没有。这个细节很多商用产品都做得不好值得注意。5. 实测中那些文档不会写的坑5.1 探头材质和电镀层的影响我一开始用普通覆铜板自己刻了两个梳齿电极干燥时电阻确实很大但用了两周后发现有水时电阻变得不稳定有时候要等好几秒才降到阈值以下。拆下来一看铜箔表面氧化发黑氧化层影响了导电。后来换成不锈钢电极问题解决。再后来发现即使用不锈钢如果水质很软比如纯净水电阻依然很高可能触发不了。这时候要么换用更灵敏的检测方式比如电容式要么在固件里把有水阈值调低但调低又会增加误报风险。我的经验是如果监测的是自来水、雨水、空调冷凝水这类含杂质的水导电式方案够用如果是纯水或去离子水趁早换电容式或光学式传感器别在导电式上死磕。5.2 电源波动对 ADC 的影响ESP32 用 USB 供电时3.3V 稳压出来的电压其实会随负载波动。如果板子上有 WiFi 正在发射电流突变会导致 3.3V 瞬间跌落几十毫伏ADC 读数跟着跳。我遇到过断线告警在 WiFi 连接瞬间误触发的情况。解决办法有两个一是在 ADC 参考电压上做文章用外部基准源比如 TL431给分压电路供电而不是直接用 3.3V二是在固件里WiFi 发射期间暂停采样或者把采样窗口拉长让波动被平均掉。我选了后者简单有效。5.3 长线缆引入的干扰线缆超过 5 米之后50Hz 工频干扰开始明显。ADC 读数会出现周期性的波动幅度可能有几十个计数。如果阈值设得太紧就会误判。硬件上我在 ADC 输入端加了一个 RC 低通滤波1kΩ 电阻串联0.1uF 电容对地截止频率约 1.6kHz对 50Hz 衰减有限但配合固件的滑动平均效果就够了。如果现场干扰特别严重可以考虑用屏蔽线屏蔽层单端接地。软件上我把采样率降到每秒 10 次每次采样间隔随机化加一点随机延迟这样工频干扰不会和采样频率同步平均之后更容易被抵消。5.4 上电自检别等出事才发现传感器没接设备上电时应该主动做一次自检读取 ADC 值如果落在断线区间直接点亮红色 LED 并发送断线告警而不是等运行中才发现。这个逻辑很简单但很多项目都漏掉了。我还会在自检时做一个探头短接测试如果硬件支持用一个 MOSFET 短暂短接探头两端看 ADC 读数是否跳到有水区间。如果跳不过去说明检测通路有问题。这个测试对生产环节特别有用可以快速筛出焊接不良的板子。6. 从单点检测到多点组网这个方案的扩展空间单点报警器解决的是这一个位置有没有漏水但实际场景往往需要多个点。比如一个机房有 8 个空调每个下面都要放探头。这时候如果每个探头都配一个 ESP32成本太高也不方便统一管理。一个自然的扩展是用 ESP32 做主控通过 RS485 或者单总线挂多个探头节点每个节点只负责读取自己的分压电路并上报。主控轮询各个节点统一判断状态并上报到服务器。这样成本可以降下来布线也更整洁。另一个扩展方向是加本地存储。网络断了的时候告警记录先存到 Flash 里网络恢复后补传。这个功能在工业现场很实用因为现场网络往往不稳定。还有一个我觉得很有价值的方向把断线检测的思路推广到其他传感器。比如温度传感器 PT100断线时读数会跳到满量程或零用类似的标识电阻思路可以可靠地判断传感器是否在线。这个思路一旦掌握可以用在很多地方。7. 关于阈值标定我踩过的那些坑最后聊聊阈值标定这件事因为它看起来简单实际上最容易出问题。我最初的做法是在实验室里测几组数据取个中间值当阈值烧进去就完事。结果现场装上去第一天下雨探头被雨水淋到电阻比自来水低很多直接触发告警——但那是正常的雨水不是漏水。后来把有水阈值调高又发现空调冷凝水很纯净触发不了。正确的做法是在现场实际环境下用实际会接触到的液体做标定。具体步骤是先把探头干燥记录 100 次 ADC 读数取平均值和标准差再把探头浸入实际液体记录 100 次读数然后根据两组数据的分布选一个误判率最低的阈值。如果两种液体的电阻差异太大那就得考虑用多个阈值区间或者换传感器类型。还有一点阈值不是一劳永逸的。探头会老化电极会氧化水质会变化。我现在的做法是在固件里加一个自适应逻辑每次从有水状态恢复到干燥状态时记录一次干燥时的 ADC 值如果发现这个值在缓慢漂移就微调断线阈值。这个逻辑不复杂但能显著延长设备的免维护周期。提示自适应逻辑要有边界不能无限调整。我设的是每次调整不超过 5 个计数累计调整不超过初始值的 20%。超过就锁定并发送需要维护的提醒。这套方案我从最初的原型到现在稳定运行前后改了大概七八版。最大的体会是断线检测这件事电路上多花一个电阻固件里多写几十行状态机就能把误报率降一个数量级。对于任何要长期无人值守运行的传感器系统这个投入都非常值得。