[APM32E0] 物联网,启动!基于OneNET平台实现物联网功能

[复制链接]
 楼主| 宇岚 发表于 2025-7-16 10:50 | 显示全部楼层 |阅读模式
本帖最后由 宇岚 于 2025-7-17 14:51 编辑

#申请原创# #技术资源#

背景

随着时代的发展,“智慧生活”一词逐渐被频繁提及;智慧生活平台是依托云计算技术的存储,在家庭场景功能融合、增值服务挖掘的指导思想下,采用主流的互联网通信渠道,配合丰富的智能家居产品终端,构建享受智能家居控制系统带来的新的生活方式,简单来讲,就是利用物联网实现嵌入式设备自动化;

简介

物联网(Internet of Things,简称 IoT),是通过传感器、通信技术、计算设备等将物理世界中的物体连接到互联网,实现物与物、物与人之间的数据交互和智能控制的系统。OneNET是由中国移动打造的PaaS物联网开放平台。平台能够帮助开发者轻松实现设备接入与设备连接,实现方法简单,功能多样,最重要的是,基础功能免费!


实现原理

IoT平台主要通信方式为MQTT协议,MQTT协议中主要参与者有三位,发布者(Publisher)、服务器(Broker)以及订阅者(Subscriber),消息结构则是分为主体(Topic)和负载(Payload)两部分;MQTT协议运行工作,简单来讲,就是发布者凭借服务器在平台发布作品,订阅者在平台订阅作品

协议结构

MQTT协议中主要参与者有三位,发布者(Publisher)、服务器(Broker)以及订阅者(Subscriber),消息结构则是分为主体(Topic)和负载(Payload)两部分;
Broker(代理服务器):MQTT 系统中负责接收和分发消息的服务器,是 MQTT 系统的核心组件。
Publisher(发布者):向 Broker 发布消息的客户端,可以选择一个或多个主题进行消息发布。
Subscriber(订阅者):订阅感兴趣的主题,并接收相关消息的客户端。
Topic(主题):用来标识消息的分类和结构。Publisher 将消息发布到特定的主题上,Subscriber 订阅特定的主题以接收相关消息。
Payload(负载):消息订阅者所具体接收的内容。

MQTT协议图.jpg

协议通信过程

MQTT协议通信过程如下:

  • 客户端连接:Publisher 或 Subscriber 通过 TCP/IP 连接到 Broker。
  • 客户端注册:客户端发送 CONNECT 报文到 Broker,包含客户端的身份认证信息、协议版本号等。
  • 会话建立:Broker 接收到 CONNECT 报文后,根据客户端的身份认证信息进行验证,并为客户端创建对应的会话。
  • 主题订阅:Subscriber 发送 SUBSCRIBE 报文到 Broker,指定订阅的主题。
  • 订阅确认:Broker 收到 SUBSCRIBE 报文后,在订阅列表中将 Subscriber 加入,并返回 SUBACK 报文给 Subscriber。
  • 消息发布:Publisher 发送 PUBLISH 报文到 Broker,包含消息的主题和内容。
  • 消息分发:Broker 收到 PUBLISH 报文后,根据主题将消息分发给订阅了该主题的所有 Subscriber。
  • 消息传递:Subscriber 收到经过分发的消息后,进行相应的处理。
  • 客户端断开:客户端发送 DISCONNECT 报文到 Broker,表明断开连接。
通信过程.jpg
其中,MQTT定义了三种消息质量等级,用于确保消息的可靠传递:
第一种消息格式为:最多一次(At Most Once):消息发布后,不进行任何确认和重传机制,消息可能会丢失或重复;
第二种消息格式为:至少一次(At Least Once):消息发布后,接收者必须返回一个确认消息(PUBACK)给发布者,如果发布者没有收到确认消息,则会重传消息,确保至少一次的消息传递;
第三种消息格式为:只有一次(Exactly Once):消息发布和传递过程中进行了多次握手和确认,确保消息只被传递一次。

