2026年9月12日 星期六

Easybuilder pro + WOKWI esp32 DHT22 + 4 LED 控制 (MQTT)

Easybuilder pro + WOKWI esp32 DHT22 + 4 LED 控制  (MQTT)

























本系統實現了 ESP32 微控制器(於 Wokwi 模擬環境中執行)與 EasyBuilder Pro 人機介面 (HMI) 透過 MQTT 通訊協定進行雙向控制與溫濕度監測。

一、 系統功能整理

  1. DHT22 溫濕度監測與即時發行:

    • 每 2 秒讀取一次 DHT22 感測器的溫度與濕度。

    • 將數據打包為 JSON 格式(例如:{"value":23.50}{"value":76.00},無 [] 符號)發行至 MQTT Broker。

    • 同步更新顯示於 16x2 I2C LCD 螢幕上。

  2. LED / 繼電器遠端控制(單路 + 全區):

    • 訂閱 4 組個別控制主題(alex9ufo/led1 ~ led4)與 1 組全區控制主題(alex9ufo/allled)。

    • 相容解析 EasyBuilder Pro 發送的 JSON 控制封包(例如:{"value":true}{"value":false})。

  3. 雙向狀態回報(指示燈同步):

    • 每當硬體 GPIO 切換狀態後,ESP32 立即回傳最新狀態 JSON 至對應主題(alex9ufo/led1status ~ led4statusalex9ufo/allledstatus),讓 EasyBuilder Pro 的位元指示燈同步顯示。

  4. 完整 Serial Monitor 偵錯:

    • 印出 WiFi 連線資訊、IP 位址、MQTT 重連日誌,以及詳細的 MQTT RX (接收)MQTT TX (發行) 封包資訊。

二、 EasyBuilder Pro MQTT 與點位設定指南

要在 EasyBuilder Pro 中完成對接,請在 [物件] $\rightarrow$ [MQTT] 視窗中設置:

  • Broker 設定:

    • Server: mqttgo.io (或 broker.hivemq.com)

    • Port: 1883

    • 數據格式支援:Parse JSON (Key 設為 value)

位址與主題映射表 (Address Mapping)

類型功能說明EasyBuilder Pro 位址MQTT 主題 (Topic)傳輸方向封包範例 (JSON)
數值溫度顯示LW-10 (Float/32-bit)alex9ufo/tempESP32 $\rightarrow$ HMI{"value":23.50}
數值濕度顯示LW-12 (Float/32-bit)alex9ufo/humiESP32 $\rightarrow$ HMI{"value":76.00}
開關LED 1 按鈕LB-1alex9ufo/led1HMI $\rightarrow$ ESP32{"value":true}
開關LED 2 按鈕LB-2alex9ufo/led2HMI $\rightarrow$ ESP32{"value":true}
開關LED 3 按鈕LB-3alex9ufo/led3HMI $\rightarrow$ ESP32{"value":true}
開關LED 4 按鈕LB-4alex9ufo/led4HMI $\rightarrow$ ESP32{"value":true}
開關全開/全關LB-5alex9ufo/allledHMI $\rightarrow$ ESP32{"value":true}
指示燈LED 1 狀態燈LB-11alex9ufo/led1statusESP32 $\rightarrow$ HMI{"value":true}
指示燈LED 2 狀態燈LB-12alex9ufo/led2statusESP32 $\rightarrow$ HMI{"value":true}
指示燈LED 3 狀態燈LB-13alex9ufo/led3statusESP32 $\rightarrow$ HMI{"value":true}
指示燈LED 4 狀態燈LB-14alex9ufo/led4statusESP32 $\rightarrow$ HMI{"value":true}
指示燈全區狀態燈LB-15alex9ufo/allledstatusESP32 $\rightarrow$ HMI{"value":true}

三、 完整 ESP32 程式碼 (sketch.ino)

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "DHT.h"

