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****热点
cpp
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 是目前主流设备端使用的大面积版本,使用相对复杂。
核心特性:
- 轻量高效
- 协议头最小仅 2 字节,适合资源受限的设备(如传感器、嵌入式设备)。
- 低功耗设计,适合电池供电设备。
- 发布 / 订阅模型
- 解耦消息生产者(Publisher)和消费者(Subscriber),通过主题(Topic)路由消息。
- 支持一对多、多对多通信。
- 多种服务质量(QoS)
- QoS 0(最多一次):消息可能丢失。
- QoS 1(至少一次):消息确保送达,但可能重复。
- QoS 2(恰好一次):消息确保送达且不重复。
- 支持持久会话
- 客户端可订阅持久化消息,断线重连后接收离线期间的消息(需 Broker 支持)。
- 低协议开销
- 基于 TCP/IP,但也可适配其他传输层(如 WebSocket、MQTT - SN 用于传感器网络)。
3.3 MQTT****控制报文结构
3.3.1 MQTT****报文组成部分
Fixed header: 确定当前 MQTT 发送的数据目标,例如 连接服务器,发布数据,订阅主题
Variable header: 根据当前 MQTT Fixed header 目标任务需求,提供不同的报文参数
Payload:根据当前 MQTT 报文类型和目标数据需求,提供的用户有效载荷内容

3.3.2 Fixed header****固定报头
格式内容
- 主要有 MQTT 控制报文类型和报文类型指定标志
- 剩余长度



