4. QoS 1消息确认

任务目标

在ESP32与MQTT Broker之间使用QoS 1级别发布消息,验证Broker正确返回PUBACK确认,并理解QoS 1“至少一次投递”的语义。本案例聚焦发布端实现,订阅端仅作辅助验证。

环境与依赖

  • 硬件:ESP32开发板(如ESP32-DevKitC)
  • 软件:Arduino IDE(安装esp32核心)或ESP-IDF
  • 库:PubSubClient 2.8+(Arduino环境下通过库管理器安装)
  • Broker:本地或公网MQTT Broker(如mosquitto,broker.emqx.io:1883)

核心实现

PubSubClient库的publish()函数第三个参数即为QoS等级。当QoS=1时,库内部自动处理PUBACK等待与重发逻辑。以下代码实现:连接WiFi与Broker后,以QoS 1发布一条消息,并通过回调函数确认Broker已接收。

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

const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";
const char* mqtt_server = "broker.emqx.io";
const int mqtt_port = 1883;
const char* topic = "esp32/qos1/test";

WiFiClient espClient;
PubSubClient client(espClient);

// 用于验证Broker是否收到消息的订阅主题
const char* ack_topic = "esp32/qos1/ack";

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("收到消息,主题: ");
  Serial.println(topic);
  Serial.print("内容: ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("连接WiFi: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi已连接");
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("连接MQTT Broker...");
    if (client.connect("ESP32_QoS1_Client")) {
      Serial.println("已连接");
      // 订阅确认主题,用于验证Broker确实收到了QoS 1消息
      client.subscribe(ack_topic, 1);
    } else {
      Serial.print("连接失败,状态码=");
      Serial.print(client.state());
      Serial.println(" 5秒后重试");
      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();

  static unsigned long last_publish = 0;
  if (millis() - last_publish > 10000) {  // 每10秒发布一次
    String msg = "QoS1消息 #" + String(millis() / 1000);
    // 第三个参数1表示QoS级别为1
    boolean result = client.publish(topic, msg.c_str(), 1);
    if (result) {
      Serial.print("QoS 1发布成功: ");
      Serial.println(msg);
    } else {
      Serial.println("QoS 1发布失败");
    }
    last_publish = millis();
  }
}

验证方法

  1. 将代码中的WiFi凭据替换为实际值,编译并烧录到ESP32。
  2. 打开串口监视器(波特率115200),观察日志:应看到“WiFi已连接”→“连接MQTT Broker...已连接”→“QoS 1发布成功: QoS1消息 #xxxx”。
  3. 使用MQTT客户端工具(如MQTT X、mosquitto_sub)订阅esp32/qos1/test主题,确认能收到ESP32发布的消息。
  4. 在串口监视器中观察收到消息,主题: esp32/qos1/ack——该消息由外部工具向esp32/qos1/ack主题发送,用于验证ESP32的订阅回调正常工作。

通过条件:外部MQTT客户端能持续收到来自ESP32的QoS 1消息(无重复或少量重复均可,取决于Broker实现),且ESP32串口输出“QoS 1发布成功”且无“发布失败”日志。若网络异常,PubSubClient会自动重发,这正是QoS 1“至少一次”的体现。

关键说明

  • PubSubClient的publish()返回true仅表示消息已进入发送流程并收到PUBACK(QoS 1),不代表Broker已转发给订阅者。
  • QoS 1可能产生重复消息(Broker重发场景),订阅端需自行处理幂等性。
  • 若需观察QoS 1与QoS 0的差异,可将代码中publish()第三参数改为0,对比Broker端接收行为。
QoS 1 消息确认流程 ESP32 发布端 PubSubClient MQTT Broker EMQX/mosquitto 订阅端 MQTT X/工具 ① PUBLISH QoS=1, DUP=0 ② PUBACK ③ 转发消息 ④ 订阅ack主题(可选验证) 关键点: • QoS 1保证消息至少到达Broker一次,Broker回复PUBACK后发布流程结束 • 若发布端未收到PUBACK,将重发消息(DUP=1),可能导致订阅端收到重复消息