【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器

        上一篇使用USB转串口的方式通过ESP8266wifi模块的方式成功连接上了阿里云,现在就要通过单片机来替换电脑上位机了,这样单片机自动的去调用并发送串口数据更加方便,也更加符合一个产品的开发。板载的传感器有NTC温度,光强,这两个主要用来设备上传到平台,另外一个是RGB的灯,这个主要是用来平台下发设备的接收和解析。这里为了直观我直接用串口打印出来。只要数据部分对了其他的都好说。
【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器_第1张图片网页上的运行状态图

【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器_第2张图片

温度曲线图

【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器_第3张图片

光强曲线图

【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器_第4张图片

颜色色值曲线图,这里其实是通过单片机随机函数生成的一个数据,所以变化也是比较大的,为了直观我也把这个值上传到平台。

【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器_第5张图片

平台下发数据、设备解析测试。

这里要注意一下,最开始我通过strstr方法来找出关键字“hue”,但是测了下不行,后来找到原因,估计是平台下发的josn数据包中含有多个0,比方说设备收到的是“123456\0789”,这里的‘\0’其实hex就是0x00,也正好是字符串的结尾标识符,这样通过strstr要找出“789”就找不到的,因为再者之前字符串就认为已经是断了,后来自己写了一个查找函数,解决!

网络部分和MQTT部分我都做了封装,非常方便参考和移植,不多说直接贴上代码:

main.c部分

#include "fy_includes.h"
#include "fy_tlink.h"
/*
晶振使用的是16M 其他频率在system_stm32f10x.c中修改
使用printf需要在fy_includes.h修改串口重定向为#define PRINTF_USART USART1 
*/

_typdef_adc _adc;
u16 adc_light;
u16 adc_ntc;
u16 battery;
float temperature;
u8 led_sta;
u8 sta;
void Adc_GetValue(void);

u8 txbuf[256];
u8 rxbuf[256];

char mqtt_message[200];


void Mqtt_Progress(u8 *buf,u16 len);

void UsartTrance(void)
{
	while(1)
	{
		Led_Tog();
		Delay_ms(500);
	}
}

