From 688e8f03bba91f8a3af786eeb4a13a7ef7888420 Mon Sep 17 00:00:00 2001 From: huskee Date: Tue, 19 Dec 2023 22:09:27 +0200 Subject: [PATCH] Add CSV recording capabilities --- data/index.html | 51 +++++---------- data/script.js | 166 ++++++++++++++++++++++++++++++------------------ src/main.cpp | 12 +++- 3 files changed, 128 insertions(+), 101 deletions(-) diff --git a/data/index.html b/data/index.html index 259b00c..5c733df 100644 --- a/data/index.html +++ b/data/index.html @@ -9,39 +9,22 @@

/* therminator */

last sensor update:

-
-
-

- max data points: - -

-
-
-

- update interval (ms): - -

-
-
-
-

Channel A:

-
- -

Channel B:

-
- -
-
+

chart update interval (ms):

+

data update interval (ms):

+ +
@@ -56,8 +39,6 @@
-
- diff --git a/data/script.js b/data/script.js index 31bc09a..7192c41 100644 --- a/data/script.js +++ b/data/script.js @@ -1,11 +1,11 @@ const canvasA = document.getElementById("chartA"); const canvasB = document.getElementById("chartB"); -const mobCanvasA = document.getElementById("chartA-mobile"); -const mobCanvasB = document.getElementById("chartB-mobile"); - -const maxValsSelect = document.getElementById("maxVals"); -let maxVals = maxValsSelect.options[maxValsSelect.selectedIndex].text; - +const chartUpdateInt = document.getElementById("chartUpd").value; +document.getElementById("stopRec").disabled = true; +let data; +let record = 0; +let filename; +const csvData = ["time, tempA, tempB"]; const chartAdata = { data: { labels: [], @@ -35,6 +35,12 @@ const chartBdata = { }; const options = { + animation: false, + elements: { + point: { + pointStyle: false, + }, + }, responsive: true, plugins: { legend: { @@ -107,38 +113,6 @@ const chartB = new Chart(canvasB, { options: options, }); -const mobChartA = new Chart(mobCanvasA, { - type: "line", - data: { - labels: [], - datasets: [ - { - label: "Channel A temperature", - backgroundColor: "red", - borderColor: "red", - data: [], - }, - ], - }, - options: options, -}); - -const mobChartB = new Chart(mobCanvasB, { - type: "line", - data: { - labels: [], - datasets: [ - { - label: "Channel B temperature", - backgroundColor: "blue", - borderColor: "blue", - data: [], - }, - ], - }, - options: options, -}); - var gateway = `ws://${window.location.hostname}/ws`; var websocket; // Init web socket when the page loads @@ -152,6 +126,22 @@ function onload(event) { function getReadings() { websocket.send("getReadings"); } +function setUpdateInterval() { + let updateInt = document.getElementById("upd").value; + websocket.send("u" + updateInt); + location.reload(); +} + +function getUpdateInterval() { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function () { + if (this.readyState == 4 && this.status == 200) { + document.getElementById("upd").value = this.responseText; + } + }; + xhttp.open("GET", "/int", true); + xhttp.send(); +} function initWebSocket() { console.log("Trying to open a WebSocket connection..."); @@ -187,30 +177,19 @@ function removeFirstData(chart) { }); } -function changeUpdateInterval() { - const updateSelect = document.getElementById("updateInterval"); - let updateInt = updateSelect.options[updateSelect.selectedIndex].text; - websocket.send("u" + updateInt); - location.reload(); -} - // Function that receives the message from the ESP32 with the readings function onMessage(event) { - console.log(event.data); - var data = JSON.parse(event.data); - var today = new Date(); - var date = - today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate(); - var time = - today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds(); - var dateTime = date + " " + time; - document.getElementById("time").innerHTML = dateTime; - if (chartA.data.labels.length > maxVals) { - removeFirstData(chartA); - } - if (chartB.data.labels.length > maxVals) { - removeFirstData(chartB); + //console.log(event.data); + data = JSON.parse(event.data); + let timestamp = new Date(); + document.getElementById("time").innerHTML = timestamp.getTime(); + data["time"] = timestamp.getTime(); + if (record == 1) { + csvData.push(createCSV(data)); } +} + +setInterval(function () { if (isNaN(data["tempA"])) { document.getElementById("error-tempA").innerHTML = "

" + data["tempA"] + "

"; @@ -223,8 +202,69 @@ function onMessage(event) { } else { document.getElementById("error-tempB").innerText = ""; } - addData(chartA, data["time"] / 1000, data["tempA"]); - addData(chartB, data["time"] / 1000, data["tempB"]); - addData(mobChartA, data["time"] / 1000, data["tempA"]); - addData(mobChartB, data["time"] / 1000, data["tempB"]); + addData(chartA, data["time"], data["tempA"]); + addData(chartB, data["time"], data["tempB"]); +}, chartUpdateInt); + +function escapeValue(value) { + var result = ""; + var escape = false; + + for (var i = 0; i < value.length; ++i) { + var entry = value[i]; + + result += entry; + + switch (entry) { + case '"': + result += '"'; + case ",": + escape = true; + } + } + + if (escape) return '"' + result + '"'; + + return result; +} + +function createCSV(object) { + var body = ""; + + for (var column in object) { + if (object.hasOwnProperty(column)) { + if (body) body += ","; + + var value = object[column]; + + if (value == null) continue; + + body += escapeValue(value.toString()); + } + } + + return body; +} + +function startRecording() { + date = new Date(); + let filedate = date.toISOString().slice(0, -5).replaceAll(":", ""); + filename = "therminator-" + filedate + ".csv"; + record = 1; + document.getElementById("startRec").disabled = true; + document.getElementById("stopRec").disabled = false; +} +function stopRecording() { + record = 0; + let csvFile = csvData.join("\n"); + const blob = new Blob([csvFile], { type: "text/csv;charset=utf-8," }); + const objUrl = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.setAttribute("href", objUrl); + link.setAttribute("download", filename); + document.querySelector("body").append(link); + link.click(); + csvData.length = 1; + document.getElementById("startRec").disabled = false; + document.getElementById("stopRec").disabled = true; } diff --git a/src/main.cpp b/src/main.cpp index edf476c..e66b210 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,9 +8,12 @@ #include #include +#include + #define CS_A D2 #define CS_B D3 + Adafruit_MAX31855 thermocoupleA(CS_A); Adafruit_MAX31855 thermocoupleB(CS_B); @@ -39,9 +42,9 @@ String checkSensor(Adafruit_MAX31855 &sensor) { return String(sensor.readCelsius()); } String getSensorReadings() { + readings["time"] = String(lastTime); readings["tempA"] = checkSensor(thermocoupleA); readings["tempB"] = checkSensor(thermocoupleB); - readings["time"] = String(lastTime); String jsonString = JSON.stringify(readings); return jsonString; } @@ -59,7 +62,7 @@ void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) { if (strcmp((char *)data, "getReadings") == 0) { // if it is, send current sensor readings String sensorReadings = getSensorReadings(); - Serial.print(sensorReadings); + Serial.printf("%s\n", sensorReadings.c_str()); notifyClients(sensorReadings); } if (message.startsWith("u")) { @@ -126,6 +129,10 @@ void setup() { request->send(404, "text/plain", "404 Not Found"); }); + server.on("/int", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send_P(200, "text/plain", String(timerDelay).c_str()); + }); + server.serveStatic("/", LittleFS, "/"); server.begin(); Serial.printf("webserver init\n"); @@ -136,7 +143,6 @@ void loop() { String sensorReadings = getSensorReadings(); Serial.println(sensorReadings); notifyClients(sensorReadings); - lastTime = millis(); }