本帖最后由 宇岚 于 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: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Segoe UI"; 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
|