STM32+ESP8266+MQTT protocol to connect OneNet IOT platform

Posted by FusionComputers on Mon, 10 Jan 2022 10:55:08 +0100

1, Environment introduction

Single chip microcomputer: STM32F103C8T6

Internet access mode: ESP8266 can be used, or other devices can be used instead, as long as TCP protocol is supported. For example: GSM module, wired network card, etc.

Development software: keil5

Hardware connection function: ESP8266 is connected to serial port 3 of STM32. Communicate with ESP8266 through AT command.

Note: This article does not post the bottom programming code of ESP8266. If you do not know the bottom programming of ESP8266, please see here:

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

If you need to understand the ESP8266+MQTT protocol to connect to Alibaba cloud Internet of things server, please see here: https://blog.csdn.net/xiaolong1126626497/article/details/107311897

Download the complete source code of the project: https://download.csdn.net/download/xiaolong1126626497/15803518

2, Function introduction

2.1 function description

Remote uploading and distribution of equipment data and data interaction are realized through onenet Internet of things server (those who do not know the function of onenet Internet of things server can enter Baidu's official website to see the introduction). The previous onenet server did not support the standard MQTT protocol login. Now the official website supports the standard MQTT protocol after updating. This article introduces the use of STM32+ESP8266 to log in to the onenet server using the standard MQTT protocol to realize data interaction. Implementation steps onenet official provides a very detailed document for reference.

Document address: https://open.iot.10086.cn/devdoc/

2.2 hardware resources

There are 4 LED lights, a buzzer and 4 keys on the currently used development board. The ESP8266 model is ESP-12F and the STM32 model is STM32F103C8T6.

3, MQTT protocol version supported by OneNet

At present, OneNet server supports MQTT version 3.1.1. MQTT protocol official website: http://mqtt.org/?spm=a2c4g.11186623.2.11.19083f86gxhJ7h

Message support: connect, subscribe, publish, ping, unsubscribe, disconnect and other messages are supported, but pubrec, pubrel and pubcomp messages are not supported.

4, Log in to OneNet server to create Internet of things products

If there is no registered account, you need to log in to the official website to register the account in advance, and then enter the following steps:

Fill in here according to your own products.

After the product is created successfully, click the product name, jump to the page and continue to add the device.

Next, select the data source of the dashboard according to the data points you created.

Create a text control to display the update time of data points for debugging.

OneNte has a mobile APP. You can also see this page after logging in.

Download address: https://open.iot.10086.cn/doc/book/device-develop/multpro/sdk-doc-tool/APP.html

The following is the interface effect seen when logging in to the APP on the mobile phone:

5, Introduction to related formats of MQTT login address and subscription topic of OneNet server

Official website introduction document address: https://open.iot.10086.cn/doc/mqtt/book/get-start/connect.html

5.1 MQTT server login address

At present, MQTT protocol supports two IP addresses and port numbers, one needs encryption and the other does not need encryption.

Note: it is troublesome to transplant the encryption algorithm on the single chip microcomputer. The port that does not need encryption is used here. (IP address: 183.230.40.96 port: 1883)

5.2 MQTT login: device ID, user name, password format parameters

The above picture shows the corresponding relationship between OneNet device parameters and login parameters of standard MQTT protocol. OneNet device parameters can be viewed on the device page.

See the following steps for login password generation:

Note: the tool may prompt untrusted programs when running on win10 system. Click any to run.

The following is an example of the tool used to generate the MQTT login key.

Note: please refer to the document for the parameter description filled in the tool.

Format of res option parameter: products / {product ID}/devices / {device name}

et is to set the expiration time of the token: calculate the expiration time from 1970-1-1 to the time you want to set. The unit is seconds. Just fill in.

For example, if the timeout is set to 2020-07-20, the seconds filled in here are the second unit time between 1970-1-1 and 2020-07-20.

Code under Linux:

#include <stdio.h>
#include <time.h>
 #include <time.h>

int main()
{
    time_t time_sec;
    time_sec=time(NULL);  //Current second unit time -- UTC time
	printf("current time (second):%ld\n",time_sec);
	printf("Plus 30 days(second):%ld\n",time_sec+30*24*60*60);
	return 0;
}

Key parameter format: the key in the device details page after the device is created

The result value generated by the tool is directly used as the password for MQTT login.

5.3 theme subscription format

Document address: https://open.iot.10086.cn/doc/mqtt/book/device-develop/protocol.html

5.4 equipment live time

5.5 transmitting data points to the server

6, Core code

6.1 matt.c code

#include "mqtt.h"

u8 *mqtt_rxbuf;
u8 *mqtt_txbuf;
u16 mqtt_rxlen;
u16 mqtt_txlen;
u8 _mqtt_txbuf[256];//Send data buffer
u8 _mqtt_rxbuf[256];//Receive data buffer

typedef enum
{
	//name 	     Value 			 Message flow direction 	 describe
	M_RESERVED1	=0	,	//	prohibit 	 retain
	M_CONNECT		,	//	Client to server 	 The client requests to connect to the server
	M_CONNACK		,	//	Server to client 	 Connection message confirmation
	M_PUBLISH		,	//	Both directions are allowed 	 Release news
	M_PUBACK		,	//	Both directions are allowed 	 QoS 1 message publishing receipt confirmation
	M_PUBREC		,	//	Both directions are allowed 	 Release received (first step of guaranteed delivery)
	M_PUBREL		,	//	Both directions are allowed 	 Release release (the second step of guaranteed delivery)
	M_PUBCOMP		,	//	Both directions are allowed 	 QoS 2 message publishing is completed (the third step of ensuring interaction)
	M_SUBSCRIBE		,	//	Client to server 	 Client subscription request
	M_SUBACK		,	//	Server to client 	 Subscription request message confirmation
	M_UNSUBSCRIBE	,	//	Client to server 	 Client unsubscribe request
	M_UNSUBACK		,	//	Server to client 	 Unsubscribe message confirmation
	M_PINGREQ		,	//	Client to server 	 Heartbeat request
	M_PINGRESP		,	//	Server to client 	 Heartbeat response
	M_DISCONNECT	,	//	Client to server 	 Client disconnect
	M_RESERVED2		,	//	prohibit 	 retain
}_typdef_mqtt_message;

//Connection succeeded. The server responded 20 02 00 00
//Client actively disconnects 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)
{
    //Buffer assignment
	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);
	
	//Unconditional active disconnection
	MQTT_Disconnect();
    delay_ms(100);
	MQTT_Disconnect();
    delay_ms(100);
}

