From 743c38e994c9744cbc69590a46d7701ef85e2e35 Mon Sep 17 00:00:00 2001 From: raw-designs Date: Wed, 19 Aug 2026 01:02:30 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Auswahl=20beim=20Aufwecken=20=C3=BCber?= =?UTF-8?q?=20Touch=20entfiel=20manchmal=20(S3=205.6.3=20/=20P4=201.4.2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Über den Schalter kam die Abfrage "Espresso oder Cold Extraction" zuverlässig, über Antippen am Display nur manchmal. Ursache waren zwei unterschiedliche Bedingungen für dieselbe Sache: - askMode (zeigt die Auswahl im Dialog) verlangte zusätzlich cxArmable - choiceRelevant (sendet "wakeEspresso" und schließt damit das Auswahlfenster der S3) verlangte es nicht In der Lücke dazwischen zeigte der Dialog nur "Aufwecken", unterdrückte aber trotzdem die Rückfrage der S3 - die Frage war auf beiden Seiten weg. Der Schalterweg hatte diese Lücke nie, weil er allein an der S3 hängt. P4: beide Stellen benutzen jetzt wake_choice_offered(). Bot der Dialog die Auswahl nicht an, sendet er "deactivateStandby"; dann öffnet die S3 ihr eigenes Auswahlfenster und die Frage kommt trotzdem. S3: Die Fensterentscheidung ist als Bedingungskette geschrieben und hält Quelle und Ergebnis des letzten Aufweckens fest. /Cold-Extraction zeigt das an - die bisherige Live-Liste zeigt nur den jetzigen Stand, der beim Nachsehen längst wieder ein anderer sein kann. Co-Authored-By: Claude Opus 5 --- Changelog.txt | 17 +++++ Dual-PID.ino | 101 ++++++++++++++++++++++++++++-- JC_Display_Firmware/Changelog.txt | 11 ++++ JC_Display_Firmware/config.h | 2 +- JC_Display_Firmware/ui.cpp | 22 +++++-- 5 files changed, 141 insertions(+), 12 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 9b4329f..c62e24c 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,20 @@ +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 + ein und dieselbe Sache: Das Display prüfte zum ANZEIGEN der Auswahl zusätzlich, ob der Modus gerade + scharfschaltbar ist (cxArmable) - zum UNTERDRÜCKEN der Rückfrage der S3 („wakeEspresso") aber nicht. + Fiel diese Bedingung weg, zeigte der Dialog nur noch „Aufwecken", schloss aber trotzdem das + Auswahlfenster der S3: Die Frage war auf beiden Seiten weg. Der Schalterweg hatte diese Lücke nie, + weil er allein an der S3 hängt. Behoben in der Display-Firmware 1.4.2. +- Diagnose: /Cold-Extraction zeigt jetzt zusätzlich zum Live-Stand der Voraussetzungen auch das + LETZTE Aufwecken - über welchen Weg (Schalter/Timer/Web oder Display, dort mit getroffener Wahl) + und mit welchem Ergebnis (Fenster geöffnet, am Display bereits entschieden, Abfrage aus, kein + Display verbunden, Wartung/Reinigung/Tuning aktiv, nicht freigeschaltet). Hintergrund: Die + Live-Liste zeigt nur den jetzigen Stand - blieb die Frage aus, hatte sich die Lage bis zum + Nachsehen längst wieder geändert, und der Grund war nicht mehr zu ermitteln. +- Die Entscheidung über das Auswahlfenster ist als Bedingungskette geschrieben, damit jeder Zweig + seinen Grund hinterlässt (vorher eine einzige verknüpfte Bedingung ohne Rückmeldung). + Version 5.6.2: - Bug-Fix: Die Abfrage beim Aufwecken erschien nicht, sobald der Cold-Extraction-Modus noch aktiv war - und da er persistent ist, war er das nach dem ersten kalten Bezug dauerhaft. Die Bedingung „Modus ist diff --git a/Dual-PID.ino b/Dual-PID.ino index 3b90f9b..41fa930 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.2"; +String version = "5.6.3"; String versionHersteller = "Thomas Müller"; String versionHerstellerMail = "thomas@mueller.black"; @@ -804,6 +804,28 @@ unsigned long coldExtractionWakeDecisionMs = 0; // Zeitpunkt der letzten ex const unsigned long COLDEX_WAKE_CHOICE_MS = 20000UL; // Bedenkzeit const unsigned long COLDEX_WAKE_DECISION_GRACE_MS = 5000UL; // frisch entschieden -> kein Fenster +// Diagnose des letzten Aufweckens: Bleibt die Abfrage aus, ist von aussen nicht erkennbar, +// welche der Voraussetzungen gefehlt hat - die Live-Anzeige auf /Cold-Extraction zeigt nur +// den JETZIGEN Stand, und der kann sich bis zum Nachsehen laengst wieder geaendert haben. +// Deshalb wird das Ergebnis beim Aufwecken festgehalten. Reine Anzeige, keine Logik. +#define COLDEX_WAKE_SRC_UNKNOWN 0 // Schalter, Timer, Web - alles ohne Display-Beteiligung +#define COLDEX_WAKE_SRC_TOUCH 1 // Display: "Aufwecken" (ohne Auswahl im Dialog) +#define COLDEX_WAKE_SRC_ESPRESSO 2 // Display: "Espresso" gewaehlt +#define COLDEX_WAKE_SRC_COLD 3 // Display: "Cold Extraction" gewaehlt +#define COLDEX_WAKE_RES_NONE 0 // seit dem Start noch nicht aufgeweckt +#define COLDEX_WAKE_RES_OPENED 1 // Auswahlfenster geoeffnet +#define COLDEX_WAKE_RES_DECIDED 2 // am Display bereits entschieden -> keine zweite Frage +#define COLDEX_WAKE_RES_ASK_OFF 3 // Abfrage in den Einstellungen ausgeschaltet +#define COLDEX_WAKE_RES_NO_DISP 4 // kein Touch-Display verbunden +#define COLDEX_WAKE_RES_NOT_ARM 5 // Wartung / Reinigung / PID-Tuning aktiv +#define COLDEX_WAKE_RES_DISABLED 6 // Funktion nicht freigeschaltet +uint8_t coldExtractionWakeSource = COLDEX_WAKE_SRC_UNKNOWN; // Quelle des laufenden Aufweckens +unsigned long coldExtractionWakeSourceMs = 0; // ... wann gemeldet (verfaellt) +const unsigned long COLDEX_WAKE_SRC_VALID_MS = 3000UL; // danach gilt sie als veraltet +uint8_t coldExtractionLastWakeSource = COLDEX_WAKE_SRC_UNKNOWN; // ... des letzten, fuer die Anzeige +uint8_t coldExtractionLastWakeResult = COLDEX_WAKE_RES_NONE; +unsigned long coldExtractionLastWakeMs = 0; + const bool defaultColdExtractionEnabled = false; const float defaultColdExtractionMaxTempC = 30.0f; const float defaultColdExtractionPreInfusionSeconds = 10.0f; @@ -5452,12 +5474,34 @@ void loop() { // die Abfrage muss in BEIDE Richtungen gehen - auch von "kalt" zurueck auf Espresso. // Sonst bliebe man nach dem ersten kalten Bezug dauerhaft im kalten Modus haengen, // ohne beim Aufwecken je wieder gefragt zu werden. - if (coldExtractionEnabled && coldExtractionAskOnWake && touchUartClientActive && - coldExtractionArmable() && - (coldExtractionWakeDecisionMs == 0 || - (currentMillis - coldExtractionWakeDecisionMs) > COLDEX_WAKE_DECISION_GRACE_MS)) { + // Als Kette geschrieben, damit jeder Zweig seinen Grund hinterlaesst (Anzeige auf + // /Cold-Extraction). Ohne das bleibt ein ausgebliebenes Fenster nicht nachvollziehbar. + // Eine gemeldete Quelle gilt nur kurz: Wird der Modus im Standby ueber die + // Einstellseite scharfgestellt, ohne dass ein Aufwecken folgt, darf sie nicht + // spaeter am naechsten Aufwecken kleben. + coldExtractionLastWakeSource = + (coldExtractionWakeSourceMs != 0 && + (currentMillis - coldExtractionWakeSourceMs) <= COLDEX_WAKE_SRC_VALID_MS) + ? coldExtractionWakeSource : COLDEX_WAKE_SRC_UNKNOWN; + coldExtractionWakeSource = COLDEX_WAKE_SRC_UNKNOWN; + coldExtractionWakeSourceMs = 0; + coldExtractionLastWakeMs = currentMillis; + if (!coldExtractionEnabled) { + coldExtractionLastWakeResult = COLDEX_WAKE_RES_DISABLED; + } else if (!coldExtractionAskOnWake) { + coldExtractionLastWakeResult = COLDEX_WAKE_RES_ASK_OFF; + } else if (!touchUartClientActive) { + coldExtractionLastWakeResult = COLDEX_WAKE_RES_NO_DISP; + } else if (!coldExtractionArmable()) { + coldExtractionLastWakeResult = COLDEX_WAKE_RES_NOT_ARM; + } else if (coldExtractionWakeDecisionMs != 0 && + (currentMillis - coldExtractionWakeDecisionMs) <= COLDEX_WAKE_DECISION_GRACE_MS) { + // Am Display wurde gerade gewaehlt - eine zweite Frage waere nur laestig. + coldExtractionLastWakeResult = COLDEX_WAKE_RES_DECIDED; + } else { coldExtractionWakeChoicePending = true; coldExtractionWakeChoiceUntilMs = currentMillis + COLDEX_WAKE_CHOICE_MS; + coldExtractionLastWakeResult = COLDEX_WAKE_RES_OPENED; } } // Gleiche Auswahl nach einem Kaltstart: Wer die Maschine am Netzschalter einschaltet, @@ -5469,6 +5513,9 @@ void loop() { coldExtractionBootChoiceDone = true; coldExtractionWakeChoicePending = true; coldExtractionWakeChoiceUntilMs = currentMillis + COLDEX_WAKE_CHOICE_MS; + coldExtractionLastWakeSource = COLDEX_WAKE_SRC_UNKNOWN; + coldExtractionLastWakeResult = COLDEX_WAKE_RES_OPENED; + coldExtractionLastWakeMs = currentMillis; } else if (!coldExtractionBootChoiceDone && touchUartClientActive && standbyModeActive) { // Start in den Standby hinein: Der Standby-Uebergang uebernimmt die Abfrage. coldExtractionBootChoiceDone = true; @@ -7868,6 +7915,33 @@ void handleColdExtraction(AsyncWebServerRequest *request) { long remainSec = (remainMs > 0) ? ((remainMs + 999) / 1000) : 0; diag += "Auswahlfenster ist gerade offen (noch " + String(remainSec) + " s).
"; } + // Letztes Aufwecken: Die Liste darueber zeigt nur den jetzigen Stand. Blieb die Frage + // aus, ist der Grund von damals hier festgehalten - sonst muesste man ihn raten. + if (coldExtractionLastWakeResult != COLDEX_WAKE_RES_NONE) { + String src; + switch (coldExtractionLastWakeSource) { + case COLDEX_WAKE_SRC_TOUCH: src = "Display (Aufwecken)"; break; + case COLDEX_WAKE_SRC_ESPRESSO: src = "Display (Espresso gewählt)"; break; + case COLDEX_WAKE_SRC_COLD: src = "Display (Cold Extraction gewählt)"; break; + default: src = "Schalter / Timer / Web"; break; + } + String res; + bool resOk = false; + switch (coldExtractionLastWakeResult) { + case COLDEX_WAKE_RES_OPENED: res = "Auswahlfenster geöffnet"; resOk = true; break; + case COLDEX_WAKE_RES_DECIDED: res = "am Display bereits entschieden"; resOk = true; break; + case COLDEX_WAKE_RES_ASK_OFF: res = "Abfrage ist ausgeschaltet"; break; + case COLDEX_WAKE_RES_NO_DISP: res = "kein Touch-Display verbunden"; break; + case COLDEX_WAKE_RES_NOT_ARM: res = "Wartung / Reinigung / PID-Tuning aktiv"; break; + case COLDEX_WAKE_RES_DISABLED: res = "Funktion nicht freigeschaltet"; break; + default: res = "unbekannt"; break; + } + unsigned long agoSec = (millis() - coldExtractionLastWakeMs) / 1000UL; + diag += "Letztes Aufwecken vor " + String(agoSec) + " s über " + src + + ": " + res + ".
"; + } diag += ""; cxFieldsHtml.replace("{CX_ASK_DIAG}", diag); } @@ -17442,6 +17516,23 @@ static bool touchUartHandleCommandLine(const String& line) { String message = ""; bool settingsChanged = false; bool pidNeedsUpdate = false; + // Herkunft des Aufweckens fuer die Diagnose festhalten - nur hier ist bekannt, dass + // die Aktion vom Touch-Display kommt und nicht aus der Web-UI. "Cold Extraction" + // schickt erst den Modus und dann das Aufwecken, deshalb darf das nachfolgende + // deactivateStandby die schon gesetzte Quelle nicht ueberschreiben. + if (standbyModeActive) { + if (action == "wakeEspresso") { + coldExtractionWakeSource = COLDEX_WAKE_SRC_ESPRESSO; + coldExtractionWakeSourceMs = millis(); + } else if (action == "startColdExtraction") { + coldExtractionWakeSource = COLDEX_WAKE_SRC_COLD; + coldExtractionWakeSourceMs = millis(); + } else if (action == "deactivateStandby" && + coldExtractionWakeSource == COLDEX_WAKE_SRC_UNKNOWN) { + coldExtractionWakeSource = COLDEX_WAKE_SRC_TOUCH; + coldExtractionWakeSourceMs = millis(); + } + } executeDashboardAction(action, value, success, message, settingsChanged, pidNeedsUpdate); touchUartSendAck(id, success, message); touchUartSendState(); diff --git a/JC_Display_Firmware/Changelog.txt b/JC_Display_Firmware/Changelog.txt index 1b764f6..c31f595 100644 --- a/JC_Display_Firmware/Changelog.txt +++ b/JC_Display_Firmware/Changelog.txt @@ -1,3 +1,14 @@ +Version 1.4.2: +- Bug-Fix: Beim Aufwecken über Antippen erschien die Auswahl „Espresso oder Cold Extraction" + manchmal nicht. Der Dialog entschied nach einer anderen Bedingung, ob er die Auswahl ANZEIGT + (verlangte zusätzlich cxArmable), als er beim Absenden benutzte, um die Rückfrage der S3 zu + UNTERDRÜCKEN. In der Lücke dazwischen zeigte er nur „Aufwecken", sendete aber „wakeEspresso" - + und damit war die Frage auch auf der S3 erledigt. Beide Stellen benutzen jetzt dieselbe Funktion + wake_choice_offered(). +- Hat der Dialog die Auswahl nicht angeboten, sendet er wieder „deactivateStandby". Dann öffnet die + S3 ihr eigenes Auswahlfenster, und die Frage kommt trotzdem - derselbe Weg wie beim Aufwecken über + den Schalter. Die Auswahl kann damit nicht mehr ersatzlos entfallen. + Version 1.4.1: - Passend zu S3 5.6.2: Die Auswahl beim Aufwecken erscheint auch dann, wenn der Cold-Extraction-Modus bereits aktiv ist - sie geht in beide Richtungen. „Espresso" schaltet den Modus dann ab. diff --git a/JC_Display_Firmware/config.h b/JC_Display_Firmware/config.h index 82a6110..4c62eb7 100644 --- a/JC_Display_Firmware/config.h +++ b/JC_Display_Firmware/config.h @@ -65,7 +65,7 @@ // ------------------------------------------------------------------------------------- // Firmware // ------------------------------------------------------------------------------------- -#define DISPLAY_FW_VERSION "1.4.1" // Firmware-Stand der P4-Display-Steuerung (Info-Seite) +#define DISPLAY_FW_VERSION "1.4.2" // Firmware-Stand der P4-Display-Steuerung (Info-Seite) // ------------------------------------------------------------------------------------- // Protokoll diff --git a/JC_Display_Firmware/ui.cpp b/JC_Display_Firmware/ui.cpp index 9861dd9..be43ad5 100644 --- a/JC_Display_Firmware/ui.cpp +++ b/JC_Display_Firmware/ui.cpp @@ -598,6 +598,16 @@ static void wake_confirm_show() { lv_obj_move_foreground(g_wakeConfirm); } 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 +// Extraction" wirklich anbietet. Wird an ZWEI Stellen gebraucht: beim Aufbau des Dialogs und +// beim Absenden. Standen dort verschiedene Bedingungen, konnte der Dialog nur "Aufwecken" +// zeigen, aber trotzdem "wakeEspresso" senden - und damit das Auswahlfenster der S3 +// schliessen. Dann war die Frage auf beiden Seiten weg (der Schalterweg hat diese Luecke +// nicht, weil er allein am S3 haengt). +static bool wake_choice_offered() { + return g_state.cxWakeChoice || + (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 wake_confirm_hide(); @@ -609,10 +619,11 @@ static void wake_yes_cb(lv_event_t*) { // Aufwecken -> hell lassen, S3 bee // g_wakeDialogOpen bleibt true, bis der Standby wirklich endet (ui_update) - sonst wuerde // das Display in der Uebergangsphase kurz auf Standby-Helligkeit abdunkeln. if (!g_client) return; - // "wakeEspresso" beendet den Standby UND schliesst das Auswahlfenster der S3. Ohne das - // wuerde nach dem Aufwecken noch einmal gefragt bzw. der Wasserkreis bliebe kurz aus. - const bool choiceRelevant = g_state.cxWakeChoice || (g_state.cxEnabled && g_state.cxAskOnWake); - g_client->sendAction(choiceRelevant ? "wakeEspresso" : "deactivateStandby"); + // "wakeEspresso" beendet den Standby UND schliesst das Auswahlfenster der S3 - das ist + // nur dann richtig, wenn dieser Dialog die Auswahl gerade wirklich angeboten hat. Sonst + // "deactivateStandby": dann oeffnet die S3 ihr eigenes Auswahlfenster, und die Frage + // kommt trotzdem (gleicher Weg wie beim Aufwecken ueber den Schalter). + g_client->sendAction(wake_choice_offered() ? "wakeEspresso" : "deactivateStandby"); showToast(g_state.standbyActive ? "Maschine wird aufgeweckt..." : "Espresso: Kessel heizt auf.", false); } // Aufwecken direkt in die kalte Extraktion. Reihenfolge ist wichtig: erst den Modus scharf @@ -3606,8 +3617,7 @@ void ui_update(const MachineState& st) { // Kein "!st.cxActive": Der Modus ist persistent, die Auswahl muss in beide // Richtungen gehen - sonst haengt man nach dem ersten kalten Bezug dauerhaft fest. const bool choiceOpen = st.cxWakeChoice; - const bool askMode = choiceOpen || - (st.cxEnabled && st.cxArmable && st.cxAskOnWake); + const bool askMode = wake_choice_offered(); // g_state == st (oben zugewiesen) if (g_wakeColdBtn) { if (askMode) lv_obj_remove_flag(g_wakeColdBtn, LV_OBJ_FLAG_HIDDEN); else lv_obj_add_flag(g_wakeColdBtn, LV_OBJ_FLAG_HIDDEN); -- 2.54.0