F407+ESP8266+MQTT+AliIoT(五)——MQTT移植

簡介

這裏主要講的是進行MQTT移植,對於阿里雲物聯網的連接的開發,可以使用阿里雲提供的代碼,也可以使用標準的MQTT協議代碼。
參考了網上很多的帖子,經過多方嘗試,發現這兩種方法都不可行,就採用了自己的方法,先用wireshark抓網絡包,然後讓esp8266模擬這種網絡包。

MQTT.fx接入物聯網平臺

這部分可以參考阿里雲物聯網文檔,即
阿里雲物聯網平臺 > 最佳實踐 > 設備接入 > 使用MQTT.fx接入物聯網平臺
通過wireshark監控計算機的端口1883,抓取到的MQTT數據幀有如下類型:

  1. Connect Command
  2. Connect Ack
  3. Subscribe Request
  4. Subscribe Ack
  5. Publish Message
  6. Publish Ack
  7. Ping Request
  8. Ping Respose
    其中,本地發給阿里雲數據幀1,則阿里雲返回數據幀2;本地發給阿里雲數據幀3,則阿里雲返回數據幀4;本地發給阿里雲5,則阿里雲返回數據幀6(前提是QoS=1);本地發給阿里雲數據幀7,則阿里雲返回數據幀8。當阿里雲向下傳輸數據幀5時,本地不做應答。
    參考MQTT協議版本3,在抓取到的數據的基礎上,仿寫了類似的數據幀實現了相應的功能。

生成MQTT數據幀

在程序中定義瞭如下幾個函數,完成connect、subscribe、publish和ping功能。
u8 iot_connect(void);
u8 iot_subscribe(void);
u8 iot_sendheart(void);
u8 iot_publishdata(float temp, float humi);
u16 cGetDataPUBLISH(u8 *buff,u8 qos,u8 *topic,u8 *msg);

注意事項

在mqtt.c文件中,用到了malloc.h文件,因爲在給一個buf申請內存之前,要初始化MCU的存儲空間。也就是keil工程要添加sram.c、sram.h、XMRAM.h和XMRAM.lib文件,在main函數中要有:

my_mem_init(SRAMIN);			//初始化內部內存池

相關程序源碼

啥也別說了,直接上代碼。

mqtt.c

#include "mqtt.h"
#include <stdlib.h>
#include "string.h"
#include "malloc.h"	 
#include "usart3.h"	  
#include "usart.h"	 
#include "delay.h"
 
//連接成功服務器迴應 20 02 00 00
//客戶端主動斷開連接 e0 00
const u8 mqtt_connetAck[] = {0x20,0x02,0x00,0x00};
const u8 mqtt_pubAck[] = {0x40,0x02,0x00,0x01};
const u8 mqtt_disconnet[] = {0xe0,0x00};
const u8 mqtt_heart[] = {0xc0,0x00};
const u8 mqtt_heartAck[] = {0xd0,0x00};
const u8 mqtt_subAck[] = {0x90,0x03};
 
uint16_t buflen=200;
unsigned char buf[200];

char ClientID[128];
u8 ClientID_len;

char Username[128];
u8 Username_len;

char Password[128];
u8 Password_len;
  
u8 iot_connect(void)
{
    uint32_t buflen;
    u16 i;
    u8* buf;
    buf=mymalloc(SRAMIN,200);               //申請200字節內存
    GetDataConnet(buf);  
    buflen = buf[1]+2;   
    
    // 連接兩次不成功則返回0
    u8 cnt=2;       // 嘗試發送次數
    u8 wait=30;     // 等待時間
    while(cnt--)
    {
        USART3_RX_STA=0;
        HAL_UART_Transmit(&UART1_Handler, buf, buflen,1000);    // for DEBUG
        HAL_UART_Transmit(&UART3_Handler, buf, buflen,1000);
        while(wait--)
        {
            if(USART3_RX_STA&0x8000)        // 收到應答信號,uart1轉發該應答信號
            {
                if(USART3_RX_BUF[0]==mqtt_connetAck[0] && USART3_RX_BUF[1]==mqtt_connetAck[1]) //連接成功	
                {
                    myfree(SRAMIN,buf);     //釋放內存
                    USART3_RX_STA=0;
                    printf("\r\nconnect successful!\r\n");
                    return 1;               // 連接成功
                }
            }
            delay_ms(100);
        }        
    }
    myfree(SRAMIN,buf);           //釋放內存
    USART3_RX_STA=0;
    return 0;
}
 
