☰
STM32+华为云IoT室内环境监测系统:从传感器采集到MQTT上云完整方案
2026/10/28 8:48:34 网站建设 项目流程

简介:基于STM32与华为云物联网平台设计的室内环境监测系统,是一套完整可复现的嵌入式教学与毕业设计工程包。资源压缩包约168.83MB,内附STM32F103RCT6完整源码、设计文档、电路原理图、实物接线图、Android上位机APP、开发工具及详细使用说明,覆盖从硬件选型到云端联调的完整链路。系统通过DHT11采集温湿度、BH1750检测光照强度、MQ135感知空气质量,数据在本地上LCD屏幕实时显示,同时借助ESP8266串口WiFi上传至华为云IoT平台,移动端可远程查看环境参数。硬件连接、代码编译、云平台配置等流程在设计文档中均有清晰描述,读者只需按文档准备物料、接线并烧录程序,即可完成同款项目。当前已有1129人学习/下载,适合物联网课程设计、电子竞赛筹备或毕业设计参考;配套专栏文章与B站视频提供了进一步的设计思路讲解,便于快速上手。

1. 室内环境监测系统源码包:这套STM32+华为云IOT方案解决什么问题

想做一个能远程看到室内温湿度、烟雾浓度和光照强度的物联网项目,大部分人最后卡住的位置不是传感器读取,而是数据上了云之后平台端怎么认、怎么存、怎么告警。这套基于STM32设计的室内环境监测系统,就是一条把“STM32采集 + 华为云IOT接入 + 云端展示”完整串起来的参考路径。源码包把设备端固件、华为云平台配置流程和对接要点打包在一起,照着做能把开发周期从一个月压到三五天。适合正在准备基于STM32的毕业设计、或者第一次接触华为云设备的嵌入式新手,也适合想快速搭一个可演示的产品原型的工程师参考。

2. 硬件架构与传感器选型:STM32F103C8T6搭配哪些外设才算完整

2.1 主控选型理由:为什么STM32F103C8T6是这类项目的默认答案

室内环境监测这种数据采集类的STM32项目,选主控的标准不是“性能越强越好”,而是“外设够用、资料够多、调试手段透明”。STM32F103C8T6是Cortex-M3内核,72MHz主频,20KB RAM、64KB Flash,跑一个传感器采集加MQTT组包的应用,资源占用不到一半。更重要的是它的ADC、USART、I2C引脚互不冲突,不会出现要两路串口却发现引脚被复用占掉的情况。

选F103C8T6还有一个很实际的理由:keil下的标准库和HAL库工程模板多,不管是自己新建还是一边查错一边补课,上手难度都比F407低一个量级。2023年的源码包大多是Keil MDK工程,有些用标准库,有些用HAL库,两者差别主要在初始化和延时函数调用上,核心代码逻辑是一致的。如果你还在纠结stm32标准库新建工程还是HAL库下载,建议先按源码包里的现状跑通再换,不要一上来就做库的迁移。

最小系统部分,我一般会外接8MHz晶振配合两个20pF负载电容,复位脚NRST接10k上拉电阻加0.1uF电容到地,SWD下载口留出PA13和PA14,这三样齐了单片机就能稳定跑。电源用AMS1117-3.3从USB的5V转下来,输入输出各加一个10uF和一个0.1uF电容。STM32F103C8T6的3.3V逻辑电平对5V容忍,这给传感器接口留了余地,但后面接ESP8266时仍然要小心供电而不是电平,这点在2.3节详细说。

2.2 传感器选型与接线:DHT11、MQ-2、BH1750的协议和引脚分配

室内监测三个基本维度是温湿度、空气质量和光照,对应三颗最经典的传感器:DHT11、MQ-2、BH1750。我常跟人说这三颗传感器凑在一起,等于把嵌入式里三种典型外设接口都过了一遍:单总线、模拟量ADC、I2C。把它们调通之后,以后再玩什么传感器都有套路。

