当前位置:网站首页>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);#endif6.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; } }}七、设备登录运行效果
登录成功之后,网页会显示在线状态。
按下开发按键上传烟雾数据到服务器效果:


边栏推荐
- Five data structures of redis
- 2022 Summer Project Training (II)
- F200——搭载基于模型设计的国产开源飞控系统无人机
- Scratch epidemic isolation and nucleic acid detection Analog Electronics Society graphical programming scratch grade examination level 3 true questions and answers analysis June 2022
- On time and parameter selection of asemi rectifier bridge db207
- Jerry's setting currently uses the dial. Switch the dial through this function [chapter]
- VR panoramic wedding helps couples record romantic and beautiful scenes
- 2022暑期项目实训(一)
- Kivy tutorial: support Chinese in Kivy to build cross platform applications (tutorial includes source code)
- 队列的实现
猜你喜欢

QT中Model-View-Delegate委托代理机制用法介绍

STM32按键状态机2——状态简化与增加长按功能

I want to say more about this communication failure
![Jerry is the custom background specified by the currently used dial enable [chapter]](/img/32/6c22033bda8ff1b53993bacef254cd.jpg)
Jerry is the custom background specified by the currently used dial enable [chapter]

The easycvr authorization expiration page cannot be logged in. How to solve it?

微信为什么使用 SQLite 保存聊天记录?

推荐好用的后台管理脚手架,人人开源

78 year old professor Huake has been chasing dreams for 40 years, and the domestic database reaches dreams to sprint for IPO

F200——搭载基于模型设计的国产开源飞控系统无人机

Easy introduction to SQL (1): addition, deletion, modification and simple query
随机推荐
Distill knowledge from the interaction model! China University of science and Technology & meituan proposed virt, which combines the efficiency of the two tower model and the performance of the intera
面试突击62:group by 有哪些注意事项?
The integrated real-time HTAP database stonedb, how to replace MySQL and achieve nearly a hundredfold performance improvement
历史上的今天:Google 之母出生;同一天诞生的两位图灵奖先驱
After entering Alibaba for the interview and returning with a salary of 35K, I summarized an interview question of Alibaba test engineer
I want to say more about this communication failure
Fleet tutorial 13 basic introduction to listview's most commonly used scroll controls (tutorial includes source code)
adb常用命令
C language exchanges two numbers through pointers
编译原理——自上而下分析与递归下降分析构造(笔记)
UDP协议:因性善而简单,难免碰到“城会玩”
Recommend easy-to-use backstage management scaffolding, everyone open source
C语言指针*p++、*(p++)、*++p、*(++p)、(*p)++、++(*p)对比实例
2022 Summer Project Training (II)
IP, subnet mask, gateway, default gateway
编译原理——预测表C语言实现
FMT open source self driving instrument | FMT middleware: a high real-time distributed log module Mlog
Recursive way
Grafana 9.0 正式发布!堪称最强!
Five data structures of redis
![点击并拖拽以移动]