目录

  • ESP8266简介
  • ESP_01引脚图
  • ESP_01引脚介绍
  • ESP_01 AT指令简介
  • MQTT简介
  • MQTT协议部分报文格式
  • 请求连接报文
  • 确认链接报文
  • 订阅消息报文
  • 订阅确认报文
  • 发表消息报文
  • 发布确认
  • 模块构成
  • MQTT协议实现
  • ESP—01实现 MQTT协议的步骤
  • 初始化ESP—01
  • 连接到WiFi热点
  • 连接到MQTT服务器
  • 登录MQTT服务器
  • 订阅一个主题
  • 发送一条消息
  • 接收订阅的主题更新的消息
  • 结束语


ESP8266简介

ESP_01引脚图

智能家居自动化代码 智能家居设计代码_嵌入式

ESP_01引脚介绍

引脚

功能

工作时接线

3V3

3.3V供电,不要使用5V

3.3V

RX

串口接收端

MCU:TX

RST

外部复位引脚

NC

IO0

I/O口0,状态:悬空:Flash下载模式或工作模式;下拉:串口下载模式

NC

EN

使能端口:高电平工作,低电平不工作

3.3V

IO2

I/O口2,开机上电是禁止下拉,默认高电平

NC

TX

串口发送端

MCU:RX

GND

电源地线

GND

ESP_01 AT指令简介

功能

AT指令

工作模式设置

AT+CWMODE=2 # 1-Station 模式,2-AP 模式,3-AP & Station 模式

重启

AT+RST

查询周围热点

AT+CWLAP

连接到热点

AT+CWJAP=“HOTPOINT”,“1234”

查询已连接的热点

AT+CWJAP?

查询自身IP

AT+CIFSR

连接到TCP服务器

AT+CIPSTART=“TCP”,“192.168.1.100”,8080

发送数据

AT+CIPSEND=4 \n数据

开启透传模式

AT+CIPMODE=1

开始透传

AT+CIPSEND

退出透传

+++

MQTT简介

MQTT协议部分报文格式

请求连接报文

请求连接报文格式

报文头

遗嘱主题

遗嘱消息

用户名

密码

连接请求报文头格式

Bits

Data

说明

byte1

0x16

固定报头

byte2

剩余长度

byte1~byte5

协议类型

byte7

0x04

协议级别

byte8

连接标志

byte9~byte10

保持链接

协议类型格式

Bits

7

6

5

4

3

2

1

0

MSB

0

0

0

0

0

0

0

0

LSB

0

0

0

0

0

1

0

0

M

0

1

0

0

1

1

0

1

Q

0

1

0

1

0

0

0

1

T

0

1

0

1

0

1

0

0

T

0

1

0

1

0

1

0

0

连接标志格式

byte

User Name Flag

Password Flag

Will Retain

Will Qos

Will Qos

Will Flag

Claen Session

Reserved

保持链接标志:两个字节的数据,代表的是连接心跳的时间。

链接标志格式

保持链接 Keep Alive MSB

保持链接 Keep Alive LSB

确认链接报文

确认链接报文格式

Bits

7

6

5

4

3

2

1

0

说明

byte1

0

0

1

0

0

0

0

0

固定报头

byte2

0

0

0

0

0

0

1

0

剩余长度

byte3

0

0

0

0

0

0

0

Session Present

连接确认标志

byte4

x

x

x

x

x

x

x

x

连接返回码

订阅消息报文

订阅消息报文格式

Bits

7

6

5

4

3

2

1

0

说明

byte1

1

0

0

0

0

0

1

0

固定报头

byte2

剩余长度(最多4字节)

byte3-byte4

报文标识符

bytex

有效载荷

报文标识符格式

描述

报文标识符

byte1

MSB(0)0x00

byte2

LSB(0) 0x01

有效载荷格式

描述

主题过滤器

byte1

长度MSB

byte2

长度LSB

byte3~N

主题过滤器 Topic Filter

服务质量要求

byteN+1

0 0 0 0 0 0 X X

订阅确认报文

订阅确认报文格式

Bits

7

6

5

4

3

2

1

0

说明

byte1

1

0

0

1

0

0

0

0

固定报头

byte2

剩余长度

byte3~byte4

报文标识符

byteN

有效载荷

返回码格式

描述

7

6

5

4

3

2

1

0

返回码

x

0

0

0

0

0

x

x

返回码取值说明:

  • 0x00:最大QoS 0
  • 0x01:成功—最大QoS 1
  • 0x02:成功—最大QoS 2
  • ox80:Failure失败

发表消息报文

发表消息报文格式

结构

固定报头

剩余最大长度

可变报头

发布消息的内容(有效载荷=剩余长度-可变报头长度)

固定报头格式

Bits

7

6

5

4

3

2

1

0

byte1

0

0

1

1

DUP

QoS-H

QoS-

Retain

