mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-10 18:24:27 +05:00
refactor: reworked emergency mode; reworked hysteresis algorithm; improved detection of connection state for MANUAL & BOILER type sensors
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
195
src/utils.h
195
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<byte>(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>()) {
|
||||
bool value = src["emergency"]["enable"].as<bool>();
|
||||
|
||||
if (value != dst.emergency.enable) {
|
||||
dst.emergency.enable = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["emergency"]["tresholdTime"].isNull()) {
|
||||
unsigned short value = src["emergency"]["tresholdTime"].as<unsigned short>();
|
||||
|
||||
@@ -973,83 +955,49 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (src["emergency"]["useEquitherm"].is<bool>()) {
|
||||
bool value = src["emergency"]["useEquitherm"].as<bool>();
|
||||
|
||||
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>()) {
|
||||
bool value = src["equitherm"]["enable"].as<bool>();
|
||||
|
||||
} else if (dst.emergency.useEquitherm) {
|
||||
dst.emergency.useEquitherm = false;
|
||||
if (!dst.opentherm.nativeHeatingControl) {
|
||||
if (value != dst.equitherm.enable) {
|
||||
dst.equitherm.enable = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (dst.emergency.useEquitherm && dst.emergency.usePid) {
|
||||
dst.emergency.usePid = false;
|
||||
changed = true;
|
||||
}
|
||||
} else if (dst.equitherm.enable) {
|
||||
dst.equitherm.enable = false;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (src["emergency"]["usePid"].is<bool>()) {
|
||||
bool value = src["emergency"]["usePid"].as<bool>();
|
||||
if (!src["equitherm"]["n_factor"].isNull()) {
|
||||
float value = src["equitherm"]["n_factor"].as<float>();
|
||||
|
||||
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>()) {
|
||||
bool value = src["emergency"]["onNetworkFault"].as<bool>();
|
||||
if (!src["equitherm"]["k_factor"].isNull()) {
|
||||
float value = src["equitherm"]["k_factor"].as<float>();
|
||||
|
||||
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>()) {
|
||||
bool value = src["emergency"]["onMqttFault"].as<bool>();
|
||||
if (!src["equitherm"]["t_factor"].isNull()) {
|
||||
float value = src["equitherm"]["t_factor"].as<float>();
|
||||
|
||||
if (value != dst.emergency.onMqttFault) {
|
||||
dst.emergency.onMqttFault = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (src["emergency"]["onIndoorSensorDisconnect"].is<bool>()) {
|
||||
bool value = src["emergency"]["onIndoorSensorDisconnect"].as<bool>();
|
||||
|
||||
if (value != dst.emergency.onIndoorSensorDisconnect) {
|
||||
dst.emergency.onIndoorSensorDisconnect = value;
|
||||
dst.emergency.usePid = false;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (src["emergency"]["onOutdoorSensorDisconnect"].is<bool>()) {
|
||||
bool value = src["emergency"]["onOutdoorSensorDisconnect"].as<bool>();
|
||||
|
||||
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<unsigned char>();
|
||||
|
||||
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<unsigned char>();
|
||||
short value = src["pid"]["minTemp"].as<short>();
|
||||
|
||||
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<short>();
|
||||
|
||||
// equitherm
|
||||
if (src["equitherm"]["enable"].is<bool>()) {
|
||||
bool value = src["equitherm"]["enable"].as<bool>();
|
||||
|
||||
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<float>();
|
||||
|
||||
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<float>();
|
||||
|
||||
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<float>();
|
||||
|
||||
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<bool>()) {
|
||||
@@ -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<unsigned char>();
|
||||
|
||||
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<unsigned char>();
|
||||
|
||||
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<byte>(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<byte>(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<byte>(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<byte>(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<float>() : 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,
|
||||
|
||||
@@ -224,27 +224,13 @@
|
||||
},
|
||||
|
||||
"emergency": {
|
||||
"desc": "<b>!</b> Emergency mode can be useful <u>only</u> 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:<br />- if «Equitherm» is enabled and the outdoor temperature sensor is disconnected;<br />- if «PID» or OT option <i>«Native heating control»</i> is enabled and the indoor temperature sensor is disconnected.<br /><b>Note:</b> On network fault or MQTT fault, sensors with <i>«Manual via MQTT/API»</i> type will be in DISCONNECTED state.",
|
||||
|
||||
"target": {
|
||||
"title": "Target temperature",
|
||||
"note": "<u>Indoor temperature</u> if Equitherm or PID is <b>enabled</b><br /><u>Heat carrier temperature</u> if Equitherm and PID <b>is disabled</b>"
|
||||
"note": "<b>Important:</b> <u>Target indoor temperature</u> if OT option <i>«Native heating control»</i> is enabled.<br />In all other cases, the <u>target heat carrier temperature</u>."
|
||||
},
|
||||
"treshold": "Treshold time <small>(sec)</small>",
|
||||
|
||||
"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 <small>(requires at least an external (DS18B20) or boiler <u>outdoor</u> sensor)</small>",
|
||||
"pid": "PID <small>(requires at least an external (DS18B20) <u>indoor</u> sensor)</small>"
|
||||
}
|
||||
"treshold": "Treshold time <small>(sec)</small>"
|
||||
},
|
||||
|
||||
"equitherm": {
|
||||
@@ -260,7 +246,8 @@
|
||||
"p": "P factor",
|
||||
"i": "I factor",
|
||||
"d": "D factor",
|
||||
"dt": "DT <small>in seconds</small>"
|
||||
"dt": "DT <small>in seconds</small>",
|
||||
"noteMinMaxTemp": "<b>Important:</b> When using «Equitherm» and «PID» at the same time, the min and max temperatures limit the influence on the «Equitherm» result temperature.<br />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 <code>equitherm_result - 15</code> to <code>equitherm_result + 15</code>."
|
||||
},
|
||||
|
||||
"ot": {
|
||||
@@ -313,7 +300,7 @@
|
||||
|
||||
"nativeHeating": {
|
||||
"title": "Native heating control (boiler)",
|
||||
"note": "Works <u>ONLY</u> 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 <u>ONLY</u> 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."
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -224,11 +224,11 @@
|
||||
},
|
||||
|
||||
"emergency": {
|
||||
"desc": "<b>!</b> Аварийный режим может быть полезен <u>только</u> при использовании ПЗА и/или ПИД и при передачи наружной/внутренней температуры через MQTT/API/BLE. В этом режиме значения датчиков, передаваемые через MQTT/API/BLE, не используются.",
|
||||
"desc": "Аварийный режим активируется автоматически, если «PID» или «Equitherm» не могут рассчитать уставку теплоносителя:<br />- если «Equitherm» включен и датчик наружной температуры отключен;<br />- если включен «PID» или OT опция <i>«Передать управление отоплением котлу»</i> и датчик температуры в помещении отключен.<br /><b>Примечание:</b> При сбое сети или MQTT датчики с типом <i>«Вручную через MQTT/API»</i> будут находиться в состоянии ОТКЛЮЧЕН.",
|
||||
|
||||
"target": {
|
||||
"title": "Целевая температура",
|
||||
"note": "Целевая <u>внутренняя температура</u> если ПЗА и/или ПИД <b>включены</b><br />Целевая <u>температура теплоносителя</u> если ПЗА и ПИД <b>выключены</b>"
|
||||
"note": "<b>Важно:</b> <u>Целевая температура в помещении</u>, если включена ОТ опция <i>«Передать управление отоплением котлу»</i>.<br />Во всех остальных случаях <u>целевая температура теплоносителя</u>."
|
||||
},
|
||||
"treshold": "Пороговое время включения <small>(сек)</small>",
|
||||
|
||||
@@ -260,7 +260,8 @@
|
||||
"p": "Коэффициент P",
|
||||
"i": "Коэффициент I",
|
||||
"d": "Коэффициент D",
|
||||
"dt": "DT <small>(сек)</small>"
|
||||
"dt": "DT <small>(сек)</small>",
|
||||
"noteMinMaxTemp": "<b>Важно:</b> При использовании «Equitherm» и «PID» одновременно, мин. и макс. температура ограничивает влияние на расчётную температуру «Equitherm».<br />Таким образом, если мин. температура задана как -15, а макс. как 15, то конечная температура теплоносителя будет от <code>equitherm_result - 15</code> до <code>equitherm_result + 15</code>."
|
||||
},
|
||||
|
||||
"ot": {
|
||||
@@ -313,7 +314,7 @@
|
||||
|
||||
"nativeHeating": {
|
||||
"title": "Передать управление отоплением котлу",
|
||||
"note": "Работает <u>ТОЛЬКО</u> если котел требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД, ПЗА и гистерезисом."
|
||||
"note": "Работает <u>ТОЛЬКО</u> если котел требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД и ПЗА."
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -208,11 +208,6 @@
|
||||
<div id="emergency-settings-busy" aria-busy="true"></div>
|
||||
<form action="/api/settings" id="emergency-settings" class="hidden">
|
||||
<fieldset>
|
||||
<label for="emergency-enable">
|
||||
<input type="checkbox" id="emergency-enable" name="emergency[enable]" value="true">
|
||||
<span data-i18n>settings.enable</span>
|
||||
</label>
|
||||
|
||||
<small data-i18n>settings.emergency.desc</small>
|
||||
</fieldset>
|
||||
|
||||
@@ -229,43 +224,6 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend data-i18n>settings.emergency.events.desc</legend>
|
||||
|
||||
<label for="emergency-on-network-fault">
|
||||
<input type="checkbox" id="emergency-on-network-fault" name="emergency[onNetworkFault]" value="true">
|
||||
<span data-i18n>settings.emergency.events.network</span>
|
||||
</label>
|
||||
|
||||
<label for="emergency-on-mqtt-fault">
|
||||
<input type="checkbox" id="emergency-on-mqtt-fault" name="emergency[onMqttFault]" value="true">
|
||||
<span data-i18n>settings.emergency.events.mqtt</span>
|
||||
</label>
|
||||
|
||||
<label for="emergency-on-indoor-sensor-disconnect">
|
||||
<input type="checkbox" id="emergency-on-indoor-sensor-disconnect" name="emergency[onIndoorSensorDisconnect]" value="true">
|
||||
<span data-i18n>settings.emergency.events.indoorSensorDisconnect</span>
|
||||
</label>
|
||||
|
||||
<label for="emergency-on-outdoor-sensor-disconnect">
|
||||
<input type="checkbox" id="emergency-on-outdoor-sensor-disconnect" name="emergency[onOutdoorSensorDisconnect]" value="true">
|
||||
<span data-i18n>settings.emergency.events.outdoorSensorDisconnect</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend data-i18n>settings.emergency.regulators.desc</legend>
|
||||
|
||||
<label for="emergency-use-equitherm">
|
||||
<input type="checkbox" id="emergency-use-equitherm" name="emergency[useEquitherm]" value="true">
|
||||
<span data-i18n>settings.emergency.regulators.equitherm</span>
|
||||
</label>
|
||||
<label for="emergency-use-pid">
|
||||
<input type="checkbox" id="emergency-use-pid" name="emergency[usePid]" value="true">
|
||||
<span data-i18n>settings.emergency.regulators.pid</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<button type="submit" data-i18n>button.save</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -358,6 +316,8 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<small data-i18n>settings.pid.noteMinMaxTemp</small>
|
||||
|
||||
<button type="submit" data-i18n>button.save</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user