最后我们来讨论一下MQTT协议通信的数据报文格式:
固定报头Fixed header),第一个字节高4位是MQTT控制报文的类型 + 第一个字节低4位用于指定控制报文类型的标志位(只在PUBLISH发布消息时用到) + 剩余长度1~4个字节(剩余长度字段最大4个字节)
可变报头(Variable header),可变报头的内容根据报文类型的不同而不同;
有效载体(Payload),某些 MQTT 控制报文在报文的最后部分包含一个有效载荷;
20760687898d87ce0f.png

实现步骤
OneNet平台建设
①搜索OneNet,进入网站,登录并进入开发者中心;
平台建设1.jpg
创建产品,点击“产品开发”,创建你的产品,并填写产品信息;
平台建设2.jpg
平台建设3.jpg
说明:所属地区随意,不会影响使用,节点类型根须自身需求选择,接入协议选择MQTT;连接方式可以选择蜂窝或者WiFi,相对简单;开发方案选择自定义方案;
③创建设备,选择谁被接入管理,点击设备管理,为产品添加设备,填写设备信息;
平台建设4.jpg
平台建设5.jpg
收集三元组,分别为产品ID、access_key以及设备名称,这三个重要数据要写入代码中,需准确无误;
平台建设6.jpg
⑤添加产品属性,添加数据流,注意:数据流名称必须与上传的名称相同;
也可以添加任何数据流,上传数据时,平台会自动创建同名数据流;
至此,代理(Breaker)建设完毕,等待发布者或订阅者传输信息;

OneNet平台连接前准备
平台建设步骤中,准备收集好的三元组,在连接平台之前需要登陆密钥,这个密钥需要通过token算法得到,不过不用担心,官方已经准备好了计算工具下载连接;
工具中需要输入以下内容:
res : products/产品ID/devices/设备名称
et : 时间戳,可直接网站搜索获取,注意:此时填写的必须比当前时间,即填写“未来”的某个时间
key:三元组中获取的access_key;
准备阶段1.jpg
③进入OneNET文档中心,查找服务器接入地址与端口,这个地址与端口会在连接时用上;
准备阶段2.jpg




一切准备好后,开始着手准备连接OneNET平台;

OneNet平台连接调试

MQTT.fx是一款基于Eclipse Paho,使用Java语言编写的MQTT协议客户端工具。支持通过Topic订阅和发布消息,用来前期和物理云平台调试非常方便;
①下载MQTT.fx软件,推荐使用1.7.1版本,免费切资源丰富;下载好之后打开,进入设置界面;
MQTTx1.jpg
②进入设置界面,填入Broker Address,Broken Point,修改通信版本
MQTTx2.jpg
③修改用户信息,第二、三步非常重要,请多次核实确保填入信息无误;
MQTTx.jpg
④设置好之后保存,点击“Connect”进行连接,状态灯显示绿色则连接正确; MQTTx4.jpg
此时,进入OneNET工作台,想要连接的设备已经处于在线状态;
MQTTx5.jpg
⑤调试订阅消息功能,指令格式:$sys/产品ID/设备名称/dp/post/json/+
MQTTx6.jpg
⑥调试发布消息功能,指令格式:$sys/产品ID/设备名称/dp/post/json ;并且发布消息内容根据一下格式进行修改;
MQTTx7.jpg
成功收到消息后,能看到此时来自平台的消息;
MQTTx8.jpg
根据以上格式,改变数据内容,多发送几次;
⑦进入平台操作界面,进入设备详情;
MQTTx10.jpg
点击数据流,即可看到刚才上传的数据名称以及其数据,别忘了打开实时刷新;
MQTTx11.jpg



点击数据,还能根据数据变化绘制曲线图;
MQTTx12.jpg
以上则是MQTT.fx与OneNET平台连接的全过程;




