MQTT5 物联网网关
串口转 MQTT v5.0 物联网网关 —— TLS 加密传输、HTTPS 网页配置、mDNS 免 IP 直连、OTA 远程升级。无需串口调试助手,浏览器即可完成全部配置。
产品概述
MQTT5 物联网网关是一款基于 ESP32-S3 的量产级「串口 ↔ MQTT」透传网关。设备通过 UART 串口与外部 MCU(如 STM32)对接,把 MCU 的传感器数据、控制指令桥接到 MQTT v5.0 云端;同时为网关自身与下游 MCU 提供完整的 远程升级、网页配置与运行监控能力。
网关内建 HTTPS 管理后台(esp_https_server + mbedTLS),工程师在浏览器里即可完成 broker 地址、TLS 证书、Topic、串口参数、项目名等全部配置,并把配置导出为加密 .bin 文件用于批量量产烧录。
核心规格
| 项目 | 说明 |
|---|---|
| 主控芯片 | ESP32-S3 |
| 软件框架 | ESP-IDF v6.1-rc1 + mbedTLS 4.x |
| 通信协议 | MQTT v5.0,支持 mqtts://(TLS 加密) |
| 对接接口 | UART1(业务 AT 通道)+ UART2(主控故障回溯) |
| 配置方式 | HTTPS 网页后台 / AT 命令 / 配置文件导入导出 |
| 升级能力 | MQTT OTA · WEB OTA · 外部 MCU 固件中转(SOTA) |
核心亮点
下面四个能力,是这台网关区别于普通「串口转网模块」的地方——把传统要用串口助手、抓包工具、多个软件才能完成的事,收敛到一个浏览器页面里。
免串口调试,浏览器直接收发 MQTT
传统调试要:接 USB-TTL → 开串口助手 → 记 AT 命令 → 手抄报文。我们把这段链路直接搬进网页:填 Topic、填报文,点发送,收发记录与自动生成的 MCU 串口 AT 帧同屏呈现。
- 双栏联动:左侧填业务报文,右侧实时给出对应的串口 AT 帧,MCU 侧照抄即可跑通。
- 收发留痕:下行 / 上行报文带时间戳与字节数留档,联调不再靠猜。
- 零工具依赖:无需安装串口调试助手,产线、售后、客户现场只用一个浏览器。

可视化监控,把「网络卡」变成可测量的数据
被动救火和主动维护的差别,就是「有没有人先看到」。证书剩余天数、DRAM / PSRAM 空闲、MQTT 收发速率与 RTT 延时,全部在系统概览里做成实时曲线,异常一眼可见。
- MQTT 健康九宫格:连接状态、重连次数、发送失败、TLS 握手耗时、收发缓存水位一屏尽览。
- 通信分析环形图:按主题统计收发分布、按命令类型统计 MCU 串口交互频次。
- 存储占用仪表:PSRAM 与 Flash 使用率圆环显示。
- 亮暗主题:夜间值守与强光现场同样清晰,一键切换。

双路 OTA 升级,模组自己升级,还能替外部 MCU 升级
网关不只是「自己会升级」——它还能当外部 MCU 的升级中转站。模组固件走 MQTT OTA / WEB OTA,MCU 固件则经 RSA-3072 + AES-256-GCM 预加密后沿串口分包下发,两条链路互不干扰。
- MQTT OTA:远程下发模组固件,断点续传、落盘校验,现场零接触。
- WEB OTA:浏览器直接上传固件,边收边落盘,不占额外 RAM。
- MCU 固件中转:加密固件经串口分包透传,MCU 侧解密后自更新,硬件无需改动。
- 硬件加速:签名校验与解密全程走硬件外设,升级期间不影响业务串口。

HTTPS 安全后台,私钥不出设备
管理后台全程 HTTPS(端口 443),ECDSA P-256 密钥对在设备本地生成(PSA 加密引擎),私钥不出设备;配套单用户互斥会话与统一鉴权,安全与易用兼顾。
- 证书本地生成:有效期 10 年,IP 变更自动重签,损坏自动重建。
- 单用户互斥:64 位令牌、30 分钟超时、活动刷新,防局域网 DDoS。
- 安全时间校准:加密 HTTPS 通道校时(非明文 NTP),防中间人篡改。
- 证书到期预警:30 天黄 / 7 天红,提前续签避免批量掉线。

界面实拍
以下均为设备真实运行的 Web 控制台截图,非示意图、非设计稿。

安全登录
HTTPS 加密页面,账号密码 + 64 位会话令牌,单用户互斥登录。

通信分析
按主题统计收发消息分布、按命令类型统计 MCU 串口交互频次。

MQTT 健康九宫格
连接状态、重连次数、TLS 握手耗时、收发缓存水位一屏尽览。

WiFi 配置
扫描周边热点直接选择,或手机一键 SmartConfig 配网。

Broker 接入
内置根证书包,公共 CA 证书直接连 mqtts,无需上传证书。

订阅 Topic(下行)
配置云→设备的订阅主题,完全匹配校验,防 Topic 伪造注入。

上行 Topic
可选登记设备→云的上行主题,MCU 侧也可用 AT 命令自带 Topic 发布。

配置备份
一键导出全部配置,故障机快速恢复,避免现场逐项重填。

账户管理
修改后台登录凭据,配合会话超时策略降低局域网风险。

