## 參考文獻 [Node-Red Publish MQTT Server](https://hackmd.io/@HugoPeng/Sk6tsnp9A) ## 系統架構 ![架構](https://hackmd.io/_uploads/HyAGgdyo0.jpg) ## Wifi 環境確認 獲取Windows Wifi Slave IPv4位置 ![wifi ip](https://hackmd.io/_uploads/Bk7z-_kiR.png) ## Node-Red 架設 ![node-red flow chart](https://hackmd.io/_uploads/H1JIedksC.png) ### broker server 設定 MQTT Server PC IPv4位置,PORT設為1883。 ![node-red broker server setting](https://hackmd.io/_uploads/SJwhWOkjR.png) ### mqtt in * Humedity,Topic(主題)為ESP32發佈的識別名。 ![mqtt in humedity](https://hackmd.io/_uploads/SyGFZOkiR.png) * Temperature,Topic(主題)為ESP32發佈的識別名。 ![mqtt in temp](https://hackmd.io/_uploads/BJ4SM_yjA.png) ### mqtt out * LED Output,Topic(主題)為ESP32訂閱的識別名。 ![mqtt out led ](https://hackmd.io/_uploads/H1AULd1jA.png) ### dashboard group * 如此專案未使用過,則新建新的group。Name 使用 Default。 ![dash group](https://hackmd.io/_uploads/r1wpQ_yoC.png) ### dashboard tab * 如此專案未使用過,tab。Name 使用 Home。 ![dash tab](https://hackmd.io/_uploads/ByLZEdJiA.png) ### switch On Payload 為 ESP32 接收的資料型態及資料。 ![switch settings](https://hackmd.io/_uploads/HkPr7_ysA.png) ### gauge * Temperature,Range為UI要顯示的資料範圍。 ![gauge temp](https://hackmd.io/_uploads/rkeuVu1j0.png) * Humedity,Range為UI要顯示的資料範圍。 ![gauge humedity](https://hackmd.io/_uploads/rkHj4O1sC.png) ## ESP32程式說明 ### Add PubSubClient Libary ``` [platformio] src_dir = ./ [env:esp32-s3-devkitc-1] platform = espressif32 @ 6.5.0 board = esp32-s3-devkitc-1 framework = arduino board_build.filesystem = littlefs build_flags = -O0 -I board -I lib -I driver -I sensor_module -I canopen/CanFestival -I canopen -I app -DCORE_DEBUG_LEVEL=3 -DARDUINO_USB_CDC_ON_BOOT=1 lib_deps = pierremolinaro/ACAN_ESP32@^1.1.2 khoih-prog/ESP32TimerInterrupt@^2.3.0 ivanseidel/LinkedList@0.0.0-alpha+sha.dac3874d28 bakercp/CRC32@^2.0.0 tkjelectronics/Kalman Filter Library@^1.0.2 denyssene/SimpleKalmanFilter@^0.1.0 michalmonday/CSV Parser@^1.4.1 knolleary/PubSubClient@^2.8 monitor_speed = 921600 monitor_port = COM10 upload_speed = 10000000 upload_port = COM10 debug_tool = esp-builtin debug_init_break = break setup build_type = debug board_build.mcu = esp32s3 board_build.f_cpu = 240000000L ``` ### 範例程式 ``` #include <Arduino.h> #include <PubSubClient.h> #include <LittleFS.h> #include <esp_task_wdt.h> #include "g_sensor.h" #include "console.h" #include "env_server.h" #include "canopen.h" #include "canopen_io.h" #include "reg.h" #include "usb_cdc.h" #include "env_server.h" #include "debug.h" #include "private_protocal.h" #include "hard_switching.h" #include "hard_output.h" #include "led.h" const char *mqtt_server = "192.168.0.111"; WiFiClient espClient; PubSubClient client(espClient); void MonitorTask(void) { // TaskHandle_t task_hankle; // TaskStatus_t task_status; // task_hankle = xTaskGetHandle("GSEN_GetImuFifoTask"); // vTaskGetInfo() } #define SAMPLE_FREQ 1000 void callback(char *topic, byte *message, unsigned int length) { String messageTemp; for (int i = 0; i < length; i++) { Serial.print((char)message[i]); messageTemp += (char)message[i]; // 將接收訊息拷貝至messageTemp } DB_LOG(DLL_INFO, "message arrived, topic: %s, message = %s", topic, messageTemp); // 若主題是esp32/output,則判斷訊息內容,若為”on”,則開啟LED燈,若為”on”,則關閉LED燈 if (String(topic) == "esp32/output") { if (messageTemp == "on") { DB_LOG(DLL_INFO, "on"); LED_SetLed(BL_RED, HIGH); } else if (messageTemp == "off") { DB_LOG(DLL_INFO, "off"); LED_SetLed(BL_RED, LOW); } } } // 重新連線MQTT伺服器函式 void reconnect() { // Loop until we're reconnected while (!client.connected()) { DB_LOG(DLL_INFO, "connecting…"); // 重新連接MQTT伺服器 if (client.connect("ESP32Client")) { DB_LOG(DLL_INFO, "connected"); // 訂閱esp32/output主題 client.subscribe("esp32/output"); } else { DB_LOG(DLL_INFO, "connect failed, status code: %s", client.state()); DB_LOG(DLL_INFO, "connect again in 5 seconds..."); // Wait 5 seconds before retrying delay(5000); } } } void setup() { esp_task_wdt_init(5000, TRUE); delay(3000); // xTaskCreateUniversal((TaskFunction_t)MonitorTask, "MonitorTask", 10240, NULL, osPriorityLow7, NULL, tskNO_AFFINITY); HS_Init(); HO_Init(); LED_Init(); g_UsbCdcHandle.Init(); g_WiFiServerHandle.Init(); g_DebugHandle.Init(); g_PrivatePtotocalHandle.Init(); g_CanOpenHandle.Init(); g_ConsoleHandle.Init(); g_EnvRegHandle.Init(); g_G_Sensor.Init(); g_EnvIoHandle.Init(); // if (BIT_TEST(HS_GetHWId(), BF_CANOPEN_EN) == FALSE) // { // g_EnvRegHandle.WriteReg(REG_VAL(EFA_GSensor, EGA_DETECT_MODE), 1); // g_EnvRegHandle.WriteReg(REG_VAL(EFA_GSensor, EGA_RUN), 1); // } DB_LOG(DLL_INFO, "InitFinish"); espClient.setTimeout(5000); client.setServer(mqtt_server, 1883); client.setCallback(callback); } void loop() { byte temperature = HS_GetRotarySW1Id(); byte humidity = HS_GetRotarySW2Id(); // 若MQTT斷線,則重新連接 if (!client.connected()) { reconnect(); } client.loop(); // 將溫度轉換成字串,並發佈溫度訊息 char tempString[8]; dtostrf((double)temperature, 1, 2, tempString); DB_LOG(DLL_INFO, "Temperature: %s", tempString); client.publish("esp32/temperature", tempString); // 將濕度轉換成字串,並發佈濕度訊息 char humString[8]; dtostrf((double)humidity, 1, 2, humString); DB_LOG(DLL_INFO, "Humidity: %s", humString); client.publish("esp32/humidity", humString); delay(10); // 每隔10ms重新執行loop函式 } ``` ## Node-Red UI 測試 典籍進入Node-Red UI http://127.0.0.1:1880/ui ![node-red dash ui](https://hackmd.io/_uploads/BkDV8_yiA.png)