在连接和发布报文中控制位置0即可,而订阅报文控制位必须是0x02.
**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 数据包分析
一、整体结构
MQTT Connect 报文由 Fixed Header(固定头) 、Variable Header(可变头) 、Payload(有效载荷) 三部分组成,用数组
MQTT_Connect_Data[256]
存储报文数据。
二、Fixed Header(固定头,2 字节)
字节索引 作用 计算 / 取值逻辑 示例值 0 报文类型 + 标志位 固定为 0x10
(二进制0001 0000
,表示 CONNECT 报文,无额外标志位)0x10
1 剩余长度(可变头 + 有效载荷总长度) 可变头长度(10 字节) + 有效载荷长度(19 字节) = 29 → 二进制 0001 1101
→0x1D
0x1D
核心逻辑 :固定头是报文 "身份标识",第 0 字节固定
0x10
,第 1 字节需计算可变头 + 有效载荷的总长度。
三、Variable Header(可变头,10 字节)
1. 协议名称(2 字节 + 协议内容 "MQTT" 4 字节 → 共 6 字节)
字节索引 作用 计算 / 取值逻辑 示例值 2 协议长度高位(MSB) 协议内容 "MQTT" 长度是 4 → 4 / 256 = 0
→ 二进制0000 0000
→0x00
0x00
3 协议长度低位(LSB) 4 % 256 = 4
→ 二进制0000 0100
→0x04
0x04
4-7 协议内容 "MQTT" 字符转 16 进制: M(0x4D)、Q(0x51)、T(0x54)、T(0x54)
0x4D
0x51
0x54
0x54
核心逻辑 :用
2 + N
模式(2 字节存长度 + N 字节存内容),这里 "MQTT" 长度是 4,所以先存0x00 0x04
,再存字符对应的 16 进制。2. 协议版本(1 字节)
字节索引 作用 计算 / 取值逻辑 示例值 8 MQTT 版本(3.1.1) 固定用 0x04
表示 MQTT 3.1.10x04
核心逻辑 :协议规定 MQTT 3.1.1 对应
0x04
,直接填固定值。3. 连接标志(Connect Flags,1 字节)
字节索引 作用 计算 / 取值逻辑 示例值 9 标志位组合 组成部分是告知当前 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 0xC2
核心逻辑:每一位代表不同功能(如是否用用户名密码、是否清除会话等),按需求组合后转 16 进制。
4. 保持连接时间(Keep Alive,2 字节)
字节索引 作用 计算 / 取值逻辑 示例值 10 时间高位(MSB) 假设 Keep Alive = 60秒 → 60 / 256 = 0
→ 二进制0000 0000
→0x00
0x00
11 时间低位(LSB) 60 % 256 = 60
→ 二进制0011 1100
→0x3C
(注意:原示例计算可能有误,60 转 16 进制是0x3C
,非0x3CD
,这里以正确逻辑为准 )0x3C
核心逻辑:用 2 字节存时长(秒),高位存商、低位存余数,超过范围会触发重连。
四、Payload(有效载荷,19 字节)
按
2 + N
模式存储 客户端标识、用户名、密码 ,总长度 =2(客户端标识长度头) + 9(客户端标识内容) + 2(用户名长度头) + 2(用户名内容) + 2(密码长度头) + 2(密码内容) = 19
字节。1. 客户端标识(示例:Client_GL ,9 字节内容 + 2 字节长度头 → 共 11 字节)
字节索引 作用 计算 / 取值逻辑 示例值 12 长度高位(MSB) 客户端标识长度 9 → 9 / 256 = 0
→0x00
0x00
13 长度低位(LSB) 9 % 256 = 9
→0x09
0x09
14-22 客户端标识 "Client_GL" 字符转 16 进制: C(0x43)、l(0x6C)、i(0x69)、e(0x65)、n(0x6E)、t(0x74)、_(0x5F)、G(0x47)、L(0x4C)
对应字节依次为 0x43
0x6C
0x69
0x65
0x6E
0x74
0x5F
0x47
0x4C
2. 用户名(示例:CG ,2 字节内容 + 2 字节长度头 → 共 4 字节)
字节索引 作用 计算 / 取值逻辑 示例值 23 长度高位(MSB) 用户名长度 2 → 2 / 256 = 0
→0x00
0x00
24 长度低位(LSB) 2 % 256 = 2
→0x02
0x02
25-26 用户名 "CG" 字符转 16 进制: C(0x43)、G(0x47)
0x43
0x47
3. 密码(示例:HL ,2 字节内容 + 2 字节长度头 → 共 4 字节)
字节索引 作用 计算 / 取值逻辑 示例值 27 长度高位(MSB) 密码长度 2 → 2 / 256 = 0
→0x00
0x00
28 长度低位(LSB) 2 % 256 = 2
→0x02
0x02
29-30 密码 "HL" 字符转 16 进制: H(0x48)、L(0x4C)
0x48
0x4C
核心逻辑 :所有字段都用
2 + N
模式(2 字节存长度 + N 字节存内容),按实际字符长度计算存储。
五、总结流程(快速梳理)
- 固定头 :填
0x10
(报文类型) + 计算 "可变头 + 有效载荷长度" 填0x1D
。- 可变头 :
- 协议名称:存
0x00 0x04
+ "MQTT"(0x4D 0x51 0x54 0x54
)。- 协议版本:填
0x04
。- 连接标志:按需求填
0xC2
(含用户名密码、清除会话等)。- 保持连接:填
0x00
(高位) +0x3C
(低位,Keep Alive=60 时)。- 有效载荷 :
- 客户端标识:存
0x00 0x09
+ "Client_GL" 字符对应的 16 进制。- 用户名:存
0x00 0x02
+ "CG" 字符对应的 16 进制。- 密码:存
0x00 0x02
+ "HL" 字符对应的 16 进制。
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 服务器连接之后

