ESP32使用MQTT通信基础教程示例

硬件基础

这次使用的环境是https://wokwi.com/虚拟仿真平台

需要设备:ESP32S3,一个外部LED,一个电阻。

wokwi仿真平台环境搭建

1. 外观

{

  "version": 1,

  "author": "Anonymous maker",

  "editor": "wokwi",

  "parts": [

    { "type": "board-esp32-s3-devkitc-1", "id": "esp", "top": -9.78, "left": 110.17, "attrs": {} },

    { "type": "wokwi-led", "id": "led1", "top": 73.2, "left": -53.8, "attrs": { "color": "red" } },

    {

      "type": "wokwi-resistor",

      "id": "r1",

      "top": 13.55,

      "left": -28.8,

      "attrs": { "value": "1000" }

    }

  ],

  "connections": [

    [ "esp:TX", "$serialMonitor:RX", "", [] ],

    [ "esp:RX", "$serialMonitor:TX", "", [] ],

    [ "led1:A", "r1:1", "green", [ "v0" ] ],

    [ "led1:C", "esp:GND.1", "green", [ "v0" ] ],

    [ "r1:2", "esp:12", "green", [ "v0" ] ]

  ],

  "dependencies": {}

}

2. 程序代码

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

// ==================== WiFi配置 ====================
const char* ssid = "Wokwi-GUEST";  // Wokwi仿真WiFi名称
const char* password = "";         // Wokwi仿真WiFi密码(无密码)
const int wifiChannel = 6;         // Wokwi使用的WiFi通道
const bool hiddenSSID = false;     // 网络是否隐藏

// ==================== MQTT配置 ====================
const char* mqtt_server = "60.204.140.141"; // MQTT服务器
const int mqtt_port = 1883;                 // MQTT端口
const char* mqtt_user = "admin";                 // 用户名
const char* mqtt_password = "plulic";             // 密码

// ==================== MQTT主题 ====================
const char* topic_led = "wokwi/test/led";    // LED控制主题
const char* topic_status = "wokwi/test/status"; // 状态主题

// ==================== 硬件引脚定义 ====================
const int LED_PIN = 12;           // LED引脚 GPIO12
const int INTERNAL_LED = 2;       // ESP32内置LED (GPIO2),避免使用BUILTIN_LED

// ==================== 全局对象 ====================
WiFiClient espClient;
PubSubClient client(espClient);

// ==================== 全局变量 ====================
bool ledState = false;            // LED状态 (true=开, false=关)
bool wifiConnected = false;       // WiFi连接状态
unsigned long lastReconnectAttempt = 0;
const unsigned long reconnectInterval = 3000;
unsigned long lastHeartbeat = 0;
const unsigned long heartbeatInterval = 5000;

// ==================== 函数声明 ====================
void setupWiFi();
void connectToWiFi();
void mqttCallback(char* topic, byte* payload, unsigned int length);
boolean reconnectMQTT();
void controlLED(bool state);
void blinkLED(int pin, int times, int delayTime);
void publishStatus();
void printWiFiStatus();

// ==================== 初始化设置 ====================
void setup() {
  Serial.begin(115200);
  delay(1000); // 等待串口初始化

  Serial.println("\n==========================================");
  Serial.println("   ESP32 Wokwi仿真 - MQTT LED控制测试");
  Serial.println("==========================================");

  // 设置引脚模式
  pinMode(LED_PIN, OUTPUT);
  pinMode(INTERNAL_LED, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  digitalWrite(INTERNAL_LED, LOW);

  // 闪烁LED表示开始启动
  blinkLED(INTERNAL_LED, 2, 200);

  // 设置WiFi和MQTT
  setupWiFi();

  Serial.println("\n✅ 系统初始化完成!");
  Serial.println("📡 MQTT控制命令:");
  Serial.println("   主题: wokwi/test/led");
  Serial.println("   消息: on       - 打开LED");
  Serial.println("   消息: off      - 关闭LED");
  Serial.println("   消息: toggle   - 切换LED状态");
  Serial.println("   消息: blink    - LED闪烁3次");
  Serial.println("==========================================");
}

// ==================== WiFi设置函数 ====================
void setupWiFi() {
  Serial.println("\n🔧 配置WiFi连接...");
  Serial.print("WiFi网络: ");
  Serial.println(ssid);
  Serial.print("WiFi通道: ");
  Serial.println(wifiChannel);

  // 在Wokwi中,可以尝试设置WiFi配置
  WiFi.mode(WIFI_STA);

  // 开始连接WiFi
  connectToWiFi();
}

// ==================== WiFi连接函数 ====================
void connectToWiFi() {
  Serial.print("🔗 连接WiFi...");

  // 断开之前的连接
  WiFi.disconnect(true);
  delay(100);

  // 开始连接
  WiFi.begin(ssid, password);

  // 等待连接,带超时
  unsigned long startTime = millis();
  const unsigned long timeout = 15000; // 15秒超时

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");

    // 闪烁内置LED表示连接中
    digitalWrite(INTERNAL_LED, !digitalRead(INTERNAL_LED));

    // 检查超时
    if (millis() - startTime > timeout) {
      Serial.println("\n❌ WiFi连接超时!");
      Serial.println("可能的原因:");
      Serial.println("1. 不在Wokwi仿真环境中");
      Serial.println("2. WiFi名称或密码错误");
      Serial.println("3. 网络配置问题");

      // 慢闪表示失败
      while (true) {
        digitalWrite(INTERNAL_LED, HIGH);
        delay(300);
        digitalWrite(INTERNAL_LED, LOW);
        delay(700);
      }
    }
  }

  Serial.println("\n✅ WiFi连接成功!");
  wifiConnected = true;

  // 显示WiFi状态
  printWiFiStatus();

  // 快速闪烁表示成功
  blinkLED(INTERNAL_LED, 3, 100);

  // 设置MQTT
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(mqttCallback);

  // 初始化MQTT连接
  reconnectMQTT();
}

