updated and fixed charts

This commit is contained in:
Emanuele Trabattoni
2026-04-13 10:26:55 +02:00
parent f8c3c69e80
commit 212b37c95f
7 changed files with 132 additions and 65 deletions

View File

@@ -185,11 +185,17 @@
<!-- TAB 2 (grafico) --> <!-- TAB 2 (grafico) -->
<div id="tab2" class="tab-content"> <div id="tab2" class="tab-content">
<canvas id="chart" height="100"></canvas> <div class="chart-container">
<h3>Box A</h3>
<canvas id="chartA" height="100"></canvas>
</div>
<div class="chart-container">
<h3>Box B</h3>
<canvas id="chartB" height="100"></canvas>
</div>
</div> </div>
<script src="chart.js"></script>
<script src="script.js"></script>
</body> </body>
<div class="upload-section"> <div class="upload-section">
@@ -200,7 +206,7 @@
<div id="uploadStatus" class="upload-status">No file uploaded yet.</div> <div id="uploadStatus" class="upload-status">No file uploaded yet.</div>
</div> </div>
<script src="chart.js"></script>
<script src="script.js"></script> <script src="script.js"></script>
</body>
</html> </html>

View File

@@ -3,16 +3,23 @@ let lastMessageTimestamp = 0;
const IDLE_THRESHOLD_MS = 1000; const IDLE_THRESHOLD_MS = 1000;
const loadingIndicator = document.getElementById("loadingIndicator"); const loadingIndicator = document.getElementById("loadingIndicator");
let chart; let chartA, chartB;
let chartData = {
let dataA = {
labels: [], labels: [],
datasets: [ datasets: [
{ label: "BoxA RPM", data: [] }, { label: "RPM", data: [] },
{ label: "BoxB RPM", data: [] }, { label: "Coils12 Delay", data: [] },
{ label: "Coils12A Delay", data: [] }, { label: "Coils34 Delay", data: [] }
{ label: "Coils34A Delay", data: [] }, ]
{ label: "Coils12B Delay", data: [] }, };
{ label: "Coils34B 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(); lastMessageTimestamp = Date.now();
setLoadingIndicator(false); setLoadingIndicator(false);
updateChart(data) updateCharts(data)
// Update Box_A // Update Box_A
if (data.box_a) { if (data.box_a) {
@@ -138,42 +145,49 @@ function connectWS() {
}; };
} }
function updateChart(data) { function updateCharts(data) {
const time = new Date().toLocaleTimeString();
chartData.labels.push(time); const t = new Date().toLocaleTimeString();
// ===== BOX A =====
dataA.labels.push(t);
if (data.box_a) { if (data.box_a) {
const boxA = data.box_a; dataA.datasets[0].data.push(data.box_a.eng_rpm / 10);
const coils12A = boxA.coils12 || {}; dataA.datasets[1].data.push(data.box_a.coils12.spark_delay);
const coils34A = boxA.coils34 || {}; dataA.datasets[2].data.push(data.box_a.coils34.spark_delay);
chartData.datasets[0].data.push(boxA.eng_rpm / 10); } else {
chartData.datasets[2].data.push(coils12A.spark_delay); dataA.datasets[0].data.push(undefined);
chartData.datasets[3].data.push(coils34A.spark_delay); dataA.datasets[1].data.push(undefined);
chartData.datasets[1].data.push(0); dataA.datasets[2].data.push(undefined);
chartData.datasets[4].data.push(0);
chartData.datasets[5].data.push(0);
} }
// ===== BOX B =====
dataB.labels.push(t);
if (data.box_b) { if (data.box_b) {
const boxB = data.box_b; dataB.datasets[0].data.push(data.box_b.eng_rpm / 10);
const coils12B = boxB.coils12 || {}; dataB.datasets[1].data.push(data.box_b.coils12.spark_delay);
const coils34B = boxB.coils34 || {}; dataB.datasets[2].data.push(data.box_b.coils34.spark_delay);
chartData.datasets[1].data.push(boxB.eng_rpm / 10); } else {
chartData.datasets[4].data.push(coils12B.spark_delay); dataB.datasets[0].data.push(undefined);
chartData.datasets[5].data.push(coils34B.spark_delay); dataB.datasets[1].data.push(undefined);
chartData.datasets[0].data.push(0); dataB.datasets[2].data.push(undefined);
chartData.datasets[2].data.push(0);
chartData.datasets[3].data.push(0);
} }
// limite punti (tipo buffer) // limite buffer
if (chartData.labels.length > 100) { const maxPoints = 100;
chartData.labels.shift();
chartData.datasets.forEach(d => d.data.shift()); 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() { function start() {
@@ -232,12 +246,29 @@ function openTab(tabId) {
event.target.classList.add('active'); event.target.classList.add('active');
} }
function initChart() { function initCharts() {
const ctx = document.getElementById('chart').getContext('2d'); const ctxA = document.getElementById('chartA').getContext('2d');
const ctxB = document.getElementById('chartB').getContext('2d');
chart = new Chart(ctx, { chartA = new Chart(ctxA, {
type: 'line', 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: { options: {
animation: false, animation: false,
responsive: true, responsive: true,
@@ -254,7 +285,7 @@ function initChart() {
} }
window.onload = () => { window.onload = () => {
initChart(); initCharts();
}; };
setInterval(updateLoadingState, 200); setInterval(updateLoadingState, 200);

View File

@@ -248,3 +248,12 @@ span {
.tab-content.active { .tab-content.active {
display: block; 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);
}

View File

@@ -52,7 +52,7 @@ void setup()
IPAddress gateway(10, 11, 12, 1); IPAddress gateway(10, 11, 12, 1);
IPAddress subnet(255, 255, 255, 0); IPAddress subnet(255, 255, 255, 0);
WiFi.softAPConfig(local_IP, gateway, subnet); 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)) if (WiFi.softAP(WIFI_SSID, WIFI_PASSWORD))
{ {
LOG_INFO("WiFi AP Mode Started"); LOG_INFO("WiFi AP Mode Started");
@@ -184,7 +184,7 @@ void loop()
.rt_queue = nullptr, .rt_queue = nullptr,
.dev = dev}; .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); delay(50);
auto task_B = rtIgnitionTask(taskB_params, 4096, 256, CORE_1, fs_mutex); auto task_B = rtIgnitionTask(taskB_params, 4096, 256, CORE_1, fs_mutex);
@@ -211,34 +211,38 @@ void loop()
led.setStatus(RGBled::LedStatus::OK); led.setStatus(RGBled::LedStatus::OK);
AstroWebServer webPage(80, LittleFS); // Initialize webserver and Websocket AstroWebServer webPage(80, LittleFS); // Initialize webserver and Websocket
ArduinoJson::JsonDocument json_data;
task_A.onMessage([&webPage](ignitionBoxStatusFiltered sts){ bool data_a, data_b;
ArduinoJson::JsonDocument doc; task_A.onMessage([&webPage, &json_data, &data_a](ignitionBoxStatusFiltered sts){
doc["box_a"] = sts.toJson(); json_data["box_a"] = sts.toJson();
doc["box_b"] = ArduinoJson::JsonDocument(); data_a = true;
webPage.sendWsData(doc.as<String>());
}); });
task_B.onMessage([&webPage](ignitionBoxStatusFiltered sts){ task_B.onMessage([&webPage, &json_data, &data_b](ignitionBoxStatusFiltered sts){
ArduinoJson::JsonDocument doc; json_data["box_b"] = sts.toJson();
doc["box_a"] = ArduinoJson::JsonDocument(); data_b = true;
doc["box_b"] = sts.toJson();
webPage.sendWsData(doc.as<String>());
}); });
task_A.enableSave(true, "ignitionA_test.csv"); // task_A.enableSave(true, "ignitionA_test.csv");
task_B.enableSave(true, "ignitionB_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 ///////////////////// //////////////// INNER LOOP /////////////////////
while (running) while (running)
{ {
if ((millis() - last_loop) > 2000) uint32_t this_loop = millis();
if (this_loop - monitor_loop > 2000)
{ {
clearScreen(); clearScreen();
printRunningTasksMod(Serial); printRunningTasksMod(Serial);
delay(100); monitor_loop = millis();
last_loop = millis(); }
if ((data_a && data_b) || (this_loop - data_loop > 500)) {
webPage.sendWsData(json_data.as<String>());
json_data.clear();
data_a = data_b = false;
data_loop = millis();
} }
} //////////////// INNER LOOP ///////////////////// } //////////////// INNER LOOP /////////////////////

View File

@@ -360,7 +360,7 @@ void rtIgnitionTask::run()
m_info_filtered.update(m_last_status); m_info_filtered.update(m_last_status);
(*m_active_history)[m_counter_status] = 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); m_on_message_cb(m_info_filtered);
} }

View File

@@ -1,10 +1,19 @@
#include <webserver.h> #include <webserver.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
static const std::map<const std::string, AstroWebServer::c_commandEnum> s_webserverCommands = { static std::map<const std::string, AstroWebServer::c_commandEnum> s_webserverCommands = {
{"setTime", AstroWebServer::SET_TIME}, {"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) 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");
@@ -20,11 +29,15 @@ AstroWebServer::AstroWebServer(const uint8_t port, fs::FS &filesystem) : m_port(
{ onUploadHandler(request, filename, index, data, len, final); }); { onUploadHandler(request, filename, index, data, len, final); });
m_webserver.begin(); 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"); LOG_DEBUG("Webserver Init OK");
} }
AstroWebServer::~AstroWebServer() AstroWebServer::~AstroWebServer()
{ {
xTimerDelete(m_pingTimer, pdMS_TO_TICKS(10));
m_webserver.removeHandler(&m_websocket); m_webserver.removeHandler(&m_websocket);
m_webserver.end(); m_webserver.end();
} }
@@ -47,6 +60,9 @@ void AstroWebServer::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *cli
case WS_EVT_DISCONNECT: case WS_EVT_DISCONNECT:
LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] DISCONNECTED"); LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] DISCONNECTED");
break; break;
case WS_EVT_PONG:
LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] PONG");
break;
case WS_EVT_DATA: case WS_EVT_DATA:
{ {
AwsFrameInfo *info = (AwsFrameInfo *)arg; AwsFrameInfo *info = (AwsFrameInfo *)arg;

View File

@@ -38,6 +38,7 @@ private:
AsyncWebSocket m_websocket; AsyncWebSocket m_websocket;
bool m_uploadFailed = false; bool m_uploadFailed = false;
fs::File m_uploadFile; fs::File m_uploadFile;
TimerHandle_t m_pingTimer = NULL;
public: public:
enum c_commandEnum enum c_commandEnum