// --- 網路與 MQTT 設定 ---
const char* ssid        = "Wokwi-GUEST";
const char* password    = "";
const char* mqtt_server = "mqtt-dashboard.com";
const int   mqtt_port   = 1883;

// --- MQTT 主題設定 ---
const char* TOPIC_TEMP   = "alex9ufo/temp";      // 映射至 EasyBuilder Pro LW-10
const char* TOPIC_HUMI   = "alex9ufo/humi";      // 映射至 EasyBuilder Pro LW-12

// 控制主題 (LB-1 ~ LB-5)
const char* subTopics[5] = {
  "alex9ufo/led1",   // LB-1
  "alex9ufo/led2",   // LB-2
  "alex9ufo/led3",   // LB-3
  "alex9ufo/led4",   // LB-4
  "alex9ufo/allled"  // LB-5 (全開/全關)
};

// 狀態回傳主題 (對應 EasyBuilder Pro 指示燈位址)
const char* pubTopics[5] = {
  "alex9ufo/led1status",   // 對應 LB-11 (LED 1 狀態)
  "alex9ufo/led2status",   // 對應 LB-12 (LED 2 狀態)
  "alex9ufo/led3status",   // 對應 LB-13 (LED 3 狀態)
  "alex9ufo/led4status",   // 對應 LB-14 (LED 4 狀態)
  "alex9ufo/allledstatus"  // 對應 LB-15 / LB-5 (全區狀態)
};

// --- 硬體腳位定義 ---
#define DHTPIN 4
#define DHTTYPE DHT22

const int relayPins[4] = {27, 14, 12, 13}; // Relay 1~4 (GPIO 27, 14, 12, 13)

// --- 元件初始化 ---
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2);

WiFiClient espClient;
PubSubClient client(espClient);

unsigned long lastMsg = 0;
float currentTemp = 0.0;
float currentHumi = 0.0;

// LCD 更新顯示函式
void updateLCD() {
  lcd.setCursor(0, 0);
  lcd.print("Temp: ");
  lcd.print(currentTemp, 1);
  lcd.print((char)223); // 顯示 ° 符號
  lcd.print("C   ");

  lcd.setCursor(0, 1);
  lcd.print("Humi: ");
  lcd.print(currentHumi, 1);
  lcd.print(" %   ");
}

// Wi-Fi 連線設定
void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("[WiFi] Connecting to ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

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

  Serial.println("\n[WiFi] WiFi connected!");
  Serial.print("[WiFi] IP address: ");
  Serial.println(WiFi.localIP());
}

// 發行無 [ ] 的 JSON 狀態給 EasyBuilder Pro 位元指示燈
void publishStatus(int index, bool isOn) {
  // 建立無 [] 符號的標準 JSON Payload
  String payload = isOn ? "{\"value\":true}" : "{\"value\":false}";
  client.publish(pubTopics[index], payload.c_str());
 
  Serial.print("[MQTT TX] Published JSON status '");
  Serial.print(payload);
  Serial.print("' to topic: ");
  Serial.println(pubTopics[index]);
}