// ==================== 打印WiFi状态 ====================
void printWiFiStatus() {
  Serial.println("\n📡 WiFi连接信息:");
  Serial.print("  SSID: ");
  Serial.println(WiFi.SSID());

  Serial.print("  IP地址: ");
  Serial.println(WiFi.localIP());

  Serial.print("  MAC地址: ");
  Serial.println(WiFi.macAddress());

  Serial.print("  信号强度: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");

  Serial.print("  子网掩码: ");
  Serial.println(WiFi.subnetMask());

  Serial.print("  网关: ");
  Serial.println(WiFi.gatewayIP());

  Serial.print("  DNS服务器: ");
  Serial.println(WiFi.dnsIP());
}

// ==================== MQTT回调函数 ====================
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("📨 收到MQTT消息 [");
  Serial.print(topic);
  Serial.print("]: ");

  // 将消息转换为字符串
  String message;
  for (unsigned int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);

  // 处理LED控制消息
  if (String(topic) == topic_led) {
    if (message == "on") {
      controlLED(true);
      client.publish(topic_status, "LED已打开");
    }
    else if (message == "off") {
      controlLED(false);
      client.publish(topic_status, "LED已关闭");
    }
    else if (message == "toggle") {
      ledState = !ledState;
      controlLED(ledState);
      String status = ledState ? "LED已打开" : "LED已关闭";
      client.publish(topic_status, status.c_str());
    }
    else if (message == "blink") {
      blinkLED(LED_PIN, 3, 200);
      client.publish(topic_status, "LED已闪烁3次");
    }
    else if (message == "status") {
      publishStatus();
    }
    else {
      String errorMsg = "未知命令: " + message;
      client.publish(topic_status, errorMsg.c_str());
      Serial.println("❌ 未知LED命令");
    }
  }
}

// ==================== MQTT重连函数 ====================
boolean reconnectMQTT() {
  if (!wifiConnected) {
    Serial.println("❌ WiFi未连接,无法连接MQTT");
    return false;
  }

  Serial.print("🔗 连接MQTT服务器... ");

  // 生成唯一的客户端ID
  String clientId = "Wokwi-ESP32-";
  clientId += String(random(0xffff), HEX);

  if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
    Serial.println("✅ 连接成功!");

    // 订阅主题
    client.subscribe(topic_led);
    Serial.print("  订阅主题: ");
    Serial.println(topic_led);

    // 发布连接成功消息
    String connectMsg = "设备已连接 - IP: " + WiFi.localIP().toString();
    client.publish(topic_status, connectMsg.c_str());

    // 发布初始状态
    publishStatus();

    // 闪烁表示成功
    blinkLED(INTERNAL_LED, 2, 150);

    return true;
  } else {
    Serial.print("❌ 连接失败, 错误码: ");
    Serial.println(client.state());

    return false;
  }
}

// ==================== 主循环 ====================
void loop() {
  // 检查WiFi连接
  if (WiFi.status() != WL_CONNECTED) {
    if (wifiConnected) {
      Serial.println("⚠️  WiFi连接丢失!");
      wifiConnected = false;
      digitalWrite(INTERNAL_LED, LOW);
    }

    // 慢闪表示WiFi断开
    static unsigned long lastBlink = 0;
    if (millis() - lastBlink > 1000) {
      lastBlink = millis();
      digitalWrite(INTERNAL_LED, !digitalRead(INTERNAL_LED));
    }

    // 每10秒尝试重连
    static unsigned long lastReconnect = 0;
    if (millis() - lastReconnect > 10000) {
      lastReconnect = millis();
      Serial.println("🔄 尝试重新连接WiFi...");
      connectToWiFi();
    }

    delay(100);
    return;
  }

  // WiFi已连接
  if (!wifiConnected) {
    wifiConnected = true;
    Serial.println("✅ WiFi重新连接成功!");
    printWiFiStatus();
  }

  // 检查MQTT连接
  if (!client.connected()) {
    unsigned long now = millis();
    if (now - lastReconnectAttempt > reconnectInterval) {
      lastReconnectAttempt = now;
      reconnectMQTT();
    }
  } else {
    client.loop();
  }

  // 发送心跳(每5秒)
  if (millis() - lastHeartbeat > heartbeatInterval && client.connected()) {
    lastHeartbeat = millis();

    // 发送简单的心跳
    String heartbeat = "{\"device\":\"Wokwi-ESP32\",\"uptime\":" + String(millis() / 1000) + ",\"led\":" + String(ledState) + "}";
    client.publish("wokwi/test/heartbeat", heartbeat.c_str());

    // 快速闪烁表示活跃
    digitalWrite(INTERNAL_LED, HIGH);
    delay(10);
    digitalWrite(INTERNAL_LED, LOW);
  }

  delay(10);
}

