2025年4月16日 星期三

作業3 WOKWI + Node-Red + SQlite + Line Developer

 作業3  WOKWI + Node-Red + SQlite + Line Developer

1)系統圖

(第一次執行node-red 時 要先將 資料庫 C:\Users\User\ 2025EX2_RFID.db 建立  按圖示 <<建立RFID資料庫>>





2)實驗結果


                    https://www.youtube.com/watch?v=B08TaYfROi0


3)Wokwi Arduino程式

#include <ArduinoMqttClient.h>
#include <WiFi.h>
//MFRC522 程式庫  模擬mfrc522 送出卡號 PB可以控制
int LED1 = 23;
int LED2 = 22;
int LED3 = 21;
int LED4 = 19;

char ssid[]="Wokwi-GUEST";
char pass[]="";
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);

//const char broker[] = "test.mosquitto.org";
const char broker[] = "broker.mqttgo.io";
//const char broker[] = "broker.mqtt-dashboard.com";
int        port     = 1883;

const char *SubTopic1 = "alex9ufo/esp32/led";
const char *PubTopic2 = "alex9ufo/esp32/led_status";
const char *PubTopic3 = "alex9ufo/esp32/RFID";
const char willTopic[] = "alex9ufo/esp32/Starting";

int inPin = 12;    // pushbutton connected to digital pin 7
int val = 0;      // variable to store the read value
bool create=false;
bool Send = false;  //true

bool Flash1 = false;  //true
bool Flash2 = false;  //true
bool Flash3 = false;  //true
bool Flash4 = false;  //true

String LEDjson = "";
//===========================================================
void onMqttMessage(int messageSize) {
  // we received a message, print out the topic and contents
  Serial.print("Received a message with topic '");
  Serial.print(mqttClient.messageTopic());
  String Topic= mqttClient.messageTopic();
  Serial.print("', duplicate = ");
  Serial.print(mqttClient.messageDup() ? "true" : "false");
  Serial.print(", QoS = ");
  Serial.print(mqttClient.messageQoS());
  Serial.print(", retained = ");
  Serial.print(mqttClient.messageRetain() ? "true" : "false");
  Serial.print("', length ");
  Serial.print(messageSize);
  Serial.println(" bytes:");
  String message="";
  // use the Stream interface to print the contents
  while (mqttClient.available()) {
    //Serial.print((char)mqttClient.read());
    message += (char)mqttClient.read();
  }

  Serial.println(message);
  message.trim();
  Topic.trim();

  if (Topic=="alex9ufo/esp32/led") {
  if (message == "led1on") {
    digitalWrite(LED1, HIGH);  // Turn on the LED
    //ledState = true;  //ledState = ture HIGH
    //設定 各個 旗號
    LEDjson ="LED1ON";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }
  if (message == "led2on") {
    digitalWrite(LED2, HIGH);  // Turn on the LED
    //ledState = true;  //ledState = ture HIGH
    //設定 各個 旗號
    LEDjson ="LED2ON";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }
  if (message == "led3on") {
    digitalWrite(LED3, HIGH);  // Turn on the LED
    //ledState = true;  //ledState = ture HIGH
    //設定 各個 旗號
    LEDjson ="LED3ON";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }
  if (message == "led4on") {
    digitalWrite(LED4, HIGH);  // Turn on the LED
    //ledState = true;  //ledState = ture HIGH
    //設定 各個 旗號
    LEDjson ="LED4ON";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }

  if (message == "led1off" ) {
    digitalWrite(LED1, LOW); // Turn off the LED
    //ledState = false; //ledState = false LOW
    LEDjson ="LED1OFF";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }
  if (message == "led2off" ) {
    digitalWrite(LED2, LOW); // Turn off the LED
    //ledState = false; //ledState = false LOW
    LEDjson ="LED2OFF";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }

  if (message == "led3off" ) {
    digitalWrite(LED3, LOW); // Turn off the LED
    //ledState = false; //ledState = false LOW
    LEDjson ="LED3OFF";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }
  if (message == "led4off" ) {
    digitalWrite(LED4, LOW); // Turn off the LED
    //ledState = false; //ledState = false LOW
    LEDjson ="LED4OFF";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }
  if (message == "led1flash" ) {
    digitalWrite(LED1, HIGH); // Turn off the LED
    Flash1 = true;
    //Flash2 = false;
    //Flash3 = false;
    //Flash4 = false;
    LEDjson ="LED1FLASH";
    Send = true ;  
    Serial.println(LEDjson);      
  }
  if (message == "led2flash" ) {
    digitalWrite(LED2, HIGH); // Turn off the LED
    Flash2 = true;
    //Flash1 = false;
    //Flash3 = false;
    //Flash4 = false;
    LEDjson ="LED2FLASH";
    Send = true ;  
    Serial.println(LEDjson);      
  }
  if (message == "led3flash" ) {
    digitalWrite(LED3, HIGH); // Turn off the LED
    Flash3 = true;
    //Flash1 = false;
    //Flash2 = false;
    //Flash4 = false;
    LEDjson ="LED3FLASH";
    Send = true ;  
    Serial.println(LEDjson);      
  }
  if (message == "led4flash" ) {
    digitalWrite(LED4, HIGH); // Turn off the LED
    Flash4 = true;
    //Flash1 = false;
    //Flash2 = false;
    //Flash3 = false;
    LEDjson ="LED4FLASH";
    Send = true ;  
    Serial.println(LEDjson);      
  }

  if (message == "ledalloff" ) {
    digitalWrite(LED1, LOW); // Turn off the LED
    digitalWrite(LED2, LOW); // Turn off the LED
    digitalWrite(LED3, LOW); // Turn off the LED
    digitalWrite(LED4, LOW); // Turn off the LED
    //ledState = false; //ledState = false LOW
    LEDjson ="LEDALLOFF";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }

  if (message == "ledallon") {
    digitalWrite(LED1, HIGH);  // Turn on the LED
    digitalWrite(LED2, HIGH);  // Turn on the LED
    digitalWrite(LED3, HIGH);  // Turn on the LED
    digitalWrite(LED4, HIGH);  // Turn on the LED
   
    //ledState = true;  //ledState = ture HIGH
    //設定 各個 旗號
    LEDjson ="LEDALLON";
    Flash1 = false;
    Flash2 = false;
    Flash3 = false;
    Flash4 = false;
    Send = true ;
    Serial.println(LEDjson);
  }
  Serial.println();
  Serial.println("-----------------------");
  }  
}
//===========================================================
//===========================================================
//判斷 旗號Flash , Timer 是否為真
void LED_Message() {
  //判斷 旗號 Flash / timer  是否為真 ? 閃爍 定時
  if (Flash1 && Flash2 && Flash3 && Flash4){
      digitalWrite(LED1, !digitalRead(LED1));
      digitalWrite(LED2, !digitalRead(LED2));
      digitalWrite(LED3, !digitalRead(LED3));
      digitalWrite(LED4, !digitalRead(LED4));      
      delay(500);  
  }

  if (Flash1) {
    digitalWrite(LED1, !digitalRead(LED1));
    delay(500);
  } //(Flash1)
  if (Flash2){
    digitalWrite(LED2, !digitalRead(LED2));
    delay(500);
  } //(Flash2)
  if (Flash3){
    digitalWrite(LED3, !digitalRead(LED3));
    delay(500);
  } //(Flash3)
  if (Flash4){
    digitalWrite(LED4, !digitalRead(LED4));
    delay(500);
  } //(Flash4)
 
  ////判斷 旗號 Send 是否為真 回傳MQTT訊息到MQTT Broker 
  if (Send) {
    // Convert JSON string to character array
    Serial.print("Publish message: ");
    Serial.println(LEDjson);
    LEDjson.trim();
    bool retained = false;
    int qos = 1;
    bool dup = false;
   
    // Publish JSON character array to MQTT topic
    mqttClient.beginMessage(PubTopic2,  LEDjson.length(), retained, qos, dup);  //LED Status
    mqttClient.print(LEDjson);
    mqttClient.endMessage();
    Send = false;    //處理過後 旗號 Send為假
  }

}
//===========================================================
//===========================================================
//副程式  setup wifi
void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);     //print ssid
  WiFi.begin(ssid, pass);  //初始化WiFi 函式庫並回傳目前的網路狀態
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }   //假設 wifi 未連接 show ………

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}
//==================================================================
void setup() {
  Serial.begin(115200); // 初始化序列埠
  pinMode(LED1, OUTPUT);
  digitalWrite(LED1, LOW);  // Turn off the LED initially
  pinMode(LED2, OUTPUT);
  digitalWrite(LED2, LOW);  // Turn off the LED initially
  pinMode(LED3, OUTPUT);
  digitalWrite(LED3, LOW);  // Turn off the LED initially
  pinMode(LED4, OUTPUT);
  digitalWrite(LED4, LOW);  // Turn off the LED initially

  pinMode(inPin, INPUT);    // sets the digital pin 12 as input
  randomSeed(analogRead(0)); // 設定亂數種子,增加亂數的隨機性  

  //Initialize serial and wait for port to open:
  Serial.begin(115200);   // Initialize serial communications with the PC
  while (!Serial);    // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
 
  setup_wifi();
  Serial.println("You're connected to the network");
  Serial.println();
 
  String willPayload = "ESP32 Start working....!";
  bool willRetain = true;
  int willQos = 1;

  mqttClient.beginWill(willTopic, willPayload.length(), willRetain, willQos);
  mqttClient.print(willPayload);
  mqttClient.endWill();

  Serial.print("Attempting to connect to the MQTT broker: ");
  Serial.println(broker);

  if (!mqttClient.connect(broker, port)) {
    Serial.print("MQTT connection failed! Error code = ");
    Serial.println(mqttClient.connectError());

    while (1);
  }
  Serial.println("You're connected to the MQTT broker!");
  Serial.println();

  // set the message receive callback
  mqttClient.onMessage(onMqttMessage);
  Serial.print("Subscribing to topic: ");
  Serial.println(SubTopic1);
 
  // subscribe to a topic
  // the second parameter sets the QoS of the subscription,
  // the the library supports subscribing at QoS 0, 1, or 2
  int subscribeQos = 1;
  mqttClient.subscribe(SubTopic1, subscribeQos);
  Serial.println();
  /***
  Init SPI bus
  Init MFRC522
  ***/
}
//==================================================================
void loop() {
  val = digitalRead(inPin);   // read the input pin
  if (val==LOW) create= false;
  if (val==HIGH) create= true;

  // call poll() regularly to allow the library to receive MQTT messages and
  // send MQTT keep alives which avoids being disconnected by the broker
  mqttClient.poll();
 
  LED_Message();

  if (create) {

    // 模擬 RFID UID 結構
    struct RFID_UID {
      uint8_t uidByte[10]; // UID 位元組陣列,最大長度為 10
      uint8_t size;        // UID 長度
    };
    // 建立一個 RFID UID 物件
    RFID_UID uid;
    // 設定 UID 資料
    uid.size = 4;
   
    for (int i = 0; i < uid.size; i++) {
      uid.uidByte[i] = random(256); // 生成 0-255 的亂數
    }


    // 顯示 UID
    Serial.print("UID: ");
    for (int i = 0; i < uid.size; i++) {
      Serial.print(uid.uidByte[i] < 0x10 ? " 0" : " "); // 補零
      Serial.print(uid.uidByte[i], HEX);             // 以十六進位格式輸出
    }
    Serial.println();

    String json = "{ \"uid\": \"";
    for (int i = 0; i < uid.size; i++) {
        json += String(uid.uidByte[i], HEX);
      }
    json += "\" }";

    json.trim();
    bool retained = false;
    int qos = 1;
    bool dup = false;
    mqttClient.beginMessage(PubTopic3,  json.length(), retained, qos, dup);
    mqttClient.print(json);
    mqttClient.endMessage();
    Serial.println(json);
    Serial.println();
  }
}

