Fix Thermostat sensor status corruption

Fix Thermostat sensor status corruption regression from 8.5.0.1 (#9449)
This commit is contained in:
Theo Arends 2020-10-10 15:19:11 +02:00
parent 0949dda650
commit b8e55203b6
4 changed files with 84 additions and 78 deletions

View File

@ -32,6 +32,7 @@ All notable changes to this project will be documented in this file.
- Shutter timing problem due to buffer overflow in calibration matrix (#9458) - Shutter timing problem due to buffer overflow in calibration matrix (#9458)
- Light wakeup exception 0 (divide by zero) when ``WakeupDuration`` is not initialised (#9466) - Light wakeup exception 0 (divide by zero) when ``WakeupDuration`` is not initialised (#9466)
- ADC initalization sequence (#9473) - ADC initalization sequence (#9473)
- Thermostat sensor status corruption regression from 8.5.0.1 (#9449)
### Removed ### Removed
- Support for direct upgrade from Tasmota versions before 7.0 - Support for direct upgrade from Tasmota versions before 7.0

View File

@ -86,6 +86,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Shutter timing problem due to buffer overflow in calibration matrix (#9458) - Shutter timing problem due to buffer overflow in calibration matrix (#9458)
- Light wakeup exception 0 (divide by zero) when ``WakeupDuration`` is not initialised (#9466) - Light wakeup exception 0 (divide by zero) when ``WakeupDuration`` is not initialised (#9466)
- ADC initalization sequence (#9473) - ADC initalization sequence (#9473)
- Thermostat sensor status corruption regression from 8.5.0.1 (#9449)
### Removed ### Removed
- Support for direct upgrade from Tasmota versions before 7.0 - Support for direct upgrade from Tasmota versions before 7.0

View File

@ -473,8 +473,11 @@ bool SettingsUpdateText(uint32_t index, const char* replace_me) {
settings_text_mutex = false; settings_text_mutex = false;
} }
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d, Id %d = \"%s\""), GetSettingsTextLen(), settings_text_size, settings_text_busy_count, index_save, replace); #ifdef DEBUG_FUNC_SETTINGSUPDATETEXT
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d, Id %02d = \"%s\""), GetSettingsTextLen(), settings_text_size, settings_text_busy_count, index_save, replace);
#else
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d"), GetSettingsTextLen(), settings_text_size, settings_text_busy_count); AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d"), GetSettingsTextLen(), settings_text_size, settings_text_busy_count);
#endif
return true; return true;
} }

View File

