基于STM32的湿度检测开关传感器设计
一、系统总体设计
1.1 系统架构
当环境湿度超过(或低于)我们设定的安全阈值时,它会像管家一样,自动拉下电闸(开启排风扇或除湿器)。
┌──────────────────────────────────────────────────────────────┐
│ 湿度检测开关系统架构 │
├──────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 湿度传感器 │ │ STM32F103 │ │ 执行机构 │ │
│ │ (DHT11/SHT30)│───▶│ 主控芯片 │───▶│ (继电器/风扇) │ │
│ │ 采集环境湿度 │ │ 逻辑决策中枢 │ │ 执行开关动作 │ │
│ └──────────────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ┌──────────────┐ │ ┌──────────┐ │ ┌──────────┐ │
│ │ OLED显示 │◀───┘ │ 按键设定 │◀──┘ │ 蜂鸣报警 │ │
│ │ (实时数值) │ │ (调阈值) │ │ (超限提示)│ │
│ └──────────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────────────────────────────────────┘
1.2 核心指标
| 参数 | 规格 | 说明 |
|---|---|---|
| 湿度检测范围 | 20% RH ~ 90% RH | 满足绝大多数室内环境 |
| 检测精度 | ±5% RH (DHT11) / ±2% RH (SHT30) | 精度决定控制细腻度 |
| 开关响应时间 | < 200ms | 发现超标立即动作 |
| 开关负载能力 | AC 250V / 10A (通过继电器) | 可直接驱动家用排风扇 |
| 设定方式 | 按键设定 / 出厂预设 | 灵活应对不同场景 |
二、核心硬件设计
2.1 核心元件选型对比
在湿度检测开关中,传感器的选型决定了系统的档次。
| 型号 | 接口方式 | 湿度精度 | 价格成本 | 推荐应用场景 |
|---|---|---|---|---|
| DHT11 | 单总线 (1-Wire) | ±5% RH | 极低 ($1) | 入门练手、普通家居 |
| DHT22 (AM2302) | 单总线 (1-Wire) | ±2% RH | 低 ($2) | 对精度有一定要求的场景 |
| SHT30 / SHT31 | I2C 数字接口 | ±2% RH | 中等 ($4) | 工业级、大棚种植、高端家电 |
| 电容式湿敏电阻 | 模拟电压输出 (ADC) | ±3% RH | 低 ($1.5) | 需要自己搭建运放电路的进阶玩法 |
设计建议:如果是初次上手,推荐使用 DHT11,因为只需要一根数据线,时序虽然严格但极易理解;如果是做产品原型,SHT30** 是目前的行业标杆,I2C协议极其稳定,且自带温度补偿。
2.2 硬件连接表
我们以经典的 STM32F103C8T6(蓝色药丸板) 搭配 DHT11 和 一路继电器模块 为例。
/* 硬件连接定义 */
// ---------------------------------------------------------
// 1. DHT11 湿度传感器 (单总线模式)
// ---------------------------------------------------------
#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_Pin_0 // PA0 连接 DHT11 DATA引脚
// 注意:DHT11 的 VCC 接 3.3V 或 5V (推荐3.3V与MCU同压),GND 接地
// 建议在 DATA 引脚与 VCC 之间并联一个 4.7kΩ 或 10kΩ 的上拉电阻,保证信号稳定
// ---------------------------------------------------------
// 2. 执行机构 - 继电器模块 (控制排风扇/除湿器)
// ---------------------------------------------------------
#define RELAY_PORT GPIOA
#define RELAY_PIN GPIO_Pin_1 // PA1 连接 继电器 IN 引脚
// 注意:大多数继电器模块是 低电平触发(给0继电器吸合吸合,给1断开)
// 继电器模块的 VCC 接 5V,GND 接地,COM/NO/NC 接你的家用电器
// ---------------------------------------------------------
// 3. 人机交互 (按键 + OLED)
// ---------------------------------------------------------
#define KEY_SET_PIN GPIO_Pin_2 // PA2 设定键 (增减湿度阈值)
#define OLED_SDA_PIN GPIO_Pin_7 // PB7 (I2C1_SDA)
#define OLED_SCL_PIN GPIO_Pin_6 // PB6 (I2C1_SCL)
// ---------------------------------------------------------
// 4. 报警装置 (蜂鸣器)
// ---------------------------------------------------------
#define BEEP_PIN GPIO_Pin_3 // PA3 蜂鸣器 (达到阈值时滴滴响)
三、软件系统设计
3.1 基础数据结构
/* 湿度开关数据结构 */
#include "stm32f10x.h"
#include <stdio.h>
// 系统开关的工作模式
typedef enum {
MODE_AUTO = 0, // 自动模式(超过阈值自动开启继电器)
MODE_MANUAL = 1, // 手动模式(强制开启/关闭)
MODE_SETTING = 2 // 设定模式(正在调节湿度阈值)
} WorkMode;
// 全局核心变量
WorkMode sys_mode = MODE_AUTO;
float current_humidity = 0.0f; // 当前检测到的湿度 (百分比)
float current_temp = 0.0f; // 当前温度 (摄氏度)
float humi_threshold = 70.0f; // 湿度开关阈值 (默认70%,超过则开启)
uint8_t relay_status = 0; // 继电器状态:0关,1开
uint32_t system_tick = 0; // 系统滴答节拍
3.2 DHT11 单总线驱动(核心难点)
DHT11不需要复杂的I2C协议,它走的是自定义的“单总线时序”。读数的关键在于微秒级的延时控制和严密的高低电平判断。
/* DHT11 驱动程序 */
// 配置PA0为输出模式
void DHT11_IO_OUT(void) {
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}
// 配置PA0为输入模式
void DHT11_IO_IN(void) {
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}
// 微妙延时 (针对72MHz主频的粗略校准)
void DHT11_DelayUs(uint16_t us) {
uint32_t temp = us * 8; // 经验系数,不同编译器需微调
while(temp--);
}
// 发送起始信号并读取一次数据
// 返回值:0成功,1失败
uint8_t DHT11_Read_Data(float *temperature, float *humidity) {
uint8_t buf[5] = {0};
uint8_t i, j;
// 1. 主机发送起始信号(拉低至少18ms)
DHT11_IO_OUT();
GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
DHT11_DelayUs(20000); // 拉低20ms
// 2. 主机释放总线,等待DHT11响应
GPIO_SetBits(DHT11_PORT, DHT11_PIN);
DHT11_DelayUs(30);
DHT11_IO_IN();
// 3. 检测DHT11的响应信号(拉低约80us,再拉高80us)
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == 0) {
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == 0); // 等待低电平过去
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == 1); // 等待高电平过去
// 4. 正式接收40位数据 (8位湿度整数 + 8位湿度小数 + 8位温度整数 + 8位温度小数 + 8位校验和)
for(i = 0; i < 5; i++) {
for(j = 0; j < 8; j++) {
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == 0); // 等待50us的低电平起始位过去
DHT11_DelayUs(40); // 延时40us,如果是高电平,说明数据位是1;如果是低电平,说明是0
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == 1) {
buf[i] |= (1 << (7-j));
}
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == 1); // 等待剩余的高电平过去
}
}
// 5. 校验数据正确性
if(buf[0] + buf[1] + buf[2] + buf[3] == buf[4]) {
*humidity = (float)buf[0];
*temperature = (float)buf[2];
return 0; // 成功
}
}
return 1; // 读取失败
}
3.3 执行器与逻辑控制
/* 继电器与开关逻辑 */
// 初始化继电器和蜂鸣器引脚
void Actuator_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = RELAY_PIN | BEEP_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, RELAY_PIN); // 默认关闭继电器
GPIO_SetBits(GPIOA, BEEP_PIN); // 默认关闭蜂鸣器
}
// 核心开关决策逻辑:这就是湿度开关的大脑!
void Humidity_Switch_Logic(void) {
switch(sys_mode) {
case MODE_AUTO:
// 核心逻辑:如果湿度 > 设定阈值,开启排风;否则关闭
if(current_humidity > humi_threshold) {
if(relay_status == 0) {
relay_status = 1;
GPIO_ResetBits(RELAY_PORT, RELAY_PIN); // 吸合继电器
GPIO_ResetBits(GPIOA, BEEP_PIN); // 滴滴两声报警
DHT11_DelayUs(100000);
GPIO_SetBits(GPIOA, BEEP_PIN);
DHT11_DelayUs(100000);
GPIO_ResetBits(GPIOA, BEEP_PIN);
DHT11_DelayUs(100000);
GPIO_SetBits(GPIOA, BEEP_PIN);
printf("[Switch] 湿度超标! 开启排风扇...\n");
}
} else {
if(relay_status == 1) {
relay_status = 0;
GPIO_SetBits(RELAY_PORT, RELAY_PIN); // 断开继电器
printf("[Switch] 湿度恢复正常,关闭排风扇。\n");
}
}
break;
case MODE_MANUAL:
// 不做任何自动判断,完全由人为按键控制继电器的开和关
break;
}
}
// 按键扫描:调整湿度阈值(例如每次按键增加/减少5%)
void Key_Scan_Setting(void) {
static uint8_t key_lock = 0; // 按键自锁,防止长按连续触发
if(GPIO_ReadInputDataBit(GPIOA, KEY_SET_PIN) == 0) {
if(key_lock == 0) {
key_lock = 1;
humi_threshold += 5.0f;
if(humi_threshold > 95.0f) humi_threshold = 30.0f; // 循环调整
printf("[Setting] 湿度阈值调整为: %.0f%% \n", humi_threshold);
}
} else {
key_lock = 0;
}
}
四、主程序与系统集成
4.1 完整主程序
/* 湿度检测开关主程序 */
#include "stm32f10x.h"
#include "system_stm32f10x.h"
// 模拟系统滴答时钟
void SysTick_Init(void) {
SysTick_Config(SystemCoreClock / 1000); // 1ms触发一次
}
void SysTick_Handler(void) {
system_tick++;
}
// 毫秒延时函数
void delay_ms(uint32_t ms) {
uint32_t start = system_tick;
while((system_tick - start) < ms);
}
int main(void) {
// 1. 底层初始化
SystemInit();
SysTick_Init();
USART1_Init(115200); // 初始化串口用于电脑监控
Actuator_Init(); // 初始化继电器和蜂鸣器
OLED_Init(); // 初始化显示屏(此处省略具体初始化代码)
printf("\r\n==== 湿度检测开关系统启动 ====\r\n");
printf("默认阈值: %.0f%% \r\n", humi_threshold);
uint32_t last_dht_read_time = 0;
// 2. 系统主循环
while(1) {
// --- 任务1:每隔2秒读取一次湿度传感器(DHT11读取不能太频繁)---
if(system_tick - last_dht_read_time >= 2000) {
if(DHT11_Read_Data(¤t_temp, ¤t_humidity) == 0) {
printf("[Sensor] 温度: %.1f°C, 湿度: %.1f%% \n", current_temp, current_humidity);
// 在OLED屏幕上显示数值
char disp_buff[30];
sprintf(disp_buff, "Humidity: %.1f %% ", current_humidity);
OLED_ShowString(0, 0, disp_buff);
sprintf(disp_buff, "Threshld: %.0f %% ", humi_threshold);
OLED_ShowString(0, 2, disp_buff);
} else {
OLED_ShowString(0, 0, "Sensor Error!");
printf("[Error] DHT11 Read Failed!\n");
}
last_dht_read_time = system_tick;
}
// --- 任务2:扫描按键,调整阈值 ---
Key_Scan_Setting();
// --- 任务3:执行核心开关逻辑 ---
Humidity_Switch_Logic();
// 主循环延时,降低CPU空转发热
delay_ms(10);
}
}
参考代码 湿敏传感器(设计方案+原理图+程序+芯片资料) www.youwenfan.com/contentcnt/124170.html
五、应用场景与总结
核心应用场景:
- 浴室/卫生间:安装在吊顶里,当洗澡导致湿度飙升超过80%时,自动开启排气扇,洗完澡自动关停,防止发霉。
- 地下室/储藏室:梅雨季节自动开启工业除湿机,保护存放的字画或器材。
- 农业大棚:土壤湿度或者空气湿度超标时,自动开启排风机或喷淋系统。
- 智能家居联动:作为一个独立的WiFi/蓝牙边缘节点,上报湿度数据给手机App。
总结:
这个基于STM32的湿度检测开关系统,抓住了“感知(DHT11/SHT30)+ 决策(STM32的逻辑判断)+ 执行(继电器强电控制) 这一物联网设备的本质骨架。