news 2026/9/16 14:30:01

STM32+ESP8266+OneNet:物联网数据上报完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32+ESP8266+OneNet:物联网数据上报完整指南

简介:面向ESP8266与STM32F103ZET6开发初学者的智能物联实战资料,演示将DHT11温湿度数据通过ESP8266模块上传至OneNET云平台。内容涵盖器件选型、引脚接线说明(ESP8266-01S与正点原子模组均适用)、云端产品与设备创建、固件烧录方法、实物演示及常见问题汇总,帮助读者快速打通“单片机—Wi-Fi模块—云平台”的完整链路。资源包为RAR压缩格式,共289个文件、58.82MB,以C语言工程源文件(h/c)、编译中间文件(o/d/axf)、固件镜像(bin)、烧录工具(exe)及说明文档(pdf/txt)为主,另包含工程配置、界面截图等辅助文件,便于对照Keil工程直接学习、调试或二次开发。目前已有497人学习下载,适合具备基础C语言和单片机知识、希望上手OneNET物联网平台的学生与开发者;整包既可作为课程设计或毕业设计的参考,也可作为入门智能家居、远程监测项目的起步模板。

1. ZET6 把数据交给 ESP8266,再由 ESP8266 POST 到 OneNet

B站搜「ESP8266 上传 OneNet ZET6」,能刷出一批播放量不低的视频,评论区出现频率最高的问题是:数据没上去、平台看不到点、串口助手卡在某条 AT 指令。这套方案的本质是一条完整链路——STM32F103ZET6 负责采集和组包,通过串口把 JSON 交给 ESP8266;ESP8266 连上路由器后,以 HTTP POST 把数据写入 OneNet 的数据点,最后在网页端画成折线图。适合三类人:做课程设计或竞赛项目、需要把单片机数据搬上云的学生;工位上想远程看设备状态的工程师;以及想搞懂 AT 指令与 HTTP 上报如何衔接的初学者。真正的难点不在代码本身,而在每一层的接通顺序和参数匹配,接下来就从这两块板子的分工说起。

2. 先分清楚 ZET6 与 ESP8266 的职责:采集归单片机,上云归 ESP8266

2.1 为什么需要两块板子:ZET6 管实时采集,ESP8266 管网络协议

STM32F103ZET6 是 512KB Flash、64KB RAM 的高容量型号,串口多、ADC 通道多、定时器资源丰富,适合干确定性强的活:定时采样、跑 DHT11 之类的单总线时序、处理本地逻辑。ESP8266 的价值在射频和协议栈,出厂固件里已经把 TCP/IP、Wi-Fi 连接、DNS、Socket 都封装成了 AT 指令,你不需要关心数据链路层。但 ESP8266 的 GPIO 少,裸模块上跑完整应用代码的体验并不好,尤其当你需要同时管 ADC、按键、显示时,引脚和内存都不够用。把它们组合在一起,就成了物联网里最经典的「主控 + 透传网卡」架构,这也是标题里同时出现 ZET6 和 ESP8266 的原因。

另有一种常见做法是给 ESP8266 刷 Arduino 固件,让模块自己采集传感器再上报,STM32 只在旁边打下手。这套方案在很多 ESP8266 入门教程里很流行,但你的项目已经以 ZET6 为主控,再引入一套 Arduino 工具链,等于把两个平台的学习曲线叠在一起,排查问题时要同时排查两套栈。我的建议是:ESP8266 在这里就当一个外设,用 AT 指令控制,代码量最少,哪层出问题也一眼能看出来。

2.2 接线与电平匹配:ZET6、ESP8266 和电源的接法

先用表格确定物理连接。以 USART2 为例,PA2 发、PA3 收:

STM32F103ZET6ESP8266 模块
PA2 (USART2_TX)RXD
PA3 (USART2_RX)TXD
GNDGND
3V3VCC
3V3 经 10kΩ 电阻EN / CH_PD

整条链路最容易出问题的是供电。ESP8266 在 Wi-Fi 发射瞬间电流可以达到 300mA 级别,如果 3.3V 电源纹波大、掉压明显,模块会不断复位,表现就是串口第一句 AT 都没有回复。ZET6 开发板上的板载 3.3V LDO 通常是为单片机设计的,余量不大,我一般给 ESP8266 单独用一个 AMS1117-3.3,从 5V 取电,再把两边的 GND 接在一起。EN/CH_PD 在 ESP-01 这类模块上必须拉高,很多模块出厂已内部上拉,但如果你用的是裸模块或二手板,建议显式接一个 10kΩ 到 3V3,避免上电后模块不工作。

