Compare commits

...
Author SHA1 Message Date
raw-designsandClaude Opus 5 01d960258a feat(P4): Uhr läuft lokal weiter, wenn die S3 keine Zeit liefert (1.8.6)
Die Uhrzeit kommt von der Hauptplatine und ist leer, solange dort keine
Zeitquelle synchronisiert ist. Bei einem erneuten Abgleich ist sie kurz weg,
und die Uhr verschwand für ein paar Sekunden - im Standby unruhig.

Der P4 merkt sich jetzt die zuletzt empfangene Zeit (Minuten seit
Mitternacht plus lv_tick zum Empfangszeitpunkt) und rechnet zwischen den
Meldungen selbst weiter. Sobald die S3 wieder eine Zeit schickt, gilt deren
Wert - der P4 stellt nichts richtig und ersetzt keine Zeitquelle, er
überbrückt nur die Lücke. Ohne je empfangene Zeit bleibt die Anzeige leer
wie bisher.

Gilt für die Kopfleisten-Uhr und die große Standby-Uhr; beide setzen den
Text jetzt über label_set_if_changed, sparen also unnötige Neuzeichnungen.

Beide Panel-Varianten mit arduino-cli gegengebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STPpZoK3ToDhrcniVqE6MJ
2026-09-03 00:33:50 +02:00
thomas 3c333f0280 Merge pull request 'fix(P4): Standby-Verzögerung, Dialog-Timeout, Kurvenhintergrund, Bindestrich (1.8.5)' (#65) from fix/standby-verzoegerung-und-feinschliff into main 2026-09-03 00:26:30 +02:00
raw-designsandClaude Opus 5 b6c6214b41 fix(P4): Standby-Verzögerung, Dialog-Timeout, Kurvenhintergrund, Bindestrich (1.8.5)
- Die ~2 s Verzögerung rund um den Standby lag nicht an der Helligkeit:
  lv_obj_move_foreground(g_standbyScreen) lief bei JEDER Zustandsmeldung.
  Das ordnet die Ebenen neu und lässt LVGL den ganzen Bildschirm neu
  zeichnen - einmal pro Sekunde, 1280x720 in 24 Bit. Nach vorn geholt wird
  die Uhr jetzt nur beim Einblenden.
- Der Aufweckdialog schließt sich nach 30 s ohne Auswahl selbst und wird wie
  Abbrechen gewertet (lv_timer, repeat_count 1). Sonst bliebe das Display
  hell stehen, weil der Dialog auf normale Helligkeit schaltet.
- Die Verlaufskurve hat keinen eigenen Hintergrund mehr. Die abgesetzte
  Fläche war nur nötig, solange die Kurve vor dem ersten Messwert leer war -
  seit sie beim aktuellen Wert startet, ist immer eine Linie da.
- Statistik: Bei fehlendem Gewicht stand ein En-Dash, den der Zeichensatz
  nicht enthält - erschien als leeres Rechteck. Jetzt ASCII-Bindestrich.

Beide Panel-Varianten mit arduino-cli gegengebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STPpZoK3ToDhrcniVqE6MJ
2026-09-03 00:26:12 +02:00
thomas 63290afa07 Merge pull request 'fix(P4): Übergänge ohne Animation, kein blaues Aufblitzen, Temperaturfarbe wählbar (1.8.4)' (#64) from fix/uebergaenge-und-temperaturfarbe into main 2026-09-03 00:04:43 +02:00
raw-designsandClaude Opus 5 cf57e11ccb fix(P4): Übergänge ohne Animation, kein blaues Aufblitzen, Temperaturfarbe wählbar (1.8.4)
- Standby-Uhr und Aufweckdialog kamen spürbar verzögert. Ursache waren die
  Fade-Animationen: Eine Deckkraft-Animation ist bei einem Bildspeicher in
  RGB888 das Teuerste überhaupt, weil pro Einzelbild die ganze Fläche unter
  der Ebene neu gezeichnet und gemischt werden muss. UI_FADE_MS ist für
  WS_PANEL_7H auf 0; ui_fade_in/ui_fade_out_hide zeigen bzw. verstecken dann
  direkt. Die JC-Panels behalten die Animation.
- Beim Wechsel der Schriftgröße blitzte Blau auf. Das ist das Panel selbst -
  ohne Videosignal zeigt es Blau, und der nötige Neustart reißt das Signal
  ab. hal_backlight(0) vor esp_restart().
- Neue Einstellung "Temperaturzahl in der Zustandsfarbe" (Info-Seite,
  Abschnitt Darstellung, NVS ui/tempcol, Vorgabe aus). Aus bleibt die Zahl
  weiß; die Zustandsfarbe steckt dann allein im Balken. Warnungen bleiben
  immer rot.

Beide Panel-Varianten mit arduino-cli gegengebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STPpZoK3ToDhrcniVqE6MJ
2026-09-03 00:03:58 +02:00
thomas 6d345d2145 Merge pull request 'fix(P4): Fehlendes °C, unsichtbarer Verlauf, fette Zahlen (1.8.3)' (#63) from fix/dashboard-schrift-und-verlauf into main 2026-09-02 20:12:52 +02:00
2 changed files with 177 additions and 14 deletions
+43
View File
@@ -1,3 +1,46 @@
Version 1.8.6:
- Die Uhrzeit verschwindet nicht mehr, wenn die Hauptplatine ihre Zeit gerade neu abgleicht.
Der P4 merkt sich die zuletzt empfangene Uhrzeit und zählt zwischen den Meldungen selbst
weiter; sobald die Hauptplatine wieder eine Zeit schickt, gilt deren Wert.
- Der P4 stellt dabei nichts richtig und ersetzt keine Zeitquelle - er überbrückt nur die
Lücke. Hat er noch nie eine Uhrzeit bekommen, bleibt die Anzeige wie bisher leer.
- Gilt für die Uhr in der Kopfleiste und für die große Standby-Uhr.
Version 1.8.5:
- Die Verzögerung von rund zwei Sekunden rund um den Standby ist behoben. Sie lag nicht an
der Helligkeitsumschaltung, sondern daran, dass die Standby-Uhr bei JEDER Zustandsmeldung
der Hauptplatine nach vorn geholt wurde. Das ordnet die Ebenen neu und lässt LVGL den
gesamten Bildschirm neu zeichnen - einmal pro Sekunde, jedes Mal 1280x720 in 24 Bit.
Dadurch hing die Oberfläche dauerhaft ein bis zwei Sekunden hinterher. Nach vorn geholt
wird die Uhr jetzt beim Einblenden, danach bleibt sie dort.
- Der Aufweckdialog schließt sich nach 30 Sekunden ohne Auswahl von selbst und wird wie
„Abbrechen" gewertet. Vorher blieb das Display hell stehen - der Dialog schaltet auf
normale Helligkeit, und ein versehentlicher Tipp auf die Uhr hätte die Maschine die ganze
Nacht leuchten lassen.
- Die Verlaufskurve hat keinen eigenen Hintergrund mehr; sie liegt jetzt direkt auf der
Karte. Die abgesetzte Fläche war nur nötig, solange die Kurve vor dem ersten Messwert
leer blieb - seit sie beim aktuellen Wert startet, ist immer eine Linie da.
- Auf der Statistikseite erschien ein leeres Rechteck, wenn ohne Waage bezogen wurde. Dort
stand ein typografischer Gedankenstrich, den der Zeichensatz der Schrift nicht enthält.
Jetzt ein einfacher Bindestrich.
Version 1.8.4:
- Standby-Uhr und Aufweckdialog erscheinen jetzt sofort statt mit spürbarer Verzögerung.
Ursache waren die weichen Ein- und Ausblendungen: Eine Deckkraft-Animation ist bei einem
Bildspeicher in RGB888 das Teuerste, was die Oberfläche tun kann, weil für jedes Einzelbild
die gesamte Fläche unter der Ebene neu gezeichnet und mit ihr gemischt werden muss. Auf
1280x720 dauerte eine solche Blende deutlich länger als die 450 ms, die vorgesehen waren.
Auf dem Waveshare-Panel laufen die Übergänge deshalb ohne Animation; die JC-Panels
behalten sie.
- Beim Wechsel der Schriftgröße blitzte kurz ein blaues Bild auf. Das kommt nicht von der
Firmware: Das Panel zeigt von sich aus Blau, sobald kein Videosignal anliegt - und beim
nötigen Neustart reißt das Signal kurz ab. Die Beleuchtung wird jetzt vorher abgeschaltet;
dunkel ist unauffälliger als blau.
- Neue Einstellung unter Darstellung: „Temperaturzahl in der Zustandsfarbe". Aus bleibt die
Zahl weiß, die Zustandsfarbe steckt dann allein im Balken darunter - ruhiger, und die Zahl
bleibt eine Zahl. Ein bedeutet, sie färbt sich mit, wie bisher. Vorgabe ist aus.
Warnungen bleiben in jedem Fall rot; das ist keine Gestaltung, sondern eine Meldung.
Version 1.8.3:
- Fehlerbehebung: Bei allen Darstellungen stand hinter der Temperatur ein leeres Rechteck
statt des C. Die beiden großen Schriften waren auf Ziffern, Komma, Punkt, Minus und
+134 -14
View File
@@ -22,6 +22,7 @@
#include "ui.h"
#include <Preferences.h>
#include "esp_system.h"
#include "display_hal.h"
// ------------------------------------------------------------------------------------
// Schriftgroesse
@@ -82,6 +83,10 @@ static void font_step_save_and_restart(uint8_t step) {
}
// Der Zaehler der Absturzsicherung darf diesen gewollten Neustart nicht mitzaehlen.
{ Preferences b; if (b.begin("bootguard", false)) { b.putUShort("early", 0); b.end(); } }
// Beleuchtung aus, bevor der Neustart die Bildausgabe abreissen laesst. Ohne Signal
// zeigt das Panel von sich aus ein blaues Bild - das blitzte beim Wechsel der
// Schriftgroesse kurz auf. Dunkel ist unauffaelliger als blau.
hal_backlight(0);
delay(120);
esp_restart();
}
@@ -168,6 +173,7 @@ static void label_set_if_changed(lv_obj_t* lbl, const char* txt); // Definitio
// TD_THERMO - vertikale Thermometer-Saeulen mit Kolben und Glanzstreifen
enum TempDesign { TD_GEWICHTET = 0, TD_VERLAUF, TD_FLAECHEN, TD_BAND, TD_DESIGN_COUNT };
static uint8_t g_tempDesign = TD_BAND;
static bool g_tempColored = false; // Temperaturzahl in der Zustandsfarbe statt weiss
#define DASH_SPARK_POINTS 60 // eine Minute Verlauf, ein Punkt je Sekunde
static lv_obj_t* g_sparkW = nullptr; // Verlaufskurven der Darstellung TD_VERLAUF
static lv_obj_t* g_sparkD = nullptr;
@@ -670,7 +676,9 @@ static void stat_list_set(int i, const char* dt, float d, float w, bool kalt) {
lv_label_set_text(g_statTime[i], dt);
lv_label_set_text_fmt(g_statDur[i], "%.1f s", d);
if (w >= 0.0f) lv_label_set_text_fmt(g_statWgt[i], "%.1f g", w);
else lv_label_set_text(g_statWgt[i], "");
// Bewusst ein einfacher Bindestrich: Der typografisch schoenere Gedankenstrich
// fehlt im Zeichensatz der Schrift und erschiene als leeres Rechteck.
else lv_label_set_text(g_statWgt[i], "-");
lv_label_set_text(g_statCold[i], kalt ? "kalt" : "");
}
@@ -772,7 +780,19 @@ static void err_icon_cb(lv_event_t*) { show_page(PG_DASH); } // Tipp auf Fehl
// Bewusst ueber das 'opa'-Style (faellt via opa_recursive auf die Kinder durch) und NICHT
// ueber 'opa_layered' - Letzteres wuerde einen Vollbild-Zwischenpuffer allozieren
// und koennte den LVGL-Speicherpool sprengen. So bleibt das Fade speicher-neutral.
// Dauer der weichen Uebergaenge (Standby-Uhr, Aufweckdialog, Bezugsuebersicht).
//
// Auf dem Waveshare-Panel: 0, also ohne Animation. Eine Deckkraft-Animation ist bei einem
// Bildspeicher in RGB888 das Teuerste, was die Oberflaeche tun kann - fuer jedes Einzelbild
// muss die gesamte Flaeche unter der Ebene neu gezeichnet und mit ihr gemischt werden. Auf
// 1280x720 dauert eine solche Blende deutlich laenger als die 450 ms, die sie dauern soll;
// spuerbar war das als Verzoegerung, bis die Standby-Uhr erschien und bis nach dem
// Fingertipp der Aufweckdialog kam. Ohne Animation erscheinen beide sofort.
#if JC_PANEL_TYPE == WS_PANEL_7H
static const uint32_t UI_FADE_MS = 0;
#else
static const uint32_t UI_FADE_MS = 450; // ruhige, unaufdringliche Dauer
#endif
static void ui_fade_exec_cb(void* obj, int32_t v) {
lv_obj_set_style_opa((lv_obj_t*)obj, (lv_opa_t)v, 0);
@@ -787,6 +807,12 @@ static void ui_fade_hide_done_cb(lv_anim_t* a) {
// Blendet 'obj' weich ein (sichtbar schalten, Opazitaet 0 -> voll).
static void ui_fade_in(lv_obj_t* obj, uint32_t ms) {
if (!obj) return;
if (ms == 0) { // ohne Animation: sofort sichtbar
lv_anim_delete(obj, ui_fade_exec_cb);
lv_obj_set_style_opa(obj, LV_OPA_COVER, 0);
lv_obj_remove_flag(obj, LV_OBJ_FLAG_HIDDEN);
return;
}
lv_anim_delete(obj, ui_fade_exec_cb); // evtl. laufende Ausblendung abbrechen
lv_obj_set_style_opa(obj, LV_OPA_TRANSP, 0);
lv_obj_remove_flag(obj, LV_OBJ_FLAG_HIDDEN);
@@ -802,6 +828,11 @@ static void ui_fade_in(lv_obj_t* obj, uint32_t ms) {
static void ui_fade_out_hide(lv_obj_t* obj, uint32_t ms) {
if (!obj || lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return;
lv_anim_delete(obj, ui_fade_exec_cb);
if (ms == 0) { // ohne Animation: sofort weg
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_style_opa(obj, LV_OPA_COVER, 0);
return;
}
lv_anim_t a; lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_exec_cb(&a, ui_fade_exec_cb);
@@ -835,6 +866,18 @@ static void ui_fade_out_delete(lv_obj_t* obj, uint32_t ms) {
static int standby_backlight() {
return g_state.standbyShowClock ? (int)g_state.backlightStandbyClock : 0;
}
// Der Aufweckdialog schliesst sich von selbst, wenn niemand entscheidet. Sonst bliebe das
// Display hell stehen, weil der Dialog auf normale Helligkeit schaltet - jemand tippt im
// Vorbeigehen versehentlich auf die Uhr, und die Maschine leuchtet die ganze Nacht.
// Zeitablauf wird wie "Abbrechen" gewertet: zurueck in die Standby-Helligkeit.
#define WAKE_DIALOG_TIMEOUT_MS 30000
static lv_timer_t* g_wakeTimeout = nullptr;
static void wake_timeout_stop() {
if (g_wakeTimeout) { lv_timer_delete(g_wakeTimeout); g_wakeTimeout = nullptr; }
}
static void wake_timeout_cb(lv_timer_t* t); // weiter unten, braucht wake_cancel()
static void wake_confirm_show() {
if (!g_wakeConfirm) return;
g_wakeDialogOpen = true;
@@ -844,6 +887,9 @@ static void wake_confirm_show() {
hal_backlight(bl);
lv_obj_remove_flag(g_wakeConfirm, LV_OBJ_FLAG_HIDDEN);
lv_obj_move_foreground(g_wakeConfirm);
wake_timeout_stop();
g_wakeTimeout = lv_timer_create(wake_timeout_cb, WAKE_DIALOG_TIMEOUT_MS, nullptr);
lv_timer_set_repeat_count(g_wakeTimeout, 1);
}
static void wake_confirm_hide() { if (g_wakeConfirm) lv_obj_add_flag(g_wakeConfirm, LV_OBJ_FLAG_HIDDEN); }
// Einzige Quelle der Wahrheit dafuer, ob der Aufweck-Dialog die Auswahl "Espresso oder Cold
@@ -857,12 +903,19 @@ static bool wake_choice_offered() {
(g_state.cxEnabled && g_state.cxArmable && g_state.cxAskOnWake);
}
static void standby_screen_cb(lv_event_t*) { wake_confirm_show(); } // Tipp auf Standby-Uhr -> nachfragen
static void wake_no_cb(lv_event_t*) { // Abbrechen -> zurueck in die Standby-Helligkeit
static void wake_cancel() { // Abbrechen -> zurueck in die Standby-Helligkeit
wake_timeout_stop();
wake_confirm_hide();
g_wakeDialogOpen = false;
if (g_state.standbyActive) hal_backlight(standby_backlight());
}
static void wake_no_cb(lv_event_t*) { wake_cancel(); }
static void wake_timeout_cb(lv_timer_t*) { // niemand hat entschieden -> wie Abbrechen
g_wakeTimeout = nullptr; // Timer laeuft nur einmal, loescht sich selbst
wake_cancel();
}
static void wake_yes_cb(lv_event_t*) { // Aufwecken -> hell lassen, S3 beendet den Standby
wake_timeout_stop();
wake_confirm_hide();
// g_wakeDialogOpen bleibt true, bis der Standby wirklich endet (ui_update) - sonst wuerde
// das Display in der Uebergangsphase kurz auf Standby-Helligkeit abdunkeln.
@@ -878,6 +931,7 @@ static void wake_yes_cb(lv_event_t*) { // Aufwecken -> hell lassen, S3 bee
// stellen, dann den Standby beenden - sonst heizt der Wasserkreis in der Zwischenzeit an,
// und genau das soll die Funktion ja verhindern.
static void wake_cold_cb(lv_event_t*) {
wake_timeout_stop();
wake_confirm_hide();
if (g_client) {
g_client->sendAction("startColdExtraction");
@@ -922,6 +976,18 @@ static void temp_progress_set(lv_obj_t* bar, lv_obj_t* arc, lv_obj_t* bulb, int
}
if (bulb) lv_obj_set_style_bg_color(bulb, col, 0);
}
// Farbe der grossen Temperaturzahl.
//
// Die Zustandsfarbe steckt ohnehin schon im Balken darunter. Ob die Zahl selbst mitgefaerbt
// wird, ist deshalb Geschmackssache: Eingefaerbt ist der Zustand auch aus dem Augenwinkel
// erkennbar, weiss wirkt ruhiger und laesst die Zahl als Zahl stehen.
//
// Warnungen bleiben immer rot - das ist keine Gestaltung, sondern eine Meldung.
static lv_color_t temp_value_color(lv_color_t zustand) {
return g_tempColored ? zustand : COL_TEXT;
}
// ------------------------------------------------------------------------------------
// Temperaturbereich des Dashboards - vier Darstellungen
//
@@ -1003,9 +1069,7 @@ static lv_obj_t* tempSpark(lv_obj_t* parent, lv_color_t col, lv_chart_series_t**
lv_chart_set_div_line_count(ch, 0, 0);
// Schwach abgesetzte Flaeche: Ohne sie waere der Bereich vor dem ersten Messwert
// vollstaendig unsichtbar - man haette gar nicht erkannt, dass dort eine Kurve gehoert.
lv_obj_set_style_bg_color(ch, lv_color_hex(0x151515), 0);
lv_obj_set_style_bg_opa(ch, LV_OPA_COVER, 0);
lv_obj_set_style_radius(ch, 6, 0);
lv_obj_set_style_bg_opa(ch, LV_OPA_TRANSP, 0);
lv_obj_set_style_border_width(ch, 0, 0);
lv_obj_set_style_pad_all(ch, 4, 0);
lv_obj_set_style_size(ch, 0, 0, LV_PART_INDICATOR); // keine Punktmarkierungen
@@ -2975,6 +3039,13 @@ static void design_btn_cb(lv_event_t* e) {
ui_update(g_state); // neu aufgebaute Anzeige sofort mit den aktuellen Werten fuellen
}
// Live-Bezugsschirm ein-/ausschalten (P4-lokal in NVS gespeichert)
// Temperaturzahl einfaerben oder weiss lassen. Wirkt sofort - die Farbe wird bei jeder
// Zustandsmeldung neu gesetzt, es muss nichts neu aufgebaut werden.
static void temp_color_sw_cb(lv_event_t* e) {
g_tempColored = sw_get((lv_obj_t*)lv_event_get_target(e));
{ Preferences prefs; prefs.begin("ui", false); prefs.putBool("tempcol", g_tempColored); prefs.end(); }
}
static void brew_live_sw_cb(lv_event_t* e) {
g_brewLiveEnabled = sw_get((lv_obj_t*)lv_event_get_target(e));
{ Preferences prefs; prefs.begin("ui", false); prefs.putBool("brewlive", g_brewLiveEnabled); prefs.end(); }
@@ -3012,6 +3083,10 @@ static void build_info(lv_obj_t* p) {
lv_obj_add_event_cb(fb, font_pick_cb, LV_EVENT_CLICKED, (void*)(intptr_t)i);
}
lv_obj_t* swTempCol = add_switch(dcard, "Temperaturzahl in der Zustandsfarbe");
sw_set(swTempCol, g_tempColored);
lv_obj_add_event_cb(swTempCol, temp_color_sw_cb, LV_EVENT_VALUE_CHANGED, nullptr);
swBrewLive = add_switch(dcard, "Live-Bezugsschirm (Vollbild während des Bezugs)");
sw_set(swBrewLive, g_brewLiveEnabled);
lv_obj_add_event_cb(swBrewLive, brew_live_sw_cb, LV_EVENT_VALUE_CHANGED, nullptr);
@@ -3354,6 +3429,7 @@ void ui_init(ProtocolClient* client) {
g_tempDesign = prefs.getUChar("tempdsgn", TD_BAND);
if (g_tempDesign >= TD_DESIGN_COUNT) g_tempDesign = TD_BAND;
g_brewLiveEnabled = prefs.getBool("brewlive", true);
g_tempColored = prefs.getBool("tempcol", false);
g_chartWin = prefs.getUChar("chartwin", 0);
if (g_chartWin > 3) g_chartWin = 0;
g_chartDuty = prefs.getBool("chartduty", false);
@@ -3819,6 +3895,44 @@ static void dash_brew_mode_tick(const MachineState& st) {
dash_brew_mode(aktiv || (war && (uint32_t)(jetzt - zuletzt) < DASH_BREW_HOLD_MS));
}
// ------------------------------------------------------------------------------------
// Uhr läuft lokal weiter
//
// Die Uhrzeit kommt von der Hauptplatine und ist leer, solange dort keine Zeitquelle
// synchronisiert ist. Bei einem erneuten Abgleich ist sie kurz weg - auf dem Display
// verschwand die Uhr dann fuer ein paar Sekunden, was gerade im Standby unruhig wirkt.
//
// Deshalb merkt sich der P4 die zuletzt empfangene Zeit und zaehlt zwischen den Meldungen
// selbst weiter. Er stellt nichts richtig: Sobald die Hauptplatine wieder eine Zeit
// schickt, gilt deren Wert. Die lokale Uhr ueberbrueckt nur die Luecke.
// ------------------------------------------------------------------------------------
static bool g_clockKnown = false; // schon einmal eine Zeit empfangen?
static uint32_t g_clockMinute = 0; // zuletzt bekannte Uhrzeit in Minuten seit Mitternacht
static uint32_t g_clockTick = 0; // Zeitpunkt (lv_tick), zu dem sie galt
// "HH:MM" der Hauptplatine uebernehmen. Andere Formate werden nicht angetastet.
static void clock_note(const String& hhmm) {
if (hhmm.length() < 5 || hhmm.charAt(2) != ':') return;
int h = hhmm.substring(0, 2).toInt();
int m = hhmm.substring(3, 5).toInt();
if (h < 0 || h > 23 || m < 0 || m > 59) return;
g_clockMinute = (uint32_t)(h * 60 + m);
g_clockTick = lv_tick_get();
g_clockKnown = true;
}
// Anzuzeigende Uhrzeit: die der Hauptplatine, solange sie eine schickt - sonst die lokal
// weitergezaehlte. Leerer Rueckgabewert heisst: noch nie eine Zeit gehabt, nichts anzeigen.
static String clock_text(const String& vonS3) {
if (vonS3.length()) return vonS3;
if (!g_clockKnown) return String();
uint32_t min = g_clockMinute + (uint32_t)((lv_tick_get() - g_clockTick) / 60000UL);
char b[6];
snprintf(b, sizeof(b), "%02u:%02u", (unsigned)((min / 60) % 24), (unsigned)(min % 60));
return String(b);
}
void ui_update(const MachineState& st) {
if (!g_uiReady) return;
@@ -3835,10 +3949,13 @@ void ui_update(const MachineState& st) {
bool wifiOk = st.wifiConnected && !st.apMode;
if (g_wifiIcon) lv_obj_set_style_text_color(g_wifiIcon, wifiOk ? COL_OK : (st.apMode ? COL_WARN : COL_TEXT_MUT), 0);
// Uhrzeit (nur wenn NTP synchronisiert)
// Uhrzeit. Angezeigt wird die der Hauptplatine; faellt sie voruebergehend aus, zaehlt
// der P4 die zuletzt bekannte Zeit selbst weiter (siehe clock_text).
clock_note(st.timeStr);
String zeit = clock_text(st.timeStr);
if (g_clock) {
if (st.timeStr.length()) {
lv_label_set_text(g_clock, st.timeStr.c_str());
if (zeit.length()) {
label_set_if_changed(g_clock, zeit.c_str());
lv_obj_remove_flag(g_clock, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(g_clock, LV_OBJ_FLAG_HIDDEN);
@@ -3851,13 +3968,16 @@ void ui_update(const MachineState& st) {
if (g_standbyScreen) {
bool shouldShow = st.standbyActive && st.standbyShowClock;
if (shouldShow) {
lv_label_set_text(g_standbyClock, st.timeStr.length() ? st.timeStr.c_str() : "--:--");
label_set_if_changed(g_standbyClock, zeit.length() ? zeit.c_str() : "--:--");
if (!g_standbyShown) {
ui_fade_in(g_standbyScreen, UI_FADE_MS); // Einschlaf-Animation
g_standbyShown = true;
} else {
lv_obj_move_foreground(g_standbyScreen);
}
// Frueher wurde die Uhr hier bei JEDER Zustandsmeldung nach vorn geholt. Das
// ordnet die Ebenen neu und laesst LVGL den gesamten Bildschirm neu zeichnen -
// einmal pro Sekunde, jedesmal 1280x720 in 24 Bit. Genau das war der Grund,
// warum sich rund um den Standby alles um ein bis zwei Sekunden verzoegerte.
// Nach vorn geholt wird die Uhr beim Einblenden, danach bleibt sie dort.
} else if (g_standbyShown) {
ui_fade_out_hide(g_standbyScreen, UI_FADE_MS); // Aufweck-Animation
g_standbyShown = false;
@@ -4128,14 +4248,14 @@ void ui_update(const MachineState& st) {
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, lv_color_hex(0x1976d2), 0);
lv_obj_set_style_text_color(lblTempW, temp_value_color(lv_color_hex(0x1976d2)), 0);
label_set_if_changed(lblSetW, "Cold Extraction - Heizen aus");
lv_obj_set_style_text_color(lblSetW, lv_color_hex(0x1976d2), 0);
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, colW, 0);
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);
// Fortschritt = Ist/Soll in %, gedeckelt auf 100 (Ist >= Soll -> voll)
@@ -4152,7 +4272,7 @@ void ui_update(const MachineState& st) {
} else {
lv_label_set_text(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, colD, 0);
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);
int pctD = (st.setD > 0.0f) ? (int)(st.tempD / st.setD * 100.0f + 0.5f) : 0;