打印
[APM32E0]

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

[复制链接]
603|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
宇岚|  楼主 | 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协议通信过程如下:

  • 客户端连接: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,表明断开连接。

其中,MQTT定义了三种消息质量等级,用于确保消息的可靠传递:
第一种消息格式为:最多一次(At Most Once):消息发布后,不进行任何确认和重传机制,消息可能会丢失或重复;
第二种消息格式为:至少一次(At Least Once):消息发布后,接收者必须返回一个确认消息(PUBACK)给发布者,如果发布者没有收到确认消息,则会重传消息,确保至少一次的消息传递;
第三种消息格式为:只有一次(Exactly Once):消息发布和传递过程中进行了多次握手和确认,确保消息只被传递一次。

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

实现步骤
OneNet平台建设
①搜索OneNet,进入网站,登录并进入开发者中心;

创建产品,点击“产品开发”,创建你的产品,并填写产品信息;


说明:所属地区随意,不会影响使用,节点类型根须自身需求选择,接入协议选择MQTT;连接方式可以选择蜂窝或者WiFi,相对简单;开发方案选择自定义方案;
③创建设备,选择谁被接入管理,点击设备管理,为产品添加设备,填写设备信息;


收集三元组,分别为产品ID、access_key以及设备名称,这三个重要数据要写入代码中,需准确无误;
⑤添加产品属性,添加数据流,注意:数据流名称必须与上传的名称相同;
也可以添加任何数据流,上传数据时,平台会自动创建同名数据流;
至此,代理(Breaker)建设完毕,等待发布者或订阅者传输信息;

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




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

OneNet平台连接调试

MQTT.fx是一款基于Eclipse Paho,使用Java语言编写的MQTT协议客户端工具。支持通过Topic订阅和发布消息,用来前期和物理云平台调试非常方便;
①下载MQTT.fx软件,推荐使用1.7.1版本,免费切资源丰富;下载好之后打开,进入设置界面;

②进入设置界面,填入Broker Address,Broken Point,修改通信版本
③修改用户信息,第二、三步非常重要,请多次核实确保填入信息无误;
④设置好之后保存,点击“Connect”进行连接,状态灯显示绿色则连接正确;
此时,进入OneNET工作台,想要连接的设备已经处于在线状态;

⑤调试订阅消息功能,指令格式:$sys/产品ID/设备名称/dp/post/json/+

⑥调试发布消息功能,指令格式:$sys/产品ID/设备名称/dp/post/json ;并且发布消息内容根据一下格式进行修改;

成功收到消息后,能看到此时来自平台的消息;

根据以上格式,改变数据内容,多发送几次;
⑦进入平台操作界面,进入设备详情;
点击数据流,即可看到刚才上传的数据名称以及其数据,别忘了打开实时刷新;



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




MCU与OneNet平台连接
硬件分析首先来说说项目使用的硬件,主控选型上采用极海半导体推出的工业级基础拓展型MCU:APM32e030R8T6,这款MCU集成搭载 Arm® Cortex®-M0 + 内核,工作主频 72MHz;而本人则是有幸能拿到极海家最新出品的MicroE030开发板;

其次Wifi模块是不可或缺的部分;通过WIFI连接互联网,以实现基于OneNet平台的物联网功能;本次项目采用ESP8266作为WIFI模块;


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模块;



<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模块连接服务器的账号和密码;

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


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

        strcpy(buf, "{\"id\":123,\"dp\":{");
        
        memset(text, 0, sizeof(text));
        sprintf(text, "\"Temperature\":[{\"v\":%1f}],", Temperature);
        strcat(buf, text);
        
        memset(text, 0, sizeof(text));
        sprintf(text, "\"Heart_Rate\":[{\"v\":%d}],", hrAvg);        //心率
        strcat(buf, text);
        
        memset(text, 0, sizeof(text));
        sprintf(text, "\"Blood_Oxygen\":[{\"v\":%d}],", spo2Avg);          //心率
        strcat(buf, text);
