2025年7月6日 星期日

ESP32 雙核心控制 LED 與 DHT22 溫濕度感測器 (Wokwi 模擬) EX1 --MQTTx , MQTT client WEB

 ESP32 雙核心控制 LED 與 DHT22 溫濕度感測器 (Wokwi 模擬) --MQTTx , MQTT client WEB

這個教學將引導你如何在 Wokwi 網站上使用 ESP32 雙核心,透過 MQTTx 控制 LED 的不同模式,並訂閱 DHT22 的溫濕度數據。

實驗目標

  1. LED 控制:

    • 利用 ESP32 的雙核心特性,一個核心處理 LED 的亮滅、閃爍和定時控制。

    • 透過 MQTT 接收指令,切換 LED 的四種模式:on (開啟)、off (關閉)、flash (閃爍)、timer (定時 10 秒後關閉)。

  2. DHT22 溫濕度監測:

    • 另一個核心讀取 DHT22 溫濕度感測器的數據。

    • 透過 MQTT 發布 DHT22 的溫度和濕度數據。

  3. MQTTx  及  mqttgo client 互動 :

    • 使用 MQTTx  (Mqttgo) 工具發布控制 LED 的主題與酬載 (payload)。

    • 使用 MQTTx  (Mqttgo)  訂閱 DHT22 的溫濕度主題。

必備資源

  • Wokwi 帳號: 用於 ESP32 模擬。

  • MQTT Broker: 建議使用免費的公共 MQTT Broker,例如 broker.hivemq.commqtt.eclipseprojects.io 或 broker.mqttgo.io

  • MQTTx 軟體: 用於 MQTT 訊息的發布與訂閱。

  • ESP32 相關函式庫:

    • WiFi.h      ssid = "Wokwi-GUEST" password = ""

    • PubSubClient.h (MQTT 客戶端函式庫)

    • DHT.h (DHT 感測器函式庫)



Wokwi 設定

  1. 開啟 Wokwi 專案:

    • 進入 Wokwi 網站,建立一個新的 ESP32 Dev Kit C 專案。

  2. 加入元件:

    • LED: 在板上連接一個 LED 到 GPIO 2 (或你選擇的其他 GPIO)。記得串聯一個 220 歐姆的電阻。

    • DHT22: 將 DHT22 感測器連接到 GPIO 4 (或你選擇的其他 GPIO)。

      • VCC 接 3.3V

      • GND 接 GND

      • Data 接 GPIO 4

  3. Wokwi 線路定:


      
    
    

Arduino 程式碼 (雙核心)

ESP32 擁有雙核心 (Core 0 和 Core 1)。我們將利用 xTaskCreatePinnedToCore 函式,將不同的任務分配給不同的核心。

  • Core 0: 處理 Wi-Fi 連線、MQTT 通訊、以及 LED 的控制邏輯。

  • Core 1: 專門負責讀取 DHT22 感測器數據並發布。

#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <DHT_U.h> // 需要同時包含 DHT_U.h

// --- Wi-Fi 設定 ---
const char* ssid = "Wokwi-GUEST";
const char* password = "";

// --- MQTT 設定 ---
const char* mqtt_server = "broker.mqttgo.io"; // 或 "mqtt.eclipseprojects.io"
const int mqtt_port = 1883;
const char* mqtt_client_id = "ESP32_Wokwi_Client";

// MQTT 主題
const char* mqtt_topic_led_control = "wokwi/led/control";
const char* mqtt_topic_temperature = "wokwi/dht/temperature";
const char* mqtt_topic_humidity = "wokwi/dht/humidity";

WiFiClient espClient;
PubSubClient client(espClient);

// --- LED 設定 ---
const int ledPin = 2; // 連接到 GPIO 2
enum LedMode { ON, OFF, FLASH, TIMER };
volatile LedMode currentLedMode = OFF;
volatile unsigned long timerStartTime = 0;
volatile bool ledState = false; // 用於閃爍模式

