mirror of
https://github.com/vincentvdwal/crust-craft.git
synced 2026-08-18 17:26:39 +02:00
remove Kd
This commit is contained in:
+41
-79
@@ -10,47 +10,52 @@
|
|||||||
#include "secrets.h"
|
#include "secrets.h"
|
||||||
#include "pid.h"
|
#include "pid.h"
|
||||||
|
|
||||||
|
// #define RELAY 21;
|
||||||
|
// #define THERMO_DO 19;
|
||||||
|
// #define THERMO_CS 23;
|
||||||
|
// #define THERMO_CLK 5;
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
AsyncWebSocket ws("/ws");
|
AsyncWebSocket ws("/ws");
|
||||||
|
|
||||||
// Json Variable to Hold Sensor Readings
|
|
||||||
JsonDocument readings;
|
|
||||||
String jsonString;
|
|
||||||
|
|
||||||
// Timer variables
|
// Timer variables
|
||||||
unsigned long lastTime = 0;
|
unsigned long lastSensor = 0;
|
||||||
unsigned long timerDelay = 500;
|
unsigned long SENSOR_INTERVAL = 100;
|
||||||
|
|
||||||
|
unsigned long lastWebSocket = 0;
|
||||||
|
unsigned long WS_INTERVAL = 500;
|
||||||
|
|
||||||
int relay = 21;
|
int relay = 21;
|
||||||
int thermoDO = 19;
|
int thermoDO = 19;
|
||||||
int thermoCS = 23;
|
int thermoCS = 23;
|
||||||
int thermoCLK = 5;
|
int thermoCLK = 5;
|
||||||
|
|
||||||
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
|
|
||||||
|
|
||||||
float temperature = 0;
|
float temperature = 0;
|
||||||
int targetTemp = 300;
|
int targetTemp = 300;
|
||||||
float setOverShoot = 20;
|
|
||||||
float setUnderShoot = 20;
|
|
||||||
float derivedOverShoot = targetTemp;
|
|
||||||
float derivedUnderShoot = targetTemp;
|
|
||||||
unsigned long lastSwitch = 0;
|
unsigned long lastSwitch = 0;
|
||||||
unsigned long autoSwitchDelay = 20000; // 20s
|
float pwmSwitchDelayOn = 2000; // 2s
|
||||||
float pwmSwitchDelayOn = 2000; // 4s
|
float pwmSwitchDelayOff = 4000; // 4s
|
||||||
float pwmSwitchDelayOff = 4000; // 7s
|
|
||||||
float power = 0;
|
float power = 0;
|
||||||
|
|
||||||
float kp = 0.25;
|
float kp = 0.540938;
|
||||||
float ki = 0.25;
|
float ki = 0.000721;
|
||||||
float kd = 0.25;
|
float kd = 0.0;
|
||||||
|
|
||||||
String mode = "off";
|
String mode = "off";
|
||||||
|
|
||||||
PIDController pid(kp, ki, kd, 100.0, 0.0, 50.0); // Kp, Ki, Kd, max, min, integ_max
|
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
|
||||||
|
|
||||||
|
PIDController pid(kp, ki, kd, 100.0, 0.0, 100.0); // Kp, Ki, Kd, max, min, max_integral
|
||||||
|
|
||||||
// Get Sensor Readings and return JSON object
|
// Get Sensor Readings and return JSON object
|
||||||
String getSensorReadings()
|
String getSensorReadings()
|
||||||
|
|
||||||
{
|
{
|
||||||
|
JsonDocument readings;
|
||||||
|
String jsonString;
|
||||||
|
|
||||||
temperature = thermocouple.readCelsius();
|
temperature = thermocouple.readCelsius();
|
||||||
|
|
||||||
readings["temperature"] = temperature;
|
readings["temperature"] = temperature;
|
||||||
@@ -65,10 +70,10 @@ String getSensorReadings()
|
|||||||
}
|
}
|
||||||
|
|
||||||
readings["target_temp"] = targetTemp;
|
readings["target_temp"] = targetTemp;
|
||||||
readings["derived_overshoot"] = derivedOverShoot;
|
|
||||||
readings["derived_undershoot"] = derivedUnderShoot;
|
|
||||||
readings["mode"] = mode;
|
readings["mode"] = mode;
|
||||||
readings["pid"] = power;
|
readings["pid"] = power;
|
||||||
|
|
||||||
readings["pwm_on"] = pwmSwitchDelayOn;
|
readings["pwm_on"] = pwmSwitchDelayOn;
|
||||||
readings["pwm_off"] = pwmSwitchDelayOff;
|
readings["pwm_off"] = pwmSwitchDelayOff;
|
||||||
|
|
||||||
@@ -100,7 +105,8 @@ void initWiFi()
|
|||||||
while (WiFi.status() != WL_CONNECTED)
|
while (WiFi.status() != WL_CONNECTED)
|
||||||
{
|
{
|
||||||
Serial.print('.');
|
Serial.print('.');
|
||||||
delay(1000);
|
delay(SENSOR_INTERVAL);
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
Serial.println(WiFi.localIP());
|
Serial.println(WiFi.localIP());
|
||||||
}
|
}
|
||||||
@@ -110,12 +116,6 @@ void notifyClients(String sensorReadings)
|
|||||||
ws.textAll(sensorReadings);
|
ws.textAll(sensorReadings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetDerivedValues()
|
|
||||||
{
|
|
||||||
derivedOverShoot = targetTemp;
|
|
||||||
derivedUnderShoot = targetTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len)
|
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len)
|
||||||
{
|
{
|
||||||
AwsFrameInfo *info = (AwsFrameInfo *)arg;
|
AwsFrameInfo *info = (AwsFrameInfo *)arg;
|
||||||
@@ -142,7 +142,6 @@ void handleWebSocketMessage(void *arg, uint8_t *data, size_t len)
|
|||||||
digitalWrite(relay, HIGH);
|
digitalWrite(relay, HIGH);
|
||||||
lastSwitch = millis();
|
lastSwitch = millis();
|
||||||
}
|
}
|
||||||
resetDerivedValues();
|
|
||||||
}
|
}
|
||||||
if (message.startsWith("setTargetTemp"))
|
if (message.startsWith("setTargetTemp"))
|
||||||
{
|
{
|
||||||
@@ -180,19 +179,19 @@ void handleWebSocketMessage(void *arg, uint8_t *data, size_t len)
|
|||||||
{
|
{
|
||||||
String k = message.substring(6, 12);
|
String k = message.substring(6, 12);
|
||||||
kp = k.toFloat();
|
kp = k.toFloat();
|
||||||
// pid.setSetKp(0.3);
|
pid.setSetKp(kp);
|
||||||
}
|
}
|
||||||
if (message.startsWith("setKi"))
|
if (message.startsWith("setKi"))
|
||||||
{
|
{
|
||||||
String k = message.substring(6, 12);
|
String k = message.substring(6, 12);
|
||||||
ki = k.toFloat();
|
ki = k.toFloat();
|
||||||
// pid.setSetKp(0.3);
|
pid.setSetKi(ki);
|
||||||
}
|
}
|
||||||
if (message.startsWith("setKd"))
|
if (message.startsWith("setKd"))
|
||||||
{
|
{
|
||||||
String k = message.substring(6, 12);
|
String k = message.substring(6, 12);
|
||||||
kd = k.toFloat();
|
kd = k.toFloat();
|
||||||
// pid.setSetKp(0.3);
|
pid.setSetKd(kd);
|
||||||
}
|
}
|
||||||
if (message.startsWith("setPWMOn"))
|
if (message.startsWith("setPWMOn"))
|
||||||
{
|
{
|
||||||
@@ -241,48 +240,6 @@ void initWebSocket()
|
|||||||
|
|
||||||
void regulateRelais()
|
void regulateRelais()
|
||||||
{
|
{
|
||||||
if (mode == "auto_switch")
|
|
||||||
{
|
|
||||||
float sinceLastSwitch = millis() - lastSwitch;
|
|
||||||
float fiveMinutesInMillis = 5 * 60 * 1000;
|
|
||||||
float shootFactor = sinceLastSwitch / fiveMinutesInMillis;
|
|
||||||
|
|
||||||
float overshoot = setOverShoot * shootFactor; // derive over 5 mins -> 1 min eq. 3°C, 5mins eq. 15°C
|
|
||||||
float dirivedOvershoot = min(overshoot, setOverShoot);
|
|
||||||
|
|
||||||
float undershoot = setUnderShoot * shootFactor; // derive over 5 min -> 1 min eq. 3°C, 5mins eq. 15°C
|
|
||||||
float dirivedUndershoot = min(undershoot, setUnderShoot);
|
|
||||||
|
|
||||||
float overShootCorrectedTarget = derivedOverShoot = targetTemp - dirivedOvershoot;
|
|
||||||
float underShootCorrectedTarget = derivedUnderShoot = targetTemp + dirivedUndershoot;
|
|
||||||
|
|
||||||
if ((millis() - lastSwitch) > autoSwitchDelay)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (temperature > overShootCorrectedTarget) // > 280°C
|
|
||||||
{
|
|
||||||
if (digitalRead(relay) == HIGH)
|
|
||||||
{
|
|
||||||
digitalWrite(relay, LOW);
|
|
||||||
lastSwitch = millis();
|
|
||||||
resetDerivedValues();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((millis() - lastSwitch) > autoSwitchDelay)
|
|
||||||
{
|
|
||||||
if (temperature < underShootCorrectedTarget) // < 320°C
|
|
||||||
{
|
|
||||||
if (digitalRead(relay) == LOW)
|
|
||||||
{
|
|
||||||
digitalWrite(relay, HIGH);
|
|
||||||
lastSwitch = millis();
|
|
||||||
resetDerivedValues();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == "pwm")
|
if (mode == "pwm")
|
||||||
{
|
{
|
||||||
if ((millis() - lastSwitch) > pwmSwitchDelayOn)
|
if ((millis() - lastSwitch) > pwmSwitchDelayOn)
|
||||||
@@ -305,7 +262,7 @@ void regulateRelais()
|
|||||||
}
|
}
|
||||||
if (mode == "pid")
|
if (mode == "pid")
|
||||||
{
|
{
|
||||||
// output = Kp×error + Ki×∫error×dt + Kd×(Δerror/Δt)`[^2][^5]
|
// output = Kp×error + Ki×∫error×dt + Kd×(Δerror/Δt)
|
||||||
power = pid.compute(temperature);
|
power = pid.compute(temperature);
|
||||||
pwmSwitchDelayOff = (pwmSwitchDelayOn / (power / 100)) - pwmSwitchDelayOn;
|
pwmSwitchDelayOff = (pwmSwitchDelayOn / (power / 100)) - pwmSwitchDelayOn;
|
||||||
|
|
||||||
@@ -353,7 +310,7 @@ void setup()
|
|||||||
|
|
||||||
// Create PID controller instance
|
// Create PID controller instance
|
||||||
pid.setSetpoint(targetTemp);
|
pid.setSetpoint(targetTemp);
|
||||||
pid.setDt(2);
|
pid.setDt(SENSOR_INTERVAL / 1000);
|
||||||
|
|
||||||
ElegantOTA.begin(&server);
|
ElegantOTA.begin(&server);
|
||||||
|
|
||||||
@@ -363,12 +320,17 @@ void setup()
|
|||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
if ((millis() - lastTime) > timerDelay)
|
if ((millis() - lastSensor) > SENSOR_INTERVAL)
|
||||||
|
{
|
||||||
|
regulateRelais();
|
||||||
|
lastSensor = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((millis() - lastWebSocket) > WS_INTERVAL)
|
||||||
{
|
{
|
||||||
String sensorReadings = getSensorReadings();
|
String sensorReadings = getSensorReadings();
|
||||||
notifyClients(sensorReadings);
|
notifyClients(sensorReadings);
|
||||||
regulateRelais();
|
lastWebSocket = millis();
|
||||||
lastTime = millis();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.cleanupClients();
|
ws.cleanupClients();
|
||||||
|
|||||||
+8
-8
@@ -51,16 +51,16 @@ double PIDController::compute(double current_temp)
|
|||||||
integral += ki * error * dt;
|
integral += ki * error * dt;
|
||||||
integral = clamp(integral, -integral_max, integral_max);
|
integral = clamp(integral, -integral_max, integral_max);
|
||||||
|
|
||||||
// Derivative
|
// // Derivative
|
||||||
double d_term = 0.0;
|
// double d_term = 0.0;
|
||||||
if (!first_run)
|
// if (!first_run)
|
||||||
{
|
// {
|
||||||
d_term = kd * (error - prev_error) / dt;
|
// d_term = kd * (error - prev_error) / dt;
|
||||||
}
|
// }
|
||||||
first_run = false;
|
// first_run = false;
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
double output = p_term + integral + d_term;
|
double output = p_term + integral; // d_term;
|
||||||
output = clamp(output, min_output, max_output);
|
output = clamp(output, min_output, max_output);
|
||||||
|
|
||||||
prev_error = error;
|
prev_error = error;
|
||||||
|
|||||||
+249
-262
@@ -1,29 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
import Chart from 'chart.js/auto';
|
|
||||||
import 'chartjs-adapter-date-fns';
|
|
||||||
|
|
||||||
import annotationPlugin from 'chartjs-plugin-annotation';
|
|
||||||
|
|
||||||
import { nl } from 'date-fns/locale';
|
|
||||||
|
|
||||||
import { pad } from '$lib/utils';
|
import { pad } from '$lib/utils';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
|
|
||||||
let gateway = `ws://${page.url.host}/ws`;
|
let gateway = `ws://${page.url.host}/ws`;
|
||||||
let ws: WebSocket;
|
let ws: WebSocket;
|
||||||
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
let mode = $state('off');
|
let mode = $state('off');
|
||||||
let temp = $state(300);
|
let temp = $state(300);
|
||||||
let relais = $state(0);
|
let relais = $state(0);
|
||||||
let targetTemp = $state(300);
|
let targetTemp = $state(300);
|
||||||
let setOverShoot = 20;
|
|
||||||
let setUnderShoot = 20;
|
|
||||||
let targetTempOverShoot = $state(targetTemp);
|
|
||||||
let targetTempUnderShoot = $state(targetTemp);
|
|
||||||
let calculatedPidOutput = $state(0);
|
let calculatedPidOutput = $state(0);
|
||||||
|
|
||||||
let lastSwitch = new Date();
|
let lastSwitch = new Date();
|
||||||
@@ -34,7 +21,6 @@
|
|||||||
let pauseGraphUpdate = $state(false);
|
let pauseGraphUpdate = $state(false);
|
||||||
let spoofInterval = 250;
|
let spoofInterval = 250;
|
||||||
let speedFactor = 250 / spoofInterval;
|
let speedFactor = 250 / spoofInterval;
|
||||||
const switchDelay = (30 / speedFactor) * 1000;
|
|
||||||
let pwmOn = $state(2);
|
let pwmOn = $state(2);
|
||||||
let pwmOnDelay = (2 / speedFactor) * 1000;
|
let pwmOnDelay = (2 / speedFactor) * 1000;
|
||||||
let pwmOff = $state(4);
|
let pwmOff = $state(4);
|
||||||
@@ -44,260 +30,261 @@
|
|||||||
let ki = $state(0.25);
|
let ki = $state(0.25);
|
||||||
let kd = $state(0.25);
|
let kd = $state(0.25);
|
||||||
|
|
||||||
let maxValues = 4 * 2 * 60 * 60; // 120 minutes (4 values/sec)
|
let maxValues = 24 * 2 * 60 * 60; // 24h (2 values/sec)
|
||||||
|
|
||||||
let temperatureData: Array<[number, number]> = [];
|
let temperatureData: Array<[number, number]> = [];
|
||||||
let relaisData: Array<[number, number]> = [];
|
let relaisData: Array<[number, number]> = [];
|
||||||
|
let powerData: Array<[number, number]> = [];
|
||||||
|
|
||||||
let downloadCSVButton: HTMLElement;
|
let downloadCSVButton: HTMLElement;
|
||||||
let modeSpan: HTMLElement;
|
let modeSpan: HTMLElement;
|
||||||
|
|
||||||
const getRelaisBands = () => {
|
// const getRelaisBands = () => {
|
||||||
let relaisBands = {};
|
// let relaisBands = {};
|
||||||
let active = false;
|
// let active = false;
|
||||||
let count = 0;
|
// let count = 0;
|
||||||
|
|
||||||
for (let r of relaisData) {
|
// for (let r of relaisData) {
|
||||||
let key = 'relais' + count;
|
// let key = 'relais' + count;
|
||||||
if (r[1] === 1 && !active) {
|
// if (r[1] === 1 && !active) {
|
||||||
active = true;
|
// active = true;
|
||||||
relaisBands[key] = {
|
// relaisBands[key] = {
|
||||||
type: 'box',
|
// type: 'box',
|
||||||
backgroundColor: 'rgba(255, 0, 0, 0.1)',
|
// backgroundColor: 'rgba(255, 0, 0, 0.1)',
|
||||||
borderWidth: 1,
|
// borderWidth: 1,
|
||||||
borderColor: 'rgba(255, 0, 0, 0.5)',
|
// borderColor: 'rgba(255, 0, 0, 0.5)',
|
||||||
drawTime: 'beforeDraw',
|
// drawTime: 'beforeDraw',
|
||||||
|
|
||||||
xMax: relaisData[relaisData.length - 1][0] + 10,
|
// xMax: relaisData[relaisData.length - 1][0] + 10,
|
||||||
xMin: r[0]
|
// xMin: r[0]
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
if (r[1] === 0 && active) {
|
// if (r[1] === 0 && active) {
|
||||||
relaisBands[key]['xMax'] = r[0];
|
// relaisBands[key]['xMax'] = r[0];
|
||||||
active = false;
|
// active = false;
|
||||||
count++;
|
// count++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return relaisBands;
|
// return relaisBands;
|
||||||
};
|
// };
|
||||||
|
|
||||||
const getAnnotations = () => {
|
// const getAnnotations = () => {
|
||||||
return {
|
// return {
|
||||||
annotations: {
|
// annotations: {
|
||||||
limitGreen: {
|
// limitGreen: {
|
||||||
type: 'box',
|
// type: 'box',
|
||||||
drawTime: 'beforeDraw',
|
// drawTime: 'beforeDraw',
|
||||||
backgroundColor: 'rgba(0, 255, 133, 0.25)',
|
// backgroundColor: 'rgba(0, 255, 133, 0.25)',
|
||||||
borderWidth: 0,
|
// borderWidth: 0,
|
||||||
yMax: 0,
|
// yMax: 0,
|
||||||
yMin: 350
|
// yMin: 350
|
||||||
},
|
// },
|
||||||
limitOrange: {
|
// limitOrange: {
|
||||||
type: 'box',
|
// type: 'box',
|
||||||
drawTime: 'beforeDraw',
|
// drawTime: 'beforeDraw',
|
||||||
backgroundColor: 'rgba(255, 138, 0 ,0.25)',
|
// backgroundColor: 'rgba(255, 138, 0 ,0.25)',
|
||||||
borderWidth: 0,
|
// borderWidth: 0,
|
||||||
yMax: 350,
|
// yMax: 350,
|
||||||
yMin: 450
|
// yMin: 450
|
||||||
},
|
// },
|
||||||
limitRed: {
|
// limitRed: {
|
||||||
type: 'box',
|
// type: 'box',
|
||||||
drawTime: 'beforeDraw',
|
// drawTime: 'beforeDraw',
|
||||||
|
|
||||||
backgroundColor: 'rgba(255, 0, 0, 0.25)',
|
// backgroundColor: 'rgba(255, 0, 0, 0.25)',
|
||||||
borderWidth: 0,
|
// borderWidth: 0,
|
||||||
yMax: 450,
|
// yMax: 450,
|
||||||
yMin: 550
|
// yMin: 550
|
||||||
},
|
// },
|
||||||
targetTemp: {
|
// targetTemp: {
|
||||||
type: 'line',
|
// type: 'line',
|
||||||
drawTime: 'beforeDraw',
|
// drawTime: 'beforeDraw',
|
||||||
borderColor: 'black',
|
// borderColor: 'black',
|
||||||
borderWidth: 2,
|
// borderWidth: 2,
|
||||||
scaleID: 'y',
|
// scaleID: 'y',
|
||||||
value: targetTemp,
|
// value: targetTemp,
|
||||||
borderDash: [6, 6],
|
// borderDash: [6, 6],
|
||||||
borderDashOffset: 0,
|
// borderDashOffset: 0,
|
||||||
label: {
|
// label: {
|
||||||
backgroundColor: 'transparent',
|
// backgroundColor: 'transparent',
|
||||||
display: true,
|
// display: true,
|
||||||
color: 'rgba(0,0,0, 1)',
|
// color: 'rgba(0,0,0, 1)',
|
||||||
content: 'Target temp',
|
// content: 'Target temp',
|
||||||
position: 'center',
|
// position: 'center',
|
||||||
yAdjust: -9
|
// yAdjust: -9
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
...getRelaisBands()
|
// ...getRelaisBands()
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
let chart: Chart;
|
// let chart: Chart;
|
||||||
Chart.register(annotationPlugin);
|
// Chart.register(annotationPlugin);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
initWebSocket();
|
initWebSocket();
|
||||||
|
|
||||||
chart = new Chart(canvas, {
|
// chart = new Chart(canvas, {
|
||||||
type: 'line',
|
// type: 'line',
|
||||||
options: {
|
// options: {
|
||||||
plugins: {
|
// plugins: {
|
||||||
legend: {
|
// legend: {
|
||||||
display: false
|
// display: false
|
||||||
},
|
// },
|
||||||
tooltip: {
|
// tooltip: {
|
||||||
mode: 'nearest',
|
// mode: 'nearest',
|
||||||
intersect: false,
|
// intersect: false,
|
||||||
animation: false
|
// animation: false
|
||||||
},
|
// },
|
||||||
annotation: getAnnotations()
|
// annotation: getAnnotations()
|
||||||
},
|
// },
|
||||||
scales: {
|
// scales: {
|
||||||
x: {
|
// x: {
|
||||||
type: 'time',
|
// type: 'time',
|
||||||
time: {
|
// time: {
|
||||||
tooltipFormat: 'HH:mm:ss',
|
// tooltipFormat: 'HH:mm:ss',
|
||||||
|
|
||||||
displayFormats: {
|
// displayFormats: {
|
||||||
millisecond: 'HH:mm:ss.S',
|
// millisecond: 'HH:mm:ss.S',
|
||||||
second: 'HH:mm:ss',
|
// second: 'HH:mm:ss',
|
||||||
minute: 'HH:mm',
|
// minute: 'HH:mm',
|
||||||
hour: 'HH:mm'
|
// hour: 'HH:mm'
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
adapters: {
|
// adapters: {
|
||||||
date: {
|
// date: {
|
||||||
locale: nl
|
// locale: nl
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
y: {
|
// y: {
|
||||||
min: 0
|
// min: 0
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
data: {
|
// data: {
|
||||||
labels: temperatureData.map((row) => row[0]),
|
// labels: temperatureData.map((row) => row[0]),
|
||||||
datasets: [
|
// datasets: [
|
||||||
{
|
// {
|
||||||
label: 'Temperature',
|
// label: 'Temperature',
|
||||||
data: temperatureData.map((row) => row[1]),
|
// data: temperatureData.map((row) => row[1]),
|
||||||
fill: true,
|
// fill: true,
|
||||||
tension: 0.25
|
// tension: 0.25
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
// if (import.meta.env.DEV) {
|
||||||
// spoof some test data
|
// // spoof some test data
|
||||||
setInterval(() => {
|
// setInterval(() => {
|
||||||
if (temperatureData.length > maxValues) {
|
// if (temperatureData.length > maxValues) {
|
||||||
temperatureData.shift();
|
// temperatureData.shift();
|
||||||
relaisData.shift();
|
// relaisData.shift();
|
||||||
}
|
// }
|
||||||
let d = new Date();
|
// let d = new Date();
|
||||||
|
|
||||||
if (temp < targetTemp / 2) {
|
// if (temp < targetTemp / 2) {
|
||||||
temp = temp + Math.random() * 0.5 - 0.125;
|
// temp = temp + Math.random() * 0.5 - 0.125;
|
||||||
} else if (temp < targetTemp / (3 / 2)) {
|
// } else if (temp < targetTemp / (3 / 2)) {
|
||||||
temp = temp + Math.random() * 0.5 - 0.15;
|
// temp = temp + Math.random() * 0.5 - 0.15;
|
||||||
} else {
|
// } else {
|
||||||
temp = temp + Math.random() * 0.5 - 0.25;
|
// temp = temp + Math.random() * 0.5 - 0.25;
|
||||||
}
|
// }
|
||||||
|
|
||||||
let shootFactor = Math.min(
|
// let shootFactor = Math.min(
|
||||||
((d.getTime() - lastSwitch.getTime()) / speedFactor -
|
// ((d.getTime() - lastSwitch.getTime()) / speedFactor -
|
||||||
(lastSwitchDuration * 0.25) / speedFactor) /
|
// (lastSwitchDuration * 0.25) / speedFactor) /
|
||||||
((3 * 60 * 1000) / speedFactor),
|
// ((3 * 60 * 1000) / speedFactor),
|
||||||
1
|
// 1
|
||||||
); // 5mins
|
// ); // 5mins
|
||||||
shootFactor = shootFactor;
|
// shootFactor = shootFactor;
|
||||||
|
|
||||||
if (relais == 0) {
|
// if (relais == 0) {
|
||||||
temp = temp - 0.3 * shootFactor;
|
// temp = temp - 0.3 * shootFactor;
|
||||||
} else {
|
// } else {
|
||||||
temp = temp + 0.3 * shootFactor;
|
// temp = temp + 0.3 * shootFactor;
|
||||||
}
|
// }
|
||||||
|
|
||||||
let mod = temp % 0.25;
|
// let mod = temp % 0.25;
|
||||||
if (mod > 0.125) {
|
// if (mod > 0.125) {
|
||||||
temp = temp - mod + 0.25;
|
// temp = temp - mod + 0.25;
|
||||||
} else {
|
// } else {
|
||||||
temp = temp - mod;
|
// temp = temp - mod;
|
||||||
}
|
// }
|
||||||
|
|
||||||
let derivedShootFactor = Math.min(
|
// let derivedShootFactor = Math.min(
|
||||||
(d.getTime() - lastSwitch.getTime()) / speedFactor / ((3 * 60 * 1000) / speedFactor),
|
// (d.getTime() - lastSwitch.getTime()) / speedFactor / ((3 * 60 * 1000) / speedFactor),
|
||||||
1
|
// 1
|
||||||
); // 5mins
|
// ); // 5mins
|
||||||
|
|
||||||
let overshoot = setOverShoot * derivedShootFactor; // derive over 5 mins -> 1 min eq. 3°C, 5mins eq. 15°C
|
// let overshoot = setOverShoot * derivedShootFactor; // derive over 5 mins -> 1 min eq. 3°C, 5mins eq. 15°C
|
||||||
let dirivedOvershoot = Math.min(overshoot, setOverShoot);
|
// let dirivedOvershoot = Math.min(overshoot, setOverShoot);
|
||||||
|
|
||||||
let undershoot = setUnderShoot * derivedShootFactor; // derive over 5 min -> 1 min eq. 3°C, 5mins eq. 15°C
|
// let undershoot = setUnderShoot * derivedShootFactor; // derive over 5 min -> 1 min eq. 3°C, 5mins eq. 15°C
|
||||||
let dirivedUndershoot = Math.min(undershoot, setUnderShoot);
|
// let dirivedUndershoot = Math.min(undershoot, setUnderShoot);
|
||||||
|
|
||||||
targetTempOverShoot = targetTemp - dirivedOvershoot;
|
// targetTempOverShoot = targetTemp - dirivedOvershoot;
|
||||||
targetTempUnderShoot = targetTemp + dirivedUndershoot;
|
// targetTempUnderShoot = targetTemp + dirivedUndershoot;
|
||||||
|
|
||||||
if (mode === 'auto_switch') {
|
// if (mode === 'auto_switch') {
|
||||||
if (d.getTime() - lastSwitch.getTime() > switchDelay) {
|
// if (d.getTime() - lastSwitch.getTime() > switchDelay) {
|
||||||
if (temp > targetTempOverShoot) {
|
// if (temp > targetTempOverShoot) {
|
||||||
// > 270 °C
|
// // > 270 °C
|
||||||
if (relais == 1) {
|
// if (relais == 1) {
|
||||||
relais = 0;
|
// relais = 0;
|
||||||
|
|
||||||
targetTempOverShoot = targetTemp;
|
// targetTempOverShoot = targetTemp;
|
||||||
targetTempUnderShoot = targetTemp;
|
// targetTempUnderShoot = targetTemp;
|
||||||
lastSwitchDuration = d.getTime() - lastSwitch.getTime();
|
// lastSwitchDuration = d.getTime() - lastSwitch.getTime();
|
||||||
lastSwitch = new Date();
|
// lastSwitch = new Date();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (d.getTime() - lastSwitch.getTime() > switchDelay) {
|
// if (d.getTime() - lastSwitch.getTime() > switchDelay) {
|
||||||
if (temp < targetTempUnderShoot) {
|
// if (temp < targetTempUnderShoot) {
|
||||||
// < 310°C
|
// // < 310°C
|
||||||
if (relais == 0) {
|
// if (relais == 0) {
|
||||||
relais = 1;
|
// relais = 1;
|
||||||
|
|
||||||
targetTempOverShoot = targetTemp;
|
// targetTempOverShoot = targetTemp;
|
||||||
targetTempUnderShoot = targetTemp;
|
// targetTempUnderShoot = targetTemp;
|
||||||
lastSwitchDuration = d.getTime() - lastSwitch.getTime();
|
// lastSwitchDuration = d.getTime() - lastSwitch.getTime();
|
||||||
lastSwitch = new Date();
|
// lastSwitch = new Date();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (mode === 'pwm') {
|
// if (mode === 'pwm') {
|
||||||
if (d.getTime() - lastSwitch.getTime() > pwmOnDelay) {
|
// if (d.getTime() - lastSwitch.getTime() > pwmOnDelay) {
|
||||||
if (relais) {
|
// if (relais) {
|
||||||
relais = 0;
|
// relais = 0;
|
||||||
lastSwitch = new Date();
|
// lastSwitch = new Date();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (d.getTime() - lastSwitch.getTime() > pwmOffDelay) {
|
// if (d.getTime() - lastSwitch.getTime() > pwmOffDelay) {
|
||||||
if (!relais) {
|
// if (!relais) {
|
||||||
relais = 1;
|
// relais = 1;
|
||||||
lastSwitch = new Date();
|
// lastSwitch = new Date();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
temperatureData.push([d.getTime(), temp]);
|
// temperatureData.push([d.getTime(), temp]);
|
||||||
relaisData.push([d.getTime(), relais]);
|
// relaisData.push([d.getTime(), relais]);
|
||||||
|
|
||||||
if (!pauseGraphUpdate) {
|
// if (!pauseGraphUpdate) {
|
||||||
chart.options.plugins.annotation = getAnnotations();
|
// chart.options.plugins.annotation = getAnnotations();
|
||||||
chart.data.labels.push(d.getTime());
|
// chart.data.labels.push(d.getTime());
|
||||||
chart.data.datasets.forEach((dataset) => {
|
// chart.data.datasets.forEach((dataset) => {
|
||||||
dataset.data.push(temp);
|
// dataset.data.push(temp);
|
||||||
});
|
// });
|
||||||
chart.update('none');
|
// chart.update('none');
|
||||||
}
|
// }
|
||||||
}, spoofInterval);
|
// }, spoofInterval);
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
const getReadings = () => {
|
const getReadings = () => {
|
||||||
@@ -341,26 +328,26 @@
|
|||||||
if (temperatureData.length > maxValues) {
|
if (temperatureData.length > maxValues) {
|
||||||
temperatureData.shift();
|
temperatureData.shift();
|
||||||
}
|
}
|
||||||
temperatureData.push([d.getTime(), myObj[key]]);
|
|
||||||
temp = myObj[key];
|
temp = myObj[key];
|
||||||
|
temperatureData.push([d.getTime(), temp]);
|
||||||
} else if (key === 'relais') {
|
} else if (key === 'relais') {
|
||||||
if (relaisData.length > maxValues) {
|
if (relaisData.length > maxValues) {
|
||||||
relaisData.shift();
|
relaisData.shift();
|
||||||
}
|
}
|
||||||
relaisData.push([d.getTime(), myObj[key]]);
|
|
||||||
if (myObj[key]) {
|
if (myObj[key]) {
|
||||||
relais = 1;
|
relais = 1;
|
||||||
} else {
|
} else {
|
||||||
relais = 0;
|
relais = 0;
|
||||||
}
|
}
|
||||||
|
relaisData.push([d.getTime(), relais]);
|
||||||
} else if (key === 'target_temp') {
|
} else if (key === 'target_temp') {
|
||||||
targetTemp = myObj[key];
|
targetTemp = myObj[key];
|
||||||
} else if (key === 'derived_overshoot') {
|
|
||||||
targetTempOverShoot = myObj[key];
|
|
||||||
} else if (key === 'derived_undershoot') {
|
|
||||||
targetTempUnderShoot = myObj[key];
|
|
||||||
} else if (key === 'pid') {
|
} else if (key === 'pid') {
|
||||||
|
if (powerData.length > maxValues) {
|
||||||
|
powerData.shift();
|
||||||
|
}
|
||||||
calculatedPidOutput = myObj[key];
|
calculatedPidOutput = myObj[key];
|
||||||
|
powerData.push([d.getTime(), calculatedPidOutput]);
|
||||||
} else if (key === 'pwm_on') {
|
} else if (key === 'pwm_on') {
|
||||||
pwmOn = Number(myObj[key]) / 1000;
|
pwmOn = Number(myObj[key]) / 1000;
|
||||||
} else if (key === 'pwm_off') {
|
} else if (key === 'pwm_off') {
|
||||||
@@ -376,14 +363,14 @@
|
|||||||
modeSpan.innerText = mode;
|
modeSpan.innerText = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pauseGraphUpdate) {
|
// if (!pauseGraphUpdate) {
|
||||||
chart.options.plugins.annotation = getAnnotations();
|
// chart.options.plugins.annotation = getAnnotations();
|
||||||
chart.data.labels.push(d.getTime());
|
// chart.data.labels.push(d.getTime());
|
||||||
chart.data.datasets.forEach((dataset) => {
|
// chart.data.datasets.forEach((dataset) => {
|
||||||
dataset.data.push(temp);
|
// dataset.data.push(temp);
|
||||||
});
|
// });
|
||||||
chart.update('none');
|
// chart.update('none');
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -412,8 +399,6 @@
|
|||||||
ws.send('setTargetTemp: ' + pad(value, 3));
|
ws.send('setTargetTemp: ' + pad(value, 3));
|
||||||
} else {
|
} else {
|
||||||
targetTemp = value;
|
targetTemp = value;
|
||||||
targetTempOverShoot = value;
|
|
||||||
targetTempUnderShoot = value;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -464,10 +449,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const prepareDownloadCSV = () => {
|
const prepareDownloadCSV = () => {
|
||||||
let csvContent = 'Time,Temp,Relais\n';
|
let csvContent = 'Time,Temp,Relais,Power\n';
|
||||||
|
|
||||||
for (let [i, temp] of temperatureData.entries()) {
|
for (let [i, temp] of temperatureData.entries()) {
|
||||||
csvContent += `${temp[0]},${temp[1]},${relaisData[i] ? relaisData[i][1] : ''}\n`;
|
csvContent += `${temp[0]},${temp[1]},${relaisData[i] ? relaisData[i][1] : ''},${powerData[i][1]}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8,' });
|
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8,' });
|
||||||
@@ -585,17 +570,19 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if mode === 'pid'}
|
{#if mode === 'pid'}
|
||||||
<div class="mt-3 flex items-center gap-1">
|
<div class="mt-3 flex items-center gap-1">
|
||||||
PID: {calculatedPidOutput.toFixed(0)}% {(calculatedPidOutput * 12).toFixed(0)}W
|
PID: {calculatedPidOutput ? calculatedPidOutput.toFixed(0) : 0}% {calculatedPidOutput
|
||||||
|
? (calculatedPidOutput * 12).toFixed(0)
|
||||||
|
: 0}W
|
||||||
</div>
|
</div>
|
||||||
<div>ON:{pwmOn.toFixed(2)}s OFF:{pwmOff.toFixed(2)}s</div>
|
<div>ON:{pwmOn.toFixed(2)}s OFF:{pwmOff ? pwmOff.toFixed(2) : 0}s</div>
|
||||||
<div class="mt-3 flex w-full justify-between px-9">
|
<div class="mt-3 flex w-full justify-between px-13">
|
||||||
<div>Kp</div>
|
<div>Kp</div>
|
||||||
<div>Ki</div>
|
<div>Ki</div>
|
||||||
<div>Kd</div>
|
<!-- <div>Kd</div> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<input
|
<input
|
||||||
class="min-w-[85px] rounded"
|
class="min-w-[120px] rounded"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="50"
|
max="50"
|
||||||
@@ -605,7 +592,7 @@
|
|||||||
onchange={(e) => changeKValue(e, 'p')}
|
onchange={(e) => changeKValue(e, 'p')}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="min-w-[85px] rounded"
|
class="min-w-[120px] rounded"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="50"
|
max="50"
|
||||||
@@ -614,7 +601,7 @@
|
|||||||
id="target_temperature"
|
id="target_temperature"
|
||||||
onchange={(e) => changeKValue(e, 'i')}
|
onchange={(e) => changeKValue(e, 'i')}
|
||||||
/>
|
/>
|
||||||
<input
|
<!-- <input
|
||||||
class="min-w-[85px] rounded"
|
class="min-w-[85px] rounded"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
@@ -623,7 +610,7 @@
|
|||||||
value={kd}
|
value={kd}
|
||||||
id="target_temperature"
|
id="target_temperature"
|
||||||
onchange={(e) => changeKValue(e, 'd')}
|
onchange={(e) => changeKValue(e, 'd')}
|
||||||
/>
|
/> -->
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user