STM32F103+A7680C 4G模块通过MQTT连接OneNet上传温湿度
发布时间:2026/9/12 6:50:20来源:尧图网络
简介这套例程面向嵌入式物联网开发者以STM32F103为主控芯片演示A7680C 4G模块采集温湿度数据并通过MQTT协议上传至中移OneNET平台可直接作为项目原型参考。程序基于KEIL标准库编写代码中定义了单片机与模块的接线方式并配有详细注释方便移植到同系列其他型号芯片时自行调整工程默认区分J-Link与ST-Link下载选项使用前需按实际调试器选择。压缩包共245个文件大小约6.87MB核心内容为40个C源文件与45个头文件涵盖定时器、Flash、RCC、ADC等STM32外设驱动同时包含Keil工程配置、编译生成的hex/axf文件、一键清理编译残余的批处理脚本以及htm格式说明文档帮助理解工程结构和编译流程。已有185人浏览学习。对刚接触4G物联网接入的开发者来说这份资源能提供完整的MQTT上云示例、外设初始化框架、接线定义与排错思路并且从串口打印到云端平台数据展示均有参考价值可节省从零搭建的时间。1. STM32F103 接 A7680C-4G 上传 OneNet 的完整链路这是一套在 4G 网络里走 MQTT 的典型工程STM32F103 读温湿度传感器把结果通过串口交给 A7680C-4G 模组再用 SIM 卡接入蜂窝网以 MQTT 客户端身份连接中国移动物联网平台 OneNet。压缩包里通常有 Keil 工程、接线说明和一份 AT 指令流程但照抄这些内容只能看到模块亮灯数据不一定会出现在平台的折线图里。卡住人的位置往往有三个A7680C 开机时序不对、MQTT 的 AT 命令与平台鉴权参数不匹配、JSON 数据流 id 与平台模板不一致。下面这条链路从硬件接线写到云平台数据可视化参数和坑一起标注适合刚拿到 4G 模块的开发者也适合被“能发 AT 却传不上平台”卡住的人。2. 先解决供电和串口stm32f103 最小系统怎么接 A7680C-4G2.1 最小系统上的三个不能省的点电源、PWRKEY、电平转换很多人在 A7680C 上翻车不是因为代码而是因为把模块电源接到了开发板的 3.3V。4G 模组在注册网络和发送数据时的瞬态电流可以达到 2A 甚至更高USB 口或 AMS1117 线性稳压器根本撑不住表现就是模块反复重启或者发AT指令偶尔有回显偶尔没回显。常见做法是在模块 VBAT 引脚旁边并联一个 1000µF 电解电容再用最大输出 3A 的低压差稳压电源给模块单独供电MCU 的 3.3V 电源和模块 VBAT 不要共用一个线性稳压器。PWRKEY 是第二个容易漏的点。A7680C 上电后不会直接进入就绪状态需要 MCU 把 PWRKEY 引脚拉低几百毫秒再释放。工程代码里一般会这样写开机函数void A7680C_PowerOn(void) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); gpio.GPIO_Mode GPIO_Mode_Out_PP; gpio.GPIO_Pin GPIO_Pin_7; gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOC, gpio); GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_SET); // PWRKEY 先置高 delay_ms(100); GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_RESET); // 拉低触发开机 delay_ms(700); GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_SET); // 释放 }这里用的是标准外设库 v3.5 风格的初始化代码换 HAL 库时只要把 GPIO 配置改成HAL_GPIO_WritePin即可。注意不同批次模块对 PWRKEY 的电平极性和最小脉宽定义不一样有些要求低有效有些要求高电平保持 1.2 秒先看你手里模块手册上的“开机时序”不要照抄网上现成接线图。典型接法汇总如下。STM32F103 引脚A7680C 引脚说明PA9 / USART1_TXURXD单片机发给模块PA10 / USART1_RXUTXD模块发给单片机PC7示例PWRKEY开机时序控制GNDGND必须共地3V3独立稳压VBAT电压范围按手册通常 3.4V~4.2V第三个容易被忽略的是电平。F103 工作电压是 3.3V而 A7680C 的 UART 接口电平不一定兼容 3.3V。如果模块手册标注 UTXD 高电平按 VBAT 走那最好在 UTXD 到 MCU_RX 之间加一级分压电阻模块 URXD 接收方向不用太担心但高速率下也可能出现误码。开发阶段为了省事可以直连量产阶段不要把直连放到原理图上。2.2 DHT11 还是 SHT30数据采集引脚和协议差异ZIP 工程里最常见的温湿度传感器是 DHT11因为它的单总线驱动在正点原子和野火的例程里都有几乎不用改就能编译过。单总线只有一根 DATA 线连到 F103 任意普通 GPIO 即可比如 PB4。但 DHT11 湿度精度只有 ±5%而且时序严格如果在 FreeRTOS 里被高优先级任务打断采样很容易失败。常见做法是把 DHT11 读取放在定时器中断里或者读取前临时关掉会对微秒延时产生影响的调度器。如果工程默认是 SHT30驱动会变成 I2C。STM32CubeMX 里把 I2C1 配成 Fast ModeSCL/SDA 引脚选开漏输出并外接 4.7kΩ 上拉电阻。SHT30 返回物理量原始值和 CRC 校验总线上还可以再接其它 I2C 设备比 DHT11 更适合做多传感器采集。换传感器不影响 OneNet 侧数据格式平台只认温度和湿度数值把 DHT11 读到的整型温度和 SHT30 算出来的浮点温度都归一化成浮点数后面拼 JSON 时是一致的。2.3 串口 1 和串口 3 的使用差异模块到底挂哪一路模块和调试打印最好分到两路串口。stm32f103 的串口 1 和串口 3 使用差异主要体现在中断入口、时钟源和复用引脚上USART1 挂在 APB2时钟 72MHzUSART3 挂在 APB1时钟 36MHz所以在相同波特率下 USART1 的波特率误差更小但 USART3 的引脚 PB10/PB11 在最小系统板上更容易引出来而且不会和 SWD 仿真器冲突。我一般会把 A7680C 挂在 USART3把printf调试输出留在 USART1。这样模块的 URC 上报和调试日志互不干扰。如果你把调试和模块放同一路串口最典型的症状是printf转义字符打断 AT 命令拼接导致模块偶尔返回 ERROR。还要确认模块默认波特率很多 4G 模块出厂是 115200但也有 9600 和 230400 的固件版本。无论 Keil 工程里USART_InitStructure.USART_BaudRate写多少先打开串口工具接到模块发一条AT确认返回OK再让单片机接管。3. 不走弯路A7680C 的 MQTT AT 指令和 STM32F103 侧状态机3.1 mqtt 协议详解模块内置客户端还是自己移植 PahoOneNet 平台对普通设备建议走 MQTT它比 HTTP 长连接省电也比 TCP 裸传多了主题和 QoS 两层语义。对 STM32F103 来说并不是一定要引入 Eclipse Paho 源码才能在单片机上收发 MQTT 报文。A7680C-4G 内部已经实现了 TCP/IP 协议栈和 MQTT 客户端MCU 通过 AT 指令告诉它“服务器地址是什么、Topic 叫什么、要发什么内容”剩下 CONNECT 报文、PUBLISH 报文、PINGREQ 心跳这些底层包都由模组完成。记住这一点可以避开一个常见误区不要在 F103 里用 TCP 透传去拼 MQTT 固定报头。F103 RAM 只有 20KB一个 1KB payload 加上重传缓冲区很容易让 malloc 失败而且 MQTT 报文里剩余长度字段采用变长编码稍不留神就数组越界。除非你就是为了学 mqtt 协议否则用模块自带的 AT 指令更省事。真正要自己处理的是应用层状态机网络注册是否完成、MQTT 会话是否建立、平台有没有返回 publish ACK。3.2 A7680C 入网和连接 OneNet 的典型 AT 序列不同固件版本的 A7680C 命令前缀可能差很多有的量产固件继续兼容旧的ATCMQTT*有的改成了ATMQTT*所以我不直接给一份写死的命令而是把可执行流程和命令模板放在一起。拿到模块先发两条查询命令确定它支持哪套ATCMQTTSTART? ATMQTTCFG?收到OK或CMQTTSTART: 0说明是老版本 MQTT 客户端返回ERROR的换ATMQTT?再试。接下来按这个顺序做网络注册和 MQTT 建链// 网络层附着 4G 网络设置 APN。A7680C 一般用中国移动物联网卡通用 APN send_at(ATCGATT1, OK, 3000); send_at(ATCGDCONT1,\IP\,\cmiot\, OK, 3000); // MQTT 配置clientId、产品 id、鉴权 tokenkeepalive 设 60 秒 send_at(ATMQTTCFG\240914\,\productid\,\token\,60,1, OK, 3000); // 打开到 OneNet 的 TCP 连接地址端口以控制台为准 send_at(ATMQTTOPENOneNet接入地址,1883, OK, 5000); // 建立 MQTT 会话等待 CONNACK 事件 send_at(ATMQTTCONN, MQTTSTAT: 0, 10000);ATCGATT返回 1 才表示网络侧已就绪CGDCONT里的 CID 为 1PDP 类型是 IPAPN 写成cmiot是中国移动物联网卡最常见的专用 APN联通或电信卡要换成卡对应的 APN。MQTT 配置里60是 keepalive 秒数1表示 clean session如果平台上看板希望模块断电恢复后能立即续传自定义订阅可以尝试开 persistent session。ATMQTTOPEN里的地址和端口必须以 OneNet 控制台提供的接入信息为准教程工程里写死的 IP 可能是老平台测试地址不能直接盲用。如果平台要求 TLS端口一般变为 8883同时还要给模块导入根证书这一步留到调试通过之后再补。3.3 单片机侧的状态处理发送 AT 后不能只等一次 OK上面代码里的send_at只是简化封装实际工程里不应该简单阻塞 2 秒等返回值。A7680C 串口会同时输出多种 URC例如信号变化、MQTT 断开、时间同步通知如果单片机只查找OK遇到 URC 插入就会误判。建议按“发送-等待-重试”状态机处理每发一条 AT 命令把回复累计到一个 256 字节环形缓冲区然后只匹配最后一行超时没有匹配到就把缓冲区清空并重发连续 3 次失败则复位模块。typedef enum { AT_STATE_SEND, AT_STATE_WAIT, AT_STATE_RETRY, AT_STATE_FAIL } at_state_t; at_state_t at_state AT_STATE_SEND; uint8_t at_retry 0; while (1) { switch (at_state) { case AT_STATE_SEND: uart_send(usart1, AT\r\n, 4); at_state AT_STATE_WAIT; break; case AT_STATE_WAIT: if (at_has_response_ok(usart1)) { at_state AT_STATE_SEND; // 进入下一条命令 } else if (at_response_has_error(usart1)) { at_state AT_STATE_RETRY; } break; case AT_STATE_RETRY: if (at_retry 3) { at_state AT_STATE_SEND; } else { A7680C_PowerOn(); // 直接重新开机 at_retry 0; } break; default: break; } }这种做法的好处是即使模块在开机 10 秒后才输出 URC也不会影响对普通 AT 应答的判断。最重要的是ATMQTTCONN的“成功”不是由OK决定的而是由后续出现的MQTTSTAT: 0或CMQTTCONNECT: 0决定很多人在这一步只判断了 OK看到平台日志里没有连接记录就误以为模块坏了。MQTT 会话建立起来之后单片机再发布温湿度数据平台才会接收。4. OneNet 云平台接入从生成 APIKey 到折线图出现4.1 OneNet 生成 APIKey 和控制台里的接入三要素OneNet 平台从旧版“设备云”升级到新版 Studio 之后入口和术语改过好几次但接入信息仍是三个产品 id、设备 id、鉴权信息有时叫 APIKey有时叫设备密钥或 access_key。在控制台创建产品时接入协议选 MQTT网络类型选移动网络创建完产品后进设备列表添加真实设备设备名称填类似stm32f103_rht鉴权信息可以自己填一组 16 位随机字符串。完成后控制台会给出 MQTT 接入地址和端口复制到工程里的宏定义中。新版平台对“生成 APIKey”的管理方式变化比较大找不到 APIKey 入口时不要在设备列表里死磕去产品详情页的“MQTT 连接参数”或“设备鉴权”里看。旧版 APIKey 本质上是一个长字符串同时承担设备身份认证和 HTTP 接口调用鉴权两种功能新版把两者拆开发布 MQTT 数据只需要设备级密钥APIKey 通常留给云平台 REST API 使用。所以“生成 APIKey”在 MQTT 场景下更准确的理解是“拿到一组 MQTT 连接凭证”而不是在 HTTP 请求头里拼 APIKey。换成 NB 模组接入 OneNet 时LwM2M 协议下的接入三要素也是同一套逻辑只是 AT 命令完全不同。4.2 温湿度 JSON 的拼装和 MQTT 主题选择OneNet 接入 MQTT 后发布数据有两种主题体系。旧版“数据流”方式是 publish 到$dp主题消息体用 OneNet 自有 JSON 格式新版 Studio 可以在自定义主题里发任意 JSON前提是产品里已经定义数据模板。一个兼容性较好的做法是使用旧版$dp数据流格式只要数据流 id 与平台一致控制台就会自动落库并画折线图。payload 长这样{datastreams:[{id:temp,datapoints:[{value:25.3}]},{id:humi,datapoints:[{value:61.2}]}]}在 STM32 里拼这个字符串用snprintf浮点数格式化控制在一位小数char payload[160]; int payload_len; payload_len snprintf(payload, sizeof(payload), {\datastreams\:[{ \id\:\temp\,\datapoints\:[{\value\:%.1f}]}, {\id\:\humi\,\datapoints\:[{\value\:%.1f}]}]}, temp, humi); if (payload_len 0 payload_len (int)sizeof(payload)) { snprintf(cmd, sizeof(cmd), ATMQTTPUB\$dp\,%d,\%s\\r\n, payload_len, payload); uart_send(usart3, cmd, strlen(cmd)); }snprintf返回实际应写入的字符数用返回值做判断能避免 JSON 被截断。%.1f会把浮点精度压到一位小数OneNet 面板显示 25.3 比 25.333333 更干净如果要保留原始精度格式改成%.2f并把payload数组加到 200 字节。注意ATMQTTPUB的真实语法各固件不一有的把 topic 和 payload 放同一行有的要求把 payload 长度分成第二段接入前先在串口助手验证一次再写进代码。4.3 折线图不是“自动出现”的数据流名字要一致数据到达 OneNet 后折线图能否显示取决于数据流 id 是否和产品里定义的数据流名称完全一致。工程里写的是temp平台数据流叫temperature那平台虽然收到数据却不会关联到任何图表。还有一个高频错误是把value值写成字符串比如value:25.3OneNet 的折线图组件只接受数字类型遇到字符串会自动丢弃。排错时最常对比的是下面三种情况平台现象可能原因处理方式设备一直在线折线图不更新publish 的 topic 不是$dp或datastreams拼写错误对照控制台日志确认 publish topic偶发几秒内能到之后断线keepalive 参数太长模块没发心跳把 keepalive 改 60–120 秒显示 value 类型 ERRORJSON 值带了引号或小数点超长去掉引号只发25.3除了平台自带的数据展示OneNet 新版控制台还提供“可视化”或“应用开发”功能新建仪表盘后添加折线图组件再绑定数据流到设备维度。这个绑定操作必须精确到数据流 id绑错组件时数据流里已经有 1000 个点图上仍然一根线也没有。5. A7680C 连 OneNet 的排错顺序和验证技巧5.1 按 AT 命令逐层确认网络注册状态不要一上来就怀疑代码。用 MCU 或 USB-TTL 直接连 A7680C依次敲四组命令ATCPIN? // SIM 卡是否就绪 ATCSQ // 信号强度20 以上才稳定 ATCGATT? // 是否附着网络返回 1 才行 ATMQTTCONN? // 查询 MQTT 会话状态如果ATCGATT?返回 0继续查ATCEREG?或ATCREG?返回 1 或 5 表示网络已注册。信号显示99且反复变化时先查天线有没有接好再换一个位置。SIM 卡欠费、卡槽接触不良都会让ATCPIN?返回 ERROR。还有一类情况是模块已开过机但你用的是采购商自测的物联网卡需要手动执行ATCGDCONT1,IP,cmiot才能拿到私网地址。5.2 三个高频现场问题A7670C 固件、心跳和数据格式这类工程有一个很典型的坑很多人手里的模块丝印是 A7680C但板子是从 A7670C 老设计改过来的两个模块的 MQTT AT 指令版本可能不兼容。报错现象通常是ATMQTTCFG返回 ERROR因为老固件里命令叫ATCMQTTACCQ。这时不要改代码先查模块固件版本ATCGMR再对照手册把命令前缀统一。第二个现场问题是 MQTT 连接半小时后断开。A7680C 进入省电模式后模块可能不再上报数据但平台侧还在等心跳代码里除了设置 keepalive还需要在定时器里主动检测 MQTT 会话状态如果收到模块上报的断开 URC就重新执行“打开 TCP → 建 MQTT 会话”两步。第三个问题最容易迷惑人数据明明发到了 TCP 层OneNet 上却没有任何记录。把模块换成串口助手直接发一次$dpJSON用平台设备日志做对照就能定位是 JSON 转义问题还是平台主动过滤非法数据。最后留一个常用排错技巧在 STM32 工程里把 A7680C 所有不以 OK/ERROR 开头的串口行都集中到一个urc_parse()函数里先打印日志再进状态机。平台调试看起来像“随机断开”其实每次断开前都会先出现MQTTSTAT或CMQTTNONET之类的事件把这一条事件记录丢到串口调试助手里对照后面的状态码去查模块手册比插 20 次仿真器都有用。本文还有配套的精品资源点击获取
网站建设高端定制企业官网