文章目录
-
- 一、前言
-
- [1.1 技术背景](#1.1 技术背景)
- [1.2 MQTT协议特点](#1.2 MQTT协议特点)
- [1.3 本文目标](#1.3 本文目标)
- 二、环境准备
-
- [2.1 EMQ X服务器搭建](#2.1 EMQ X服务器搭建)
- [2.2 硬件准备](#2.2 硬件准备)
- [2.3 软件环境](#2.3 软件环境)
- 三、核心实现
-
- [3.1 MQTT协议栈移植](#3.1 MQTT协议栈移植)
- [3.2 ESP8266 TCP驱动](#3.2 ESP8266 TCP驱动)
- [3.3 主程序实现](#3.3 主程序实现)
- 四、测试验证
-
- [4.1 EMQ X Dashboard测试](#4.1 EMQ X Dashboard测试)
- [4.2 功能测试](#4.2 功能测试)
- 五、故障排查
-
- [5.1 连接问题](#5.1 连接问题)
- [5.2 通信问题](#5.2 通信问题)
- 六、总结
-
- [6.1 核心知识点](#6.1 核心知识点)
- [6.2 扩展学习](#6.2 扩展学习)
一、前言
1.1 技术背景
MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅消息协议,专为低带宽、高延迟或不可靠网络设计。它是物联网(IoT)领域最常用的通信协议之一。
EMQ X是一款开源的高性能MQTT消息服务器,支持百万级并发连接,提供企业级的稳定性。使用STM32连接EMQ X,可以构建可靠的物联网通信系统。
1.2 MQTT协议特点
- 轻量级:协议头部最小仅2字节
- 发布/订阅模式:解耦消息生产者和消费者
- QoS机制:支持三种服务质量等级
- 遗嘱消息:设备异常断开时通知其他客户端
- 保留消息:新订阅者立即收到最新消息
QoS等级:
- QoS 0:最多一次(At most once)
- QoS 1:至少一次(At least once)
- QoS 2:恰好一次(Exactly once)
1.3 本文目标
通过本文,你将学会:
- MQTT协议核心概念和工作原理
- EMQ X服务器的搭建和配置
- STM32 MQTT客户端实现
- 发布/订阅模式的实际应用
- QoS等级选择和实现
- 遗嘱消息和保留消息的使用
技术栈:
- 开发板:STM32F103C8T6
- WiFi模块:ESP8266
- MQTT服务器:EMQ X
- 客户端库:Paho MQTT Embedded
二、环境准备
2.1 EMQ X服务器搭建
方式1:本地部署(Docker)
bash
# 拉取EMQ X镜像
docker pull emqx/emqx:5.0.0
# 运行容器
docker run -d --name emqx \
-p 1883:1883 \
-p 8083:8083 \
-p 8084:8084 \
-p 8883:8883 \
-p 18083:18083 \
emqx/emqx:5.0.0
# 访问Dashboard
# http://localhost:18083
# 默认账号:admin / public
方式2:使用公共测试服务器
服务器:broker.emqx.io
端口:1883(MQTT)/ 8083(WebSocket)
方式3:云服务部署
- EMQ X Cloud(托管服务)
- AWS IoT Core
- 阿里云物联网平台
2.2 硬件准备
| 器件 | 数量 | 说明 |
|---|---|---|
| STM32F103C8T6 | 1 | 主控芯片 |
| ESP8266-01S | 1 | WiFi模块 |
| LED | 2 | 状态指示 |
| 杜邦线 | 若干 | 连接 |
硬件连接:
STM32F103C8T6 ESP8266-01S
3.3V --------------- VCC
GND --------------- GND
PA9 --------------- RXD
PA10 --------------- TXD
STM32F103C8T6 LED
PB12 --------------- LED1 (连接状态)
PB13 --------------- LED2 (数据指示)
2.3 软件环境
STM32CubeMX配置:
- USART1:调试串口(PA9/PA10)
- USART2:ESP8266通信(PA2/PA3)
- TIM2:1ms定时器(MQTT保活)
三、核心实现
3.1 MQTT协议栈移植
📄 创建文件:
Inc/MQTTClient.h
c
#ifndef __MQTT_CLIENT_H
#define __MQTT_CLIENT_H
#include "stm32f1xx_hal.h"
#include <string.h>
#include <stdio.h>
// MQTT配置
#define MQTT_MAX_PACKET_SIZE 512
#define MQTT_KEEPALIVE 60
#define MQTT_MAX_RECONNECT 3
// QoS等级
#define MQTT_QOS0 0
#define MQTT_QOS1 1
#define MQTT_QOS2 2
// 消息回调函数类型
typedef void (*MQTTMessageCallback)(char *topic, uint8_t *payload, uint16_t len);
typedef void (*MQTTConnectCallback)(uint8_t connected);
// MQTT客户端结构体
typedef struct
{
char client_id[32];
char username[32];
char password[32];
char will_topic[64];
char will_msg[64];
uint8_t will_qos;
uint8_t will_retain;
uint8_t clean_session;
uint16_t keepalive;
// 回调函数
MQTTMessageCallback msg_callback;
MQTTConnectCallback conn_callback;
// 状态
uint8_t connected;
uint32_t last_ping;
} MQTTClient_TypeDef;
// 函数声明
uint8_t MQTT_Init(MQTTClient_TypeDef *client);
uint8_t MQTT_Connect(char *broker, uint16_t port);
void MQTT_Disconnect(void);
uint8_t MQTT_Publish(char *topic, uint8_t *payload, uint16_t len, uint8_t qos, uint8_t retain);
uint8_t MQTT_Subscribe(char *topic, uint8_t qos);
uint8_t MQTT_Unsubscribe(char *topic);
void MQTT_Process(void);
uint8_t MQTT_IsConnected(void);
#endif
📄 创建文件:
Src/MQTTClient.c
c
#include "MQTTClient.h"
#include "esp8266.h"
// MQTT固定头部标志
#define MQTT_CONNECT 1
#define MQTT_CONNACK 2
#define MQTT_PUBLISH 3
#define MQTT_PUBACK 4
#define MQTT_PUBREC 5
#define MQTT_PUBREL 6
#define MQTT_PUBCOMP 7
#define MQTT_SUBSCRIBE 8
#define MQTT_SUBACK 9
#define MQTT_UNSUBSCRIBE 10
#define MQTT_UNSUBACK 11
#define MQTT_PINGREQ 12
#define MQTT_PINGRESP 13
#define MQTT_DISCONNECT 14
static MQTTClient_TypeDef *mqtt_client = NULL;
static uint8_t packet_buffer[MQTT_MAX_PACKET_SIZE];
/**
* @brief 编码剩余长度
*/
static uint16_t EncodeRemainingLength(uint8_t *buf, uint32_t length)
{
uint16_t i = 0;
uint8_t encoded_byte;
do
{
encoded_byte = length % 128;
length = length / 128;
if(length > 0)
encoded_byte |= 128;
buf[i++] = encoded_byte;
} while(length > 0);
return i;
}
/**
* @brief 解码剩余长度
*/
static uint16_t DecodeRemainingLength(uint8_t *buf, uint32_t *length)
{
uint16_t i = 0;
uint8_t encoded_byte;
uint32_t multiplier = 1;
*length = 0;
do
{
encoded_byte = buf[i++];
*length += (encoded_byte & 127) * multiplier;
multiplier *= 128;
if(multiplier > 128 * 128 * 128)
return 0; // 错误
} while((encoded_byte & 128) != 0);
return i;
}
/**
* @brief 写入字符串(2字节长度+数据)
*/
static uint16_t WriteString(uint8_t *buf, char *str)
{
uint16_t len = strlen(str);
buf[0] = (len >> 8) & 0xFF;
buf[1] = len & 0xFF;
memcpy(&buf[2], str, len);
return len + 2;
}
/**
* @brief MQTT初始化
*/
uint8_t MQTT_Init(MQTTClient_TypeDef *client)
{
mqtt_client = client;
client->connected = 0;
client->last_ping = 0;
return 0;
}
/**
* @brief 构建CONNECT报文
*/
static uint16_t BuildConnectPacket(uint8_t *buf, MQTTClient_TypeDef *client)
{
uint16_t i = 0;
uint16_t payload_len = 0;
uint16_t connect_flags = 0;
// 固定头部
buf[i++] = MQTT_CONNECT << 4;
// 计算载荷长度
payload_len += 2 + 4; // 协议名长度 + "MQTT"
payload_len += 1; // 协议级别
payload_len += 1; // 连接标志
payload_len += 2; // 保活时间
payload_len += 2 + strlen(client->client_id); // 客户端ID
if(strlen(client->username) > 0)
{
connect_flags |= 0x80;
payload_len += 2 + strlen(client->username);
}
if(strlen(client->password) > 0)
{
connect_flags |= 0x40;
payload_len += 2 + strlen(client->password);
}
if(strlen(client->will_topic) > 0)
{
connect_flags |= 0x04 | (client->will_qos << 3);
if(client->will_retain)
connect_flags |= 0x20;
payload_len += 2 + strlen(client->will_topic);
payload_len += 2 + strlen(client->will_msg);
}
if(client->clean_session)
connect_flags |= 0x02;
// 编码剩余长度
i += EncodeRemainingLength(&buf[i], payload_len);
// 可变头部
i += WriteString(&buf[i], "MQTT"); // 协议名
buf[i++] = 4; // 协议级别(MQTT 3.1.1)
buf[i++] = connect_flags;
buf[i++] = (client->keepalive >> 8) & 0xFF;
buf[i++] = client->keepalive & 0xFF;
// 载荷
i += WriteString(&buf[i], client->client_id);
if(strlen(client->will_topic) > 0)
{
i += WriteString(&buf[i], client->will_topic);
i += WriteString(&buf[i], client->will_msg);
}
if(strlen(client->username) > 0)
{
i += WriteString(&buf[i], client->username);
}
if(strlen(client->password) > 0)
{
i += WriteString(&buf[i], client->password);
}
return i;
}
/**
* @brief 连接MQTT服务器
*/
uint8_t MQTT_Connect(char *broker, uint16_t port)
{
char port_str[8];
uint16_t packet_len;
uint32_t timeout;
if(mqtt_client == NULL)
return 1;
printf("Connecting to MQTT broker: %s:%d\r\n", broker, port);
// 建立TCP连接
snprintf(port_str, sizeof(port_str), "%d", port);
if(ESP8266_TCPConnect(broker, port_str) != 0)
{
printf("TCP connect failed!\r\n");
return 1;
}
// 发送CONNECT报文
packet_len = BuildConnectPacket(packet_buffer, mqtt_client);
if(ESP8266_TCPSend(packet_buffer, packet_len) != 0)
{
printf("Send CONNECT failed!\r\n");
return 1;
}
// 等待CONNACK
timeout = HAL_GetTick() + 5000;
while(HAL_GetTick() < timeout)
{
uint16_t rx_len = ESP8266_TCPReceive(packet_buffer, MQTT_MAX_PACKET_SIZE, 100);
if(rx_len > 0)
{
// 解析CONNACK
if((packet_buffer[0] >> 4) == MQTT_CONNACK)
{
uint8_t return_code = packet_buffer[3];
if(return_code == 0)
{
mqtt_client->connected = 1;
mqtt_client->last_ping = HAL_GetTick();
if(mqtt_client->conn_callback)
mqtt_client->conn_callback(1);
printf("MQTT connected!\r\n");
return 0;
}
else
{
printf("CONNACK error code: %d\r\n", return_code);
return 1;
}
}
}
}
printf("MQTT connect timeout!\r\n");
return 1;
}
/**
* @brief 断开连接
*/
void MQTT_Disconnect(void)
{
uint8_t disconnect_packet[2] = {MQTT_DISCONNECT << 4, 0};
ESP8266_TCPSend(disconnect_packet, 2);
ESP8266_TCPDisconnect();
mqtt_client->connected = 0;
if(mqtt_client->conn_callback)
mqtt_client->conn_callback(0);
printf("MQTT disconnected\r\n");
}
/**
* @brief 构建PUBLISH报文
*/
static uint16_t BuildPublishPacket(uint8_t *buf, char *topic,
uint8_t *payload, uint16_t payload_len,
uint8_t qos, uint8_t retain, uint16_t *packet_id)
{
uint16_t i = 0;
uint16_t variable_len;
uint8_t fixed_header;
static uint16_t next_packet_id = 1;
// 固定头部
fixed_header = MQTT_PUBLISH << 4;
if(qos > 0)
fixed_header |= (qos << 1);
if(retain)
fixed_header |= 0x01;
buf[i++] = fixed_header;
// 计算可变头部长度
variable_len = 2 + strlen(topic);
if(qos > 0)
{
variable_len += 2; // Packet ID
*packet_id = next_packet_id++;
}
// 编码剩余长度
i += EncodeRemainingLength(&buf[i], variable_len + payload_len);
// 可变头部
i += WriteString(&buf[i], topic);
if(qos > 0)
{
buf[i++] = (*packet_id >> 8) & 0xFF;
buf[i++] = *packet_id & 0xFF;
}
// 载荷
memcpy(&buf[i], payload, payload_len);
i += payload_len;
return i;
}
/**
* @brief 发布消息
*/
uint8_t MQTT_Publish(char *topic, uint8_t *payload, uint16_t len,
uint8_t qos, uint8_t retain)
{
uint16_t packet_len;
uint16_t packet_id;
if(!mqtt_client->connected)
return 1;
packet_len = BuildPublishPacket(packet_buffer, topic, payload, len,
qos, retain, &packet_id);
if(ESP8266_TCPSend(packet_buffer, packet_len) != 0)
return 1;
// QoS 1: 等待PUBACK
if(qos == 1)
{
// 实现PUBACK等待逻辑
// 简化处理,实际应用需要重传机制
}
return 0;
}
/**
* @brief 构建SUBSCRIBE报文
*/
static uint16_t BuildSubscribePacket(uint8_t *buf, char *topic, uint8_t qos)
{
uint16_t i = 0;
uint16_t payload_len;
static uint16_t packet_id = 1;
// 固定头部
buf[i++] = (MQTT_SUBSCRIBE << 4) | 0x02; // QoS 1
// 计算载荷长度
payload_len = 2; // Packet ID
payload_len += 2 + strlen(topic) + 1; // Topic + QoS
// 编码剩余长度
i += EncodeRemainingLength(&buf[i], payload_len);
// 可变头部
buf[i++] = (packet_id >> 8) & 0xFF;
buf[i++] = packet_id & 0xFF;
packet_id++;
// 载荷
i += WriteString(&buf[i], topic);
buf[i++] = qos;
return i;
}
/**
* @brief 订阅主题
*/
uint8_t MQTT_Subscribe(char *topic, uint8_t qos)
{
uint16_t packet_len;
if(!mqtt_client->connected)
return 1;
printf("Subscribing to: %s (QoS %d)\r\n", topic, qos);
packet_len = BuildSubscribePacket(packet_buffer, topic, qos);
if(ESP8266_TCPSend(packet_buffer, packet_len) != 0)
return 1;
return 0;
}
/**
* @brief 处理接收到的报文
*/
static void ProcessReceivedPacket(uint8_t *buf, uint16_t len)
{
uint8_t packet_type = (buf[0] >> 4) & 0x0F;
switch(packet_type)
{
case MQTT_PUBLISH:
{
// 解析PUBLISH报文
uint16_t topic_len = (buf[2] << 8) | buf[3];
char topic[64];
uint16_t payload_offset = 4 + topic_len;
uint8_t qos = (buf[0] >> 1) & 0x03;
if(qos > 0)
payload_offset += 2; // Packet ID
memcpy(topic, &buf[4], topic_len);
topic[topic_len] = '\0';
uint16_t payload_len = len - payload_offset - 2; // 减去固定头部
if(mqtt_client->msg_callback)
{
mqtt_client->msg_callback(topic, &buf[payload_offset], payload_len);
}
// QoS 1: 发送PUBACK
if(qos == 1)
{
uint8_t puback[4];
puback[0] = MQTT_PUBACK << 4;
puback[1] = 2;
puback[2] = buf[payload_offset - 2];
puback[3] = buf[payload_offset - 1];
ESP8266_TCPSend(puback, 4);
}
break;
}
case MQTT_PINGRESP:
printf("PINGRESP received\r\n");
break;
case MQTT_SUBACK:
printf("SUBACK received\r\n");
break;
default:
printf("Received packet type: %d\r\n", packet_type);
break;
}
}
/**
* @brief MQTT主循环处理
*/
void MQTT_Process(void)
{
uint16_t rx_len;
if(!mqtt_client->connected)
return;
// 接收数据
rx_len = ESP8266_TCPReceive(packet_buffer, MQTT_MAX_PACKET_SIZE, 0);
if(rx_len > 0)
{
ProcessReceivedPacket(packet_buffer, rx_len);
}
// 发送PINGREQ(保活)
if(HAL_GetTick() - mqtt_client->last_ping >= (mqtt_client->keepalive * 1000 / 2))
{
uint8_t ping_packet[2] = {MQTT_PINGREQ << 4, 0};
ESP8266_TCPSend(ping_packet, 2);
mqtt_client->last_ping = HAL_GetTick();
printf("PINGREQ sent\r\n");
}
}
/**
* @brief 检查连接状态
*/
uint8_t MQTT_IsConnected(void)
{
return mqtt_client ? mqtt_client->connected : 0;
}
3.2 ESP8266 TCP驱动
📄 创建文件:
Src/esp8266_tcp.c
c
#include "esp8266.h"
extern UART_HandleTypeDef huart2;
/**
* @brief 建立TCP连接
*/
uint8_t ESP8266_TCPConnect(char *server, char *port)
{
char cmd[128];
// 设置单连接模式
ESP8266_SendCommand("AT+CIPMUX=0", "OK", 1000);
// 建立连接
snprintf(cmd, sizeof(cmd), "AT+CIPSTART=\"TCP\",\"%s\",%s", server, port);
if(ESP8266_SendCommand(cmd, "OK", 10000) != 0)
{
printf("TCP connect failed!\r\n");
return 1;
}
printf("TCP connected to %s:%s\r\n", server, port);
return 0;
}
/**
* @brief 断开TCP连接
*/
void ESP8266_TCPDisconnect(void)
{
ESP8266_SendCommand("AT+CIPCLOSE", "OK", 2000);
}
/**
* @brief 发送TCP数据
*/
uint8_t ESP8266_TCPSend(uint8_t *data, uint16_t len)
{
char cmd[32];
char response[32];
// 设置发送长度
snprintf(cmd, sizeof(cmd), "AT+CIPSEND=%d", len);
// 发送命令,等待">"提示
HAL_UART_Transmit(&huart2, (uint8_t*)cmd, strlen(cmd), 1000);
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", 2, 100);
// 等待">"
uint32_t timeout = HAL_GetTick() + 5000;
while(HAL_GetTick() < timeout)
{
uint16_t rx_len = ESP8266_GetRxData((uint8_t*)response, sizeof(response));
if(rx_len > 0 && strstr(response, ">") != NULL)
{
break;
}
HAL_Delay(10);
}
// 发送数据
HAL_UART_Transmit(&huart2, data, len, 5000);
// 等待发送完成
timeout = HAL_GetTick() + 5000;
while(HAL_GetTick() < timeout)
{
uint16_t rx_len = ESP8266_GetRxData((uint8_t*)response, sizeof(response));
if(rx_len > 0 && strstr(response, "SEND OK") != NULL)
{
return 0;
}
HAL_Delay(10);
}
return 1;
}
/**
* @brief 接收TCP数据
*/
uint16_t ESP8266_TCPReceive(uint8_t *buf, uint16_t max_len, uint32_t timeout)
{
uint16_t rx_len = ESP8266_GetRxData(buf, max_len);
if(rx_len > 0)
{
// 检查是否是+IPD数据
char *ipd = strstr((char*)buf, "+IPD,");
if(ipd != NULL)
{
// 解析数据长度
int data_len;
sscanf(ipd, "+IPD,%d:", &data_len);
// 找到数据开始位置
char *data_start = strchr(ipd, ':') + 1;
// 复制数据
if(data_len > max_len)
data_len = max_len;
memcpy(buf, data_start, data_len);
return data_len;
}
}
return 0;
}
3.3 主程序实现
📄 创建文件:
Src/main.c
c
#include "main.h"
#include "MQTTClient.h"
#include "esp8266.h"
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
TIM_HandleTypeDef htim2;
// MQTT客户端配置
MQTTClient_TypeDef mqtt_client;
// 消息回调
void MQTT_MessageCallback(char *topic, uint8_t *payload, uint16_t len)
{
payload[len] = '\0';
printf("Received message:\r\n");
printf(" Topic: %s\r\n", topic);
printf(" Payload: %s\r\n", payload);
// LED控制
if(strstr(topic, "led/control") != NULL)
{
if(strstr((char*)payload, "on") != NULL)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
printf("LED ON\r\n");
}
else if(strstr((char*)payload, "off") != NULL)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
printf("LED OFF\r\n");
}
}
}
// 连接回调
void MQTT_ConnectCallback(uint8_t connected)
{
if(connected)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
printf("MQTT Connected!\r\n");
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);
printf("MQTT Disconnected!\r\n");
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_TIM2_Init();
printf("\r\n================================\r\n");
printf("STM32 MQTT Client Demo\r\n");
printf("================================\r\n\r\n");
// 初始化ESP8266
while(ESP8266_Init() != 0)
{
printf("ESP8266 init failed, retry...\r\n");
HAL_Delay(2000);
}
// 连接WiFi
while(ESP8266_ConnectWiFi() != 0)
{
printf("WiFi connect failed, retry...\r\n");
HAL_Delay(5000);
}
// 配置MQTT客户端
strcpy(mqtt_client.client_id, "stm32_client_001");
strcpy(mqtt_client.username, "");
strcpy(mqtt_client.password, "");
strcpy(mqtt_client.will_topic, "stm32/status");
strcpy(mqtt_client.will_msg, "offline");
mqtt_client.will_qos = MQTT_QOS1;
mqtt_client.will_retain = 1;
mqtt_client.clean_session = 1;
mqtt_client.keepalive = 60;
mqtt_client.msg_callback = MQTT_MessageCallback;
mqtt_client.conn_callback = MQTT_ConnectCallback;
MQTT_Init(&mqtt_client);
// 连接MQTT服务器
while(MQTT_Connect("broker.emqx.io", 1883) != 0)
{
printf("MQTT connect failed, retry...\r\n");
HAL_Delay(5000);
}
// 订阅主题
MQTT_Subscribe("stm32/led/control", MQTT_QOS1);
MQTT_Subscribe("stm32/command", MQTT_QOS0);
// 发布上线消息(保留消息)
MQTT_Publish("stm32/status", (uint8_t*)"online", 6, MQTT_QOS1, 1);
printf("System ready!\r\n");
uint32_t last_publish = 0;
char payload[64];
while(1)
{
// MQTT处理
MQTT_Process();
// 每10秒发布一次数据
if(HAL_GetTick() - last_publish >= 10000)
{
last_publish = HAL_GetTick();
// 生成模拟数据
int temp = 20 + (HAL_GetTick() % 15);
int humidity = 40 + (HAL_GetTick() % 30);
snprintf(payload, sizeof(payload),
"{\"temperature\":%d,\"humidity\":%d}", temp, humidity);
MQTT_Publish("stm32/sensor", (uint8_t*)payload, strlen(payload),
MQTT_QOS0, 0);
printf("Published: %s\r\n", payload);
}
HAL_Delay(100);
}
}
四、测试验证
4.1 EMQ X Dashboard测试
1. 查看连接:
- 登录EMQ X Dashboard
- 进入"连接"页面
- 确认STM32客户端已连接
2. 发布测试消息:
主题:stm32/led/control
消息:on
QoS:1
3. 订阅测试:
主题:stm32/sensor
观察接收到的传感器数据
4.2 功能测试
| 测试项 | 操作 | 预期结果 |
|---|---|---|
| 连接测试 | 启动设备 | LED2亮起,显示在线 |
| 订阅测试 | Dashboard发布消息 | STM32接收并处理 |
| 发布测试 | 等待10秒 | 传感器数据上传到Dashboard |
| 遗嘱测试 | 断开设备电源 | Dashboard收到offline消息 |
五、故障排查
5.1 连接问题
问题1:无法连接MQTT服务器
- 检查服务器地址和端口
- 确认防火墙允许1883端口
- 验证网络连接
问题2:CONNACK返回错误码
- 0x01:不支持协议版本
- 0x02:无效的客户端标识符
- 0x03:服务器不可用
- 0x04:无效的用户名或密码
- 0x05:未授权
5.2 通信问题
问题3:消息发布失败
- 检查TCP连接状态
- 确认主题格式正确
- 验证消息大小不超过限制
问题4:无法接收消息
- 确认已订阅对应主题
- 检查QoS等级匹配
- 验证消息格式正确
六、总结
6.1 核心知识点
- MQTT协议:发布/订阅模式、QoS机制、遗嘱消息
- 报文格式:CONNECT、PUBLISH、SUBSCRIBE等报文的构建和解析
- EMQ X:开源MQTT服务器的部署和使用
6.2 扩展学习
- TLS加密:使用MQTT over TLS保障通信安全
- WebSocket:在浏览器中使用MQTT
- 集群部署:EMQ X集群配置实现高可用