add websocket support

This commit is contained in:
CaCO3
2023-04-30 21:37:35 +02:00
parent ba7d6b3621
commit 716dcb5a84
14 changed files with 249 additions and 8 deletions

View File

@@ -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)

View File

@@ -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 + "\"}");
}
@@ -269,6 +272,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
@@ -331,6 +336,8 @@ void ClassFlowControll::setActStatus(std::string _aktstatus)
{
aktstatus = _aktstatus;
aktstatusWithTime = aktstatus;
schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
}
@@ -348,6 +355,8 @@ void ClassFlowControll::doFlowTakeImageOnly(string time)
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false);
#endif //ENABLE_MQTT
schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
FlowControll[i]->doFlow(time);
}
}
@@ -379,6 +388,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
@@ -413,6 +423,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

View File

@@ -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);

View File

@@ -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);

View File

@@ -13,6 +13,7 @@
#include "Helper.h"
#include "statusled.h"
#include "websocket.h"
#include "esp_camera.h"
#include "time_sntp.h"
#include "ClassControllCamera.h"
@@ -985,6 +986,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();
@@ -1012,12 +1017,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()) {

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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\"}");
}

View File

@@ -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"
@@ -492,6 +493,7 @@ extern "C" void app_main(void)
ESP_LOGD(TAG, "starting servers");
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");

View File

@@ -427,7 +427,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.

View File

@@ -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

View File

@@ -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>