feat: added freeze protection parameter for heating, removed forced start of heating in emergency mode #157

This commit is contained in:
Yurii
2025-06-27 00:28:38 +03:00
parent 58b0c18448
commit f6cfdf3263
9 changed files with 87 additions and 7 deletions

View File

@@ -152,6 +152,7 @@ protected:
}
this->yield();
this->heating();
this->emergency();
this->ledStatus();
this->cascadeControl();
@@ -228,6 +229,52 @@ protected:
}
}
void heating() {
// anti freeze protection
if (!settings.heating.enabled) {
float minTemp = 255.0f;
uint8_t availableSensors = 0;
if (Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) {
auto value = Sensors::getMeanValueByPurpose(Sensors::Purpose::INDOOR_TEMP, Sensors::ValueType::PRIMARY);
if (value < minTemp) {
minTemp = value;
}
availableSensors++;
}
if (Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::HEATING_TEMP)) {
auto value = Sensors::getMeanValueByPurpose(Sensors::Purpose::HEATING_TEMP, Sensors::ValueType::PRIMARY);
if (value < minTemp) {
minTemp = value;
}
availableSensors++;
}
if (Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::HEATING_RETURN_TEMP)) {
auto value = Sensors::getMeanValueByPurpose(Sensors::Purpose::HEATING_RETURN_TEMP, Sensors::ValueType::PRIMARY);
if (value < minTemp) {
minTemp = value;
}
availableSensors++;
}
if (availableSensors && minTemp <= settings.heating.antiFreezeTemp) {
settings.heating.enabled = true;
fsSettings.update();
Log.sinfoln(
FPSTR(L_MAIN),
F("Heating turned on by anti freeze protection, current min temp: %.2f, threshold: %hhu"),
minTemp, settings.heating.antiFreezeTemp
);
}
}
}
void emergency() {
// flags
uint8_t emergencyFlags = 0b00000000;

View File

@@ -169,7 +169,7 @@ protected:
// Heating settings
vars.master.heating.enabled = this->isReady()
&& (settings.heating.enabled || vars.emergency.state)
&& settings.heating.enabled
&& vars.cascadeControl.input
&& !vars.master.heating.blocking
&& !vars.master.heating.overheat;

View File

@@ -110,6 +110,7 @@ struct Settings {
uint8_t maxModulation = 100;
uint8_t overheatHighTemp = 95;
uint8_t overheatLowTemp = 90;
uint8_t antiFreezeTemp = 10;
} heating;
struct {

View File

@@ -44,6 +44,7 @@ const char S_APP_VERSION[] PROGMEM = "appVersion";
const char S_AUTH[] PROGMEM = "auth";
const char S_AUTO_DIAG_RESET[] PROGMEM = "autoDiagReset";
const char S_AUTO_FAULT_RESET[] PROGMEM = "autoFaultReset";
const char S_ANTI_FREEZE_TEMP[] PROGMEM = "antiFreezeTemp";
const char S_BACKTRACE[] PROGMEM = "backtrace";
const char S_BATTERY[] PROGMEM = "battery";
const char S_BAUDRATE[] PROGMEM = "baudrate";

View File

@@ -497,6 +497,7 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
heating[FPSTR(S_MAX_MODULATION)] = src.heating.maxModulation;
heating[FPSTR(S_OVERHEAT_HIGH_TEMP)] = src.heating.overheatHighTemp;
heating[FPSTR(S_OVERHEAT_LOW_TEMP)] = src.heating.overheatLowTemp;
heating[FPSTR(S_ANTI_FREEZE_TEMP)] = src.heating.antiFreezeTemp;
auto dhw = dst[FPSTR(S_DHW)].to<JsonObject>();
dhw[FPSTR(S_ENABLED)] = src.dhw.enabled;
@@ -1369,6 +1370,15 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
changed = true;
}
if (!src[FPSTR(S_HEATING)][FPSTR(S_ANTI_FREEZE_TEMP)].isNull()) {
unsigned short value = src[FPSTR(S_HEATING)][FPSTR(S_ANTI_FREEZE_TEMP)].as<unsigned short>();
if (isValidTemp(value, dst.system.unitSystem, 1, 30) && value != dst.heating.antiFreezeTemp) {
dst.heating.antiFreezeTemp = value;
changed = true;
}
}
// dhw
if (src[FPSTR(S_DHW)][FPSTR(S_ENABLED)].is<bool>()) {

View File

@@ -350,7 +350,11 @@
"heating": {
"hyst": "Hysteresis <small>(in degrees)</small>",
"turboFactor": "Turbo mode coeff."
"turboFactor": "Turbo mode coeff.",
"antiFreezeTemp": {
"title": "Freeze protection temperature",
"note": "If the heat carrier or indoor temperature drops below this value, the heating will be forced to turn on"
}
},
"emergency": {

View File

@@ -350,7 +350,11 @@
"heating": {
"hyst": "Isteresi <small>(in gradi)</small>",
"turboFactor": "Turbo mode coeff."
"turboFactor": "Turbo mode coeff.",
"antiFreezeTemp": {
"title": "Temperatura di protezione antigelo",
"note": "Se la temperatura del fluido termovettore o interna scende al di sotto di questo valore, il riscaldamento verrà forzato ad accendersi"
}
},
"emergency": {

View File

@@ -350,7 +350,11 @@
"heating": {
"hyst": "Гистерезис <small>(в градусах)</small>",
"turboFactor": "Коэфф. турбо режима"
"turboFactor": "Коэфф. турбо режима",
"antiFreezeTemp": {
"title": "Температура защиты от замерзания",
"note": "Отопление будет принудительно включено, если температура теплоносителя или внутренняя температура опустится ниже этого значения"
}
},
"emergency": {

View File

@@ -207,6 +207,12 @@
<input type="number" inputmode="numeric" name="heating[maxModulation]" min="1" max="100" step="1" required>
</label>
<label>
<span data-i18n>settings.heating.antiFreezeTemp.title</span>
<input type="number" inputmode="numeric" name="heating[antiFreezeTemp]" min="0" max="0" step="1" required>
<small data-i18n>settings.heating.antiFreezeTemp.note</small>
</label>
<details>
<summary><b data-i18n>settings.overheat.title</b></summary>
@@ -926,6 +932,10 @@
setInputValue("[name='heating[maxModulation]']", data.heating.maxModulation);
setInputValue("[name='heating[overheatHighTemp]']", data.heating.overheatHighTemp);
setInputValue("[name='heating[overheatLowTemp]']", data.heating.overheatLowTemp);
setInputValue("[name='heating[antiFreezeTemp]']", data.heating.antiFreezeTemp, {
"min": data.system.unitSystem == 0 ? 1 : 34,
"max": data.system.unitSystem == 0 ? 30 : 86
});
setBusy('#heating-settings-busy', '#heating-settings', false);
// DHW
@@ -943,11 +953,10 @@
setBusy('#dhw-settings-busy', '#dhw-settings', false);
// Emergency mode
setInputValue("[name='emergency[tresholdTime]']", data.emergency.tresholdTime);
if (data.opentherm.options.nativeHeatingControl) {
setInputValue("[name='emergency[target]']", data.emergency.target, {
"min": data.system.unitSystem == 0 ? 5 : 41,
"max": data.system.unitSystem == 0 ? 40 : 86
"max": data.system.unitSystem == 0 ? 40 : 104
});
} else {
@@ -956,7 +965,7 @@
"max": data.heating.maxTemp,
});
}
setInputValue("[name='emergency[tresholdTime]']", data.emergency.tresholdTime);
setBusy('#emergency-settings-busy', '#emergency-settings', false);
// Equitherm