refactor: dynamic sensors

This commit is contained in:
Yurii
2024-11-09 17:10:26 +03:00
parent c3d0d94806
commit e71f3868fd
27 changed files with 4666 additions and 3388 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ using namespace NetworkUtils;
extern NetworkMgr* network;
extern MqttTask* tMqtt;
extern OpenThermTask* tOt;
extern FileData fsSettings, fsNetworkSettings;
extern FileData fsNetworkSettings, fsSettings, fsSensorsSettings;
extern ESPTelnetStream* telnetStream;
@@ -60,12 +60,16 @@ protected:
void loop() {
network->loop();
if (fsNetworkSettings.tick() == FD_WRITE) {
Log.sinfoln(FPSTR(L_NETWORK_SETTINGS), F("Updated"));
}
if (fsSettings.tick() == FD_WRITE) {
Log.sinfoln(FPSTR(L_SETTINGS), F("Updated"));
}
if (fsNetworkSettings.tick() == FD_WRITE) {
Log.sinfoln(FPSTR(L_NETWORK_SETTINGS), F("Updated"));
if (fsSensorsSettings.tick() == FD_WRITE) {
Log.sinfoln(FPSTR(L_SENSORS_SETTINGS), F("Updated"));
}
if (vars.actions.restart) {
@@ -75,6 +79,9 @@ protected:
// save settings
fsSettings.updateNow();
// save sensors settings
fsSensorsSettings.updateNow();
// force save network settings
if (fsNetworkSettings.updateNow() == FD_FILE_ERR && LittleFS.begin()) {
fsNetworkSettings.write();
@@ -83,8 +90,9 @@ protected:
Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec."));
}
vars.states.mqtt = tMqtt->isConnected();
vars.sensors.rssi = network->isConnected() ? WiFi.RSSI() : 0;
vars.mqtt.connected = tMqtt->isConnected();
vars.network.connected = network->isConnected();
vars.network.rssi = network->isConnected() ? WiFi.RSSI() : 0;
if (settings.system.logLevel >= TinyLogger::Level::SILENT && settings.system.logLevel <= TinyLogger::Level::VERBOSE) {
if (Log.getLevel() != settings.system.logLevel) {
@@ -98,20 +106,14 @@ protected:
this->telnetStarted = true;
}
if (settings.mqtt.enable && !tMqtt->isEnabled()) {
if (settings.mqtt.enabled && !tMqtt->isEnabled()) {
tMqtt->enable();
} else if (!settings.mqtt.enable && tMqtt->isEnabled()) {
} else if (!settings.mqtt.enabled && tMqtt->isEnabled()) {
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;
}
Sensors::setConnectionStatusByType(Sensors::Type::MANUAL, !settings.mqtt.enabled || vars.mqtt.connected, false);
} else {
if (this->telnetStarted) {
@@ -123,13 +125,7 @@ protected:
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;
}
Sensors::setConnectionStatusByType(Sensors::Type::MANUAL, false, false);
}
this->yield();
@@ -209,18 +205,21 @@ protected:
uint8_t emergencyFlags = 0b00000000;
// set outdoor sensor flag
if (settings.equitherm.enable && !vars.sensors.outdoor.connected) {
emergencyFlags |= 0b00000001;
if (settings.equitherm.enabled) {
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) {
emergencyFlags |= 0b00000001;
}
}
// set indoor sensor flag
if (!settings.equitherm.enable && settings.pid.enable && !vars.sensors.indoor.connected) {
emergencyFlags |= 0b00000010;
}
// set indoor sensor flag for OT native heating control
if (settings.opentherm.nativeHeatingControl && !vars.sensors.indoor.connected) {
emergencyFlags |= 0b00000100;
// set indoor sensor flags
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP)) {
if (!settings.equitherm.enabled && settings.pid.enabled) {
emergencyFlags |= 0b00000010;
}
if (settings.opentherm.nativeHeatingControl) {
emergencyFlags |= 0b00000100;
}
}
// if any flags is true
@@ -230,10 +229,10 @@ protected:
this->emergencyDetected = true;
this->emergencyFlipTime = millis();
} else if (this->emergencyDetected && !vars.states.emergency) {
} else if (this->emergencyDetected && !vars.emergency.state) {
// enable emergency
if (millis() - this->emergencyFlipTime > (settings.emergency.tresholdTime * 1000)) {
vars.states.emergency = true;
vars.emergency.state = true;
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode enabled (%hhu)"), emergencyFlags);
}
}
@@ -244,10 +243,10 @@ protected:
this->emergencyDetected = false;
this->emergencyFlipTime = millis();
} else if (!this->emergencyDetected && vars.states.emergency) {
} else if (!this->emergencyDetected && vars.emergency.state) {
// disable emergency
if (millis() - this->emergencyFlipTime > (settings.emergency.tresholdTime * 1000)) {
vars.states.emergency = false;
vars.emergency.state = false;
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode disabled"));
}
}
@@ -286,15 +285,15 @@ protected:
errors[errCount++] = 2;
}
if (!vars.states.otStatus) {
if (!vars.slave.connected) {
errors[errCount++] = 3;
}
if (vars.states.fault) {
if (vars.slave.fault.active) {
errors[errCount++] = 4;
}
if (vars.states.emergency) {
if (vars.emergency.state) {
errors[errCount++] = 5;
}
@@ -342,7 +341,7 @@ protected:
static unsigned long outputChangedTs = 0;
// input
if (settings.cascadeControl.input.enable) {
if (settings.cascadeControl.input.enabled) {
if (settings.cascadeControl.input.gpio != configuredInputGpio) {
if (configuredInputGpio != GPIO_IS_NOT_CONFIGURED) {
pinMode(configuredInputGpio, OUTPUT);
@@ -393,7 +392,7 @@ protected:
}
}
if (!settings.cascadeControl.input.enable || configuredInputGpio == GPIO_IS_NOT_CONFIGURED) {
if (!settings.cascadeControl.input.enabled || configuredInputGpio == GPIO_IS_NOT_CONFIGURED) {
if (!vars.cascadeControl.input) {
vars.cascadeControl.input = true;
@@ -407,7 +406,7 @@ protected:
// output
if (settings.cascadeControl.output.enable) {
if (settings.cascadeControl.output.enabled) {
if (settings.cascadeControl.output.gpio != configuredOutputGpio) {
if (configuredOutputGpio != GPIO_IS_NOT_CONFIGURED) {
pinMode(configuredOutputGpio, OUTPUT);
@@ -437,13 +436,13 @@ protected:
if (configuredOutputGpio != GPIO_IS_NOT_CONFIGURED) {
bool value = false;
if (settings.cascadeControl.output.onFault && vars.states.fault) {
if (settings.cascadeControl.output.onFault && vars.slave.fault.active) {
value = true;
} else if (settings.cascadeControl.output.onLossConnection && !vars.states.otStatus) {
} else if (settings.cascadeControl.output.onLossConnection && !vars.slave.connected) {
value = true;
} else if (settings.cascadeControl.output.onEnabledHeating && settings.heating.enable && vars.cascadeControl.input) {
} else if (settings.cascadeControl.output.onEnabledHeating && settings.heating.enabled && vars.cascadeControl.input) {
value = true;
}
@@ -475,7 +474,7 @@ protected:
}
}
if (!settings.cascadeControl.output.enable || configuredOutputGpio == GPIO_IS_NOT_CONFIGURED) {
if (!settings.cascadeControl.output.enabled || configuredOutputGpio == GPIO_IS_NOT_CONFIGURED) {
if (vars.cascadeControl.output) {
vars.cascadeControl.output = false;
@@ -516,75 +515,75 @@ protected:
}
if (configuredGpio == GPIO_IS_NOT_CONFIGURED) {
if (vars.states.externalPump) {
vars.states.externalPump = false;
vars.parameters.extPumpLastEnableTime = millis();
if (vars.externalPump.state) {
vars.externalPump.state = false;
vars.externalPump.lastEnableTime = millis();
Log.sinfoln("EXTPUMP", F("Disabled: use = off"));
Log.sinfoln(FPSTR(L_EXTPUMP), F("Disabled: use = off"));
}
return;
}
if (!vars.states.heating && this->heatingEnabled) {
if (!vars.master.heating.enabled && this->heatingEnabled) {
this->heatingEnabled = false;
this->heatingDisabledTime = millis();
} else if (vars.states.heating && !this->heatingEnabled) {
} else if (vars.master.heating.enabled && !this->heatingEnabled) {
this->heatingEnabled = true;
}
if (!settings.externalPump.use) {
if (vars.states.externalPump) {
if (vars.externalPump.state) {
digitalWrite(configuredGpio, LOW);
vars.states.externalPump = false;
vars.parameters.extPumpLastEnableTime = millis();
vars.externalPump.state = false;
vars.externalPump.lastEnableTime = millis();
Log.sinfoln("EXTPUMP", F("Disabled: use = off"));
Log.sinfoln(FPSTR(L_EXTPUMP), F("Disabled: use = off"));
}
return;
}
if (vars.states.externalPump && !this->heatingEnabled) {
if (vars.externalPump.state && !this->heatingEnabled) {
if (this->extPumpStartReason == MainTask::PumpStartReason::HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
digitalWrite(configuredGpio, LOW);
vars.states.externalPump = false;
vars.parameters.extPumpLastEnableTime = millis();
vars.externalPump.state = false;
vars.externalPump.lastEnableTime = millis();
Log.sinfoln("EXTPUMP", F("Disabled: expired post circulation time"));
Log.sinfoln(FPSTR(L_EXTPUMP), F("Disabled: expired post circulation time"));
} else if (this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) {
digitalWrite(configuredGpio, LOW);
vars.states.externalPump = false;
vars.parameters.extPumpLastEnableTime = millis();
vars.externalPump.state = false;
vars.externalPump.lastEnableTime = millis();
Log.sinfoln("EXTPUMP", F("Disabled: expired anti stuck time"));
Log.sinfoln(FPSTR(L_EXTPUMP), F("Disabled: expired anti stuck time"));
}
} else if (vars.states.externalPump && this->heatingEnabled && this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK) {
} else if (vars.externalPump.state && this->heatingEnabled && this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK) {
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
} else if (!vars.states.externalPump && this->heatingEnabled) {
vars.states.externalPump = true;
} else if (!vars.externalPump.state && this->heatingEnabled) {
vars.externalPump.state = true;
this->externalPumpStartTime = millis();
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
digitalWrite(configuredGpio, HIGH);
Log.sinfoln("EXTPUMP", F("Enabled: heating on"));
Log.sinfoln(FPSTR(L_EXTPUMP), F("Enabled: heating on"));
} else if (!vars.states.externalPump && (vars.parameters.extPumpLastEnableTime == 0 || millis() - vars.parameters.extPumpLastEnableTime >= (settings.externalPump.antiStuckInterval * 1000ul))) {
vars.states.externalPump = true;
} else if (!vars.externalPump.state && (vars.externalPump.lastEnableTime == 0 || millis() - vars.externalPump.lastEnableTime >= (settings.externalPump.antiStuckInterval * 1000ul))) {
vars.externalPump.state = true;
this->externalPumpStartTime = millis();
this->extPumpStartReason = MainTask::PumpStartReason::ANTISTUCK;
digitalWrite(configuredGpio, HIGH);
Log.sinfoln("EXTPUMP", F("Enabled: anti stuck"));
Log.sinfoln(FPSTR(L_EXTPUMP), F("Enabled: anti stuck"));
}
}
};

View File

@@ -1,3 +1,4 @@
#include <unordered_map>
#include <MqttClient.h>
#include <MqttWiFiClient.h>
#include <MqttWriter.h>
@@ -61,10 +62,18 @@ public:
this->prevPubSettingsTime = 0;
}
inline void resetPublishedSensorTime(uint8_t sensorId) {
this->prevPubSensorTime[sensorId] = 0;
}
inline void resetPublishedVarsTime() {
this->prevPubVarsTime = 0;
}
inline void rebuildHaEntity(uint8_t sensorId, Sensors::Settings& prevSettings) {
this->queueRebuildingHaEntities[sensorId] = prevSettings;
}
protected:
MqttWiFiClient* wifiClient = nullptr;
MqttClient* client = nullptr;
@@ -72,12 +81,14 @@ protected:
MqttWriter* writer = nullptr;
UnitSystem currentUnitSystem = UnitSystem::METRIC;
bool currentHomeAssistantDiscovery = false;
std::unordered_map<uint8_t, Sensors::Settings> queueRebuildingHaEntities;
unsigned short readyForSendTime = 30000;
unsigned long lastReconnectTime = 0;
unsigned long connectedTime = 0;
unsigned long disconnectedTime = 0;
unsigned long prevPubVarsTime = 0;
unsigned long prevPubSettingsTime = 0;
std::unordered_map<uint8_t, unsigned long> prevPubSensorTime;
bool connected = false;
bool newConnection = false;
@@ -173,11 +184,6 @@ protected:
}
void loop() {
if (settings.mqtt.interval > 120) {
settings.mqtt.interval = 5;
fsSettings.update();
}
if (this->connected && !this->client->connected()) {
this->connected = false;
this->onDisconnect();
@@ -226,6 +232,28 @@ protected:
this->prevPubSettingsTime = millis();
}
// publish sensors
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
if (!Sensors::hasEnabledAndValid(sensorId)) {
continue;
}
auto& rSensor = Sensors::results[sensorId];
bool needUpdate = false;
if (millis() - this->prevPubSensorTime[sensorId] > ((this->haHelper->getExpireAfter() - 10) * 1000u)) {
needUpdate = true;
} else if (rSensor.activityTime >= this->prevPubSensorTime[sensorId]) {
auto estimated = rSensor.activityTime - this->prevPubSensorTime[sensorId];
needUpdate = estimated > 1000u;
}
if (this->newConnection || needUpdate) {
this->publishSensor(sensorId);
this->prevPubSensorTime[sensorId] = millis();
}
}
// publish ha entities if not published
if (settings.mqtt.homeAssistantDiscovery) {
if (this->newConnection || !this->currentHomeAssistantDiscovery || this->currentUnitSystem != settings.system.unitSystem) {
@@ -239,6 +267,79 @@ protected:
this->publishNonStaticHaEntities();
}
for (auto& [sensorId, prevSettings] : this->queueRebuildingHaEntities) {
Log.sinfoln(FPSTR(L_MQTT_HA), F("Rebuilding config for sensor #%hhu '%s'"), sensorId, prevSettings.name);
// delete old config
if (strlen(prevSettings.name) && prevSettings.enabled) {
switch (prevSettings.type) {
case Sensors::Type::BLUETOOTH:
this->haHelper->deleteConnectionDynamicSensor(prevSettings);
this->haHelper->deleteSignalQualityDynamicSensor(prevSettings);
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::TEMPERATURE);
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::HUMIDITY);
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::BATTERY);
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::RSSI);
break;
case Sensors::Type::DALLAS_TEMP:
this->haHelper->deleteConnectionDynamicSensor(prevSettings);
this->haHelper->deleteSignalQualityDynamicSensor(prevSettings);
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::TEMPERATURE);
break;
case Sensors::Type::MANUAL: {
String topic = this->haHelper->getDeviceTopic(
F("sensors"),
Sensors::makeObjectId(prevSettings.name),
F("set")
);
this->client->unsubscribe(topic.c_str());
}
default:
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::PRIMARY);
}
}
if (!Sensors::hasEnabledAndValid(sensorId)) {
continue;
}
// make new config
auto& sSettings = Sensors::settings[sensorId];
switch (sSettings.type) {
case Sensors::Type::BLUETOOTH:
this->haHelper->publishConnectionDynamicSensor(sSettings);
this->haHelper->publishSignalQualityDynamicSensor(sSettings, false);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::HUMIDITY, settings.system.unitSystem);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::BATTERY, settings.system.unitSystem);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::RSSI, settings.system.unitSystem, false);
break;
case Sensors::Type::DALLAS_TEMP:
this->haHelper->publishConnectionDynamicSensor(sSettings);
this->haHelper->publishSignalQualityDynamicSensor(sSettings, false);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
break;
case Sensors::Type::MANUAL: {
String topic = this->haHelper->getDeviceTopic(
F("sensors"),
Sensors::makeObjectId(prevSettings.name),
F("set")
);
this->client->subscribe(topic.c_str());
}
default:
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem);
}
}
this->queueRebuildingHaEntities.clear();
} else if (this->currentHomeAssistantDiscovery) {
this->currentHomeAssistantDiscovery = false;
}
@@ -303,33 +404,51 @@ protected:
doc.shrinkToFit();
if (this->haHelper->getDeviceTopic("state/set").equals(topic)) {
this->writer->publish(this->haHelper->getDeviceTopic("state/set").c_str(), nullptr, 0, true);
this->writer->publish(topic, nullptr, 0, true);
if (jsonToVars(doc, vars)) {
this->resetPublishedVarsTime();
}
} else if (this->haHelper->getDeviceTopic("settings/set").equals(topic)) {
this->writer->publish(this->haHelper->getDeviceTopic("settings/set").c_str(), nullptr, 0, true);
this->writer->publish(topic, nullptr, 0, true);
if (safeJsonToSettings(doc, settings)) {
this->resetPublishedSettingsTime();
fsSettings.update();
}
} else {
this->writer->publish(topic, nullptr, 0, true);
String _topic = topic;
String sensorsTopic = this->haHelper->getDeviceTopic("sensors/");
unsigned short stLength = sensorsTopic.length();
if (_topic.startsWith(sensorsTopic) && _topic.endsWith("/set")) {
if (_topic.length() > stLength + 4) {
String name = _topic.substring(stLength, _topic.indexOf('/', stLength));
int16_t id = Sensors::getIdByObjectId(name.c_str());
if (id == -1) {
return;
}
if (jsonToSensorResult(id, doc)) {
this->resetPublishedSensorTime(id);
}
}
}
}
}
void publishHaEntities() {
// heating
this->haHelper->publishSwitchHeating(false);
this->haHelper->publishSwitchHeatingTurbo(false);
this->haHelper->publishInputHeatingHysteresis(settings.system.unitSystem);
this->haHelper->publishInputHeatingTurboFactor(false);
this->haHelper->publishInputHeatingMinTemp(settings.system.unitSystem);
this->haHelper->publishInputHeatingMaxTemp(settings.system.unitSystem);
this->haHelper->publishSensorHeatingSetpoint(settings.system.unitSystem, false);
this->haHelper->publishSensorBoilerHeatingMinTemp(settings.system.unitSystem, false);
this->haHelper->publishSensorBoilerHeatingMaxTemp(settings.system.unitSystem, false);
// pid
this->haHelper->publishSwitchPid();
@@ -347,103 +466,98 @@ protected:
this->haHelper->publishInputEquithermFactorT(false);
// states
this->haHelper->publishStateStatus();
this->haHelper->publishStateEmergency();
this->haHelper->publishStateOtStatus();
this->haHelper->publishStateHeating();
this->haHelper->publishStateFlame();
this->haHelper->publishStateFault();
this->haHelper->publishStateDiagnostic();
this->haHelper->publishStateExtPump(false);
this->haHelper->publishStatusState();
this->haHelper->publishEmergencyState();
this->haHelper->publishOpenthermConnectedState();
this->haHelper->publishHeatingState();
this->haHelper->publishFlameState();
this->haHelper->publishFaultState();
this->haHelper->publishDiagState();
this->haHelper->publishExternalPumpState(false);
// sensors
this->haHelper->publishSensorModulation();
this->haHelper->publishSensorPressure(settings.system.unitSystem, false);
this->haHelper->publishSensorPower();
this->haHelper->publishSensorFaultCode();
this->haHelper->publishSensorDiagnosticCode();
this->haHelper->publishSensorRssi(false);
this->haHelper->publishSensorUptime(false);
this->haHelper->publishOutdoorSensorConnected();
this->haHelper->publishOutdoorSensorRssi(false);
this->haHelper->publishOutdoorSensorBattery(false);
this->haHelper->publishOutdoorSensorHumidity(false);
this->haHelper->publishIndoorSensorConnected();
this->haHelper->publishIndoorSensorRssi(false);
this->haHelper->publishIndoorSensorBattery(false);
this->haHelper->publishIndoorSensorHumidity(false);
// temperatures
this->haHelper->publishSensorHeatingTemp(settings.system.unitSystem);
this->haHelper->publishSensorHeatingReturnTemp(settings.system.unitSystem, false);
this->haHelper->publishSensorExhaustTemp(settings.system.unitSystem, false);
this->haHelper->publishFaultCode();
this->haHelper->publishDiagCode();
this->haHelper->publishNetworkRssi(false);
this->haHelper->publishUptime(false);
// buttons
this->haHelper->publishButtonRestart(false);
this->haHelper->publishButtonResetFault();
this->haHelper->publishButtonResetDiagnostic();
this->haHelper->publishRestartButton(false);
this->haHelper->publishResetFaultButton();
this->haHelper->publishResetDiagButton();
// dynamic sensors
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
if (!Sensors::hasEnabledAndValid(sensorId)) {
continue;
}
auto& sSettings = Sensors::settings[sensorId];
switch (sSettings.type) {
case Sensors::Type::BLUETOOTH:
this->haHelper->publishConnectionDynamicSensor(sSettings);
this->haHelper->publishSignalQualityDynamicSensor(sSettings, false);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::HUMIDITY, settings.system.unitSystem);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::BATTERY, settings.system.unitSystem);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::RSSI, settings.system.unitSystem, false);
break;
case Sensors::Type::DALLAS_TEMP:
this->haHelper->publishConnectionDynamicSensor(sSettings);
this->haHelper->publishSignalQualityDynamicSensor(sSettings, false);
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
break;
case Sensors::Type::MANUAL: {
String topic = this->haHelper->getDeviceTopic(
F("sensors"),
Sensors::makeObjectId(sSettings.name),
F("set")
);
this->client->subscribe(topic.c_str());
}
default:
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem);
}
}
}
bool publishNonStaticHaEntities(bool force = false) {
static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp = 0;
static bool _noRegulators, _editableOutdoorTemp, _editableIndoorTemp, _dhwPresent = false;
static bool _indoorTempControl, _dhwPresent = false;
bool published = false;
bool noRegulators = !settings.opentherm.nativeHeatingControl && !settings.pid.enable && !settings.equitherm.enable;
byte heatingMinTemp = 0;
byte heatingMaxTemp = 0;
bool editableOutdoorTemp = settings.sensors.outdoor.type == SensorType::MANUAL;
bool editableIndoorTemp = settings.sensors.indoor.type == SensorType::MANUAL;
if (noRegulators) {
heatingMinTemp = settings.heating.minTemp;
heatingMaxTemp = settings.heating.maxTemp;
} else {
heatingMinTemp = convertTemp(THERMOSTAT_INDOOR_MIN_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
heatingMaxTemp = convertTemp(THERMOSTAT_INDOOR_MAX_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
}
if (force || _dhwPresent != settings.opentherm.dhwPresent) {
_dhwPresent = settings.opentherm.dhwPresent;
if (_dhwPresent) {
this->haHelper->publishSwitchDhw(false);
this->haHelper->publishSensorBoilerDhwMinTemp(settings.system.unitSystem, false);
this->haHelper->publishSensorBoilerDhwMaxTemp(settings.system.unitSystem, false);
this->haHelper->publishInputDhwMinTemp(settings.system.unitSystem);
this->haHelper->publishInputDhwMaxTemp(settings.system.unitSystem);
this->haHelper->publishStateDhw();
this->haHelper->publishSensorDhwTemp(settings.system.unitSystem);
this->haHelper->publishSensorDhwFlowRate(settings.system.unitSystem);
this->haHelper->publishDhwState();
} else {
this->haHelper->deleteSwitchDhw();
this->haHelper->deleteSensorBoilerDhwMinTemp();
this->haHelper->deleteSensorBoilerDhwMaxTemp();
this->haHelper->deleteInputDhwMinTemp();
this->haHelper->deleteInputDhwMaxTemp();
this->haHelper->deleteStateDhw();
this->haHelper->deleteSensorDhwTemp();
this->haHelper->deleteDhwState();
this->haHelper->deleteInputDhwTarget();
this->haHelper->deleteClimateDhw();
this->haHelper->deleteSensorDhwFlowRate();
}
published = true;
}
if (force || _noRegulators != noRegulators || _heatingMinTemp != heatingMinTemp || _heatingMaxTemp != heatingMaxTemp) {
_heatingMinTemp = heatingMinTemp;
_heatingMaxTemp = heatingMaxTemp;
_noRegulators = noRegulators;
if (force || _indoorTempControl != vars.master.heating.indoorTempControl || _heatingMinTemp != vars.master.heating.minTemp || _heatingMaxTemp != vars.master.heating.maxTemp) {
_heatingMinTemp = vars.master.heating.minTemp;
_heatingMaxTemp = vars.master.heating.maxTemp;
_indoorTempControl = vars.master.heating.indoorTempControl;
this->haHelper->publishInputHeatingTarget(settings.system.unitSystem, heatingMinTemp, heatingMaxTemp, false);
this->haHelper->publishClimateHeating(
settings.system.unitSystem,
heatingMinTemp,
heatingMaxTemp,
noRegulators ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR
vars.master.heating.minTemp,
vars.master.heating.maxTemp
);
published = true;
@@ -453,40 +567,11 @@ protected:
_dhwMinTemp = settings.dhw.minTemp;
_dhwMaxTemp = settings.dhw.maxTemp;
this->haHelper->publishInputDhwTarget(settings.system.unitSystem, settings.dhw.minTemp, settings.dhw.maxTemp, false);
this->haHelper->publishClimateDhw(settings.system.unitSystem, settings.dhw.minTemp, settings.dhw.maxTemp);
published = true;
}
if (force || _editableOutdoorTemp != editableOutdoorTemp) {
_editableOutdoorTemp = editableOutdoorTemp;
if (editableOutdoorTemp) {
this->haHelper->deleteSensorOutdoorTemp();
this->haHelper->publishInputOutdoorTemp(settings.system.unitSystem);
} else {
this->haHelper->deleteInputOutdoorTemp();
this->haHelper->publishSensorOutdoorTemp(settings.system.unitSystem);
}
published = true;
}
if (force || _editableIndoorTemp != editableIndoorTemp) {
_editableIndoorTemp = editableIndoorTemp;
if (editableIndoorTemp) {
this->haHelper->deleteSensorIndoorTemp();
this->haHelper->publishInputIndoorTemp(settings.system.unitSystem);
} else {
this->haHelper->deleteInputIndoorTemp();
this->haHelper->publishSensorIndoorTemp(settings.system.unitSystem);
}
published = true;
}
return published;
}
@@ -498,6 +583,25 @@ protected:
return this->writer->publish(topic, doc, true);
}
bool publishSensor(uint8_t sensorId) {
auto& sSettings = Sensors::settings[sensorId];
if (!Sensors::isValidSensorId(sensorId)) {
return false;
} else if (!strlen(sSettings.name)) {
return false;
}
JsonDocument doc;
sensorResultToJson(sensorId, doc);
doc.shrinkToFit();
String objId = Sensors::makeObjectId(sSettings.name);
String topic = this->haHelper->getDeviceTopic(F("sensors"), objId);
return this->writer->publish(topic.c_str(), doc, true);
}
bool publishVariables(const char* topic) {
JsonDocument doc;
varsToJson(vars, doc);

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ using WebServer = ESP8266WebServer;
using namespace NetworkUtils;
extern NetworkMgr* network;
extern FileData fsSettings, fsNetworkSettings;
extern FileData fsNetworkSettings, fsSettings, fsSensorsSettings;
extern MqttTask* tMqtt;
@@ -140,6 +140,18 @@ protected:
});
this->webServer->addHandler(settingsPage);
// sensors page
auto sensorsPage = (new StaticPage("/sensors.html", &LittleFS, "/pages/sensors.html", PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
return false;
}
return true;
});
this->webServer->addHandler(sensorsPage);
// upgrade page
auto upgradePage = (new StaticPage("/upgrade.html", &LittleFS, "/pages/upgrade.html", PORTAL_CACHE))
->setBeforeSendCallback([this]() {
@@ -194,17 +206,19 @@ protected:
}
}
JsonDocument networkSettingsDoc;
networkSettingsToJson(networkSettings, networkSettingsDoc);
networkSettingsDoc.shrinkToFit();
JsonDocument settingsDoc;
settingsToJson(settings, settingsDoc);
settingsDoc.shrinkToFit();
JsonDocument doc;
doc["network"] = networkSettingsDoc;
doc["settings"] = settingsDoc;
auto networkDoc = doc["network"].to<JsonObject>();
networkSettingsToJson(networkSettings, networkDoc);
auto settingskDoc = doc["settings"].to<JsonObject>();
settingsToJson(settings, settingskDoc);
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
auto sensorSettingskDoc = doc["sensors"][sensorId].to<JsonObject>();
sensorSettingsToJson(sensorId, Sensors::settings[sensorId], sensorSettingskDoc);
}
doc.shrinkToFit();
this->webServer->sendHeader(F("Content-Disposition"), F("attachment; filename=\"backup.json\""));
@@ -240,13 +254,13 @@ protected:
}
bool changed = false;
if (doc["settings"] && jsonToSettings(doc["settings"], settings)) {
if (!doc["settings"].isNull() && jsonToSettings(doc["settings"], settings)) {
vars.actions.restart = true;
fsSettings.update();
changed = true;
}
if (doc["network"] && jsonToNetworkSettings(doc["network"], networkSettings)) {
if (!doc["network"].isNull() && jsonToNetworkSettings(doc["network"], networkSettings)) {
fsNetworkSettings.update();
network->setHostname(networkSettings.hostname)
->setStaCredentials(networkSettings.sta.ssid, networkSettings.sta.password, networkSettings.sta.channel)
@@ -262,6 +276,19 @@ protected:
changed = true;
}
if (!doc["sensors"].isNull()) {
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
if (doc["sensors"][sensorId].isNull()) {
continue;
}
auto sensorSettingsDoc = doc["sensors"][sensorId].to<JsonObject>();
if (jsonToSensorSettings(sensorId, sensorSettingsDoc, Sensors::settings[sensorId])){
changed = true;
}
}
}
doc.clear();
doc.shrinkToFit();
@@ -446,6 +473,135 @@ protected:
});
// sensors list
this->webServer->on("/api/sensors", HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
}
bool detailed = false;
if (this->webServer->hasArg("detailed")) {
detailed = this->webServer->arg("detailed").toInt() > 0;
}
JsonDocument doc;
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
if (detailed) {
auto& sSensor = Sensors::settings[sensorId];
doc[sensorId]["name"] = sSensor.name;
doc[sensorId]["purpose"] = static_cast<uint8_t>(sSensor.purpose);
sensorResultToJson(sensorId, doc[sensorId]);
} else {
doc[sensorId] = Sensors::settings[sensorId].name;
}
}
doc.shrinkToFit();
this->bufferedWebServer->send(200, "application/json", doc);
});
// sensor settings
this->webServer->on("/api/sensor", HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
}
if (!this->webServer->hasArg("id")) {
return this->webServer->send(400);
}
auto id = this->webServer->arg("id");
if (!isDigit(id.c_str())) {
return this->webServer->send(400);
}
uint8_t sensorId = id.toInt();
id.clear();
if (!Sensors::isValidSensorId(sensorId)) {
return this->webServer->send(404);
}
JsonDocument doc;
sensorSettingsToJson(sensorId, Sensors::settings[sensorId], doc);
doc.shrinkToFit();
this->bufferedWebServer->send(200, "application/json", doc);
});
this->webServer->on("/api/sensor", HTTP_POST, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
}
#ifdef ARDUINO_ARCH_ESP8266
if (!this->webServer->hasArg("id") || this->webServer->args() != 1) {
return this->webServer->send(400);
}
#else
if (!this->webServer->hasArg("id") || this->webServer->args() != 2) {
return this->webServer->send(400);
}
#endif
auto id = this->webServer->arg("id");
if (!isDigit(id.c_str())) {
return this->webServer->send(400);
}
uint8_t sensorId = id.toInt();
id.clear();
if (!Sensors::isValidSensorId(sensorId)) {
return this->webServer->send(404);
}
auto plain = this->webServer->arg(1);
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/sensor/?id=%hhu %d bytes: %s"), sensorId, plain.length(), plain.c_str());
if (plain.length() < 5) {
return this->webServer->send(406);
} else if (plain.length() > 1024) {
return this->webServer->send(413);
}
bool changed = false;
auto prevSettings = Sensors::settings[sensorId];
{
JsonDocument doc;
DeserializationError dErr = deserializeJson(doc, plain);
plain.clear();
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
return this->webServer->send(400);
}
if (jsonToSensorSettings(sensorId, doc, Sensors::settings[sensorId])) {
changed = true;
}
}
{
JsonDocument doc;
auto& sSettings = Sensors::settings[sensorId];
sensorSettingsToJson(sensorId, sSettings, doc);
doc.shrinkToFit();
this->bufferedWebServer->send(changed ? 201 : 200, "application/json", doc);
}
if (changed) {
tMqtt->rebuildHaEntity(sensorId, prevSettings);
fsSensorsSettings.update();
}
});
// vars
this->webServer->on("/api/vars", HTTP_GET, [this]() {
JsonDocument doc;

View File

@@ -10,9 +10,12 @@ public:
RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
protected:
float prevHeatingTarget = 0;
float prevEtResult = 0;
float prevPidResult = 0;
float prevHeatingTarget = 0.0f;
float prevEtResult = 0.0f;
float prevPidResult = 0.0f;
bool indoorSensorsConnected = false;
//bool outdoorSensorsConnected = false;
#if defined(ARDUINO_ARCH_ESP32)
const char* getTaskName() override {
@@ -29,20 +32,50 @@ protected:
#endif
void loop() {
if (!settings.pid.enable && fabs(pidRegulator.integral) > 0.01f) {
this->indoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP);
//this->outdoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP);
if (settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.nativeHeatingControl) {
vars.master.heating.indoorTempControl = true;
vars.master.heating.minTemp = THERMOSTAT_INDOOR_MIN_TEMP;
vars.master.heating.maxTemp = THERMOSTAT_INDOOR_MAX_TEMP;
} else {
vars.master.heating.indoorTempControl = false;
vars.master.heating.minTemp = settings.heating.minTemp;
vars.master.heating.maxTemp = settings.heating.maxTemp;
}
if (!settings.pid.enabled && fabsf(pidRegulator.integral) > 0.01f) {
pidRegulator.integral = 0.0f;
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
}
this->turbo();
this->hysteresis();
vars.master.heating.targetTemp = constrain(
this->getHeatingSetpoint(),
vars.master.heating.minTemp,
vars.master.heating.maxTemp
);
Sensors::setValueByType(
Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.targetTemp,
Sensors::ValueType::PRIMARY, true, true
);
}
void turbo() {
if (settings.heating.turbo) {
if (!settings.heating.enable || vars.states.emergency || !vars.sensors.indoor.connected) {
if (!settings.heating.enabled || vars.emergency.state || !this->indoorSensorsConnected) {
settings.heating.turbo = false;
} else if (!settings.pid.enable && !settings.equitherm.enable) {
} else if (!settings.pid.enabled && !settings.equitherm.enabled) {
settings.heating.turbo = false;
} else if (fabs(settings.heating.target - vars.temperatures.indoor) <= 1.0f) {
} else if (fabsf(settings.heating.target - vars.master.heating.indoorTemp) <= 1.0f) {
settings.heating.turbo = false;
}
@@ -50,45 +83,58 @@ protected:
Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled"));
}
}
}
void hysteresis() {
bool useHyst = false;
if (settings.heating.hysteresis > 0.01f && this->indoorSensorsConnected) {
useHyst = settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.nativeHeatingControl;
}
float newTemp = vars.states.emergency
? settings.emergency.target
: this->getNormalModeTemp();
if (useHyst) {
if (!vars.master.heating.blocking && vars.master.heating.indoorTemp - settings.heating.target + 0.0001f >= settings.heating.hysteresis) {
vars.master.heating.blocking = true;
// Limits
newTemp = constrain(
newTemp,
!settings.opentherm.nativeHeatingControl ? settings.heating.minTemp : THERMOSTAT_INDOOR_MIN_TEMP,
!settings.opentherm.nativeHeatingControl ? settings.heating.maxTemp : THERMOSTAT_INDOOR_MAX_TEMP
);
} else if (vars.master.heating.blocking && vars.master.heating.indoorTemp - settings.heating.target - 0.0001f <= -(settings.heating.hysteresis)) {
vars.master.heating.blocking = false;
}
if (fabs(vars.parameters.heatingSetpoint - newTemp) > 0.09f) {
vars.parameters.heatingSetpoint = newTemp;
} else if (vars.master.heating.blocking) {
vars.master.heating.blocking = false;
}
}
float getNormalModeTemp() {
float getHeatingSetpoint() {
float newTemp = 0;
if (fabs(prevHeatingTarget - settings.heating.target) > 0.0001f) {
if (fabsf(prevHeatingTarget - settings.heating.target) > 0.0001f) {
prevHeatingTarget = settings.heating.target;
Log.sinfoln(FPSTR(L_REGULATOR), F("New target: %.2f"), settings.heating.target);
/*if (settings.pid.enable) {
/*if (settings.pid.enabled) {
pidRegulator.integral = 0.0f;
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
}*/
}
if (vars.emergency.state) {
return settings.emergency.target;
} else if (settings.opentherm.nativeHeatingControl) {
return settings.heating.target;
} else if (!settings.equitherm.enabled && !settings.pid.enabled) {
return settings.heating.target;
}
// if use equitherm
if (settings.equitherm.enable) {
if (settings.equitherm.enabled) {
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;
float indoorTemp = vars.master.heating.indoorTemp;
float outdoorTemp = vars.master.heating.outdoorTemp;
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
minTemp = f2c(minTemp);
@@ -98,7 +144,7 @@ protected:
outdoorTemp = f2c(outdoorTemp);
}
if (!vars.sensors.indoor.connected || settings.pid.enable) {
if (!this->indoorSensorsConnected || settings.pid.enabled) {
etRegulator.Kt = 0.0f;
etRegulator.indoorTemp = 0.0f;
@@ -118,7 +164,7 @@ protected:
etResult = c2f(etResult);
}
if (fabs(prevEtResult - etResult) > 0.09f) {
if (fabsf(prevEtResult - etResult) > 0.09f) {
prevEtResult = etResult;
newTemp += etResult;
@@ -130,18 +176,18 @@ protected:
}
// if use pid
if (settings.pid.enable) {
if (settings.pid.enabled) {
//if (vars.parameters.heatingEnabled) {
if (settings.heating.enable && vars.sensors.indoor.connected) {
if (settings.heating.enabled && this->indoorSensorsConnected) {
pidRegulator.Kp = settings.heating.turbo ? 0.0f : settings.pid.p_factor;
pidRegulator.Kd = settings.pid.d_factor;
pidRegulator.setLimits(settings.pid.minTemp, settings.pid.maxTemp);
pidRegulator.setDt(settings.pid.dt * 1000u);
pidRegulator.input = vars.temperatures.indoor;
pidRegulator.input = vars.master.heating.indoorTemp;
pidRegulator.setpoint = settings.heating.target;
if (fabs(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) {
if (fabsf(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) {
pidRegulator.Ki = settings.pid.i_factor;
pidRegulator.integral = 0.0f;
pidRegulator.getResultNow();
@@ -150,7 +196,7 @@ protected:
}
float pidResult = pidRegulator.getResultTimer();
if (fabs(prevPidResult - pidResult) > 0.09f) {
if (fabsf(prevPidResult - pidResult) > 0.09f) {
prevPidResult = pidResult;
newTemp += pidResult;
@@ -167,19 +213,14 @@ protected:
}
// Turbo mode
if (settings.heating.turbo && (settings.equitherm.enable || settings.pid.enable)) {
if (settings.heating.turbo && (settings.equitherm.enabled || settings.pid.enabled)) {
newTemp += constrain(
settings.heating.target - vars.temperatures.indoor,
settings.heating.target - vars.master.heating.indoorTemp,
-3.0f,
3.0f
) * settings.heating.turboFactor;
}
// default temp, manual mode
if (!settings.equitherm.enable && !settings.pid.enable) {
newTemp = settings.heating.target;
}
return newTemp;
}
};

424
src/Sensors.h Normal file
View File

@@ -0,0 +1,424 @@
#pragma once
class Sensors {
protected:
static uint8_t maxSensors;
public:
enum class Type : uint8_t {
OT_OUTDOOR_TEMP = 0,
OT_HEATING_TEMP = 1,
OT_HEATING_RETURN_TEMP = 2,
OT_DHW_TEMP = 3,
OT_DHW_TEMP2 = 4,
OT_DHW_FLOW_RATE = 5,
OT_CH2_TEMP = 6,
OT_EXHAUST_TEMP = 7,
OT_HEAT_EXCHANGER_TEMP = 8,
OT_PRESSURE = 9,
OT_MODULATION_LEVEL = 10,
OT_CURRENT_POWER = 11,
NTC_10K_TEMP = 50,
DALLAS_TEMP = 51,
BLUETOOTH = 52,
HEATING_SETPOINT_TEMP = 253,
MANUAL = 254,
NOT_CONFIGURED = 255
};
enum class Purpose : uint8_t {
OUTDOOR_TEMP = 0,
INDOOR_TEMP = 1,
HEATING_TEMP = 2,
HEATING_RETURN_TEMP = 3,
DHW_TEMP = 4,
DHW_RETURN_TEMP = 5,
DHW_FLOW_RATE = 6,
EXHAUST_TEMP = 7,
MODULATION_LEVEL = 8,
CURRENT_POWER = 9,
PRESSURE = 252,
HUMIDITY = 253,
TEMPERATURE = 254,
NOT_CONFIGURED = 255
};
enum class ValueType : uint8_t {
PRIMARY = 0,
TEMPERATURE = 0,
HUMIDITY = 1,
BATTERY = 2,
RSSI = 3
};
typedef struct {
bool enabled = false;
char name[33];
Purpose purpose = Purpose::NOT_CONFIGURED;
Type type = Type::NOT_CONFIGURED;
uint8_t gpio = GPIO_IS_NOT_CONFIGURED;
uint8_t address[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
float offset = 0.0f;
float factor = 1.0f;
bool filtering = false;
float filteringFactor = 0.15f;
} Settings;
typedef struct {
bool connected = false;
unsigned long activityTime = 0;
uint8_t signalQuality = 0;
//float raw[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float values[4] = {0.0f, 0.0f, 0.0f, 0.0f};
} Result;
static Settings* settings;
static Result* results;
static inline void setMaxSensors(uint8_t value) {
maxSensors = value;
}
static inline uint8_t getMaxSensors() {
return maxSensors;
}
static uint8_t getMaxSensorId() {
uint8_t maxSensors = getMaxSensors();
return maxSensors > 1 ? (maxSensors - 1) : 0;
}
static inline bool isValidSensorId(const uint8_t id) {
return id >= 0 && id <= getMaxSensorId();
}
static inline bool isValidValueId(const uint8_t id) {
return id >= (uint8_t) ValueType::TEMPERATURE && id <= (uint8_t) ValueType::RSSI;
}
static bool hasEnabledAndValid(const uint8_t id) {
if (!isValidSensorId(id) || !settings[id].enabled) {
return false;
}
if (settings[id].type == Type::NOT_CONFIGURED || settings[id].purpose == Purpose::NOT_CONFIGURED) {
return false;
}
return true;
}
static uint8_t getAmountByType(Type type) {
if (settings == nullptr) {
return 0;
}
uint8_t amount = 0;
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
if (settings[id].type == type) {
amount++;
}
}
return amount;
}
static int16_t getIdByName(const char* name) {
if (settings == nullptr) {
return 0;
}
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
if (strcmp(settings[id].name, name) == 0) {
return id;
}
}
return -1;
}
static int16_t getIdByObjectId(const char* objectId) {
if (settings == nullptr) {
return 0;
}
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
String _objectId = Sensors::makeObjectId(settings[id].name);
if (strcmp(_objectId.c_str(), objectId) == 0) {
return id;
}
}
return -1;
}
static bool setValueById(const uint8_t sensorId, float value, const ValueType valueType, const bool updateActivityTime = false, const bool markConnected = false) {
if (settings == nullptr || results == nullptr) {
return false;
}
uint8_t valueId = (uint8_t) valueType;
if (!isValidSensorId(sensorId) || !isValidValueId(valueId)) {
return false;
}
auto& sSensor = settings[sensorId];
auto& rSensor = results[sensorId];
float compensatedValue = value;
if (valueType == ValueType::PRIMARY) {
if (fabsf(sSensor.factor) > 0.001f) {
compensatedValue *= sSensor.factor;
}
if (fabsf(sSensor.offset) > 0.001f) {
compensatedValue += sSensor.offset;
}
} else if (valueType == ValueType::RSSI) {
if (sSensor.type == Type::BLUETOOTH) {
rSensor.signalQuality = Sensors::bluetoothRssiToQuality(value);
}
}
if (sSensor.filtering && fabs(rSensor.values[valueId]) >= 0.1f) {
rSensor.values[valueId] += (compensatedValue - rSensor.values[valueId]) * sSensor.filteringFactor;
} else {
rSensor.values[valueId] = compensatedValue;
}
if (updateActivityTime) {
rSensor.activityTime = millis();
}
if (markConnected) {
if (!rSensor.connected) {
rSensor.connected = true;
Log.snoticeln(
FPSTR(L_SENSORS), F("#%hhu '%s' new status: CONNECTED"),
sensorId, sSensor.name
);
}
}
Log.snoticeln(
FPSTR(L_SENSORS), F("#%hhu '%s' new value %hhu: %.2f, compensated: %.2f, raw: %.2f"),
sensorId, sSensor.name, valueId, rSensor.values[valueId], compensatedValue, value
);
return true;
}
static uint8_t setValueByType(Type type, float value, const ValueType valueType, const bool updateActivityTime = false, const bool markConnected = false) {
if (settings == nullptr) {
return 0;
}
uint8_t updated = 0;
// read sensors data for current instance
for (uint8_t sensorId = 0; sensorId < getMaxSensorId(); sensorId++) {
auto& sSensor = settings[sensorId];
// only target & valid sensors
if (!sSensor.enabled || sSensor.type != type) {
continue;
}
if (setValueById(sensorId, value, valueType, updateActivityTime, markConnected)) {
updated++;
}
}
return updated;
}
static bool getConnectionStatusById(const uint8_t sensorId) {
if (settings == nullptr || results == nullptr) {
return false;
}
if (!isValidSensorId(sensorId)) {
return false;
}
return results[sensorId].connected;
}
static bool setConnectionStatusById(const uint8_t sensorId, const bool status, const bool updateActivityTime = true) {
if (settings == nullptr || results == nullptr) {
return false;
}
if (!isValidSensorId(sensorId)) {
return false;
}
auto& sSensor = settings[sensorId];
auto& rSensor = results[sensorId];
if (rSensor.connected != status) {
Log.snoticeln(
FPSTR(L_SENSORS), F("#%hhu '%s' new status: %s"),
sensorId, sSensor.name, status ? F("CONNECTED") : F("DISCONNECTED")
);
rSensor.connected = status;
}
if (updateActivityTime) {
rSensor.activityTime = millis();
}
return true;
}
static uint8_t setConnectionStatusByType(Type type, const bool status, const bool updateActivityTime = true) {
if (settings == nullptr) {
return 0;
}
uint8_t updated = 0;
// read sensors data for current instance
for (uint8_t sensorId = 0; sensorId < getMaxSensorId(); sensorId++) {
auto& sSensor = settings[sensorId];
// only target & valid sensors
if (!sSensor.enabled || sSensor.type != type) {
continue;
}
if (setConnectionStatusById(sensorId, status, updateActivityTime)) {
updated++;
}
}
return updated;
}
static float getMeanValueByPurpose(Purpose purpose, const ValueType valueType, bool onlyConnected = true) {
if (settings == nullptr || results == nullptr) {
return 0;
}
uint8_t valueId = (uint8_t) valueType;
if (!isValidValueId(valueId)) {
return false;
}
float value = 0.0f;
uint8_t amount = 0;
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 (!amount) {
return 0.0f;
} else if (amount == 1) {
return value;
} else {
return value / amount;
}
}
static bool existsConnectedSensorsByPurpose(Purpose purpose) {
if (settings == nullptr || results == nullptr) {
return 0;
}
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
if (settings[id].purpose == purpose && results[id].connected) {
return true;
}
}
return false;
}
template <class T>
static String cleanName(T value, char space = ' ') {
String clean = value;
// only valid symbols
for (uint8_t pos = 0; pos < clean.length(); pos++) {
char symbol = clean.charAt(pos);
// 0..9
if (symbol >= 48 && symbol <= 57) {
continue;
}
// A..Z
if (symbol >= 65 && symbol <= 90) {
continue;
}
// a..z
if (symbol >= 97 && symbol <= 122) {
continue;
}
// _-
if (symbol == 95 || symbol == 45 || symbol == space) {
continue;
}
clean.setCharAt(pos, space);
}
clean.trim();
return clean;
}
template <class T>
static String makeObjectId(T value, char separator = '_') {
auto objId = cleanName(value);
objId.toLowerCase();
objId.replace(' ', separator);
return objId;
}
template <class TV, class TS>
static auto makeObjectIdWithSuffix(TV value, TS suffix, char separator = '_') {
auto objId = makeObjectId(value, separator);
objId += separator;
objId += suffix;
return objId;
}
template <class TV, class TP>
static auto makeObjectIdWithPrefix(TV value, TP prefix, char separator = '_') {
String objId = prefix;
objId += separator;
objId += makeObjectId(value, separator);
return objId;
}
static uint8_t bluetoothRssiToQuality(int rssi) {
return constrain(map(rssi, -110, -50, 0, 100), 0, 100);;
}
};
uint8_t Sensors::maxSensors = 0;
Sensors::Settings* Sensors::settings = nullptr;
Sensors::Result* Sensors::results = nullptr;

File diff suppressed because it is too large Load Diff

View File

@@ -27,12 +27,12 @@ struct Settings {
uint8_t logLevel = DEFAULT_LOG_LEVEL;
struct {
bool enable = DEFAULT_SERIAL_ENABLE;
bool enabled = DEFAULT_SERIAL_ENABLED;
unsigned int baudrate = DEFAULT_SERIAL_BAUD;
} serial;
struct {
bool enable = DEFAULT_TELNET_ENABLE;
bool enabled = DEFAULT_TELNET_ENABLED;
unsigned short port = DEFAULT_TELNET_PORT;
} telnet;
@@ -51,12 +51,12 @@ struct Settings {
byte inGpio = DEFAULT_OT_IN_GPIO;
byte outGpio = DEFAULT_OT_OUT_GPIO;
byte rxLedGpio = DEFAULT_OT_RX_LED_GPIO;
unsigned int memberIdCode = 0;
uint8_t memberId = 0;
uint8_t flags = 0;
uint8_t maxModulation = 100;
float pressureFactor = 1.0f;
float dhwFlowRateFactor = 1.0f;
float minPower = 0.0f;
float maxPower = 0.0f;
bool dhwPresent = true;
bool summerWinterMode = false;
bool heatingCh2Enabled = true;
@@ -67,15 +67,10 @@ struct Settings {
bool getMinMaxTemp = true;
bool nativeHeatingControl = false;
bool immergasFix = false;
struct {
bool enable = false;
float factor = 0.1f;
} filterNumValues;
} opentherm;
struct {
bool enable = false;
bool enabled = DEFAULT_MQTT_ENABLED;
char server[81] = DEFAULT_MQTT_SERVER;
unsigned short port = DEFAULT_MQTT_PORT;
char user[33] = DEFAULT_MQTT_USER;
@@ -91,7 +86,7 @@ struct Settings {
} emergency;
struct {
bool enable = true;
bool enabled = true;
bool turbo = false;
float target = DEFAULT_HEATING_TARGET_TEMP;
float hysteresis = 0.5f;
@@ -101,14 +96,14 @@ struct Settings {
} heating;
struct {
bool enable = true;
bool enabled = true;
float target = DEFAULT_DHW_TARGET_TEMP;
byte minTemp = DEFAULT_DHW_MIN_TEMP;
byte maxTemp = DEFAULT_DHW_MAX_TEMP;
} dhw;
struct {
bool enable = false;
bool enabled = false;
float p_factor = 2.0f;
float i_factor = 0.0055f;
float d_factor = 0.0f;
@@ -118,28 +113,12 @@ struct Settings {
} pid;
struct {
bool enable = false;
bool enabled = false;
float n_factor = 0.7f;
float k_factor = 3.0f;
float t_factor = 2.0f;
} equitherm;
struct {
struct {
SensorType type = SensorType::BOILER_OUTDOOR;
byte gpio = DEFAULT_SENSOR_OUTDOOR_GPIO;
uint8_t bleAddress[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
float offset = 0.0f;
} outdoor;
struct {
SensorType type = SensorType::MANUAL;
byte gpio = DEFAULT_SENSOR_INDOOR_GPIO;
uint8_t bleAddress[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
float offset = 0.0f;
} indoor;
} sensors;
struct {
bool use = false;
byte gpio = DEFAULT_EXT_PUMP_GPIO;
@@ -150,14 +129,14 @@ struct Settings {
struct {
struct {
bool enable = false;
bool enabled = false;
byte gpio = GPIO_IS_NOT_CONFIGURED;
byte invertState = false;
unsigned short thresholdTime = 60;
} input;
struct {
bool enable = false;
bool enabled = false;
byte gpio = GPIO_IS_NOT_CONFIGURED;
byte invertState = false;
unsigned short thresholdTime = 60;
@@ -170,51 +149,95 @@ struct Settings {
char validationValue[8] = SETTINGS_VALID_VALUE;
} settings;
Sensors::Settings sensorsSettings[SENSORS_AMOUNT] = {
{
false,
"Indoor temp",
Sensors::Purpose::OUTDOOR_TEMP,
Sensors::Type::DALLAS_TEMP,
DEFAULT_SENSOR_OUTDOOR_GPIO
},
{
false,
"Outdoor temp",
Sensors::Purpose::INDOOR_TEMP,
Sensors::Type::DALLAS_TEMP,
DEFAULT_SENSOR_INDOOR_GPIO
},
{
true,
"Heating temp",
Sensors::Purpose::HEATING_TEMP,
Sensors::Type::OT_HEATING_TEMP,
},
{
true,
"Heating return temp",
Sensors::Purpose::HEATING_RETURN_TEMP,
Sensors::Type::OT_HEATING_RETURN_TEMP,
},
{
true,
"Heating setpoint temp",
Sensors::Purpose::TEMPERATURE,
Sensors::Type::HEATING_SETPOINT_TEMP,
},
{
true,
"DHW temp",
Sensors::Purpose::DHW_TEMP,
Sensors::Type::OT_DHW_TEMP,
},
{
true,
"DHW flow rate",
Sensors::Purpose::DHW_FLOW_RATE,
Sensors::Type::OT_DHW_FLOW_RATE,
},
{
true,
"Exhaust temp",
Sensors::Purpose::EXHAUST_TEMP,
Sensors::Type::OT_EXHAUST_TEMP,
},
{
true,
"Pressure",
Sensors::Purpose::PRESSURE,
Sensors::Type::OT_PRESSURE,
},
{
true,
"Modulation level",
Sensors::Purpose::MODULATION_LEVEL,
Sensors::Type::OT_MODULATION_LEVEL,
},
{
true,
"Power",
Sensors::Purpose::CURRENT_POWER,
Sensors::Type::OT_CURRENT_POWER,
}
};
struct Variables {
struct {
bool otStatus = false;
bool emergency = false;
bool heating = false;
bool dhw = false;
bool flame = false;
bool fault = false;
bool diagnostic = false;
bool externalPump = false;
bool mqtt = false;
} states;
struct {
float modulation = 0.0f;
float pressure = 0.0f;
float dhwFlowRate = 0.0f;
float power = 0.0f;
byte faultCode = 0;
unsigned short diagnosticCode = 0;
bool connected = false;
int8_t rssi = 0;
struct {
bool connected = false;
int8_t rssi = 0;
float battery = 0.0f;
float humidity = 0.0f;
} outdoor;
struct {
bool connected = false;
int8_t rssi = 0;
float battery = 0.0f;
float humidity = 0.0f;
} indoor;
} sensors;
} network;
struct {
float indoor = 0.0f;
float outdoor = 0.0f;
float heating = 0.0f;
float heatingReturn = 0.0f;
float dhw = 0.0f;
float exhaust = 0.0f;
} temperatures;
bool connected = false;
} mqtt;
struct {
bool state = false;
} emergency;
struct {
bool state = false;
unsigned long lastEnableTime = 0;
} externalPump;
struct {
bool input = false;
@@ -222,27 +245,104 @@ struct Variables {
} cascadeControl;
struct {
bool heatingEnabled = false;
byte heatingMinTemp = DEFAULT_HEATING_MIN_TEMP;
byte heatingMaxTemp = DEFAULT_HEATING_MAX_TEMP;
float heatingSetpoint = 0;
unsigned long extPumpLastEnableTime = 0;
byte dhwMinTemp = DEFAULT_DHW_MIN_TEMP;
byte dhwMaxTemp = DEFAULT_DHW_MAX_TEMP;
byte minModulation = 0;
byte maxModulation = 0;
uint8_t maxPower = 0;
uint8_t slaveMemberId = 0;
uint8_t slaveFlags = 0;
uint8_t slaveType = 0;
uint8_t slaveVersion = 0;
float slaveOtVersion = 0.0f;
uint8_t masterMemberId = 0;
uint8_t masterFlags = 0;
uint8_t masterType = 0;
uint8_t masterVersion = 0;
float masterOtVersion = 0;
} parameters;
uint8_t memberId = 0;
uint8_t flags = 0;
uint8_t type = 0;
uint8_t appVersion = 0;
float protocolVersion = 0.0f;
struct {
bool blocking = false;
bool enabled = false;
bool indoorTempControl = false;
float targetTemp = 0.0f;
float currentTemp = 0.0f;
float returnTemp = 0.0f;
float indoorTemp = 0.0f;
float outdoorTemp = 0.0f;
float minTemp = 0.0f;
float maxTemp = 0.0f;
} heating;
struct {
bool enabled = false;
float targetTemp = 0.0f;
float currentTemp = 0.0f;
float returnTemp = 0.0f;
} dhw;
struct {
bool enabled = false;
float targetTemp = 0.0f;
} ch2;
} master;
struct {
uint8_t memberId = 0;
uint8_t flags = 0;
uint8_t type = 0;
uint8_t appVersion = 0;
float protocolVersion = 0.0f;
bool connected = false;
bool flame = false;
float pressure = 0.0f;
float exhaustTemp = 0.0f;
float heatExchangerTemp = 0.0f;
struct {
bool active = false;
uint8_t code = 0;
} fault;
struct {
bool active = false;
uint16_t code = 0;
} diag;
struct {
uint8_t current = 0;
uint8_t min = 0;
uint8_t max = 100;
} modulation;
struct {
float current = 0.0f;
float min = 0.0f;
float max = 0.0f;
} power;
struct {
bool active = false;
bool enabled = false;
float targetTemp = 0.0f;
float currentTemp = 0.0f;
float returnTemp = 0.0f;
float indoorTemp = 0.0f;
float outdoorTemp = 0.0f;
uint8_t minTemp = DEFAULT_HEATING_MIN_TEMP;
uint8_t maxTemp = DEFAULT_HEATING_MAX_TEMP;
} heating;
struct {
bool active = false;
bool enabled = false;
float targetTemp = 0.0f;
float currentTemp = 0.0f;
float currentTemp2 = 0.0f;
float returnTemp = 0.0f;
float flowRate = 0.0f;
uint8_t minTemp = DEFAULT_DHW_MIN_TEMP;
uint8_t maxTemp = DEFAULT_DHW_MAX_TEMP;
} dhw;
struct {
bool enabled = false;
float targetTemp = 0.0f;
float currentTemp = 0.0f;
float indoorTemp = 0.0f;
} ch2;
} slave;
struct {
bool restart = false;

View File

@@ -2,10 +2,6 @@
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
#define MQTT_RECONNECT_INTERVAL 15000
#define EXT_SENSORS_INTERVAL 5000
#define EXT_SENSORS_FILTER_K 0.15
#define CONFIG_URL "http://%s/"
#define SETTINGS_VALID_VALUE "stvalid" // only 8 chars!
#define GPIO_IS_NOT_CONFIGURED 0xff
@@ -22,6 +18,13 @@
#define THERMOSTAT_INDOOR_MIN_TEMP 5
#define THERMOSTAT_INDOOR_MAX_TEMP 30
#define DEFAULT_NTC_NOMINAL_RESISTANCE 10000.0f
#define DEFAULT_NTC_NOMINAL_TEMP 25.0f
#define DEFAULT_NTC_REF_RESISTANCE 10000.0f
#define DEFAULT_NTC_BETA_FACTOR 3950.0f
#define DEFAULT_NTC_VREF 3300.0f
#define DEFAULT_NTC_VLOW_TRESHOLD 25.0f
#ifndef BUILD_VERSION
#define BUILD_VERSION "0.0.0"
#endif
@@ -30,16 +33,16 @@
#define BUILD_ENV "undefined"
#endif
#ifndef DEFAULT_SERIAL_ENABLE
#define DEFAULT_SERIAL_ENABLE true
#ifndef DEFAULT_SERIAL_ENABLED
#define DEFAULT_SERIAL_ENABLED true
#endif
#ifndef DEFAULT_SERIAL_BAUD
#define DEFAULT_SERIAL_BAUD 115200
#endif
#ifndef DEFAULT_TELNET_ENABLE
#define DEFAULT_TELNET_ENABLE true
#ifndef DEFAULT_TELNET_ENABLED
#define DEFAULT_TELNET_ENABLED true
#endif
#ifndef DEFAULT_TELNET_PORT
@@ -86,6 +89,10 @@
#define DEFAULT_PORTAL_PASSWORD ""
#endif
#ifndef DEFAULT_MQTT_ENABLED
#define DEFAULT_MQTT_ENABLED false
#endif
#ifndef DEFAULT_MQTT_SERVER
#define DEFAULT_MQTT_SERVER ""
#endif
@@ -130,6 +137,10 @@
#define DEFAULT_SENSOR_INDOOR_GPIO GPIO_IS_NOT_CONFIGURED
#endif
#ifndef SENSORS_AMOUNT
#define SENSORS_AMOUNT 20
#endif
#ifndef DEFAULT_EXT_PUMP_GPIO
#define DEFAULT_EXT_PUMP_GPIO GPIO_IS_NOT_CONFIGURED
#endif
@@ -146,17 +157,9 @@
#define GPIO_IS_VALID(gpioNum) (gpioNum != GPIO_IS_NOT_CONFIGURED && GPIO_IS_VALID_GPIO(gpioNum))
enum class SensorType : byte {
BOILER_OUTDOOR = 0,
BOILER_RETURN = 4,
MANUAL = 1,
DS18B20 = 2,
BLUETOOTH = 3
};
enum class UnitSystem : byte {
METRIC,
IMPERIAL
enum class UnitSystem : uint8_t {
METRIC = 0,
IMPERIAL = 1
};
char buffer[255];

View File

@@ -1,13 +1,15 @@
#include <Arduino.h>
#include "defines.h"
#include "strings.h"
#include "CrashRecorder.h"
#include <ArduinoJson.h>
#include <FileData.h>
#include <LittleFS.h>
#include <ESPTelnetStream.h>
#include <TinyLogger.h>
#include <NetworkMgr.h>
#include "defines.h"
#include "strings.h"
#include "CrashRecorder.h"
#include "Sensors.h"
#include "Settings.h"
#include "utils.h"
@@ -31,10 +33,13 @@
using namespace NetworkUtils;
// Vars
FileData fsNetworkSettings(&LittleFS, "/network.conf", 'n', &networkSettings, sizeof(networkSettings), 1000);
FileData fsSettings(&LittleFS, "/settings.conf", 's', &settings, sizeof(settings), 60000);
ESPTelnetStream* telnetStream = nullptr;
NetworkMgr* network = nullptr;
Sensors::Result sensorsResults[SENSORS_AMOUNT];
FileData fsNetworkSettings(&LittleFS, "/network.conf", 'n', &networkSettings, sizeof(networkSettings), 1000);
FileData fsSettings(&LittleFS, "/settings.conf", 's', &settings, sizeof(settings), 60000);
FileData fsSensorsSettings(&LittleFS, "/sensors.conf", 'e', &sensorsSettings, sizeof(sensorsSettings), 60000);
// Tasks
MqttTask* tMqtt;
@@ -47,6 +52,9 @@ MainTask* tMain;
void setup() {
CrashRecorder::init();
Sensors::setMaxSensors(SENSORS_AMOUNT);
Sensors::settings = sensorsSettings;
Sensors::results = sensorsResults;
LittleFS.begin();
Log.setLevel(TinyLogger::Level::VERBOSE);
@@ -64,10 +72,14 @@ void setup() {
});
Serial.begin(115200);
#if ARDUINO_USB_MODE
Serial.setTxBufferSize(512);
#endif
Log.addStream(&Serial);
Log.print("\n\n\r");
// network settings
//
// Network settings
switch (fsNetworkSettings.read()) {
case FD_FS_ERR:
Log.swarningln(FPSTR(L_NETWORK_SETTINGS), F("Filesystem error, load default"));
@@ -86,7 +98,27 @@ void setup() {
break;
}
// settings
network = (new NetworkMgr)
->setHostname(networkSettings.hostname)
->setStaCredentials(
strlen(networkSettings.sta.ssid) ? networkSettings.sta.ssid : nullptr,
strlen(networkSettings.sta.password) ? networkSettings.sta.password : nullptr,
networkSettings.sta.channel
)->setApCredentials(
strlen(networkSettings.ap.ssid) ? networkSettings.ap.ssid : nullptr,
strlen(networkSettings.ap.password) ? networkSettings.ap.password : nullptr,
networkSettings.ap.channel
)
->setUseDhcp(networkSettings.useDhcp)
->setStaticConfig(
networkSettings.staticConfig.ip,
networkSettings.staticConfig.gateway,
networkSettings.staticConfig.subnet,
networkSettings.staticConfig.dns
);
//
// Settings
switch (fsSettings.read()) {
case FD_FS_ERR:
Log.swarningln(FPSTR(L_SETTINGS), F("Filesystem error, load default"));
@@ -112,8 +144,8 @@ void setup() {
break;
}
// logs
if (!settings.system.serial.enable) {
// Logs settings
if (!settings.system.serial.enabled) {
Serial.end();
Log.clearStreams();
@@ -125,7 +157,7 @@ void setup() {
Log.addStream(&Serial);
}
if (settings.system.telnet.enable) {
if (settings.system.telnet.enabled) {
telnetStream = new ESPTelnetStream;
telnetStream->setKeepAliveInterval(500);
Log.addStream(telnetStream);
@@ -135,34 +167,34 @@ void setup() {
Log.setLevel(static_cast<TinyLogger::Level>(settings.system.logLevel));
}
// network
network = (new NetworkMgr)
->setHostname(networkSettings.hostname)
->setStaCredentials(
strlen(networkSettings.sta.ssid) ? networkSettings.sta.ssid : nullptr,
strlen(networkSettings.sta.password) ? networkSettings.sta.password : nullptr,
networkSettings.sta.channel
)->setApCredentials(
strlen(networkSettings.ap.ssid) ? networkSettings.ap.ssid : nullptr,
strlen(networkSettings.ap.password) ? networkSettings.ap.password : nullptr,
networkSettings.ap.channel
)
->setUseDhcp(networkSettings.useDhcp)
->setStaticConfig(
networkSettings.staticConfig.ip,
networkSettings.staticConfig.gateway,
networkSettings.staticConfig.subnet,
networkSettings.staticConfig.dns
);
//
// Sensors settings
switch (fsSensorsSettings.read()) {
case FD_FS_ERR:
Log.swarningln(FPSTR(L_SENSORS), F("Filesystem error, load default"));
break;
case FD_FILE_ERR:
Log.swarningln(FPSTR(L_SENSORS), F("Bad data, load default"));
break;
case FD_WRITE:
Log.sinfoln(FPSTR(L_SENSORS), F("Not found, load default"));
break;
case FD_ADD:
case FD_READ:
Log.sinfoln(FPSTR(L_SENSORS), F("Loaded"));
default:
break;
}
// tasks
//
// Make tasks
tMqtt = new MqttTask(false, 500);
Scheduler.start(tMqtt);
tOt = new OpenThermTask(true, 750);
Scheduler.start(tOt);
tSensors = new SensorsTask(true, EXT_SENSORS_INTERVAL);
tSensors = new SensorsTask(true, 1000);
Scheduler.start(tSensors);
tRegulator = new RegulatorTask(true, 10000);

View File

@@ -15,15 +15,20 @@ const char L_PORTAL_CAPTIVE[] PROGMEM = "PORTAL.CAPTIVE";
const char L_PORTAL_OTA[] PROGMEM = "PORTAL.OTA";
const char L_MAIN[] PROGMEM = "MAIN";
const char L_MQTT[] PROGMEM = "MQTT";
const char L_MQTT_HA[] PROGMEM = "MQTT.HA";
const char L_MQTT_MSG[] PROGMEM = "MQTT.MSG";
const char L_OT[] PROGMEM = "OT";
const char L_OT_DHW[] PROGMEM = "OT.DHW";
const char L_OT_HEATING[] PROGMEM = "OT.HEATING";
const char L_SENSORS_OUTDOOR[] PROGMEM = "SENSORS.OUTDOOR";
const char L_SENSORS_INDOOR[] PROGMEM = "SENSORS.INDOOR";
const char L_OT_CH2[] PROGMEM = "OT.CH2";
const char L_SENSORS[] PROGMEM = "SENSORS";
const char L_SENSORS_SETTINGS[] PROGMEM = "SENSORS.SETTINGS";
const char L_SENSORS_DALLAS[] PROGMEM = "SENSORS.DALLAS";
const char L_SENSORS_NTC[] PROGMEM = "SENSORS.NTC";
const char L_SENSORS_BLE[] PROGMEM = "SENSORS.BLE";
const char L_REGULATOR[] PROGMEM = "REGULATOR";
const char L_REGULATOR_PID[] PROGMEM = "REGULATOR.PID";
const char L_REGULATOR_EQUITHERM[] PROGMEM = "REGULATOR.EQUITHERM";
const char L_CASCADE_INPUT[] PROGMEM = "CASCADE.INPUT";
const char L_CASCADE_OUTPUT[] PROGMEM = "CASCADE.OUTPUT";
const char L_CASCADE_OUTPUT[] PROGMEM = "CASCADE.OUTPUT";
const char L_EXTPUMP[] PROGMEM = "EXTPUMP";

File diff suppressed because it is too large Load Diff