diff --git a/Changelog.txt b/Changelog.txt index 7bd9a5e..913bcfa 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,15 @@ +Version 5.3.4: +- Cold Extraction weist jetzt deutlich darauf hin, wenn keine Waage aktiv/verbunden ist. Ohne Waage + greifen weder das Zielgewicht noch die Stillstands-Erkennung - der Bezug endet dann erst nach der + maximalen Bezugsdauer, die damit faktisch zur Dosierung wird. Bisher passierte das stillschweigend. +- Die Funktion bleibt ohne Waage bewusst nutzbar (Zeitsteuerung als Rückfallebene): Ein kurzzeitig + abgemeldeter HX711 soll die kalte Extraktion nicht komplett blockieren. +- Angezeigt wird das an vier Stellen: dauerhafter Warnhinweis auf /Brew-Control (dort ist das + Zielgewicht-Feld zusätzlich als „nur mit Waage wirksam" beschriftet), Hinweiszeile unter dem + Dashboard-Schalter, Statustext „Cold Extraction bereit (ohne Waage: Zeitsteuerung)" sowie eine + einmalige Meldung beim Aktivieren. +- Neuer Code im UART-Feld cxNotice: 4 = ohne Waage aktiviert (additiv, Protokoll bleibt v2). + Version 5.3.3: - Bug-Fix (Cold Extraction, hätte die Funktion mit Waage komplett unbrauchbar gemacht): Die Stillstands-Erkennung lief bereits während der Vorbenetzung mit. In dieser Phase sättigt der Puck diff --git a/Doku/JC-Display_UART-Protokoll.md b/Doku/JC-Display_UART-Protokoll.md index f2f4a06..27dea7b 100644 --- a/Doku/JC-Display_UART-Protokoll.md +++ b/Doku/JC-Display_UART-Protokoll.md @@ -396,7 +396,13 @@ Aus `getTouchUartStatus()`. Für sprach-unabhängige UI-Logik/Icons auf dem P4 v `4` Reinigungsassistent, `5` PID-Tuning, `6` Sensorfehler, `7` Wasserkessel zu warm. `cxNotice`: `0` keine, `1` Start abgewiesen (Grund steht in `cxBlock`), `2` Bezug wegen -fehlenden Zulaufs beendet, `3` Umschalten während eines Bezugs abgelehnt. +fehlenden Zulaufs beendet, `3` Umschalten während eines Bezugs abgelehnt, `4` ohne Waage +aktiviert (ab S3 5.3.4). + +**Ohne Waage** (`scaleEnabled`/`scaleConnected` false) läuft die kalte Extraktion rein +zeitgesteuert: `cxTarget` ist wirkungslos und die Stillstands-Erkennung ist inaktiv, es +bleibt `cxMaxSecs` als Ende. Das ist Absicht (ein kurz abgemeldeter HX711 soll die Funktion +nicht blockieren) und **kein** Sperrgrund — das Display sollte es aber dauerhaft anzeigen. Bewusst **Codes statt Klartext**: Die State-Zeile muss unter dem Zeilenlimit des P4 bleiben (`PROTO_RX_LINE_MAX`; sie liegt real bereits bei ~2,1 KB), und die Texte gehören ins Display — diff --git a/Dual-PID.ino b/Dual-PID.ino index 1aee607..058d94d 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.3.3"; +String version = "5.3.4"; String versionHersteller = "Thomas Müller"; String versionHerstellerMail = "thomas@mueller.black"; @@ -857,12 +857,13 @@ enum ColdExtractionBlock : uint8_t { COLDEX_BLOCK_TOO_WARM = 7 }; // Transiente Meldung (cxNotice): 0=keine, 1=Start abgewiesen, 2=Bezug wegen fehlenden -// Zulaufs beendet, 3=Umschalten waehrend eines Bezugs abgelehnt +// Zulaufs beendet, 3=Umschalten waehrend eines Bezugs abgelehnt, 4=ohne Waage aktiviert enum ColdExtractionNotice : uint8_t { COLDEX_NOTICE_NONE = 0, COLDEX_NOTICE_START_REJECTED = 1, COLDEX_NOTICE_NO_FLOW = 2, - COLDEX_NOTICE_BUSY = 3 + COLDEX_NOTICE_BUSY = 3, + COLDEX_NOTICE_NO_SCALE = 4 }; uint8_t coldExtractionNotice = COLDEX_NOTICE_NONE; @@ -4050,6 +4051,13 @@ bool coldExtractionTemperatureOk() { return (InputWasser < (double)limit); } +// Steht eine nutzbare Waage bereit? Ohne sie laeuft die kalte Extraktion rein zeitgesteuert: +// Zielgewicht und Stillstands-Erkennung greifen nicht, es bleibt nur die maximale Bezugsdauer. +// Bewusst kein Sperrgrund - ein kurz abgemeldeter HX711 soll die Funktion nicht blockieren. +bool coldExtractionScaleUsable() { + return scaleEnabled && scaleConnected; +} + // Allokationsfreie Freigabepruefung - wird in den JSON-Pushes (2x/s) verwendet. bool coldExtractionConditionsOk() { return coldExtractionEnabled && @@ -4124,6 +4132,15 @@ bool setColdExtractionMode(bool enable, String& message) { resetColdExtractionRuntime(); persistColdExtractionMode(); message = "Cold Extraction aktiviert - Wasserkreis heizt nicht (bleibt auch nach Standby/Neustart aktiv)."; + if (!coldExtractionScaleUsable()) { + // Ohne Waage faellt die Dosierung auf die maximale Bezugsdauer zurueck - das + // muss der Benutzer wissen, sonst wundert er sich ueber das Ergebnis. + message += " Achtung: keine Waage - Zielgewicht und Stillstands-Erkennung greifen nicht," + " der Bezug endet erst nach der maximalen Bezugsdauer."; + coldExtractionBlockMessage = "Keine Waage: Bezug endet nach Zeit, nicht nach Gewicht."; + coldExtractionBlockMessageUntilMs = millis() + COLDEX_BLOCK_MESSAGE_MS; + coldExtractionNotice = COLDEX_NOTICE_NO_SCALE; + } return true; } @@ -7158,7 +7175,7 @@ static const char brewControlCX_Fields[] PROGMEM = R"rawliteral( - + @@ -7316,12 +7333,21 @@ void handleBrewControl(AsyncWebServerRequest *request) { snprintf(buffer, sizeof(buffer), "%.1f", coldExtractionPumpRestSec); cxFieldsHtml.replace("{CX_PUMP_REST}", buffer); if (coldExtractionEnabled) { - String cxStatus = coldExtractionBlockReason(); - if (cxStatus.length() > 0) { - cxFieldsHtml.replace("{CX_STATUS_MSG}", "
" + cxStatus + ""); - } else { - cxFieldsHtml.replace("{CX_STATUS_MSG}", "
Kalte Extraktion derzeit möglich."); + String cxStatus; + // Waagen-Hinweis zuerst: er gilt unabhaengig von der Temperatur-Freigabe und + // erklaert, warum das Zielgewicht oben wirkungslos ist. + if (!coldExtractionScaleUsable()) { + cxStatus += "
Keine Waage aktiv/verbunden: Zielgewicht und " + "Stillstands-Erkennung greifen nicht. Der Bezug endet erst nach der maximalen " + "Bezugsdauer - diesen Wert dann als Dosierung verstehen."; } + String reason = coldExtractionBlockReason(); + if (reason.length() > 0) { + cxStatus += "
" + reason + ""; + } else { + cxStatus += "
Kalte Extraktion derzeit möglich."; + } + cxFieldsHtml.replace("{CX_STATUS_MSG}", cxStatus); } else { cxFieldsHtml.replace("{CX_STATUS_MSG}", ""); } @@ -14996,6 +15022,11 @@ static const char dashboardJavaScript[] PROGMEM = R"rawliteral( hintText = 'Cold Extraction derzeit nicht möglich.'; } } + // Waagen-Hinweis gilt zusätzlich und unabhängig von der Temperatur-Freigabe + if (data.cxEnabled === true && (data.scaleEnabled !== true || data.scaleConnected !== true)) { + if (hintText) { hintText += ' '; } + hintText += 'Ohne Waage: Bezug endet nach Zeit, nicht nach Gewicht.'; + } coldexHint.textContent = hintText; coldexHint.style.display = hintText ? '' : 'none'; } @@ -16041,7 +16072,9 @@ void buildDashboardJson(char *buffer, size_t bufferSize) { } statusKey = "brewing"; } else if (coldExtractionModeActive) { - statusText = "Cold Extraction bereit (Wasserkreis heizt nicht)"; + statusText = coldExtractionScaleUsable() + ? "Cold Extraction bereit (Wasserkreis heizt nicht)" + : "Cold Extraction bereit (ohne Waage: Zeitsteuerung)"; statusKey = "coldextraction"; } else if (ecoModeAktiv && ecoSwitchActive) { statusText = dynamicEcoActive ? "Eco+ Modus aktiv (Schalter)" : "Eco Modus aktiv (Schalter)"; @@ -16288,7 +16321,8 @@ static void getTouchUartStatus(String& statusText, String& statusKey) { } statusKey = "brewing"; } else if (coldExtractionModeActive) { - statusText = "Cold Extraction bereit"; + statusText = coldExtractionScaleUsable() ? "Cold Extraction bereit" + : "Cold Extraction bereit (Zeitsteuerung)"; statusKey = "coldextraction"; } else if (ecoModeAktiv) { statusText = dynamicEcoActive ? "Eco+ Modus aktiv" : "Eco Modus aktiv"; diff --git a/JC_Display_Firmware/Changelog.txt b/JC_Display_Firmware/Changelog.txt index 4509fbe..d7c4d93 100644 --- a/JC_Display_Firmware/Changelog.txt +++ b/JC_Display_Firmware/Changelog.txt @@ -1,3 +1,12 @@ +Version 1.1.2: +- Cold Extraction: Fehlt eine nutzbare Waage, weist das Display jetzt dauerhaft darauf hin. + Ohne Waage greifen weder Zielgewicht noch Stillstands-Erkennung, der Bezug endet erst nach der + maximalen Bezugsdauer (braucht S3-Firmware ab 5.3.4). + - Statuszeile: „Cold Extraction aktiv - ohne Waage, Ende nach Zeit" (gelb) statt des blauen + Normalhinweises; beim Aktivieren zusätzlich kurz die Meldung zum neuen cxNotice-Code 4. + - Erklärzeile unter dem Schalter und die Parameter-Anzeige auf der Brew-Seite ergänzen den + Hinweis; bei aktivem Modus ohne Waage nennt die Zeile die Zeit statt des Zielgewichts. + Version 1.1.1: - Compiler-Warnungen des P4-Builds aufgeräumt (rein technisch, keine Funktionsänderung): - „LV_FS_DEFAULT_DRIVE_LETTER is deprecated": Die Option heißt ab LVGL 9.3 diff --git a/JC_Display_Firmware/config.h b/JC_Display_Firmware/config.h index e1b5ffe..1ae7412 100644 --- a/JC_Display_Firmware/config.h +++ b/JC_Display_Firmware/config.h @@ -65,7 +65,7 @@ // ------------------------------------------------------------------------------------- // Firmware // ------------------------------------------------------------------------------------- -#define DISPLAY_FW_VERSION "1.1.1" // Firmware-Stand der P4-Display-Steuerung (Info-Seite) +#define DISPLAY_FW_VERSION "1.1.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 e80f410..7af9225 100644 --- a/JC_Display_Firmware/ui.cpp +++ b/JC_Display_Firmware/ui.cpp @@ -3446,6 +3446,9 @@ void ui_update(const MachineState& st) { // Cold-Extraction-Hinweis: erklaert dauerhaft, warum der Wasserkreis nicht heizt. // Transiente Meldung (abgewiesener Start / Abbruch) hat Vorrang vor dem Dauerzustand. + // Ohne nutzbare Waage laeuft die kalte Extraktion rein zeitgesteuert (Zielgewicht und + // Stillstands-Erkennung greifen nicht) - das wird dauerhaft mit angezeigt. + const bool cxScaleOk = st.scaleEnabled && st.scaleConnected; if (st.cxNotice == 2) { status_set(ST_COLDEX, true, LV_SYMBOL_WARNING " Cold Extraction beendet: kein Zulauf", COL_WARN, lv_color_hex(0x000000)); @@ -3456,6 +3459,9 @@ void ui_update(const MachineState& st) { String t = "Cold Extraction nicht möglich - "; t += coldex_block_text(st.cxBlock, st.cxMaxTemp, st.tempW); status_set(ST_COLDEX, true, t.c_str(), COL_WARN, lv_color_hex(0x000000)); + } else if (st.cxNotice == 4) { + status_set(ST_COLDEX, true, LV_SYMBOL_WARNING " Cold Extraction ohne Waage: Bezug endet nach Zeit", + COL_WARN, lv_color_hex(0x000000)); } else if (st.cxActive && st.cxResting) { status_set(ST_COLDEX, true, "Cold Extraction: Pumpenpause (Pumpenschutz)", COL_ACCENT, lv_color_hex(0x000000)); @@ -3463,6 +3469,11 @@ void ui_update(const MachineState& st) { String t = "Cold Extraction aktiv, Bezug gesperrt - "; t += coldex_block_text(st.cxBlock, st.cxMaxTemp, st.tempW); status_set(ST_COLDEX, true, t.c_str(), COL_WARN, lv_color_hex(0x000000)); + } else if (st.cxActive && !cxScaleOk) { + // Dauerhinweis, solange ohne Waage gefahren wird: das Zielgewicht greift nicht, + // der Bezug endet erst nach der maximalen Bezugsdauer. + status_set(ST_COLDEX, true, "Cold Extraction aktiv - ohne Waage, Ende nach Zeit", + COL_WARN, lv_color_hex(0x000000)); } else if (st.cxActive) { status_set(ST_COLDEX, true, "Cold Extraction aktiv - Wasserkreis heizt nicht", lv_color_hex(0x1976d2), COL_TEXT); @@ -3845,13 +3856,19 @@ void ui_update(const MachineState& st) { if (usable) lv_obj_remove_state(swColdEx, LV_STATE_DISABLED); else lv_obj_add_state(swColdEx, LV_STATE_DISABLED); if (lblColdExHint && st.cxEnabled) { + const bool scaleOk = st.scaleEnabled && st.scaleConnected; String hint; if (st.cxActive) { hint = "Wasserkreis heizt nicht (bleibt über Standby und Neustart aktiv). "; if (st.cxAllowed) { char b[96]; - snprintf(b, sizeof(b), "Bezug regulär starten - Ziel %.0f g, max %.0f s.", - (double)st.cxTarget, (double)st.cxMaxSecs); + if (scaleOk) { + snprintf(b, sizeof(b), "Bezug regulär starten - Ziel %.0f g, max %.0f s.", + (double)st.cxTarget, (double)st.cxMaxSecs); + } else { + snprintf(b, sizeof(b), "Bezug regulär starten - Ende nach %.0f s.", + (double)st.cxMaxSecs); + } hint += b; } else { hint += coldex_block_text(st.cxBlock, st.cxMaxTemp, st.tempW); @@ -3865,18 +3882,25 @@ void ui_update(const MachineState& st) { (double)st.tempW, (double)st.cxMaxTemp); hint = b; } + if (!scaleOk) { + hint += " Ohne Waage: Zielgewicht und Stillstands-Erkennung greifen nicht, " + "der Bezug endet erst nach der maximalen Bezugsdauer."; + } label_set_if_changed(lblColdExHint, hint.c_str()); } } if (lblBrewColdEx) { if (st.cxEnabled) { - char b[256]; // der Text ist ~205 Zeichen lang (das ° zaehlt als 2 Bytes) + char b[320]; // Grundtext ~205 Zeichen (das ° zaehlt als 2 Bytes) + Waagen-Hinweis snprintf(b, sizeof(b), "Cold Extraction (nur Anzeige, Einstellung in der Web-UI):\n" "Freigabe < %.1f °C | Vorbenetzung %.0f s | Ziel %.0f g | max. %.0f s\n" - "Geschaltet wird der Modus auf der Seite \"Temperaturen\" unter \"Modi\".", + "Geschaltet wird der Modus auf der Seite \"Temperaturen\" unter \"Modi\".%s", (double)st.cxMaxTemp, (double)st.cxPreInfSec, - (double)st.cxTarget, (double)st.cxMaxSecs); + (double)st.cxTarget, (double)st.cxMaxSecs, + (st.scaleEnabled && st.scaleConnected) + ? "" + : "\nOhne Waage ist das Zielgewicht wirkungslos - der Bezug endet nach der max. Dauer."); label_set_if_changed(lblBrewColdEx, b); lv_obj_remove_flag(lblBrewColdEx, LV_OBJ_FLAG_HIDDEN); } else {