可变报头

Bits

说明

byte1

主题名

byte2

报文标识符(只有Qos等级为1/2时才有)

主题名格式

描述

7

6

5

4

3

2

1

0

主题名

byte1

length MSB

0

0

0

0

0

0

0

0

byte2

length LSB

0

0

0

0

0

0

1

1

byte3

a(0x61)

byte4

/(0x2F)

byte5

b(0x62)

报文标识符

byte6

MSB(0)

byte7

LSB(0)

发布确认

Bits

7

6

5

4

3

2

1

0

说明

byte1

0

1

0

0

0

0

0

0

固定报头

byte2

0

0

0

0

0

0

1

0

剩余长度

byte3~byte4






可变报头

模块构成

ESP01与STM32的USART1相连,组成WiFi模块。STM32的USART2与ZigBee网络的协调器相连,实现网络之间数据传输。

MQTT协议实现

ESP—01实现 MQTT协议的步骤

  • 初始化ESP—01
  • 连接到WiFi热点
  • 连接到MQTT服务器
  • 登录MQTT
  • 订阅一个主题
  • 发布一条消息
  • 接收订阅的主题更新的消息

初始化ESP—01

  1. 退出透传模式
  2. 检查ESP—01是否存在
  3. 关闭uart回显
uint8_t ESP8266_Init(void)
{
	ESP8266_ExitUnvarnishedTrans();		//退出透传
	HAL_Delay(500);
	ESP8266_ATSendString("AT+RST\r\n");
	HAL_Delay(800);
	if(ESP8266_Check()==0)              //使用AT指令检查ESP8266是否存在
	{
		return 0;
	}
	ESP8266_ATSendString("ATE0\r\n");     	//关闭回显 
	if(FindStr((char*)usart1_rxbuf,"OK",500)==0)  //设置不成功
	{
		return 0;      
	}
	return 1;                         //设置成功
}

连接到WiFi热点

使用AT指令连接到既定的热点

连接到MQTT服务器

调用连接到TCP服务器的指令连接到MQTT服务器,端口号为1883

登录MQTT服务器

请求登录报文格式为:协议头+主机名(长度+数据)+用户名(长度+数据)+密码(长度+数据)。

登录确认:得到登录确认报文后就证明登陆成功。

报文格式有点出入,但是除去个别变化外其余结构如上节所示。

uint8_t MQTT_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);
	
	//固定报头
	//控制报文类型
  usart1_txbuf[MQTT_TxLen++] = 0x10;		//MQTT Message Type CONNECT
	//剩余长度(不包括固定头部)
	do
	{
		uint8_t 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;
		usart1_txbuf[MQTT_TxLen++] = encodedByte;
	}while ( DataLen > 0 );
    	
	//可变报头
	//协议名
	usart1_txbuf[MQTT_TxLen++] = 0;        		// Protocol Name Length MSB    
	usart1_txbuf[MQTT_TxLen++] = 4;        		// Protocol Name Length LSB    
	usart1_txbuf[MQTT_TxLen++] = 'M';        	// ASCII Code for M    
	usart1_txbuf[MQTT_TxLen++] = 'Q';        	// ASCII Code for Q    
	usart1_txbuf[MQTT_TxLen++] = 'T';        	// ASCII Code for T    
	usart1_txbuf[MQTT_TxLen++] = 'T';        	// ASCII Code for T    
	//协议级别
	usart1_txbuf[MQTT_TxLen++] = 4;        		// MQTT Protocol version = 4    
	//连接标志
	usart1_txbuf[MQTT_TxLen++] = 0xc2;        	// conn flags 
	usart1_txbuf[MQTT_TxLen++] = 0;        		// Keep-alive Time Length MSB    
	usart1_txbuf[MQTT_TxLen++] = 60;        	// Keep-alive Time Length LSB  60S心跳包  

	usart1_txbuf[MQTT_TxLen++] = BYTE1(ClientIDLen);// Client ID length MSB    
	usart1_txbuf[MQTT_TxLen++] = BYTE0(ClientIDLen);// Client ID length LSB  	
	memcpy(&usart1_txbuf[MQTT_TxLen],ClientID,ClientIDLen);
	MQTT_TxLen += ClientIDLen;
	
	if(UsernameLen > 0)
	{   
		usart1_txbuf[MQTT_TxLen++] = BYTE1(UsernameLen);		//username length MSB    
		usart1_txbuf[MQTT_TxLen++] = BYTE0(UsernameLen);    	//username length LSB    
		memcpy(&usart1_txbuf[MQTT_TxLen],Username,UsernameLen);
		MQTT_TxLen += UsernameLen;
	}
	
	if(PasswordLen > 0)
	{    
		usart1_txbuf[MQTT_TxLen++] = BYTE1(PasswordLen);		//password length MSB    
		usart1_txbuf[MQTT_TxLen++] = BYTE0(PasswordLen);    	//password length LSB  
		memcpy(&usart1_txbuf[MQTT_TxLen],Password,PasswordLen);
		MQTT_TxLen += PasswordLen; 
	}    
	
	uint8_t cnt=2;
	uint8_t wait;
	while(cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));
		MQTT_SendBuf(usart1_txbuf,MQTT_TxLen);
		wait=30;//等待3s时间
		while(wait--)
		{
			//CONNECT
			if(usart1_rxbuf[0]==parket_connetAck[0] && usart1_rxbuf[1]==parket_connetAck[1]) //连接成功			   
			{
				return 1;//连接成功
			}
			HAL_Delay(100);			
		}
	}
	return 0;
}