電路圖








4) Node-Red程式


[{"id":"508a83bf78d1af97","type":"ui_switch","z":"fcce0e6bead574e7","name":"","label":"LED1 ON OFF","tooltip":"","group":"7376e0c4dc8cc058","order":1,"width":4,"height":1,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"led1on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"led1off","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":1040,"y":40,"wires":[["6a97f1d3529e0cfd","b799e713f3da2249"]]},{"id":"20f5422869d00e3b","type":"ui_switch","z":"fcce0e6bead574e7","name":"","label":"LED2 ON OFF","tooltip":"","group":"7376e0c4dc8cc058","order":3,"width":4,"height":1,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"led2on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"led2off","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":1040,"y":80,"wires":[["6a97f1d3529e0cfd"]]},{"id":"f2291e7009d6d78b","type":"ui_switch","z":"fcce0e6bead574e7","name":"","label":"LED3 ON OFF","tooltip":"","group":"7376e0c4dc8cc058","order":5,"width":4,"height":1,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"led3on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"led3off","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":1040,"y":120,"wires":[["6a97f1d3529e0cfd"]]},{"id":"88f06c8d911ee9df","type":"ui_switch","z":"fcce0e6bead574e7","name":"","label":"LED4 ON OFF","tooltip":"","group":"7376e0c4dc8cc058","order":7,"width":4,"height":1,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"led4on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"led4off","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":1040,"y":160,"wires":[["6a97f1d3529e0cfd"]]},{"id":"c70dbeb8107da82e","type":"ui_button","z":"fcce0e6bead574e7","name":"","group":"7376e0c4dc8cc058","order":9,"width":6,"height":1,"passthru":false,"label":"LED all ON","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"ledallon","payloadType":"str","topic":"topic","topicType":"msg","x":1030,"y":200,"wires":[["6a97f1d3529e0cfd"]]},{"id":"27fc93ca75339a2f","type":"ui_button","z":"fcce0e6bead574e7","name":"","group":"7376e0c4dc8cc058","order":10,"width":6,"height":1,"passthru":false,"label":"LED all OFF","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"ledalloff","payloadType":"str","topic":"topic","topicType":"msg","x":1030,"y":240,"wires":[["6a97f1d3529e0cfd"]]},{"id":"6a97f1d3529e0cfd","type":"mqtt out","z":"fcce0e6bead574e7","name":"LED Control","topic":"alex9ufo/esp32/led","qos":"1","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"584db2f88f8050c2","x":1290,"y":140,"wires":[]},{"id":"b799e713f3da2249","type":"debug","z":"fcce0e6bead574e7","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1240,"y":40,"wires":[]},{"id":"a1815bd178772a81","type":"mqtt in","z":"fcce0e6bead574e7","name":"LED Status","topic":"alex9ufo/esp32/led_status","qos":"2","datatype":"auto-detect","broker":"584db2f88f8050c2","nl":false,"rap":true,"rh":0,"inputs":0,"x":90,"y":240,"wires":[["653157f6067482cb","2","4187189e16b0375c"]]},{"id":"653157f6067482cb","type":"debug","z":"fcce0e6bead574e7","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":240,"y":400,"wires":[]},{"id":"2","type":"function","z":"fcce0e6bead574e7","name":"LED Dispatcher","func":"// 建立一個陣列來裝每個輸出的訊息,預設都為 null\nlet outputs = Array(10).fill(null);\n\n// 根據 payload 決定要送出的輸出端口 (0~9)\nswitch (msg.payload) {\n    case \"LED1ON\":\n        outputs[0] = msg;\n        break;\n    case \"LED1OFF\":\n        outputs[1] = msg;\n        break;\n    case \"LED2ON\":\n        outputs[2] = msg;\n        break;\n    case \"LED2OFF\":\n        outputs[3] = msg;\n        break;\n    case \"LED3ON\":\n        outputs[4] = msg;\n        break;\n    case \"LED3OFF\":\n        outputs[5] = msg;\n        break;\n    case \"LED4ON\":\n        outputs[6] = msg;\n        break;\n    case \"LED4OFF\":\n        outputs[7] = msg;\n        break;\n    case \"LEDALLON\":\n        outputs[8] = msg;\n        break;\n    case \"LEDALLOFF\":\n        outputs[9] = msg;\n        break;\n    default:\n        return null;\n}\n\n// 回傳包含 10 個輸出的陣列\nreturn outputs;","outputs":10,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":240,"wires":[["debug1","0a22d4a2657165ad"],["debug2","90d79b4c9d4b0773"],["debug3","fbc38c9011e4c418"],["debug4","69a9a1509ba3e739"],["debug5","d44f7266d3524809"],["debug6","29786589908dd6a7"],["debug7","0d6635a689b7d6b0"],["debug8","1b28cd3697bad642"],["debug9","f4bf4a7b5204983c"],["debug10","d617b4f4b928de16"]]},{"id":"debug1","type":"debug","z":"fcce0e6bead574e7","name":"LED1 ON","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","statusVal":"","statusType":"auto","x":540,"y":40,"wires":[]},{"id":"debug2","type":"debug","z":"fcce0e6bead574e7","name":"LED1 OFF","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":530,"y":120,"wires":[]},{"id":"debug3","type":"debug","z":"fcce0e6bead574e7","name":"LED2 ON","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":520,"y":180,"wires":[]},{"id":"debug4","type":"debug","z":"fcce0e6bead574e7","name":"LED2 OFF","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":530,"y":220,"wires":[]},{"id":"debug5","type":"debug","z":"fcce0e6bead574e7","name":"LED3 ON","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":530,"y":270,"wires":[]},{"id":"debug6","type":"debug","z":"fcce0e6bead574e7","name":"LED3 OFF","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":530,"y":300,"wires":[]},{"id":"debug7","type":"debug","z":"fcce0e6bead574e7","name":"LED4 ON","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":520,"y":340,"wires":[]},{"id":"debug8","type":"debug","z":"fcce0e6bead574e7","name":"LED4 OFF","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":530,"y":380,"wires":[]},{"id":"debug9","type":"debug","z":"fcce0e6bead574e7","name":"ALL ON","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":520,"y":420,"wires":[]},{"id":"debug10","type":"debug","z":"fcce0e6bead574e7","name":"ALL OFF","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":540,"y":520,"wires":[]},{"id":"d617b4f4b928de16","type":"function","z":"fcce0e6bead574e7","name":"function LEDALLOFF","func":"if (msg.payload === \"LEDALLOFF\") {\n    msg.payload = false;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":480,"wires":[["f1ab102d6af41123"]]},{"id":"f4bf4a7b5204983c","type":"function","z":"fcce0e6bead574e7","name":"function LEDALLON","func":"if (msg.payload === \"LEDALLON\") {\n    msg.payload = true;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":440,"wires":[["f1ab102d6af41123"]]},{"id":"0a22d4a2657165ad","type":"function","z":"fcce0e6bead574e7","name":"function LED1ON","func":"if (msg.payload === \"LED1ON\") {\n    msg.payload = true;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":60,"wires":[["1623401c00f3052c","553292363b556395"]]},{"id":"90d79b4c9d4b0773","type":"function","z":"fcce0e6bead574e7","name":"function LED1OFF","func":"if (msg.payload === \"LED1OFF\") {\n    msg.payload = false;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":100,"wires":[["1623401c00f3052c","553292363b556395"]]},{"id":"1623401c00f3052c","type":"ui_led","z":"fcce0e6bead574e7","order":2,"group":"7376e0c4dc8cc058","width":2,"height":1,"label":"1","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":810,"y":80,"wires":[]},{"id":"fbc38c9011e4c418","type":"function","z":"fcce0e6bead574e7","name":"function LED2ON","func":"if (msg.payload === \"LED2ON\") {\n    msg.payload = true;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":160,"wires":[["37fad941c49aee36","553292363b556395"]]},{"id":"69a9a1509ba3e739","type":"function","z":"fcce0e6bead574e7","name":"function LED2OFF","func":"if (msg.payload === \"LED2OFF\") {\n    msg.payload = false;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":200,"wires":[["37fad941c49aee36","553292363b556395"]]},{"id":"37fad941c49aee36","type":"ui_led","z":"fcce0e6bead574e7","order":4,"group":"7376e0c4dc8cc058","width":2,"height":1,"label":"2","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":810,"y":180,"wires":[]},{"id":"d44f7266d3524809","type":"function","z":"fcce0e6bead574e7","name":"function LED3ON","func":"if (msg.payload === \"LED3ON\") {\n    msg.payload = true;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":260,"wires":[["866d4531a00e4d06","553292363b556395"]]},{"id":"29786589908dd6a7","type":"function","z":"fcce0e6bead574e7","name":"function LED3OFF","func":"if (msg.payload === \"LED3OFF\") {\n    msg.payload = false;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":300,"wires":[["866d4531a00e4d06","553292363b556395"]]},{"id":"866d4531a00e4d06","type":"ui_led","z":"fcce0e6bead574e7","order":6,"group":"7376e0c4dc8cc058","width":2,"height":1,"label":"3","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":810,"y":280,"wires":[]},{"id":"0d6635a689b7d6b0","type":"function","z":"fcce0e6bead574e7","name":"function LED4ON","func":"if (msg.payload === \"LED4ON\") {\n    msg.payload = true;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":340,"wires":[["e43da95604e0e558","553292363b556395"]]},{"id":"1b28cd3697bad642","type":"function","z":"fcce0e6bead574e7","name":"function LED4OFF","func":"if (msg.payload === \"LED4OFF\") {\n    msg.payload = false;\n    return msg;\n} else {\n    return null;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":380,"wires":[["e43da95604e0e558","553292363b556395"]]},{"id":"e43da95604e0e558","type":"ui_led","z":"fcce0e6bead574e7","order":8,"group":"7376e0c4dc8cc058","width":2,"height":1,"label":"4","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":810,"y":360,"wires":[]},{"id":"f1ab102d6af41123","type":"link out","z":"fcce0e6bead574e7","name":"link out 5","mode":"link","links":["60c56a73fae93760"],"x":725,"y":460,"wires":[]},{"id":"60c56a73fae93760","type":"link in","z":"fcce0e6bead574e7","name":"link in 7","links":["f1ab102d6af41123"],"x":725,"y":140,"wires":[["1623401c00f3052c","37fad941c49aee36","866d4531a00e4d06","e43da95604e0e558","553292363b556395"]]},{"id":"553292363b556395","type":"debug","z":"fcce0e6bead574e7","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":820,"y":220,"wires":[]},{"id":"9a349abe035372bb","type":"function","z":"fcce0e6bead574e7","name":"CREATE DATABASE","func":"//CREATE TABLE RFIDUID (\n//id INTEGER,\n//UID TEXT,\n//Date DATE,\n//Time TIME,\n//PRIMARY KEY (id)\n//);\n//CREATE TABLE LEDSTATUS (id INTEGER,UID TEXT,Date DATE,Time TIME,PRIMARY KEY (id));\nmsg.topic = \"CREATE TABLE RFIDUID (id INTEGER,UID TEXT,Date DATE,Time TIME,PRIMARY KEY (id))\";\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1220,"y":480,"wires":[["a2d7d7399a152e7d"]]},{"id":"955a2e1ec6b03d9a","type":"ui_button","z":"fcce0e6bead574e7","name":"","group":"c1460b15f679e676","order":2,"width":3,"height":1,"passthru":false,"label":"建立RFID資料庫","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"建立資料庫","payloadType":"str","topic":"topic","topicType":"msg","x":1000,"y":480,"wires":[["9a349abe035372bb","27477e6cbb0d1ab0"]]},{"id":"1d13d8d27746f41f","type":"function","z":"fcce0e6bead574e7","name":"INSERT","func":"var Today = new Date();\nvar yyyy = Today.getFullYear(); //年\nvar MM = Today.getMonth()+1;    //月\nvar dd = Today.getDate();       //日\nvar h = Today.getHours();       //時\nvar m = Today.getMinutes();     //分\nvar s = Today.getSeconds();     //秒\nif(MM<10)\n{\n   MM = '0'+MM;\n}\n\nif(dd<10)\n{\n   dd = '0'+dd;\n}\n\nif(h<10)\n{\n   h = '0'+h;\n}\n\nif(m<10)\n{\n  m = '0' + m;\n}\n\nif(s<10)\n{\n  s = '0' + s;\n}\nvar var_date = yyyy+'/'+MM+'/'+dd;\nvar var_time = h+':'+m+':'+s;\n\nvar myUID = msg.payload.uid;\n\n\nmsg.topic = \"INSERT INTO RFIDUID ( UID , Date , Time ) VALUES ($myUID,  $var_date ,  $var_time ) \" ;\nmsg.payload = [myUID, var_date , var_time ]\nreturn msg;\n\n\n//INSERT INTO LEDSTATUS (\n//id INTEGER,\n//UID TEXT,\n//Date DATE,\n//Time TIME,\n//PRIMARY KEY (id)\n//);\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1160,"y":340,"wires":[["ceaf38b381bc12e0"]]},{"id":"ceaf38b381bc12e0","type":"sqlite","z":"fcce0e6bead574e7","mydb":"f5c97c74cc496505","sqlquery":"msg.topic","sql":"","name":"RFID_2025EX2","x":1340,"y":340,"wires":[["95a108ee567f790d","5ba82d08c111b413"]]},{"id":"ce776f39256a83d6","type":"debug","z":"fcce0e6bead574e7","name":"debug ","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1590,"y":480,"wires":[]},{"id":"95a108ee567f790d","type":"debug","z":"fcce0e6bead574e7","name":"debug ","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1530,"y":340,"wires":[]},{"id":"f19ba4f89182c827","type":"ui_button","z":"fcce0e6bead574e7","name":"","group":"c1460b15f679e676","order":1,"width":3,"height":1,"passthru":false,"label":"檢視資料庫資料","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"檢視資料","payloadType":"str","topic":"topic","topicType":"msg","x":1000,"y":420,"wires":[["5ba82d08c111b413","27477e6cbb0d1ab0"]]},{"id":"5ba82d08c111b413","type":"function","z":"fcce0e6bead574e7","name":"檢視資料","func":"//INSERT INTO RFIDUID (\n//id INTEGER,\n//UID TEXT,\n//Date DATE,\n//Time TIME,\n//PRIMARY KEY (id)\n//);\n\n//SELECT * FROM RFIDUID ORDER BY  id DESC LIMIT 50;\n\nmsg.topic = \"SELECT * FROM RFIDUID ORDER BY id DESC LIMIT 50\";\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1300,"y":420,"wires":[["d75616872c411e2a"]]},{"id":"0125ad9a2c641256","type":"ui_table","z":"fcce0e6bead574e7","group":"c1460b15f679e676","name":"","order":4,"width":9,"height":5,"columns":[],"outputs":0,"cts":false,"x":1630,"y":420,"wires":[]},{"id":"b344e00afda936d9","type":"ui_button","z":"fcce0e6bead574e7","name":"","group":"c1460b15f679e676","order":3,"width":3,"height":1,"passthru":false,"label":"刪除所有資料 ","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"刪除所有資料 ","payloadType":"str","topic":"topic","topicType":"msg","x":1000,"y":540,"wires":[["6f036d83ed92ce38","27477e6cbb0d1ab0"]]},{"id":"b57e0d922b95bf93","type":"function","z":"fcce0e6bead574e7","name":"DELETE ALL DATA","func":"//CREATE TABLE RFIDUID (\n//id INTEGER,\n//UID TEXT,\n//Date DATE,\n//Time TIME,\n//PRIMARY KEY (id)\n//);\n//CREATE TABLE LEDSTATUS (id INTEGER,UID TEXT,Date DATE,Time TIME,PRIMARY KEY (id));\nmsg.topic = \"DELETE from RFIDUID\";\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1570,"y":540,"wires":[["6a6d7897c3cfb419"]]},{"id":"6f036d83ed92ce38","type":"ui_toast","z":"fcce0e6bead574e7","position":"prompt","displayTime":"3","highlight":"","sendall":true,"outputs":1,"ok":"OK","cancel":"Cancel","raw":true,"className":"","topic":"","name":"","x":1190,"y":540,"wires":[["a0ef9725fe77f51f"]]},{"id":"a0ef9725fe77f51f","type":"function","z":"fcce0e6bead574e7","name":"OK or Cancel","func":"var topic=msg.payload;\nif (topic==\"\"){\n    return [msg,null];\n    \n}\nif (topic==\"Cancel\"){\n    return [null,msg];\n    \n}\nreturn msg;","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1360,"y":540,"wires":[["b57e0d922b95bf93"],[]]},{"id":"27477e6cbb0d1ab0","type":"ui_audio","z":"fcce0e6bead574e7","name":"","group":"c1460b15f679e676","voice":"Microsoft Hanhan - Chinese (Traditional, Taiwan)","always":false,"x":1145,"y":440,"wires":[],"l":false},{"id":"8601544629976bf3","type":"link out","z":"fcce0e6bead574e7","name":"link out 14","mode":"link","links":["5673b5e03b67335b"],"x":1745,"y":480,"wires":[]},{"id":"5673b5e03b67335b","type":"link in","z":"fcce0e6bead574e7","name":"link in 17","links":["8601544629976bf3"],"x":1815,"y":480,"wires":[["5ba82d08c111b413"]]},{"id":"2e1dafb86b158821","type":"mqtt in","z":"fcce0e6bead574e7","name":"RFID in","topic":"alex9ufo/esp32/RFID","qos":"1","datatype":"auto-detect","broker":"584db2f88f8050c2","nl":false,"rap":true,"rh":0,"inputs":0,"x":970,"y":340,"wires":[["1d13d8d27746f41f","8f883ca3e2e45fba","2175a631874bfc3c","27477e6cbb0d1ab0"]]},{"id":"d75616872c411e2a","type":"sqlite","z":"fcce0e6bead574e7","mydb":"f5c97c74cc496505","sqlquery":"msg.topic","sql":"","name":"RFID_2025EX2","x":1460,"y":420,"wires":[["0125ad9a2c641256"]]},{"id":"a2d7d7399a152e7d","type":"sqlite","z":"fcce0e6bead574e7","mydb":"f5c97c74cc496505","sqlquery":"msg.topic","sql":"","name":"RFID_2025EX2","x":1420,"y":480,"wires":[["ce776f39256a83d6"]]},{"id":"6a6d7897c3cfb419","type":"sqlite","z":"fcce0e6bead574e7","mydb":"f5c97c74cc496505","sqlquery":"msg.topic","sql":"","name":"RFID_2025EX2","x":1760,"y":540,"wires":[["8601544629976bf3"]]},{"id":"8f883ca3e2e45fba","type":"debug","z":"fcce0e6bead574e7","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.uid","targetType":"msg","statusVal":"","statusType":"auto","x":1140,"y":300,"wires":[]},{"id":"4187189e16b0375c","type":"ui_audio","z":"fcce0e6bead574e7","name":"","group":"c1460b15f679e676","voice":"Microsoft Hanhan - Chinese (Traditional, Taiwan)","always":true,"x":105,"y":340,"wires":[],"l":false},{"id":"2175a631874bfc3c","type":"function","z":"fcce0e6bead574e7","name":"增加 日期 時間","func":"var Today = new Date();\nvar yyyy = Today.getFullYear(); //年\nvar MM = Today.getMonth()+1;    //月\nvar dd = Today.getDate();       //日\nvar h = Today.getHours();       //時\nvar m = Today.getMinutes();     //分\nvar s = Today.getSeconds();     //秒\nif(MM<10)\n{\n   MM = '0'+MM;\n}\n\nif(dd<10)\n{\n   dd = '0'+dd;\n}\n\nif(h<10)\n{\n   h = '0'+h;\n}\n\nif(m<10)\n{\n  m = '0' + m;\n}\n\nif(s<10)\n{\n  s = '0' + s;\n}\nvar var_date = yyyy+'/'+MM+'/'+dd;\nvar var_time = h+':'+m+':'+s;\n\nvar myUID = msg.payload.uid;\n\n\nmsg.payload=\"新增一筆:\"+ myUID +\", 日期: \"+ var_date +\", 時間:\"+var_time;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1260,"y":260,"wires":[["2390120793a57c4c"]]},{"id":"feaf4daafd32f27d","type":"comment","z":"fcce0e6bead574e7","name":"LINE  Messaging API","info":"","x":1290,"y":220,"wires":[]},{"id":"2390120793a57c4c","type":"function","z":"fcce0e6bead574e7","name":"LINE  Messaging API","func":"//CHANNEL_ACCESS_TOKEN = 'Messaging API Token';\nCHANNEL_ACCESS_TOKEN = 'E2ElxCQeoDszSHF0N1TJsyytnnae+HF1XZ733YVYj1RQcpjAT1oi2Fa2TScNbOz+q815fFVp4MuFW+UC4PsfKbvtc/o0OM6hchpxuQURZyvizGq+/cAz/TgCwJ9wQ4ebkQyEXEDzaYUZ/Dx3yaaBgwdB04t89/1O/w1cDnyilFU=';\nUSER_ID = 'Ua2f646d82a75d31dee884a6fdfa95f76'; //'使用者ID(不是Line ID)';\n\nvar msg1=msg.payload;\n\nmessage = {\n    type:'text',\n    text:'Line Developers傳送的訊息:'+msg1\n};\n\n\nheaders = {\n    'Content-Type': 'application/json; charset=UTF-8',\n    'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,\n};\npayload = {\n    'to':  USER_ID,\n    'messages': [message]\n};\nmsg.headers = headers;\nmsg.payload = payload;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1480,"y":260,"wires":[["5d4c661a5d375779"]]},{"id":"5d4c661a5d375779","type":"http request","z":"fcce0e6bead574e7","name":"LINE Developers Messaging API 傳送","method":"POST","ret":"txt","paytoqs":"ignore","url":"https://api.line.me/v2/bot/message/push","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1650,"y":300,"wires":[["f9cb0f54c0825f98"]]},{"id":"f9cb0f54c0825f98","type":"debug","z":"fcce0e6bead574e7","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1930,"y":300,"wires":[]},{"id":"7376e0c4dc8cc058","type":"ui_group","name":"LED控制","tab":"82f3819207ab5853","order":1,"disp":true,"width":6,"collapse":false,"className":""},{"id":"584db2f88f8050c2","type":"mqtt-broker","name":"broker.mqttgo.io","broker":"broker.mqttgo.io","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"c1460b15f679e676","type":"ui_group","name":"RFID 資料","tab":"82f3819207ab5853","order":2,"disp":true,"width":9,"collapse":false,"className":""},{"id":"f5c97c74cc496505","type":"sqlitedb","db":"2025EX2_RFID.db","mode":"RWC"},{"id":"82f3819207ab5853","type":"ui_tab","name":"WOKWI RFID","icon":"dashboard","order":142,"disabled":false,"hidden":false}]


LINE Developers 說明



Node-Red  <<LINE Notify + LINE Developers



SQLite 資料庫的檢視












手機 Line 通知畫面

 



Node-Red 節點說明









節點圖示

說明

LED控制 Node-red發行到 WOkWI ESP32 的部分





On Payloadled1on

Off Payloadled1off

On Payloadled2on

Off Payloadled2off

On Payloadled3on

Off Payloadled3off

On Payloadled4on

Off Payloadled4off



Payload : ledallon

 

Payload : ledalloff



MQTT out 節點

服務端 : broker.mqttgo.io : 1883

主題 :alex9ufo/esp32/led

LED接收  Node-red訂閱  WOkWI ESP32發行的 部分



MQTT in 節點

服務端 : broker.mqttgo.io : 1883

主題 : alex9ufo/esp32/led_status

 




LED Dispatcher 功能

輸出要設定10

 

// 建立一個陣列來裝每個輸出的訊息,預設都為 null

let outputs = Array(10).fill(null);

 

// 根據 payload 決定要送出的輸出端口 (0~9)

switch (msg.payload) {

    case "LED1ON":

        outputs[0] = msg;

        break;

    case "LED1OFF":

        outputs[1] = msg;

        break;

    case "LED2ON":

        outputs[2] = msg;

        break;

    case "LED2OFF":

        outputs[3] = msg;

        break;

    case "LED3ON":

        outputs[4] = msg;

        break;

    case "LED3OFF":

        outputs[5] = msg;

        break;

    case "LED4ON":

        outputs[6] = msg;

        break;

    case "LED4OFF":

        outputs[7] = msg;

        break;

    case "LEDALLON":

        outputs[8] = msg;

        break;

    case "LEDALLOFF":

        outputs[9] = msg;

        break;

    default:

        return null;

}

 

// 回傳包含 10 個輸出的陣列

return outputs;



LED1ON 功能內程式

if (msg.payload === "LED1ON") {

    msg.payload = true;

    return msg;

} else {

    return null;

}

 

 

LED1OFF 功能內程式

 

if (msg.payload === "LED1OFF") {

    msg.payload = false;

    return msg;

} else {

    return null;

}

 

其餘依此列推

 

if (msg.payload === "LEDALLON") {

    msg.payload = true;

    return msg;

} else {

    return null;

}

 

if (msg.payload === "LEDALLOFF") {

    msg.payload = false;

    return msg;

} else {

    return null;

}



 

設定顏色

true: green

false: red

RFID 資料庫 與 Line Message API 部分  訂閱WOKWI 模擬RFID送出UID碼 暨 發出Line通知



MQTT in 節點

服務端 : broker.mqttgo.io : 1883

主題 :  alex9ufo/esp32/RFID

 



增加 日期 時間 功能程式

var Today = new Date();

var yyyy = Today.getFullYear(); //

var MM = Today.getMonth()+1;    //

var dd = Today.getDate();       //

var h = Today.getHours();       //

var m = Today.getMinutes();     //

var s = Today.getSeconds();     //

if(MM<10)

{

   MM = '0'+MM;

}

if(dd<10)

{

   dd = '0'+dd;

}

if(h<10)

{

   h = '0'+h;

}

 

if(m<10)

{

  m = '0' + m;

}

 

if(s<10)

{

  s = '0' + s;

}

var var_date = yyyy+'/'+MM+'/'+dd;

var var_time = h+':'+m+':'+s;

 

var myUID = msg.payload.uid;

 

 

msg.payload="新增一筆:"+ myUID +", 日期: "+ var_date +", 時間:"+var_time;

return msg;



LINE  Messaging API 功能程式

需修成成自己的CHANNEL_ACCESS_TOKEN USER_ID

 

 

//CHANNEL_ACCESS_TOKEN = 'Messaging API Token';

CHANNEL_ACCESS_TOKEN = 'E2ElxCQeoDszSHF0N1TJsyytnnae+HF1XZ733YVYj1RQcpjAT1oi2Fa2TScNbOz+q815fFVp4MuFW+UC4PsfKbvtc/o0OM6hchpxuQURZyvizGq+/cAz/TgCwJ9wQ4ebkQyEXEDzaYUZ/Dx3yaaBgwdB04t89/1O/w1cDnyilFU=';

USER_ID = 'Ua2f646d82a75d31dee884a6fdfa95f76'; //'使用者ID(不是Line ID)';

 

var msg1=msg.payload;

 

message = {

    type:'text',

    text:'Line Developers傳送的訊息:'+msg1

};

 

 

headers = {

    'Content-Type': 'application/json; charset=UTF-8',

    'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,

};

payload = {

    'to':  USER_ID,

    'messages': [message]

};

msg.headers = headers;

msg.payload = payload;

return msg;

 




 




http request節點

 

請求方式 : POST

URL : https://api.line.me/v2/bot/message/push

新增一筆資料的INSERT功能程式

 

var Today = new Date();

var yyyy = Today.getFullYear(); //

var MM = Today.getMonth()+1;    //

var dd = Today.getDate();       //

var h = Today.getHours();       //

var m = Today.getMinutes();     //

var s = Today.getSeconds();     //

if(MM<10)

{

   MM = '0'+MM;

}

 

if(dd<10)

{

   dd = '0'+dd;

}

 

if(h<10)

{

   h = '0'+h;

}

 

if(m<10)

{

  m = '0' + m;

}

 

if(s<10)

{

  s = '0' + s;

}

var var_date = yyyy+'/'+MM+'/'+dd;

var var_time = h+':'+m+':'+s;

 

var myUID = msg.payload.uid;

 

 

msg.topic = "INSERT INTO RFIDUID ( UID , Date , Time ) VALUES ($myUID,  $var_date ,  $var_time ) " ;

msg.payload = [myUID, var_date , var_time ]

return msg;

 

 

//INSERT INTO RFIDUID (

//id INTEGER,

//UID TEXT,

//Date DATE,

//Time TIME,

//PRIMARY KEY (id)

//);


 

名稱 :  RFID_2025EX2

 Database :  2025EX2_RFID.db  <<Read-Write-Create>>

存放位置 : C:\Users\User\ 2025EX2_RFID.db

(第一次執行node-red 要先將 資料庫 C:\Users\User\ 2025EX2_RFID.db 建立  按圖示 <<建立RFID資料庫>>

 




檢視資料 節點功能

 

//INSERT INTO RFIDUID (

//id INTEGER,

//UID TEXT,

//Date DATE,

//Time TIME,

//PRIMARY KEY (id)

//);

 

//SELECT * FROM RFIDUID ORDER BY  id DESC LIMIT 50;

 

msg.topic = "SELECT * FROM RFIDUID ORDER BY id DESC LIMIT 50";

return msg;


CREATE DATABASE 功能節點程式

 

//CREATE TABLE RFIDUID (

//id INTEGER,

//UID TEXT,

//Date DATE,

//Time TIME,

//PRIMARY KEY (id)

//);

//CREATE TABLE LEDSTATUS (id INTEGER,UID TEXT,Date DATE,Time TIME,PRIMARY KEY (id));

msg.topic = "CREATE TABLE RFIDUID (id INTEGER,UID TEXT,Date DATE,Time TIME,PRIMARY KEY (id))";

return msg;


DELETE ALL DATA功能節點程式

 

//CREATE TABLE RFIDUID (

//id INTEGER,

//UID TEXT,

//Date DATE,

//Time TIME,

//PRIMARY KEY (id)

//);

//CREATE TABLE LEDSTATUS (id INTEGER,UID TEXT,Date DATE,Time TIME,PRIMARY KEY (id));

msg.topic = "DELETE from RFIDUID";

return msg;

 


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}]

Node-Red 抓取 opendata 的資料集產生AQI 空氣品質指

Node-Red 抓取 opendata 的資料集產生AQI 空氣品質指標 實現透過 Node-RED 抓取環境部 OpenData(空氣品質 AQI)資料,最核心的流程為: 注入觸發(Inject) → HTTP 請求(http request) → 解析 JSON 資料(Fu...