Set time from browser

This commit is contained in:
Emanuele Trabattoni
2026-04-12 14:40:58 +02:00
parent a153402d28
commit 7da58c8a49
5 changed files with 114 additions and 42 deletions

View File

@@ -38,6 +38,11 @@ function connectWS() {
console.log("WebSocket connesso"); console.log("WebSocket connesso");
lastMessageTimestamp = Date.now(); lastMessageTimestamp = Date.now();
setLoadingIndicator(false); setLoadingIndicator(false);
ws.send(JSON.stringify({
cmd: "setTime",
time: Math.floor(Date.now() / 1000)
}));
}; };
ws.onclose = () => { ws.onclose = () => {

View File

@@ -210,7 +210,7 @@ void loop()
LOG_DEBUG("Real Time Tasks A & B initialized"); LOG_DEBUG("Real Time Tasks A & B initialized");
led.setStatus(RGBled::LedStatus::OK); led.setStatus(RGBled::LedStatus::OK);
WebPage webPage(80, LittleFS); // Initialize webserver and Websocket AstroWebServer webPage(80, LittleFS); // Initialize webserver and Websocket
task_A.onMessage([&webPage](ignitionBoxStatusFiltered sts){ task_A.onMessage([&webPage](ignitionBoxStatusFiltered sts){
ArduinoJson::JsonDocument doc; ArduinoJson::JsonDocument doc;
@@ -237,6 +237,7 @@ void loop()
{ {
clearScreen(); clearScreen();
printRunningTasksMod(Serial); printRunningTasksMod(Serial);
delay(100);
last_loop = millis(); last_loop = millis();
} }
} //////////////// INNER LOOP ///////////////////// } //////////////// INNER LOOP /////////////////////

View File

@@ -99,7 +99,14 @@ void printRunningTasksMod(Print &printer, std::function<bool(const TaskStatus_t
// PRINT MEMORY INFO // PRINT MEMORY INFO
printer.printf("\033[H"); printer.printf("\033[H");
printer.printf(COLOR_LBLUE "=================== ESP32 SYSTEM MONITOR ===================\n\n" COLOR_RESET); printer.printf(COLOR_LBLUE "=================== ESP32 SYSTEM MONITOR ===================\n" COLOR_RESET);
std::string buffer;
time_t now = time(nullptr);
struct tm *t = localtime(&now);
buffer.resize(64);
strftime(buffer.data(), sizeof(buffer), "%Y-%m-%d %H:%M:%S", t);
printer.printf(COLOR_YELLOW "=============== Datetime: %s ===============\n\n" COLOR_RESET, buffer.c_str());
// ===== HEAP ===== // ===== HEAP =====
size_t freeHeap = esp_get_free_heap_size(); size_t freeHeap = esp_get_free_heap_size();

View File

@@ -1,6 +1,11 @@
#include <webserver.h> #include <webserver.h>
#include <ArduinoJson.h>
WebPage::WebPage(const uint8_t port, fs::FS &filesystem) : m_port(port), m_webserver(AsyncWebServer(port)), m_websocket(AsyncWebSocket("/ws")), m_filesystem(filesystem) static const std::map<const std::string, AstroWebServer::c_commandEnum> s_webserverCommands = {
{"setTime", AstroWebServer::SET_TIME},
};
AstroWebServer::AstroWebServer(const uint8_t port, fs::FS &filesystem) : m_port(port), m_webserver(AsyncWebServer(port)), m_websocket(AsyncWebSocket("/ws")), m_filesystem(filesystem)
{ {
LOG_DEBUG("Initializing Web Server"); LOG_DEBUG("Initializing Web Server");
m_websocket.onEvent([this](AsyncWebSocket *server, AsyncWebSocketClient *client, m_websocket.onEvent([this](AsyncWebSocket *server, AsyncWebSocketClient *client,
@@ -10,62 +15,107 @@ WebPage::WebPage(const uint8_t port, fs::FS &filesystem) : m_port(port), m_webse
m_webserver.addHandler(&m_websocket); m_webserver.addHandler(&m_websocket);
m_webserver.serveStatic("/", m_filesystem, "/").setDefaultFile("index.html"); m_webserver.serveStatic("/", m_filesystem, "/").setDefaultFile("index.html");
m_webserver.on("/upload", HTTP_POST, m_webserver.on("/upload", HTTP_POST, [this](AsyncWebServerRequest *request)
[this](AsyncWebServerRequest *request) { onUploadRequest(request); }, [this](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
{ onUploadRequest(request); }, { onUploadHandler(request, filename, index, data, len, final); });
[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(); m_webserver.begin();
LOG_DEBUG("Webserver Init OK"); LOG_DEBUG("Webserver Init OK");
} }
WebPage::~WebPage() AstroWebServer::~AstroWebServer()
{ {
m_webserver.removeHandler(&m_websocket); m_webserver.removeHandler(&m_websocket);
m_webserver.end(); m_webserver.end();
} }
void WebPage::sendWsData(const String &data){ void AstroWebServer::sendWsData(const String &data)
if (m_websocket.count()){ {
if (m_websocket.count())
{
m_websocket.textAll(data); m_websocket.textAll(data);
} }
} }
void WebPage::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) void AstroWebServer::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
{ {
switch (type) switch (type)
{ {
case WS_EVT_CONNECT: case WS_EVT_CONNECT:
Serial.printf("WS client IP[%s]-ID[%u] CONNECTED\r\n", client->remoteIP().toString().c_str(), client->id()); LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] CONNECTED");
break; break;
case WS_EVT_DISCONNECT: case WS_EVT_DISCONNECT:
Serial.printf("WS client ID[%u] DISCONNECTED\r\n", client->remoteIP().toString().c_str(), client->id()); LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] DISCONNECTED");
break; break;
case WS_EVT_DATA:
{
AwsFrameInfo *info = (AwsFrameInfo *)arg;
if (info->final && info->index == 0 && info->len == len)
{
std::string data_str((char *)data, len);
ArduinoJson::JsonDocument doc;
if (auto rv = ArduinoJson::deserializeJson(doc, data_str) != ArduinoJson::DeserializationError::Ok)
{
LOG_ERROR("WS Client unable to deserialize Json");
return;
}
if (!doc["cmd"].is<std::string>() || !s_webserverCommands.contains(doc["cmd"]))
{
LOG_WARN("WS Client Invalid Json command [", doc["cmd"].as<std::string>().c_str(), "]");
return;
}
std::string buffer;
switch (s_webserverCommands.at(doc["cmd"]))
{
case SET_TIME:
{
auto epoch = doc["time"].as<time_t>();
timeval te{
.tv_sec = epoch,
.tv_usec = 0,
};
timezone tz{
.tz_minuteswest = 0,
.tz_dsttime = DST_MET,
};
settimeofday(&te, &tz);
time_t now = time(nullptr);
struct tm *t = localtime(&now);
buffer.resize(64);
strftime(buffer.data(), sizeof(buffer), "%Y-%m-%d %H:%M:%S", t);
LOG_DEBUG("WS Client set Datetime to: ", buffer.c_str());
break;
}
default:
// call external command callback
break;
}
}
}
} }
} }
void WebPage::onUploadRequest(AsyncWebServerRequest *request) void AstroWebServer::onUploadRequest(AsyncWebServerRequest *request)
{ {
if (m_upload_failed) if (m_uploadFailed)
request->send(500, "text/plain", "Upload failed"); request->send(500, "text/plain", "Upload failed");
else else
request->send(200, "text/plain", "Upload successful"); 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) void AstroWebServer::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 if (index == 0) // only on first iteration to open file
{ {
m_upload_failed = false; m_uploadFailed = false;
String safeName = filename; String safeName = filename;
int slashIndex = safeName.lastIndexOf('/'); int slashIndex = safeName.lastIndexOf('/');
if (slashIndex >= 0) if (slashIndex >= 0)
safeName = safeName.substring(slashIndex + 1); safeName = safeName.substring(slashIndex + 1);
if (safeName.length() == 0) if (safeName.length() == 0)
{ {
m_upload_failed = true; m_uploadFailed = true;
LOG_ERROR("Invalid file name"); LOG_ERROR("Invalid file name");
return; return;
} }
@@ -74,27 +124,27 @@ void WebPage::onUploadHandler(AsyncWebServerRequest *request, const String &file
if (m_filesystem.exists(filePath.c_str())) if (m_filesystem.exists(filePath.c_str()))
m_filesystem.remove(filePath.c_str()); m_filesystem.remove(filePath.c_str());
m_upload_file = m_filesystem.open(filePath.c_str(), FILE_WRITE); m_uploadFile = m_filesystem.open(filePath.c_str(), FILE_WRITE);
if (!m_upload_file) if (!m_uploadFile)
{ {
m_upload_failed = true; m_uploadFailed = true;
LOG_ERROR("Failed to open upload file:", filePath.c_str()); LOG_ERROR("Failed to open upload file:", filePath.c_str());
return; return;
} }
} }
// Actual write of file data // Actual write of file data
if (!m_upload_failed && m_upload_file) if (!m_uploadFailed && m_uploadFile)
{ {
if (m_upload_file.write(data, len) != len) if (m_uploadFile.write(data, len) != len)
m_upload_failed = true; m_uploadFailed = true;
} }
// close the file and save on final call // close the file and save on final call
if (final && m_upload_file) if (final && m_uploadFile)
{ {
m_upload_file.close(); m_uploadFile.close();
if (!m_upload_failed) if (!m_uploadFailed)
LOG_INFO("Uploaded file to LittleFS:", filename.c_str()); LOG_INFO("Uploaded file to LittleFS:", filename.c_str());
} }
} }

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO #define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
// System includes // System includes
#include <Arduino.h> #include <Arduino.h>
@@ -7,20 +7,16 @@
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <filesystem> #include <filesystem>
#include <map>
#include <FS.h> #include <FS.h>
class WebPage class AstroWebServer
{ {
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: public:
WebPage(const uint8_t port, fs::FS &filesystem); AstroWebServer(const uint8_t port, fs::FS &filesystem);
~WebPage(); ~AstroWebServer();
void sendWsData(const String &data); void sendWsData(const String &data);
@@ -35,4 +31,17 @@ private:
void onStop(AsyncWebServerRequest *request); void onStop(AsyncWebServerRequest *request);
void onDownload(AsyncWebServerRequest *request); void onDownload(AsyncWebServerRequest *request);
private:
const uint8_t m_port = 80;
fs::FS &m_filesystem;
AsyncWebServer m_webserver;
AsyncWebSocket m_websocket;
bool m_uploadFailed = false;
fs::File m_uploadFile;
public:
enum c_commandEnum
{
SET_TIME
};
}; };