From a5dab0cb60f3248df1506b04d2c167bf68bc0834 Mon Sep 17 00:00:00 2001 From: raw-designs Date: Sat, 12 Sep 2026 02:42:40 +0200 Subject: [PATCH] feat: Temperaturen mit Nachkommastelle anzeigen (S3 5.6.4 / P4 1.12.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Dampf-/Thermoblock-Temperatur wurde direkt nach dem Einlesen auf ganze Grad gerundet. Damit war die Nachkommastelle weg, bevor irgendeine Anzeige sie sehen konnte - obwohl Webinterface, Verlaufskurven und die UART-Verbindung zum Display sie längst übertragen haben. Sichtbar wurde sie nur durch die Offset-Kompensation, die im kalten Bereich den Rohwert durchreicht; daher stand beim Wasserkessel dort zufällig eine Nachkommastelle, im Betrieb aber nie. Die Anzeigewerte entstehen jetzt auf einem getrennten Weg aus dem geglätteten Rohwert (Wasser, Dampf, Zusatzsensor). Die Regelung bleibt unverändert: PID, Sicherheitsabschaltungen und Auto-Tune arbeiten weiter mit dem auf ganze Grad gerundeten Messwert, damit sich das Heizverhalten nicht verschiebt. Die letzte Stelle ist leicht tiefpassgefiltert - das Thermoelement liefert ein 0,25-Grad-Raster, der NTC das Rauschen des ADC -, ein größerer Sprung wird weiterhin sofort übernommen. Das OLED zeigt weiter ganze Grad, rundet den Wert jetzt aber, statt ihn abzuschneiden. Auf dem Display bekommt die Dampftemperatur dieselbe Auflösung wie die Wassertemperatur. Weil "124,5°C" rund ein Drittel breiter ist als "124°C" und die Beschriftung feste Maße hat, wäre der Text in der randlosen Flächen-Darstellung umgebrochen und die zweite Zeile aus der Karte gefallen - das betraf schon vorher die Wassertemperatur ab 100 °C. Die große Zahl weicht jetzt automatisch eine Schriftstufe zurück, und zwar für beide Kessel gemeinsam, damit nicht nur eine von beiden schrumpft. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015dHPsRS7nbF6ZCyuF6tDck --- Changelog.txt | 17 +++++ Dual-PID.ino | 77 ++++++++++++++++++----- P4_Display_Firmware/Changelog.txt | 13 ++++ P4_Display_Firmware/config.h | 2 +- P4_Display_Firmware/ui.cpp | 100 +++++++++++++++++++++++++++--- 5 files changed, 181 insertions(+), 28 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index c62e24c..4d5e87d 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,20 @@ +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 + ganze Grad gerundet; die Nachkommastelle war damit schon weg, bevor irgendeine Anzeige sie sehen + konnte. Die einzige Ausnahme war die Offset-Kompensation, die im kalten Bereich den Rohwert + durchreicht - daher stand beim Wasserkessel dort zufällig eine Nachkommastelle, im Betrieb aber nie. +- Das gilt genauso für den Wasserkessel und den Zusatzsensor (Gehäuse/Tassenablage), damit alle + Temperaturen einheitlich aufgelöst sind. +- Die Regelung bleibt unverändert: PID, Sicherheitsabschaltungen und Auto-Tune arbeiten weiter mit dem + auf ganze Grad gerundeten Messwert. Die feinere Zahl entsteht auf einem getrennten Anzeigeweg, damit + sich das Heizverhalten durch diese Änderung nicht verschiebt. +- Die letzte Stelle ist leicht geglättet. Ungefiltert würde sie ständig springen - das Thermoelement + am Dampfkessel liefert ein 0,25-Grad-Raster, der NTC am Wasserkessel das Rauschen des ADC. Ein + größerer Temperatursprung wird weiterhin sofort übernommen, die Anzeige läuft also nicht nach. +- Das kleine OLED zeigt weiterhin ganze Grad (dort ist kein Platz), rundet den Wert jetzt aber, statt + die Nachkommastelle abzuschneiden. + Version 5.6.3: - Bug-Fix: Beim Aufwecken über das Touch-Display blieb die Auswahl „Espresso oder Cold Extraction" manchmal aus, während sie über den Schalter kam. Ursache waren zwei unterschiedliche Bedingungen für diff --git a/Dual-PID.ino b/Dual-PID.ino index 41fa930..52f7a3f 100644 --- a/Dual-PID.ino +++ b/Dual-PID.ino @@ -369,7 +369,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire); * Firmware-Informationen ************************************************************************************/ -String version = "5.6.3"; +String version = "5.6.4"; String versionHersteller = "Thomas Müller"; String versionHerstellerMail = "thomas@mueller.black"; @@ -603,6 +603,10 @@ double SetpointWasser, InputWasser, OutputWasser; double OffsetDampf = 0.0, OffsetWasser = 0.0; double OffsetCase = 0.0; double RawInputDampf = NAN, RawInputWasser = NAN; +// Geglaettete Rohwerte - ausschliesslich fuer die Anzeige. Die Regelung arbeitet weiter mit +// den auf ganze Grad gerundeten Input*-Werten, damit sich am Regelverhalten nichts aendert; +// hier entsteht nur die feinere Zahl fuers Auge. +double SmoothRawDampf = NAN, SmoothRawWasser = NAN, SmoothRawCase = NAN; double displayReferenceRawDampf = NAN, displayReferenceRawWasser = NAN; bool offsetCompensationEnabled = false; const double OFFSET_COMPENSATION_MAX_REFERENCE_RAW_C = 40.0; @@ -621,6 +625,33 @@ void updateOffsetCompensationReferences() { } } +// Tiefpass fuer die Anzeigewerte. Ohne ihn zappelt die Nachkommastelle dauernd: Das +// Thermoelement am Dampfkessel liefert ein 0,25-Grad-Raster, der NTC am Wasserkessel das +// Rauschen des ADC. Ein grosser Sprung (Kaltstart, Sensorwechsel, Bezug) wird sofort +// uebernommen, damit die Anzeige nicht hinterherlaeuft. +const double DISPLAY_TEMP_SMOOTHING = 0.25; // Gewicht des neuen Messwerts je Messtakt +const double DISPLAY_TEMP_JUMP_RESET_C = 5.0; // ab dieser Abweichung ungeglaettet uebernehmen + +void updateSmoothedRawTemperature(double &smoothed, double measured) { + if (!isfinite(measured)) { + smoothed = measured; + return; + } + if (!isfinite(smoothed) || fabs(measured - smoothed) >= DISPLAY_TEMP_JUMP_RESET_C) { + smoothed = measured; + return; + } + smoothed += DISPLAY_TEMP_SMOOTHING * (measured - smoothed); +} + +// Anzeigewerte auf eine Nachkommastelle festzurren, damit Weboberflaeche, Display und +// Verlaufskurven denselben Wert sehen und die letzte Stelle nicht zwischen den Kanaelen +// auseinanderlaeuft. +double quantizeDisplayTemperature(double value) { + if (!isfinite(value)) return value; + return round(value * 10.0) / 10.0; +} + double getDisplayTemperature(double correctedTemp, double rawTemp, double offset, double referenceRaw) { if (!offsetCompensationEnabled || offset >= 0.0 || !isfinite(correctedTemp) || !isfinite(rawTemp) || !isfinite(referenceRaw)) { return correctedTemp; @@ -634,11 +665,17 @@ double getDisplayTemperature(double correctedTemp, double rawTemp, double offset } double getDisplayedWaterTemperature() { - return getDisplayTemperature(InputWasser, RawInputWasser, OffsetWasser, displayReferenceRawWasser); + return quantizeDisplayTemperature(getDisplayTemperature( + SmoothRawWasser + OffsetWasser, SmoothRawWasser, OffsetWasser, displayReferenceRawWasser)); } double getDisplayedSteamTemperature() { - return getDisplayTemperature(InputDampf, RawInputDampf, OffsetDampf, displayReferenceRawDampf); + return quantizeDisplayTemperature(getDisplayTemperature( + SmoothRawDampf + OffsetDampf, SmoothRawDampf, OffsetDampf, displayReferenceRawDampf)); +} + +double getDisplayedCaseTemperature() { + return quantizeDisplayTemperature(SmoothRawCase + OffsetCase); } // PID-Konstanten (Regelungsparameter) @@ -3243,6 +3280,7 @@ void resetToDefaults() { digitalWrite(SSR_LIGHT_PIN, LOW); standbyModeActive = false; InputCase = NAN; + SmoothRawCase = NAN; caseSensorError = false; // PID Limits neu setzen (falls Fenstergröße geändert wurde) @@ -5546,12 +5584,17 @@ void loop() { RawInputWasser = readNTCTemperature(); InputDampf = round(RawInputDampf + OffsetDampf); InputWasser = round(RawInputWasser + OffsetWasser); + updateSmoothedRawTemperature(SmoothRawDampf, RawInputDampf); + updateSmoothedRawTemperature(SmoothRawWasser, RawInputWasser); updateOffsetCompensationReferences(); if (caseSensorEnabled) { - InputCase = round(readCaseTemperature() + OffsetCase); + double rawCase = readCaseTemperature(); + InputCase = round(rawCase + OffsetCase); + updateSmoothedRawTemperature(SmoothRawCase, rawCase); caseSensorError = (isnan(InputCase) || InputCase < 0.0); } else { InputCase = NAN; + SmoothRawCase = NAN; caseSensorError = false; } yield(); @@ -6456,7 +6499,7 @@ if (scaleModeActive) { display.println(F("Heizt auf 93 C")); display.println(F("vor...")); display.print(F("Wasser: ")); - display.print((int)displayedWaterTemp); + display.print((int)lround(displayedWaterTemp)); display.print(F("/")); display.print((int)cleaningAssistantWaterSetpoint); display.print(F(" ")); @@ -6480,7 +6523,7 @@ if (scaleModeActive) { display.print((remainingMs + 999UL) / 1000UL); display.println(F(" s")); display.print(F("Wasser: ")); - display.print((int)displayedWaterTemp); + display.print((int)lround(displayedWaterTemp)); display.print(F("/")); display.print((int)cleaningAssistantWaterSetpoint); display.print(F(" ")); @@ -6522,7 +6565,7 @@ if (scaleModeActive) { else { display.print(F("Init")); } display.println(); display.print("Temperatur:"); - display.print((int)(autoTuneWasserActive ? displayedWaterTemp : displayedSteamTemp)); + display.print((int)lround(autoTuneWasserActive ? displayedWaterTemp : displayedSteamTemp)); display.print(F(" ")); display.print((char)247); display.print(F("C")); @@ -6553,7 +6596,7 @@ if (scaleModeActive) { display.println(F("wenn Temp. erreicht!")); display.println(F(" ")); display.println(F("Temperatur: ")); - display.print((int)displayedWaterTemp); + display.print((int)lround(displayedWaterTemp)); display.print(F(" / ")); display.print((int)SetpointWasser); display.print(F(" ")); @@ -6832,13 +6875,13 @@ if (scaleModeActive) { else if (wasserSensorError) { display.print(F("Sensorfehler!")); } else if (coldExtractionModeActive) { // Cold Extraction: kein Sollwert-Vergleich sinnvoll, der Wasserkreis heizt nicht - display.print((int)displayedWaterTemp); + display.print((int)lround(displayedWaterTemp)); display.print(F(" ")); display.print((char)247); display.print(F("C (COLD)")); } else { - display.print((int)displayedWaterTemp); + display.print((int)lround(displayedWaterTemp)); display.print(F(" / ")); display.print((int)SetpointWasser); display.print(F(" ")); @@ -6857,7 +6900,7 @@ if (scaleModeActive) { bool steamDelayActiveSystem = (dampfVerzoegerung > 0 && (millis() - startupTime < (unsigned long)dampfVerzoegerung * 60000UL)); bool shouldShowDelayMessage = steamDelayActiveSystem && !steamDelayOverridden; if (!shouldShowDelayMessage) { - display.print((int)displayedSteamTemp); + display.print((int)lround(displayedSteamTemp)); display.print(F(" / ")); display.print((int)SetpointDampf); display.print(F(" ")); @@ -8311,9 +8354,9 @@ void recordResetCheckpoint(uint16_t checkpoint) { if (steamHeatDisabledByUser) flags |= RESET_DIAG_FLAG_STEAM_HEAT_DISABLED; rtcResetDiag.flags = flags; - rtcResetDiag.waterTempDeciC = encodeResetDiagSignedTenths((float)InputWasser); - rtcResetDiag.steamTempDeciC = encodeResetDiagSignedTenths((float)InputDampf); - rtcResetDiag.caseTempDeciC = encodeResetDiagSignedTenths((float)InputCase); + rtcResetDiag.waterTempDeciC = encodeResetDiagSignedTenths((float)getDisplayedWaterTemperature()); + rtcResetDiag.steamTempDeciC = encodeResetDiagSignedTenths((float)getDisplayedSteamTemperature()); + rtcResetDiag.caseTempDeciC = encodeResetDiagSignedTenths((float)getDisplayedCaseTemperature()); rtcResetDiag.weightDeciG = encodeResetDiagSignedTenths(currentWeightReading); rtcResetDiag.scaleType = scaleType; rtcResetDiag.wifiStatus = (int8_t)WiFi.status(); @@ -11533,7 +11576,7 @@ void handleChartData(AsyncWebServerRequest *request) { if (!caseSensorEnabled || caseSensorError || isnan(InputCase)) { response->print(F("null")); } else { - snprintf(buffer, sizeof(buffer), "%.1f", InputCase); + snprintf(buffer, sizeof(buffer), "%.1f", getDisplayedCaseTemperature()); response->print(buffer); } @@ -16544,7 +16587,7 @@ void buildDashboardJson(char *buffer, size_t bufferSize) { if (!caseTempDashboardActive || caseSensorError || isnan(InputCase)) { offset += snprintf(buffer + offset, bufferSize - offset, "\"caseTemp\":null,"); } else { - dtostrf(InputCase, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"caseTemp\":%s,", floatBuf); + dtostrf(getDisplayedCaseTemperature(), 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"caseTemp\":%s,", floatBuf); } // Shot-Daten @@ -16973,7 +17016,7 @@ static void buildTouchUartStateJson(char *buffer, size_t bufferSize) { if (!caseSensorEnabled || caseSensorError || isnan(InputCase)) { offset += snprintf(buffer + offset, bufferSize - offset, "\"caseTemp\":null,"); } else { - dtostrf(InputCase, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"caseTemp\":%s,", floatBuf); + dtostrf(getDisplayedCaseTemperature(), 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"caseTemp\":%s,", floatBuf); } offset += snprintf(buffer + offset, bufferSize - offset, "\"maintenanceInterval\":%d,", maintenanceInterval); diff --git a/P4_Display_Firmware/Changelog.txt b/P4_Display_Firmware/Changelog.txt index f814ef6..a9367d5 100644 --- a/P4_Display_Firmware/Changelog.txt +++ b/P4_Display_Firmware/Changelog.txt @@ -1,3 +1,16 @@ +Version 1.12.0: +- Die Dampf-/Thermoblock-Temperatur wird jetzt mit einer Nachkommastelle angezeigt, wie die + Wassertemperatur. Das gilt für die große Zahl auf der Startseite, den Sollwert darunter und + die Beschriftung der Verlaufskurve. Der Wert kam schon immer fein genug über die Verbindung + zur Maschine an - nur die Anzeige hat die Stelle weggeworfen. Voraussetzung ist die + Maschinen-Firmware 5.6.4, davor liefert die Maschine immer ganze Grad. +- Die große Temperaturzahl verkleinert sich automatisch, wenn sie sonst nicht mehr in die + Karte passt. "124,5°C" ist rund ein Drittel breiter als "124°C", und in der randlosen + Flächen-Darstellung reicht die halbe Bildschirmbreite dafür nicht. Bisher brach LVGL die + Zeile dann um und die zweite Zeile fiel aus der Karte - das betraf schon vorher die + Wassertemperatur, sobald sie 100 °C erreichte. +- Im Reinigungsassistenten steht die Wassertemperatur ebenfalls mit einer Nachkommastelle. + Version 1.11.3: - Der Punkt sitzt wieder genau auf dem letzten Messwert, statt eine halbe Punktbreite davor. Die Linie schaut damit rechts nicht mehr unter ihm hervor. Damit trotzdem nichts diff --git a/P4_Display_Firmware/config.h b/P4_Display_Firmware/config.h index 9cd6a16..d778b12 100644 --- a/P4_Display_Firmware/config.h +++ b/P4_Display_Firmware/config.h @@ -126,7 +126,7 @@ // ------------------------------------------------------------------------------------- // Firmware // ------------------------------------------------------------------------------------- -#define DISPLAY_FW_VERSION "1.11.3" // Firmware-Stand der P4-Display-Steuerung (Info-Seite) +#define DISPLAY_FW_VERSION "1.12.0" // Firmware-Stand der P4-Display-Steuerung (Info-Seite) // ------------------------------------------------------------------------------------- // Protokoll diff --git a/P4_Display_Firmware/ui.cpp b/P4_Display_Firmware/ui.cpp index c537e5e..e3d8224 100644 --- a/P4_Display_Firmware/ui.cpp +++ b/P4_Display_Firmware/ui.cpp @@ -1098,6 +1098,9 @@ static lv_obj_t* tempField(lv_obj_t* parent, const char* title, lv_color_t accen // auseinandergezogen; ein paar Pixel weniger binden die Ziffern zu einer Zahl zusammen. if (valueFont == &lv_font_maven_pro_bold_140) lv_obj_set_style_text_letter_space(*big, -3, 0); else if (valueFont == &lv_font_maven_pro_bold_96) lv_obj_set_style_text_letter_space(*big, -2, 0); + // Ausgangsschrift merken: big_temp_apply() darf nur von hier aus verkleinern und muss + // zurueckfinden, sobald der Text wieder kuerzer wird. + lv_obj_set_user_data(*big, (void*)valueFont); *setp = lv_label_create(c); lv_label_set_text(*setp, "Soll --.-"); @@ -1120,6 +1123,79 @@ static lv_obj_t* tempField(lv_obj_t* parent, const char* title, lv_color_t accen return c; } +static int32_t big_temp_letter_space(const lv_font_t* f) { + if (f == &lv_font_maven_pro_bold_140) return -3; + if (f == &lv_font_maven_pro_bold_96) return -2; + return 0; +} + +// Schriftleiter fuer die grosse Temperaturzahl, von gross nach klein. +static const lv_font_t* const kBigTempLadder[] = { + &lv_font_maven_pro_bold_140, &lv_font_maven_pro_bold_96, + &lv_font_maven_pro_48, &lv_font_maven_pro_40, + &lv_font_maven_pro_28 +}; +static const int kBigTempLadderLen = (int)(sizeof(kBigTempLadder) / sizeof(kBigTempLadder[0])); + +static const lv_font_t* big_temp_base(lv_obj_t* label) { + const lv_font_t* base = (const lv_font_t*)lv_obj_get_user_data(label); + return base ? base : lv_obj_get_style_text_font(label, LV_PART_MAIN); +} + +// Kleinste Stufe, in die der Text noch ohne Umbruch passt. Breite UND Hoehe der +// Beschriftung sind fest (siehe tempField) - laeuft der Text ueber, bricht LVGL die Zeile +// um und die zweite Zeile faellt aus der Karte heraus, statt nur eng zu wirken. Mit der +// Nachkommastelle ist "124,5°C" rund ein Drittel breiter als "124°C"; in der randlosen +// Flaechen-Darstellung reicht die halbe Bildschirmbreite dafuer nicht mehr. +// -1 heisst "nicht entscheidbar" - dann bleibt es bei der Ausgangsschrift. +static int big_temp_fit_index(lv_obj_t* label, const char* txt) { + const lv_font_t* base = big_temp_base(label); + int idx = 0; + while (idx < kBigTempLadderLen && kBigTempLadder[idx] != base) idx++; + if (idx >= kBigTempLadderLen) return -1; // Schrift nicht in der Leiter + int32_t avail = lv_obj_get_content_width(label); + if (avail <= 0) return idx; // vor dem ersten Layout noch unbekannt + for (; idx < kBigTempLadderLen - 1; idx++) { + lv_point_t sz; + lv_text_get_size(&sz, txt, kBigTempLadder[idx], big_temp_letter_space(kBigTempLadder[idx]), + 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE); + if (sz.x <= avail) break; + } + return idx; +} + +static void big_temp_apply(lv_obj_t* label, int idx, const char* txt) { + const lv_font_t* base = big_temp_base(label); + const lv_font_t* pick = (idx >= 0 && idx < kBigTempLadderLen) ? kBigTempLadder[idx] : base; + if (lv_obj_get_style_text_font(label, LV_PART_MAIN) != pick) { + lv_obj_set_style_text_font(label, pick, 0); + lv_obj_set_style_text_letter_space(label, big_temp_letter_space(pick), 0); + // Die Hoehe bleibt die der Ausgangsschrift (sonst raeumt LVGL das Layout der ganzen + // Seite neu). Die kleinere Zahl wird darin per Innenabstand mittig gehalten, damit + // sie nicht an der Oberkante klebt. + int32_t padTop = (lv_font_get_line_height(base) - lv_font_get_line_height(pick)) / 2; + lv_obj_set_style_pad_top(label, padTop > 0 ? padTop : 0, 0); + } + label_set_if_changed(label, txt); +} + +// Setzt beide grossen Temperaturzahlen gemeinsam. Gleich grosse Felder muessen gleich gross +// bleiben: Muss eine Zahl kleiner gesetzt werden, geht die andere mit. Sonst schrumpfte +// allein der Dampfkessel, weil "125,5°C" breiter ist als "93,5°C" - zwei verschieden +// grosse Zahlen nebeneinander sehen aus wie ein Fehler. Wo die Felder absichtlich ungleich +// sind (gewichtete Darstellung), bleibt jede Zahl bei ihrer eigenen Stufe. +static void big_temp_set_pair(lv_obj_t* a, const char* ta, lv_obj_t* b, const char* tb) { + if (!a || !b || !ta || !tb) return; + int ia = big_temp_fit_index(a, ta); + int ib = big_temp_fit_index(b, tb); + if (ia >= 0 && ib >= 0 && big_temp_base(a) == big_temp_base(b)) { + int common = (ia > ib) ? ia : ib; + ia = ib = common; + } + big_temp_apply(a, ia, ta); + big_temp_apply(b, ib, tb); +} + // Flache Verlaufskurve unter einer Temperatur. Zeigt, ob die Regelung haelt - das ist die // eigentliche Frage an eine PID-Regelung, und eine Zahl kann sie grundsaetzlich nicht // beantworten. Gespeist wird sie aus derselben Sekundenabtastung wie die Verlaufsseite. @@ -2202,7 +2278,7 @@ static void chart_sample_cb(lv_timer_t*) { (int)(dw + 0.5f), (int)(dd + 0.5f)); if (g_legW) lv_label_set_text(g_legW, ("Wasser " + tempStr(g_state.tempW, 1)).c_str()); - if (g_legD) lv_label_set_text(g_legD, ("Dampf " + tempStr(g_state.tempD, 0)).c_str()); + if (g_legD) lv_label_set_text(g_legD, ("Dampf " + tempStr(g_state.tempD, 1)).c_str()); if (g_legC) { if (caseOn) { const char* name = (g_state.caseSensorType == 1) ? "Tassenablage " : "Gehäuse "; @@ -2766,13 +2842,13 @@ static void cln_refresh() { String s; if (st.cleaningWaiting) { s = "Heizt auf 93 °C vor...\n\n"; - s += "Wasser: " + tempStr(st.tempW, 0) + " / 93°C\n\n"; + s += "Wasser: " + tempStr(st.tempW, 1) + " / 93°C\n\n"; s += "Die Zyklen starten automatisch,\nsobald die Temperatur erreicht ist."; } else { s = "Zyklus " + String(st.cleaningCycle) + " / " + String(st.cleaningCycles) + "\n\n"; s += "Phase: " + String(st.cleaningBrewPhase ? "Bezug" : "Pause") + "\n"; s += "Rest: " + String(st.cleaningPhaseRemainSec) + " s\n\n"; - s += "Wasser: " + tempStr(st.tempW, 0) + " / 93°C\n"; + s += "Wasser: " + tempStr(st.tempW, 1) + " / 93°C\n"; s += "Dampfheizung aus"; } if (g_clnProgLbl) label_set_if_changed(g_clnProgLbl, s.c_str()); @@ -4749,14 +4825,20 @@ void ui_update(const MachineState& st) { // Dashboard - Temperaturkarten (im Fehlerfall Warnsymbol + Klartext statt Zahl, wie OLED) if (lblTempW) { - if (st.wasserSafetyShutdown || st.wasserSensorError) { - label_set_if_changed(lblTempW, LV_SYMBOL_WARNING); + // Beide grossen Zahlen zusammen setzen, damit sie bei knapper Kartenbreite + // gemeinsam eine Stufe kleiner werden (siehe big_temp_set_pair). + const bool warnW = st.wasserSafetyShutdown || st.wasserSensorError; + const bool warnD = st.dampfSafetyShutdown || st.dampfSensorError; + String bigW = warnW ? String(LV_SYMBOL_WARNING) : tempStr(st.tempW, 1); + String bigD = warnD ? String(LV_SYMBOL_WARNING) : tempStr(st.tempD, 1); + big_temp_set_pair(lblTempW, bigW.c_str(), lblTempD, bigD.c_str()); + + if (warnW) { label_color_if_changed(lblTempW, COL_DANGER); label_set_if_changed(lblSetW, st.wasserSafetyShutdown ? "Sicherheitsabsch." : "Sensorfehler!"); label_color_if_changed(lblSetW, COL_DANGER); temp_progress_set(barDutyW, arcTempW, bulbTempW, 0, COL_DANGER); } else { - label_set_if_changed(lblTempW, tempStr(st.tempW, 1).c_str()); if (st.cxActive) { // Cold Extraction: der Sollwert ist bedeutungslos, es wird nicht geheizt. // Statt eines Zielbands, das nie erreicht wird, den Grund anzeigen. @@ -4775,17 +4857,15 @@ void ui_update(const MachineState& st) { temp_progress_set(barDutyW, arcTempW, bulbTempW, pctW, colW); } } - if (st.dampfSafetyShutdown || st.dampfSensorError) { - label_set_if_changed(lblTempD, LV_SYMBOL_WARNING); + if (warnD) { label_color_if_changed(lblTempD, COL_DANGER); label_set_if_changed(lblSetD, st.dampfSafetyShutdown ? "Sicherheitsabsch." : "Sensorfehler!"); label_color_if_changed(lblSetD, COL_DANGER); temp_progress_set(barDutyD, arcTempD, bulbTempD, 0, COL_DANGER); } else { - label_set_if_changed(lblTempD, tempStr(st.tempD, 0).c_str()); lv_color_t colD = temp_state_color(st.tempD, st.setD); label_color_if_changed(lblTempD, temp_value_color(colD)); - label_set_if_changed(lblSetD, ("Soll " + tempStr(st.setD, 0)).c_str()); + label_set_if_changed(lblSetD, ("Soll " + tempStr(st.setD, 1)).c_str()); label_color_if_changed(lblSetD, COL_TEXT_DIM); int pctD = (st.setD > 0.0f) ? (int)(st.tempD / st.setD * 100.0f + 0.5f) : 0; temp_progress_set(barDutyD, arcTempD, bulbTempD, pctD, colD); -- 2.54.0