3. QoS 0消息传递

QoS 0消息传递 学习卡片

任务目标

本案例要求你在ESP32上配置MQTT客户端,以QoS 0级别发布和订阅消息,并通过人为制造网络不稳定来观察QoS 0可能带来的消息丢失现象。完成本案例后,你将理解QoS 0的“尽力而为”特性及其适用场景。

环境与依赖

  • 硬件:ESP32开发板(如ESP32-DevKitC)
  • 软件:Arduino IDE(需安装ESP32开发板支持包)或ESP-IDF
  • MQTT Broker:本地运行Mosquitto(或使用公共测试Broker如broker.emqx.io:1883)
  • 所需库:PubSubClient(Arduino环境下通过库管理器安装)

完整实现代码

以下代码实现了QoS 0消息的发布与订阅。核心逻辑包括:连接WiFi、连接MQTT Broker、订阅主题、以QoS 0发布消息,并通过串口输出消息ID以便观察丢失情况。

#include <WiFi.h>
#include <PubSubClient.h>

// WiFi配置
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// MQTT Broker配置
const char* mqtt_server = "broker.emqx.io";
const int mqtt_port = 1883;
const char* client_id = "ESP32_QoS0_Client";

// 主题定义
const char* pub_topic = "esp32/qos0/test";
const char* sub_topic = "esp32/qos0/response";

WiFiClient espClient;
PubSubClient client(espClient);

// 消息计数器
unsigned int msgCounter = 0;
unsigned long lastPublishTime = 0;
const unsigned long publishInterval = 2000; // 每2秒发布一次

void setup_wifi() {
    delay(10);
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("\nWiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.println();
}

void reconnect() {
    while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        if (client.connect(client_id)) {
            Serial.println("connected");
            client.subscribe(sub_topic, 0); // QoS 0订阅
        } else {
            Serial.print("failed, rc=");
            Serial.print(client.state());
            Serial.println(" try again in 5 seconds");
            delay(5000);
        }
    }
}

void setup() {
    Serial.begin(115200);
    setup_wifi();
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(callback);
}

void loop() {
    if (!client.connected()) {
        reconnect();
    }
    client.loop();

    // 定时发布QoS 0消息
    if (millis() - lastPublishTime > publishInterval) {
        lastPublishTime = millis();
        msgCounter++;
        String payload = "QoS0_MSG_" + String(msgCounter);
        client.publish(pub_topic, payload.c_str(), false); // QoS 0发布
        Serial.print("Published: ");
        Serial.print(payload);
        Serial.print(" | QoS: 0 | MsgID: ");
        Serial.println(msgCounter);
    }
}

运行与验证方法

运行步骤:

  1. 在Arduino IDE中,将上述代码中的WiFi凭据替换为你自己的网络信息。
  2. 选择正确的开发板(Tools → Board → ESP32 Dev Module)和端口,编译并上传代码。
  3. 打开串口监视器(波特率115200),观察ESP32连接WiFi和MQTT Broker的日志。

验证QoS 0消息传递:

  1. 在PC上安装Mosquitto客户端工具(或使用MQTT X等图形工具),订阅主题esp32/qos0/test:
mosquitto_sub -h broker.emqx.io -t "esp32/qos0/test" -v
  1. 观察串口监视器,ESP32每2秒发布一条带递增编号的消息。PC端应能收到大部分消息,但可能偶尔丢失。
  2. 制造消息丢失:在ESP32运行期间,拔掉路由器电源5秒再恢复。重新连接后,观察串口输出——ESP32会继续从断点编号发布消息,但PC端收到的消息编号会出现跳号(例如从5直接跳到8),证明QoS 0在连接中断期间的消息已丢失。

通过判定标准:

  • PC端收到的消息编号不连续(存在跳号),证明QoS 0不保证消息必达。
  • ESP32串口输出显示消息编号连续递增,说明发送端未感知丢失。
  • 重新连接后,ESP32能自动恢复发布(无需重启),且PC端能继续收到新消息。

关键说明

QoS 0是MQTT协议中开销最低的级别,消息发送后即丢弃,不等待Broker确认。本案例中client.publish()的第三个参数设为false即表示QoS 0。若需观察更明显的丢失效果,可增大发布频率或使用不稳定的网络环境(如移动热点)。实际工程中,QoS 0适用于传感器数据上报等允许偶发丢失的场景,而QoS 1/2则用于需要可靠投递的控制指令。