// --- DHT22 設定 ---
#define DHTPIN 4      // 連接到 GPIO 4
#define DHTTYPE DHT22 // DHT 22  (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);

// --- 任務句柄 ---
TaskHandle_t TaskLEDControl = NULL;
TaskHandle_t TaskDHTSensor = NULL;

// --- 函式宣告 ---
void setup_wifi();
void reconnect_mqtt();
void callback(char* topic, byte* payload, unsigned int length);

void ledControlTask(void *pvParameters);
void dhtSensorTask(void *pvParameters);

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW); // 確保初始是關閉的

  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);

  dht.begin(); // 初始化 DHT 感測器

  // 創建 LED 控制任務,運行在 Core 0
  xTaskCreatePinnedToCore(
    ledControlTask,   /* 任務函式 */
    "LED Control",    /* 任務名稱 */
    2048,             /* 堆疊大小 (字節) */
    NULL,             /* 任務參數 */
    1,                /* 任務優先級 */
    &TaskLEDControl,  /* 任務句柄 */
    0                 /* 運行在 Core 0 */
  );

  // 創建 DHT 感測器任務,運行在 Core 1
  xTaskCreatePinnedToCore(
    dhtSensorTask,    /* 任務函式 */
    "DHT Sensor",     /* 任務名稱 */
    4096,             /* 堆疊大小 (字節) */
    NULL,             /* 任務參數 */
    1,                /* 任務優先級 */
    &TaskDHTSensor,   /* 任務句柄 */
    1                 /* 運行在 Core 1 */
  );
}

void loop() {
  // 主循環中只負責維持 MQTT 連線
  if (!client.connected()) {
    reconnect_mqtt();
  }
  client.loop(); // 處理 MQTT 訊息
  delay(10); // 短暫延遲,避免佔用太多 CPU
}

// --- Wi-Fi 連線函式 ---
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("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

// --- MQTT 重連函式 ---
void reconnect_mqtt() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // 嘗試連線
    if (client.connect(mqtt_client_id)) {
      Serial.println("connected");
      // 訂閱 LED 控制主題
      client.subscribe(mqtt_topic_led_control);
      Serial.print("Subscribed to: ");
      Serial.println(mqtt_topic_led_control);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // 等待 5 秒後重試
      delay(5000);
    }
  }
}

// --- MQTT 訊息回調函式 ---
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 (String(topic) == mqtt_topic_led_control) {
    if (message == "on") {
      currentLedMode = ON;
      digitalWrite(ledPin, HIGH);
      Serial.println("LED Mode: ON");
    } else if (message == "off") {
      currentLedMode = OFF;
      digitalWrite(ledPin, LOW);
      Serial.println("LED Mode: OFF");
    } else if (message == "flash") {
      currentLedMode = FLASH;
      Serial.println("LED Mode: FLASH");
    } else if (message == "timer") {
      currentLedMode = TIMER;
      digitalWrite(ledPin, HIGH); // 定時模式開始時先開啟 LED
      timerStartTime = millis();
      Serial.println("LED Mode: TIMER (10s)");
    } else {
      Serial.println("Unknown LED command.");
    }
  }
}

// --- LED 控制任務 (運行在 Core 0) ---
void ledControlTask(void *pvParameters) {
  (void) pvParameters; // 避免編譯器警告

  for (;;) { // 無限循環
    switch (currentLedMode) {
      case ON:
        // LED 保持亮著,由 callback 函式設置
        break;
      case OFF:
        // LED 保持熄滅,由 callback 函式設置
        break;
      case FLASH:
        digitalWrite(ledPin, ledState);
        ledState = !ledState;
        vTaskDelay(pdMS_TO_TICKS(500)); // 每 500ms 改變一次狀態
        break;
      case TIMER:
        if (millis() - timerStartTime >= 10000) { // 10 秒
          digitalWrite(ledPin, LOW);
          currentLedMode = OFF; // 定時結束後轉為 OFF 模式
          Serial.println("LED Timer finished. LED OFF.");
        }
        vTaskDelay(pdMS_TO_TICKS(10)); // 短暫延遲
        break;
      default:
        digitalWrite(ledPin, LOW); // 預設為關閉
        break;
    }
    vTaskDelay(pdMS_TO_TICKS(10)); // 短暫延遲,讓其他任務有機會執行
  }
}

