CaCO3 преди 2 години
родител
ревизия
6c86317d52

+ 1 - 1
code/components/jomjol_flowcontroll/CMakeLists.txt

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
 
 idf_component_register(SRCS ${app_sources}
                     INCLUDE_DIRS "."
-                    REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
+                    REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan jomjol_helper)
 
 

+ 11 - 0
code/components/jomjol_flowcontroll/ClassFlowControll.cpp

@@ -24,6 +24,7 @@ extern "C" {
     #include "server_mqtt.h"
 #endif //ENABLE_MQTT
 
+#include "websocket.h"
 #include "server_help.h"
 #include "MainFlowControl.h"
 #include "../../include/defines.h"
@@ -187,6 +188,8 @@ void ClassFlowControll::SetInitialParameter(void)
     aktRunNr = 0;
     aktstatus = "Flow task not yet created";
     aktstatusWithTime = aktstatus;
+
+    schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
 }
 
 
@@ -264,6 +267,8 @@ void ClassFlowControll::InitFlow(std::string config)
     aktstatus = "Initialization";
     aktstatusWithTime = aktstatus;
 
+    schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
+
     //#ifdef ENABLE_MQTT
         //MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization", 1, false); // Right now, not possible -> MQTT Service is going to be started later
     //#endif //ENABLE_MQTT
@@ -326,6 +331,8 @@ void ClassFlowControll::setActStatus(std::string _aktstatus)
 {
     aktstatus = _aktstatus;
     aktstatusWithTime = aktstatus;
+
+    schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
 }
 
 
@@ -342,6 +349,8 @@ void ClassFlowControll::doFlowTakeImageOnly(string time)
             #ifdef ENABLE_MQTT
                 MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false);
             #endif //ENABLE_MQTT
+            
+            schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
 
             FlowControll[i]->doFlow(time);
         }
@@ -374,6 +383,7 @@ bool ClassFlowControll::doFlow(string time)
         aktstatus = TranslateAktstatus(FlowControll[i]->name());
         aktstatusWithTime = aktstatus + " (" + zw_time + ")";
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Status: " + aktstatusWithTime);
+        schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
         #ifdef ENABLE_MQTT
             MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
         #endif //ENABLE_MQTT
@@ -408,6 +418,7 @@ bool ClassFlowControll::doFlow(string time)
     aktstatus = "Flow finished";
     aktstatusWithTime = aktstatus + " (" + zw_time + ")";
     //LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime);
+    schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
     #ifdef ENABLE_MQTT
         MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
     #endif //ENABLE_MQTT

+ 10 - 3
code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp

@@ -9,6 +9,7 @@
 #include "ClassLogFile.h"
 
 #include "time_sntp.h"
+#include "websocket.h"
 #include "interface_mqtt.h"
 #include "ClassFlowPostProcessing.h"
 #include "ClassFlowControll.h"
@@ -249,11 +250,15 @@ bool ClassFlowMQTT::doFlow(string zwtime)
                 namenumber = maintopic + "/" + namenumber + "/";
 
 
-            if (result.length() > 0)   
+            if (result.length() > 0) {
                 success |= MQTTPublish(namenumber + "value", result, qos, SetRetainFlag);
+                schedule_websocket_message("{\"value\": \"" + result + "\", \"number\": \"" + (*NUMBERS)[i]->name + "\"}");
+            }
 
-            if (resulterror.length() > 0)  
+            if (resulterror.length() > 0) {
                 success |= MQTTPublish(namenumber + "error", resulterror, qos, SetRetainFlag);
+                schedule_websocket_message("{\"error\": \"" + resulterror + "\", \"number\": \"" + (*NUMBERS)[i]->name + "\"}");
+            }
 
             if (resultrate.length() > 0) {
                 success |= MQTTPublish(namenumber + "rate", resultrate, qos, SetRetainFlag);
@@ -273,8 +278,10 @@ bool ClassFlowMQTT::doFlow(string zwtime)
                 success |= MQTTPublish(namenumber + "rate_per_digitalization_round", resultchangabs, qos, SetRetainFlag);
             }
 