MCU与OneNet平台连接
硬件分析首先来说说项目使用的硬件,主控选型上采用极海半导体推出的工业级基础拓展型MCU:APM32e030R8T6,这款MCU集成搭载 Arm® Cortex®-M0 + 内核,工作主频 72MHz;而本人则是有幸能拿到极海家最新出品的MicroE030开发板;
Micro开发板.jpg
其次Wifi模块是不可或缺的部分;通过WIFI连接互联网,以实现基于OneNet平台的物联网功能;本次项目采用ESP8266作为WIFI模块;
WIFI.jpg 乐鑫ESP8266.jpg

ESP8266模块拥有一套完整的AT指令集,不需要你懂Wifi的知识,只需要利用串口进行相关的数据收发即可;常用的基础AT指令:
指令
说明
返回结果
AT
测试AT是否OK
OK
AT+RST
复位指令
OK
AT+CWMODE = 1
设置模块WIFI模式,设置为Station模式
OK
AT+CWDHCP=1
设置DHCP模式,设置为开启CWDHCP
OK
AT+CWJAP=\"账号,\"密码\"
设置模块的AP信息,连接WIFI
GOT IP
AT+CIPSTART="ip","网关","子网掩码"
设置 Station 的 IP 地址
OK
AT+CIPSEND=xxx
通过WIFI向IP发送数据
SEND OK
此模块引出了8个引脚,其解释和连线如下:
引脚名称
描述
VCC
供给电压5V
RST
PA4
EN
PA5
TXD
(RX)PA3
RXD
(TX)PA2
IO0
悬空
IO2
悬空
GND
地线

按照连线,硬件准备完毕

软件分析

模块硬件初始化:配置PA4复位引脚和PA5使能引脚,并且初始化USART模块;



  1. <span style="color: rgb(38, 38, 38); font-family: &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif, &quot;Segoe UI&quot;; background-color: rgb(248, 248, 248); font-size: 14px; font-variant-ligatures: none; text-wrap-mode: nowrap;">void ESP8266_GPIO_Init(void)</span>
模块初始化:

1.硬件模块复位,需将RST引脚拉低200ms以上,再拉高并保持高电平;

2.发送指令”AT“,保证模块通信无误;

3.发送指令”AT+CWMODE“,配置WIFI为Station模式;//wifi模式

4.发送指令”AT+CWDHCP ”,配置WIFI开启DHCP;

5.发送指令“AT+CWJAP   ”,配置WIFI模块连接服务器的账号和密码;

  1. #define ESP8266_WIFI_INFO "AT+CWJAP="Maxxx Pro","12345678"\r\n" //WIFI名称与密码


其中,Wifi发送函数与接收含函数如下:
  1. <blockquote>_Bool ESP8266_SendCmd(char *cmd, char *res,short timeout)//发送函数
在Wifi连接成功之后,就需要连接OneNet平台,回应Success,则表示连接成功,此时OneNet平台设备状态应该为绿色在线;
  1. <blockquote>#define PROID<span style="white-space:pre">                        </span>"rPt76ojBtN"<span style="white-space:pre">                                        </span>  <span style="white-space:pre">                                                        </span>//产品ID
数据发送前,需要将数据打包为OneNet平台可识别格式;
  1. unsigned char OneNet_FillBuf1(char *buf)
  2. {
  3.         
  4.         char text[200]= " " ;
  5.         memset(text, 0, sizeof(text));

  6.         strcpy(buf, "{"id":123,"dp":{");
  7.         
  8.         memset(text, 0, sizeof(text));
  9.         sprintf(text, ""Temperature":[{"v":%1f}],", Temperature);
  10.         strcat(buf, text);
  11.         
  12.         memset(text, 0, sizeof(text));
  13.         sprintf(text, ""Heart_Rate":[{"v":%d}],", hrAvg);        //心率
  14.         strcat(buf, text);
  15.         
  16.         memset(text, 0, sizeof(text));
  17.         sprintf(text, ""Blood_Oxygen":[{"v":%d}],", spo2Avg);          //心率
  18.         strcat(buf, text);
  19. //
  20.         memset(text, 0, sizeof(text));
  21.         sprintf(text, ""Longitude":[{"v":%5f}],", longitude_sum);        //经度
  22.         strcat(buf, text);
  23.         memset(text, 0, sizeof(text));
  24.         sprintf(text, ""Latitude":[{"v":%5f}]",latitude_sum);  //纬度
  25.         strcat(buf, text);
  26.         strcat(buf, "}}");
  27.         return strlen(buf);

  28. }
