资讯动态

STM32自动浇花系统源码:从土壤湿度采集到水泵控制

发布时间:2026/10/19 2:33:47 来源:尧图企业网站定制
简介STM32自动浇花系统是一套面向嵌入式初学者与电子爱好者的完整智能硬件实践方案以STM32F103C8T6为核心融合土壤温湿度、光照、空气温湿度传感器与OLED显示、DC-DC电源、水泵控制等模块实现按湿度阈值或定时策略自动灌溉帮助读者理解从传感器采集到执行控制的完整闭环。资源包共535个文件以86个C源文件与70个头文件为主体配合Keil工程文件、PCB设计资料、PDF文档、示例程序及图片素材压缩包约27.77MB覆盖固件代码、硬件设计与调试记录。目前已有3779人学习下载适合希望动手搭建嵌入式项目、掌握STM32CubeMX与HAL库开发流程的读者参考可据此复现硬件连接、移植控制逻辑并扩展远程配置功能。1. 从“出差一周花全枯了”说起STM32自动浇花系统能帮你做什么出差一周回来阳台那盆养了三年的绿萝叶子全耷拉了土干得裂开——这事我经历过不止一次。后来我干脆用STM32搭了一套自动浇花系统土壤湿度低于阈值就自动开泵湿度够了就停手机都不用掏。这份STM32自动浇花系统.zip就是一套完整的工程源码包含主控逻辑、传感器采集、水泵驱动和状态显示几个核心模块。它适合两类人一是刚学完STM32外设驱动、想找个完整小项目练手的嵌入式新手二是家里养了花又经常忘记浇水、想自己动手做一个的开发者。整套代码基于标准库或HAL库组织外设覆盖ADC、GPIO、定时器拿来就能编译烧录改几个阈值参数就能适配你自己的花盆。2. 系统架构拆解从土壤湿度采集到水泵控制的完整链路2.1 硬件模块划分与数据流向这套系统的硬件结构不复杂但每个模块都有明确的职责。主控芯片负责调度整个采集-判断-执行循环土壤湿度传感器把模拟量送到ADC通道MCU读回来做滤波和阈值比较判断结果驱动继电器或MOS管控制水泵同时把当前湿度值和泵的状态刷到OLED或LCD上。如果加了ESP8266之类的WiFi模块还能把数据推到手机端看。数据流向是这样的传感器→ADC→MCU内部处理→GPIO输出→驱动电路→水泵。这条链路里最容易出问题的是ADC采样精度和驱动电路的隔离后面避坑章节会细说。常见做法是把整个工程分成三层硬件抽象层传感器读取、泵控制、显示驱动、业务逻辑层阈值判断、浇水时长控制、防抖、应用层主循环调度、状态机。这样分层的好处是换传感器或换屏幕时只改硬件抽象层业务逻辑不动。2.2 目录结构长什么样拿到源码包解压后典型的目录结构大致如下不同版本可能略有差异但核心文件跑不掉STM32_AutoWater/ ├── Core/ │ ├── Inc/ │ │ ├── main.h │ │ ├── adc.h │ │ ├── gpio.h │ │ ├── tim.h │ │ └── oled.h │ └── Src/ │ ├── main.c │ ├── adc.c │ ├── gpio.c │ ├── tim.c │ └── oled.c ├── Drivers/ │ ├── STM32F1xx_HAL_Driver/ │ └── CMSIS/ ├── Middlewares/ │ └── (可选) WiFi/蓝牙协议栈 ├── MDK-ARM/ 或 STM32CubeIDE/ │ └── 工程文件 └── README.mdCore/Src/main.c是主循环所在地adc.c管传感器采样gpio.c管继电器和按键tim.c管定时中断用来做周期性采样oled.c管显示。如果你用的是标准库而不是HAL库目录会变成User/和Library/的经典结构逻辑一样。提示拿到工程先看main.c里的while(1)循环那是整个系统的“心脏”所有调度逻辑都在里面。2.3 主循环调度逻辑主循环的骨架大概长这样我按HAL库的写法给你还原一下核心结构/* main.c 主循环核心片段 */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM2_Init(); OLED_Init(); uint16_t soil_raw 0; uint8_t pump_state 0; uint32_t last_sample_tick 0; while (1) { /* 每500ms采样一次避免频繁读取导致ADC抖动 */ if (HAL_GetTick() - last_sample_tick 500) { last_sample_tick HAL_GetTick(); /* 读取ADC通道获取土壤湿度原始值 */ HAL_ADC_Start(hadc1); if (HAL_ADC_PollForConversion(hadc1, 10) HAL_OK) { soil_raw HAL_ADC_GetValue(hadc1); } HAL_ADC_Stop(hadc1); /* 原始值映射为百分比0%最干100%最湿 */ uint8_t soil_percent map_adc_to_percent(soil_raw); /* 阈值判断低于30%开泵高于60%关泵中间保持 */ if (soil_percent 30 pump_state 0) { HAL_GPIO_WritePin(PUMP_GPIO_Port, PUMP_Pin, GPIO_PIN_SET); pump_state 1; } else if (soil_percent 60 pump_state 1) { HAL_GPIO_WritePin(PUMP_GPIO_Port, PUMP_Pin, GPIO_PIN_RESET); pump_state 0; } /* 刷新OLED显示当前湿度与泵状态 */ OLED_ShowString(0, 0, Soil:); OLED_ShowNum(40, 0, soil_percent, 3); OLED_ShowString(0, 2, pump_state ? Pump:ON : Pump:OFF); } } }这段代码有几个关键点。第一采样周期用HAL_GetTick()做非阻塞延时而不是HAL_Delay()这样主循环不会被卡死后面加按键或通信不会互相干扰。第二阈值用了30%和60%两个点形成回差区间避免湿度在阈值附近波动时泵反复开关——这个在控制里叫滞回后面避坑章节会专门讲。第三map_adc_to_percent()需要你根据自己传感器的校准值来写12位ADC满量程4095但传感器实际输出范围通常不是0到4095得实测。2.4 传感器采样与滤波处理土壤湿度传感器输出的是模拟电压STM32的ADC是12位精度理论上0到4095对应0到3.3V。但实际用的时候传感器在空气中和泡在水里的输出值并不是0和4095常见做法是实测两个端点值然后做线性映射。/* 将ADC原始值映射为湿度百分比 */ uint8_t map_adc_to_percent(uint16_t raw) { /* 这两个值需要你实测空气中读数 和 水中读数 */ const uint16_t AIR_VALUE 3800; /* 传感器在干燥空气中的ADC值 */ const uint16_t WATER_VALUE 1200; /* 传感器泡在水中的ADC值 */ if (raw AIR_VALUE) return 0; if (raw WATER_VALUE) return 100; /* 线性映射 */ return (uint8_t)((AIR_VALUE - raw) * 100 / (AIR_VALUE - WATER_VALUE)); }参数说明AIR_VALUE和WATER_VALUE必须自己测。把传感器放空气中读一次ADC再泡水里读一次把两个值填进去。不同批次的传感器这两个值能差好几百直接用别人的参数大概率翻车。滤波方面最简单的做法是连续采5次去掉最大最小值再取平均代码里加个数组循环就行。如果ADC读数跳得厉害可以在硬件上并在传感器输出端加一个0.1uF电容到地软件硬件一起上才稳。3. 跑起来编译、烧录与参数校准的完整步骤3.1 开发环境准备与工程导入这套工程大概率是用Keil MDK或者STM32CubeIDE建的。如果你拿到的是MDK工程需要装好Keil MDK和对应的STM32F1xx器件包如果是CubeIDE工程直接File→Import→Existing Projects into Workspace导入即可。以MDK为例打开MDK-ARM目录下的.uvprojx文件检查两件事一是Options for Target里的器件型号是否和你手上的板子一致常见是STM32F103C8T6二是Debug选项卡里选的下载器是不是你用的ST-Link还是J-Link。这两项不对编译能过但烧不进去。编译前先点Rebuild All看有没有报错。常见报错是头文件路径没配好在Options for Target→C/C→Include Paths里把Core/Inc和Drivers的路径加进去就行。3.2 烧录与首次上电检查烧录用ST-Link的话接线是SWDIO、SWCLK、GND、3.3V四根线。烧录成功后先别急着接水泵按这个顺序检查第一步只看OLED。上电后屏幕应该显示当前湿度和Pump:OFF。如果屏幕不亮先查OLED的I2C地址对不对常见是0x78或0x3C在oled.c里改。第二步手动改变传感器环境。把传感器从空气中放进湿土里看OLED上的湿度值有没有变化。如果数值不动用万用表量传感器输出脚电压确认传感器本身在工作。第三步接水泵测试。注意水泵不能直接接STM32的GPIO必须经过继电器模块或MOS管驱动板。GPIO输出3.3V的电流只有十几毫安驱动不了水泵直接接上去要么泵不转要么把IO口烧了。# 如果用命令行烧录st-flash工具 st-flash write build/STM32_AutoWater.bin 0x8000000 # 烧录后复位 st-flash reset3.3 阈值参数怎么改才合理代码里默认的30%开泵、60%关泵不是万能值。不同植物对土壤湿度要求不一样多肉可能20%就够了蕨类可能要保持在50%以上。改的时候注意两点一是开泵阈值和关泵阈值之间至少留20%的间隔间隔太小泵会频繁启停继电器咔咔响寿命也短。二是浇水时长要限制不能光靠湿度判断。万一传感器坏了或者被拔出来湿度一直读0%泵就会一直浇把花淹死。常见做法是在代码里加一个最大浇水时长比如泵连续运行超过10秒就强制关闭并报一个错误状态。/* 浇水超时保护 */ if (pump_state 1 (HAL_GetTick() - pump_start_tick 10000)) { HAL_GPIO_WritePin(PUMP_GPIO_Port, PUMP_Pin, GPIO_PIN_RESET); pump_state 0; fault_flag 1; /* 标记故障OLED显示ERR */ }这个保护逻辑我强烈建议加上成本几行代码但能避免“自动浇花变成自动淹花”的惨剧。4. 避坑与排查调试自动浇花系统时最容易翻车的几个点4.1 现象湿度读数一直在跳泵跟着反复开关原因ADC采样没有滤波或者传感器供电不稳。土壤湿度传感器的输出阻抗比较高STM32的ADC采样时间如果设得太短采到的值就不准。解决把ADC的采样时间从默认的1.5周期改成71.5周期或更长同时在软件里加滑动平均滤波。硬件上在传感器输出和GND之间并一个0.1uF电容。另外阈值判断一定要用回差别用单点比较。4.2 现象水泵不转但OLED显示Pump:ON原因GPIO输出正常但驱动电路没工作。常见是继电器模块的VCC没接或者继电器是5V的而STM32输出3.3V驱动不了。解决先量继电器模块的VCC脚有没有5V。如果是3.3V继电器检查IN脚有没有收到高电平。如果用的是MOS管驱动检查MOS管的栅极电阻和下拉电阻有没有焊。最稳妥的方案是用光耦隔离的继电器模块3.3V能直接驱动。4.3 现象烧录后程序不跑或者跑几秒就死机原因堆栈溢出或者中断优先级冲突。如果工程里开了多个中断定时器、串口、ADC优先级配置不对会导致中断嵌套出问题。解决在CubeMX里检查NVIC配置把SysTick和定时器中断的优先级设好别让高优先级中断里做太多事。另外检查启动文件里的堆栈大小默认可能是0x400如果用了OLED显存数组改成0x800。4.4 现象传感器泡水几天后读数漂移甚至完全失效原因土壤湿度传感器长期通电会电解腐蚀这是这类电阻式传感器的通病。解决不要给传感器长期供电改成采样时才供电用一个GPIO控制传感器的VCC采完就断电。这样能大幅延长寿命。或者换电容式土壤湿度传感器虽然贵一点但不腐蚀。注意电阻式传感器不适合长期埋在土里通电这是硬件层面的坑软件补不了。5. 二次开发从能跑到好用几个改造方向和一个校准技巧这套系统跑通之后能改的地方其实很多。我按优先级给你排几个方向。第一个方向是加WiFi上报。用ESP-01S或ESP8266模块通过AT指令连上MQTT服务器把湿度和泵状态推上去。代码改动集中在主循环里加一个串口发送函数注意别在中断里发放主循环里做。数据格式用JSON最简单{soil:45,pump:0}服务器端随便用什么都能解析。第二个方向是加本地按键和菜单。现在阈值是写死在代码里的改一次要重新烧录。加两个按键和OLED菜单就能在设备上直接调阈值。实现上用状态机短按切换菜单项长按进入编辑代码量大概两百行。第三个方向是加数据记录。用STM32内部的Flash模拟EEPROM每隔一小时存一次湿度值存满一圈覆盖最老的。这样能回看过去几天的湿度曲线判断浇水策略是否合理。最后说一个校准技巧也是我踩过坑之后养成的习惯每次换传感器或者换花盆我都会先跑一遍“干湿标定”。具体做法是在代码里加一个标定模式上电时按住按键进入先把传感器放空气中按一下键记录AIR_VALUE再泡水里按键记录WATER_VALUE两个值自动写进Flash下次上电直接读。这样换传感器不用改代码重烧省事很多。/* 标定模式核心逻辑 */ void calibration_mode(void) { OLED_ShowString(0, 0, Dry: press KEY1); while (KEY1_Read() 0); /* 等待按下 */ AIR_VALUE read_adc_average(); OLED_ShowString(0, 2, Wet: press KEY1); while (KEY1_Read() 0); WATER_VALUE read_adc_average(); save_to_flash(AIR_VALUE, WATER_VALUE); OLED_ShowString(0, 4, Saved!); HAL_Delay(1000); }从那以后我每次换传感器都强制走一遍标定流程再也没出现过“明明土是湿的但显示0%”这种玄学问题。希望这套源码和这些改造思路能帮到你拿到手先跑通主循环再按自己的需求往上加功能比一上来就大改要稳得多。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