电平方面,ZET6 开发板正常以 3.3V 供电时,PA2/PA3 的输出高电平就是 3.3V,与 ESP8266 兼容,直连没有问题。如果你手上是 5V 单片机或者中间还串了 TTL 转 USB 模块,才需要额外加电平转换或电阻分压,否则长期高压可能损伤 ESP8266 的 RX 引脚。

2.3 先用四条 AT 指令验证 ESP8266 能连上路由器

在写任何 STM32 代码之前,先把 ESP8266 接到 USB-TTL 上,用串口助手手动发一遍下面的序列,确认模块本身是好的:

ATE0 AT+CWMODE=1 AT+RST AT+CWJAP="你的SSID","你的密码" AT+CIFSR

ATE0 关闭命令回显,让后续输出更干净;AT+CWMODE=1 把模块设为 Station 模式,也就是只作为设备去连接路由器,而不是自己开热点;改完模式后必须重启一次,所以紧接着发 AT+RST。AT+CWJAP 带上你的 Wi-Fi 名称和密码,返回 WIFI CONNECTED 再出现 OK,表示联网成功,注意这一步要等一两秒,不要看到 OK 就去建 TCP 连接。AT+CIFSR 显示模块拿到的 IP 地址,能看到 192.168.x.x 这类地址就说明路由器已经给模块分配了地址。

如果 AT 指令完全没反应,先检查两件事:波特率是不是 115200(部分旧固件是 9600);EN 脚和供电是不是正常。如果 AT+CWJAP 一直返回 ERROR,多半是路由器开了 5GHz 频段——ESP8266 只支持 2.4GHz,手机热点也要手动切成 2.4GHz 再试。

3. OneNet 云平台接入:生成 APIKey 与数据流的三个准备步骤

3.1 在控制台创建产品和设备:老版多协议接入的 HTTP 通道

OneNet 要接收数据,先得有产品和设备。登录云平台后,找一个叫「多协议接入」的入口(不同版本控制台布局不太一样,首页找不到就站内搜索这个关键词),选择 HTTP 协议,创建一个产品。产品名随意,比如“ZET6温度上报”。然后在产品下添加设备,平台会分配一串数字格式的设备 ID,后面所有上报请求都要用到这个 ID。接着生成 APIKey:在设备或产品管理页找到 APIKey 菜单,添加一个,授权对象选刚才那台设备,不要选产品级别。设备级 APIKey 只对单台设备有效,即使将来泄露,影响范围也被限制在一台设备上。

这里说明一下平台现状。OneNet 后来的新平台主推 MQTT 和一套新的设备接入体系,但很多入门的 AT 指令教程仍然基于老版多协议接入的 HTTP 接口,原因是这条链路最短:不需要理解 topic、clientId、心跳这些概念,一条 POST 就能把一个数据点写进去。如果你看的是老视频,控制台入口可能已经挪了位置,但接口路径 api.heclouds.com 和 api-key 请求头仍然有效。遇到控制台页面不一致时,优先在平台文档里搜「HTTP 数据点」来对齐字段。

3.2 数据流模板:先建好 temp 和 hum,避免类型推断的坑

数据流可以理解为一台设备的“传感器字段”。OneNet 的机制是:你用 POST 上传一个带 id 的数值,如果这个数据流不存在,平台会自动创建出来。但自动创建的类型是按第一次上报的值推断的:第一次上传的是整数 26,平台就建一个 int 类型的数据流,之后你想传 26.5,要么被截断,要么直接报错,网页端折线图也会显示成锯齿状整数台阶。这种“上传成功但数值不对”的现象非常容易误导人,排查半天往往才发现是类型问题。

所以在上传之前,先到设备的「数据流模板」里手动添加两个数据流,名字叫 temp 和 hum,类型选 float。这一步看起来多余,但能省掉后面一整类问题。模板里加好之后,上报的 JSON 里 id 必须和模板名字一字不差,否则平台仍然会按新数据流自动创建,绕过了你预建的模板。

