智能家居监控——基于STM32与ESP8266-01S的DHT11温湿度数据实时上传至阿里云物联网平台(一)

张开发
2026/4/18 19:20:43 15 分钟阅读

分享文章

智能家居监控——基于STM32与ESP8266-01S的DHT11温湿度数据实时上传至阿里云物联网平台(一)
1. 智能家居温湿度监控系统概述想象一下这样的场景炎炎夏日你在办公室突然想起家里的宠物会不会太热但又不确定室内实际温度或者梅雨季节你担心地下室是否过于潮湿导致物品发霉。这时候一个能实时监测家中温湿度并远程查看的系统就显得尤为重要。这正是我们今天要搭建的智能家居温湿度监控系统。这个系统的核心由三部分组成STM32单片机作为大脑负责数据处理DHT11传感器充当感官采集环境数据ESP8266-01S模块则像信使一样把数据传送到阿里云物联网平台。我去年帮朋友部署过类似系统实测下来稳定性相当不错即使在30米外的房间也能准确获取数据。选择STM32F103C8T6这款单片机是因为它性价比极高市场价不到20元却拥有72MHz主频和丰富的外设接口。DHT11虽然精度不如更贵的SHT30±2℃和±5%RH的精度但对于家庭环境监测完全够用关键是价格只有几块钱。ESP8266-01S模块更是WiFi模块中的明星产品内置TCP/IP协议栈开发者可以直接通过AT指令控制省去了自己实现网络协议的麻烦。2. 硬件准备与连接2.1 元器件清单与选型建议在开始动手前我们需要准备以下硬件STM32F103C8T6最小系统板带USB转串口芯片ESP8266-01S WiFi模块注意是01S版本比01多了金属屏蔽罩DHT11温湿度传感器建议购买带PCB板的版本杜邦线若干建议使用20cm长度的公对公线3.3V稳压电源电流需≥500mAUSB转TTL模块用于调试ESP8266这里有个容易踩坑的地方ESP8266-01S的供电问题。很多新手直接用STM32的3.3V引脚给模块供电结果发现WiFi经常断连。这是因为ESP8266在发射信号时瞬时电流可达300mA而STM32的LDO稳压芯片通常只能提供150mA左右。我的经验是外接一个AMS1117-3.3V稳压模块单独供电。2.2 电路连接详解具体接线方式如下STM32与DHT11连接DHT11 VCC → STM32 3.3VDHT11 GND → STM32 GNDDHT11 DATA → STM32 PA1可配置为推挽输出STM32与ESP8266-01S连接ESP8266 VCC → 外部3.3V电源ESP8266 GND → 共地ESP8266 TX → STM32 PA3USART2_RXESP8266 RX → STM32 PA2USART2_TXESP8266 EN → 3.3V使能端ESP8266 RST → 悬空正常使用时不需要复位特别注意ESP8266的RX引脚要接STM32的TXTX接RX这个反接关系新手经常搞错。我当初第一次调试时因为这个原因卡了半天后来用示波器抓信号才发现问题。3. ESP8266-01S模块配置3.1 固件烧录与测试虽然ESP8266-01S出厂自带AT固件但为了确保MQTT功能正常建议重新烧录最新固件。我推荐使用安信可官方提供的MQTT透传固件版本号1471这个固件经过实测与阿里云兼容性最好。烧录步骤下载Flash_Download_Tool_v3.8.5工具设置烧录参数CrystalFreq: 26MSPI Mode: DIOFlash Size: 8Mbit选择COM口波特率115200点击START开始烧录烧录完成后用串口助手发送AT指令测试正常会返回OK。如果没反应检查一下接线是否正确特别是EN脚是否接高电平。有个小技巧烧录时IO0需要接地进入下载模式完成后要断开接地才能正常运行。3.2 网络配置与MQTT连接配置ESP8266连接路由器的AT指令如下ATCWMODE1 // 设置为STA模式 ATCWJAP你的WiFi名称,密码 // 连接路由器 ATMQTTUSERCFG0,1,NULL,NULL,NULL,0,0, // MQTT配置 ATMQTTCONN0,iot-xxxx.mqtt.aliyuncs.com,1883,1 // 连接阿里云这里有个实际项目中的经验如果网络环境复杂比如公司内网可能需要额外配置DNSATCIPDNS_CUR8.8.8.8 // 设置Google DNS4. 阿里云物联网平台配置4.1 产品与设备创建登录阿里云物联网平台后按以下步骤操作创建新产品品类选择自定义品类在功能定义中添加两个属性温度标识符temp数据类型float湿度标识符humi数据类型float发布产品后创建设备记下三元组信息ProductKeyDeviceNameDeviceSecret特别注意在设备证书页面务必将一机一密选项打开这样每个设备都有独立的密钥安全性更高。我在一个商业项目中就因为没有启用这个选项导致后期安全审计不过关不得不返工。4.2 Topic与通信格式阿里云使用标准的MQTT协议通信主要涉及两个Topic属性上报/sys/${productKey}/${deviceName}/thing/event/property/post属性设置/sys/${productKey}/${deviceName}/thing/event/property/set数据格式采用JSON例如上报温湿度{ params: { temp: 26.5, humi: 45.3 } }在调试阶段建议先用MQTT.fx工具测试通信是否正常。具体操作是用设备三元组生成连接参数订阅设置Topic发布测试数据到上报Topic5. STM32程序设计5.1 DHT11数据采集DHT11使用单总线协议时序要求严格。以下是关键代码片段#define DHT11_PIN GPIO_PIN_1 #define DHT11_PORT GPIOA void DHT11_Start(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); HAL_Delay(18); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); delay_us(30); GPIO_InitStruct.Mode GPIO_MODE_INPUT; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); } uint8_t DHT11_ReadByte(void) { uint8_t data 0; for(int i0; i8; i) { while(!HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)); delay_us(40); if(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)) { data | (1 (7-i)); while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)); } } return data; }实际使用中发现DHT11对时序非常敏感建议在读取失败时加入重试机制。我的做法是连续读取3次取中间值作为最终结果。5.2 ESP8266通信实现通过USART2与ESP8266通信关键是要处理好AT指令的发送与响应解析。这里分享一个实用的环形缓冲区实现#define RX_BUF_SIZE 256 typedef struct { uint8_t buffer[RX_BUF_SIZE]; uint16_t head; uint16_t tail; } RingBuffer; void UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART2) { rb.buffer[rb.head] (uint8_t)(huart-Instance-DR 0xFF); rb.head (rb.head 1) % RX_BUF_SIZE; } } int ESP8266_SendCmd(const char* cmd, const char* ack, uint32_t timeout) { HAL_UART_Transmit(huart2, (uint8_t*)cmd, strlen(cmd), 1000); uint32_t start HAL_GetTick(); uint8_t match_idx 0; uint8_t ack_len strlen(ack); while(HAL_GetTick() - start timeout) { if(rb.head ! rb.tail) { uint8_t data rb.buffer[rb.tail]; rb.tail (rb.tail 1) % RX_BUF_SIZE; if(data ack[match_idx]) { if(match_idx ack_len) return 0; } else { match_idx 0; } } } return -1; }在项目中我通常会封装几个常用的MQTT操作函数int MQTT_Publish(float temp, float humi) { char payload[100]; sprintf(payload, {\params\:{\temp\:%.1f,\humi\:%.1f}}, temp, humi); char cmd[150]; sprintf(cmd, ATMQTTPUB0,\/sys/a1xxxxxx/test/thing/event/property/post\,\%s\,1,0\r\n, payload); return ESP8266_SendCmd(cmd, OK, 5000); }6. 系统集成与调试6.1 数据上传策略优化在实际部署中直接每次采集都上传会导致两个问题一是功耗高特别是电池供电场景二是可能触发阿里云的频率限制。我的解决方案是设置10秒的采集间隔只有当温度变化超过0.5℃或湿度变化超过3%时才立即上传无论如何至少每5分钟上传一次心跳数据对应的STM32代码逻辑float temp_prev 0, humi_prev 0; uint32_t last_upload_time 0; void Main_Loop() { float temp, humi; if(DHT11_Read(temp, humi) 0) { uint32_t now HAL_GetTick(); if(fabs(temp - temp_prev) 0.5 || fabs(humi - humi_prev) 3 || (now - last_upload_time) 300000) { if(MQTT_Publish(temp, humi) 0) { temp_prev temp; humi_prev humi; last_upload_time now; } } } HAL_Delay(10000); }6.2 异常处理机制网络环境不稳定时ESP8266可能会断开连接。完善的异常处理应包括心跳检测每1分钟发送AT指令检查连接自动重连检测到断开后尝试重新连接数据缓存网络中断时暂存数据恢复后补传这里分享一个实用的状态机实现typedef enum { STATE_INIT, STATE_CONNECTING, STATE_CONNECTED, STATE_ERROR } WIFI_State; void WIFI_StateMachine() { static WIFI_State state STATE_INIT; static uint32_t last_check 0; switch(state) { case STATE_INIT: if(ESP8266_Init()) state STATE_CONNECTING; break; case STATE_CONNECTING: if(ESP8266_ConnectWiFi() ESP8266_ConnectMQTT()) { state STATE_CONNECTED; last_check HAL_GetTick(); } else { state STATE_ERROR; } break; case STATE_CONNECTED: if(HAL_GetTick() - last_check 60000) { if(ESP8266_SendCmd(AT\r\n, OK, 1000) ! 0) { state STATE_ERROR; } last_check HAL_GetTick(); } break; case STATE_ERROR: HAL_Delay(5000); state STATE_INIT; break; } }7. 实际部署建议7.1 电源管理技巧如果采用电池供电可以考虑以下优化使用TPS62730等高效DC-DC转换器效率90%配置ESP8266进入深度睡眠模式// 发送AT指令让ESP8266进入深度睡眠 ESP8266_SendCmd(ATGSLP30000\r\n, OK, 1000);STM32使用低功耗模式Stop模式通过RTC定时唤醒实测下来采用这些措施后2000mAh的锂电池可以维持系统工作约45天。7.2 外壳与安装建议使用3D打印外壳保护电路板注意在DHT11周围开孔保证空气流通WiFi天线位置不要被金属遮挡留出复位按钮和配置按钮的开口安装位置选择远离空调出风口和窗户离地面1.2-1.5米高度与人坐高相当多个节点部署时保持至少3米间距8. 功能扩展思路基础系统搭建完成后可以考虑以下扩展多传感器融合增加光照传感器BH1750和空气质量传感器SGP30本地显示添加0.96寸OLED屏幕显示实时数据报警功能当温湿度超出阈值时触发蜂鸣器历史数据使用阿里云TSDB服务存储长期数据联动控制通过规则引擎与智能插座联动例如添加OLED显示的代码片段void OLED_Display(float temp, float humi) { char str[20]; OLED_Clear(); sprintf(str, Temp: %.1fC, temp); OLED_ShowString(0, 0, (uint8_t*)str, 16); sprintf(str, Humi: %.1f%%, humi); OLED_ShowString(0, 2, (uint8_t*)str, 16); OLED_Refresh(); }在后续项目中我还尝试将数据同时上传到本地服务器做备份使用InfluxDBGranfana搭建了可视化看板。当主系统检测到异常数据时会通过企业微信机器人发送报警通知到手机这套组合在实际智能家居项目中运行非常稳定。

更多文章