-            if (resultraw.length() > 0)   
+            if (resultraw.length() > 0) {
                 success |= MQTTPublish(namenumber + "raw", resultraw, qos, SetRetainFlag);
+                schedule_websocket_message("{\"raw\": \"" + resultraw + "\", \"number\": \"" + (*NUMBERS)[i]->name + "\"}");
+            }
 
             if (resulttimestamp.length() > 0)
                 success |= MQTTPublish(namenumber + "timestamp", resulttimestamp, qos, SetRetainFlag);

+ 5 - 0
code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp

@@ -8,6 +8,7 @@
 
 #include <time.h>
 
+#include "websocket.h"
 #include "time_sntp.h"
 
 #include "esp_log.h"
@@ -882,6 +883,8 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
                     NUMBERS[j]->ReturnValue = "";
                     NUMBERS[j]->lastvalue = imagetime;
 
+                    schedule_websocket_message("{\"status\": \"" + NUMBERS[j]->ErrorMessageText + "\", \"number\": \"" + NUMBERS[j]->name + "\"}");
+
                     string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText;
                     LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _zw);
                     WriteDataLog(j);
@@ -915,6 +918,8 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
                 NUMBERS[j]->ReturnRateValue = "";
                 NUMBERS[j]->lastvalue = imagetime;
 
+                schedule_websocket_message("{\"status\": \"" + NUMBERS[j]->ErrorMessageText + "\", \"number\": \"" + NUMBERS[j]->name + "\"}");
+                
                 string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText;
                 LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _zw);
                 WriteDataLog(j);

+ 8 - 0
code/components/jomjol_flowcontroll/MainFlowControl.cpp

@@ -13,6 +13,7 @@
 #include "Helper.h"
 #include "statusled.h"
 
+#include "websocket.h"
 #include "esp_camera.h"
 #include "time_sntp.h"
 #include "ClassControllCamera.h"
@@ -968,6 +969,10 @@ void task_autodoFlow(void *pvParameter)
     {
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "----------------------------------------------------------------"); // Clear separation between runs
         std::string _zw = "Round #" + std::to_string(++countRounds) + " started";
+
+        schedule_websocket_message("{\"round\": \"" + std::to_string(countRounds) + "\"}");
+        schedule_websocket_message("{\"uptime\": \"" + std::to_string(getUpTime()) + "\"}");
+
         time_t roundStartTime = getUpTime();
         LogFile.WriteToFile(ESP_LOG_INFO, TAG, _zw); 
         fr_start = esp_timer_get_time();
@@ -995,12 +1000,15 @@ void task_autodoFlow(void *pvParameter)
         // Round finished -> Logfile
         LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Round #" + std::to_string(countRounds) + 
                 " completed (" + std::to_string(getUpTime() - roundStartTime) + " seconds)");
+        schedule_websocket_message("{\"round duration\": \"" + std::to_string(getUpTime() - roundStartTime) + "\"}");
         
         // CPU Temp -> Logfile
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CPU Temperature: " + std::to_string((int)temperatureRead()) + "°C");
+        schedule_websocket_message("{\"cpu temperature\": \"" + std::to_string(temperatureRead()) + "\"}");
         
         // WIFI Signal Strength (RSSI) -> Logfile
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "WIFI Signal (RSSI): " + std::to_string(get_WIFI_RSSI()) + "dBm");
+        schedule_websocket_message("{\"wifi rssi\": \"" + std::to_string(get_WIFI_RSSI()) + "\"}");
 
         // Check if time is synchronized (if NTP is configured)
         if (getUseNtp() && !getTimeIsSet()) {

+ 1 - 1
code/components/jomjol_helper/CMakeLists.txt

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
 
 idf_component_register(SRCS ${app_sources}
                     INCLUDE_DIRS "."
-                    REQUIRES esp_timer tflite-lib jomjol_logfile fatfs sdmmc)
+                    REQUIRES esp_timer tflite-lib jomjol_logfile fatfs sdmmc esp_http_server)
 
 

+ 129 - 0
code/components/jomjol_helper/websocket.cpp