提示:先建模板再上传,能避免自动创建数据流导致的类型错误,这也是「上传成功但折线图不对」最常见的原因。

3.3 上报接口与请求头:curl 先验证,再交给单片机

在动板子之前,先用电脑上的 curl 验证一遍云端配置,把设备 ID 和 APIKey 填进去:

curl -X POST \ http://api.heclouds.com/devices/1234567890/datapoints \ -H "api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"datastreams":[{"id":"temp","datapoints":[{"value":26.5}]}]}'

这里 -H 指定了两个请求头:api-key 是 OneNet 用来鉴权的凭证,Content-Type 声明内容为 JSON。请求体最外层是 datastreams 数组,每项代表一个数据流,id 是你在模板里创建的字段名,datapoints 里放一个或多个数据点,value 就是数值本身。下面的表格列出了 body 里常用字段的含义:

字段作用说明
id数据流名称必须与模板一致,不能带空格
value数据点的值支持 float、int、string 等
at数据点时间可选,不填则使用平台服务器时间
datapoints数据点数组一次可以上报多个值,按顺序排列

如果一切正常,返回体是 {"errno":0,"error":"succ"}。errno 为 0 表示成功,非 0 时按 error 字段的文本内容排查,最常见的两类是 api-key 不对和 device_id 写错。curl 这一步能通,云端就算准备好了,后面调单片机时不用再怀疑云平台配置。

3.4 设备 ID、APIKey 与请求头的三个高频错误

这三个错误几乎覆盖了所有“云端配置没问题却发不出去”的场景。第一是把设备 ID 写成产品 ID 或者别的数字,OneNet 按设备路由数据点,ID 错了返回的错误提示有时并不明显,建议在控制台设备列表里复制而不是手敲。第二是用 Authorization 替代 api-key,老教程里经常混用这两种写法,但旧版 HTTP 接口认的是 api-key 这个字段名,换掉之后就返回鉴权失败。第三是 Content-Length 与实际 body 不一致,这在 curl 里不存在,因为工具会自动算,但在 STM32 代码里需要手动计算,等到了第 4 章会专门讲这个坑。

4. 核心实现:STM32 拼 HTTP 报文,ESP8266 经 CIPSEND 上传

4.1 透传与非透传:为什么上报场景推荐 CIPSEND=length

ESP8266 的 AT 固件提供两种发送 TCP 数据的方式。透传模式由 AT+CIPMODE=1 开启,进入后数据直接发出,退出时需要发一串 + 号(固件版本不同,时序要求也不一样,有时还得配合延时)。这种模式的优点是省去每次发送的长度计算,缺点是一旦退出时序不对,模块就卡在透传状态,后续 AT 指令全部失效,只能复位重来。非透传模式则要求每次发送前执行 AT+CIPSEND=<长度>,模块回一个 > 提示符,你发完指定长度的字节后自动返回 SEND OK,整个流程有明确的开始和结束,不用手动退出。对于周期上报温湿度这类短连接场景,我更推荐非透传,代码量也更少。下面的表格是两种方式的直观对比:

对比项透传模式非透传模式
发送前无需长度AT+CIPSEND=长度
发送结束需自行退出自动返回 SEND OK
代码复杂度低但状态难控略高但行为确定
适合场景长连接持续上行周期短连接上报

4.2 核心代码:拼报文、发 CIPSEND、等 OK 与 succ

下面的代码是 STM32 侧最小可用的上报函数骨架,假设 USART2 连接 ESP8266,串口中断把收到的字节以字符串形式持续写入 uart2_rx_buf(末尾补 \0):

void uart2_send(const char *s) { while (*s) { while (!(USART2->SR & USART_SR_TXE)); USART2->DR = *s++; } } uint8_t wait_resp(const char *expect, uint32_t ms) { uint32_t start = uwTick; while (uwTick - start < ms) { if (strstr((char *)uart2_rx_buf, expect)) { return 1; } } return 0; } void onenet_post(float temp) { char body[64]; char req[256]; memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf)); /* 避免上次残留干扰匹配 */ sprintf(body, "{\"datastreams\":[{\"id\":\"temp\",\"datapoints\":[{\"value\":%.1f}]}]}", temp); sprintf(req, "POST /devices/%s/datapoints HTTP/1.1\r\n" "Host: api.heclouds.com\r\n" "api-key: %s\r\n" "Content-Type: application/json\r\n" "Content-Length: %d\r\n" "\r\n" "%s", DEVICE_ID, API_KEY, (int)strlen(body), body); uart2_send("AT+CIPSTART=\"TCP\",\"api.heclouds.com\",80\r\n"); if (!wait_resp("CONNECT OK", 3000)) { uart2_send("AT+CIPCLOSE\r\n"); return; } char cipsend[32]; sprintf(cipsend, "AT+CIPSEND=%d\r\n", (int)strlen(req)); uart2_send(cipsend); if (!wait_resp(">", 1000)) return; uart2_send(req); wait_resp("SEND OK", 3000); wait_resp("succ", 3000); }

