这个基于STM32的温湿度控制系统是我在实验室折腾了两周的成果,主要解决室内环境监测与自动调节的需求。系统以STM32F103C8T6为核心,通过DHT11传感器实时采集环境数据,配合继电器控制的风扇和加湿器实现温湿度自动调节。当数值超过设定阈值时,还会触发声光报警提醒用户。
核心功能模块包括:
硬件选型时特别注意了DHT11的10K上拉电阻问题,实测发现不加这个电阻会导致数据读取失败率高达30%。继电器选用的是HJR-3FF-S-Z,触点容量10A/250VAC,足够驱动常见的小功率家电。
整个硬件系统采用模块化设计,各功能单元通过排针连接,方便调试和故障排查。电源部分采用AMS1117-3.3稳压芯片,为STM32和传感器提供稳定供电。以下是各模块的关键参数:
| 模块 | 型号 | 关键参数 | 接口方式 |
|---|---|---|---|
| MCU | STM32F103C8T6 | 72MHz主频,64KB Flash,20KB RAM | 直接焊接 |
| 显示屏 | LCD1602 | 16x2字符,5V供电 | GPIO模拟4位并行 |
| 传感器 | DHT11 | 单总线协议,3-5.5V供电 | PA0+10K上拉 |
| 驱动芯片 | ULN2003 | 500mA/50V达林顿阵列 | PC13-PC15 |
| 继电器 | HJR-3FF-S-Z | 10A/250VAC触点 | ULN2003驱动 |
传感器接口电路需要特别注意:
c复制// DHT11连接示意图
VCC ----+ +---- 3.3V
| |
10KΩ |
| |
DATA ---+--------+---- PA0
|
GND
继电器驱动部分采用光耦隔离设计,避免大电流干扰MCU:
c复制MCU_IO -> ULN2003输入 -> 继电器线圈
|
二极管续流保护
实际布线时发现,当继电器切换时会在电源线上产生约200mV的电压波动。解决方法是在继电器电源端并联一个470μF的电解电容,同时给MCU供电线路增加0.1μF去耦电容。
DHT11采用单总线协议,对时序要求极为严格。以下是经过实测稳定的驱动代码:
c复制#define DHT11_OUT GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP
#define DHT11_IN GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU
uint8_t DHT11_Read(void) {
uint8_t data[5] = {0};
uint16_t retry = 0;
// 启动信号
DHT11_OUT;
GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
delay_ms(18); // 手册要求至少18ms
GPIO_SetBits(DHT11_PORT, DHT11_PIN);
delay_us(30); // 主机拉高20-40us
DHT11_IN;
// 等待从机响应
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) && retry<100) {
retry++;
delay_us(1);
}
if(retry>=100) return 1;
// 读取40位数据
for(int i=0; i<5; i++) {
for(int j=0; j<8; j++) {
while(!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN));
delay_us(40);
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
data[i] |= (1<<(7-j));
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN));
}
}
// 校验和数据验证
if(data[4] == (data[0]+data[1]+data[2]+data[3])) {
temp = data[2];
humi = data[0];
return 0;
}
return 2;
}
调试时发现,当环境温度低于0℃时,DHT11的响应时间会延长约15%。解决方法是将启动信号的保持时间从18ms调整到25ms,并在读取数据前增加5ms的额外等待时间。
主程序采用状态机设计,每500ms采集一次数据并更新显示,同时检查阈值条件:
c复制while(1) {
static uint32_t tick = 0;
if(HAL_GetTick() - tick > 500) {
tick = HAL_GetTick();
// 读取传感器数据
if(DHT11_Read() == 0) {
// 更新LCD显示
sprintf(lcd_buf, "T:%02dC/%02dC H:%02d%%/%02d%%",
temp, tempThreshold, humi, humiThreshold);
LCD_WriteString(0, 0, lcd_buf);
// 温度控制逻辑
if(temp > tempThreshold) {
FAN_ON();
if(!buzzerState) {
BUZZER_ON();
buzzerState = 1;
buzzerTimer = HAL_GetTick();
}
} else {
FAN_OFF();
}
// 湿度控制逻辑
if(humi < humiThreshold) {
HUMIDIFIER_ON();
LED_ON();
} else {
HUMIDIFIER_OFF();
LED_OFF();
}
}
// 蜂鸣器超时关闭
if(buzzerState && (HAL_GetTick()-buzzerTimer>2000)) {
BUZZER_OFF();
buzzerState = 0;
}
}
// 按键扫描
Key_Process();
}
元件库配置:
常见仿真问题:
电源噪声问题:
传感器抗干扰:
继电器保护电路:
c复制// 继电器线圈两端必须并联续流二极管
VCC ----+-----+---- 继电器+
| |
二极管 |
反向并联 |
GND ----+-----+---- 继电器-
现象:连续读取时出现约15%的失败率
排查过程:
解决方案:
现象:上电瞬间继电器会错误吸合约200ms
原因分析:
硬件改进:
软件优化:
c复制void GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 继电器控制引脚初始化
GPIO_InitStructure.GPIO_Pin = FAN_PIN | HUMI_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(FAN_PORT, &GPIO_InitStructure);
// 上电立即置低
GPIO_ResetBits(FAN_PORT, FAN_PIN);
GPIO_ResetBits(HUMI_PORT, HUMI_PIN);
// 延时500ms确保系统稳定
delay_ms(500);
}
无线传输功能:
c复制ESP8266_TX -- PA3 (USART2_RX)
ESP8266_RX -- PA2 (USART2_TX)
ESP8266_VCC -- 3.3V
ESP8266_GND -- GND
多节点组网:
c复制NRF24L01_MOSI -- PA7 (SPI1_MOSI)
NRF24L01_MISO -- PA6 (SPI1_MISO)
NRF24L01_SCK -- PA5 (SPI1_SCK)
NRF24L01_CE -- PB0
NRF24L01_CSN -- PB1
历史数据记录:
c复制typedef struct {
uint8_t maxTemp;
uint8_t minTemp;
uint8_t maxHumi;
uint8_t minHumi;
uint32_t recordTime;
} EnvRecord;
这个项目最让我意外的是继电器寿命问题——在频繁开关测试中(每分钟10次),普通继电器的机械寿命约5万次。对于需要长期运行的应用,建议改用固态继电器或MOSFET方案。另外,DHT11的精度确实有限,如果预算允许可以升级为SHT30(I2C接口,±0.2℃精度),不过需要重新设计传感器驱动电路。