u8 iot_subscribe(void)
{    
    uint32_t buflen;
    u16 i;
    u8* buf;
    buf=mymalloc(SRAMIN,200);               //申請200字節內存
    GetDataSUBSCRIBE(buf,S_TOPIC_NAME,2,0);  
    buflen = buf[1]+2;
    
    // 連接兩次不成功則返回0
    u8 cnt=2;       // 嘗試發送次數
    u8 wait=30;     // 等待時間
    while(cnt--)
    {
        USART3_RX_STA=0;
        HAL_UART_Transmit(&UART1_Handler, buf, buflen,1000);    // For DEBUG
        HAL_UART_Transmit(&UART3_Handler, buf, buflen,1000);
        while(wait--)
        {
            if(USART3_RX_STA&0x8000)        // 收到應答信號,uart1轉發該應答信號
            {
                if(USART3_RX_BUF[0]==mqtt_subAck[0] && USART3_RX_BUF[1]==mqtt_subAck[1]) //連接成功	
                {
                    myfree(SRAMIN,buf);     //釋放內存
                    USART3_RX_STA=0;
                    printf("\r\nsubscribe successful!\r\n");
                    return 1;               // 連接成功
                }
            }
            delay_ms(100);
        }        
    }
    myfree(SRAMIN,buf);           //釋放內存
    USART3_RX_STA=0;
    return 0;
}


u8 iot_sendheart(void)
{    
    uint32_t buflen;
    u16 i;
    u8* buf;
    buf=mymalloc(SRAMIN,20);               //申請20字節內存
//    GetDataPINGREQ(buf);  
    buf[0]=mqtt_heart[0]; 
    buf[1]=mqtt_heart[1];
    buflen = 2;
    
    // 連接兩次不成功則返回0
    u8 cnt=2;       // 嘗試發送次數
    u16 wait=300;     // 等待時間
    while(cnt--)
    {
        USART3_RX_STA=0;
        HAL_UART_Transmit(&UART1_Handler, buf, buflen,1000);    // For DEBUG
        HAL_UART_Transmit(&UART3_Handler, buf, buflen,1000);
        while(wait--)
        {
            if(USART3_RX_STA&0x8000)        // 收到應答信號,uart1轉發該應答信號
            {
                if(USART3_RX_BUF[0]==mqtt_heartAck[0] && USART3_RX_BUF[1]==mqtt_heartAck[1]) //連接成功	
                {
                    myfree(SRAMIN,buf);     //釋放內存
                    USART3_RX_STA=0;
                    printf("\r\nsendheart successful! (cnt,wait)=%d,%d\r\n", cnt,wait);
                    return 1;               // 連接成功
                }
            }
            delay_ms(10);
        }        
    }
    myfree(SRAMIN,buf);           //釋放內存
    USART3_RX_STA=0;
    printf("\r\nsendheart failed! (cnt,wait)=%d,%d\r\n", cnt,wait);
    return 0;
}

