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:
|
||||
- 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
|
||||
|
||||
+78
-3
@@ -78,6 +78,7 @@ struct SensorSettingsUpdate;
|
||||
struct SensorSettingsApplyResult;
|
||||
struct ServiceSettingsUpdate;
|
||||
struct FullyKioskConfig;
|
||||
struct SensorGlitchBridge;
|
||||
class AsyncWebServerRequest;
|
||||
|
||||
// Vorwaertsdeklarationen fuer Funktionen, die vor ihrer Definition genutzt werden
|
||||
@@ -369,7 +370,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
|
||||
* Firmware-Informationen
|
||||
************************************************************************************/
|
||||
|
||||
String version = "5.6.4";
|
||||
String version = "5.6.5";
|
||||
String versionHersteller = "Thomas Müller";
|
||||
String versionHerstellerMail =
|
||||
"<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);
|
||||
}
|
||||
|
||||
// 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
|
||||
// Verlaufskurven denselben Wert sehen und die letzte Stelle nicht zwischen den Kanaelen
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -5582,6 +5651,9 @@ void loop() {
|
||||
// Temperaturmessung
|
||||
RawInputDampf = thermocouple.readCelsius();
|
||||
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);
|
||||
InputWasser = round(RawInputWasser + OffsetWasser);
|
||||
updateSmoothedRawTemperature(SmoothRawDampf, RawInputDampf);
|
||||
@@ -5589,6 +5661,7 @@ void loop() {
|
||||
updateOffsetCompensationReferences();
|
||||
if (caseSensorEnabled) {
|
||||
double rawCase = readCaseTemperature();
|
||||
rawCase = bridgeSensorGlitch(sensorBridgeCase, rawCase, OffsetCase, currentMillis, "Gehaeuse");
|
||||
InputCase = round(rawCase + OffsetCase);
|
||||
updateSmoothedRawTemperature(SmoothRawCase, rawCase);
|
||||
caseSensorError = (isnan(InputCase) || InputCase < 0.0);
|
||||
@@ -5613,7 +5686,7 @@ void loop() {
|
||||
wasserSafetyShutdown = false;
|
||||
OutputWasser = 0;
|
||||
// Ton ausgeben zur Warnung
|
||||
beepShort();
|
||||
beepSensorAlarm();
|
||||
// Serial.println("WARNUNG: Wassertemperatursensor liefert ungueltigen Wert!"); // Optional Log
|
||||
} else if (InputWasser > maxTempWasser) {
|
||||
wasserSensorError = false;
|
||||
@@ -5640,7 +5713,7 @@ void loop() {
|
||||
dampfSafetyShutdown = false;
|
||||
OutputDampf = 0;
|
||||
// Ton ausgeben zur Warnung
|
||||
beepShort();
|
||||
beepSensorAlarm();
|
||||
// Serial.println("WARNUNG: Dampftemperatursensor liefert ungueltigen Wert!"); // Optional Log
|
||||
} else if (InputDampf > maxTempDampf) {
|
||||
dampfSensorError = false;
|
||||
@@ -5658,6 +5731,8 @@ void loop() {
|
||||
if (activateBoostDampf) { OutputDampf = windowSizeDampf; } else if (!autoTuneDampfActive) { pidDampf.Compute(); }
|
||||
}
|
||||
}
|
||||
// Warnton-Wiederholung erst zuruecksetzen, wenn beide Fuehler wieder liefern
|
||||
if (!wasserSensorError && !dampfSensorError) { sensorAlarmBeepActive = false; }
|
||||
yield();
|
||||
|
||||
// Pruefen der Startverzoegerung fuer Dampf
|
||||
|
||||
Reference in new issue
Block a user