Add CSV recording capabilities

This commit is contained in:
huskee 2023-12-19 22:09:27 +02:00
parent 4ea73aa32c
commit 688e8f03bb
3 changed files with 128 additions and 101 deletions

View file

@ -9,39 +9,22 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<h1>/* therminator */</h1>
<p>last sensor update: <span id="time"></span></p>
<div id="options">
<div id="mv">
<p>
max data points:
<select id="maxVals" onchange="location.reload()">
<option>15</option>
<option>20</option>
<option>30</option>
<option>50</option>
<option>100</option>
</select>
</p>
</div>
<div id="ui">
<p>
update interval (ms):
<select id="updateInterval" onchange="changeUpdateInterval()">
<p>chart update interval (ms): <select id="chartUpd" onchange="location.reload()">
<option>200</option>
<option selected>500</option>
<option>1000</option>
<option>3000</option>
</select>
</p>
</div>
</div>
<div id="chart-small">
<h2>Channel A:</h2>
<div id="error-tempA"></div>
<canvas id="chartA-mobile"></canvas>
<h2>Channel B:</h2>
<div id="error-tempB"></div>
<canvas id="chartB-mobile"></canvas>
</div>
<div id="chart-large">
</select></p>
<p>data update interval (ms): <select id="upd" onload="getUpdateInterval()" onchange="setUpdateInterval()">
<option>10</option>
<option>20</option>
<option>50</option>
<option>100</option>
<option>200</option>
<option selected>500</option>
<option>1000</option>
</select></p>
<button id="startRec" onclick="startRecording()">Start recording</button>
<button id="stopRec" onclick="stopRecording()">Stop recording</button>
<table>
<tr>
<td class="chart-container">
@ -56,8 +39,6 @@
</td>
</tr>
</table>
</div>
<script src="script.js"></script>
</body>
</html>

View file

@ -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);
//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));
}
if (chartB.data.labels.length > maxVals) {
removeFirstData(chartB);
}
setInterval(function () {
if (isNaN(data["tempA"])) {
document.getElementById("error-tempA").innerHTML =
"<h3>" + data["tempA"] + "</h3>";
@ -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;
}

View file

@ -8,9 +8,12 @@
#include <SPI.h>
#include <pinout.h>
#include <chrono>
#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();
}