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
6 changed files with 38410 additions and 13 deletions
+21
View File
@@ -1,3 +1,24 @@
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: Version 1.9.5:
- Die festen Breiten aus 1.9.4 haben nicht gereicht: Auch die HÖHE muss festliegen. LVGL - 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 prüft beim Setzen eines Textes, ob sich die Eigengröße der Beschriftung geändert haben
@@ -67,11 +67,25 @@ static void onState(const MachineState& st) {
} }
if (bl < 0) bl = 0; if (bl > 100) bl = 100; if (bl < 0) bl = 0; if (bl > 100) bl = 100;
if (bl != s_lastBacklight) { 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); 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; 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) { static void onAck(uint32_t id, bool ok, const String& message) {
// Automatische/Heartbeat-Acks nicht als Toast zeigen (pong, Handshake, Listen-Refresh). // Automatische/Heartbeat-Acks nicht als Toast zeigen (pong, Handshake, Listen-Refresh).
bool routine = ok && (message == "pong" || message == "hello" || bool routine = ok && (message == "pong" || message == "hello" ||
@@ -332,6 +346,7 @@ static unsigned long s_lastDbgMs = 0;
#endif #endif
void loop() { void loop() {
main_heartbeat();
g_client.loop(); // UART lesen, hello/heartbeat, state -> UI g_client.loop(); // UART lesen, hello/heartbeat, state -> UI
hal_loop(); // realer Modus: leer; Stub-Modus: lv_timer_handler() 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_maven_pro_bold_140) \
LV_FONT_DECLARE(lv_font_clock_120) \ LV_FONT_DECLARE(lv_font_clock_120) \
LV_FONT_DECLARE(lv_font_clock_180) \ 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*/ /*Always set a default font*/
#define LV_FONT_DEFAULT &lv_font_maven_pro_14 #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
+29 -12
View File
@@ -127,8 +127,8 @@ static lv_obj_t* g_otaStatus = nullptr;
static lv_obj_t* g_otaBytes = nullptr; static lv_obj_t* g_otaBytes = nullptr;
// Navigation // 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 }; 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", "Info" }; 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_content = nullptr;
static lv_obj_t* g_pages[PG_COUNT] = { nullptr }; static lv_obj_t* g_pages[PG_COUNT] = { nullptr };
static lv_obj_t* g_drawer = 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() { static const lv_font_t* clock_font() {
switch (g_clockSize) { 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_KLEIN: return &lv_font_clock_120;
case UHR_MITTEL: return &lv_font_clock_180; case UHR_MITTEL: return &lv_font_clock_180;
default: return &lv_font_clock_240; default: return &lv_font_clock_240;
#endif
} }
} }
@@ -3100,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(); } { 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 if (!g_brewLiveEnabled) brew_live_hide(); // laeuft gerade ein Bezug: sofort ausblenden
} }
static void build_info(lv_obj_t* p) { // Eigener Menuepunkt "Darstellung". Die Einstellungen sassen frueher oben auf der
section_title(p, "Darstellung"); // 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_t* dcard = th_card(p);
lv_obj_set_width(dcard, LV_PCT(100)); lv_obj_set_width(dcard, LV_PCT(100));
lv_obj_set_height(dcard, LV_SIZE_CONTENT); lv_obj_set_height(dcard, LV_SIZE_CONTENT);
@@ -3154,6 +3164,9 @@ static void build_info(lv_obj_t* p) {
sw_set(swBrewLive, g_brewLiveEnabled); sw_set(swBrewLive, g_brewLiveEnabled);
lv_obj_add_event_cb(swBrewLive, brew_live_sw_cb, LV_EVENT_VALUE_CHANGED, nullptr); 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"); section_title(p, "Firmware-Stände & Verbindung");
lv_obj_t* card = th_card(p); lv_obj_t* card = th_card(p);
@@ -3552,12 +3565,10 @@ static void refr_ready_cb(lv_event_t*) {
(int)g_refrArea[i].y1, (int)g_refrArea[i].y2); (int)g_refrArea[i].y1, (int)g_refrArea[i].y2);
} }
// Meldet, wenn ein ganzer Container ungueltig gemacht wird. Damit laesst sich unterscheiden, static void ui_heartbeat_cb(lv_timer_t*) {
// ob eine einzelne Beschriftung den Bildaufbau ausloest oder ob gleich der komplette lv_mem_monitor_t m; lv_mem_monitor(&m);
// Inhaltsbereich neu gezeichnet werden soll - zwei verschiedene Ursachen. esp_rom_printf("[UI] Herzschlag, LVGL-Speicher frei %u von %u Byte, groesster Block %u\n",
static void inval_watch_cb(lv_event_t* e) { (unsigned)m.free_size, (unsigned)m.total_size, (unsigned)m.free_biggest_size);
const char* name = (const char*)lv_event_get_user_data(e);
esp_rom_printf("[UI] %s wird komplett ungueltig\n", name);
} }
static void refr_monitor_install() { static void refr_monitor_install() {
@@ -3709,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_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_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_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]); g_pages[PG_INFO] = make_page(g_content); build_info(g_pages[PG_INFO]);
// --- Schublade (Scrim + Menue-Panel) --- // --- Schublade (Scrim + Menue-Panel) ---
@@ -3970,8 +3982,7 @@ void ui_init(ProtocolClient* client) {
if (g_client) g_client->sendListProfiles(); if (g_client) g_client->sendListProfiles();
refr_monitor_install(); refr_monitor_install();
if (g_content) lv_obj_add_event_cb(g_content, inval_watch_cb, LV_EVENT_INVALIDATE_AREA, (void*)"Inhaltsbereich"); lv_timer_create(ui_heartbeat_cb, 2000, nullptr);
if (g_pages[PG_DASH]) lv_obj_add_event_cb(g_pages[PG_DASH], inval_watch_cb, LV_EVENT_INVALIDATE_AREA, (void*)"Dashboard-Seite");
g_uiReady = true; g_uiReady = true;
} }
@@ -4161,8 +4172,14 @@ void ui_update(const MachineState& st) {
if (shouldShow) { if (shouldShow) {
label_set_if_changed(g_standbyClock, zeit.length() ? zeit.c_str() : "--:--"); label_set_if_changed(g_standbyClock, zeit.length() ? zeit.c_str() : "--:--");
if (!g_standbyShown) { 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 ui_fade_in(g_standbyScreen, UI_FADE_MS); // Einschlaf-Animation
g_standbyShown = true; 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 // Frueher wurde die Uhr hier bei JEDER Zustandsmeldung nach vorn geholt. Das
// ordnet die Ebenen neu und laesst LVGL den gesamten Bildschirm neu zeichnen - // ordnet die Ebenen neu und laesst LVGL den gesamten Bildschirm neu zeichnen -