// --- DHT 感測器任務 (運行在 Core 1) ---
void dhtSensorTask(void *pvParameters) {
  (void) pvParameters; // 避免編譯器警告

  for (;;) { // 無限循環
    delay(2000); // 每 2 秒讀取一次數據,避免頻繁讀取導致錯誤

    float h = dht.readHumidity();
    float t = dht.readTemperature(); // 讀取攝氏溫度

    // 檢查是否讀取失敗,如果是則嘗試重讀
    if (isnan(h) || isnan(t)) {
      Serial.println(F("Failed to read from DHT sensor!"));
    } else {
      Serial.print(F("Humidity: "));
      Serial.print(h);
      Serial.print(F("%  Temperature: "));
      Serial.print(t);
      Serial.println(F("°C"));

      // 發布溫度
      char tempString[8];
      dtostrf(t, 4, 2, tempString); // 浮點數轉字串
      client.publish(mqtt_topic_temperature, tempString);

      // 發布濕度
      char humString[8];
      dtostrf(h, 4, 2, humString); // 浮點數轉字串
      client.publish(mqtt_topic_humidity, humString);
    }
    vTaskDelay(pdMS_TO_TICKS(5000)); // 每 5 秒執行一次,避免 MQTT 發布過於頻繁
  }
}


程式碼說明

  1. Libraries: 包含了 Wi-Fi、PubSubClient (MQTT)、和 DHT 函式庫。

  2. Wi-Fi & MQTT 設定: 定義了 Wi-Fi 的 SSID、密碼,以及 MQTT Broker 的位址、埠號和客戶端 ID。請使用 Wokwi-GUEST 作為 SSID,密碼為空。

  3. LED 控制變數: currentLedMode 是一個 volatile 變數,用於跨任務安全地修改 LED 模式。timerStartTime 記錄定時模式的開始時間。

  4. DHT 感測器: 定義了 DHT22 的引腳和類型。

  5. setup() 函式:

    • 初始化序列埠、LED 引腳和 DHT 感測器。

    • 設定 Wi-Fi 連線和 MQTT 客戶端。

    • 關鍵: 使用 xTaskCreatePinnedToCore() 創建兩個 FreeRTOS 任務:

      • ledControlTask:綁定到 Core 0,處理 LED 的邏輯。

      • dhtSensorTask:綁定到 Core 1,處理 DHT 的讀取和發布。

  6. loop() 函式:

    • 主循環主要負責檢查 MQTT 連線狀態,並呼叫 client.loop() 來處理 MQTT 訊息的收發。因為大部分工作由 FreeRTOS 任務處理,這裡的 loop 函式變得非常精簡。

  7. setup_wifi() & reconnect_mqtt() 標準的 Wi-Fi 和 MQTT 連線/重連邏輯。

  8. callback() 函式:

    • 當收到 MQTT 訊息時觸發。

    • 根據收到的 payload (酬載) 更新 currentLedMode 變數,以控制 LED 的行為。

  9. ledControlTask() 函式 (Core 0):

    • 這是 LED 控制的無限循環任務。

    • 根據 currentLedMode 的值,執行不同的 LED 操作:

      • ON:LED 保持亮起。

      • OFF:LED 保持關閉。

      • FLASH:每 500ms 切換一次 LED 狀態。

      • TIMER:亮起 LED,10 秒後自動關閉並切換到 OFF 模式。

    • 使用 vTaskDelay() 進行非阻塞延遲,讓 CPU 可以切換到其他任務。

  10. dhtSensorTask() 函式 (Core 1):

    • 這是 DHT 感測器讀取的無限循環任務。

    • 每隔 2 秒讀取一次 DHT22 的濕度和溫度。

    • 檢查讀取是否成功,如果成功則將數據透過 MQTT 發布到相應的主題。

    • 使用 vTaskDelay() 進行非阻塞延遲。

