资讯动态

STM32+ESP8266+阿里云温湿度上云:从AT指令到MQTT数据上报全流程

发布时间:2026/10/14 16:42:00 来源:尧图企业网站定制
简介基于STM32与ESP8266-01S的阿里云物联网温湿度监测完整工程面向嵌入式与物联网入门开发者解决从数据采集、Wi-Fi上云到OLED本地显示的三合一需求。压缩包共253个文件、约7.01MB除标准外设库的C/H源码外还保留Keil工程配置、编译中间文件及可烧录hex固件涵盖USART、ADC、I2C、定时器与RCC等常用外设驱动和底层模块便于直接打开编译也能帮助理解时钟树与单片机初始化流程。另含docx与txt说明文件可辅助梳理工程结构与运行逻辑。已有244人浏览学习适合作为毕业设计、课程项目或个人自学实践的参考资料。通过参考该工程可掌握STM32外设初始化方式、ESP8266-01S接入阿里云平台的流程、OLED显示温湿度数据的实现细节以及传感器数据解析思路对完整物联网项目落地、功能裁剪与二次开发均有直接帮助。1. 为什么用 STM32 ESP8266-01S 阿里云这套组合做温湿度上云STM32 采集温湿度、ESP8266-01S 负责连阿里云、OLED 做本地显示这是很多入门物联网的人都会尝试的一套组合。我见过两种翻车现场一种是把云平台当主角最后卡在设备注册、签名计算和 AT 指令上数据始终出不去另一种是本地显示已经能跑但上报链路一加进来就互相干扰温湿度数据变得不稳定。这套组合的真正价值在于把本地显示和云端上报两条链路同时打通OLED 给你即时反馈阿里云给你远程数据和后续做告警、曲线的基础。它适合刚做完基础外设、想往物联网方向走一步的人也适合快速做环境监测原型的场景。下面按硬件到软件的路线说我实际会怎么搭、怎么设参数、以及哪些坑值得提前躲开。2. 硬件选型与接线4 个影响成败的关键选择这套方案看着器件不多但每个选择都会影响后面调试的难度。先讲最容易忽略的三个选型问题再给接线和上电顺序照着做能少走很多弯路。2.1 为什么用 ESP8266-01S 而不是 NodeMCU 开发板ESP8266-01S 引脚少、没有 USB 转串口、需要自己接电源但体积小、成本低把它当成一个挂在 STM32 串口上的“WiFi 网卡”非常合适。NodeMCU 自带 USB 转串口和稳压对新手友好但体积大而且如果直接用它的 USB 供电去联调经常会因为两个电源域没有共地而出现串口乱码。ESP8266-12F 引脚多但外围电路更多对只传温湿度这个场景来说属于杀鸡用牛刀。我个人习惯是联调阶段就把 ESP8266-01S 独立出来用一块 USB 转 TTL 先验证 AT 指令确认没问题再接回 STM32 的串口。这样能把“模块本身没跑通”和“STM32 代码有问题”这两类故障分开调试效率反而最高。2.2 DHT11、DHT22 还是 SHT30传感器选型对比温湿度传感器看着都是三根线实际差别不小按精度需求决定不要盲目选贵的。传感器接口典型量程精度采样间隔DHT11单总线0~50℃20~90%RH±2℃±5%RH1sDHT22单总线-40~80℃0~100%RH±0.5℃±2%RH2sSHT30I2C-40~125℃0~100%RH±0.2℃±2%RH毫秒级做一般环境监测DHT11 的精度够看趋势不够做精密控制。如果数据要用于回差控制或长期曲线记录直接换 DHT22代码里只需要微调时序宽度。SHT30 精度最好但它走 I2C会和 OLED 抢总线调试时要多处理一层地址冲突和复用问题。本工程按 DHT11 展开换成 DHT22 成本很低。2.3 接线表与供电3.3V 不是接上就行以常见的 STM32F103C8T6 开发板为例接线如下信号STM32 端ESP8266-01S 端注意事项USART2 TXPA2RXD交叉连接USART2 RXPA3TXD交叉连接电源3.3VVCC、CH_PDCH_PD 必须拉高地GNDGND必须共地ESP8266-01S 的 VCC 不能接 5VCH_PDEN引脚如果悬空模块会一直处于复位状态表现就是 AT 无响应。GPIO0 在运行时不能拉低否则模块直接进入下载模式串口发什么都不会回 OK。供电是最容易出问题的地方。ESP8266-01S 发射瞬间电流能到 300mA 左右如果板载 LDO 余量不足现象就是模块能正常连接但一上报就重启。我一般会单独用一路 3.3V 供电给 WiFi 模块STM32 和传感器用另一路或者至少保证总 3.3V 输出余量超过 500mA。2.4 上电自检顺序别一上来就接 STM32我习惯按下面顺序验证硬件只给 ESP8266-01S 接 3.3V 和 GND看板载 LED 是否稳定亮起。确认 CH_PD 已经接 3.3V模块正常启动。用 USB 转 TTL3.3V 逻辑电平接串口115200 波特率发 AT能回 OK 再继续。全部通过后再接回 STM32 的 PA2/PA3。这套顺序能帮我区分供电问题、逻辑电平问题、固件问题。最常见的新手错误是直接把 VCC 接 5V模块发烫后面的步骤全部白费或者是 USB 转 TTL 是 5V 逻辑接上去后乱码把锅甩给 STM32。注意USB 转 TTL 模块的输出电压有 3.3V 和 5V 两种版本必须选 3.3V 逻辑的给 ESP8266-01S。5V 逻辑直接连接轻则乱码重则损坏模块。3. 让 ESP8266-01S 连上阿里云AT 指令、签名脚本与 MQTT 参数硬件接线完成后的下一步是先把 ESP8266-01S 和阿里云的连接跑通。很多项目卡在这一步因为涉及串口指令、平台配置、签名算法三件事。我的建议是先在串口助手里手动跑通再写进 STM32 工程。3.1 先验证 AT 固件串口助手里的最小指令序列ESP8266-01S 出厂通常带 AT 固件但不同固件版本对 MQTT 指令的支持有差异。先接 USB 转 TTL打开串口助手波特率 115200发送下面指令AT ATCWMODE1 ATCWJAP你的WiFi名,你的WiFi密码 ATMQTTUSERCFG0,1,clientId占位,username占位,password占位,0,0, ATMQTTCONN0,接入域名,1883,1 ATMQTTSUB0,/sys/你的产品Key/你的设备名/thing/service/property/set,1每条指令后面都必须有回车换行模块才会执行。AT回 OK 表示固件基本正常ATCWMODE1把模块设为 station 模式ATCWJAP参数是 WiFi 名和密码注意逗号是英文逗号密码用英文双引号包住。如果ATMQTTUSERCFG返回 ERROR说明这个固件版本不支持 MQTT 指令需要刷支持 MQTT 的 AT 固件或者改走下一章说的“透传模式”。连接域名和端口不要凭记忆手输从平台控制台复制粘贴后面会一步步讲。3.2 阿里云平台上的一次性配置产品、物模型、设备进入阿里云物联网平台控制台按下面步骤创建创建产品节点类型选“设备”连网方式选 WiFi数据格式选 Alink JSON认证方式选设备密钥。产品创建完成后在“物模型”里添加两个属性温度标识符 Temperature类型 float、湿度标识符 Humidity类型 float。在产品下添加设备系统会生成三元组ProductKey、DeviceName、DeviceSecret。这里有两个容易踩的坑。第一产品创建后节点类型和数据格式不能修改错了只能删除重建。第二物模型里的标识符就是你后面上报 JSON 里的 key命名要统一不要一会儿用Temperature一会儿用temp平台能不能识别数据就看这个字段。设备添加成功后DeviceSecret 只在创建时完整显示一次要提前存好。后面所有签名计算都依赖它丢了只能重新添加设备。3.3 用 Python 脚本生成 MQTT 签名ESP8266-01S 的 AT 固件本身不帮你算签名所以要在 PC 上先把密码算好。阿里云一机一密的规则是username直接填DeviceNamepassword是一段 HMAC-SHA1 签名签名的原文是按固定顺序拼接的键值字符串。我一般在 PC 上跑一个小脚本把三元组填进去算出来的密码在联调阶段直接用确认没问题后再把算法或常量写进 STM32 工程。import hmac import hashlib import time product_key a1YourProductKey device_name demo01 device_secret yourDeviceSecret client_id custom_client_01 now_ms str(int(time.time() * 1000)) content clientId{0}deviceName{1}productKey{2}timestamp{3}.format( client_id, device_name, product_key, now_ms ) password hmac.new( device_secret.encode(), content.encode(), hashlib.sha1 ).hexdigest() print(password:, password) print(cliendId:, {}|securemode3,signmethodhmacsha1,timestamp{}|.format(client_id, now_ms))这段脚本里content的字段顺序必须和平台要求一致顺序不对签名就校验失败。timestamp用毫秒级设备连接时平台会校验时间窗口PC 和模块共用同一个签名里的时间戳即可。securemode3是文档里常见的连接参数示例实际以你控制台“设备详情”里复制的连接参数为准。把脚本输出的password和clientId填进上一节的ATMQTTUSERCFG然后执行ATMQTTCONN返回 OK 就说明 MQTT 链路已经通了。注意这个脚本只在联调和初期调试时用不要把 Python 运行时搬进 STM32。量产时要么在设备端实现 HMAC-SHA1要么用更加受限但安全的接入方式。3.4 上报数据Alink JSON 与 ATMQTTPUB连接建立后往属性上报的 Topic 发一条 Alink JSON。上报 Topic 有固定格式payload 的params里的 key 必须和物模型的identifier完全一致。Topic: /sys/{你的ProductKey}/{你的DeviceName}/thing/event/property/post Payload: {id:1,version:1.0,params:{Temperature:25.3,Humidity:61},method:thing.event.property.post}用 AT 指令发送时整个 payload 要作为一个字符串参数所以 JSON 里的双引号都要转义否则模块会把 JSON 拆散解析出错。ATMQTTPUB0,/sys/{你的ProductKey}/{你的DeviceName}/thing/event/property/post,{\id\:\1\,\version\:\1.0\,\params\:{\Temperature\:25.3,\Humidity\:61},\method\:\thing.event.property.post\},0,0平台收到后会返回一条应答消息里面带了设备上报的id并且code为 200 表示数据格式合法。如果应答里报错优先检查两件事params里的 key 和物模型标识符是否一致以及Temperature的值是否超出了物模型定义的范围。4. STM32 端最小可跑工程采集、显示、上报三条链路平台参数全部调通之后回到 STM32 工程。我习惯把代码拆成采集、显示、上报三条链路分别打通再合并不要在一条 while 循环里把所有功能堆在一起。4.1 架构裸机状态机 1 秒时间片这个项目不需要上 RTOS。DHT11 读一次约 20msOLED 刷新 1 秒一次MQTT 上报 10 秒一次裸机完全跑得动。用一个结构体保存事件标志由定时器置位主循环轮询处理。typedef struct { uint8_t dht11_need_read; uint8_t oled_need_refresh; uint8_t mqtt_need_report; } app_flags_t; app_flags_t app_flags;在 SysTick 中断里做时间累计每 1000ms 置位一次oled_need_refresh每 10000ms 置位一次mqtt_need_report。主循环每次执行完一个任务后清掉对应标志这样不会有某个延时函数把整条链路堵死。如果oled_need_refresh还停留在里就说明主循环被某个阻塞操作卡住了这种设计能把问题暴露得很早。4.2 DHT11 采集单总线时序与三次采样DHT11 的数据脚在 STM32 上配置为开漏输出并接上拉电阻。主机先拉低至少 18ms 发送起始信号然后释放总线传感器会拉低 80us 再拉高 80us之后输出 40 位数据。每一位的“0”是高电平约 26us“1”是高电平约 70us所以读取时在高电平持续到 40us 左右采样即可区分。static uint8_t dht11_read_byte(void) { uint8_t value 0; for (uint8_t i 0; i 8; i) { while (DHT11_PIN_READ() 0); /* 等待低电平结束 */ delay_us(40); /* 高电平持续 40us 后判断 */ if (DHT11_PIN_READ() 1) { value | (0x80 i); /* 高位在前 */ } while (DHT11_PIN_READ() 1); /* 等待该位结束 */ } return value; }这段代码要求delay_us在读取期间不被中断干扰。我通常在读取前关掉 UART 中断读完再恢复否则一个字节的时序会被串口中断打断读出来的值直接变成 0。读取完成后把 40 位数据分成 5 个字节前两位是湿度整数和小数中间两位是温度整数和小数最后一位是校验和。连续读三次间隔 50ms 以上取中间值不要取平均值。温湿度传感器偶尔会跳出一个毛刺平均值会被毛刺带偏中间值能做到既平滑又保留真实变化。4.3 OLED 显示软件 I2C 与刷新节奏0.96 寸 OLED 通常是 SSD1306 驱动I2C 版本只有四根线接起来最省事。我一般直接用软件模拟 I2C不用 STM32 的硬件 I2C 外设。原因是硬件 I2C 在某些型号上会遇到总线错误标志卡死的问题软件模拟虽然占一点 CPU但调试简单、可移植性高。显示温湿度时把浮点数格式化成字符串再用自带的 6x8 字符点阵写进显存static void oled_show_sensor(float temp, float hum) { char line[16]; snprintf(line, sizeof(line), Temp: %.1f C, temp); oled_show_string(0, 0, line); snprintf(line, sizeof(line), Humi: %.1f %%, hum); oled_show_string(0, 2, line); oled_update(); }oled_update是一次性把显存推送到屏幕不要每写一个字符就刷新一次否则屏幕会闪。刷新周期 1 秒足够。I2C 地址常见是 0x3C也有少数屏是 0x3D如果屏幕不亮先做一次 I2C 地址扫描别急着怀疑屏坏了。4.4 上报链路UART 透传和 JSON 组包上报的逻辑比采集和显示更依赖串口稳定性。我建议用硬件串口加中断接收发送用阻塞方式。每次需要上报时把温度和湿度拼成 JSON payload再包装成 ATMQTTPUB 指令发给 ESP8266-01S。static void mqtt_publish_temperature(float temp, float hum) { char payload[128]; static uint16_t msg_id 0; msg_id; snprintf(payload, sizeof(payload), {\id\:\%u\,\version\:\1.0\,\params\:{\Temperature\:%.1f,\Humidity\:%.1f}, \method\:\thing.event.property.post\}, msg_id, temp, hum); /* 发送时把 payload 里的双引号转义AT 固件要求整体作为一个字符串参数 */ /* 同时补齐 \r\n 结尾ESP8266 才能识别指令边界 */ }组包时有几个细节要特别注意。第一AT 指令一律以\r\n结尾很多人漏了\r模块把两条指令当成一条永远报 ERROR。第二payload 里的双引号在 AT 层需要转义我的做法是先组好普通 JSON 字符串再用一个转义函数把替换成\最后拼成完整指令。第三msg_id每次上报要自增如果一直用固定值平台端做去重时会把新数据丢掉。上报周期我取 10~20 秒。温度变化本身就慢10 秒上报一次足够对平台消息量也友好。OLED 刷新 1 秒一次DHT11 采样因为要间隔 1 秒以上所以在 1 秒时间片里只读一次即可。5. 避坑串口乱码、连接失败、OLED 花屏与采样翻车这套方案能跑通的标志是OLED 稳定刷新、平台能看到持续上行的数据。但实际调试过程中下面五个问题几乎每个项目都会遇到其中一两个按现象、原因、解决的顺序列出来。5.1 上电后 AT 无响应或乱码现象接上 USB 转 TTL 后发 AT 没反应或者收到ÿÿÿ一样的乱码。原因供电不足、CH_PD 未拉高、GPIO0 被拉低、逻辑电平不匹配、波特率不对。ESP8266-01S 上电瞬间串口会吐几个异常字节这是正常的关键是之后是否稳定。解决先只供电等 2 秒再发 AT别在开机的瞬间输入指令。确认 3.3V 电压在模块发射时不低于 3.0V不行就单独供电。电平不匹配就换 3.3V 逻辑的 USB 转 TTL或加电平转换芯片。波特率先用 115200/8N1如果不行试 9600部分固件出厂是低波特率。5.2 阿里云显示设备离线或反复重连现象ATMQTTCONN 返回 ERROR或者平台显示设备在线但几十秒后又离线。原因签名密码算错、timestamp 过期、clientId 里的 securemode 与实际端口不一致、Topic 拼写错误。解决先用 3.3 节的脚本重新算一次密码把生成的参数和 AT 指令逐字段核对。签名用的 timestamp 和实际连接时间要接近偏差超过平台限制就会认证失败。联调阶段可以把签名里的 timestamp 写死后快速试验跑通后再改成动态生成。连接参数从控制台“设备详情”里复制不要自己手写拼接。5.3 OLED 不亮、白屏或乱码现象屏幕全白点不亮或者刷新过程中花屏。原因I2C 地址不是 0x3C、SCL/SDA 接反、电源电压不稳、初始化序列执行时被打断。解决先做 I2C 地址扫描确认地址是 0x3C 还是 0x3D。检查模块是否自带 3.3V 稳压和上拉电阻没有上拉时把 STM32 引脚配置为开漏并启用内部上拉。初始化每个命令之间加 2ms 延时不要在初始化过程中被串口中断打断。如果偶尔花屏更新完显示后重新发送一次初始化序列比单纯清屏更可靠。5.4 DHT11 读数一直是 0 或偶尔跳变现象温度显示 0.0、湿度 99.9或者某个值突然跳一下又恢复。原因数据线没有上拉电阻、起始信号拉低时间不够、读取期间被中断打断、两次读取间隔小于 1 秒、引脚模式配错。解决数据线加 4.7k 上拉没有独立上拉时先用内部上拉应急。读取期间关掉串口中断或进入临界区。每次读取之间间隔至少 1.2 秒不要在主循环里连续读。连续采集三次取中间值能过滤掉大部分毛刺。5.5 上报时有时无链路排查顺序现象设备在线但平台数据断断续续有时一条成功、下一条失败。原因WiFi 信号弱导致 TCP 断开、AT 固件的 MQTT 连接超时被服务端回收、上报消息里 id 重复被平台去重。解决按顺序排查。先看串口日志里 ATMQTTPUB 是否返回 OK再看平台应答的 code 是否为 200。如果连接已经被服务端断开继续 Publish 只会一直失败需要在固件里检测到这个状态后重新 ATMQTTCONN。长期运行后模块还是掉就用 ATRST 定期重启 WiFi 模块这招能解决很多玄学问题。6. 把工程改稳断线重连、补传与状态机重构原型能跑通之后我建议立刻把“裸奔式”上报改成状态机管理否则项目一加了断线重连和补传功能就会变成一团乱麻。6.1 用状态机管理连接生命周期把上报逻辑拆成几个状态不让主循环在已断连的情况下反复尝试 Publish。状态定义很简单typedef enum { NET_INIT, /* 上电初始化 */ NET_WAIT_WIFI, /* 等待 WiFi 连接 */ NET_MQTT_CONN, /* 建立 MQTT 连接 */ NET_IDLE, /* 正常工作 */ NET_RECONNECT /* 等待重连 */ } net_state_t;主循环里只有NET_IDLE状态下才发数据发送失败立刻切到NET_RECONNECT重连成功再回NET_IDLE。这样能把“已经断开还反复上报”的隐患从根上杜绝。6.2 断线补传本地暂存并携带时间戳保存最近一次发送失败的数据重连成功后先补发暂存数据再发当前值。同时在 OLED 第一行显示网络状态 ONLINE 或 OFFLINE调试网络时这个反馈非常有用。我以前也喜欢把所有逻辑都堆在 while 里功能一多就顾此失彼。后来改成硬件定时器加状态机的写法反而省心不少。这套断线重连和补传的思路希望能帮你的温湿度项目不再动不动断档。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