然后,数据需要符合MQTT报文结构,即可利用Wifi模块发送;
  1. //==========================================================
  2. //        函数名称:        MQTT_PacketSubscribe
  3. //
  4. //        函数功能:        Subscribe消息组包
  5. //
  6. //        入口参数:        pkt_id:pkt_id
  7. //                                qos:消息重发次数
  8. //                                topics:订阅的消息
  9. //                                topics_cnt:订阅的消息个数
  10. //                                mqttPacket:包指针
  11. //
  12. //        返回参数:        0-成功                其他-失败
  13. //
  14. //        说明:               
  15. //==========================================================
  16. uint8 MQTT_PacketSubscribe(uint16 pkt_id, enum MqttQosLevel qos, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket)
  17. {
  18.         
  19.         uint32 topic_len = 0, remain_len = 0;
  20.         int16 len = 0;
  21.         uint8 i = 0;
  22.         
  23.         if(pkt_id == 0)
  24.                 return 1;
  25.         
  26.         //计算topic长度-------------------------------------------------------------------------
  27.         for(; i < topics_cnt; i++)
  28.         {
  29.                 if(topics[i] == NULL)
  30.                         return 2;
  31.                
  32.                 topic_len += strlen(topics[i]);
  33.         }
  34.         
  35.         //2 bytes packet id + topic filter(2 bytes topic + topic length + 1 byte reserve)------
  36.         remain_len = 2 + 3 * topics_cnt + topic_len;
  37.         
  38.         //分配内存------------------------------------------------------------------------------
  39.         MQTT_NewBuffer(mqttPacket, remain_len + 5);
  40.         if(mqttPacket->_data == NULL)
  41.                 return 3;
  42.         
  43. /*************************************固定头部***********************************************/
  44.         
  45.         //固定头部----------------------头部消息-------------------------------------------------
  46.         mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_SUBSCRIBE << 4 | 0x02;
  47.         
  48.         //固定头部----------------------剩余长度值-----------------------------------------------
  49.         len = MQTT_DumpLength(remain_len, mqttPacket->_data + mqttPacket->_len);
  50.         if(len < 0)
  51.         {
  52.                 MQTT_DeleteBuffer(mqttPacket);
  53.                 return 4;
  54.         }
  55.         else
  56.                 mqttPacket->_len += len;
  57.         
  58. /*************************************payload***********************************************/
  59.         
  60.         //payload----------------------pkt_id---------------------------------------------------
  61.         mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id);
  62.         mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id);
  63.         
  64.         //payload----------------------topic_name-----------------------------------------------
  65.         for(i = 0; i < topics_cnt; i++)
  66.         {
  67.                 topic_len = strlen(topics[i]);
  68.                 mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len);
  69.                 mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len);
  70.                
  71.                 strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topics[i], topic_len);
  72.                 mqttPacket->_len += topic_len;
  73.                
  74.                 mqttPacket->_data[mqttPacket->_len++] = qos & 0xFF;
  75.         }

  76.         return 0;

  77. }
