尧图精选

STM32自动浇花系统实战:土壤湿度传感器与状态机设计

🕒 发布时间:2026/9/7 13:57:50 📁 来源:尧图网络
简介这是一套STM32自动浇花系统的完整工程资源面向嵌入式初学者、电子竞赛选手及智能硬件DIY爱好者解决植物养护中土壤湿度、光照强度与空气温湿度的自动监测以及按需智能浇水的问题。压缩包共535个文件、约27.77MB包含86个C语言源文件、70个头文件、原理图与PCB的PNG图片、PDF说明文档、TXT配置记录和Keil工程文件等可完整看到从驱动编写到应用层控制的代码结构目录层次清晰。系统以STM32F103C8T6为核心集成土壤温湿度、光敏及空气温湿度传感器通过OLED实时显示环境参数并由水泵依据定时或湿度阈值触发灌溉文件中还涉及DC-DC电源模块、继电器控制和PCB布局设计能帮助理解传感器采集、信号处理与功率驱动的完整链路。目前已有3773人学习下载资源内附带工程源码、原理图与说明文档便于对照硬件设计和软件逻辑进行排障、移植或扩展为更高阶的物联网浇花平台。1. 项目背景与整体设计思路1.1 为什么值得做一个自动浇花系统做这个项目的起因特别简单我家里养了十几盆绿植隔三差五出差回来不是干死就是涝死。花盆里的土壤湿度这事儿靠肉眼和经验真的很难把控——表面看着干了底下可能还湿着表面摸着湿润底下可能已经干透了。后来一想这不就是个典型的嵌入式控制问题吗传感器采集数据、MCU做决策、执行器干活STM32刚好样样都能干。这个系统说起来不复杂核心就是三件事测土壤湿度、判断要不要浇水、控制水泵浇水。但真正做下来你会发现每个环节都有不少值得抠的细节——传感器选型、ADC采样稳定性、水泵驱动方式、浇水策略设计哪一个做不好都会影响实际使用效果。也正因为如此它特别适合作为STM32的入门到进阶的练手项目既是经典的毕设选题也是很多电子爱好者会做的第一个能真正解决生活问题的小作品。1.2 系统架构与技术选型整个系统的数据流向是这样的土壤湿度传感器实时检测花盆里的湿度输出电压信号STM32通过ADC引脚读取电压换算成湿度值主控芯片根据预设的阈值和浇水策略决定是否开启水泵水泵通过MOS管或继电器驱动从水箱抽水浇灌同时可以加一个OLED屏或数码管显示实时湿度或者通过串口把数据发到电脑上监控在这个架构里最推荐也最通用的方案是STM32F103C8T6作为主控这是整个STM32家族里性价比最高、资料最全的一颗芯片网络上随便一搜就能找到大量参考代码和教程遇到问题也容易查。传感器方面土壤湿度探头有两种主流类型——电阻式和电容式这个选择直接影响系统的使用寿命我后面会专门讲。软件层面我这次用的是HAL库配合STM32CubeMX做初始化配置。相比标准库HAL库的抽象层更清晰而且CubeMX可以图形化配置引脚和时钟几秒钟就能生成工程框架特别适合这种中小型项目。如果你之前习惯用标准库也没问题核心逻辑是一样的无非是寄存器操作和封装函数调用的区别。2. 硬件选型与电路设计细节2.1 核心器件清单与选型理由先把这次用到的所有硬件列个清单都是很常见、容易买到且价格不贵的器件器件型号/规格作用参考价格主控STM32F103C8T6最小系统板核心控制10-15元土壤湿度传感器电容式推荐或电阻式检测土壤湿度3-10元水泵3.3V-5V微型潜水泵抽水浇灌5-15元驱动模块MOS管模块如AO3400或5V继电器驱动水泵通断2-8元电源USB 5V/2A供电或18650电池组系统供电10-20元显示模块0.96寸OLEDI2C接口显示湿度与状态8-15元其他面包板/洞洞板、杜邦线、水箱连接与结构件10元左右这里面有两点要特别说一下。第一是传感器类型电阻式湿度探头靠两根金属探针插入土壤测的是土壤的电阻值价格便宜两三块钱一个但有个致命缺点——金属探针在湿润土壤里通电久了会发生电解腐蚀用个把月读数就开始漂移最后彻底失效。电容式探头测的是土壤介电常数变化电极不会直接接触土壤寿命长得多虽然贵几块钱但从长期使用角度绝对划算。我自己的第一版用的就是电阻式三个月坏了两个探头后来换了电容式用了大半年一点问题没有。第二是水泵驱动方式。STM32的GPIO引脚最大只能输出20mA左右的电流直接驱动水泵想都不要想必须要加驱动电路。两种主流方案里继电器的好处是导通电阻极小、压降低缺点是机械触点有寿命限制而且频繁通断会有咔嗒声MOS管是纯电子开关无机械磨损、开关频率高但需要注意选型——AO3400这种低压MOS管在3.3V栅极电压下能完全导通是STM32项目的常见搭配。我推荐用MOS管方案静音而且寿命长。2.2 电路连接与接线要点我这次的实际接线方案是这样的以STM32F103C8T6最小系统板为例电容式土壤湿度传感器VCC接3.3VGND接GNDAO模拟输出接PA0ADC1通道0OLED显示屏VCC接3.3VGND接GNDSCL接PB6I2C1_SCLSDA接PB7I2C1_SDAMOS管模块信号端接PA1电源正极接5V输出端串接水泵正极水泵负极接GND水泵供电如果水泵是3.3V-5V额定电压可以直接从USB 5V取电如果水泵功率稍大比如超过2W建议单独用一节锂电池供电避免水泵启动瞬间拉低系统电压导致MCU复位这里有两个非常容易踩的坑我当初都吃过亏。第一个是共地问题——传感器、OLED、模块和STM32必须共地也就是所有GND要连到一起否则信号电压没有参考点ADC读出来的数据会完全乱掉。我第一版调试时OLED死活不亮折腾了两小时最后发现是I2C模块的GND没接简直是基础中的基础但坑人于无形。第二个是水泵供电不要直接从STM32的3.3V引脚取。水泵启动瞬间的电流尖峰能到额定电流的三四倍如果整个系统都用USB 5V供电水泵启动时电压会被拉低到4V甚至更低STM32F103虽然自带稳压电路能撑住3.6V以上的供电但供电电压猛跌还是会导致执行异常、ADC读数跳变。稳妥做法是主控用3.3V供电从板上LDO出来水泵单独从5V电源轨取电中间只通过MOS管做开关隔离。另外MOS管模块的选型还要注意一个参数——逻辑电平。有些模块板载了三极管放大电路输入高电平阈值比较高3.3V的GPIO可能驱动不彻底最好选那种信号端直接接MOS管栅极的纯模块或者用AO3400自己去搭3.3V驱动完全没问题。3. 软件核心逻辑与代码实现3.1 CubeMX工程初始化配置软件部分我习惯先用STM32CubeMX生成初始化代码把引脚和时钟配置好再在生成的框架上写业务逻辑。关键配置项如下调试接口Serial WireSWD保留调试下载能力。这里要提醒一下很多人新建工程时会把调试接口选成No Debug结果第一次下载程序后就再也连不上芯片了只能按住复位键抢时间擦除非常折腾。时钟HSE外部晶振8MHzPLL倍频到72MHz主频。F103的最高主频就是72MHz跑这种逻辑绰绰有余。ADC1开启通道0PA0配置为连续转换模式分辨率12位采样时间尽量拉长——我用的55.5周期因为土壤湿度传感器输出阻抗偏高采样时间太短会导致采到的电压偏低。关于这一点我后面会在问题排查里细说。I2C1标准模式100KHz或快速模式400KHz都行OLED屏用100KHz比较稳。GPIOPA1配置为推挽输出用于控制MOS管。推挽输出比开漏输出好因为开漏不接上拉电阻的话输出不了高电平。CubeMX生成的代码框架里时钟初始化、GPIO初始化、ADC初始化、I2C初始化都是现成的直接在main.c里写业务逻辑就行。3.2 湿度采集与数据处理ADC采样的核心逻辑很直接但有几个细节决定最终效果。我的代码如下uint16_t adc_read_raw(void) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); if (HAL_IS_BIT_SET(hadc1.Instance-SR, ADC_FLAG_EOC)) return HAL_ADC_GetValue(hadc1); return 0; } float get_soil_moisture_percent(void) { uint32_t sum 0; const uint8_t samples 10; for (uint8_t i 0; i samples; i) { sum adc_read_raw(); HAL_Delay(1); } uint16_t avg sum / samples; // 校准ADC值越大电压越高湿度越小电容式传感器输出特性 if (avg 1500) return 100.0f; // 极湿 if (avg 3200) return 0.0f; // 极干 return (3200.0f - avg) / (3200.0f - 1500.0f) * 100.0f; }这段代码里有几个值得展开的点多次采样取平均是有必要的。传感器的模拟输出会受土壤颗粒接触状态、环境EMI、电源纹波等多种因素影响单次采样可能在10%的范围内波动。我实测了10次采样取平均后数据稳定度从±80个ADC量化等级约±8%湿度降到了±15个等级以内约±1.5%效果立竿见影。采样间隔加1ms延时是为了让ADC的采样保持电容有足够时间充电。如果连续快速读后一次采样会受前一次采样残留电荷影响导致结果偏向同一方向这种系统误差靠多次平均也无法消除。线性换算公式里用的1500和3200这两个校准值是拿我的传感器的输出特性标定的——把探头完全泡在水里测ADC值约1500放在干燥空气中约3200。不同品牌、不同批次探头参数会有差异建议拿到传感器后先用这两组极端情况校准一下再把代码里的阈值换掉。这就是标定省略了这一步系统可能永远无法在正确时机浇水。3.3 浇水策略与状态机设计关键问题来了——怎么判断该浇水了最直观的方式是设定一个湿度阈值低于就开启水泵高于就关闭。但直接这么做会产生一个实际问题浇水后土壤湿度变化有迟滞探头周围的水分扩散到完全均匀需要时间如果阈值判断频率太高浇水启动几秒后水分还没扩散开传感器读到的湿度还是低就会继续浇结果浇过头了。我的方案是引入滞回比较加状态机浇水状态机 状态IDLE空闲 湿度 阈值下限如40%→ 进入WATERING状态 否则 → 保持IDLE 状态WATERING浇水 浇水累计时间 单次最大时长如5秒→ 进入PAUSE状态 湿度 阈值上限如65%→ 进入COOLDOWN状态 否则 → 保持WATERING 状态PAUSE间隔等待 延时10秒等待水分扩散和传感器稳定→ 进入COOLDOWN状态 状态COOLDOWN冷却保护 延时2分钟防止水泵过热和过度浇水→ 回到IDLE状态为什么要有这么复杂的逻辑因为我踩过坑。最早我就是一个预设阈值直接判断结果水泵频繁开关——上午十点到下午两点之间每小时启停十几次继电器触点都有点发黑了而且有时候刚浇完水没多久传感器读数回落又触发了一轮浇水整个系统感觉像在抽风。改成状态机之后逻辑清晰了很多。它保证了几个关键特性单次浇水最多持续5秒不会因为传感器响应慢而浇过头每次浇水后强制冷却2分钟既保护水泵也避免系统神经质地反复浇临界状态下的滞回区间40%启动、65%停止避免了在阈值附近反复跳变。这个滞回状态机的思路不夸张地说适用于几乎所有带执行器的自动控制系统——空调温控、鱼缸加热棒、智能窗帘都是同一个模式。学会这一招很多项目就都能举一反三了。控制部分的核心代码如下typedef enum { STATE_IDLE 0, STATE_WATERING, STATE_PAUSE, STATE_COOLDOWN } system_state_t; system_state_t state STATE_IDLE; uint32_t state_start_tick 0; void watering_task(void) { float soil_humidity get_soil_moisture_percent(); uint32_t now HAL_GetTick(); switch (state) { case STATE_IDLE: if (soil_humidity 40.0f) { state STATE_WATERING; state_start_tick now; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // 开泵 } break; case STATE_WATERING: if (soil_humidity 65.0f) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 停泵 state STATE_PAUSE; state_start_tick now; } else if (now - state_start_tick 5000) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 超时停泵 state STATE_PAUSE; state_start_tick now; } break; case STATE_PAUSE: if (now - state_start_tick 10000) { state STATE_COOLDOWN; state_start_tick now; } break; case STATE_COOLDOWN: if (now - state_start_tick 120000) { state STATE_IDLE; state_start_tick now; } break; } }注意我用了HAL_GetTick()作为时间基准这是HAL库内置的毫秒计数器从系统启动开始累加靠Systick中断驱动用它来计时比空转延时靠谱得多。千万不要在状态机里用HAL_Delay()——那会阻塞整个主循环传感器采集和其他任务都会被卡住。4. 数据监控与功能扩展4.1 OLED显示与串口输出系统做出来总要能直观地看到数据不然每次调试都得用电脑接串口看日志。我加了一块0.96寸OLEDI2C接口SSD1306驱动芯片实时显示土壤湿度百分比和当前系统状态。显示的数据包括土壤湿度XX%实时刷新系统状态IDLE / WATERING / PAUSE / COOLDOWN本次浇水累计时长便于观察系统行为OLED的驱动代码用的是常见的SSD1306软件库在GitHub上搜ssd1306 hal就能找到U8g2库也可以只是体积略大。I2C只要初始化对了剩下的就是往显存里写像素然后在主循环里调用显示刷新函数。串口输出也是一个很好的调试手段。初始化一个USART比如USART1PA9/PA10用printf重定向到串口就可以在电脑上通过串口助手实时监控系统状态。重定向代码很简单#include stdio.h int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF); return ch; }然后就可以在代码里用printf(Soil humidity: %.1f%%\n, soil_humidity);打印日志了。这个能力在调试状态机逻辑的时候极其有用不用靠猜直接看串口数据就能知道系统在哪个状态、做了什么决策。4.2 扩展方向WiFi控制和多区域浇灌做完了基础版本你会发现这个系统的扩展空间非常大。我自己的下一步计划是加一块ESP8266模块通过串口和STM32通信实现手机端远程查看湿度和控制浇水。这个方向上热搜词里也提到了ESP8266 wifi模块教程stm32和stm32 8266 宿舍控制灯开发实战说明这个玩法确实是很多人在关注的热点。ESP8266和STM32通信的要点是两边串口配置一致波特率115200是最常见的通信协议自己定义一个简单的帧格式就行了比如帧头命令字数据校验。用AT命令还是用SDK开发取决于你想做到多深入——如果只是想快速实现功能用AT命令就够了如果追求低延迟和可靠性可以让ESP8266跑MicroPython或Arduino固件把MQTT协议栈跑起来然后STM32只管采集和执行逻辑层全部放到云端。还有一个很实用的扩展是接多个湿度传感器分管不同的花盆或区域。F103的ADC1有10个通道PA0-PA7、PB0、PB1每个通道接一个探头用DMA循环采样实现多区域独立浇灌是完全可行的。这样一套系统管一个阳台的绿植每盆花的浇水量都不一样非常实用。5. 常见问题与排查技巧实录这部分是我最想说的。整个项目从零到调通我踩了不少坑有些问题看起来莫名其妙但背后都是有规律可循的。我把最典型的几个问题和解决思路整理成表格后面再挑几个展开细说故障现象可能原因排查方法解决方案串口/调试器找不到芯片SWD引脚被禁用或被复用按住复位键的同时连调试器捕捉上电瞬间改用PA13/PA14的SWD接口避免复用程序里保留SWD配置OLED屏幕不亮或花屏I2C地址错误或接线松脱I2C扫描程序查地址SSD1306常见地址是0x3C或0x3D确认后修改宏定义ADC读数一直为4095满量程传感器信号线悬空或引脚未配置为模拟模式万用表量AO引脚对地电压CubeMX里确认PA0配置为ADC1_IN0模拟输入接线重新插紧ADC读数偏低且波动大采样时间太短或电源纹波干扰波形图观察ADC值分布ADC采样时间改到55.5周期以上水泵启动时与ADC采样任务错开水泵不转但GPIO输出正常MOS管栅极驱动电压不足示波器看MOS管GS波形换成逻辑电平MOS管3.3V可完全导通浇水后土壤水分过好导致传感器读数回升慢水分扩散需要时间观察浇水后3分钟内的读数变化状态机里加入PAUSE等待时间别靠单次判断做决策程序下载过一次后无法再次下载CubeMX工程配置没选SWD按住复位键连接快速下载重刷固件后用SWD重新烧录以后新建工程记得选Serial Wire几个重点问题展开说一下。关于下载一次后芯片就死了。这是F103新手最常见的翻车场景我毫不夸张地说十个搞单片机的至少五个都遇到过。原因就是CubeMX新建工程时Debug选项默认是No Debug第一次下载后代码把SWD引脚复用成了普通GPIO调试接口自然就断了。解决办法也不难按住开发板上的复位键不放点下载在下载开始的瞬间松开复位就能连上并重新烧录。之后再在CubeMX里把SYS的Debug选项改成Serial Wire重新生成代码烧进去以后就正常了。关于ADC读数漂移。如果你发现传感器放在同一个位置不动湿度读数却在一两秒内跳好几个百分点先别急着怀疑传感器坏了。把OLED的刷新频率降下来改成每500ms采集一次、每2秒刷新一次界面你会发现读数稳定了很多。因为OLED刷新和在做I2C通信时会产生电源波动而这个波动恰好被ADC看见了。另外ADC引脚和I2C引脚如果刚好在相邻引脚信号串扰也可能造成影响布线时尽量让模拟信号线远离数字信号线。关于换了新水泵后系统频繁复位。我遇到过一次一开始以为代码写错了排查了很久发现是水泵功率太大启动瞬间把USB 5V电压拉到了4.3V主板上的3.3V LDO输出也跟着跌破了3.0VMCU直接掉电复位。这个问题的本质是电源设计要考虑负载的瞬态电流而不是只算平均功耗。解决方法是给水泵单独供电比如从充电宝的D和D-取电——开玩笑的正经方案是加一个二极管隔离主控供电和水泵供电或者直接给水泵加一个大电解电容缓冲启动电流470uF起步。关于土壤湿度传感器测量的假饱和现象。这是做农业自动化的人都知道的坑刚浇完水土壤表层湿度接近100%但往下几厘米还是干的。如果你的传感器探头插得浅就会出现表面看着湿度足够、实际根部缺水的情况。一个有效的改进是把传感器探头尽量往下插插到花盆中下部那里才是根系主要分布区另外不要只看单次读数触发浇水可以观察连续几次读数的变化趋势——如果浇水后读数上涨很快说明表层湿了但深层可能还干着这时候应该让水泵多运行一会儿。关于安全措施。这个项目虽然简单但有个必须提的安全点水泵在断电情况下还有余水如果长期不用容易滋生细菌。我加的逻辑是每次浇水完成后让水泵空转0.5秒把管道里的水排空这样下次启动时浇的是新鲜水。另外整个系统的电源入口我加了一个自恢复保险丝500mA万一水泵堵转或短路保险丝会自动断开保护主板和USB口成本也就一块钱建议都加上。6. 成品展示与实际使用效果到目前为止我的这套系统已经在家里的绿植上稳定运行了两个多月效果非常满意——出差一周回来花的状态比我亲自照顾的时候还好。数据上总结一下湿度测量精度±3%和专业的土壤湿度计对比过单次浇水时长3-8秒视土壤干燥程度自动调整系统功耗待机状态约0.05W浇水时峰值约2.5W无水状态下2节18650电池2000mAh可以用约10天实际使用中也有一些预想不到的收获。比如通过串口日志发现我阳台上的绿植在傍晚和清晨的蒸腾作用显著降低湿度下降速度比白天慢很多这就启发我可以进一步优化浇水策略——只在早晚两个时段允许浇水太阳直射时不浇水避免水分快速蒸发浪费。这种数据反哺生活的体验是做这个项目之前完全没想到的。如果你也想做这个项目我的建议是第一版先用最少的器件把核心功能跑通——STM32最小系统板、一个湿度传感器、一个小水泵、一个MOS管就够了先实现湿度低→开启水泵→湿度达标→停泵这个最小闭环然后再去加OLED、加WiFi、加多传感器。一步到位做加法出了问题反而难排查。最小闭环验证通过之后剩下的扩展都只是时间问题。我自己在实际操作中最大的体会是这类感知-决策-执行的自动化系统真正的难点从来不在单个模块怎么用而在于怎么把几个模块合理地拼在一起让它们协同工作时不互相干扰。ADC采样要稳定、状态机逻辑要严密、电源设计要留足余量把这三个点想清楚你的自动浇花系统就能稳定跑很久而且这种嵌入式系统设计的综合感觉对以后做任何单片机的项目都有帮助。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →