2025年3月8日 星期六

作業2 MQTT (Relay + DHT22) 控制 ------- 利用Node-Red

作業2 MQTT (Relay + DHT22) 控制 ------- 利用Node-Red


1) 安裝Node-Red 




2) 安裝Dashboard UI 與 LED元件

http://blog.3dgowl.com/node-red-dashboard%E5%84%80%E8%A1%A8%E6%9D%BF/

https://www.gechic.com/tw/rpi-nodered-touchscreen-gui/

https://flows.nodered.org/node/node-red-contrib-ui-led





WokWI程式

#include <WiFi.h>
#include <MQTTPubSubClient.h>
#include <Adafruit_Sensor.h>
#include <DHT_U.h>
extern "C" {
  #include "freertos/FreeRTOS.h"
  #include "freertos/timers.h"
}

#define Relay4            15     //D15 LED-Orangs = Water-1
#define Relay3            2      //D2   LED-Blue  = Water-2
#define Relay2            19     //D19  LED-Blue   = Motor
#define Relay1            23     //D23  LED-RED    = Fan

#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""

#define MQTT_HOST "test.mosquitto.org"
//#define MQTT_HOST "broker.mqttgo.io"
#define MQTT_PORT 1883

#define DHTPIN 12
// DHT parameters
#define DHTTYPE    DHT22     // DHT 12
DHT_Unified dht(DHTPIN, DHTTYPE);

WiFiClient client;
MQTTPubSubClient mqtt;
int count;
float temp, hum;
bool Send= false;
String json="";

//=============================================================================
void connect() {
  connect_to_wifi:
    Serial.print("connecting to wifi...");
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(1000);
    }
    Serial.println(" connected!");

  connect_to_host:
    Serial.print("connecting to host...");
    client.stop();
    while (!client.connect(MQTT_HOST, 1883)) {//while (!client.connect("public.cloud.shiftr.io", 1883)) {
        Serial.print(".");
        delay(1000);
        if (WiFi.status() != WL_CONNECTED) {
            Serial.println("WiFi disconnected");
            goto connect_to_wifi;
        }
    }
    Serial.println(" connected!");

    Serial.print("connecting to mqtt broker...");
    mqtt.disconnect();
    while (!mqtt.connect("arduino", "", "")) {
        Serial.print(".");
        delay(1000);
        if (WiFi.status() != WL_CONNECTED) {
            Serial.println("WiFi disconnected");
            goto connect_to_wifi;
        }
        if (client.connected() != 1) {
            Serial.println("WiFiClient disconnected");
            goto connect_to_host;
        }
    }
    Serial.println(" connected!");
}
//=============================================================================
void  Publish_message() {
  if (Send){
    mqtt.publish("alex9ufo/esp32/led/status", json);

    Serial.println();  
    Serial.print("publish message to MQTT ---");
    Serial.println(json);
   
    Send= false;
    json="";
  }
}
//=============================================================================
void setup() {

    pinMode(Relay1, OUTPUT);
    pinMode(Relay2, OUTPUT);
    pinMode(Relay3, OUTPUT);
    pinMode(Relay4, OUTPUT);

    Serial.begin(115200);
    dht.begin();
    // Get temperature sensor details.
    sensor_t sensor;
    dht.temperature().getSensor(&sensor);
    dht.humidity().getSensor(&sensor);



    WiFi.begin(WIFI_SSID, WIFI_PASSWORD, 6);
    mqtt.begin(client);
    connect();

    mqtt.subscribe([](const String& topic, const String& payload, const size_t size) {
        Serial.println("mqtt received: " + topic + " - " + payload);
    });

    mqtt.subscribe("alex9ufo/esp32/led/control", [] (const String& payload, const size_t size) {
        Serial.print("alex9ufo/esp32/led/control");
        Serial.println(payload);
       
        String message=payload;
        message.trim();
        Serial.print(message);


        if ( message == "1on" ) {
            digitalWrite(Relay1, HIGH);  // Turn on the LED
            Send = true;
            json="Relay1on";

        }
        if ( message == "1off" ) {
            digitalWrite(Relay1, LOW);  // Turn off the LED
            Send = true;
            json="Relay1off";
        }    
        if ( message == "2on" ) {
            digitalWrite(Relay2, HIGH);  // Turn on the LED
            Send = true;
            json="Relay2on";  
        }
        if ( message == "2off" ) {
            digitalWrite(Relay2, LOW);  // Turn off the LED
            Send = true;
            json="Relay2off";
        }  
        if ( message == "3on" ) {
            digitalWrite(Relay3, HIGH);  // Turn on the LED
            Send = true;
            json="Relay3on";
        }
        if ( message == "3off" ) {
            digitalWrite(Relay3, LOW);  // Turn off the LED
            Send = true;
            json="Relay3off";
        }    
        if ( message == "4on" ) {
            digitalWrite(Relay4, HIGH);  // Turn on the LED
            Send = true;
            json="Relay4on";
        }
        if ( message == "4off" ) {
            digitalWrite(Relay4, LOW);  // Turn off the LED
            Send = true;
            json="Relay4off";
        }
    });
}

//=============================================================================
void loop() {
    mqtt.update();
    Publish_message();

    if (!mqtt.isConnected()) {
        connect();
    }

    // publish message
    static uint32_t prev_ms = millis();
    if (millis() > prev_ms + 2500) {
        prev_ms = millis();
        sensors_event_t event;
      dht.temperature().getEvent(&event);
      if (isnan(event.temperature)) {
          Serial.println(F("Error reading temperature!"));
      }
      else {
        Serial.print(F("Temperature: "));
        temp = event.temperature;
        Serial.print(temp);
        Serial.println(F("°C"));
      }
      // Get humidity event and print its value
      dht.humidity().getEvent(&event);
      if (isnan(event.relative_humidity)) {
        Serial.println(F("Error reading humidity!"));
      }
      else {
        Serial.print(F("Humidity: "));
        hum = event.relative_humidity;
        Serial.print(hum);
        Serial.println(F("%"));
      }
      String msgStr = String(temp) + "," + String(hum) ;

      //mqtt.publish("alex9ufo/esp32/dht/temp", String(temp));
      //delay(100);
      //mqtt.publish("alex9ufo/esp32/dht/humi", String(hum));
      delay(100);  
      mqtt.publish("alex9ufo/esp32/dht/temphumi", msgStr);
    }
}


Node-Red程式的設定

(發行 Publish) LED控制 主題與訊息



SWITCH :  LED1控制



SWITCH :  LED2控制

2on

2off

SWITCH :  LED3控制

3on

3off

SWITCH :  LED4控制

4on

4off

 


 MQTT OUT (LED控制)



接收(訂閱 Subscribe)溫溼度資料


MQTT IN (溫溼度)




Function112

var dataString = msg.payload; // 取得輸入的資料字串

var dataArray = dataString.split(","); // 將字串以逗號分隔成陣列

 

if (dataArray.length === 2) { // 檢查陣列是否包含兩個元素

    var value1 = parseFloat(dataArray[0]); // 將第一個元素轉換為數字

    var value2 = parseFloat(dataArray[1]); // 將第二個元素轉換為數字

 

    var newMsg1 = { payload: value1 }; // 建立第一個輸出訊息

    var newMsg2 = { payload: value2 }; // 建立第二個輸出訊息

 

    return [newMsg1, newMsg2]; // 將兩個訊息作為陣列傳回

} else {

    node.warn("輸入資料格式不正確:" + dataString); // 如果資料格式不正確,發出警告

    return null; // 不輸出任何訊息

}

 



接收(訂閱 Subscribe) LED的狀態亮滅資料

MQTT IN


 

Function程式

LED1

var temp = msg.payload;

 

// Initialize output array

var msg1=null;

var msg2=null;

var msg3=null;

var msg4=null;

 

if (temp=== "Relay1on") {

    msg1=true;

} else if (temp=== "Relay1off") {

    msg1 = false;

} else if (temp== "Relay2on") {

    msg2 = true;

} else if (temp== "Relay2off") {

    msg2 = false;

} else if (temp == "Relay3on") {

    msg3 = true;

} else if (temp == "Relay3off") {

    msg3 = false;

} else if (temp == "Relay4on") {

    msg4 = true;

} else if (temp == "Relay4off") {

    msg4 = false;

}

msg.payload=[msg1,msg2,msg3,msg4];

return msg;

 

LED2

 

