refactored webserver code
This commit is contained in:
@@ -180,4 +180,3 @@ div[style*="max-width: 900px"] strong {
|
|||||||
span {
|
span {
|
||||||
color: var(--text-dark);
|
color: var(--text-dark);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,18 +7,14 @@
|
|||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
|
|
||||||
// Definitions
|
// Definitions
|
||||||
#include <tasks.h>
|
#include <tasks.h>
|
||||||
#include <devices.h>
|
#include <devices.h>
|
||||||
#include <datasave.h>
|
#include <datasave.h>
|
||||||
|
#include <webserver.h>
|
||||||
#include <ui.h>
|
#include <ui.h>
|
||||||
|
|
||||||
static File uploadFile;
|
|
||||||
static bool uploadFailed = false;
|
|
||||||
|
|
||||||
// FreeRTOS directives
|
// FreeRTOS directives
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
@@ -29,20 +25,6 @@ static bool uploadFailed = false;
|
|||||||
#define WIFI_SSID "AstroRotaxMonitor"
|
#define WIFI_SSID "AstroRotaxMonitor"
|
||||||
#define WIFI_PASSWORD "maledettirotax"
|
#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()
|
void setup()
|
||||||
{
|
{
|
||||||
Serial.begin(921600);
|
Serial.begin(921600);
|
||||||
@@ -201,7 +183,7 @@ void loop()
|
|||||||
RT_TASK_PRIORITY,
|
RT_TASK_PRIORITY,
|
||||||
&trigA_TaskHandle,
|
&trigA_TaskHandle,
|
||||||
CORE_0);
|
CORE_0);
|
||||||
delay(100);
|
delay(100); // give some time to the thread to start
|
||||||
|
|
||||||
// Ignition B on Core 1
|
// Ignition B on Core 1
|
||||||
auto ignB_task_success = pdPASS;
|
auto ignB_task_success = pdPASS;
|
||||||
@@ -214,7 +196,7 @@ void loop()
|
|||||||
RT_TASK_PRIORITY, // priorità leggermente più alta
|
RT_TASK_PRIORITY, // priorità leggermente più alta
|
||||||
&trigB_TaskHandle,
|
&trigB_TaskHandle,
|
||||||
CORE_1);
|
CORE_1);
|
||||||
delay(100);
|
delay(100); // give some time to the thread to start
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ignA_task_success != pdPASS || ignB_task_success != pdPASS)
|
if (ignA_task_success != pdPASS || ignB_task_success != pdPASS)
|
||||||
@@ -234,78 +216,7 @@ void loop()
|
|||||||
ignitionBoxStatus ign_info;
|
ignitionBoxStatus ign_info;
|
||||||
ignitionBoxStatusAverage ign_info_avg(filter_k);
|
ignitionBoxStatusAverage ign_info_avg(filter_k);
|
||||||
LITTLEFSGuard fsGuard;
|
LITTLEFSGuard fsGuard;
|
||||||
|
WebPage webPage(80, LittleFS); // Initialize webserver and Websocket
|
||||||
// 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"); });
|
|
||||||
|
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
@@ -328,14 +239,12 @@ void loop()
|
|||||||
auto &hist = *active_history;
|
auto &hist = *active_history;
|
||||||
hist[counter++ % active_history->size()] = ign_info;
|
hist[counter++ % active_history->size()] = ign_info;
|
||||||
ign_info_avg.update(ign_info); // update moving average with latest ignition status
|
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');
|
Serial.printf("\033[2K Data Received: %d/%d\r", counter, hist.size());
|
||||||
|
if ( counter % filter_k == 0) // send data every 10 samples
|
||||||
if (ws.count() > 0 && counter % filter_k == 0) // send data every 10 samples
|
|
||||||
{
|
{
|
||||||
Serial.println();
|
Serial.println();
|
||||||
LOG_DEBUG("Sending average ignition status to websocket clients...");
|
LOG_DEBUG("Sending average ignition status to websocket clients...");
|
||||||
auto msg = ign_info_avg.toJson().as<String>();
|
webPage.sendWsData(ign_info_avg.toJson().as<String>());
|
||||||
ws.textAll(msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include <webserver.h>
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO
|
||||||
|
|
||||||
|
// System includes
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <DebugLog.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <AsyncTCP.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <FS.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user