基于STM32的湿度检测开关传感器设计

基于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(&current_temp, &current_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

五、应用场景与总结

核心应用场景:

  1. 浴室/卫生间:安装在吊顶里,当洗澡导致湿度飙升超过80%时,自动开启排气扇,洗完澡自动关停,防止发霉。
  2. 地下室/储藏室:梅雨季节自动开启工业除湿机,保护存放的字画或器材。
  3. 农业大棚:土壤湿度或者空气湿度超标时,自动开启排风机或喷淋系统。
  4. 智能家居联动:作为一个独立的WiFi/蓝牙边缘节点,上报湿度数据给手机App。

总结:

这个基于STM32的湿度检测开关系统,抓住了“感知(DHT11/SHT30)+ 决策(STM32的逻辑判断)+ 执行(继电器强电控制) 这一物联网设备的本质骨架。

 

专注于matlab/simulink,电子电路,编程