先看 body 和 req 两个缓冲区:body 只放数据流 JSON,req 把 HTTP 请求头和 body 拼成完整报文。拼报文时行尾必须用 \r\n,尤其是请求头和 body 之间的空行,少一个 \r\n 都会让 OneNet 认为请求没结束。Content-Length 用 strlen(body) 计算,指的是 body 的字节数,不包含结尾的 \0;这里最常踩的坑是用 sizeof(body),那会把整个缓冲区大小 64 算进去,服务器会一直等剩下的数据直到超时。CIPSEND 后面的长度是完整 req 的字节数,和 Content-Length 不是同一个值,别混。

注意:Content-Length 和 CIPSEND 的参数都要用 strlen 动态计算,不要用 sizeof 或者写死的魔数。

函数开头清一次串口缓冲,是为了避免上一次上报残留的 OK、SEND OK 干扰这次的匹配。wait_resp 用 strstr 在缓冲里找目标字符串,找到就返回,找不到就超时返回 0。每次上报前先建立 TCP 连接,失败时主动发 AT+CIPCLOSE 关闭连接,避免下一次 CIPSTART 返回 ALREADY CONNECTED。整个流程可以理解成:CIPSTART 建链路、CIPSEND 告知长度、发送报文、SEND OK 表示本地发出、succ 表示云端接收。

4.3 从复位到数据可见的完整 AT 指令时序

把上面的 C 代码翻译成串口日志,就是下面这一串往返。如果你用 USB-TTL 接 ESP8266 手动调试,也应该能看到一致的节奏:

AT ATE0 AT+CWMODE=1 AT+RST # 等待重启完成,出现 ready 再继续 AT+CWJAP="你的SSID","你的密码" # 返回 WIFI CONNECTED / OK AT+CIPSTART="TCP","api.heclouds.com",80 # 返回 CONNECT OK AT+CIPSEND=281 # 返回 > # 粘贴完整 HTTP 请求(含请求头和 body) # 返回 SEND OK # 返回 {"errno":0,"error":"succ"}

注意 AT+CIPSTART 的返回:CONNECT OK 表示 TCP 链路建立成功;如果返回 DNS FAIL,多半是路由器 DNS 有问题或者域名写错;如果返回 ALREADY CONNECTED,说明上一次连接没关干净,需要在代码里先发 CIPCLOSE。AT+CIPSEND 后如果迟迟不出现 > 提示符,不要盲发数据,先排查链路状态。发送字节数和 CIPSEND 参数不一致时,模块表现比较怪异:发少了连接挂起,发多了多余字节会被当作下一条 AT 指令处理,日志里会出现一段乱码。

4.4 没有传感器也能跑:先让链路通再谈数据精度

很多初学者卡在传感器时序上,其实这一环可以暂时放掉。ZET6 的 ADC 随便接一个电位器,读一个 0 到 4095 的整数,缩放成 0.0 到 100.0 的浮点数,直接塞进 onenet_post 就能把链路打通。等 OneNet 网页端的折线图出现变化,再去替换真正的温湿度传感器。链路不通时,数据来源是什么根本不重要;链路通了,再回头按 DHT11 或 SHT30 的数据手册把采集部分补上,整个调试难度会小很多。

5. ESP8266 数据没上报到 OneNet:从串口日志逐层定位,两步缩小故障范围

5.1 第一步:把 ESP8266 单独拆下来验证

