若该文为原创文章,转载请注明原文出处。
一、 项目背景
随着生活水平的提高,人们对居住环境的舒适度和健康程度日益关注。空气湿度是影响环境舒适度的关键参数之一。湿度过低会导致皮肤干燥、呼吸道不适、静电等问题,尤其是在秋冬季节或空调房中。
传统加湿器多为机械开关控制,功能单一,用户无法精确知晓当前环境湿度,需要手动开启和关闭,既不方便也无法实现精准的湿度控制。智能物联网(IoT)技术的成熟为家居设备的智能化提供了完美解决方案。
本项目旨在设计一款基于STM32微控制器的智能空气加湿器。该系统能够实时监测环境温湿度和水箱水位,通过OLED屏幕本地显示,用户可通过按键设置心仪的湿度阈值或手动切换模式。系统核心功能是自动控制加湿,当环境湿度低于设定值时自动启动加湿,达到设定值后停止。同时,系统具备低水位保护功能(声光报警、禁止加湿),并通过Wi-Fi模块连接云平台,用户可通过手机APP远程实时监控环境数据和远程控制加湿器的工作状态,实现真正的智能化和远程化管理。
二、 硬件设计
1. 系统硬件框架图
2. 关键硬件选型与说明
-
主控芯片 (MCU): STM32F103C8T6 (蓝色pill开发板),成本低、性能强大、外设丰富,足以处理所有任务。
-
温湿度传感器: DHT11,数字输出,直接提供湿度、温度值,节省MCU资源。
-
水位检测:
-
方案一 (低成本): 使用常开型干簧管水位开关,无水时断开,有水时闭合。电路简单,但只能判断有无水。
-
方案二 (高精度): 使用模拟式水位传感器(利用导电性原理),输出0-3.3V模拟电压,通过STM32的ADC读取可估算剩余水量百分比。
-
-
执行单元:
-
继电器: 控制水泵或超声雾化片的总电源。注意:若驱动超声雾化片,需匹配其工作电压(如24V)和功率,继电器控制其电源适配器的通断。
-
水泵: 用于自动上水功能,从备用水箱中抽水到主水箱。
-
-
通信模块: ESP8266 (如ESP-01S),通过串口AT指令与STM32通信,性价比极高的Wi-Fi解决方案,可连接家庭路由器上云。
-
人机交互:
-
OLED显示屏: 显示当前湿度、温度、水位、设定阈值、工作模式、Wi-Fi连接状态等。
-
按键: 3个按键,用于开关、模式切换、参数设置。
-
蜂鸣器: 无水时发出报警声。
-
-
电源: 采用5V/2A电源适配器供电,经LDO稳压至3.3V为MCU和逻辑器件供电。水泵/雾化片由5V或更高电压直接驱动。
三、 软件设计
1. 主程序流程图
2. 关键软件模块功能
-
系统初始化: 配置时钟、GPIO、ADC、I2C、UART、定时器等。
-
传感器驱动: 编写DHT11的时序读取函数、ADC读取水位值函数。
-
OLED显示驱动: 显示多层菜单界面(主界面、设置界面)。
-
ESP8266驱动: 基于AT指令集,编写连接Wi-Fi、连接MQTT服务器、订阅主题、发布消息的函数。
-
业务逻辑核心:
-
自动模式:
if (当前湿度 < 设定湿度下限 && 水位正常) { 启动加湿; } else if (当前湿度 > 设定湿度上限) { 停止加湿; }
-
手动模式: 直接响应按键或APP的开关指令。
-
保护逻辑:
if (水位过低) { 强制停止加湿; 触发蜂鸣器报警; }
-
自动上水逻辑(如果支持):
if (主水箱水位低 && 备用水箱有水) { 启动水泵上水; }
-
-
云平台通信: 采用MQTT协议。设备定时发布(Publish)传感器数据(JSON格式);APP下发控制指令,设备订阅(Subscribe)特定主题
/* 包含头文件 */ #include "main.h" #include "dht11.h" #include "oled.h" #include "esp8266.h" #include "stdio.h" #include "string.h" /* 全局变量定义 */ I2C_HandleTypeDef hi2c1; UART_HandleTypeDef huart1; // 用于ESP8266 TIM_HandleTypeDef htim2; // 定时器 uint8_t current_humi = 0; uint8_t current_temp = 0; uint8_t water_level = 0; // 水位百分比 或 0/1 uint8_t set_humi_low = 40; // 默认湿度下限 uint8_t set_humi_high = 60;// 默认湿度上限 uint8_t system_mode = 0; // 0:自动模式, 1:手动模式 uint8_t pump_status = 0; // 0:关闭, 1:开启 uint8_t buzzer_status = 0; uint8_t data_update_flag = 0; // 数据采集定时标志位 /* 函数声明 */ void System_Clock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C1_Init(void); static void MX_USART1_UART_Init(void); static void MX_TIM2_Init(void); void Sensor_Update_Task(void); void Control_Task(void); void Update_OLED_Task(void); void Cloud_Comm_Task(void); int main(void) { HAL_Init(); System_Clock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); MX_USART1_UART_Init(); MX_TIM2_Init(); OLED_Init(); OLED_Clear(); OLED_ShowString(0, 0, "System Booting...", 16); DHT11_Init(); ESP8266_Init(); // 初始化Wi-Fi,连接网络和MQTT HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断 OLED_Clear(); OLED_ShowString(0, 0, "Ready! Mode:Auto", 16); while (1) { // 1. 按键处理任务 (非阻塞方式扫描) Key_Scan_Task(); // 2. 处理云平台下发的指令 if(esp8266_rx_flag == 1) { Parse_Cloud_Command((char*)esp8266_rx_buffer); esp8266_rx_flag = 0; memset(esp8266_rx_buffer, 0, sizeof(esp8266_rx_buffer)); } // 3. 定时采集与控制任务 if(data_update_flag == 1) { data_update_flag = 0; // 清除标志 Sensor_Update_Task(); // 采集数据 Control_Task(); // 执行控制逻辑 Update_OLED_Task(); // 更新显示 Cloud_Comm_Task(); // 上传数据 } HAL_Delay(10); // 短暂延迟,防止程序跑飞 } } // 定时器中断回调函数 (每2秒触发一次) void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM2) { data_update_flag = 1; // 设置标志位,主循环中处理 } }
来接收并执行。
-
按键处理: 采用状态机模型,区分短按、长按,实现模式切换、参数设置等功能。
四、代码片段
一、 主循环与初始化 (main.c)
/* 包含头文件 */
#include "main.h"
#include "dht11.h"
#include "oled.h"
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
/* 全局变量定义 */
I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart1; // 用于ESP8266
TIM_HandleTypeDef htim2; // 定时器
uint8_t current_humi = 0;
uint8_t current_temp = 0;
uint8_t water_level = 0; // 水位百分比 或 0/1
uint8_t set_humi_low = 40; // 默认湿度下限
uint8_t set_humi_high = 60;// 默认湿度上限
uint8_t system_mode = 0; // 0:自动模式, 1:手动模式
uint8_t pump_status = 0; // 0:关闭, 1:开启
uint8_t buzzer_status = 0;
uint8_t data_update_flag = 0; // 数据采集定时标志位
/* 函数声明 */
void System_Clock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_TIM2_Init(void);
void Sensor_Update_Task(void);
void Control_Task(void);
void Update_OLED_Task(void);
void Cloud_Comm_Task(void);
int main(void) {
HAL_Init();
System_Clock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
MX_TIM2_Init();
OLED_Init();
OLED_Clear();
OLED_ShowString(0, 0, "System Booting...", 16);
DHT11_Init();
ESP8266_Init(); // 初始化Wi-Fi,连接网络和MQTT
HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断
OLED_Clear();
OLED_ShowString(0, 0, "Ready! Mode:Auto", 16);
while (1) {
// 1. 按键处理任务 (非阻塞方式扫描)
Key_Scan_Task();
// 2. 处理云平台下发的指令
if(esp8266_rx_flag == 1) {
Parse_Cloud_Command((char*)esp8266_rx_buffer);
esp8266_rx_flag = 0;
memset(esp8266_rx_buffer, 0, sizeof(esp8266_rx_buffer));
}
// 3. 定时采集与控制任务
if(data_update_flag == 1) {
data_update_flag = 0; // 清除标志
Sensor_Update_Task(); // 采集数据
Control_Task(); // 执行控制逻辑
Update_OLED_Task(); // 更新显示
Cloud_Comm_Task(); // 上传数据
}
HAL_Delay(10); // 短暂延迟,防止程序跑飞
}
}
// 定时器中断回调函数 (每2秒触发一次)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if(htim->Instance == TIM2) {
data_update_flag = 1; // 设置标志位,主循环中处理
}
}
二、 数据采集任务 (sensor_task.c)
#include "sensor_task.h"
#include "adc.h"
#define WATER_EMPTY_ADC_VALUE 2500 // 无水时的ADC值,需校准
#define WATER_FULL_ADC_VALUE 1000 // 满水时的ADC值,需校准
void Sensor_Update_Task(void) {
// 1. 读取DHT11温湿度
if(DHT11_ReadData(¤t_temp, ¤t_humi) == HAL_OK) {
// 读取成功,数据已存入全局变量
} else {
// 读取失败,可设置错误标志
}
// 2. 读取水位 (假设使用模拟传感器,接在PA0)
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 50);
uint32_t adc_value = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
// 将ADC值转换为水位百分比 (注意:需根据实际传感器校准)
// 注意:如果使用开关式传感器,此处直接判断高低电平即可
if(adc_value >= WATER_EMPTY_ADC_VALUE) {
water_level = 0; // 无水
} else if(adc_value <= WATER_FULL_ADC_VALUE) {
water_level = 100; // 满水
} else {
// 线性映射计算百分比
water_level = (uint8_t)((WATER_EMPTY_ADC_VALUE - adc_value) * 100 /
(WATER_EMPTY_ADC_VALUE - WATER_FULL_ADC_VALUE));
}
}
三、 核心控制逻辑 (control_task.c)
#include "control_task.h"
extern uint8_t current_humi;
extern uint8_t water_level;
extern uint8_t set_humi_low;
extern uint8_t set_humi_high;
extern uint8_t system_mode;
extern uint8_t pump_status;
void Control_Task(void) {
// 1. 无水保护逻辑 (最高优先级)
if(water_level == 0) { // 或者 if(water_level < 5)
Pump_OFF(); // 强制关闭水泵
buzzer_status = 1; // 触发报警
Buzzer_ON();
return; // 直接返回,不执行后续湿度控制
} else {
buzzer_status = 0;
Buzzer_OFF();
}
// 2. 手动模式
if(system_mode == 1) {
// 手动开关状态由按键或APP直接设置,此处不改变pump_status
// 只需根据pump_status状态输出即可
if(pump_status == 1) {
Pump_ON();
} else {
Pump_OFF();
}
return;
}
// 3. 自动模式
if(system_mode == 0) {
if(current_humi < set_humi_low) {
Pump_ON();
pump_status = 1;
} else if(current_humi > set_humi_high) {
Pump_OFF();
pump_status = 0;
}
// 如果湿度在区间内,保持原状态不变
}
}
// 简单的GPIO控制函数
void Pump_ON(void) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // 假设继电器接PB0
}
void Pump_OFF(void) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
}
void Buzzer_ON(void) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); // 假设蜂鸣器接PB1
}
void Buzzer_OFF(void) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
四、 云平台通信任务 (cloud_comm.c)
#include "cloud_comm.h"
// 发布JSON数据到云平台
void Cloud_Comm_Task(void) {
char json_buffer[128];
// 构造JSON字符串
sprintf(json_buffer,
"{\"temp\":%d, \"humi\":%d, \"water\":%d, \"mode\":%d, \"pump\":%d, \"buzzer\":%d}",
current_temp, current_humi, water_level, system_mode, pump_status, buzzer_status);
// 通过ESP8266发送MQTT发布命令 (AT指令)
ESP8266_MQTT_Publish("device/humidifier/data", json_buffer);
}
// 解析云平台下发的命令
void Parse_Cloud_Command(char* cmd) {
// 示例: 收到 {"command": "pump", "value": 1}
// 这是一个简单的解析示例,实际应使用cJSON等库解析JSON
if(strstr(cmd, "\"command\": \"pump\"") != NULL) {
if(strstr(cmd, "\"value\": 1") != NULL) {
pump_status = 1; // 手动开启
system_mode = 1; // 切换为手动模式
} else if(strstr(cmd, "\"value\": 0") != NULL) {
pump_status = 0; // 手动关闭
system_mode = 1; // 切换为手动模式
}
}
// 示例: 收到 {"command": "mode", "value": 0}
if(strstr(cmd, "\"command\": \"mode\"") != NULL) {
if(strstr(cmd, "\"value\": 0") != NULL) {
system_mode = 0; // 切换回自动模式
} else if(strstr(cmd, "\"value\": 1") != NULL) {
system_mode = 1; // 切换为手动模式
}
}
// 示例: 收到 {"command": "set_humi", "low": 40, "high": 60}
// ... 解析并更新 set_humi_low 和 set_humi_high ...
}
关键点说明
-
非阻塞设计: 所有任务都在主循环中快速执行,通过
data_update_flag
标志位控制采集周期,避免使用delay()
。 -
模块化: 每个功能都分离到不同的
.c
文件中,结构清晰,易于维护。 -
全局变量: 使用全局变量在模块间传递数据,注意规范命名。
-
错误处理: 实际应用中需增加传感器读取失败、网络断开等情况的错误处理和重连机制。
-
JSON解析: 云平台命令解析部分仅为示例,强烈建议使用cJSON等开源库来可靠地解析JSON数据。
-
校准: 水位传感器的
WATER_EMPTY_ADC_VALUE
和WATER_FULL_ADC_VALUE
需要根据实际硬件进行校准。
五、 总结与优化期望
总结
本项目成功设计了一个功能完备的智能加湿器系统原型。它集成了环境感知、智能决策、精准执行、本地交互和远程监控五大功能,体现了物联网技术的核心思想。系统具备以下特点:
-
智能化: 完全自动运行,无需人工干预。
-
安全性: 具备低水位保护,有效防止干烧等安全隐患。
-
交互友好: 本地显示与按键设置直观方便。
-
远程化: 打破地域限制,通过手机APP随时随地监控和控制。
-
模块化: 硬件和软件均采用模块化设计,易于维护和功能扩展。
优化与期望
-
增加执行单元: 加入风扇模块,实现湿度与温度的联合调节(如夏季除湿、冬季加湿)。
-
水质监测: 增加TDS(总溶解固体)传感器,监测水质,提醒用户更换用水,防止白粉污染。
-
人性化功能: 增加光敏传感器,实现夜间自动关闭显示屏和提示灯功能;增加定时开关机功能。
-
低功耗优化: 在设备空闲时让MCU进入休眠模式,并通过事件(如按键按下、定时到达)唤醒,进一步降低待机功耗。
-
语音控制: 集成语音识别模块(如LD3320)或对接智能音箱(天猫精灵、小爱同学),实现语音控制。
-
UI升级: 使用彩色LCD屏,设计更美观的图形化用户界面(GUI)。
-
产品化设计: 设计专用的PCB板和三防(防水、防尘、防潮)外壳,提升产品的稳定性和美观度。
如有侵权,或需要完整代码,请及时联系博主。