feat(P4): Cold Extraction am Touch-Display + RX-Zeilenlimit-Fix (1.1.0 / S3 5.3.2)
Display-Seite der kalten Extraktion: Schalter unter "Modi" auf der Temperatur-Seite mit Erklärzeile, eigener Slot in der Statuszeile direkt hinter der Sicherheitsmeldung, "Cold Extraction - Heizen aus" in der Wasser-Kachel statt eines unerreichbaren Sollwerts, unterdrückter Aufheiz-Countdown, "Vorbenetzung" als erste Phase des kalten Bezugs, Parameter-Anzeige auf der Brew-Seite und "(kalt)"-Markierung in der Statistik (kalte Bezüge zählen nicht in die mittlere Dauer). Bug-Fix P4: PROTO_RX_LINE_MAX lag bei 2048 Bytes, während die State-Zeile der S3 bereits rund 2,1 KB erreicht. Mit langem Status-, Profil- oder SSID-Text lag sie darüber — dann wurde die ganze Zeile verworfen und das Display fror auf dem letzten Stand ein, ohne dass die Verbindung als tot erkannt wurde. Limit jetzt 4096 Bytes; der Puffer bleibt bei 2 KB reserviert, weil der interne RAM knapp ist und eine Arduino-String nicht ins PSRAM alloziert werden kann. S3 5.3.2: Sperrgrund und transiente Meldung gehen als Codes (cxBlock/cxNotice) statt als Klartext ans Display, dazu cxPreInf für die Phasenleiste. Der bis zu 110 Byte lange Freitext hätte die State-Zeile über das Zeilenlimit gedrückt; die Texte rendert jetzt das Display, analog zur Trennung statusKey/statusText. Auf dem OLED und in den ack-Antworten bleibt der Klartext unverändert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
57b7969f8b
commit
e8edc8eb30
+58
-8
@@ -369,7 +369,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
|
||||
* Firmware-Informationen
|
||||
************************************************************************************/
|
||||
|
||||
String version = "5.3.1";
|
||||
String version = "5.3.2";
|
||||
String versionHersteller = "Thomas Müller";
|
||||
String versionHerstellerMail =
|
||||
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
|
||||
@@ -832,11 +832,36 @@ unsigned long coldExtractionPumpSampleMs = 0;
|
||||
float coldExtractionStallLastWeight = NAN;
|
||||
unsigned long coldExtractionStallSinceMs = 0;
|
||||
bool lastShotWasColdExtraction = false; // fuer Anzeige/Log des letzten Bezugs
|
||||
// Sperrmeldung fuer OLED und Touch-Display, wenn ein kalter Bezug abgewiesen wurde
|
||||
// Sperrmeldung fuer das OLED, wenn ein kalter Bezug abgewiesen bzw. abgebrochen wurde.
|
||||
// Der Klartext bleibt S3-intern; ans Touch-Display gehen nur die Codes unten, damit die
|
||||
// State-Zeile kurz bleibt (der P4 hat ein Zeilenlimit) und das Display seine eigenen
|
||||
// Texte rendern kann - dieselbe Trennung wie bei statusKey/statusText.
|
||||
const unsigned long COLDEX_BLOCK_MESSAGE_MS = 6000UL;
|
||||
String coldExtractionBlockMessage = "";
|
||||
unsigned long coldExtractionBlockMessageUntilMs = 0;
|
||||
|
||||
// Sperrgrund (cxBlock): 0=frei, 1=nicht freigeschaltet, 2=Standby, 3=Wartung,
|
||||
// 4=Reinigungsassistent, 5=PID-Tuning, 6=Sensorfehler, 7=Wasserkessel zu warm
|
||||
enum ColdExtractionBlock : uint8_t {
|
||||
COLDEX_BLOCK_NONE = 0,
|
||||
COLDEX_BLOCK_NOT_ENABLED = 1,
|
||||
COLDEX_BLOCK_STANDBY = 2,
|
||||
COLDEX_BLOCK_MAINTENANCE = 3,
|
||||
COLDEX_BLOCK_CLEANING = 4,
|
||||
COLDEX_BLOCK_TUNING = 5,
|
||||
COLDEX_BLOCK_SENSOR = 6,
|
||||
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
|
||||
enum ColdExtractionNotice : uint8_t {
|
||||
COLDEX_NOTICE_NONE = 0,
|
||||
COLDEX_NOTICE_START_REJECTED = 1,
|
||||
COLDEX_NOTICE_NO_FLOW = 2,
|
||||
COLDEX_NOTICE_BUSY = 3
|
||||
};
|
||||
uint8_t coldExtractionNotice = COLDEX_NOTICE_NONE;
|
||||
|
||||
/************************************************************************************
|
||||
* Standardwerte und Default-Einstellungen
|
||||
************************************************************************************/
|
||||
@@ -4032,6 +4057,19 @@ bool coldExtractionConditionsOk() {
|
||||
coldExtractionTemperatureOk();
|
||||
}
|
||||
|
||||
// Sperrgrund als Code fuer das Touch-Display (0 = frei). Gleiche Reihenfolge wie
|
||||
// coldExtractionBlockReason(), damit Text und Code immer dieselbe Ursache nennen.
|
||||
uint8_t coldExtractionBlockCode() {
|
||||
if (!coldExtractionEnabled) return COLDEX_BLOCK_NOT_ENABLED;
|
||||
if (standbyModeActive) return COLDEX_BLOCK_STANDBY;
|
||||
if (wartungsModusAktiv) return COLDEX_BLOCK_MAINTENANCE;
|
||||
if (cleaningAssistantActive) return COLDEX_BLOCK_CLEANING;
|
||||
if (autoTuneWasserActive || autoTuneDampfActive) return COLDEX_BLOCK_TUNING;
|
||||
if (wasserSensorError || wasserSafetyShutdown || !isfinite(InputWasser)) return COLDEX_BLOCK_SENSOR;
|
||||
if (!coldExtractionTemperatureOk()) return COLDEX_BLOCK_TOO_WARM;
|
||||
return COLDEX_BLOCK_NONE;
|
||||
}
|
||||
|
||||
// Klartext-Grund, warum gerade keine kalte Extraktion moeglich ist ("" = alles frei).
|
||||
String coldExtractionBlockReason() {
|
||||
if (!coldExtractionEnabled) {
|
||||
@@ -5253,6 +5291,7 @@ void loop() {
|
||||
// kalten Bezug wieder abkuehlen zu muessen. Nur die Laufzeitdaten werden verworfen.
|
||||
resetColdExtractionRuntime();
|
||||
coldExtractionBlockMessage = "";
|
||||
coldExtractionNotice = COLDEX_NOTICE_NONE;
|
||||
} else if (lastStandbyModeActive) {
|
||||
startupTime = currentMillis;
|
||||
ecoForcedActive = false;
|
||||
@@ -5484,6 +5523,7 @@ void loop() {
|
||||
stopForTime = true;
|
||||
coldExtractionBlockMessage = "Cold Extraction beendet: kein Zulauf (Puck dicht oder Pumpenschutz?).";
|
||||
coldExtractionBlockMessageUntilMs = millis() + COLDEX_BLOCK_MESSAGE_MS;
|
||||
coldExtractionNotice = COLDEX_NOTICE_NO_FLOW;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5985,6 +6025,7 @@ void updateDisplay() {
|
||||
if (coldExtractionBlockMessage.length() > 0) {
|
||||
if ((long)(millis() - coldExtractionBlockMessageUntilMs) >= 0) {
|
||||
coldExtractionBlockMessage = "";
|
||||
coldExtractionNotice = COLDEX_NOTICE_NONE;
|
||||
} else if (!standbyModeActive) {
|
||||
display.clearDisplay();
|
||||
display.setTextSize(1);
|
||||
@@ -15491,6 +15532,8 @@ bool executeDashboardAction(const String& action, const String& value,
|
||||
if (!success) {
|
||||
coldExtractionBlockMessage = message;
|
||||
coldExtractionBlockMessageUntilMs = millis() + COLDEX_BLOCK_MESSAGE_MS;
|
||||
coldExtractionNotice = (shotActive || shotSoftwareActive) ? COLDEX_NOTICE_BUSY
|
||||
: COLDEX_NOTICE_START_REJECTED;
|
||||
}
|
||||
} else if (action == "startShot") {
|
||||
if (standbyModeActive) {
|
||||
@@ -15509,6 +15552,7 @@ bool executeDashboardAction(const String& action, const String& value,
|
||||
if (message.length() == 0) { message = "Cold Extraction derzeit nicht moeglich."; }
|
||||
coldExtractionBlockMessageUntilMs = millis() + COLDEX_BLOCK_MESSAGE_MS;
|
||||
coldExtractionBlockMessage = message;
|
||||
coldExtractionNotice = COLDEX_NOTICE_START_REJECTED;
|
||||
beepShort();
|
||||
success = false;
|
||||
} else {
|
||||
@@ -16432,15 +16476,21 @@ static void buildTouchUartStateJson(char *buffer, size_t bufferSize) {
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxResting\":%s,", coldExtractionPumpResting ? "true" : "false");
|
||||
dtostrf(coldExtractionMaxTempC, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxMaxTemp\":%s,", floatBuf);
|
||||
dtostrf(coldExtractionTargetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxTarget\":%s,", floatBuf);
|
||||
dtostrf(coldExtractionPreInfusionSeconds, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxPreInf\":%s,", floatBuf);
|
||||
dtostrf(coldExtractionMaxSeconds, 5, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxMaxSecs\":%s,", floatBuf);
|
||||
// Sperrmeldung nach abgewiesenem Start (leer = keine); laeuft nach COLDEX_BLOCK_MESSAGE_MS ab.
|
||||
// Die Texte stammen ausschliesslich aus dieser Firmware und enthalten keine JSON-Sonderzeichen.
|
||||
// Sperrgrund + transiente Meldung als Codes (kein Freitext - die State-Zeile muss kurz
|
||||
// bleiben, der P4 begrenzt eingehende Zeilen; Texte rendert das Display selbst).
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxBlock\":%u,", (unsigned int)coldExtractionBlockCode());
|
||||
{
|
||||
const char* cxMsg = "";
|
||||
if (coldExtractionBlockMessage.length() > 0 && (long)(millis() - coldExtractionBlockMessageUntilMs) < 0) {
|
||||
cxMsg = coldExtractionBlockMessage.c_str();
|
||||
uint8_t notice = COLDEX_NOTICE_NONE;
|
||||
if (coldExtractionNotice != COLDEX_NOTICE_NONE) {
|
||||
if ((long)(millis() - coldExtractionBlockMessageUntilMs) < 0) {
|
||||
notice = coldExtractionNotice;
|
||||
} else {
|
||||
coldExtractionNotice = COLDEX_NOTICE_NONE;
|
||||
}
|
||||
}
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxMessage\":\"%s\",", cxMsg);
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxNotice\":%u,", (unsigned int)notice);
|
||||
}
|
||||
dtostrf(brewByWeightTargetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"bbwTarget\":%s,", floatBuf);
|
||||
dtostrf(brewByWeightOffsetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"bbwOffset\":%s,", floatBuf);
|
||||
|
||||
Reference in New Issue
Block a user