当数据始终没有出现在 OneNet 时,第一个动作不是改代码,而是把物理链路拆开。用 USB-TTL 接住 ESP8266,在串口助手里手动把 4.3 节的指令序列重发一遍,如果手动能成功,说明问题出在 STM32 程序;如果手动也失败,那问题在 Wi-Fi 或云平台配置,和单片机代码毫无关系。这一步能把排查范围缩小一半,也能避免在代码里加各种 debug 打印却看不到真实网络交互的尴尬。

5.2 第二步:对照串口日志分级表定位

把串口助手里看到的现象和故障层面对应起来,按下面这张表逐条核对:

串口现象故障层优先检查项
AT 无任何回复模块本身供电电流、EN 引脚、波特率
迟迟不出现 WIFI CONNECTED路由器2.4GHz 频段、密码、信号强度
CONNECT OK 不出现TCP/DNS域名是否完整、路由器外网状态
SEND OK 后没有 succHTTP 内容api-key、device_id、Content-Length
有 succ 但折线图不对数据流模板类型是否 float、字段名是否一致

这张表基本覆盖了入门阶段能遇到的所有断点。特别注意前两行:AT 无反应优先查电源,ESP8266 对供电敏感,瞬间掉压会表现为“时好时坏”;连不上路由器先查频段,ESP8266 不支持 5GHz,手机热点默认开 5GHz 就会一直失败。

5.3 三类容易忽略的边界情况

Content-Length 和 CIPSEND 都用 strlen 计算,不要用 sizeof;在缓冲区以外的地方多一个 \0 不影响 strlen,但如果你误用了数组长度,服务器端就会一直等待剩余字节。上报周期不建议小于 5 秒,OneNet 对数据点写入有限流,更重要的是每次上报都涉及一次完整的 TCP 握手和 HTTP 解析,周期太短除了给自己制造日志噪音,没有任何收益。还要注意 HTTP 响应体不一定和 SEND OK 同时到达,wait_resp 的等待时间要留够,不要在收到 SEND OK 后立刻关 socket。

5.4 工程化的小修改:APIKey 不要留在固件里

开发阶段把 APIKey 写成宏是图省事,但如果视频会拍屏幕、代码会发到仓库,这个习惯就得改。简单做法是把 DEVICE_ID 和 APIKey 写进 ZET6 Flash 的某个固定扇区,上电时读出来拼进 req 字符串,代码仓库里只保留占位符。设备多了之后,每个设备单独创建 APIKey 而不是共用一个产品级密钥,这样即使某台设备被反读固件,影响的也只是那一台设备的数据。

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

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

Nuemark 语法完全参考:Nue 内容优先标记语言详解

Nuemark 语法完全参考&#xff1a;Nue 内容优先标记语言详解 【免费下载链接】nue Fastest way to build modern websites 项目地址: https://gitcode.com/GitHub_Trending/nu/nue Nuemark 是 Nue 项目中面向内容创作者的 Markdown 扩展格式&#xff0c;它保留了标准 Ma…

作者头像 李华
网站建设 2026/9/16 14:28:41

SpringBoot考研咨询网站源码解析:数据脚本、查询与部署

简介&#xff1a;面向Java毕业设计与课程设计的考研咨询网站系统完整交付包&#xff0c;基于SpringBoot与MySQL开发&#xff0c;覆盖学生前台、管理员后台、学生后台三大端口&#xff0c;适合需要整体方案与可运行源码的开发者参考。压缩包共1806个文件&#xff0c;约94.26MB&a…

作者头像 李华
网站建设 2026/9/16 14:28:24

燃烧污染物控制技术与仿真优化方法详解

1. 燃烧污染物控制技术概述燃烧过程产生的污染物排放一直是能源利用和工业生产中的关键环保难题。作为一名在燃烧仿真领域工作多年的工程师&#xff0c;我见证了从早期简单排放控制到如今复杂污染物协同治理的技术演进历程。燃烧污染物控制技术&#xff08;Combustion Pollutio…

作者头像 李华
网站建设 2026/9/16 14:25:46

基于反步法的船舶直线路径跟踪控制:Matlab仿真与控制器设计解析

简介&#xff1a;基于反步法的船舶直线路径跟踪控制MATLAB程序包&#xff0c;面向船舶控制、自动化、计算机等专业的学生与研究人员&#xff0c;旨在解决船舶自动循迹控制中的建模与仿真问题&#xff0c;适用于课程设计、期末大作业与毕业设计等环节。包内共8个文件&#xff0c…

作者头像 李华