int main(void)
{
	u8 cnt_2s=0;
	u16 cnt_5s=0;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Systick_Configuration();
    Led_Configuration();
//	UsartTrance();
	Key_Configuration();
	Adc_Configuration();
	Adc_DMA_Configuration((u32)&_adc.buf,ADC_FILTER*ADC_CH_MAX);
	Usart1_Configuration(115200);
	Usart2_Configuration(115200);
	Oled_Configuration();
	Usart1_SendString("  usart1 is ok!\r\n");
//	Usart2_SendString("  usart2 is ok!\r\n");
	Delay_ms(200);
	
//	UsartTrance();
	sta=0;
	
	//检查ESP8266
    if(_net.Init(rxbuf,sizeof(rxbuf),txbuf,sizeof(txbuf))!=0){
		Oled_ShowString(0,0*16,"Net Init OK!",8,16,1);
//		printf("Net Init OK!\r\n");
		sta++;
	}
	else{
		Oled_ShowString(0,0*16,"Net Init Error!",8,16,1);
//		printf("Net Init Error!\r\n");
		sta=0;
	}
	Oled_RefreshGram();
	
	if(sta==1){
		//连接热点
		if(_net.ConnectAP("ssid","password")!=0){
			Oled_ShowString(0,1*16,"Conncet AP OK!",8,16,1);
//			printf("Conncet AP OK!\r\n");
			sta++;
		}
		else {
			Oled_ShowString(0,1*16,"Conncet AP Error!",8,16,1);
//			printf("Conncet AP Error!\r\n");
			sta=0;
		}
		Oled_RefreshGram();
	}
	
	if(sta==2){	
		//连接TCP
		if(_net.ConnectServer("TCP","a1ugBNniFGU.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883)!=0) {
			Oled_ShowString(0,2*16,"Conet Server OK!",8,16,1);
//			printf("Conncet Server OK!\r\n");
			sta++;
		}
		else{
			Oled_ShowString(0,2*16,"Server Error!",8,16,1);
//			printf("Conncet Server Error!\r\n");	
			sta=0;
		}
		Oled_RefreshGram();
	}
	
	if(sta==3){
		//登录MQTT
		_mqtt.Init(rxbuf,sizeof(rxbuf),txbuf,sizeof(txbuf));		
		if(_mqtt.Connect(
			"123456|securemode=3,signmethod=hmacsha1,timestamp=789|",//ClientID
			"FY-STM32&a1ugBNniFGU",//Username
			"b447b9f26938d8eba6b2a4878066aae91839600c"//Password
			) != 0){
			Oled_ShowString(0,3*16,"Enter MQTT OK!",8,16,1);
//			printf("Enter MQTT OK!\r\n");
			sta++;
		}
		else{		
			Oled_ShowString(0,3*16,"Enter MQTT Error",8,16,1);
//			printf("Enter MQTT Error!\r\n");
			sta=0;
		}	
		Oled_RefreshGram();
	}	
	
	if(sta==4){
		Oled_Clear();
		//订阅主题
		if(_mqtt.SubscribeTopic("/sys/a1ugBNniFGU/FY-STM32/thing/service/property/set",0,1) != 0){
			Oled_ShowString(0,0*16,"Subscribe OK!",8,16,1);
//			printf("SubscribeTopic OK!\r\n");
		}
		else{			
			Oled_ShowString(0,0*16,"Subscribe Error!",8,16,1);
//			printf("SubscribeTopic Error!\r\n");
		}
		Oled_RefreshGram();		
	}
	Delay_ms(1000);
	Oled_Clear();			
	Oled_ShowString(32,0*16,"Mars Tech",8,16,1);
	Oled_ShowString(0,1*16," VCC: 0000 mV",8,16,1);
	Oled_ShowString(0,2*16,"Temp: 00.0 C",8,16,1);
	Oled_ShowString(0,3*16," LUX: 0000  ",8,16,1);
	Oled_RefreshGram();
	sta = 0;
    while(1)
    {
		if(++cnt_2s>=200){
			cnt_2s=0;
			Adc_GetValue();
			temperature = Ntc_GetTemp(adc_ntc)*0.1f;
			Oled_ShowNum(8*6,1*16,battery,4,8,16);			
			Oled_ShowNum(8*6,2*16,(u8)temperature,2,8,16);
			Oled_ShowNum(8*6+3*8,2*16,(u16)(temperature*10)%10,1,8,16);			
			Oled_ShowNum(8*6,3*16,adc_light,4,8,16);
			
			Oled_RefreshGram();
		}
		if(++cnt_5s>=500){//
			cnt_5s=0;
			sprintf(mqtt_message,
			"{\"method\":\"thing.service.property.set\",\"id\":\"630262306\",\"params\":{\
				\"CurrentTemperature\":%.1f,\
				\"hue\":%d,\
				\"mlux\":%d\
			},\"version\":\"1.0.0\"}",
			temperature,
			rand()%0x00ffffff,
			adc_light
			);
			
			_mqtt.PublishData("/sys/a1ugBNniFGU/FY-STM32/thing/event/property/post",mqtt_message,0);			
		}
		if(_mqtt.rxlen){
			Mqtt_Progress(_mqtt.rxbuf,_mqtt.rxlen);		
			memset(_mqtt.rxbuf,0,_mqtt.rxlen);
			_mqtt.rxlen = 0;
		}			
		Delay_ms(10); 
    }
}

void Adc_GetValue(void)
{
	u32 sum[ADC_CH_MAX];	
	memset(sum,0,sizeof(sum));
	for(u8 ch=0; chDR;
//		USART1->DR = temp;//这里用作串口1打印WIFI模块发送的数据
		if(rxlen>=255) rxlen=0;
        rxbuf[rxlen++] = temp;
        rxlen%=sizeof(rxbuf);
    }

    if(USART_GetITStatus(USART2, USART_IT_IDLE))
    {
		temp = USART2->DR;
		temp = USART2->SR;
		_mqtt.rxlen = rxlen;
//		Mqtt_Progress(rxbuf,rxlen);//主循环做异步处理
		rxlen=0;
    }
}
//void *StrStr(void *dest,void *src);

u8 *p;
void Mqtt_Progress(u8 *buf,u16 len)
{ 
	char *keyStr = "hue";
	u8 keyStrLen = strlen(keyStr)-1;
	u8 i,j;
	for(i=0;i

net.c部分

#include "fy_network.h"

//#define _DEBUG_NET

static u8 Check(void);
static u8 Init(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen);
static void Restore(void);
static u8 ConnectAP(char* ssid,char* pswd);
static u8 ConnectServer(char* mode,char* ip,u16 port);
static u8 DisconnectServer(void);
static u8 OpenTransmission(void);
static void CloseTransmission(void);
static void SendString(char* str);
static void SendBuf(u8* buf,u16 len);


_typdef_net _net=
{
	0,0,
	0,0,
	Check,
	Init,
	Restore,
	ConnectAP,
	ConnectServer,
	DisconnectServer,
	OpenTransmission,
	CloseTransmission,
	SendString,
	SendBuf
};

/**
 * 功能:初始化ESP8266
 * 参数:None
 * 返回值:初始化结果,非0为初始化成功,0为失败
 */
static u8 Init(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen)
{
	_net.rxbuf = prx;_net.rxlen = rxlen;
	_net.txbuf = ptx;_net.txlen = txlen;
	
	memset(_net.rxbuf,0,_net.rxlen);
	memset(_net.txbuf,0,_net.txlen);
	
	_net.CloseTransmission();          //退出透传 
    Delay_ms(500); 
	_net.SendString("AT+RST\r\n");   //重启ESP8266 
	Delay_ms(800);
    if(_net.Check()==0)              //使用AT指令检查ESP8266是否存在
    {
        return 0;
    }

    memset(_net.rxbuf,0,_net.rxlen);    //清空接收缓冲
    _net.SendString("ATE0\r\n");     	//关闭回显 
    if(FindStr((char*)_net.rxbuf,"OK",500)==0)  //设置不成功
    {
        return 0;      
    }
    return 1;                         //设置成功

}

/**
 * 功能:恢复出厂设置
 * 参数:None
 * 返回值:None
 * 说明:此时ESP8266中的用户设置将全部丢失回复成出厂状态
 */
static void Restore(void)
{
	_net.CloseTransmission();           //退出透传
    Delay_ms(500);
	_net.SendString("AT+RESTORE\r\n");//恢复出厂 	
//    NVIC_SystemReset();                 //同时重启单片机   
}

/**
 * 功能:检查ESP8266是否正常
 * 参数:None
 * 返回值:ESP8266返回状态
 *        非0 ESP8266正常
 *        0 ESP8266有问题  
 */
static u8 Check(void)
{
	u8 check_cnt=5;
	while(check_cnt--)
	{
		memset(_net.rxbuf,0,_net.rxlen); 	 //清空接收缓冲
		_net.SendString("AT\r\n");     		 //发送AT握手指令	
		if(FindStr((char*)_net.rxbuf,"OK",200) != 0)
		{
			return 1;
		}
	}
	return 0;
}

/**
 * 功能:连接热点
 * 参数:
 *         ssid:热点名
 *         pwd:热点密码
 * 返回值:
 *         连接结果,非0连接成功,0连接失败
 * 说明: 
 *         失败的原因有以下几种(UART通信和ESP8266正常情况下)
 *         1. WIFI名和密码不正确
 *         2. 路由器连接设备太多,未能给ESP8266分配IP
 */
static u8 ConnectAP(char* ssid,char* pswd)
{
	u8 cnt=5;
	while(cnt--)
	{
		memset(_net.rxbuf,0,_net.rxlen);     
		_net.SendString("AT+CWMODE_CUR=1\r\n");              //设置为STATION模式	
		if(FindStr((char*)_net.rxbuf,"OK",200) != 0)
		{
			break;
		}             		
	}
	if(cnt == 0) 
		return 0;

	cnt=2;
	while(cnt--)
	{
		memset(_net.txbuf,0,_net.txlen);                            //清空发送缓冲
		memset(_net.rxbuf,0,_net.rxlen);                            //清空接收缓冲
		sprintf((char*)_net.txbuf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pswd);//连接目标AP
		_net.SendString((char*)_net.txbuf);	
		if(FindStr((char*)_net.rxbuf,"OK",8000)!=0)                      //连接成功且分配到IP
		{
			return 1;
		}
	}
	return 0;
}

/**
 * 功能:使用指定协议(TCP/UDP)连接到服务器
 * 参数:
 *         mode:协议类型 "TCP","UDP"
 *         ip:目标服务器IP
 *         port:目标是服务器端口号
 * 返回值:
 *         连接结果,非0连接成功,0连接失败
 * 说明: 
 *         失败的原因有以下几种(UART通信和ESP8266正常情况下)
 *         1. 远程服务器IP和端口号有误
 *         2. 未连接AP
 *         3. 服务器端禁止添加(一般不会发生)
 */
static u8 ConnectServer(char* mode,char* ip,u16 port)
{
	u8 cnt;
   
    _net.CloseTransmission();                   //多次连接需退出透传
    Delay_ms(500);

	//连接服务器
	cnt=2;
	while(cnt--)
	{
		memset(_net.rxbuf,0,_net.rxlen);      
		memset(_net.txbuf,0,_net.txlen);   
		sprintf((char*)_net.txbuf,"AT+CIPSTART=\"%s\",\"%s\",%d\r\n",mode,ip,port);
		_net.SendString((char*)_net.txbuf);
		if(FindStr((char*)_net.rxbuf,"CONNECT",8000) !=0 )
		{
			break;
		}
	}
	if(cnt == 0) 
		return 0;
	
	//设置透传模式
	if(_net.OpenTransmission()==0) return 0;
	
	//开启发送状态
	cnt=2;
	while(cnt--)
	{
		memset(_net.rxbuf,0,_net.rxlen); 
		_net.SendString("AT+CIPSEND\r\n");//开始处于透传发送状态
		if(FindStr((char*)_net.rxbuf,">",200)!=0)
		{
			return 1;
		}
	}
	return 0;
}

/**
 * 功能:主动和服务器断开连接
 * 参数:None
 * 返回值:
 *         连接结果,非0断开成功,0断开失败
 */
static u8 DisconnectServer(void)
{
	u8 cnt;
    _net.CloseTransmission();	//退出透传
    Delay_ms(500);
	while(cnt--)
	{
		memset(_net.rxbuf,0,_net.rxlen);  
		_net.SendString("AT+CIPCLOSE\r\n");//关闭链接

		if(FindStr((char*)_net.rxbuf,"CLOSED",200)!=0)//操作成功,和服务器成功断开
		{
			break;
		}
	}
	if(cnt) return 1;
	return 0;
}

/**
 * 功能:透传模式下的数据发送函数
 * 参数:
 *      buffer:待发送数据
 * 返回值:None
 */
static void SendBuf(u8* buf,u16 len)
{
    memset(_net.rxbuf,0,_net.rxlen);
	#ifdef _DEBUG_NET
	Usart1_SendBuf(buf,len);
	#endif	
	Net_SendBuf(buf,len);
}


/**
 * 功能:透传模式下的数据发送函数
 * 参数:
 *      buffer:待发送数据
 * 返回值:None
 */
static void SendString(char* str)
{
    memset(_net.rxbuf,0,_net.rxlen);
	#ifdef _DEBUG_NET
	Usart1_SendString(str);
	#endif	
	Net_SendString(str);
}

static u8 OpenTransmission(void)
{
	//设置透传模式
	u8 cnt=2;
	while(cnt--)
	{
        memset(_net.rxbuf,0,_net.rxlen);    
        _net.SendString("AT+CIPMODE=1\r\n");  
        if(FindStr((char*)_net.rxbuf,"OK",200)!=0)
        {	
			return 1;
		}			
	}
	return 0;
}
//退出透传
static void CloseTransmission(void)
{
	_net.SendString("+++"); Delay_ms(50);
	_net.SendString("+++"); Delay_ms(50);
}

/*********************************************END OF FILE********************************************/

net.h部分

#ifndef __FY_NETWORK_H
#define __FY_NETWORK_H

#include "fy_includes.h"

/*连接AP宏定义*/
#define SSID "ssid"
#define PWD  "password"

/*连接服务器宏定义*/
#define TCP "TCP"
#define UDP "UDP"
#define IP  "122.114.122.174"
#define PORT 40915

#define Net_SendString(str) Usart2_SendString(str)
#define Net_SendBuf(buf,len) Usart2_SendBuf(buf,len)

typedef struct
{
	u8 *rxbuf;u16 rxlen;
	u8 *txbuf;u16 txlen;
	
	u8 (*Check)(void);
	u8 (*Init)(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen);
	void (*Restore)(void);
	u8 (*ConnectAP)(char *ssid,char *pswd);
	u8 (*ConnectServer)(char* mode,char *ip,u16 port);
	u8 (*DisconnectServer)(void);
	u8 (*OpenTransmission)(void);
	void (*CloseTransmission)(void);		
	void (*SendString)(char *str);
	void (*SendBuf)(u8 *buf,u16 len);
}_typdef_net;

extern _typdef_net _net;

#endif

/*********************************************END OF FILE********************************************/

mqtt.c部分

#include "fy_mqtt.h"

//#define _DEBUG_MQTT

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};

static void Mqtt_SendBuf(u8 *buf,u16 len);

static void Init(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen);
static u8 Connect(char *ClientID,char *Username,char *Password);
static u8 SubscribeTopic(char *topic,u8 qos,u8 whether);
static u8 PublishData(char *topic, char *message, u8 qos);
static void SentHeart(void);
static void Disconnect(void);

_typdef_mqtt _mqtt = 
{
	0,0,
	0,0,
	Init,
	Connect,
	SubscribeTopic,
	PublishData,
	SentHeart,
	Disconnect,
};

static void Init(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen)
{
	_mqtt.rxbuf = prx;_mqtt.rxlen = rxlen;
	_mqtt.txbuf = ptx;_mqtt.txlen = txlen;
	
	memset(_mqtt.rxbuf,0,_mqtt.rxlen);
	memset(_mqtt.txbuf,0,_mqtt.txlen);
	
	//无条件先主动断开
	_mqtt.Disconnect();Delay_ms(100);
	_mqtt.Disconnect();Delay_ms(100);
}


//连接服务器的打包函数
static u8 Connect(char *ClientID,char *Username,char *Password)
{
    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    
	//连接标志
    _mqtt.txbuf[_mqtt.txlen++] = 0xc2;        	// conn flags 
    _mqtt.txbuf[_mqtt.txlen++] = 0;        		// Keep-alive Time Length MSB    
    _mqtt.txbuf[_mqtt.txlen++] = 60;        	// Keep-alive Time Length LSB  60S心跳包  
	
    _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; 
    }    
	
	u8 cnt=2;
	u8 wait;
	while(cnt--)
	{
		memset(_mqtt.rxbuf,0,_mqtt.rxlen);
		Mqtt_SendBuf(_mqtt.txbuf,_mqtt.txlen);
		wait=30;//等待3s时间
		while(wait--)
		{
			//CONNECT
			if(_mqtt.rxbuf[0]==parket_connetAck[0] && _mqtt.rxbuf[1]==parket_connetAck[1]) //连接成功			   
			{
				return 1;//连接成功
			}
			Delay_ms(100);			
		}
	}
	return 0;
}


//MQTT订阅/取消订阅数据打包函数
//topic       主题 
//qos         消息等级 
//whether     订阅/取消订阅请求包
static u8 SubscribeTopic(char *topic,u8 qos,u8 whether)
{    
	_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++] = 0x01;           //消息标识符 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级别
    }
	
	u8 cnt=2;
	u8 wait;
	while(cnt--)
	{
		memset(_mqtt.rxbuf,0,_mqtt.rxlen);
		Mqtt_SendBuf(_mqtt.txbuf,_mqtt.txlen);
		wait=30;//等待3s时间
		while(wait--)
		{
			if(_mqtt.rxbuf[0]==parket_subAck[0] && _mqtt.rxbuf[1]==parket_subAck[1]) //订阅成功			   
			{
				return 1;//订阅成功
			}
			Delay_ms(100);			
		}
	}
	if(cnt) return 1;	//订阅成功
	return 0;
}

