fix: Zielgewicht bei Cold Extraction ueberall korrekt anzeigen (S3 5.4.1 / P4 1.2.1) #20

Merged
thomas merged 1 commits from fix/cold-extraction-zielgewicht-anzeige into main 2026-08-12 00:52:08 +02:00
5 changed files with 46 additions and 12 deletions
+10
View File
@@ -1,3 +1,13 @@
Version 5.4.1:
- Bug-Fix: Während einer kalten Extraktion stand als Zielgewicht überall noch der Brew-by-Weight-Wert
des normalen Espresso-Bezugs. Betroffen waren OLED, Web-Dashboard und Touch-Display, weil alle drei
direkt brewByWeightTargetGrams anzeigten - die kalte Extraktion ersetzt Brew-by-Weight aber und stoppt
nach coldExtractionTargetGrams. Neue Hilfsfunktion activeTargetWeightGrams() liefert jetzt das Ziel
des aktuellen bzw. nächsten Bezugs; das UART-Feld "targetWeight" führt diesen Wert (bbwTarget bleibt
unverändert die Roh-Einstellung).
- Auf dem OLED wird während eines kalten Bezugs jetzt überhaupt Gewicht angezeigt: Die Anzeige hing an
„Brew-by-Weight aktiv", das bei kalter Extraktion typischerweise aus ist - trotz vorhandenem Ziel.
Version 5.4.0: Version 5.4.0:
- Cold Extraction hat eine eigene Seite: /Cold-Extraction, eigener Eintrag im Menü. Auf /Brew-Control - Cold Extraction hat eine eigene Seite: /Cold-Extraction, eigener Eintrag im Menü. Auf /Brew-Control
steht nur noch ein Verweis darauf - die Seite war mit dem angehängten Block unübersichtlich geworden. steht nur noch ein Verweis darauf - die Seite war mit dem angehängten Block unübersichtlich geworden.
+15 -5
View File
@@ -369,7 +369,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
* Firmware-Informationen * Firmware-Informationen
************************************************************************************/ ************************************************************************************/
String version = "5.4.0"; String version = "5.4.1";
String versionHersteller = "Thomas Müller"; String versionHersteller = "Thomas Müller";
String versionHerstellerMail = String versionHerstellerMail =
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>"; "<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
@@ -4060,6 +4060,14 @@ bool coldExtractionTemperatureOk() {
return (InputWasser < (double)limit); return (InputWasser < (double)limit);
} }
// Zielgewicht des aktuellen bzw. naechsten Bezugs. Bei aktiver Cold Extraction ist das
// coldExtractionTargetGrams - brewByWeightTargetGrams gilt dann nicht, weil die kalte
// Extraktion Brew-by-Weight ersetzt. Alle Anzeigen (OLED, Web, Display) muessen diesen
// Wert nehmen, sonst steht dort das Espresso-Ziel waehrend eines kalten Bezugs.
float activeTargetWeightGrams() {
return coldExtractionModeActive ? coldExtractionTargetGrams : brewByWeightTargetGrams;
}
// Steht eine nutzbare Waage bereit? Ohne sie laeuft die kalte Extraktion rein zeitgesteuert: // 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. // Zielgewicht und Stillstands-Erkennung greifen nicht, es bleibt nur die maximale Bezugsdauer.
// Bewusst kein Sperrgrund - ein kurz abgemeldeter HX711 soll die Funktion nicht blockieren. // Bewusst kein Sperrgrund - ein kurz abgemeldeter HX711 soll die Funktion nicht blockieren.
@@ -6566,7 +6574,9 @@ if (scaleModeActive) {
// Wenn die Zustandsmaschine nicht aktiv ist, zeige den laufenden Shot oder die Temperaturen // Wenn die Zustandsmaschine nicht aktiv ist, zeige den laufenden Shot oder die Temperaturen
else if (shotActive) { else if (shotActive) {
// Priorität: Shot läuft *aktuell*? // Priorität: Shot läuft *aktuell*?
if (brewByWeightEnabled && scaleConnected) { // Auch ein kalter Bezug hat ein Gewichtsziel - unabhaengig von Brew-by-Weight,
// das die Cold Extraction ersetzt.
if ((brewByWeightEnabled || coldExtractionShotActive) && scaleConnected) {
display.setTextSize(1); display.setTextSize(1);
display.setCursor(0, 0); display.setCursor(0, 0);
display.println(F("Bezug aktiv:")); display.println(F("Bezug aktiv:"));
@@ -6583,7 +6593,7 @@ if (scaleModeActive) {
netWeight = 0.0; netWeight = 0.0;
} }
char weightBuffer[15]; char weightBuffer[15];
snprintf(weightBuffer, sizeof(weightBuffer), "%.1f/%.1fg", netWeight, brewByWeightTargetGrams); snprintf(weightBuffer, sizeof(weightBuffer), "%.1f/%.1fg", netWeight, activeTargetWeightGrams());
// --- ENDE ÄNDERUNG --- // --- ENDE ÄNDERUNG ---
display.setCursor(0, 30); display.setCursor(0, 30);
@@ -16325,7 +16335,7 @@ void buildDashboardJson(char *buffer, size_t bufferSize) {
// Plattformdaten // Plattformdaten
dtostrf(getWeightReadingForUi(), 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"weight\":%s,", floatBuf); dtostrf(getWeightReadingForUi(), 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"weight\":%s,", floatBuf);
dtostrf(brewByWeightTargetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"targetWeight\":%s,", floatBuf); dtostrf(activeTargetWeightGrams(), 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"targetWeight\":%s,", floatBuf); // bei Cold Extraction das kalte Ziel
offset += snprintf(buffer + offset, bufferSize - offset, "\"scaleEnabled\":%s,", scaleEnabled ? "true" : "false"); offset += snprintf(buffer + offset, bufferSize - offset, "\"scaleEnabled\":%s,", scaleEnabled ? "true" : "false");
offset += snprintf(buffer + offset, bufferSize - offset, "\"scaleType\":%u,", (unsigned int)scaleType); offset += snprintf(buffer + offset, bufferSize - offset, "\"scaleType\":%u,", (unsigned int)scaleType);
offset += snprintf(buffer + offset, bufferSize - offset, "\"scaleConnected\":%s,", scaleConnected ? "true" : "false"); offset += snprintf(buffer + offset, bufferSize - offset, "\"scaleConnected\":%s,", scaleConnected ? "true" : "false");
@@ -16610,7 +16620,7 @@ static void buildTouchUartStateJson(char *buffer, size_t bufferSize) {
} }
dtostrf(getWeightReadingForUi(), 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"weight\":%s,", floatBuf); dtostrf(getWeightReadingForUi(), 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"weight\":%s,", floatBuf);
dtostrf(brewByWeightTargetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"targetWeight\":%s,", floatBuf); dtostrf(activeTargetWeightGrams(), 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"targetWeight\":%s,", floatBuf); // bei Cold Extraction das kalte Ziel
// Endgewicht des letzten Bezugs inkl. Offset (identisch zur OLED: Netto beim Stopp + Offset). // Endgewicht des letzten Bezugs inkl. Offset (identisch zur OLED: Netto beim Stopp + Offset).
// Nur bei gewichtsbasiertem Bezug aussagekraeftig -> lastShotByWeight signalisiert das dem P4. // Nur bei gewichtsbasiertem Bezug aussagekraeftig -> lastShotByWeight signalisiert das dem P4.
dtostrf(lastShotFinalNetWeight + brewByWeightOffsetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"lastShotWeight\":%s,", floatBuf); dtostrf(lastShotFinalNetWeight + brewByWeightOffsetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"lastShotWeight\":%s,", floatBuf);
+7
View File
@@ -1,3 +1,10 @@
Version 1.2.1:
- Bug-Fix: Während einer kalten Extraktion zeigten Waage-Kachel und Live-Bezugsschirm das Zielgewicht
des normalen Espresso-Bezugs (bbwTarget) statt des Ziels der kalten Extraktion (cxTarget). Außerdem
erschienen Gewichtsanzeige und Fortschrittsbalken nur, wenn Brew-by-Weight aktiv war - bei kalter
Extraktion ist das typischerweise aus, obwohl ein Ziel existiert. Beide Anzeigen nutzen jetzt
cxTarget, sobald cxShot gesetzt ist.
Version 1.2.0: Version 1.2.0:
- Neue Seite „Cold Extraction" mit eigenem Menüeintrag (braucht S3-Firmware ab 5.4.0). Dort stehen jetzt - Neue Seite „Cold Extraction" mit eigenem Menüeintrag (braucht S3-Firmware ab 5.4.0). Dort stehen jetzt
alle Parameter der kalten Extraktion und lassen sich direkt am Display ändern: Freigabetemperatur, alle Parameter der kalten Extraktion und lassen sich direkt am Display ändern: Freigabetemperatur,
+1 -1
View File
@@ -65,7 +65,7 @@
// ------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------
// Firmware // Firmware
// ------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------
#define DISPLAY_FW_VERSION "1.2.0" // Firmware-Stand der P4-Display-Steuerung (Info-Seite) #define DISPLAY_FW_VERSION "1.2.1" // Firmware-Stand der P4-Display-Steuerung (Info-Seite)
// ------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------
// Protokoll // Protokoll
+13 -6
View File
@@ -1858,12 +1858,16 @@ static void brew_live_tick() {
} }
// Zielfortschritt: Brew-by-Weight (Gewicht) vor Brew-by-Time (Zeit), sonst versteckt // Zielfortschritt: Brew-by-Weight (Gewicht) vor Brew-by-Time (Zeit), sonst versteckt
if (g_bzBarRow && g_bzBar && g_bzBarLbl) { if (g_bzBarRow && g_bzBar && g_bzBarLbl) {
bool bbw = scale && g_state.bbwEnabled && g_state.bbwTarget > 0.0f; // Ein kalter Bezug hat sein eigenes Ziel (cxTarget) und ersetzt Brew-by-Weight -
// sonst stuende hier das Espresso-Ziel.
const bool cxShotNow = g_state.cxShot && g_state.cxTarget > 0.0f;
const float wTarget = cxShotNow ? g_state.cxTarget : g_state.bbwTarget;
bool bbw = scale && (cxShotNow || (g_state.bbwEnabled && g_state.bbwTarget > 0.0f));
if (bbw || (g_shotBbtMode && g_bbtTarget > 0.0f)) { if (bbw || (g_shotBbtMode && g_bbtTarget > 0.0f)) {
int pct; int pct;
if (bbw) { if (bbw) {
pct = (int)(w / g_state.bbwTarget * 100.0f + 0.5f); pct = (int)(w / wTarget * 100.0f + 0.5f);
snprintf(b, sizeof(b), "%.1f g / %.1f g", w, g_state.bbwTarget); snprintf(b, sizeof(b), "%.1f g / %.1f g", w, wTarget);
} else { } else {
pct = (int)(el / g_bbtTarget * 100.0f + 0.5f); pct = (int)(el / g_bbtTarget * 100.0f + 0.5f);
snprintf(b, sizeof(b), "%.1f s / %.1f s", el, g_bbtTarget); snprintf(b, sizeof(b), "%.1f s / %.1f s", el, g_bbtTarget);
@@ -3915,7 +3919,10 @@ void ui_update(const MachineState& st) {
else lv_obj_add_flag(cellBezug, LV_OBJ_FLAG_HIDDEN); else lv_obj_add_flag(cellBezug, LV_OBJ_FLAG_HIDDEN);
} }
if (lblBezug) { if (lblBezug) {
bool bbwShot = st.shotActive && st.bbwEnabled && st.bbwTarget > 0.0f; // Bei kaltem Bezug zaehlt cxTarget, nicht das Brew-by-Weight-Ziel
const bool cxShotNow = st.cxShot && st.cxTarget > 0.0f;
const float shotTarget = cxShotNow ? st.cxTarget : st.bbwTarget;
bool bbwShot = st.shotActive && (cxShotNow || (st.bbwEnabled && st.bbwTarget > 0.0f));
// Winzige negative Messwerte (z.B. -0.04 g) oder negatives Float-Null wuerden von // Winzige negative Messwerte (z.B. -0.04 g) oder negatives Float-Null wuerden von
// "%.1f" als "-0.0" dargestellt -> auf 0.0 normalisieren (kein fuehrendes Minus). // "%.1f" als "-0.0" dargestellt -> auf 0.0 normalisieren (kein fuehrendes Minus).
float wDisp = st.weight; float wDisp = st.weight;
@@ -3924,14 +3931,14 @@ void ui_update(const MachineState& st) {
lv_label_set_text(lblBezug, "--"); lv_label_set_text(lblBezug, "--");
} else if (bbwShot) { } else if (bbwShot) {
// Waehrend des Bezugs mit Brew-by-Weight: Ist oben, Ziel darunter (zweizeilig) // Waehrend des Bezugs mit Brew-by-Weight: Ist oben, Ziel darunter (zweizeilig)
lv_label_set_text_fmt(lblBezug, "%.1f g\n/ %.1f g", wDisp, st.bbwTarget); lv_label_set_text_fmt(lblBezug, "%.1f g\n/ %.1f g", wDisp, shotTarget);
} else { } else {
lv_label_set_text_fmt(lblBezug, "%.1f g", wDisp); lv_label_set_text_fmt(lblBezug, "%.1f g", wDisp);
} }
// Fortschrittsbalken Gewicht/Ziel (nur waehrend BBW-Bezug) // Fortschrittsbalken Gewicht/Ziel (nur waehrend BBW-Bezug)
if (g_bbwBar) { if (g_bbwBar) {
if (bbwShot) { if (bbwShot) {
int pct = (int)(st.weight / st.bbwTarget * 100.0f + 0.5f); int pct = (int)(st.weight / shotTarget * 100.0f + 0.5f);
if (pct < 0) pct = 0; if (pct > 100) pct = 100; if (pct < 0) pct = 0; if (pct > 100) pct = 100;
lv_bar_set_value(g_bbwBar, pct, LV_ANIM_OFF); lv_bar_set_value(g_bbwBar, pct, LV_ANIM_OFF);
lv_obj_remove_flag(g_bbwBar, LV_OBJ_FLAG_HIDDEN); lv_obj_remove_flag(g_bbwBar, LV_OBJ_FLAG_HIDDEN);