DHT11是数字温湿度传感器,单总线协议,温度精度±2℃,湿度精度±5%RH,量程温度0到50℃、湿度20%到90%RH,对应“人在室内舒不舒服”的常规区间是够用的。单总线意味着数据脚既要输出也要输入,所以必须外接4.7k到10k的上拉电阻到3.3V。DHT11每次返回40位数据,依次是湿度整数、湿度小数、温度整数、温度小数和校验和,校验和等于前四个字节之和的低8位。很多第一次用DHT11的人不看校验和,数据乱跳也不知道是硬件还是时序问题。

MQ-2负责烟雾和可燃气体浓度检测,气敏元件需要加热,刚上电的前几分钟输出会漂,这是材料特性,不是故障。它的模拟输出范围是0到5V,而STM32F103的ADC采样范围是0到3.3V,所以不能直接把AOUT接到PA1上,必须在中间做分压。常见做法是用两个10k电阻串联分压取中点,再用一个0.1uF电容并在ADC引脚对地做低通滤波。

BH1750是I2C接口的数字光照传感器,量程0到65535lx,3.3V供电即可。它在源码包中通常和可选的OLED屏幕共用一条I2C1总线,BH1750地址是0x23,SSD1306一般是0x3C,两个器件地址不同,挂在同一条总线上互不干扰。I2C速率配置为100kHz标准模式,比400kHz在长走线场景下更稳定。

下表是我常用的一套接线表,照着焊基本不会错:

硬件/模块接口类型信号STM32引脚备注
DHT11单总线DATAPA0接4.7k上拉到3.3V
MQ-2模拟量AOUTPA1(ADC1_IN1)经10k+10k分压,接0.1uF滤波电容
BH1750I2CSDAPB7与OLED共用总线
BH1750I2CSCLPB6接2.2k上拉到3.3V
ESP8266-01SUARTTX/RXPA3/PA2USART2,模块CH_PD接3.3V
OLED(可选)I2CSDA/SCLPB7/PB6地址0x3C

2.3 WiFi模块选型:ESP8266用AT指令方案而非SDK方案的三个理由

WiFi通信这一环,源码包用的是ESP8266-01S配合出厂AT固件,让STM32通过串口发AT指令控制它上网。市面还有一种方案是把ESP8266刷成SDK固件,让8266自己跑MQTT,STM32只负责采集数据通过串口丢给8266。两种方案我都试过,源码包采用AT指令方案是有道理的。

第一,调试透明。AT指令方案里每个状态都有文字反馈:AT+CWJAP返回WIFI GOT IP说明连上路由器,AT+CIPSTART返回CONNECT说明TCP建链成功,错在哪一步一眼就能看到。SDK方案两边都在跑代码,数据丢了不知道是STM32采集错还是8266协议栈错,黑匣子感很强,尤其不适合新手。第二,代码结构简单,STM32端不用集成WiFi协议栈,只要维护几条指令序列和超时重试。第三,硬件可替换性强,如果8266坏了或者要换串口WiFi模块,STM32端的驱动只需改AT指令部分,业务代码完全不动。

ESP8266-01S的坑在供电。模块瞬时发射电流可以达到300mA,从STM32板上LDO取电很容易把电压拉到3V以下触发欠压复位。我一般给它单独一路3.3V供电,或者用输出能力不低于500mA的稳压芯片,并在模块VCC和GND之间并联100uF电解电容加0.1uF陶瓷电容。CH_PD使能脚必须接3.3V,不能悬空,这是模块不上电的常见原因。

串口电平方面,ESP8266是3.3V逻辑,不能直接连5V单片机的串口。源码包用的是STM32F103的USART2,3.3V电平本来就匹配,只需要确保STM32和模块共地。波特率一般设为115200,和AT固件默认值一致。ESP8266对AT指令间隔敏感,代码里每条指令之间加10到20ms延时,避免指令缓冲区错乱。