//MQTT发布数据打包函数
//topic   主题 
//message 消息
//qos     消息等级 
static u8 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;
}

static void SentHeart(void)
{
	Mqtt_SendBuf((u8 *)parket_heart,sizeof(parket_heart));
}

static void Disconnect(void)
{
	Mqtt_SendBuf((u8 *)parket_disconnet,sizeof(parket_disconnet));
}

static void Mqtt_SendBuf(u8 *buf,u16 len)
{
	#ifdef _DEBUG_MQTT
	Usart1_SendBuf(buf,len);
	#endif
	Usart2_SendBuf(buf,len);
}	

/*********************************************END OF FILE********************************************/

mqtt.h部分

#ifndef __FY_MQTT_H_
#define __FY_MQTT_H_

#include "fy_includes.h"



typedef struct
{
	u8 *rxbuf;u16 rxlen;
	u8 *txbuf;u16 txlen;
	void (*Init)(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen);
	u8 (*Connect)(char *ClientID,char *Username,char *Password);
	u8 (*SubscribeTopic)(char *topic,u8 qos,u8 whether);
	u8 (*PublishData)(char *topic, char *message, u8 qos);
	void (*SendHeart)(void);
	void (*Disconnect)(void);
}_typdef_mqtt;

extern _typdef_mqtt _mqtt;

#endif

完整工程:https://download.csdn.net/download/qq997758497/11239490。

By Urien 2019年6月13日 14:59:29

 

你可能感兴趣的:(单片机,MQTT)