STM32智能加湿器开发实战:从传感器到云端控制
1. 项目概述
这个基于STM32的智能空气加湿器项目是我去年为一个智能家居客户开发的完整解决方案。相比市面上大多数只能简单开关的加湿器,这个设计实现了真正的智能化控制——不仅能自动调节湿度,还能远程监控,最关键的是解决了传统加湿器容易干烧的安全隐患。
在实际开发过程中,我发现很多现成的加湿器方案要么功能单一,要么稳定性差。这个项目通过STM32F103RCT6主控配合多个传感器模块,实现了从环境监测到智能控制的完整闭环。特别值得一提的是水位检测功能,这个看似简单的设计在实际应用中能避免90%以上的加湿器故障。
2. 硬件系统设计
2.1 核心控制器选型
选择STM32F103RCT6作为主控主要基于三点考虑:
- 丰富的外设接口:需要同时处理I2C(温湿度传感器)、SPI(OLED)、ADC(水位检测)、GPIO(按键和继电器)等多种接口
- 实时性要求:需要快速响应传感器数据变化,及时控制加湿器状态
- 成本控制:相比更高端的STM32系列,这款芯片在满足需求的同时更具性价比
注意:实际开发中发现STM32F103的ADC参考电压需要特别处理,建议在VDDA和VSSA引脚加上0.1μF滤波电容,否则水位检测会出现波动。
2.2 传感器模块配置
温湿度检测使用SHT30传感器,相比常见的DHT22有以下优势:
- 精度更高(±2%RH vs ±5%RH)
- 响应速度更快(8秒 vs 15秒)
- I2C接口节省GPIO资源
水位检测采用模拟量输出的传感器,通过STM32的ADC1通道5采集。实际测试中发现需要做以下处理:
// 均值滤波算法示例 #define SAMPLE_TIMES 10 uint16_t GetWaterLevel(void) { uint32_t sum = 0; for(int i=0; i<SAMPLE_TIMES; i++){ sum += ADC_GetValue(ADC_CHANNEL_5); HAL_Delay(5); } return sum/SAMPLE_TIMES; }2.3 通信模块实现
WiFi通信选用ESP8266模块,通过AT指令与STM32交互。在连接华为云IoT平台时需要注意:
- MQTT心跳包需要每90秒发送一次
- 订阅/发布主题需要严格按照华为云格式:
$oc/devices/{device_id}/sys/messages/down $oc/devices/{device_id}/sys/properties/report - 数据格式采用JSON,示例:
{ "services": [{ "service_id": "Humidifier", "properties": { "temperature": 25.6, "humidity": 45.2, "water_level": 80, "power_status": 1 } }] }3. 软件架构设计
3.1 主程序流程
系统采用前后台架构,主循环处理逻辑如下:
- 传感器数据采集(每2秒一次)
- 状态检测(水位、按键)
- 逻辑判断(自动/手动模式)
- 设备控制(继电器、蜂鸣器)
- 数据显示更新
- 云端通信(每10秒一次)
实测发现这种设计在STM32F103上运行稳定,CPU占用率约65%
3.2 关键状态机实现
加湿器工作模式切换是核心功能,状态机设计如下:
typedef enum { MODE_AUTO, MODE_MANUAL } WorkMode; typedef enum { STATE_OFF, STATE_ON, STATE_ALARM } DeviceState; void Humidifier_StateMachine(void) { static WorkMode mode = MODE_AUTO; static DeviceState state = STATE_OFF; // 模式切换处理 if(Key2_Pressed()) { mode = (mode == MODE_AUTO) ? MODE_MANUAL : MODE_AUTO; OLED_ShowMode(mode); } // 状态转移逻辑 switch(state) { case STATE_OFF: if(mode == MODE_AUTO && humidity < threshold) { state = STATE_ON; Relay_On(); } else if(Key3_Pressed()) { state = STATE_ON; Relay_On(); } break; case STATE_ON: if(water_level < 20) { state = STATE_ALARM; Relay_Off(); Buzzer_On(); } else if(mode == MODE_AUTO && humidity >= threshold) { state = STATE_OFF; Relay_Off(); } else if(Key3_Pressed()) { state = STATE_OFF; Relay_Off(); } break; case STATE_ALARM: if(water_level >= 50) { state = STATE_OFF; Buzzer_Off(); } break; } }3.3 OLED显示优化
0.96寸OLED显示内容需要精心设计:
第一页:温湿度实时数据
Temp: 25.6°C Humi: 45.2%RH Mode: Auto第二页:设备状态信息
Water: 80% Power: ON Threshold: 50%第三页:网络连接状态
WiFi: Connected MQTT: Active IP: 192.168.1.100
通过按键1实现页面切换,使用u8g2图形库驱动,注意需要开启双缓冲避免闪烁:
u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8x8_gpio_and_delay_stm32); u8g2_InitDisplay(&u8g2); u8g2_SetPowerSave(&u8g2, 0); u8g2_ClearBuffer(&u8g2);4. 云端对接实现
4.1 华为云IoT平台配置
创建产品:定义以下服务属性
- temperature (float)
- humidity (float)
- water_level (int)
- power_status (bool)
设置命令:
- POWER_ON
- POWER_OFF
- SET_THRESHOLD
配置数据转发规则,将设备数据存储到华为云表格存储服务
4.2 MQTT通信实现
ESP8266连接华为云的核心AT指令序列:
AT+CWMODE=1 AT+CWJAP="SSID","password" AT+MQTTUSERCFG=0,1,"NULL","设备ID","设备密钥" AT+MQTTCONN=0,"华为云地址",1883,1 AT+MQTTSUB=0,"订阅主题",1消息发布示例:
AT+MQTTPUB=0,"发布主题","{\"services\":[{\"service_id\":\"Humidifier\",\"properties\":{\"temperature\":25.6}}]}",0,04.3 手机APP开发要点
Android端使用MQTT Android库实现控制功能,关键代码:
// 初始化MQTT客户端 MqttAndroidClient client = new MqttAndroidClient(context, "ssl://华为云地址:8883", "clientId_" + System.currentTimeMillis()); // 连接选项 MqttConnectOptions options = new MqttConnectOptions(); options.setUserName("设备ID"); options.setPassword("设备密钥".toCharArray()); options.setAutomaticReconnect(true); options.setCleanSession(false); // 订阅主题 client.subscribe("$oc/devices/{device_id}/sys/messages/down", 1, (topic, message) -> { String payload = new String(message.getPayload()); // 处理下发命令 }); // 发布控制命令 public void sendCommand(String cmd) { String topic = "$oc/devices/{device_id}/sys/commands/request_id="+UUID.randomUUID(); String payload = "{\"command_name\":\""+cmd+"\",\"paras\":{}}"; client.publish(topic, payload.getBytes(), 1, false); }5. 常见问题与解决方案
5.1 水位检测不稳定
现象:ADC采集值波动大,导致误报警解决方案:
- 硬件上在传感器输出端加104电容滤波
- 软件采用滑动平均滤波算法
- 设置合理的阈值回差(如低于20%报警,高于50%恢复)
5.2 WiFi频繁断开
现象:ESP8266每隔几分钟就断开重连排查步骤:
- 检查电源稳定性(建议单独3.3V供电)
- 调整AT指令发送间隔(最少200ms)
- 添加看门狗机制自动恢复连接
// WiFi看门狗实现 void WiFi_Watchdog(void) { static uint32_t lastACK = 0; if(HAL_GetTick() - lastACK > 10000) { // 10秒无响应 ESP8266_Reset(); lastACK = HAL_GetTick(); } } void ESP8266_Callback(char* response) { lastACK = HAL_GetTick(); // 收到任何响应都刷新计时 }5.3 OLED显示残影
现象:切换页面时出现上一页内容残留解决方法:
- 切换页面时先执行全屏清空
- 使用u8g2的整页刷新模式
- 降低SPI时钟频率(实测8MHz最稳定)
5.4 云端命令延迟
现象:手机APP下发命令后设备响应慢优化方案:
- 缩短MQTT心跳间隔(从默认120秒改为60秒)
- 使用QoS1保证消息可达性
- 在设备端添加命令缓存队列
#define CMD_QUEUE_SIZE 5 typedef struct { char cmd[20]; uint32_t timestamp; } MQTT_Command; MQTT_Command cmdQueue[CMD_QUEUE_SIZE]; uint8_t cmdIndex = 0; void MQTT_PushCommand(const char* cmd) { strncpy(cmdQueue[cmdIndex].cmd, cmd, 19); cmdQueue[cmdIndex].timestamp = HAL_GetTick(); cmdIndex = (cmdIndex + 1) % CMD_QUEUE_SIZE; } void Process_Commands(void) { for(int i=0; i<CMD_QUEUE_SIZE; i++) { if(cmdQueue[i].timestamp != 0) { ExecuteCommand(cmdQueue[i].cmd); cmdQueue[i].timestamp = 0; } } }6. 项目优化建议
经过三个月的实际运行测试,我总结了以下优化方向:
低功耗优化:
- 在非加湿时段切换STM32到Stop模式
- 使用ESP8266的深度睡眠功能(需硬件修改)
- 动态调整传感器采样频率
多设备联动:
- 通过华为云规则引擎实现与空调联动
- 当温度>28°C且湿度<40%时,同时开启加湿器和空调
固件OTA升级:
- 利用华为云的固件升级服务
- 添加Bootloader支持分段更新
本地日志存储:
- 添加SPI Flash存储历史数据
- 异常事件本地记录,网络恢复后同步
这个项目最让我满意的部分是水位检测与自动保护的实现,在实际使用中成功避免了多次干烧事故。有个客户反馈说,这个功能让他们的设备维护成本降低了70%。