u8 iot_publishdata(float temp, float humi)
{    
    uint32_t buflen;
    u16 i;
    u8* buf;
    buf=mymalloc(SRAMIN,200);                //申請200字節內存
    u8* msg;
    msg=mymalloc(SRAMIN,200);               //申請200字節內存
    sprintf((char*)msg,"{\"method\":\"thing.event.property.post\",\"id\":\"477655683\",\"params\":{\"temperature\":%.1f,\"humidity\":%.1f}}", temp, humi);
    buflen = cGetDataPUBLISH(buf,1,P_TOPIC_NAME,msg);
    
    printf("publishdata: topiclen=%d,msglen=%d\r\n",strlen(P_TOPIC_NAME),strlen(msg));
    
    // 連接兩次不成功則返回0
    int cnt=2;       // 嘗試發送次數
    int wait=300;     // 等待時間
    while(cnt>0)
    {
        USART3_RX_STA=0;
        HAL_UART_Transmit(&UART1_Handler, buf, buflen,1000);    // For DEBUG
        HAL_UART_Transmit(&UART3_Handler, buf, buflen,1000);
        while(wait>0)
        {
            if(USART3_RX_STA&0x8000)        // 收到應答信號,uart1轉發該應答信號
            {
                if(USART3_RX_BUF[0]==mqtt_pubAck[0] && USART3_RX_BUF[1]==mqtt_pubAck[1]) //發送成功	
                {
                    myfree(SRAMIN,msg);           //釋放內存
                    myfree(SRAMIN,buf);     //釋放內存
                    USART3_RX_STA=0;
                    printf("\r\npublish data successful! (cnt,wait)=%d,%d\r\n", cnt,wait);
                    return 1;               // 連接成功
                }
            }
            delay_ms(10);
            wait--;
        }        
        cnt--;
    }
    
    myfree(SRAMIN,msg);           //釋放內存
    myfree(SRAMIN,buf);           //釋放內存
    USART3_RX_STA=0;
    printf("\r\npublish data failed! (cnt,wait)=%d,%d\r\n", cnt,wait);
    return 0;
}
 
unsigned char GetDataFixedHead(unsigned char MesType,unsigned char DupFlag,unsigned char QosLevel,unsigned char Retain)
{
	unsigned char dat = 0;
	dat = (MesType & 0x0f) << 4;
	dat |= (DupFlag & 0x01) << 3;
	dat |= (QosLevel & 0x03) << 1;
	dat |= (Retain & 0x01);
	return dat;
}
void GetDataConnet(unsigned char *buff)//獲取連接的數據包正確連接返回20 02 00 00
{
	unsigned int i,len,lennum = 0;
	unsigned char *msg;
	buff[0] = GetDataFixedHead(MQTT_TypeCONNECT,0,0,0);
	buff[2] = 0x00;
	buff[3] = 0x04;
	buff[4] = 'M';
	buff[5] = 'Q';
	buff[6] = 'T';
	buff[7] = 'T';
	buff[8] = 0x04;//協議級別 Protocol Level
	buff[9] = 0 | (MQTT_StaCleanSession << 1) | (MQTT_StaWillFlag << 1)
                | (MQTT_StaWillQoS << 3) | (MQTT_StaWillRetain << 5) 
                | (MQTT_StaPasswordFlag << 6) |(MQTT_StaUserNameFlag << 7);//連接標誌
	buff[10] = MQTT_KeepAlive >> 8;
	buff[11] = MQTT_KeepAlive;
	len = strlen(MQTT_ClientIdentifier);
	buff[12] = len >> 8;
	buff[13] = len;
	msg = MQTT_ClientIdentifier;
	for(i = 0;i<len;i++)
	{
		buff[14+i] =  msg[i];
	}
	lennum += len;
	if(MQTT_StaWillFlag)
	{
		len = strlen(MQTT_WillTopic);
		buff[13 + lennum + 1] = len >> 8;
		buff[13 + lennum + 2] = len;
		lennum += 2;
		msg = MQTT_WillTopic;
		for(i = 0;i<len;i++)
		{
			buff[14+lennum+i] =  msg[i];
		}
		lennum += len;
		len = strlen(MQTT_WillMessage);
		buff[12] = len >> 8;
		buff[13] = len;
		lennum += 2;
		msg = MQTT_WillMessage;
		for(i = 0;i<len;i++)
		{
			buff[14+lennum+i] =  msg[i];
		}
		lennum += len;
	}
	if(MQTT_StaUserNameFlag)
	{
		len = strlen(MQTT_UserName);
		buff[13 + lennum + 1] = len >> 8;
		buff[13 + lennum + 2] = len;
		lennum += 2;
		msg = MQTT_UserName;
		for(i = 0;i<len;i++)
		{
			buff[14+lennum+i] =  msg[i];
		}
		lennum += len;
		
	}
	if(MQTT_StaPasswordFlag)
	{
		len = strlen(MQTT_Password);
		buff[13 + lennum + 1] = len >> 8;
		buff[13 + lennum + 2] = len;
		lennum += 2;
		msg = MQTT_Password;
		for(i = 0;i<len;i++)
		{
			buff[14+lennum+i] =  msg[i];
		}
		lennum += len;
	}
	buff[1] = 13 + lennum - 1;
}
void GetDataDisConnet(unsigned char *buff)//獲取斷開連接的數據包
{
	buff[0] = 0xe0;
	buff[1] = 0;
}
void GetDataPINGREQ(unsigned char *buff)//心跳請求的數據包成功返回d0 00
{
	buff[0] = 0xc0;
	buff[1] = 0;
}

