Fixed Filters, split file in html, script and css

This commit is contained in:
Emanuele Trabattoni
2026-04-08 16:55:03 +02:00
parent 4dc45954e9
commit 12e1e8e7a4
10 changed files with 167 additions and 155 deletions

View File

@@ -3,37 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>ESP32 Dashboard</title> <title>ESP32 Dashboard</title>
<style> <link rel="stylesheet" href="style.css">
body {
font-family: Arial;
text-align: center;
margin-top: 40px;
}
table {
margin: auto;
border-collapse: collapse;
width: 100%;
max-width: 900px;
}
th, td {
border: 1px solid #ccc;
padding: 10px;
font-size: 16px;
text-align: left;
}
th {
background-color: #f4f4f4;
}
button {
margin: 10px;
padding: 10px 20px;
font-size: 16px;
}
</style>
</head> </head>
<body> <body>
@@ -44,6 +14,7 @@
<div style="max-width: 900px; margin: 0 auto; text-align: left;"> <div style="max-width: 900px; margin: 0 auto; text-align: left;">
<p><strong>Timestamp:</strong> <span id="timestamp">-</span></p> <p><strong>Timestamp:</strong> <span id="timestamp">-</span></p>
<p><strong>Data Valid:</strong> <span id="datavalid">-</span></p>
<p><strong>Generator voltage:</strong> <span id="volts_gen">-</span></p> <p><strong>Generator voltage:</strong> <span id="volts_gen">-</span></p>
<p><strong>Engine RPM:</strong> <span id="eng_rpm">-</span></p> <p><strong>Engine RPM:</strong> <span id="eng_rpm">-</span></p>
<p><strong>ADC read time:</strong> <span id="adc_read_time">-</span></p> <p><strong>ADC read time:</strong> <span id="adc_read_time">-</span></p>
@@ -58,16 +29,6 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr>
<td>Trigger time</td>
<td id="coils12_trig_time">-</td>
<td id="coils34_trig_time">-</td>
</tr>
<tr>
<td>Spark time</td>
<td id="coils12_spark_time">-</td>
<td id="coils34_spark_time">-</td>
</tr>
<tr> <tr>
<td>Spark delay</td> <td>Spark delay</td>
<td id="coils12_spark_delay">-</td> <td id="coils12_spark_delay">-</td>
@@ -122,77 +83,6 @@
</table> </table>
</div> </div>
<script> <script src="script.js"></script>
let ws;
function connectWS() {
ws = new WebSocket("ws://" + location.host + "/ws");
ws.onopen = () => {
console.log("WebSocket connesso");
};
ws.onclose = () => {
console.log("WebSocket disconnesso, retry...");
setTimeout(connectWS, 1000);
};
ws.onmessage = (event) => {
let data;
try {
data = JSON.parse(event.data);
} catch (e) {
console.error("Invalid JSON received", e);
return;
}
document.getElementById("timestamp").textContent = data.timestamp ?? "-";
document.getElementById("volts_gen").textContent = data.volts_gen ?? "-";
document.getElementById("eng_rpm").textContent = data.eng_rpm ?? "-";
document.getElementById("adc_read_time").textContent = data.adc_read_time ?? "-";
document.getElementById("n_queue_errors").textContent = data.n_queue_errors ?? "-";
const coils12 = data.coils12 || {};
const coils34 = data.coils34 || {};
document.getElementById("coils12_trig_time").textContent = coils12.trig_time ?? "-";
document.getElementById("coils34_trig_time").textContent = coils34.trig_time ?? "-";
document.getElementById("coils12_spark_time").textContent = coils12.spark_time ?? "-";
document.getElementById("coils34_spark_time").textContent = coils34.spark_time ?? "-";
document.getElementById("coils12_spark_delay").textContent = coils12.spark_delay ?? "-";
document.getElementById("coils34_spark_delay").textContent = coils34.spark_delay ?? "-";
document.getElementById("coils12_spark_status").textContent = coils12.spark_status ?? "-";
document.getElementById("coils34_spark_status").textContent = coils34.spark_status ?? "-";
document.getElementById("coils12_sstart_status").textContent = coils12.sstart_status ?? "-";
document.getElementById("coils34_sstart_status").textContent = coils34.sstart_status ?? "-";
document.getElementById("coils12_peak_p_in").textContent = coils12.peak_p_in ?? "-";
document.getElementById("coils34_peak_p_in").textContent = coils34.peak_p_in ?? "-";
document.getElementById("coils12_peak_n_in").textContent = coils12.peak_n_in ?? "-";
document.getElementById("coils34_peak_n_in").textContent = coils34.peak_n_in ?? "-";
document.getElementById("coils12_peak_p_out").textContent = coils12.peak_p_out ?? "-";
document.getElementById("coils34_peak_p_out").textContent = coils34.peak_p_out ?? "-";
document.getElementById("coils12_peak_n_out").textContent = coils12.peak_n_out ?? "-";
document.getElementById("coils34_peak_n_out").textContent = coils34.peak_n_out ?? "-";
document.getElementById("coils12_level_spark").textContent = coils12.level_spark ?? "-";
document.getElementById("coils34_level_spark").textContent = coils34.level_spark ?? "-";
document.getElementById("coils12_n_events").textContent = coils12.n_events ?? "-";
document.getElementById("coils34_n_events").textContent = coils34.n_events ?? "-";
document.getElementById("coils12_n_missed_firing").textContent = coils12.n_missed_firing ?? "-";
document.getElementById("coils34_n_missed_firing").textContent = coils34.n_missed_firing ?? "-";
};
}
function start() {
fetch("/start");
}
function stop() {
fetch("/stop");
}
connectWS();
</script>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,66 @@
let ws;
function connectWS() {
ws = new WebSocket("ws://" + location.host + "/ws");
ws.onopen = () => {
console.log("WebSocket connesso");
};
ws.onclose = () => {
console.log("WebSocket disconnesso, retry...");
setTimeout(connectWS, 5000);
};
ws.onmessage = (event) => {
let data;
try {
data = JSON.parse(event.data);
} catch (e) {
console.error("Invalid JSON received", e);
return;
}
document.getElementById("datavalid").textContent = data.datavalid ?? "-";
document.getElementById("timestamp").textContent = data.timestamp ?? "-";
document.getElementById("volts_gen").textContent = data.volts_gen ?? "-";
document.getElementById("eng_rpm").textContent = data.eng_rpm ?? "-";
document.getElementById("adc_read_time").textContent = data.adc_read_time ?? "-";
document.getElementById("n_queue_errors").textContent = data.n_queue_errors ?? "-";
const coils12 = data.coils12 || {};
const coils34 = data.coils34 || {};
document.getElementById("coils12_spark_delay").textContent = coils12.spark_delay ?? "-";
document.getElementById("coils34_spark_delay").textContent = coils34.spark_delay ?? "-";
document.getElementById("coils12_spark_status").textContent = coils12.spark_status ?? "-";
document.getElementById("coils34_spark_status").textContent = coils34.spark_status ?? "-";
document.getElementById("coils12_sstart_status").textContent = coils12.sstart_status ?? "-";
document.getElementById("coils34_sstart_status").textContent = coils34.sstart_status ?? "-";
document.getElementById("coils12_peak_p_in").textContent = coils12.peak_p_in ?? "-";
document.getElementById("coils34_peak_p_in").textContent = coils34.peak_p_in ?? "-";
document.getElementById("coils12_peak_n_in").textContent = coils12.peak_n_in ?? "-";
document.getElementById("coils34_peak_n_in").textContent = coils34.peak_n_in ?? "-";
document.getElementById("coils12_peak_p_out").textContent = coils12.peak_p_out ?? "-";
document.getElementById("coils34_peak_p_out").textContent = coils34.peak_p_out ?? "-";
document.getElementById("coils12_peak_n_out").textContent = coils12.peak_n_out ?? "-";
document.getElementById("coils34_peak_n_out").textContent = coils34.peak_n_out ?? "-";
document.getElementById("coils12_level_spark").textContent = coils12.level_spark ?? "-";
document.getElementById("coils34_level_spark").textContent = coils34.level_spark ?? "-";
document.getElementById("coils12_n_events").textContent = coils12.n_events ?? "-";
document.getElementById("coils34_n_events").textContent = coils34.n_events ?? "-";
document.getElementById("coils12_n_missed_firing").textContent = coils12.n_missed_firing ?? "-";
document.getElementById("coils34_n_missed_firing").textContent = coils34.n_missed_firing ?? "-";
};
}
function start() {
fetch("/start");
}
function stop() {
fetch("/stop");
}
connectWS();

View File

@@ -0,0 +1,29 @@
body {
font-family: Arial;
text-align: center;
margin-top: 40px;
}
table {
margin: auto;
border-collapse: collapse;
width: 100%;
max-width: 900px;
}
th, td {
border: 1px solid #ccc;
padding: 10px;
font-size: 16px;
text-align: left;
}
th {
background-color: #f4f4f4;
}
button {
margin: 10px;
padding: 10px 20px;
font-size: 16px;
}

View File

@@ -1,7 +1,20 @@
#include "datasave.h" #include "datasave.h"
#include <math.h>
static const size_t min_free = 1024 * 1024; // minimum free space in SPIFFS to allow saving history (1MB) static const size_t min_free = 1024 * 1024; // minimum free space in SPIFFS to allow saving history (1MB)
void ignitionBoxStatusAverage::filter(int32_t &old, const int32_t value, const uint32_t k)
{
float alpha = 1.0f / (float)k;
old = old + (int32_t)(alpha * (float)(value - old));
}
void ignitionBoxStatusAverage::filter(float &old, const float value, const uint32_t k)
{
float alpha = 1.0f / (float)k;
old = old + (float)(alpha * (float)(value - old));
}
void ignitionBoxStatusAverage::reset() void ignitionBoxStatusAverage::reset()
{ {
m_last = ignitionBoxStatus(); m_last = ignitionBoxStatus();
@@ -11,12 +24,11 @@ void ignitionBoxStatusAverage::reset()
void ignitionBoxStatusAverage::update(const ignitionBoxStatus &new_status) void ignitionBoxStatusAverage::update(const ignitionBoxStatus &new_status)
{ {
if (m_count == 0) if (m_count == 0 && !m_data_valid)
{ {
m_last = new_status; m_last = new_status;
} }
else m_count++;
{
// simple moving average calculation // simple moving average calculation
m_last.timestamp = new_status.timestamp; // keep timestamp of latest status m_last.timestamp = new_status.timestamp; // keep timestamp of latest status
@@ -24,27 +36,25 @@ void ignitionBoxStatusAverage::update(const ignitionBoxStatus &new_status)
m_last.coils12.n_missed_firing = new_status.coils12.n_missed_firing; // sum missed firings instead of averaging m_last.coils12.n_missed_firing = new_status.coils12.n_missed_firing; // sum missed firings instead of averaging
m_last.coils12.spark_status = new_status.coils12.spark_status; // take latest spark status m_last.coils12.spark_status = new_status.coils12.spark_status; // take latest spark status
m_last.coils12.sstart_status = new_status.coils12.sstart_status; // take latest soft start status m_last.coils12.sstart_status = new_status.coils12.sstart_status; // take latest soft start status
m_last.coils12.spark_delay += (uint32_t)(0.1f * (float)(new_status.coils12.spark_delay - m_last.coils12.spark_delay)); // incremental average calculation filter(m_last.coils12.spark_delay, new_status.coils12.spark_delay, m_max_count); // incremental average calculation
m_last.coils12.peak_p_in += 1.0f / m_max_count * (new_status.coils12.peak_p_in - m_last.coils12.peak_p_in); // incremental average calculation filter(m_last.coils12.peak_p_in, new_status.coils12.peak_p_in, m_max_count); // incremental average calculation
m_last.coils12.peak_n_in += 1.0f / m_max_count * (new_status.coils12.peak_n_in - m_last.coils12.peak_n_in); // incremental average calculation filter(m_last.coils12.peak_n_in, new_status.coils12.peak_n_in, m_max_count); // incremental average calculation
m_last.coils12.peak_p_out += 1.0f / m_max_count * (new_status.coils12.peak_p_out - m_last.coils12.peak_p_out); // incremental average calculation filter(m_last.coils12.peak_p_out, new_status.coils12.peak_p_out, m_max_count); // incremental average calculation
m_last.coils12.peak_n_out += 1.0f / m_max_count * (new_status.coils12.peak_n_out - m_last.coils12.peak_n_out); // incremental average calculation filter(m_last.coils12.peak_n_out, new_status.coils12.peak_n_out, m_max_count); // incremental average calculation
m_last.coils34.n_events = new_status.coils34.n_events; // sum events instead of averaging m_last.coils34.n_events = new_status.coils34.n_events; // sum events instead of averaging
m_last.coils34.n_missed_firing = new_status.coils34.n_missed_firing; // sum missed firings instead of averaging m_last.coils34.n_missed_firing = new_status.coils34.n_missed_firing; // sum missed firings instead of averaging
m_last.coils34.spark_status = new_status.coils34.spark_status; // take latest spark status m_last.coils34.spark_status = new_status.coils34.spark_status; // take latest spark status
m_last.coils34.sstart_status = new_status.coils34.sstart_status; // take latest soft start status m_last.coils34.sstart_status = new_status.coils34.sstart_status; // take latest soft start status
m_last.coils34.spark_delay += (uint32_t)(0.1f * (float)(new_status.coils34.spark_delay - m_last.coils34.spark_delay)); // incremental average calculation filter(m_last.coils34.spark_delay, new_status.coils34.spark_delay, m_max_count); // incremental average calculation
m_last.coils34.peak_p_in += 1.0f / m_max_count * (new_status.coils34.peak_p_in - m_last.coils34.peak_p_in); // incremental average calculation filter(m_last.coils34.peak_p_in, new_status.coils34.peak_p_in, m_max_count); // incremental average calculation
m_last.coils34.peak_n_in += 1.0f / m_max_count * (new_status.coils34.peak_n_in - m_last.coils34.peak_n_in); // incremental average calculation filter(m_last.coils34.peak_n_in, new_status.coils34.peak_n_in, m_max_count); // incremental average calculation
m_last.coils34.peak_p_out += 1.0f / m_max_count * (new_status.coils34.peak_p_out - m_last.coils34.peak_p_out); // incremental average calculation filter(m_last.coils34.peak_p_out, new_status.coils34.peak_p_out, m_max_count); // incremental average calculation
m_last.coils34.peak_n_out += 1.0f / m_max_count * (new_status.coils34.peak_n_out - m_last.coils34.peak_n_out); // incremental average calculation filter(m_last.coils34.peak_n_out, new_status.coils34.peak_n_out, m_max_count); // incremental average calculation
filter(m_last.eng_rpm, new_status.eng_rpm, m_max_count); // incremental average calculation // incremental average calculation
m_last.eng_rpm += (uint32_t)(0.1f * (float)(new_status.eng_rpm - m_last.eng_rpm)); // incremental average calculation filter(m_last.adc_read_time, m_last.adc_read_time, m_max_count); // incremental average calculation
m_last.adc_read_time += (uint32_t)(0.1f * (float)(new_status.adc_read_time - m_last.adc_read_time)); // incremental average calculation
m_last.n_queue_errors = new_status.n_queue_errors; // take last of queue errors since it's a cumulative count of errors in the queue, not an average value m_last.n_queue_errors = new_status.n_queue_errors; // take last of queue errors since it's a cumulative count of errors in the queue, not an average value
}
m_count++;
if (m_count >= m_max_count) if (m_count >= m_max_count)
{ {
m_count = 0; // reset count after reaching max samples to average m_count = 0; // reset count after reaching max samples to average
@@ -67,6 +77,7 @@ const ArduinoJson::JsonDocument ignitionBoxStatusAverage::toJson() const
if (m_data_valid) if (m_data_valid)
{ {
doc["timestamp"] = m_last.timestamp; doc["timestamp"] = m_last.timestamp;
doc["datavalid"] = m_data_valid ? "TRUE" : "FALSE";
doc["coils12"]["n_events"] = m_last.coils12.n_events; doc["coils12"]["n_events"] = m_last.coils12.n_events;
doc["coils12"]["n_missed_firing"] = m_last.coils12.n_missed_firing; doc["coils12"]["n_missed_firing"] = m_last.coils12.n_missed_firing;
@@ -113,8 +124,9 @@ void saveHistoryTask(void *pvParameters)
void save_history(const PSRAMVector<ignitionBoxStatus> &history, const std::filesystem::path &file_name) void save_history(const PSRAMVector<ignitionBoxStatus> &history, const std::filesystem::path &file_name)
{ {
// Initialize SPIFFS // Initialize SPIFFS
if (!SAVE_HISTORY_TO_SPIFFS) return; if (!SAVE_HISTORY_TO_SPIFFS)
//auto spiffs_guard = SPIFFSGuard(); // use RAII guard to ensure SPIFFS is properly mounted and unmounted return;
// auto spiffs_guard = SPIFFSGuard(); // use RAII guard to ensure SPIFFS is properly mounted and unmounted
if (SPIFFS.totalBytes() - SPIFFS.usedBytes() < min_free) // check if at least 1MB is free for saving history if (SPIFFS.totalBytes() - SPIFFS.usedBytes() < min_free) // check if at least 1MB is free for saving history
{ {

View File

@@ -53,12 +53,19 @@ private:
public: public:
ignitionBoxStatusAverage() = default; ignitionBoxStatusAverage() = default;
ignitionBoxStatusAverage(const uint32_t max_count) : m_max_count(max_count) {} ignitionBoxStatusAverage(const uint32_t max_count) : m_max_count(max_count) {
m_data_valid = false;
m_count = 0;
}
void reset(); void reset();
void update(const ignitionBoxStatus &new_status); void update(const ignitionBoxStatus &new_status);
const bool get(ignitionBoxStatus &status) const; const bool get(ignitionBoxStatus &status) const;
const ArduinoJson::JsonDocument toJson() const; const ArduinoJson::JsonDocument toJson() const;
private:
void filter(int32_t &old, const int32_t value, const uint32_t k);
void filter(float &old, const float value, const uint32_t k);
}; };
// Task and function declarations // Task and function declarations

View File

@@ -61,7 +61,7 @@ struct coilsStatus
{ {
int64_t trig_time = 0; int64_t trig_time = 0;
int64_t spark_time = 0; int64_t spark_time = 0;
uint32_t spark_delay = 0; // in microseconds int32_t spark_delay = 0; // in microseconds
sparkStatus spark_status = sparkStatus::SPARK_POS_OK; sparkStatus spark_status = sparkStatus::SPARK_POS_OK;
softStartStatus sstart_status = softStartStatus::NORMAL; softStartStatus sstart_status = softStartStatus::NORMAL;
float peak_p_in = 0.0; float peak_p_in = 0.0;
@@ -83,8 +83,8 @@ struct ignitionBoxStatus
// voltage from generator // voltage from generator
float volts_gen = 0.0; float volts_gen = 0.0;
// enine rpm // enine rpm
uint32_t eng_rpm = 0; int32_t eng_rpm = 0;
// debug values // debug values
uint32_t n_queue_errors = 0; uint32_t n_queue_errors = 0;
uint32_t adc_read_time = 0; int32_t adc_read_time = 0;
}; };

View File

@@ -258,7 +258,7 @@ void loop()
ignA_avg.update(ign_info); // update moving average with latest ignition status ignA_avg.update(ign_info); // update moving average with latest ignition status
Serial.print("Data Received: " + String(counter) + "/" + String(hist.size()) + '\r'); Serial.print("Data Received: " + String(counter) + "/" + String(hist.size()) + '\r');
if (ws.count() > 0 && counter % max_queue == 0) if (ws.count() > 0 && counter % 10 == 0) // send data every 10 samples
{ {
Serial.println(); Serial.println();
LOG_INFO("Sending average ignition status to websocket clients..."); LOG_INFO("Sending average ignition status to websocket clients...");

View File

@@ -157,7 +157,7 @@ void rtIgnitionTask(void *pvParameters)
auto current_time = esp_timer_get_time(); auto current_time = esp_timer_get_time();
auto cycle_time = current_time - last_cycle_time; auto cycle_time = current_time - last_cycle_time;
last_cycle_time = current_time; last_cycle_time = current_time;
ign_box_sts.eng_rpm = (uint32_t)(60.0f / (cycle_time / 1000000.0f)); ign_box_sts.eng_rpm = (int32_t)(60.0f / (cycle_time / 1000000.0f));
} }
case TRIG_FLAG_12N: case TRIG_FLAG_12N:
coils = &ign_box_sts.coils12; coils = &ign_box_sts.coils12;
@@ -177,7 +177,7 @@ void rtIgnitionTask(void *pvParameters)
// Timeout not occourred, expected POSITIVE edge spark OCCOURRED // Timeout not occourred, expected POSITIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_TIMEOUT) if (spark_flag != SPARK_FLAG_TIMEOUT)
{ {
coils->spark_delay = (uint32_t)(coils->spark_time - coils->trig_time); coils->spark_delay = (int32_t)(coils->spark_time - coils->trig_time);
coils->sstart_status = softStartStatus::NORMAL; // because spark on positive edge coils->sstart_status = softStartStatus::NORMAL; // because spark on positive edge
coils->spark_status = sparkStatus::SPARK_POS_OK; // do not wait for spark on negative edge coils->spark_status = sparkStatus::SPARK_POS_OK; // do not wait for spark on negative edge
} }
@@ -197,7 +197,7 @@ void rtIgnitionTask(void *pvParameters)
// Timeout not occourred, expected NEGATIVE edge spark OCCOURRED // Timeout not occourred, expected NEGATIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_TIMEOUT && expected_negative) if (spark_flag != SPARK_FLAG_TIMEOUT && expected_negative)
{ {
coils->spark_delay = (uint32_t)(coils->spark_time - coils->trig_time); coils->spark_delay = (int32_t)(coils->spark_time - coils->trig_time);
coils->sstart_status = softStartStatus::SOFT_START; coils->sstart_status = softStartStatus::SOFT_START;
coils->spark_status = sparkStatus::SPARK_NEG_OK; coils->spark_status = sparkStatus::SPARK_NEG_OK;
} }
@@ -248,7 +248,7 @@ void rtIgnitionTask(void *pvParameters)
ign_box_sts.coils12.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_12N_OUT); ign_box_sts.coils12.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_12N_OUT);
ign_box_sts.coils34.peak_p_out = adcReadChannel(adc, ADC_CH_PEAK_34P_OUT); ign_box_sts.coils34.peak_p_out = adcReadChannel(adc, ADC_CH_PEAK_34P_OUT);
ign_box_sts.coils34.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_34N_OUT); ign_box_sts.coils34.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_34N_OUT);
ign_box_sts.adc_read_time = (uint32_t)(esp_timer_get_time() - start_adc_read); ign_box_sts.adc_read_time = (int32_t)(esp_timer_get_time() - start_adc_read);
} }
else // simulate adc read timig else // simulate adc read timig
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));

View File

@@ -14,6 +14,11 @@ void setCursor(const uint8_t x, const uint8_t y)
} }
void printField(const char name[], const uint32_t val) void printField(const char name[], const uint32_t val)
{
Serial.printf("%15s: %06u\n", name, val);
}
void printField(const char name[], const int32_t val)
{ {
Serial.printf("%15s: %06d\n", name, val); Serial.printf("%15s: %06d\n", name, val);
} }

