feat(S3): kurze Sensoraussetzer überbrücken statt Sensorfehler (5.6.5) #94
No files matched your search
@@ -1,3 +1,15 @@
|
|||||||
|
Version 5.6.5:
|
||||||
|
- Kurze Sensoraussetzer lösen keinen Sensorfehler mehr aus. Liefert ein Temperaturfühler für einen
|
||||||
|
Moment keinen gültigen Wert, wird bis zu 3 Sekunden lang der letzte gültige Messwert weiterbenutzt -
|
||||||
|
die Regelung läuft normal weiter, Heizung, Anzeige und Piezo bleiben ruhig. Erst wenn der Ausfall
|
||||||
|
länger dauert, gilt er wie bisher als Sensorfehler mit Heizungsabschaltung und Warnung.
|
||||||
|
- Das gilt für alle drei Fühler: Wasserkessel (NTC), Dampfkessel (Thermoelement) und den Zusatzsensor
|
||||||
|
für Gehäuse/Tassenablage. Jede Überbrückung steht mit Dauer und gehaltenem Wert im seriellen Log.
|
||||||
|
- Der Warnton bei einem echten Sensorfehler ist kein Dauerton mehr. Bisher wurde er alle 250 ms neu
|
||||||
|
angestoßen, obwohl ein Ton eine Sekunde dauert. Jetzt piept es einmal beim Auftreten und danach als
|
||||||
|
Erinnerung alle 30 Sekunden, bis der Fühler wieder liefert. Der Ton bei Übertemperatur
|
||||||
|
(Sicherheitsabschaltung) bleibt unverändert.
|
||||||
|
|
||||||
Version 5.6.4:
|
Version 5.6.4:
|
||||||
- Dampf-/Thermoblock-Temperatur wird jetzt mit einer Nachkommastelle angezeigt - im Webinterface, in
|
- Dampf-/Thermoblock-Temperatur wird jetzt mit einer Nachkommastelle angezeigt - im Webinterface, in
|
||||||
den Verlaufskurven und auf dem Touch-Display. Bisher wurde der Messwert direkt nach dem Einlesen auf
|
den Verlaufskurven und auf dem Touch-Display. Bisher wurde der Messwert direkt nach dem Einlesen auf
|
||||||
|
|||||||
+78
-3
@@ -78,6 +78,7 @@ struct SensorSettingsUpdate;
|
|||||||
struct SensorSettingsApplyResult;
|
struct SensorSettingsApplyResult;
|
||||||
struct ServiceSettingsUpdate;
|
struct ServiceSettingsUpdate;
|
||||||
struct FullyKioskConfig;
|
struct FullyKioskConfig;
|
||||||
|
struct SensorGlitchBridge;
|
||||||
class AsyncWebServerRequest;
|
class AsyncWebServerRequest;
|
||||||
|
|
||||||
// Vorwaertsdeklarationen fuer Funktionen, die vor ihrer Definition genutzt werden
|
// Vorwaertsdeklarationen fuer Funktionen, die vor ihrer Definition genutzt werden
|
||||||
@@ -369,7 +370,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
|
|||||||
* Firmware-Informationen
|
* Firmware-Informationen
|
||||||
************************************************************************************/
|
************************************************************************************/
|
||||||
|
|
||||||
String version = "5.6.4";
|
String version = "5.6.5";
|
||||||
String versionHersteller = "Thomas Müller";
|
String versionHersteller = "Thomas Müller";
|
||||||
String versionHerstellerMail =
|
String versionHerstellerMail =
|
||||||
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
|
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
|
||||||
@@ -644,6 +645,58 @@ void updateSmoothedRawTemperature(double &smoothed, double measured) {
|
|||||||
smoothed += DISPLAY_TEMP_SMOOTHING * (measured - smoothed);
|
smoothed += DISPLAY_TEMP_SMOOTHING * (measured - smoothed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kurzzeitige Sensoraussetzer ueberbruecken. Ein ADC-Ausreisser am NTC oder ein verpasster
|
||||||
|
// SPI-Frame am Thermoelement liefert fuer ein bis zwei Messtakte gar keinen Wert. Ohne
|
||||||
|
// Ueberbrueckung geht die Steuerung sofort in den Sensorfehler (Heizung aus, Warnung im
|
||||||
|
// Webinterface und auf dem Display, Ton am Piezo), obwohl der Fuehler kurz darauf wieder
|
||||||
|
// liefert. Waehrend der Haltezeit wird stattdessen der letzte gueltige Messwert
|
||||||
|
// weitergereicht; erst wenn der Ausfall laenger dauert, gilt er als echter Sensorfehler.
|
||||||
|
const unsigned long SENSOR_GLITCH_HOLD_MS = 3000; // maximale Ueberbrueckungsdauer
|
||||||
|
|
||||||
|
struct SensorGlitchBridge {
|
||||||
|
double lastGoodValue = NAN; // letzter gueltiger Rohwert
|
||||||
|
unsigned long lastGoodMs = 0; // Zeitpunkt dieses Werts
|
||||||
|
bool holding = false; // wird gerade ueberbrueckt?
|
||||||
|
uint16_t holdEvents = 0; // Anzahl der Ueberbrueckungen seit dem Start
|
||||||
|
};
|
||||||
|
|
||||||
|
SensorGlitchBridge sensorBridgeWasser;
|
||||||
|
SensorGlitchBridge sensorBridgeDampf;
|
||||||
|
SensorGlitchBridge sensorBridgeCase;
|
||||||
|
|
||||||
|
// Liefert den Messwert oder - bei ungueltiger Messung innerhalb der Haltezeit - den letzten
|
||||||
|
// gueltigen Wert. Die Gueltigkeitspruefung entspricht der spaeteren Sensorfehler-Pruefung,
|
||||||
|
// damit nicht ein Wert ueberbrueckt wird, den die Sicherheitslogik trotzdem verwirft.
|
||||||
|
double bridgeSensorGlitch(SensorGlitchBridge &bridge, double rawValue, double offsetValue,
|
||||||
|
unsigned long nowMs, const char *sensorName) {
|
||||||
|
bool valid = isfinite(rawValue) && (rawValue + offsetValue) >= 0.0;
|
||||||
|
if (valid) {
|
||||||
|
if (bridge.holding) {
|
||||||
|
Serial.printf("INFO: %s-Sensor nach %lu ms Ueberbrueckung wieder gueltig.\n",
|
||||||
|
sensorName, (unsigned long)(nowMs - bridge.lastGoodMs));
|
||||||
|
bridge.holding = false;
|
||||||
|
}
|
||||||
|
bridge.lastGoodValue = rawValue;
|
||||||
|
bridge.lastGoodMs = nowMs;
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
if (isfinite(bridge.lastGoodValue) && (nowMs - bridge.lastGoodMs) <= SENSOR_GLITCH_HOLD_MS) {
|
||||||
|
if (!bridge.holding) {
|
||||||
|
bridge.holding = true;
|
||||||
|
if (bridge.holdEvents < 0xFFFF) bridge.holdEvents++;
|
||||||
|
Serial.printf("INFO: %s-Sensor ohne Wert - Ueberbrueckung mit %.2f C.\n",
|
||||||
|
sensorName, bridge.lastGoodValue);
|
||||||
|
}
|
||||||
|
return bridge.lastGoodValue;
|
||||||
|
}
|
||||||
|
if (bridge.holding) {
|
||||||
|
Serial.printf("WARNUNG: %s-Sensor laenger als %lu ms ohne Wert - Sensorfehler.\n",
|
||||||
|
sensorName, SENSOR_GLITCH_HOLD_MS);
|
||||||
|
bridge.holding = false;
|
||||||
|
}
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
// Anzeigewerte auf eine Nachkommastelle festzurren, damit Weboberflaeche, Display und
|
// Anzeigewerte auf eine Nachkommastelle festzurren, damit Weboberflaeche, Display und
|
||||||
// Verlaufskurven denselben Wert sehen und die letzte Stelle nicht zwischen den Kanaelen
|
// Verlaufskurven denselben Wert sehen und die letzte Stelle nicht zwischen den Kanaelen
|
||||||
// auseinanderlaeuft.
|
// auseinanderlaeuft.
|
||||||
@@ -3341,6 +3394,22 @@ void beepMaintenanceAlert(int frequency = 3000, int duration = 3000) {
|
|||||||
// delay(duration + 10); // Kurze Pause danach, falls nötig, aber `tone` mit Dauer ist blockierend
|
// delay(duration + 10); // Kurze Pause danach, falls nötig, aber `tone` mit Dauer ist blockierend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Warnton bei Sensorfehler. Die Pruefung laeuft alle 250 ms, ein Ton dauert 1000 ms -
|
||||||
|
// unveraendert angestossen ergibt das einen Dauerton. Deshalb einmal beim Auftreten und
|
||||||
|
// danach nur noch als Erinnerung im Abstand von SENSOR_ALARM_REPEAT_MS.
|
||||||
|
const unsigned long SENSOR_ALARM_REPEAT_MS = 30000;
|
||||||
|
unsigned long lastSensorAlarmBeepMs = 0;
|
||||||
|
bool sensorAlarmBeepActive = false;
|
||||||
|
|
||||||
|
void beepSensorAlarm() {
|
||||||
|
unsigned long nowMs = millis();
|
||||||
|
if (!sensorAlarmBeepActive || (unsigned long)(nowMs - lastSensorAlarmBeepMs) >= SENSOR_ALARM_REPEAT_MS) {
|
||||||
|
beepShort();
|
||||||
|
lastSensorAlarmBeepMs = nowMs;
|
||||||
|
sensorAlarmBeepActive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************
|
/************************************************************************************
|
||||||
* ESP-NOW Empfangsfunktion
|
* ESP-NOW Empfangsfunktion
|
||||||
@@ -5582,6 +5651,9 @@ void loop() {
|
|||||||
// Temperaturmessung
|
// Temperaturmessung
|
||||||
RawInputDampf = thermocouple.readCelsius();
|
RawInputDampf = thermocouple.readCelsius();
|
||||||
RawInputWasser = readNTCTemperature();
|
RawInputWasser = readNTCTemperature();
|
||||||
|
// Kurze Aussetzer ueberbruecken, bevor irgendetwas mit den Werten passiert
|
||||||
|
RawInputDampf = bridgeSensorGlitch(sensorBridgeDampf, RawInputDampf, OffsetDampf, currentMillis, "Dampf");
|
||||||
|
RawInputWasser = bridgeSensorGlitch(sensorBridgeWasser, RawInputWasser, OffsetWasser, currentMillis, "Wasser");
|
||||||
InputDampf = round(RawInputDampf + OffsetDampf);
|
InputDampf = round(RawInputDampf + OffsetDampf);
|
||||||
InputWasser = round(RawInputWasser + OffsetWasser);
|
InputWasser = round(RawInputWasser + OffsetWasser);
|
||||||
updateSmoothedRawTemperature(SmoothRawDampf, RawInputDampf);
|
updateSmoothedRawTemperature(SmoothRawDampf, RawInputDampf);
|
||||||
@@ -5589,6 +5661,7 @@ void loop() {
|
|||||||
updateOffsetCompensationReferences();
|
updateOffsetCompensationReferences();
|
||||||
if (caseSensorEnabled) {
|
if (caseSensorEnabled) {
|
||||||
double rawCase = readCaseTemperature();
|
double rawCase = readCaseTemperature();
|
||||||
|
rawCase = bridgeSensorGlitch(sensorBridgeCase, rawCase, OffsetCase, currentMillis, "Gehaeuse");
|
||||||
InputCase = round(rawCase + OffsetCase);
|
InputCase = round(rawCase + OffsetCase);
|
||||||
updateSmoothedRawTemperature(SmoothRawCase, rawCase);
|
updateSmoothedRawTemperature(SmoothRawCase, rawCase);
|
||||||
caseSensorError = (isnan(InputCase) || InputCase < 0.0);
|
caseSensorError = (isnan(InputCase) || InputCase < 0.0);
|
||||||
@@ -5613,7 +5686,7 @@ void loop() {
|
|||||||
wasserSafetyShutdown = false;
|
wasserSafetyShutdown = false;
|
||||||
OutputWasser = 0;
|
OutputWasser = 0;
|
||||||
// Ton ausgeben zur Warnung
|
// Ton ausgeben zur Warnung
|
||||||
beepShort();
|
beepSensorAlarm();
|
||||||
// Serial.println("WARNUNG: Wassertemperatursensor liefert ungueltigen Wert!"); // Optional Log
|
// Serial.println("WARNUNG: Wassertemperatursensor liefert ungueltigen Wert!"); // Optional Log
|
||||||
} else if (InputWasser > maxTempWasser) {
|
} else if (InputWasser > maxTempWasser) {
|
||||||
wasserSensorError = false;
|
wasserSensorError = false;
|
||||||
@@ -5640,7 +5713,7 @@ void loop() {
|
|||||||
dampfSafetyShutdown = false;
|
dampfSafetyShutdown = false;
|
||||||
OutputDampf = 0;
|
OutputDampf = 0;
|
||||||
// Ton ausgeben zur Warnung
|
// Ton ausgeben zur Warnung
|
||||||
beepShort();
|
beepSensorAlarm();
|
||||||
// Serial.println("WARNUNG: Dampftemperatursensor liefert ungueltigen Wert!"); // Optional Log
|
// Serial.println("WARNUNG: Dampftemperatursensor liefert ungueltigen Wert!"); // Optional Log
|
||||||
} else if (InputDampf > maxTempDampf) {
|
} else if (InputDampf > maxTempDampf) {
|
||||||
dampfSensorError = false;
|
dampfSensorError = false;
|
||||||
@@ -5658,6 +5731,8 @@ void loop() {
|
|||||||
if (activateBoostDampf) { OutputDampf = windowSizeDampf; } else if (!autoTuneDampfActive) { pidDampf.Compute(); }
|
if (activateBoostDampf) { OutputDampf = windowSizeDampf; } else if (!autoTuneDampfActive) { pidDampf.Compute(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Warnton-Wiederholung erst zuruecksetzen, wenn beide Fuehler wieder liefern
|
||||||
|
if (!wasserSensorError && !dampfSensorError) { sensorAlarmBeepActive = false; }
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
// Pruefen der Startverzoegerung fuer Dampf
|
// Pruefen der Startverzoegerung fuer Dampf
|
||||||
|
|||||||
Reference in new issue
Block a user