// MQTT 接收控制指令處理 (Callback)
void callback(char* topic, byte* payload, unsigned int length) {
  String message = "";
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  message.trim();
  message.toLowerCase();

  Serial.println("----------------------------------------");
  Serial.print("[MQTT RX] Topic: ");
  Serial.println(topic);
  Serial.print("[MQTT RX] Payload: ");
  Serial.println(message);

  // 判斷接收到的值是 true (開啟) 還是 false (關閉)
  bool targetState = (message.indexOf("true") != -1 || message == "1" || message == "on");

  // 1. 全區控制 (LB-5 -> alex9ufo/allled)
  if (String(topic) == subTopics[4]) {
    for (int i = 0; i < 4; i++) {
      digitalWrite(relayPins[i], targetState ? HIGH : LOW);
      publishStatus(i, targetState); // 更新各個別燈號 (alex9ufo/led1status~4status)
    }
    // 回傳全區狀態 (alex9ufo/allledstatus)
    publishStatus(4, targetState);

    Serial.print("[Hardware] ALL LEDs set to: ");
    Serial.println(targetState ? "ON" : "OFF");
  }
  // 2. 個別控制 (LB-1~4 -> alex9ufo/led1~4)
  else {
    for (int i = 0; i < 4; i++) {
      if (String(topic) == subTopics[i]) {
        digitalWrite(relayPins[i], targetState ? HIGH : LOW);
        Serial.print("[Hardware] LED ");
        Serial.print(i + 1);
        Serial.print(" (GPIO ");
        Serial.print(relayPins[i]);
        Serial.print(") set to: ");
        Serial.println(targetState ? "ON" : "OFF");

        // 回傳該路 JSON 狀態給 EasyBuilder Pro
        publishStatus(i, targetState);
        break;
      }
    }
  }
  Serial.println("----------------------------------------");
}

// MQTT 自動重連機制
void reconnect() {
  while (!client.connected()) {
    Serial.print("[MQTT] Attempting MQTT connection to ");
    Serial.print(mqtt_server);
    Serial.print("...");
    String clientId = "ESP32Client-";
    clientId += String(random(0xffff), HEX);

    if (client.connect(clientId.c_str())) {
      Serial.println(" connected!");
     
      // 訂閱 LB-1 ~ LB-5 的所有控制主題
      for (int i = 0; i < 5; i++) {
        client.subscribe(subTopics[i]);
        Serial.print("[MQTT] Subscribed to: ");
        Serial.println(subTopics[i]);
      }
    } else {
      Serial.print(" failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);

  // 初始化 LCD (GPIO 21=SDA, GPIO 22=SCL)
  Wire.begin(21, 22);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Connecting WiFi...");

  // 初始化繼電器腳位
  for (int i = 0; i < 4; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], LOW);
  }

  // 初始化 DHT 感測器與 Wi-Fi
  dht.begin();
  setup_wifi();

  lcd.clear();
  lcd.print("WiFi Connected!");
  delay(1000);
  lcd.clear();

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

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

  // 每 2 秒讀取感測器、更新 LCD 與發行 JSON 格式 MQTT 數據
  unsigned long now = millis();
  if (now - lastMsg > 2000) {
    lastMsg = now;

    float h = dht.readHumidity();
    float t = dht.readTemperature();

    if (!isnan(h) && !isnan(t)) {
      currentTemp = t;
      currentHumi = h;

      // 打包成不帶 [] 的 JSON 格式字串
      String tempJson = "{\"value\":" + String(t, 2) + "}";
      String humiJson = "{\"value\":" + String(h, 2) + "}";

      // 發行至 MQTT (對應 EasyBuilder Pro LW-10, LW-12)
      client.publish(TOPIC_TEMP, tempJson.c_str());
      client.publish(TOPIC_HUMI, humiJson.c_str());

      // 印出發行紀錄至 Serial Monitor
      Serial.print("[MQTT TX JSON] ");
      Serial.print(TOPIC_TEMP);
      Serial.print(" -> ");
      Serial.println(tempJson);

      Serial.print("[MQTT TX JSON] ");
      Serial.print(TOPIC_HUMI);
      Serial.print(" -> ");
      Serial.println(humiJson);

      // 更新 16x2 LCD
      updateLCD();
    } else {
      Serial.println("[DHT22 ERROR] Failed to read from DHT sensor!");
    }
  }
}



沒有留言:

張貼留言

Easybuilder pro + WOKWI esp32 DHT22 + 4 LED 控制 (MQTT)

Easybuilder pro + WOKWI esp32 DHT22 + 4 LED 控制  (MQTT) 本系統實現了 ESP32 微控制器 (於 Wokwi 模擬環境中執行)與 EasyBuilder Pro 人機介面 (HMI) 透過 MQTT 通訊協定 進行雙向控制與...