3.5.2****发布数据包分析
一、整体结构
MQTT PUBLISH 报文用于 客户端向服务器发布消息 ,由 Fixed Header(固定头) 、Variable Header(可变头) 、Payload(有效载荷) 三部分组成,用数组
MQTT_Publish_Data[256]
存储。
二、Fixed Header(固定头,2 字节)
字节索引 作用 计算 / 取值逻辑 示例值 0 报文类型 + 标志位 固定为 0x30
(二进制0011 0000
,表示 PUBLISH 报文,QoS 0 时标志位为0000
)0x30
1 剩余长度(可变头 + 有效载荷总长度) 可变头长度( 2 + strlen("attributes")
) + 有效载荷长度(JSON 字符串长度)需计算 核心逻辑:
- 第 0 字节固定
0x30
(PUBLISH 报文类型)。- 第 1 字节需动态计算:
可变头长度 + 有效载荷长度
,具体值取决于主题名和 JSON 内容长度。
三、Variable Header(可变头,
2 + N
字节,N
是主题名长度)作用:存储 发布主题(Topic) ,格式为
2 + N
(2 字节存长度 + N 字节存主题名)。以 ThingsCloud 要求的主题
attributes
为例:
字节索引 作用 计算 / 取值逻辑 示例值 2 主题名长度高位(MSB) strlen("attributes") / 256
→ 主题名长度是 10 →10 / 256 = 0
→0x00
0x00
3 主题名长度低位(LSB) strlen("attributes") % 256
→10 % 256 = 10
→0x0A
0x0A
4-13 主题名字符串 "attributes" 字符转 16 进制: a(0x61)、t(0x74)、t(0x74)、r(0x72)、i(0x69)、b(0x62)、u(0x75)、t(0x74)、e(0x65)、s(0x73)
对应字节依次为 0x61
0x74
0x74
0x72
0x69
0x62
0x75
0x74
0x65
0x73
核心逻辑 :
用
2 + N
模式存储主题名,先存长度(高位 + 低位),再存主题名字符对应的 16 进制。
四、Payload(有效载荷,JSON 字符串长度字节)
作用:存储 发布的实际数据,格式为 JSON 字符串(如传感器数据)。
以示例 JSON
{"Temp":25.3,"Hum":55}
为例:
字节索引 作用 计算 / 取值逻辑 示例值 14-... JSON 字符串内容 直接拼接在可变头之后,字符转 16 进制存储 如 0x7B 0x22 0x54 0x65 0x6D 0x70 0x22 0x3A 0x32 0x53 0x2E 0x33 0x2C 0x22 0x48 0x75 0x6D 0x22 0x3A 0x35 0x35 0x7D
核心逻辑 :
有效载荷是 JSON 格式的业务数据,直接拼接在可变头之后,按字符转 16 进制存储。
五、代码实现流程(快速梳理)
初始化数组 :
u8 MQTT_Publish_Data[256] = "";
(预留足够空间存报文)固定头:
- 第 0 字节:
MQTT_Publish_Data[0] = 0x30;
(PUBLISH 报文类型)- 第 1 字节:需计算
可变头长度 + 有效载荷长度
,动态填充。可变头(主题名):
- 计算主题名长度:
len = strlen("attributes")
(结果为 10)- 存长度高位:
MQTT_Publish_Data[2] = len / 256;
(即0x00
)- 存长度低位:
MQTT_Publish_Data[3] = len % 256;
(即0x0A
)- 存主题名字符:
memcpy(&MQTT_Publish_Data[4], "attributes", len);
有效载荷(JSON 数据):
- 准备 JSON 字符串:
{"Temp":25.3,"Hum":55}
- 拼接数据:
memcpy(&MQTT_Publish_Data[4 + len], json_str, strlen(json_str));
发送报文 :
调用
ESP8266_SendBuffer(...)
发送完整数组。
**3.6 SUBSCRIBE -**订阅主题
3.6.1订阅主题Topic

