基于STM32的温室大棚温湿度监测控制系统设计与实现
2026/9/18 10:51:05 网站建设 项目流程

简介:一份完整的温室大棚温湿度控制系统毕业设计文档,适合电气、电子信息类专业学生及设施农业自动化方向的工程实践者参考。文档以STC89C51与AT89C51微控制器为核心,结合DHT11温湿度传感器,从系统总体结构出发,先后展开单片机最小系统、震荡电路、复位电路、中断系统等硬件设计,并详细说明DHT11的引脚定义、电源连接和单线双向串行接口等应用要点,同时对软件控制算法、数据处理与存储方式做了系统梳理,还讨论了国内外温室控制领域的发展现状、趋势与挑战。资源包仅包含1个doc文件,整体大小2.29MB,内有开题报告、章节目录和完整系统设计流程,结构清晰便于查阅;目前已有1201人学习下载。对正在准备温室环境监测或单片机应用类毕业设计的读者来说,这份文档既能提供整体框架参考,也可帮助理解温湿度采集电路的连接细节与接口实现思路,节省方案调研和论文组织的时间。

1. 从环境监测到闭环控制:一套可落地的温室大棚温湿度系统该怎么做

做嵌入式或者农业物联网的同学,十有八九都接过“温室大棚温湿度控制”这类需求。表面看就是读两个传感器、控制风机和湿帘,但真正往深了做,你会发现难点根本不在传感器驱动上,而在于系统怎么在“环境变化慢但干扰源多”的温室场景里,做到稳定、省电、可远程维护。本篇文章要聊的这套基于STM32的温湿度监测控制系统,核心是把传感器采集、控制策略、执行机构驱动和人机交互串成一个闭环,而不是简单做个体积小巧的测温仪。

这套系统在工程上通常分为现场采集与控制终端、上位机监控界面两层。STM32负责底层数据采集、控制逻辑和报警处理,上位机通过串口或无线模块拿到数据做展示与参数下发。对5年以上的开发者来说,这篇文章有价值的部分集中在控制策略的选取、传感器滤波与校准、以及系统抗干扰设计这些容易被忽略的细节上。我们直接按“传感器选型 → 电路与驱动 → 控制策略 → 调试验证”这条主线展开。

2. 温湿度传感器选型与STM32接口设计:从DHT11到SHT30的迁移路径

很多课设和毕业设计里选DHT11,因为便宜、资料多、代码到处都是。但在温室大棚场景下,DHT11的±2℃温度和±5%RH湿度精度,以及1Hz的最高采样率,往往不能满足昼夜温差大、湿度变化剧烈的控制需求。更关键的是DHT11的单总线协议对时序要求极严,在STM32上还要关中断去模拟时序,稍有不慎就读取失败。

2.1.1 传感器选型对比:DHT11、DHT22与SHT30的实测差异
参数DHT11DHT22 (AM2302)SHT30
温度精度±2℃±0.5℃±0.3℃
湿度精度±5%RH±2%RH±2%RH
采样周期1s2s0.5ms(typ)
通信接口单总线单总线I2C(最高1MHz)
典型价格2-5元10-15元8-20元

如果你的系统是毕业设计或者演示用途,DHT22是性价比最高的选择,精度够、代码成熟。如果做实际落地项目,优先选SHT30,因为I2C接口可以直接挂载到STM32的硬件I2C外设上,不需要像单总线那样模拟时序,抗干扰能力也更强。另外SHT30内置了加热元件,可以在高湿环境下清除传感器表面的凝露,这对大棚环境非常重要。

2.1.2 基于STM32的SHT30驱动代码框架

SHT30的I2C地址默认是0x44,命令格式为两个字节,比如0x2C 0x06表示开启周期测量模式。下面给出一个最小驱动示例,使用STM32的HAL库。

