STM32 + ESP8266 + MQTT协议连接OneNet

发布时间:2023-06-21  

一、环境介绍

单片机采用:STM32F103C8T6

上网方式:采用ESP8266,也可以使用其他设备代替,只要支持TCP协议即可。比如:GSM模块、有线网卡等。

开发软件:keil5

硬件连接功能:ESP8266接在STM32的串口3上。通过AT指令与ESP8266进行通信。

注意:本篇文章没有贴ESP8266的底层编程代码,如果不会ESP8266底层编程,请看这里:

https://blog.csdn.net/xiaolong1126626497/article/details/107379554

如果需要了解ESP8266+MQTT协议连接阿里云物联网服务器请看这里:https://blog.csdn.net/xiaolong1126626497/article/details/107311897

二、功能介绍

2.1 功能说明

通过OneNet物联网服务器实现设备数据远程上传、下发,实现数据交互(不清楚OneNet物联网服务器功能的可以百度一下进入官网看简介)。之前的OneNet服务器不支持标准MQTT协议登录的,现在官网更新之后支持标准的MQTT协议,本篇文章介绍使用STM32+ESP8266使用标准MQTT协议登录Onenet服务器,实现数据交互。实现步骤OneNet官方提供了很详细的文档,可以参考一下。

文档地址:OneNET-中国移动物联网开放平台

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


2.2 硬件资源

在当前使用的开发板上有4盏LED灯、一个蜂鸣器、4个按键,ESP8266型号是ESP-12F,STM32型号是:STM32F103C8T6。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


三、OneNet支持的MQTT协议版本

目前OneNet服务器支持MQTT 3.1.1版本,MQTT协议官网:MQTT - The Standard for IoT Messaging

报文支持情况: 支持connect、subscribe、publish、ping、unsubscribe、disconnect等报文,不支持pubrec、pubrel、pubcomp报文。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

四、登录OneNet服务器创建物联网产品

没有注册账号的,需要提前登录官网注册账号,再进入下面步骤:

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


这里根据自己产品情况填写。


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


产品创建成功之后,点击产品名称,跳转页面,继续添加设备。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png



poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


下面选择仪表盘的数据来源,根据自己创建的数据点选择。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


创建一个文本控件,显示数据点更新的时间,方便调试。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


OneNte有手机版本的APP,登录之后也可以看到该页面。

下载地址:https://open.iot.10086.cn/doc/book/device-develop/multpro/sdk-doc-tool/APP.html


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


下面是手机上登录APP看到的界面效果:


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

五、OneNet服务器MQTT登录地址与订阅主题相关格式介绍

官网介绍文档地址:设备连接_开发者文档_OneNET

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


5.1 MQTT服务器登录地址

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png



目前MQTT协议支持两个IP地址和端口号,一个需要加密、一个不需要加密。

注意:单片机上移植加密算法很麻烦,这里采用不需要加密的端口。(IP地址: 183.230.40.96 端口: 1883)

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


5.2 MQTT登录的:设备ID、用户名称、密码 格式参数

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

上面图片里说明了,OneNet的设备参数与标准MQTT协议的登录参数对应关系。 OneNet的设备参数,在设备页面可以去查看。


登录密码生成看下面步骤:


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png



注意:该工具在win10系统运行可能会提示非信任程序,点击任要运行即可。

下面是生成MQTT登录密匙的工具使用示例。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


注意:工具中填的参数说明请看文档介绍。

res选项参数的格式:products/{产品ID}/devices/{设备名称}

et是设置token过期时间:算出1970-1-1到你想要设置的到期时间,单位是秒,填入即可。

比如: 超时时间设置为2020-07-20 ,那么,这里填入的秒就是:1970-1-1到2020-07-20之间的秒单位时间。

Linux下代码:

#include 
#include 
 #include 

int main()
{
    time_t time_sec;
    time_sec=time(NULL);  //当前的秒单位时间--UTC时间
	printf("当前时间(秒):%ldn",time_sec);
	printf("加30天的时间(秒):%ldn",time_sec+30*24*60*60);
	return 0;
}

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

key的参数格式: 就是设备创建之后,在设备详情页的key

工具生成的结果值,直接当做MQTT登录的密码。


5.3 主题订阅格式

文档地址:协议规范_开发者文档_OneNET

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


5.4 设备保活时间

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png



5.5 向服务器传数据点

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


poYBAGDYdXCAWkKMAAAAK8RNs4s030.png


六、核心代码

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 00

const 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;i++)usart1_printf("%#x>

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

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

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

6.3 main.c 主函数代码

复制

#include "stm32f10x.h"

#include "led.h"

#include "delay.h"

#include "key.h"

#include "usart.h"

#include 

#include "timer.h"

#include "esp8266.h"

#include "mqtt.h"


/*

序号 符号 编码

1 + %2B

2 空格%20

3 / %2F

4 ? %3F

5 % %25

6 # %23

7 & %26

8 = %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:%dn",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.16",8883,1));

      

      //非加密端口

      USART1_Printf("WIFI:%dn",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.96",1883,1));

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>