/*
Function function: login server
 Function return value: 0 means success, 1 means failure
*/
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;
	//Variable header + Payload each field contains a two byte length identifier
    DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
	
	//Fixed header
	//Control message type
    mqtt_txbuf[mqtt_txlen++] = 0x10;		//MQTT Message Type CONNECT
	//Remaining length (excluding fixed head)
	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 );
    	
	//Variable header
	//Protocol name
    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    
	//Protocol level
    mqtt_txbuf[mqtt_txlen++] = 4;        		// MQTT Protocol version = 4 for version 3.1.1 protocol, the value of the protocol level field is 4(0x04)   
	//Connection flag
    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 heartbeat packet keep alive time
	
    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]) //Connection succeeded			   
        {
            return 0;//Connection succeeded
        }
    }
    
	return 1;
}

/*
Function function: MQTT subscription / unsubscribe data packaging function
 Function parameters:
    topic       theme   
    qos         Message level 0: distribute at most once 1: distribute at least once 2: distribute only once
    whether     Subscribe / unsubscribe request package (1 means subscribe, 0 means unsubscribe)
Return value: 0 indicates success, 1 indicates failure
*/
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);//The length of the variable header (2 bytes) plus the length of the payload
	//Fixed header
	//Control message type
    if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //Message type and flag subscription
    else	mqtt_txbuf[mqtt_txlen++] = 0xA2;    //Unsubscribe

	//Remaining length
	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 );	
	
	//Variable header
    mqtt_txbuf[mqtt_txlen++] = 0;			//Message identifier MSB
    mqtt_txbuf[mqtt_txlen++] = 0x0A;        //Message identifier LSB
	//Payload
    mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//Subject length MSB
    mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//Subject length LSB   
	memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen);
    mqtt_txlen += topiclen;
    
    if(whether)
    {
       mqtt_txbuf[mqtt_txlen++] = qos;//QoS level
    }
    
    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]) //Subscription succeeded			   
			{
				return 0;//Subscription succeeded
			}
        }
    }
	return 1; //fail
}

