资讯动态

基于ESP32与KiwisIoT的洪水监测系统实战

发布时间:2026/10/3 6:49:58 来源:尧图企业网站定制
连着下了几天暴雨物业群里凌晨三点开始刷“地库入口积水了”的照片而我这边 KiwisIoT 平台上那条水位曲线已经提前一个多小时冲到了 3 米报警线。我搭的这套《洪水监测系统 using water level sensor KiwisIoT》说白了就是一个“无人值守的河道/低洼水位哨兵”超声波水位传感器Water Level Sensor负责测距ESP32 负责采集和联网KiwisIoT 负责云端的存储、曲线展示和告警推送。整套东西预算不到两百块不用自己买服务器注册个平台账号就能用非常适合个人开发者、物业运维、农业合作社这类没有专职 IT 支持的团队。下面把我从选型、接线、代码到现场踩坑的完整过程写出来。我一直认为大部分内涝预警的痛点不是“测不准”而是“发现晚”。人工巡检在暴雨天根本忙不过来等肉眼看到水面逼近警戒线往往留给疏散的时间已经很少了。水位传感器跑在野外要抗高温、防冷凝、扛得住偶尔断网还得保证没人维护也能自己工作几个月。这套系统要做的事情其实就三件持续量到水位数据、把数据可靠地送到云端、在到达阈值时第一时间发出告警。围绕这三件事整个项目的架构、硬件选型和代码逻辑就全定了。1. 项目概述与痛点拆解1.1 先搞明白你要监测的是哪种“水”做洪水监测最容易犯的错误就是上来直接买传感器。实际上“监测水”这件事在不同场景下差别非常大需求拆解不到位后面全白搭。河道漫溢的特点是水位变化相对平缓但一旦涨起来就非常顽固关键在于长时间趋势判断和异常加速识别。这类场景上报频率可以放宽到 5 分钟一次阈值告警反而不是最主要需求你更需要的是“水位以每小时 0.5 米的速度上升”这种趋势信号。城市下穿通道和桥洞积水是完全另外一种性格。暴雨时十几分钟就能从 20 厘米涨到 1 米以上留给系统的反应时间极短。这种地方必须高频采集、高频上报建议 30 秒一个数据点而且必须配上本地声光报警因为现场的人往往比平台用户更早需要信息。地下车库、机房、配电房门口的倒灌监测核心诉求是“提前量”。水位只要超过地面高度 10 厘米就说明排水系统已经吃不住了必须在这个阶段就告警而不是等水淹到配电柜。所以这类场景的传感器安装要预留足够低的位置阈值也要以小值为起点。农田沟渠、池塘的蓄洪调度水位变化周期长对实时性要求不高但对长时间漂移和可靠性要求高。传感器装上去可能要半年不管数据链路断一天可以接受但绝对不能今天报 1.5 米明天报 1.8 米同一个实际水位在不同时间读出的数值必须稳定。我最终把主要场景定为“河道漫溢 下穿隧道积水”两种一个管趋势、一个管实时告警。这两个需求恰好把系统的高频上报能力和云端趋势分析能力都锻炼到了。1.2 整体架构与设计思路整套系统是典型的三层物联网架构我特意选了最能“站在巨人肩膀上”的组件避免重复造轮子。采集层超声波水位传感器 ESP32 主控。传感器负责把物理水位变成距离值ESP32 负责读取、滤波、判定报警再通过 WiFi 上报。选 ESP32 而不是 Arduino Uno 的理由很现实它自带 WiFi双核跑 MQTT 库绰绰有余价格还不到三十块钱。更重要的是 ESP32 的 3.3V 逻辑电平可以直接兼容绝大多数传感器模块省去了一堆转接电路。传输层WiFi MQTT 协议。MQTT 是物联网设备上报数据的事实标准轻量、支持断线重连而且几乎所有 IoT 平台都原生支持。我使用 KiwisIoT 平台提供的 MQTT Broker 作为云端接入点设备只负责发布消息不需要维护长连接状态。平台层KiwisIoT 负责设备接入、数据存储、曲线展示、告警规则。这块如果自己写要处理数据库、Web 后端、消息推送工作量直接翻十倍。而 KiwisIoT 这类平台把“设备管理”和“数据可视化”做成了现成的界面设备注册完就能看到实时数据曲线配置告警规则也只是在网页上点几下的事。层级使用组件核心职责典型问题采集层超声波传感器 ESP32原始物理量采样、滤波、本地判定供电不稳、传感器进水、量程不足传输层WiFi MQTT数据封装、可靠传输、断线重连网络波动、Topic 配置错误、JSON 格式不匹配应用层KiwisIoT 平台数据存储、可视化、告警通知告警规则不生效、数据未上报聚焦这套架构最大的优势是每一层都可以独立替换。传感器坏了只换传感器平台不满意可以换另一个支持 MQTT 的 IoT 平台主控坏了重新烧录固件就能恢复。我把每一层之间的接口都定义成标准格式水位数据统一成{water_level: 2.35}这样的 JSON后面想加温度、加电池电压都只是追加字段的问题。2. 核心硬件选型水位传感器的选择与接线2.1 三种常见水位传感器对比我在方案阶段对比了市面上最常见的三类水位检测方案每一种都有明确的技术路线和适配场景。超声波测距传感器的原理是发射一束超声波脉冲测量遇到水面反射回来的时间差再乘以声速除以 2 得到距离。声速在空气中大约是 340m/s1 毫秒的往返时间对应大约 17 厘米的距离测距刷新率可以达到每秒钟几十次。它最大的优点是探头不需要接触水体安装位置和后期维护都相对灵活。缺点是水面有泡沫、漂浮物或者风浪大时回波信号可能出现杂波。市面上常见的 HC-SR04 只有几十块钱防水的 JSN-SR04T 也就六十块钱上下属于成本最低的非接触式方案。浮子式水位计是机械式方案一个浮球漂在水面上通过连接机构和编码器把位置变成电信号。它的优点是原理简单、不依赖电子回波数据非常直观缺点是要建测井或者导波管而且水草、泥沙、冰面都可能卡住浮子现场维护率偏高。这种方案适合水工设施相对完善的站点不适合我们这种快速部署的轻量场景。压力式投入式液位计是把探头沉到水底测量探头承受的静水压力压力值乘以系数就是水深。它不害怕水面泡沫和水草阻挡量程可以做得很大但有一个难以回避的问题探头必须接触水体密封一旦老化就容易进水而且长时间泡水后水垢和微生物会给压力膜片带来漂移。价格一般在一两百起步想要精度高一些的型号更贵。方案原理优点缺点典型价格适合场景超声波声波回波测距非接触、安装方便、成本低泡沫/漂浮物可能干扰20~80 元河道、桥洞、沟渠浮子式浮球随水位升降原理简单、数据直观易被水草卡住、需测井100~300 元闸门、水库等固定设施压力式静水压力换算水深不怕泡沫、量程大接触水体、长期漂移150~500 元浑浊水体、深水位我最后选择超声波方案核心原因是“非接触式”给后期省了太多麻烦。探头永远不碰水水体是什么颜色、水里漂着什么我都不关心只要定期清理探头表面的凝结水和灰尘就行。2.2 传感器选型与接线实操我这里选用的是 JSN-SR04T 防水超声波模块它和普通 HC-SR04 最大的区别是探头和电路板分离探头通过一段延长线引出可以直接固定在支架上而电路板放进防水盒里。这种结构对野外部署非常友好因为真正需要防水的位置只有探头本身而探头内部是灌胶密封的长期暴露在潮湿环境中没问题。接线表如下ESP32 的引脚我特意避开了默认的烧录串口引脚以及常用于 SD 卡的引脚避免后续扩展时冲突传感器引脚接 ESP32说明VCC5V 引脚供电需要 5V模块工作电流约 20mAGNDGND与 ESP32 共地必须接否则信号不连续TrigGPIO5触发信号输出高电平脉冲 10usEchoGPIO18回波信号建议通过分压电阻接入有一个接线细节特别重要很多超声波模块的 Echo 回波引脚输出的是 5V 电平而 ESP32 的 GPIO 是 3.3V 逻辑直接接上去短期能用但长时间运行存在烧坏 GPIO 的风险。稳妥的做法是在 Echo 和 GPIO18 之间加一个 1k 到 2k 的限流电阻再并在 GPIO18 到 GND 之间加一个 3.3k 电阻组成简单的分压。我实际用的是 1k 串接加 2k 对地实测波形干净长时间运行没有异常发热。安装时要反复核对量程。JSN-SR04T 的量程通常是 25cm 到 450cm意味着探头距离水面的距离必须落在 25cm 和 450cm 之间。假如探头安装高度是 4 米河底距离探头 4 米那最低水位时测距是 4 米超量程了水位涨到距探头 0.3 米时又太近。所以我实际安装时把探头固定在距最高预期水位以上约 2 米的位置保证整个量程都落在 0.3~4 米区间内。2.3 供电与续航计算野外监测设备最容易死的地方是供电我花了挺多时间做这块的功耗估算。ESP32 在正常工作状态下 WiFi 连接但不上报数据时电流大约 80mA5V也就是 0.4W 功耗。每次上报数据时电流会冲到 200mA 左右但只持续不到 2 秒平摊下来几乎可以忽略。传感器每 30 秒触发一次一次触发从发射到接收也就几十毫秒同样不是功耗大头。整机实际功耗大约在 0.5W。用 12V 10Ah 锂电池供电的话电池容量是 120Wh理论续航 240 小时也就是 10 天。考虑到 DC-DC 降压效率约 85%实际续航大概 8 天左右。单靠电池撑整个汛期显然不够所以我在部署点加了一块 20W 太阳能板和控制器。按每天有效光照 4 小时估算20W 板子一天能产出约 80Wh扣除充电损耗还有 60Wh 以上的净补充只要不是连续半个月完全无光照就能自持运行。安装时我把太阳能板固定成 45 度倾角朝正南方。这个角度在冬季能兼顾太阳高度角偏低的情况夏季虽然效率不是最优但胜在不需要人为调整。电池则放在带呼吸阀的防水箱里不要完全密封否则充电时内部气压变化容易把箱子撑裂。3. KiwisIoT 平台接入与数据链路搭建3.1 先弄清楚 KiwisIoT 平台能帮你做什么很多人在 IoT 项目里纠结要不要自己写后端我的看法是如果项目目标是快速交付一套能用的预警系统直接用成熟的 IoT 平台是性价比最高的选择。KiwisIoT 这类平台的核心价值在于把物联网项目里重复的脏活累活都接走了。它主要提供四块能力。设备接入方面平台支持 MQTT、HTTP 等协议设备通过密钥认证后就能建立通讯通道。数据流处理方面每次上报的数据会按时间戳存入时序数据库网页上直接画出变化曲线。告警规则方面可以设定“当 water_level 大于某个值时触发告警”平台负责检查每一条上报数据并执行规则。数据可视化方面不需要写前端代码把测点拖到仪表盘上就能生成实时面板。我第一次用这类平台时最困惑的是“物模型”这个概念。其实物模型就是给设备定义一个“说明书”这个设备有哪些测点每个测点是什么类型、什么单位。比如我们这个设备有一个测点叫water_level类型是 double单位是 m。平台按这个“说明书”来解析设备上报的数据所以建产品和上报格式必须保持一致。3.2 设备快速开通的完整步骤从注册到收到第一条数据整个过程大约半小时。我以 KiwisIoT 平台为例把步骤列出来其他同类平台的流程基本相似只是菜单名称可能略有差异。第一步注册并登录平台进入控制台。 第二步在产品管理页面创建产品产品类型选“直连设备”网络协议选 MQTT。 第三步定义物模型新增一个测点属性名称water_level数据类型 double单位 m读写方式为只读。如果还想监测电池电压可以再加一个battery_voltage测点。 第四步在产品下添加设备输入设备名称后系统会生成唯一的设备标识和密钥。密钥要保存好固件里要用。 第五步记录平台给出的 MQTT Broker 地址和端口。默认端口通常是 1883如果平台支持 TLS 加密则用 8883 端口更安全。上报数据用的是 MQTT 的发布功能我按照平台文档的格式发布 JSON 消息到设备的遥测 Topic。这里我用通用格式演示实际项目里以你注册设备时平台分配到的 Topic 为准mosquitto_pub -h mqtt.example.com -p 1883 -t v1/devices/me/telemetry -u YOUR_DEVICE_KEY -m {water_level: 2.35}这条命令里我直接用了 mosquitto 命令行工具做测试如果能在终端里收到平台的回显说明设备密钥、Topic、JSON 格式全都没问题。固件里只要有 ESP32 的 MQTT 库逻辑是完全一样的。3.3 告警规则和数据可视化配置平台收到数据之后最重要的一步是配置告警规则。我踩过一次坑只设了“水位超过 3 米”这一个条件结果水面漂浮物偶尔挡住超声波回波出现了一个瞬间的 10 米假信号平台直接误报了。后来我调整规则加上了“持续时间”这个维度要求水位连续 60 秒都超过阈值才判定为真实告警误报率立刻降下来了。我的建议是把告警规则拆成两级。警告级水位超过 2.5 米持续 60 秒推送即时通知。危险级水位超过 3 米持续 30 秒除了推送通知还要触发现场的声光报警器。两级结构的好处是第一级让值班人员提高警惕第二级才真正启动应急响应避免高频率的告警把人搞麻木。可视化配置相对简单。在仪表盘页面新建一个面板把water_level测点拖到图表组件里时间范围选最近 24 小时刷新间隔设为自动。这样就能看到完整的涨水过程。我额外把“水位变化速率”做成一个衍生图形具体做法是让平台上对最近 10 个数据点做线性拟合斜率就是速率。涨水速率比绝对水位更能提前反映风险。4. 固件实现与关键代码拆解4.1 ESP32 采集端程序结构整个固件用 Arduino 框架开发核心逻辑分成四个模块WiFi 连接、传感器采集、数据处理、MQTT 上报。完整代码的框架如下直接可烧录到 ESP32 使用。#include WiFi.h #include PubSubClient.h #include ArduinoJson.h const char* ssid YOUR_WIFI_SSID; const char* password YOUR_WIFI_PASSWORD; const char* mqtt_server YOUR_MQTT_BROKER_ADDR; const int mqtt_port 1883; const char* device_key YOUR_DEVICE_KEY; const char* telemetry_topic v1/devices/me/telemetry; const int trigPin 5; const int echoPin 18; const float sensorHeight 3.0;4.2 水位数据校准与滤波算法4.3 告警逻辑与数据补发机制我差点在最关键的模块翻车。一开始以为只要把传感器数据发到平台就行直到有一次测试发现采到的数值在水面上漂着一片树叶的情况下能跳 20 厘米才知道真正的难点在数据处理那一环。水位数据必须过两道关滤波和校准。先说滤波。水面不是静止的风吹起波浪、雨点砸在水面上、探头下方偶尔游过去一条鱼都会让超声波回波距离产生毛刺。我采用的方案是简单而耐用的中值滤波连续采集 5 次距离值排序后取中间值。相比平均值滤波中值滤波在消除毛刺方面果断得多一个离群的异常值不会把结果拉偏。实现函数如下float getFilteredDistance() { const int N 5; float samples[N]; for (int i 0; i N; i) { samples[i] readDistanceOnce(); delay(50); } for (int i 0; i N - 1; i) { for (int j i 1; j N; j) { if (samples[j] samples[i]) { float tmp samples[i]; samples[i] samples[j]; samples[j] tmp; } } } return samples[N / 2]; }readDistanceOnce()内部就是标准的超声波读取时序Trig 拉高 10us然后用pulseIn等高电平持续时间乘以 0.017 换算成厘米。注意pulseIn要加超时参数我设的是 8000 微秒超过 1.4 米量程就返回-1避免程序卡住。校准这一步很多人会漏掉。传感器的读数是“探头到水面的距离”水位应该是探头安装高度减去这个距离。理论算很简单但实际安装时支架不可能刚好水平、探头也不可能刚好在预设高度上所以我在现场做了一次单点校准。方法是用卷尺实测当时水面到探头底部的距离同时记录传感器读数两者之差就是安装偏差。我实测得到传感器读数 1.42 米卷尺量 1.39 米差 0.03 米我就在程序里把sensorHeight修正为理论高度 - 0.03。4.3 阈值告警与数据补发机制告警逻辑写进固件的好处是不依赖平台推送即使断网也能驱动现场的声光报警器。我的设计是三级确认机制本地检测到水位超过阈值后不是立刻报警而是连续 3 次采集都超阈值才确认。const float warningLevel 2.5; const float dangerLevel 3.0; uint8_t tripCount 0; void checkAlarm(float waterLevel) { if (waterLevel dangerLevel) { tripCount; if (tripCount 3) { digitalWrite(ALARM_PIN, HIGH); } } else { tripCount 0; digitalWrite(ALARM_PIN, LOW); } }这个设计的逻辑是过滤掉极端毛刺但也不漏掉真实险情。三次采集的时间间隔是 30 秒加上滤波本身会消耗 250 毫秒整个确认过程最长 90 秒对于水位上涨场景来说完全来得及。数据补发机制是我在维护过程中才加的。野外的 WiFi 不像家里那么可靠我遇到过连续两天里面断线十几次的情况。MQTT 断线期间平台那边曲线会出现大段空白虽然事后能连上但缺失的历史数据没法回补。解决方法是 ESP32 在内存中维护一个环形缓冲区断线时把采集到的数据存进缓冲区重连成功后按时间顺序补发。const int MAX_CACHE 100; float cacheLevels[MAX_CACHE]; unsigned long cacheTimestamps[MAX_CACHE]; int cacheCount 0; void cacheData(float level, unsigned long ts) { if (cacheCount MAX_CACHE) { cacheLevels[cacheCount] level; cacheTimestamps[cacheCount] ts; cacheCount; } } void flushCache() { for (int i 0; i cacheCount; i) { client.publish(telemetry_topic, buildJson(cacheLevels[i], cacheTimestamps[i]).c_str()); delay(100); } cacheCount 0; }补发的数据量不要一味贪大我设 100 条是因为 30 秒一条能覆盖 50 分钟断线时间足够应付绝大多数网络抖动了。缓存太多反而会占用内存而且断线太久意味着现场 WiFi 环境本身有问题先进去排查网络更实在。5. 真实部署中的避坑指南5.1 现场安装最容易忽略的三个环境问题设备在室内测试跑得好好的一到野外就各种幺蛾子这是物联网项目的常态。我总结了三个最容易让水位监测系统“翻车”的现场问题。第一个是水面漂浮物。超声波测距的原理决定了它只能测到“第一个回波”如果探头下方正好漂着一片大树叶或一团泡沫读到的距离就是漂浮物的距离不是水面的距离。我自己的处理方法是给探头装了一个直径 15 厘米的 PVC 导波管管子底部伸进水面以下探头在水面上方管子里向下测距。这样漂到管子口的东西被挡住管内的水面相对平静回波稳定很多。第二个是冷凝水。探头长期暴露在外昼夜温差大时探头表面会凝结一层水膜超声波在遇到水膜时会产生折射导致测距偏大。我试过加热丝、也试过涂防雾涂层最后发现最有效的组合是持续降低探头的发热量并且在外壳上开斜向下的透气小孔让内部湿气自然排出去。探头表面再涂一层薄薄的硅油能有效延缓冷凝。第三个是太阳能板夏季性能衰减。很多新手以为夏天日照强、发电量一定最大实际是太阳能板温度每升高 10 度发电效率下降 3% 到 5%。夏天的暴晒反而让板子表面温度高达 70 度输出功率比春秋天还低。我后来把板子抬高了 30 厘米底部留出空气流通间隙让热空气能从下方散走面板温度明显下降充电电流也恢复了不少。5.2 常见问题排查速查表整理一份我在调试和维护过程中整理的排查表遇到问题时按表格顺序查基本能绕开绝大部分坑。问题现象常见原因排查与解决ESP32 无法连接 WiFi信号太弱或 SSID 名称被漏掉检查 2.4G 频段室外超过 50 米距离建议加定向天线或改用 4GMQTT 频繁掉线网络不稳定或心跳间隔过短把 keepalive 调整为 60 秒检查 Broker 地址是否填对水位读数偶尔跳变水面波纹或漂浮物遮挡加导波管确认中值滤波在生效适当延长采集时间间隔平台收不到数据Topic 或 JSON 格式不匹配用 mosquitto_pub 命令行单独测试排除固件问题数据全部显示 NaN物模型里测点类型没对齐检查 double 类型和water_level字段大小写是否一致告警一直不触发告警规则里持续时间设太长先把持续时间降到 1 秒测试确认规则生效后再调回实际值夜间电池电压掉得快太阳能板脏污或充电控制器配置错检查板子表面清洁确认控制器是 PWM 还是 MPPT与电池电压匹配长时间运行后读数整体偏高探头表面粘了灰尘或水痕定期清理探头如果频率很高可以考虑加装防凝露的罩子这里要提醒一句排查问题时最忌讳一次改多个变量。我吃过亏同时改了滤波算法和上报频率结果读数变化了却不知道是哪个改动引起的。规范的做法是每次只改一个参数验证通过再动下一个。5.3 多站点扩展与后续增强方向这套系统本身就是一个“最小可用样板”验证通了之后扩展的空间其实很大。我现在部署了三个监测点分别在河道上游、桥梁下方和入湖口每个点一台 ESP32、一个传感器、一组太阳能供电共用同一个 KiwisIoT 平台账号。多站点并网后最关键的变化从“看单点曲线”变成了“看多点对比”。我把三个站点的water_level拖到同一个仪表盘面板上水量从上往下赶的时间差一目了然。上游监测点先涨水 20 分钟下游桥洞点差不多 40 分钟后才开始涨这样一个时间差就是非常宝贵的预警窗口。这类“数据之间互相印证”的能力是单点部署无论如何也得不到的。下一步我打算基于这些实时数据做涨水速率预测。目前是拿最近 30 分钟的水位变化斜率估算未来 1 小时水位准确率还不高但已经够用于粗粒度分级。以后数据积累多了可以匹配历史雨量数据训练一个更精细的预测模型。硬件层面我计划在主控附近加一个内置电池的无线报警器位置远一点也能联动这样即使主设备被水淹了远端报警器还能工作。最后再说一个我装在杆子上的小经验超声波的安装角度别死脑筋地对准水面正中央稍微向下倾斜 5 到 10 度反而能减少水花和漂浮物的正面干扰但也别倾斜太多角度大了反射波就跑偏了。我最终是在支架焊了一个可调角度的 U 型卡现场慢慢微调找到一个读数最稳定的角度才固定住。这类细节听起来不起眼但决定你的系统会不会在雨季最需要它的那一刻掉链子。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