feat(S3): Aktives Profil führen und anzeigen — Grundlage für Profil-Schnellwahl am Display #7
@@ -1,3 +1,15 @@
|
||||
Version 5.1.1:
|
||||
- Aktives Profil wird jetzt geführt und angezeigt: Die Steuerung merkt sich das zuletzt geladene bzw.
|
||||
gespeicherte Profil dauerhaft im EEPROM (überlebt Neustarts). Web-UI: Anzeige auf dem Dashboard über der
|
||||
Profil-Auswahl sowie auf der Profil-Seite (aktives Profil zusätzlich mit grünem Punkt in der Liste).
|
||||
- „Geändert"-Erkennung: Werden nach dem Laden profil-relevante Einstellungen geändert (Solltemperaturen,
|
||||
PID-Werte inkl. AutoTune-Ergebnis und AutoTune-Parameter, Boost, Eco, Dampfverzögerung, Fast-Heat-Up,
|
||||
Brew-Control, Piezo), erscheint hinter dem Profilnamen „✱ (geändert)". Reine Anzeige-/Systemeinstellungen
|
||||
(Anzeigemodus, Aufheizzeit, Licht, FlowGuard, Waage/Sensoren) lösen das bewusst nicht aus.
|
||||
- Profil löschen setzt die Anzeige zurück, wenn das gelöschte Profil das aktive war; Werksreset ebenso.
|
||||
- Neue additive State-Felder "profile" und "profDirty" im UART-JSON für das Touch-Display (Protokoll bleibt
|
||||
kompatibel, ältere Display-Firmware ignoriert die Felder) - Grundlage für die Profil-Schnellwahl am Display.
|
||||
|
||||
Version 5.1.0:
|
||||
- Display-Auswahl in die Web-UI (/Sensoren) verlegt: Ohne Display / OLED (SH1106, I2C) / UART-Touch-Display.
|
||||
Der Compile-Schalter ENABLE_DISPLAY entfällt, die Einstellung wird im EEPROM gespeichert und ohne Neustart
|
||||
|
||||
+146
-7
@@ -369,7 +369,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
|
||||
* Firmware-Informationen
|
||||
************************************************************************************/
|
||||
|
||||
String version = "5.1.0";
|
||||
String version = "5.1.1";
|
||||
String versionHersteller = "Thomas Müller";
|
||||
String versionHerstellerMail =
|
||||
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
|
||||
@@ -942,7 +942,9 @@ const int EEPROM_ADDR_BACKLIGHT_ACTIVE_PERCENT = 908; // 1 Byte (uint8_t)
|
||||
const int EEPROM_ADDR_BACKLIGHT_STANDBY_CLOCK_PERCENT = 909; // 1 Byte (uint8_t) -> Ende 909
|
||||
const int EEPROM_ADDR_POWERON_STANDBY = 910; // 1 Byte (bool) -> Ende 910
|
||||
const int EEPROM_ADDR_DISPLAY_TYPE = 911; // 1 Byte (uint8_t) -> Ende 911
|
||||
// Naechste freie Adresse: 912 (Innerhalb EEPROM_SIZE=1024)
|
||||
const int EEPROM_ADDR_ACTIVE_PROFILE_NAME = 912; // 31 Bytes (char[31], nullterminiert) -> Ende 942
|
||||
const int EEPROM_ADDR_ACTIVE_PROFILE_DIRTY = 943; // 1 Byte (bool) -> Ende 943
|
||||
// Naechste freie Adresse: 944 (Innerhalb EEPROM_SIZE=1024)
|
||||
|
||||
/************************************************************************************
|
||||
* Eco-Mode Variablen
|
||||
@@ -1312,6 +1314,45 @@ char hostname[33] = "Dual-PID"; // Max 32 Zeichen + Nullterminator
|
||||
String profileStatusMessage = ""; // für Nachrichten auf der Profil-Seite
|
||||
String standbyTimerStatusMessage = ""; // fuer Nachrichten auf der Timer-Seite
|
||||
|
||||
// Zuletzt geladenes/gespeichertes Profil (leer = keines). Wird im EEPROM gehalten,
|
||||
// damit die Anzeige einen Neustart überlebt. activeProfileDirty markiert, dass nach
|
||||
// dem Laden profil-relevante Einstellungen geändert wurden (Anzeige "geändert").
|
||||
String activeProfileName = "";
|
||||
bool activeProfileDirty = false;
|
||||
|
||||
// Aktives Profil + Dirty-Flag ins EEPROM schreiben (char[31], nullterminiert)
|
||||
void persistActiveProfile() {
|
||||
char nameBuf[31];
|
||||
memset(nameBuf, 0, sizeof(nameBuf));
|
||||
strncpy(nameBuf, activeProfileName.c_str(), sizeof(nameBuf) - 1);
|
||||
for (size_t i = 0; i < sizeof(nameBuf); i++) {
|
||||
EEPROM.write(EEPROM_ADDR_ACTIVE_PROFILE_NAME + i, (uint8_t)nameBuf[i]);
|
||||
}
|
||||
EEPROM.write(EEPROM_ADDR_ACTIVE_PROFILE_DIRTY, activeProfileDirty ? 1 : 0);
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
void setActiveProfile(const String& name) {
|
||||
activeProfileName = name;
|
||||
activeProfileDirty = false;
|
||||
persistActiveProfile();
|
||||
}
|
||||
|
||||
void clearActiveProfile() {
|
||||
activeProfileName = "";
|
||||
activeProfileDirty = false;
|
||||
persistActiveProfile();
|
||||
}
|
||||
|
||||
// Nach einer profil-relevanten Einstellungsänderung aufrufen: markiert das aktive
|
||||
// Profil als "geändert". Ohne aktives Profil oder bei bereits gesetztem Flag ein No-Op.
|
||||
void markProfileDirty() {
|
||||
if (activeProfileName.length() == 0 || activeProfileDirty) return;
|
||||
activeProfileDirty = true;
|
||||
EEPROM.write(EEPROM_ADDR_ACTIVE_PROFILE_DIRTY, 1);
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
/************************************************************************************
|
||||
* WiFi Signal Bitmap (Display-Anzeige)
|
||||
************************************************************************************/
|
||||
@@ -2925,6 +2966,9 @@ void resetToDefaults() {
|
||||
// Alle Änderungen ins EEPROM schreiben
|
||||
EEPROM.commit();
|
||||
|
||||
// Werksreset -> kein Profil mehr aktiv
|
||||
clearActiveProfile();
|
||||
|
||||
// WICHTIG: Globale Variablen auch auf Default setzen
|
||||
SetpointWasser = defaultSetpointWasser; SetpointDampf = defaultSetpointDampf;
|
||||
OffsetWasser = defaultOffsetWasser; OffsetDampf = defaultOffsetDampf;
|
||||
@@ -4264,6 +4308,28 @@ void setup() {
|
||||
storedDisplayType = DEFAULT_DISPLAY_TYPE; // u. a. unprogrammierte 0xFF
|
||||
}
|
||||
displayType = storedDisplayType;
|
||||
// Zuletzt geladenes Profil aus dem EEPROM holen. Unprogrammierter Bereich (0xFF)
|
||||
// oder nicht druckbare Zeichen -> kein aktives Profil.
|
||||
{
|
||||
char nameBuf[31];
|
||||
for (size_t i = 0; i < sizeof(nameBuf); i++) {
|
||||
nameBuf[i] = (char)EEPROM.read(EEPROM_ADDR_ACTIVE_PROFILE_NAME + i);
|
||||
}
|
||||
nameBuf[sizeof(nameBuf) - 1] = '\0';
|
||||
bool valid = true;
|
||||
for (size_t i = 0; nameBuf[i] != '\0'; i++) {
|
||||
// Steuerzeichen oder 0xFF (unprogrammiertes EEPROM) -> ungueltig.
|
||||
// Bytes >= 0x80 bleiben erlaubt (UTF-8-Umlaute im Anzeigenamen).
|
||||
if ((uint8_t)nameBuf[i] < 0x20 || (uint8_t)nameBuf[i] == 0xFF) { valid = false; break; }
|
||||
}
|
||||
if (valid && nameBuf[0] != '\0') {
|
||||
activeProfileName = String(nameBuf);
|
||||
activeProfileDirty = (EEPROM.read(EEPROM_ADDR_ACTIVE_PROFILE_DIRTY) == 1);
|
||||
} else {
|
||||
activeProfileName = "";
|
||||
activeProfileDirty = false;
|
||||
}
|
||||
}
|
||||
uint8_t storedScaleEnabled = 0;
|
||||
uint8_t storedScaleType = defaultScaleType;
|
||||
EEPROM.get(EEPROM_ADDR_SCALE_ENABLED, storedScaleEnabled);
|
||||
@@ -6353,6 +6419,7 @@ void handleAutoTune(AsyncWebServerRequest *request) {
|
||||
autoTuneWasserResultKi = (float)KiWasser;
|
||||
autoTuneWasserResultKd = (float)KdWasser;
|
||||
autoTuneWasserStatus = (peaks > 9) ? AT_FAILSAFE_PEAKS : AT_SUCCESS;
|
||||
markProfileDirty(); // PID-Werte weichen jetzt vom geladenen Profil ab
|
||||
}
|
||||
|
||||
// Serial.println("AutoTune Wasser erfolgreich abgeschlossen.");
|
||||
@@ -6411,6 +6478,7 @@ void handleAutoTune(AsyncWebServerRequest *request) {
|
||||
autoTuneDampfResultKi = (float)KiDampf;
|
||||
autoTuneDampfResultKd = (float)KdDampf;
|
||||
autoTuneDampfStatus = (peaks > 9) ? AT_FAILSAFE_PEAKS : AT_SUCCESS;
|
||||
markProfileDirty(); // PID-Werte weichen jetzt vom geladenen Profil ab
|
||||
}
|
||||
|
||||
// Serial.println("AutoTune Dampf erfolgreich abgeschlossen.");
|
||||
@@ -6707,7 +6775,6 @@ bool applyBrewControlUpdate(const BrewControlUpdate& update) {
|
||||
if (update.hasFlowGuardEnabled && update.flowGuardEnabled != flowGuardEnabled) {
|
||||
flowGuardEnabled = update.flowGuardEnabled;
|
||||
EEPROM.put(EEPROM_ADDR_FLOWGUARD_ENABLED, flowGuardEnabled);
|
||||
changed = true;
|
||||
flowGuardConfigChanged = true;
|
||||
}
|
||||
if (update.hasFlowGuardMinSecs) {
|
||||
@@ -6715,7 +6782,6 @@ bool applyBrewControlUpdate(const BrewControlUpdate& update) {
|
||||
if (newFlowGuardMinSecs >= FLOW_GUARD_MIN_SECONDS_MIN && newFlowGuardMinSecs <= FLOW_GUARD_MIN_SECONDS_MAX && fabs(newFlowGuardMinSecs - flowGuardMinBrewSeconds) > 0.01f) {
|
||||
flowGuardMinBrewSeconds = newFlowGuardMinSecs;
|
||||
EEPROM.put(EEPROM_ADDR_FLOWGUARD_MIN_SECONDS, flowGuardMinBrewSeconds);
|
||||
changed = true;
|
||||
flowGuardConfigChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -6724,7 +6790,6 @@ bool applyBrewControlUpdate(const BrewControlUpdate& update) {
|
||||
if (newPulseMs >= FLOW_GUARD_PULSE_PERIOD_MIN_MS && newPulseMs <= FLOW_GUARD_PULSE_PERIOD_MAX_MS && newPulseMs != flowGuardPulsePeriodMs) {
|
||||
flowGuardPulsePeriodMs = newPulseMs;
|
||||
EEPROM.put(EEPROM_ADDR_FLOWGUARD_PULSE_PERIOD_MS, flowGuardPulsePeriodMs);
|
||||
changed = true;
|
||||
flowGuardConfigChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -6733,7 +6798,6 @@ bool applyBrewControlUpdate(const BrewControlUpdate& update) {
|
||||
if (newFlowGuardMinDuty >= FLOW_GUARD_MIN_DUTY_MIN && newFlowGuardMinDuty <= FLOW_GUARD_MIN_DUTY_MAX && fabs(newFlowGuardMinDuty - flowGuardMinDutyPercent) > 0.01f) {
|
||||
flowGuardMinDutyPercent = newFlowGuardMinDuty;
|
||||
EEPROM.put(EEPROM_ADDR_FLOWGUARD_MIN_DUTY, flowGuardMinDutyPercent);
|
||||
changed = true;
|
||||
flowGuardConfigChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -6778,10 +6842,16 @@ bool applyBrewControlUpdate(const BrewControlUpdate& update) {
|
||||
}
|
||||
}
|
||||
|
||||
// "changed" umfasst hier nur noch profil-relevante Felder (BBT/BBW/PI/SBT);
|
||||
// FlowGuard ist kein Bestandteil von TemperatureProfile und laeuft ueber
|
||||
// flowGuardConfigChanged separat -> markProfileDirty() bleibt praezise.
|
||||
if (changed) {
|
||||
markProfileDirty();
|
||||
}
|
||||
if (changed || flowGuardConfigChanged) {
|
||||
EEPROM.commit();
|
||||
}
|
||||
return changed;
|
||||
return changed || flowGuardConfigChanged;
|
||||
}
|
||||
|
||||
void handleSaveBrewControl(AsyncWebServerRequest *request) {
|
||||
@@ -8507,6 +8577,7 @@ void handleFastHeatUpSettings(AsyncWebServerRequest *request) {
|
||||
|
||||
EEPROM.put(EEPROM_ADDR_FASTHEATUP_DATA, fastHeatUpAktiv);
|
||||
EEPROM.commit();
|
||||
markProfileDirty(); // fastHeatUpAktiv ist Profilfeld
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(303);
|
||||
resp->addHeader(F("Location"), F("/Fast-Heat-Up"));
|
||||
@@ -8999,6 +9070,7 @@ void handleSettingsUpdate(AsyncWebServerRequest *request) {
|
||||
// --- EEPROM speichern und PID aktualisieren ---
|
||||
if (valueChanged) { // Nur speichern, wenn sich mindestens ein Wert geändert hat
|
||||
EEPROM.commit();
|
||||
markProfileDirty(); // Setpoints/Offsets/PID/Boost/MaxTemp/Window sind Profilfelder
|
||||
}
|
||||
|
||||
// PID-Parameter an die Regler übergeben
|
||||
@@ -9386,6 +9458,7 @@ void handleUpdateAutotuneSettings(AsyncWebServerRequest *request) {
|
||||
|
||||
if (changed) {
|
||||
EEPROM.commit();
|
||||
markProfileDirty(); // AutoTune-Parameter sind Profilfelder
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse(303);
|
||||
@@ -9733,6 +9806,8 @@ void handleEcoUpdate(AsyncWebServerRequest *request) {
|
||||
EEPROM.put(EEPROM_ADDR_BACKLIGHT_ACTIVE_PERCENT, backlightActivePercent);
|
||||
EEPROM.put(EEPROM_ADDR_BACKLIGHT_STANDBY_CLOCK_PERCENT, backlightStandbyClockPercent);
|
||||
EEPROM.commit();
|
||||
// Eco/Dampfverzoegerung/Wake-Verhalten sind Profilfelder -> Profil als geaendert markieren
|
||||
markProfileDirty();
|
||||
|
||||
// Wenn Eco deaktiviert, Setpoints zurückladen
|
||||
if (ecoModeMinutes == 0 && !ecoSwitchActive) {
|
||||
@@ -9796,6 +9871,7 @@ void handleUpdatePiezoSettings(AsyncWebServerRequest *request) {
|
||||
|
||||
if (changed) {
|
||||
EEPROM.commit();
|
||||
markProfileDirty(); // piezoEnabled ist Teil des Profils
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(303);
|
||||
@@ -11271,6 +11347,8 @@ bool saveProfile(const String& profileName, const TemperatureProfile& profileDat
|
||||
|
||||
if (bytesWritten == sizeof(TemperatureProfile)) {
|
||||
// Serial.println(" Profile saved successfully (Binary Format).");
|
||||
// Gespeicherter Ist-Zustand entspricht ab jetzt diesem Profil -> als aktiv merken
|
||||
setActiveProfile(displayName);
|
||||
return true;
|
||||
} else {
|
||||
// Serial.printf(" ERROR: Failed to write complete profile data! Bytes written: %d, Expected: %d\n", bytesWritten, sizeof(TemperatureProfile));
|
||||
@@ -11418,6 +11496,9 @@ void applyProfileSettings(const TemperatureProfile& profileData) {
|
||||
// Eco-Status zurücksetzen
|
||||
ecoModeAktiv = false; ecoModeActivatedTime = 0;
|
||||
// Serial.println(" Eco mode status reset.");
|
||||
|
||||
// Erfolgreich angewendet -> als aktives Profil merken (setzt Dirty-Flag zurück)
|
||||
setActiveProfile(normalizeProfileDisplayName(String(profileData.profileName)));
|
||||
}
|
||||
|
||||
|
||||
@@ -11480,6 +11561,11 @@ bool deleteProfile(const String& profileName) {
|
||||
if (LittleFS.exists(filePath)) {
|
||||
if (LittleFS.remove(filePath)) {
|
||||
// Serial.println(" Profile deleted successfully.");
|
||||
// War das geloeschte Profil das aktive -> Anzeige zuruecksetzen
|
||||
if (activeProfileName.length() > 0 &&
|
||||
sanitizeProfileName(activeProfileName) == sanitizedName) {
|
||||
clearActiveProfile();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// Serial.println(" ERROR: Failed to delete profile file.");
|
||||
@@ -11745,6 +11831,19 @@ void handleProfilesPage(AsyncWebServerRequest *request) {
|
||||
profileStatusMessage = ""; // Nachricht entfernen, nachdem sie angezeigt wurde
|
||||
}
|
||||
|
||||
// Aktives Profil anzeigen (zuletzt geladen/gespeichert; Stern = danach geändert)
|
||||
{
|
||||
String activeDiv = "<div style=\"margin-bottom:12px;opacity:0.85;\">Aktives Profil: ";
|
||||
if (activeProfileName.length() > 0) {
|
||||
activeDiv += htmlEscape(activeProfileName);
|
||||
if (activeProfileDirty) { activeDiv += " ✱ (geändert)"; }
|
||||
} else {
|
||||
activeDiv += "—";
|
||||
}
|
||||
activeDiv += "</div>";
|
||||
response->print(activeDiv);
|
||||
}
|
||||
|
||||
// Profilliste generieren
|
||||
response->print(FPSTR(profilesPageListStart)); // <ul>
|
||||
if (profiles.empty()) {
|
||||
@@ -11756,6 +11855,9 @@ void handleProfilesPage(AsyncWebServerRequest *request) {
|
||||
String escapedConfirmJs = htmlEscape(confirmJs);
|
||||
String listItem = FPSTR(profilesPageListItemStart);
|
||||
listItem += escapedName;
|
||||
if (name == activeProfileName) {
|
||||
listItem += F(" <span style=\"color:#4CAF50;\" title=\"aktives Profil\">●</span>");
|
||||
}
|
||||
listItem += F("</span><div class=\"profile-actions\">");
|
||||
listItem += F("<form action=\"/loadProfile\" method=\"POST\" class=\"profiles-form\"><input type=\"hidden\" name=\"profileName\" value=\"");
|
||||
listItem += escapedName;
|
||||
@@ -13559,6 +13661,7 @@ static const char dashboardControlPanelHTML[] PROGMEM = R"rawliteral(
|
||||
</div>
|
||||
|
||||
<h3>Profile</h3>
|
||||
<div id="active_profile_line" style="margin-bottom:8px;opacity:0.85;">Aktives Profil: <span id="active_profile_name">—</span></div>
|
||||
<select id="profile_select">
|
||||
<option value="">Profil wählen...</option>
|
||||
{PROFILE_OPTIONS} </select>
|
||||
@@ -13866,6 +13969,16 @@ static const char dashboardJavaScript[] PROGMEM = R"rawliteral(
|
||||
// Status Text
|
||||
if (statusSpan) { statusSpan.textContent = data.statusText || 'Unbekannt'; statusSpan.classList.toggle('error', data.errorW || data.errorD || data.safetyW || data.safetyD); }
|
||||
|
||||
// Aktives Profil (zuletzt geladen/gespeichert; Stern = danach geänderte Einstellungen)
|
||||
const activeProfileEl = document.getElementById('active_profile_name');
|
||||
if (activeProfileEl) {
|
||||
if (data.profile && data.profile.length > 0) {
|
||||
activeProfileEl.textContent = data.profile + (data.profDirty ? ' ✱ (geändert)' : '');
|
||||
} else {
|
||||
activeProfileEl.textContent = '—';
|
||||
}
|
||||
}
|
||||
|
||||
if (caseTempDisplay) {
|
||||
const showCaseTemp = (data.caseTempDashboard === true);
|
||||
caseTempDisplay.style.display = showCaseTemp ? 'block' : 'none';
|
||||
@@ -14739,6 +14852,14 @@ bool executeDashboardAction(const String& action, const String& value,
|
||||
// --- Abschlussaktionen ---
|
||||
if (settingsChanged) {
|
||||
EEPROM.commit();
|
||||
// Profil-relevante Aenderung (Feld ist Teil von TemperatureProfile) -> aktives
|
||||
// Profil als "geaendert" markieren. Reine Anzeige-/Systemeinstellungen
|
||||
// (setTempDisplayMode, setHeatUpMinutes, toggleLight) zaehlen nicht.
|
||||
if (action == "setTempW" || action == "setTempD" ||
|
||||
action == "toggleBoostW" || action == "toggleBoostD" ||
|
||||
action == "togglePI" || action == "toggleBBT" || action == "toggleBBW") {
|
||||
markProfileDirty();
|
||||
}
|
||||
}
|
||||
if (pidNeedsUpdate) {
|
||||
pidWasser.SetTunings(KpWasser, KiWasser, KdWasser);
|
||||
@@ -15031,6 +15152,15 @@ void buildDashboardJson(char *buffer, size_t bufferSize) {
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"statusText\":\"%s\",", statusText.c_str());
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"statusKey\":\"%s\",", statusKey.c_str());
|
||||
|
||||
// Zuletzt geladenes Profil (leer = keines) + "geaendert"-Flag
|
||||
{
|
||||
String escapedProfile = activeProfileName;
|
||||
escapedProfile.replace("\\", "\\\\");
|
||||
escapedProfile.replace("\"", "\\\"");
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"profile\":\"%s\",", escapedProfile.c_str());
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"profDirty\":%s,", activeProfileDirty ? "true" : "false");
|
||||
}
|
||||
|
||||
bool caseTempDashboardActive = (caseTempOnDashboard && caseSensorEnabled);
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"caseTempDashboard\":%s,", caseTempDashboardActive ? "true" : "false");
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"caseType\":%u,", (unsigned int)caseSensorType);
|
||||
@@ -15323,6 +15453,14 @@ static void buildTouchUartStateJson(char *buffer, size_t bufferSize) {
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"steamHeatDisabled\":%s,", steamHeatDisabledByUser ? "true" : "false");
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"lightOn\":%s,", lightOn ? "true" : "false");
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"piezoEnabled\":%s,", piezoEnabled ? "true" : "false");
|
||||
// Zuletzt geladenes Profil (leer = keines) + "geaendert"-Flag fuer die Anzeige am Display
|
||||
{
|
||||
String escapedProfile = activeProfileName;
|
||||
escapedProfile.replace("\\", "\\\\");
|
||||
escapedProfile.replace("\"", "\\\"");
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"profile\":\"%s\",", escapedProfile.c_str());
|
||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"profDirty\":%s,", activeProfileDirty ? "true" : "false");
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -16258,6 +16396,7 @@ static bool touchUartHandleCommandLine(const String& line) {
|
||||
return false;
|
||||
}
|
||||
EEPROM.commit();
|
||||
markProfileDirty(); // PID-Werte sind Profilfelder
|
||||
if (changedW) { pidWasser.SetTunings(KpWasser, KiWasser, KdWasser); }
|
||||
if (changedD) { pidDampf.SetTunings(KpDampf, KiDampf, KdDampf); }
|
||||
touchUartSendAck(id, true, "PID gespeichert");
|
||||
|
||||
Reference in New Issue
Block a user