//訂閱主題的數據包 
//Num:主題序號 
//RequestedQoS:服務質量要求0,1或2
//成功返回90 0x 00 Num RequestedQoS
void GetDataSUBSCRIBE(unsigned char *buff,const char *dat,unsigned int Num,unsigned char RequestedQoS)
{
	unsigned int i,len = 0,lennum = 0; 
	buff[0] = 0x82;
	len = strlen(dat);
	buff[2] = Num>>8;
	buff[3] = Num;
	buff[4] = len>>8;
	buff[5] = len;
	for(i = 0;i<len;i++)
	{
		buff[6+i] = dat[i];
	}
	lennum = len;
	buff[6 + lennum ] = RequestedQoS;
	buff[1] = lennum + 5;
}
void GetDataPUBLISH(unsigned char *buff,unsigned char dup, unsigned char qos,unsigned char retain,const char *topic ,const char *msg)//獲取發佈消息的數據包
{
	unsigned int i,len=0,lennum=0;
	buff[0] = GetDataFixedHead(MQTT_TypePUBLISH,dup,qos,retain);
	len = strlen(topic);
	buff[2] = len>>8;
	buff[3] = len;
	for(i = 0;i<len;i++)
	{
		buff[4+i] = topic[i];
	}
	lennum = len;
	len = strlen(msg);
	for(i = 0;i<len;i++)
	{
		buff[4+i+lennum] = msg[i];
	}
	lennum += len;
	buff[1] = lennum + 2;
}

u16 cGetDataPUBLISH(u8 *buff,u8 qos,u8 *topic,u8 *msg)//獲取發佈消息的數據包
{
    int topiclen = strlen(topic);
    int msglen = strlen(msg);
    u16 framelen;
    u16 len=0;
    static u16 id=0;    // message ID
    
    if(qos) framelen = (2+topiclen) + 2 + msglen;
    else    framelen = (2+topiclen) + msglen;
    
    // qos=1 則mqtt服務器有ACK信號返回
    buff[len++] = GetDataFixedHead(MQTT_TypePUBLISH,0,qos,0);     // MQTT Message Type PUBLISH
    
    // Rest length
    do
    {
        u8 encodedByte = framelen % 128;
        framelen = framelen / 128;
        // if there are more data to encode, set the top bit of this byte, see MQTT-3.1.1 manual
        if(framelen > 0)
            encodedByte = encodedByte | 128;
        buff[len++] = encodedByte;
    }while(framelen > 0);
    
    buff[len++] = topiclen / 256;  // MSB
    buff[len++] = topiclen % 256;  // LSB
    memcpy(&buff[len], topic, topiclen);
    len += topiclen;
    
    if(qos)
    {
        buff[len++] = id / 256;
        buff[len++] = id % 256;
        id++;
    }
    
    memcpy(&buff[len], msg, msglen);
    len += msglen;
    
    return len;
}

mqtt.h

#ifndef __MQTT_H
#define __MQTT_H	 
#include "sys.h"
#include <string.h>
#include "transport.h"