View File

@@ -7,6 +7,7 @@
void clearScreen(); void clearScreen();
void setCursor(const uint8_t x, const uint8_t y); void setCursor(const uint8_t x, const uint8_t y);
void printField(const char name[], const uint32_t val); void printField(const char name[], const uint32_t val);
void printField(const char name[], const int32_t val);
void printField(const char name[], const int64_t val); void printField(const char name[], const int64_t val);
void printField(const char name[], const float val); void printField(const char name[], const float val);
void printField(const char name[], const char *val); void printField(const char name[], const char *val);
@@ -60,6 +61,7 @@ static const std::string htmlTest = R"rawliteral(
<div style="max-width: 900px; margin: 0 auto; text-align: left;"> <div style="max-width: 900px; margin: 0 auto; text-align: left;">
<p><strong>Timestamp:</strong> <span id="timestamp">-</span></p> <p><strong>Timestamp:</strong> <span id="timestamp">-</span></p>
<p><strong>Data Valid:</strong> <span id="datavalid">-</span></p>
<p><strong>Generator voltage:</strong> <span id="volts_gen">-</span></p> <p><strong>Generator voltage:</strong> <span id="volts_gen">-</span></p>
<p><strong>Engine RPM:</strong> <span id="eng_rpm">-</span></p> <p><strong>Engine RPM:</strong> <span id="eng_rpm">-</span></p>
<p><strong>ADC read time:</strong> <span id="adc_read_time">-</span></p> <p><strong>ADC read time:</strong> <span id="adc_read_time">-</span></p>
@@ -153,6 +155,7 @@ static const std::string htmlTest = R"rawliteral(
return; return;
} }
document.getElementById("datavalid").textContent = data.datavalid ?? "-";
document.getElementById("timestamp").textContent = data.timestamp ?? "-"; document.getElementById("timestamp").textContent = data.timestamp ?? "-";
document.getElementById("volts_gen").textContent = data.volts_gen ?? "-"; document.getElementById("volts_gen").textContent = data.volts_gen ?? "-";
document.getElementById("eng_rpm").textContent = data.eng_rpm ?? "-"; document.getElementById("eng_rpm").textContent = data.eng_rpm ?? "-";