//MQTT publish data packaging function
//Topic topic 
//Message message
//qos message level 
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;
	//The length of the payload is calculated by subtracting the length of the variable header from the value of the remaining length field in the fixed header
	//No identifier when QOS is 0
	//Data length subject name message identifier payload
    if(qos)	DataLen = (2+topicLength) + 2 + messageLength;       
    else	DataLen = (2+topicLength) + messageLength;   

    //Fixed header
	//Control message type
    mqtt_txbuf[mqtt_txlen++] = 0x30;    // MQTT Message Type PUBLISH  

	//Remaining length
	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);//Subject length MSB
    mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//Subject length LSB 
	memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//Copy theme
    mqtt_txlen += topicLength;
        
	//message identifier 
    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 code

#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))
    
//User name initialization
void OneNet_LoginInit(char *ProductKey,char *DeviceName,char *DeviceSecret);
//MQTT protocol related function declaration
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 main function code

#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"

/*
Serial number 	 Symbol 	 code
1	+	%2B
2	Space% 20
3	/	%2F
4	?	%3F
5	%	%25
6	#	%23
7	&	%26
8	=	%3D
*/

//Device information of OneNet IOT server
#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"

//Topics for subscriptions and publications
//Format: $sys / {product ID} / {device name}/#
#define SET_TOPIC  "$sys/361594/mq2/#"/ / subscribe to all device information

//Format: $sys / {product ID} / {device name} / dp/post/json
#define POST_TOPIC "$sys/361594/mq2/dp/post/json" / / publish

char mqtt_message[200];//Report data buffer

int main()
{
   u32 time_cnt=0;
   u32 i;
   u8 key;
   LED_Init();
   BEEP_Init();
   KEY_Init();
   USART1_Init(115200);
   TIMER1_Init(72,20000); //Timeout 20ms
   USART3_Init(115200);//Serial WIFI
   TIMER3_Init(72,20000); //Timeout 20ms
   USART1_Printf("Initializing WIFI One moment please.\n");
   if(ESP8266_Init())
   {
      USART1_Printf("ESP8266 Hardware detection error.\n");  
   }
   else
   {
      //Encryption port
      //USART1_Printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.16",8883,1));
      
      //Unencrypted port
      USART1_Printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.96",1883,1));
  
   }
   
    //2. MQTT protocol initialization	
    MQTT_Init(); 
    //3. Connect OneNet server        
    while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord))
    {
        USART1_Printf("OneNet Server connection failed,Retrying...\n");
        delay_ms(500);
    }
    USART1_Printf("OneNet Server connection succeeded.\n");
    
    //3. Subscribe to topics
    if(MQTT_SubscribeTopic(SET_TOPIC,0,1))
    {
        USART1_Printf("Topic subscription failed.\n");
    }
    else
    {
        USART1_Printf("Topic subscription succeeded.\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("Send status 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("Send status 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;
        }

        //Send heartbeat packets regularly and keep connected
        delay_ms(10);
        time_cnt++;
        if(time_cnt==500)
        {
            MQTT_SentHeart();//Send heartbeat packet
            time_cnt=0;
        }
    }
}

7, Device login operation effect

After successful login, the web page will display the online status.

Press the development button to upload smoke data to the server effect: