diff --git a/RotaxMonitor/data/index.html b/RotaxMonitor/data/index.html index 1ced547..1f56e79 100644 --- a/RotaxMonitor/data/index.html +++ b/RotaxMonitor/data/index.html @@ -185,11 +185,17 @@
- +
+

Box A

+ +
+ +
+

Box B

+ +
- -
@@ -200,7 +206,7 @@
No file uploaded yet.
+ - \ No newline at end of file diff --git a/RotaxMonitor/data/script.js b/RotaxMonitor/data/script.js index a115e8e..ffb4a5f 100644 --- a/RotaxMonitor/data/script.js +++ b/RotaxMonitor/data/script.js @@ -3,16 +3,23 @@ let lastMessageTimestamp = 0; const IDLE_THRESHOLD_MS = 1000; const loadingIndicator = document.getElementById("loadingIndicator"); -let chart; -let chartData = { +let chartA, chartB; + +let dataA = { labels: [], datasets: [ - { label: "BoxA RPM", data: [] }, - { label: "BoxB RPM", data: [] }, - { label: "Coils12A Delay", data: [] }, - { label: "Coils34A Delay", data: [] }, - { label: "Coils12B Delay", data: [] }, - { label: "Coils34B Delay", data: [] } + { label: "RPM", data: [] }, + { label: "Coils12 Delay", data: [] }, + { label: "Coils34 Delay", data: [] } + ] +}; + +let dataB = { + labels: [], + datasets: [ + { label: "RPM", data: [] }, + { label: "Coils12 Delay", data: [] }, + { label: "Coils34 Delay", data: [] } ] }; @@ -64,7 +71,7 @@ function connectWS() { lastMessageTimestamp = Date.now(); setLoadingIndicator(false); - updateChart(data) + updateCharts(data) // Update Box_A if (data.box_a) { @@ -138,42 +145,49 @@ function connectWS() { }; } -function updateChart(data) { - const time = new Date().toLocaleTimeString(); +function updateCharts(data) { - chartData.labels.push(time); + const t = new Date().toLocaleTimeString(); + // ===== BOX A ===== + dataA.labels.push(t); if (data.box_a) { - const boxA = data.box_a; - const coils12A = boxA.coils12 || {}; - const coils34A = boxA.coils34 || {}; - chartData.datasets[0].data.push(boxA.eng_rpm / 10); - chartData.datasets[2].data.push(coils12A.spark_delay); - chartData.datasets[3].data.push(coils34A.spark_delay); - chartData.datasets[1].data.push(0); - chartData.datasets[4].data.push(0); - chartData.datasets[5].data.push(0); + dataA.datasets[0].data.push(data.box_a.eng_rpm / 10); + dataA.datasets[1].data.push(data.box_a.coils12.spark_delay); + dataA.datasets[2].data.push(data.box_a.coils34.spark_delay); + } else { + dataA.datasets[0].data.push(undefined); + dataA.datasets[1].data.push(undefined); + dataA.datasets[2].data.push(undefined); } + // ===== BOX B ===== + dataB.labels.push(t); if (data.box_b) { - const boxB = data.box_b; - const coils12B = boxB.coils12 || {}; - const coils34B = boxB.coils34 || {}; - chartData.datasets[1].data.push(boxB.eng_rpm / 10); - chartData.datasets[4].data.push(coils12B.spark_delay); - chartData.datasets[5].data.push(coils34B.spark_delay); - chartData.datasets[0].data.push(0); - chartData.datasets[2].data.push(0); - chartData.datasets[3].data.push(0); + dataB.datasets[0].data.push(data.box_b.eng_rpm / 10); + dataB.datasets[1].data.push(data.box_b.coils12.spark_delay); + dataB.datasets[2].data.push(data.box_b.coils34.spark_delay); + } else { + dataB.datasets[0].data.push(undefined); + dataB.datasets[1].data.push(undefined); + dataB.datasets[2].data.push(undefined); } - // limite punti (tipo buffer) - if (chartData.labels.length > 100) { - chartData.labels.shift(); - chartData.datasets.forEach(d => d.data.shift()); + // limite buffer + const maxPoints = 100; + + if (dataA.labels.length > maxPoints) { + dataA.labels.shift(); + dataA.datasets.forEach(d => d.data.shift()); } - chart.update(); + if (dataB.labels.length > maxPoints) { + dataB.labels.shift(); + dataB.datasets.forEach(d => d.data.shift()); + } + + chartA.update(); + chartB.update(); } function start() { @@ -232,12 +246,29 @@ function openTab(tabId) { event.target.classList.add('active'); } -function initChart() { - const ctx = document.getElementById('chart').getContext('2d'); +function initCharts() { + const ctxA = document.getElementById('chartA').getContext('2d'); + const ctxB = document.getElementById('chartB').getContext('2d'); - chart = new Chart(ctx, { + chartA = new Chart(ctxA, { type: 'line', - data: chartData, + data: dataA, + options: { + animation: false, + responsive: true, + scales: { + x: { + display: true + }, + y: { + beginAtZero: true + } + } + } + }); + chartB = new Chart(ctxB, { + type: 'line', + data: dataB, options: { animation: false, responsive: true, @@ -254,7 +285,7 @@ function initChart() { } window.onload = () => { - initChart(); + initCharts(); }; setInterval(updateLoadingState, 200); diff --git a/RotaxMonitor/data/style.css b/RotaxMonitor/data/style.css index 08930a0..f64ff04 100644 --- a/RotaxMonitor/data/style.css +++ b/RotaxMonitor/data/style.css @@ -248,3 +248,12 @@ span { .tab-content.active { display: block; } + +.chart-container { + max-width: 1000px; + margin: 20px auto; + background: white; + padding: 20px; + border-radius: 6px; + box-shadow: 0 1px 3px rgba(0,0,0,0.08); +} \ No newline at end of file diff --git a/RotaxMonitor/src/main.cpp b/RotaxMonitor/src/main.cpp index b8b6f5a..0451ba0 100644 --- a/RotaxMonitor/src/main.cpp +++ b/RotaxMonitor/src/main.cpp @@ -52,7 +52,7 @@ void setup() IPAddress gateway(10, 11, 12, 1); IPAddress subnet(255, 255, 255, 0); WiFi.softAPConfig(local_IP, gateway, subnet); - WiFi.setTxPower(WIFI_POWER_5dBm); // reduce wifi power + WiFi.setTxPower(WIFI_POWER_13dBm); // reduce wifi power if (WiFi.softAP(WIFI_SSID, WIFI_PASSWORD)) { LOG_INFO("WiFi AP Mode Started"); @@ -184,7 +184,7 @@ void loop() .rt_queue = nullptr, .dev = dev}; - auto task_A = rtIgnitionTask(taskA_params, 4096, 256, CORE_1, fs_mutex); + auto task_A = rtIgnitionTask(taskA_params, 4096, 256, CORE_0, fs_mutex); delay(50); auto task_B = rtIgnitionTask(taskB_params, 4096, 256, CORE_1, fs_mutex); @@ -211,34 +211,38 @@ void loop() led.setStatus(RGBled::LedStatus::OK); AstroWebServer webPage(80, LittleFS); // Initialize webserver and Websocket - - task_A.onMessage([&webPage](ignitionBoxStatusFiltered sts){ - ArduinoJson::JsonDocument doc; - doc["box_a"] = sts.toJson(); - doc["box_b"] = ArduinoJson::JsonDocument(); - webPage.sendWsData(doc.as()); + ArduinoJson::JsonDocument json_data; + bool data_a, data_b; + task_A.onMessage([&webPage, &json_data, &data_a](ignitionBoxStatusFiltered sts){ + json_data["box_a"] = sts.toJson(); + data_a = true; }); - task_B.onMessage([&webPage](ignitionBoxStatusFiltered sts){ - ArduinoJson::JsonDocument doc; - doc["box_a"] = ArduinoJson::JsonDocument(); - doc["box_b"] = sts.toJson(); - webPage.sendWsData(doc.as()); + task_B.onMessage([&webPage, &json_data, &data_b](ignitionBoxStatusFiltered sts){ + json_data["box_b"] = sts.toJson(); + data_b = true; }); - task_A.enableSave(true, "ignitionA_test.csv"); - task_B.enableSave(true, "ignitionB_test.csv"); + // task_A.enableSave(true, "ignitionA_test.csv"); + // task_B.enableSave(true, "ignitionB_test.csv"); - uint32_t last_loop = millis(); + uint32_t monitor_loop = millis(); + uint32_t data_loop = monitor_loop; //////////////// INNER LOOP ///////////////////// while (running) { - if ((millis() - last_loop) > 2000) + uint32_t this_loop = millis(); + if (this_loop - monitor_loop > 2000) { clearScreen(); printRunningTasksMod(Serial); - delay(100); - last_loop = millis(); + monitor_loop = millis(); + } + if ((data_a && data_b) || (this_loop - data_loop > 500)) { + webPage.sendWsData(json_data.as()); + json_data.clear(); + data_a = data_b = false; + data_loop = millis(); } } //////////////// INNER LOOP ///////////////////// diff --git a/RotaxMonitor/src/tasks.cpp b/RotaxMonitor/src/tasks.cpp index bcc7859..f691590 100644 --- a/RotaxMonitor/src/tasks.cpp +++ b/RotaxMonitor/src/tasks.cpp @@ -360,7 +360,7 @@ void rtIgnitionTask::run() m_info_filtered.update(m_last_status); (*m_active_history)[m_counter_status] = m_last_status; - if (m_on_message_cb && m_counter_status % 10) + if (m_on_message_cb && m_counter_status % 10 == 0) { m_on_message_cb(m_info_filtered); } diff --git a/RotaxMonitor/src/webserver.cpp b/RotaxMonitor/src/webserver.cpp index 82ab122..086f485 100644 --- a/RotaxMonitor/src/webserver.cpp +++ b/RotaxMonitor/src/webserver.cpp @@ -1,10 +1,19 @@ #include #include -static const std::map s_webserverCommands = { +static std::map s_webserverCommands = { {"setTime", AstroWebServer::SET_TIME}, }; +void on_ping(TimerHandle_t xTimer) +{ + if (!xTimer) + return; + auto ws = (AsyncWebSocket *)pvTimerGetTimerID(xTimer); + ws->pingAll(); + ws->cleanupClients(); +} + 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"); @@ -20,11 +29,15 @@ AstroWebServer::AstroWebServer(const uint8_t port, fs::FS &filesystem) : m_port( { onUploadHandler(request, filename, index, data, len, final); }); m_webserver.begin(); + m_websocket.enable(true); + + m_pingTimer = xTimerCreate("wsPingTimer", pdMS_TO_TICKS(2000), pdTRUE, (void *)&m_websocket, on_ping); LOG_DEBUG("Webserver Init OK"); } AstroWebServer::~AstroWebServer() { + xTimerDelete(m_pingTimer, pdMS_TO_TICKS(10)); m_webserver.removeHandler(&m_websocket); m_webserver.end(); } @@ -47,6 +60,9 @@ void AstroWebServer::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *cli case WS_EVT_DISCONNECT: LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] DISCONNECTED"); break; + case WS_EVT_PONG: + LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] PONG"); + break; case WS_EVT_DATA: { AwsFrameInfo *info = (AwsFrameInfo *)arg; diff --git a/RotaxMonitor/src/webserver.h b/RotaxMonitor/src/webserver.h index 3eb8aac..c461787 100644 --- a/RotaxMonitor/src/webserver.h +++ b/RotaxMonitor/src/webserver.h @@ -38,6 +38,7 @@ private: AsyncWebSocket m_websocket; bool m_uploadFailed = false; fs::File m_uploadFile; + TimerHandle_t m_pingTimer = NULL; public: enum c_commandEnum