简介这份资源面向嵌入式开发初学者与物联网应用开发者聚焦STM32微控制器与ESP-01S Wi-Fi模块的协同通信通过AT指令集配置MQTT协议实现传感器数据的无线发布与远程传输可应用于智能家居、环境监测、工业自动化等场景。压缩包共5个文件约39KB包含C语言源码与头文件实现AT指令控制与MQTT客户端逻辑、README与说明文档梳理项目架构与使用方法、附赠文档等便于快速理解代码结构与移植思路。目前已有96人学习下载。读者可从中掌握ESP-01S联网配置、MQTT发布/订阅机制、STM32数据采集与服务器通信的完整链路并参考源码中的AT指令编写与排错方式为自建物联网无线传输方案提供可复用的实践模板。1. 从一块 STM32 和一片 ESP-01S 说起MQTT 上云到底难在哪很多人第一次做物联网无线传输手里往往是一块 STM32 最小系统板加一片几块钱的 ESP-01S目标很朴素把传感器数据发到云端手机或后台能实时看到。听起来简单真动手才发现卡点全在细节里——ESP-01S 的 AT 指令回显怎么处理、MQTT 报文怎么拼、连接断了怎么重连、数据发出去对方收不到怎么排查。这套「STM32 ESP-01S AT 指令 MQTT」的组合是嵌入式无线通信里最经典也最容易翻车的一条链路因为它把串口时序、AT 状态机、TCP 连接、MQTT 协议四层东西叠在了一起任何一层出问题现象都是「数据没上去」黑匣子感极强。这篇笔记面向的是已经会点 STM32 串口收发、想把这套链路真正跑通的开发者。我会按「先讲清每层在干什么 → 再给可复现的配置和代码 → 最后把踩过的坑摊开」的顺序写参数怎么设、失败看哪里都会落到具体位置。读完你应该能自己搭出一条稳定的数据发布链路而不是停留在「能连上但不知道为什么不稳」的阶段。2. 链路拆解与选型为什么是 ESP-01S 配 AT 指令而不是别的方案2.1 四层结构各自负责什么把整条链路拆开看从下到上依次是STM32 通过 UART 和 ESP-01S 通信ESP-01S 内部跑固件负责 Wi-Fi 连接和 TCP/IP 协议栈AT 指令是 STM32 控制 ESP-01S 的「遥控器语言」MQTT 则是跑在 TCP 之上的应用层协议负责把数据按主题发布出去。关键认知是STM32 本身不实现 TCP/IP也不实现 MQTT它只做两件事——发 AT 指令、解析返回。真正的网络活儿全在 ESP-01S 里。所以调试时如果数据没上去先别怀疑 MQTT 报文先确认 AT 指令的返回是不是符合预期。这个分层意识能省掉大量瞎猜时间。ESP-01S 出厂一般自带 AT 固件常见的是支持 MQTT AT 指令集的版本比如 ATMQTTUSERCFG、ATMQTTCONN 这一套。如果你的模块固件太老可能只有 TCP 透传指令那就得自己在 STM32 侧手拼 MQTT 报文工作量翻倍。所以第一步永远是确认固件版本和它支持的指令集。2.2 选型对比AT 指令方案 vs 自己移植协议栈方案开发量稳定性适合场景ESP-01S AT 指令低串口收发即可依赖固件重连要自己写快速验证、小数据量、成本敏感STM32 移植 lwIP 以太网高要调协议栈可控性强有线场景、数据量大换用带协议栈的模组如某些 Cat.1 模组中AT 指令更规范较好需要蜂窝网络、远程部署我一般会建议如果只是几十字节的传感器数据、几分钟发一次ESP-01S AT 指令完全够用成本能压到最低。它的短板在于并发能力弱、重连逻辑要自己兜底但只要把状态机写扎实长期跑没问题。2.3 硬件连接与上电注意点ESP-01S 是 3.3V 供电峰值电流能到 300mA 左右千万别用 STM32 板上的小电流 LDO 直接带否则一发数据就复位现象是「连上了但一发就断」。常见做法是单独给它一路 3.3V且电源脚旁边并一个 100uF 以上的电解电容做储能。接线就四根ESP-01S 的 TX 接 STM32 的 RXRX 接 STM32 的 TXVCC 和 GND 接好。注意 ESP-01S 的 ENCH_PD脚要拉高才能工作很多新手漏了这根线结果发指令毫无反应。# 上电后先用串口助手手动验证模块是否活着 # 波特率默认 115200部分老固件是 9600以实际为准 # 发送 AT # 期望返回 OK # 如果返回乱码先查波特率和供电如果无返回查 EN 脚和 TX/RX 是否接反这一步是整个项目的地基。手动都通不了就别急着写 STM32 代码。我见过太多人跳过这步直接烧程序然后花两天怀疑代码最后发现是 TX/RX 接反。3. AT 指令配置 Wi-Fi 与 MQTT从入网到发布的最小可用流程3.1 入网三连模式、连接、查 IPESP-01S 入网的标准流程是固定的三步顺序不能乱。先设工作模式为 Station再连 AP最后查 IP 确认拿到地址。ATCWMODE1 # 设为 Station 模式 ATCWJAPssid,pass # 连接热点成功返回 WIFI GOT IP ATCIPSTA? # 查询本机 IP确认入网成功ATCWMODE1里的 1 表示 Station2 是 AP3 是共存。做设备端一律用 1。ATCWJAP是最容易超时的一条热点信号弱或密码错都会卡在这里返回CWJAP:3之类的错误码。建议在代码里给这条指令留足 10 秒以上超时并且失败后重试。3.2 MQTT 参数配置把五元组填对支持 MQTT 的 AT 固件配置流程一般是先设用户参数再设连接参数最后发起连接。核心是这几个参数客户端 ID、用户名、密码、 broker 地址和端口。ATMQTTUSERCFG0,1,client_id,username,password,0,0, # 参数依次链路号,方案(1MQTT),客户端ID,用户名,密码,证书使能,加密套件,CA ATMQTTCONN0,broker.example.com,1883,0 # 参数链路号,服务器地址,端口(1883),是否自动重连(0关1开)ATMQTTUSERCFG里客户端 ID 必须唯一如果两台设备用同一个 ID后连的会把先连的踢下线现象是「设备轮流掉线」这个坑很隐蔽。ATMQTTCONN的最后一个参数建议设 1 开启自动重连能省掉一部分断线恢复的代码但别完全依赖它后面避坑章节会讲为什么。3.3 发布与订阅主题格式和 QoS 选择连接成功后发布数据用ATMQTTPUB订阅用ATMQTTSUB。ATMQTTPUB0,device/001/data,1,0,0 # 参数链路号,主题,QoS,retain,是否透传 # 发送后模块返回 提示符此时再发实际数据 {temp:25.3,hum:60}QoS 选 0 是「最多一次」发出去不管到没到最省资源QoS 1 是「至少一次」会等确认适合不能丢的数据。小数据量场景我一般用 QoS 0配合应用层自己做序号比协议层重传更可控。主题命名建议带设备唯一标识比如device/{sn}/data方便后台按设备分流。3.4 STM32 侧串口收发框架STM32 这边要做的是发指令、等返回、判断成功失败。推荐用「发送后阻塞等待关键字」的简单模型配合超时。// 简化示例发送 AT 指令并等待期望关键字 // 实际项目建议用环形缓冲区 空闲中断接收 uint8_t esp_send_cmd(const char *cmd, const char *expect, uint32_t timeout_ms) { uart_send_string(cmd); // 通过 UART 发出指令注意补 \r\n uart_send_string(\r\n); return uart_wait_for(expect, timeout_ms); // 在超时内等待期望字符串 } // 调用示例 if (esp_send_cmd(ATCWMODE1, OK, 2000) 0) { // 入网成功继续下一步 }uart_wait_for的实现要点用接收中断把数据塞进缓冲区主循环里比对是否出现期望关键字。超时时间按指令类型给——AT给 500msATCWJAP给 10000msATMQTTCONN给 5000ms。超时太短会误判失败太长会拖慢整体流程。注意ESP-01S 默认回显是开的你发的指令会被原样回显一遍。如果uart_wait_for匹配的是 OK回显里恰好没有 OK 就没问题但如果匹配的是指令本身就会误判。稳妥做法是先ATATE0关回显。4. 数据发布与断线重连让链路能长期跑下去4.1 发布数据的完整时序一次完整的数据发布时序是这样的确认 MQTT 已连接 → 发ATMQTTPUB→ 等提示符 → 发实际 payload → 等OK。任何一步超时都要走异常处理。// 发布一条 JSON 数据的流程 if (mqtt_is_connected()) { if (esp_send_cmd(ATMQTTPUB0,\device/001/data\,0,0,0, , 3000) 0) { uart_send_string({\temp\:25.3}\r\n); // 发 payload if (uart_wait_for(OK, 3000) 0) { // 发布成功 } } }这里有个细节payload 后面要跟\r\n吗取决于固件实现有的固件按长度收有的按换行收。最稳的办法是查你手上固件的 AT 手册或者手动测一次看返回。别照搬网上的例子固件版本不同行为会变。4.2 心跳与保活为什么连接会「静默死亡」MQTT 协议本身有 keepalive 机制客户端要定期发 PING 包。AT 固件一般会自动处理但如果你的设备长时间不发数据某些 broker 会主动断开。现象是设备看起来还在线但发数据没反应。常见做法是应用层加一个心跳比如每 60 秒发一条状态数据既保活又能上报设备在线状态。这比依赖协议层 keepalive 更直观后台也容易判断设备死活。4.3 断线检测与重连状态机重连逻辑是这套方案里最考验功底的部分。我的做法是维护一个连接状态机分四个状态Wi-Fi 未连接、Wi-Fi 已连但 MQTT 未连、MQTT 已连、异常恢复中。typedef enum { WIFI_DOWN, WIFI_UP_MQTT_DOWN, MQTT_UP, RECOVERING } conn_state_t; void conn_state_machine(void) { switch (state) { case WIFI_DOWN: if (wifi_connect() 0) state WIFI_UP_MQTT_DOWN; break; case WIFI_UP_MQTT_DOWN: if (mqtt_connect() 0) state MQTT_UP; break; case MQTT_UP: if (publish_failed_times 3) state RECOVERING; // 连续失败触发恢复 break; case RECOVERING: mqtt_disconnect(); wifi_reconnect(); state WIFI_DOWN; // 从头再来 break; } }关键点是不要只重连 MQTTWi-Fi 也可能掉。很多翻车案例是 Wi-Fi 断了但代码只重连 MQTT结果永远连不上。恢复时从 Wi-Fi 层重新走一遍最稳。重连要有退避比如失败后等 5 秒再试别疯狂重试把模块搞死。5. 避坑与排查那些让数据发不出去的隐蔽问题5.1 现象AT 指令全部超时模块毫无反应原因通常是三类供电不足导致模块反复复位、EN 脚没拉高、TX/RX 接反。排查顺序是先量 VCC 电压在发指令瞬间是否跌落再查 EN 脚电平最后对调 TX/RX 试一次。供电问题最容易被忽略因为空载电压正常一发数据就掉。5.2 现象Wi-Fi 连上了但 MQTT 一直连不上先确认 broker 地址和端口对不对再确认客户端 ID 是否重复。如果 broker 要求用户名密码ATMQTTUSERCFG里填错也会连不上。还有一个隐蔽点某些网络环境屏蔽了 1883 端口换成 8883 或别的端口试试。排查时用ATMQTTCONN?查连接状态比瞎猜快。5.3 现象数据发出去了后台收不到先确认主题topic拼写完全一致MQTT 主题是大小写敏感的device/001/data和Device/001/data是两个主题。再确认后台订阅的主题有没有通配符匹配对。最后看 QoS如果后台订阅的是 QoS 1 而设备发的是 QoS 0某些 broker 行为会有差异。逐层用工具验证别在设备端死磕。5.4 现象跑几小时后掉线重启就好这是典型的资源泄漏或心跳缺失。检查两点一是接收缓冲区有没有溢出没清二是有没有定期发心跳。长时间运行还要注意 STM32 侧的串口接收中断有没有丢数据建议加一个溢出计数跑一段时间看计数是否增长。5.5 现象一发数据模块就复位几乎可以断定是供电问题。ESP-01S 发射瞬间电流冲高电源带不动就复位。解决办法是单独供电、加大电容、缩短电源线。别想着在软件上绕过去这是硬件问题。6. 进阶技巧把 AT 指令链路做得更省心6.1 用透传模式减少指令开销如果数据格式固定、频率高可以考虑进入透传模式ATCIPMODE1配合ATCIPSEND之后串口发什么就传什么省掉每次拼 AT 指令的开销。但透传模式下你失去了对连接的细粒度控制断线检测要靠模块的提示适合链路已经非常稳定的场景。我的建议是先用指令模式跑通稳定后再评估要不要转透传。6.2 指令重试与幂等设计网络抖动是常态指令失败要重试但重试要幂等。比如ATMQTTPUB重试可能导致同一条数据发两次后台要能去重。做法是在 payload 里带一个自增序号后台按序号去重。这个习惯在数据敏感场景里能救命。6.3 用日志把黑匣子变透明调试期一定要把每条 AT 指令的发送和返回都打到日志里带时间戳。出问题时看日志能立刻定位是卡在哪条指令。我一般会留一个环形日志缓冲区设备跑飞后复位还能读出最后几十条记录比在线调试器还管用。6.4 一个验证链路是否健康的小技巧定期发一条带运行时长和重连次数的状态数据到单独的主题比如device/001/status。后台看这个主题就能判断设备是否健康、重连是否频繁。重连次数持续增长说明链路有问题比等用户报障主动得多。这套方案我前后在几个小项目里用过最大的教训是别信「连上就完事」真正花时间的是断线恢复和长期稳定性。把状态机写扎实、把日志留好、把供电做足这三件事做到位ESP-01S 这种几块钱的模块也能跑得很稳。希望帮到你。本文还有配套的精品资源点击获取