diff --git a/src/MainTask.h b/src/MainTask.h index f97329b..f087921 100644 --- a/src/MainTask.h +++ b/src/MainTask.h @@ -225,7 +225,7 @@ protected: uint8_t availableSensors = 0; if (Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) { - auto value = Sensors::getMeanValueByPurpose(Sensors::Purpose::INDOOR_TEMP, Sensors::ValueType::PRIMARY); + auto value = Sensors::getMeanValueByPurpose(Sensors::Purpose::INDOOR_TEMP, Sensors::ValueType::PRIMARY, settings.heating.indoorTempAvgType); if (value < lowTemp) { lowTemp = value; } diff --git a/src/Sensors.h b/src/Sensors.h index bd9b524..53b9285 100644 --- a/src/Sensors.h +++ b/src/Sensors.h @@ -77,6 +77,12 @@ public: RSSI = 3 }; + enum class AverageType : uint8_t { + MEAN = 0, + MINIMUM = 1, + MAXIMUM = 2 + }; + typedef struct { bool enabled = false; char name[33]; @@ -330,7 +336,7 @@ public: return updated; } - static float getMeanValueByPurpose(Purpose purpose, const ValueType valueType, bool onlyConnected = true) { + static float getMeanValueByPurpose(Purpose purpose, const ValueType valueType, const AverageType avgType = AverageType::MEAN, bool onlyConnected = true) { if (settings == nullptr || results == nullptr) { return 0.0f; } @@ -340,27 +346,65 @@ public: return 0.0f; } - float value = 0.0f; - uint8_t amount = 0; + if (avgType == AverageType::MEAN) { + float value = 0.0f; + uint8_t amount = 0; - for (uint8_t id = 0; id <= getMaxSensorId(); id++) { - auto& sSensor = settings[id]; - auto& rSensor = results[id]; + for (uint8_t id = 0; id <= getMaxSensorId(); id++) { + auto& sSensor = settings[id]; + auto& rSensor = results[id]; - if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) { - value += rSensor.values[valueId]; - amount++; + if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) { + value += rSensor.values[valueId]; + amount++; + } } - } - if (!amount) { - return 0.0f; - - } else if (amount == 1) { - return value; + if (!amount) { + return 0.0f; + + } else if (amount == 1) { + return value; + + } else { + return value / amount; + } + + } else if (avgType == AverageType::MINIMUM) { + float value = NAN; + + for (uint8_t id = 0; id <= getMaxSensorId(); id++) { + auto& sSensor = settings[id]; + auto& rSensor = results[id]; + + if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) { + if (value == NAN || rSensor.values[valueId] < value) { + value = rSensor.values[valueId]; + } + } + } + + return value != NAN ? value : 0.0f; + + } else if (avgType == AverageType::MAXIMUM) { + float value = NAN; + + for (uint8_t id = 0; id <= getMaxSensorId(); id++) { + auto& sSensor = settings[id]; + auto& rSensor = results[id]; + + if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) { + if (value == NAN || rSensor.values[valueId] > value) { + value = rSensor.values[valueId]; + } + } + } + + return value != NAN ? value : 0.0f; } else { - return value / amount; + // bad mean type + return 0.0f; } } diff --git a/src/SensorsTask.h b/src/SensorsTask.h index 7c5fa6e..09b0333 100644 --- a/src/SensorsTask.h +++ b/src/SensorsTask.h @@ -423,8 +423,8 @@ protected: } void updateMasterValues() { - vars.master.heating.outdoorTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::OUTDOOR_TEMP, Sensors::ValueType::PRIMARY); - vars.master.heating.indoorTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::INDOOR_TEMP, Sensors::ValueType::PRIMARY); + vars.master.heating.outdoorTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::OUTDOOR_TEMP, Sensors::ValueType::PRIMARY, settings.heating.outdoorTempAvgType); + vars.master.heating.indoorTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::INDOOR_TEMP, Sensors::ValueType::PRIMARY, settings.heating.indoorTempAvgType); vars.master.heating.currentTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::HEATING_TEMP, Sensors::ValueType::PRIMARY); vars.master.heating.returnTemp = Sensors::getMeanValueByPurpose(Sensors::Purpose::HEATING_RETURN_TEMP, Sensors::ValueType::PRIMARY); diff --git a/src/Settings.h b/src/Settings.h index 62e6289..2f3e23a 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -107,6 +107,8 @@ struct Settings { uint8_t minTemp = DEFAULT_HEATING_MIN_TEMP; uint8_t maxTemp = DEFAULT_HEATING_MAX_TEMP; uint8_t maxModulation = 100; + Sensors::AverageType indoorTempAvgType = Sensors::AverageType::MEAN; + Sensors::AverageType outdoorTempAvgType = Sensors::AverageType::MEAN; struct { bool enabled = true; diff --git a/src/strings.h b/src/strings.h index c7a17af..d4b8c62 100644 --- a/src/strings.h +++ b/src/strings.h @@ -115,6 +115,7 @@ const char S_IGNORE_DIAG_STATE[] PROGMEM = "ignoreDiagState"; const char S_IMMERGAS_FIX[] PROGMEM = "immergasFix"; const char S_ALWAYS_SEND_INDOOR_TEMP[] PROGMEM = "alwaysSendIndoorTemp"; const char S_INDOOR_TEMP[] PROGMEM = "indoorTemp"; +const char S_INDOOR_TEMP_AVG_TYPE[] PROGMEM = "indoorTempAvgType"; const char S_INDOOR_TEMP_CONTROL[] PROGMEM = "indoorTempControl"; const char S_IN_GPIO[] PROGMEM = "inGpio"; const char S_INPUT[] PROGMEM = "input"; @@ -155,6 +156,7 @@ const char S_ON_LOSS_CONNECTION[] PROGMEM = "onLossConnection" const char S_OPENTHERM[] PROGMEM = "opentherm"; const char S_OPTIONS[] PROGMEM = "options"; const char S_OUTDOOR_TEMP[] PROGMEM = "outdoorTemp"; +const char S_OUTDOOR_TEMP_AVG_TYPE[] PROGMEM = "outdoorTempAvgType"; const char S_OUT_GPIO[] PROGMEM = "outGpio"; const char S_OUTPUT[] PROGMEM = "output"; const char S_OVERHEAT[] PROGMEM = "overheat"; diff --git a/src/utils.h b/src/utils.h index ea4f5c8..52c5d13 100644 --- a/src/utils.h +++ b/src/utils.h @@ -498,6 +498,8 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) { heating[FPSTR(S_MIN_TEMP)] = src.heating.minTemp; heating[FPSTR(S_MAX_TEMP)] = src.heating.maxTemp; heating[FPSTR(S_MAX_MODULATION)] = src.heating.maxModulation; + heating[FPSTR(S_INDOOR_TEMP_AVG_TYPE)] = static_cast(src.heating.indoorTempAvgType); + heating[FPSTR(S_OUTDOOR_TEMP_AVG_TYPE)] = static_cast(src.heating.outdoorTempAvgType); auto heatingOverheatProtection = heating[FPSTR(S_OVERHEAT_PROTECTION)].to(); heatingOverheatProtection[FPSTR(S_HIGH_TEMP)] = src.heating.overheatProtection.highTemp; @@ -1393,6 +1395,42 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false } } + if (!src[FPSTR(S_HEATING)][FPSTR(S_INDOOR_TEMP_AVG_TYPE)].isNull()) { + uint8_t value = src[FPSTR(S_HEATING)][FPSTR(S_INDOOR_TEMP_AVG_TYPE)].as(); + + switch (value) { + case static_cast(Sensors::AverageType::MEAN): + case static_cast(Sensors::AverageType::MINIMUM): + case static_cast(Sensors::AverageType::MAXIMUM): + if (static_cast(dst.heating.indoorTempAvgType) != value) { + dst.heating.indoorTempAvgType = static_cast(value); + changed = true; + } + break; + + default: + break; + } + } + + if (!src[FPSTR(S_HEATING)][FPSTR(S_OUTDOOR_TEMP_AVG_TYPE)].isNull()) { + uint8_t value = src[FPSTR(S_HEATING)][FPSTR(S_OUTDOOR_TEMP_AVG_TYPE)].as(); + + switch (value) { + case static_cast(Sensors::AverageType::MEAN): + case static_cast(Sensors::AverageType::MINIMUM): + case static_cast(Sensors::AverageType::MAXIMUM): + if (static_cast(dst.heating.outdoorTempAvgType) != value) { + dst.heating.outdoorTempAvgType = static_cast(value); + changed = true; + } + break; + + default: + break; + } + } + if (!src[FPSTR(S_HEATING)][FPSTR(S_OVERHEAT_PROTECTION)][FPSTR(S_HIGH_TEMP)].isNull()) { unsigned char value = src[FPSTR(S_HEATING)][FPSTR(S_OVERHEAT_PROTECTION)][FPSTR(S_HIGH_TEMP)].as(); diff --git a/src_data/locales/en.json b/src_data/locales/en.json index 90e2a68..deba603 100644 --- a/src_data/locales/en.json +++ b/src_data/locales/en.json @@ -315,6 +315,11 @@ "min": "Minimum temperature", "max": "Maximum temperature" }, + "avgType": { + "mean": "Mean temperature", + "min": "Minimum temperature", + "max": "Maximum temperature" + }, "maxModulation": "Max modulation level", "ohProtection": { "title": "Overheating protection", @@ -379,7 +384,15 @@ "set0target": "Set null target" } }, - "turboFactor": "Turbo mode coeff." + "turboFactor": "Turbo mode coeff.", + "indoorTempAvgType": { + "title": "Indoor temp. averaging type", + "desc": "May be useful when using two or more indoor temp. sensors (when using «Equitherm» and/or «PID»)." + }, + "outdoorTempAvgType": { + "title": "Outdoor temp. averaging type", + "desc": "May be useful when using two or more outdoor temp. sensors (when using «Equitherm»)." + } }, "emergency": { diff --git a/src_data/pages/settings.html b/src_data/pages/settings.html index 68276fa..63b765c 100644 --- a/src_data/pages/settings.html +++ b/src_data/pages/settings.html @@ -198,6 +198,28 @@ +
+ + + +
+
@@ -1171,6 +1193,8 @@ setSelectValue("[name='heating[hysteresis][action]']", data.heating.hysteresis.action); setInputValue("[name='heating[turboFactor]']", data.heating.turboFactor); setInputValue("[name='heating[maxModulation]']", data.heating.maxModulation); + setSelectValue("[name='heating[indoorTempAvgType]']", data.heating.indoorTempAvgType); + setSelectValue("[name='heating[outdoorTempAvgType]']", data.heating.outdoorTempAvgType); setInputValue("[name='heating[overheatProtection][highTemp]']", data.heating.overheatProtection.highTemp, { "min": 0, "max": data.system.unitSystem == 0 ? 100 : 212