Broker 状态
证书剩余天数、链路质量实时可见,30 天黄 / 7 天红告警。
快速上手
把设备接上云有两条路:串口 AT 命令是传统做法 —— 接 USB-TTL、开串口助手、逐条敲命令;Web 后台免串口是本产品的主打方式 —— 上电配网后,浏览器直连设备,配置 Broker、配置 Topic、收发报文测试一气呵成,全程不需要串口线,也不需要安装任何调试软件。
两条路线配置结果完全一致,可随时交叉使用:先用网页把设备调通,再把网页自动生成的 AT 命令帧抄进 MCU 程序即可。
先选路线
| 对比项 | 路线 A · 串口 AT 命令 | 路线 B · Web 后台免串口 |
|---|---|---|
| 所需工具 | USB-TTL 模块 + 串口助手 + 线缆 | 一个浏览器(电脑 / 手机均可) |
| 需要记忆 | 命令格式、参数顺序、CRC32 | 表单填写,下拉可选 |
| 凭据出现位置 | 串口明文、PC 工具历史、MCU 代码 | 浏览器直写设备内部存储 |
| Topic 配置 | 命令拼接,容易写错 | 页面登记,完全匹配校验 |
| 收发测试 | 手敲报文、人工比对 | 网页收发同屏,自动生成 MCU 侧 AT 帧 |
| 批量复制 | 逐台重发命令 | 导出加密 .bin,量产导入 |
| 适合场景 | MCU 未就绪、现场只有串口条件 | 首次配置、现场调试、售后维护、量产复制 |
共同前置:供电与配网
- 按产品引脚定义给模块供电,上电后等待约 10 秒完成初始化。
- 首次使用或更换 WiFi 时配网:手机接入 2.4 GHz 网络,用厂商配网 App(或 ESP 官方
ESP SoftAP Provisioning)广播凭据。 - 已配过网的设备上电自动重连,无需重复操作。
也可通过串口命令手动触发 SmartConfig:
AT+CWSTARTSMARTCRLF // 启动 SmartConfig 智能配网 → OK CRLF
// 配网成功后自动停止;也可手动停止:
AT+CWSTOPSMARTCRLF // 停止 SmartConfig → OK CRLF
配网期间会暂停 MQTT 事件处理,配网成功后自动恢复。仅支持 2.4 GHz 频段。
路线 A串口 AT 命令(传统方式)
适合 MCU 程序尚未就绪、或现场只有串口条件的场合。需要一块 USB 转串口模块与任意串口助手。
A1 · 硬件准备与接线
所需硬件
- 一块 MQTT5 物联网网关(ESP32-S3 模组)
- USB 转串口模块(4 线:VCC / GND / TX / RX)
- 可联网的 WiFi(2.4 GHz)
串口引脚定义
| 串口 | 方向 | GPIO | 默认参数 | 用途 |
|---|---|---|---|---|
| UART1 | TX(网关 → MCU) | GPIO 1 | 230400, 8N1 (可配 1200 ~ 4,000,000) | 业务 AT 通道,UHCI DMA 自动收发 |
| UART1 | RX(MCU → 网关) | GPIO 2 | ||
| UART2 | RX-only | GPIO 8 | 230400, 8N1 | 主控 HardFault 故障回溯「落发」通道 |
UART1 需与外部 MCU 的 TX/RX 交叉接线(网关 TX 接 MCU RX,网关 RX 接 MCU TX),且两侧波特率、校验位、停止位必须一致。
A2 · 串口助手参数设置
- 波特率 230400,8 位数据位、无校验、1 位停止位(8N1)。
- 发送区设为「新行模式」,每条命令以
CR-LF结束。 - 字符串参数用双引号包裹,特殊字符(
,、"、\)需转义;单条命令建议不超过 256 字节。
串口参数可在 Web 后台「通讯设置」页调整为 1200 ~ 4,000,000,与 MCU 侧保持一致即可。
A3 · 用 AT 命令配置与收发
ATCRLF // 通讯测试 → 收到 +AT:OK CRLF
AT+CIPSTA?CRLF // 查询 Station IP / 网关 / 子网掩码
AT+MQTTCONN?CRLF // 查询连接状态,5 = 已连接已订阅(可发送)
AT+MQTTPUB=0,"my_project/<设备ID>/telemetry/data",0,0,15000,3610A686,5,"hello"CRLF
// 发布消息 → 收到 +MQTTPUB:OK / FAIL / BUSY
// 云端下发由模块主动推送(无需查询),模块发出的一行同样以 CRLF 收尾:
+MQTTSUBRECV:0,1,"my_project/<设备ID>/ctrl_set",B00506B6,8,"relay_on"CRLF
完整命令定义与逐字节 HEX 对照见 AT 命令参考。
A4 · 这条路线的局限
- 依赖 PC 端软件:要装驱动、串口助手,换台电脑就得重来一遍。
- 命令靠记忆:参数顺序、引号转义、CRC32 都要手工处理。
- 凭据明文暴露:Broker 账号密码以明文出现在串口线与 PC 工具里,实践中还常被写进 MCU 代码和交付文档。
- 批量复制困难:每台设备都要重发一遍命令。
下面这条路线,就是为消除这些麻烦而设计的。
路线 BWeb 后台免串口(推荐)
配网完成后,剩下的事全部在浏览器里做完:登录后台 → 配置 Broker → 配置 Topic → 收发报文测试。不用接串口、不用装驱动和调试助手,产线、售后、客户现场只用一个浏览器。
设计思路:把「配置与调试」收进设备本身
传统串口模块把配置能力放在 PC 侧:模块只是执行者,所有信息都要经过串口线在 PC 与模块之间来回搬运。本产品反过来做 —— 模块自身就是一个 HTTPS 服务器,配置界面、收发测试、状态监控全部由设备提供,浏览器只充当显示器与键盘。
这个设计带来四个直接结果:
- 零工具依赖:不接串口线、不装驱动与串口助手,电脑、平板、手机都能配。
- 点对点直连:浏览器 ↔ 设备 一条 TLS 加密链路,配置数据不经过任何云端或第三方平台中转。
- 机密信息不出芯片:Broker 地址与账号密码直接写进设备内部存储;HTTPS 证书私钥在设备本地生成、永不导出(详见 为什么「机密信息不出芯片」)。
- 配置即文档:网页发一帧,同时给出 MCU 侧要发的 AT 命令帧,联调不用再靠抄和猜。
B1 · 浏览器直连设备
设备联网后自动注册 mDNS 服务 _https._tcp,浏览器直接访问即可,无需记忆 IP:
https://<设备主机名>.local/
主机名由设备自动生成,出厂铭牌与控制台首页均有标注,无需手工推算。若 mDNS 不可用,可用串口 AT+CIPSTA? 查询 IP 后访问 https://<设备IP>/;首次访问提示自签名证书时选择「继续访问」。
B2 · 登录 HTTPS 管理后台

输入管理密码登录。后台采用单用户互斥会话:同一时间仅允许一个活跃会话,第二个用户登录会提示「已有其他用户登录」;会话 30 分钟无操作自动超时,每次操作自动续期。
B3 · 配置 Broker
在「MQTT 配置」页填写接入信息,保存后自动生效并重新连接:
| 参数 | 说明 |
|---|---|
| Broker URI | 支持 mqtt:// 与 mqtts://(TLS 加密)。端口范围 1–65535。 |
| 用户名 / 密码 | 支持空密码(可连接公开 Broker)。 |
| TLS 证书 | 服务器证书校验走内置根证书包(Let's Encrypt 根已包含)。 |
| Client ID | 设备自动生成并声明,无需手工填写;具体格式属安全设计细节,不在公开文档展开。 |
| 项目名 | 第一级 Topic 前缀,自定义英文短名(如 my_project),最长 31 字符。 |

B4 · 配置 Topic
Topic 管理拆成两页:订阅(云 → 设备,必配)与上行(设备 → 云,可选)。


- 订阅:按
{产品名}/{设备ID}/{分支}完全匹配,云端下发经+MQTTSUBRECV送达 MCU。 - 上行:登记后可在网页直接发布测试;也可留空,由 MCU 侧
AT+MQTTPUB自带 Topic 发布。
B5 · 收发报文测试(免串口调试)
最省时间的一步:不接串口、不写 MCU 代码,在网页里直接发一条业务报文,立刻能确认云端是否收到;云端下发也在同屏可见。


- 双栏联动:网页填业务报文,同步给出 MCU 侧对应的串口 AT 帧,照抄即可跑通。
- 收发留痕:报文带时间戳与字节数留档,便于回溯与对账。
- 闭环验证:网页发 → 云端收 → 云端下发 → 网页显示,一条链路一次跑完。
- 首次调通时间:从小时级压缩到分钟级。
B6 · 导出配置,量产复制
配置完成后导出加密 .bin 配置文件,量产烧录时批量导入,实现「一次配置、万台复制」。故障机也可用备份文件快速恢复,无需现场逐项重填。
先用路线 B 在网页里把设备调通(配置、Topic、收发测试一次做完),再把网页生成的 AT 命令帧抄进 MCU 程序 —— 两步合一,比纯串口方式省掉大量试错。
为什么「机密信息不出芯片」
「免串口」带来的不只是方便,更是安全模型的改变:配置与调试不再借道 PC 与串口线,机密数据从输入到落盘全程留在设备内部闭环。
| 环节 | 传统串口 AT 方式 | 本产品 Web 后台方式 |
|---|---|---|
| 配置入口 | PC 串口助手 / 上位机软件 | 设备自带 HTTPS 后台,浏览器直连 |
| 数据路径 | 串口线 ↔ PC,明文、可被抓包,工具还会留下历史 | 浏览器 ↔ 设备,TLS 点对点加密,不经任何第三方服务器 |
| 凭据落地 | 常写进 MCU 代码、AT 脚本、交付文档 | 直接写入设备内部存储,MCU 侧无需保存凭据 |
| 后台私钥 | 证书私钥需随固件或服务器分发 | 设备本地生成(ECDSA P-256 / PSA 加密引擎),不可导出 |
| 第三方依赖 | 驱动、串口工具,部分方案还要过云端配置平台 | 无,设备不依赖任何外部服务即可完成配置 |
| 残留风险 | 串口日志、截图、工具历史泄露凭据 | 只剩后台登录口令本身需要保管 |
它指的是:配置与凭据不经由任何第三方服务器或云端中转,HTTPS 私钥在设备内生成且不可导出;设备出厂已默认启用芯片级 Flash 加密与 Secure Boot(安全启动),固件与配置以密文形式存储于 Flash,抵御非法读取与固件篡改。设备自身仍由后台登录口令保护,请按路由器管理密码的级别保管。串口仅在对接 MCU 时才需要接。
配置完成后:对接外部 MCU
网关通过 UART1 向 MCU 提供标准 AT 命令接口。MCU 侧按以下流程即可收发 MQTT 报文:
- 上电后轮询连接状态:
AT+MQTTCONN?CRLF,仅当返回状态5(已连接已订阅,空闲)时才下发数据。 - 发布消息:
AT+MQTTPUB=0,"topic",qos,retain,timeout,CRC32,dataLen,"data"CRLF。 - 接收云端下发:模块主动推送
+MQTTSUBRECV:...CRLF。 - 报文中转 MCU 固件:
AT+FMWOTALLCRLF分包输出,供 MCU 校验写入 Flash。
每一条命令都必须以 CR + LF(0x0D 0x0A)结尾,否则网关回 ERROR 且不执行;详见 结束符 CRLF。
完整的命令定义见 AT 命令参考。
状态 5 表示「已连接且已订阅(空闲)」,只有此状态下发送才能保证不丢消息;状态 6(发送中)直接下发会被模块拒绝并返回 +MQTTPUB:BUSYCRLF(15 字节 2B 4D 51 54 54 50 55 42 3A 42 55 53 59 0D 0A)。
AT 命令参考
网关通过 UART1 向外部 MCU 提供标准 AT 命令接口,MCU 据此收发 MQTT 报文、查询状态、触发配网与固件中转。本章所有示例都对每一行报文给出完整字节:ASCII 形式用 CRLF 标记行尾两个字节,并附对应的十六进制(HEX)序列,可直接在串口工具的十六进制视图里逐字节核对。
基本约定
| 类型 | 命令格式 | 说明 | 成功响应 |
|---|---|---|---|
| 查询命令 | AT+<命令>? | 返回当前参数值 | 仅返回数据行(不带 OK) |
| 设置命令 | AT+<命令>=<…> | 设置参数并运行 | OKCRLF |
| 执行命令 | AT+<命令> | 运行无参数命令 | OKCRLF |
| 测试命令 | AT+<命令>=? | 查询参数取值范围 | 视具体命令而定 |
- 串口默认 230400, 8N1,可在 Web「通讯设置」页配置(1200 ~ 4,000,000)。
- 每一条命令的最后两个字节必须是 CR(0x0D)+ LF(0x0A),顺序不可颠倒——这是硬性要求,详见 结束符 CRLF。
- 网关发回的每一行同样以 CR + LF 收尾,MCU 用 DMA + IDLE 不定长接收时可直接按 CRLF 分帧。
- 查询类命令(
?)一次性返回全部数据、不再单独回OK;设置/执行类回OKCRLF;失败一律回ERRORCRLF。 - 字符串参数用双引号包裹,数据内容中可含引号(如 JSON),网关从行尾反向定位闭合引号。
- 单条命令长度建议不超过 256 字节。
结束符 CRLF:最容易踩的坑
网关要求每条 AT 命令的最后两个字节必须是 0x0D(回车 CR)和 0x0A(换行 LF)。只发 \n、只发 \r、或者什么都不发,都不符合要求。
固件在解析命令前先校验末尾两字节,不匹配就整行丢弃并返回 ERRORCRLF,命令不会被执行。唯一例外:若此刻网关正在发送上一条 AT+MQTTPUB 且该行以 AT+MQTTPUB 开头,则返回 +MQTTPUB:BUSYCRLF,便于上位机区分「忙」与「格式错」。
为避免歧义,本页所有报文示例统一用 CRLF 徽标代表这两个字节。例如下面这条命令:
也就是说,AT+GMR? 这 7 个可见字符,实际要发出 9 个字节:
| 字节序 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 字符 | A | T | + | G | M | R | ? | CR | LF |
| 十六进制 | 41 | 54 | 2B | 47 | 4D | 52 | 3F | 0D | 0A |
网关的响应同样以 CRLF 结尾——例如 OKCRLF 实际发出的四个字节是 4F 4B 0D 0A,ERRORCRLF 是 45 52 52 4F 52 0D 0A。
凡是串口上发出或收到的完整一行,末尾都会标上这个徽标,代表该行以 0x0D 0x0A 结束。因此命令侧和响应侧都会出现 CRLF:命令侧表示「这两个字节必须跟着命令一起发出去」,响应侧表示「网关回来的这行以此收尾」。命令列表、速查表、流程示例中的完整报文一律遵循此约定,无一例外。
如何在你的工具里发出 CRLF
| 使用场景 | 正确做法 |
|---|---|
| 串口调试助手 (SSCOM / XCOM 等) |
勾选发送区的「发送新行」「加回车换行」或「\r\n」复选框(各家叫法不同)。勾选后输入框只填 AT+GMR?,点发送时工具自动追加 CRLF。注意:少数工具的「发送新行」只追加 \n,务必开「十六进制显示」确认收发区出现 0D 0A。 |
| 单片机 C 代码 (STM32 / STC 等上位机) |
把 \r\n 写进字符串末尾:uart_send("AT+GMR?\r\n"); 或 printf("AT+GMR?\r\n");。切勿只写 "AT+GMR?\n"。 |
| Python / 上位机脚本 | ser.write(b"AT+GMR?\r\n")——注意是 bytes 字面量,且 \r\n 必须连写。 |
| 不勾选、纯手输 | 在命令末尾手动敲一次回车键(Enter)。多数串口工具的 Enter 即等价于 CRLF;若工具配置了「回车发送」,直接回车即可。 |
接好串口后先发一条 ATCRLF(4 字节 41 54 0D 0A)。若立刻收到 +AT:OKCRLF(8 字节 2B 41 54 3A 4F 4B 0D 0A),说明波特率、接线、结束符三者都正确;若只收到 ERRORCRLF,八成是没带 CRLF,其次才是波特率或 TX/RX 接反。
支持的命令列表
模块上电后发送 +CMD:<index>,"<name>",<execute>,<query>,<set>CRLF 列表(每条一行),也可用 AT+CMD?CRLF 查询。
| 序号 | 命令 | 执行 | 查询 | 设置 | 用途 |
|---|---|---|---|---|---|
| 0 | AT | ✓ | 通讯测试 | ||
| 1 | AT+RST | ✓ | 重启模块 | ||
| 2 | AT+GMR | ✓ | 查看版本 | ||
| 3 | AT+CMD | ✓ | 查询命令列表 | ||
| 4 | AT+SYSTIMESTAMP | ✓ | ✓ | 系统时间戳 | |
| 5 | AT+CIPSTAMAC | ✓ | ✓ | Station MAC | |
| 6 | AT+FUID | ✓ | Flash 唯一 ID | ||
| 7 | AT+CIPSTA | ✓ | ✓ | Station IP | |
| 8 | AT+CWSTARTSMART | ✓ | 启动 SmartConfig | ||
| 9 | AT+CWSTOPSMART | ✓ | 停止 SmartConfig | ||
| 10 | AT+MQTTCONN | ✓ | ✓ | MQTT 连接状态 | |
| 11 | AT+MQTTPUB | ✓ | 发布消息 | ||
| 12 | AT+MQTTRECV | ✓ | 按索引重传报文 | ||
| 13 | AT+MQTTRECVMSG | ✓ | 按 msg_id 重传 | ||
| 14 | AT+FMWOTALL | ✓ | 串口输出固件 |
短命令 HEX 速查(发送侧)
下表给出每条短命令含结束符的完整字节序列,共 N 个字节。调试时可直接粘进串口工具的「十六进制发送」,或在逻辑分析仪里对照抓到的波形。
| 命令(ASCII) | 字节数 | 十六进制序列(HEX) |
|---|---|---|
ATCRLF | 4 | 41 54 0D 0A |
AT+RSTCRLF | 8 | 41 54 2B 52 53 54 0D 0A |
AT+GMR?CRLF | 9 | 41 54 2B 47 4D 52 3F 0D 0A |
AT+CMD?CRLF | 9 | 41 54 2B 43 4D 44 3F 0D 0A |
AT+SYSTIMESTAMP?CRLF | 18 | 41 54 2B 53 59 53 54 49 4D 45 53 54 41 4D 50 3F 0D 0A |
AT+FUID?CRLF | 10 | 41 54 2B 46 55 49 44 3F 0D 0A |
AT+CIPSTAMAC?CRLF | 15 | 41 54 2B 43 49 50 53 54 41 4D 41 43 3F 0D 0A |
AT+CIPSTA?CRLF | 12 | 41 54 2B 43 49 50 53 54 41 3F 0D 0A |
AT+CWSTARTSMARTCRLF | 17 | 41 54 2B 43 57 53 54 41 52 54 53 4D 41 52 54 0D 0A |
AT+CWSTOPSMARTCRLF | 16 | 41 54 2B 43 57 53 54 4F 50 53 4D 41 52 54 0D 0A |
AT+MQTTCONN?CRLF | 14 | 41 54 2B 4D 51 54 54 43 4F 4E 4E 3F 0D 0A |
AT+MQTTCONNCRLF | 13 | 41 54 2B 4D 51 54 54 43 4F 4E 4E 0D 0A |
AT+MQTTRECV=1CRLF | 15 | 41 54 2B 4D 51 54 54 52 45 43 56 3D 31 0D 0A |
AT+MQTTRECVMSG=12345CRLF | 22 | 41 54 2B 4D 51 54 54 52 45 43 56 4D 53 47 3D 31 32 33 34 35 0D 0A |
AT+FMWOTALLCRLF | 13 | 41 54 2B 46 4D 57 4F 54 41 4C 4C 0D 0A |
常见响应 HEX 速查(接收侧)
| 响应(ASCII) | 字节数 | 十六进制序列(HEX) |
|---|---|---|
OKCRLF | 4 | 4F 4B 0D 0A |
ERRORCRLF | 7 | 45 52 52 4F 52 0D 0A |
+AT:OKCRLF | 8 | 2B 41 54 3A 4F 4B 0D 0A |
+RST:OKCRLF | 9 | 2B 52 53 54 3A 4F 4B 0D 0A |
+MQTTCONN:5CRLF | 13 | 2B 4D 51 54 54 43 4F 4E 4E 3A 35 0D 0A |
+MQTTPUB:OKCRLF | 13 | 2B 4D 51 54 54 50 55 42 3A 4F 4B 0D 0A |
+MQTTPUB:FAILCRLF | 15 | 2B 4D 51 54 54 50 55 42 3A 46 41 49 4C 0D 0A |
+MQTTPUB:BUSYCRLF | 15 | 2B 4D 51 54 54 50 55 42 3A 42 55 53 59 0D 0A |
所有命令都以 0D 0A 收尾;AT 开头固定是 41 54,加号是 2B,问号是 3F,等号是 3D,双引号是 22,逗号是 2C。抓到一个字节流,先找 0D 0A 分帧,再按这几个分隔符切片即可还原字段。
基础命令
AT — 通讯测试
AT+RST — 重启模块
AT+GMR? — 查看版本信息
查询类命令只返回四行数据、不再回 OK,每行各以 CRLF 收尾,MCU 一次 DMA + IDLE 接收即可拿全。
AT+CMD? — 列出全部支持的命令
AT+SYSTIMESTAMP — 查询/设置系统时间戳
<Unix毫秒>:Unix 时间戳(毫秒),设置后系统时间同步更新。设备联网后会通过加密 HTTPS 通道自动校时。
AT+FUID? — 查询 SPI Flash 唯一 ID
该指令用于读出芯片侧的唯一标识,供产线建档与售后追溯使用。设备自身的 client_id 由固件按内部规则派生,无需上位机手工拼接。
WiFi 命令
AT+CIPSTAMAC — 查询/设置 Station MAC
AT+CIPSTA — 查询/设置 Station IP
AT+CWSTARTSMART / AT+CWSTOPSMART — SmartConfig 配网
配网期间暂停 MQTT 事件处理,成功后自动恢复。
MQTT 命令
AT+MQTTCONN? — 连接状态查询
无后缀的执行形式 AT+MQTTCONNCRLF(13 字节 41 54 2B 4D 51 54 54 43 4F 4E 4E 0D 0A)为保留命令,不做实际操作,仅回 OKCRLF。
<state> 取值:
| 值 | 含义 | 可否发布 |
|---|---|---|
| 0 | 未初始化 (INIT) | 否 |
| 1 | 已断开 (DISCONNECTED) | 否 |
| 3 | 等待重连 (WAIT_RECONNECT) | 否 |
| 4 | 已连接但未订阅 | 否 |
| 5 | 已连接且已订阅(空闲,可发送) | 是 |
| 6 | 发送中(繁忙) | 否(回 BUSY) |
固件不会返回状态 2;「已连接但未订阅」归并为状态 4。发布前请只在状态 5 下发。
AT+MQTTPUB — 发布消息
AT+MQTTPUB=<LinkID>,"<topic>",<qos>,<retain>,<timeout_ms>,<CRC32>,<dataLen>,"<data>"CRLF
完整示例(发布 hello 到 my_project/DEV01/telemetry/data,QoS 0、不保留):
返回(注意:不带末尾 OK/ERROR,仅三选一):
| 情况 | 响应(ASCII) | 字节数 | 十六进制序列(HEX) |
|---|---|---|---|
| 成功 | +MQTTPUB:OKCRLF | 13 | 2B 4D 51 54 54 50 55 42 3A 4F 4B 0D 0A |
| 失败(参数或 CRC 校验不过) | +MQTTPUB:FAILCRLF | 15 | 2B 4D 51 54 54 50 55 42 3A 46 41 49 4C 0D 0A |
| 繁忙(上一条仍在发送) | +MQTTPUB:BUSYCRLF | 15 | 2B 4D 51 54 54 50 55 42 3A 42 55 53 59 0D 0A |
参数:
<LinkID>:当前仅支持 0。<"topic">:最大 128 字节,双引号包裹。<qos>:0 / 1 / 2,非法值默认 2。<retain>:0 / 1,非法值默认 1。<timeout_ms>:1000 ~ 60000,默认 15000。<CRC32>:data 内容 CRC32(8 位大写十六进制,不足补零),仅算数据内容本身(不含双引号)。示例数据hello的 CRC32 =3610A686。<dataLen>:data 内容长度(不含双引号)。<"data">:消息体,可含引号(如 JSON),网关从行尾反向定位闭合引号。
算法为 ESP32 ROM CRC32(多项式 0x04C11DB7 反射,等价于常见 zlib / STM32 硬件 CRC32)。结果以大写十六进制填写、不足 8 位前补零(如 0x0C5A1B3E 填 0C5A1B3E)。网关逐包校验,不匹配即回 +MQTTPUB:FAILCRLF。
MCU → 网关: AT+MQTTCONN?CRLF
网关 → MCU: +MQTTCONN:5CRLF // 状态 5 = 空闲,可发送(不回 OK)
MCU → 网关: AT+MQTTPUB=0,"my_project/DEV01/telemetry/data",0,0,15000,3610A686,5,"hello"CRLF
网关 → MCU: +MQTTPUB:OKCRLF // 发布成功
// 若状态为 6(发送中),直接下发会被拒绝:
MCU → 网关: AT+MQTTPUB=0,"my_project/DEV01/telemetry/data",0,0,15000,3610A686,5,"hello"CRLF
网关 → MCU: +MQTTPUB:BUSYCRLF // 模块丢弃该条,上位机应稍后重试
MQTT 接收(主动推送)
+MQTTSUBRECV:<LinkID>,<msg_id>,"<topic>",<CRC32>,<data_length>,"<data>"CRLF
<msg_id>:MQTT 报文 ID(16 位无符号),QoS 0 时为 0。- 模块会去除 JSON 外部空白,使 JSON 紧凑化(如
{ "a" : 1 }→{"a":1})。 "123"(字符串)与123(数字)含义不同,模块原样转发。- 推送行同样以
0D 0A收尾,可直接按 CRLF 分帧。
报文重传(环形缓存 64 条 / 单条最大 10KB)
每条命令独立成行、各自带一个 CRLF;箭头右侧的中文是注释,不是报文内容,实际发送时不要输入。适用场景:CRC 校验失败重取、超时后主动补取。
遗嘱消息(Will Message)
模块连接 Broker 时自动设置遗嘱,异常离线时由 Broker 发布:
| 项 | 值 |
|---|---|
| 主题 | {product_name}/{client_id}/will |
| 内容 | "i will leave"(固定 12 字节) |
| QoS / Retain | 2 / false |
| 延迟 | 断开 3 秒后发布 |
心跳消息(Heartbeat)
连接成功后每 60 秒发送一次,QoS 0:
{"ts":<epoch_milliseconds>,"state":<state_code>}
固件输出命令
AT+FMWOTALL — 串口输出全部固件
将已缓存的从机固件镜像分包输出,供 MCU 校验、解密后写入 Flash。触发命令:
分包包头的 ASCII 字段同样以 CR + LF 收尾,之后紧接 datalen 字节二进制数据。以「第 1 包 / 共 12 包 / CRC32 = A1B2C3D4 / 49152 字节」为例:
| 字段 | 说明 |
|---|---|
| 总包数 / 当前包号 | 分包总数与当前序号(从 1 开始) |
| CRC32 | 当前分包数据 CRC32(esp_rom_crc32_le) |
| datalen | 当前分包数据字节数 |
| 二进制数据 | 分包固件内容,每包最大 48 KB |
包头是 ASCII,可用 0D 0A 定位包头结束;包头解析出 datalen 后,再按字节数收满二进制体即可,不要按 CRLF 去找固件数据的结尾(二进制内容里本来就可能出现 0D 0A)。每包之间有约 10 ms 间隔供接收端处理。
详细分包协议见 OTA 升级。
Topic 定义
设备与云端的 MQTT 通信遵循统一的 snake_case 命名与层级分组规范,所有请求-响应共用统一通道,通过 msgId 关联。
命名规范
- 全小写 + 下划线(snake_case),避免 MQTT 大小写敏感歧义。
- 产品名第一级:
{product_name}作为第一级路径,由用户在配置页自定义。 - 设备标识第二级:
{client_id}由设备自动生成并在连接时声明。 - 动词后置:
_set/_get/_start后缀表示操作类型。 - 层级分组:上行按语义分为
telemetry/、event/、alarm/与顶级hardfault。
完整 Topic 前缀格式:{product_name}/{client_id}/…
Topic 总览
网关提供一组平台级通用能力,覆盖数据采集、设备控制、参数管理、故障上报与固件升级。下表为能力清单——具体业务语义(如采集什么、控制什么)由接入的 MCU 侧定义,网关仅负责可靠传输。
{product_name}/{client_id}/
├── data_get / data_set # 下行: 数据采集 / 写入
├── ctrl_set / ctrl_get # 下行: 设备控制 / 状态查询
├── mode_set # 下行: 运行模式切换
├── param_get / param_set # 下行: 系统参数查询 / 设置
├── bus_cfg_get / bus_cfg_set # 下行: 总线(Modbus 等)配置
├── task_set / task_get # 下行: 任务下发 / 状态查询
├── fw_update # 下行: 从机固件升级
├── response # 上行: 下行指令统一响应
├── telemetry/… # 上行: 周期数据上报
├── alarm/… # 上行: 故障上报
├── event/… # 上行: 事件通知
├── heartbeat # 上行: 保活心跳
├── will # 上行: 遗嘱消息
└── hardfault/{target} # 上行: 主控严重故障回溯
上述 Topic 可按项目需要启用子集。设备端对未启用的 Topic 不下发、不订阅,因此实际可用清单以项目配置为准。
下行 Topic(云 → 设备)
| Topic | 功能 | 携带 msgId | QoS |
|---|---|---|---|
data_get | 读取采集数据 | ✅ | 2 |
data_set | 写入数据 / 设定值 | ✅ | 2 |
ctrl_set | 设备控制(开关 / 定量) | ✅ | 2 |
ctrl_get | 查询控制通道当前状态 | ✅ | 2 |
mode_set | 运行模式切换(手动 / 自动) | ✅ | 2 |
param_get | 系统参数查询 | ✅ | 2 |
param_set | 系统参数设置 | ✅ | 2 |
bus_cfg_get | 总线配置查询(Modbus 等) | ✅ | 2 |
bus_cfg_set | 总线配置设置(Modbus 等) | ✅ | 2 |
task_set | 任务下发(含目标参数校验) | ✅ | 2 |
task_get | 任务状态 / 计划查询 | ✅ | 2 |
fw_update | 从机固件 OTA(网关本地处理,不转发 MCU) | ✅ | 2 |
上行 Topic(设备 → 云)
| Topic | 功能 | msgId | QoS | 触发方式 |
|---|---|---|---|---|
response | 下行指令统一响应 | ✅ 原样返回 | 2 | 收到下行指令后 |
telemetry/data | 采集数据周期上报 | ❌ | 0 | 定时周期 |
telemetry/ctrl | 控制通道状态周期上报 | ❌ | 0 | 定时周期 |
telemetry/task | 任务执行进度周期上报 | ❌ | 0 | 任务期间周期上报 |
alarm/system | 系统级故障(重启、看门狗、传感器离线等) | ❌ | 1 | 事件驱动 |
alarm/task | 任务故障(材料不足、溢出、超时等) | ❌ | 1 | 事件驱动 |
event/ctrl | 控制执行完毕通知 | ❌ | 0 | 执行完毕后 |
event/status | 设备状态变更(模式切换、配网完成等) | ❌ | 0 | 事件驱动 |
heartbeat | 保活心跳 | ❌ | 0 | 60 秒周期 |
will | 遗嘱(异常离线通知) | ❌ | 2 | Broker 自动发布 |
hardfault/{target} | 主控严重故障回溯 | ❌ | 2 | 从机严重故障触发 |
event 与 alarm 分支
event(事件通知)
- 用途:正常业务流程中的状态通知,用于云端记录与监控。
- QoS 0(尽力送达),丢失不影响业务。
- 子分支:
event/ctrl(执行完毕)、event/status(状态变更)。
alarm(故障/错误)
- 用途:异常情况上报,需要云端关注和处理。
- QoS 1(至少送达一次),确保故障不丢失。
- 子分支:
alarm/system(系统级)、alarm/task(任务执行异常)。
hardfault 主控严重故障上报
hardfault 是独立顶级 Topic,不属于 alarm/ 或 event/。当从机主控发生严重故障(HardFault、总线错误、内存越界等)时:
- 业务通道与 RTOS 均不可用,无法持久化落盘;
- 从机仅能通过独立的调试串口直接输出回溯信息;
- 网关经专用接收通道获取后,以 QoS 2 + Retain 1 发布到云端;
- 发布完成后关闭 MQTT 通信:延时停止心跳 → 停止互联网探测 → 主动断开客户端;
- 此后设备不再主动操作(含重连),必须人工现场断电重启。
这是刻意设计:避免云端看到「一台还在心跳的死设备」。恢复手段只有现场断电重启。
alarm 是系统正常运行中可上报的异常(上报后设备仍可运行);hardfault 意味着设备已死,上报后无法进行任何后续操作。
消息格式
心跳消息
{"ts":1756195200123,"state":5}
| 字段 | 类型 | 说明 |
|---|---|---|
ts | number | Unix 时间戳(毫秒) |
state | number | 连接状态码(0~6) |
遗嘱消息
内容不含业务数据,携带设备型号、固件版本等用户属性,设备异常断开后由 Broker 自动发布。
安全校验
设备对收到的 MQTT 消息执行 Topic 前缀校验:
- 仅接受前缀与设备自身标识一致的 Topic;
- 不匹配的消息直接丢弃并记录告警日志;
- 该机制与 Broker 侧 ACL 配合,形成双重隔离,防止越权订阅或注入。
设备标识的生成方式与校验前缀规则属于产品安全设计细节,不在公开文档中展开。集成时请参考随附的《集成手册》。
OTA 升级
网关提供三条独立的固件升级链路,并支持向外部 MCU 中转固件,覆盖「模组自身升级」与「下游 MCU 升级」全场景。
三条 OTA 链路总览
| 链路 | 入口 | 固件流向 | 结束行为 |
|---|---|---|---|
| MQTT OTA | 无线固件更新话题 | 落盘 → 流式解密 → 写入 OTA 分区 | 写入完成后自动重启 |
| WEB OTA | HTTP /api/upload | 边收边落盘 → 独立任务流式解密 | 写入完成后自动重启 |
| 从机 OTA | 专用下行话题 | 落盘 → 解密校验 → 串口转发给从机 | 继续运行(不重启) |
三条链路共用落盘文件模型:网关自身固件(MQTT / WEB 入口互斥)共用同一固件镜像文件;从机固件使用独立镜像文件。单条 MQTT 消息最大支持 2 MB,可容纳一整包从机固件,无需应用层再做分片重组。
升级过程在后台独立完成,不影响设备既有的 MQTT 业务通信与串口透传;向外部 MCU 中转固件时设备亦保持持续运行。
固件预加密
固件采用 esp_encrypted_img 组件做 RSA-3072 + AES-256-GCM 预加密:固件在传输与落盘全程以密文存在,仅持有对应私钥的设备才能解密刷写,杜绝固件被非法提取或篡改。
STM32 串口固件分包协议
STM32 固件经 MQTT 下发到 ESP32,解密校验后通过 UART 串口分包发送给 STM32(AT+FMWOTALL 协议):
+FMWOTALL:<总包数>,<当前包号>,<CRC32>,<数据长度>,CRLF<二进制数据>
包头 ASCII 段以 CR + LF 收尾(0D 0A),其后紧接 数据长度 字节的二进制包体;MCU 先按 CRLF 定包头、再按长度收满包体,切勿按 CRLF 找固件数据结尾。
| 参数 | 值 | 说明 |
|---|---|---|
| 串口参数 | 230400, 8N1 | 无流控,TX=GPIO1 / RX=GPIO2 |
| 固件包大小 | 48 KB | 每包固定 48 KB(末包按余量) |
| 包间延时 | 300 ms | 发送间隔,便于接收侧完成校验与写入 |
| CRC32 | esp_rom_crc32_le | 多项式 0x04C11DB7,仅校验包体二进制数据 |
网关 → MCU: +FMWOTALL:3,1,A1B2C3D4,49152,CRLF<49152 字节二进制>
网关 → MCU: +FMWOTALL:3,2,E5F6A7B8,49152,CRLF<49152 字节二进制>
网关 → MCU: +FMWOTALL:3,3,9C0D1E2F,4096,CRLF<4096 字节二进制>
网关 → MCU: OKCRLF // 全部输出完成
首包包头的十六进制序列(2B 46 4D 57 4F 54 41 4C 4C 3A = +FMWOTALL:,0D 0A = CRLF):
2B 46 4D 57 4F 54 41 4C 4C 3A 33 2C 31 2C 41 31 42 32 43 33 44 34 2C 34 39 31 35 32 2C 0D 0A
静默策略
| 链路 | 是否恢复订阅 | 原因 |
|---|---|---|
| MQTT OTA | 否 | 结束即重启 |
| WEB OTA | 否 | 结束即重启 |
| 从机 OTA | 是 | 发完固件设备继续运行,须恢复订阅才能继续收业务报文 |
HTTPS 管理后台
浏览器直连的加密配置后台,自签名证书本地生成、私钥不出设备,配套单用户会话与统一鉴权,安全与易用兼顾。
HTTPS 服务器
- 协议:Web 管理后台采用 HTTPS(端口 443),
esp_https_server+ mbedTLS。 - 证书自动生成:ECDSA P-256 密钥对在设备本地生成(PSA 加密引擎),私钥不出设备。
- 证书持久化:证书与私钥存放于设备内部 LittleFS 分区,重启后复用,不重复生成。
- 有效期:自签证书有效期 10 年。
- IP 变更自动重签:WiFi 漫游或 DHCP 续租导致 IP 变化时,自动 stop → regenerate → start,SAN 扩展绑定新 IP。
- 证书校验容错:LittleFS 中证书损坏时自动触发重新生成。
mDNS 设备发现
设备联网后广播 _https._tcp 服务,浏览器访问 <设备主机名>.local 即可直连,无需记忆 IP。主机名由设备自动生成,出厂铭牌与控制台首页均可查。
单用户会话管理
| 机制 | 说明 |
|---|---|
| 会话令牌 | 登录成功返回 64 位随机 hex 令牌(由硬件随机数发生器生成) |
| 单用户互斥 | 同一时间仅允许一个活跃会话,第二个用户登录提示「已有其他用户登录」 |
| 会话超时 | 30 分钟无操作自动清除 |
| 活动刷新 | 每次 API 调用自动刷新最后活动时间,活跃使用不会超时 |
| 主动登出 | POST /api/logout |
统一鉴权
后端新增统一鉴权层,全部写接口与敏感读接口统一鉴权。认证方式双通道:
X-Session-Token头(优先,会话管理 + 超时)X-Password头(向后兼容,无会话跟踪)
认证错误区分「会话过期」与「密码错误」,用户体验更清晰。
输入合法性校验
| 项 | 校验规则 |
|---|---|
| broker URI | 端口 1–65535、主机名/端口字符合法性 |
| MQTT 密码 | 非空 + 长度 + 控制字符拒绝(V1.1.1 起允许 Broker 空密码) |
| topic 后缀 | 拒绝 + / # 通配符 |
| UART | 校验位 0/1/2、停止位 1/2、波特率 1200–4000000 |
| 项目名 | 最长 31 字符 + 字符集(字母/数字/下划线/连字符) |
| Client ID | 支持默认 / 自定义两种模式 |
安全时间校准
设备联网后自动校准系统时间。与常见明文 NTP 不同,本方案全程走加密 HTTPS 通道:
- 防篡改:时间来源与服务器证书校验绑定在同一条加密链路内,杜绝局域网中间人伪造时间。
- 多源冗余:内置多个时间源,国内/国际双路可达。
- 自动执行:联网后自动完成,无需配置与人工干预。
证书到期预警
概览页实时显示服务器证书剩余天数:不足 30 天黄色提醒、不足 7 天红色告警,把「被动救火」变成「主动维护」,避免证书过期导致整批设备掉线。
可视化监控仪表盘
- MQTT 健康九宫格:连接状态、重连次数、发送失败、TLS 握手耗时、收发缓存水位、订阅进度。
- 通信分析环形图:按主题统计收发消息分布、按命令类型统计 MCU 串口交互频次。
- 存储占用仪表:PSRAM 与 Flash 使用率圆环显示。
- CPU / 内存 / MQTT 速率与往返时延实时曲线,亮色/暗色主题一键切换。
更新日志
项目持续研发 14 个月,从 V1.0.x 一路迭代到 V1.1.4,覆盖 TLS 加密、HTTPS 后台、免串口调试、内存优化等关键里程碑。
版本号规则
- 主版本:重大架构变更或协议升级。
- 次版本:新增功能模块(如新 Web 页面、新 API)。
- 修订号:Bug 修复、UI 微调、配置调整。
V1.1.4 2026-09-24
新增:HTTPS 安全时间校准
- 全程走加密 HTTPS 通道,防局域网中间人伪造时间。
- 多源冗余,国内/国际双路可达,单一站点故障不影响校时。
- 联网后自动完成,无需配置。
改版:系统概览页面
- MQTT 健康九宫格状态墙,一屏掌握连接状态、重连次数、发送失败、TLS 握手耗时、证书剩余天数等。
- 证书到期预警:30 天黄 / 7 天红。
- 通信分析环形图、存储占用仪表(PSRAM / Flash 圆环)。
- 页面顶部显示设备真实时间。
V1.1.3 2026-09-23
新增:免串口调试
- Web 页面直接收发 MQTT 报文,无需 MCU 与串口助手。
- 自动生成对应的 MCU 串口 AT 命令帧与逐字段解析表,校验值长度自动算好。
- 首次调通时间从小时级压缩到分钟级。
可视化监控增强
- 新增 MQTT 消息速率与往返时延曲线。
- CPU / 内存实时曲线,亮/暗主题切换。
- MQTT 订阅主题上限提升至 1024 条。
稳定性提升
- 修复极端情况内存不足导致的连接失败,内部可用内存最低水位由十余 KB 提升至 110KB 以上。
V1.1.2 2026-09-23
重大更新:内部内存优化(PSRAM 迁移)
- 内部堆历史最低空闲由约 16.9 KB 提升至约 111.3 KB,彻底消除 TLS 握手 / WiFi 重连峰值期内存不足。
- UART 收发缓冲、AT 命令缓冲、MQTT 收发/重传缓存(约 650KB)、HTTPS 证书缓冲全部迁至 PSRAM。
新增:业务回传话题(Biz Topic)
- 上行话题独立管理,拼接规则
{项目名}/{设备ID}/{后缀}。 - Topic 管理页拆分为「订阅 / 上行」两页,业务话题随配置导入导出。
V1.1.1 2026-09-22
- 允许 Broker 空密码,支持连接无密码的公开 MQTT/MQTTS Broker。
- 头部 header 新增「重启设备」快捷按钮。
V1.1.0 2026-09-22
重大更新:HTTPS Web 管理服务器
- Web 后台从 HTTP 升级为 HTTPS(端口 443),
esp_https_server+ mbedTLS。 - ECDSA P-256 密钥对设备本地生成(PSA 加密引擎),私钥不出设备。
- 证书持久化 LittleFS,有效期 10 年,IP 变更自动重签。
- mDNS 设备发现
<设备主机名>.local。
单用户登录限制
- 64 位随机令牌、单用户互斥、30 分钟超时、活动刷新、
/api/logout。 - 认证双通道:
X-Session-Token/X-Password。
安全增强
- 认证错误明确区分「会话过期」与「密码错误」,避免账户枚举风险。
V1.0.19 2026-09-22
MQTT / TLS 连接
- Broker URI 支持
mqtts://(TLS 加密连接)。 - mbedTLS 内存策略改 Default alloc mode,解决内存不足导致的连接失败。
- TLS 服务器证书校验走内置根证书包(Let's Encrypt 根已包含)。
Web 后台安全加固
- 前端对用户输入全面校验;后端统一鉴权层。
- 输入合法性校验补齐(broker、密码、topic、UART、项目名、Client ID)。
V1.0.18 2026-09-21
- Client ID 支持默认/自定义切换。
- SOTA 话题为 STM32 OTA 单独分配密钥。
V1.0.17 2026-08-18
本版本之前的变更记录未保留,以此为起点开始归档。