2. 发布/订阅基础

任务目标

本案例实现ESP32通过MQTT协议同时发布和订阅多个主题:设备向esp32/status周期发布在线状态,同时订阅esp32/led和esp32/msg两个主题,分别用于控制板载LED和接收文本消息,演示消息的实时双向收发。

环境与依赖

  • 硬件:ESP32开发板(板载LED通常接GPIO2)
  • 软件:Arduino IDE(安装esp32开发板包)
  • MQTT Broker:本机运行Mosquitto,或使用公共Broker(如broker.emqx.io:1883)
  • 库:PubSubClient(Arduino库管理器搜索安装)

关键完整代码

#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-client-001";

// 主题定义
const char* pub_topic = "esp32/status";
const char* sub_topic_led = "esp32/led";
const char* sub_topic_msg = "esp32/msg";

WiFiClient espClient;
PubSubClient client(espClient);

unsigned long lastPublishTime = 0;
const long publishInterval = 5000; // 5秒发布一次

void setup_wifi() {
    delay(10);
    Serial.begin(115200);
    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.println("IP address: ");
    Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");

    String message;
    for (int i = 0; i < length; i++) {
        message += (char)payload[i];
    }
    Serial.println(message);

    // 根据主题处理消息
    if (strcmp(topic, sub_topic_led) == 0) {
        if (message == "on") {
            digitalWrite(LED_BUILTIN, HIGH);
            Serial.println("LED ON");
        } else if (message == "off") {
            digitalWrite(LED_BUILTIN, LOW);
            Serial.println("LED OFF");
        }
    } else if (strcmp(topic, sub_topic_msg) == 0) {
        Serial.print("Received message: ");
        Serial.println(message);
    }
}

void reconnect() {
    while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        if (client.connect(client_id)) {
            Serial.println("connected");
            // 连接成功后订阅主题
            client.subscribe(sub_topic_led);
            client.subscribe(sub_topic_msg);
        } else {
            Serial.print("failed, rc=");
            Serial.print(client.state());
            Serial.println(" try again in 5 seconds");
            delay(5000);
        }
    }
}

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    setup_wifi();
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(callback);
}

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

    // 周期发布状态
    if (millis() - lastPublishTime > publishInterval) {
        String status = "online";
        client.publish(pub_topic, status.c_str());
        Serial.println("Published: " + status);
        lastPublishTime = millis();
    }
}

运行与验证

  1. 将代码中的ssid和password替换为实际WiFi信息。
  2. 编译并烧录到ESP32开发板,打开串口监视器(波特率115200)。
  3. 观察串口输出:WiFi连接成功后,显示“Attempting MQTT connection...connected”,说明MQTT连接成功。
  4. 每5秒串口输出“Published: online”,说明发布功能正常。

验证订阅功能:使用MQTT客户端工具(如MQTTX或mosquitto_pub)向Broker发送消息:

# 控制LED(在终端执行)
mosquitto_pub -h broker.emqx.io -t "esp32/led" -m "on"
mosquitto_pub -h broker.emqx.io -t "esp32/led" -m "off"

# 发送文本消息
mosquitto_pub -h broker.emqx.io -t "esp32/msg" -m "Hello ESP32"

通过条件:

  • 发送on/off到esp32/led时,板载LED相应点亮/熄灭,串口输出“LED ON”/“LED OFF”。
  • 发送任意文本到esp32/msg时,串口输出“Received message: 对应内容”。
  • 串口持续每5秒显示“Published: online”,且无断连重连日志。
ESP32 发布: esp32/status 订阅: esp32/led 订阅: esp32/msg MQTT Broker broker.emqx.io 端口: 1883 外部客户端 mosquitto_pub 外部订阅者 MQTTX / mosquitto_sub 发布 status 订阅 led/msg 发布 led/msg 转发 status 发布路径 订阅路径 外部→ESP32 ESP32→外部

常见问题

  • 连接失败(rc=-2):网络不通或Broker地址错误,检查WiFi连接和Broker地址。
  • 收不到消息:确认订阅主题与发布主题完全一致(区分大小写),且已在reconnect()中完成订阅。
  • LED不动作:确认开发板LED引脚定义,部分开发板LED_BUILTIN可能为GPIO2或GPIO16,按实际硬件调整。