【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器_单片机发送at指令使wi-fi模块进入ap模式并配置好账户密码方便进行云平台的连接

74 阅读6分钟
}

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,016,"Subscribe Error!",8,16,1); // printf("SubscribeTopic Error!\r\n"); } Oled_RefreshGram(); } Delay_ms(1000); Oled_Clear(); Oled_ShowString(32,016,"Mars Tech",8,16,1); Oled_ShowString(0,116," VCC: 0000 mV",8,16,1); Oled_ShowString(0,216,"Temp: 00.0 C",8,16,1); Oled_ShowString(0,316," 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(86,116,battery,4,8,16); Oled_ShowNum(86,216,(u8)temperature,2,8,16); Oled_ShowNum(86+38,216,(u16)(temperature10)%10,1,8,16); Oled_ShowNum(86,316,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_FILTER3300/409511; //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 );	

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。 img img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!