最后即可通过函数实现MQTT订阅和发布
  1. //==========================================================
  2. //        函数名称:        OneNET_Publish
  3. //
  4. //        函数功能:        发布消息
  5. //
  6. //        入口参数:        topic:发布的主题
  7. //                                msg:消息内容
  8. //
  9. //        返回参数:        无
  10. //
  11. //        说明:               
  12. //==========================================================
  13. void OneNET_Publish(const char *topic, const char *msg)
  14. {

  15.         MQTT_PACKET_STRUCTURE mqtt_packet = {NULL, 0, 0, 0};                                                //协议包
  16.         
  17. //        UsartPrintf(USART_DEBUG, "Publish Topic: %s, Msg: %s\r\n", topic, msg);
  18.         
  19.         if(MQTT_PacketPublish(MQTT_PUBLISH_ID, topic, msg, strlen(msg), MQTT_QOS_LEVEL0, 0, 1, &mqtt_packet) == 0)
  20.         {
  21.                 ESP8266_SendData(mqtt_packet._data, mqtt_packet._len);                                        //向平台发送订阅请求
  22.                
  23.                 MQTT_DeleteBuffer(&mqtt_packet);                                                                                //删包
  24.         }

  25. }

  26. //==========================================================
  27. //        函数名称:        OneNET_Subscribe
  28. //
  29. //        函数功能:        订阅
  30. //
  31. //        入口参数:        无
  32. //
  33. //        返回参数:        无
  34. //
  35. //        说明:               
  36. //==========================================================
  37. void OneNET_Subscribe(void)
  38. {
  39.         
  40.         MQTT_PACKET_STRUCTURE mqtt_packet = {NULL, 0, 0, 0};                                                //协议包
  41.         
  42.         char topic_buf[56];
  43.         const char *topic = topic_buf;
  44.         
  45.         snprintf(topic_buf, sizeof(topic_buf), "$sys/%s/%s/cmd/#", PROID, DEVICE_NAME);
  46.         
  47. //        UsartPrintf(USART_DEBUG, "Subscribe Topic: %s\r\n", topic_buf);
  48.         
  49.         if(MQTT_PacketSubscribe(MQTT_SUBSCRIBE_ID, MQTT_QOS_LEVEL0, &topic, 1, &mqtt_packet) == 0)
  50.         {
  51.                 ESP8266_SendData(mqtt_packet._data, mqtt_packet._len);                                        //向平台发送订阅请求
  52.                
  53.                 MQTT_DeleteBuffer(&mqtt_packet);                                                                                //删包
  54.         }

  55. }

至此,MCU即可实现利用传感器获取数据,通过WIFI模块上传OneNet平台,根据获取数据进行其他操作;

总结
本篇文章讨论了如何使用OneNet作为物联网平台,并给出详细搭建和调试步骤,并使用APME030R8T6作为主控MCU实现IoT数据交互功能;

资料文章涉及内容较为浅显,若读者对相关知识有兴趣,可跳转至下方资料深入学习MQTT学习资料:
MQTT协议
https://blog.csdn.net/weixin_44788542/article/details/129690265?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522f90e144d7df6261ab98db9b525c062d7%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=f90e144d7df6261ab98db9b525c062d7&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-129690265-null-null.142^v102^pc_search_result_base6&utm_term=MQTT&spm=1018.2226.3001.4187
OneNet物联网文档中心
https://open.iot.10086.cn/doc/aiot/fuse/search?kw=token&path=new_platform


密钥计算助手token
https://open.iot.10086.cn/college/video/onenet-portal/2024-04-19/17134946071850.exe



80125687714a329fce.png
MQTTx9.jpg
Gfan 发表于 2025-8-6 09:52 | 显示全部楼层
不得不说干货满满,从 MQTT 讲到 OneNET 平台,再到用APM32E030的 上手实操,一条龙全给整明白了
高亮加精华 安排
DawnFervor 发表于 2025-8-6 19:22 | 显示全部楼层
学习了。
这是把MQTT的协议跑在了E030上面了吧!
如果把其安排在ESP8266 可以吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

6

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部

2

主题

6

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部