当前位置:网站首页>STM32+ESP8266+MQTT协议连接OneNet物联网平台
STM32+ESP8266+MQTT协议连接OneNet物联网平台
2022-07-06 10:17:00 【华为云】
一、环境介绍
单片机采用: STM32F103C8T6
上网方式: 采用ESP8266,也可以使用其他设备代替,只要支持TCP协议即可。比如:GSM模块、有线网卡等。
开发软件: keil5
硬件连接功能: ESP8266接在STM32的串口3上。通过AT指令与ESP8266进行通信。
二、功能介绍
2.1 功能说明
通过OneNet物联网服务器实现设备数据远程上传、下发,实现数据交互(不清楚OneNet物联网服务器功能的可以百度一下进入官网看简介)。之前的OneNet服务器不支持标准MQTT协议登录的,现在官网更新之后支持标准的MQTT协议,本篇文章介绍使用STM32+ESP8266使用标准MQTT协议登录Onenet服务器,实现数据交互。实现步骤OneNet官方提供了很详细的文档,可以参考一下。
文档地址:OneNET-中国移动物联网开放平台
![点击并拖拽以移动]
![点击并拖拽以移动]
2.2 硬件资源
在当前使用的开发板上有4盏LED灯、一个蜂鸣器、4个按键,ESP8266型号是ESP-12F,STM32型号是:STM32F103C8T6。
![点击并拖拽以移动]
三、OneNet支持的MQTT协议版本
目前OneNet服务器支持MQTT 3.1.1版本,MQTT协议官网: http://mqtt.org/?spm=a2c4g.11186623.2.11.19083f86gxhJ7h
报文支持情况: 支持connect、subscribe、publish、ping、unsubscribe、disconnect等报文,不支持pubrec、pubrel、pubcomp报文。
![点击并拖拽以移动]
四、登录OneNet服务器创建物联网产品
没有注册账号的,需要提前登录官网注册账号,再进入下面步骤:
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
这里根据自己产品情况填写。
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
产品创建成功之后,点击产品名称,跳转页面,继续添加设备。
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
下面选择仪表盘的数据来源,根据自己创建的数据点选择。
![点击并拖拽以移动]
创建一个文本控件,显示数据点更新的时间,方便调试。
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
OneNte有手机版本的APP,登录之后也可以看到该页面。
下载地址:https://open.iot.10086.cn/doc/book/device-develop/multpro/sdk-doc-tool/APP.html
![点击并拖拽以移动]
![点击并拖拽以移动]
下面是手机上登录APP看到的界面效果:
![点击并拖拽以移动]
![点击并拖拽以移动]
五、OneNet服务器MQTT登录地址与订阅主题相关格式介绍
官网介绍文档地址: 设备连接_开发者文档_OneNET
![点击并拖拽以移动]
5.1 MQTT服务器登录地址
![点击并拖拽以移动]
目前MQTT协议支持两个IP地址和端口号,一个需要加密、一个不需要加密。
注意:单片机上移植加密算法很麻烦,这里采用不需要加密的端口。(IP地址: 183.230.40.96 端口: 1883)
![点击并拖拽以移动]
5.2 MQTT登录的:设备ID、用户名称、密码 格式参数
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
上面图片里说明了,OneNet的设备参数与标准MQTT协议的登录参数对应关系。 OneNet的设备参数,在设备页面可以去查看。
登录密码生成看下面步骤:
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
注意:该工具在win10系统运行可能会提示非信任程序,点击任要运行即可。
下面是生成MQTT登录密匙的工具使用示例。
编辑
注意:工具中填的参数说明请看文档介绍。
res选项参数的格式: products/{产品ID}/devices/{设备名称}
et是设置token过期时间:算出1970-1-1到你想要设置的到期时间,单位是秒,填入即可。
比如: 超时时间设置为2020-07-20 ,那么,这里填入的秒就是:1970-1-1到2020-07-20之间的秒单位时间。
Linux下代码:
#include <stdio.h>#include <time.h> #include <time.h>int main(){ time_t time_sec; time_sec=time(NULL); //当前的秒单位时间--UTC时间 printf("当前时间(秒):%ld\n",time_sec); printf("加30天的时间(秒):%ld\n",time_sec+30*24*60*60); return 0;}
key的参数格式: 就是设备创建之后,在设备详情页的key
工具生成的结果值,直接当做MQTT登录的密码。
5.3 主题订阅格式
文档地址:协议规范_开发者文档_OneNET
![点击并拖拽以移动]
![点击并拖拽以移动]
5.4 设备保活时间
![点击并拖拽以移动]
![点击并拖拽以移动]
5.5 向服务器传数据点
![点击并拖拽以移动]
![点击并拖拽以移动]
![点击并拖拽以移动]
六、核心代码
6.1 matt.c代码
#include "mqtt.h"u8 *mqtt_rxbuf;u8 *mqtt_txbuf;u16 mqtt_rxlen;u16 mqtt_txlen;u8 _mqtt_txbuf[256];//发送数据缓存区u8 _mqtt_rxbuf[256];//接收数据缓存区typedef enum{ //名字 值 报文流动方向 描述 M_RESERVED1 =0 , // 禁止 保留 M_CONNECT , // 客户端到服务端 客户端请求连接服务端 M_CONNACK , // 服务端到客户端 连接报文确认 M_PUBLISH , // 两个方向都允许 发布消息 M_PUBACK , // 两个方向都允许 QoS 1消息发布收到确认 M_PUBREC , // 两个方向都允许 发布收到(保证交付第一步) M_PUBREL , // 两个方向都允许 发布释放(保证交付第二步) M_PUBCOMP , // 两个方向都允许 QoS 2消息发布完成(保证交互第三步) M_SUBSCRIBE , // 客户端到服务端 客户端订阅请求 M_SUBACK , // 服务端到客户端 订阅请求报文确认 M_UNSUBSCRIBE , // 客户端到服务端 客户端取消订阅请求 M_UNSUBACK , // 服务端到客户端 取消订阅报文确认 M_PINGREQ , // 客户端到服务端 心跳请求 M_PINGRESP , // 服务端到客户端 心跳响应 M_DISCONNECT , // 客户端到服务端 客户端断开连接 M_RESERVED2 , // 禁止 保留}_typdef_mqtt_message;//连接成功服务器回应 20 02 00 00//客户端主动断开连接 e0 00const u8 parket_connetAck[] = {0x20,0x02,0x00,0x00};const u8 parket_disconnet[] = {0xe0,0x00};const u8 parket_heart[] = {0xc0,0x00};const u8 parket_heart_reply[] = {0xc0,0x00};const u8 parket_subAck[] = {0x90,0x03};void MQTT_Init(void){ //缓冲区赋值 mqtt_rxbuf = _mqtt_rxbuf; mqtt_rxlen = sizeof(_mqtt_rxbuf); mqtt_txbuf = _mqtt_txbuf; mqtt_txlen = sizeof(_mqtt_txbuf); memset(mqtt_rxbuf,0,mqtt_rxlen); memset(mqtt_txbuf,0,mqtt_txlen); //无条件先主动断开 MQTT_Disconnect(); delay_ms(100); MQTT_Disconnect(); delay_ms(100);}/*函数功能: 登录服务器函数返回值: 0表示成功 1表示失败*/u8 MQTT_Connect(char *ClientID,char *Username,char *Password){ u8 i,j; int ClientIDLen = strlen(ClientID); int UsernameLen = strlen(Username); int PasswordLen = strlen(Password); int DataLen; mqtt_txlen=0; //可变报头+Payload 每个字段包含两个字节的长度标识 DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2); //固定报头 //控制报文类型 mqtt_txbuf[mqtt_txlen++] = 0x10; //MQTT Message Type CONNECT //剩余长度(不包括固定头部) do { u8 encodedByte = DataLen % 128; DataLen = DataLen / 128; // if there are more data to encode, set the top bit of this byte if ( DataLen > 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); //可变报头 //协议名 mqtt_txbuf[mqtt_txlen++] = 0; // Protocol Name Length MSB mqtt_txbuf[mqtt_txlen++] = 4; // Protocol Name Length LSB mqtt_txbuf[mqtt_txlen++] = 'M'; // ASCII Code for M mqtt_txbuf[mqtt_txlen++] = 'Q'; // ASCII Code for Q mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T //协议级别 mqtt_txbuf[mqtt_txlen++] = 4; // MQTT Protocol version = 4 对于 3.1.1 版协议,协议级别字段的值是 4(0x04) //连接标志 mqtt_txbuf[mqtt_txlen++] = 0xc2; // conn flags mqtt_txbuf[mqtt_txlen++] = 0; // Keep-alive Time Length MSB mqtt_txbuf[mqtt_txlen++] = 100; // Keep-alive Time Length LSB 100S心跳包 保活时间 mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen);// Client ID length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen);// Client ID length LSB memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen); mqtt_txlen += ClientIDLen; if(UsernameLen > 0) { mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen); //username length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen); //username length LSB memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen); mqtt_txlen += UsernameLen; } if(PasswordLen > 0) { mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen); //password length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen); //password length LSB memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen); mqtt_txlen += PasswordLen; } memset(mqtt_rxbuf,0,mqtt_rxlen); MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); for(j=0;j<10;j++) { delay_ms(50); if(USART3_RX_FLAG) { memcpy((char *)mqtt_rxbuf,USART3_RX_BUFFER,USART3_RX_CNT); //memcpy for(i=0;i<USART3_RX_CNT;i++)USART1_Printf("%#x ",USART3_RX_BUFFER[i]); USART3_RX_FLAG=0; USART3_RX_CNT=0; } //CONNECT if(mqtt_rxbuf[0]==parket_connetAck[0] && mqtt_rxbuf[1]==parket_connetAck[1]) //连接成功 { return 0;//连接成功 } } return 1;}/*函数功能: MQTT订阅/取消订阅数据打包函数函数参数: topic 主题 qos 消息等级 0:最多分发一次 1: 至少分发一次 2: 仅分发一次 whether 订阅/取消订阅请求包 (1表示订阅,0表示取消订阅)返回值: 0表示成功 1表示失败*/u8 MQTT_SubscribeTopic(char *topic,u8 qos,u8 whether){ u8 i,j; mqtt_txlen=0; int topiclen = strlen(topic); int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度 //固定报头 //控制报文类型 if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //消息类型和标志订阅 else mqtt_txbuf[mqtt_txlen++] = 0xA2; //取消订阅 //剩余长度 do { u8 encodedByte = DataLen % 128; DataLen = DataLen / 128; // if there are more data to encode, set the top bit of this byte if ( DataLen > 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); //可变报头 mqtt_txbuf[mqtt_txlen++] = 0; //消息标识符 MSB mqtt_txbuf[mqtt_txlen++] = 0x0A; //消息标识符 LSB //有效载荷 mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//主题长度 MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//主题长度 LSB memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen); mqtt_txlen += topiclen; if(whether) { mqtt_txbuf[mqtt_txlen++] = qos;//QoS级别 } for(i=0;i<10;i++) { memset(mqtt_rxbuf,0,mqtt_rxlen); MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); for(j=0;j<10;j++) { delay_ms(50); if(USART3_RX_FLAG) { memcpy((char *)mqtt_rxbuf,(char*)USART3_RX_BUFFER,USART3_RX_CNT); USART3_RX_FLAG=0; USART3_RX_CNT=0; } if(mqtt_rxbuf[0]==parket_subAck[0] && mqtt_rxbuf[1]==parket_subAck[1]) //订阅成功 { return 0;//订阅成功 } } } return 1; //失败}//MQTT发布数据打包函数//topic 主题 //message 消息//qos 消息等级 u8 MQTT_PublishData(char *topic, char *message, u8 qos){ int topicLength = strlen(topic); int messageLength = strlen(message); static u16 id=0; int DataLen; mqtt_txlen=0; //有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度 //QOS为0时没有标识符 //数据长度 主题名 报文标识符 有效载荷 if(qos) DataLen = (2+topicLength) + 2 + messageLength; else DataLen = (2+topicLength) + messageLength; //固定报头 //控制报文类型 mqtt_txbuf[mqtt_txlen++] = 0x30; // MQTT Message Type PUBLISH //剩余长度 do { u8 encodedByte = DataLen % 128; DataLen = DataLen / 128; // if there are more data to encode, set the top bit of this byte if ( DataLen > 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength);//主题长度MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//主题长度LSB memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//拷贝主题 mqtt_txlen += topicLength; //报文标识符 if(qos) { mqtt_txbuf[mqtt_txlen++] = BYTE1(id); mqtt_txbuf[mqtt_txlen++] = BYTE0(id); id++; } memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength); mqtt_txlen += messageLength; MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); return mqtt_txlen;}void MQTT_SentHeart(void){ MQTT_SendBuf((u8 *)parket_heart,sizeof(parket_heart));}void MQTT_Disconnect(void){ MQTT_SendBuf((u8 *)parket_disconnet,sizeof(parket_disconnet));}void MQTT_SendBuf(u8 *buf,u16 len){ USARTx_DataSend(USART3,buf,len);}
6.2 mqtt.h代码
#ifndef __FY_MQTT_H_#define __FY_MQTT_H_#include "stm32f10x.h"#include "string.h"#include "stdio.h"#include "stdlib.h"#include "stdarg.h"#include "delay.h"#include "usart.h"#define BYTE0(dwTemp) (*( char *)(&dwTemp))#define BYTE1(dwTemp) (*((char *)(&dwTemp) + 1))#define BYTE2(dwTemp) (*((char *)(&dwTemp) + 2))#define BYTE3(dwTemp) (*((char *)(&dwTemp) + 3)) //用户名初始化void OneNet_LoginInit(char *ProductKey,char *DeviceName,char *DeviceSecret);//MQTT协议相关函数声明u8 MQTT_PublishData(char *topic, char *message, u8 qos);u8 MQTT_SubscribeTopic(char *topic,u8 qos,u8 whether);void MQTT_Init(void);u8 MQTT_Connect(char *ClientID,char *Username,char *Password);void MQTT_SentHeart(void);void MQTT_Disconnect(void);void MQTT_SendBuf(u8 *buf,u16 len);#endif
6.3 main.c 主函数代码
#include "stm32f10x.h"#include "led.h"#include "delay.h"#include "key.h"#include "usart.h"#include <string.h>#include "timer.h"#include "esp8266.h"#include "mqtt.h"/*序号 符号 编码1 + %2B2 空格%203 / %2F4 ? %3F5 % %256 # %237 & %268 = %3D*///OneNet物联网服务器的设备信息#define MQTT_ClientID "mq2"#define MQTT_UserName "361594"#define MQTT_PassWord "version=2018-10-31&res=products%2F361594%2Fdevices%2Fmq2&et=1597492895&method=sha1&sign=uqvA0KkjXw0FlN01aT6fWrGBLGw%3D"//订阅与发布的主题//格式:$sys/{产品ID}/{设备名称}/##define SET_TOPIC "$sys/361594/mq2/#" //订阅设备所有信息//格式: $sys/{产品ID}/{设备名称}/dp/post/json#define POST_TOPIC "$sys/361594/mq2/dp/post/json" //发布char mqtt_message[200];//上报数据缓存区int main(){ u32 time_cnt=0; u32 i; u8 key; LED_Init(); BEEP_Init(); KEY_Init(); USART1_Init(115200); TIMER1_Init(72,20000); //超时时间20ms USART3_Init(115200);//串口-WIFI TIMER3_Init(72,20000); //超时时间20ms USART1_Printf("正在初始化WIFI请稍等.\n"); if(ESP8266_Init()) { USART1_Printf("ESP8266硬件检测错误.\n"); } else { //加密端口 //USART1_Printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.16",8883,1)); //非加密端口 USART1_Printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.96",1883,1)); } //2. MQTT协议初始化 MQTT_Init(); //3. 连接OneNet服务器 while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord)) { USART1_Printf("OneNet服务器连接失败,正在重试...\n"); delay_ms(500); } USART1_Printf("OneNet服务器连接成功.\n"); //3. 订阅主题 if(MQTT_SubscribeTopic(SET_TOPIC,0,1)) { USART1_Printf("主题订阅失败.\n"); } else { USART1_Printf("主题订阅成功.\n"); } while(1) { key=KEY_Scan(0); if(key==2) { time_cnt=0; sprintf(mqtt_message,"{\"id\":1,\"dp\":{\"mq2\":[{\"v\":50}]}}"); MQTT_PublishData(POST_TOPIC,mqtt_message,0); USART1_Printf("发送状态1\r\n"); } else if(key==3) { time_cnt=0; sprintf(mqtt_message,"{\"id\":1,\"dp\":{\"mq2\":[{\"v\":80}]}}"); MQTT_PublishData(POST_TOPIC,mqtt_message,0); USART1_Printf("发送状态0\r\n"); } if(USART3_RX_FLAG) { USART3_RX_BUFFER[USART3_RX_CNT]='\0'; for(i=0;i<USART3_RX_CNT;i++) { USART1_Printf("%c",USART3_RX_BUFFER[i]); } USART3_RX_CNT=0; USART3_RX_FLAG=0; } //定时发送心跳包,保持连接 delay_ms(10); time_cnt++; if(time_cnt==500) { MQTT_SentHeart();//发送心跳包 time_cnt=0; } }}
七、设备登录运行效果
登录成功之后,网页会显示在线状态。
按下开发按键上传烟雾数据到服务器效果:
边栏推荐
- High precision operation
- Stealing others' vulnerability reports and selling them into sidelines, and the vulnerability reward platform gives rise to "insiders"
- J'aimerais dire quelques mots de plus sur ce problème de communication...
- 重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用
- Growth of operation and maintenance Xiaobai - week 7
- MS-TCT:Inria&SBU提出用于动作检测的多尺度时间Transformer,效果SOTA!已开源!(CVPR2022)...
- The latest financial report release + tmall 618 double top, Nike energy leads the next 50 years
- 面向程序员的精品开源字体
- Video fusion cloud platform easycvr adds multi-level grouping, which can flexibly manage access devices
- After entering Alibaba for the interview and returning with a salary of 35K, I summarized an interview question of Alibaba test engineer
猜你喜欢
Recursive way
Interview shock 62: what are the precautions for group by?
【Android】Kotlin代码编写规范化文档
偷窃他人漏洞报告变卖成副业,漏洞赏金平台出“内鬼”
declval(指导函数返回值范例)
I want to say more about this communication failure
趣-关于undefined的问题
UDP protocol: simple because of good nature, it is inevitable to encounter "city can play"
Jerry is the custom background specified by the currently used dial enable [chapter]
Declval of template in generic programming
随机推荐
Open source and safe "song of ice and fire"
[Android] kotlin code writing standardization document
Kivy tutorial: support Chinese in Kivy to build cross platform applications (tutorial includes source code)
High precision operation
F200 - UAV equipped with domestic open source flight control system based on Model Design
Declval (example of return value of guidance function)
Prophet模型的简介以及案例分析
Principle and usage of extern
《ASP.NET Core 6框架揭秘》样章发布[200页/5章]
Transport layer congestion control - slow start and congestion avoidance, fast retransmission, fast recovery
Getting started with pytest ----- allow generate report
There is a gap in traditional home decoration. VR panoramic home decoration allows you to experience the completion effect of your new house
Jerry is the custom background specified by the currently used dial enable [chapter]
The third season of Baidu online AI competition is coming in midsummer, looking for you who love AI!
面试突击62:group by 有哪些注意事项?
J'aimerais dire quelques mots de plus sur ce problème de communication...
Markdown grammar - better blogging
HMS Core 机器学习服务打造同传翻译新“声”态,AI让国际交流更顺畅
Release of the sample chapter of "uncover the secrets of asp.net core 6 framework" [200 pages /5 chapters]
Declval of template in generic programming