@@ -0,0 +1,129 @@
+#include "esp_log.h"
+#include <esp_http_server.h>
+
+#include "../../include/defines.h"
+
+#include "ClassLogFile.h"
+#include "freertos/ringbuf.h"
+#include "psram.h"
+
+#include "websocket.h"
+
+
+#define MAX_MESSAGE_LENGTH  100
+
+static const char *TAG = "WEBSOCKET";
+
+static httpd_handle_t server = NULL;
+static httpd_handle_t websocket_handle = NULL;
+
+/*
+ * Structure holding server handle and message
+ * in order to use out of request send */
+struct async_resp_arg {
+    httpd_handle_t hd;
+    char message[MAX_MESSAGE_LENGTH];
+};
+
+
+/*
+ * async send function, which we put into the httpd work queue
+ */
+static void websocket_send_pending_message(void *arg) {
+    esp_err_t ret;
+    struct async_resp_arg *resp_arg = (struct async_resp_arg *)arg;
+
+    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Sending Websocket message: '" + std::string(resp_arg->message) + "'");
+    
+    httpd_ws_frame_t ws_pkt;
+    memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
+    ws_pkt.payload = (uint8_t *)resp_arg->message;
+    ws_pkt.len = strlen(resp_arg->message);
+    ws_pkt.type = HTTPD_WS_TYPE_TEXT;
+
+    static size_t max_clients = CONFIG_LWIP_MAX_LISTENING_TCP;
+    size_t fds = max_clients;
+    int client_fds[max_clients];
+
+    ret = httpd_get_client_list(server, &fds, client_fds);
+    if (ret != ESP_OK) {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get Websocket client ist: " + std::to_string(ret) + "!");
+        free_psram_heap("websocket msg", resp_arg);
+        return;
+    }
+
+    /* Send it to all websocket clients */
+    for (int i = 0; i < fds; i++) {
+        int client_info = httpd_ws_get_fd_info(server, client_fds[i]);
+        if (client_info == HTTPD_WS_CLIENT_WEBSOCKET) {
+            httpd_ws_send_frame_async(websocket_handle, client_fds[i], &ws_pkt);
+        }
+    }
+
+    free_psram_heap("websocket msg", resp_arg);
+}
+
+
+esp_err_t schedule_websocket_message(std::string message) {
+   // return 0;
+    esp_err_t ret;
+
+    if (websocket_handle == NULL) { // No websocket connecten open
+        return ESP_OK;
+    }
+
+    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Scheduled websocket message: '" + message + "'");
+
+    struct async_resp_arg *resp_arg = (struct async_resp_arg *)malloc_psram_heap("websocket msg", 
+            sizeof(struct async_resp_arg), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+    if (resp_arg == NULL) {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to malloc memory for scheduled websocket message!");
+        return ESP_ERR_NO_MEM;
+    }
+   
+    strncpy(resp_arg->message, message.c_str(), MAX_MESSAGE_LENGTH);
+
+    ret = httpd_queue_work(websocket_handle, websocket_send_pending_message, resp_arg);
+    if (ret != ESP_OK) {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Websocket Scheduling failed: " + std::to_string(ret) + "!");
+        free_psram_heap("websocket msg", resp_arg);
+    }
+
+    return ret;
+}
+
+
+static esp_err_t ws_handler(httpd_req_t *req) {
+    if (req->method == HTTP_GET) {
+        LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Handshake done, the new websocket connection was opened");
+        websocket_handle = req->handle;
+    }
+
+    return ESP_OK;
+}
+
+
+static const httpd_uri_t ws_uri = {
+    .uri        = "/ws",
+    .method     = HTTP_GET,
+    .handler    = ws_handler,
+    .user_ctx   = NULL,
+    .is_websocket = true
+};
+
+
+esp_err_t start_websocket_server(httpd_handle_t _server) {
+    esp_err_t ret;
+
+    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init Websocket Server");
+
+    server = _server;
+
+    // Registering the ws handler
+    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Registering URI handler");
+    ret = httpd_register_uri_handler(server, &ws_uri);
+    if (ret != ESP_OK) {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Registering Websocket URI handler failed: " + std::to_string(ret));
+    }
+    return ret;
+}

+ 10 - 0
code/components/jomjol_helper/websocket.h

@@ -0,0 +1,10 @@
+#ifndef WEBSOCKET_H
+#define WEBSOCKET_H
+
+#include <esp_http_server.h>
+
+esp_err_t start_websocket_server(httpd_handle_t server);
+
+esp_err_t schedule_websocket_message(std::string message);
+
+#endif // WEBSOCKET_H

+ 1 - 1
code/components/jomjol_time_sntp/CMakeLists.txt

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
 
 idf_component_register(SRCS ${app_sources}
                     INCLUDE_DIRS "."
-                    REQUIRES tflite-lib jomjol_logfile jomjol_configfile)
+                    REQUIRES tflite-lib jomjol_logfile jomjol_configfile jomjol_helper)
 
 