#define IOT_DOMAIN_NAME "productkey.iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define IOT_PORTNUM 	"1883"
#define  PRODUCTKEY           "<productkey>"                                        
#define  PRODUCTKEY_LEN       strlen(PRODUCTKEY)                                   
#define  DEVICENAME			  "<devicename>"                                              
#define  DEVICENAME_LEN       strlen(DEVICENAME)                                   
#define  DEVICESECRE          "<devicesecre>"                  				 
#define  DEVICESECRE_LEN      strlen(DEVICESECRE)

#define  S_TOPIC_NAME         "/sys/<productkey>/<devicename>/thing/service/property/set"    
#define  P_TOPIC_NAME         "/sys/<productkey>/<devicename>/thing/event/property/post" 

extern uint16_t buflen;
extern unsigned char buf[200];

u8 iot_connect(void);
u8 iot_subscribe(void);
u8 iot_sendheart(void);
u8 iot_publishdata(float temp, float humi);
u16 cGetDataPUBLISH(u8 *buff,u8 qos,u8 *topic,u8 *msg);

///////////////////////////////////////////////////////////////////////////////////
#define		MQTT_TypeCONNECT							1//請求連接  
#define		MQTT_TypeCONNACK							2//請求應答  
#define		MQTT_TypePUBLISH							3//發佈消息  
#define		MQTT_TypePUBACK								4//發佈應答  
#define		MQTT_TypePUBREC								5//發佈已接收,保證傳遞1  
#define		MQTT_TypePUBREL								6//發佈釋放,保證傳遞2  
#define		MQTT_TypePUBCOMP							7//發佈完成,保證傳遞3  
#define		MQTT_TypeSUBSCRIBE						    8//訂閱請求  
#define		MQTT_TypeSUBACK								9//訂閱應答  
#define		MQTT_TypeUNSUBSCRIBE					    10//取消訂閱  
#define		MQTT_TypeUNSUBACK							11//取消訂閱應答  
#define		MQTT_TypePINGREQ							12//ping請求  
#define		MQTT_TypePINGRESP							13//ping響應  
#define		MQTT_TypeDISCONNECT 					    14//斷開連接  
 
#define		MQTT_StaCleanSession					    1	//清理會話 
#define		MQTT_StaWillFlag							0	//遺囑標誌
#define		MQTT_StaWillQoS								0	//遺囑QoS連接標誌的第4和第3位。
#define		MQTT_StaWillRetain						    0	//遺囑保留
#define		MQTT_StaUserNameFlag					    1	//用戶名標誌 User Name Flag
#define		MQTT_StaPasswordFlag					    1	//密碼標誌 Password Flag
#define		MQTT_KeepAlive								60
#define		MQTT_ClientIdentifier  "<devicename>|securemode=3,signmethod=hmacsha1|"	    //客戶端標識符 Client Identifier
#define		MQTT_WillTopic			""				//遺囑主題 Will Topic
#define		MQTT_WillMessage		""				//遺囑消息 Will Message
#define		MQTT_UserName			"<devicename>&<productkey>"			                //用戶名 User Name
#define		MQTT_Password			"<password>"	    //密碼 Password
 
unsigned char GetDataFixedHead(unsigned char MesType,unsigned char DupFlag,unsigned char QosLevel,unsigned char Retain);
void GetDataPUBLISH(unsigned char *buff,unsigned char dup, unsigned char qos,unsigned char retain,const char *topic ,const char *msg);//獲取發佈消息的數據包		 	
void GetDataSUBSCRIBE(unsigned char *buff,const char *dat,unsigned int Num,unsigned char RequestedQoS);//訂閱主題的數據包 Num:主題序號 RequestedQoS:服務質量要求0,1或2
void GetDataDisConnet(unsigned char *buff);//獲取斷開連接的數據包
void GetDataConnet(unsigned char *buff);//獲取連接的數據包正確連接返回20 02 00 00
void GetDataPINGREQ(unsigned char *buff);//心跳請求的數據包成功返回d0 00
#endif
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章