let leds = context.get('leds') || [false, false, false, false]; // 取得或初始化 LED 狀態

 

if (Array.isArray(msg.payload) && msg.payload.length === 4) {

    for (let i = 0; i < 4; i++) {

        if (msg.payload[i] !== null) {

            leds[i] = !!msg.payload[i]; // 更新 LED 狀態

        }

    }

    context.set('leds', leds); // 儲存更新後的 LED 狀態

}

msg.payload = leds; // LED 狀態陣列傳遞給輸出

return msg;

 

LED31

var a=msg.payload[0];

msg.payload=a;

return msg;

 

LED32

var a=msg.payload[1];

msg.payload=a;

return msg;

 

LED33

var a=msg.payload[2];

msg.payload=a;

return msg;

 

LED34

var a=msg.payload[3];

msg.payload=a;

return msg;

 

 LED


Node-Red程式

[{"id":"a1735906557852c2","type":"ui_switch","z":"9b7a845b50263bcf","name":"","label":"LED1控制","tooltip":"","group":"5929b808848c0fee","order":1,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"1on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"1off","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":80,"y":200,"wires":[["24bfbc86259cef3d"]]},{"id":"f11746e2b089b534","type":"ui_switch","z":"9b7a845b50263bcf","name":"","label":"LED2控制","tooltip":"","group":"5929b808848c0fee","order":2,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"2on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"2off","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":80,"y":260,"wires":[["24bfbc86259cef3d"]]},{"id":"89084d6b91d72403","type":"ui_switch","z":"9b7a845b50263bcf","name":"","label":"LED3控制","tooltip":"","group":"5929b808848c0fee","order":3,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"3on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"3off","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":80,"y":320,"wires":[["24bfbc86259cef3d"]]},{"id":"1c7539ab31941412","type":"ui_switch","z":"9b7a845b50263bcf","name":"","label":"LED4控制","tooltip":"","group":"5929b808848c0fee","order":4,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"4on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"4off","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":80,"y":380,"wires":[["24bfbc86259cef3d"]]},{"id":"b3f83e9e2d0d8087","type":"ui_led","z":"9b7a845b50263bcf","order":1,"group":"0e6c2f0033795310","width":0,"height":0,"label":"LED1","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#ff0000","value":"false","valueType":"bool"},{"color":"#008000","value":"true","valueType":"bool"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":true,"name":"","x":670,"y":80,"wires":[]},{"id":"f20f2741c4243990","type":"ui_led","z":"9b7a845b50263bcf","order":2,"group":"0e6c2f0033795310","width":0,"height":0,"label":"LED2","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#ff0000","value":"false","valueType":"bool"},{"color":"#008000","value":"true","valueType":"bool"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":true,"name":"","x":670,"y":120,"wires":[]},{"id":"ea1272dddf0a3494","type":"ui_led","z":"9b7a845b50263bcf","order":3,"group":"0e6c2f0033795310","width":0,"height":0,"label":"LED3","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#ff0000","value":"false","valueType":"bool"},{"color":"#008000","value":"true","valueType":"bool"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":true,"name":"","x":670,"y":160,"wires":[]},{"id":"dcf4fc286a36c784","type":"ui_led","z":"9b7a845b50263bcf","order":4,"group":"0e6c2f0033795310","width":0,"height":0,"label":"LED4","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#ff0000","value":"false","valueType":"bool"},{"color":"#008000","value":"true","valueType":"bool"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":true,"name":"","x":670,"y":200,"wires":[]},{"id":"5ba22e78e16be7e6","type":"ui_numeric","z":"9b7a845b50263bcf","name":"","label":"溫度","tooltip":"","group":"7ed56bed88e03b20","order":5,"width":0,"height":0,"wrap":false,"passthru":true,"topic":"topic","topicType":"msg","format":"{{value}}","min":"-40","max":"80","step":1,"className":"","x":670,"y":320,"wires":[[]]},{"id":"0214d3565fb1631b","type":"ui_gauge","z":"9b7a845b50263bcf","name":"溫度","group":"7ed56bed88e03b20","order":6,"width":0,"height":0,"gtype":"gage","title":"gauge","label":"℃","format":"{{value}}","min":"-40","max":"80","colors":["#00b500","#e6e600","#ca3838"],"seg1":"25","seg2":"45","className":"","x":670,"y":360,"wires":[]},{"id":"f225c6944b2d86a6","type":"ui_numeric","z":"9b7a845b50263bcf","name":"","label":"濕度","tooltip":"","group":"7ed56bed88e03b20","order":8,"width":0,"height":0,"wrap":false,"passthru":true,"topic":"topic","topicType":"msg","format":"{{value}}","min":0,"max":"100","step":1,"className":"","x":670,"y":400,"wires":[[]]},{"id":"df89fce5f9933313","type":"ui_gauge","z":"9b7a845b50263bcf","name":"","group":"7ed56bed88e03b20","order":9,"width":0,"height":0,"gtype":"gage","title":"濕度","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"40","seg2":"70","className":"","x":670,"y":440,"wires":[]},{"id":"24bfbc86259cef3d","type":"mqtt out","z":"9b7a845b50263bcf","name":"LED控制","topic":"alex9ufo/esp32/led/control","qos":"1","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"89b71037bb2c4fb7","x":260,"y":280,"wires":[]},{"id":"c3578d05047cbd69","type":"mqtt in","z":"9b7a845b50263bcf","name":"LE狀態態","topic":"alex9ufo/esp32/led/status","qos":"1","datatype":"auto-detect","broker":"89b71037bb2c4fb7","nl":false,"rap":true,"rh":0,"inputs":0,"x":80,"y":140,"wires":[["31d2e8f0c871143d"]]},{"id":"051c962fed8f7c59","type":"mqtt in","z":"9b7a845b50263bcf","name":"溫度濕度","topic":"alex9ufo/esp32/dht/temphumi","qos":"2","datatype":"auto-detect","broker":"89b71037bb2c4fb7","nl":false,"rap":true,"rh":0,"inputs":0,"x":320,"y":360,"wires":[["c08e418b8b51e3d3","b71b54293f8a48d2"]]},{"id":"c08e418b8b51e3d3","type":"function","z":"9b7a845b50263bcf","name":"function 112","func":"var dataString = msg.payload; // 取得輸入的資料字串\nvar dataArray = dataString.split(\",\"); // 將字串以逗號分隔成陣列\n\nif (dataArray.length === 2) { // 檢查陣列是否包含兩個元素\n    var value1 = parseFloat(dataArray[0]); // 將第一個元素轉換為數字\n    var value2 = parseFloat(dataArray[1]); // 將第二個元素轉換為數字\n\n    var newMsg1 = { payload: value1 }; // 建立第一個輸出訊息\n    var newMsg2 = { payload: value2 }; // 建立第二個輸出訊息\n\n    return [newMsg1, newMsg2]; // 將兩個訊息作為陣列傳回\n} else {\n    node.warn(\"輸入資料格式不正確:\" + dataString); // 如果資料格式不正確,發出警告\n    return null; // 不輸出任何訊息\n}","outputs":2,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":360,"wires":[["5ba22e78e16be7e6","0214d3565fb1631b"],["df89fce5f9933313","f225c6944b2d86a6"]]},{"id":"31d2e8f0c871143d","type":"function","z":"9b7a845b50263bcf","name":"LED1","func":"var temp = msg.payload;\n\n// Initialize output array\nvar msg1=null;\nvar msg2=null;\nvar msg3=null;\nvar msg4=null;\n\nif (temp=== \"Relay1on\") {\n    msg1=true;\n} else if (temp=== \"Relay1off\") {\n    msg1 = false;\n} else if (temp== \"Relay2on\") {\n    msg2 = true;\n} else if (temp== \"Relay2off\") {\n    msg2 = false;\n} else if (temp == \"Relay3on\") {\n    msg3 = true;\n} else if (temp == \"Relay3off\") {\n    msg3 = false;\n} else if (temp == \"Relay4on\") {\n    msg4 = true;\n} else if (temp == \"Relay4off\") {\n    msg4 = false;\n}\nmsg.payload=[msg1,msg2,msg3,msg4];\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":210,"y":140,"wires":[["5e5b9c7fe6df7c78","96602e674d2e57de"]]},{"id":"5e5b9c7fe6df7c78","type":"function","z":"9b7a845b50263bcf","name":"LED2","func":"\nlet leds = context.get('leds') || [false, false, false, false]; // 取得或初始化 LED 狀態\n\nif (Array.isArray(msg.payload) && msg.payload.length === 4) {\n    for (let i = 0; i < 4; i++) {\n        if (msg.payload[i] !== null) {\n            leds[i] = !!msg.payload[i]; // 更新 LED 狀態\n        }\n    }\n    context.set('leds', leds); // 儲存更新後的 LED 狀態\n}\nmsg.payload = leds; // 將 LED 狀態陣列傳遞給輸出\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":140,"wires":[["7c40435a3090416c","7e555d8a51a622db","26c5ac43b1671dd3","346cd5d42e5696cd"]]},{"id":"96602e674d2e57de","type":"debug","z":"9b7a845b50263bcf","name":"debug ","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":390,"y":100,"wires":[]},{"id":"27c17cd3c4fc02a7","type":"debug","z":"9b7a845b50263bcf","name":"debug ","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":670,"y":240,"wires":[]},{"id":"b71b54293f8a48d2","type":"debug","z":"9b7a845b50263bcf","name":"debug ","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":450,"y":320,"wires":[]},{"id":"7c40435a3090416c","type":"function","z":"9b7a845b50263bcf","name":"led31","func":"var a=msg.payload[0];\nmsg.payload=a;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":80,"wires":[["b3f83e9e2d0d8087"]]},{"id":"7e555d8a51a622db","type":"function","z":"9b7a845b50263bcf","name":"led34","func":"var a=msg.payload[3];\nmsg.payload=a;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":200,"wires":[["dcf4fc286a36c784","27c17cd3c4fc02a7"]]},{"id":"26c5ac43b1671dd3","type":"function","z":"9b7a845b50263bcf","name":"led32","func":"var a=msg.payload[1];\nmsg.payload=a;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":120,"wires":[["f20f2741c4243990"]]},{"id":"346cd5d42e5696cd","type":"function","z":"9b7a845b50263bcf","name":"led33","func":"var a=msg.payload[2];\nmsg.payload=a;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":160,"wires":[["ea1272dddf0a3494"]]},{"id":"3037d1f8ac490a78","type":"junction","z":"9b7a845b50263bcf","x":340,"y":100,"wires":[[]]},{"id":"5929b808848c0fee","type":"ui_group","name":"Publish 控制LED","tab":"04d2510ba7f3f463","order":1,"disp":true,"width":"4","collapse":false,"className":""},{"id":"0e6c2f0033795310","type":"ui_group","name":"Subscribe LED狀態","tab":"04d2510ba7f3f463","order":2,"disp":true,"width":"4","collapse":false,"className":""},{"id":"7ed56bed88e03b20","type":"ui_group","name":"Sunscribe 溫溼度","tab":"04d2510ba7f3f463","order":3,"disp":true,"width":"6","collapse":false,"className":""},{"id":"89b71037bb2c4fb7","type":"mqtt-broker","name":"test.mosquitto.org","broker":"test.mosquitto.org","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"04d2510ba7f3f463","type":"ui_tab","name":"EX1","icon":"dashboard","order":13,"disabled":false,"hidden":false}]

2025年2月28日 星期五

作業1 MQTT (Relay + DHT22) 控制 ------- 利用MQTT BOX (PC上) MyMQTT (手機上APP)

作業1 MQTT (Relay + DHT22) 控制  ------- 利用MQTT BOX (PC上)   MyMQTT (手機上APP) 


1)系統圖






2) 利用gmail註冊





