ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于STM32F103C8T6的智慧大棚:自动浇灌与温湿度控制实战

基于STM32F103C8T6的智慧大棚:自动浇灌与温湿度控制实战 蹲在自家菜园子里看着那几棵黄瓜苗从下午到傍晚一直蔫头耷脑我蹲到腿麻了才回过味来——不是缺水是这几天下雨把根泡了土壤湿度一直超着我还在天天浇水。那一刻突然觉得种了这么多年菜全靠经验和感觉该让机器帮我看一看了。兜里正好有一块STM32F103C8T6开发板蓝色药丸那种最小系统板放了好几个月没动。我想了想与其让它吃灰不如直接把菜园子升级成一个小型智慧大棚能自己测土壤湿度自己开泵浇水温度高了自动开风扇通风还能显示出来让我一眼看清状态。这篇文章就把我用STM32F103C8T6开发板从零搭智慧大棚的全过程写下来。里面包括硬件选型、传感器接线、ADC采样、继电器控制、DHT11温湿度读取以及最后联调时踩的各种坑。适合两类朋友一是学单片机想做个真能落地的小项目而不是点亮LED就结束的二是家里有菜园、阳台花园想用低成本实现自动浇灌和通风管理的。我会把思路和代码都讲清楚照着抄基本能复现。1. 那颗蔫黄瓜提出的需求清单1.1 传统管理模式到底哪里撑不住了我家的菜园不大大概二十来平种了黄瓜、番茄和一些绿叶菜。以前管理靠什么靠下班回家看一眼。土发白了就浇叶子打蔫了就查下雨了就愁出差就焦虑。黄瓜尤其娇气土壤太湿容易沤根太干容易落花温度超过35度基本不长。这些规律我都懂可人总有没法定时到位的时候。那几棵蔫黄瓜让我重新梳理了需求浇不浇水不能只凭肉眼要用数据说话。土壤湿度需要被持续监测浇水要能自动执行别等我回家再救大棚里的温度和湿度也要盯着通风得跟着温度走。说白了我需要一个不偷懒、不凭感觉、全天候盯着的菜园管家。1.2 功能清单先想清楚再动手做这种项目最怕一上来就焊板子写代码。我先在手机上列了个清单把要管的事分成四类土壤湿度检测用湿度传感器插入土中实时读取含水量让系统知道当前土是干是湿。自动灌溉控制湿度低于下限时打开水泵达到目标湿度后停止同时保留手动模式方便育苗、施肥时人工干预。空气温湿度监测棚内温度过高时自动打开排风扇降低温度极端高温时还能通过蜂鸣器或屏幕提示。本地状态显示用一块OLED屏滚动显示土壤湿度、空气温湿度、水泵和风扇状态让我路过时瞄一眼就知道系统在干什么。这套东西不需要联网不需要云平台先把基础自动化跑通就是一套合格的智慧大棚雏形。1.3 为什么偏偏是STM32F103C8T6可能有人会问这类场景用Arduino Uno或者ESP32不是更省事吗确实论上手速度Arduino更友好论联网ESP32更直接。但我选STM32F103C8T6主要是三个理由一是稳定性和资源够用。F103C8T6主频72MHz20KB SRAM64KB Flash带12位ADC、多个定时器、USART、I2C、SPI跑四五个传感器加控制逻辑绰绰有余不卡顿不重启。二是学习价值。Keil4下建立工程、标准库函数调用、寄存器级的底层操作这些是嵌入式开发的基本功。用Arduino写digitalWrite习惯了回头再看STM32的GPIO配置真的会打开另一个世界。三是成本极低。蓝色药丸板在电商平台上也就十来块钱传感器模块加继电器、水泵全套下来不超60元比市面上任何成品智能浇灌设备都便宜而且坏了哪块换哪块。注意我用的STM32F103C8T6最小系统板是Blue Pill那种板上自带一个8MHz晶振和USB座。买的时候留意是否有Micro USB口方便烧录和供电个别山寨板子晶振虚焊会导致USART不稳定这是后话。2. 从兜里掏出来的STM32F103C8T6硬件选型与最小系统搭建2.1 开发板选购与关键元器件辨识STM32F103C8T6最小系统板看起来很简单一块板、一个芯片、一个LED、一个复位键、一个USB口外加两排排针。但买的时候有细节看丝印正品STM32F103C8T6丝印清晰C8表示64KB FlashT6表示LQFP48封装。有些翻新片丝印模糊用久了容易复位。看晶振板上有两个晶振一个8MHz主晶振一个32.768kHz副晶振。主晶振虚焊会导致串口波特率不准、程序跑飞。看排针最好让卖家焊接好排针自己焊容易虚焊特别是电源脚和BOOT0脚虚焊会让你怀疑人生。2.2 传感器与执行器全家福我这套系统的硬件清单如下每样都标了大致费用和用途器件型号/规格作用参考成本主控板STM32F103C8T6最小系统板系统大脑15元土壤湿度传感器电容式带模拟输出检测土壤含水量10元空气温湿度传感器DHT11或DHT22测棚内温湿度5-10元继电器模块单路5V低电平触发控制水泵和风扇5元/个微型潜水泵直流5V/12V灌溉浇水8元排风扇5V/12V直流风扇大棚通风降温8元显示屏0.96寸OLEDI2C或LCD1602显示状态和参数10-15元降压模块LM2596或AMS1117等电源分配3-5元按键轻触开关两个调节阈值/切换模式1元土壤湿度传感器这里我特别说一句市面上常见的蓝色那种是电阻式两片裸露金属插土里靠测土壤电阻判断湿度便宜但容易电解腐蚀。我选的是电容式PCB镀金测的是土壤介电常数变化寿命长得多。后面第三节会细讲两者的数据差异。2.3 供电分布与接线安全这是最容易被忽略、也最危险的一环。整套系统有三种电压等级传感器和单片机用3.3V继电器模块和风扇可以用5V水泵多数是5V或12V。我试过直接用电脑USB口给全系统供电单片机带得了但一开继电器水泵就复位因为瞬间电流超过了USB口的承载。我的最终供电方案是用一个5V/2A的旧手机充电器作为总电源。5V直接供给继电器模块、风扇、水泵水泵实际运行电流约300mA风扇约150mA2A够用。5V接入开发板的5V引脚板载稳压器转出3.3V给OLED和传感器供电。全部共地所有模块的GND都汇聚到同一个节点否则I2C和DHT11时序会乱。接线布局上强电弱电分开走。虽然我这里都是直流低电压但继电器触点通断时会产生瞬间电流波动如果传感器线跟继电器输出线平行绑在一起ADC读数会出现周期性毛刺。我后来用了一根屏蔽线传输土壤湿度模拟量毛刺立刻消失。这个经验后面还会细讲。2.4 Keil4下建立工程与烧录流程有朋友看到Keil4下建立工程就打退堂鼓觉得老工具难用。实际上针对F103C8T6Keil4加标准外设库是网上资料最全的组合很多坑别人都替你踩完了。我建立工程的习惯是这么几步下载标准外设库STM32F10x Standard Peripheral Library解压后找到Libraries目录里面有CMSIS和StdPeriph_Driver两个文件夹。在Keil4里新建工程芯片选STM32F103C8然后在工程树里添加启动文件startup_stm32f10x_md.s。添加标准库源文件stm32f10x_gpio.c、stm32f10x_rcc.c、stm32f10x_adc.c、stm32f10x_tim.c等用到哪个加哪个别全加否则编译会报一堆无关警告。在Options的C/C选项卡里定义USE_STDPERIPH_DRIVER并在Include Path里把Libraries的四个头文件目录全部加入。烧录不用ST-Link的话可以直接用串口ISP下载BOOT0跳线跳到1接USB转TTL模块把PA9TX接模块RX、PA10RX接模块TX然后打开FlyMcu或mcuisp选串口烧写。烧完后把BOOT0跳回0按复位键就能运行。这套流程第一次搞大概一下午第二次十分钟搞定。工程模板建好后一定存个压缩包备份以后每个项目都从模板复制省事太多。提示如果你手头有ST-Link或JLink直接用SWD调试更省心。SWD只需要连接SWDIO、SWCLK、GND三个引脚不需要占用PA9/PA10的串口资源。但串口ISP依然是救命的备份方案两个都配好最稳妥。3. 土壤湿度传感器与ADC让开发板知道土干没干3.1 电容式与电阻式土壤湿度传感器的差别这块我强烈建议打算长期使用的朋友直接上电容式。电阻式的原理是给两片金属探针加电压土壤越湿电阻越小通过比较器输出数字量和模拟量。它便宜、响应快缺点是金属探针在通电状态下容易电解用不了几个月表面就出现腐蚀读数飘得没法看。电容式传感器通过高频振荡测土壤介电常数。水的介电常数大约是80干土才3到5土壤含水量一变传感器电容跟着变输出模拟电压也跟着变。它两个电极被PCB保护层覆盖不经受电解反应埋在土里大半年基本没事。这两种传感器我都用过。同一块地里电阻式的AO输出电压在1.2V到3.2V之间电容式的在1.6V到2.8V之间换算成湿度百分比时标定曲线完全不一样。所以程序里的上下限阈值必须跟着传感器类型走不能照抄别人的参数。我用的电容式模块标称是干土输出电压高、湿土输出电压低这个逻辑各家模块可能正好相反代码注释里一定要写清楚。3.2 ADC采样与数据滤波别让毛刺骗了你的水泵STM32F103C8T6内部有2个12位ADC共16个通道我用的土壤湿度传感器模拟输出接在PA1上。ADC配置用标准库写其实不复杂// 开启GPIOA时钟和ADC1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); // 配置PA1为模拟输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); ADC_Cmd(ADC1, ENABLE);单次转换的流程是启动转换等待EOC标志位置位然后从ADC_DR寄存器读数据。直接读的数值抖动很大特别是土壤里有石块或者探针周围有缝隙时单次读数可能虚高或虚低。我的滤波方法分两层第一层是软件去毛刺。连续采样20次去掉最大值和最小值剩下18个取平均。ADC转换速度很快20次采样也就几百微秒对控制周期没有影响。第二层是时序补偿。土壤湿度不是秒级变化的东西浇水后需要十几分钟到几小时才会重新分布。所以我不需要高频采样而是每5秒采一次并且把最近5次平均值再取平均作为当前湿度参与判断。这样即使继电器吸合瞬间引起电压小波动湿度值也不会猛地跳一下导致误动作。3.3 滞回比较判断别让水泵疯狂启停刚开始我写逻辑很简单低于30%就浇水高于60%就停。实际跑起来发现问题了。因为湿度值总是在阈值附近波动水泵会开机十秒、停十秒、再开机继电器咔哒咔哒响个不停一个晚上能动作几十次。继电器触点频繁通断寿命下降是小事水泵频繁启停也容易烧。解决办法是引入滞回比较机制。类似于空调温控器的原理启动阈值和停止阈值分开中间隔一段缓冲区间。我的设置是土壤湿度低于30%启动水泵土壤湿度达到60%停止水泵在30%到60%之间保持当前状态不动作。这样一来一个浇水周期里水泵只动作一次灌到60%之后随着蒸发自然回落直到再次跌破30%才重启。这套逻辑对慢变量尤其重要因为你根本不需要精确控制在某个点。代码里就是这个状态if (soil_humidity HUMIDITY_LOW) { watering_flag 1; } else if (soil_humidity HUMIDITY_HIGH) { watering_flag 0; } // watering_flag为1时开启水泵为0时关闭3.4 继电器控制水泵与手动/自动切换执行端我用的5V单路继电器模块低电平触发。STM32的GPIO输出能力有限高电平驱动能力弱低电平反而更稳所以继电器模块都设计成低电平触发这是常识但新手踩坑最多的地方就是这里以为IO输出高电平继电器就吸合结果怎么弄都没反应其实是没看触发逻辑。GPIO配置成推挽输出初始化时输出高电平让继电器保持断开。自动模式下程序根据滞回逻辑给PA2引脚写低电平或高电平来控制继电器的吸合和断开。我额外加了一个三位拨动开关自动挡、手动挡、关闭挡。自动挡时程序控制手动挡时直接接通水泵电源给菜地浇水关闭挡则是彻底断电防止误动作。这个手动开关救过我一次有次传感器探头被蜗牛爬过导致读数异常系统判断土壤极度缺水水泵一直开着要不是我及时拨到手动挡那块地就水漫金山了。所以自动化系统再智能也不能取消物理后备手段。继电器模块驱动水泵时我串联了一个二极管型号选1N4007反向并联在水泵两端作续流保护。直流电机是感性负载断电瞬间会产生反向电动势尖峰可能击穿继电器触点和驱动管。加续流二极管后电流在二极管和电机之间续流消耗掉继电器触点寿命明显改善。4. 大棚的呼吸系统温湿度监测与排风联动4.1 DHT11单总线时序与读取实现DHT11是智慧大棚项目里的老朋友3根引脚VCC、DATA、GND单总线通信。别看它便宜时序要求挺严格用标准库配置GPIO操作时时钟延时要特别留意。DHT11的数据读取流程是这样主机先拉低总线至少18ms发起开始信号然后释放总线传感器响应后会拉低80us再拉高80us表示准备好。之后就是40个数据位每个数据位以50us低电平开头紧接着的高电平时间决定该位是0还是126到28us高电平算070us高电平算1。这里最实用的坑是读数据过程中要把GPIO从输出模式切成输入模式读完再切回输出模式。用标准库操作时切换方向其实就是一个函数调用GPIO_InitStructure.GPIO_Pin GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOC, GPIO_InitStructure); // 发开始信号 GPIO_ResetBits(GPIOC, GPIO_Pin_3); delay_ms(20); GPIO_SetBits(GPIOC, GPIO_Pin_3); // 切换成输入模式 GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOC, GPIO_InitStructure);DHT11的精度是温度±2度、湿度±5%RH对大棚场景完全够用。如果你需要更高精度可以把库函数换成DHT22分辨率能到0.1度但时序读取方式几乎一样。4.2 温度上限触发排风机阈值与延迟配合大棚夏天最怕的就是闷热棚内温度能比外界高出七八度。我设定的是超过32°C自动开风扇降到28°C以下才关风扇。这里同样用了滞回区间避免风扇在28°C到32°C之间反复启停。但光有温度触发还不够因为大棚内的温度变化是渐进的风扇启动后热量不会立刻降下来。我加了一个软件延迟温度超过32°C后必须连续持续60秒的5次检测结果都超限才真正启动风扇。这样既滤掉了瞬时波动又不会让系统反应过慢。我还顺手做了一个报警动作当温度超过40°C或者空气湿度低于20%时蜂鸣器会间歇鸣叫提醒我人工干预。就算系统再自动化极端天气下还是需要人去看一眼。4.3 OLED显示与按键设置不用电脑也能调参系统跑起来之后总不能每次改阈值都插电脑重新编译烧录那也太原始了。我加了一块0.96寸的OLED屏和两个按键实现现场调参。OLED用I2C接口只需要SDA和SCL两根线。用标准库配置I2C1外设把OLED的驱动库文件加入工程然后在主循环里刷新显示第一行显示土壤湿度百分比第二行显示空气温度和湿度第三行显示水泵和风扇状态第四行显示当前模式自动/手动和湿度上下限值。按键的用途长按按键1进入参数设置模式短按切换编辑哪个参数字段按键2加减数值。改完后长按按键1退出新的阈值直接写到Flash里下次开机不用重新设置。这个功能做出来后整个系统的产品感提升了一个档次。以前我改个阈值要接电脑开Keil现在蹲在地头按几下按键就搞定这才叫真正把开发板变成了工具。5. 把所有模块拼成一套能跑的系统接线、主循环与联调5.1 引脚规划总表系统一复杂引脚规划就特别重要。我一开始随手接线后面调试发现PA9和PA10要留给串口打印日志I2C引脚跟DHT11的引脚冲突只能重新焊线。第二次学乖了先在纸上把所有外设的引脚列成一张总表再动手。外设信号引脚说明土壤湿度传感器AOPA1模拟量输入ADC1通道1水泵继电器INPA2低电平触发控制水泵风扇继电器INPA3低电平触发控制排风扇DHT11DATAPC13单总线数据注意此引脚也接了板上LEDOLEDSCLPB6I2C1时钟OLEDSDAPB7I2C1数据按键1输入PB0模式/确认按键2输入PB1加减/切换蜂鸣器输入PA5温度异常报警有两个坑必须提醒第一PC13这个引脚有点特殊它同时连接了开发板上的LED通常是一个蓝色LED而且因为引脚驱动能力有限直接驱动DHT11的数据线可以但别接重负载。我后来为了避免干扰把DHT11挪到了PB5PC13只用来看程序是否在运行的指示灯。第二PA9和PA10我特意没占用留着串口打印调试信息。联调阶段系统运行状态通过串口以115200波特率往电脑发日志比看OLED屏幕实时多了。你在Keil里用串口调试助手一开湿度变化曲线一目了然。5.2 主循环框架别让一个传感器卡死整台机器嵌入式系统最忌讳的就是在main函数里用一个大循环顺序执行某个模块一旦卡死整个系统全跟着停摆。我在主循环里用了时间片轮询的思路像这样while (1) { if (time_now - last_adc_time 5000) { // 每5秒采集土壤湿度 sample_soil_humidity(); update_water_control(); last_adc_time time_now; } if (time_now - last_dht_time 3000) { // 每3秒读取温湿度 read_dht11(); update_fan_control(); last_dht_time time_now; } if (time_now - last_display_time 500) { // 每500ms刷新屏幕 oled_show_all(); last_display_time time_now; } check_keyboard(); // 按键检测放在循环前部优先响应 }每个功能块都在自己的时间片里运行互不阻塞。即使DHT11一次读取失败超时也不会影响土壤湿度的采集和水泵控制。这套框架看起来简单但比起很多人一开始采用的while(1){ 读传感器; 控制; 显示; }模式稳定性和可维护性好了一个量级。5.3 实测数据与效果系统完整跑起来后我记录了连续三天的大棚数据。这里挑几组典型的白天中午棚内空气温度从31°C升高到35°C程序在连续60秒超32°C后自动打开排风扇大概25分钟后温度降回28°C风扇自动关闭。整个过程我全程没碰任何按键。土壤湿度方面自动浇水逻辑起了大作用。之前夏天我每天傍晚浇水土壤湿度在40%上下波动但黄瓜根系集中在10到15厘米土层表面干了不代表根系区域缺水。传感器探针埋深10厘米左右测出的数据更接近真实根系环境。系统把30%作为启动线后一夜之间自动补水两次第二天早上黄瓜叶子明显挺直了不少。三天里有一个下雨天空气湿度接近90%土壤湿度掉得很慢系统一次水泵都没启动。换做我之前的习惯看到土表略干就会浇一遍那些水其实完全没必要。数据帮我改掉了看表面定干湿的错误直觉。5.4 联调中的问题与修复一个完整的排查链路联调阶段不是一帆风顺我至少遇到三个典型问题逐个说说排查思路。第一个问题是OLED屏幕花屏乱码。一开始我以为是I2C时序不对调了几天都没用。后来用示波器看发现SDA线上叠加了周期性毛刺源头是继电器吸合瞬间电源电压跌落导致OLED主控复位。解决方法是给OLED的VCC脚就近加一个100uF电解电容同时把I2C线尽量缩短、远离继电器输出线。电容加完花屏现象彻底消失。这个排查链路的关键是不要一上来就怀疑固件时序先看电源纹波。第二个问题是土壤湿度读数间歇性狂跳。某天下午湿度值突然从45%跳到5%然后又跳回45%间隔毫无规律。我怀疑三件事接线松动、ADC参考电压波动、传感器探头旁边有石子。排查顺序是先查硬件再查软件。我把传感器从土里拔出来放在空气中测试读数稳定说明传感器本身没坏重新插入后问题依旧挖开土一看探头旁边压着一块小石头它改变了探头周围的介电常数分布。把石子清掉、把探头周围土壤压实后读数恢复稳定。所以说调程序之前先看物理环境。第三个问题最隐蔽继电器不动作。现象是程序写入PA2低电平后继电器模块没有反应。我用万用表量PA2的输出电压发现确实是0V说明序号指令已经执行。那就不是软件问题而是模块的触发逻辑和我理解的不一样。仔细看模块丝印才发现我买的继电器是高电平触发版本和常见的低电平触发恰好相反。程序里把控制逻辑取反问题立即解决。所以拿到任何模块第一件事是看丝印说明而不是默认它有标准触发电平。6. 缠着胶布的地线飘忽的数据连续运行后的反思与优化6.1 数据漂移不是玄学传感器老化与标定周期系统稳定跑了大概一个月后我注意到土壤湿度读数整体上升了约8个百分点。同一块地、同样的浇水逻辑水泵启动频率变低了。刚开始我以为是土壤确实变湿了但用手摸土层感觉是偏干的说明读数失真。排查后发现电容式传感器虽然耐腐蚀但其输出会随探头表面附着物和土壤盐分积累而漂移。土壤里的离子会慢慢附着在镀金探头上相当于串联了一个阻抗改变了有效介电常数。解决办法不是换传感器而是做定期标定。我把标定流程固化成习惯每个月把探头取出在空气中测一次读数A再插入一杯清水中测一次读数B然后把这两个值作为新的标定点写入程序。土壤湿度的百分比按线性映射重新计算。这个步骤用OLED菜单就能完成不需要连电脑。标定后读数回到正常范围水泵动作再次符合预期。6.2 继电器触点打火与水泵空转保护连续运行后我还发现一个隐患水泵在低水位时如果继续运行会空转发热容易烧坏电机。我的水泵放在蓄水桶里水用完不一定能及时补充所以空转保护必须做。我的办法是每次水泵启动前先记录当前土壤湿度然后设定最长运行时间。如果持续运行超过5分钟湿度却没有任何上升趋势就判定为空转强制停止水泵并让蜂鸣器报警提示。这个逻辑很简单但能避免一次潜在的水泵烧毁事故。实际上正常灌溉周期一般在1到2分钟5分钟确实大得足够判断异常了。继电器触点打火的现象在交流电路里更常见但直流电感性负载一样存在。我除了加续流二极管还给继电器触点涂了一层薄薄的触点保护油脂。这东西可能不太好买手边如果没有至少做到定期检查触点表面的氧化层发黑就用细砂纸轻磨一下。继电器是易损件尤其是频繁通断时备两个模块永久不亏。6.3 后续还能怎么玩从单机到云端的几条路这套系统跑通以后下一步的扩展方向很明确。首先是联网。STM32F103C8T6本身不带WiFi但可以通过USART接一个ESP8266或ESP32模块当作协处理器把传感器数据定期推到手机APP或云端平台。这么做的最大好处是出差在外也能实时看到大棚状态甚至远程手动控制水泵。其次是加装补光灯用定时器输出PWM控制LED灯亮度模拟日照时长这对冬季育苗很有用。最后是用光伏板加锂电池构建独立供电系统让整套设备完全脱离市电真正做到扔在地里不管.我现在的菜园已经离不开这套系统了。它不是一个多大的工程却实实在在解决了我的问题。那颗蔫了的黄瓜苗后来救回来了现在长势比同期其他菜还要好。很多时候不是说传统农业本身有什么大错而是我们手里的工具已经足够先进没理由继续全靠人肉盯梢。如果你手头也有一块STM32F103C8T6开发板在吃灰我建议你别再点LED了直接用它给阳台的花盆、菜箱装一套自动浇灌踏踏实实用一个夏天。你会发现调试遇到的每一个坑都是比任何教程都值钱的收获。该下手就下手工具是拿来干活的不是拿来收藏的。
返回列表