// ==================== 控制LED函数 ====================
void controlLED(bool state) {
  ledState = state;
  digitalWrite(LED_PIN, state ? HIGH : LOW);

  Serial.print("💡 LED ");
  Serial.println(state ? "已打开" : "已关闭");

  // 视觉反馈
  digitalWrite(INTERNAL_LED, HIGH);
  delay(50);
  digitalWrite(INTERNAL_LED, LOW);
}

// ==================== 闪烁LED函数 ====================
void blinkLED(int pin, int times, int delayTime) {
  Serial.print("✨ 闪烁");
  if (pin == LED_PIN) Serial.print("外部");
  else if (pin == INTERNAL_LED) Serial.print("内置");
  Serial.print("LED ");
  Serial.print(times);
  Serial.println(" 次");

  for (int i = 0; i < times; i++) {
    digitalWrite(pin, HIGH);
    delay(delayTime);
    digitalWrite(pin, LOW);
    if (i < times - 1) {
      delay(delayTime);
    }
  }
}

// ==================== 发布状态函数 ====================
void publishStatus() {
  if (client.connected()) {
    // 构建JSON状态字符串
    String status = "{";
    status += "\"led\":" + String(ledState ? "true" : "false") + ",";
    status += "\"wifi_rssi\":" + String(WiFi.RSSI()) + ",";
    status += "\"ip\":\"" + WiFi.localIP().toString() + "\",";
    status += "\"free_heap\":" + String(esp_get_free_heap_size()) + ",";
    status += "\"uptime\":" + String(millis() / 1000) + ",";
    status += "\"name\":\"杨同学\"";  // 注意:这里需要双引号
    status += "}";

    client.publish(topic_status, status.c_str());
    Serial.println("📤 已发布系统状态");
    Serial.println(status);
  }
}

MQTT服务器的搭建

MQTT 服务可以用公共的服务器:如果您无需自行部署 MQTT 服务,您可以使用 EMQX 提供的在线公开版本进行快速测试:

Broker 地址: broker.emqx.io
Broker TCP 端口: 1883
Broker SSL 端口: 8883

下面是自己创建MQTT的服务器教程

主播这里用的docker安装,如果没有下载docker可以下载一个

1.安装MQTT

docker run -d --name emqx \
  -p 0.0.0.0:18083:18083 \
  -p 0.0.0.0:1883:1883 \
  -p 0.0.0.0:8083:8083 \
  -p 0.0.0.0:8883:8883 \
  -p 0.0.0.0:8084:8084 \
  -e EMQX_DASHBOARD__LISTENER__HTTP__BIND=0.0.0.0:18083 \
  emqx/emqx

注意需要开通对应端口,否则访问不了服务

访问服务:http://60.204.140.141:18083/ 这里60.204.140.141你的服务器IP,这里第一次进入需要账号密码:admin/public

2.安装mqttx-web

当然也可以不安装网页版的客户端,可以直接下载window版本的客户端:MQTTX:全功能 MQTT 客户端工具

docker run -d --name mqttx-web -p 88:80 emqx/mqttx-web

访问服务mqttx-web:http://60.204.140.141:88/ 这里60.204.140.141你的服务器IP,第一次进入会让你连接MQTT服务器,主播连过了就没有了

开始使用:

1.连接MQTT服务器

2.连接订阅

const char* topic_led = "wokwi/test/led";    // LED控制主题
const char* topic_status = "wokwi/test/status"; // 状态主题

有两个主题一个控制的:wokwi/test/led,一个状态的:wokwi/test/status,这里wokwi/test/led订阅发送的指令,esp32接收到处理完后会将信息反馈到wokwi/test/status订阅中。

下面开始连接:一共有两个主题都要连接

2.发送指令

首先要启动仿真平台

打开MQTTX软件

3.接收指令并返回状态

当wokwi/test/led订阅发送指令。仿真平台的esp32会接收并处理指令,然后再返回状态

结尾

🆗,到此结束,主播是计算机科学与技术专业的之前都是学软件开发的,也是刚刚学嵌入式,对硬件不是很了解,如有错误,请谅解,共同学习~,后面会出一期编写手机APP控制灯的开关教程。

Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