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;
|
_minOut = min_output;
|
||||||
_maxOut = max_output;
|
_maxOut = max_output;
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _minOut = 20, _maxOut = 90;
|
unsigned short _minOut = 20, _maxOut = 90;
|
||||||
|
|
||||||
// температура контура отопления в зависимости от наружной температуры
|
// температура контура отопления в зависимости от наружной температуры
|
||||||
datatype getResultN() {
|
datatype getResultN() {
|
||||||
|
|||||||
@@ -565,12 +565,12 @@ public:
|
|||||||
|
|
||||||
if (unit == UnitSystem::METRIC) {
|
if (unit == UnitSystem::METRIC) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
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;
|
doc[FPSTR(HA_MAX)] = 99;
|
||||||
|
|
||||||
} else if (unit == UnitSystem::IMPERIAL) {
|
} else if (unit == UnitSystem::IMPERIAL) {
|
||||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
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;
|
doc[FPSTR(HA_MAX)] = 211;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,14 @@ protected:
|
|||||||
tMqtt->disable();
|
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 {
|
} else {
|
||||||
if (this->telnetStarted) {
|
if (this->telnetStarted) {
|
||||||
telnetStream->stop();
|
telnetStream->stop();
|
||||||
@@ -114,6 +122,14 @@ protected:
|
|||||||
if (tMqtt->isEnabled()) {
|
if (tMqtt->isEnabled()) {
|
||||||
tMqtt->disable();
|
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();
|
this->yield();
|
||||||
|
|
||||||
@@ -189,42 +205,22 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void emergency() {
|
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
|
// flags
|
||||||
uint8_t emergencyFlags = 0b00000000;
|
uint8_t emergencyFlags = 0b00000000;
|
||||||
|
|
||||||
// set network flag
|
// set outdoor sensor flag
|
||||||
if (settings.emergency.onNetworkFault && !network->isConnected()) {
|
if (settings.equitherm.enable && !vars.sensors.outdoor.connected) {
|
||||||
emergencyFlags |= 0b00000001;
|
emergencyFlags |= 0b00000001;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set mqtt flag
|
// set indoor sensor flag
|
||||||
if (settings.emergency.onMqttFault && (!tMqtt->isEnabled() || !tMqtt->isConnected())) {
|
if (!settings.equitherm.enable && settings.pid.enable && !vars.sensors.indoor.connected) {
|
||||||
emergencyFlags |= 0b00000010;
|
emergencyFlags |= 0b00000010;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set outdoor sensor flag
|
// set indoor sensor flag for OT native heating control
|
||||||
if (settings.sensors.outdoor.type == SensorType::DS18B20 || settings.sensors.outdoor.type == SensorType::BLUETOOTH) {
|
if (settings.opentherm.nativeHeatingControl && !vars.sensors.indoor.connected) {
|
||||||
if (settings.emergency.onOutdoorSensorDisconnect && !vars.sensors.outdoor.connected) {
|
emergencyFlags |= 0b00000100;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if any flags is true
|
// if any flags is true
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ protected:
|
|||||||
bool isInitialized = false;
|
bool isInitialized = false;
|
||||||
unsigned long initializedTime = 0;
|
unsigned long initializedTime = 0;
|
||||||
unsigned int initializedMemberIdCode = 0;
|
unsigned int initializedMemberIdCode = 0;
|
||||||
bool pump = true;
|
|
||||||
unsigned long lastSuccessResponse = 0;
|
unsigned long lastSuccessResponse = 0;
|
||||||
unsigned long prevUpdateNonEssentialVars = 0;
|
unsigned long prevUpdateNonEssentialVars = 0;
|
||||||
unsigned long dhwSetTempTime = 0;
|
unsigned long dhwSetTempTime = 0;
|
||||||
unsigned long heatingSetTempTime = 0;
|
unsigned long heatingSetTempTime = 0;
|
||||||
|
bool heatingBlocking = false;
|
||||||
byte configuredRxLedGpio = GPIO_IS_NOT_CONFIGURED;
|
byte configuredRxLedGpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#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;
|
bool heatingCh2Enabled = settings.opentherm.heatingCh2Enabled;
|
||||||
if (settings.opentherm.heatingCh1ToCh2) {
|
if (settings.opentherm.heatingCh1ToCh2) {
|
||||||
heatingCh2Enabled = heatingEnabled;
|
heatingCh2Enabled = heatingEnabled;
|
||||||
@@ -182,6 +185,10 @@ protected:
|
|||||||
} else if (vars.states.otStatus && millis() - this->lastSuccessResponse > 1150) {
|
} else if (vars.states.otStatus && millis() - this->lastSuccessResponse > 1150) {
|
||||||
Log.swarningln(FPSTR(L_OT), F("Disconnected"));
|
Log.swarningln(FPSTR(L_OT), F("Disconnected"));
|
||||||
|
|
||||||
|
if (settings.sensors.outdoor.type == SensorType::BOILER) {
|
||||||
|
vars.sensors.outdoor.connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
vars.states.otStatus = false;
|
vars.states.otStatus = false;
|
||||||
this->isInitialized = false;
|
this->isInitialized = false;
|
||||||
}
|
}
|
||||||
@@ -386,9 +393,17 @@ protected:
|
|||||||
// Get outdoor temp (if necessary)
|
// Get outdoor temp (if necessary)
|
||||||
if (settings.sensors.outdoor.type == SensorType::BOILER) {
|
if (settings.sensors.outdoor.type == SensorType::BOILER) {
|
||||||
if (this->updateOutdoorTemp()) {
|
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);
|
Log.snoticeln(FPSTR(L_OT), F("Received outdoor temp: %.2f"), vars.temperatures.outdoor);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (vars.sensors.outdoor.connected) {
|
||||||
|
vars.sensors.outdoor.connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
Log.swarningln(FPSTR(L_OT), F("Failed receive outdoor temp"));
|
Log.swarningln(FPSTR(L_OT), F("Failed receive outdoor temp"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -489,9 +504,17 @@ protected:
|
|||||||
// Get outdoor temp (if necessary)
|
// Get outdoor temp (if necessary)
|
||||||
if (settings.sensors.outdoor.type == SensorType::BOILER) {
|
if (settings.sensors.outdoor.type == SensorType::BOILER) {
|
||||||
if (this->updateOutdoorTemp()) {
|
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);
|
Log.snoticeln(FPSTR(L_OT), F("Received outdoor temp: %.2f"), vars.temperatures.outdoor);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (vars.sensors.outdoor.connected) {
|
||||||
|
vars.sensors.outdoor.connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
Log.swarningln(FPSTR(L_OT), F("Failed receive outdoor temp"));
|
Log.swarningln(FPSTR(L_OT), F("Failed receive outdoor temp"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,7 +587,7 @@ protected:
|
|||||||
float indoorTemp = 0.0f;
|
float indoorTemp = 0.0f;
|
||||||
float convertedTemp = 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;
|
indoorTemp = vars.temperatures.indoor;
|
||||||
convertedTemp = convertTemp(indoorTemp, settings.system.unitSystem, settings.opentherm.unitSystem);
|
convertedTemp = convertTemp(indoorTemp, settings.system.unitSystem, settings.opentherm.unitSystem);
|
||||||
}
|
}
|
||||||
@@ -595,11 +618,6 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// force enable pump
|
|
||||||
if (!this->pump) {
|
|
||||||
this->pump = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Update heating temp
|
// Update heating temp
|
||||||
if (heatingEnabled && (this->needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001f)) {
|
if (heatingEnabled && (this->needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001f)) {
|
||||||
@@ -634,18 +652,22 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
// Hysteresis
|
// Hysteresis
|
||||||
// Only if enabled PID or/and Equitherm
|
// Only if enabled PID or/and Equitherm or Native heating control via OT
|
||||||
if (settings.heating.hysteresis > 0 && (!vars.states.emergency || settings.emergency.usePid) && (settings.equitherm.enable || settings.pid.enable)) {
|
bool useHyst = false;
|
||||||
float halfHyst = settings.heating.hysteresis / 2;
|
if (settings.heating.hysteresis > 0.01f && vars.sensors.indoor.connected) {
|
||||||
if (this->pump && vars.temperatures.indoor - settings.heating.target + 0.0001f >= halfHyst) {
|
useHyst = settings.equitherm.enable || settings.pid.enable || settings.opentherm.nativeHeatingControl;
|
||||||
this->pump = false;
|
}
|
||||||
|
|
||||||
} else if (!this->pump && vars.temperatures.indoor - settings.heating.target - 0.0001f <= -(halfHyst)) {
|
if (useHyst) {
|
||||||
this->pump = true;
|
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) {
|
} else if (this->heatingBlocking) {
|
||||||
this->pump = true;
|
this->heatingBlocking = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,27 +29,55 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
float newTemp = vars.parameters.heatingSetpoint;
|
bool pidIntergralResetted = false;
|
||||||
|
if (fabs(pidRegulator.Kp - settings.pid.p_factor) >= 0.0001f) {
|
||||||
if (vars.states.emergency) {
|
pidRegulator.Kp = settings.pid.p_factor;
|
||||||
if (settings.heating.turbo) {
|
pidRegulator.integral = 0;
|
||||||
settings.heating.turbo = false;
|
pidIntergralResetted = true;
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Limits
|
||||||
newTemp = constrain(
|
newTemp = constrain(
|
||||||
newTemp,
|
newTemp,
|
||||||
@@ -57,58 +85,12 @@ protected:
|
|||||||
!settings.opentherm.nativeHeatingControl ? settings.heating.maxTemp : THERMOSTAT_INDOOR_MAX_TEMP
|
!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;
|
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 getNormalModeTemp() {
|
||||||
float newTemp = 0;
|
float newTemp = 0;
|
||||||
|
|
||||||
@@ -116,17 +98,49 @@ protected:
|
|||||||
prevHeatingTarget = settings.heating.target;
|
prevHeatingTarget = settings.heating.target;
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR), F("New target: %.2f"), 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;
|
pidRegulator.integral = 0;
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// if use equitherm
|
// if use equitherm
|
||||||
if (settings.equitherm.enable) {
|
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;
|
prevEtResult = etResult;
|
||||||
newTemp += etResult;
|
newTemp += etResult;
|
||||||
|
|
||||||
@@ -140,13 +154,14 @@ protected:
|
|||||||
// if use pid
|
// if use pid
|
||||||
if (settings.pid.enable) {
|
if (settings.pid.enable) {
|
||||||
//if (vars.parameters.heatingEnabled) {
|
//if (vars.parameters.heatingEnabled) {
|
||||||
if (settings.heating.enable) {
|
if (settings.heating.enable && vars.sensors.indoor.connected) {
|
||||||
float pidResult = getPidTemp(
|
pidRegulator.setLimits(settings.pid.minTemp, settings.pid.maxTemp);
|
||||||
settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp,
|
pidRegulator.setDt(settings.pid.dt * 1000u);
|
||||||
settings.pid.maxTemp
|
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;
|
prevPidResult = pidResult;
|
||||||
newTemp += pidResult;
|
newTemp += pidResult;
|
||||||
|
|
||||||
@@ -156,13 +171,10 @@ protected:
|
|||||||
} else {
|
} else {
|
||||||
newTemp += prevPidResult;
|
newTemp += prevPidResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
newTemp += prevPidResult;
|
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
|
// default temp, manual mode
|
||||||
@@ -172,96 +184,4 @@ protected:
|
|||||||
|
|
||||||
return newTemp;
|
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;
|
} mqtt;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = false;
|
|
||||||
float target = DEFAULT_HEATING_TARGET_TEMP;
|
float target = DEFAULT_HEATING_TARGET_TEMP;
|
||||||
unsigned short tresholdTime = 120;
|
unsigned short tresholdTime = 120;
|
||||||
bool useEquitherm = false;
|
|
||||||
bool usePid = false;
|
|
||||||
bool onNetworkFault = true;
|
|
||||||
bool onMqttFault = true;
|
|
||||||
bool onIndoorSensorDisconnect = false;
|
|
||||||
bool onOutdoorSensorDisconnect = false;
|
|
||||||
} emergency;
|
} emergency;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -119,8 +112,8 @@ struct Settings {
|
|||||||
float i_factor = 0.0055f;
|
float i_factor = 0.0055f;
|
||||||
float d_factor = 0;
|
float d_factor = 0;
|
||||||
unsigned short dt = 180;
|
unsigned short dt = 180;
|
||||||
byte minTemp = 0;
|
short minTemp = 0;
|
||||||
byte maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
short maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||||
} pid;
|
} pid;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
197
src/utils.h
197
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"]["interval"] = src.mqtt.interval;
|
||||||
dst["mqtt"]["homeAssistantDiscovery"] = src.mqtt.homeAssistantDiscovery;
|
dst["mqtt"]["homeAssistantDiscovery"] = src.mqtt.homeAssistantDiscovery;
|
||||||
|
|
||||||
dst["emergency"]["enable"] = src.emergency.enable;
|
|
||||||
dst["emergency"]["target"] = roundd(src.emergency.target, 2);
|
dst["emergency"]["target"] = roundd(src.emergency.target, 2);
|
||||||
dst["emergency"]["tresholdTime"] = src.emergency.tresholdTime;
|
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;
|
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"]["minTemp"] = src.dhw.minTemp;
|
||||||
dst["dhw"]["maxTemp"] = src.dhw.maxTemp;
|
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"]["enable"] = src.pid.enable;
|
||||||
dst["pid"]["p_factor"] = roundd(src.pid.p_factor, 3);
|
dst["pid"]["p_factor"] = roundd(src.pid.p_factor, 3);
|
||||||
dst["pid"]["i_factor"] = roundd(src.pid.i_factor, 4);
|
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"]["minTemp"] = src.pid.minTemp;
|
||||||
dst["pid"]["maxTemp"] = src.pid.maxTemp;
|
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"]["type"] = static_cast<byte>(src.sensors.outdoor.type);
|
||||||
dst["sensors"]["outdoor"]["gpio"] = src.sensors.outdoor.gpio;
|
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;
|
dst.opentherm.nativeHeatingControl = value;
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
dst.emergency.useEquitherm = false;
|
|
||||||
dst.emergency.usePid = false;
|
|
||||||
dst.equitherm.enable = false;
|
dst.equitherm.enable = false;
|
||||||
dst.pid.enable = false;
|
dst.pid.enable = false;
|
||||||
}
|
}
|
||||||
@@ -956,15 +947,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
|
|
||||||
|
|
||||||
// emergency
|
// 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()) {
|
if (!src["emergency"]["tresholdTime"].isNull()) {
|
||||||
unsigned short value = src["emergency"]["tresholdTime"].as<unsigned short>();
|
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;
|
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) {
|
// equitherm
|
||||||
if (value != dst.emergency.useEquitherm) {
|
if (src["equitherm"]["enable"].is<bool>()) {
|
||||||
dst.emergency.useEquitherm = value;
|
bool value = src["equitherm"]["enable"].as<bool>();
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (dst.emergency.useEquitherm) {
|
if (!dst.opentherm.nativeHeatingControl) {
|
||||||
dst.emergency.useEquitherm = false;
|
if (value != dst.equitherm.enable) {
|
||||||
changed = true;
|
dst.equitherm.enable = value;
|
||||||
}
|
|
||||||
|
|
||||||
if (dst.emergency.useEquitherm && dst.emergency.usePid) {
|
|
||||||
dst.emergency.usePid = false;
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (dst.equitherm.enable) {
|
||||||
|
dst.equitherm.enable = false;
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (src["emergency"]["usePid"].is<bool>()) {
|
if (!src["equitherm"]["n_factor"].isNull()) {
|
||||||
bool value = src["emergency"]["usePid"].as<bool>();
|
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 > 0 && value <= 10 && fabs(value - dst.equitherm.n_factor) > 0.0001f) {
|
||||||
if (value != dst.emergency.usePid) {
|
dst.equitherm.n_factor = roundd(value, 3);
|
||||||
dst.emergency.usePid = value;
|
changed = true;
|
||||||
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 (src["emergency"]["onNetworkFault"].is<bool>()) {
|
if (!src["equitherm"]["k_factor"].isNull()) {
|
||||||
bool value = src["emergency"]["onNetworkFault"].as<bool>();
|
float value = src["equitherm"]["k_factor"].as<float>();
|
||||||
|
|
||||||
if (value != dst.emergency.onNetworkFault) {
|
if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.k_factor) > 0.0001f) {
|
||||||
dst.emergency.onNetworkFault = value;
|
dst.equitherm.k_factor = roundd(value, 3);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (src["emergency"]["onMqttFault"].is<bool>()) {
|
if (!src["equitherm"]["t_factor"].isNull()) {
|
||||||
bool value = src["emergency"]["onMqttFault"].as<bool>();
|
float value = src["equitherm"]["t_factor"].as<float>();
|
||||||
|
|
||||||
if (value != dst.emergency.onMqttFault) {
|
if (value >= 0 && value <= 10 && fabs(value - dst.equitherm.t_factor) > 0.0001f) {
|
||||||
dst.emergency.onMqttFault = value;
|
dst.equitherm.t_factor = roundd(value, 3);
|
||||||
changed = true;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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()) {
|
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;
|
dst.pid.minTemp = value;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!src["pid"]["maxTemp"].isNull()) {
|
||||||
|
short value = src["pid"]["maxTemp"].as<short>();
|
||||||
|
|
||||||
// equitherm
|
if (isValidTemp(value, dst.system.unitSystem) && value != dst.pid.maxTemp) {
|
||||||
if (src["equitherm"]["enable"].is<bool>()) {
|
dst.pid.maxTemp = value;
|
||||||
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;
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!src["equitherm"]["n_factor"].isNull()) {
|
if (dst.pid.maxTemp < dst.pid.minTemp) {
|
||||||
float value = src["equitherm"]["n_factor"].as<float>();
|
dst.pid.maxTemp = dst.pid.minTemp;
|
||||||
|
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["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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
// dhw
|
||||||
if (src["dhw"]["enable"].is<bool>()) {
|
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()) {
|
if (!src["dhw"]["minTemp"].isNull()) {
|
||||||
unsigned char value = src["dhw"]["minTemp"].as<unsigned char>();
|
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;
|
dst.dhw.minTemp = value;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@@ -1238,12 +1152,16 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
if (!src["dhw"]["maxTemp"].isNull()) {
|
if (!src["dhw"]["maxTemp"].isNull()) {
|
||||||
unsigned char value = src["dhw"]["maxTemp"].as<unsigned char>();
|
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;
|
dst.dhw.maxTemp = value;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dst.dhw.maxTemp < dst.dhw.minTemp) {
|
||||||
|
dst.dhw.maxTemp = dst.dhw.minTemp;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
// sensors
|
// sensors
|
||||||
if (!src["sensors"]["outdoor"]["type"].isNull()) {
|
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):
|
case static_cast<byte>(SensorType::MANUAL):
|
||||||
if (dst.sensors.outdoor.type != SensorType::MANUAL) {
|
if (dst.sensors.outdoor.type != SensorType::MANUAL) {
|
||||||
dst.sensors.outdoor.type = SensorType::MANUAL;
|
dst.sensors.outdoor.type = SensorType::MANUAL;
|
||||||
dst.emergency.useEquitherm = false;
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1276,7 +1193,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
case static_cast<byte>(SensorType::BLUETOOTH):
|
case static_cast<byte>(SensorType::BLUETOOTH):
|
||||||
if (dst.sensors.outdoor.type != SensorType::BLUETOOTH) {
|
if (dst.sensors.outdoor.type != SensorType::BLUETOOTH) {
|
||||||
dst.sensors.outdoor.type = SensorType::BLUETOOTH;
|
dst.sensors.outdoor.type = SensorType::BLUETOOTH;
|
||||||
dst.emergency.useEquitherm = false;
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1335,8 +1251,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
case static_cast<byte>(SensorType::MANUAL):
|
case static_cast<byte>(SensorType::MANUAL):
|
||||||
if (dst.sensors.indoor.type != SensorType::MANUAL) {
|
if (dst.sensors.indoor.type != SensorType::MANUAL) {
|
||||||
dst.sensors.indoor.type = SensorType::MANUAL;
|
dst.sensors.indoor.type = SensorType::MANUAL;
|
||||||
dst.emergency.usePid = false;
|
|
||||||
dst.opentherm.nativeHeatingControl = false;
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1352,7 +1266,6 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
case static_cast<byte>(SensorType::BLUETOOTH):
|
case static_cast<byte>(SensorType::BLUETOOTH):
|
||||||
if (dst.sensors.indoor.type != SensorType::BLUETOOTH) {
|
if (dst.sensors.indoor.type != SensorType::BLUETOOTH) {
|
||||||
dst.sensors.indoor.type = SensorType::BLUETOOTH;
|
dst.sensors.indoor.type = SensorType::BLUETOOTH;
|
||||||
dst.emergency.usePid = false;
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1597,7 +1510,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
// force check emergency target
|
// force check emergency target
|
||||||
{
|
{
|
||||||
float value = !src["emergency"]["target"].isNull() ? src["emergency"]["target"].as<float>() : dst.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(
|
bool valid = isValidTemp(
|
||||||
value,
|
value,
|
||||||
dst.system.unitSystem,
|
dst.system.unitSystem,
|
||||||
|
|||||||
@@ -224,27 +224,13 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"emergency": {
|
"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": {
|
"target": {
|
||||||
"title": "Target temperature",
|
"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>",
|
"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>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"equitherm": {
|
"equitherm": {
|
||||||
@@ -260,7 +246,8 @@
|
|||||||
"p": "P factor",
|
"p": "P factor",
|
||||||
"i": "I factor",
|
"i": "I factor",
|
||||||
"d": "D 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": {
|
"ot": {
|
||||||
@@ -313,7 +300,7 @@
|
|||||||
|
|
||||||
"nativeHeating": {
|
"nativeHeating": {
|
||||||
"title": "Native heating control (boiler)",
|
"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": {
|
"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": {
|
"target": {
|
||||||
"title": "Целевая температура",
|
"title": "Целевая температура",
|
||||||
"note": "Целевая <u>внутренняя температура</u> если ПЗА и/или ПИД <b>включены</b><br />Целевая <u>температура теплоносителя</u> если ПЗА и ПИД <b>выключены</b>"
|
"note": "<b>Важно:</b> <u>Целевая температура в помещении</u>, если включена ОТ опция <i>«Передать управление отоплением котлу»</i>.<br />Во всех остальных случаях <u>целевая температура теплоносителя</u>."
|
||||||
},
|
},
|
||||||
"treshold": "Пороговое время включения <small>(сек)</small>",
|
"treshold": "Пороговое время включения <small>(сек)</small>",
|
||||||
|
|
||||||
@@ -260,7 +260,8 @@
|
|||||||
"p": "Коэффициент P",
|
"p": "Коэффициент P",
|
||||||
"i": "Коэффициент I",
|
"i": "Коэффициент I",
|
||||||
"d": "Коэффициент D",
|
"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": {
|
"ot": {
|
||||||
@@ -313,7 +314,7 @@
|
|||||||
|
|
||||||
"nativeHeating": {
|
"nativeHeating": {
|
||||||
"title": "Передать управление отоплением котлу",
|
"title": "Передать управление отоплением котлу",
|
||||||
"note": "Работает <u>ТОЛЬКО</u> если котел требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД, ПЗА и гистерезисом."
|
"note": "Работает <u>ТОЛЬКО</u> если котел требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД и ПЗА."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -208,11 +208,6 @@
|
|||||||
<div id="emergency-settings-busy" aria-busy="true"></div>
|
<div id="emergency-settings-busy" aria-busy="true"></div>
|
||||||
<form action="/api/settings" id="emergency-settings" class="hidden">
|
<form action="/api/settings" id="emergency-settings" class="hidden">
|
||||||
<fieldset>
|
<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>
|
<small data-i18n>settings.emergency.desc</small>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
@@ -229,43 +224,6 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</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>
|
<button type="submit" data-i18n>button.save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -358,6 +316,8 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<small data-i18n>settings.pid.noteMinMaxTemp</small>
|
||||||
|
|
||||||
<button type="submit" data-i18n>button.save</button>
|
<button type="submit" data-i18n>button.save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -958,18 +918,20 @@
|
|||||||
setBusy('#dhw-settings-busy', '#dhw-settings', false);
|
setBusy('#dhw-settings-busy', '#dhw-settings', false);
|
||||||
|
|
||||||
// Emergency mode
|
// Emergency mode
|
||||||
setCheckboxValue('#emergency-enable', data.emergency.enable);
|
|
||||||
setInputValue('#emergency-treshold-time', data.emergency.tresholdTime);
|
setInputValue('#emergency-treshold-time', data.emergency.tresholdTime);
|
||||||
setCheckboxValue('#emergency-use-equitherm', data.emergency.useEquitherm);
|
if (data.opentherm.nativeHeatingControl) {
|
||||||
setCheckboxValue('#emergency-use-pid', data.emergency.usePid);
|
setInputValue('#emergency-target', data.emergency.target, {
|
||||||
setCheckboxValue('#emergency-on-network-fault', data.emergency.onNetworkFault);
|
"min": data.system.unitSystem == 0 ? 5 : 41,
|
||||||
setCheckboxValue('#emergency-on-mqtt-fault', data.emergency.onMqttFault);
|
"max": data.system.unitSystem == 0 ? 30 : 86
|
||||||
setCheckboxValue('#emergency-on-indoor-sensor-disconnect', data.emergency.onIndoorSensorDisconnect);
|
});
|
||||||
setCheckboxValue('#emergency-on-outdoor-sensor-disconnect', data.emergency.onOutdoorSensorDisconnect);
|
|
||||||
setInputValue('#emergency-target', data.emergency.target, {
|
} else {
|
||||||
"min": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.minTemp : 10,
|
setInputValue('#emergency-target', data.emergency.target, {
|
||||||
"max": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.maxTemp : 30,
|
"min": data.heating.minTemp,
|
||||||
});
|
"max": data.heating.maxTemp,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setBusy('#emergency-settings-busy', '#emergency-settings', false);
|
setBusy('#emergency-settings-busy', '#emergency-settings', false);
|
||||||
|
|
||||||
// Equitherm
|
// Equitherm
|
||||||
@@ -986,12 +948,12 @@
|
|||||||
setInputValue('#pid-d-factor', data.pid.d_factor);
|
setInputValue('#pid-d-factor', data.pid.d_factor);
|
||||||
setInputValue('#pid-dt', data.pid.dt);
|
setInputValue('#pid-dt', data.pid.dt);
|
||||||
setInputValue('#pid-min-temp', data.pid.minTemp, {
|
setInputValue('#pid-min-temp', data.pid.minTemp, {
|
||||||
"min": 0,
|
"min": data.equitherm.enable ? (data.system.unitSystem == 0 ? -100 : -146) : (data.system.unitSystem == 0 ? 0 : 32),
|
||||||
"max": data.system.unitSystem == 0 ? 99 : 211
|
"max": (data.system.unitSystem == 0 ? 99 : 211)
|
||||||
});
|
});
|
||||||
setInputValue('#pid-max-temp', data.pid.maxTemp, {
|
setInputValue('#pid-max-temp', data.pid.maxTemp, {
|
||||||
"min": 1,
|
"min": (data.system.unitSystem == 0 ? 0 : 33),
|
||||||
"max": data.system.unitSystem == 0 ? 100 : 212
|
"max": (data.system.unitSystem == 0 ? 100 : 212)
|
||||||
});
|
});
|
||||||
setBusy('#pid-settings-busy', '#pid-settings', false);
|
setBusy('#pid-settings-busy', '#pid-settings', false);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user