文章目录
- 
- [WiFi 和 MQTT 协议](#WiFi 和 MQTT 协议)
- 
- 
- [1. 物联网组网](#1. 物联网组网)
- [2. ESP 8266 WiFi 模块](#2. ESP 8266 WiFi 模块)
- 
- [2.1 原理图](#2.1 原理图)
- [2.2 ESP8266 模块和 STM32 链接](#2.2 ESP8266 模块和 STM32 链接)
- [2.3 AT 指令操作 WiFi 模块连接 AP 热点](#2.3 AT 指令操作 WiFi 模块连接 AP 热点)
 
- [3. MQTT 协议](#3. MQTT 协议)
- 
- [3.1 MQTT 概述](#3.1 MQTT 概述)
- [3.2 MQTT 相关技术内容](#3.2 MQTT 相关技术内容)
- [3.3 MQTT 控制报文结构](#3.3 MQTT 控制报文结构)
- 
- [3.3.1 MQTT 报文组成部分](#3.3.1 MQTT 报文组成部分)
- [3.3.2 Fixed header 固定报头](#3.3.2 Fixed header 固定报头)
 
- [3.4 CONNECT -- 连接服务端](#3.4 CONNECT – 连接服务端)
- 
- [3.4.1 CONNECT 主要作用](#3.4.1 CONNECT 主要作用)
- [3.4.2 CONNECT 数据包分析](#3.4.2 CONNECT 数据包分析)
- [3.4.3 STM32 利用 ESP8266 连接 MQTT 流程](#3.4.3 STM32 利用 ESP8266 连接 MQTT 流程)
 
- [3.5 PUBLISH -- 发布消息](#3.5 PUBLISH – 发布消息)
- 
- [3.5.1 发布消息 Topic](#3.5.1 发布消息 Topic)
- [3.5.2 发布数据包分析](#3.5.2 发布数据包分析)
 
- [3.6 SUBSCRIBE - 订阅主题](#3.6 SUBSCRIBE - 订阅主题)
- 
- [3.6.1 订阅主题 Topic](#3.6.1 订阅主题 Topic)
- [3.6.2 订阅主题数据包分析](#3.6.2 订阅主题数据包分析)
 
 
 
 
- 
 
WiFi 和 MQTT 协议
1. 物联网组网
- 设备组网是通过一定技术手段,将设备可以在一定的范围内进行数据传递和操作控制。常见的组网设备
- WiFi 4G/5G NB-IoT LoRa BlueTooth LwIP(以太网)
- 联网之后的设备可以将数据上发到云端,同时可以从云端得到相关的数据内容。
- 远程监控
- 远程操作
- 批量多路处理
2. ESP 8266 WiFi 模块
2.1 原理图
▪ 802.11 b/g/n
▪ Wi-Fi Direct (P2P)、soft-AP
▪ 内置TCP/IP协议栈
▪ 内置TR开关、balun、LNA、功率放大器和匹配网络
▪ 内置PLL、稳压器和电源管理组件
▪ 802.11b模式下+19.5dBm的输出功率
▪ ⽀支持天线分集
▪ 断电泄露电流小于10uA
▪ 内置低功率32位CPU:可以兼作应用处理器
▪ SDIO 2.0、 SPI、UART
▪ STBC、1x1 MIMO、2x1 MIMO
▪ A-MPDU 、A-MSDU的聚合和 0.4μs的保护间隔
▪ 2ms之内唤醒、连接并传递数据包
▪ 待机状态消耗功率小于1.0mW (DTIM3)

2.2 ESP8266 模块和 STM32 链接
ESP8266 模块对应的电路引脚情况
- 重点关注 RXD 和 TXD
- RXD 是用于接收 MCU 提供的 AT 指令
- TXD 是用于反馈数据到 MCU ,包括 AT 指令响应,网络数据响应内容,主要数据形式为【字符串形式】,除了基本字符串格式,包括方便后续解析使用的 JSON 格式数据

当前开发板中,对应的板载链接效果
- ESP8266 模块的 RXD ==> MCU USART3 TX ==> PB10
- ESP8266 模块的 TXD ==> MCU USART3 RX ==> PB11
- 需要通过配置 USART3 数据发送和接收,来进行 ESP8266 WiFi 模块控制,利用 USART3 TX 发送 AT 指令到 WiFi 模块,利用 USART3 RX 接受 WiFi 模块的反馈数据。
- PB11 ==> RX 浮空输入模式
- PB10 ==> TX 复用推挽模式



2.3 AT 指令操作 WiFi 模块连接 AP 热点
参考文档

            
            
              c
              
              
            
          
          #include "ESP8266.h"
ESP8266_Data esp8266_val = {0};
void ESP8266_Init(void)
{
	/*
	1. 时钟使能
		需要进行使用内容有 GPIOB 和 USART3
		APB2ENR --> GPIOB
		APB1ENR --> USART3
	*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	
	/*
	2. 配置 GPIO
	*/
	GPIO_InitTypeDef GPIO_InitStructure;
	
	// PB11 --> RX 浮空输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	// PB10 --> TX 复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	/*
	3. 配置 USART3 工作模式
	*/
	USART_InitTypeDef USART_InitStructure;
	
	// 波特率
	USART_InitStructure.USART_BaudRate = 115200;
	// 软件控制
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	// 8N1
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 使能 RX 和 TX
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART3, &USART_InitStructure);
	
	// USART3 开启工作
	USART_Cmd(USART3, ENABLE);
	
	/*
	4. USART3 对应的中断使能配置
	*/
	NVIC_InitTypeDef NVIC_InitStructure;
		
	/*
	分别开启 USART_IT_IDLE 和 USART_IT_RXNE 中断
	*/
	USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
	
	// 设置当前优先级组为 NVIC_PriorityGroup_2 2 个占先,2 个次级
	NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
	
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  // 占先优先级 0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;         // 次级优先级 1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
}
void USART3_IRQHandler(void)
{
	u16 val = 0;
	
	
	/*
	1. 处理 RXNE 中断
		接收数据缓冲区非空,需要进行接收数据处理
	2. 处理 IDLE 中断
		当前接收数据总线已空闲,数据接收完毕
	*/
	/*
	检测到数据总线空闲,表示数据接收完毕,进行展示处理,同时
	处理当前中断标志位
		IDLE 清除要求
			1. 读取 USARTx->SR
			2. 读取 USARTx->DR
	不建议使用 void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
	*/
	if (USART_GetITStatus(USART3, USART_IT_IDLE) == SET)
	{
		esp8266_val.flag = 1;
			
		val = USART3->SR;
		val = USART3->DR;
		
		USART1_SendBuffer(esp8266_val.data, esp8266_val.count);
	}
	/*
	如果 flag 为 1 表示以当前数据已进行后续处理,需要对当前占用的内存空间
	进行擦除操作,方便进入下一次数据接受
	*/
	if (esp8266_val.flag)
	{
		memset(&esp8266_val, 0, sizeof(ESP8266_Data));
	}
	
	if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
	{
		// 从 USART3 读取数据,存储到 esp8266_val 结构中,同时赋值 data 存储数据
		// count 累加有效数据个数
		esp8266_val.data[esp8266_val.count++] = USART_ReceiveData(USART3);
	
		// 如果数据已满,利用 USART1 发送数据到 PC 串口调试工具
		if (esp8266_val.count == ESP8266_DATA_SIZE)
		{
			// USART1_SendBuffer(esp8266_val.data, esp8266_val.count);
			esp8266_val.flag = 1;
		}
	}
	
	
}
void ESP8266_SendByte(u8 byte) 
{
	while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
	
	USART_SendData(USART3, byte);
}
void ESP8266_SendBuffer(u8 *buffer, u16 count)
{
	while (count--)
	{
		ESP8266_SendByte(*buffer);
		buffer++;
	}
}
void ESP8266_SendString(const char * str)
{
	while (*str)
	{
		ESP8266_SendByte(*str);
		str++;
	}
}
void ESP8266_Send_AT(const char *cmd, const char *ack, u16 timeout)
{
	// 展示 AT 指令内容
	printf("AT cmd : %s", cmd);
	
	/*
	发送 AT 指令到 ESP8266
	*/
	ESP8266_SendString(cmd);
	
	/*
	ESP8266 会返回对应的响应信息,USART3 接收数据信息是通过
	USART3_IRQHandler 处理,数据会存储到 ESP8266_Data 结构体中
	*/
	while (timeout)
	{
		// 根据应答结果判断,确定应答效果
		if (!ESP8266_AckCheck(ack))
		{
			break;
		}
		
		timeout--;
		SysTick_Delay_ms(1);
	}
	
	// 如果没有超时,表示应答信息正常
	if (timeout)
	{
		// printf("ACK Success\n");
	}
	else
	{
		// printf("ACK Failed\n");
	}
}
u8 ESP8266_AckCheck(const char *ack)
{
	// 判断当前 ESP8266 数据释放发送完成
	// 根据 ESP8266_Data 结构体中的 flag 进行判断
	if (esp8266_val.flag)
	{
		if (strstr((const char *)esp8266_val.data, ack))
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
	
	return 1;
}
void ESP8266_Connect(const char *ssid, const char *psk)
{
	printf("ESP8266 WiFi Connect!\n");
	
	// 1. 发送 AT 指令,判断当前设备是否正常工作
	ESP8266_Send_AT("AT\r\n", "OK", 2000);
	SysTick_Delay_ms(500);
	
	// 2. 设置当前 ESP8266 工作模式为 STA(Station) 模式
	ESP8266_Send_AT("AT+CWMODE=1\r\n", "OK", 2000);
	SysTick_Delay_ms(500);
	
	// 3. 扫描当前可以连接的 AP 热点信息,包括 SSID,信号强度
	ESP8266_Send_AT("AT+CWLAP\r\n", "OK", 2000);
	SysTick_Delay_ms(5000);
	
	// 4. 连接目标热点,提供 SSID 和 PSK,组装字符串时,必须将
	// AT 指令以 \r\n 结尾
	char connectCmd[128] = "";
	sprintf(connectCmd, "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, psk);
	ESP8266_Send_AT(connectCmd, "OK", 2000);
	SysTick_Delay_ms(5000);
	
	// 5. 网络连接情况
	ESP8266_Send_AT("AT+CIFSR\r\n", "OK", 2000);
	SysTick_Delay_ms(2000);
	
}3. MQTT 协议
3.1 MQTT 概述
- 广泛用于 物联网设备联网通信的传输协议,基于 TCP/IP 实现
- MQTT 区分客户端和服务器,本地 STM32 + ESP8266 + MQTT 支持,可以认为是 MQTT 的客户端,同时云平台 (ThingsCloud Onenet Aliyun) 物联网平台可以认为是 MQTT 服务器。
- MQTT 协议中,重点内容是**【服务器连接】【订阅主题】【发布消息】**
- 服务器连接 : 当前 MQTT 客户端链接对应的 MQTT 服务器
- **订阅主题 : ** MQTT 服务器一旦通过指定主题下发数据,MQTT 客户端可以收到服务器下发的数据内容,可以完成设备控制,例如 LED 亮灭,舵机控制。。。
- 发布消息 : MQTT 客户端按照 MQTT 服务器要求,和云平台自定义内容,将本地数据发送到云平台。可以将传感器数据进行上传。。。
3.2 MQTT 相关技术内容
MQTT 技术特征
- MQTT 版本有 5.0.0 和 3.1.1
- 5.0.0 是最新版本,支持功能更多,使用更为方便
- 3.1.1 是目前主流设备端使用的大面积版本,使用相对复杂。

3.3 MQTT 控制报文结构
3.3.1 MQTT 报文组成部分
- Fixed header : 确定当前 MQTT 发送的数据目标,例如 连接服务器,发布数据,订阅主题
- Variable header : 根据当前 MQTT Fixed header 目标任务需求,提供不同的报文参数
- Payload :根据当前 MQTT 报文类型和目标数据需求,提供的用户有效载荷内容
| 组成部分 | 内容分析 | 
|---|---|
| Fixed header | 固定报头,所有控制报文都包含 | 
| Variable header | 可变报头,部分控制报文包含 | 
| Payload | 有效载荷,部分控制报文包含 | 
3.3.2 Fixed header 固定报头
格式内容
- 主要有 MQTT 控制报文类型和报文类型指定标志
- 剩余长度

MQTT 控制报文类型,在 Fixed Header 固定头中,对应的是第一个字节的 7 ~ 4 位
| 名字 | 值 | 报文流动方向 | 描述 | 
|---|---|---|---|
| Reserved | 0 | 禁止 | 保留 | 
| CONNECT | 1 | 客户端到服务端 | 客户端请求连接服务端 | 
| CONNACK | 2 | 服务端到客户端 | 连接报文确认 | 
| PUBLISH | 3 | 两个方向都允许 | 发布消息 | 
| PUBACK | 4 | 两个方向都允许 | QoS 1消息发布收到确认 | 
| PUBREC | 5 | 两个方向都允许 | 发布收到(保证交付第一步) | 
| PUBREL | 6 | 两个方向都允许 | 发布释放(保证交付第二步) | 
| PUBCOMP | 7 | 两个方向都允许 | QoS 2消息发布完成(保证交互第三步) | 
| SUBSCRIBE | 8 | 客户端到服务端 | 客户端订阅请求 | 
| SUBACK | 9 | 服务端到客户端 | 订阅请求报文确认 | 
| UNSUBSCRIBE | 10 | 客户端到服务端 | 客户端取消订阅请求 | 
| UNSUBACK | 11 | 服务端到客户端 | 取消订阅报文确认 | 
| PINGREQ | 12 | 客户端到服务端 | 心跳请求 | 
| PINGRESP | 13 | 服务端到客户端 | 心跳响应 | 
| DISCONNECT | 14 | 客户端到服务端 | 客户端断开连接 | 
| Reserved | 15 | 禁止 | 保留 | 
控制报文类型标志位
- 重点是发布数据的标志内容
- 其他控制报文类型,对应的标志位都是固定格式,按照当前表格要求提供

固定报头中的剩余长度,对应的
- Variable Header 和 Payload 字节数。
- 根据当前数据大小,可以选择剩余长度占用的字节数,通常情况下 MQTT 报文字节数数据不大。

3.4 CONNECT -- 连接服务端
3.4.1 CONNECT 主要作用
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT报文 [MQTT-3.1.0-1]。
在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接 [MQTT-3.1.0-2]。有关错误处理的信息请查看4.8节。
有效载荷包含一个或多个编码的字段。包括客户端的唯一标识符,Will主题,Will消息,用户名和密码。除了客户端标识之外,其它的字段都是可选的,基于标志位来决定可变报头中是否需要包含这些字段。
3.4.2 CONNECT 数据包分析
            
            
              c
              
              
            
          
          MQTT Connect 报文组成
	u8 MQTT_Connect_Data[256] = "";
Fixed_Header
	MQTT_Connect_Data[0] 是报文类型 + 报文标志位
		0001 0000 ==> 0x10
	MQTT_Connect_Data[1] 是报文剩余长度,对应 可变头 + 有效载荷
		可变头(10) + 有效载荷(19) ==> 0001 1101 ==> 29 ==> 0x1D
		
Variable_Header 
	【协议内容】
		可变头第一个组成部分是 协议名称数据提交方式为 【2 + N】 模式
		需要提供的数据是协议长度(2 Byte) + 协议内容,当前是使用 MQTT 协议,对应内容为 MQTT 字符串
		协议长度 => 4 个字节,协议内容 => MQTT
			MQTT_Connect_Data[2] 数据长度高位 MSB   4 / 256 ==> 0000 0000 ==>0x00
			MQTT_Connect_Data[3] 数据长度低位 LSB    4 % 256 ==> 0000 0100 ==> 0x04
			MQTT_Connect_Data[4] 'M' ==> 0100 1101 ==> 0x4D
			MQTT_Connect_Data[5] 'Q' ==> 0101 0001 ==> 0x51
			MQTT_Connect_Data[6] 'T' ==>  0101 0100 ==> 0x54
			MQTT_Connect_Data[7] 'T' ==>  0101 0100 ==> 0x54
	【协议版本】
		当前使用的协议版本是 MQTT 3.1.1 采用一个字节数据来告知 MQTT 服务器对应的版本信息
			MQTT_Connect_Data[8] ==> 0000 0100 ==> 0x04
	【连接标志 Connect Flags】
		组成部分是告知当前 MQTT 协议后续的载荷内容,每一个标志位告示后续的数据中,
		是否包含对应的数据内容
		Username(占1位) Password(占1位) Will Retain(占1位) Will QoS(占2位) Will Flag(占1位)  Clean Session(占1位) 保留(占1位)
		根据 ThingsCloud 分析,需要提供用户名和密码,同时按照常规内容,不需要返回,QoS=0 flag 不需要,Clean Session 需要
			MQTT_Connect_Data[9]  ==> 1100 0010 ==> 0xC2
	【Keep Alive 时间】
		MQTT 客户端和服务器之间发送数据的最大间隔,超出范围登录退出。
		需要两个字节 16 位来描述对应的时间
		分别对应 
			假设 Keep Alive ==> 60 
			Keep Alive 时间 MSB 高位 MQTT_Connect_Data[10] ==> 60 / 256 ==> 0000 0000 ==> 0x00
			Keep Alive 时间 LSB 低位  MQTT_Connect_Data[11] ==> 60 % 256 ==> 0011 1100 ==> 0x3CD
Payload
	Payload 字节数 = 2 + 客户端名称字节数 + 2 + 用户名字节数 + 2 + 密码字节数
				= 2 + 9 + 2 + 2 + 2 + 2 ==> 19
	根据当前报文分析和标志位分析,当前有效载荷的组成部分是,全部采用 2 + N 模式
	【客户端标识符】假设客户端标识符/客户端名称是 Client_GL
		2 + N 模式分析
		2 表示当前数据的字节个数 ==> 9 byte
			MQTT_Connect_Data[12] 数据长度高位 MSB ==> 9 / 256 ==> 0000 0000 ==> 0x00
			MQTT_Connect_Data[13] 数据长度低位 LSB ==> 9 % 256 ==> 0000 1001 ==> 0x09
		N 对应的数据,按照字节方式处理
			MQTT_Connect_Data[14] ==> 'C'
			MQTT_Connect_Data[15] ==> 'l'
			MQTT_Connect_Data[16] ==> 'i'
			MQTT_Connect_Data[17] ==> 'e'
			MQTT_Connect_Data[18] ==> 'n'
			MQTT_Connect_Data[19] ==> 't'
			MQTT_Connect_Data[20] ==> '_'
			MQTT_Connect_Data[21] ==> 'G'
			MQTT_Connect_Data[22] ==> 'L'
	【用户名】假设用户名 CG
		2 + N 模式分析
		2 表示当前数据的字节个数 ==> 2 byte
			MQTT_Connect_Data[23] 数据长度高位 MSB ==> 2 / 256 ==> 0000 0000 ==> 0x00
			MQTT_Connect_Data[24] 数据长度低位 LSB ==> 2 % 256 ==> 0000 0010 ==> 0x02
		N 对应的数据,按照字节方式处理
			MQTT_Connect_Data[25] ==> 'C'
			MQTT_Connect_Data[26] ==> 'G'
	【密码】假设密码 HL
		2 + N 模式分析
		2 表示当前数据的字节个数 ==> 2 byte
			MQTT_Connect_Data[27] 数据长度高位 MSB ==> 2 / 256 ==> 0000 0000 ==> 0x00
			MQTT_Connect_Data[28] 数据长度低位 LSB ==> 2 % 256 ==> 0000 0010 ==> 0x02
		N 对应的数据,按照字节方式处理
			MQTT_Connect_Data[29] ==> 'H'
			MQTT_Connect_Data[30] ==> 'L'3.4.3 STM32 利用 ESP8266 连接 MQTT 流程
- ESP8266 联网
- ESP8266 利用 TCP 协议和 MQTT 云平台建立连接
- ESP8266 开启【透传】模式 + 【数据直发】方式
- 将 Connect 操作数据包发送到 MQTT 云平台,建立和项目的连接。
3.5 PUBLISH -- 发布消息
3.5.1 发布消息 Topic
MQTT 客户端到服务器或者服务器到客户端,都需要通过发布主题进行明确当前提交的信息内容是什么内容。
ThingsCloud 支持的发布主题 Topic。针对于 ThingsCloud 云端设备,选择 public_topic 对应 attributes。提交数据格式为 JSON 格式。
【注意】发布消息是在 MQTT 服务器连接之后
| 消息类型 | 主题 | 
|---|---|
| 设备上报属性值 | attributes | 
| 设备获取当前属性值 | attributes/get/ <id> | 
| 设备上报事件 | event/report/ <id> | 
| 设备回复命令 | command/reply/ <id> | 
| 设备自定义数据上报 | data/ <identifier> | 
| 设备上报离线主题 | attributes/series{} | 
ThingsCloud 服务器数据属性名称

根据 ThingsCloud 要求的 JSON 格式数据形式,通过 MQTT 协议条件的数据【有效载荷】为
            
            
              json
              
              
            
          
          {
    "Temp":25.3,
    "Hum":55
}
- 注意,双引号是 JSON 格式键名 (Key) 对应的格式要求,在通过 MQTT 数发送时,需要提供对应的转义操作。
cchar * data = "{\"Temp\":25.3,\"Hum\":55}";
3.5.2 发布数据包分析
            
            
              c
              
              
            
          
          MQTT PUBLISH  报文组成
	u8 MQTT_Publish_Data[256] = "";
Fixed_Header
	MQTT_Publish_Data[0] 是报文类型 + 报文标志位
		0011 0000 ==> 0x30
	MQTT_Publish_Data[1] 是报文剩余长度,对应 可变头 + 有效载荷
		XXXX XXXX ==> 可变头(2 +  strlen("attributes")) +
		
Variable_Header 
	【发布主题】
	ThingsCloud 要求的发布主题为 attributes,首先可变头中的是 2 + N
	    发布主题名称字节长度高位和低位
	MQTT_Publish_Data[2] = strlen("attributes") / 256;
	MQTT_Publish_Data[3] = strlen("attributes") % 256;
	  memcpy(&MQTT_Publish_Data[4], "attributes", strlen("attributes"));
	
Payload
	有效载荷是当前提交的 JSON 格式字符串,直接在 Variable_Header 拼接即可
	"{\"Temp\":25.3,\"Hum\":55}"
	memcpy(&MQTT_Publish_Data[4 + strlen("attributes")], "{\"Temp\":25.3,\"Hum\":55}", strlen("{\"Temp\":25.3,\"Hum\":55}"))3.6 SUBSCRIBE - 订阅主题
3.6.1 订阅主题 Topic
订阅主题,MQTT 服务器可以通过对应主题进行数据下发,MQTT 客户端可以收到对应的数据内容。数据形式一般都是【字符串形式】并且通常都是 JSON 格式数据。
在 ThingsCloud 中支持的订阅主题有
【注意】订阅主题是在 MQTT 服务器连接之后
| 消息类型 | 主题 | 
|---|---|
| 接收属性上报的响应 | attributes/response | 
| 接收属性获取的响应 | attributes/get/response/+ | 
| 接收下发的属性 | attributes/push | 
| 接收事件上报的响应 | event/response/+ | 
| 接收下发的命令 | command/send/+ | 
| 接收命令回复的响应 | command/reply/response/+ | 
| 接收自定义数据下发 | data/ <identifier>/set | 
| 接收离线属性上报的响应 | attributes/series/response | 
当前 STM32 + ESP8266 + MQTT 订阅主题 attributes/push,可以收到服务器下发的数据内容。
ESP8266 收到 MQTT 服务器下发的数据内容,通过 USART3 告知 MCU。可以通过程序,对 JSON 格式数据进行解析判断,从而满足硬件功能控制。
            
            
              json
              
              
            
          
          {
    "Led0": false,
    "Led1": false,
    "SG90": "Turn_Clockwise",
    "Beep": false
} 3.6.2 订阅主题数据包分析
            
            
              c
              
              
            
          
          MQTT SUBSCRIBE  报文组成
	u8 MQTT_Subscirbe_Data[256] = "";
Fixed_Header
	MQTT_Subscirbe_Data[0] 是报文类型 + 报文标志位
		1000 0000 ==> 0x80
	MQTT_Subscirbe_Data[1] 是报文剩余长度,对应 可变头 + 有效载荷
		2 + 2 +  strlen("attributes/push") + 1
Variable_Header 
	2 个字节,订阅主题数据包 ID,因为当前使用的是 QoS=0 服务等级
	完全可以自定义
	MQTT_Subscirbe_Data[2] ==> 0x00
	MQTT_Subscirbe_Data[3] ==> 0x08
	
Payload
	2 + N + 1
	2 ==> MSB + LSB
		MQTT_Subscirbe_Data[4] ==> strlen("attributes/push") / 256
		MQTT_Subscirbe_Data[5] ==> strlen("attributes/push") % 256
        N ==> 订阅主题字符串数据
		memcpy(&MQTT_Subscirbe_Data[6],"attributes/push", strlen("attributes/push"));
        1 ==> QoS = 0
		MQTT_Subscirbe_Data[6 + strlen("attributes/push")] = 0x00