STM32+EC800-4G+MQTT接入OneNET:环境监测物联网实战 📅 发布时间:2026/8/31 13:31:09 👁 浏览次数: 简介本资源是一套面向嵌入式物联网开发初学者与中级工程师的实战项目例程聚焦STM32F103单片机通过EC800-4G模块接入ONENET云平台的完整实现解决温湿度数据采集、4G联网、MQTT协议封装与云端上传等典型物联网终端开发痛点。压缩包含234个文件主体为KEIL标准库工程文件43个.h头文件、38个.c源码、40个.o编译对象及39个.d依赖文件辅以位图界面提示7个.bmp、调试配置.sct/.axf/.uvprojx和清理脚本.bat总大小6.97MB结构规范便于理解编译流程与硬件驱动逻辑。已有288人学习下载代码全程中文注释关键接口如UART通信、AT指令解析、MQTT连接与发布均有详细说明接线定义明确写入代码配套多状态提示图如“在线了.bmp”“APIKEY.bmp”直观反映运行状态可快速移植适配同类传感器或平台。 我前阵子帮客户做了一个环境监测的样板需求很明确大棚里的温湿度要实时传到云端人在家里打开手机就能看。一开始我想的是ESP8266结果客户有一处大棚在深山老林里WiFi根本覆盖不到拉到主路由也不现实。后来我换成了这次的方案STM32F103采集温湿度通过EC800-4G模块走蜂窝网络用MQTT协议把数据推到ONENET平台。整套链路做完从采集到云端展示的过程就完全打通了。这个方案其实覆盖了一个典型的物联网项目闭环传感器采集、单片机处理、4G上云、云平台可视化。如果你也在做类似的数据采集项目或者手头正好有STM32F103和一快4G模块这篇内容应该能帮你少走不少弯路。1. 方案整体设计与硬件连接1.1 为什么最后选了EC800-4G MQTT这套组合先说说选型的思路。STM32F103做主控这个没什么好纠结的便宜、资料多、生态成熟做数据采集绰绰有余。关键在通信链路怎么选WiFi模块、2G模块、4G Cat.1模块还是NB-IoT模块。WiFi模块ESP8266在这类项目里最常见但它的短板也很明显必须有可用的热点环境。如果设备是移动的比如冷链车、外勤设备或者部署在空旷农田、偏僻厂房WiFi方案就直接废了。2G模块比如SIM800C虽然也能上云但2G网络在逐步退网很多地方信号已经开始变差新项目再往2G上押注风险太高。NB-IoT低功耗确实香但它的带宽小、时延偏高而且接入的是平台专用核心网配置和调试比普通4G链路麻烦更适合水表电表这类固定位置、低频次、极低功耗的场景。EC800-4G是移远通信的Cat.1模块定位就是中速率物联网场景用它来做这个项目有几个直接优势一是走的是运营商4G网络只要手机有信号的地方它就有信号不受环境和位置限制二是功耗比传统4G模块低静态功耗能到微安级野外供电压力小三是模块自带完整的MQTT AT指令集不用自己在单片机里移植MQTT协议栈省掉了一大堆底层的头疼事。实际测试下来从模块上电到注册上网络大概几秒钟然后一条AT指令就能把MQTT连接建起来发布一条消息也就毫秒级的事。选MQTT而不用HTTP原因也很直接。HTTP是请求-响应模式每次上传数据都要建立TCP连接、发送头部、等待响应对设备的功耗和流量都是浪费而且服务端要主动推送命令给设备时HTTP基本做不了只能靠设备轮询。MQTT是发布-订阅模式设备连上一次broker之后可以一直保持连接随时双向收发消息。ONENET本身就是一个MQTT broker设备连上去之后上报温湿度用发布主题平台要下发控制指令就用订阅主题一条链路全部搞定。这个场景下温度、湿度这种周期性数据用MQTT太合适了报文小、开销低、功耗可控、连接保持期间还能随时接收平台下发的指令。1.2 硬件组成与接线明细整套系统硬件清单不复杂列一下这次实际用到的器件型号/规格作用主控板STM32F103C8T6最小系统板采集、逻辑控制、串口通信4G模块EC800-4GLCC封装焊接在主板上蜂窝网络连接MQTT数据转发温湿度传感器DHT11如果精度要求高可换SHT30环境温湿度采集SIM卡移动/联通/电信物联网卡均可建议开通流量网络接入天线4G胶棒天线或PCB天线信号收发电源5V/2A适配器 AMS1117-3.3板上供电串口调试USB转TTLCH340模块单独调试、日志打印接线这块有个容易被新手忽视的点EC800-4G的UART和电源引脚电平。这个模块的IO电平一般是1.8V/3.3V兼容的而STM32F103的引脚是3.3V理论上可以直接连但我还是习惯加一个电平匹配保护尤其是从模块给单片机的TX引脚那一路最好确认一下模块手册上的电平范围别等着模块输出1.8V然后单片机正好能识别后面换个型号就翻车。我这次的实际接法是EC800-4G的UART_TX接STM32F103的PA3USART2_RXUART_RX接PA2USART2_TX。为什么不用USART1因为PA9/PA10这对USART1引脚我留给了调试日志输出接USB转TTL直接在电脑上看打印。这样模块数据和调试日志互不干扰排查问题的时候方便得多。还有一个细节EC800-4G模块的启动需要一定的电流而且SIM卡座、天线座、模块电源引脚这些地方焊接的时候最容易虚焊。我这次第一批板子回来有一块就是死活找不到SIM卡最后用放大镜一看SIM卡座的一个引脚虚焊了补焊一下立马正常。所以拿到板子先做静态检查别急着上电。电源方面模块突发发射时电流可以达到1A以上如果用最小系统板上的3.3V稳压直接给模块供电大概率会掉电压导致模块死机或无法注册网络。正确做法是模块电源独立供电或者至少用能输出2A的稳压芯片而且要在模块电源引脚旁边就近放一个100uF电解电容和一个100nF陶瓷电容作为瞬态电流缓冲。这个电容我实测下来非常关键不加电容的时候模块一发起网络注册单片机这边就会莫名其妙复位。2. 温湿度采集与MQTT协议核心机制2.1 DHT11温湿度采集要点传感器这块DHT11入门够用但有几个坑得提前说。DHT11只有一根数据线协议是单总线必须严格按照时序来读否则读回来全是0。读取一次的基本流程是主机拉低总线至少18ms发起开始信号然后释放总线DHT11会拉低80us响应再拉高80us随后输出40bit数据。每个bit用高电平的持续时间来区分0和1高电平持续26~28us是0持续70us左右是1。延时如果不够准确就会读错。我当时为了省事直接用了HAL_Delay和微秒级延时函数结果发现代码里用的微秒延时是基于SysTick搞的延时精度在48MHz主频下还行但要注意把编译器优化级别调低否则延时函数被优化后时序全乱。我建议写驱动的时候微秒级延时最好自己用循环卡一下周期不要依赖系统延时函数。代码层面一个稳妥的DHT11读取函数大概长这样简化骨架uint8_t dht11_read_data(uint8_t *temp, uint8_t *hum) { uint8_t data[5] {0}; // 1. 主机拉低 18ms然后释放 DHT_DATA_GPIO_MODE_OUTPUT(); HAL_GPIO_WritePin(DHT_DATA_PORT, DHT_DATA_PIN, GPIO_PIN_RESET); delay_ms(20); HAL_GPIO_WritePin(DHT_DATA_PORT, DHT_DATA_PIN, GPIO_PIN_SET); delay_us(30); // 释放后稍等 // 2. 切换输入模式检测响应 DHT_DATA_GPIO_MODE_INPUT(); // 等待低电平响应超时判断 while (HAL_GPIO_ReadPin(DHT_DATA_PORT, DHT_DATA_PIN) GPIO_PIN_SET); while (HAL_GPIO_ReadPin(DHT_DATA_PORT, DHT_DATA_PIN) GPIO_PIN_RESET); // 80us低 while (HAL_GPIO_ReadPin(DHT_DATA_PORT, DHT_DATA_PIN) GPIO_PIN_SET); // 80us高 // 3. 循环读取40bit for (int i 0; i 40; i) { while (HAL_GPIO_ReadPin(DHT_DATA_PORT, DHT_DATA_PIN) GPIO_PIN_RESET); // 等bit开始 delay_us(40); // 40us处采样 if (HAL_GPIO_ReadPin(DHT_DATA_PORT, DHT_DATA_PIN) GPIO_PIN_SET) { data[i / 8] | (0x80 (i % 8)); } while (HAL_GPIO_ReadPin(DHT_DATA_PORT, DHT_DATA_PIN) GPIO_PIN_SET); // 等待bit结束 } // 4. 校验data[0]data[1]data[2]data[3] data[4] if ((uint8_t)(data[0] data[1] data[2] data[3]) ! data[4]) { return 1; // 校验失败 } *hum data[0]; *temp data[1]; return 0; }如果你的项目对精度有要求DHT11那±2℃、±5%RH的指标可能不太够用建议换SHT30或者AHT20I2C接口的传感器读起来更稳定而且不用自己掐时序。DHT11的优势就是便宜、逻辑简单、随便找都有例程做原型验证效率很高。2.2 MQTT协议与ONENET接入原理MQTT能把这套链路串起来核心是它的发布-订阅模型。你可以把broker理解成一栋楼的收发室设备A往房间号Topic里放了一封信设备B只要订阅了这个房间号收发室就会把那封信送过去。ONENET平台本质上就扮演了这栋楼收发室的角色STM32通过EC800-4G连上ONENET的MQTT地址设备上报数据就走发布平台下发指令就走订阅。MQTT有3个QoS级别这个项目上报数据我用的是QoS 0因为温湿度是周期性数据丢一帧不影响大局下一帧马上就来。如果是设备状态、报警这类不能丢的消息建议用QoS 1保证至少送达一次。QoS 2尽量避免握手流程多、开销大而且很多模块对QoS 2的支持并不完善容易出幺蛾子。报文结构方面MQTT固定头只有两个字节其中第一个字节的高4位是报文类型低4位是标志位第二个字节是剩余长度用来标识后面可变头和负载的总长度最长支持到256MB但对我们的温湿度包来说一个包也就几十个字节开销很小。剩下的可变头里CONNECT报文要带ClientId、用户名、密码、Keep Alive等字段发布报文的可变头则是主题名。EC800-4G最省心的地方在于它把这些协议细节全部封装成了AT指令。你在单片机里要做的事情只有两个给模块发AT指令建立连接然后发AT指令发布数据。模块内置协议栈会帮你完成报文组帧、QoS控制、心跳维持不需要你在STM32里移植lwIP或者MQTT库。这样做的好处是单片机端代码量大幅减少出问题的可能性也低很多坏处是如果模块固件有bug你没法绕过它自己实现。不过移远模块的MQTT指令集已经比较成熟了这个项目实测下来没遇到协议层面的问题。MQTT还有个Keep Alive机制连接建立时客户端会告诉broker一个保活周期比如60秒之后客户端和broker之间如果一直没有数据往来就要发送心跳报文来维持连接。EC800-4G的AT指令集里也支持配置这个参数我一般设成60秒如果设备端有上报就正常上报没上报就自动发心跳平台侧不会超时断开。3. 代码实现与关键流程拆解3.1 工程搭建与串口资源分配我用的是标准外设库HAL库当然也能做但标准库在串口中断和GPIO控制上写起来更直接。如果习惯用CubeMX生成HAL工程也完全没问题核心逻辑不变只是API名字换一下。串口资源分配是这块的核心决策串口用途引脚USART1调试日志接USB转TTLPA9 (TX), PA10 (RX)USART2EC800-4G通信PA2 (TX), PA3 (RX)上电后第一步先把USART1的日志打印跑通这是所有调试工作的基础。建议在main函数里加一个开机自检流程依次打印MCU启动、传感器初始化、串口初始化、开始配置模块这几条日志每一条日志都对应一个明确的阶段后面出了问题看日志就能快速定位。串口跟模块通信时我建议不要用简单的阻塞式发送因为AT指令的响应时间不确定有的指令100ms就回有的要等几秒比如网络注册。阻塞式等待会把MCU卡死。更好的做法是用串口中断接收把收到的数据放进一个环形缓冲区主循环里轮询缓冲区是否有完整响应。这个方案实现起来也就几十行代码但调试体验完全不同。3.2 AT指令控制EC800-4G的完整流程EC800-4G的AT指令集你手里要是有模块手册就对着手册看没有的话我这边以移远通用MQTT AT指令为例梳理整个流程具体指令名以你的模块型号和固件版本为准步骤功能典型AT指令1测试串口是否正常AT期望返回OK2关闭回显ATE03查询SIM卡状态ATCPIN?期望返回READY4查询信号强度ATCSQ返回rssi数值越大越好5设置APNATCGDCONT1,IP,cmnet根据运营商定6激活PDP上下文ATCGACT1,17配置MQTT参数ATQMTCFGaliauth,0,... 或者对应EC800的MQTT配置指令8打开MQTT网络ATQMTOPEN0,broker地址,18839建立MQTT连接ATQMTCONN0,clientId,username,password10发布数据ATQMTPUB0,0,0,0,topic,数据长度串口发送AT指令的时候每条指令后面必须带回车换行符\r\n这是我从一开始就踩过的坑如果只发\r或者只发\n模块直接无视。AT指令的响应是异步的每条指令发出后必须等模块返回OK、ERROR或者具体数据再发下一条。不能一股脑全发出去模块的串口缓冲区有限数据发太快会丢。MQTT连接这里特别重要ATQMTCONN里那三个参数不是随便填的必须和ONENET平台上的设备信息严格对应。ONENET平台设备接入使用的是三元组鉴权我这次用的是经典OneNET多协议接入的MQTT产品规则是ClientID设备IDUsername产品IDPassword设备密钥APIKey等等这里不同版本的ONENET平台规则还不一样。新版OneNET Studio的规则是ClientID填productID_deviceNameUsername填productIDPassword填deviceSecret。所以你在配置之前一定要先确认自己注册的是哪个平台版本。我在开发的时候先看了平台接入文档再填的AT指令参数不然随便填上去后面必然报连接失败。这里给出一个我实际使用的MQTT连接流程以OneNET Studio规则为例// 伪代码示意 const char *cmds[] { AT\r\n, ATE0\r\n, ATCPIN?\r\n, ATCSQ\r\n, ATCGDCONT1,\IP\,\cmnet\\r\n, ATCGACT1,1\r\n, // 假设模块固件用 QMTOPEN/QMTCONN 系列指令 ATQMTOPEN0,\studio-mqtt.heclouds.com\,1883\r\n, ATQMTCONN0,\productID_deviceName\,\productID\,\deviceSecret\\r\n, };如果某些指令返回ERROR不要继续往下走先回头排查。比如ATCPIN?返回ERROR那就是SIM卡没识别到ATCSQ返回99那就是没信号。把这些前置条件全部搞定之后再进MQTT流程。3.3 数据封装与定时上报实现温湿度数据要发到ONENET平台侧需要能解析的数据格式。OneNET的经典数据点上报格式是JSON最简单的长这样{temp:25.5,hum:60}设备ID、时间戳这些信息平台会根据连接信息自动关联我们只需要在JSON里把数据流的名字和值写清楚。注意在C代码里构造JSON字符串时别拿sprintf硬拼拼接尤其是有浮点数的时候浮点转字符串会引入精度问题。我习惯直接用整数乘以10来规避浮点问题比如温度25.5就存成255JSON里写成25.5这样既省了printf的浮点格式化开销也不用担心精度。定时上报这块我一开始是死循环里直接延时然后读取、上报代码简单但有个问题如果模块在发送期间出现网络异常单片机只能干等。后来我改成状态机设计主循环不断扫描当前状态按顺序推进IDLE状态定时器到了上报周期跳到SENSOR_READ。SENSOR_READ状态读DHT11拼JSON跳到MQTT_PUB。MQTT_PUB状态发AT命令等模块返回OK跳到IDLE。任意一步出错进入ERROR状态延时后重新初始化对应步骤。用状态机的好处是任何一步卡住都不会拖死整个系统而且每步的超时判断可以分别加比如读传感器5秒超时、发指令10秒超时哪个环节断了日志一目了然。这个改动让我后来排错轻松很多强烈建议你也这么做。4. ONEENET平台侧配置与数据上云4.1 创建产品与设备平台配置说起来其实不难但有些细节错了设备怎么连都连不上。先登录OneNET平台进入开发者中心创建一个产品。产品类型选物联网操作系统或者多协议接入里的MQTT根据自己的版本选。产品创建好之后进入产品详情里面有一个设备列表添加设备填设备名称、设备描述然后系统会生成三元组信息。我这里重点提醒一下平台版本的差异。早期OneNET平台和OneNET Studio平台的接入地址、鉴权方式都不一样如果你搜到一篇老教程接入地址填的是old.heclouds.com但你自己注册的是新版Studio那必然连不上。我这次用的就是OneNET Studio平台MQTT接入地址是studio-mqtt.heclouds.com端口1883明文或8883TLS。单片机连1883即可TLS在模块端配置麻烦而且温湿度数据不是高敏感数据明文传输够用。平台鉴权配置完成后建议先不着急写单片机代码用PC端的MQTT调试工具比如MQTTX先模拟一遍设备接入。这个步骤极其重要能把问题划分为平台配置错误还是设备代码错误两类。我习惯的做法是MQTTX里填上三元组ClientID填productID_deviceNameUsername填productIDPassword填deviceSecret如果MQTTX能连上平台发布消息能看到平台数据流变化那平台的配置就是对的后面单片机连不上问题就在单片机或模块侧。4.2 数据上报主题与数据流查看OneNET Studio的数据上报我用的是标准数据点主题格式为sys/{productID}/{deviceName}/dp/post/json数据负载就是上面提到的JSON{temp:25.5,hum:60}。平台收到之后在设备详情页的数据流里就能看到这两个数据点开始有数据流入。数据流的名称就是JSON里的键名也就是temp和hum。如果你用的是经典OneNET的多协议接入MQTT旧版上报主题则是$dp这个差异我在开发时反复确认过因为平台文档更新频繁网上教程又新旧混杂最好的办法是直接看你注册平台版本里的接入文档以官方文档为准。订阅方面如果后续要做平台下发指令给设备一般是订阅sys/{productID}/{deviceName}/dp/post/json/reply来接收平台对上报的应答或者订阅自定义命令主题。我这次只做了数据上行就先把上报这条链路跑通了命令下发那块留了接口位后面要加功能直接补。4.3 数据可视化与API调用数据上了平台之后可视化展示就相对简单了。OneNET平台自带可视化功能可以创建应用、添加图表控件选择数据流就能直接看到温湿度的实时曲线和最新值。对于不想自建前端的场景这个免费功能完全够用。如果要在自己的业务系统里拿数据可以调OneNET提供的HTTP API。比如通过设备ID查询最新数据点API返回JSON你可以在后端定时拉取也可以让平台通过消息队列HTTP推送。我用API最多的是做状态大屏展示客户那边想看实时温湿度不想登录平台我就写了个简单网页后端定时调OneNET API拿最新数据再渲染成图表。整个过程下来平台的配置占了前面百分之三十的时间后面都是基于API做二次扩展。5. 常见问题与排查记录5.1 硬件层模块不开机、SIM卡不识别、信号弱这里我把实际操作中遇到的问题集中列一下方便后面有人踩坑时对照。模块没有任何反应发送AT没回复。先量模块供电3.3V是否稳定最好用示波器看有没有跌落。如果供电正常检查复位引脚、使能引脚的默认电平状态有些模块需要拉高/拉低某个引脚才会开机这个数据手册里一定找得到。还有一个高频问题是串口TX和RX接反了STM32的TX要接模块的RXSTM32的RX接模块的TX两块板子如果各用各的标注一接就反直接没响应。ATCPIN?返回ERROR。SIM卡没插好或者卡座虚焊是最常见原因。我这次就遇到过重新补焊SIM卡座解决。另外物联网卡和普通手机卡都行但如果用的是某些虚拟运营商卡APN参数可能需要特殊配置比如有的卡APN不是cmnet而是别的这个问卡商要就行。ATCSQ返回99。说明没信号。先检查天线有没有接好天线座是IPEX座还是焊盘IPEX头有没有卡到位。然后检查当前环境的信号覆盖地下室、金属屏蔽柜里测到99很正常找个窗户边试试。模块刚上电时信号也会偏弱等10秒再查一次。如果信号在10到15之间rssi值这种强度能连通但可能不稳定建议换个天线位置或者用延长线把天线挪到设备外壳外面。我测试时发现天线贴着单片机主板和远离主板信号值能差出5个格这个差异对MQTT长连接的稳定性影响很明显。5.2 网络与MQTT层连不上Broker、发布失败、掉线MQTT连接返回ERROR。优先查三项地址、端口、三元组。地址填错一个字符就完蛋端口填成8883但模块没走TLS也会失败三元组格式不对平台直接拒绝连接。这时候建议先用MQTTX在电脑上验证一遍平台参数再回来查模块配置。能连上但发布失败。主题名不对或者JSON格式不对。OneNET Studio的topic写sys/{productID}/{deviceName}/dp/post/json这个字符串里的大括号要替换成实际值一个字母都不能错。JSON如果是非法格式平台不接收但MQTT协议本身不报错所以设备端看到的是发布成功模块返回OK平台上却看不到数据这个最坑。排查方法把设备上报的JSON原样打出来放进JSON解析工具里验证一次确认没问题再看主题拼写。设备运行一段时间后掉线。掉线基本都是Keep Alive参数没配对。连接时配置的保活周期如果太短模块长时间没数据、心跳没发出去broker会判定离线保活周期太长中间网络设备比如运营商NAT又可能把空闲连接断开。我实测下来60到120秒比较稳妥并且要确保模块在无上报时自动发心跳。如果模块本身没有自动心跳机制就得在MCU里加一个定时任务周期到了主动发一条PING报文。5.3 代码层与调试技巧单片机接收模块响应偶尔乱码或者丢字节。先查波特率是否和模块一致常见115200或9600。再查串口接收中断有没有正确处理中断里如果做了重活比如在中断里直接解析字符串很容易丢掉后续字节。正确做法是中断里只做缓存解析放主循环。开发阶段的排查建议先把EC800-4G模块从单片机板上拆下来用USB转TTL直接连接模块在电脑串口助手里面手动敲AT指令把整个流程先调通再接单片机。这个步骤能帮你厘清问题属于模块还是单片机。我每次做新模块集成都是这么干的看着是多了一步实际省下的排错时间远超这一步的时间。日志分级代码里可以加几个宏开关比如DEBUG_AT控制是否打印发给模块的指令和收到的响应DEBUG_MQTT控制是否打印连接状态。正式部署的时候把日志关掉省流量也省串口时间排查问题的时候再打开。5.4 一些硬件设计的个人经验最后聊几句硬件设计上我个人的心得这块很多教程不怎么提但影响非常大。EC800-4G这种Cat.1模块电源设计是整个项目稳定性的基石。模块在发射瞬间会有比较大的电流波动如果电源路径太长、走线太细、或者没有足够的去耦电容即使看起来模块上电正常一发射就可能拉低电压轻则网络注册失败重则整板复位。我做第二版板子的时候专门把模块电源走线加宽并加了地平面铺铜还把100uF电容移到了模块电源引脚正下方掉线问题大幅减少。天线布局也和稳定性直接相关。天线周围的环境越空旷越好别把天线放在单片机、显示屏这种高频噪声源旁边。如果设备要装进金属外壳天线必须引到外壳外面否则信号直接屏蔽这个很容易踩坑。最后再分享一个实际操作的技巧调试这套系统的时候我最喜欢用的工具是串口日志加时间戳。每条日志前面加一个毫秒级时间戳这样能清楚看到每一步耗时比如SIM卡注册花了多少秒、MQTT连接花了多少秒、一条AT指令最久多久返回。模块和单片机之间的AT交互如果慢得离谱就可以判断是模块侧的问题还是串口波特率的问题。另外如果你手头有逻辑分析仪建议在模块的UART_TX引脚上挂一个通道直接把模块发出的原始数据抓下来这样能看到模块到底有没有回数据、回了什么数据比只听单片机日志里解析后的结果更直观。没有逻辑分析仪的话用USB转TTL模块并联监听也行反正串口是异步的挂个高阻监听不会影响通信。这套方案跑通之后其实往上扩展的空间挺大比如加一个继电器控制设备通过平台下发指令远程开关或者把DHT11换成更精确的SHT30数据格式不变后面要接更多传感器也就在JSON里多一个键。物联网这块通信链路通了后面接什么传感器都只是时间问题。希望这篇经验对你有所帮助。本文还有配套的精品资源点击获取