diff --git a/lib/Equitherm/Equitherm.h b/lib/Equitherm/Equitherm.h index c0dd13a..eba4f07 100644 --- a/lib/Equitherm/Equitherm.h +++ b/lib/Equitherm/Equitherm.h @@ -27,7 +27,7 @@ public: } // лимит выходной величины - void setLimits(int min_output, int max_output) { + void setLimits(unsigned short min_output, unsigned short max_output) { _minOut = min_output; _maxOut = max_output; } @@ -40,7 +40,7 @@ public: } private: - int _minOut = 20, _maxOut = 90; + unsigned short _minOut = 20, _maxOut = 90; // температура контура отопления в зависимости от наружной температуры datatype getResultN() { diff --git a/src/HaHelper.h b/src/HaHelper.h index 87678bd..3e1a549 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -565,12 +565,12 @@ public: if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); - doc[FPSTR(HA_MIN)] = 0; + doc[FPSTR(HA_MIN)] = -99; doc[FPSTR(HA_MAX)] = 99; } else if (unit == UnitSystem::IMPERIAL) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F); - doc[FPSTR(HA_MIN)] = 0; + doc[FPSTR(HA_MIN)] = -146; doc[FPSTR(HA_MAX)] = 211; } diff --git a/src/MainTask.h b/src/MainTask.h index 18585fa..9b69e24 100644 --- a/src/MainTask.h +++ b/src/MainTask.h @@ -105,6 +105,14 @@ protected: tMqtt->disable(); } + if (settings.sensors.indoor.type == SensorType::MANUAL) { + vars.sensors.indoor.connected = !settings.mqtt.enable || vars.states.mqtt; + } + + if (settings.sensors.outdoor.type == SensorType::MANUAL) { + vars.sensors.outdoor.connected = !settings.mqtt.enable || vars.states.mqtt; + } + } else { if (this->telnetStarted) { telnetStream->stop(); @@ -114,6 +122,14 @@ protected: if (tMqtt->isEnabled()) { tMqtt->disable(); } + + if (settings.sensors.indoor.type == SensorType::MANUAL) { + vars.sensors.indoor.connected = false; + } + + if (settings.sensors.outdoor.type == SensorType::MANUAL) { + vars.sensors.outdoor.connected = false; + } } this->yield(); @@ -189,42 +205,22 @@ protected: } void emergency() { - if (!settings.emergency.enable && vars.states.emergency) { - this->emergencyDetected = false; - vars.states.emergency = false; - - Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode disabled")); - } - - if (!settings.emergency.enable) { - return; - } - // flags uint8_t emergencyFlags = 0b00000000; - // set network flag - if (settings.emergency.onNetworkFault && !network->isConnected()) { + // set outdoor sensor flag + if (settings.equitherm.enable && !vars.sensors.outdoor.connected) { emergencyFlags |= 0b00000001; } - // set mqtt flag - if (settings.emergency.onMqttFault && (!tMqtt->isEnabled() || !tMqtt->isConnected())) { + // set indoor sensor flag + if (!settings.equitherm.enable && settings.pid.enable && !vars.sensors.indoor.connected) { emergencyFlags |= 0b00000010; } - // set outdoor sensor flag - if (settings.sensors.outdoor.type == SensorType::DS18B20 || settings.sensors.outdoor.type == SensorType::BLUETOOTH) { - if (settings.emergency.onOutdoorSensorDisconnect && !vars.sensors.outdoor.connected) { - emergencyFlags |= 0b00000100; - } - } - - // set indoor sensor flag - if (settings.sensors.indoor.type == SensorType::DS18B20 || settings.sensors.indoor.type == SensorType::BLUETOOTH) { - if (settings.emergency.onIndoorSensorDisconnect && !vars.sensors.indoor.connected) { - emergencyFlags |= 0b00001000; - } + // set indoor sensor flag for OT native heating control + if (settings.opentherm.nativeHeatingControl && !vars.sensors.indoor.connected) { + emergencyFlags |= 0b00000100; } // if any flags is true diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index ca8cc44..e9895c3 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -22,11 +22,11 @@ protected: bool isInitialized = false; unsigned long initializedTime = 0; unsigned int initializedMemberIdCode = 0; - bool pump = true; unsigned long lastSuccessResponse = 0; unsigned long prevUpdateNonEssentialVars = 0; unsigned long dhwSetTempTime = 0; unsigned long heatingSetTempTime = 0; + bool heatingBlocking = false; byte configuredRxLedGpio = GPIO_IS_NOT_CONFIGURED; #if defined(ARDUINO_ARCH_ESP32) @@ -136,7 +136,10 @@ protected: } } - bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && this->pump && vars.cascadeControl.input && this->isReady(); + bool heatingEnabled = (vars.states.emergency || settings.heating.enable) + && vars.cascadeControl.input + && this->isReady() + && !this->heatingBlocking; bool heatingCh2Enabled = settings.opentherm.heatingCh2Enabled; if (settings.opentherm.heatingCh1ToCh2) { heatingCh2Enabled = heatingEnabled; @@ -182,6 +185,10 @@ protected: } else if (vars.states.otStatus && millis() - this->lastSuccessResponse > 1150) { Log.swarningln(FPSTR(L_OT), F("Disconnected")); + if (settings.sensors.outdoor.type == SensorType::BOILER) { + vars.sensors.outdoor.connected = false; + } + vars.states.otStatus = false; this->isInitialized = false; } @@ -386,9 +393,17 @@ protected: // Get outdoor temp (if necessary) if (settings.sensors.outdoor.type == SensorType::BOILER) { if (this->updateOutdoorTemp()) { + if (!vars.sensors.outdoor.connected) { + vars.sensors.outdoor.connected = true; + } + Log.snoticeln(FPSTR(L_OT), F("Received outdoor temp: %.2f"), vars.temperatures.outdoor); } else { + if (vars.sensors.outdoor.connected) { + vars.sensors.outdoor.connected = false; + } + Log.swarningln(FPSTR(L_OT), F("Failed receive outdoor temp")); } } @@ -489,9 +504,17 @@ protected: // Get outdoor temp (if necessary) if (settings.sensors.outdoor.type == SensorType::BOILER) { if (this->updateOutdoorTemp()) { + if (!vars.sensors.outdoor.connected) { + vars.sensors.outdoor.connected = true; + } + Log.snoticeln(FPSTR(L_OT), F("Received outdoor temp: %.2f"), vars.temperatures.outdoor); } else { + if (vars.sensors.outdoor.connected) { + vars.sensors.outdoor.connected = false; + } + Log.swarningln(FPSTR(L_OT), F("Failed receive outdoor temp")); } } @@ -564,7 +587,7 @@ protected: float indoorTemp = 0.0f; float convertedTemp = 0.0f; - if (!vars.states.emergency || settings.sensors.indoor.type != SensorType::MANUAL) { + if (!vars.sensors.indoor.connected) { indoorTemp = vars.temperatures.indoor; convertedTemp = convertTemp(indoorTemp, settings.system.unitSystem, settings.opentherm.unitSystem); } @@ -595,11 +618,6 @@ protected: } } - // force enable pump - if (!this->pump) { - this->pump = true; - } - } else { // Update heating temp if (heatingEnabled && (this->needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001f)) { @@ -634,18 +652,22 @@ protected: // Hysteresis - // Only if enabled PID or/and Equitherm - if (settings.heating.hysteresis > 0 && (!vars.states.emergency || settings.emergency.usePid) && (settings.equitherm.enable || settings.pid.enable)) { - float halfHyst = settings.heating.hysteresis / 2; - if (this->pump && vars.temperatures.indoor - settings.heating.target + 0.0001f >= halfHyst) { - this->pump = false; + // Only if enabled PID or/and Equitherm or Native heating control via OT + bool useHyst = false; + if (settings.heating.hysteresis > 0.01f && vars.sensors.indoor.connected) { + useHyst = settings.equitherm.enable || settings.pid.enable || settings.opentherm.nativeHeatingControl; + } - } else if (!this->pump && vars.temperatures.indoor - settings.heating.target - 0.0001f <= -(halfHyst)) { - this->pump = true; + if (useHyst) { + if (!this->heatingBlocking && vars.temperatures.indoor - settings.heating.target + 0.0001f >= settings.heating.hysteresis) { + this->heatingBlocking = true; + + } else if (this->heatingBlocking && vars.temperatures.indoor - settings.heating.target - 0.0001f <= -(settings.heating.hysteresis)) { + this->heatingBlocking = false; } - } else if (!this->pump) { - this->pump = true; + } else if (this->heatingBlocking) { + this->heatingBlocking = false; } } } diff --git a/src/RegulatorTask.h b/src/RegulatorTask.h index 5b4d65d..9a8d005 100644 --- a/src/RegulatorTask.h +++ b/src/RegulatorTask.h @@ -29,27 +29,55 @@ protected: #endif void loop() { - float newTemp = vars.parameters.heatingSetpoint; - - if (vars.states.emergency) { - if (settings.heating.turbo) { - settings.heating.turbo = false; - - Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled")); - } - - newTemp = this->getEmergencyModeTemp(); - - } else { - if (settings.heating.turbo && (fabs(settings.heating.target - vars.temperatures.indoor) < 1 || !settings.heating.enable || (settings.equitherm.enable && settings.pid.enable))) { - settings.heating.turbo = false; - - Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled")); - } - - newTemp = this->getNormalModeTemp(); + bool pidIntergralResetted = false; + if (fabs(pidRegulator.Kp - settings.pid.p_factor) >= 0.0001f) { + pidRegulator.Kp = settings.pid.p_factor; + pidRegulator.integral = 0; + pidIntergralResetted = true; } + if (fabs(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) { + pidRegulator.Ki = settings.pid.i_factor; + pidRegulator.integral = 0; + pidIntergralResetted = true; + } + + if (fabs(pidRegulator.Kd - settings.pid.d_factor) >= 0.0001f) { + pidRegulator.Kd = settings.pid.d_factor; + pidRegulator.integral = 0; + pidIntergralResetted = true; + } + + if (!settings.pid.enable && fabs(pidRegulator.integral) > 0.01f) { + pidRegulator.integral = 0.0f; + pidIntergralResetted = true; + } + + if (pidIntergralResetted) { + Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset")); + } + + if (settings.heating.turbo) { + if (!settings.heating.enable || vars.states.emergency) { + settings.heating.turbo = false; + + } else if (settings.pid.enable) { + settings.heating.turbo = false; // not implemented + + } else if (fabs(settings.heating.target - vars.temperatures.indoor) < 1.0f) { + settings.heating.turbo = false; + } + + if (!settings.heating.turbo) { + Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled")); + } + } + + + float newTemp = vars.states.emergency + ? settings.emergency.target + : this->getNormalModeTemp(); + // Limits newTemp = constrain( newTemp, @@ -57,58 +85,12 @@ protected: !settings.opentherm.nativeHeatingControl ? settings.heating.maxTemp : THERMOSTAT_INDOOR_MAX_TEMP ); - if (fabs(vars.parameters.heatingSetpoint - newTemp) > 0.4999f) { + if (fabs(vars.parameters.heatingSetpoint - newTemp) > 0.09f) { vars.parameters.heatingSetpoint = newTemp; } } - float getEmergencyModeTemp() { - float newTemp = 0; - - // if use equitherm - if (settings.emergency.useEquitherm) { - float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp); - - if (fabs(prevEtResult - etResult) > 0.4999f) { - prevEtResult = etResult; - newTemp += etResult; - - Log.sinfoln(FPSTR(L_REGULATOR_EQUITHERM), F("New emergency result: %.2f"), etResult); - - } else { - newTemp += prevEtResult; - } - - } else if(settings.emergency.usePid) { - if (vars.parameters.heatingEnabled) { - float pidResult = getPidTemp( - settings.heating.minTemp, - settings.heating.maxTemp - ); - - if (fabs(prevPidResult - pidResult) > 0.4999f) { - prevPidResult = pidResult; - newTemp += pidResult; - - Log.sinfoln(FPSTR(L_REGULATOR_PID), F("New emergency result: %.2f"), pidResult); - - } else { - newTemp += prevPidResult; - } - - } else if (!vars.parameters.heatingEnabled && prevPidResult != 0) { - newTemp += prevPidResult; - } - - } else { - // default temp, manual mode - newTemp = settings.emergency.target; - } - - return newTemp; - } - float getNormalModeTemp() { float newTemp = 0; @@ -116,17 +98,49 @@ protected: prevHeatingTarget = settings.heating.target; Log.sinfoln(FPSTR(L_REGULATOR), F("New target: %.2f"), settings.heating.target); - if (/*settings.equitherm.enable && */settings.pid.enable) { + /*if (settings.pid.enable) { pidRegulator.integral = 0; Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset")); - } + }*/ } // if use equitherm if (settings.equitherm.enable) { - float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp); + unsigned short minTemp = settings.heating.minTemp; + unsigned short maxTemp = settings.heating.maxTemp; + float targetTemp = settings.heating.target; + float indoorTemp = vars.temperatures.indoor; + float outdoorTemp = vars.temperatures.outdoor; - if (fabs(prevEtResult - etResult) > 0.4999f) { + if (settings.system.unitSystem == UnitSystem::IMPERIAL) { + minTemp = f2c(minTemp); + maxTemp = f2c(maxTemp); + targetTemp = f2c(targetTemp); + indoorTemp = f2c(indoorTemp); + outdoorTemp = f2c(outdoorTemp); + } + + if (!vars.sensors.indoor.connected || settings.pid.enable) { + etRegulator.Kt = 0; + etRegulator.indoorTemp = 0; + + } else { + etRegulator.Kt = settings.heating.turbo ? 10.0f : settings.equitherm.t_factor; + etRegulator.indoorTemp = indoorTemp; + } + + etRegulator.setLimits(minTemp, maxTemp); + etRegulator.Kn = settings.equitherm.n_factor; + etRegulator.Kk = settings.equitherm.k_factor; + etRegulator.targetTemp = targetTemp; + etRegulator.outdoorTemp = outdoorTemp; + float etResult = etRegulator.getResult(); + + if (settings.system.unitSystem == UnitSystem::IMPERIAL) { + etResult = c2f(etResult); + } + + if (fabs(prevEtResult - etResult) > 0.09f) { prevEtResult = etResult; newTemp += etResult; @@ -140,13 +154,14 @@ protected: // if use pid if (settings.pid.enable) { //if (vars.parameters.heatingEnabled) { - if (settings.heating.enable) { - float pidResult = getPidTemp( - settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp, - settings.pid.maxTemp - ); + if (settings.heating.enable && vars.sensors.indoor.connected) { + pidRegulator.setLimits(settings.pid.minTemp, settings.pid.maxTemp); + pidRegulator.setDt(settings.pid.dt * 1000u); + pidRegulator.input = vars.temperatures.indoor; + pidRegulator.setpoint = settings.heating.target; - if (fabs(prevPidResult - pidResult) > 0.4999f) { + float pidResult = pidRegulator.getResultTimer(); + if (fabs(prevPidResult - pidResult) > 0.09f) { prevPidResult = pidResult; newTemp += pidResult; @@ -156,13 +171,10 @@ protected: } else { newTemp += prevPidResult; } + } else { newTemp += prevPidResult; } - - } else if (fabs(pidRegulator.integral) > 0.0001f) { - pidRegulator.integral = 0; - Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset")); } // default temp, manual mode @@ -172,96 +184,4 @@ protected: return newTemp; } - - /** - * @brief Get the Equitherm Temp - * Calculations in degrees C, conversion occurs when using F - * - * @param minTemp - * @param maxTemp - * @return float - */ - float getEquithermTemp(int minTemp, int maxTemp) { - float targetTemp = vars.states.emergency ? settings.emergency.target : settings.heating.target; - float indoorTemp = vars.temperatures.indoor; - float outdoorTemp = vars.temperatures.outdoor; - - if (settings.system.unitSystem == UnitSystem::IMPERIAL) { - minTemp = f2c(minTemp); - maxTemp = f2c(maxTemp); - targetTemp = f2c(targetTemp); - indoorTemp = f2c(indoorTemp); - outdoorTemp = f2c(outdoorTemp); - } - - if (vars.states.emergency) { - if (settings.sensors.indoor.type == SensorType::MANUAL) { - etRegulator.Kt = 0; - etRegulator.indoorTemp = 0; - - } else if ((settings.sensors.indoor.type == SensorType::DS18B20 || settings.sensors.indoor.type == SensorType::BLUETOOTH) && !vars.sensors.indoor.connected) { - etRegulator.Kt = 0; - etRegulator.indoorTemp = 0; - - } else { - etRegulator.Kt = settings.equitherm.t_factor; - etRegulator.indoorTemp = indoorTemp; - } - - etRegulator.outdoorTemp = outdoorTemp; - - } else if (settings.pid.enable) { - etRegulator.Kt = 0; - etRegulator.indoorTemp = round(indoorTemp); - etRegulator.outdoorTemp = round(outdoorTemp); - - } else { - if (settings.heating.turbo) { - etRegulator.Kt = 10; - } else { - etRegulator.Kt = settings.equitherm.t_factor; - } - etRegulator.indoorTemp = indoorTemp; - etRegulator.outdoorTemp = outdoorTemp; - } - - etRegulator.setLimits(minTemp, maxTemp); - etRegulator.Kn = settings.equitherm.n_factor; - etRegulator.Kk = settings.equitherm.k_factor; - etRegulator.targetTemp = targetTemp; - float result = etRegulator.getResult(); - - if (settings.system.unitSystem == UnitSystem::IMPERIAL) { - result = c2f(result); - } - - return result; - } - - float getPidTemp(int minTemp, int maxTemp) { - if (fabs(pidRegulator.Kp - settings.pid.p_factor) >= 0.0001f) { - pidRegulator.Kp = settings.pid.p_factor; - pidRegulator.integral = 0; - Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset")); - } - - if (fabs(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) { - pidRegulator.Ki = settings.pid.i_factor; - pidRegulator.integral = 0; - Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset")); - } - - if (fabs(pidRegulator.Kd - settings.pid.d_factor) >= 0.0001f) { - pidRegulator.Kd = settings.pid.d_factor; - pidRegulator.integral = 0; - Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset")); - } - - pidRegulator.setLimits(minTemp, maxTemp); - pidRegulator.setDt(settings.pid.dt * 1000u); - pidRegulator.input = vars.temperatures.indoor; - pidRegulator.setpoint = vars.states.emergency ? settings.emergency.target : settings.heating.target; - - return pidRegulator.getResultTimer(); - } }; diff --git a/src/Settings.h b/src/Settings.h index bd717cd..f81e5d0 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -86,15 +86,8 @@ struct Settings { } mqtt; struct { - bool enable = false; float target = DEFAULT_HEATING_TARGET_TEMP; unsigned short tresholdTime = 120; - bool useEquitherm = false; - bool usePid = false; - bool onNetworkFault = true; - bool onMqttFault = true; - bool onIndoorSensorDisconnect = false; - bool onOutdoorSensorDisconnect = false; } emergency; struct { @@ -119,8 +112,8 @@ struct Settings { float i_factor = 0.0055f; float d_factor = 0; unsigned short dt = 180; - byte minTemp = 0; - byte maxTemp = DEFAULT_HEATING_MAX_TEMP; + short minTemp = 0; + short maxTemp = DEFAULT_HEATING_MAX_TEMP; } pid; struct { diff --git a/src/utils.h b/src/utils.h index 8f4d2d5..6e268b5 100644 --- a/src/utils.h +++ b/src/utils.h @@ -386,15 +386,8 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { dst["mqtt"]["interval"] = src.mqtt.interval; dst["mqtt"]["homeAssistantDiscovery"] = src.mqtt.homeAssistantDiscovery; - dst["emergency"]["enable"] = src.emergency.enable; dst["emergency"]["target"] = roundd(src.emergency.target, 2); dst["emergency"]["tresholdTime"] = src.emergency.tresholdTime; - dst["emergency"]["useEquitherm"] = src.emergency.useEquitherm; - dst["emergency"]["usePid"] = src.emergency.usePid; - dst["emergency"]["onNetworkFault"] = src.emergency.onNetworkFault; - dst["emergency"]["onMqttFault"] = src.emergency.onMqttFault; - dst["emergency"]["onIndoorSensorDisconnect"] = src.emergency.onIndoorSensorDisconnect; - dst["emergency"]["onOutdoorSensorDisconnect"] = src.emergency.onOutdoorSensorDisconnect; } dst["heating"]["enable"] = src.heating.enable; @@ -409,6 +402,11 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { dst["dhw"]["minTemp"] = src.dhw.minTemp; dst["dhw"]["maxTemp"] = src.dhw.maxTemp; + dst["equitherm"]["enable"] = src.equitherm.enable; + dst["equitherm"]["n_factor"] = roundd(src.equitherm.n_factor, 3); + dst["equitherm"]["k_factor"] = roundd(src.equitherm.k_factor, 3); + dst["equitherm"]["t_factor"] = roundd(src.equitherm.t_factor, 3); + dst["pid"]["enable"] = src.pid.enable; dst["pid"]["p_factor"] = roundd(src.pid.p_factor, 3); dst["pid"]["i_factor"] = roundd(src.pid.i_factor, 4); @@ -417,11 +415,6 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { dst["pid"]["minTemp"] = src.pid.minTemp; dst["pid"]["maxTemp"] = src.pid.maxTemp; - dst["equitherm"]["enable"] = src.equitherm.enable; - dst["equitherm"]["n_factor"] = roundd(src.equitherm.n_factor, 3); - dst["equitherm"]["k_factor"] = roundd(src.equitherm.k_factor, 3); - dst["equitherm"]["t_factor"] = roundd(src.equitherm.t_factor, 3); - dst["sensors"]["outdoor"]["type"] = static_cast(src.sensors.outdoor.type); dst["sensors"]["outdoor"]["gpio"] = src.sensors.outdoor.gpio; @@ -861,8 +854,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false dst.opentherm.nativeHeatingControl = value; if (value) { - dst.emergency.useEquitherm = false; - dst.emergency.usePid = false; dst.equitherm.enable = false; dst.pid.enable = false; } @@ -956,15 +947,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false // emergency - if (src["emergency"]["enable"].is()) { - bool value = src["emergency"]["enable"].as(); - - if (value != dst.emergency.enable) { - dst.emergency.enable = value; - changed = true; - } - } - if (!src["emergency"]["tresholdTime"].isNull()) { unsigned short value = src["emergency"]["tresholdTime"].as(); @@ -973,83 +955,49 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false changed = true; } } + } - if (src["emergency"]["useEquitherm"].is()) { - bool value = src["emergency"]["useEquitherm"].as(); - if (!dst.opentherm.nativeHeatingControl && dst.sensors.outdoor.type != SensorType::MANUAL && dst.sensors.outdoor.type != SensorType::BLUETOOTH) { - if (value != dst.emergency.useEquitherm) { - dst.emergency.useEquitherm = value; - changed = true; - } + // equitherm + if (src["equitherm"]["enable"].is()) { + bool value = src["equitherm"]["enable"].as(); - } else if (dst.emergency.useEquitherm) { - dst.emergency.useEquitherm = false; - changed = true; - } - - if (dst.emergency.useEquitherm && dst.emergency.usePid) { - dst.emergency.usePid = false; + if (!dst.opentherm.nativeHeatingControl) { + if (value != dst.equitherm.enable) { + dst.equitherm.enable = value; changed = true; } + + } else if (dst.equitherm.enable) { + dst.equitherm.enable = false; + changed = true; } + } - if (src["emergency"]["usePid"].is()) { - bool value = src["emergency"]["usePid"].as(); + if (!src["equitherm"]["n_factor"].isNull()) { + float value = src["equitherm"]["n_factor"].as(); - if (!dst.opentherm.nativeHeatingControl && dst.sensors.indoor.type != SensorType::MANUAL && dst.sensors.indoor.type != SensorType::BLUETOOTH) { - if (value != dst.emergency.usePid) { - dst.emergency.usePid = value; - changed = true; - } - - } else if (dst.emergency.usePid) { - dst.emergency.usePid = false; - changed = true; - } - - if (dst.emergency.usePid && dst.emergency.useEquitherm) { - dst.emergency.useEquitherm = false; - changed = true; - } + if (value > 0 && value <= 10 && fabs(value - dst.equitherm.n_factor) > 0.0001f) { + dst.equitherm.n_factor = roundd(value, 3); + changed = true; } + } - if (src["emergency"]["onNetworkFault"].is()) { - bool value = src["emergency"]["onNetworkFault"].as(); + if (!src["equitherm"]["k_factor"].isNull()) { + float value = src["equitherm"]["k_factor"].as(); - if (value != dst.emergency.onNetworkFault) { - dst.emergency.onNetworkFault = value; - changed = true; - } + if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.k_factor) > 0.0001f) { + dst.equitherm.k_factor = roundd(value, 3); + changed = true; } + } - if (src["emergency"]["onMqttFault"].is()) { - bool value = src["emergency"]["onMqttFault"].as(); + if (!src["equitherm"]["t_factor"].isNull()) { + float value = src["equitherm"]["t_factor"].as(); - if (value != dst.emergency.onMqttFault) { - dst.emergency.onMqttFault = value; - changed = true; - } - } - - if (src["emergency"]["onIndoorSensorDisconnect"].is()) { - bool value = src["emergency"]["onIndoorSensorDisconnect"].as(); - - if (value != dst.emergency.onIndoorSensorDisconnect) { - dst.emergency.onIndoorSensorDisconnect = value; - dst.emergency.usePid = false; - changed = true; - } - } - - if (src["emergency"]["onOutdoorSensorDisconnect"].is()) { - bool value = src["emergency"]["onOutdoorSensorDisconnect"].as(); - - if (value != dst.emergency.onOutdoorSensorDisconnect) { - dst.emergency.onOutdoorSensorDisconnect = value; - dst.emergency.useEquitherm = false; - changed = true; - } + if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.t_factor) > 0.0001f) { + dst.equitherm.t_factor = roundd(value, 3); + changed = true; } } @@ -1106,66 +1054,27 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } } - if (!src["pid"]["maxTemp"].isNull()) { - unsigned char value = src["pid"]["maxTemp"].as(); - - if (isValidTemp(value, dst.system.unitSystem) && value > dst.pid.minTemp && value != dst.pid.maxTemp) { - dst.pid.maxTemp = value; - changed = true; - } - } - if (!src["pid"]["minTemp"].isNull()) { - unsigned char value = src["pid"]["minTemp"].as(); + short value = src["pid"]["minTemp"].as(); - if (isValidTemp(value, dst.system.unitSystem) && value < dst.pid.maxTemp && value != dst.pid.minTemp) { + if (isValidTemp(value, dst.system.unitSystem, dst.equitherm.enable ? -99.9f : 0.0f) && value != dst.pid.minTemp) { dst.pid.minTemp = value; changed = true; } } + if (!src["pid"]["maxTemp"].isNull()) { + short value = src["pid"]["maxTemp"].as(); - // equitherm - if (src["equitherm"]["enable"].is()) { - bool value = src["equitherm"]["enable"].as(); - - if (!dst.opentherm.nativeHeatingControl) { - if (value != dst.equitherm.enable) { - dst.equitherm.enable = value; - changed = true; - } - - } else if (dst.equitherm.enable) { - dst.equitherm.enable = false; + if (isValidTemp(value, dst.system.unitSystem) && value != dst.pid.maxTemp) { + dst.pid.maxTemp = value; changed = true; } } - if (!src["equitherm"]["n_factor"].isNull()) { - float value = src["equitherm"]["n_factor"].as(); - - if (value > 0 && value <= 10 && fabs(value - dst.equitherm.n_factor) > 0.0001f) { - dst.equitherm.n_factor = roundd(value, 3); - changed = true; - } - } - - if (!src["equitherm"]["k_factor"].isNull()) { - float value = src["equitherm"]["k_factor"].as(); - - if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.k_factor) > 0.0001f) { - dst.equitherm.k_factor = roundd(value, 3); - changed = true; - } - } - - if (!src["equitherm"]["t_factor"].isNull()) { - float value = src["equitherm"]["t_factor"].as(); - - if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.t_factor) > 0.0001f) { - dst.equitherm.t_factor = roundd(value, 3); - changed = true; - } + if (dst.pid.maxTemp < dst.pid.minTemp) { + dst.pid.maxTemp = dst.pid.minTemp; + changed = true; } @@ -1215,6 +1124,11 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } } + if (dst.heating.maxTemp < dst.heating.minTemp) { + dst.heating.maxTemp = dst.heating.minTemp; + changed = true; + } + // dhw if (src["dhw"]["enable"].is()) { @@ -1229,7 +1143,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false if (!src["dhw"]["minTemp"].isNull()) { unsigned char value = src["dhw"]["minTemp"].as(); - if (value >= vars.parameters.dhwMinTemp && value < vars.parameters.dhwMaxTemp && value != dst.dhw.minTemp) { + if (value >= vars.parameters.dhwMinTemp && value != dst.dhw.minTemp) { dst.dhw.minTemp = value; changed = true; } @@ -1238,12 +1152,16 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false if (!src["dhw"]["maxTemp"].isNull()) { unsigned char value = src["dhw"]["maxTemp"].as(); - if (value > vars.parameters.dhwMinTemp && value <= vars.parameters.dhwMaxTemp && value != dst.dhw.maxTemp) { + if (value > vars.parameters.dhwMinTemp && value != dst.dhw.maxTemp) { dst.dhw.maxTemp = value; changed = true; } } + if (dst.dhw.maxTemp < dst.dhw.minTemp) { + dst.dhw.maxTemp = dst.dhw.minTemp; + changed = true; + } // sensors if (!src["sensors"]["outdoor"]["type"].isNull()) { @@ -1260,7 +1178,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false case static_cast(SensorType::MANUAL): if (dst.sensors.outdoor.type != SensorType::MANUAL) { dst.sensors.outdoor.type = SensorType::MANUAL; - dst.emergency.useEquitherm = false; changed = true; } break; @@ -1276,7 +1193,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false case static_cast(SensorType::BLUETOOTH): if (dst.sensors.outdoor.type != SensorType::BLUETOOTH) { dst.sensors.outdoor.type = SensorType::BLUETOOTH; - dst.emergency.useEquitherm = false; changed = true; } break; @@ -1335,8 +1251,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false case static_cast(SensorType::MANUAL): if (dst.sensors.indoor.type != SensorType::MANUAL) { dst.sensors.indoor.type = SensorType::MANUAL; - dst.emergency.usePid = false; - dst.opentherm.nativeHeatingControl = false; changed = true; } break; @@ -1352,7 +1266,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false case static_cast(SensorType::BLUETOOTH): if (dst.sensors.indoor.type != SensorType::BLUETOOTH) { dst.sensors.indoor.type = SensorType::BLUETOOTH; - dst.emergency.usePid = false; changed = true; } break; @@ -1597,7 +1510,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false // force check emergency target { float value = !src["emergency"]["target"].isNull() ? src["emergency"]["target"].as() : dst.emergency.target; - bool noRegulators = !dst.opentherm.nativeHeatingControl && !dst.emergency.useEquitherm && !dst.emergency.usePid; + bool noRegulators = !dst.opentherm.nativeHeatingControl; bool valid = isValidTemp( value, dst.system.unitSystem, diff --git a/src_data/locales/en.json b/src_data/locales/en.json index c2805d3..207da8f 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -224,27 +224,13 @@ }, "emergency": { - "desc": "! Emergency mode can be useful only when using Equitherm and/or PID (when normal work) and when reporting indoor/outdoor temperature via MQTT/API/BLE. In this mode, sensor values that are reported via MQTT/API/BLE are not used.", + "desc": "Emergency mode is activated automatically when «PID» or «Equitherm» cannot calculate the heat carrier setpoint:
- if «Equitherm» is enabled and the outdoor temperature sensor is disconnected;
- if «PID» or OT option «Native heating control» is enabled and the indoor temperature sensor is disconnected.
Note: On network fault or MQTT fault, sensors with «Manual via MQTT/API» type will be in DISCONNECTED state.", "target": { "title": "Target temperature", - "note": "Indoor temperature if Equitherm or PID is enabled
Heat carrier temperature if Equitherm and PID is disabled" + "note": "Important: Target indoor temperature if OT option «Native heating control» is enabled.
In all other cases, the target heat carrier temperature." }, - "treshold": "Treshold time (sec)", - - "events": { - "desc": "Events", - "network": "On network fault", - "mqtt": "On MQTT fault", - "indoorSensorDisconnect": "On loss connection with indoor sensor", - "outdoorSensorDisconnect": "On loss connection with outdoor sensor" - }, - - "regulators": { - "desc": "Using regulators", - "equitherm": "Equitherm (requires at least an external (DS18B20) or boiler outdoor sensor)", - "pid": "PID (requires at least an external (DS18B20) indoor sensor)" - } + "treshold": "Treshold time (sec)" }, "equitherm": { @@ -260,7 +246,8 @@ "p": "P factor", "i": "I factor", "d": "D factor", - "dt": "DT in seconds" + "dt": "DT in seconds", + "noteMinMaxTemp": "Important: When using «Equitherm» and «PID» at the same time, the min and max temperatures limit the influence on the «Equitherm» result temperature.
Thus, if the min temperature is set to -15 and the max temperature is set to 15, then the final heat carrier setpoint will be from equitherm_result - 15 to equitherm_result + 15." }, "ot": { @@ -313,7 +300,7 @@ "nativeHeating": { "title": "Native heating control (boiler)", - "note": "Works ONLY if the boiler requires the desired room temperature and regulates the temperature of the coolant itself. Not compatible with PID and Equitherm regulators and hysteresis in firmware." + "note": "Works ONLY if the boiler requires the desired room temperature and regulates the temperature of the coolant itself. Not compatible with PID and Equitherm regulators in firmware." } }, diff --git a/src_data/locales/ru.json b/src_data/locales/ru.json index ca21cb5..1e85649 100644 --- a/src_data/locales/ru.json +++ b/src_data/locales/ru.json @@ -224,11 +224,11 @@ }, "emergency": { - "desc": "! Аварийный режим может быть полезен только при использовании ПЗА и/или ПИД и при передачи наружной/внутренней температуры через MQTT/API/BLE. В этом режиме значения датчиков, передаваемые через MQTT/API/BLE, не используются.", + "desc": "Аварийный режим активируется автоматически, если «PID» или «Equitherm» не могут рассчитать уставку теплоносителя:
- если «Equitherm» включен и датчик наружной температуры отключен;
- если включен «PID» или OT опция «Передать управление отоплением котлу» и датчик температуры в помещении отключен.
Примечание: При сбое сети или MQTT датчики с типом «Вручную через MQTT/API» будут находиться в состоянии ОТКЛЮЧЕН.", "target": { "title": "Целевая температура", - "note": "Целевая внутренняя температура если ПЗА и/или ПИД включены
Целевая температура теплоносителя если ПЗА и ПИД выключены" + "note": "Важно: Целевая температура в помещении, если включена ОТ опция «Передать управление отоплением котлу».
Во всех остальных случаях целевая температура теплоносителя." }, "treshold": "Пороговое время включения (сек)", @@ -260,7 +260,8 @@ "p": "Коэффициент P", "i": "Коэффициент I", "d": "Коэффициент D", - "dt": "DT (сек)" + "dt": "DT (сек)", + "noteMinMaxTemp": "Важно: При использовании «Equitherm» и «PID» одновременно, мин. и макс. температура ограничивает влияние на расчётную температуру «Equitherm».
Таким образом, если мин. температура задана как -15, а макс. как 15, то конечная температура теплоносителя будет от equitherm_result - 15 до equitherm_result + 15." }, "ot": { @@ -313,7 +314,7 @@ "nativeHeating": { "title": "Передать управление отоплением котлу", - "note": "Работает ТОЛЬКО если котел требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД, ПЗА и гистерезисом." + "note": "Работает ТОЛЬКО если котел требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД и ПЗА." } }, diff --git a/src_data/pages/settings.html b/src_data/pages/settings.html index c47ddc9..492ec01 100644 --- a/src_data/pages/settings.html +++ b/src_data/pages/settings.html @@ -208,11 +208,6 @@
@@ -358,6 +316,8 @@ + settings.pid.noteMinMaxTemp + @@ -958,18 +918,20 @@ setBusy('#dhw-settings-busy', '#dhw-settings', false); // Emergency mode - setCheckboxValue('#emergency-enable', data.emergency.enable); setInputValue('#emergency-treshold-time', data.emergency.tresholdTime); - setCheckboxValue('#emergency-use-equitherm', data.emergency.useEquitherm); - setCheckboxValue('#emergency-use-pid', data.emergency.usePid); - setCheckboxValue('#emergency-on-network-fault', data.emergency.onNetworkFault); - setCheckboxValue('#emergency-on-mqtt-fault', data.emergency.onMqttFault); - setCheckboxValue('#emergency-on-indoor-sensor-disconnect', data.emergency.onIndoorSensorDisconnect); - setCheckboxValue('#emergency-on-outdoor-sensor-disconnect', data.emergency.onOutdoorSensorDisconnect); - setInputValue('#emergency-target', data.emergency.target, { - "min": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.minTemp : 10, - "max": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.maxTemp : 30, - }); + if (data.opentherm.nativeHeatingControl) { + setInputValue('#emergency-target', data.emergency.target, { + "min": data.system.unitSystem == 0 ? 5 : 41, + "max": data.system.unitSystem == 0 ? 30 : 86 + }); + + } else { + setInputValue('#emergency-target', data.emergency.target, { + "min": data.heating.minTemp, + "max": data.heating.maxTemp, + }); + } + setBusy('#emergency-settings-busy', '#emergency-settings', false); // Equitherm @@ -986,12 +948,12 @@ setInputValue('#pid-d-factor', data.pid.d_factor); setInputValue('#pid-dt', data.pid.dt); setInputValue('#pid-min-temp', data.pid.minTemp, { - "min": 0, - "max": data.system.unitSystem == 0 ? 99 : 211 + "min": data.equitherm.enable ? (data.system.unitSystem == 0 ? -100 : -146) : (data.system.unitSystem == 0 ? 0 : 32), + "max": (data.system.unitSystem == 0 ? 99 : 211) }); setInputValue('#pid-max-temp', data.pid.maxTemp, { - "min": 1, - "max": data.system.unitSystem == 0 ? 100 : 212 + "min": (data.system.unitSystem == 0 ? 0 : 33), + "max": (data.system.unitSystem == 0 ? 100 : 212) }); setBusy('#pid-settings-busy', '#pid-settings', false); };