MQTTx 操作

  1. 下載並安裝 MQTTx: 如果你還沒有安裝,請從 MQTTx 官方網站 下載並安裝。

  2. 新增連線:

    • 打開 MQTTx。

    • 點擊左側的 New Connection

    • Name: 任意填寫,例如 "Wokwi ESP32"。

    • Host: broker.mqttgo.io 或 broker.hivemq.com (或你選擇的其他公共 Broker)。

    • Port: 1883

    • 點擊 Connect

  3. 訂閱主題 (DHT22):

    • 連線成功後,點擊頁面中間的 New Subscription

    • Topic: wokwi/dht/temperature

    • 點擊 Confirm

    • 重複步驟,訂閱 wokwi/dht/humidity

    • 你會看到 Wokwi 上的 ESP32 開始運行後,DHT22 的溫濕度數據會每隔一段時間顯示在 MQTTx 的訊息列表中。

  4. 發布訊息 (LED 控制):

    • 點擊左下角的 New Message (或在已建立的連線中點擊 Publish 區域)。

    • Topic: wokwi/led/control

    • Payload:

      • 要開啟 LED:輸入 on

      • 要關閉 LED:輸入 off

      • 要閃爍 LED:輸入 flash

      • 要定時關閉 LED (10秒後):輸入 timer

    • 選擇 QoS0

    • 點擊 Publish 按鈕。

    • Wokwi 上的 ESP32 會收到指令並控制 LED。

實驗步驟與觀察

  1. 將上述程式碼複製貼上到 Wokwi 的 sketch.ino 檔案中。

  2. 確認 diagram.json 檔案中的 Wi-Fi 和硬體連接設定正確。

  3. 點擊 Wokwi 上的 Run 按鈕。

  4. 觀察 Wokwi 的 Serial Monitor:

    • 你會看到 Wi-Fi 連線的訊息。

    • MQTT 連線成功的訊息。

    • 訂閱 LED 控制主題的訊息。

    • DHT22 讀取的溫濕度數據,以及發布到 MQTT 的訊息。你會看到這些數據是從 Core 1 輸出的。

  5. 在 MQTTx 中,你會看到 wokwi/dht/temperaturewokwi/dht/humidity 主題下不斷湧入的溫濕度數據。

  6. 在 MQTTx 中,向 wokwi/led/control 主題發布不同的 payload (on, off, flash, timer)。

  7. 觀察 Wokwi 模擬器中的 LED,確認其行為與你發布的指令一致。特別注意 timer 模式下,LED 會亮起 10 秒後自動熄滅。

  8. broker.mqttgo.io 設定






    9. mqttx設定
         






