ดาวน์โหลดงานนำเสนอ
งานนำเสนอกำลังจะดาวน์โหลด โปรดรอ
ได้พิมพ์โดยPhattana Mulmanee ได้เปลี่ยน 3 ปีที่แล้ว
1
https://bit.ly/iot_with_nodered
IoT with Node-RED
2
Agenda Day 1 Internet of Thing Node with ESP32 MQTT Broker Day 2
Node-RED Day 3 Dashboard LoRaWAN
3
Internet of Things
4
1.1 ยุคต่าง ๆ ของอุปกรณ์ที่เชื่อมต่ออินเตอร์เน็ต
คอมพิวเตอร์ส่วนบุคคล (PC) คอมพิวเตอร์โน้ตบุ๊ค (Laptop) โทรศัพท์เคลื่อนที่ (Mobile) อุปกรณ์ฝังตัว/ไมโครคอนโทรลเลอร์ (Embedded/Microcontroller)
5
1.2 รูปแบบการเชื่อมต่ออินเตอร์เน็ต
ผ่านเครือข่ายโทรศัพท์เคลื่อนที่ 3G/4G NB-IoT ผ่านเครือข่ายผู้ให้บริการ LoRaWAN SixFox ผ่านสัญญาณ WiFi / Bluetooth ต้องมี Gateway เพื่อเชื่อมต่ออินเตอร์เน็ต
6
1.3 เปรียบเทียบการเชื่อมต่ออินเตอร์เน็ต
WiFi ระยะไม่เกิน 100m รับส่งข้อมูลขนาดใหญ่ได้ ราคาถูก ตั้ง Gateway เองได้ LoRa/LoRaWAN ระยะ 10Km ยิ่งไกลยิ่งส่งข้อมูลได้น้อย ราคาปานกลาง ตั้ง Gateway เองได้ NB-IoT ระยะ 10 Km ส่งข้อมูลได้มากว่า LoRa แต่น้อยกว่า WiFi ราคาสูง มีค่าบริการรายเดือน ตั้ง Gateway เองไม่ได้
7
2. Node with ESP32
8
2.1 ESP32 & ESP8266
9
2.2 Arduino IDE Installing using Board Manager
File > Preferences > Additional Board Manager URLs Stable Development EP8266 Tools > Board Manager > ESP32, ESP8266
10
2.3 WiFi Connection Non-Blocking WiFi Connection
void wifi_event(WiFiEvent_t event){ if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) { Serial.print("WIFI: Connected"); } else if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { Serial.println("WIFI: Lost Connection"); } } void setup() { WiFi.disconnect(true); WiFi.onEvent(wifi_event); WiFi.begin(wifi_ssid, wifi_pass); }
11
2.3 WiFi Connection (Cont.)
Use WiFiMulti.h #ifdef ESP #include <WiFi.h> #include <WiFiMulti.h> WiFiMulti wifiMulti; #else // ESP #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; #endif void setup() { wifiMulti.addAP("ssid1", "psk1"); wifiMulti.addAP("ssid2", "psk2"); } void loop() { wifiMulti.run(); } #ifdef ESP32 #include <WiFi.h> #include <WiFiMulti.h> WiFiMulti wifiMulti; #elif ESP8266 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; #define ARDUINO_EVENT_WIFI_STA_GOT_IP WIFI_EVENT_STAMODE_GOT_IP #define ARDUINO_EVENT_WIFI_STA_DISCONNECTED WIFI_EVENT_STAMODE_DISCONNECTED #endif void wifi_event(WiFiEvent_t event){ if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) { Serial.println("WIFI: Connected"); // WIFI_EVENT_STAMODE_GOT_IP } else if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { Serial.println("WIFI: Lost Connection"); } void setup() { Serial.begin(115200); WiFi.onEvent(wifi_event); wifiMulti.addAP("xenex-ap", " "); void loop() { wifiMulti.run();
12
2.4 MQTT Connection Use PubSubClient (Nick O'Leary version 2.8.0)
#include <WiFi.h> #include <WiFiClient.h> #include <PubSubClient.h> WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); void mqtt_callback(char* topic, byte* payload, unsigned int len) { // TODO: process downlink here }
13
2.4 MQTT Connection (Cont.)
void setup() { mqttClient.setBufferSize(2000); mqttClient.setServer(mqtt_server, mqtt_port); mqttClient.setCallback(mqtt_callback); } void loop() { if (!mqttClient.connected()) { if (mqttClient.connect(mqtt_id, mqtt_user, mqtt_pass)) { // TODO: subscribe } } else { mqttClient.loop(); } }
14
Workshop 2.1 ESP32 with MQTT Use WiFiMulti to connect to access point
Try to reconnect when lost connection Use PubSubClient to connect to MQTT MQTT host=mqtt.itdevclub.com port=1883 user=demo pass=demo Check WiFi connection status Add delay between MQTT reconnection Subscribe for topic "node/0999/cmd" Publish every 5s topic = "node/0999/data" payload = "{\"name\":\"Somsak\"}" ////////////////////////////////////////// // WIFI #ifdef ESP32 #include <WiFi.h> #include <WiFiMulti.h> WiFiMulti wifiMulti; #elif ESP8266 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; #define ARDUINO_EVENT_WIFI_STA_GOT_IP WIFI_EVENT_STAMODE_GOT_IP #define ARDUINO_EVENT_WIFI_STA_DISCONNECTED WIFI_EVENT_STAMODE_DISCONNECTED #endif void wifi_event(WiFiEvent_t event){ if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) { Serial.println("WIFI: Connected"); // WIFI_EVENT_STAMODE_GOT_IP } else if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { Serial.println("WIFI: Lost Connection"); } void wifi_setup() { WiFi.onEvent(wifi_event); wifiMulti.addAP("xenex-ap", " "); void wifi_loop() { wifiMulti.run(); // MQTT #include <PubSubClient.h> WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); unsigned long mqtt_uplink_timer = 0; char mqtt_server[64] = "mqtt.itdevclub.com"; uint16_t mqtt_port = 1883; char mqtt_id[64] = "id_sak"; char mqtt_user[64] = "demo"; char mqtt_pass[64] = "demo"; void mqtt_callback(char* topic, byte* payload, unsigned int len) { // TODO: process downlink here Serial.print("MQTT: topic="); Serial.println(topic); Serial.print("MQTT: payload="); payload[len] = 0; Serial.println((char*)payload); void mqtt_setup() { mqttClient.setBufferSize(2000); mqttClient.setServer(mqtt_server, mqtt_port); mqttClient.setCallback(mqtt_callback); void mqtt_loop() { if (!mqttClient.connected()) { if (mqttClient.connect(mqtt_id, mqtt_user, mqtt_pass)) { Serial.println("MQTT: Connected"); mqttClient.subscribe("node/+/cmd"); } else { if (millis() - mqtt_uplink_timer > 5000) { Serial.println("MQTT: Publish"); mqttClient.publish("node/0001/data", "Somsak Sae-Lim"); mqtt_uplink_timer = millis(); mqttClient.loop(); // MAIN void setup() { Serial.begin(115200); wifi_setup(); mqtt_setup(); void loop() { wifi_loop(); mqtt_loop();
15
2.5 Configuration Use struct as configuration
#ifdef ESP #include <SPIFFS.h> #elif ESP #include <FS.h> #endif typedef struct { char wifi_ssid[64]; char wifi_pass[64]; } config_t; config_t cfg;
16
void setup() { strcpy(cfg. wifi_ssid, "ssid1"); strcpy(cfg
void setup() { strcpy(cfg.wifi_ssid, "ssid1"); strcpy(cfg.wifi_pass, "pass1"); SPIFFS.begin(true); // Format on failed if (SPIFFS.exists("/config.dat")) { File cfg_file = SPIFFS.open("/config.dat", "rb"); // binary Serial.println("CFG: READ CONFIG"); cfg_file.read((uint8_t *)&cfg, sizeof(cfg)); cfg_file.close(); } else { Serial.println("CFG: CREATE CONFIG"); File cfg_file = SPIFFS.open("/config.dat", "wb"); // binary cfg_file.write((uint8_t *)&cfg, sizeof(cfg)); cfg_file.close(); } }
17
2.5 Configuration Use ArduinoJson as configuration
Should use with struct to improve performance Save to file DynamicJsonDocument doc(1024); doc["sensor"] = "gps"; doc["time"] = ; doc["data"][0] = ; doc["data"][1] = ; File file = SPIFFS.open(filename, "w"); serializeJson(doc, file); file.close();
18
Load from file File file = SPIFFS.open(filename, "r"); DynamicJsonDocument doc(1024); deserializeJson(doc, file); file.close(); const char* sensor = doc["sensor"]; long time = doc["time"]; double latitude = doc["data"][0]; double longitude = doc["data"][1];
19
Workshop 2.2 ESP32 Configuration
Create struct config_t typedef struct { char wifi_ssid[64]; char wifi_pass[64]; char mqtt_server[64]; uint16_t mqtt_port; char mqtt_user[64]; char mqtt_pass[64]; uint16_t upload_interval = 5; // second } config_t; Create function to load and save config (binary or JSON) void config_load(config_t &cfg, char *filename) void config_save(config_t &cfg, char *filename) Subscribe topic node/XXXX/cmd for new configuration payload="{\"cfg\":{\"uplink_interval\":7000}}" Save new configuration and ESP.restart() char CFG_FILE[64] = "/config.dat"; typedef struct { char wifi_ssid[64] = "xenex-ap"; char wifi_pass[64] = " "; char mqtt_server[64] = "mqtt.itdevclub.com"; uint16_t mqtt_port = 1883; char mqtt_user[64] = "demo"; char mqtt_pass[64] = "demo"; uint16_t upload_interval = 15; // second } config_t; config_t cfg; ////////////////////////////////////////// // WIFI #ifdef ESP32 #include <WiFi.h> #include <WiFiMulti.h> WiFiMulti wifiMulti; #else #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; #define ARDUINO_EVENT_WIFI_STA_GOT_IP WIFI_EVENT_STAMODE_GOT_IP #define ARDUINO_EVENT_WIFI_STA_DISCONNECTED WIFI_EVENT_STAMODE_DISCONNECTED #endif void wifi_event(WiFiEvent_t event){ if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) { Serial.println("WIFI: Connected"); // WIFI_EVENT_STAMODE_GOT_IP } else if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { Serial.println("WIFI: Lost Connection"); } void wifi_setup() { WiFi.onEvent(wifi_event); wifiMulti.addAP(cfg.wifi_ssid, cfg.wifi_pass); void wifi_loop() { wifiMulti.run(); // MQTT #include <PubSubClient.h> WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); unsigned long mqtt_uplink_timer = 0; char mqtt_id[64] = "id_saknarak"; void mqtt_callback(char* topic, byte* payload, unsigned int len) { // TODO: process downlink here Serial.print("MQTT: topic="); Serial.println(topic); Serial.print("MQTT: payload="); payload[len] = 0; Serial.println((char*)payload); // node/1234/cmd // node/1234/cfg int t_len = strlen(topic); if (topic[t_len - 4] == '/' && topic[t_len - 3] == 'c' && topic[t_len - 2] == 'f' && topic[t_len - 1] == 'g') { cfg.upload_interval = atoi((char*)payload); config_save(cfg, CFG_FILE); ESP.restart(); void mqtt_setup() { mqttClient.setBufferSize(2000); mqttClient.setServer(cfg.mqtt_server, cfg.mqtt_port); mqttClient.setCallback(mqtt_callback); void mqtt_loop() { if (!mqttClient.connected()) { if (mqttClient.connect(mqtt_id, cfg.mqtt_user, cfg.mqtt_pass)) { Serial.println("MQTT: Connected"); mqttClient.subscribe("node/+/cmd"); mqttClient.subscribe("node/1234/cfg"); } else { if (millis() - mqtt_uplink_timer > 5000) { Serial.println("MQTT: Publish"); mqttClient.publish("node/1234/data", "Somsak Sae-Lim"); mqtt_uplink_timer = millis(); mqttClient.loop(); // CONFIG #include <SPIFFS.h> void config_load(config_t &cfg, char *filename) { SPIFFS.begin(true); // Format on failed if (!SPIFFS.exists(filename)) { Serial.println("CFG: NO config file found"); return; File cfg_file = SPIFFS.open(filename, "rb"); // binary Serial.println("CFG: LOAD CONFIG"); cfg_file.read((uint8_t *)&cfg, sizeof(cfg)); cfg_file.close(); void config_save(config_t &cfg, char *filename) { Serial.println("CFG: SAVE CONFIG"); File cfg_file = SPIFFS.open(filename, "wb"); // binary cfg_file.write((uint8_t *)&cfg, sizeof(cfg)); void config_setup() { // strcpy(cfg.wifi_ssid, "xenex-ap"); config_load(cfg, CFG_FILE); // print config Serial.print("CFG: upload_interval="); Serial.println(cfg.upload_interval); // MAIN void setup() { Serial.begin(115200); config_setup(); wifi_setup(); mqtt_setup(); void loop() { wifi_loop(); mqtt_loop();
20
Over-The-Air Update Partition Scheme APP + SPIFFS <= 50%
Sketch > Export compiled binary ESP32 ESP8266 ////////////////////////////////////////// // INCLUDE // config #ifdef ESP32 #include <SPIFFS.h> #elif ESP8266 #include <FS.h> #endif // wifi #include <WiFi.h> #include <WiFiMulti.h> WiFiMulti wifiMulti; #else #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti wifiMulti; #define ARDUINO_EVENT_WIFI_STA_GOT_IP WIFI_EVENT_STAMODE_GOT_IP #define ARDUINO_EVENT_WIFI_STA_DISCONNECTED WIFI_EVENT_STAMODE_DISCONNECTED // mqtt #include <PubSubClient.h> // ota #include <Update.h> #include <ESP8266httpUpdate.h> // DEFINE // #define NODE_ID "1234" #define MY_NAME "Somchai" #define CFG_FILE "/config.dat" // GLOBAL VARIABLES typedef struct { char wifi_ssid[64] = "xenex-ap"; char wifi_pass[64] = " "; char mqtt_server[64] = "mqtt.itdevclub.com"; uint16_t mqtt_port = 1883; char mqtt_user[64] = "demo"; char mqtt_pass[64] = "demo"; uint16_t upload_interval = 15; // second } config_t; config_t cfg; WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); unsigned long mqtt_uplink_timer = 0; char mqtt_id[64] = "mqtt_id"; // CONFIG void config_load(config_t &cfg, char *filename) { SPIFFS.begin(true); // Format on failed SPIFFS.begin(); // format on failed if (!SPIFFS.exists(filename)) { Serial.println("CFG: NO config file found"); return; } File cfg_file = SPIFFS.open(filename, "rb"); // binary Serial.println("CFG: LOAD CONFIG"); cfg_file.read((uint8_t *)&cfg, sizeof(cfg)); cfg_file.close(); void config_save(config_t &cfg, char *filename) { Serial.println("CFG: SAVE CONFIG"); File cfg_file = SPIFFS.open(filename, "wb"); // binary cfg_file.write((uint8_t *)&cfg, sizeof(cfg)); void config_setup() { config_load(cfg, (char *)CFG_FILE); //strcpy(cfg.wifi_pass, " "); config_save(cfg, (char *)CFG_FILE); // print config Serial.print("CFG: upload_interval="); Serial.println(cfg.upload_interval); Serial.print("CFG: wifi_ssid="); Serial.println(cfg.wifi_ssid); Serial.print("CFG: wifi_pass="); Serial.println(cfg.wifi_pass); // WIFI void wifi_setup() { WiFi.disconnect(); WiFi.onEvent(wifi_event); wifiMulti.addAP(cfg.wifi_ssid, cfg.wifi_pass); void wifi_loop() { wifiMulti.run(); void wifi_event(WiFiEvent_t event) { if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) { Serial.println("WIFI: Connected"); // WIFI_EVENT_STAMODE_GOT_IP } else if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { Serial.println("WIFI: Lost Connection"); // MQTT void mqtt_setup() { mqttClient.setBufferSize(2000); mqttClient.setServer(cfg.mqtt_server, cfg.mqtt_port); mqttClient.setCallback(mqtt_callback); uint64_t efuseMac = ESP.getEfuseMac(); sprintf(mqtt_id, "mqtt_%02X%02X%02X", (efuseMac >> 40) & 0xff, (efuseMac >> 32) & 0xff, (efuseMac >> 24) & 0xff ); void mqtt_loop() { if (!mqttClient.connected()) { if (mqttClient.connect(mqtt_id, cfg.mqtt_user, cfg.mqtt_pass)) { Serial.println("MQTT: Connected"); mqttClient.subscribe("node/0000/cmd"); char topic[64]; sprintf(topic, "node/%s/cmd", NODE_ID); mqttClient.subscribe(topic); sprintf(topic, "node/%s/cfg", NODE_ID); } else { if (millis() - mqtt_uplink_timer > 5000) { Serial.println("MQTT: Publish"); sprintf(topic, "node/%s/data", NODE_ID); mqttClient.publish(topic, MY_NAME); mqtt_uplink_timer = millis(); mqttClient.loop(); void mqtt_callback(char* topic, byte* payload, unsigned int len) { // TODO: process downlink here Serial.print("MQTT: topic="); Serial.println(topic); Serial.print("MQTT: payload="); payload[len] = 0; Serial.println((char*)payload); // node/1234/cmd // node/1234/cfg int t_len = strlen(topic); if (topic[t_len - 4] == '/' && topic[t_len - 3] == 'c' && topic[t_len - 2] == 'f' && topic[t_len - 1] == 'g') { cfg.upload_interval = atoi((char*)payload); ESP.restart(); } else if (topic[t_len - 4] == '/' && topic[t_len - 3] == 'c' && topic[t_len - 2] == 'm' && topic[t_len - 1] == 'd') { // "{ "config": {}, "ota": {}, "restart": 1 }" char path[64]; sprintf(path, "/firmware/%s.bin", NODE_ID); ota_run("ota.itdevclub.com", 80, path); // OTA WiFiClient otaClient; String ota_getHeaderValue(String header, String headerName) { return header.substring(strlen(headerName.c_str())); void ota_run(char *host, uint16_t port, char *path) { #ifdef ESP8266 ESPhttpUpdate.update(otaClient, host, port, path); #elif ESP32 long contentLength = 0; bool isValidContentType = false; Serial.print("Connecting to: "); Serial.println(host); // Connect to S3 if (otaClient.connect(host, port)) { // Connection Succeed. // Fecthing the bin Serial.println("Fetching Bin: " + String(path)); // Get the contents of the bin file otaClient.print(String("GET ") + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n\r\n"); // Check what is being sent // Serial.print(String("GET ") + bin + " HTTP/1.1\r\n" + // "Host: " + host + "\r\n" + // "Cache-Control: no-cache\r\n" + // "Connection: close\r\n\r\n"); unsigned long timeout = millis(); while (otaClient.available() == 0) { if (millis() - timeout > 5000) { Serial.println("Client Timeout !"); otaClient.stop(); // Once the response is available, // check stuff /* Response Structure HTTP/ OK x-amz-id-2: NVKxnU1aIQMmpGKhSwpCBh8y2JPbak18QLIfE+OiUDOos+7UftZKjtCFqrwsGOZRN5Zee0jpTd0= x-amz-request-id: 2D56B47560B764EC Date: Wed, 14 Jun :33:59 GMT Last-Modified: Fri, 02 Jun :50:11 GMT ETag: "d2afebbaaebc38cd669ce af9" Accept-Ranges: bytes Content-Type: application/octet-stream Content-Length: Server: AmazonS3 {{BIN FILE CONTENTS}} */ while (otaClient.available()) { // read line till /n String line = otaClient.readStringUntil('\n'); // remove space, to check if the line is end of headers line.trim(); // if the the line is empty, // this is end of headers // break the while and feed the // remaining `client` to the // Update.writeStream(); if (!line.length()) { //headers ended break; // and get the OTA started // Check if the HTTP Response is 200 // else break and Exit Update if (line.startsWith("HTTP/1.1")) { if (line.indexOf("200") < 0) { Serial.println("Got a non 200 status code from server. Exiting OTA Update."); break; // extract headers here // Start with content length if (line.startsWith("Content-Length: ")) { contentLength = atol((ota_getHeaderValue(line, "Content-Length: ")).c_str()); Serial.println("Got " + String(contentLength) + " bytes from server"); // Next, the content type if (line.startsWith("Content-Type: ")) { String contentType = ota_getHeaderValue(line, "Content-Type: "); Serial.println("Got " + contentType + " payload."); if (contentType == "application/octet-stream") { isValidContentType = true; // Connect to S3 failed // May be try? // Probably a choppy network? Serial.println("Connection to " + String(host) + " failed. Please check your setup"); // retry?? // execOTA(); // Check what is the contentLength and if content type is `application/octet-stream` Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType)); // check contentLength and content type if (contentLength && isValidContentType) { // Check if there is enough to OTA Update bool canBegin = Update.begin(contentLength); // If yes, begin if (canBegin) { Serial.println("Begin OTA. This may take mins to complete. Things might be quite for a while.. Patience!"); // No activity would appear on the Serial monitor // So be patient. This may take 2 - 5mins to complete size_t written = Update.writeStream(otaClient); if (written == contentLength) { Serial.println("Written : " + String(written) + " successfully"); Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?" ); if (Update.end()) { Serial.println("OTA done!"); if (Update.isFinished()) { Serial.println("Update successfully completed. Rebooting."); Serial.println("Update not finished? Something went wrong!"); Serial.println("Error Occurred. Error #: " + String(Update.getError())); // not enough space to begin OTA // Understand the partitions and // space availability Serial.println("Not enough space to begin OTA"); otaClient.flush(); Serial.println("There was no content in the response"); // MAIN void setup() { Serial.begin(115200); Serial.println("VERION 1.2"); config_setup(); wifi_setup(); mqtt_setup(); void loop() { wifi_loop(); mqtt_loop();
21
ESP Tools Exception Decoder Plugin
FS Upload Plugin ESP32 ESP8266 Flash Download Tools
22
3. MQTT Broker
23
3.1 Publish/Subscribe Publish คือการเผยแพร่ข้อมูล ประกอบด้วย
topic หัวเรื่อง payload ข้อมูล Subscribe คือการบอกรับข้อมูล ต้องระบุ topic ที่ต้องการ
24
3.2 MQTT Broker Eclipse Mosquitto Mosca (Node.js) EMQX HiveMQ
25
3.3 Mosquitto Install https://mosquitto.org/download/ MQTT Client
mosquitto_pub -h server -p u demo -P demo -t topic -m message mosquitto_sub -h server -p u demo -P demo -t topic MQTT Server C:\Program Files\mosquitto\mosquitto.conf allow_anonymous false password_file c:\mosquitto\passwords.txt listener
26
Create password file mosquitto_passwd -c -b passfile user1 pass2 Delete user mosquitto_passwd -D passfile user2
27
Workshop 3.1 Mosquitto server and client
Install mosquitto
28
3.4 EMQX docker-compose.yml see note MySQL mqtt_user sha2('pass', 256)
mqtt_acl access 1 = subscribe 2 = publish 3 = publish + subscribe topic + = 1 level * = any levels services: emqx01: image: emqx/emqx:4.3.0-alpine-amd64 networks: - net - mysql - nginx ports: - "1888:1883" - "8083:8083" - "8883:8883" - "8084:8084" - "18083:18083" - "18084:18084" volumes: - /opt/emqx/share:/opt/share - /opt/emqx/acl.conf:/opt/emqx/etc/acl.conf environment: - EMQX_ACL_NOMATCH=deny - EMQX_ALLOW_ANONYMOUS=false - EMQX_LOADED_PLUGINS=emqx_recon,emqx_retainer,emqx _management,emqx_dashboard,emqx_rule_engine,emqx_ telemetry,emqx_auth_mysql - EMQX_AUTH__MYSQL__SERVER=mysql_mysql01:3306 - EMQX_AUTH__MYSQL__USERNAME=emqx - - EMQX_AUTH__MYSQL__DATABASE=emqx - EMQX_AUTH__MYSQL__POOL=8 - EMQX_AUTH__MYSQL__QUERY_TIMEOUT=5s - EMQX_DASHBOARD__DEFAULT_USER__LOGIN=admin - EMQX_DASHBOARD__DEFAULT_USER__PASSWORD=pass - TZ=Asia/Bangkok
29
Workshop 3.2 EMQX with MySQL Authentication
30
3.5 Public MQTT Brokers
31
4. Node-RED
32
4.1 Node-RED Introduction
Flow-based Programming ออกแบบการไหลของข้อมูล Low Code Programming เขียนน้อยแต่ได้มาก
33
4.2 Installing Node-RED ติดตั้ง Node.js ก่อน https://nodejs.org/en/
พิมพ์คำสั่ง npm i -g node-red เพื่อติดตั้ง Node-RED พิมพ์ node-red เพื่อเริ่มการทำงาน เปิดหน้าเว็บไปที่
34
Workshop 4.1 Installing Node-RED
35
4.3 Message + Node = Flow msg คือ javascript object ที่ถูกส่งเข้า/ออก node ต่าง ๆ แต่ละ Node ต้องการ msg ที่มีข้อมูลแตกต่างกัน แต่ละ Node อาจมีการเปลี่ยนแปลง msg ที่ออกมา topic, payload เป็น key ที่ถูกใช้บ่อยและมักเปลี่ยนแปลงเมื่อเข้าและออกจาก Node จึงไม่ควรใช้เก็บข้อมูล Node คือหน่วยประมวลผล Message มี 0-1 input มี 0-N output เพิ่มและตั้งชื่อได้
36
Node ใน Node-RED
37
4.4 MQTT Nodes
38
4.2 Workshop MQTT Publish and Subscribe
39
4.5 MySQL Nodes
40
Workshop 4.3 Collect telemetry data with MySQL
41
4.6 HTTP Nodes
42
4.7 Workshop: LINE Notification
43
5. Dashboard
44
5.1 Node-RED Dashboard
45
5.2 Chart
46
5.3 Control
47
Workshop 5.1 Node-RED Dashboard
48
5.4 Grafana
49
5.5 Dashboard with Grafana
50
Workshop 5.2 Dashboard with Grafana
51
5.6 ThingsBoard thingsboard.io
52
6. LoRaWAN
53
6.1 Introduction to LoRa WAN
54
6.2 LoRaWAN Nodes Class A Class B Class C
55
6.3 LoRaWAN Gateways Dragino RAK
56
6.4 ChirpStack Open-source LoRaWAN Network Server stack
Architecture
57
6.7 LoRaWAN Demo
งานนำเสนอที่คล้ายกัน
© 2025 SlidePlayer.in.th Inc.
All rights reserved.