add under and over shoot corrections

This commit is contained in:
Vincent van der Wal
2025-09-06 20:49:46 +02:00
parent b249004542
commit 65ac75cb9f
5 changed files with 90 additions and 31 deletions
+2 -1
View File
@@ -141,4 +141,5 @@ ESP32Async/ESPAsyncWebServer | https://github.com/ESP32Async/ESPAsyncWebServer |
### Display ### Display
In the long run a capacative LCD screen might be used in addition to the WebSocket connection - Add capacative LCD screen to be used in addition to the WebSocket connection for direct control on the oven
- Maybe add sensor to indicate if the lid has been opened
+2 -2
View File
@@ -15,7 +15,7 @@
<div class="card-grid"> <div class="card-grid">
<div class="card"> <div class="card">
<p class="card-title"> <p class="card-title">
Temperature</p> Sensor Temperature</p>
<p class="reading"><span id="temperature"></span> &deg;C</p> <p class="reading"><span id="temperature"></span> &deg;C</p>
</div> </div>
<div class="card relais" id="relais_switch"> <div class="card relais" id="relais_switch">
@@ -34,7 +34,7 @@
<div class="oven" id="oven"> <div class="oven" id="oven">
<div class="oven-wrapper"> <div class="oven-wrapper">
<div class="oven-img-wrapper"> <div class="oven-img-wrapper">
<img src="/oven.png"> <img src="/oven_small.png">
</div> </div>
</div> </div>
</div> </div>
+59 -14
View File
@@ -31,10 +31,12 @@ int thermoCLK = 5;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO); MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
int targetTemp = 300; int targetTemp = 300;
int overShoot = 10; float setOverShoot = 15;
int underShoot = 5; float setUnderShoot = 10;
float derivedOverShoot = targetTemp;
float derivedUnderShoot = targetTemp;
unsigned long lastSwitch = 0; unsigned long lastSwitch = 0;
unsigned long switchDelay = 60000; unsigned long switchDelay = 30000; // 30s
bool autoSwitch = true; bool autoSwitch = true;
// Get Sensor Readings and return JSON object // Get Sensor Readings and return JSON object
@@ -49,6 +51,9 @@ String getSensorReadings()
{ {
readings["relais"] = 0; readings["relais"] = 0;
} }
readings["target_temp"] = targetTemp;
readings["derived_overshoot"] = derivedOverShoot;
readings["derived_undershoot"] = derivedUnderShoot;
serializeJson(readings, jsonString); serializeJson(readings, jsonString);
return jsonString; return jsonString;
} }
@@ -108,11 +113,12 @@ 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"))
{ {
String target = message.substring(15, 18); String target = message.substring(15, 18);
targetTemp = target.toInt(); targetTemp = target.toFloat();
Serial.printf("\nTarget temp set to "); Serial.printf("\nTarget temp set to ");
Serial.print(targetTemp); Serial.print(targetTemp);
Serial.printf("°C"); Serial.printf("°C");
@@ -145,6 +151,54 @@ void initWebSocket()
server.addHandler(&ws); server.addHandler(&ws);
} }
void regulateRelais()
{
if (autoSwitch)
{
if ((millis() - lastSwitch) > switchDelay)
{
float temperature = thermocouple.readCelsius();
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 (temperature > overShootCorrectedTarget) // > 285°C
{
if (digitalRead(relay) == HIGH)
{
digitalWrite(relay, LOW);
lastSwitch = millis();
resetDerivedValues();
}
}
else if (temperature < underShootCorrectedTarget) // < 310°C
{
if (digitalRead(relay) == LOW)
{
digitalWrite(relay, HIGH);
lastSwitch = millis();
resetDerivedValues();
}
}
}
}
}
void resetDerivedValues()
{
derivedOverShoot = targetTemp;
derivedUnderShoot = targetTemp;
}
void setup() void setup()
{ {
Serial.begin(9600); Serial.begin(9600);
@@ -172,16 +226,7 @@ void loop()
{ {
String sensorReadings = getSensorReadings(); String sensorReadings = getSensorReadings();
notifyClients(sensorReadings); notifyClients(sensorReadings);
if (((millis() - lastSwitch) > switchDelay) && thermocouple.readCelsius() >= (targetTemp - overShoot)) // >= 285°C regulateRelais();
{
digitalWrite(relay, LOW);
lastSwitch = millis();
}
if (((millis() - lastSwitch) > switchDelay) && thermocouple.readCelsius() < (targetTemp - underShoot)) // < 270°C
{
digitalWrite(relay, HIGH);
lastSwitch = millis();
}
lastTime = millis(); lastTime = millis();
} }
+26 -13
View File
@@ -19,17 +19,17 @@ let ws: WebSocket;
const now = new Date() const now = new Date()
let container: HTMLElement | null = document.querySelector('#plot') let container: HTMLElement | null = document.querySelector('#plot')
let temperatureData = [
[now.getTime(), 25]
]
let relais = 0 let relais = 0
let relaisData = [ let relaisData = [
[now.getTime(), 0] [now.getTime(), 0]
] ]
let temp = 25; let temp = 300;
let targetTemp = 350 let targetTemp = 300
let targetTempOverShoot = 300
let targetTempUnderShoot = 300
const oven = document.querySelector('#oven') const oven = document.querySelector('#oven')
const temperature = document.querySelector('#temperature') const temperature = document.querySelector('#temperature')
@@ -42,6 +42,10 @@ let maxValues = 4 * 60 * 60 // 60 minutes (4 values/sec)
let hs: Highcharts.StockChart; let hs: Highcharts.StockChart;
let hsOptions: Highcharts.Options let hsOptions: Highcharts.Options
let temperatureData = [
[now.getTime(), temp]
]
const getRelaisBands = () => { const getRelaisBands = () => {
let relaisBands = [] let relaisBands = []
let active = false; let active = false;
@@ -65,15 +69,18 @@ const getRelaisBands = () => {
} }
const dashStyle: DashStyleValue = 'Dash' const dashStyle: DashStyleValue = 'Dash'
const getTargetLine = () => { const getTargetLines = () => {
return [ return [
{ value: targetTemp, width: 3, dashStyle: dashStyle } { value: targetTemp, width: 3, dashStyle: dashStyle },
{ value: targetTempOverShoot, width: 1, dashStyle: dashStyle, color: "red" },
{ value: targetTempUnderShoot, width: 1, dashStyle: dashStyle, color: "red" }
] ]
} }
const createChart = () => { const createChart = () => {
hsOptions = { hsOptions = {
chart: { chart: {
height: '650px',
animation: { animation: {
duration: 250, duration: 250,
easing: 'linear' easing: 'linear'
@@ -121,7 +128,7 @@ const createChart = () => {
to: 600, to: 600,
color: 'rgba(255, 0, 0, 0.4)' color: 'rgba(255, 0, 0, 0.4)'
}], }],
plotLines: getTargetLine() plotLines: getTargetLines()
}, },
xAxis: { xAxis: {
@@ -233,7 +240,7 @@ const onLoad = () => {
hs.xAxis[0].update({ plotBands: getRelaisBands() }) hs.xAxis[0].update({ plotBands: getRelaisBands() })
hs.yAxis[0].update({ plotLines: getTargetLine() }) hs.yAxis[0].update({ plotLines: getTargetLines() })
hs.series[0].setData(temperatureData, true) hs.series[0].setData(temperatureData, true)
}, 250) }, 250)
} }
@@ -302,10 +309,16 @@ const onMessage = (event: MessageEvent) => {
if (relaisSwitchInput) if (relaisSwitchInput)
relaisSwitchInput.checked = false relaisSwitchInput.checked = false
} }
} else if (key === 'target_temp') {
targetTemp = myObj[key]
} else if (key === 'derived_overshoot') {
targetTempOverShoot = myObj[key]
} else if (key === 'derived_undershoot') {
targetTempUnderShoot = myObj[key]
} }
hs.xAxis[0].update({ plotBands: getRelaisBands() }) hs.xAxis[0].update({ plotBands: getRelaisBands() })
hs.yAxis[0].update({ plotLines: getTargetLine() }) hs.yAxis[0].update({ plotLines: getTargetLines() })
hs.series[0].setData(temperatureData, true) hs.series[0].setData(temperatureData, true)
} }
} }
@@ -336,10 +349,10 @@ const switchRelais = () => {
const changeTargetTemp = (e: Event) => { const changeTargetTemp = (e: Event) => {
const target = e.target as HTMLTextAreaElement; const target = e.target as HTMLTextAreaElement;
let value = Number(target?.value) let value = Number(target?.value)
targetTemp = value // targetTemp = value
console.log('Changed target temp to ' + targetTemp) console.log('Changed target temp to ' + value)
if (!import.meta.env.DEV) { if (!import.meta.env.DEV) {
ws.send("setTargetTemp: " + pad(targetTemp, 3)); ws.send("setTargetTemp: " + pad(value, 3));
} }
} }
+1 -1
View File
@@ -36,7 +36,7 @@ p {
} }
.graph { .graph {
height: 310px; height: 570px;
overflow: hidden; overflow: hidden;
display: flex; display: flex;
justify-content: center; justify-content: center;