常用的免費 MQTT Broker

  1. Eclipse Mosquitto

    • 類型: 開源軟體 (可以自己架設) / 提供公共測試伺服器

    • 優點:

      • 輕量級: 非常適合資源有限的設備,如 Raspberry Pi。

      • 穩定可靠: 是最廣泛使用的開源 MQTT Broker 之一。

      • 完整支援: 支援 MQTT 3.1、3.1.1 和 5.0 協議。

      • 公共測試伺服器: 提供了公開的測試伺服器 test.mosquitto.org,方便快速測試。

      • 社群活躍: 擁有龐大的使用者社群,容易找到支援。

    • 缺點: 如果要用於生產環境,通常需要自己架設和維護。

    • 適用情境: 開發、測試、個人專案、小型 IoT 部署,以及教學用途。

  2. HiveMQ Cloud (Free Plan)

    • 類型: 雲端託管服務 (提供免費方案)

    • 優點:

      • 雲端管理: 無需自己架設,HiveMQ 負責基礎設施的維護和擴展。

      • 高度可靠: 提供企業級的穩定性和安全性。

      • 免費層級: 通常提供免費方案,允許連接少量設備 (例如 100 個設備) 和一定的訊息量,非常適合原型開發和小型專案。

      • 完整功能: 支援完整的 MQTT 規範。

    • 缺點: 免費方案有連接數和訊息量的限制,超出部分需要付費。

    • 適用情境: 需要穩定、託管的 MQTT 服務,不想自己維護 Broker 的開發者。

  3. EMQX Cloud (Serverless / Free Tier)

    • 類型: 雲端託管服務 (提供免費方案 / Serverless)

    • 優點:

      • 高擴展性: 專為大規模 IoT 應用設計,能處理百萬級的併發連接。

      • 全球部署: 支援多區域部署,提供更好的延遲表現。

      • 免費方案/Serverless: 提供免費的 Serverless 方案或免費額度,通常以「Session 分鐘數」來計費,對於開發和測試非常方便。

      • 功能豐富: 支援 MQTT 5.0,並提供數據整合、規則引擎等進階功能。

    • 缺點: 免費額度用完後會按量計費,需注意用量。

    • 適用情境: 需要測試大規模 IoT 連接、數據處理,或計畫未來可能擴展的專案。

  4. Public MQTT Brokers (例如 broker.hivemq.com, mqtt.eclipseprojects.io)

    • 類型: 公共測試 Broker

    • 優點:

      • 無需設定: 立即可用,不需要任何安裝或設定。

      • 方便測試: 快速測試 MQTT 連線和訊息發送/接收功能。

    • 缺點:

      • 安全性低: 通常不支援認證,所有發布的訊息都是公開的,不適合傳輸任何敏感數據

      • 穩定性不保證: 作為公共測試用途,其穩定性和可用性無法保證,不適合生產環境。

      • 流量限制: 可能會有流量或連接數的限制。

    • 適用情境: 快速的原型開發、初步功能測試、學習 MQTT 協議。

台灣本地提供的免費/測試 MQTT 服務

雖然數量相對較少,但台灣也有一些公司或個人提供免費的 MQTT 服務,通常會有一定的使用限制。

  • 台灣智能感測科技 (TaiwanSensor)

    • 他們提供免費的 MQTT 伺服器,聲稱可連接 100 台設備並提供 10GB 流量。這對於個人專案或小型團隊來說是相當不錯的免費額度。

    • 其服務基於 HiveMQ 的免費服務,因此在穩定性和功能上應該有不錯的表現。

    • 聯絡資訊: 官網上通常會有聯絡電話或信箱。

    • 適用情境: 對於需要一個比公共 Broker 更穩定,且有一定額度的台灣本地服務的開發者。

  • 小霸王實驗室 - mqttgo.io (HiveMQ CE 架設)

    • 由「小霸王實驗室」提供的 mqttgo.io 是一個在台灣架設的 MQTT Broker。

    • 地址: mqttgo.io

    • Port: 1883

    • 特色: 根據作者說明,這是基於 HiveMQ CE (Community Edition) 架設,目的是提供一個在台灣速度快、容易記憶的免費 MQTT 服務。不需要帳號密碼,也不需要事先申請。

    • 優點: 針對台灣用戶有較好的網路速度,使用方便,適合教學和個人專案。

    • 缺點: 作為個人維護的免費服務,其穩定性和長期支援可能不如商業級的雲端服務,仍不適合關鍵任務。

  • 元米科技 (ICDT) - mqttws.hmi.tw

    • 元米科技提供 mqttws.hmi.tw 免費服務,這是一個集 Web Server 和 MQTT Broker 於一體的解決方案。

    • 特色: 對於 32 點以下的設備可以免費應用,主要用於快速完成客製化網頁圖控系統。

    • 適用情境: 如果你的專案需要將 MQTT 與簡單的網頁圖控結合,且點位數不多,這會是一個方便的選項。

沒有留言:

張貼留言

ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite

 ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite  ESP32 VS Code 程式 ; PlatformIO Project Configuration File ; ;   Build op...