fix(S3): Cold Extraction benetzt sanft statt mit voller Leistung (5.3.6) #18
@@ -1,3 +1,17 @@
|
|||||||
|
Version 5.3.6:
|
||||||
|
- Cold Extraction: Die Vorbenetzung läuft nicht mehr mit voller Pumpenleistung, sondern gepulst mit
|
||||||
|
eigener, niedriger Leistung (neue Einstellung „Pumpenleistung Vorbenetzung", Standard 10 %).
|
||||||
|
Messung an der Maschine: Nach 7 s Vorbenetzung kommen heiß 1-2 Tropfen, kalt bei voller Leistung
|
||||||
|
rund 10 g dünne Brühe ohne Crema - etwa zehnfacher Durchfluss trotz dreifacher Viskosität.
|
||||||
|
Ursache ist nicht die Viskosität, sondern das Kaffeebett: Kaltes Mehl quillt nicht, ist schlecht
|
||||||
|
benetzbar, es wandert kein Feinanteil und es lösen sich kaum Feststoffe, die das Bett verdichten.
|
||||||
|
Volle Leistung auf den trockenen kalten Puck legt deshalb sofort Kanäle an, die sich - anders als
|
||||||
|
bei heißem Bezug - nie wieder schließen; der restliche Bezug läuft daran vorbei statt zu extrahieren.
|
||||||
|
- Standard der Pumpenleistung im Hauptbezug von 30 % auf 20 % gesenkt (gleicher Grund: das kalte Bett
|
||||||
|
ist erheblich durchlässiger als ein heißes). Bestehende Installationen behalten ihren Wert.
|
||||||
|
- Der Erklärtext auf /Brew-Control benennt den Zusammenhang jetzt und weist darauf hin, dass die
|
||||||
|
Vorbenetzung deutlich schwächer eingestellt sein sollte als der Hauptbezug.
|
||||||
|
|
||||||
Version 5.3.5:
|
Version 5.3.5:
|
||||||
- Cold Extraction lässt sich jetzt auch im Standby und bei noch warmem Kessel scharf schalten.
|
- Cold Extraction lässt sich jetzt auch im Standby und bei noch warmem Kessel scharf schalten.
|
||||||
Bisher galt „Standby aktiv" und „Kessel zu warm" auch fürs Aktivieren - das war falsch herum
|
Bisher galt „Standby aktiv" und „Kessel zu warm" auch fürs Aktivieren - das war falsch herum
|
||||||
|
|||||||
+51
-17
@@ -369,7 +369,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
|
|||||||
* Firmware-Informationen
|
* Firmware-Informationen
|
||||||
************************************************************************************/
|
************************************************************************************/
|
||||||
|
|
||||||
String version = "5.3.5";
|
String version = "5.3.6";
|
||||||
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>";
|
||||||
@@ -782,9 +782,10 @@ bool flowGuardPumpPulseActive = false;
|
|||||||
************************************************************************************/
|
************************************************************************************/
|
||||||
bool coldExtractionEnabled = false; // Funktion in der Web-UI freigeschaltet
|
bool coldExtractionEnabled = false; // Funktion in der Web-UI freigeschaltet
|
||||||
float coldExtractionMaxTempC = 30.0f; // Freigabeschwelle Wasserkessel
|
float coldExtractionMaxTempC = 30.0f; // Freigabeschwelle Wasserkessel
|
||||||
float coldExtractionPreInfusionSeconds = 10.0f; // Vorbenetzung (Pumpe laeuft ungepulst)
|
float coldExtractionPreInfusionSeconds = 10.0f; // Vorbenetzung (gepulst, eigene Leistung)
|
||||||
|
float coldExtractionPreInfDutyPercent = 10.0f; // Pumpenleistung waehrend der Vorbenetzung
|
||||||
uint16_t coldExtractionPulsePeriodMs = 2000; // Pulsperiode der Pumpe im Hauptbezug
|
uint16_t coldExtractionPulsePeriodMs = 2000; // Pulsperiode der Pumpe im Hauptbezug
|
||||||
float coldExtractionDutyPercent = 30.0f; // Pumpen-Duty im Hauptbezug
|
float coldExtractionDutyPercent = 20.0f; // Pumpen-Duty im Hauptbezug
|
||||||
float coldExtractionTargetGrams = 60.0f; // Zielgewicht (ca. 1:3 auf 18 g)
|
float coldExtractionTargetGrams = 60.0f; // Zielgewicht (ca. 1:3 auf 18 g)
|
||||||
float coldExtractionMaxSeconds = 300.0f; // Sicherheits-Timeout des Bezugs
|
float coldExtractionMaxSeconds = 300.0f; // Sicherheits-Timeout des Bezugs
|
||||||
float coldExtractionPumpMaxRunSec = 60.0f; // kumulierte Pumpenlaufzeit bis zur Zwangspause
|
float coldExtractionPumpMaxRunSec = 60.0f; // kumulierte Pumpenlaufzeit bis zur Zwangspause
|
||||||
@@ -792,13 +793,17 @@ float coldExtractionPumpRestSec = 20.0f; // Dauer der Zwangspause
|
|||||||
|
|
||||||
const bool defaultColdExtractionEnabled = false;
|
const bool defaultColdExtractionEnabled = false;
|
||||||
const float defaultColdExtractionMaxTempC = 30.0f;
|
const float defaultColdExtractionMaxTempC = 30.0f;
|
||||||
// 10 s statt der urspruenglichen 30 s: Die Pumpe laeuft in dieser Phase ungepulst auf
|
|
||||||
// voller Leistung. Heiss kommt der erste Tropfen nach ~7 s, kalt (rund dreifache
|
|
||||||
// Viskositaet) entsprechend spaeter - 30 s waeren laengst voller Bezug bei vollem Druck
|
|
||||||
// gewesen, und die Haelfte des Pumpen-Laufzeitbudgets waere vor dem Hauptbezug verbraucht.
|
|
||||||
const float defaultColdExtractionPreInfusionSeconds = 10.0f;
|
const float defaultColdExtractionPreInfusionSeconds = 10.0f;
|
||||||
|
// Die Vorbenetzung laeuft bewusst SEHR sanft. Messung an der Maschine: heiss kommen nach
|
||||||
|
// 7 s Vorbenetzung 1-2 Tropfen, kalt bei voller Pumpenleistung rund 10 g duenne Bruehe -
|
||||||
|
// etwa zehnfacher Durchfluss trotz dreifacher Viskositaet. Kaltes Mehl quillt nicht und ist
|
||||||
|
// schlecht benetzbar; volle Leistung auf den trockenen Puck legt sofort Kanaele an, die sich
|
||||||
|
// - anders als heiss - nie wieder zudruecken. Der restliche Bezug laeuft dann daran vorbei.
|
||||||
|
const float defaultColdExtractionPreInfDutyPercent = 10.0f;
|
||||||
const uint16_t defaultColdExtractionPulsePeriodMs = 2000;
|
const uint16_t defaultColdExtractionPulsePeriodMs = 2000;
|
||||||
const float defaultColdExtractionDutyPercent = 30.0f;
|
// 20 % statt 30 %: Das kalte Kaffeebett ist deutlich durchlaessiger als ein heisses
|
||||||
|
// (kein Quellen, keine Feinanteil-Wanderung, keine geloesten Feststoffe, die es verdichten).
|
||||||
|
const float defaultColdExtractionDutyPercent = 20.0f;
|
||||||
const float defaultColdExtractionTargetGrams = 60.0f;
|
const float defaultColdExtractionTargetGrams = 60.0f;
|
||||||
const float defaultColdExtractionMaxSeconds = 300.0f;
|
const float defaultColdExtractionMaxSeconds = 300.0f;
|
||||||
const float defaultColdExtractionPumpMaxRunSec = 60.0f;
|
const float defaultColdExtractionPumpMaxRunSec = 60.0f;
|
||||||
@@ -1067,7 +1072,8 @@ const int EEPROM_ADDR_COLDEX_MAX_SECONDS = 965; // 4 Bytes (float) -> Ende
|
|||||||
const int EEPROM_ADDR_COLDEX_PUMP_MAX_RUN = 969; // 4 Bytes (float) -> Ende 972
|
const int EEPROM_ADDR_COLDEX_PUMP_MAX_RUN = 969; // 4 Bytes (float) -> Ende 972
|
||||||
const int EEPROM_ADDR_COLDEX_PUMP_REST = 973; // 4 Bytes (float) -> Ende 976
|
const int EEPROM_ADDR_COLDEX_PUMP_REST = 973; // 4 Bytes (float) -> Ende 976
|
||||||
const int EEPROM_ADDR_COLDEX_MODE_ACTIVE = 977; // 1 Byte (uint8_t) -> Ende 977
|
const int EEPROM_ADDR_COLDEX_MODE_ACTIVE = 977; // 1 Byte (uint8_t) -> Ende 977
|
||||||
// Naechste freie Adresse: 978 (Innerhalb EEPROM_SIZE=1024)
|
const int EEPROM_ADDR_COLDEX_PREINF_DUTY = 978; // 4 Bytes (float) -> Ende 981
|
||||||
|
// Naechste freie Adresse: 982 (Innerhalb EEPROM_SIZE=1024)
|
||||||
|
|
||||||
/************************************************************************************
|
/************************************************************************************
|
||||||
* Eco-Mode Variablen
|
* Eco-Mode Variablen
|
||||||
@@ -3054,6 +3060,7 @@ void resetToDefaults() {
|
|||||||
EEPROM.put(EEPROM_ADDR_COLDEX_PREINF_SEC, defaultColdExtractionPreInfusionSeconds);
|
EEPROM.put(EEPROM_ADDR_COLDEX_PREINF_SEC, defaultColdExtractionPreInfusionSeconds);
|
||||||
EEPROM.put(EEPROM_ADDR_COLDEX_PULSE_PERIOD_MS, defaultColdExtractionPulsePeriodMs);
|
EEPROM.put(EEPROM_ADDR_COLDEX_PULSE_PERIOD_MS, defaultColdExtractionPulsePeriodMs);
|
||||||
EEPROM.put(EEPROM_ADDR_COLDEX_DUTY, defaultColdExtractionDutyPercent);
|
EEPROM.put(EEPROM_ADDR_COLDEX_DUTY, defaultColdExtractionDutyPercent);
|
||||||
|
EEPROM.put(EEPROM_ADDR_COLDEX_PREINF_DUTY, defaultColdExtractionPreInfDutyPercent);
|
||||||
EEPROM.put(EEPROM_ADDR_COLDEX_TARGET_GRAMS, defaultColdExtractionTargetGrams);
|
EEPROM.put(EEPROM_ADDR_COLDEX_TARGET_GRAMS, defaultColdExtractionTargetGrams);
|
||||||
EEPROM.put(EEPROM_ADDR_COLDEX_MAX_SECONDS, defaultColdExtractionMaxSeconds);
|
EEPROM.put(EEPROM_ADDR_COLDEX_MAX_SECONDS, defaultColdExtractionMaxSeconds);
|
||||||
EEPROM.put(EEPROM_ADDR_COLDEX_PUMP_MAX_RUN, defaultColdExtractionPumpMaxRunSec);
|
EEPROM.put(EEPROM_ADDR_COLDEX_PUMP_MAX_RUN, defaultColdExtractionPumpMaxRunSec);
|
||||||
@@ -3142,6 +3149,7 @@ void resetToDefaults() {
|
|||||||
coldExtractionPreInfusionSeconds = defaultColdExtractionPreInfusionSeconds;
|
coldExtractionPreInfusionSeconds = defaultColdExtractionPreInfusionSeconds;
|
||||||
coldExtractionPulsePeriodMs = defaultColdExtractionPulsePeriodMs;
|
coldExtractionPulsePeriodMs = defaultColdExtractionPulsePeriodMs;
|
||||||
coldExtractionDutyPercent = defaultColdExtractionDutyPercent;
|
coldExtractionDutyPercent = defaultColdExtractionDutyPercent;
|
||||||
|
coldExtractionPreInfDutyPercent = defaultColdExtractionPreInfDutyPercent;
|
||||||
coldExtractionTargetGrams = defaultColdExtractionTargetGrams;
|
coldExtractionTargetGrams = defaultColdExtractionTargetGrams;
|
||||||
coldExtractionMaxSeconds = defaultColdExtractionMaxSeconds;
|
coldExtractionMaxSeconds = defaultColdExtractionMaxSeconds;
|
||||||
coldExtractionPumpMaxRunSec = defaultColdExtractionPumpMaxRunSec;
|
coldExtractionPumpMaxRunSec = defaultColdExtractionPumpMaxRunSec;
|
||||||
@@ -4230,10 +4238,12 @@ void applyColdExtractionPumpControl(unsigned long currentShotTimeMillis) {
|
|||||||
bool pumpOn;
|
bool pumpOn;
|
||||||
if (coldExtractionPumpResting) {
|
if (coldExtractionPumpResting) {
|
||||||
pumpOn = false;
|
pumpOn = false;
|
||||||
} else if (currentPreInfusionState == PI_PRE_BREW) {
|
|
||||||
pumpOn = true; // Vorbenetzung: volle Pumpenleistung
|
|
||||||
} else {
|
} else {
|
||||||
float duty = coldExtractionDutyPercent / 100.0f;
|
// Vorbenetzung und Hauptbezug pulsen beide - nur mit unterschiedlicher Leistung.
|
||||||
|
// Volle Leistung auf den trockenen kalten Puck legt Kanaele an, die sich mangels
|
||||||
|
// Quellung nie wieder schliessen (siehe Kommentar bei den Defaults).
|
||||||
|
float duty = ((currentPreInfusionState == PI_PRE_BREW) ? coldExtractionPreInfDutyPercent
|
||||||
|
: coldExtractionDutyPercent) / 100.0f;
|
||||||
duty = constrain(duty, COLDEX_DUTY_MIN / 100.0f, 1.0f);
|
duty = constrain(duty, COLDEX_DUTY_MIN / 100.0f, 1.0f);
|
||||||
uint16_t periodMs = coldExtractionPulsePeriodMs;
|
uint16_t periodMs = coldExtractionPulsePeriodMs;
|
||||||
if (periodMs < COLDEX_PULSE_PERIOD_MIN_MS || periodMs > COLDEX_PULSE_PERIOD_MAX_MS) {
|
if (periodMs < COLDEX_PULSE_PERIOD_MIN_MS || periodMs > COLDEX_PULSE_PERIOD_MAX_MS) {
|
||||||
@@ -4698,6 +4708,7 @@ void setup() {
|
|||||||
EEPROM.get(EEPROM_ADDR_COLDEX_PREINF_SEC, coldExtractionPreInfusionSeconds);
|
EEPROM.get(EEPROM_ADDR_COLDEX_PREINF_SEC, coldExtractionPreInfusionSeconds);
|
||||||
EEPROM.get(EEPROM_ADDR_COLDEX_PULSE_PERIOD_MS, coldExtractionPulsePeriodMs);
|
EEPROM.get(EEPROM_ADDR_COLDEX_PULSE_PERIOD_MS, coldExtractionPulsePeriodMs);
|
||||||
EEPROM.get(EEPROM_ADDR_COLDEX_DUTY, coldExtractionDutyPercent);
|
EEPROM.get(EEPROM_ADDR_COLDEX_DUTY, coldExtractionDutyPercent);
|
||||||
|
EEPROM.get(EEPROM_ADDR_COLDEX_PREINF_DUTY, coldExtractionPreInfDutyPercent);
|
||||||
EEPROM.get(EEPROM_ADDR_COLDEX_TARGET_GRAMS, coldExtractionTargetGrams);
|
EEPROM.get(EEPROM_ADDR_COLDEX_TARGET_GRAMS, coldExtractionTargetGrams);
|
||||||
EEPROM.get(EEPROM_ADDR_COLDEX_MAX_SECONDS, coldExtractionMaxSeconds);
|
EEPROM.get(EEPROM_ADDR_COLDEX_MAX_SECONDS, coldExtractionMaxSeconds);
|
||||||
EEPROM.get(EEPROM_ADDR_COLDEX_PUMP_MAX_RUN, coldExtractionPumpMaxRunSec);
|
EEPROM.get(EEPROM_ADDR_COLDEX_PUMP_MAX_RUN, coldExtractionPumpMaxRunSec);
|
||||||
@@ -4714,6 +4725,10 @@ void setup() {
|
|||||||
if (!isfinite(coldExtractionDutyPercent) || coldExtractionDutyPercent < COLDEX_DUTY_MIN || coldExtractionDutyPercent > COLDEX_DUTY_MAX) {
|
if (!isfinite(coldExtractionDutyPercent) || coldExtractionDutyPercent < COLDEX_DUTY_MIN || coldExtractionDutyPercent > COLDEX_DUTY_MAX) {
|
||||||
coldExtractionDutyPercent = defaultColdExtractionDutyPercent;
|
coldExtractionDutyPercent = defaultColdExtractionDutyPercent;
|
||||||
}
|
}
|
||||||
|
// Beim Update aus <= 5.3.5 steht hier 0xFFFFFFFF (NaN) -> Default greift automatisch
|
||||||
|
if (!isfinite(coldExtractionPreInfDutyPercent) || coldExtractionPreInfDutyPercent < COLDEX_DUTY_MIN || coldExtractionPreInfDutyPercent > COLDEX_DUTY_MAX) {
|
||||||
|
coldExtractionPreInfDutyPercent = defaultColdExtractionPreInfDutyPercent;
|
||||||
|
}
|
||||||
if (!isfinite(coldExtractionTargetGrams) || coldExtractionTargetGrams < COLDEX_TARGET_GRAMS_MIN || coldExtractionTargetGrams > COLDEX_TARGET_GRAMS_MAX) {
|
if (!isfinite(coldExtractionTargetGrams) || coldExtractionTargetGrams < COLDEX_TARGET_GRAMS_MIN || coldExtractionTargetGrams > COLDEX_TARGET_GRAMS_MAX) {
|
||||||
coldExtractionTargetGrams = defaultColdExtractionTargetGrams;
|
coldExtractionTargetGrams = defaultColdExtractionTargetGrams;
|
||||||
}
|
}
|
||||||
@@ -7207,11 +7222,13 @@ static const char brewControlCX_ToggleContainerEnd[] PROGMEM = R"rawliteral(><sp
|
|||||||
static const char brewControlCX_Fields[] PROGMEM = R"rawliteral(
|
static const char brewControlCX_Fields[] PROGMEM = R"rawliteral(
|
||||||
<label for='cxMaxTemp'>Freigabe bis Wassertemperatur (°C):</label>
|
<label for='cxMaxTemp'>Freigabe bis Wassertemperatur (°C):</label>
|
||||||
<input type='number' min='10.0' max='40.0' step='0.5' id='cxMaxTemp' name='cxMaxTemp' value='{CX_MAX_TEMP}' required>
|
<input type='number' min='10.0' max='40.0' step='0.5' id='cxMaxTemp' name='cxMaxTemp' value='{CX_MAX_TEMP}' required>
|
||||||
<label for='cxPreInf'>Vorbenetzung (Sekunden, Pumpe läuft dabei durchgehend):</label>
|
<label for='cxPreInf'>Vorbenetzung (Sekunden):</label>
|
||||||
<input type='number' min='0.0' max='120.0' step='1.0' id='cxPreInf' name='cxPreInf' value='{CX_PREINF}' required>
|
<input type='number' min='0.0' max='120.0' step='1.0' id='cxPreInf' name='cxPreInf' value='{CX_PREINF}' required>
|
||||||
|
<label for='cxPreInfDuty'>Pumpenleistung Vorbenetzung (%):</label>
|
||||||
|
<input type='number' min='5.0' max='100.0' step='1.0' id='cxPreInfDuty' name='cxPreInfDuty' value='{CX_PREINF_DUTY}' required>
|
||||||
<label for='cxPulseMs'>Pulsperiode (ms):</label>
|
<label for='cxPulseMs'>Pulsperiode (ms):</label>
|
||||||
<input type='number' min='200' max='5000' step='50' id='cxPulseMs' name='cxPulseMs' value='{CX_PULSE_MS}' required>
|
<input type='number' min='200' max='5000' step='50' id='cxPulseMs' name='cxPulseMs' value='{CX_PULSE_MS}' required>
|
||||||
<label for='cxDuty'>Pumpenleistung (%):</label>
|
<label for='cxDuty'>Pumpenleistung Hauptbezug (%):</label>
|
||||||
<input type='number' min='5.0' max='100.0' step='1.0' id='cxDuty' name='cxDuty' value='{CX_DUTY}' required>
|
<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, nur mit Waage wirksam):</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>
|
<input type='number' min='10.0' max='300.0' step='1.0' id='cxTarget' name='cxTarget' value='{CX_TARGET}' required>
|
||||||
@@ -7224,9 +7241,12 @@ static const char brewControlCX_Fields[] PROGMEM = R"rawliteral(
|
|||||||
<small class="toggle-description-input">(Die Maschine hat keinen Bypass um den Wasserkessel: Kaltes Wasser kommt nur bei kaltem
|
<small class="toggle-description-input">(Die Maschine hat keinen Bypass um den Wasserkessel: Kaltes Wasser kommt nur bei kaltem
|
||||||
Kessel am Puck an. Deshalb ist die kalte Extraktion nur unterhalb der Freigabetemperatur möglich, praktisch also
|
Kessel am Puck an. Deshalb ist die kalte Extraktion nur unterhalb der Freigabetemperatur möglich, praktisch also
|
||||||
aus dem kalten Zustand heraus. Solange der Modus aktiv ist, heizt der Wasserkreis nicht - der Dampfkreis bleibt
|
aus dem kalten Zustand heraus. Solange der Modus aktiv ist, heizt der Wasserkreis nicht - der Dampfkreis bleibt
|
||||||
unberührt und darf weiter heizen. Während der Vorbenetzung läuft die Pumpe durchgehend auf voller
|
unberührt und darf weiter heizen. Vorbenetzung und Hauptbezug pulsen beide, nur mit unterschiedlicher
|
||||||
Leistung (kein Pulsen) - sinnvoll ist eine Dauer knapp unterhalb des ersten Tropfens; gepulst wird erst im
|
Leistung: Kaltes Kaffeemehl quillt nicht und ist schlecht benetzbar - volle Leistung auf den trockenen Puck
|
||||||
Hauptbezug. Die Pumpenlaufzeit wird kumuliert gezählt; nach der eingestellten
|
legt sofort Kanäle an, die sich (anders als bei heißem Bezug) nie wieder schließen, und der
|
||||||
|
restliche Bezug läuft daran vorbei. Deshalb die Vorbenetzung deutlich schwächer einstellen als den
|
||||||
|
Hauptbezug. Das kalte Bett ist insgesamt viel durchlässiger als ein heißes, niedrige Werte sind
|
||||||
|
also normal. Die Pumpenlaufzeit wird kumuliert gezählt; nach der eingestellten
|
||||||
Laufzeit legt die Steuerung eine Zwangspause ein. Der Modus selbst wird auf dem Dashboard bzw. am Touch-Display
|
Laufzeit legt die Steuerung eine Zwangspause ein. Der Modus selbst wird auf dem Dashboard bzw. am Touch-Display
|
||||||
ein- und ausgeschaltet und bleibt über Standby und Neustart hinweg aktiv, bis er dort wieder ausgeschaltet
|
ein- und ausgeschaltet und bleibt über Standby und Neustart hinweg aktiv, bis er dort wieder ausgeschaltet
|
||||||
wird - solange heizt der Wasserkreis nicht.)</small>
|
wird - solange heizt der Wasserkreis nicht.)</small>
|
||||||
@@ -7362,6 +7382,8 @@ void handleBrewControl(AsyncWebServerRequest *request) {
|
|||||||
cxFieldsHtml.replace("{CX_PULSE_MS}", buffer);
|
cxFieldsHtml.replace("{CX_PULSE_MS}", buffer);
|
||||||
snprintf(buffer, sizeof(buffer), "%.1f", coldExtractionDutyPercent);
|
snprintf(buffer, sizeof(buffer), "%.1f", coldExtractionDutyPercent);
|
||||||
cxFieldsHtml.replace("{CX_DUTY}", buffer);
|
cxFieldsHtml.replace("{CX_DUTY}", buffer);
|
||||||
|
snprintf(buffer, sizeof(buffer), "%.1f", coldExtractionPreInfDutyPercent);
|
||||||
|
cxFieldsHtml.replace("{CX_PREINF_DUTY}", buffer);
|
||||||
snprintf(buffer, sizeof(buffer), "%.1f", coldExtractionTargetGrams);
|
snprintf(buffer, sizeof(buffer), "%.1f", coldExtractionTargetGrams);
|
||||||
cxFieldsHtml.replace("{CX_TARGET}", buffer);
|
cxFieldsHtml.replace("{CX_TARGET}", buffer);
|
||||||
snprintf(buffer, sizeof(buffer), "%.1f", coldExtractionMaxSeconds);
|
snprintf(buffer, sizeof(buffer), "%.1f", coldExtractionMaxSeconds);
|
||||||
@@ -7460,6 +7482,8 @@ struct BrewControlUpdate {
|
|||||||
uint16_t cxPulseMs = 0;
|
uint16_t cxPulseMs = 0;
|
||||||
bool hasCXDuty = false;
|
bool hasCXDuty = false;
|
||||||
float cxDuty = 0.0f;
|
float cxDuty = 0.0f;
|
||||||
|
bool hasCXPreInfDuty = false;
|
||||||
|
float cxPreInfDuty = 0.0f;
|
||||||
bool hasCXTarget = false;
|
bool hasCXTarget = false;
|
||||||
float cxTarget = 0.0f;
|
float cxTarget = 0.0f;
|
||||||
bool hasCXMaxSecs = false;
|
bool hasCXMaxSecs = false;
|
||||||
@@ -7625,6 +7649,14 @@ bool applyBrewControlUpdate(const BrewControlUpdate& update) {
|
|||||||
coldExtractionConfigChanged = true;
|
coldExtractionConfigChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (update.hasCXPreInfDuty) {
|
||||||
|
float v = update.cxPreInfDuty;
|
||||||
|
if (isfinite(v) && v >= COLDEX_DUTY_MIN && v <= COLDEX_DUTY_MAX && fabs(v - coldExtractionPreInfDutyPercent) > 0.01f) {
|
||||||
|
coldExtractionPreInfDutyPercent = v;
|
||||||
|
EEPROM.put(EEPROM_ADDR_COLDEX_PREINF_DUTY, coldExtractionPreInfDutyPercent);
|
||||||
|
coldExtractionConfigChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (update.hasCXTarget) {
|
if (update.hasCXTarget) {
|
||||||
float v = update.cxTarget;
|
float v = update.cxTarget;
|
||||||
if (isfinite(v) && v >= COLDEX_TARGET_GRAMS_MIN && v <= COLDEX_TARGET_GRAMS_MAX && fabs(v - coldExtractionTargetGrams) > 0.01f) {
|
if (isfinite(v) && v >= COLDEX_TARGET_GRAMS_MIN && v <= COLDEX_TARGET_GRAMS_MAX && fabs(v - coldExtractionTargetGrams) > 0.01f) {
|
||||||
@@ -7715,6 +7747,8 @@ void handleSaveBrewControl(AsyncWebServerRequest *request) {
|
|||||||
if (update.hasCXPulseMs) { update.cxPulseMs = (uint16_t)request->arg("cxPulseMs").toInt(); }
|
if (update.hasCXPulseMs) { update.cxPulseMs = (uint16_t)request->arg("cxPulseMs").toInt(); }
|
||||||
update.hasCXDuty = request->hasArg("cxDuty");
|
update.hasCXDuty = request->hasArg("cxDuty");
|
||||||
if (update.hasCXDuty) { update.cxDuty = request->arg("cxDuty").toFloat(); }
|
if (update.hasCXDuty) { update.cxDuty = request->arg("cxDuty").toFloat(); }
|
||||||
|
update.hasCXPreInfDuty = request->hasArg("cxPreInfDuty");
|
||||||
|
if (update.hasCXPreInfDuty) { update.cxPreInfDuty = request->arg("cxPreInfDuty").toFloat(); }
|
||||||
update.hasCXTarget = request->hasArg("cxTarget");
|
update.hasCXTarget = request->hasArg("cxTarget");
|
||||||
if (update.hasCXTarget) { update.cxTarget = request->arg("cxTarget").toFloat(); }
|
if (update.hasCXTarget) { update.cxTarget = request->arg("cxTarget").toFloat(); }
|
||||||
update.hasCXMaxSecs = request->hasArg("cxMaxSecs");
|
update.hasCXMaxSecs = request->hasArg("cxMaxSecs");
|
||||||
|
|||||||
Reference in New Issue
Block a user