3) 開新專案




4) 建立電路圖






5 ) 程式與電路圖相關資料

繼電器Relay 接腳

#define Relay1            15     //D15 LED-Orangs = Water-1
#define Relay2            2      //D2   LED-Blue  = Water-2
#define Relay3            19     //D19  LED-Blue   = Motor
#define Relay4            23     //D23  LED-RED    = Fan

DHT22 溫溼度Sensor 接腳
#define DHTPIN 12

WIFI 帳號密碼

#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""

MQTT Broker伺服器

// #define MQTT_HOST "test.mosquitto.org"
#define MQTT_HOST "broker.mqttgo.io"
#define MQTT_PORT 1883




NMKing夜市小霸王

夜市小霸王 輕鬆入門級IoT物聯網應用教學



https://broker.mqttgo.io/


或是另一種 伺服器

#define MQTT_HOST "test.mosquitto.org"
//#define MQTT_HOST "broker.mqttgo.io"
#define MQTT_PORT 1883



6) WOKWI程式如下

下面程式使用broker.mqttgo.io


#include <WiFi.h>
#include <MQTTPubSubClient.h>
#include <Adafruit_Sensor.h>
#include <DHT_U.h>
extern "C" {
  #include "freertos/FreeRTOS.h"
  #include "freertos/timers.h"
}

