Compare commits

..
Author SHA1 Message Date
raw-designsandClaude Opus 5 0ec3d89973 diag(P4): Messpunkte für den Stillstand beim Standby-Wechsel (1.9.7)
Beim Wechsel in den Standby bleibt das Display stehen: Bild bleibt auf der
alten Seite, keine Bedienung, auch der S3 weckt nicht mehr auf. Der Log
bricht nach dem letzten Bildaufbau ab, ohne Panic.

Statt zu raten, Messpunkte:
- Herzschlag alle 2 s aus dem Arduino-loop (freier Heap) und aus einem
  lv_timer im LVGL-Task (lv_mem_monitor). Bleibt nur einer aus, ist klar,
  welcher Task steht.
- ui_fade_in() der Standby-Uhr wird eingeklammert: "wird eingeblendet" /
  "eingeblendet, N ms".
- hal_backlight() wird gemessen; über 50 ms wird die Dauer gemeldet.
  Der Bridge-Schreibzugriff läuft über denselben I2C-Bus wie der Touch.

Außerdem entfernt: die LV_EVENT_INVALIDATE_AREA-Callbacks aus 1.9.5. Das
Event geht per lv_display_send_event an das Display, nicht an Objekte - die
Callbacks konnten nie feuern, ihr Ausbleiben war kein Beweis.

Beide Panel-Varianten mit arduino-cli gegengebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeFMr439WcLMdebE3kU7fS
2026-09-03 13:02:35 +02:00
thomas 06d9710b16 Merge pull request 'feat(P4): Eigener Menüpunkt „Darstellung“, größere Uhr im Standby (1.9.6)' (#75) from feat/darstellung-eigene-seite-groessere-uhr into main 2026-09-03 09:24:31 +02:00
raw-designsandClaude Opus 5 b3b96362c3 feat(P4): Eigener Menüpunkt "Darstellung", größere Uhr im Standby (1.9.6)
- Der Darstellungsblock wandert aus build_info() in ein eigenes build_display()
  mit neuer Seite PG_DISPLAY. Die Info-Seite zeigt nur noch Firmware-Stände und
  Verbindungsdaten.
- clock_font() liefert für WS_PANEL_7H jetzt 180/270/360 statt 120/180/240. Für
  JC_PANEL_43 bleiben die alten Stufen - 360 px passen nicht auf 480x480.
- Neue Fonts lv_font_clock_270/_360 (Maven Pro Regular, bpp 4, Range
  32,45,0x30-0x3A). bpp 3 scheidet aus: lv_font_conv erlaubt das nur mit
  Kompression, und die kostet beim Zeichnen genau das, was gerade eingespart wird.
- Alle fünf Uhr-Fonts sind deklariert; der Linker verwirft die je nach Panel
  ungenutzten. Netto wächst die WS-Firmware um 196 KB.

Beide Panel-Varianten mit arduino-cli gegengebaut (PartitionScheme=custom,
passend zur partitions.csv mit zwei 6,5-MB-Slots).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeFMr439WcLMdebE3kU7fS
2026-09-03 09:23:50 +02:00
thomas 14f1a4789d Merge pull request 'perf(P4): Auch feste Höhen - LVGL markiert sonst das Layout als dirty (1.9.5)' (#74) from perf/feste-hoehen-und-nachweis into main 2026-09-03 07:25:04 +02:00
raw-designsandClaude Opus 5 326af4288a perf(P4): Auch feste Höhen - LVGL markiert sonst das Layout als dirty (1.9.5)
Die festen Breiten aus 1.9.4 haben nicht gereicht. lv_label_set_text ruft
lv_obj_refresh_self_size(); ist eines der beiden Maße LV_SIZE_CONTENT, gilt
das Parent-Layout als dirty und der Container wird komplett invalidiert.

- tempField(): big und setp bekommen zusätzlich eine feste Höhe aus
  lv_font_get_line_height().
- infoCell(): val ebenso, mit Platz für zwei Zeilen.
- g_clock: feste Breite/Höhe, rechtsbündig. Erklärt den Bereich
  "x 6..57, y 2..41" (Menü-Knopf), der bei jedem Minutenwechsel durch das
  Header-Relayout mit invalidiert wurde.

Zusätzlich eine gezielte Diagnose: LV_EVENT_INVALIDATE_AREA auf g_content
und der Dashboard-Seite meldet, wenn ein ganzer Container invalidiert wird -
damit ist "einzelnes Label" von "kompletter Bereich" zu unterscheiden.

Beide Panel-Varianten mit arduino-cli gegengebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 03:10:40 +02:00
thomas 2e8651bc00 Merge pull request 'perf(P4): Feste Breiten für Anzeigewerte - kein Neulayout mehr (1.9.4)' (#73) from perf/feste-breiten-kein-neulayout into main 2026-09-03 02:56:24 +02:00
raw-designsandClaude Opus 5 b13e5fdaf1 perf(P4): Feste Breiten für Anzeigewerte - kein Neulayout mehr (1.9.4)
Die Koordinaten haben den Verursacher benannt: Der invalidierte Bereich war
"x 0..1279, y 44..719" - der komplette Content-Container, bei jeder
Zustandsmeldung.

Grund: Die Wert-Labels hatten LV_SIZE_CONTENT. Ändert sich ihre Breite mit
dem Inhalt (92,4 °C -> 100,1 °C), löst LVGL ein Flex-Relayout der ganzen
Seite aus und invalidiert sie komplett. Change-checked Text allein reicht
also nicht - der Text darf beim Ändern auch die Breite nicht ändern.

tempField() und infoCell() setzen die Wert-Labels jetzt auf LV_PCT(100) mit
zentriertem Text. Optisch identisch, da die Werte ohnehin mittig standen.

Beide Panel-Varianten mit arduino-cli gegengebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 02:56:05 +02:00
thomas 671a6696ce Merge pull request 'fix(P4): Menüvorhang wieder durchscheinend, weitere Invalidierung entfernt (1.9.3)' (#72) from fix/menuevorhang-und-weitere-invalidierung into main 2026-09-03 02:36:48 +02:00
6 changed files with 38463 additions and 5 deletions
+48
View File
@@ -1,3 +1,51 @@
Version 1.9.7:
- Messpunkte für den Stillstand beim Wechsel in den Standby: Beide Abläufe - der Haupt- und
der Zeichenablauf - melden jetzt alle zwei Sekunden einen Herzschlag mit Speicherstand.
Bleibt nur einer von beiden aus, ist klar, welcher stehen bleibt.
- Das Einblenden der Standby-Uhr meldet Anfang und Ende mit Dauer. Bleibt die zweite Meldung
aus, hängt es genau dort.
- Jeder Helligkeitswechsel, der länger als 50 ms braucht, wird mit seiner Dauer gemeldet. Der
Schreibzugriff geht über denselben Bus wie der Touch - ein Verdacht für die bekannte
Verzögerung von ein bis zwei Sekunden.
- Die Rückmeldung aus 1.9.5, die einen komplett ungültigen Inhaltsbereich melden sollte, ist
wieder entfernt. Sie konnte nie auslösen: LVGL schickt diese Meldung an das Display, nicht
an einzelne Elemente. Ihr Ausbleiben war also kein Beweis.
Version 1.9.6:
- Die Darstellungseinstellungen haben einen eigenen Menüpunkt „Darstellung" bekommen. Sie
standen bisher oben auf der Info-Seite, zwischen Sachen, mit denen sie nichts zu tun
haben. Die Info-Seite zeigt jetzt nur noch Firmware-Stände und Verbindungsdaten.
- Die Uhr im Standby ist auf dem 7-Zoll-Panel deutlich größer geworden: Die drei Stufen
liegen jetzt bei 180, 270 und 360 Pixeln statt bei 120, 180 und 240. Auf dem 4,3-Zoll-
Panel bleiben die bisherigen Stufen, dort passen größere schlicht nicht auf den Schirm.
Version 1.9.5:
- Die festen Breiten aus 1.9.4 haben nicht gereicht: Auch die HÖHE muss festliegen. LVGL
prüft beim Setzen eines Textes, ob sich die Eigengröße der Beschriftung geändert haben
könnte - ist eines der beiden Maße frei, gilt das Layout des Elternteils als neu zu
berechnen, und das macht die ganze Seite ungültig. Temperaturwerte, Sollwerte, die Werte
der unteren Zeile und die Uhrzeit in der Kopfleiste haben jetzt feste Maße in beide
Richtungen.
- Die Uhrzeit hatte denselben Effekt in der Kopfleiste: Beim Wechsel der Minute wurde die
Leiste samt Menü-Knopf neu berechnet. Das erklärt den Bereich „x 6..57, y 2..41", der in
fast jedem Durchgang auftauchte.
- Zusätzlich meldet die Firmware jetzt, wenn der Inhaltsbereich oder die Dashboard-Seite als
Ganzes ungültig gemacht wird. Damit ist zu unterscheiden, ob eine einzelne Beschriftung
den Bildaufbau auslöst oder gleich der komplette Bereich - zwei verschiedene Ursachen mit
verschiedenen Gegenmitteln.
Version 1.9.4:
- Die Messung mit Koordinaten hat den Verursacher benannt: Der ungültige Bereich war
„x 0..1279, y 44..719" - also der gesamte Inhalt unterhalb der Kopfleiste, bei jeder
Zustandsmeldung.
- Grund: Die Anzeigewerte hatten keine feste Breite. Wächst oder schrumpft eine Beschriftung
mit ihrem Inhalt - etwa von „92,4 °C" auf „100,1 °C" -, rechnet LVGL das Layout der
gesamten Seite neu und macht sie dabei komplett ungültig. Es genügt also nicht, den Text
nur bei echter Änderung zu setzen; er darf beim Ändern auch die Breite nicht verändern.
- Temperaturwerte, Sollwerte und die Werte der unteren Zeile haben jetzt eine feste Breite
mit zentriertem Text. Sichtbar ändert sich dadurch nichts - die Werte standen ohnehin
mittig.
Version 1.9.3:
- Fehlerbehebung: Bei geöffnetem Menü war der Seiteninhalt dahinter verschwunden. Der
Vorhang des Menüs war in 1.8.7 deckend gemacht worden, um Zeichenlast zu sparen - dabei
@@ -67,11 +67,25 @@ static void onState(const MachineState& st) {
}
if (bl < 0) bl = 0; if (bl > 100) bl = 100;
if (bl != s_lastBacklight) {
// Vermessen: Rund um jeden Helligkeitswechsel hing die Bedienung bisher ein bis
// zwei Sekunden. Der Schreibzugriff geht ueber denselben I2C-Bus wie der Touch.
uint32_t t0 = millis();
hal_backlight(bl);
uint32_t dt = millis() - t0;
if (dt > 50) Serial.printf("[HAL] Helligkeit %d %% gesetzt, %u ms\n", bl, (unsigned)dt);
s_lastBacklight = bl;
}
}
// Herzschlag aus dem Haupt-Ablauf. Zusammen mit dem Herzschlag aus dem Zeichen-Ablauf
// laesst sich unterscheiden, welcher der beiden stehen bleibt, wenn nichts mehr geht.
static void main_heartbeat() {
static uint32_t s_last = 0;
if (millis() - s_last < 2000) return;
s_last = millis();
Serial.printf("[Haupt] Herzschlag, freier Speicher %u Byte\n", (unsigned)ESP.getFreeHeap());
}
static void onAck(uint32_t id, bool ok, const String& message) {
// Automatische/Heartbeat-Acks nicht als Toast zeigen (pong, Handshake, Listen-Refresh).
bool routine = ok && (message == "pong" || message == "hello" ||
@@ -332,6 +346,7 @@ static unsigned long s_lastDbgMs = 0;
#endif
void loop() {
main_heartbeat();
g_client.loop(); // UART lesen, hello/heartbeat, state -> UI
hal_loop(); // realer Modus: leer; Stub-Modus: lv_timer_handler()
+3 -1
View File
@@ -571,7 +571,9 @@
LV_FONT_DECLARE(lv_font_maven_pro_bold_140) \
LV_FONT_DECLARE(lv_font_clock_120) \
LV_FONT_DECLARE(lv_font_clock_180) \
LV_FONT_DECLARE(lv_font_clock_240)
LV_FONT_DECLARE(lv_font_clock_240) \
LV_FONT_DECLARE(lv_font_clock_270) \
LV_FONT_DECLARE(lv_font_clock_360)
/*Always set a default font*/
#define LV_FONT_DEFAULT &lv_font_maven_pro_14
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+55 -4
View File
@@ -127,8 +127,8 @@ static lv_obj_t* g_otaStatus = nullptr;
static lv_obj_t* g_otaBytes = nullptr;
// Navigation
enum { PG_DASH = 0, PG_CHART, PG_STATS, PG_TEMP, PG_BREW, PG_COLDEX, PG_SERVICE, PG_PROFILE, PG_LIGHT, PG_WIFI, PG_INFO, PG_COUNT };
static const char* PAGE_NAMES[PG_COUNT] = { "Dashboard", "Verlauf", "Statistik", "Temperatur", "Brew-Control", "Cold Extraction", "Reinigung/Wartung", "Profile", "Licht", "WiFi", "Info" };
enum { PG_DASH = 0, PG_CHART, PG_STATS, PG_TEMP, PG_BREW, PG_COLDEX, PG_SERVICE, PG_PROFILE, PG_LIGHT, PG_WIFI, PG_DISPLAY, PG_INFO, PG_COUNT };
static const char* PAGE_NAMES[PG_COUNT] = { "Dashboard", "Verlauf", "Statistik", "Temperatur", "Brew-Control", "Cold Extraction", "Reinigung/Wartung", "Profile", "Licht", "WiFi", "Darstellung", "Info" };
static lv_obj_t* g_content = nullptr;
static lv_obj_t* g_pages[PG_COUNT] = { nullptr };
static lv_obj_t* g_drawer = nullptr;
@@ -700,9 +700,17 @@ static lv_obj_t* g_clockSizeBtn[3] = {};
static const lv_font_t* clock_font() {
switch (g_clockSize) {
#if JC_PANEL_TYPE == WS_PANEL_7H
// 7 Zoll, 1280x720: Hier war selbst die groesste Stufe noch zu klein.
case UHR_KLEIN: return &lv_font_clock_180;
case UHR_MITTEL: return &lv_font_clock_270;
default: return &lv_font_clock_360;
#else
// 4,3 Zoll, 480x480: Groessere Stufen passen schlicht nicht auf das Panel.
case UHR_KLEIN: return &lv_font_clock_120;
case UHR_MITTEL: return &lv_font_clock_180;
default: return &lv_font_clock_240;
#endif
}
}
@@ -1056,6 +1064,18 @@ static lv_obj_t* tempField(lv_obj_t* parent, const char* title, lv_color_t accen
lv_label_set_text(*big, "--.-");
lv_obj_set_style_text_color(*big, COL_TEXT, 0);
lv_obj_set_style_text_font(*big, valueFont, 0);
// Feste Breite, Text zentriert. Waechst oder schrumpft eine Beschriftung mit ihrem
// Inhalt - etwa von "92,4 °C" auf "100,1 °C" -, rechnet LVGL das Flex-Layout der
// gesamten Seite neu und macht sie damit komplett ungueltig. Genau das war im
// Messprotokoll als "x 0..1279, y 44..719" zu sehen: der ganze Inhaltsbereich, bei
// jeder Zustandsmeldung. Mit fester Breite bleibt nur die Beschriftung selbst ungueltig.
lv_obj_set_width(*big, LV_PCT(100));
lv_obj_set_style_text_align(*big, LV_TEXT_ALIGN_CENTER, 0);
// Auch die HOEHE muss fest sein. LVGL prueft beim Setzen eines Textes, ob sich die
// Eigengroesse der Beschriftung geaendert haben koennte - ist eines der beiden Masse
// frei, gilt das Layout des Elternteils als neu zu berechnen, und das macht die ganze
// Seite ungueltig. Erst mit festen Massen bleibt nur die Beschriftung selbst ungueltig.
lv_obj_set_height(*big, lv_font_get_line_height(valueFont) + 6);
// Grosse Zahlen etwas enger setzen. Bei dieser Groesse wirken die Werksabstaende
// 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);
@@ -1065,6 +1085,9 @@ static lv_obj_t* tempField(lv_obj_t* parent, const char* title, lv_color_t accen
lv_label_set_text(*setp, "Soll --.-");
lv_obj_set_style_text_color(*setp, COL_TEXT_DIM, 0);
lv_obj_set_style_text_font(*setp, font_text(), 0);
lv_obj_set_width(*setp, LV_PCT(100));
lv_obj_set_style_text_align(*setp, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_height(*setp, lv_font_get_line_height(font_text()) + 6);
*bar = lv_bar_create(c);
lv_obj_set_size(*bar, LV_PCT(92), 8);
@@ -1210,6 +1233,10 @@ static lv_obj_t* infoCell(lv_obj_t* parent, const char* title, lv_obj_t** val, l
lv_obj_set_style_text_color(*val, COL_TEXT, 0);
lv_obj_set_style_text_font(*val, font_title(), 0);
lv_obj_set_style_text_align(*val, LV_TEXT_ALIGN_CENTER, 0); // fuer mehrzeilige Werte
// Feste Masse in BEIDE Richtungen - siehe die ausfuehrliche Begruendung in tempField.
// Zwei Zeilen Platz, weil manche Werte zweizeilig sind ("32,4 g / 36,0 g").
lv_obj_set_width(*val, LV_PCT(100));
lv_obj_set_height(*val, lv_font_get_line_height(font_title()) * 2 + 6);
return card;
}
// Tipp auf die Statuszeile: Wartung -> Serviceseite, Reinigung -> Sub-Screen,
@@ -3081,8 +3108,10 @@ static void brew_live_sw_cb(lv_event_t* e) {
{ Preferences prefs; prefs.begin("ui", false); prefs.putBool("brewlive", g_brewLiveEnabled); prefs.end(); }
if (!g_brewLiveEnabled) brew_live_hide(); // laeuft gerade ein Bezug: sofort ausblenden
}
static void build_info(lv_obj_t* p) {
section_title(p, "Darstellung");
// Eigener Menuepunkt "Darstellung". Die Einstellungen sassen frueher oben auf der
// Info-Seite; dort standen sie zwischen Firmware-Staenden und Verbindungsdaten,
// mit denen sie nichts zu tun haben.
static void build_display(lv_obj_t* p) {
lv_obj_t* dcard = th_card(p);
lv_obj_set_width(dcard, LV_PCT(100));
lv_obj_set_height(dcard, LV_SIZE_CONTENT);
@@ -3135,6 +3164,9 @@ static void build_info(lv_obj_t* p) {
sw_set(swBrewLive, g_brewLiveEnabled);
lv_obj_add_event_cb(swBrewLive, brew_live_sw_cb, LV_EVENT_VALUE_CHANGED, nullptr);
}
static void build_info(lv_obj_t* p) {
section_title(p, "Firmware-Stände & Verbindung");
lv_obj_t* card = th_card(p);
@@ -3533,6 +3565,12 @@ static void refr_ready_cb(lv_event_t*) {
(int)g_refrArea[i].y1, (int)g_refrArea[i].y2);
}
static void ui_heartbeat_cb(lv_timer_t*) {
lv_mem_monitor_t m; lv_mem_monitor(&m);
esp_rom_printf("[UI] Herzschlag, LVGL-Speicher frei %u von %u Byte, groesster Block %u\n",
(unsigned)m.free_size, (unsigned)m.total_size, (unsigned)m.free_biggest_size);
}
static void refr_monitor_install() {
lv_display_t* d = lv_display_get_default();
if (!d) return;
@@ -3650,6 +3688,11 @@ void ui_init(ProtocolClient* client) {
lv_obj_set_style_text_color(g_clock, COL_TEXT, 0);
lv_obj_set_style_pad_right(g_clock, 10, 0);
lv_obj_set_style_text_font(g_clock, font_text(), 0);
// Feste Masse, sonst rechnet LVGL beim Wechsel der Minute das Layout der Kopfleiste neu
// und macht sie samt Menue-Knopf ungueltig (siehe tempField).
lv_obj_set_width(g_clock, 130);
lv_obj_set_height(g_clock, lv_font_get_line_height(font_text()) + 4);
lv_obj_set_style_text_align(g_clock, LV_TEXT_ALIGN_RIGHT, 0);
g_wifiIcon = lv_label_create(header);
lv_label_set_text(g_wifiIcon, LV_SYMBOL_WIFI);
@@ -3677,6 +3720,7 @@ void ui_init(ProtocolClient* client) {
g_pages[PG_SERVICE] = make_page(g_content); build_service(g_pages[PG_SERVICE]);
g_pages[PG_WIFI] = make_page(g_content); build_wifi(g_pages[PG_WIFI]);
g_pages[PG_PROFILE] = make_page(g_content); build_profiles(g_pages[PG_PROFILE]);
g_pages[PG_DISPLAY] = make_page(g_content); build_display(g_pages[PG_DISPLAY]);
g_pages[PG_INFO] = make_page(g_content); build_info(g_pages[PG_INFO]);
// --- Schublade (Scrim + Menue-Panel) ---
@@ -3938,6 +3982,7 @@ void ui_init(ProtocolClient* client) {
if (g_client) g_client->sendListProfiles();
refr_monitor_install();
lv_timer_create(ui_heartbeat_cb, 2000, nullptr);
g_uiReady = true;
}
@@ -4127,8 +4172,14 @@ void ui_update(const MachineState& st) {
if (shouldShow) {
label_set_if_changed(g_standbyClock, zeit.length() ? zeit.c_str() : "--:--");
if (!g_standbyShown) {
// Vermessen: Das Einblenden der Uhr ist der Moment, in dem das Display
// zuletzt stehen geblieben ist. Bleibt die zweite Zeile aus, haengt es hier.
esp_rom_printf("[UI] Standby-Uhr wird eingeblendet\n");
uint32_t t0 = lv_tick_get();
ui_fade_in(g_standbyScreen, UI_FADE_MS); // Einschlaf-Animation
g_standbyShown = true;
esp_rom_printf("[UI] Standby-Uhr eingeblendet, %u ms\n",
(unsigned)(lv_tick_get() - t0));
}
// Frueher wurde die Uhr hier bei JEDER Zustandsmeldung nach vorn geholt. Das
// ordnet die Ebenen neu und laesst LVGL den gesamten Bildschirm neu zeichnen -