+ 3 - 0
code/components/jomjol_time_sntp/time_sntp.cpp

@@ -13,6 +13,7 @@
 #include "esp_sntp.h"
 #include "../../include/defines.h"
 
+#include "websocket.h"
 #include "ClassLogFile.h"
 
 #include "configFile.h"
@@ -68,6 +69,8 @@ void time_sync_notification_cb(struct timeval *tv)
     }
     LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is synced with NTP Server " +
             getServerName() + ": " + getCurrentTimeString("%Y-%m-%d %H:%M:%S"));
+            
+    schedule_websocket_message("{\"ntp\": \"synchronized\"}");
 }
 
 

+ 3 - 1
code/main/main.cpp

@@ -36,6 +36,7 @@
 #include "MainFlowControl.h"
 #include "server_file.h"
 #include "server_ota.h"
+#include "websocket.h"
 #include "time_sntp.h"
 #include "configFile.h"
 //#include "ClassControllCamera.h"
@@ -491,7 +492,8 @@ extern "C" void app_main(void)
     // ********************************************
     ESP_LOGD(TAG, "starting servers");
 
-    server = start_webserver();   
+    server = start_webserver();
+    start_websocket_server(server);
     register_server_camera_uri(server); 
     register_server_main_flow_task_uri(server);
     register_server_file_uri(server, "/sdcard");

+ 1 - 1
code/main/server_main.cpp

@@ -438,7 +438,7 @@ httpd_handle_t start_webserver(void)
     config.server_port = 80;
     config.ctrl_port = 32768;
     config.max_open_sockets = 5; //20210921 --> previously 7   
-    config.max_uri_handlers = 39; // previously 24, 20220511: 35, 20221220: 37, 2023-01-02:38             
+    config.max_uri_handlers = 45; // previously 24, 20220511: 35, 20221220: 37, 2023-01-02:38, 20230430 (adding websocket): 45
     config.max_resp_headers = 8;                        
     config.backlog_conn = 5;                        
     config.lru_purge_enable = true; // this cuts old connections if new ones are needed.               

+ 1 - 0
code/sdkconfig.defaults

@@ -109,6 +109,7 @@ CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
 
 CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
 CONFIG_HTTPD_PURGE_BUF_LEN=16
+CONFIG_HTTPD_WS_SUPPORT=y
 
 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=16
 CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=16

+ 65 - 0
sd-card/html/websocket.html

@@ -0,0 +1,65 @@
+<html>
+<head>
+<style>
+    #rx {
+        font-family: 'Courier New', Courier, monospace;
+        font-size: small;
+    }
+</style>
+<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script> 
+</head>
+<body>
+
+<div id="rx"></div>
+
+
+<script>
+  var gateway = getDomainname().replace("http://", "ws://") + "/ws";
+  var websocket;
+  
+  window.addEventListener('load', onLoad);
+
+  function initWebSocket() {
+      console.log('Trying to open a WebSocket connection...');
+      addToLog('Trying to open a WebSocket connection...');  
+      
+      websocket = new WebSocket(gateway);
+      websocket.onopen    = onOpen;
+      websocket.onclose   = onClose;
+      websocket.onmessage = onMessage; // <-- add this line
+  }
+  
+  
+  function onOpen(event) {
+      console.log('Connection opened');
+      addToLog('Connection opened');   
+  }
+
+  
+  function onClose(event) {
+      console.log('Connection closed');
+      addToLog('Connection closed');   
+      setTimeout(initWebSocket, 2000);
+  }
+  
+  
+  function onMessage(event) {
+      console.log(event);
+      addToLog(event.data);              
+  }
+  
+  
+  
+  function onLoad(event) {
+      initWebSocket();
+  }
+  
+  
+  function addToLog(msg) {
+      document.getElementById('rx').innerHTML += "[" + new Date().toLocaleTimeString() + "] " +msg + "<br>\n";
+      window.scrollBy(0,document.body.scrollHeight);
+  }
+  
+</script>
+</body>
+</html>