订阅一个主题

报文格式同上节所示,有限等待后得到确认报文后就证明登录成功。

uint8_t MQTT_SubscribeTopic(char *topic,uint8_t qos,uint8_t whether)
{    
	MQTT_TxLen=0;
	int topiclen = strlen(topic);
	
	int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
	//固定报头
	//控制报文类型
	if(whether) usart1_txbuf[MQTT_TxLen++] = 0x82; //消息类型和标志订阅
	else	usart1_txbuf[MQTT_TxLen++] = 0xA2;    //取消订阅

	//剩余长度
	do
	{
		uint8_t 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;
		usart1_txbuf[MQTT_TxLen++] = encodedByte;
	}while ( DataLen > 0 );	
	
	//可变报头
	usart1_txbuf[MQTT_TxLen++] = 0;				//消息标识符 MSB
	usart1_txbuf[MQTT_TxLen++] = 0x01;           //消息标识符 LSB
	//有效载荷
	usart1_txbuf[MQTT_TxLen++] = BYTE1(topiclen);//主题长度 MSB
	usart1_txbuf[MQTT_TxLen++] = BYTE0(topiclen);//主题长度 LSB   
	memcpy(&usart1_txbuf[MQTT_TxLen],topic,topiclen);
	MQTT_TxLen += topiclen;

	if(whether)
	{
		usart1_txbuf[MQTT_TxLen++] = qos;//QoS级别
	}
	
	uint8_t cnt=2;
	uint8_t wait;
	while(cnt--)
	{
		memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));
		MQTT_SendBuf(usart1_txbuf,MQTT_TxLen);
		wait=30;//等待3s时间
		while(wait--)
		{
			if(usart1_rxbuf[0]==parket_subAck[0] && usart1_rxbuf[1]==parket_subAck[1]) //订阅成功			   
			{
				return 1;//订阅成功
			}
			HAL_Delay(100);			
		}
	}
	if(cnt) return 1;	//订阅成功
	return 0;
}

发送一条消息

报文格式同上节所示。

uint8_t MQTT_PublishData(char *topic, char *message, uint8_t qos)
{  
	int topicLength = strlen(topic);    
	int messageLength = strlen(message);     
	static uint16_t id=0;
	int DataLen;
	MQTT_TxLen=0;
	//有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
	//QOS为0时没有标识符
	//数据长度             主题名   报文标识符   有效载荷
	if(qos)	DataLen = (2+topicLength) + 2 + messageLength;       
	else	DataLen = (2+topicLength) + messageLength;   

    //固定报头
	//控制报文类型
	usart1_txbuf[MQTT_TxLen++] = 0x30;    // MQTT Message Type PUBLISH  

	//剩余长度
	do
	{
		uint8_t 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;
		usart1_txbuf[MQTT_TxLen++] = encodedByte;
	}while ( DataLen > 0 );	
	
	usart1_txbuf[MQTT_TxLen++] = BYTE1(topicLength);//主题长度MSB
	usart1_txbuf[MQTT_TxLen++] = BYTE0(topicLength);//主题长度LSB 
	memcpy(&usart1_txbuf[MQTT_TxLen],topic,topicLength);//拷贝主题
	MQTT_TxLen += topicLength;
        
	//报文标识符
	if(qos)
	{
			usart1_txbuf[MQTT_TxLen++] = BYTE1(id);
			usart1_txbuf[MQTT_TxLen++] = BYTE0(id);
			id++;
	}
	memcpy(&usart1_txbuf[MQTT_TxLen],message,messageLength);
  MQTT_TxLen += messageLength;
        
	MQTT_SendBuf(usart1_txbuf,MQTT_TxLen);
  return MQTT_TxLen;
}

接收订阅的主题更新的消息

ESP—01收到数据后会通过串口发送到STM32,根据协议对数据进行拆包,将需要的数据进行转发。

结束语

以上介绍了ESP-01模块,使用到的ESP8266 的部分AT指令,MQTT协议的报文格式以及实际使用方法。关于MQTT协议的具体内容,我也是刚开始学习,如果有同学对于这里有问题或者有兴趣的话欢迎讨论学习。最后欢迎大神批评指导。