//
        memset(text, 0, sizeof(text));
        sprintf(text, "\"Longitude\":[{\"v\":%5f}],", longitude_sum);        //经度
        strcat(buf, text);
        memset(text, 0, sizeof(text));
        sprintf(text, "\"Latitude\":[{\"v\":%5f}]",latitude_sum);  //纬度
        strcat(buf, text);
        strcat(buf, "}}");
        return strlen(buf);

}
然后,数据需要符合MQTT报文结构,即可利用Wifi模块发送;
//==========================================================
//        函数名称:        MQTT_PacketSubscribe
//
//        函数功能:        Subscribe消息组包
//
//        入口参数:        pkt_id:pkt_id
//                                qos:消息重发次数
//                                topics:订阅的消息
//                                topics_cnt:订阅的消息个数
//                                mqttPacket:包指针
//
//        返回参数:        0-成功                其他-失败
//
//        说明:               
//==========================================================
uint8 MQTT_PacketSubscribe(uint16 pkt_id, enum MqttQosLevel qos, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket)
{
        
        uint32 topic_len = 0, remain_len = 0;
        int16 len = 0;
        uint8 i = 0;
        
        if(pkt_id == 0)
                return 1;
        
        //计算topic长度-------------------------------------------------------------------------
        for(; i < topics_cnt; i++)
        {
                if(topics[i] == NULL)
                        return 2;
               
                topic_len += strlen(topics[i]);
        }
        
        //2 bytes packet id + topic filter(2 bytes topic + topic length + 1 byte reserve)------
        remain_len = 2 + 3 * topics_cnt + topic_len;
        
        //分配内存------------------------------------------------------------------------------
        MQTT_NewBuffer(mqttPacket, remain_len + 5);
        if(mqttPacket->_data == NULL)
                return 3;
        
/*************************************固定头部***********************************************/
        
        //固定头部----------------------头部消息-------------------------------------------------
        mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_SUBSCRIBE << 4 | 0x02;
        
        //固定头部----------------------剩余长度值-----------------------------------------------
        len = MQTT_DumpLength(remain_len, mqttPacket->_data + mqttPacket->_len);
        if(len < 0)
        {
                MQTT_DeleteBuffer(mqttPacket);
                return 4;
        }
        else
                mqttPacket->_len += len;
        
/*************************************payload***********************************************/
        
        //payload----------------------pkt_id---------------------------------------------------
        mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id);
        mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id);
        
        //payload----------------------topic_name-----------------------------------------------
        for(i = 0; i < topics_cnt; i++)
        {
                topic_len = strlen(topics[i]);
                mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len);
                mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len);
               
                strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topics[i], topic_len);
                mqttPacket->_len += topic_len;
               
                mqttPacket->_data[mqttPacket->_len++] = qos & 0xFF;
        }

        return 0;

}
最后即可通过函数实现MQTT订阅和发布
//==========================================================
//        函数名称:        OneNET_Publish
//
//        函数功能:        发布消息
//
//        入口参数:        topic:发布的主题
//                                msg:消息内容
//
//        返回参数:        无
//
//        说明:               
//==========================================================
void OneNET_Publish(const char *topic, const char *msg)
{

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

}

//==========================================================
//        函数名称:        OneNET_Subscribe
//
//        函数功能:        订阅
//
//        入口参数:        无
//
//        返回参数:        无
//
//        说明:               
//==========================================================
void OneNET_Subscribe(void)
{
        
        MQTT_PACKET_STRUCTURE mqtt_packet = {NULL, 0, 0, 0};                                                //协议包
        
        char topic_buf[56];
        const char *topic = topic_buf;
        
        snprintf(topic_buf, sizeof(topic_buf), "$sys/%s/%s/cmd/#", PROID, DEVICE_NAME);
        
//        UsartPrintf(USART_DEBUG, "Subscribe Topic: %s\r\n", topic_buf);
        
        if(MQTT_PacketSubscribe(MQTT_SUBSCRIBE_ID, MQTT_QOS_LEVEL0, &topic, 1, &mqtt_packet) == 0)
        {
                ESP8266_SendData(mqtt_packet._data, mqtt_packet._len);                                        //向平台发送订阅请求
               
                MQTT_DeleteBuffer(&mqtt_packet);                                                                                //删包
        }

}

至此,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 (1.71 MB )

80125687714a329fce.png

MQTTx9.jpg (221.15 KB )

MQTTx9.jpg

使用特权

评论回复
沙发
Gfan| | 2025-8-6 09:52 | 只看该作者
不得不说干货满满,从 MQTT 讲到 OneNET 平台,再到用APM32E030的 上手实操,一条龙全给整明白了
高亮加精华 安排

使用特权

评论回复
板凳
DawnFervor| | 2025-8-6 19:22 | 只看该作者
学习了。
这是把MQTT的协议跑在了E030上面了吧!
如果把其安排在ESP8266 可以吗?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

6

帖子

2

粉丝