#define Relay4            15     //D15 LED-Orangs = Water-1
#define Relay3            2      //D2   LED-Blue  = Water-2
#define Relay2            19     //D19  LED-Blue   = Motor
#define Relay1            23     //D23  LED-RED    = Fan

#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""

//#define MQTT_HOST "test.mosquitto.org"
#define MQTT_HOST "broker.mqttgo.io"
#define MQTT_PORT 1883

#define DHTPIN 12
// DHT parameters
#define DHTTYPE    DHT22     // DHT 12
DHT_Unified dht(DHTPIN, DHTTYPE);

WiFiClient client;
MQTTPubSubClient mqtt;
int count;
float temp, hum;
bool Send= false;
String json="";
//=============================================================================
void connect() {
  connect_to_wifi:
    Serial.print("connecting to wifi...");
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(1000);
    }
    Serial.println(" connected!");

  connect_to_host:
    Serial.print("connecting to host...");
    client.stop();
    while (!client.connect(MQTT_HOST, 1883)) {
        Serial.print(".");
        delay(1000);
        if (WiFi.status() != WL_CONNECTED) {
            Serial.println("WiFi disconnected");
            goto connect_to_wifi;
        }
    }
    Serial.println(" connected!");

    Serial.print("connecting to mqtt broker...");
    mqtt.disconnect();
    while (!mqtt.connect("arduino", "", "")) {
        Serial.print(".");
        delay(1000);
        if (WiFi.status() != WL_CONNECTED) {
            Serial.println("WiFi disconnected");
            goto connect_to_wifi;
        }
        if (client.connected() != 1) {
            Serial.println("WiFiClient disconnected");
            goto connect_to_host;
        }
    }
    Serial.println(" connected!");
}
//=============================================================================
void  Publish_message() {
  if (Send){
    mqtt.publish("alex9ufo/esp32/led/status", json);

    Serial.println();  
    Serial.print("publish message to MQTT ---");
    Serial.println(json);
   
    Send= false;
    json="";
  }
}
//=============================================================================
void setup() {
    pinMode(Relay1, OUTPUT);
    pinMode(Relay2, OUTPUT);
    pinMode(Relay3, OUTPUT);
    pinMode(Relay4, OUTPUT);

    Serial.begin(115200);
    dht.begin();
    // Get temperature sensor details.
    sensor_t sensor;
    dht.temperature().getSensor(&sensor);
    dht.humidity().getSensor(&sensor);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD, 6);
    mqtt.begin(client);
    connect();

    mqtt.subscribe([](const String& topic, const String& payload, const size_t size) {
        Serial.println("mqtt received: " + topic + " - " + payload);
    });

    mqtt.subscribe("alex9ufo/esp32/led/control", [] (const String& payload, const size_t size) {
        Serial.print("alex9ufo/esp32/led/control");
        Serial.println(payload);
       
        String message=payload;
        message.trim();
        Serial.print(message);
        if ( message == "1on" ) {
            digitalWrite(Relay1, HIGH);  // Turn on the LED
            Send = true;
            json="Relay1on";

        }
        if ( message == "1off" ) {
            digitalWrite(Relay1, LOW);  // Turn off the LED
            Send = true;
            json="Relay1off";
        }    
        if ( message == "2on" ) {
            digitalWrite(Relay2, HIGH);  // Turn on the LED
            Send = true;
            json="Relay2on";  
        }
        if ( message == "2off" ) {
            digitalWrite(Relay2, LOW);  // Turn off the LED
            Send = true;
            json="Relay2off";
        }  
        if ( message == "3on" ) {
            digitalWrite(Relay3, HIGH);  // Turn on the LED
            Send = true;
            json="Relay3on";
        }
        if ( message == "3off" ) {
            digitalWrite(Relay3, LOW);  // Turn off the LED
            Send = true;
            json="Relay3off";
        }    
        if ( message == "4on" ) {
            digitalWrite(Relay4, HIGH);  // Turn on the LED
            Send = true;
            json="Relay4on";
        }
        if ( message == "4off" ) {
            digitalWrite(Relay4, LOW);  // Turn off the LED
            Send = true;
            json="Relay4off";
        }
    });
}
//=============================================================================
void loop() {
    mqtt.update();
    Publish_message();

    if (!mqtt.isConnected()) {
        connect();
    }

    // publish message
    static uint32_t prev_ms = millis();
    if (millis() > prev_ms + 2500) {
        prev_ms = millis();
        sensors_event_t event;
      dht.temperature().getEvent(&event);
      if (isnan(event.temperature)) {
          Serial.println(F("Error reading temperature!"));
      }
      else {
        Serial.print(F("Temperature: "));
        temp = event.temperature;
        Serial.print(temp);
        Serial.println(F("°C"));
      }
      // Get humidity event and print its value
      dht.humidity().getEvent(&event);
      if (isnan(event.relative_humidity)) {
        Serial.println(F("Error reading humidity!"));
      }
      else {
        Serial.print(F("Humidity: "));
        hum = event.relative_humidity;
        Serial.print(hum);
        Serial.println(F("%"));
      }
      String msgStr = String(temp) + "," + String(hum) ;

      //mqtt.publish("alex9ufo/esp32/dht/temp", String(temp));
      //delay(100);
      //mqtt.publish("alex9ufo/esp32/dht/humi", String(hum));
      delay(100);  
      mqtt.publish("alex9ufo/esp32/dht/temphumi", msgStr);
    }
}


