fix(P4): Delle nach dem Standby per Median statt Sperrfrist (1.10.4) #86
@@ -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:
|
Version 1.10.3:
|
||||||
- Direkt nach dem Aufwachen aus dem Standby zog die Wasserkurve eine kurze Delle nach
|
- 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
|
unten. Ursache ist die Messung, nicht der Kessel: In genau diesem Moment schalten die
|
||||||
|
|||||||
@@ -126,7 +126,7 @@
|
|||||||
// -------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------
|
||||||
// Firmware
|
// 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
|
// 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
|
// 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
|
// 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
|
// ADC, einmal je Regelzyklus abgetastet - ein Kessel kann binnen einer Sekunde nicht um
|
||||||
// eine Stoerung der Messung, kein Vorgang an der Maschine. Fuer die Dauer des Uebergangs
|
// Grade einbrechen, die Delle ist also eine Stoerung der Messung, kein Vorgang an der
|
||||||
// schreiben die Verlaufskurven deshalb den letzten Wert von vor dem Wechsel weiter. Die
|
// Maschine.
|
||||||
// angezeigten Zahlen bleiben unangetastet - was gemessen wird, steht dort weiter drin.
|
//
|
||||||
#define KURVEN_SPERRE_MS 6000
|
// In die Kurven geht deshalb nicht die frische Probe, sondern der mittlere Wert der
|
||||||
static uint32_t g_kurvenSperreBis = 0; // millis(); bis dahin Werte halten
|
// letzten fuenf (Median). Eine oder zwei verirrte Proben verschwinden damit vollstaendig,
|
||||||
static float g_haltW = 0.0f;
|
// waehrend echte Temperaturaenderungen unverfaelscht durchkommen - nur eben zwei Sekunden
|
||||||
static float g_haltD = 0.0f;
|
// 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) {
|
static void spark_scan(lv_obj_t* ch, lv_chart_series_t* ser, int32_t* mn, int32_t* mx) {
|
||||||
if (!ch || !ser) return;
|
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
|
// Bewusst vor dem Ausstieg unten: Die Kurven im Dashboard haengen nicht daran, ob
|
||||||
// die Verlauf-Seite mit ihrem grossen Diagramm schon aufgebaut ist.
|
// die Verlauf-Seite mit ihrem grossen Diagramm schon aufgebaut ist.
|
||||||
// Sperrfrist nach einem Standby-Wechsel: letzten Wert halten statt der frischen Probe.
|
// Frische Probe einreihen, in die Kurven geht der mittlere der letzten fuenf Werte.
|
||||||
bool sperre = g_kurvenSperreBis != 0 && (int32_t)(millis() - g_kurvenSperreBis) < 0;
|
g_medW[g_medPos] = g_state.tempW;
|
||||||
if (!sperre) { g_haltW = g_state.tempW; g_haltD = g_state.tempD; }
|
g_medD[g_medPos] = g_state.tempD;
|
||||||
const float probeW = sperre ? g_haltW : g_state.tempW;
|
g_medPos = (uint8_t)((g_medPos + 1) % KURVEN_MEDIAN);
|
||||||
const float probeD = sperre ? g_haltD : g_state.tempD;
|
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)
|
if (g_sparkSerW && g_sparkW)
|
||||||
lv_chart_set_next_value(g_sparkW, g_sparkSerW, (int32_t)(probeW * 10.0f));
|
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
|
// Standby-Uhr-Overlay (Backlight wird in onState gesteuert). Uebergaenge werden weich
|
||||||
// gefadet: Einschlafen blendet die Uhr ein, Aufwecken blendet sie aus. Nur bei echtem
|
// gefadet: Einschlafen blendet die Uhr ein, Aufwecken blendet sie aus. Nur bei echtem
|
||||||
// Statuswechsel animieren (g_standbyShown), sonst nur Uhrzeit/Vordergrund aktualisieren.
|
// 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) {
|
if (g_standbyScreen) {
|
||||||
bool shouldShow = st.standbyActive && st.standbyShowClock;
|
bool shouldShow = st.standbyActive && st.standbyShowClock;
|
||||||
if (shouldShow) {
|
if (shouldShow) {
|
||||||
|
|||||||
Reference in New Issue
Block a user