#include "stm32f1xx_hal.h" #include <math.h> #define SHT30_ADDR_WRITE 0x88 // 0x44 << 1 #define SHT30_ADDR_READ 0x89 extern I2C_HandleTypeDef hi2c1; uint8_t SHT30_ReadTempHum(float *temp, float *hum) { uint8_t cmd[2] = {0x2C, 0x06}; // 周期测量模式,重复性高 uint8_t buf[6] = {0}; uint16_t raw_temp = 0, raw_hum = 0; // 发送测量命令 if (HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR_WRITE, cmd, 2, 100) != HAL_OK) return 1; // 等待测量完成,周期模式下约需16ms HAL_Delay(20); // 读取6字节数据 if (HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR_READ, buf, 6, 100) != HAL_OK) return 1; // 校验CRC(简化版,实际应做CRC8校验) raw_temp = (buf[0] << 8) | buf[1]; raw_hum = (buf[3] << 8) | buf[4]; *temp = (float)raw_temp * 175.0f / 65535.0f - 45.0f; *hum = (float)raw_hum * 100.0f / 65535.0f; return 0; }

这段代码的逻辑分三步:发送命令、延时等待、读取数据。需要注意HAL_I2C_Master_Transmit的第二个参数是设备地址左移一位后的值,因为HAL库内部会再右移一位。如果你用的是寄存器版本的驱动,务必确认地址是否带读写位,这是最常见的移植错误。实际工程中建议加上CRC8校验函数,SHT30的数据手册里有完整的多项式算法示例。

3. 控制策略设计:位置式PID与滞回控制的取舍

温湿度控制系统光把数据读上来没用,关键在控制算法。大棚环境是一个典型的大惯性系统,温度变化缓慢,但执行机构(风机、湿帘、天窗)的动作又是离散的。很多初学者直接上PID,结果发现系统振荡得很厉害,原因在于大棚的滞后性远大于普通电机控制场景。

3.1.1 为什么不用纯PID:滞后系统与积分饱和问题

在大棚里,风机开启后温度要过好几分钟才会明显下降,传感器检测到变化再反馈到控制器,这个纯滞后时间往往超过系统时间常数的三分之一。传统PID在这种系统里,积分项会持续累积误差,等到温度真的降下来时,控制量已经超调过头了。这就是所谓的积分饱和现象。

解决思路有两种:一是对PID输出做限幅,并设置积分分离;二是干脆不用PID,改用滞回控制。对于继电器控制的风机或湿帘,滞回控制更实用。它的原理很简单:设定一个目标温度T_target,允许误差范围ΔT,当温度高于T_target+ΔT时开启风机,低于T_target-ΔT时关闭风机。这样避免执行机构频繁动作,延长继电器寿命。

3.1.2 滞回控制参数表与代码实现

以温度控制为例,参数设置如下:

参数典型值说明
目标温度25℃根据作物种类调整
滞回上限27℃高于此值启动降温设备
滞回下限23℃低于此值关闭降温设备
采样周期5s传感器读取间隔
控制周期30s控制逻辑执行间隔

采样周期和控制周期是两个不同的概念。传感器可以每5秒读一次用于显示和记录,但控制逻辑每30秒执行一次就行,避免执行机构在临界点附近频繁启停。