3.5.2****订阅主题数据包分析
一、整体结构
MQTT SUBSCRIBE 报文用于 客户端向服务器订阅主题 ,由 Fixed Header(固定头) 、Variable Header(可变头) 、Payload(有效载荷) 三部分组成,用数组
MQTT_Subscribe_Data[256]
存储。
二、Fixed Header(固定头,2 字节)
字节索引 作用 计算 / 取值逻辑 示例值 0 报文类型 + 标志位 固定为 0x80
(二进制1000 0000
,表示 SUBSCRIBE 报文,QoS 0 时标志位为0000
)0x80
1 剩余长度(可变头 + 有效载荷总长度) 可变头长度(2 字节) + 有效载荷长度( 2 + strlen("attributes/push") + 1
)需计算 核心逻辑:
- 第 0 字节固定
0x80
(SUBSCRIBE 报文类型)。- 第 1 字节需动态计算:
可变头长度 + 有效载荷长度
,具体值取决于主题名长度。
三、Variable Header(可变头,2 字节)
作用:存储 订阅数据包 ID(QoS 0 时可自定义,无实际校验逻辑)。
字节索引 作用 计算 / 取值逻辑 示例值 2 数据包 ID 高位 自定义为 0x00
0x00
3 数据包 ID 低位 自定义为 0x08
0x08
核心逻辑 :
QoS 0 时,ID 仅用于占位,可随意填(如
0x00 0x08
),不影响功能。
四、Payload(有效载荷,
2 + N + 1
字节,N
是主题名长度)作用:存储 订阅主题(Topic) 和 QoS 等级 ,格式为
2(主题名长度) + N(主题名字符) + 1(QoS 等级)
。以订阅主题
attributes/push
为例:
字节索引 作用 计算 / 取值逻辑 示例值 4 主题名长度高位(MSB) strlen("attributes/push") / 256
→ 主题名长度是 14 →14 / 256 = 0
→0x00
0x00
5 主题名长度低位(LSB) strlen("attributes/push") % 256
→14 % 256 = 14
→0x0E
0x0E
6-19 主题名字符串 "attributes/push" 字符转 16 进制: a(0x61)、t(0x74)、t(0x74)、r(0x72)、i(0x69)、b(0x62)、u(0x75)、t(0x74)、e(0x65)、s(0x73)、/(0x2F)、p(0x70)、u(0x75)、s(0x73)、h(0x68)
对应字节依次为 0x61
0x74
0x74
0x72
0x69
0x62
0x75
0x74
0x65
0x73
0x2F
0x70
0x75
0x73
0x68
20 QoS 等级 QoS 0 对应 0x00
(二进制0000 0000
)0x00
核心逻辑 :
用
2 + N + 1
模式存储订阅信息:
- 先存主题名长度(高位 + 低位),
- 再存主题名字符对应的 16 进制,
- 最后存 QoS 等级(QoS 0 填
0x00
)。
五、代码实现流程(快速梳理)
初始化数组 :
u8 MQTT_Subscribe_Data[256] = "";
(预留足够空间存报文)固定头:
- 第 0 字节:
MQTT_Subscribe_Data[0] = 0x80;
(SUBSCRIBE 报文类型)- 第 1 字节:需计算
可变头长度(2) + 有效载荷长度(2 + strlen("attributes/push") + 1)
,动态填充。可变头(数据包 ID):
- 第 2 字节:
MQTT_Subscribe_Data[2] = 0x00;
(ID 高位,自定义)- 第 3 字节:
MQTT_Subscribe_Data[3] = 0x08;
(ID 低位,自定义)有效载荷(订阅主题 + QoS):
- 计算主题名长度:
len = strlen("attributes/push")
(结果为 14)- 存长度高位:
MQTT_Subscribe_Data[4] = len / 256;
(即0x00
)- 存长度低位:
MQTT_Subscribe_Data[5] = len % 256;
(即0x0E
)- 存主题名字符:
memcpy(&MQTT_Subscribe_Data[6], "attributes/push", len);
- 存 QoS 等级:
MQTT_Subscribe_Data[6 + len] = 0x00;
(QoS 0)发送报文 :
调用
ESP8266_SendBuffer(...)
发送完整数组。
3.7 代码示例
mqtt.h:
cpp
#ifndef _MQTT_H
#define _MQTT_H
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "stm32f10x.h"
#include "ESP8266.h"
#include "systick.h"
//替换自己的
#define CLIENT_ID ""
#define USARNAME ""
#define PASSWORD ""
#define KEEP_ALIVE (60)
#define MQTT_SERVER_ADDR "bj-2-mqtt.iot-api.com"
#define MQTT_SERVER_PORT (1883)
#define PUBLISH_TOPIC "attributes"
#define SUBSCRIBE_TOPIC "attributes/push"
void MQTT_Config(void);
u8 MQTT_Connect_ThingsCloud(const char *protocol, const char *mqtt_addr, u16 mqtt_port);
void MQTT_Send_Connect_Package(void);
void MQTT_Send_Publish_Package(const char *topic, const char *data, u32 len);
void MQTT_Send_Subscribe_Package(const char *topic);
void MQTT_Data_Handler(u8 *buffer, u16 length);
#endif
mqtt.c:
cpp
#include "mqtt.h"
//固定头字节数
u32 fixed_header_len = 0;
//可变头子结束吧
u32 variable_header_len = 0;
//有效载荷字节数
u32 payload_len = 0;
void MQTT_Config(void){
// TCP连接云平台
MQTT_Connect_ThingsCloud("TCP",MQTT_SERVER_ADDR,MQTT_SERVER_PORT);
SysTick_Delay_ms(1000); // 延时1秒,等待连接稳定
//开启ESP8266透传模式,数据不解析
ESP8266_OpenTransparent();
SysTick_Delay_ms(1000);
//开启数据直发模式,起点是wifi模块,终点直发云服务器
ESP8266_SendMode();
SysTick_Delay_ms(1000);
//告知MQTT平台,当前连接的用户项目是哪一个
MQTT_Send_Connect_Package();
SysTick_Delay_ms(1000);
//发送订阅包,订阅指定的主题
MQTT_Send_Subscribe_Package(SUBSCRIBE_TOPIC);
}
/**
* @brief 通过TCP连接到MQTT云平台
* @param protocol 协议类型(如"TCP")
* @param mqtt_addr MQTT服务器地址
* @param mqtt_port MQTT服务器端口
* @return 连接结果(1表示成功,0表示失败)
*/
u8 MQTT_Connect_ThingsCloud(const char *protocol,const char *mqtt_addr,u16 mqtt_port){
// 1.定义存储AT指令的缓冲区
char cmd_buffer[128] = "";
// 2.格式化AT指令,用于建立TCP连接
// AT+CIPSTART指令用于ESP8266建立网络连接
sprintf(cmd_buffer, "AT+CIPSTART=\"%s\",\"%s\",%d\r\n", protocol, mqtt_addr, mqtt_port);
//发送AT指令
ESP8266_SendString(cmd_buffer);
SysTick_Delay_ms(500); // 延时500ms,等待指令响应
// 3. 检查ESP8266的响应是否包含"OK",返回检查结果
return ESP8266_AckCheck("OK");
}
/**
* @brief 发送MQTT连接包(CONNECT报文)
* @note 用于客户端与MQTT服务器建立连接,包含客户端标识、用户名、密码等信息
*/
void MQTT_Send_Connect_Package(void){
/*
1. 数据准备
*/
u8 MQTT_Connect_Data[256] = ""; // 存储MQTT连接报文的缓冲区
//计算客户端ID,用户名。密码的长度
u32 client_id_len = strlen(CLIENT_ID);
u32 username_len = strlen(USARNAME); // 用户名长度(注:变量名可能应为USERNAME)
u32 password_len = strlen(PASSWORD); // 密码长度
//计算各部分的长度
fixed_header_len = 2; //固定头部长度就是2
variable_header_len = 10;
//有效载荷长度
payload_len = 2 + 2 + 2 + client_id_len + username_len + password_len;
// 2. 【处理固定头】
MQTT_Connect_Data[0] = 0x10; // 报文类型:CONNECT(0x10),无标志位
// 剩余长度 = 可变头长度 + 有效载荷长度
MQTT_Connect_Data[1] = variable_header_len + payload_len;
// 3. 【处理可变头】
// 3.1 协议名(MQTT)及其长度
// 协议名长度为2字节(高位在前,低位在后)
MQTT_Connect_Data[2] = strlen("MQTT") / 256; //高字节
MQTT_Connect_Data[3] = strlen("MQTT") % 256; //低字节
// 拷贝协议名字符串("MQTT")到缓冲区,四个字节
memcpy(&MQTT_Connect_Data[4], "MQTT", strlen("MQTT"));
MQTT_Connect_Data[8] = 0x04; // 协议版本:0x04表示MQTT 3.1.1
// 3.2 连接标志(Connect Flags)
// 0xC2 = 11000010,含义:
// - 第7位:用户名标志(1,表示有效载荷包含用户名)
// - 第6位:密码标志(1,表示有效载荷包含密码)
// - 第1位:清除会话标志(1,表示清除之前的会话)
MQTT_Connect_Data[9] = 0xC2;
// 3.3 保持连接时间(Keep Alive),单位为秒
// 这里设置为60秒,分为高低两个字节存储
MQTT_Connect_Data[10] = KEEP_ALIVE / 256; // 高位字节
MQTT_Connect_Data[11] = KEEP_ALIVE % 256; // 低位字节
// 4. Payload 有效载荷内容
/* Client_ID - 客户端标识 */
// 客户端ID长度(2字节)
MQTT_Connect_Data[12] = client_id_len / 256; // 高位
MQTT_Connect_Data[13] = client_id_len % 256; // 低位
// 拷贝客户端ID到缓冲区
memcpy(&MQTT_Connect_Data[14], CLIENT_ID, client_id_len);
/* Username - 用户名 */
// 计算用户名在缓冲区中的起始位置
u16 username_index = 14 + client_id_len;
// 用户名长度(2字节)
MQTT_Connect_Data[username_index] = username_len / 256; // 高位
MQTT_Connect_Data[username_index + 1] = username_len % 256; // 低位
// 拷贝用户名到缓冲区
memcpy(&MQTT_Connect_Data[username_index + 2], USARNAME, username_len);
/* Password - 密码 */
// 计算密码在缓冲区中的起始位置
u16 password_index = username_index + 2 + username_len;
// 密码长度(2字节)
MQTT_Connect_Data[password_index] = password_len / 256; // 高位
MQTT_Connect_Data[password_index + 1] = password_len % 256; // 低位
// 拷贝密码到缓冲区
memcpy(&MQTT_Connect_Data[password_index + 2], PASSWORD, password_len);
// 发送完整的MQTT连接包(固定头+可变头+有效载荷)
ESP8266_SendBuffer(MQTT_Connect_Data, fixed_header_len + variable_header_len + payload_len);
}
/**
* @brief 发送MQTT订阅包(SUBSCRIBE报文)
* @param topic 要订阅的主题
* @note 用于客户端订阅指定的主题,以接收该主题的消息
*/
void MQTT_Send_Subscribe_Package(const char *topic){
// 1.数据准备
u8 MQTT_Subscribe_Data[128] = "";
u32 topic_len = strlen(topic);
//计算各部分的长度
fixed_header_len = 2;
variable_header_len = 2;
//有效载荷长度,主题长度 + 主题 + qos0 1个字节
payload_len = 2 + topic_len + 1;
// 1.固定头内容
MQTT_Subscribe_Data[0] = 0x82; // 报文类型:SUBSCRIBE(0x80),标志位0x02
//剩余长度 = 可变头长度 + 有效
MQTT_Subscribe_Data[1] = variable_header_len + payload_len;
// 2. 可变头 - 消息ID(用于匹配订阅确认)由于是qos0,这两个字节随便填
MQTT_Subscribe_Data[2] = 0x00; // 消息ID高位
MQTT_Subscribe_Data[3] = 0x08; // 消息ID低位(此处固定为0x0008)
// 3. 有效载荷
// 主题长度(2字节)
MQTT_Subscribe_Data[4] = topic_len / 256; // 高位
MQTT_Subscribe_Data[5] = topic_len % 256; // 低位
// 拷贝主题到缓冲区
memcpy(&MQTT_Subscribe_Data[6], topic, topic_len);
// QoS等级(0x00表示QoS 0,最多一次交付)
MQTT_Subscribe_Data[6 + topic_len] = 0x00;
// 发送完整的订阅包
ESP8266_SendBuffer(MQTT_Subscribe_Data, fixed_header_len + variable_header_len + payload_len);
}
/**
* @brief 发送MQTT发布包(PUBLISH报文)
* @param topic 发布消息的主题
* @param data 要发布的数据内容
* @param len 数据内容的长度
* @note 用于客户端向指定主题发布消息
*/
void MQTT_Send_Publish_Package(const char *topic, const char *data, u32 len)
{
// 1. 数据准备
u8 MQTT_Publish_Data[128] = ""; // 存储发布报文的缓冲区
u32 topic_len = strlen(topic); // 发布主题的长度
// 计算各部分长度
fixed_header_len = 2; // 固定头长度为2字节
variable_header_len = 2 + topic_len; // 可变头长度 = 主题长度(2字节) + 主题内容长度
payload_len = len; // 有效载荷长度为数据长度
// 2. 固定头内容
MQTT_Publish_Data[0] = 0x30; // 报文类型:PUBLISH(0x30),标志位0x00(QoS 0)
// 剩余长度 = 可变头长度 + 有效载荷长度
MQTT_Publish_Data[1] = variable_header_len + payload_len;
// 3. 可变头 - 主题相关信息
// 主题长度(2字节)
MQTT_Publish_Data[2] = topic_len / 256; // 高位
MQTT_Publish_Data[3] = topic_len % 256; // 低位
// 拷贝主题到缓冲区
memcpy(&MQTT_Publish_Data[4], topic, topic_len);
// 4. 有效载荷 - 发布的数据内容
memcpy(&MQTT_Publish_Data[4 + topic_len], data, len);
// 发送完整的发布包
ESP8266_SendBuffer(MQTT_Publish_Data, fixed_header_len + variable_header_len + payload_len);
}