基于STM32F103C8T6与A7670C-4G模块的MQTT实战:从AT指令到OneNet数据上云

张开发
2026/4/17 19:23:05 15 分钟阅读

分享文章

基于STM32F103C8T6与A7670C-4G模块的MQTT实战:从AT指令到OneNet数据上云
1. 硬件准备与环境搭建第一次用STM32F103C8T6搭配A7670C-4G模块做物联网项目时我花了整整两天时间才搞定硬件连接。现在回想起来其实只要注意几个关键点就能少走弯路。先说硬件选型蓝色PCB的STM32F103C8T6最小系统板某宝20元左右A7670C-4G模块带陶瓷天线版本更稳定建议额外配个USB转TTL模块用于调试。接线时最容易出错的是串口交叉连接。A7670C的TX要接STM32的PA3USART2_RXRX接PA2USART2_TX千万别接反。我习惯用彩色杜邦线区分红色接VCC3.3V黑色接GND黄绿线交叉连接串口。电源部分要特别注意——A7670C峰值电流能达到500mA必须单独供电我用的是一块3.7V锂电池通过AMS1117稳压到3.3V。开发环境推荐两种方案Arduino IDE适合快速验证需要安装STM32duino支持包Keil MDK更适合正式项目开发配置稍复杂实测发现A7670C对波特率比较敏感建议先用ATUART115200,8,1,0,0命令锁定波特率后面所有示例都基于115200波特率。有个坑要注意模块刚上电时有3秒左右的启动时间发送AT命令前最好先延时3秒否则可能无响应。2. AT指令交互实战和A7670C打交道就像跟一个固执的老头交流——必须严格按照它的语法来。我整理了最关键的8条AT指令这些是MQTT通信的基础网络检测ATCPIN? # 检查SIM卡状态 ATCSQ # 查看信号强度数值越大越好当信号值小于10时数据传输可能不稳定建议调整天线位置。APN配置以中国移动为例ATCGDCONT1,IP,CMNET # 设置APN ATCGACT1,1 # 激活PDP上下文不同运营商APN不同联通是3GNET电信是CTNET。MQTT服务器配置ATCMQTTSTART # 启动MQTT服务 ATCMQTTACCQ0,client1 # 创建客户端实例 ATCMQTTCONNECT0,tcp://mqtt.heclouds.com:1883,60,1 # 连接OneNet这里有个大坑OneNet的MQTT地址在2023年更新过旧文档里的地址可能失效一定要用最新的mqtt.heclouds.com。调试时建议先用串口助手手动发AT指令确认每步都返回OK再写代码。我习惯用下面这个函数封装AT指令发送String sendATCommand(String cmd, int timeout 2000) { Serial2.println(cmd); // 使用USART2 delay(50); String response ; long start millis(); while (millis() - start timeout) { if (Serial2.available()) { char c Serial2.read(); response c; } } return response; }3. MQTT协议对接OneNetOneNet的MQTT协议有三处特殊设计需要特别注意鉴权方式 需要将产品ID、设备ID、鉴权信息拼接成username产品ID;设备ID;鉴权信息密码留空即可。比如我的设备配置是ATCMQTTUSERNAME0,123456;dev123;202308 ATCMQTTPASSWORD0,主题(Topic)规范 OneNet要求发布主题格式为$sys/产品ID/设备ID/dp/post/json用AT指令订阅主题时ATCMQTTSUBTOPIC0,$sys/123456/dev123/dp/post/json,1数据格式 必须包含特定时间戳字段这是我常用的JSON模板{ id: 123, dp: { temperature: [{v: 25.3}], humidity: [{v: 60}] } }实际发送时需要转义双引号ATCMQTTTOPIC0,58 ATCMQTTPAYLOAD0,98 {\id\:123,\dp\:{\temperature\:[{\v\:25.3}],\humidity\:[{\v\:60}]}}最关键的提交命令很多人会漏掉ATCMQTTPUB0,1,60 # 这个必须发否则数据不会上传4. 稳定性优化与故障排查在实际项目中我遇到过凌晨3点设备掉线的紧急情况后来总结出这些经验心跳机制ATCMQTTCONNECT0,tcp://mqtt.heclouds.com:1883,60,1最后一个参数1表示开启keepalive默认60秒建议改为120秒ATCMQTTCONNECT0,tcp://mqtt.heclouds.com:1883,120,1断线重连 在loop()函数中加入状态检测if (millis() - lastPing 60000) { String resp sendATCommand(ATCMQTTSTATUS?); if (resp.indexOf(CMQTTSTATUS: 0,3) 0) { // 状态3表示已连接 reconnectMQTT(); } lastPing millis(); }常见错误码513JSON格式错误检查转义字符516鉴权失败检查设备三元组519Topic格式不符电源管理 遇到频繁断网时可以尝试ATCFUN0 # 关闭射频 delay(1000); ATCFUN1 # 重新开启有次现场调试发现数据时有时无最后发现是SIM卡接触不良。现在我的检查清单里一定会包含用ATCSQ检查信号强度用ATCGATT?确认附着网络用ATCMQTTSTATUS?查看MQTT状态5. 完整代码框架分享一个经过三个项目验证的稳定版本基于Arduino框架#include ArduinoJson.h #define DEBUG_SERIAL Serial #define MODEM_SERIAL Serial2 struct MQTTConfig { String productId; String deviceId; String authKey; }; void setup() { DEBUG_SERIAL.begin(115200); MODEM_SERIAL.begin(115200); init4GModule(); connectMQTT({123456, dev123, 202308}); } void loop() { static unsigned long lastSend 0; if (millis() - lastSend 30000) { float temp readTemperature(); float humi readHumidity(); publishData(temp, humi); lastSend millis(); } checkConnection(); } void publishData(float temp, float humi) { DynamicJsonDocument doc(256); doc[id] millis(); JsonObject dp doc.createNestedObject(dp); JsonArray tempArr dp.createNestedArray(temperature); tempArr.add(JsonObject().set(v, temp)); JsonArray humiArr dp.createNestedArray(humidity); humiArr.add(JsonObject().set(v, humi)); String payload; serializeJson(doc, payload); sendATCommand(ATCMQTTTOPIC0, String(58)); sendATCommand(ATCMQTTPAYLOAD0, String(payload.length())); sendATCommand(payload); sendATCommand(ATCMQTTPUB0,1,60); }这个框架里最实用的两个技巧使用ArduinoJson动态构建JSON避免手动拼接出错用millis()作为消息ID确保每次消息唯一6. 进阶技巧低功耗设计当设备需要电池供电时这几个优化能让续航提升3倍间隔唤醒void enterSleep(int seconds) { sendATCommand(ATCFUN0); LowPower.deepSleep(seconds * 1000); sendATCommand(ATCFUN1); }数据批量上报 缓存10条数据后一次性发送减少连接次数ATCMQTTTOPIC0,58 ATCMQTTPAYLOAD0,512 {id:123,dp:{temp:[{v:25.3},{v:25.5}],humi:[{v:60},{v:61}]}}信号强度自适应 弱信号环境下降低上报频率int getSignalQuality() { String resp sendATCommand(ATCSQ); int commaPos resp.indexOf(,); return resp.substring(commaPos-2, commaPos).toInt(); } void adjustInterval() { int csq getSignalQuality(); if (csq 10) interval 600000; // 10分钟 else interval 300000; // 5分钟 }最近一个农业监测项目用这些优化后2000mAh电池能续航45天。关键是要在ATCFUN0期间关闭所有外设电源STM32也进入STOP模式。

更多文章