fix(P4): Delle nach dem Standby per Median statt Sperrfrist (1.10.4)
Die Massnahme aus 1.10.3 verlaengerte die Delle, statt sie zu entfernen. Sie hielt beim Umschalten des Standby-Merkers den zuletzt gemeldeten Wert fest - zu diesem Zeitpunkt lag die gestoerte Probe aber bereits an. Festgehalten wurde also ausgerechnet die Delle, und das sekundenlang. Die Verlaufskurven tragen jetzt den mittleren Wert der letzten fuenf Proben ein (Median). Ein bis zwei verirrte Proben verschwinden damit vollstaendig, unabhaengig vom Zeitpunkt. Echte Aenderungen kommen unverfaelscht durch, nur zwei Sekunden spaeter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gwqJf2DcqHFQpoY3XkDHX
This commit is contained in:
co-authored by
Claude Opus 5
parent
a84f902f75
commit
1febc0b092
@@ -1,3 +1,15 @@
|
||||
Version 1.10.4:
|
||||
- Die Maßnahme aus 1.10.3 machte die Delle nach dem Standby länger statt kleiner. Sie
|
||||
hielt beim Umschalten des Standby-Merkers den zuletzt gemeldeten Wert fest - zu diesem
|
||||
Zeitpunkt lag die gestörte Probe aber bereits an. Festgehalten wurde also ausgerechnet
|
||||
die Delle, und das sekundenlang.
|
||||
- Statt einer Sperrfrist tragen die Verlaufskurven jetzt den mittleren Wert der letzten
|
||||
fünf Proben ein (Median). Eine oder zwei verirrte Proben verschwinden damit vollständig,
|
||||
unabhängig davon, wann sie auftreten - beim Standby-Ende ebenso wie beim Bezugsstart.
|
||||
- Echte Temperaturänderungen kommen unverfälscht durch, nur zwei Sekunden später. Das ist
|
||||
die Verzögerung, um die es hier ging.
|
||||
- Die angezeigten Temperaturzahlen bleiben weiterhin ungefiltert.
|
||||
|
||||
Version 1.10.3:
|
||||
- Direkt nach dem Aufwachen aus dem Standby zog die Wasserkurve eine kurze Delle nach
|
||||
unten. Ursache ist die Messung, nicht der Kessel: In genau diesem Moment schalten die
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Firmware
|
||||
// -------------------------------------------------------------------------------------
|
||||
#define DISPLAY_FW_VERSION "1.10.3" // Firmware-Stand der P4-Display-Steuerung (Info-Seite)
|
||||
#define DISPLAY_FW_VERSION "1.10.4" // Firmware-Stand der P4-Display-Steuerung (Info-Seite)
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Protokoll
|
||||
|
||||
+38
-23
@@ -1124,14 +1124,37 @@ static int32_t g_sparkMax = INT32_MIN;
|
||||
|
||||
// Kurz nach einem Standby-Wechsel sackt die Wassertemperatur fuer einen Sekundenbruchteil
|
||||
// ab. In genau diesem Moment schalten die Heizungen, und der Wassersensor ist ein NTC am
|
||||
// ADC - ein Kessel kann binnen einer Sekunde nicht um Grade einbrechen, die Delle ist also
|
||||
// eine Stoerung der Messung, kein Vorgang an der Maschine. Fuer die Dauer des Uebergangs
|
||||
// schreiben die Verlaufskurven deshalb den letzten Wert von vor dem Wechsel weiter. Die
|
||||
// angezeigten Zahlen bleiben unangetastet - was gemessen wird, steht dort weiter drin.
|
||||
#define KURVEN_SPERRE_MS 6000
|
||||
static uint32_t g_kurvenSperreBis = 0; // millis(); bis dahin Werte halten
|
||||
static float g_haltW = 0.0f;
|
||||
static float g_haltD = 0.0f;
|
||||
// ADC, einmal je Regelzyklus abgetastet - ein Kessel kann binnen einer Sekunde nicht um
|
||||
// Grade einbrechen, die Delle ist also eine Stoerung der Messung, kein Vorgang an der
|
||||
// Maschine.
|
||||
//
|
||||
// In die Kurven geht deshalb nicht die frische Probe, sondern der mittlere Wert der
|
||||
// letzten fuenf (Median). Eine oder zwei verirrte Proben verschwinden damit vollstaendig,
|
||||
// waehrend echte Temperaturaenderungen unverfaelscht durchkommen - nur eben zwei Sekunden
|
||||
// spaeter. Genau diese kleine Verzoegerung ist der Preis.
|
||||
//
|
||||
// Ein erster Anlauf hielt stattdessen beim Standby-Wechsel den zuletzt gemeldeten Wert
|
||||
// fest. Das machte es schlimmer: Beim Umschalten des Merkers lag die gestoerte Probe
|
||||
// bereits an, gehalten wurde also ausgerechnet die Delle - und zwar sekundenlang.
|
||||
// Die angezeigten Zahlen bleiben unangetastet - was gemessen wird, steht dort weiter drin.
|
||||
#define KURVEN_MEDIAN 5
|
||||
static float g_medW[KURVEN_MEDIAN];
|
||||
static float g_medD[KURVEN_MEDIAN];
|
||||
static uint8_t g_medPos = 0;
|
||||
static uint8_t g_medFill = 0;
|
||||
|
||||
// Mittlerer Wert von hoechstens fuenf Proben. Bei so wenigen Werten ist Sortieren durch
|
||||
// Einfuegen das Einfachste, was die Aufgabe loest.
|
||||
static float median_von(const float* quelle, uint8_t anzahl) {
|
||||
float f[KURVEN_MEDIAN];
|
||||
for (uint8_t i = 0; i < anzahl; i++) {
|
||||
float v = quelle[i];
|
||||
int8_t j = (int8_t)i - 1;
|
||||
while (j >= 0 && f[j] > v) { f[j + 1] = f[j]; j--; }
|
||||
f[j + 1] = v;
|
||||
}
|
||||
return f[anzahl / 2];
|
||||
}
|
||||
|
||||
static void spark_scan(lv_obj_t* ch, lv_chart_series_t* ser, int32_t* mn, int32_t* mx) {
|
||||
if (!ch || !ser) return;
|
||||
@@ -1970,11 +1993,13 @@ static void chart_sample_cb(lv_timer_t*) {
|
||||
//
|
||||
// Bewusst vor dem Ausstieg unten: Die Kurven im Dashboard haengen nicht daran, ob
|
||||
// die Verlauf-Seite mit ihrem grossen Diagramm schon aufgebaut ist.
|
||||
// Sperrfrist nach einem Standby-Wechsel: letzten Wert halten statt der frischen Probe.
|
||||
bool sperre = g_kurvenSperreBis != 0 && (int32_t)(millis() - g_kurvenSperreBis) < 0;
|
||||
if (!sperre) { g_haltW = g_state.tempW; g_haltD = g_state.tempD; }
|
||||
const float probeW = sperre ? g_haltW : g_state.tempW;
|
||||
const float probeD = sperre ? g_haltD : g_state.tempD;
|
||||
// Frische Probe einreihen, in die Kurven geht der mittlere der letzten fuenf Werte.
|
||||
g_medW[g_medPos] = g_state.tempW;
|
||||
g_medD[g_medPos] = g_state.tempD;
|
||||
g_medPos = (uint8_t)((g_medPos + 1) % KURVEN_MEDIAN);
|
||||
if (g_medFill < KURVEN_MEDIAN) g_medFill++;
|
||||
const float probeW = median_von(g_medW, g_medFill);
|
||||
const float probeD = median_von(g_medD, g_medFill);
|
||||
|
||||
if (g_sparkSerW && g_sparkW)
|
||||
lv_chart_set_next_value(g_sparkW, g_sparkSerW, (int32_t)(probeW * 10.0f));
|
||||
@@ -4275,16 +4300,6 @@ void ui_update(const MachineState& st) {
|
||||
// Standby-Uhr-Overlay (Backlight wird in onState gesteuert). Uebergaenge werden weich
|
||||
// gefadet: Einschlafen blendet die Uhr ein, Aufwecken blendet sie aus. Nur bei echtem
|
||||
// Statuswechsel animieren (g_standbyShown), sonst nur Uhrzeit/Vordergrund aktualisieren.
|
||||
// Standby-Wechsel merken: die Verlaufskurven halten danach kurz ihren letzten Wert,
|
||||
// damit die Messstoerung beim Schalten der Heizungen keine Delle in die Linie zieht.
|
||||
static bool letzterStandby = false;
|
||||
if (st.standbyActive != letzterStandby) {
|
||||
letzterStandby = st.standbyActive;
|
||||
g_haltW = st.tempW;
|
||||
g_haltD = st.tempD;
|
||||
g_kurvenSperreBis = millis() + KURVEN_SPERRE_MS;
|
||||
}
|
||||
|
||||
if (g_standbyScreen) {
|
||||
bool shouldShow = st.standbyActive && st.standbyShowClock;
|
||||
if (shouldShow) {
|
||||
|
||||
Reference in New Issue
Block a user