refactor: reworking the freeze protection algorithm (#242)

* refactor: impoved freeze protection

* refactor: reworked freeze protection
This commit is contained in:
Yurii
2026-05-11 14:26:14 +03:00
committed by GitHub
parent 9a3a171370
commit cbc32fd378
13 changed files with 120 additions and 55 deletions
+30 -21
View File
@@ -43,8 +43,6 @@ protected:
bool telnetStarted = false;
bool emergencyDetected = false;
unsigned long emergencyFlipTime = 0;
bool freezeDetected = false;
unsigned long freezeDetectedTime = 0;
#if defined(ARDUINO_ARCH_ESP32)
const char* getTaskName() override {
@@ -243,7 +241,7 @@ protected:
void heating() {
// freeze protection
if (!settings.heating.enabled) {
{
float lowTemp = 255.0f;
uint8_t availableSensors = 0;
@@ -274,29 +272,40 @@ protected:
availableSensors++;
}
if (availableSensors && lowTemp <= settings.heating.freezeProtection.lowTemp) {
if (!this->freezeDetected) {
this->freezeDetected = true;
this->freezeDetectedTime = millis();
if (availableSensors) {
if (vars.master.heating.freezing) {
if (lowTemp - (float) settings.heating.freezeProtection.highTemp + 0.0001f >= 0.0f) {
vars.master.heating.freezing = false;
} else if (millis() - this->freezeDetectedTime > (settings.heating.freezeProtection.thresholdTime * 1000)) {
this->freezeDetected = false;
settings.heating.enabled = true;
fsSettings.update();
Log.sinfoln(
FPSTR(L_MAIN),
F("No freezing detected. Current low temp: %.2f, threshold (high): %hhu"),
lowTemp, settings.heating.freezeProtection.highTemp
);
}
Log.sinfoln(
FPSTR(L_MAIN),
F("Heating turned on by freeze protection, current low temp: %.2f, threshold: %hhu"),
lowTemp, settings.heating.freezeProtection.lowTemp
);
} else {
if ((float) settings.heating.freezeProtection.lowTemp - lowTemp + 0.0001f >= 0.0f) {
vars.master.heating.freezing = true;
if (!settings.heating.enabled) {
settings.heating.enabled = true;
fsSettings.update();
}
Log.sinfoln(
FPSTR(L_MAIN),
F("Freezing detected! Current low temp: %.2f, threshold (low): %hhu"),
lowTemp, settings.heating.freezeProtection.lowTemp
);
}
}
} else if (this->freezeDetected) {
this->freezeDetected = false;
}
} else if (vars.master.heating.freezing) {
vars.master.heating.freezing = false;
} else if (this->freezeDetected) {
this->freezeDetected = false;
Log.sinfoln(FPSTR(L_MAIN), F("No sensors available, freeze protection unavailable!"));
}
}
}
+1 -2
View File
@@ -170,8 +170,7 @@ protected:
// Heating settings
vars.master.heating.enabled = this->isReady()
&& settings.heating.enabled
&& vars.cascadeControl.input
&& !vars.master.heating.blocking
&& (vars.master.heating.freezing || (vars.cascadeControl.input && !vars.master.heating.blocking))
&& !vars.master.heating.overheat;
// DHW settings
+5
View File
@@ -240,6 +240,11 @@ protected:
) * settings.heating.turboFactor;
}
// If freezing, set temperature to no lower than low temp provided by freeze protection
if (vars.master.heating.freezing && fabsf(settings.heating.freezeProtection.lowTemp - newTemp) < 0.0001f) {
newTemp = settings.heating.freezeProtection.lowTemp;
}
return newTemp;
}
};
+2 -1
View File
@@ -121,8 +121,8 @@ struct Settings {
} overheatProtection;
struct {
uint8_t highTemp = 15;
uint8_t lowTemp = 10;
unsigned short thresholdTime = 600;
} freezeProtection;
} heating;
@@ -304,6 +304,7 @@ struct Variables {
bool enabled = false;
bool indoorTempControl = false;
bool overheat = false;
bool freezing = false;
float setpointTemp = 0.0f;
float targetTemp = 0.0f;
float currentTemp = 0.0f;
+1
View File
@@ -87,6 +87,7 @@ const char S_EXTERNAL_PUMP[] PROGMEM = "externalPump";
const char S_FACTOR[] PROGMEM = "factor";
const char S_FAULT[] PROGMEM = "fault";
const char S_FREEZE_PROTECTION[] PROGMEM = "freezeProtection";
const char S_FREEZING[] PROGMEM = "freezing";
const char S_FILTERING[] PROGMEM = "filtering";
const char S_FILTERING_FACTOR[] PROGMEM = "filteringFactor";
const char S_FLAGS[] PROGMEM = "flags";
+14 -10
View File
@@ -505,8 +505,8 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
heatingOverheatProtection[FPSTR(S_LOW_TEMP)] = src.heating.overheatProtection.lowTemp;
auto freezeProtection = heating[FPSTR(S_FREEZE_PROTECTION)].to<JsonObject>();
freezeProtection[FPSTR(S_HIGH_TEMP)] = src.heating.freezeProtection.highTemp;
freezeProtection[FPSTR(S_LOW_TEMP)] = src.heating.freezeProtection.lowTemp;
freezeProtection[FPSTR(S_THRESHOLD_TIME)] = src.heating.freezeProtection.thresholdTime;
auto dhw = dst[FPSTR(S_DHW)].to<JsonObject>();
dhw[FPSTR(S_ENABLED)] = src.dhw.enabled;
@@ -1426,6 +1426,15 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true;
}
if (!src[FPSTR(S_HEATING)][FPSTR(S_FREEZE_PROTECTION)][FPSTR(S_HIGH_TEMP)].isNull()) {
unsigned short value = src[FPSTR(S_HEATING)][FPSTR(S_FREEZE_PROTECTION)][FPSTR(S_HIGH_TEMP)].as<uint8_t>();
if (isValidTemp(value, dst.system.unitSystem, 1, 50) && value != dst.heating.freezeProtection.highTemp) {
dst.heating.freezeProtection.highTemp = value;
changed = true;
}
}
if (!src[FPSTR(S_HEATING)][FPSTR(S_FREEZE_PROTECTION)][FPSTR(S_LOW_TEMP)].isNull()) {
unsigned short value = src[FPSTR(S_HEATING)][FPSTR(S_FREEZE_PROTECTION)][FPSTR(S_LOW_TEMP)].as<uint8_t>();
@@ -1435,15 +1444,9 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}
if (!src[FPSTR(S_HEATING)][FPSTR(S_FREEZE_PROTECTION)][FPSTR(S_THRESHOLD_TIME)].isNull()) {
unsigned short value = src[FPSTR(S_HEATING)][FPSTR(S_FREEZE_PROTECTION)][FPSTR(S_THRESHOLD_TIME)].as<unsigned short>();
if (value >= 30 && value <= 1800) {
if (value != dst.heating.freezeProtection.thresholdTime) {
dst.heating.freezeProtection.thresholdTime = value;
changed = true;
}
}
if (dst.heating.freezeProtection.highTemp < dst.heating.freezeProtection.lowTemp) {
dst.heating.freezeProtection.highTemp = dst.heating.freezeProtection.lowTemp;
changed = true;
}
@@ -2170,6 +2173,7 @@ void varsToJson(const Variables& src, JsonVariant dst) {
mHeating[FPSTR(S_BLOCKING)] = src.master.heating.blocking;
mHeating[FPSTR(S_INDOOR_TEMP_CONTROL)] = src.master.heating.indoorTempControl;
mHeating[FPSTR(S_OVERHEAT)] = src.master.heating.overheat;
mHeating[FPSTR(S_FREEZING)] = src.master.heating.freezing;
mHeating[FPSTR(S_SETPOINT_TEMP)] = roundf(src.master.heating.setpointTemp, 2);
mHeating[FPSTR(S_TARGET_TEMP)] = roundf(src.master.heating.targetTemp, 2);
mHeating[FPSTR(S_CURRENT_TEMP)] = roundf(src.master.heating.currentTemp, 2);