Webpage is OK, html in memory since SPIFFS is to slow.
Moving average to be fixed
This commit is contained in:
198
RotaxMonitor/data/index.html
Normal file
198
RotaxMonitor/data/index.html
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>ESP32 Dashboard</title>
|
||||||
|
<style>
|
||||||
|
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>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2>RotaxMonitor realtime data</h2>
|
||||||
|
|
||||||
|
<button onclick="start()">Start</button>
|
||||||
|
<button onclick="stop()">Stop</button>
|
||||||
|
|
||||||
|
<div style="max-width: 900px; margin: 0 auto; text-align: left;">
|
||||||
|
<p><strong>Timestamp:</strong> <span id="timestamp">-</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>ADC read time:</strong> <span id="adc_read_time">-</span></p>
|
||||||
|
<p><strong>Queue errors:</strong> <span id="n_queue_errors">-</span></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Property</th>
|
||||||
|
<th>Coils 12</th>
|
||||||
|
<th>Coils 34</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<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>
|
||||||
|
<td>Spark delay</td>
|
||||||
|
<td id="coils12_spark_delay">-</td>
|
||||||
|
<td id="coils34_spark_delay">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Spark status</td>
|
||||||
|
<td id="coils12_spark_status">-</td>
|
||||||
|
<td id="coils34_spark_status">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Soft start status</td>
|
||||||
|
<td id="coils12_sstart_status">-</td>
|
||||||
|
<td id="coils34_sstart_status">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Peak P in</td>
|
||||||
|
<td id="coils12_peak_p_in">-</td>
|
||||||
|
<td id="coils34_peak_p_in">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Peak N in</td>
|
||||||
|
<td id="coils12_peak_n_in">-</td>
|
||||||
|
<td id="coils34_peak_n_in">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Peak P out</td>
|
||||||
|
<td id="coils12_peak_p_out">-</td>
|
||||||
|
<td id="coils34_peak_p_out">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Peak N out</td>
|
||||||
|
<td id="coils12_peak_n_out">-</td>
|
||||||
|
<td id="coils34_peak_n_out">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Level spark</td>
|
||||||
|
<td id="coils12_level_spark">-</td>
|
||||||
|
<td id="coils34_level_spark">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Events</td>
|
||||||
|
<td id="coils12_n_events">-</td>
|
||||||
|
<td id="coils34_n_events">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Missed firings</td>
|
||||||
|
<td id="coils12_n_missed_firing">-</td>
|
||||||
|
<td id="coils34_n_missed_firing">-</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</html>
|
||||||
@@ -11,60 +11,62 @@
|
|||||||
[env:esp32-s3-devkitc1-n16r8]
|
[env:esp32-s3-devkitc1-n16r8]
|
||||||
board = esp32-s3-devkitc1-n16r8
|
board = esp32-s3-devkitc1-n16r8
|
||||||
board_build.partitions = partitions/no_ota_10mb_spiffs.csv
|
board_build.partitions = partitions/no_ota_10mb_spiffs.csv
|
||||||
|
board_build.filesystem = spiffs
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
hideakitai/DebugLog@^0.8.4
|
hideakitai/DebugLog@^0.8.4
|
||||||
bblanchon/ArduinoJson@^7.4.2
|
bblanchon/ArduinoJson@^7.4.2
|
||||||
hideakitai/PCA95x5@^0.1.3
|
hideakitai/PCA95x5@^0.1.3
|
||||||
adafruit/Adafruit SSD1306@^2.5.16
|
me-no-dev/AsyncTCP@^3.3.2
|
||||||
garfius/Menu-UI@^1.2.0
|
me-no-dev/ESPAsyncWebServer@^3.6.0
|
||||||
|
|
||||||
;Upload protocol configuration
|
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
upload_port = COM8
|
upload_port = COM8
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
|
|
||||||
;Monitor configuration
|
|
||||||
monitor_port = COM4
|
monitor_port = COM4
|
||||||
monitor_speed = 921600
|
monitor_speed = 921600
|
||||||
|
|
||||||
; Build configuration
|
|
||||||
build_type = release
|
build_type = release
|
||||||
build_flags =
|
build_flags =
|
||||||
-DARDUINO_USB_CDC_ON_BOOT=0
|
-DCORE_DEBUG_LEVEL=5
|
||||||
-DARDUINO_USB_MODE=0
|
-DARDUINO_USB_CDC_ON_BOOT=0
|
||||||
-fstack-protector-all
|
-DARDUINO_USB_MODE=0
|
||||||
-DCONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=1
|
-DCONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=1
|
||||||
-DCONFIG_FREERTOS_USE_TRACE_FACILITY=1
|
-DCONFIG_FREERTOS_USE_TRACE_FACILITY=1
|
||||||
|
-DCONFIG_ASYNC_TCP_MAX_ACK_TIME=5000
|
||||||
|
-DCONFIG_ASYNC_TCP_PRIORITY=20
|
||||||
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
|
-DCONFIG_ASYNC_TCP_RUNNING_CORE=1
|
||||||
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
|
-fstack-protector-all
|
||||||
|
|
||||||
[env:esp32-s3-devkitc1-n16r8-debug]
|
[env:esp32-s3-devkitc1-n16r8-debug]
|
||||||
board = ${env:esp32-s3-devkitc1-n16r8.board}
|
board = ${env:esp32-s3-devkitc1-n16r8.board}
|
||||||
board_build.partitions = ${env:esp32-s3-devkitc1-n16r8.board_build.partitions}
|
board_build.partitions = ${env:esp32-s3-devkitc1-n16r8.board_build.partitions}
|
||||||
|
board_build.filesystem = ${env:esp32-s3-devkitc1-n16r8.board_build.filesystem}
|
||||||
platform = ${env:esp32-s3-devkitc1-n16r8.platform}
|
platform = ${env:esp32-s3-devkitc1-n16r8.platform}
|
||||||
framework = ${env:esp32-s3-devkitc1-n16r8.framework}
|
framework = ${env:esp32-s3-devkitc1-n16r8.framework}
|
||||||
lib_deps = ${env:esp32-s3-devkitc1-n16r8.lib_deps}
|
lib_deps =
|
||||||
|
${env:esp32-s3-devkitc1-n16r8.lib_deps}
|
||||||
;Upload protocol configuration
|
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
upload_port = COM8
|
upload_port = COM8
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
|
|
||||||
;Monitor configuration
|
|
||||||
monitor_port = COM4
|
monitor_port = COM4
|
||||||
monitor_speed = 921600
|
monitor_speed = 921600
|
||||||
|
|
||||||
; Debug configuration
|
|
||||||
debug_tool = esp-builtin
|
debug_tool = esp-builtin
|
||||||
debug_speed = 15000
|
debug_speed = 15000
|
||||||
|
|
||||||
; Build configuration
|
|
||||||
build_type = debug
|
build_type = debug
|
||||||
build_flags =
|
build_flags =
|
||||||
-O0
|
-O0
|
||||||
-g3
|
-g3
|
||||||
-ggdb3
|
-ggdb3
|
||||||
-DCORE_DEBUG_LEVEL=5
|
-DCORE_DEBUG_LEVEL=5
|
||||||
-DARDUINO_USB_CDC_ON_BOOT=0
|
-DARDUINO_USB_CDC_ON_BOOT=0
|
||||||
-DARDUINO_USB_MODE=0
|
-DARDUINO_USB_MODE=0
|
||||||
-fstack-protector-all
|
-DCONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=1
|
||||||
|
-DCONFIG_FREERTOS_USE_TRACE_FACILITY=1
|
||||||
|
-DCONFIG_ASYNC_TCP_MAX_ACK_TIME=5000
|
||||||
|
-DCONFIG_ASYNC_TCP_PRIORITY=20
|
||||||
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
|
-DCONFIG_ASYNC_TCP_RUNNING_CORE=1
|
||||||
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
|
-fstack-protector-all
|
||||||
|
|||||||
@@ -2,12 +2,106 @@
|
|||||||
|
|
||||||
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::reset()
|
||||||
|
{
|
||||||
|
m_last = ignitionBoxStatus();
|
||||||
|
m_count = 0;
|
||||||
|
m_data_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ignitionBoxStatusAverage::update(const ignitionBoxStatus &new_status)
|
||||||
|
{
|
||||||
|
if (m_count == 0)
|
||||||
|
{
|
||||||
|
m_last = new_status;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// simple moving average calculation
|
||||||
|
m_last.timestamp = new_status.timestamp; // keep timestamp of latest status
|
||||||
|
|
||||||
|
m_last.coils12.n_events = new_status.coils12.n_events; // sum events 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.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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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.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.spark_delay += (uint32_t)(0.1f * (float)(new_status.coils34.spark_delay - m_last.coils34.spark_delay)); // 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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
m_last.eng_rpm += (uint32_t)(0.1f * (float)(new_status.eng_rpm - m_last.eng_rpm)); // 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_count++;
|
||||||
|
if (m_count >= m_max_count)
|
||||||
|
{
|
||||||
|
m_count = 0; // reset count after reaching max samples to average
|
||||||
|
m_data_valid = true; // set data valid flag after first average is calculated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool ignitionBoxStatusAverage::get(ignitionBoxStatus &status) const
|
||||||
|
{
|
||||||
|
if (m_data_valid)
|
||||||
|
{
|
||||||
|
status = m_last;
|
||||||
|
}
|
||||||
|
return m_data_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ArduinoJson::JsonDocument ignitionBoxStatusAverage::toJson() const
|
||||||
|
{
|
||||||
|
ArduinoJson::JsonDocument doc;
|
||||||
|
if (m_data_valid)
|
||||||
|
{
|
||||||
|
doc["timestamp"] = m_last.timestamp;
|
||||||
|
|
||||||
|
doc["coils12"]["n_events"] = m_last.coils12.n_events;
|
||||||
|
doc["coils12"]["n_missed_firing"] = m_last.coils12.n_missed_firing;
|
||||||
|
doc["coils12"]["spark_delay"] = m_last.coils12.spark_delay;
|
||||||
|
doc["coils12"]["spark_status"] = sparkStatusNames.at(m_last.coils12.spark_status);
|
||||||
|
doc["coils12"]["peak_p_in"] = m_last.coils12.peak_p_in;
|
||||||
|
doc["coils12"]["peak_n_in"] = m_last.coils12.peak_n_in;
|
||||||
|
doc["coils12"]["peak_p_out"] = m_last.coils12.peak_p_out;
|
||||||
|
doc["coils12"]["peak_n_out"] = m_last.coils12.peak_n_out;
|
||||||
|
doc["coils12"]["sstart_status"] = softStartStatusNames.at(m_last.coils12.sstart_status);
|
||||||
|
|
||||||
|
doc["coils34"]["n_events"] = m_last.coils34.n_events;
|
||||||
|
doc["coils34"]["n_missed_firing"] = m_last.coils34.n_missed_firing;
|
||||||
|
doc["coils34"]["spark_delay"] = m_last.coils34.spark_delay;
|
||||||
|
doc["coils34"]["spark_status"] = sparkStatusNames.at(m_last.coils34.spark_status);
|
||||||
|
doc["coils34"]["peak_p_in"] = m_last.coils34.peak_p_in;
|
||||||
|
doc["coils34"]["peak_n_in"] = m_last.coils34.peak_n_in;
|
||||||
|
doc["coils34"]["peak_p_out"] = m_last.coils34.peak_p_out;
|
||||||
|
doc["coils34"]["peak_n_out"] = m_last.coils34.peak_n_out;
|
||||||
|
doc["coils34"]["sstart_status"] = softStartStatusNames.at(m_last.coils34.sstart_status);
|
||||||
|
|
||||||
|
doc["eng_rpm"] = m_last.eng_rpm;
|
||||||
|
doc["adc_read_time"] = m_last.adc_read_time;
|
||||||
|
doc["n_queue_errors"] = m_last.n_queue_errors;
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
void saveHistoryTask(void *pvParameters)
|
void saveHistoryTask(void *pvParameters)
|
||||||
{
|
{
|
||||||
const auto *params = static_cast<dataSaveParams *>(pvParameters);
|
const auto *params = static_cast<dataSaveParams *>(pvParameters);
|
||||||
const auto &history = *params->history;
|
const auto &history = *params->history;
|
||||||
const auto &file_path = params->file_path;
|
const auto &file_path = params->file_path;
|
||||||
if (!params) {
|
if (!params)
|
||||||
|
{
|
||||||
LOG_ERROR("Invalid parameters for saveHistoryTask");
|
LOG_ERROR("Invalid parameters for saveHistoryTask");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -19,9 +113,10 @@ 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
|
||||||
auto spiffs_guard = SPIFFSGuard(); // use RAII guard to ensure SPIFFS is properly mounted and unmounted
|
if (!SAVE_HISTORY_TO_SPIFFS) 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
|
||||||
{
|
{
|
||||||
LOG_ERROR("Not enough space in SPIFFS to save history");
|
LOG_ERROR("Not enough space in SPIFFS to save history");
|
||||||
return;
|
return;
|
||||||
@@ -35,7 +130,7 @@ void save_history(const PSRAMVector<ignitionBoxStatus> &history, const std::file
|
|||||||
if (first_save && SPIFFS.exists(file_path.c_str()))
|
if (first_save && SPIFFS.exists(file_path.c_str()))
|
||||||
{
|
{
|
||||||
first_save = false;
|
first_save = false;
|
||||||
save_flags |= std::ios::trunc; // overwrite existing file
|
save_flags |= std::ios::trunc; // overwrite existing file
|
||||||
SPIFFS.remove(file_path.c_str()); // ensure file is removed before saving to avoid issues with appending to existing file in SPIFFS
|
SPIFFS.remove(file_path.c_str()); // ensure file is removed before saving to avoid issues with appending to existing file in SPIFFS
|
||||||
LOG_INFO("Saving history to SPIFFS, new file:", file_path.c_str());
|
LOG_INFO("Saving history to SPIFFS, new file:", file_path.c_str());
|
||||||
}
|
}
|
||||||
@@ -58,7 +153,8 @@ void save_history(const PSRAMVector<ignitionBoxStatus> &history, const std::file
|
|||||||
ofs << "TS,\
|
ofs << "TS,\
|
||||||
EVENTS_12,DLY_12,STAT_12,V_12_1,V_12_2,V_12_3,V_12_4,IGNITION_MODE_12,\
|
EVENTS_12,DLY_12,STAT_12,V_12_1,V_12_2,V_12_3,V_12_4,IGNITION_MODE_12,\
|
||||||
EVENTS_34,DLY_34,STAT_34,V_34_1,V_34_2,V_34_3,V_34_4,IGNITION_MODE_34,\
|
EVENTS_34,DLY_34,STAT_34,V_34_1,V_34_2,V_34_3,V_34_4,IGNITION_MODE_34,\
|
||||||
ENGINE_RPM,ADC_READTIME,N_QUEUE_ERRORS" << std::endl;
|
ENGINE_RPM,ADC_READTIME,N_QUEUE_ERRORS"
|
||||||
|
<< std::endl;
|
||||||
ofs.flush();
|
ofs.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,15 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
// Project Includes
|
// Project Includes
|
||||||
#include "isr.h"
|
#include "isr.h"
|
||||||
#include "psvector.h"
|
#include "psvector.h"
|
||||||
|
|
||||||
const uint32_t max_history = 256;
|
const uint32_t max_history = 256;
|
||||||
const bool SAVE_HISTORY_TO_SPIFFS = true; // Set to true to enable saving history to SPIFFS, false to disable
|
const bool SAVE_HISTORY_TO_SPIFFS = false; // Set to true to enable saving history to SPIFFS, false to disable
|
||||||
static bool first_save = true; // flag to indicate if this is the first save (to write header)
|
static bool first_save = true; // flag to indicate if this is the first save (to write header)
|
||||||
|
|
||||||
struct dataSaveParams
|
struct dataSaveParams
|
||||||
{
|
{
|
||||||
@@ -26,23 +27,40 @@ struct dataSaveParams
|
|||||||
class SPIFFSGuard
|
class SPIFFSGuard
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SPIFFSGuard() {
|
SPIFFSGuard()
|
||||||
if (!SPIFFS.begin(true)) {
|
{
|
||||||
|
if (!SPIFFS.begin(true))
|
||||||
|
{
|
||||||
LOG_ERROR("Failed to mount SPIFFS");
|
LOG_ERROR("Failed to mount SPIFFS");
|
||||||
LOG_ERROR("5 seconds to restart...");
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
|
||||||
esp_restart();
|
|
||||||
}
|
}
|
||||||
LOG_DEBUG("SPIFFS mounted successfully");
|
LOG_INFO("SPIFFS mounted successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
~SPIFFSGuard() {
|
~SPIFFSGuard()
|
||||||
|
{
|
||||||
SPIFFS.end();
|
SPIFFS.end();
|
||||||
LOG_DEBUG("SPIFFS unmounted successfully");
|
LOG_INFO("SPIFFS unmounted successfully");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ignitionBoxStatusAverage
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
ignitionBoxStatus m_last;
|
||||||
|
uint32_t m_count = 0;
|
||||||
|
uint32_t m_max_count = 100; // number of samples to average before resetting
|
||||||
|
bool m_data_valid = false; // flag to indicate if the average data is valid (i.e. at least one sample has been added)
|
||||||
|
|
||||||
|
public:
|
||||||
|
ignitionBoxStatusAverage() = default;
|
||||||
|
ignitionBoxStatusAverage(const uint32_t max_count) : m_max_count(max_count) {}
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void update(const ignitionBoxStatus &new_status);
|
||||||
|
const bool get(ignitionBoxStatus &status) const;
|
||||||
|
const ArduinoJson::JsonDocument toJson() const;
|
||||||
|
};
|
||||||
|
|
||||||
// Task and function declarations
|
// Task and function declarations
|
||||||
void saveHistoryTask(void *pvParameters);
|
void saveHistoryTask(void *pvParameters);
|
||||||
void save_history(const PSRAMVector<ignitionBoxStatus> &history, const std::filesystem::path& file_path);
|
void save_history(const PSRAMVector<ignitionBoxStatus> &history, const std::filesystem::path &file_path);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
// Device Libraries
|
// Device Libraries
|
||||||
#include <ADS1256.h>
|
#include <ADS1256.h>
|
||||||
#include <AD5292.h>
|
#include <AD5292.h>
|
||||||
#include <Adafruit_SSD1306.h>
|
|
||||||
#include <PCA95x5.h>
|
#include <PCA95x5.h>
|
||||||
|
|
||||||
// ADC Channel mapping
|
// ADC Channel mapping
|
||||||
@@ -23,7 +22,6 @@
|
|||||||
struct Devices {
|
struct Devices {
|
||||||
AD5292 *pot_a = NULL, *pot_b = NULL;
|
AD5292 *pot_a = NULL, *pot_b = NULL;
|
||||||
ADS1256 *adc_a = NULL, *adc_b = NULL;
|
ADS1256 *adc_a = NULL, *adc_b = NULL;
|
||||||
Adafruit_SSD1306* lcd = NULL;
|
|
||||||
PCA9555* io = NULL;
|
PCA9555* io = NULL;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
#include <DebugLog.h>
|
#include <DebugLog.h>
|
||||||
#include <DebugLogEnable.h>
|
#include <DebugLogEnable.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <AsyncTCP.h>
|
||||||
|
|
||||||
// Definitions
|
// Definitions
|
||||||
#include <tasks.h>
|
#include <tasks.h>
|
||||||
@@ -19,6 +23,23 @@
|
|||||||
// #define CH_B_ENABLE
|
// #define CH_B_ENABLE
|
||||||
#define TEST
|
#define TEST
|
||||||
|
|
||||||
|
#define WIFI_SSID "AstroRotaxMonitor"
|
||||||
|
#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);
|
||||||
@@ -29,21 +50,43 @@ void setup()
|
|||||||
LOG_SET_LEVEL(DebugLogLevel::LVL_INFO);
|
LOG_SET_LEVEL(DebugLogLevel::LVL_INFO);
|
||||||
|
|
||||||
// Print Processor Info
|
// Print Processor Info
|
||||||
LOG_INFO("ESP32 Chip:", ESP.getChipModel());
|
LOG_DEBUG("ESP32 Chip:", ESP.getChipModel());
|
||||||
if (psramFound())
|
if (psramFound())
|
||||||
{
|
{
|
||||||
LOG_INFO("ESP32 PSram Found");
|
LOG_DEBUG("ESP32 PSram Found");
|
||||||
LOG_INFO("ESP32 PSram:", ESP.getPsramSize());
|
LOG_DEBUG("ESP32 PSram:", ESP.getPsramSize());
|
||||||
psramInit();
|
psramInit();
|
||||||
}
|
}
|
||||||
LOG_INFO("ESP32 Flash:", ESP.getFlashChipSize());
|
LOG_DEBUG("ESP32 Flash:", ESP.getFlashChipSize());
|
||||||
LOG_INFO("ESP32 Heap:", ESP.getHeapSize());
|
LOG_DEBUG("ESP32 Heap:", ESP.getHeapSize());
|
||||||
LOG_INFO("ESP32 Sketch:", ESP.getFreeSketchSpace());
|
LOG_DEBUG("ESP32 Sketch:", ESP.getFreeSketchSpace());
|
||||||
|
|
||||||
// Initialize Interrupt pins on PICKUP detectors
|
// Initialize Interrupt pins on PICKUP detectors
|
||||||
initTriggerPinsInputs();
|
initTriggerPinsInputs();
|
||||||
// Initialize Interrupt pins on SPARK detectors
|
// Initialize Interrupt pins on SPARK detectors
|
||||||
initSparkPinInputs();
|
initSparkPinInputs();
|
||||||
|
|
||||||
|
// Init Wifi station
|
||||||
|
LOG_INFO("Initializing WiFi...");
|
||||||
|
WiFi.mode(WIFI_AP);
|
||||||
|
IPAddress local_IP(10, 11, 12, 1);
|
||||||
|
IPAddress gateway(10, 11, 12, 1);
|
||||||
|
IPAddress subnet(255, 255, 255, 0);
|
||||||
|
WiFi.softAPConfig(local_IP, gateway, subnet);
|
||||||
|
if (WiFi.softAP(WIFI_SSID, WIFI_PASSWORD))
|
||||||
|
{
|
||||||
|
LOG_INFO("WiFi AP Mode Started");
|
||||||
|
LOG_INFO("Wifi SSID:", WIFI_SSID);
|
||||||
|
LOG_INFO("Wifi Password:", WIFI_PASSWORD);
|
||||||
|
LOG_INFO("WiFi IP:" + WiFi.softAPIP().toString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to start WiFi AP Mode");
|
||||||
|
LOG_ERROR("5 seconds to restart...");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
@@ -79,7 +122,7 @@ void loop()
|
|||||||
.spark_pin_34 = SPARK_PIN_A34},
|
.spark_pin_34 = SPARK_PIN_A34},
|
||||||
.rt_resets = rtTaskResets{.rst_io_12p = RST_EXT_A12P, .rst_io_12n = RST_EXT_A12N, .rst_io_34p = RST_EXT_A34P, .rst_io_34n = RST_EXT_A34N}};
|
.rt_resets = rtTaskResets{.rst_io_12p = RST_EXT_A12P, .rst_io_12n = RST_EXT_A12N, .rst_io_34p = RST_EXT_A34P, .rst_io_34n = RST_EXT_A34N}};
|
||||||
|
|
||||||
LOG_INFO("Task Variables OK");
|
LOG_DEBUG("Task Variables OK");
|
||||||
|
|
||||||
#ifdef CH_B_ENABLE
|
#ifdef CH_B_ENABLE
|
||||||
QueueHandle_t rt_taskB_queue = xQueueCreate(max_queue, sizeof(ignitionBoxStatus));
|
QueueHandle_t rt_taskB_queue = xQueueCreate(max_queue, sizeof(ignitionBoxStatus));
|
||||||
@@ -118,7 +161,7 @@ void loop()
|
|||||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||||
esp_restart();
|
esp_restart();
|
||||||
}
|
}
|
||||||
LOG_INFO("Init SPI OK");
|
LOG_DEBUG("Init SPI OK");
|
||||||
|
|
||||||
// Init ADC_A
|
// Init ADC_A
|
||||||
dev.adc_a = new ADS1256(ADC_A_DRDY, ADS1256::PIN_UNUSED, ADC_A_SYNC, ADC_A_CS, 2.5, &SPI_A);
|
dev.adc_a = new ADS1256(ADC_A_DRDY, ADS1256::PIN_UNUSED, ADC_A_SYNC, ADC_A_CS, 2.5, &SPI_A);
|
||||||
@@ -134,7 +177,7 @@ void loop()
|
|||||||
dev.adc_a->setDRATE(DRATE_1000SPS);
|
dev.adc_a->setDRATE(DRATE_1000SPS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG_INFO("Init ADC OK");
|
LOG_DEBUG("Init ADC OK");
|
||||||
|
|
||||||
// Ignition A on Core 0
|
// Ignition A on Core 0
|
||||||
auto ignA_task_success = pdPASS;
|
auto ignA_task_success = pdPASS;
|
||||||
@@ -168,17 +211,29 @@ void loop()
|
|||||||
esp_restart();
|
esp_restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Real Time Tasks A & B initialized");
|
LOG_DEBUG("Real Time Tasks A & B initialized");
|
||||||
|
|
||||||
////////////////////// MAIN LOOP //////////////////////
|
////////////////////// MAIN LOOP //////////////////////
|
||||||
clearScreen();
|
|
||||||
setCursor(0, 0);
|
|
||||||
bool partial_save = false; // flag to indicate if a partial save has been done after a timeout
|
bool partial_save = false; // flag to indicate if a partial save has been done after a timeout
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
|
uint32_t wait_count = 0;
|
||||||
ignitionBoxStatus ign_info;
|
ignitionBoxStatus ign_info;
|
||||||
int64_t last = esp_timer_get_time();
|
int64_t last = esp_timer_get_time();
|
||||||
uint32_t missed_firings12 = 0;
|
uint32_t missed_firings12 = 0;
|
||||||
uint32_t missed_firings34 = 0;
|
uint32_t missed_firings34 = 0;
|
||||||
|
ignitionBoxStatusAverage ignA_avg(max_queue); // moving average calculator for ignition box A with a window of 100 samples
|
||||||
|
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
AsyncWebSocket ws("/ws");
|
||||||
|
ws.onEvent(onWsEvent);
|
||||||
|
server.addHandler(&ws);
|
||||||
|
|
||||||
|
auto spiffs_guard = SPIFFSGuard(); // use RAII guard to ensure SPIFFS is properly mounted and unmounted
|
||||||
|
server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
|
||||||
|
server.begin();
|
||||||
|
|
||||||
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
|
{ request->send(200, "text/html", htmlTest.c_str()); });
|
||||||
|
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
@@ -189,23 +244,10 @@ void loop()
|
|||||||
auto *temp = active_history;
|
auto *temp = active_history;
|
||||||
active_history = writable_history; // switch active and writable buffers
|
active_history = writable_history; // switch active and writable buffers
|
||||||
writable_history = temp; // ensure writable_history points to the buffer we just filled
|
writable_history = temp; // ensure writable_history points to the buffer we just filled
|
||||||
dataSaveParams save_params {
|
dataSaveParams save_params{
|
||||||
.history = writable_history,
|
.history = writable_history,
|
||||||
.file_path = "ignition_history.csv"};
|
.file_path = "ignition_history.csv"};
|
||||||
save_history(*writable_history, "ignition_history.csv"); // directly call the save task function to save without delay, since we already switched buffers and writable_history is now empty and ready for new data
|
save_history(*writable_history, "ignition_history.csv"); // directly call the save task function to save without delay
|
||||||
// if (SAVE_HISTORY_TO_SPIFFS)
|
|
||||||
// if (pdFAIL ==
|
|
||||||
// xTaskCreatePinnedToCore(
|
|
||||||
// saveHistoryTask,
|
|
||||||
// "saveHistoryTask",
|
|
||||||
// RT_TASK_STACK,
|
|
||||||
// &save_params,
|
|
||||||
// RT_TASK_PRIORITY - 1, // higher priority to ensure it runs asap after buffer switch
|
|
||||||
// NULL,
|
|
||||||
// CORE_1))
|
|
||||||
// {
|
|
||||||
// LOG_ERROR("Unable to create saveHistoryTask");
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xQueueReceive(rt_taskA_queue, &ign_info, pdMS_TO_TICKS(1000)) == pdTRUE)
|
if (xQueueReceive(rt_taskA_queue, &ign_info, pdMS_TO_TICKS(1000)) == pdTRUE)
|
||||||
@@ -213,17 +255,26 @@ void loop()
|
|||||||
// printInfo(ign_info);
|
// printInfo(ign_info);
|
||||||
auto &hist = *active_history;
|
auto &hist = *active_history;
|
||||||
hist[counter++ % active_history->size()] = ign_info;
|
hist[counter++ % active_history->size()] = ign_info;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Serial.println();
|
||||||
|
LOG_INFO("Sending average ignition status to websocket clients...");
|
||||||
|
auto msg = ignA_avg.toJson().as<String>();
|
||||||
|
ws.textAll(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Serial.println("Waiting for data... ");
|
Serial.printf("[%d] Waiting for data...\r", wait_count++);
|
||||||
if (!partial_save && counter > 0) // if timeout occurs but we have unsaved data, save it before next timeout
|
if (!partial_save && counter > 0) // if timeout occurs but we have unsaved data, save it before next timeout
|
||||||
{
|
{
|
||||||
active_history->resize(counter); // resize active history to actual number of records received to avoid saving empty records
|
active_history->resize(counter); // resize active history to actual number of records received to avoid saving empty records
|
||||||
save_history(*active_history, "ignition_history.csv");
|
save_history(*active_history, "ignition_history.csv");
|
||||||
active_history->resize(max_history); // resize back to max history size for next data cycle
|
active_history->resize(max_history); // resize back to max history size for next data cycle
|
||||||
counter = 0; // reset counter after saving
|
counter = 0; // reset counter after saving
|
||||||
partial_save = true;
|
partial_save = true;
|
||||||
first_save = true;
|
first_save = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <datastruct.h>
|
#include <datastruct.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
void clearScreen();
|
void clearScreen();
|
||||||
void setCursor(const uint8_t x, const uint8_t y);
|
void setCursor(const uint8_t x, const uint8_t y);
|
||||||
@@ -10,4 +11,191 @@ 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);
|
||||||
|
|
||||||
void printInfo(const ignitionBoxStatus &info);
|
void printInfo(const ignitionBoxStatus &info);
|
||||||
|
|
||||||
|
static const std::string htmlTest = R"rawliteral(
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>ESP32 Dashboard</title>
|
||||||
|
<style>
|
||||||
|
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>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2>RotaxMonitor realtime data</h2>
|
||||||
|
|
||||||
|
<button onclick="start()">Start</button>
|
||||||
|
<button onclick="stop()">Stop</button>
|
||||||
|
|
||||||
|
<div style="max-width: 900px; margin: 0 auto; text-align: left;">
|
||||||
|
<p><strong>Timestamp:</strong> <span id="timestamp">-</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>ADC read time:</strong> <span id="adc_read_time">-</span></p>
|
||||||
|
<p><strong>Queue errors:</strong> <span id="n_queue_errors">-</span></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Property</th>
|
||||||
|
<th>Coils 12</th>
|
||||||
|
<th>Coils 34</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Spark delay</td>
|
||||||
|
<td id="coils12_spark_delay">-</td>
|
||||||
|
<td id="coils34_spark_delay">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Spark status</td>
|
||||||
|
<td id="coils12_spark_status">-</td>
|
||||||
|
<td id="coils34_spark_status">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Soft start status</td>
|
||||||
|
<td id="coils12_sstart_status">-</td>
|
||||||
|
<td id="coils34_sstart_status">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Peak P in</td>
|
||||||
|
<td id="coils12_peak_p_in">-</td>
|
||||||
|
<td id="coils34_peak_p_in">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Peak N in</td>
|
||||||
|
<td id="coils12_peak_n_in">-</td>
|
||||||
|
<td id="coils34_peak_n_in">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Peak P out</td>
|
||||||
|
<td id="coils12_peak_p_out">-</td>
|
||||||
|
<td id="coils34_peak_p_out">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Peak N out</td>
|
||||||
|
<td id="coils12_peak_n_out">-</td>
|
||||||
|
<td id="coils34_peak_n_out">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Level spark</td>
|
||||||
|
<td id="coils12_level_spark">-</td>
|
||||||
|
<td id="coils34_level_spark">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Events</td>
|
||||||
|
<td id="coils12_n_events">-</td>
|
||||||
|
<td id="coils34_n_events">-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Missed firings</td>
|
||||||
|
<td id="coils12_n_missed_firing">-</td>
|
||||||
|
<td id="coils34_n_missed_firing">-</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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, 5000);
|
||||||
|
};
|
||||||
|
|
||||||
|
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_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>
|
||||||
|
</html>
|
||||||
|
)rawliteral";
|
||||||
Reference in New Issue
Block a user