typedef struct { float target; float hysteresis_upper; float hysteresis_lower; uint8_t cool_state; // 0=停止,1=运行 } TempController; void Temp_Control_Loop(TempController *ctl, float current_temp) { if (ctl->cool_state == 0) { if (current_temp > ctl->hysteresis_upper) { ctl->cool_state = 1; // 开启风机、湿帘 HAL_GPIO_WritePin(FAN_GPIO_Port, FAN_Pin, GPIO_PIN_SET); } } else { if (current_temp < ctl->hysteresis_lower) { ctl->cool_state = 0; HAL_GPIO_WritePin(FAN_GPIO_Port, FAN_Pin, GPIO_PIN_RESET); } } }

这段代码的核心在于状态判断的顺序:先判断当前状态,再决定是否切换。如果反过来,用传感器数值直接判断开关,那么在滞回区间内会频繁抖动。另外,滞回下限和上限之间的区间就是缓冲区,缓冲区越大,执行机构动作越少,但对目标温度的偏离就越大。大棚场景下2-4℃的缓冲区间比较合理。

如果一定要用PID,建议使用增量式PID,并将输出限制在0到100%之间。同时加入积分分离,只在误差小于某个阈值(比如2℃)时启用积分,防止快速升温阶段积分饱和。

4. 执行机构驱动与系统可靠性设计:继电器、PWM与异常保护

控制策略计算出来之后,最终要驱动风机、湿帘泵、补光灯这些执行机构。STM32的GPIO输出的电流很小,不能直接驱动交流设备,中间必须经过继电器或者固态继电器。这个环节最容易出问题的不是电路连接,而是电磁干扰导致MCU死机或重启。

4.1.1 继电器驱动电路与隔离方案

最稳妥的方案是使用光耦隔离的继电器模块。STM32的GPIO连接到光耦输入端,光耦输出端驱动继电器线圈,继电器触点再控制220V交流设备。

典型接线参数:

器件推荐型号/参数备注
光耦EL357N或PC817隔离GPIO与继电器
继电器5V/10A,SRD-05VDC感性负载需并联续流二极管
驱动三极管S8050或ULN2003S8050需配合1kΩ基极限流电阻
续流二极管1N4007反向并联在继电器线圈两端

ULN2003可以替代分立三极管,一个芯片能驱动7路继电器,适合控制设备较多的大棚系统。无论用哪种方案,GPIO到光耦之间都要串联一个300Ω到1kΩ的限流电阻,防止灌入电流过大损坏MCU引脚。

4.1.2 电磁干扰导致复位:看门狗与软件滤波

继电器吸合的瞬间,线圈电感会产生反向电动势,虽然续流二极管能吸收一部分,但仍可能通过电源线或地线干扰MCU。最有效的处理手段有三个:第一,继电器模块和STM32最小系统分开供电,继电器用5V电源,MCU用3.3V LDO供电;第二,在MCU的电源引脚附近并联100nF陶瓷电容和10μF电解电容;第三,开启STM32的独立看门狗,每500ms喂一次狗,防止程序跑飞后系统长时间无响应。

除了硬件防护,软件上也要加入滤波算法。传感器可能会因为电磁干扰读取到异常跳变值(比如温度从25℃瞬间变成60℃)。使用中位值平均滤波法效果很好:连续采集5次数据,去掉最大值和最小值,剩下求平均。

uint16_t SHT30_ReadFilteredTemp(void) { uint16_t samples[5]; uint16_t temp; int i, j; uint16_t sum = 0; for (i = 0; i < 5; i++) { // 假设该函数返回原始温度值 samples[i] = read_sht30_raw_temp(); HAL_Delay(50); } // 简单选择排序,去掉一个最大值和一个最小值 for (i = 0; i < 4; i++) { for (j = i + 1; j < 5; j++) { if (samples[j] < samples[i]) { temp = samples[i]; samples[i] = samples[j]; samples[j] = temp; } } } for (i = 1; i < 4; i++) { sum += samples[i]; } return sum / 3; }

这个滤波函数会阻塞主循环250ms左右,如果在实时性要求高的场合,建议把采样放到定时器中断里,只保留一个滑动窗口缓冲区。计算平均时只取窗口中间几个值,既平滑又不会滞后太多。

4.1.3 系统故障自检机制

一个能拿得出手的温控系统必须包含故障自检。最常见的故障类型是传感器断线(读不到数据)、继电器粘连(控制信号关闭但设备仍在工作)、温湿度超限。

传感器断线很好检测:连续读取5次,如果返回错误码或数据全为0xFF,就判定传感器离线,系统进入安全模式——关闭所有执行机构,并在OLED屏幕和上位机上同时报警。继电器粘连比较难检测,需要加一个电流互感器或者接触器辅助触点,检测成本偏高。折中方案是设置一个最长运行时间,比如风机连续运行超过15分钟就强制停止一次,防止湿帘泵过度工作损坏电机。

void System_Fault_Check(void) { static uint32_t last_cool_time = 0; static uint32_t cool_start_time = 0; uint32_t now = HAL_GetTick(); if (relay_state.fan == 1) { if (cool_start_time == 0) { cool_start_time = now; } else if (now - cool_start_time > 15 * 60 * 1000) { // 风机运行超过15分钟,强制关闭并报警 relay_state.fan = 0; HAL_GPIO_WritePin(FAN_GPIO_Port, FAN_Pin, GPIO_PIN_RESET); last_cool_time = now; fault_code |= FAULT_COOL_OVERRUN; } } else { cool_start_time = 0; } }

这个保护逻辑有一个小坑:直接在系统主循环里调用HAL_GetTick()判断时间差,如果系统使用硬件定时器进中断处理控制逻辑,需要注意临界区资源的竞争。建议把fault_code定义为volatile类型,确保在中断和主循环之间传递时不会被优化掉。

5. 上位机通信协议与实时监控设计:用Modbus RTU避免自创协议

温湿度监控系统至少要提供一个可视化界面。最常见的方案是STM32通过串口连接PC上位机,或者通过ESP8266模块将数据上报到云平台。这里推荐使用Modbus RTU协议,而不是自己定义一套简单的帧格式,原因在于Modbus RTU是一种工业级标准协议,调试工具丰富,且兼容Modbus Poll、Node-RED等现成的上位机软件。

5.1.1 上位机通信协议帧格式定义

一个标准的Modbus RTU帧包含地址码、功能码、数据域和CRC校验。对于温湿度监控,只需要用到03功能码(读保持寄存器)和06功能码(写单个寄存器)。

寄存器地址内容数据类型权限
0x0000当前温度(放大10倍)uint16只读
0x0001当前湿度(放大10倍)uint16只读
0x0002目标温度uint16读写
0x0003控制模式(0=手动,1=自动)uint16读写
0x0004继电器状态(bit0=风机,bit1=湿帘)uint16只读

数据放大10倍是工程常用技巧,因为Modbus寄存器是16位整数,直接传输浮点数需要拆分成两个寄存器,比较麻烦。上位机拿到数值后除以10即可还原小数,精度完全够用。

5.1.2 基于FreeModbus的移植要点

不建议从零手写Modbus协议栈,直接移植开源FreeModbus库效率最高。FreeModbus对多款STM32都有适配例程,核心需要实现三个接口函数:

// 获取系统Tick,用于协议超时计时 uint32_t xMBPortTimersGetTickCountMs(void) { return HAL_GetTick(); } // 启动定时器(用于3.5T字符超时判断) void vMBPortTimersEnable(void) { __HAL_TIM_SET_COUNTER(&htim6, 0); HAL_TIM_Base_Start_IT(&htim6); } // 关闭定时器 void vMBPortTimersDisable(void) { HAL_TIM_Base_Stop_IT(&htim6); }

这三个函数中最容易出错的是vMBPortTimersEnable里的定时器重设逻辑。MB_BCON_TIMER频率必须和波特率匹配,比如9600波特率时,定时器中断周期约为3.5个字符时间,需要根据MCU主频计算预分频值和自动重装载值。很多人在这个环节直接复制例程不改参数,导致上位机通信时好时坏。

Modbus主站向从站发送一帧请求后,从站的响应时间通常在50ms以内。超过100ms没有任何响应,上位机侧就要做超时重试机制,连续3次超时则判定从站离线,弹出报警提示。从站侧则要利用看门狗防止协议卡死导致系统假死。

5.1.3 定时器中断里完成温湿度采集与控制决策

为了保证控制实时性,主循环里只处理Modbus协议任务,温湿度读取和控制逻辑放到定时器中断里。这里有个设计权衡:SHT30的I2C读取需要等待约20ms,如果在中断里做,会长时间阻塞其他中断。推荐方案是把控制逻辑拆成状态机,在定时器中断里只做“读取标志位”和“发送启动转换命令”,数据的处理放到主循环中。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint32_t cnt = 0; if (htim->Instance == TIM6) { cnt++; if (cnt % 5 == 0) { // 每5个周期触发一次(1s) sensor_measure_flag = 1; } if (cnt % 30 == 0) { // 每30个周期触发一次(6s) control_loop_flag = 1; } } }

通过设置两个不同周期的标志位,既避免了在中断里执行耗时操作,又保证了采集和控制各自按合适的频率运行。主循环判断标志位后,才真正去读传感器数据和计算控制输出。这套机制在实时性和代码可维护性之间取得了平衡,也是我通常会在生产环境使用的方式。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询