news 2026/9/16 21:18:30

STM32+EC20通过MQTT接入ONENET上报温湿度实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32+EC20通过MQTT接入ONENET上报温湿度实战

简介:本资源是一套基于STM32F与EC20 4G模块实现温湿度数据上云的完整嵌入式物联网开发工程,面向嵌入式初学者、物联网开发者及高校电子类课程实践者,解决STM32端MQTT协议接入ONENET云平台的核心技术难点。压缩包含229个文件,以64个.h头文件和52个.c源码为主,涵盖STM32CubeMX生成的底层驱动、EC20串口AT指令封装、MQTT客户端(基于Paho或轻量级实现)、DHT/SHT系列传感器采集逻辑、JSON数据组包及ONENET主题配置等关键模块;另有23个.o编译目标、22个.crf依赖文件及Keil工程配置文件(uvprojx/uvoptx/bat脚本等),总大小4.82MB,结构规范,便于理解固件构建流程与通信链路分层设计。目前已有351人学习下载,提供可直接编译运行的完整工程框架、典型错误处理机制示例及ONENET平台对接配置说明,助读者快速掌握从硬件连接、固件开发到云端数据落地的全链路实践能力。

1. STM32F + EC20 模组通过 MQTT 向 ONENET 上报温湿度,不是“连上就行”,而是要过三关:AT 指令时序容错、MQTT 报文格式合规、ONENET 设备鉴权精准

很多工程师拿到 STM32F103C8T6(或类似主控)+ 合宙 EC20 4G 模组后,第一反应是“抄个 AT 指令发过去”,结果卡在 CONNECT 返回 ERROR、SUBSCRIBE 超时、PUBLISH 后平台无数据。根本原因在于:ONENET 对 MQTT 连接要求严格——它不接受非标准 ClientID、不校验但会静默丢弃非法 Topic、对 Payload 的 JSON 结构有硬性字段约束;而 EC20 在弱信号下 AT 响应延迟波动大,STM32 若未做指令超时重试与响应缓冲解析,极易把+MQTTSUB: 0,1误判为失败。本方案面向已焊接好 DHT22/AM2301 温湿度传感器、EC20 已完成供电与 SIM 卡注册的硬件环境,聚焦「从裸机固件到平台可见数据」的完整链路,覆盖 AT 指令集选型依据、MQTT 连接参数生成逻辑、ONENET 设备创建与 APIKey 绑定规则、以及 STM32 端基于 HAL 库的阻塞式+超时机制实现。适合嵌入式初学者按步骤复现,也提供熟手关注的 AT 指令响应状态机设计要点与 ONENET 数据点时间戳校准技巧。

2. 用 EC20 的 AT+MQTT 指令集在 STM32 上建立 MQTT 连接:为什么必须用 AT+MQTTSTART 而非透传模式

EC20 支持两种 MQTT 接入方式:透传模式(AT+CIPSTART + 手动拼接 MQTT 报文)和原生 MQTT 指令模式(AT+MQTTxxx)。前者需开发者完全理解 MQTT 协议二进制格式,易因字节序、剩余长度编码错误导致连接被 ONENET 拒绝;后者由模组内部协议栈处理 CONNECT/SUBSCRIBE/PUBLISH 封装,STM32 只需发送结构化 AT 指令,大幅降低出错概率。ONENET 官方文档明确要求设备使用 MQTT 3.1.1 协议,且必须携带 ClientID、Username(即 APIKey)、Password(为空字符串)三元组进行鉴权——这正是 AT+MQTTSTART 指令的输入参数来源。

2.1 ONENET 平台侧准备:创建设备、获取 APIKey 与构造标准 Topic