7) 添加 Library Manager




8) MQTT BOX設定 畫面

下載MQTT BOX


BROKER有test.mosquitto.org 或broker.mqttgo.io

依照 程式的定義
MQTT_HOST "test.mosquitto.org"
MQTT_HOST "broker.mqttgo.io"

主題TOPIC有3種
(對WOKWI程式 訂閱Subscribe 則 對MQTT BOX (My MQTT) 就是發行Publish )

 

MQTT BOX

WOKWI

1主題

alex9ufo/esp32/led/control

發行

alex9ufo/esp32/led/control

訂閱

  訊息

1on,1off,2on,2off,3on,3off,4on,4off

 

 

 

2主題

alex9ufo/esp32/led/status

訂閱

alex9ufo/esp32/led/status

發行

 

 

訊息

Realy1on, Relay1off, Realy2on, Relay2off, Realy3on, Relay3off, Realy4on, Relay4off,

 

3主題

alex9ufo/esp32/dht/temphumi

訂閱

alex9ufo/esp32/dht/temphumi

發行

 

 

訊息

溫度,濕度 (24.0,40.5)

 




alex9ufo/esp32/led/control
        1on , 1off , 2on , 2off, 3on ,3off ,4on ,4off, allon ,alloff

alex9ufo/esp32/led/status
        接收WOKWI ESP32 LED回傳狀態

alex9ufo/esp32/dht/temphumi
        接收WOKWI ESP32 DHT22回傳狀態   
 


下面的HOST

broker.mqttgo.io:1883

依據 WOKWI程式 也可以修改成

test.mosquitto.org:1883





9) 將WOKWI 組譯 (可能需要組譯 多次  因為免費)




10) 調整DHT22 溫度 濕度




11) 測試結果  使用 "test.mosquitto.org" 伺服器




MQTT BOX + WOKWI 畫面







12) MyMQTT 的設定與畫面

 

 

 

 

 

 

或修改成

test.mosquitto.org


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



13) 錄製影片並上傳至YT
  1. 按 Windows鍵+Alt+R 開始錄製剪輯。 在 「聽訊」中,您聽到:「錄製進行中」。 

  2. 若要在錄製期間開啟或關閉麥克風,請按 Windows鍵+Alt+M。

作業2 MQTT (Relay + DHT22) 控制 ------- 利用Node-Red

作業2 MQTT (Relay + DHT22) 控制 ------- 利用Node-Red 1) 安裝Node-Red  https://ithelp.ithome.com.tw/articles/10201795 https://www.youtube.com/watch?v...