mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 02:34:29 +05:00
refactor: reworked emergency mode; reworked hysteresis algorithm; improved detection of connection state for MANUAL & BOILER type sensors
This commit is contained in:
@@ -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 {
|
||||
|
||||
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"]["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;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (dst.emergency.useEquitherm && dst.emergency.usePid) {
|
||||
dst.emergency.usePid = false;
|
||||
if (!dst.opentherm.nativeHeatingControl) {
|
||||
if (value != dst.equitherm.enable) {
|
||||
dst.equitherm.enable = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
} else if (dst.equitherm.enable) {
|
||||
dst.equitherm.enable = false;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (src["emergency"]["usePid"].is<bool>()) {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user