diff --git a/P4_Display_Firmware/Changelog.txt b/P4_Display_Firmware/Changelog.txt index 5173390..248f74c 100644 --- a/P4_Display_Firmware/Changelog.txt +++ b/P4_Display_Firmware/Changelog.txt @@ -1,3 +1,19 @@ +Version 1.9.2: +- Ursache der Trägheit endlich gefunden. Die erweiterte Messung zeigte: „1 Bereich, 100 % + der Fläche" - LVGL zeichnete bei jeder Zustandsmeldung den kompletten Bildschirm neu, also + einmal pro Sekunde 240 bis 300 Millisekunden lang. Es war also kein Tempoproblem beim + Zeichnen, sondern es wurde viel zu viel als „muss neu gezeichnet werden" gemeldet. +- Grund: LVGL macht den Bereich einer Beschriftung ungültig, sobald ihr Text gesetzt wird - + auch dann, wenn er sich gar nicht geändert hat. Dasselbe gilt für Farbwechsel. Auf dem + Dashboard wurden im Sekundentakt Werte quer über den ganzen Bildschirm gesetzt: Uhrzeit + oben, Temperaturen in der Mitte, Bezugswerte unten. LVGL fasst benachbarte ungültige + Bereiche zu einem zusammen - und der umfasste dann die gesamte Fläche. +- Alle regelmäßig gesetzten Werte werden jetzt vorher verglichen und nur bei echter Änderung + neu geschrieben; für Farben gilt dasselbe. Da sich Temperaturen selten um ein Zehntelgrad + ändern und der Sollwert praktisch nie, entfallen die meisten dieser Neuzeichnungen. +- Das betrifft alle Bildschirmgrößen; auf den JC-Panels war es nur nie störend, weil dort ein + Bildaufbau ein Bruchteil davon kostet. + Version 1.9.1: - Das direkte Zeichnen in den Bildspeicher aus 1.9.0 ist zurückgenommen: Es hat die Bildaufbauzeit nicht verkürzt (weiterhin 240 bis 300 ms) und stattdessen Bildfehler diff --git a/P4_Display_Firmware/ui.cpp b/P4_Display_Firmware/ui.cpp index 784f1ce..4b3f95c 100644 --- a/P4_Display_Firmware/ui.cpp +++ b/P4_Display_Firmware/ui.cpp @@ -25,6 +25,7 @@ #include "display_hal.h" #include "esp_rom_sys.h" #include "lvgl_private.h" +#include // ------------------------------------------------------------------------------------ // Schriftgroesse @@ -1377,7 +1378,7 @@ static void flush_targ() { static void targ_w_set(float v) { if (v < 0) v = 0; if (v > 135) v = 135; g_targW = v; - if (lblTargW) lv_label_set_text(lblTargW, tempStr(g_targW, 0).c_str()); + if (lblTargW) label_set_if_changed(lblTargW, tempStr(g_targW, 0).c_str()); if (g_arcW) lv_arc_set_value(g_arcW, (int32_t)(g_targW + 0.5f)); g_targWDirty = true; schedule_targ_send(); @@ -1385,7 +1386,7 @@ static void targ_w_set(float v) { static void targ_d_set(float v) { if (v < 0) v = 0; if (v > 200) v = 200; g_targD = v; - if (lblTargD) lv_label_set_text(lblTargD, tempStr(g_targD, 0).c_str()); + if (lblTargD) label_set_if_changed(lblTargD, tempStr(g_targD, 0).c_str()); if (g_arcD) lv_arc_set_value(g_arcD, (int32_t)(g_targD + 0.5f)); g_targDDirty = true; schedule_targ_send(); @@ -3240,10 +3241,35 @@ static void eco_case_timer_cb(lv_timer_t*) { // ===================================================================================== // Setzt den Labeltext nur, wenn er sich aendert -> verhindert unnoetige Invalidierung // (im Full-Refresh-Modus loest jede Aenderung ein komplettes Neuzeichnen aus). +// LVGL macht bei jedem Setzen den Bereich der Beschriftung ungueltig - auch dann, wenn sich +// der Text gar nicht geaendert hat. Auf dem Dashboard werden im Sekundentakt Werte quer ueber +// den ganzen Bildschirm gesetzt; LVGL fasst die ungueltigen Bereiche zu einem zusammen, und +// das ist dann die gesamte Flaeche. Gemessen waren das 240 bis 300 ms pro Sekunde, in denen +// die Oberflaeche mit nichts anderem beschaeftigt war. Deshalb wird ueberall dort, wo sich +// Werte nur selten aendern, vorher verglichen. static void label_set_if_changed(lv_obj_t* lbl, const char* txt) { + if (!lbl || !txt) return; const char* cur = lv_label_get_text(lbl); if (!cur || strcmp(cur, txt) != 0) lv_label_set_text(lbl, txt); } + +// Wie oben, aber mit Formatangabe. +static void label_fmt_if_changed(lv_obj_t* lbl, const char* fmt, ...) { + if (!lbl) return; + char buf[96]; + va_list ap; va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + label_set_if_changed(lbl, buf); +} + +// Auch ein Farbwechsel macht den Bereich ungueltig. Gleiche Farbe -> nichts tun. +static void label_color_if_changed(lv_obj_t* lbl, lv_color_t col) { + if (!lbl) return; + lv_color_t cur = lv_obj_get_style_text_color(lbl, LV_PART_MAIN); + if (lv_color_to_u32(cur) != lv_color_to_u32(col)) + lv_obj_set_style_text_color(lbl, col, 0); +} // Pre-Infusion-Phasenleiste aktualisieren (100-ms-Takt aus dash_timers_cb). // Die aktive Phase kommt bevorzugt von der S3 (piState = echte Statemachine, Quelle der // Wahrheit; Push-Latenz max. 250 ms). Sendet eine aeltere S3-Firmware das Feld noch nicht @@ -4357,43 +4383,43 @@ void ui_update(const MachineState& st) { // Dashboard - Temperaturkarten (im Fehlerfall Warnsymbol + Klartext statt Zahl, wie OLED) if (lblTempW) { if (st.wasserSafetyShutdown || st.wasserSensorError) { - lv_label_set_text(lblTempW, LV_SYMBOL_WARNING); - lv_obj_set_style_text_color(lblTempW, COL_DANGER, 0); - lv_label_set_text(lblSetW, st.wasserSafetyShutdown ? "Sicherheitsabsch." : "Sensorfehler!"); - lv_obj_set_style_text_color(lblSetW, COL_DANGER, 0); + label_set_if_changed(lblTempW, LV_SYMBOL_WARNING); + 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 { - lv_label_set_text(lblTempW, tempStr(st.tempW, 1).c_str()); + 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. - lv_obj_set_style_text_color(lblTempW, temp_value_color(lv_color_hex(0x1976d2)), 0); + label_color_if_changed(lblTempW, temp_value_color(lv_color_hex(0x1976d2))); label_set_if_changed(lblSetW, "Cold Extraction - Heizen aus"); - lv_obj_set_style_text_color(lblSetW, lv_color_hex(0x1976d2), 0); + label_color_if_changed(lblSetW, lv_color_hex(0x1976d2)); temp_progress_set(barDutyW, arcTempW, bulbTempW, 0, lv_color_hex(0x1976d2)); } else { // Ist-Wert faerbt sich weich Richtung Soll (Blau -> Gold -> Gruen im Zielband) lv_color_t colW = temp_state_color(st.tempW, st.setW); - lv_obj_set_style_text_color(lblTempW, temp_value_color(colW), 0); - lv_label_set_text(lblSetW, ("Soll " + tempStr(st.setW, 1)).c_str()); - lv_obj_set_style_text_color(lblSetW, COL_TEXT_DIM, 0); + label_color_if_changed(lblTempW, temp_value_color(colW)); + label_set_if_changed(lblSetW, ("Soll " + tempStr(st.setW, 1)).c_str()); + label_color_if_changed(lblSetW, COL_TEXT_DIM); // Fortschritt = Ist/Soll in %, gedeckelt auf 100 (Ist >= Soll -> voll) int pctW = (st.setW > 0.0f) ? (int)(st.tempW / st.setW * 100.0f + 0.5f) : 0; temp_progress_set(barDutyW, arcTempW, bulbTempW, pctW, colW); } } if (st.dampfSafetyShutdown || st.dampfSensorError) { - lv_label_set_text(lblTempD, LV_SYMBOL_WARNING); - lv_obj_set_style_text_color(lblTempD, COL_DANGER, 0); - lv_label_set_text(lblSetD, st.dampfSafetyShutdown ? "Sicherheitsabsch." : "Sensorfehler!"); - lv_obj_set_style_text_color(lblSetD, COL_DANGER, 0); + label_set_if_changed(lblTempD, LV_SYMBOL_WARNING); + 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 { - lv_label_set_text(lblTempD, tempStr(st.tempD, 0).c_str()); + label_set_if_changed(lblTempD, tempStr(st.tempD, 0).c_str()); lv_color_t colD = temp_state_color(st.tempD, st.setD); - lv_obj_set_style_text_color(lblTempD, temp_value_color(colD), 0); - lv_label_set_text(lblSetD, ("Soll " + tempStr(st.setD, 0)).c_str()); - lv_obj_set_style_text_color(lblSetD, COL_TEXT_DIM, 0); + label_color_if_changed(lblTempD, temp_value_color(colD)); + label_set_if_changed(lblSetD, ("Soll " + tempStr(st.setD, 0)).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); } @@ -4542,12 +4568,12 @@ void ui_update(const MachineState& st) { float wDisp = st.weight; if (wDisp > -0.05f && wDisp <= 0.0f) wDisp = 0.0f; if (!st.scaleEnabled) { - lv_label_set_text(lblBezug, "--"); + label_set_if_changed(lblBezug, "--"); } else if (bbwShot) { // Waehrend des Bezugs mit Brew-by-Weight: Ist oben, Ziel darunter (zweizeilig) - lv_label_set_text_fmt(lblBezug, "%.1f g\n/ %.1f g", wDisp, shotTarget); + label_fmt_if_changed(lblBezug, "%.1f g\n/ %.1f g", wDisp, shotTarget); } else { - lv_label_set_text_fmt(lblBezug, "%.1f g", wDisp); + label_fmt_if_changed(lblBezug, "%.1f g", wDisp); } // Fortschrittsbalken Gewicht/Ziel (nur waehrend BBW-Bezug) if (g_bbwBar) {