diff --git a/RotaxMonitor/data/style.css b/RotaxMonitor/data/style.css index cdf3297..11dd233 100644 --- a/RotaxMonitor/data/style.css +++ b/RotaxMonitor/data/style.css @@ -180,4 +180,3 @@ div[style*="max-width: 900px"] strong { span { color: var(--text-dark); } -} diff --git a/RotaxMonitor/src/main.cpp b/RotaxMonitor/src/main.cpp index ed85363..48af1a9 100644 --- a/RotaxMonitor/src/main.cpp +++ b/RotaxMonitor/src/main.cpp @@ -7,18 +7,14 @@ #include #include #include -#include -#include // Definitions #include #include #include +#include #include -static File uploadFile; -static bool uploadFailed = false; - // FreeRTOS directives #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -29,20 +25,6 @@ static bool uploadFailed = false; #define WIFI_SSID "AstroRotaxMonitor" #define WIFI_PASSWORD "maledettirotax" -void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, - AwsEventType type, void *arg, uint8_t *data, size_t len) -{ - switch (type) - { - case WS_EVT_CONNECT: - Serial.printf("WS client IP[%s]-ID[%u] CONNECTED\r\n", client->remoteIP().toString().c_str(), client->id()); - break; - case WS_EVT_DISCONNECT: - Serial.printf("WS client ID[%u] DISCONNECTED\r\n", client->remoteIP().toString().c_str(), client->id()); - break; - } -} - void setup() { Serial.begin(921600); @@ -201,7 +183,7 @@ void loop() RT_TASK_PRIORITY, &trigA_TaskHandle, CORE_0); - delay(100); + delay(100); // give some time to the thread to start // Ignition B on Core 1 auto ignB_task_success = pdPASS; @@ -214,7 +196,7 @@ void loop() RT_TASK_PRIORITY, // priorità leggermente più alta &trigB_TaskHandle, CORE_1); - delay(100); + delay(100); // give some time to the thread to start #endif if (ignA_task_success != pdPASS || ignB_task_success != pdPASS) @@ -234,78 +216,7 @@ void loop() ignitionBoxStatus ign_info; ignitionBoxStatusAverage ign_info_avg(filter_k); LITTLEFSGuard fsGuard; - - // Initialize Web page - AsyncWebServer server(80); - AsyncWebSocket ws("/ws"); - ws.onEvent(onWsEvent); - server.addHandler(&ws); - server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html"); - - server.on("/upload", HTTP_POST, - [](AsyncWebServerRequest *request) { - if (uploadFailed) - { - request->send(500, "text/plain", "Upload failed"); - } - else - { - request->send(200, "text/plain", "Upload successful"); - } - }, - [](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { - if (index == 0) - { - uploadFailed = false; - String safeName = filename; - int slashIndex = safeName.lastIndexOf('/'); - if (slashIndex >= 0) - { - safeName = safeName.substring(slashIndex + 1); - } - if (safeName.length() == 0) - { - uploadFailed = true; - return; - } - - String filePath = "/" + safeName; - if (LittleFS.exists(filePath)) - { - LittleFS.remove(filePath); - } - - uploadFile = LittleFS.open(filePath, FILE_WRITE); - if (!uploadFile) - { - uploadFailed = true; - LOG_ERROR("Failed to open upload file:", filePath); - return; - } - } - - if (!uploadFailed && uploadFile) - { - if (uploadFile.write(data, len) != len) - { - uploadFailed = true; - } - } - - if (final && uploadFile) - { - uploadFile.close(); - if (!uploadFailed) - { - LOG_INFO("Uploaded file to LittleFS:", filename); - } - } - }); - - server.begin(); - - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) - { request->send(200, "text/plain", "OK"); }); + WebPage webPage(80, LittleFS); // Initialize webserver and Websocket while (running) { @@ -328,14 +239,12 @@ void loop() auto &hist = *active_history; hist[counter++ % active_history->size()] = ign_info; ign_info_avg.update(ign_info); // update moving average with latest ignition status - Serial.print("\033[2K Data Received: " + String(counter) + "/" + String(hist.size()) + '\r'); - - if (ws.count() > 0 && counter % filter_k == 0) // send data every 10 samples + Serial.printf("\033[2K Data Received: %d/%d\r", counter, hist.size()); + if ( counter % filter_k == 0) // send data every 10 samples { Serial.println(); LOG_DEBUG("Sending average ignition status to websocket clients..."); - auto msg = ign_info_avg.toJson().as(); - ws.textAll(msg); + webPage.sendWsData(ign_info_avg.toJson().as()); } } else diff --git a/RotaxMonitor/src/webserver.cpp b/RotaxMonitor/src/webserver.cpp index e69de29..937be9f 100644 --- a/RotaxMonitor/src/webserver.cpp +++ b/RotaxMonitor/src/webserver.cpp @@ -0,0 +1,98 @@ +#include + +WebPage::WebPage(const uint8_t port, fs::FS &filesystem) : m_port(port), m_webserver(AsyncWebServer(port)), m_websocket(AsyncWebSocket("/ws")), m_filesystem(filesystem) +{ + m_websocket.onEvent([this](AsyncWebSocket *server, AsyncWebSocketClient *client, + AwsEventType type, void *arg, uint8_t *data, size_t len) + { onWsEvent(server, client, type, arg, data, len); }); + + m_webserver.addHandler(&m_websocket); + m_webserver.serveStatic("/", m_filesystem, "/").setDefaultFile("index.html"); + + m_webserver.on("/upload", HTTP_POST, + [this](AsyncWebServerRequest *request) + { onUploadRequest(request); }, + [this](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) + { onUploadHandler(request, filename, index, data, len, final); } + ); + + m_webserver.begin(); +} + +WebPage::~WebPage() +{ + m_webserver.removeHandler(&m_websocket); + m_webserver.end(); +} + +void WebPage::sendWsData(const String &data){ + if (m_websocket.count()){ + m_websocket.textAll(data); + } +} + +void WebPage::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) +{ + switch (type) + { + case WS_EVT_CONNECT: + Serial.printf("WS client IP[%s]-ID[%u] CONNECTED\r\n", client->remoteIP().toString().c_str(), client->id()); + break; + case WS_EVT_DISCONNECT: + Serial.printf("WS client ID[%u] DISCONNECTED\r\n", client->remoteIP().toString().c_str(), client->id()); + break; + } +} + +void WebPage::onUploadRequest(AsyncWebServerRequest *request) +{ + if (m_upload_failed) + request->send(500, "text/plain", "Upload failed"); + else + request->send(200, "text/plain", "Upload successful"); +} + +void WebPage::onUploadHandler(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) +{ + if (index == 0) // only on first iteration to open file + { + m_upload_failed = false; + String safeName = filename; + int slashIndex = safeName.lastIndexOf('/'); + if (slashIndex >= 0) + safeName = safeName.substring(slashIndex + 1); + if (safeName.length() == 0) + { + m_upload_failed = true; + LOG_ERROR("Invalid file name"); + return; + } + + const std::filesystem::path filePath = std::filesystem::path(m_filesystem.mountpoint()) / safeName.c_str(); + if (m_filesystem.exists(filePath.c_str())) + m_filesystem.remove(filePath.c_str()); + + m_upload_file = m_filesystem.open(filePath.c_str(), FILE_WRITE); + if (!m_upload_file) + { + m_upload_failed = true; + LOG_ERROR("Failed to open upload file:", filePath.c_str()); + return; + } + } + + // Actual write of file data + if (!m_upload_failed && m_upload_file) + { + if (m_upload_file.write(data, len) != len) + m_upload_failed = true; + } + + // close the file and save on final call + if (final && m_upload_file) + { + m_upload_file.close(); + if (!m_upload_failed) + LOG_INFO("Uploaded file to LittleFS:", filename.c_str()); + } +} diff --git a/RotaxMonitor/src/webserver.h b/RotaxMonitor/src/webserver.h index e69de29..5e230f3 100644 --- a/RotaxMonitor/src/webserver.h +++ b/RotaxMonitor/src/webserver.h @@ -0,0 +1,38 @@ +#pragma once +#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO + +// System includes +#include +#include +#include +#include +#include +#include + +class WebPage +{ + const uint8_t m_port = 80; + fs::FS &m_filesystem; + AsyncWebServer m_webserver; + AsyncWebSocket m_websocket; + bool m_upload_failed = false; + fs::File m_upload_file; + +public: + WebPage(const uint8_t port, fs::FS &filesystem); + ~WebPage(); + + void sendWsData(const String &data); + +private: + void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, + AwsEventType type, void *arg, uint8_t *data, size_t len); + + void onUploadRequest(AsyncWebServerRequest *request); + void onUploadHandler(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final); + + void onStart(AsyncWebServerRequest *request); + void onStop(AsyncWebServerRequest *request); + void onDownload(AsyncWebServerRequest *request); + +};