feat: Cold Extraction warnt bei fehlender Waage (S3 5.3.4 / P4 1.1.2)
Ohne aktive und verbundene 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 diese Degradierung stillschweigend. Die Funktion bleibt ohne Waage bewusst nutzbar (Zeitsteuerung als Rückfallebene), damit ein kurzzeitig abgemeldeter HX711 die kalte Extraktion nicht komplett blockiert. Angezeigt wird der Zustand jetzt an vier Stellen: dauerhafter Warnhinweis auf /Brew-Control (Zielgewicht-Feld zusätzlich als "nur mit Waage wirksam" beschriftet), Hinweiszeile unter dem Dashboard-Schalter, Statustext "Cold Extraction bereit (ohne Waage: Zeitsteuerung)" und eine einmalige Meldung beim Aktivieren. P4: Statuszeile zeigt "ohne Waage, Ende nach Zeit" in Warnfarbe statt des blauen Normalhinweises; Erklärzeile am Schalter und Parameter-Anzeige auf der Brew-Seite ergänzen den Hinweis und nennen bei aktivem Modus ohne Waage die Zeit statt des Zielgewichts. Neuer cxNotice-Code 4 (additiv, Protokoll bleibt v2). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4cc9ceea7c
commit
b6155acafe
@@ -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
|
||||
|
||||
@@ -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 —
|
||||
|
||||
+45
-11
@@ -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 =
|
||||
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
|
||||
@@ -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(
|
||||
<input type='number' min='200' max='5000' step='50' id='cxPulseMs' name='cxPulseMs' value='{CX_PULSE_MS}' required>
|
||||
<label for='cxDuty'>Pumpenleistung (%):</label>
|
||||
<input type='number' min='5.0' max='100.0' step='1.0' id='cxDuty' name='cxDuty' value='{CX_DUTY}' required>
|
||||
<label for='cxTarget'>Zielgewicht (Gramm):</label>
|
||||
<label for='cxTarget'>Zielgewicht (Gramm, nur mit Waage wirksam):</label>
|
||||
<input type='number' min='10.0' max='300.0' step='1.0' id='cxTarget' name='cxTarget' value='{CX_TARGET}' required>
|
||||
<label for='cxMaxSecs'>Maximale Bezugsdauer (Sekunden):</label>
|
||||
<input type='number' min='30.0' max='900.0' step='5.0' id='cxMaxSecs' name='cxMaxSecs' value='{CX_MAX_SECS}' required>
|
||||
@@ -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}", "<br><b><small style='color: #FFCC00;'>" + cxStatus + "</small></b>");
|
||||
} else {
|
||||
cxFieldsHtml.replace("{CX_STATUS_MSG}", "<br><b><small style='color: #66CC66;'>Kalte Extraktion derzeit möglich.</small></b>");
|
||||
String cxStatus;
|
||||
// Waagen-Hinweis zuerst: er gilt unabhaengig von der Temperatur-Freigabe und
|
||||
// erklaert, warum das Zielgewicht oben wirkungslos ist.
|
||||
if (!coldExtractionScaleUsable()) {
|
||||
cxStatus += "<br><b><small style='color: #FFCC00;'>Keine Waage aktiv/verbunden: Zielgewicht und "
|
||||
"Stillstands-Erkennung greifen nicht. Der Bezug endet erst nach der maximalen "
|
||||
"Bezugsdauer - diesen Wert dann als Dosierung verstehen.</small></b>";
|
||||
}
|
||||
String reason = coldExtractionBlockReason();
|
||||
if (reason.length() > 0) {
|
||||
cxStatus += "<br><b><small style='color: #FFCC00;'>" + reason + "</small></b>";
|
||||
} else {
|
||||
cxStatus += "<br><b><small style='color: #66CC66;'>Kalte Extraktion derzeit möglich.</small></b>";
|
||||
}
|
||||
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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user