多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

ESP32智能浇水装置DIY教程:从土壤湿度传感到自动灌溉系统

ESP32智能浇水装置DIY教程:从土壤湿度传感到自动灌溉系统 我家的薄荷树是被我亲手“渴”死的。那年出差两周出门前信誓旦旦地浇透了水回来看到的却是一盆干透的草药标本。当时我就在想与其每天靠闹钟提醒“记得浇水”不如做一个能自己判断、自己动手的装置。也是从那天起我把“DIY smart electronics project”当成了一个正经周末项目来做——你别说这个项目做完以后我家的植物再没因为我的出差和健忘遭过殃。这篇 step-by-step guide 是我把完整过程重新梳理后的成果包括方案选型、硬件接线、固件逻辑、实测排错和后续升级方向。适合两类人一是刚能点亮 LED、想挑战一个“真正能干活”的电子项目的入门玩家二是已经有 Arduino 基础、但没接触过土壤湿度传感器和水泵控制的老手。我会把踩过的坑都摆出来尽量让你少走弯路。1. 这个项目为什么值得做从养死一盆薄荷说起1.1 解决的真实问题出差、健忘和水资源浪费智能浇水听起来像是“闲得没事折腾”但它解决的其实是三个非常具体的问题。第一出差或旅游时植物没人管。出门前浇透水只是赌运气夏天2-3天土就干了时间一长薄荷、薄荷、绿萝这些窗口植物基本都挺不住。第二日常忙碌导致“想起来才浇”有时候接连几天忘记然后某天突然良心发现一次性浇到盆底漏水反而把根泡烂。第三浇水全凭感觉该浇的时候不浇不该浇的时候大水漫灌浪费水也伤植物。做一个土壤湿度自动检测、自动滴灌的小装置本质上就是把“什么时候该浇”“浇多久”这两个决策交给机器。它有传感器感知土壤状态有控制器做决定有水泵执行命令再配上串口或者显示屏让你能看见状态。整个过程麻雀虽小五脏俱全。1.2 系统架构感知、决策、执行、反馈这个项目我推荐的核心硬件是 ESP32 开发板而不是 Arduino Uno。理由很直接ESP32 自带 Wi-Fi以后想加远程通知、局域网控制都不用换主控价格还差不多。整套系统天然分成四层感知层土壤湿度传感器负责把土壤里的含水量变成电信号决策层ESP32 读取传感器数据用设定好的控制策略决定开泵还是关泵执行层继电器驱动水泵把水箱里的水输送到花盆反馈层串口打印状态、指示灯、可选 Wi-Fi 通知让用户知道系统在想什么。这四个层次划分清楚以后后面的接线和调试不会乱。很多新手一上来就照着别人的电路图连线连完也不知道每根线为什么存在一旦出问题完全不知道该从哪查。而先明确“每一层负责什么”排查时就可以按层级定位传感器读数不对就查感知层水泵不动就查执行层逻辑不符合预期就查决策层。1.3 全套物料清单重点不是多贵而是选对这套东西成本不高核心物料清单如下物料建议型号/规格参考价格用途ESP32 开发板NodeMCU-32S 或兼容板约 15-25 元主控负责读取和决策电容式土壤湿度传感器SEN0193 或兼容模块约 20-30 元检测土壤含水率5V 微型水泵潜水泵或隔膜泵流量 1-2L/min约 10-15 元抽水灌溉继电器模块单路 5V 高电平触发模块约 3-5 元用小电流控制大电流5V/2A 电源适配器5V/2A 以上约 15 元给水泵、继电器和 ESP32 供电水箱矿泉水瓶或塑料瓶0 元存水硅胶管/软管内径 4-6mm约 1-2 米约 3 元输水杜邦线、面包板公对母、母对母各若干约 10 元原型验证阶段连接注意传感器一定要买电容式的别买电阻式。这里面的门道我下一节详细说它直接影响到这个设备能用几天还是用几年。2. 测量与执行的底层逻辑传感器、水泵、继电器到底怎么回事2.1 电容式 vs 电阻式传感器便宜的不一定省钱土壤湿度传感器市面上两种最常见电阻式两个金属探针插进土里和电容式一块 PCB 形状的叉子表面涂了防腐蚀涂层。电阻式的原理是测土壤的导电性。土壤越湿离子越多导电性越强。听起来没问题但致命缺陷是金属探针直接插在湿润土壤里本质上就是在做电解实验。两边的金属在电场和水分作用下会缓慢腐蚀而且土壤里的盐分还会附着在探针表面改变导通特性。它的读数初期是准的然后会漂移得越来越厉害。我第一版就图便宜买了电阻式模块结果三个星期后传感器自己“偏差到不认识世界”彻底无法使用。电容式传感器测的是土壤的介电常数两个电极之间形成一个电容水分多时介电常数高输出的模拟电压或者频率信号随之变化。因为电极不是裸露的金属长期埋在土里不容易腐蚀稳定性和寿命都远强于电阻式。多花这几块钱换来的是不需要每两个月换一次传感器这笔账怎么算都划算。2.2 校准不是可选项从 ADC 原始值到“该不该浇水”拿到传感器后第一步不是直接写控制逻辑而是先标定。ESP32 的 ADC 读出来不是“百分比湿度”是一个 0-4095 的原始整数值12位 ADC。两块相同的传感器放大电路和供电电压稍有差异同样的土壤读出来都可能差几百。我用的方法特别土但非常有效把传感器插进你这盆植物当前用的干燥土壤里等 30 秒串口打印出来的原始值记下来称为DRY_VALUE拿一杯水慢慢地浇到土壤里不用浇到大水漫灌让土壤湿润即可等 10 分钟让水分渗透均匀再读一次原始值称为WET_VALUE。注意标定的时候传感器位置要和你实际安装位置基本一致。你盆底是排水孔、盆壁是陶粒还是纯园土都会影响读数。标定完成以后控制逻辑就非常简单了原始值高于DRY_VALUE表示土壤已经干透启动水泵原始值低于WET_VALUE表示已经浇透关闭水泵。有的教程会直接把原始值映射成“湿度百分比”。公式可以这样写float mapToMoisture(int raw, int dryValue, int wetValue) { if (raw wetValue) return 100.0f; if (raw dryValue) return 0.0f; return (float)(raw - wetValue) / (dryValue - wetValue) * 100.0f; }但我实际用下来真正控制水泵的“触发点”比百分比更可靠。直接拿原始值做阈值少一层转换反而减少出错的概率。百分比更多用来给你自己看状态让人觉得直观。2.3 为什么 GPIO 不能直接驱动水泵继电器和续流二极管很多第一次做水泵项目的朋友会问ESP32 的 GPIO 不是能输出 3.3V 吗直接把水泵接上去不行吗不行。GPIO 的电流输出能力非常弱官方手册建议单脚输出电流尽量控制在 20mA 以内。微型水泵启动瞬间电流通常在 500mA 到 1A 之间是 GPIO 能力的几十倍。硬接上去的结果轻则系统重启重则 GPIO 口烧毁、主控报废。所以正确的路径是GPIO 控制继电器继电器触点控制水泵的电源通断。继电器起到的作用是一个“电流放大开关”小电流控制线圈磁力磁力吸合触点让大电流通过触点为水泵供电。继电器模块还有几个实战细节买“高电平触发”模块这样 ESP32 上电时 GPIO 默认低电平水泵不会误启动模块供电用 5V控制端兼容 3.3V 逻辑可以直接接 ESP32 的 GPIO如果水泵本身没有续流二极管保护大部分便宜微型水泵都没有建议在继电器公共端 COM 和水泵之间并一个 1N4007 二极管方向是反向电压吸收。水泵断电瞬间会产生反向电动势没有保护会加速继电器触点老化严重的还会干扰单片机复位。3. 开工接线步骤、顺序与通电前自检清单3.1 引脚分配先谈清楚再动手我给你的这个方案接线非常简单一共就 6 根关键线。完整引脚分配如下功能ESP32 引脚说明传感器 VCC3V3给传感器供电注意别接 5V3.3V 更安全传感器 GNDGND公共地传感器 模拟输出GPIO34只读 ADC 引脚不能作为普通输出继电器 VCC5V模块线圈供电继电器 GNDGND公共地必须和 ESP32 共地继电器控制脚 INGPIO26高电平开泵状态 LED可选GPIO27亮灯表示正在浇水这里有一个特别重要的原则传感器和继电器可以共用一个 5V/2A 适配器供电但水泵的电源必须先经过继电器触点不能直接从适配器引线过来并联到水泵。切换水泵启停的唯一开关就是继电器。3.2 接线顺序按“电源先、信号后”的原则第一步先把 ESP32 开发板固定在某个不碍事的地方。我建议用一个亚克力板或者洞洞板当底座把板子、继电器、传感器固定住不要直接扔在桌上桌面上的误触和拉扯太容易损坏杜邦线接头。第二步接土壤传感器。传感器的 VCC 接到 ESP32 的 3V3GND 接到 GND模拟输出接到 GPIO34。这里我多说一句ESP32 的 ADC 引脚里 GPIO34 是只读的不能当数字输出用乱配容易出问题。第三步接继电器模块。VCC 接 5VGND 接 ESP32 的 GNDIN 脚接 GPIO26。如果买的模块是高电平触发那么这个逻辑全部适用。第四步接水泵电源回路。这是最容易接错的一步。先把 5V 适配器的正极接到继电器的 COM 公共端再把继电器的 NO常开端接到水泵的正极水泵的负极直接接适配器的负极。为什么用 NO 而不是 NCNO 在继电器不吸合时是断开的也就是水泵默认关闭NC 在继电器不吸合时是导通的水泵默认打开这个状态太危险了。第五步最后确认一遍所有 GND 已经连在一起。电子系统最忌讳“各回各家”的地线传感器、继电器、ESP32 必须共地共地不完整会造成电平混乱传感器读数会跳继电器也可能误动作。3.3 通电前的 10 分钟自检每一秒都算数我第一次做这个项目水泵一直不工作排查了一个多小时后发现是继电器模块的“VCC 和 GND”杜邦线接反了模块的指示灯压根没亮。所以通电之前给自己列一个自检清单检查电源极性适配器正负极有没有接反检查信号接线传感器 OUT 是否接到 GPIO34继电器 IN 是否接到 GPIO26检查继电器触点COM 接电源正极NO 接水泵正极NC 空着先不要接水泵用一个 LED 串联 1kΩ 电阻接到继电器 NO 端口模拟水泵负载手动让 GPIO26 输出高电平观察继电器触点是否吸合、LED 是否点亮确认 LED 能亮以后再接上水泵进行第一次真实抽水测试。这个“先 LED 后水泵”的习惯后来帮我避了很多坑。你不用一上来就用水泵不然出了问题就从“接线错”“代码错”“电源不足”“继电器坏”四个因素里轮番猜排查成本很高。4. 固件逻辑从传感器读到控制水泵的完整 Arduino 代码4.1 能直接编译运行的固件下面这段代码我用 Arduino IDE 写选好 ESP32 开发板型号后可以直接编译。注释里把关键逻辑都写了建议你一行一行读过再烧录别直接当“魔法黑盒”。/* * 智能浇水控制器 - ESP32 电容式土壤湿度传感器 5V微型水泵 * * 功能 * - 定时采样土壤湿度 * - 干燥时自动开泵浇透后自动关泵 * - 带超时保护防止水泵意外长时间运行 * - 状态通过串口打印 * * 接线 * 传感器 OUT - GPIO34 * 继电器 IN - GPIO26 * 状态 LED - GPIO27 */ #include Arduino.h // 引脚定义 #define PIN_SENSOR 34 // ADC1_CH6只能做模拟输入 #define PIN_RELAY 26 // 继电器控制引脚 #define PIN_LED 27 // 浇水状态指示灯 // 校准参数请替换成你自己标定得到的数值 #define DRY_VALUE 2800 // 干土时传感器原始 ADC 值 #define WET_VALUE 1350 // 浇水后传感器原始 ADC 值 // 控制参数 #define PUMP_TIMEOUT_MS 120000L // 单次浇水最长 120 秒防止异常漏水 #define SAMPLE_INTERVAL_MS 10000L // 每 10 秒采样一次 unsigned long lastSampleTime 0; unsigned long pumpStartTime 0; bool pumpRunning false; // 移动平均滤波取 10 次采样结果的平均降低 ADC 抖动 int readSmoothedValue() { long sum 0; for (int i 0; i 10; i) { sum analogRead(PIN_SENSOR); delay(2); } return (int)(sum / 10); } // 把原始 ADC 值映射成湿度百分比仅用于显示 float mapToMoisture(int raw) { if (raw WET_VALUE) return 100.0f; if (raw DRY_VALUE) return 0.0f; return (float)(raw - WET_VALUE) / (DRY_VALUE - WET_VALUE) * 100.0f; } // 控制水泵启停统一记录时间 void setPump(bool on) { digitalWrite(PIN_RELAY, on ? HIGH : LOW); digitalWrite(PIN_LED, on ? HIGH : LOW); pumpRunning on; if (on) { pumpStartTime millis(); } } void setup() { Serial.begin(115200); pinMode(PIN_RELAY, OUTPUT); pinMode(PIN_LED, OUTPUT); // 上电默认关泵。预防 ESP32 启动瞬间 GPIO 不确定状态导致的误触发 digitalWrite(PIN_RELAY, LOW); digitalWrite(PIN_LED, LOW); Serial.println([system] smart watering ready); } void loop() { unsigned long now millis(); // 非阻塞调度每隔 10 秒读一次传感器而不是用 delay 死等 if (now - lastSampleTime SAMPLE_INTERVAL_MS) { return; } lastSampleTime now; int raw readSmoothedValue(); float moisture mapToMoisture(raw); // 控制核心双阈值 超时保护 if (!pumpRunning raw DRY_VALUE) { setPump(true); } else if (pumpRunning) { // 条件1已经浇透 // 条件2超过最长保护时间 if (raw WET_VALUE || now - pumpStartTime PUMP_TIMEOUT_MS) { setPump(false); } } // 串口输出状态方便观察系统行为 char buf[80]; snprintf(buf, sizeof(buf), [data] raw%d moisture%.1f%% pump%s, raw, moisture, pumpRunning ? ON : OFF); Serial.println(buf); }这里有两个细节值得注意一是millis()是非阻塞调度的基础如果你在循环里用delay(10000)那么系统在等待期间完全无法执行“超时保护”逻辑水泵万一卡住就永远关不掉了二是移动平均滤波ESP32 的 ADC 会有几毫伏到几十毫伏的噪声连续采10次求平均值能明显让数据平滑下来。4.2 为什么控制逻辑用了迟滞而不是单一阈值新手容易犯的一个错误是设一个“湿度低于 20% 就开泵高于 20% 就关泵”。听起来没问题但实际土壤水分变化不是均匀的。浇水的过程中水先湿润传感器周围的土然后才慢慢往深层渗透。传感器读数会先冲到很湿然后随着水分向盆底和周边扩散又缓慢回升一点。如果只设一个阈值水泵可能刚停没几分钟又因为读数越过阈值重新启动。这样一晚上能启停七八次继电器寿命和植物的根系都受不了。我的做法是让启动点和停止点分开raw DRY_VALUE干透了才启动raw WET_VALUE充分湿润才停止。两个阈值之间留了很大的“缓冲带”天然形成迟滞效果。土壤湿度处在这个区间时系统什么都不做。这跟空调的温控原理一模一样你不会希望空调在 26°C 到 26.1°C 之间反复横跳而是让它到 27°C 启动制冷、到 25°C 停机。4.3 超时保护这条代码是你家的“最后防线”代码里PUMP_TIMEOUT_MS是最能救命的参数。正常情况下我们家阳台那一盆植物水泵单次浇水也就 10 到 30 秒。但如果出现水管脱落、传感器读数始终达不到湿阈值、或者继电器触点被杂质粘连系统就可能在无人看管的周末持续运行。你没有超时保护的话可能两小时以后地板已经泡汤了。120 秒是我实测后定的合理上限足以完成一次浇灌又不会造成灾难性后果。你可以根据自己花盆大小和水泵流量适当调整但建议无论如何都要保留这个限制。没有超时保护的控制系统就算逻辑再漂亮也逃不过“偶尔出一次事故就把整个系统否定掉”的命运。5. 实测中我踩过的坑从跳动的 ADC 到半夜自动浇水5.1 传感器读数剧烈跳动电源和滤波双重问题第一次把系统跑起来我对着串口监视器看了一晚上发现一件诡异的事什么也没发生传感器的原始值却在 1000 到 3000 之间反复横跳。当时我以为是电磁干扰换了好几个位置都没用。后来找到了两个原因第一我的传感器供电接的是开发板的 3.3V而继电器和水泵用的同一个 5V 适配器每当继电器吸合或者水泵启动电源线上会产生尖峰电压影响 ADC 参考电压。解决办法是让传感器供电走独立的弱电分支并且确保电源适配器余量足够不要刚好卡在水泵的峰值电流上。第二是本身 ADC 精度有限必须靠软件滤波。我用 10 次采样取平均值以后读数基本稳定在 ±30 以内。5.2 半夜“自动浇水”的真相上电瞬间 GPIO 悬空我调试时发生过一次特别头疼的故障程序烧录完一按 ESP32 的复位键水泵自己就“嗡”地一声启动了然后才关掉。如果这是半夜断电恢复那植物可能莫名其妙被浇一次要是水箱里刚好没水水泵空转还会烧。原因在于 ESP32 复位期间 GPIO 引脚短暂处于高阻状态电平不确定。如果继电器模块恰好把这个不确定电平识别成高电平就会触发吸合。我的解决办法有两层一是选高电平触发的继电器并且在继电器 IN 脚到 GND 之间并联一个 10kΩ 下拉电阻把启动期间的电平强制拉低二是代码里setup()第一条就执行digitalWrite(PIN_RELAY, LOW)让引脚在第一时间进入确定状态。两套保险下来这个问题再没出现过。5.3 浇了很久湿度仍然显示“干”传感器摆放位置不对还有一个把我也坑了很久的问题水泵运行了 90 秒盆底都已经渗水了传感器读数还是显示“干”系统一直不关泵。我蹲下去摸了摸土明明已经湿透了。问题出在传感器位置。我把传感器埋得太靠近盆壁而水滴是往下渗透的上层土先湿下层土后湿。更关键的是传感器只有中间一小段有效感应区域我插得太深上面的湿土信号没被充分感应到。正确做法是让传感器的叉型感应头水平朝上埋在花盆高度二分之一到三分之二的位置并且尽量不要紧贴盆壁保证它感应到的是“主流土壤区域”而不是边缘死角。5.4 水泵打不出水硅胶管里有气还有“自然吸气”失败另一种“泵启动但没水”的情况更隐蔽。我的第一版管路是从 5L 矿泉水瓶里抽水软管插在水里。水泵启动时管内还有一段空气水泵没有自吸能力就一直空转流量很小甚至没有。解决办法有两个一是把水泵安装位置尽量低于水面让管路靠重力自然充满水二是第一次使用前主动向进水管注满水帮助水泵建立“虹吸启动”的条件。以后每次换水只要注意别让进水管完全排空就能稳定工作。5.5 断电恢复后的默认状态GPIO 默认值也要规划这个项目家里用偶尔会遇到停电和来电。停电时水泵肯定停但来电后系统重启我必须确保它以“关泵”状态重新启动而不是恢复到一个未知状态。这里面有两个层面硬件上继电器模块的 IN 有下拉电阻确保默认低电平软件上setup()里主动把继电器引脚写成 LOW。这还不够更保险的做法是用 EEPROM 保存上一次的控制状态重启后读取记录再决定是否恢复而不是让控制循环立刻根据当前土壤湿度判断。因为刚重启的瞬时 ADC 读数不一定可靠空载读数和插入土壤后的读数差异很大万一读出一个“假干”信号就会马上启动水泵。6. 从“能浇水”到“智能”按需升级的三个方向6.1 多盆多路一个 ESP32 管 5 盆花做了单盆以后很多人第一反应是“我家还有另外几盆”。ESP32 本身不止一个 ADC 引脚如果你只是要读 3-4 个传感器可以直接用多个 ADC 引脚。但如果传感器数量再多一些我建议用一个模拟多路复用器 CD74HC4067用 4 个 GPIO 就可以扩展出 16 路模拟输入。执行端也可以用同样的思路扩展把单路继电器换成多路继电器模块水泵抽水后接一个三通/多通分水器每路加一个电磁阀或者手动阀就能按传感器数据分别给每一盆浇水。注意每一路的软管长度不同水到达花盆的时间也不同管路长的那一路需要单独调“延迟关闭”的参数。这个扩展对固件要求不低但完全在 ESP32 的能力范围内。6.2 接入家庭自动化用 MQTT 把状态和手动控制放到手机ESP32 最大的优势就是自带 Wi-Fi不联网实在可惜。最简单的方式是跑一个 MQTT 客户端把土壤湿度、泵状态、当前时间戳周期性推送到家里路由器上的 MQTT Broker再配合 Home Assistant 或手机 App 做展示和控制。你也可以在 ESP32 上直接开一个 Web Server手机浏览器访问局域网 IP 就能看到实时数据并手动点击“立刻浇水”按钮。不过我给你的建议是联网功能调试到最后再加。先把本地闭环跑稳让它自己能保证植物存活再远程操控才有意义。否则你在外面远程开了几次水泵一旦网络断连系统就完全失联还不如一开始就做离线自动化。6.3 太阳能供电和低功耗让它在阳台角落里自成一体如果你想彻底摆脱电源线可以做太阳能锂电池供电版本。ESP32 的 Deep Sleep 模式能把整机功耗降到几十微安级别每隔 30 分钟醒来一次读传感器再睡回去24 小时平均功耗非常低。一块 5W 太阳能板加一节 18650 电池配合一个带充电保护的小板子就能长期维持系统运行。但有一个硬约束水泵是个耗电大户一次运行就要几百毫安的大电流。所以低功耗方案通常不是把水泵频繁地开开关关而是把浇水频率降下来用大水箱和更高效的滴灌头让一次浇水管好几天。同时电池容量要按“最坏情况连续阴雨天没有太阳能”来算至少保证 3 到 5 天的电量余量。最终我才明白智能硬件项目的“智能”不体现在传感器有多高级、代码有多花哨而是体现在它能长期稳定地替你做一件你懒得做、但做错了又后悔的事。这个浇水装置现在已经在我家阳台跑了快半年中间经历了几次断电、几次换水、一次传感器位置的微调。我现在出差前只需要看一眼水箱水位剩下的就交给 GPIO34、GPIO26 和那套又土又实用的控制逻辑。如果你也想做一个属于自己的智能电子项目从“照顾好一盆植物”开始是个特别好的起点。
返回列表