3. 华为云IoT平台配置:产品模型、设备注册与MQTT鉴权参数生成

3.1 创建产品与产品模型:属性定义决定设备端JSON报文格式

设备端数据要能被华为云正确解析,第一步是在华为云IoTDA设备接入服务里创建产品,然后在产品下定义“服务”,服务下面定义属性。属性名、数据类型、单位这些字段不是随便填的,设备端上报的JSON会按照这里定义的结构校验,对不上平台就丢弃。

操作路径是:登录华为云控制台,搜索“IoTDA设备接入”,进入实例后点“产品”,再点“新建产品”。产品名称填“室内环境监测”,所属行业选“智慧城市”或“智慧家居”,设备类型填“环境监测设备”。创建完成后进入产品详情页,在“模型定义”里添加服务,服务ID写成env_monitor,这个ID就是后续JSON里的service_id字段。

添加属性时,我建议按下面这个表格规划:

属性名数据类型单位访问权限说明
temperaturedecimal℃只读(上报)温度,保留一位小数
humiditydecimal%RH只读(上报)湿度,保留一位小数
smokeint-只读(上报)烟雾ADC换算值
lightintlx只读(上报)光照强度

属性定义里最容易错的是数据类型:温度如果定义成int,设备端上报26.5会被平台解析失败或截断。源码包里温度湿度都是decimal,设备端组包时用%.1f格式化,这样两边才对得上。属性添加完后先别急着接设备,用平台自带的“模拟器”功能上报一帧测试数据。在产品详情页打开调试工具,选虚拟设备,手动构造一帧services结构的数据发送,如果平台能正常显示,说明产品模型没问题,后面设备端连不上时可以放心把矛头指向设备端。

3.2 注册设备并获取连接参数:接入地址、设备ID与密钥

产品模型建好后,在IoTDA控制台的“设备”页面注册真实设备。注册时要填产品ID和设备标识码,设备标识码是自己在业务系统中的唯一编码,可以填模块MAC地址或者随便一串不重复的字符串。提交后平台会生成一个真正的设备ID,格式通常是“产品ID_设备标识码”,同时生成设备密钥。

设备密钥是设备连云的鉴权凭证,只在注册成功那一刻完整显示一次,之后就只能重置。这一点源码包的README里一般会写,但很多人还是习惯性关掉弹窗,后面找不回密钥只能重置。设备ID和密钥要填到STM32工程的配置头文件里,常见位置是iot_config.h或者mqtt_config.h。

接入地址在IoTDA实例的“总览”页面查看,不同区域的域名不一样。2023年的源码包多数是华北北京四的实例,接入域名类似 iot-mqtts.cn-north-4.myhuaweicloud.com,设备接入端口是1883(非加密)或8883(加密)。如果你使用的新建的实例区域不同,域名要按控制台显示为准,不能照抄代码包里的默认值。很多设备连不上的原因是区域不匹配,代码里写死了一个旧实例地址,而自己在另一个区域建了实例。

三元组参数汇总如下:

参数获取方式作用
接入地址IoTDA实例概览页建立TCP连接的目标域名
设备ID注册设备后生成MQTT的clientId和username
设备密钥注册时保存计算MQTT password

3.3 MQTT鉴权参数生成:clientId、username、password的计算与验证

华为云IoTDA的MQTT接入使用自研鉴权方式,三个参数里只有password需要动态计算。clientId直接填设备ID,username也填设备ID,password则是由时间戳签名和时间戳组合而成的字符串。计算规则是:取当前小时的字符串,格式yyyyMMddHH,用设备密钥对这个时间戳做HMAC-SHA256签名,得到64位十六进制小写字符串,再拼接下划线和时间戳本身。

这个机制意味着password每小时变化一次,设备断线重连时必须重新生成。源码包如果用的是固定密码方式,跨小时后连接必然失败,这也是新手最容易遇到的坑之一。下面这个Python脚本可以在PC上生成一套连接参数,用来验证平台配置是否正确:

import hmac import hashlib import time device_id = "your_device_id" # 华为云注册设备生成的设备ID device_secret = "your_device_secret" # 注册时保存的设备密钥 timestamp = time.strftime("%Y%m%d%H") # 如 2025041014,精确到小时 sign = hmac.new( device_secret.encode("utf-8"), timestamp.encode("utf-8"), hashlib.sha256 ).hexdigest() password = f"{sign}_{timestamp}" print("clientId:", device_id) print("username:", device_id) print("password:", password)

逻辑说明:HMAC的密钥是设备密钥,消息是时间戳,两者都要用utf-8编码输入;hexdigest得到的是小写十六进制字符串。拼接顺序是“签名_时间戳”,下划线是分隔符,不能颠倒,否则鉴权失败。

参数说明:时间戳用设备当前小时生成,不分时区,本地时间几点就填几点。测试时如果报密码错误,先检查电脑当前时间是否和实际一致,等整点过了之后旧密码立即失效。拿到三个参数后,用MQTT客户端工具连一次,broker地址填接入域名,端口1883,clientId和username填设备ID,password填脚本输出的一整串。连上之后到IoTDA控制台看设备状态,变成“在线”就说明平台侧全部就绪。

4. STM32端固件实现:从传感器采集到MQTT上报的完整代码路径

4.1 工程结构与数据流:main函数里各模块的调用顺序

源码包里的设备端工程一般是Keil MDK项目,打开后能看到main.c、dht11.c、adc_sensor.c、bh1750.c、esp8266.c、mqtt_client.c这几个核心文件。main函数执行的顺序通常是这样:HAL初始化或标准库的SystemInit、配置时钟、初始化USART1调试串口和USART2通信串口、初始化I2C1、初始化DHT11引脚、初始化ADC1、调用ESP8266联网、建立MQTT连接,最后进入一个10秒周期的while循环。

这里的顺序有一个坑:必须先让ESP8266完成WiFi接入和TCP建链,再去发MQTT的CONNECT报文。ESP8266在透传模式下如果没有建立TCP连接,STM32发过来的MQTT数据会被它丢弃或者返回SEND FAIL。我在调试时习惯在每一步AT指令后打印返回状态,确保WIFI GOT IP之后再走下一步,不要用一个很长的HAL_Delay糊弄过去。

工程里MQTT客户端有两种可能:用paho嵌入式C库移植过来的MQTTClient.c,或者纯手写的轻量组包逻辑。前者会有一组transport回调函数把底层收发的字节流转给串口,后者一般直接操作USART2的DMA缓冲区。第一次跑项目不要改协议栈,只关注main.c里业务数据的填入位置,把传感器读到的值正确放到上报结构体里就够了。

4.2 DHT11温湿度驱动:单总线时序、数据校验与错误处理

DHT11驱动是整个项目里最容易出现“玄学”问题的地方,因为它对微秒级延时非常敏感。单总线的完整读取流程是:主机把数据线拉低至少18ms再释放,这会触发DHT11返回一个80us低电平响应信号,然后紧接着输出40位数据。每一位数据都由50us低电平起始位和一段高电平组成,高电平持续26到28us表示0,持续70us表示1。

麻烦的是,HAL_Delay最小只能到毫秒级,正负误差比较大,做微秒延时需要自己写。常见做法是用DWT计数器做精准延时,或者用SysTick中断配合。下面是一段基于HAL库的DHT11读取核心代码:

uint8_t DHT11_ReadBit(void) { uint16_t timeout = 0; // 等待数据线被DHT11拉高,这是一位的起始同步点 while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET) { if (++timeout > 10000) return 0xFF; // 超时保护,防止卡死 } // 拉低后,DHT11先输出50us低电平,随后是高电平 while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_RESET); delay_us(30); // 在高电平出现后30us处采样 if (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET) { // 高电平持续时间长,等待它结束再返回1 while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET); return 1; } return 0; } uint8_t DHT11_ReadByte(void) { uint8_t data = 0; for (int i = 0; i < 8; i++) { data = (data << 1) | DHT11_ReadBit(); } return data; }

逻辑说明:DHT11_ReadBit先等数据线从高电平变为低电平,这对应每一位数据的起始位;低电平结束后开始出现高电平,此时延时30us再采样。如果读到高电平,说明该位高电平持续时间较长,判定为1;如果读到的还是低电平,说明高电平早已结束,判定为0。这个30us的采样点是区分0和1的关键。

参数说明:timeout上限10000次循环大约几十微秒,如果DHT11没接好或者总线被拉死,函数能够正常退出返回0xFF,而不是让整个固件陷入死循环,这是嵌入式代码里必须做的防护。delay_us(30)要用DWT实现,不能靠简单的空循环,因为编译器优化等级不同会让空循环次数失效。读完5个字节后要检查校验和,即前四个字节加和取低8位,与第五个字节相等才算读成功。

4.3 ESP8266驱动与MQTT组包:AT指令序列和上报报文封装

ESP8266的驱动代码核心是一个“发指令等应答”的函数,源码包里通常叫ESP8266_SendCmd。下面这段序列是建立WiFi连接和TCP透传的标准流程:

// ESP8266透传配置序列,每步等待明确应答再继续 ESP8266_SendCmd("AT+RST\r\n", "ready", 3000); // 复位模块 ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK", 1000); // 设置为Station模式 ESP8266_SendCmd("AT+CWJAP=\"wifi_name\",\"wifi_pass\"\r\n", "WIFI GOT IP", 15000); // 连接路由器 ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK", 1000); // 开启透传模式 ESP8266_SendCmd("AT+CIPSTART=\"TCP\",\"iot-mqtts.cn-north-4.myhuaweicloud.com\",1883\r\n", "CONNECT", 10000); // 建立TCP连接 ESP8266_SendCmd("AT+CIPSEND\r\n", ">", 1000); // 进入透传发送状态

逻辑说明:AT+RST把模块复位到干净状态等待ready;AT+CWMODE=1让模块只工作在Station模式而非AP模式;AT+CWJAP连接WiFi后必须等WIFI GOT IP,这是ESP8266拿到DHCP地址的标志;AT+CIPMODE=1开启透传,之后AT+CIPSTART建立TCP长连接,因为MQTT是跑在TCP之上的应用层协议;最后的AT+CIPSEND返回“>”提示符表示进入透传模式,之后STM32向USART2发送的任何字节都会被ESP8266原样转发到服务器。

参数说明:WiFi名称和密码要写进配置头文件,不要每次编译都改C代码;AT+CIPSTART中的端口1883对应非加密MQTT。如果改用8883加密端口,ESP8266默认AT固件通常不带TLS,要刷带SSL的固件,所以调通阶段先用1883是合理的选择。

MQTT协议部分,CONNECT报文的组装是个相对繁琐的活,如果源码包里集成的是paho嵌入式库会省很多事。手写组包时重点看这几个字段:协议名固定为“MQTT”,协议级别填4代表MQTT 3.1.1,连接标志0xC2表示启用用户名和密码且清理会话,保活时间设120秒。报文的载荷区按顺序拼接clientId、username、password,与HTTP那种键值对不同,MQTT靠位置区分这三个字段。

属性上报则通过PUBLISH报文完成,topic固定为“$oc/devices/{设备ID}/sys/properties/report”,payload采用下面这个JSON结构:

{ "services": [ { "service_id": "env_monitor", "properties": { "temperature": 26.3, "humidity": 58.0, "smoke": 210, "light": 468 } } ] }

逻辑说明:这是华为云IoTDA规定属性上报的标准格式,services是个数组,数组内每个对象包含service_id和properties两个字段。properties里的键名必须和平台产品模型里定义的属性名一字不差,否则平台无法正确解析。

参数说明:temperature和humidity用%.1f保留一位小数,这是因为产品模型里定义的是decimal类型;smoke和light按整数上传。如果后续要上报设备状态或者告警信息,可以额外定义product模型里的新属性,但不要直接往properties里塞未定义的键,平台验包会失败。

5. 接线与调试避坑指南:五个把新手卡住的实际问题

5.1 现象:ESP8266上电后反复重启,串口打印乱码

最初遇到这个问题时我也以为模块坏了,换了三块才发现是供电。ESP8266工作时电流波动很大,WiFi发射的瞬间电流接近300mA,如果供电用的是STM32板载AMS1117,瞬时压降会把模块电压拉到3V以下,模块随即复位,串口因此打印乱码或出现AT响应不完整。

解决方法是给ESP8266独立供电,或者选用输出电流不低于500mA的3.3V稳压芯片。在模块的VCC和GND之间并联一个100uF电解电容和一个0.1uF陶瓷电容,就近吸收瞬时电流。CH_PD引脚不能悬空,必须直接接到3.3V。如果还出现反复重启,检查电源线的线径和杜邦头的接触电阻,细长杜邦线在大电流场景下的压降很明显。

5.2 现象:DHT11读上来的温湿度永远不变,或者一直是0xFF

DHT11读数固定在0xFF,最常见原因是数据脚缺少上拉电阻。单总线协议的空闲状态靠上拉电阻维持高电平,引脚悬空时电平不确定,时序完全错乱,读出来的每一位都是1,校验和自然不通过,最后只能得到0xFF或者上一次的缓存值。

另一个常见原因是微秒延时函数不精准。如果代码里用简单的NOP空循环或者把HAL_Delay用于微秒延时,编译器优化等级一变,时序就崩。即使是HAL_Delay在毫秒级有几十us的误差也会导致DHT11判断错误。解决方法是使用DWT计数器实现delay_us函数,并且在数据脚上确认接入4.7k到10k上拉电阻。调试时在串口打印原始40位数据和校验结果,如果校验和一直错,用示波器抓数据线的波形,看主机释放总线后DHT11是否真的拉低了80us响应信号。

5.3 现象:PC端MQTT工具能连上华为云,STM32却一直连不上

这种情况下平台、设备和代码看起来都没问题,但STM32就是连不上,其实多半是密码里的时间戳失效了。华为云的MQTT密码和当前小时绑定,源码包里如果在PC上算好password写死在代码里,等烧录完再上电,可能已经跨过了生成密码的那个小时,密码自然失效。PC端测试时刚生成密码所以能连上,设备端上电时间晚于密码生成时间,就被拒绝了。

解决方法是把密码放到设备端启动时动态计算,工程里需要移植mbedtls的HMAC-SHA256实现,或者每次烧录前用脚本重新生成写进配置。还要检查设备端MQTT连接标志的CleanSession位,如果为0,平台会保留上次会话,握手可能失败,改成1清理会话后重连概率高很多。另外,确认STM32的RTC或系统时间如果参与时间戳计算,不能有日期错误,否则密码签名必然不对。

5.4 现象:华为云平台显示设备在线,却收不到任何属性数据

设备在线说明TCP和MQTT连接已经建立,等于说“路”是通的,但数据没送到正确的位置。最常见原因是topic写错,属性上报的topic是“$oc/devices/{设备ID}/sys/properties/report”,这和消息上报的“/sys/messages/up”是两条不同的通道,把属性数据发到消息通道,平台能从日志里看到消息但产品模型里解析不出属性。

另一种情况是payload结构不对,services不是数组、service_id拼错、properties里用了产品模型里没有的键,平台验包都会静默丢弃。排查时先在控制台用“消息跟踪”功能查看上行消息:显示“解析失败”说明是payload问题;消息状态显示已到达但设备详情页没有数据,则是topic路径错误。先停掉STM32,用MQTT工具手动往正确topic上发一帧标准JSON,平台有数据显示就说明云端解析没问题,问题锁定在设备端组包。

5.5 现象:ADC采集的烟雾值跳变严重,没法做阈值判断

MQ-2的模拟输出接到PA1后数值满量程跳,这背后通常有三个原因叠加。一是MQ-2预热不足,气敏元件的阻值还没有稳定;二是ADC引脚本身没有滤波,单片机采到的直接是夹杂高频噪声的模拟信号;三是F103的ADC参考电压VDDA不够干净,板子上VDDA和VDD直接短接,没有做隔离。

解决方法是:代码里上电先延时两到三分钟再启用烟雾告警判断逻辑,期间只采集不判断;硬件上在PA1对地加0.1uF电容做RC低通滤波,软件里连续采16次剔除最大值和最小值再取平均;电源设计上把VDDA通过10uH磁珠接到VDD,VDDA和VSSA之间并一个1uF电容。告警阈值不要用瞬时值做判断,我习惯用滑动窗口内最近10次采样的平均值,烟雾本身就是慢变化量,不需要瞬时响应,这样既稳又准。

6. 把demo变成能长期运行的系统:断线重连、看门狗与数据校准

核心链路跑通之后,我建议立刻做三件事,代码量不大,却能把一个“演示版”变成能在角落里挂机一周的稳定版本。

第一件是断线重连用指数退避。WiFi断线或者MQTT会话异常后,不要每秒重连一次,那样会在平台侧形成大量无效握手。我的做法是从1秒开始,每失败一次等待时间翻倍,到32秒封顶,连接成功则计数清零。下面是一段在main循环里执行的简化逻辑:

static uint8_t retry = 0; if (!mqtt_connected) { uint32_t wait_s = 1 << (retry > 5 ? 5 : retry); // 1,2,4,8,16,32封顶 HAL_Delay(wait_s * 1000); if (MQTT_Connect() == 0) { retry = 0; } else { retry++; } }

这段代码的重点在位移运算实现指数递增,retry大于5时封顶在32秒,避免无限增长。同时把每次重连的原因和设备状态打印在调试串口,线上排查时能看到是WiFi断还是MQTT断,比盲猜强太多。

第二件是独立看门狗。配置F103内置的IWDG,超时时间设在30到60秒,主循环正常运行时在循环末尾喂狗。注意喂狗位置一定是整个循环的最后一步,绝不能放在传感器读取之后、MQTT发送之前,否则DHT11卡死时狗被顺带喂了,看门狗就失去了兜底意义。固件跑飞或进入阻塞后,看门狗会在超时后复位系统,加上断电重连逻辑,整个设备才能具备自恢复能力。

第三件是传感器校准。DHT11的精度受温漂和个体差异影响,低温高湿时偏差更明显。我习惯拿一只靠谱的温湿度计同时放在测试环境里连续比对半天,取偏差平均值作为补偿值写进代码,比如实测温度普遍偏低1.5℃,就在上报前统一加1.5。MQ-2不追求绝对浓度,用相对基线更实际:设备稳定运行一小时后记录ADC平均值作为零位,用户设置一个超过基线的偏移量做告警阈值,这样能抵消传感器个体差异和老化漂移。

验证时我习惯从云端倒查:在华为云控制台的设备详情页看属性数据和消息跟踪,确认每10秒上报一帧,数据在平台侧以折线形式稳定更新,再去做断网拔线的恢复测试。拔掉路由器和WiFi,观察系统在30到40秒内是否自动重连并恢复上报,这个测试通过,设备才算真正能交差。这套室内环境监测系统技术栈并不复杂,传感器采集、ESP8266透传、MQTT上报、华为云解析,每一环单独看都简单,拼在一起难的是边界条件。我踩过供电、时序、鉴权时间戳这些坑,也都在源码包里对应位置做了处理,希望这些经验能让你少走弯路,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询