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

2023-11-19

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

温度曲线图

光强曲线图

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

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

这里要注意一下,最开始我通过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; ch<ADC_CH_MAX; ch++)
	{
		for(u8 filter=0; filter<ADC_FILTER; filter++)
		{
			sum[ch] += _adc.buf[filter][ch];//计算累加和
		}		
	}
	adc_light = sum[ADC_LIGHT]/ADC_FILTER;
	adc_ntc = sum[ADC_NTC]/ADC_FILTER;
	battery = sum[ADC_VCC]/ADC_FILTER*3300/4095*11; 	//mV
}

u8 temp;
void USART2_IRQHandler(void)
{
    static u8 rxlen = 0;

    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //判断接收数据寄存器是否有数据
    {
		temp = USART2->DR;
//		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<len-keyStrLen;i++)
	{
		for(j=0;j<keyStrLen;j++)
		{
			if(buf[i+j] != keyStr[j])	break;
		}	
		if(j==keyStrLen) break;
	}
	if(i<=len-keyStrLen)
    {
		u16 temp = 0;
		p = &buf[i];
				
		while(*p != ':') p++;
		p++;
		while(*p != '}')
		{
			temp = temp *10 + (*p - '0');
			p++;
		}
		printf("receive message, hue = %d \r\n",temp);
//		Usart1_SendBuf(buf,len); 
    }
}

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

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

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 基于单片机设计的电子指南针(LSM303DLH模块(三轴磁场 + 三轴加速度)

    一 前言 本项目是基于单片机设计的电子指南针 主要利用STC89C52作为主控芯片和LSM303DLH模块作为指南针模块 通过LCD1602液晶显示屏来展示检测到的指南针信息 在日常生活中 指南针是一种非常实用的工具 可以帮助我们确定方向
  • 【Proteus仿真】【Arduino单片机】视力保护仪

    文章目录 一 功能简介 二 软件设计 三 实验现象 联系作者 一 功能简介 本项目使用Proteus8仿真Arduino单片机控制器 使LCD1602液晶 DS18B20温度传感器 按键 蜂鸣器 继电器开关 HC05蓝牙模块等 主要功能 系
  • Paho MqttClient 的发布方法永远阻塞

    这非常适合启动连接并发布第一条消息 MemoryPersistence persistence new MemoryPersistence client new MqttClient tcp IrisProperties MQTT SERV
  • Paho MqttAndroidClient.connect 总是失败

    我想从 Android 发布消息service到本地服务器 这是我的代码的一部分 以最简单的形式基于片段here https stackoverflow com questions 24791118 android paho mqtt se
  • Mosquitto套接字读取错误Arduino客户端

    我刚刚从 Github 下载了最新的 Arduino 库代码 它破坏了我的 MQTT 客户端程序 我在 Arduino 上使用 PubSubClient 1 91 在 Mac OSX 上使用 Mosquitto 1 1 2 Build 20
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • 【MCU简单且容易理解的工作原理】

    SOC是啥 System on Chip 一个芯片 但是片上有好多东西的意思 市面上的AI芯片 包括你知道的麒麟xxx 骁龙xxx等等一些列手机芯片都是SOC 对于SOC设计者来讲 显然要知道码农们码出来的一行行代码是如何在SOC上跑起来的
  • 世微 AP2402降压恒流驱动IC LED刹车灯方案 全亮 半亮 瀑闪

    AP2402 是一款 PWM 工作模式 高效 率 外围简单 内置功率管 适用于 5 100V 输入的高精度降压 LED 恒流驱动芯片 输 出功率可达 15W 电流 1 5A AP2402 可实现三段功能切换 通过 MODE1 2 3 切换三
  • 如何测试“Mosquitto”服务器?

    我是新来的Mosquitto and MQTT 我下载了Mosquitto服务器库 但我不知道如何测试它 有什么办法可以测试一下Mosquitto server 在单独的终端窗口中执行以下操作 启动代理 mosquitto 启动命令行订阅者
  • 使用 TLS 的 node.js mqtt 客户端

    我正在尝试使用下面的包来实现带有 TLS 的 node js mqtt 客户端 https www npmjs com package mqtt client https www npmjs com package mqtt client
  • systick定时器

    systick定时器 文章目录 前言 一 前期疑惑 二 解答 1 关于systick是阻塞的吗 2 非阻塞 三 软件编写 总结 前言 这边记录systick相关知识点 一 前期疑惑 在学习systick志气啊 其实对于systick还是一脸
  • 最大 MQTT 连接数

    我需要创建一个服务器场 可以处理 5 100 万个连接 5 00000 个主题 每个客户端一个 每秒处理 300k 消息 我尝试了解各种消息代理的功能 因此我目前使用两个 RHEL EC2 实例 r3 4xlarge 来获取大量可用资源 所
  • 在 mqtt.js 中发布无法访问的主题时如何捕获“访问错误”?

    我正在使用带有 mqtt js 的 node js 和 mosquitto 代理创建一个项目 在 mosquitto 配置文件中 我设置了 pwfile aclfile 来控制哪个用户可以访问哪个主题 如果用户名 密码 发布主题和订阅主题正
  • 将 Reactjs 连接到 Myqtthub

    您好 我对所有物联网事物都很陌生 我希望能够使用 mqtt 从 Arduino 发送和接收数据https myqtthub com https myqtthub com作为我们的经纪人 我使用以下代码进行连接 import React Co
  • MQTT:如何知道 puback 是针对哪条消息的?

    我正在尝试设置一个 MQTT 服务器 它将客户端发送的消息保存到本地数据库中 每条消息都有一个 成功接收 标志 当接收客户端为收到的每条消息 QOS 1 返回 puback 时 我想翻转该标志 问题是 当我发布消息时 服务器正确接收从接收客
  • AWS-IOT Mqtt Broker 是否会记住跨客户端连接订阅的客户端和未传递的消息?

    AWS 文档在此 http docs aws amazon com iot latest developerguide iot message broker html http docs aws amazon com iot latest
  • Mosquitto 1.4.2 Websocket 支持

    我正在尝试利用 Mosquittos 最近的更新来支持代理中的 websocket 我正在运行 Mosquitto v1 4 2 并将以下几行添加到 mosquitto 配置文件 mosquitto conf 中 listener 1000
  • 简化情况下能否保证mqtt消息送达顺序?

    如果只有一个代理 一个发布者 一个主题和干净的会话 在这个简化的情况下 能否保证订阅者端的消息传递顺序与发布者端的发送顺序相同 会受到QoS的影响吗 Section 4 6 http docs oasis open org mqtt mqt
  • 加特林负载测试期间编译错误

    我正在尝试编写一个模拟 并且希望能够运行该模拟 我在尝试 mvn gatling execute 时遇到错误 我的 pom 有以下依赖项
  • 使用 mosca MQTT 的请求响应模式

    有没有办法使用 mosca MQTT 实现请求响应模式 以 检查客户端的回复 如果在预期时间内未收到预期回复 则重新发布 我相信这在 Mqtt 5 中是可能的 但截至目前 我必须使用 QoS 1 的 Mosca 代理 支持直到 Mqtt 3

随机推荐

  • hutool的json工具完成list和json转换

    将json和list相互转换 import cn hutool json JSONArray import cn hutool json JSONUtil List转Json maps是List类型的参数 String json JSONU
  • 因果系列文章(10)—— 因果关系发现

    如何从纷繁复杂的数据中发现其中隐含因果关系 就是因果关系发现 casual discovery 要做的工作 本节简要总结这方面的工作 主要材料来源于 Elements of Causal Inference Foundations and
  • 基于Fruits-360数据集构建CNN进行水果识别实验

    基于Fruits 360数据集的水果识别项目 前段时间导师要求做一个神经网络可视化的项目 要将水果数据集进行训练得到模型 用于TensorSpace可视化 前前后后捣鼓了很久 现在回过头总结一下整个项目过程 写下这篇博客记录遇到的问题 有任
  • CTF刷题

    刷题网站 bugku BUUCTF 本文记录了在刷题过程中所学到的知识点 坚持每天刷5道题 持续更新 坚持就是胜利鸭 CTF 1 bugku Simple SSLI 1 根据题目SSLI即服务端模板注入攻击 服务段接收用户输入 将其作为we
  • react数组遍历与onClick()点击事件

    import Component from react const user name Tom age 18 sex 男 name alice age 18 sex 女 name Time age 18 sex 女 class home e
  • MySQL_JDBC_jar包的下载与使用(for Windows)

    目录 1 下载 2 使用 1 下载 打开网站 MySQL Download Connector Jhttps dev mysql com downloads connector j 选择系统如下图 选择第二个下载 后缀为zip的 解压下载的
  • 分布式系统的特征

    分布式系统概念与设计 读书笔记 第一章 第一章 分布式系统的特征 1 0 简介 分布式系统是其组件分布在联网的计算机上 组件之间通过传递消息进行通信和动作协调的系统 该定义引出了分布式系统的下列重要特征 组建的并发性 缺乏全局时钟 组件故障
  • JS有小数保留两位,整数不显示小数

    在很多时候要展示数据 会有各种小数处理 碰到页面的数据要根据不同的情况展示不同格式的数据 比如得到的数据是一个小数 现在要将小数保留两位 而整数则不显示小数点 显示整数格式 使用toFixed n 方法 toFixed 2 里面的2表示保留
  • Python django jwt 报错 unexpected keyword argument ‘verify‘解决方法

    本文主要介绍Python中 使用django jwt时报错 TypeError decode got an unexpected keyword argument verify 的解决 原文地址 Python django jwt 报错 u
  • 【MongoDB】docker部署mongdb多机集群(跨主机副本集)

    目录 概述 MongoDB Replication 官方英文文档 冗余 数据可用性 MongoDB副本集 多主机多节点docker部署流程 概述 环境准备 部署步骤 参考文献 概述 MongoDB Replication 官方英文文档 单主
  • python转化为json格式

    转化为JSON格式 JSON是一种常用的数据交换格式 该格式在前端与后端数据传输和处理中非常常见 在Python中将数据转化为JSON格式是一个非常常见的需求 Python中自带的json模块可以非常方便地实现这个需求 转化为json格式
  • 【Linux】浅谈Linux内核定时器timer_list

    目录 1 平台说明 2 定时器timer list说明 2 1 所在头文件 2 2 结构体 2 3 重要函数说明 2 4 函数API 2 5 使用方法 3 实例 3 1 驱动程序代码 3 2 测试程序代码 3 3 结果 1 平台说明 Lin
  • 【源码篇】基于SpringBoot+thymeleaf图书馆管理系统

    1 系统介绍 系统总体功能介绍 1 分为系统管理员和用户两类用户 2 开放用户注册功能 3 系统管理员拥有用户管理 图书管理 以及用户的借书申请的确定和还书操作 4 用户只能查询图书 并进行借书操作 提出借书申请 每个用户最多借阅8本 即当
  • 2018年AI成败将见分晓;全球经济重心东移,上海料将在2035年超越巴黎

    2017临近结束 我们先来看看对于来年 都有些什么预测 埃森哲预测CES 2018 AI 5G 区块链以及无人车 明年1月9 12日 CES又将在拉斯维加斯举行 每年 咨询巨头埃森哲 Accenture 都会对CES上的技术趋势做出预测 这
  • 100天精通Python(可视化篇)——第91天:Pyecharts绘制各种折线图实战

    文章目录 专栏导读 1 基本流程 2 多条折线图 3 添加最小值最大值平均值 4 竖线提示信息 5 阶梯图 6 平滑曲线折线图 7 面积折线图 8 堆积图 9 双横坐标折线图 专栏导读 本文已收录于 100天精通Python从入门到就业 本
  • Windows Server 2008 R2 下配置AD证书服务器和HTTPS访问的图文教程 DNS+IIS+AD证书服务

    提前配置好虚拟机和客户机的访问 保证能ping通虚拟机 虚拟机和客户机网络的DNS填写为虚拟机的ip地址 接下来配置好DNS服务 配置域名解析服务 这个用于域名解析 不需要域名访问的可以不配置DNS 使用IP访问即可 安装DNS 进入DNS
  • (ros/qt报错) FATAL: ROS_MASTER_URI is not defined in the environment

    安装qt之后 明明打开roscore但是qt运行跟ros有关的节点时报错 FATAL 1450943695 306401842 ROS MASTER URI is not defined in the environment Either
  • 哪些 Java 知识不需要再学了

    张无忌在学太极拳的时候 他爹的师父张三丰告诫他一定要把之前所学习的武功全部忘掉 忘得越多就会学得越快 同样的 自学 Java 的时候一定要先知道哪些 Java 知识不需要再学了 毕竟技术的更新迭代就好像火箭一样快 Java 的一些知识点早已
  • OJ:algorithm头文件中sort函数的应用

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

    上一篇使用USB转串口的方式通过ESP8266wifi模块的方式成功连接上了阿里云 现在就要通过单片机来替换电脑上位机了 这样单片机自动的去调用并发送串口数据更加方便 也更加符合一个产品的开发 板载的传感器有NTC温度 光强 这两个主要用来