在 ONENET 物联网平台(onenet.com)控制台完成以下操作:

  • 新建产品 → 选择「MQTT」接入方式 → 填写产品名称(如stm32_ec20_th
  • 进入该产品 → 「设备管理」→ 「添加设备」→ 设备名称填sensor_001→ 保存后记录设备 ID(如58927341)和设备鉴权信息(即 MasterKey)
  • 进入「APIKey 管理」→ 「新建 APIKey」→ 权限选择「设备级」→ 关联设备选sensor_001→ 生成后复制APIKey 字符串(如a1b2c3d4e5f67890

提示:ONENET 的 MQTT 连接 Username 必须为 APIKey,Password 必须为空(不能省略),ClientID 格式为deviceid${设备ID}(注意是$符号而非{}),例如deviceid58927341。Topic 则固定为/devices/${设备ID}/tsl(用于物模型数据上报)或/devices/${设备ID}/datapoints(用于原始数据点)。本方案采用后者,因其结构简单、无需定义 TSL 模型。

2.2 STM32 端初始化 EC20:AT 指令交互框架与关键超时设置

在 STM32F103C8T6 的 HAL 库工程中,需配置 UART2(假设 EC20 接在 PA2/PA3)并实现带超时的 AT 指令收发函数。核心是避免死等模组响应:

// at_command.c #define AT_TIMEOUT_MS 3000 #define AT_RETRY_MAX 3 uint8_t AT_SendCommand(const char* cmd, const char* expect, uint32_t timeout_ms) { HAL_UART_Transmit(&huart2, (uint8_t*)cmd, strlen(cmd), 100); HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", 2, 100); uint32_t start = HAL_GetTick(); while (HAL_GetTick() - start < timeout_ms) { if (strstr((char*)rx_buffer, expect)) { // rx_buffer 为全局接收缓存 return 1; } HAL_Delay(10); } return 0; }

初始化流程必须按顺序执行,且每步失败需重试:

  1. AT→ 等待OK
  2. AT+CGATT?→ 确认附着网络(返回+CGATT: 1
  3. AT+CSQ→ 检查信号强度(+CSQ: 25,0表示良好)
  4. AT+MQTTSTART=0,"tcp://183.230.40.39:6002",120→ 启动 MQTT 客户端(IP 为 ONENET 公共 MQTT 地址,端口 6002)

注意:AT+MQTTSTART的第三个参数是 KeepAlive 时间(秒),ONENET 要求 ≥60 秒,设为 120 是为应对 4G 网络抖动。若返回ERROR,常见原因是 SIM 卡未注册成功或 DNS 解析失败,此时需先执行AT+CGDCONT=1,"IP","CMNET"设置 PDP 上下文。

2.3 构造并发送 MQTT CONNECT 指令:ClientID/Username/Password 的硬编码陷阱

EC20 的AT+MQTTCONNECT指令格式为:

AT+MQTTCONNECT="ClientID","Username","Password",KeepAlive,WillFlag,WillTopic,WillQoS,WillMessage,CleanSession

对应 ONENET 要求填入:

  • ClientID:deviceid58927341(设备 ID 前加deviceid$
  • Username:a1b2c3d4e5f67890(APIKey)
  • Password:""(空字符串,不可省略引号)
  • CleanSession:1(ONENET 要求清理会话)

实际发送代码:

char connect_cmd[128]; sprintf(connect_cmd, "AT+MQTTCONNECT=\"deviceid$%s\",\"%s\",\"\",120,0,\"\",\"\",\"\",""1\r\n", device_id, api_key); // device_id 和 api_key 为全局变量 if (!AT_SendCommand(connect_cmd, "OK", AT_TIMEOUT_MS)) { // 处理连接失败:检查 APIKey 是否过期、设备是否离线 }

提示:ONENET 对 ClientID 中的$符号敏感,若误写为deviceid{58927341}deviceid58927341(无$),CONNECT 会静默失败且无明确错误码。建议在串口调试助手中手动发送该指令验证响应。

3. 采集温湿度并封装为 ONENET 兼容的 JSON 格式:DHT22 读取与 MQTT Payload 构建

ONENET 的/devices/${设备ID}/datapointsTopic 接收标准 JSON 格式数据,其顶层必须包含datastreams数组,每个元素含id(数据流标识)和datapoints(时间序列数组),而datapoints中每个对象必须含at(ISO8601 时间戳)和value(数值)。常见错误是直接发送{"temperature":25.3,"humidity":60.1},这会导致 ONENET 丢弃整条消息。

3.1 DHT22 传感器驱动与防抖采样策略

DHT22 为单总线器件,STM32 需模拟时序读取。HAL 库下推荐使用 GPIO 输入输出切换方式(非定时器捕获),关键点在于:

  • 初始化时将 DATA 引脚设为推挽输出,拉低 18ms 启动信号
  • 然后切为浮空输入,等待 80us 响应脉冲
  • 后续 40 位数据每位以 50us 低电平起始,高电平持续时间决定 0 或 1(27~28us 为 0,70us 为 1)

为提升稳定性,采样需满足:

  • 每次读取前延时 2s(DHT22 最小采样间隔)
  • 连续 3 次读取,剔除最大最小值后取均值
  • 若校验和错误,立即重试,最多 3 次
// dht22.c typedef struct { float temperature; float humidity; } DHT22_Data; uint8_t DHT22_ReadData(DHT22_Data* data) { uint8_t buf[5] = {0}; if (DHT22_StartSignal() != HAL_OK) return 0; if (DHT22_ReadBytes(buf) != HAL_OK) return 0; if (buf[4] != (buf[0]+buf[1]+buf[2]+buf[3])) return 0; // 校验 >{ "datastreams": [ { "id": "temperature", "datapoints": [{"value": 25.3}] }, { "id": "humidity", "datapoints": [{"value": 60.1}] } ] }

在 STM32 上用 sprintf 构建(需预估 buffer 大小):

char payload[256]; float temp, humi; DHT22_ReadData(&sensor_data); temp = sensor_data.temperature; humi = sensor_data.humidity; sprintf(payload, "{\"datastreams\":[" "{\"id\":\"temperature\",\"datapoints\":[{\"value\":%.1f}]}," "{\"id\":\"humidity\",\"datapoints\":[{\"value\":%.1f}]}" "]}", temp, humi);

注意:sprintf易造成栈溢出,生产环境应改用snprintf并检查返回值。此处payload长度上限为 256 字节,足够容纳双精度浮点数(%.1f输出如25.3占 4 字节)。

3.3 通过 AT+MQTTPUB 发布数据:QoS 选择与 Topic 动态拼接

EC20 的AT+MQTTPUB指令格式为:

AT+MQTTPUB="Topic","Payload",QoS,RetailFlag
  • Topic:/devices/58927341/datapoints(设备 ID 替换为实际值)
  • QoS:ONENET 要求 QoS=0(最多一次),设为 1 会导致连接中断
  • RetailFlag:设为 0(非保留消息)

动态拼接 Topic 的 C 代码:

char topic[64]; sprintf(topic, "/devices/%s/datapoints", device_id); char pub_cmd[128]; sprintf(pub_cmd, "AT+MQTTPUB=\"%s\",\"%s\",0,0\r\n", topic, payload); if (!AT_SendCommand(pub_cmd, "OK", AT_TIMEOUT_MS)) { // 记录失败日志:可能原因包括 MQTT 连接已断开、payload 超长(>1024 字节) }

4. EC20 MQTT 连接稳定性优化:AT 指令状态机设计与 ONENET 数据验证方法

EC20 在 4G 信号波动时会出现 MQTT 连接意外断开,但模组不会主动通知 STM32。若不检测,后续 PUBLISH 将失败且无提示。必须实现连接状态心跳与自动重连机制。

4.1 基于 AT+MQTTSTAT 的连接状态轮询与自动恢复

EC20 提供AT+MQTTSTAT查询当前 MQTT 状态,返回值含义如下:

  • +MQTTSTAT: 0:未启动
  • +MQTTSTAT: 1:已连接
  • +MQTTSTAT: 2:正在连接
  • +MQTTSTAT: 3:连接断开

在主循环中每 30 秒轮询一次:

void MQTT_CheckConnection(void) { static uint8_t last_state = 0; char stat_resp[32]; if (AT_SendCommand("AT+MQTTSTAT\r\n", "+MQTTSTAT:", 1000)) { sscanf((char*)rx_buffer, "+MQTTSTAT: %hhu", &current_state); if (current_state == 3 && last_state == 1) { // 从连接态变为断开 MQTT_Reconnect(); } last_state = current_state; } }

重连逻辑需重走完整流程:AT+MQTTSTOPAT+MQTTSTARTAT+MQTTCONNECT,而非仅重发 CONNECT。

4.2 ONENET 平台侧数据验证:三步定位上报失败环节

当 STM32 日志显示 PUBLISH OK 但 ONENET 控制台无数据时,按以下顺序排查:

  1. 检查设备在线状态:控制台「设备管理」中设备状态是否为「在线」。若显示「离线」,说明 MQTT CONNECT 未成功或 KeepAlive 超时。
  2. 查看 MQTT 订阅状态:ONENET 不强制 SUBSCRIBE,但需确认设备是否已绑定到产品。进入「产品管理」→「设备列表」→ 点击设备 → 「数据流」页签,若无temperature/humidity数据流,说明首次 PUBLISH 未触发自动创建(需确保 Payload JSON 结构正确)。
  3. 启用 ONENET 日志追踪:在控制台「运维监控」→「设备日志」中,选择目标设备,设置时间范围,筛选MQTT类型日志。正常日志含MQTT_PUBLISH_SUCCESS,失败则有MQTT_PUBLISH_ERROR_INVALID_JSONMQTT_PUBLISH_ERROR_TOPIC_NOT_FOUND

提示:ONENET 的数据流(datastream)是懒创建的——首次向某id(如temperature)发送数据时自动建立。若 Payload 中id拼写错误(如temperatre),该数据流将永远不存在,后续同名数据仍被丢弃。

4.3 关键参数速查表:EC20 AT 指令与 ONENET 要求对照

参数项EC20 AT 指令ONENET 要求常见错误
MQTT 服务器地址AT+MQTTSTART=0,"tcp://183.230.40.39:6002",120IP183.230.40.39,端口6002误用183.230.40.40(测试地址)或1883(非 TLS 端口)
ClientIDAT+MQTTCONNECT="deviceid$58927341",...必须含deviceid$前缀写成deviceid58927341(缺$)或58927341(缺前缀)
UsernameAT+MQTTCONNECT=...,"a1b2c3d4e5f67890",...APIKey 字符串使用 MasterKey 或设备 ID 代替 APIKey
TopicAT+MQTTPUB="/devices/58927341/datapoints",.../devices/{deviceID}/datapoints误写为/v1.6/devices/...(HTTP API 路径)或/mqtt(无效路径)
QoSAT+MQTTPUB=...,0,0必须为0设为1导致连接被重置

5. 进阶技巧:用 AT+MQTTRECV 实现下行指令接收与温湿度阈值远程配置

ONENET 支持向设备下发指令,例如远程修改温湿度报警阈值。EC20 的AT+MQTTRECV指令可获取订阅 Topic 的消息,需提前AT+MQTTSUB订阅/devices/${设备ID}/cmds主题。

5.1 订阅命令主题并解析 JSON 指令

首先订阅命令 Topic:

char sub_cmd[64]; sprintf(sub_cmd, "AT+MQTTSUB=\"/devices/%s/cmds\",1\r\n", device_id); AT_SendCommand(sub_cmd, "OK", AT_TIMEOUT_MS);

在主循环中轮询接收:

if (AT_SendCommand("AT+MQTTRECV\r\n", "+MQTTRECV:", 500)) { // rx_buffer 形如:+MQTTRECV: "/devices/58927341/cmds","{\"cmd\":\"set_threshold\",\"temp_max\":30.0}" char* payload_start = strstr((char*)rx_buffer, "\"{"); if (payload_start) { parse_command(payload_start + 1); // 自定义 JSON 解析函数 } }

典型指令 JSON 示例:

{"cmd":"set_threshold","temp_max":30.0,"humi_min":40.0}

解析后更新本地阈值变量,并触发 LED 报警逻辑。

5.2 ONENET 控制台手动下发指令验证流程

  1. 控制台进入设备详情页 → 「设备调试」→ 「下发指令」
  2. Topic 填/devices/58927341/cmds(与订阅 Topic 一致)
  3. Payload 填 JSON 字符串(如上例),点击「下发」
  4. STM32 串口日志应打印收到的指令内容,且AT+MQTTRECV返回非空 payload

注意:ONENET 的命令下发有 5 分钟有效期,超时未被设备接收则失效。EC20 的AT+MQTTRECV是轮询式,非中断式,因此需保证主循环执行频率 ≥1Hz。

5.3 温湿度数据在 ONENET 折线图中的呈现优化

ONENET 控制台「数据可视化」中创建折线图时,若发现数据点稀疏或时间轴错乱,检查两点:

  • 数据点密度:默认每分钟最多显示 100 个点,若每 10 秒上报一次,则需在图表设置中将「时间粒度」调为「10秒」或「30秒」
  • 时间戳一致性:若使用at字段自定义时间,务必确保 STM32 时钟与 NTP 同步(通过 EC20 的AT+CCLK获取),否则折线图出现时间跳跃。推荐策略:Payload 中省略at字段,依赖 ONENET 服务端打戳,确保所有数据点时间连续。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 21:18:10

PTCMS小说聚合源码实战:从环境部署、采集规则到会员支付机制构建

简介&#xff1a;PTCMS小说聚合网站系统源码是一套基于PHP开发的小说站点程序&#xff0c;面向PHP开发者、个人站长及中小型内容创业团队&#xff0c;解决小说内容聚合、采集入库、会员付费与多终端适配等关键问题。资源包约41.77MB&#xff0c;主要包含PHP后端核心逻辑、LAYUI…

作者头像 李华
网站建设 2026/9/16 21:18:07

RevokeMsgPatcher 2.1 保姆级防撤回教程:4 步跑通

RevokeMsgPatcher 2.1 保姆级防撤回教程&#xff1a;4 步跑通 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁&#xff08;我已经看到了&#xff0c;撤回也没用了&#xff09; 项目地址: https://gitcode.com/Gi…

作者头像 李华
网站建设 2026/9/16 21:17:56

配电网韧性提升:两阶段鲁棒优化与MPS动态调度

1. 项目背景与核心价值配电网作为电力系统的"最后一公里"&#xff0c;其可靠性直接关系到民生用电质量。在极端天气事件频发的当下&#xff0c;如何提升配电网的韧性&#xff08;Resilience&#xff09;成为电力工程领域的热点课题。我们团队在复现这篇SCI一区论文时…

作者头像 李华
网站建设 2026/9/16 21:17:51

视频变速技术全解析:从原理到实践

1. 视频变速的常见需求与挑战在视频编辑领域&#xff0c;变速处理是最基础也最常用的功能之一。无论是想要制作慢动作特效的短视频创作者&#xff0c;还是需要快速浏览长视频内容的学习者&#xff0c;亦或是希望调整视频节奏的专业剪辑师&#xff0c;都会遇到视频变速的需求。常…

作者头像 李华
网站建设 2026/9/16 21:16:30

Ubuntu 22.04安装ROS2 Humble完整指南与优化配置

1. 环境准备与基础配置1.1 系统语言环境设置在Ubuntu 22.04上安装ROS2 Humble前&#xff0c;确保系统语言环境正确配置至关重要。ROS2对UTF-8编码有强依赖&#xff0c;这关系到消息传递、日志输出等核心功能的正常运行。我曾在实际项目中遇到过因语言环境配置不当导致的消息编码…

作者头像 李华
网站建设 2026/9/16 21:15:47

Linux下用Ollama本地部署LLM:从安装到实战调优

1. 为什么我建议你在Linux下用Ollama做本地LLM部署先聊点实际的。前阵子我需要测试几个大模型的推理效果&#xff0c;但手头有一批敏感业务数据&#xff0c;不方便传到云端API&#xff0c;于是开始认真折腾本地部署。试了一圈方案之后&#xff0c;最终固定在Linux Ollama这套组…

作者头像 李华