@ -25,7 +25,7 @@
//#define DEBUG_THERMOSTAT //#define DEBUG_THERMOSTAT
// Enable/disable experimental PI auto-tuning inspired by the Arduino // Enable/disable experimental PI auto-tuning inspired by the Arduino
// Autotune Library by Brett Beauregard // Autotune Library by Brett Beauregard
//#define USE_PI_AUTOTUNING // (Ziegler-Nichols closed loop method) //#define USE_PI_AUTOTUNING // (Ziegler-Nichols closed loop method)
#ifdef DEBUG_THERMOSTAT #ifdef DEBUG_THERMOSTAT
@ -162,32 +162,32 @@ const char DOMOTICZ_MES[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\"
uint16_t Domoticz_Virtual_Switches[DOMOTICZ_MAX_IDX] = { DOMOTICZ_IDX1, DOMOTICZ_IDX3, DOMOTICZ_IDX4, DOMOTICZ_IDX5 }; uint16_t Domoticz_Virtual_Switches[DOMOTICZ_MAX_IDX] = { DOMOTICZ_IDX1, DOMOTICZ_IDX3, DOMOTICZ_IDX4, DOMOTICZ_IDX5 };
#endif // DEBUG_THERMOSTAT #endif // DEBUG_THERMOSTAT
const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CMND_CLIMATEMODESET "|" const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CMND_CLIMATEMODESET "|"
D_CMND_TEMPFROSTPROTECTSET "|" D_CMND_CONTROLLERMODESET "|" D_CMND_INPUTSWITCHSET "|" D_CMND_INPUTSWITCHUSE "|" D_CMND_TEMPFROSTPROTECTSET "|" D_CMND_CONTROLLERMODESET "|" D_CMND_INPUTSWITCHSET "|" D_CMND_INPUTSWITCHUSE "|"
D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" D_CMND_TEMPFORMATSET "|" D_CMND_TEMPMEASUREDSET "|" D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" D_CMND_TEMPFORMATSET "|" D_CMND_TEMPMEASUREDSET "|"
D_CMND_TEMPTARGETSET "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_SENSORINPUTSET "|" D_CMND_STATEEMERGENCYSET "|" D_CMND_TEMPTARGETSET "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_SENSORINPUTSET "|" D_CMND_STATEEMERGENCYSET "|"
D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|" D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|"
#ifdef USE_PI_AUTOTUNING #ifdef USE_PI_AUTOTUNING
D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_PERFLEVELAUTOTUNE "|" D_CMND_TIMEMAXACTIONSET "|" D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_PERFLEVELAUTOTUNE "|" D_CMND_TIMEMAXACTIONSET "|"
#else #else
D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|" D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|"
#endif // USE_PI_AUTOTUNING #endif // USE_PI_AUTOTUNING
D_CMND_TIMEMINACTIONSET "|" D_CMND_TIMEMINTURNOFFACTIONSET "|" D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|" D_CMND_TIMEMINACTIONSET "|" D_CMND_TIMEMINTURNOFFACTIONSET "|" D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|"
D_CMND_TIMERAMPUPMAXSET "|" D_CMND_TIMERAMPUPCYCLESET "|" D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|" D_CMND_TIMERAMPUPMAXSET "|" D_CMND_TIMERAMPUPCYCLESET "|" D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|"
D_CMND_TIMEPIINTEGRREAD "|" D_CMND_TIMESENSLOSTSET "|" D_CMND_DIAGNOSTICMODESET "|" D_CMND_CTRDUTYCYCLEREAD "|" D_CMND_TIMEPIINTEGRREAD "|" D_CMND_TIMESENSLOSTSET "|" D_CMND_DIAGNOSTICMODESET "|" D_CMND_CTRDUTYCYCLEREAD "|"
D_CMND_ENABLEOUTPUTSET; D_CMND_ENABLEOUTPUTSET;
void (* const ThermostatCommand[])(void) PROGMEM = { void (* const ThermostatCommand[])(void) PROGMEM = {
&CmndThermostatModeSet, &CmndClimateModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet, &CmndThermostatModeSet, &CmndClimateModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet,
&CmndInputSwitchUse, &CmndOutputRelaySet, &CmndTimeAllowRampupSet, &CmndTempFormatSet, &CmndTempMeasuredSet, &CmndInputSwitchUse, &CmndOutputRelaySet, &CmndTimeAllowRampupSet, &CmndTempFormatSet, &CmndTempMeasuredSet,
&CmndTempTargetSet, &CmndTempMeasuredGrdRead, &CmndSensorInputSet, &CmndStateEmergencySet, &CmndTimeManualToAutoSet, &CmndTempTargetSet, &CmndTempMeasuredGrdRead, &CmndSensorInputSet, &CmndStateEmergencySet, &CmndTimeManualToAutoSet,
&CmndPropBandSet, &CmndTimeResetSet, &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet, &CmndPropBandSet, &CmndTimeResetSet, &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet,
#ifdef USE_PI_AUTOTUNING #ifdef USE_PI_AUTOTUNING
&CmndPerfLevelAutotune, &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, &CmndPerfLevelAutotune, &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet,
#else #else
&CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet,
#endif // USE_PI_AUTOTUNING #endif // USE_PI_AUTOTUNING
&CmndTempRupDeltOutSet, &CmndTimeRampupMaxSet, &CmndTimeRampupCycleSet, &CmndTempRampupPiAccErrSet, &CmndTempRupDeltOutSet, &CmndTimeRampupMaxSet, &CmndTimeRampupCycleSet, &CmndTempRampupPiAccErrSet,
&CmndTimePiProportRead, &CmndTimePiIntegrRead, &CmndTimeSensLostSet, &CmndDiagnosticModeSet, &CmndCtrDutyCycleRead, &CmndTimePiProportRead, &CmndTimePiIntegrRead, &CmndTimeSensLostSet, &CmndDiagnosticModeSet, &CmndCtrDutyCycleRead,
&CmndEnableOutputSet }; &CmndEnableOutputSet };
@ -289,24 +289,24 @@ void ThermostatInit(uint8_t ctr_output)
} }
} }
bool ThermostatMinuteCounter(uint8_t ctr_output) bool ThermostatMinuteCounter(uint8_t ctr_output)
{ {
bool result = false; bool result = false;
Thermostat[ctr_output].status.counter_seconds++; // increment time Thermostat[ctr_output].status.counter_seconds++; // increment time
if ((Thermostat[ctr_output].status.counter_seconds % 60) == 0) { if ((Thermostat[ctr_output].status.counter_seconds % 60) == 0) {
result = true; result = true;
Thermostat[ctr_output].status.counter_seconds = 0; Thermostat[ctr_output].status.counter_seconds = 0;
} }
return result; return result;
} }
inline bool ThermostatSwitchIdValid(uint8_t switchId) inline bool ThermostatSwitchIdValid(uint8_t switchId)
{ {
return (switchId >= THERMOSTAT_INPUT_SWT1 && switchId <= THERMOSTAT_INPUT_SWT8); return (switchId >= THERMOSTAT_INPUT_SWT1 && switchId <= THERMOSTAT_INPUT_SWT8);
} }
inline bool ThermostatRelayIdValid(uint8_t relayId) inline bool ThermostatRelayIdValid(uint8_t relayId)
{ {
return (relayId >= THERMOSTAT_OUTPUT_REL1 && relayId <= THERMOSTAT_OUTPUT_REL8); return (relayId >= THERMOSTAT_OUTPUT_REL1 && relayId <= THERMOSTAT_OUTPUT_REL8);
} }
@ -326,7 +326,7 @@ uint8_t ThermostatOutputStatus(uint8_t output_switch)
return (uint8_t)bitRead(power, (output_switch - 1)); return (uint8_t)bitRead(power, (output_switch - 1));
} }
int16_t ThermostatCelsiusToFahrenheit(const int32_t deg, uint8_t conv_type) { int16_t ThermostatCelsiusToFahrenheit(const int32_t deg, uint8_t conv_type) {
int32_t value; int32_t value;
value = (int32_t)(((int32_t)deg * (int32_t)90) / (int32_t)50); value = (int32_t)(((int32_t)deg * (int32_t)90) / (int32_t)50);
if (conv_type == TEMP_CONV_ABSOLUTE) { if (conv_type == TEMP_CONV_ABSOLUTE) {
@ -344,7 +344,7 @@ int16_t ThermostatCelsiusToFahrenheit(const int32_t deg, uint8_t conv_type) {
return (int16_t)value; return (int16_t)value;
} }
int16_t ThermostatFahrenheitToCelsius(const int32_t deg, uint8_t conv_type) { int16_t ThermostatFahrenheitToCelsius(const int32_t deg, uint8_t conv_type) {
int16_t offset = 0; int16_t offset = 0;
int32_t value; int32_t value;
if (conv_type == TEMP_CONV_ABSOLUTE) { if (conv_type == TEMP_CONV_ABSOLUTE) {
@ -450,9 +450,9 @@ void ThermostatHybridCtrPhase(uint8_t ctr_output)
switch (Thermostat[ctr_output].status.phase_hybrid_ctr) { switch (Thermostat[ctr_output].status.phase_hybrid_ctr) {
// Ramp-up phase with gradient control // Ramp-up phase with gradient control
case CTR_HYBRID_RAMP_UP: case CTR_HYBRID_RAMP_UP:
// If ramp-up offtime counter has been initalized // If ramp-up offtime counter has been initalized
// AND ramp-up offtime counter value reached // AND ramp-up offtime counter value reached
if((Thermostat[ctr_output].time_ctr_checkpoint != 0) if((Thermostat[ctr_output].time_ctr_checkpoint != 0)
&& (uptime >= Thermostat[ctr_output].time_ctr_checkpoint)) { && (uptime >= Thermostat[ctr_output].time_ctr_checkpoint)) {
// Reset pause period // Reset pause period
Thermostat[ctr_output].time_ctr_checkpoint = 0; Thermostat[ctr_output].time_ctr_checkpoint = 0;
@ -507,7 +507,7 @@ void ThermostatHybridCtrPhase(uint8_t ctr_output)
{ {
Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI; Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI;
} }
break; break;
#endif // USE_PI_AUTOTUNING #endif // USE_PI_AUTOTUNING
} }
} }
@ -539,7 +539,7 @@ bool ThermostatStateManualToAuto(uint8_t ctr_output)
// AND sensor alive // AND sensor alive
// AND no switch input action (time in current state) bigger than a pre-defined time // AND no switch input action (time in current state) bigger than a pre-defined time
// then go to automatic // then go to automatic
if ((Thermostat[ctr_output].status.status_input == IFACE_OFF) if ((Thermostat[ctr_output].status.status_input == IFACE_OFF)
&&(Thermostat[ctr_output].status.sensor_alive == IFACE_ON) &&(Thermostat[ctr_output].status.sensor_alive == IFACE_ON)
&& ((uptime - Thermostat[ctr_output].timestamp_input_on) > ((uint32_t)Thermostat[ctr_output].time_manual_to_auto * 60))) { && ((uptime - Thermostat[ctr_output].timestamp_input_on) > ((uint32_t)Thermostat[ctr_output].time_manual_to_auto * 60))) {
change_state = true; change_state = true;
@ -587,7 +587,7 @@ void ThermostatOutputRelay(uint8_t ctr_output, uint32_t command)
// If command received to enable output // If command received to enable output
// AND current output status is OFF // AND current output status is OFF
// then switch output to ON // then switch output to ON
if ((command == IFACE_ON) if ((command == IFACE_ON)
&& (Thermostat[ctr_output].status.status_output == IFACE_OFF)) { && (Thermostat[ctr_output].status.status_output == IFACE_OFF)) {
//#ifndef DEBUG_THERMOSTAT //#ifndef DEBUG_THERMOSTAT
if (Thermostat[ctr_output].status.enable_output == IFACE_ON) { if (Thermostat[ctr_output].status.enable_output == IFACE_ON) {
@ -619,13 +619,13 @@ void ThermostatOutputRelay(uint8_t ctr_output, uint32_t command)
void ThermostatCalculatePI(uint8_t ctr_output) void ThermostatCalculatePI(uint8_t ctr_output)
{ {
// General comment: Some variables have been increased in resolution to avoid loosing accuracy in division operations // General comment: Some variables have been increased in resolution to avoid loosing accuracy in division operations
bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING);
int32_t aux_temp_error; int32_t aux_temp_error;
// Calculate error // Calculate error
aux_temp_error = (int32_t)(Thermostat[ctr_output].temp_target_level_ctr - Thermostat[ctr_output].temp_measured) * 10; aux_temp_error = (int32_t)(Thermostat[ctr_output].temp_target_level_ctr - Thermostat[ctr_output].temp_measured) * 10;
// Invert error for cooling // Invert error for cooling
if (Thermostat[ctr_output].status.climate_mode == CLIMATE_COOLING) { if (Thermostat[ctr_output].status.climate_mode == CLIMATE_COOLING) {
aux_temp_error *= -1; aux_temp_error *= -1;
@ -641,7 +641,7 @@ void ThermostatCalculatePI(uint8_t ctr_output)
else { else {
Thermostat[ctr_output].temp_pi_error = (int16_t)aux_temp_error; Thermostat[ctr_output].temp_pi_error = (int16_t)aux_temp_error;
} }
// Kp = 100/PI.propBand. PI.propBand(Xp) = Proportional range (4K in 4K/200 controller) // Kp = 100/PI.propBand. PI.propBand(Xp) = Proportional range (4K in 4K/200 controller)
Thermostat[ctr_output].kP_pi = 100 / (uint16_t)(Thermostat[ctr_output].val_prop_band); Thermostat[ctr_output].kP_pi = 100 / (uint16_t)(Thermostat[ctr_output].val_prop_band);
// Calculate proportional // Calculate proportional
@ -655,17 +655,17 @@ void ThermostatCalculatePI(uint8_t ctr_output)
&& (Thermostat[ctr_output].time_proportional_pi > 0)) { && (Thermostat[ctr_output].time_proportional_pi > 0)) {
Thermostat[ctr_output].time_proportional_pi = ((int32_t)Thermostat[ctr_output].time_min_action * 60); Thermostat[ctr_output].time_proportional_pi = ((int32_t)Thermostat[ctr_output].time_min_action * 60);
} }
if (Thermostat[ctr_output].time_proportional_pi < 0) { if (Thermostat[ctr_output].time_proportional_pi < 0) {
Thermostat[ctr_output].time_proportional_pi = 0; Thermostat[ctr_output].time_proportional_pi = 0;
} }
else if (Thermostat[ctr_output].time_proportional_pi > ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60)) { else if (Thermostat[ctr_output].time_proportional_pi > ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60)) {
Thermostat[ctr_output].time_proportional_pi = ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60); Thermostat[ctr_output].time_proportional_pi = ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60);
} }
// Calculate integral (resolution increased to avoid use of floats in consequent operations) // Calculate integral (resolution increased to avoid use of floats in consequent operations)
Thermostat[ctr_output].kI_pi = (uint16_t)((((uint32_t)Thermostat[ctr_output].kP_pi * (uint32_t)Thermostat[ctr_output].time_pi_cycle * 6000)) / (uint32_t)Thermostat[ctr_output].time_reset); Thermostat[ctr_output].kI_pi = (uint16_t)((((uint32_t)Thermostat[ctr_output].kP_pi * (uint32_t)Thermostat[ctr_output].time_pi_cycle * 6000)) / (uint32_t)Thermostat[ctr_output].time_reset);
// Reset of antiwindup // Reset of antiwindup
// If error does not lay within the integrator scope range, do not use the integral // If error does not lay within the integrator scope range, do not use the integral
// and accumulate error = 0 // and accumulate error = 0
@ -674,13 +674,13 @@ void ThermostatCalculatePI(uint8_t ctr_output)
Thermostat[ctr_output].temp_pi_accum_error = 0; Thermostat[ctr_output].temp_pi_accum_error = 0;
} }
// Normal use of integrator // Normal use of integrator
// result will be calculated with the cummulated previous error anterior // result will be calculated with the cummulated previous error anterior
// and current error will be cummulated to the previous one // and current error will be cummulated to the previous one
else { else {
// Hysteresis limiter // Hysteresis limiter
// If error is less than or equal than hysteresis, limit output to 0, when temperature // If error is less than or equal than hysteresis, limit output to 0, when temperature
// is rising, never when falling. Limit cummulated error. If this is not done, // is rising, never when falling. Limit cummulated error. If this is not done,
// there will be very strong control actions from the integral part due to a // there will be very strong control actions from the integral part due to a
// very high cummulated error when beingin hysteresis. This triggers high // very high cummulated error when beingin hysteresis. This triggers high
// integral actions // integral actions
@ -739,7 +739,7 @@ void ThermostatCalculatePI(uint8_t ctr_output)
// Calculate output // Calculate output
Thermostat[ctr_output].time_total_pi = Thermostat[ctr_output].time_proportional_pi + Thermostat[ctr_output].time_integral_pi; Thermostat[ctr_output].time_total_pi = Thermostat[ctr_output].time_proportional_pi + Thermostat[ctr_output].time_integral_pi;
// Antiwindup of the output // Antiwindup of the output
// If result is bigger than cycle time, the result will be adjusted // If result is bigger than cycle time, the result will be adjusted
// to the cylce time minus safety time and error will not be cummulated // to the cylce time minus safety time and error will not be cummulated
@ -762,7 +762,7 @@ void ThermostatCalculatePI(uint8_t ctr_output)
&& (!flag_heating)))){ && (!flag_heating)))){
Thermostat[ctr_output].time_total_pi = 0; Thermostat[ctr_output].time_total_pi = 0;
} }
} }
// If target value has not been reached // If target value has not been reached
// AND we are within the histeresis // AND we are within the histeresis
// AND gradient is positive for heating or negative for cooling // AND gradient is positive for heating or negative for cooling
@ -791,7 +791,7 @@ void ThermostatCalculatePI(uint8_t ctr_output)
else if (Thermostat[ctr_output].time_total_pi > (((int32_t)Thermostat[ctr_output].time_pi_cycle * 60) - ((int32_t)Thermostat[ctr_output].time_min_turnoff_action * 60))) { else if (Thermostat[ctr_output].time_total_pi > (((int32_t)Thermostat[ctr_output].time_pi_cycle * 60) - ((int32_t)Thermostat[ctr_output].time_min_turnoff_action * 60))) {
Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60); Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60);
} }
// Adjust output switch point // Adjust output switch point
Thermostat[ctr_output].time_ctr_changepoint = uptime + (uint32_t)Thermostat[ctr_output].time_total_pi; Thermostat[ctr_output].time_ctr_changepoint = uptime + (uint32_t)Thermostat[ctr_output].time_total_pi;
// Adjust next cycle point // Adjust next cycle point
@ -801,7 +801,7 @@ void ThermostatCalculatePI(uint8_t ctr_output)
void ThermostatWorkAutomaticPI(uint8_t ctr_output) void ThermostatWorkAutomaticPI(uint8_t ctr_output)
{ {
bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING);
if ( (uptime >= Thermostat[ctr_output].time_ctr_checkpoint) if ( (uptime >= Thermostat[ctr_output].time_ctr_checkpoint)
|| (Thermostat[ctr_output].temp_target_level != Thermostat[ctr_output].temp_target_level_ctr) || (Thermostat[ctr_output].temp_target_level != Thermostat[ctr_output].temp_target_level_ctr)
|| ( (( (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_target_level) || ( (( (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_target_level)
&& (Thermostat[ctr_output].temp_measured_gradient < 0) && (Thermostat[ctr_output].temp_measured_gradient < 0)
@ -831,7 +831,7 @@ void ThermostatWorkAutomaticRampUp(uint8_t ctr_output)
int16_t temp_delta_rampup; int16_t temp_delta_rampup;
bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING);
// Update timestamp for temperature at start of ramp-up if temperature still // Update timestamp for temperature at start of ramp-up if temperature still
// dropping for heating or rising for cooling // dropping for heating or rising for cooling
if ( ((Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_rampup_start) if ( ((Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_rampup_start)
&& (flag_heating)) && (flag_heating))
@ -859,10 +859,10 @@ void ThermostatWorkAutomaticRampUp(uint8_t ctr_output)
// DEADTIME point reached // DEADTIME point reached
// If temperature measured minus temperature at start of ramp-up >= threshold // If temperature measured minus temperature at start of ramp-up >= threshold
// AND deadtime still 0 // AND deadtime still 0
if ( (abs(temp_delta_rampup) >= Thermostat[ctr_output].temp_rampup_delta_out) if ( (abs(temp_delta_rampup) >= Thermostat[ctr_output].temp_rampup_delta_out)
&& (Thermostat[ctr_output].time_rampup_deadtime == 0)) { && (Thermostat[ctr_output].time_rampup_deadtime == 0)) {
// Set deadtime, assuming it is half of the time until slope, since thermal inertia of the temp. fall needs to be considered // Set deadtime, assuming it is half of the time until slope, since thermal inertia of the temp. fall needs to be considered
// minus open time of the valve (arround 3 minutes). If rise/sink very fast limit it to delay of output valve // minus open time of the valve (arround 3 minutes). If rise/sink very fast limit it to delay of output valve
int32_t time_aux; int32_t time_aux;
time_aux = ((time_in_rampup / 2) - Thermostat[ctr_output].time_output_delay); time_aux = ((time_in_rampup / 2) - Thermostat[ctr_output].time_output_delay);
if (time_aux >= Thermostat[ctr_output].time_output_delay) { if (time_aux >= Thermostat[ctr_output].time_output_delay) {
@ -894,7 +894,7 @@ void ThermostatWorkAutomaticRampUp(uint8_t ctr_output)
// Calculate time to switch Off and come out of ramp-up // Calculate time to switch Off and come out of ramp-up
// y-y1 = m(x-x1) -> x = ((y-y1) / m) + x1 -> y1 = temp_rampup_cycle, x1 = (time_rampup_nextcycle - time_rampup_cycle), m = gradient in º/sec // y-y1 = m(x-x1) -> x = ((y-y1) / m) + x1 -> y1 = temp_rampup_cycle, x1 = (time_rampup_nextcycle - time_rampup_cycle), m = gradient in º/sec
// Better Alternative -> (y-y1)/(x-x1) = ((y2-y1)/(x2-x1)) -> where y = temp (target) and x = time (to switch off, what its needed) // Better Alternative -> (y-y1)/(x-x1) = ((y2-y1)/(x2-x1)) -> where y = temp (target) and x = time (to switch off, what its needed)
// x = ((y-y1)/(y2-y1))*(x2-x1) + x1 - deadtime // x = ((y-y1)/(y2-y1))*(x2-x1) + x1 - deadtime
aux_temp_delta =Thermostat[ctr_output].temp_target_level_ctr - Thermostat[ctr_output].temp_rampup_cycle; aux_temp_delta =Thermostat[ctr_output].temp_target_level_ctr - Thermostat[ctr_output].temp_rampup_cycle;
Thermostat[ctr_output].time_ctr_changepoint = (uint32_t)(uint32_t)(((uint32_t)(aux_temp_delta) * (uint32_t)(time_total_rampup)) / (uint32_t)temp_delta_rampup) + (uint32_t)Thermostat[ctr_output].time_rampup_nextcycle - (uint32_t)time_total_rampup - (uint32_t)Thermostat[ctr_output].time_rampup_deadtime; Thermostat[ctr_output].time_ctr_changepoint = (uint32_t)(uint32_t)(((uint32_t)(aux_temp_delta) * (uint32_t)(time_total_rampup)) / (uint32_t)temp_delta_rampup) + (uint32_t)Thermostat[ctr_output].time_rampup_nextcycle - (uint32_t)time_total_rampup - (uint32_t)Thermostat[ctr_output].time_rampup_deadtime;
@ -967,8 +967,8 @@ void ThermostatPeakDetectorInit(uint8_t ctr_output)
Thermostat[ctr_output].pU_pi_atune = 0; Thermostat[ctr_output].pU_pi_atune = 0;
Thermostat[ctr_output].kP_pi_atune = 0; Thermostat[ctr_output].kP_pi_atune = 0;
Thermostat[ctr_output].kI_pi_atune = 0; Thermostat[ctr_output].kI_pi_atune = 0;
Thermostat[ctr_output].kU_pi_atune = 0; Thermostat[ctr_output].kU_pi_atune = 0;
Thermostat[ctr_output].peak_ctr = 0; Thermostat[ctr_output].peak_ctr = 0;
Thermostat[ctr_output].temp_abs_max_atune = 0; Thermostat[ctr_output].temp_abs_max_atune = 0;
Thermostat[ctr_output].temp_abs_min_atune = 100; Thermostat[ctr_output].temp_abs_min_atune = 100;
Thermostat[ctr_output].time_ctr_checkpoint = uptime + THERMOSTAT_TIME_MAX_AUTOTUNE; Thermostat[ctr_output].time_ctr_checkpoint = uptime + THERMOSTAT_TIME_MAX_AUTOTUNE;
@ -985,7 +985,7 @@ void ThermostatPeakDetector(uint8_t ctr_output)
} }
if (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_abs_min_atune) { if (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_abs_min_atune) {
Thermostat[ctr_output].temp_abs_min_atune = Thermostat[ctr_output].temp_measured; Thermostat[ctr_output].temp_abs_min_atune = Thermostat[ctr_output].temp_measured;
} }
// For heating, even peak numbers look for maxes, odd for minds, the contrary for cooling // For heating, even peak numbers look for maxes, odd for minds, the contrary for cooling
// If we did not found all peaks yet // If we did not found all peaks yet
if (peak_num < THERMOSTAT_PEAKNUMBER_AUTOTUNE) { if (peak_num < THERMOSTAT_PEAKNUMBER_AUTOTUNE) {
@ -1020,7 +1020,7 @@ void ThermostatPeakDetector(uint8_t ctr_output)
if ( (cond_peak_2) if ( (cond_peak_2)
&& (abs(Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_peaks_atune[peak_num]) > Thermostat[ctr_output].temp_band_no_peak_det)) { && (abs(Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_peaks_atune[peak_num]) > Thermostat[ctr_output].temp_band_no_peak_det)) {
// Register peak timestamp; // Register peak timestamp;
Thermostat[ctr_output].time_peak_timestamps_atune[peak_num] = (uptime / 60); Thermostat[ctr_output].time_peak_timestamps_atune[peak_num] = (uptime / 60);
Thermostat[ctr_output].peak_ctr++; Thermostat[ctr_output].peak_ctr++;
peak_transition = true; peak_transition = true;
} }
@ -1038,9 +1038,9 @@ void ThermostatPeakDetector(uint8_t ctr_output)
// then the current peak value is the peak (min for heating, max for cooling), switch detection // then the current peak value is the peak (min for heating, max for cooling), switch detection
if ( (cond_peak_1) if ( (cond_peak_1)
&& (abs(Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_peaks_atune[peak_num]) > Thermostat[ctr_output].temp_band_no_peak_det)) { && (abs(Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_peaks_atune[peak_num]) > Thermostat[ctr_output].temp_band_no_peak_det)) {
// Calculate period // Calculate period
// Register peak timestamp; // Register peak timestamp;
Thermostat[ctr_output].time_peak_timestamps_atune[peak_num] = (uptime / 60); Thermostat[ctr_output].time_peak_timestamps_atune[peak_num] = (uptime / 60);
Thermostat[ctr_output].peak_ctr++; Thermostat[ctr_output].peak_ctr++;
peak_transition = true; peak_transition = true;
} }
@ -1051,17 +1051,17 @@ void ThermostatPeakDetector(uint8_t ctr_output)
ThermostatAutotuneParamCalc(ctr_output); ThermostatAutotuneParamCalc(ctr_output);
Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF; Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF;
} }
// If peak detection not finalized but bigger than 3 and we have just found a peak, check if results can be extracted // If peak detection not finalized but bigger than 3 and we have just found a peak, check if results can be extracted
if ((Thermostat[ctr_output].peak_ctr > 2) && (peak_transition)) { if ((Thermostat[ctr_output].peak_ctr > 2) && (peak_transition)) {
//Update peak_num //Update peak_num
peak_num = Thermostat[ctr_output].peak_ctr; peak_num = Thermostat[ctr_output].peak_ctr;
// Calculate average value among the last 3 peaks // Calculate average value among the last 3 peaks
peak_avg = (abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 1] peak_avg = (abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 1]
- Thermostat[ctr_output].temp_peaks_atune[peak_num - 2]) - Thermostat[ctr_output].temp_peaks_atune[peak_num - 2])
+ abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 2] + abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 2]
- Thermostat[ctr_output].temp_peaks_atune[peak_num - 3])) / 2; - Thermostat[ctr_output].temp_peaks_atune[peak_num - 3])) / 2;
if ((20 * (int32_t)peak_avg) < (int32_t)(Thermostat[ctr_output].temp_abs_max_atune - Thermostat[ctr_output].temp_abs_min_atune)) { if ((20 * (int32_t)peak_avg) < (int32_t)(Thermostat[ctr_output].temp_abs_max_atune - Thermostat[ctr_output].temp_abs_min_atune)) {
// Calculate average temperature among all peaks // Calculate average temperature among all peaks
for (uint8_t i = 0; i < peak_num; i++) { for (uint8_t i = 0; i < peak_num; i++) {
@ -1087,7 +1087,7 @@ void ThermostatAutotuneParamCalc(uint8_t ctr_output)
// Resolution increased to avoid float operations // Resolution increased to avoid float operations
Thermostat[ctr_output].kU_pi_atune = (uint16_t)(100 * ((uint32_t)400000 * (uint32_t)(Thermostat[ctr_output].dutycycle_step_autotune)) / ((uint32_t)(Thermostat[ctr_output].temp_abs_max_atune - Thermostat[ctr_output].temp_abs_min_atune) * (uint32_t)314159)); Thermostat[ctr_output].kU_pi_atune = (uint16_t)(100 * ((uint32_t)400000 * (uint32_t)(Thermostat[ctr_output].dutycycle_step_autotune)) / ((uint32_t)(Thermostat[ctr_output].temp_abs_max_atune - Thermostat[ctr_output].temp_abs_min_atune) * (uint32_t)314159));
Thermostat[ctr_output].pU_pi_atune = (Thermostat[ctr_output].time_peak_timestamps_atune[peak_num - 1] - Thermostat[ctr_output].time_peak_timestamps_atune[peak_num - 2]); Thermostat[ctr_output].pU_pi_atune = (Thermostat[ctr_output].time_peak_timestamps_atune[peak_num - 1] - Thermostat[ctr_output].time_peak_timestamps_atune[peak_num - 2]);
switch (Thermostat[ctr_output].status.autotune_perf_mode) { switch (Thermostat[ctr_output].status.autotune_perf_mode) {
case AUTOTUNE_PERF_FAST: case AUTOTUNE_PERF_FAST:
// Calculate kP/Ki autotune // Calculate kP/Ki autotune
@ -1120,7 +1120,7 @@ void ThermostatWorkAutomaticPIAutotune(uint8_t ctr_output)
if ((uptime < Thermostat[ctr_output].time_ctr_checkpoint) if ((uptime < Thermostat[ctr_output].time_ctr_checkpoint)
&&(Thermostat[ctr_output].temp_target_level_ctr == Thermostat[ctr_output].temp_target_level)) { &&(Thermostat[ctr_output].temp_target_level_ctr == Thermostat[ctr_output].temp_target_level)) {
if (uptime >= Thermostat[ctr_output].time_ctr_checkpoint) { if (uptime >= Thermostat[ctr_output].time_ctr_checkpoint) {
Thermostat[ctr_output].temp_target_level_ctr = Thermostat[ctr_output].temp_target_level; Thermostat[ctr_output].temp_target_level_ctr = Thermostat[ctr_output].temp_target_level;
// Calculate time_ctr_changepoint // Calculate time_ctr_changepoint
Thermostat[ctr_output].time_ctr_changepoint = uptime + (((uint32_t)Thermostat[ctr_output].time_pi_cycle * (uint32_t)Thermostat[ctr_output].dutycycle_step_autotune) / (uint32_t)100); Thermostat[ctr_output].time_ctr_changepoint = uptime + (((uint32_t)Thermostat[ctr_output].time_pi_cycle * (uint32_t)Thermostat[ctr_output].dutycycle_step_autotune) / (uint32_t)100);
// Reset cycle active // Reset cycle active
@ -1196,27 +1196,27 @@ void ThermostatWork(uint8_t ctr_output)
// State automatic thermostat active following to command target temp. // State automatic thermostat active following to command target temp.
case THERMOSTAT_AUTOMATIC_OP: case THERMOSTAT_AUTOMATIC_OP:
ThermostatCtrWork(ctr_output); ThermostatCtrWork(ctr_output);
break; break;
// State manual operation following input switch // State manual operation following input switch
case THERMOSTAT_MANUAL_OP: case THERMOSTAT_MANUAL_OP:
Thermostat[ctr_output].time_ctr_checkpoint = 0; Thermostat[ctr_output].time_ctr_checkpoint = 0;
Thermostat[ctr_output].status.command_output = Thermostat[ctr_output].status.status_input; Thermostat[ctr_output].status.command_output = Thermostat[ctr_output].status.status_input;
break; break;
} }
ThermostatOutputRelay(ctr_output, Thermostat[ctr_output].status.command_output); ThermostatOutputRelay(ctr_output, Thermostat[ctr_output].status.command_output);
} }
void ThermostatDiagnostics(uint8_t ctr_output) void ThermostatDiagnostics(uint8_t ctr_output)
{ {
// Diagnostic related to the plausibility of the output state // Diagnostic related to the plausibility of the output state
if ((Thermostat[ctr_output].diag.diagnostic_mode == DIAGNOSTIC_ON) if ((Thermostat[ctr_output].diag.diagnostic_mode == DIAGNOSTIC_ON)
&&(Thermostat[ctr_output].diag.output_inconsist_ctr >= THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST)) { &&(Thermostat[ctr_output].diag.output_inconsist_ctr >= THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST)) {
Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_OFF; Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_OFF;
Thermostat[ctr_output].diag.state_emergency = EMERGENCY_ON; Thermostat[ctr_output].diag.state_emergency = EMERGENCY_ON;
} }
// Diagnostic related to the plausibility of the output power implemented // Diagnostic related to the plausibility of the output power implemented
// already into the energy driver // already into the energy driver
// If diagnostics fail, emergency enabled and thermostat shutdown triggered // If diagnostics fail, emergency enabled and thermostat shutdown triggered
@ -1235,7 +1235,7 @@ bool ThermostatTimerArm(uint8_t ctr_output, int16_t tempVal)
{ {
bool result = false; bool result = false;
// TempVal unit is tenths of degrees celsius // TempVal unit is tenths of degrees celsius
if ((tempVal >= -1000) if ((tempVal >= -1000)
&& (tempVal <= 1000) && (tempVal <= 1000)
&& (tempVal >= (int16_t)Thermostat[ctr_output].temp_frost_protect)) { && (tempVal >= (int16_t)Thermostat[ctr_output].temp_frost_protect)) {
Thermostat[ctr_output].temp_target_level = tempVal; Thermostat[ctr_output].temp_target_level = tempVal;
@ -1295,7 +1295,7 @@ void ThermostatDebug(uint8_t ctr_output)
dtostrfd(Thermostat[ctr_output].status.sensor_alive, 0, result_chr); dtostrfd(Thermostat[ctr_output].status.sensor_alive, 0, result_chr);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.sensor_alive: %s"), result_chr); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.sensor_alive: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.status_cycle_active, 0, result_chr); dtostrfd(Thermostat[ctr_output].status.status_cycle_active, 0, result_chr);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_cycle_active: %s"), result_chr); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_cycle_active: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].temp_pi_error, 0, result_chr); dtostrfd(Thermostat[ctr_output].temp_pi_error, 0, result_chr);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_pi_error: %s"), result_chr); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_pi_error: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].temp_pi_accum_error, 0, result_chr); dtostrfd(Thermostat[ctr_output].temp_pi_accum_error, 0, result_chr);
@ -1328,16 +1328,17 @@ void ThermostatDebug(uint8_t ctr_output)
#endif // DEBUG_THERMOSTAT #endif // DEBUG_THERMOSTAT
void ThermostatGetLocalSensor(uint8_t ctr_output) { void ThermostatGetLocalSensor(uint8_t ctr_output) {
JsonParser parser(mqtt_data); String buf = mqtt_data; // copy the string into a new buffer that will be modified
JsonParser parser((char*)buf.c_str());
JsonParserObject root = parser.getRootObject(); JsonParserObject root = parser.getRootObject();
if (root) { if (root) {
JsonParserToken value_token = root[PSTR(THERMOSTAT_SENSOR_NAME)].getObject()[PSTR("Temperature")]; JsonParserToken value_token = root[PSTR(THERMOSTAT_SENSOR_NAME)].getObject()[PSTR("Temperature")];
if (value_token.isNum()) { if (value_token.isNum()) {
int16_t value = value_token.getFloat() * 10; int16_t value = value_token.getFloat() * 10;
if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) {
value = ThermostatFahrenheitToCelsius(value, TEMP_CONV_ABSOLUTE); value = ThermostatFahrenheitToCelsius(value, TEMP_CONV_ABSOLUTE);
} }
if ( (value >= -1000) if ( (value >= -1000)
&& (value <= 1000) && (value <= 1000)
&& (Thermostat[ctr_output].status.sensor_type == SENSOR_LOCAL)) { && (Thermostat[ctr_output].status.sensor_type == SENSOR_LOCAL)) {
uint32_t timestamp = uptime; uint32_t timestamp = uptime;
@ -1403,7 +1404,7 @@ void CmndTempFrostProtectSet(void)
else { else {
value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10);
} }
if ( (value >= 0) if ( (value >= 0)
&& (value <= 127)) { && (value <= 127)) {
Thermostat[ctr_output].temp_frost_protect = (uint8_t)value; Thermostat[ctr_output].temp_frost_protect = (uint8_t)value;
} }
@ -1534,7 +1535,7 @@ void CmndTempMeasuredSet(void)
else { else {
value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10);
} }
if ( (value >= -1000) if ( (value >= -1000)
&& (value <= 1000) && (value <= 1000)
&& (Thermostat[ctr_output].status.sensor_type == SENSOR_MQTT)) { && (Thermostat[ctr_output].status.sensor_type == SENSOR_MQTT)) {
uint32_t timestamp = uptime; uint32_t timestamp = uptime;
@ -1572,7 +1573,7 @@ void CmndTempTargetSet(void)
else { else {
value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10);
} }
if ( (value >= -1000) if ( (value >= -1000)
&& (value <= 1000) && (value <= 1000)
&& (value >= (int16_t)Thermostat[ctr_output].temp_frost_protect)) { && (value >= (int16_t)Thermostat[ctr_output].temp_frost_protect)) {
Thermostat[ctr_output].temp_target_level = value; Thermostat[ctr_output].temp_target_level = value;
@ -1701,7 +1702,7 @@ void CmndTempAntiWindupResetSet(void)
else { else {
value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10);
} }
if ( (value >= 0) if ( (value >= 0)
&& (value <= 100)) { && (value <= 100)) {
Thermostat[ctr_output].temp_reset_anti_windup = value; Thermostat[ctr_output].temp_reset_anti_windup = value;
} }
@ -1728,7 +1729,7 @@ void CmndTempHystSet(void)
else { else {
value = (int8_t)(CharToFloat(XdrvMailbox.data) * 10); value = (int8_t)(CharToFloat(XdrvMailbox.data) * 10);
} }
if ( (value >= -100) if ( (value >= -100)
&& (value <= 100)) { && (value <= 100)) {
Thermostat[ctr_output].temp_hysteresis = value; Thermostat[ctr_output].temp_hysteresis = value;
} }
@ -1827,7 +1828,7 @@ void CmndTempRupDeltInSet(void)
else { else {
value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10);
} }
if ( (value >= 0) if ( (value >= 0)
&& (value <= 100)) { && (value <= 100)) {
Thermostat[ctr_output].temp_rampup_delta_in = value; Thermostat[ctr_output].temp_rampup_delta_in = value;
} }
@ -1854,7 +1855,7 @@ void CmndTempRupDeltOutSet(void)
else { else {
value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10);
} }
if ( (value >= 0) if ( (value >= 0)
&& (value <= 100)) { && (value <= 100)) {
Thermostat[ctr_output].temp_rampup_delta_out = value; Thermostat[ctr_output].temp_rampup_delta_out = value;
} }
@ -1909,7 +1910,7 @@ void CmndTempRampupPiAccErrSet(void)
else { else {
value = (uint16_t)(CharToFloat(XdrvMailbox.data) * 100); value = (uint16_t)(CharToFloat(XdrvMailbox.data) * 100);
} }
if ( (value >= 0) if ( (value >= 0)
&& (value <= 2500)) { && (value <= 2500)) {
Thermostat[ctr_output].temp_rampup_pi_acc_error = value; Thermostat[ctr_output].temp_rampup_pi_acc_error = value;
} }