mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 18:54:28 +05:00
* feat: new portal & network manager
* refactor: migrate from PubSubClient to ArduinoMqttClient * refactor: migrate from EEManager to FileData * chore: bump ESP Telnet to 2.2 * chore: bump TinyLogger to 1.1.0
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
#include <Blinker.h>
|
||||
|
||||
extern NetworkTask* tNetwork;
|
||||
extern MqttTask* tMqtt;
|
||||
extern SensorsTask* tSensors;
|
||||
extern OpenThermTask* tOt;
|
||||
extern EEManager eeSettings;
|
||||
extern FileData fsSettings, fsNetworkSettings;
|
||||
#if USE_TELNET
|
||||
extern ESPTelnetStream TelnetStream;
|
||||
#endif
|
||||
@@ -29,7 +29,6 @@ protected:
|
||||
bool blinkerInitialized = false;
|
||||
unsigned long firstFailConnect = 0;
|
||||
unsigned long lastHeapInfo = 0;
|
||||
unsigned int heapSize = 0;
|
||||
unsigned int minFreeHeapSize = 0;
|
||||
unsigned int minMaxFreeHeapBlockSize = 0;
|
||||
unsigned long restartSignalTime = 0;
|
||||
@@ -37,6 +36,9 @@ protected:
|
||||
unsigned long heatingDisabledTime = 0;
|
||||
byte externalPumpStartReason;
|
||||
unsigned long externalPumpStartTime = 0;
|
||||
#if USE_TELNET
|
||||
bool telnetStarted = false;
|
||||
#endif
|
||||
|
||||
const char* getTaskName() {
|
||||
return "Main";
|
||||
@@ -61,29 +63,29 @@ protected:
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
this->heapSize = ESP.getHeapSize();
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
this->heapSize = 81920;
|
||||
#else
|
||||
this->heapSize = 99999;
|
||||
#endif
|
||||
this->minFreeHeapSize = heapSize;
|
||||
this->minMaxFreeHeapBlockSize = heapSize;
|
||||
this->minFreeHeapSize = getTotalHeap();
|
||||
this->minMaxFreeHeapBlockSize = getTotalHeap();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (eeSettings.tick()) {
|
||||
Log.sinfoln("MAIN", F("Settings updated (EEPROM)"));
|
||||
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 USE_TELNET
|
||||
if (this->telnetStarted) {
|
||||
TelnetStream.loop();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vars.actions.restart) {
|
||||
Log.sinfoln("MAIN", F("Restart signal received. Restart after 10 sec."));
|
||||
eeSettings.updateNow();
|
||||
Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec."));
|
||||
fsSettings.updateNow();
|
||||
fsNetworkSettings.updateNow();
|
||||
this->restartSignalTime = millis();
|
||||
vars.actions.restart = false;
|
||||
}
|
||||
@@ -92,7 +94,14 @@ protected:
|
||||
tOt->enable();
|
||||
}
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
if (tNetwork->isConnected()) {
|
||||
#if USE_TELNET
|
||||
if (!this->telnetStarted) {
|
||||
TelnetStream.begin(23, false);
|
||||
this->telnetStarted = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
vars.sensors.rssi = WiFi.RSSI();
|
||||
|
||||
if (!tMqtt->isEnabled() && strlen(settings.mqtt.server) > 0) {
|
||||
@@ -111,6 +120,13 @@ protected:
|
||||
}
|
||||
|
||||
} else {
|
||||
#if USE_TELNET
|
||||
if (this->telnetStarted) {
|
||||
TelnetStream.stop();
|
||||
this->telnetStarted = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tMqtt->isEnabled()) {
|
||||
tMqtt->disable();
|
||||
}
|
||||
@@ -122,7 +138,7 @@ protected:
|
||||
|
||||
if (millis() - this->firstFailConnect > EMERGENCY_TIME_TRESHOLD) {
|
||||
vars.states.emergency = true;
|
||||
Log.sinfoln("MAIN", F("Emergency mode enabled"));
|
||||
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode enabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +166,7 @@ protected:
|
||||
}
|
||||
|
||||
void heap() {
|
||||
unsigned int freeHeapSize = ESP.getFreeHeap();
|
||||
unsigned int freeHeapSize = getFreeHeap();
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
unsigned int maxFreeBlockSize = ESP.getMaxAllocHeap();
|
||||
#else
|
||||
@@ -189,9 +205,9 @@ protected:
|
||||
uint8_t heapFrag = 100 - maxFreeBlockSize * 100.0 / freeHeapSize;
|
||||
if (millis() - this->lastHeapInfo > 20000 || minFreeHeapSizeDiff > 0 || minMaxFreeBlockSizeDiff > 0) {
|
||||
Log.sverboseln(
|
||||
"MAIN",
|
||||
FPSTR(L_MAIN),
|
||||
F("Free heap size: %u of %u bytes (min: %u, diff: %u), max free block: %u (min: %u, diff: %u, frag: %hhu%%)"),
|
||||
freeHeapSize, this->heapSize, this->minFreeHeapSize, minFreeHeapSizeDiff, maxFreeBlockSize, this->minMaxFreeHeapBlockSize, minMaxFreeBlockSizeDiff, heapFrag
|
||||
freeHeapSize, getTotalHeap(), this->minFreeHeapSize, minFreeHeapSizeDiff, maxFreeBlockSize, this->minMaxFreeHeapBlockSize, minMaxFreeBlockSizeDiff, heapFrag
|
||||
);
|
||||
this->lastHeapInfo = millis();
|
||||
}
|
||||
@@ -209,7 +225,7 @@ protected:
|
||||
this->blinkerInitialized = true;
|
||||
}
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
if (!tNetwork->isConnected()) {
|
||||
errors[errCount++] = 2;
|
||||
}
|
||||
|
||||
@@ -270,13 +286,13 @@ protected:
|
||||
}
|
||||
|
||||
if (!settings.externalPump.use || settings.externalPump.pin == 0) {
|
||||
if (vars.externalPump.enable) {
|
||||
if (vars.states.externalPump) {
|
||||
if (settings.externalPump.pin != 0) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
}
|
||||
|
||||
vars.externalPump.enable = false;
|
||||
vars.externalPump.lastEnableTime = millis();
|
||||
vars.states.externalPump = false;
|
||||
vars.parameters.extPumpLastEnableTime = millis();
|
||||
|
||||
Log.sinfoln("EXTPUMP", F("Disabled: use = off"));
|
||||
}
|
||||
@@ -284,29 +300,29 @@ protected:
|
||||
return;
|
||||
}
|
||||
|
||||
if (vars.externalPump.enable && !this->heatingEnabled) {
|
||||
if (vars.states.externalPump && !this->heatingEnabled) {
|
||||
if (this->externalPumpStartReason == MainTask::REASON_PUMP_START_HEATING && millis() - this->heatingDisabledTime > ((unsigned int) settings.externalPump.postCirculationTime * 1000)) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
|
||||
vars.externalPump.enable = false;
|
||||
vars.externalPump.lastEnableTime = millis();
|
||||
vars.states.externalPump = false;
|
||||
vars.parameters.extPumpLastEnableTime = millis();
|
||||
|
||||
Log.sinfoln("EXTPUMP", F("Disabled: expired post circulation time"));
|
||||
|
||||
} else if (this->externalPumpStartReason == MainTask::REASON_PUMP_START_ANTISTUCK && millis() - this->externalPumpStartTime >= ((unsigned int) settings.externalPump.antiStuckTime * 1000)) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
|
||||
vars.externalPump.enable = false;
|
||||
vars.externalPump.lastEnableTime = millis();
|
||||
vars.states.externalPump = false;
|
||||
vars.parameters.extPumpLastEnableTime = millis();
|
||||
|
||||
Log.sinfoln("EXTPUMP", F("Disabled: expired anti stuck time"));
|
||||
}
|
||||
|
||||
} else if (vars.externalPump.enable && this->heatingEnabled && this->externalPumpStartReason == MainTask::REASON_PUMP_START_ANTISTUCK) {
|
||||
} else if (vars.states.externalPump && this->heatingEnabled && this->externalPumpStartReason == MainTask::REASON_PUMP_START_ANTISTUCK) {
|
||||
this->externalPumpStartReason = MainTask::REASON_PUMP_START_HEATING;
|
||||
|
||||
} else if (!vars.externalPump.enable && this->heatingEnabled) {
|
||||
vars.externalPump.enable = true;
|
||||
} else if (!vars.states.externalPump && this->heatingEnabled) {
|
||||
vars.states.externalPump = true;
|
||||
this->externalPumpStartTime = millis();
|
||||
this->externalPumpStartReason = MainTask::REASON_PUMP_START_HEATING;
|
||||
|
||||
@@ -314,8 +330,8 @@ protected:
|
||||
|
||||
Log.sinfoln("EXTPUMP", F("Enabled: heating on"));
|
||||
|
||||
} else if (!vars.externalPump.enable && (vars.externalPump.lastEnableTime == 0 || millis() - vars.externalPump.lastEnableTime >= ((unsigned long) settings.externalPump.antiStuckInterval * 1000))) {
|
||||
vars.externalPump.enable = true;
|
||||
} else if (!vars.states.externalPump && (vars.parameters.extPumpLastEnableTime == 0 || millis() - vars.parameters.extPumpLastEnableTime >= ((unsigned long) settings.externalPump.antiStuckInterval * 1000))) {
|
||||
vars.states.externalPump = true;
|
||||
this->externalPumpStartTime = millis();
|
||||
this->externalPumpStartReason = MainTask::REASON_PUMP_START_ANTISTUCK;
|
||||
|
||||
|
||||
475
src/MqttTask.h
475
src/MqttTask.h
@@ -1,16 +1,15 @@
|
||||
#include <PubSubClient.h>
|
||||
#include <MqttClient.h>
|
||||
#include <MqttWiFiClient.h>
|
||||
#include <MqttWriter.h>
|
||||
#include "HaHelper.h"
|
||||
|
||||
extern EEManager eeSettings;
|
||||
|
||||
extern FileData fsSettings;
|
||||
|
||||
class MqttTask : public Task {
|
||||
public:
|
||||
MqttTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {
|
||||
this->wifiClient = new MqttWiFiClient();
|
||||
this->client = new PubSubClient();
|
||||
this->client = new MqttClient(this->wifiClient);
|
||||
this->writer = new MqttWriter(this->client, 256);
|
||||
this->haHelper = new HaHelper();
|
||||
}
|
||||
@@ -22,7 +21,7 @@ public:
|
||||
|
||||
if (this->client != nullptr) {
|
||||
if (this->client->connected()) {
|
||||
this->client->disconnect();
|
||||
this->client->stop();
|
||||
}
|
||||
|
||||
delete this->client;
|
||||
@@ -37,9 +36,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void disable() {
|
||||
this->client->stop();
|
||||
this->wifiClient->stop();
|
||||
Task::disable();
|
||||
|
||||
Log.sinfoln(FPSTR(L_MQTT), F("Disabled"));
|
||||
}
|
||||
|
||||
void enable() {
|
||||
Task::enable();
|
||||
|
||||
Log.sinfoln(FPSTR(L_MQTT), F("Enabled"));
|
||||
}
|
||||
|
||||
bool isConnected() {
|
||||
return this->connected;
|
||||
}
|
||||
|
||||
protected:
|
||||
MqttWiFiClient* wifiClient = nullptr;
|
||||
PubSubClient* client = nullptr;
|
||||
MqttClient* client = nullptr;
|
||||
HaHelper* haHelper = nullptr;
|
||||
MqttWriter* writer = nullptr;
|
||||
unsigned short readyForSendTime = 15000;
|
||||
@@ -68,7 +85,7 @@ protected:
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Log.sinfoln("MQTT", F("Started"));
|
||||
Log.sinfoln(FPSTR(L_MQTT), F("Started"));
|
||||
|
||||
// wificlient settings
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
@@ -77,19 +94,27 @@ protected:
|
||||
#endif
|
||||
|
||||
// client settings
|
||||
this->client->setClient(*this->wifiClient);
|
||||
this->client->setKeepAlive(15);
|
||||
|
||||
//this->client->setClient(*this->wifiClient);
|
||||
this->client->setKeepAliveInterval(15000);
|
||||
this->client->setTxPayloadSize(256);
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
this->client->setSocketTimeout(1);
|
||||
this->client->setBufferSize(768);
|
||||
this->client->setConnectionTimeout(1000);
|
||||
#else
|
||||
this->client->setSocketTimeout(3);
|
||||
this->client->setBufferSize(1536);
|
||||
this->client->setConnectionTimeout(3000);
|
||||
#endif
|
||||
|
||||
this->client->setCallback([this] (char* topic, uint8_t* payload, unsigned int length) {
|
||||
this->onMessage(topic, payload, length);
|
||||
|
||||
this->client->onMessage([this] (void*, size_t length) {
|
||||
String topic = this->client->messageTopic();
|
||||
if (!length || length > 2048 || !topic.length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t payload[length];
|
||||
for (size_t i = 0; i < length && this->client->available(); i++) {
|
||||
payload[i] = this->client->read();
|
||||
}
|
||||
|
||||
this->onMessage(topic.c_str(), payload, length);
|
||||
});
|
||||
|
||||
// writer settings
|
||||
@@ -100,13 +125,13 @@ protected:
|
||||
#endif
|
||||
|
||||
this->writer->setEventPublishCallback([this] (const char* topic, size_t written, size_t length, bool result) {
|
||||
Log.straceln("MQTT", F("%s publish %u of %u bytes to topic: %s"), result ? F("Successfully") : F("Failed"), written, length, topic);
|
||||
Log.straceln(FPSTR(L_MQTT), F("%s publish %u of %u bytes to topic: %s"), result ? F("Successfully") : F("Failed"), written, length, topic);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::yield();
|
||||
#endif
|
||||
|
||||
this->client->loop();
|
||||
//this->client->poll();
|
||||
this->delay(250);
|
||||
});
|
||||
|
||||
@@ -132,7 +157,7 @@ protected:
|
||||
void loop() {
|
||||
if (settings.mqtt.interval > 120) {
|
||||
settings.mqtt.interval = 5;
|
||||
eeSettings.update();
|
||||
fsSettings.update();
|
||||
}
|
||||
|
||||
if (!this->client->connected() && this->connected) {
|
||||
@@ -141,10 +166,11 @@ protected:
|
||||
}
|
||||
|
||||
if (this->wifiClient == nullptr || (!this->client->connected() && millis() - this->lastReconnectTime >= MQTT_RECONNECT_INTERVAL)) {
|
||||
Log.sinfoln("MQTT", F("Connecting to %s:%u..."), settings.mqtt.server, settings.mqtt.port);
|
||||
Log.sinfoln(FPSTR(L_MQTT), F("Connecting to %s:%u..."), settings.mqtt.server, settings.mqtt.port);
|
||||
|
||||
this->client->setServer(settings.mqtt.server, settings.mqtt.port);
|
||||
this->client->connect(settings.hostname, settings.mqtt.user, settings.mqtt.password);
|
||||
this->client->setId(networkSettings.hostname);
|
||||
this->client->setUsernamePassword(settings.mqtt.user, settings.mqtt.password);
|
||||
this->client->connect(settings.mqtt.server, settings.mqtt.port);
|
||||
|
||||
this->lastReconnectTime = millis();
|
||||
}
|
||||
@@ -158,7 +184,7 @@ protected:
|
||||
if (settings.emergency.enable && !vars.states.emergency) {
|
||||
if (millis() - this->disconnectedTime > EMERGENCY_TIME_TRESHOLD) {
|
||||
vars.states.emergency = true;
|
||||
Log.sinfoln("MQTT", F("Emergency mode enabled"));
|
||||
Log.sinfoln(FPSTR(L_MQTT), F("Emergency mode enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +194,8 @@ protected:
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::yield();
|
||||
#endif
|
||||
this->client->loop();
|
||||
|
||||
this->client->poll();
|
||||
|
||||
// delay for publish data
|
||||
if (!this->isReadyForSend()) {
|
||||
@@ -213,11 +240,11 @@ protected:
|
||||
this->connectedTime = millis();
|
||||
this->newConnection = true;
|
||||
unsigned long downtime = (millis() - this->disconnectedTime) / 1000;
|
||||
Log.sinfoln("MQTT", F("Connected (downtime: %u s.)"), downtime);
|
||||
Log.sinfoln(FPSTR(L_MQTT), F("Connected (downtime: %u s.)"), downtime);
|
||||
|
||||
if (vars.states.emergency) {
|
||||
vars.states.emergency = false;
|
||||
Log.sinfoln("MQTT", F("Emergency mode disabled"));
|
||||
Log.sinfoln(FPSTR(L_MQTT), F("Emergency mode disabled"));
|
||||
}
|
||||
|
||||
this->client->subscribe(this->haHelper->getDeviceTopic("settings/set").c_str());
|
||||
@@ -228,16 +255,16 @@ protected:
|
||||
this->disconnectedTime = millis();
|
||||
|
||||
unsigned long uptime = (millis() - this->connectedTime) / 1000;
|
||||
Log.swarningln("MQTT", F("Disconnected (reason: %d uptime: %u s.)"), this->client->state(), uptime);
|
||||
Log.swarningln(FPSTR(L_MQTT), F("Disconnected (reason: %d uptime: %u s.)"), this->client->connectError(), uptime);
|
||||
}
|
||||
|
||||
void onMessage(char* topic, uint8_t* payload, unsigned int length) {
|
||||
void onMessage(const char* topic, uint8_t* payload, size_t length) {
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.debug) {
|
||||
Log.strace("MQTT.MSG", F("Topic: %s\r\n> "), topic);
|
||||
Log.strace(FPSTR(L_MQTT_MSG), F("Topic: %s\r\n> "), topic);
|
||||
if (Log.lock()) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (payload[i] == 0) {
|
||||
@@ -258,8 +285,12 @@ protected:
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError dErr = deserializeJson(doc, payload, length);
|
||||
if (dErr != DeserializationError::Ok || doc.isNull()) {
|
||||
Log.swarningln("MQTT.MSG", F("Error on deserialization: %s"), dErr.f_str());
|
||||
if (dErr != DeserializationError::Ok) {
|
||||
Log.swarningln(FPSTR(L_MQTT_MSG), F("Error on deserialization: %s"), dErr.f_str());
|
||||
return;
|
||||
|
||||
} else if (doc.isNull() || !doc.size()) {
|
||||
Log.swarningln(FPSTR(L_MQTT_MSG), F("Not valid json"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,254 +306,13 @@ protected:
|
||||
|
||||
|
||||
bool updateSettings(JsonDocument& doc) {
|
||||
bool flag = false;
|
||||
|
||||
if (!doc["debug"].isNull() && doc["debug"].is<bool>()) {
|
||||
settings.debug = doc["debug"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
|
||||
// emergency
|
||||
if (!doc["emergency"]["enable"].isNull() && doc["emergency"]["enable"].is<bool>()) {
|
||||
settings.emergency.enable = doc["emergency"]["enable"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["emergency"]["target"].isNull() && doc["emergency"]["target"].is<double>()) {
|
||||
if (doc["emergency"]["target"].as<double>() > 0 && doc["emergency"]["target"].as<double>() < 100) {
|
||||
settings.emergency.target = MqttTask::round(doc["emergency"]["target"].as<double>(), 2);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["emergency"]["useEquitherm"].isNull() && doc["emergency"]["useEquitherm"].is<bool>()) {
|
||||
if (settings.sensors.outdoor.type != 1) {
|
||||
settings.emergency.useEquitherm = doc["emergency"]["useEquitherm"].as<bool>();
|
||||
|
||||
} else {
|
||||
settings.emergency.useEquitherm = false;
|
||||
}
|
||||
|
||||
if (settings.emergency.useEquitherm && settings.emergency.usePid) {
|
||||
settings.emergency.usePid = false;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["emergency"]["usePid"].isNull() && doc["emergency"]["usePid"].is<bool>()) {
|
||||
if (settings.sensors.indoor.type != 1) {
|
||||
settings.emergency.usePid = doc["emergency"]["usePid"].as<bool>();
|
||||
|
||||
} else {
|
||||
settings.emergency.usePid = false;
|
||||
}
|
||||
|
||||
if (settings.emergency.usePid && settings.emergency.useEquitherm) {
|
||||
settings.emergency.useEquitherm = false;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
}
|
||||
|
||||
|
||||
// heating
|
||||
if (!doc["heating"]["enable"].isNull() && doc["heating"]["enable"].is<bool>()) {
|
||||
settings.heating.enable = doc["heating"]["enable"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["heating"]["turbo"].isNull() && doc["heating"]["turbo"].is<bool>()) {
|
||||
settings.heating.turbo = doc["heating"]["turbo"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["heating"]["target"].isNull() && doc["heating"]["target"].is<double>()) {
|
||||
if (doc["heating"]["target"].as<double>() > 0 && doc["heating"]["target"].as<double>() < 100) {
|
||||
settings.heating.target = MqttTask::round(doc["heating"]["target"].as<double>(), 2);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["heating"]["hysteresis"].isNull() && doc["heating"]["hysteresis"].is<double>()) {
|
||||
if (doc["heating"]["hysteresis"].as<double>() >= 0 && doc["heating"]["hysteresis"].as<double>() <= 5) {
|
||||
settings.heating.hysteresis = MqttTask::round(doc["heating"]["hysteresis"].as<double>(), 2);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["heating"]["maxModulation"].isNull() && doc["heating"]["maxModulation"].is<unsigned char>()) {
|
||||
if (doc["heating"]["maxModulation"].as<unsigned char>() > 0 && doc["heating"]["maxModulation"].as<unsigned char>() <= 100) {
|
||||
settings.heating.maxModulation = doc["heating"]["maxModulation"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["heating"]["maxTemp"].isNull() && doc["heating"]["maxTemp"].is<unsigned char>()) {
|
||||
if (doc["heating"]["maxTemp"].as<unsigned char>() > 0 && doc["heating"]["maxTemp"].as<unsigned char>() <= 100) {
|
||||
settings.heating.maxTemp = doc["heating"]["maxTemp"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["heating"]["minTemp"].isNull() && doc["heating"]["minTemp"].is<unsigned char>()) {
|
||||
if (doc["heating"]["minTemp"].as<unsigned char>() >= 0 && doc["heating"]["minTemp"].as<unsigned char>() < 100) {
|
||||
settings.heating.minTemp = doc["heating"]["minTemp"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// dhw
|
||||
if (!doc["dhw"]["enable"].isNull() && doc["dhw"]["enable"].is<bool>()) {
|
||||
settings.dhw.enable = doc["dhw"]["enable"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["dhw"]["target"].isNull() && doc["dhw"]["target"].is<unsigned char>()) {
|
||||
if (doc["dhw"]["target"].as<unsigned char>() >= 0 && doc["dhw"]["target"].as<unsigned char>() < 100) {
|
||||
settings.dhw.target = doc["dhw"]["target"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["dhw"]["maxTemp"].isNull() && doc["dhw"]["maxTemp"].is<unsigned char>()) {
|
||||
if (doc["dhw"]["maxTemp"].as<unsigned char>() > 0 && doc["dhw"]["maxTemp"].as<unsigned char>() <= 100) {
|
||||
settings.dhw.maxTemp = doc["dhw"]["maxTemp"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["dhw"]["minTemp"].isNull() && doc["dhw"]["minTemp"].is<unsigned char>()) {
|
||||
if (doc["dhw"]["minTemp"].as<unsigned char>() >= 0 && doc["dhw"]["minTemp"].as<unsigned char>() < 100) {
|
||||
settings.dhw.minTemp = doc["dhw"]["minTemp"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pid
|
||||
if (!doc["pid"]["enable"].isNull() && doc["pid"]["enable"].is<bool>()) {
|
||||
settings.pid.enable = doc["pid"]["enable"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["pid"]["p_factor"].isNull() && doc["pid"]["p_factor"].is<double>()) {
|
||||
if (doc["pid"]["p_factor"].as<double>() > 0 && doc["pid"]["p_factor"].as<double>() <= 1000) {
|
||||
settings.pid.p_factor = MqttTask::round(doc["pid"]["p_factor"].as<double>(), 3);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["pid"]["i_factor"].isNull() && doc["pid"]["i_factor"].is<double>()) {
|
||||
if (doc["pid"]["i_factor"].as<double>() >= 0 && doc["pid"]["i_factor"].as<double>() <= 100) {
|
||||
settings.pid.i_factor = MqttTask::round(doc["pid"]["i_factor"].as<double>(), 3);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["pid"]["d_factor"].isNull() && doc["pid"]["d_factor"].is<double>()) {
|
||||
if (doc["pid"]["d_factor"].as<double>() >= 0 && doc["pid"]["d_factor"].as<double>() <= 100000) {
|
||||
settings.pid.d_factor = MqttTask::round(doc["pid"]["d_factor"].as<double>(), 1);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["pid"]["dt"].isNull() && doc["pid"]["dt"].is<double>()) {
|
||||
if (doc["pid"]["dt"].as<unsigned short>() >= 30 && doc["pid"]["dt"].as<unsigned short>() <= 600) {
|
||||
settings.pid.dt = doc["pid"]["dt"].as<unsigned short>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["pid"]["maxTemp"].isNull() && doc["pid"]["maxTemp"].is<unsigned char>()) {
|
||||
if (doc["pid"]["maxTemp"].as<unsigned char>() > 0 && doc["pid"]["maxTemp"].as<unsigned char>() <= 100 && doc["pid"]["maxTemp"].as<unsigned char>() > settings.pid.minTemp) {
|
||||
settings.pid.maxTemp = doc["pid"]["maxTemp"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["pid"]["minTemp"].isNull() && doc["pid"]["minTemp"].is<unsigned char>()) {
|
||||
if (doc["pid"]["minTemp"].as<unsigned char>() >= 0 && doc["pid"]["minTemp"].as<unsigned char>() < 100 && doc["pid"]["minTemp"].as<unsigned char>() < settings.pid.maxTemp) {
|
||||
settings.pid.minTemp = doc["pid"]["minTemp"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
// equitherm
|
||||
if (!doc["equitherm"]["enable"].isNull() && doc["equitherm"]["enable"].is<bool>()) {
|
||||
settings.equitherm.enable = doc["equitherm"]["enable"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["equitherm"]["n_factor"].isNull() && doc["equitherm"]["n_factor"].is<double>()) {
|
||||
if (doc["equitherm"]["n_factor"].as<double>() > 0 && doc["equitherm"]["n_factor"].as<double>() <= 10) {
|
||||
settings.equitherm.n_factor = MqttTask::round(doc["equitherm"]["n_factor"].as<double>(), 3);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["equitherm"]["k_factor"].isNull() && doc["equitherm"]["k_factor"].is<double>()) {
|
||||
if (doc["equitherm"]["k_factor"].as<double>() >= 0 && doc["equitherm"]["k_factor"].as<double>() <= 10) {
|
||||
settings.equitherm.k_factor = MqttTask::round(doc["equitherm"]["k_factor"].as<double>(), 3);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["equitherm"]["t_factor"].isNull() && doc["equitherm"]["t_factor"].is<double>()) {
|
||||
if (doc["equitherm"]["t_factor"].as<double>() >= 0 && doc["equitherm"]["t_factor"].as<double>() <= 10) {
|
||||
settings.equitherm.t_factor = MqttTask::round(doc["equitherm"]["t_factor"].as<double>(), 3);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sensors
|
||||
if (!doc["sensors"]["outdoor"]["type"].isNull() && doc["sensors"]["outdoor"]["type"].is<unsigned char>()) {
|
||||
if (doc["sensors"]["outdoor"]["type"].as<unsigned char>() >= 0 && doc["sensors"]["outdoor"]["type"].as<unsigned char>() <= 2) {
|
||||
settings.sensors.outdoor.type = doc["sensors"]["outdoor"]["type"].as<unsigned char>();
|
||||
|
||||
if (settings.sensors.outdoor.type == 1) {
|
||||
settings.emergency.useEquitherm = false;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["sensors"]["outdoor"]["offset"].isNull() && doc["sensors"]["outdoor"]["offset"].is<double>()) {
|
||||
if (doc["sensors"]["outdoor"]["offset"].as<double>() >= -10 && doc["sensors"]["outdoor"]["offset"].as<double>() <= 10) {
|
||||
settings.sensors.outdoor.offset = MqttTask::round(doc["sensors"]["outdoor"]["offset"].as<double>(), 2);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["sensors"]["indoor"]["type"].isNull() && doc["sensors"]["indoor"]["type"].is<unsigned char>()) {
|
||||
if (doc["sensors"]["indoor"]["type"].as<unsigned char>() >= 1 && doc["sensors"]["indoor"]["type"].as<unsigned char>() <= 3) {
|
||||
settings.sensors.indoor.type = doc["sensors"]["indoor"]["type"].as<unsigned char>();
|
||||
|
||||
if (settings.sensors.indoor.type == 1) {
|
||||
settings.emergency.usePid = false;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["sensors"]["indoor"]["offset"].isNull() && doc["sensors"]["indoor"]["offset"].is<double>()) {
|
||||
if (doc["sensors"]["indoor"]["offset"].as<double>() >= -10 && doc["sensors"]["indoor"]["offset"].as<double>() <= 10) {
|
||||
settings.sensors.indoor.offset = MqttTask::round(doc["sensors"]["indoor"]["offset"].as<double>(), 2);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool changed = safeJsonToSettings(doc, settings);
|
||||
doc.clear();
|
||||
doc.shrinkToFit();
|
||||
|
||||
if (flag) {
|
||||
if (changed) {
|
||||
this->prevPubSettingsTime = 0;
|
||||
eeSettings.update();
|
||||
fsSettings.update();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -530,54 +320,11 @@ protected:
|
||||
}
|
||||
|
||||
bool updateVariables(JsonDocument& doc) {
|
||||
bool flag = false;
|
||||
|
||||
if (!doc["ping"].isNull() && doc["ping"]) {
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["tuning"]["enable"].isNull() && doc["tuning"]["enable"].is<bool>()) {
|
||||
vars.tuning.enable = doc["tuning"]["enable"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["tuning"]["regulator"].isNull() && doc["tuning"]["regulator"].is<unsigned char>()) {
|
||||
if (doc["tuning"]["regulator"].as<unsigned char>() >= 0 && doc["tuning"]["regulator"].as<unsigned char>() <= 1) {
|
||||
vars.tuning.regulator = doc["tuning"]["regulator"].as<unsigned char>();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["temperatures"]["indoor"].isNull() && doc["temperatures"]["indoor"].is<double>()) {
|
||||
if (settings.sensors.indoor.type == 1 && doc["temperatures"]["indoor"].as<double>() > -100 && doc["temperatures"]["indoor"].as<double>() < 100) {
|
||||
vars.temperatures.indoor = MqttTask::round(doc["temperatures"]["indoor"].as<double>(), 2);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["temperatures"]["outdoor"].isNull() && doc["temperatures"]["outdoor"].is<double>()) {
|
||||
if (settings.sensors.outdoor.type == 1 && doc["temperatures"]["outdoor"].as<double>() > -100 && doc["temperatures"]["outdoor"].as<double>() < 100) {
|
||||
vars.temperatures.outdoor = MqttTask::round(doc["temperatures"]["outdoor"].as<double>(), 2);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doc["actions"]["restart"].isNull() && doc["actions"]["restart"].is<bool>() && doc["actions"]["restart"].as<bool>()) {
|
||||
vars.actions.restart = true;
|
||||
}
|
||||
|
||||
if (!doc["actions"]["resetFault"].isNull() && doc["actions"]["resetFault"].is<bool>() && doc["actions"]["resetFault"].as<bool>()) {
|
||||
vars.actions.resetFault = true;
|
||||
}
|
||||
|
||||
if (!doc["actions"]["resetDiagnostic"].isNull() && doc["actions"]["resetDiagnostic"].is<bool>() && doc["actions"]["resetDiagnostic"].as<bool>()) {
|
||||
vars.actions.resetDiagnostic = true;
|
||||
}
|
||||
|
||||
bool changed = jsonToVars(doc, vars);
|
||||
doc.clear();
|
||||
doc.shrinkToFit();
|
||||
|
||||
if (flag) {
|
||||
if (changed) {
|
||||
this->prevPubVarsTime = 0;
|
||||
return true;
|
||||
}
|
||||
@@ -766,97 +513,15 @@ protected:
|
||||
|
||||
bool publishSettings(const char* topic) {
|
||||
JsonDocument doc;
|
||||
doc["debug"] = settings.debug;
|
||||
|
||||
doc["emergency"]["enable"] = settings.emergency.enable;
|
||||
doc["emergency"]["target"] = MqttTask::round(settings.emergency.target, 2);
|
||||
doc["emergency"]["useEquitherm"] = settings.emergency.useEquitherm;
|
||||
doc["emergency"]["usePid"] = settings.emergency.usePid;
|
||||
|
||||
doc["heating"]["enable"] = settings.heating.enable;
|
||||
doc["heating"]["turbo"] = settings.heating.turbo;
|
||||
doc["heating"]["target"] = MqttTask::round(settings.heating.target, 2);
|
||||
doc["heating"]["hysteresis"] = MqttTask::round(settings.heating.hysteresis, 2);
|
||||
doc["heating"]["minTemp"] = settings.heating.minTemp;
|
||||
doc["heating"]["maxTemp"] = settings.heating.maxTemp;
|
||||
doc["heating"]["maxModulation"] = settings.heating.maxModulation;
|
||||
|
||||
doc["dhw"]["enable"] = settings.dhw.enable;
|
||||
doc["dhw"]["target"] = settings.dhw.target;
|
||||
doc["dhw"]["minTemp"] = settings.dhw.minTemp;
|
||||
doc["dhw"]["maxTemp"] = settings.dhw.maxTemp;
|
||||
|
||||
doc["pid"]["enable"] = settings.pid.enable;
|
||||
doc["pid"]["p_factor"] = MqttTask::round(settings.pid.p_factor, 3);
|
||||
doc["pid"]["i_factor"] = MqttTask::round(settings.pid.i_factor, 3);
|
||||
doc["pid"]["d_factor"] = MqttTask::round(settings.pid.d_factor, 1);
|
||||
doc["pid"]["dt"] = settings.pid.dt;
|
||||
doc["pid"]["minTemp"] = settings.pid.minTemp;
|
||||
doc["pid"]["maxTemp"] = settings.pid.maxTemp;
|
||||
|
||||
doc["equitherm"]["enable"] = settings.equitherm.enable;
|
||||
doc["equitherm"]["n_factor"] = MqttTask::round(settings.equitherm.n_factor, 3);
|
||||
doc["equitherm"]["k_factor"] = MqttTask::round(settings.equitherm.k_factor, 3);
|
||||
doc["equitherm"]["t_factor"] = MqttTask::round(settings.equitherm.t_factor, 3);
|
||||
|
||||
doc["sensors"]["outdoor"]["type"] = settings.sensors.outdoor.type;
|
||||
doc["sensors"]["outdoor"]["offset"] = MqttTask::round(settings.sensors.outdoor.offset, 2);
|
||||
|
||||
doc["sensors"]["indoor"]["type"] = settings.sensors.indoor.type;
|
||||
doc["sensors"]["indoor"]["offset"] = MqttTask::round(settings.sensors.indoor.offset, 2);
|
||||
|
||||
doc.shrinkToFit();
|
||||
safeSettingsToJson(settings, doc);
|
||||
|
||||
return this->writer->publish(topic, doc, true);
|
||||
}
|
||||
|
||||
bool publishVariables(const char* topic) {
|
||||
JsonDocument doc;
|
||||
|
||||
doc["tuning"]["enable"] = vars.tuning.enable;
|
||||
doc["tuning"]["regulator"] = vars.tuning.regulator;
|
||||
|
||||
doc["states"]["otStatus"] = vars.states.otStatus;
|
||||
doc["states"]["heating"] = vars.states.heating;
|
||||
doc["states"]["dhw"] = vars.states.dhw;
|
||||
doc["states"]["flame"] = vars.states.flame;
|
||||
doc["states"]["fault"] = vars.states.fault;
|
||||
doc["states"]["diagnostic"] = vars.states.diagnostic;
|
||||
|
||||
doc["sensors"]["modulation"] = MqttTask::round(vars.sensors.modulation, 2);
|
||||
doc["sensors"]["pressure"] = MqttTask::round(vars.sensors.pressure, 2);
|
||||
doc["sensors"]["dhwFlowRate"] = vars.sensors.dhwFlowRate;
|
||||
doc["sensors"]["faultCode"] = vars.sensors.faultCode;
|
||||
doc["sensors"]["rssi"] = vars.sensors.rssi;
|
||||
doc["sensors"]["uptime"] = millis() / 1000ul;
|
||||
|
||||
doc["temperatures"]["indoor"] = MqttTask::round(vars.temperatures.indoor, 2);
|
||||
doc["temperatures"]["outdoor"] = MqttTask::round(vars.temperatures.outdoor, 2);
|
||||
doc["temperatures"]["heating"] = MqttTask::round(vars.temperatures.heating, 2);
|
||||
doc["temperatures"]["dhw"] = MqttTask::round(vars.temperatures.dhw, 2);
|
||||
|
||||
doc["parameters"]["heatingEnabled"] = vars.parameters.heatingEnabled;
|
||||
doc["parameters"]["heatingMinTemp"] = vars.parameters.heatingMinTemp;
|
||||
doc["parameters"]["heatingMaxTemp"] = vars.parameters.heatingMaxTemp;
|
||||
doc["parameters"]["heatingSetpoint"] = vars.parameters.heatingSetpoint;
|
||||
doc["parameters"]["dhwMinTemp"] = vars.parameters.dhwMinTemp;
|
||||
doc["parameters"]["dhwMaxTemp"] = vars.parameters.dhwMaxTemp;
|
||||
|
||||
doc.shrinkToFit();
|
||||
varsToJson(vars, doc);
|
||||
|
||||
return this->writer->publish(topic, doc, true);
|
||||
}
|
||||
|
||||
static double round(double value, uint8_t decimals = 2) {
|
||||
if (decimals == 0) {
|
||||
return (int)(value + 0.001);
|
||||
|
||||
} else if (abs(value) < 0.00000001) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double multiplier = pow10(decimals);
|
||||
value += 0.5 / multiplier * (value < 0 ? -1 : 1);
|
||||
return (int)(value * multiplier) / multiplier;
|
||||
}
|
||||
};
|
||||
406
src/NetworkTask.h
Normal file
406
src/NetworkTask.h
Normal file
@@ -0,0 +1,406 @@
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include "lwip/etharp.h"
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#include <Connection.h>
|
||||
|
||||
|
||||
class NetworkTask : public Task {
|
||||
public:
|
||||
NetworkTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {
|
||||
Connection::setup(this->useDhcp);
|
||||
}
|
||||
|
||||
NetworkTask* setHostname(const char* value) {
|
||||
this->hostname = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
NetworkTask* setApCredentials(const char* ssid, const char* password = nullptr, byte channel = 0) {
|
||||
this->apName = ssid;
|
||||
this->apPassword = password;
|
||||
this->apChannel = channel;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
NetworkTask* setStaCredentials(const char* ssid = nullptr, const char* password = nullptr, byte channel = 0) {
|
||||
this->staSsid = ssid;
|
||||
this->staPassword = password;
|
||||
this->staChannel = channel;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
NetworkTask* setUseDhcp(bool value) {
|
||||
this->useDhcp = value;
|
||||
Connection::setup(this->useDhcp);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
NetworkTask* setStaticConfig(const char* ip, const char* gateway, const char* subnet, const char* dns) {
|
||||
this->staticIp.fromString(ip);
|
||||
this->staticGateway.fromString(gateway);
|
||||
this->staticSubnet.fromString(subnet);
|
||||
this->staticDns.fromString(dns);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
NetworkTask* setStaticConfig(IPAddress &ip, IPAddress &gateway, IPAddress &subnet, IPAddress &dns) {
|
||||
this->staticIp = ip;
|
||||
this->staticGateway = gateway;
|
||||
this->staticSubnet = subnet;
|
||||
this->staticDns = dns;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool hasStaCredentials() {
|
||||
return this->staSsid != nullptr;
|
||||
}
|
||||
|
||||
bool isConnected() {
|
||||
return this->isStaEnabled() && Connection::getStatus() == Connection::Status::CONNECTED;
|
||||
}
|
||||
|
||||
bool isConnecting() {
|
||||
return this->isStaEnabled() && Connection::getStatus() == Connection::Status::CONNECTING;
|
||||
}
|
||||
|
||||
bool isStaEnabled() {
|
||||
return (WiFi.getMode() & WIFI_STA) != 0;
|
||||
}
|
||||
|
||||
bool isApEnabled() {
|
||||
return (WiFi.getMode() & WIFI_AP) != 0;
|
||||
}
|
||||
|
||||
bool hasApClients() {
|
||||
if (!this->isApEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WiFi.softAPgetStationNum() > 0;
|
||||
}
|
||||
|
||||
short int getRssi() {
|
||||
return WiFi.RSSI();
|
||||
}
|
||||
|
||||
IPAddress getApIp() {
|
||||
return WiFi.softAPIP();
|
||||
}
|
||||
|
||||
IPAddress getStaIp() {
|
||||
return WiFi.localIP();
|
||||
}
|
||||
|
||||
IPAddress getStaSubnet() {
|
||||
return WiFi.subnetMask();
|
||||
}
|
||||
|
||||
IPAddress getStaGateway() {
|
||||
return WiFi.gatewayIP();
|
||||
}
|
||||
|
||||
IPAddress getStaDns() {
|
||||
return WiFi.dnsIP();
|
||||
}
|
||||
|
||||
String getStaMac() {
|
||||
return WiFi.macAddress();
|
||||
}
|
||||
|
||||
const char* getStaSsid() {
|
||||
return this->staSsid;
|
||||
}
|
||||
|
||||
const char* getStaPassword() {
|
||||
return this->staPassword;
|
||||
}
|
||||
|
||||
byte getStaChannel() {
|
||||
return this->staChannel;
|
||||
}
|
||||
|
||||
bool resetWifi() {
|
||||
WiFi.persistent(false);
|
||||
WiFi.setAutoConnect(false);
|
||||
WiFi.setAutoReconnect(false);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
WiFi.setSleepMode(WIFI_NONE_SLEEP);
|
||||
|
||||
if (wifi_softap_dhcps_status() == DHCP_STARTED) {
|
||||
wifi_softap_dhcps_stop();
|
||||
}
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
WiFi.setSleep(WIFI_PS_NONE);
|
||||
#endif
|
||||
WiFi.softAPdisconnect();
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (wifi_station_dhcpc_status() == DHCP_STARTED) {
|
||||
wifi_station_dhcpc_stop();
|
||||
}
|
||||
#endif
|
||||
WiFi.disconnect(false, true);
|
||||
|
||||
return WiFi.mode(WIFI_OFF);
|
||||
}
|
||||
|
||||
void reconnect() {
|
||||
this->reconnectFlag = true;
|
||||
}
|
||||
|
||||
bool connect(bool force = false, unsigned int timeout = 1000u) {
|
||||
if (this->isConnected() && !force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (force && !this->isApEnabled()) {
|
||||
this->resetWifi();
|
||||
|
||||
} else {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (wifi_station_dhcpc_status() == DHCP_STARTED) {
|
||||
wifi_station_dhcpc_stop();
|
||||
}
|
||||
#endif
|
||||
|
||||
WiFi.disconnect(false, true);
|
||||
}
|
||||
|
||||
if (!this->hasStaCredentials()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->delay(200);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (this->setWifiHostname(this->hostname)) {
|
||||
Log.straceln(FPSTR(L_NETWORK), F("Set hostname '%s': success"), this->hostname);
|
||||
|
||||
} else {
|
||||
Log.serrorln(FPSTR(L_NETWORK), F("Set hostname '%s': fail"), this->hostname);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!WiFi.mode((WiFiMode_t)(WiFi.getMode() | WIFI_STA))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->delay(200);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (this->setWifiHostname(this->hostname)) {
|
||||
Log.straceln(FPSTR(L_NETWORK), F("Set hostname '%s': success"), this->hostname);
|
||||
|
||||
} else {
|
||||
Log.serrorln(FPSTR(L_NETWORK), F("Set hostname '%s': fail"), this->hostname);
|
||||
}
|
||||
|
||||
this->delay(200);
|
||||
#endif
|
||||
|
||||
if (!this->useDhcp) {
|
||||
WiFi.config(this->staticIp, this->staticGateway, this->staticSubnet, this->staticDns);
|
||||
}
|
||||
|
||||
WiFi.begin(this->staSsid, this->staPassword, this->staChannel);
|
||||
|
||||
unsigned long beginConnectionTime = millis();
|
||||
while (millis() - beginConnectionTime < timeout) {
|
||||
this->delay(100);
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static byte rssiToSignalQuality(short int rssi) {
|
||||
return constrain(map(rssi, -100, -50, 0, 100), 0, 100);
|
||||
}
|
||||
|
||||
protected:
|
||||
const unsigned int reconnectInterval = 5000;
|
||||
const unsigned int failedConnectTimeout = 30000; // 120000
|
||||
const unsigned int connectionTimeout = 15000;
|
||||
const unsigned int resetConnectionTimeout = 60000;
|
||||
|
||||
const char* hostname = "esp";
|
||||
const char* apName = "ESP";
|
||||
const char* apPassword = nullptr;
|
||||
byte apChannel = 1;
|
||||
|
||||
const char* staSsid = nullptr;
|
||||
const char* staPassword = nullptr;
|
||||
byte staChannel = 0;
|
||||
|
||||
bool useDhcp = true;
|
||||
IPAddress staticIp;
|
||||
IPAddress staticGateway;
|
||||
IPAddress staticSubnet;
|
||||
IPAddress staticDns;
|
||||
|
||||
bool connected = false;
|
||||
bool reconnectFlag = false;
|
||||
unsigned long prevArpGratuitous = 0;
|
||||
unsigned long prevReconnectingTime = 0;
|
||||
unsigned long connectedTime = 0;
|
||||
unsigned long disconnectedTime = 0;
|
||||
|
||||
const char* getTaskName() {
|
||||
return "Wifi";
|
||||
}
|
||||
|
||||
/*int getTaskCore() {
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
int getTaskPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
this->resetWifi();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (this->isConnected() && !this->hasStaCredentials()) {
|
||||
Log.sinfoln(FPSTR(L_NETWORK), F("Reset"));
|
||||
this->resetWifi();
|
||||
|
||||
} else if (this->isConnected() && !this->reconnectFlag) {
|
||||
if (!this->connected) {
|
||||
this->connectedTime = millis();
|
||||
this->connected = true;
|
||||
|
||||
Log.sinfoln(
|
||||
FPSTR(L_NETWORK),
|
||||
F("Connected, downtime: %lu s., IP: %s, RSSI: %hhd"),
|
||||
(millis() - this->disconnectedTime) / 1000,
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.RSSI()
|
||||
);
|
||||
}
|
||||
|
||||
if (this->isApEnabled() && millis() - this->connectedTime > this->reconnectInterval && !this->hasApClients()) {
|
||||
Log.sinfoln(FPSTR(L_NETWORK), F("Stop AP because connected, start only STA"));
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (millis() - this->prevArpGratuitous > 60000) {
|
||||
this->stationKeepAliveNow();
|
||||
this->prevArpGratuitous = millis();
|
||||
}
|
||||
#endif
|
||||
|
||||
} else {
|
||||
if (this->connected) {
|
||||
this->disconnectedTime = millis();
|
||||
this->connected = false;
|
||||
|
||||
Log.sinfoln(
|
||||
FPSTR(L_NETWORK),
|
||||
F("Disconnected, reason: %d, uptime: %lu s."),
|
||||
Connection::getDisconnectReason(),
|
||||
(millis() - this->connectedTime) / 1000
|
||||
);
|
||||
}
|
||||
|
||||
if (!this->hasStaCredentials() && !this->isApEnabled()) {
|
||||
Log.sinfoln(FPSTR(L_NETWORK), F("No STA credentials, start AP"));
|
||||
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.softAP(this->apName, this->apPassword, this->apChannel);
|
||||
|
||||
} else if (!this->isApEnabled() && millis() - this->disconnectedTime > this->failedConnectTimeout) {
|
||||
Log.sinfoln(FPSTR(L_NETWORK), F("Disconnected for a long time, start AP"));
|
||||
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.softAP(this->apName, this->apPassword, this->apChannel);
|
||||
|
||||
} else if (this->isConnecting() && millis() - this->prevReconnectingTime > this->resetConnectionTimeout) {
|
||||
Log.swarningln(FPSTR(L_NETWORK), F("Connection timeout, reset wifi..."));
|
||||
this->resetWifi();
|
||||
|
||||
} else if (!this->isConnecting() && (!this->prevReconnectingTime || millis() - this->prevReconnectingTime > this->reconnectInterval)) {
|
||||
if (this->hasStaCredentials()) {
|
||||
Log.sinfoln(FPSTR(L_NETWORK), F("Try connect..."));
|
||||
|
||||
this->prevReconnectingTime = millis();
|
||||
this->connect(true, this->connectionTimeout);
|
||||
this->reconnectFlag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool setWifiHostname(const char* hostname) {
|
||||
if (!this->isHostnameValid(hostname)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(WiFi.getHostname(), hostname) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return WiFi.setHostname(hostname);
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
/**
|
||||
* @brief
|
||||
* https://github.com/arendst/Tasmota/blob/e6515883f0ee5451931b6280ff847b117de5a231/tasmota/tasmota_support/support_wifi.ino#L1196
|
||||
*/
|
||||
static void stationKeepAliveNow(void) {
|
||||
for (netif* interface = netif_list; interface != nullptr; interface = interface->next) {
|
||||
if (
|
||||
(interface->flags & NETIF_FLAG_LINK_UP)
|
||||
&& (interface->flags & NETIF_FLAG_UP)
|
||||
&& interface->num == STATION_IF
|
||||
&& (!ip4_addr_isany_val(*netif_ip4_addr(interface)))
|
||||
) {
|
||||
etharp_gratuitous(interface);
|
||||
::optimistic_yield(1000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief check RFC compliance
|
||||
*
|
||||
* @param value
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
static bool isHostnameValid(const char* value) {
|
||||
size_t len = strlen(value);
|
||||
if (len > 24) {
|
||||
return false;
|
||||
} else if (value[len - 1] == '-') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!isalnum(value[i]) && value[i] != '-') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -1,13 +1,7 @@
|
||||
#include <new>
|
||||
#include <CustomOpenTherm.h>
|
||||
|
||||
CustomOpenTherm* ot;
|
||||
extern EEManager eeSettings;
|
||||
|
||||
const char S_OT[] PROGMEM = "OT";
|
||||
const char S_OT_DHW[] PROGMEM = "OT.DHW";
|
||||
const char S_OT_HEATING[] PROGMEM = "OT.HEATING";
|
||||
|
||||
extern FileData fsSettings;
|
||||
|
||||
class OpenThermTask : public Task {
|
||||
public:
|
||||
@@ -46,7 +40,7 @@ protected:
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Log.sinfoln(FPSTR(S_OT), F("Started. GPIO IN: %hhu, GPIO OUT: %hhu"), settings.opentherm.inPin, settings.opentherm.outPin);
|
||||
Log.sinfoln(FPSTR(L_OT), F("Started. GPIO IN: %hhu, GPIO OUT: %hhu"), settings.opentherm.inPin, settings.opentherm.outPin);
|
||||
|
||||
ot->setHandleSendRequestCallback(OpenThermTask::sendRequestCallback);
|
||||
ot->setYieldCallback([](void* self) {
|
||||
@@ -66,32 +60,32 @@ protected:
|
||||
|
||||
// Not all boilers support these, only try once when the boiler becomes connected
|
||||
if (updateSlaveVersion()) {
|
||||
Log.straceln(FPSTR(S_OT), F("Slave version: %u, type: %u"), vars.parameters.slaveVersion, vars.parameters.slaveType);
|
||||
Log.straceln(FPSTR(L_OT), F("Slave version: %u, type: %u"), vars.parameters.slaveVersion, vars.parameters.slaveType);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT), F("Get slave version failed"));
|
||||
Log.swarningln(FPSTR(L_OT), F("Get slave version failed"));
|
||||
}
|
||||
|
||||
// 0x013F
|
||||
if (setMasterVersion(0x3F, 0x01)) {
|
||||
Log.straceln(FPSTR(S_OT), F("Master version: %u, type: %u"), vars.parameters.masterVersion, vars.parameters.masterType);
|
||||
Log.straceln(FPSTR(L_OT), F("Master version: %u, type: %u"), vars.parameters.masterVersion, vars.parameters.masterType);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT), F("Set master version failed"));
|
||||
Log.swarningln(FPSTR(L_OT), F("Set master version failed"));
|
||||
}
|
||||
|
||||
if (updateSlaveConfig()) {
|
||||
Log.straceln(FPSTR(S_OT), F("Slave member id: %u, flags: %u"), vars.parameters.slaveMemberId, vars.parameters.slaveFlags);
|
||||
Log.straceln(FPSTR(L_OT), F("Slave member id: %u, flags: %u"), vars.parameters.slaveMemberId, vars.parameters.slaveFlags);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT), F("Get slave config failed"));
|
||||
Log.swarningln(FPSTR(L_OT), F("Get slave config failed"));
|
||||
}
|
||||
|
||||
if (setMasterConfig(settings.opentherm.memberIdCode & 0xFF, (settings.opentherm.memberIdCode & 0xFFFF) >> 8)) {
|
||||
Log.straceln(FPSTR(S_OT), F("Master member id: %u, flags: %u"), vars.parameters.masterMemberId, vars.parameters.masterFlags);
|
||||
Log.straceln(FPSTR(L_OT), F("Master member id: %u, flags: %u"), vars.parameters.masterMemberId, vars.parameters.masterFlags);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT), F("Set master config failed"));
|
||||
Log.swarningln(FPSTR(L_OT), F("Set master config failed"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,18 +113,18 @@ protected:
|
||||
);
|
||||
|
||||
if (!ot->isValidResponse(localResponse)) {
|
||||
Log.swarningln(FPSTR(S_OT), F("Invalid response after setBoilerStatus: %s"), ot->statusToString(ot->getLastResponseStatus()));
|
||||
Log.swarningln(FPSTR(L_OT), F("Invalid response after setBoilerStatus: %s"), ot->statusToString(ot->getLastResponseStatus()));
|
||||
}
|
||||
|
||||
if (vars.states.otStatus && !this->prevOtStatus) {
|
||||
this->prevOtStatus = vars.states.otStatus;
|
||||
|
||||
Log.sinfoln(FPSTR(S_OT), F("Connected. Initializing"));
|
||||
Log.sinfoln(FPSTR(L_OT), F("Connected. Initializing"));
|
||||
this->initBoiler();
|
||||
|
||||
} else if (!vars.states.otStatus && this->prevOtStatus) {
|
||||
this->prevOtStatus = vars.states.otStatus;
|
||||
Log.swarningln(FPSTR(S_OT), F("Disconnected"));
|
||||
Log.swarningln(FPSTR(L_OT), F("Disconnected"));
|
||||
}
|
||||
|
||||
if (!vars.states.otStatus) {
|
||||
@@ -141,7 +135,7 @@ protected:
|
||||
if (vars.parameters.heatingEnabled != heatingEnabled) {
|
||||
this->prevUpdateNonEssentialVars = 0;
|
||||
vars.parameters.heatingEnabled = heatingEnabled;
|
||||
Log.sinfoln(FPSTR(S_OT_HEATING), "%s", heatingEnabled ? F("Enabled") : F("Disabled"));
|
||||
Log.sinfoln(FPSTR(L_OT_HEATING), "%s", heatingEnabled ? F("Enabled") : F("Disabled"));
|
||||
}
|
||||
|
||||
vars.states.heating = ot->isCentralHeatingActive(localResponse);
|
||||
@@ -154,18 +148,18 @@ protected:
|
||||
if (millis() - this->prevUpdateNonEssentialVars > 60000) {
|
||||
if (!heatingEnabled && settings.opentherm.modulationSyncWithHeating) {
|
||||
if (setMaxModulationLevel(0)) {
|
||||
Log.snoticeln(FPSTR(S_OT_HEATING), F("Set max modulation 0% (off)"));
|
||||
Log.snoticeln(FPSTR(L_OT_HEATING), F("Set max modulation 0% (off)"));
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT_HEATING), F("Failed set max modulation 0% (off)"));
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set max modulation 0% (off)"));
|
||||
}
|
||||
|
||||
} else {
|
||||
if (setMaxModulationLevel(settings.heating.maxModulation)) {
|
||||
Log.snoticeln(FPSTR(S_OT_HEATING), F("Set max modulation %hhu%%"), settings.heating.maxModulation);
|
||||
Log.snoticeln(FPSTR(L_OT_HEATING), F("Set max modulation %hhu%%"), settings.heating.maxModulation);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT_HEATING), F("Failed set max modulation %hhu%%"), settings.heating.maxModulation);
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set max modulation %hhu%%"), settings.heating.maxModulation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,24 +168,24 @@ protected:
|
||||
if (updateMinMaxDhwTemp()) {
|
||||
if (settings.dhw.minTemp < vars.parameters.dhwMinTemp) {
|
||||
settings.dhw.minTemp = vars.parameters.dhwMinTemp;
|
||||
eeSettings.update();
|
||||
Log.snoticeln(FPSTR(S_OT_DHW), F("Updated min temp: %hhu"), settings.dhw.minTemp);
|
||||
fsSettings.update();
|
||||
Log.snoticeln(FPSTR(L_OT_DHW), F("Updated min temp: %hhu"), settings.dhw.minTemp);
|
||||
}
|
||||
|
||||
if (settings.dhw.maxTemp > vars.parameters.dhwMaxTemp) {
|
||||
settings.dhw.maxTemp = vars.parameters.dhwMaxTemp;
|
||||
eeSettings.update();
|
||||
Log.snoticeln(FPSTR(S_OT_DHW), F("Updated max temp: %hhu"), settings.dhw.maxTemp);
|
||||
fsSettings.update();
|
||||
Log.snoticeln(FPSTR(L_OT_DHW), F("Updated max temp: %hhu"), settings.dhw.maxTemp);
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT_DHW), F("Failed get min/max temp"));
|
||||
Log.swarningln(FPSTR(L_OT_DHW), F("Failed get min/max temp"));
|
||||
}
|
||||
|
||||
if (settings.dhw.minTemp >= settings.dhw.maxTemp) {
|
||||
settings.dhw.minTemp = 30;
|
||||
settings.dhw.maxTemp = 60;
|
||||
eeSettings.update();
|
||||
fsSettings.update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,24 +194,24 @@ protected:
|
||||
if (updateMinMaxHeatingTemp()) {
|
||||
if (settings.heating.minTemp < vars.parameters.heatingMinTemp) {
|
||||
settings.heating.minTemp = vars.parameters.heatingMinTemp;
|
||||
eeSettings.update();
|
||||
Log.snoticeln(FPSTR(S_OT_HEATING), F("Updated min temp: %hhu"), settings.heating.minTemp);
|
||||
fsSettings.update();
|
||||
Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated min temp: %hhu"), settings.heating.minTemp);
|
||||
}
|
||||
|
||||
if (settings.heating.maxTemp > vars.parameters.heatingMaxTemp) {
|
||||
settings.heating.maxTemp = vars.parameters.heatingMaxTemp;
|
||||
eeSettings.update();
|
||||
Log.snoticeln(FPSTR(S_OT_HEATING), F("Updated max temp: %hhu"), settings.heating.maxTemp);
|
||||
fsSettings.update();
|
||||
Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated max temp: %hhu"), settings.heating.maxTemp);
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT_HEATING), F("Failed get min/max temp"));
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed get min/max temp"));
|
||||
}
|
||||
|
||||
if (settings.heating.minTemp >= settings.heating.maxTemp) {
|
||||
settings.heating.minTemp = 20;
|
||||
settings.heating.maxTemp = 90;
|
||||
eeSettings.update();
|
||||
fsSettings.update();
|
||||
}
|
||||
|
||||
// force set max CH temp
|
||||
@@ -258,10 +252,10 @@ protected:
|
||||
if (vars.actions.resetFault) {
|
||||
if (vars.states.fault) {
|
||||
if (ot->sendBoilerReset()) {
|
||||
Log.sinfoln(FPSTR(S_OT), F("Boiler fault reset successfully"));
|
||||
Log.sinfoln(FPSTR(L_OT), F("Boiler fault reset successfully"));
|
||||
|
||||
} else {
|
||||
Log.serrorln(FPSTR(S_OT), F("Boiler fault reset failed"));
|
||||
Log.serrorln(FPSTR(L_OT), F("Boiler fault reset failed"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,10 +266,10 @@ protected:
|
||||
if (vars.actions.resetDiagnostic) {
|
||||
if (vars.states.diagnostic) {
|
||||
if (ot->sendServiceReset()) {
|
||||
Log.sinfoln(FPSTR(S_OT), F("Boiler diagnostic reset successfully"));
|
||||
Log.sinfoln(FPSTR(L_OT), F("Boiler diagnostic reset successfully"));
|
||||
|
||||
} else {
|
||||
Log.serrorln(FPSTR(S_OT), F("Boiler diagnostic reset failed"));
|
||||
Log.serrorln(FPSTR(L_OT), F("Boiler diagnostic reset failed"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +284,7 @@ protected:
|
||||
newDhwTemp = constrain(newDhwTemp, settings.dhw.minTemp, settings.dhw.maxTemp);
|
||||
}
|
||||
|
||||
Log.sinfoln(FPSTR(S_OT_DHW), F("Set temp = %u"), newDhwTemp);
|
||||
Log.sinfoln(FPSTR(L_OT_DHW), F("Set temp = %u"), newDhwTemp);
|
||||
|
||||
// Записываем заданную температуру ГВС
|
||||
if (ot->setDhwTemp(newDhwTemp)) {
|
||||
@@ -298,12 +292,12 @@ protected:
|
||||
this->dhwSetTempTime = millis();
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT_DHW), F("Failed set temp"));
|
||||
Log.swarningln(FPSTR(L_OT_DHW), F("Failed set temp"));
|
||||
}
|
||||
|
||||
if (settings.opentherm.dhwToCh2) {
|
||||
if (!ot->setHeatingCh2Temp(newDhwTemp)) {
|
||||
Log.swarningln(FPSTR(S_OT_DHW), F("Failed set ch2 temp"));
|
||||
Log.swarningln(FPSTR(L_OT_DHW), F("Failed set ch2 temp"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,7 +305,7 @@ protected:
|
||||
//
|
||||
// Температура отопления
|
||||
if (heatingEnabled && (needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001)) {
|
||||
Log.sinfoln(FPSTR(S_OT_HEATING), F("Set temp = %u"), vars.parameters.heatingSetpoint);
|
||||
Log.sinfoln(FPSTR(L_OT_HEATING), F("Set temp = %u"), vars.parameters.heatingSetpoint);
|
||||
|
||||
// Записываем заданную температуру
|
||||
if (ot->setHeatingCh1Temp(vars.parameters.heatingSetpoint)) {
|
||||
@@ -319,12 +313,12 @@ protected:
|
||||
this->heatingSetTempTime = millis();
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(S_OT_HEATING), F("Failed set temp"));
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set temp"));
|
||||
}
|
||||
|
||||
if (settings.opentherm.heatingCh1ToCh2) {
|
||||
if (!ot->setHeatingCh2Temp(vars.parameters.heatingSetpoint)) {
|
||||
Log.swarningln(FPSTR(S_OT_HEATING), F("Failed set ch2 temp"));
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set ch2 temp"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,7 +388,7 @@ protected:
|
||||
}
|
||||
|
||||
static void printRequestDetail(OpenThermMessageID id, OpenThermResponseStatus status, unsigned long request, unsigned long response, byte attempt) {
|
||||
Log.straceln(FPSTR(S_OT), F("OT REQUEST ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s"), id, request, response, attempt, ot->statusToString(status));
|
||||
Log.straceln(FPSTR(L_OT), F("OT REQUEST ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s"), id, request, response, attempt, ot->statusToString(status));
|
||||
}
|
||||
|
||||
bool updateSlaveConfig() {
|
||||
|
||||
625
src/PortalTask.h
Normal file
625
src/PortalTask.h
Normal file
@@ -0,0 +1,625 @@
|
||||
#define PORTAL_CACHE_TIME "" //"max-age=86400"
|
||||
#define PORTAL_CACHE settings.debug ? nullptr : PORTAL_CACHE_TIME
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <Updater.h>
|
||||
using WebServer = ESP8266WebServer;
|
||||
#else
|
||||
#include <WebServer.h>
|
||||
#include <Update.h>
|
||||
#endif
|
||||
#include <BufferedWebServer.h>
|
||||
#include <StaticPage.h>
|
||||
#include <DynamicPage.h>
|
||||
#include <UpgradeHandler.h>
|
||||
#include <DNSServer.h>
|
||||
|
||||
extern NetworkTask* tNetwork;
|
||||
extern FileData fsSettings, fsNetworkSettings;
|
||||
extern MqttTask* tMqtt;
|
||||
|
||||
|
||||
class PortalTask : public LeanTask {
|
||||
public:
|
||||
PortalTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {
|
||||
this->webServer = new WebServer(80);
|
||||
this->bufferedWebServer = new BufferedWebServer(this->webServer, 32u);
|
||||
this->dnsServer = new DNSServer();
|
||||
}
|
||||
|
||||
~PortalTask() {
|
||||
if (this->bufferedWebServer != nullptr) {
|
||||
delete this->bufferedWebServer;
|
||||
}
|
||||
|
||||
if (this->webServer != nullptr) {
|
||||
this->stopWebServer();
|
||||
delete this->webServer;
|
||||
}
|
||||
|
||||
if (this->dnsServer != nullptr) {
|
||||
this->stopDnsServer();
|
||||
delete this->dnsServer;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
const unsigned int changeStateInterval = 1000;
|
||||
|
||||
WebServer* webServer = nullptr;
|
||||
BufferedWebServer* bufferedWebServer = nullptr;
|
||||
DNSServer* dnsServer = nullptr;
|
||||
|
||||
bool webServerEnabled = false;
|
||||
bool dnsServerEnabled = false;
|
||||
unsigned long webServerChangeState = 0;
|
||||
unsigned long dnsServerChangeState = 0;
|
||||
|
||||
const char* getTaskName() {
|
||||
return "Portal";
|
||||
}
|
||||
|
||||
/*int getTaskCore() {
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
int getTaskPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
this->dnsServer->setTTL(0);
|
||||
this->dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
this->webServer->enableETag(true);
|
||||
//this->webServer->getServer().setNoDelay(true);
|
||||
#endif
|
||||
|
||||
// index page
|
||||
/*auto indexPage = (new DynamicPage("/", &LittleFS, "/index.html"))
|
||||
->setTemplateFunction([](const char* var) -> String {
|
||||
String result;
|
||||
|
||||
if (strcmp(var, "ver") == 0) {
|
||||
result = PROJECT_VERSION;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
this->webServer->addHandler(indexPage);*/
|
||||
this->webServer->addHandler(new StaticPage("/", &LittleFS, "/index.html", PORTAL_CACHE));
|
||||
|
||||
// restart
|
||||
this->webServer->on("/restart.html", HTTP_GET, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
this->webServer->send(401);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vars.actions.restart = true;
|
||||
this->webServer->sendHeader("Location", "/");
|
||||
this->webServer->send(302);
|
||||
});
|
||||
|
||||
// network settings page
|
||||
auto networkPage = (new StaticPage("/network.html", &LittleFS, "/network.html", PORTAL_CACHE))
|
||||
->setBeforeSendFunction([this]() {
|
||||
if (this->isNeedAuth() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
this->webServer->addHandler(networkPage);
|
||||
|
||||
// settings page
|
||||
auto settingsPage = (new StaticPage("/settings.html", &LittleFS, "/settings.html", PORTAL_CACHE))
|
||||
->setBeforeSendFunction([this]() {
|
||||
if (this->isNeedAuth() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
this->webServer->addHandler(settingsPage);
|
||||
|
||||
// upgrade page
|
||||
auto upgradePage = (new StaticPage("/upgrade.html", &LittleFS, "/upgrade.html", PORTAL_CACHE))
|
||||
->setBeforeSendFunction([this]() {
|
||||
if (this->isNeedAuth() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
this->webServer->addHandler(upgradePage);
|
||||
|
||||
// OTA
|
||||
auto upgradeHandler = (new UpgradeHandler("/api/upgrade"))->setCanUploadFunction([this](const String& uri) {
|
||||
if (this->isNeedAuth() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
this->webServer->sendHeader("Connection", "close");
|
||||
this->webServer->send(401);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})->setBeforeUpgradeFunction([](UpgradeHandler::UpgradeType type) -> bool {
|
||||
return true;
|
||||
})->setAfterUpgradeFunction([this](const UpgradeHandler::UpgradeResult& fwResult, const UpgradeHandler::UpgradeResult& fsResult) {
|
||||
unsigned short status = 200;
|
||||
if (fwResult.status == UpgradeHandler::UpgradeStatus::SUCCESS || fsResult.status == UpgradeHandler::UpgradeStatus::SUCCESS) {
|
||||
vars.actions.restart = true;
|
||||
|
||||
} else {
|
||||
status = 400;
|
||||
}
|
||||
|
||||
String response = "{\"firmware\": {\"status\": ";
|
||||
response.concat((short int) fwResult.status);
|
||||
response.concat(", \"error\": \"");
|
||||
response.concat(fwResult.error);
|
||||
response.concat("\"}, \"filesystem\": {\"status\": ");
|
||||
response.concat((short int) fsResult.status);
|
||||
response.concat(", \"error\": \"");
|
||||
response.concat(fsResult.error);
|
||||
response.concat("\"}}");
|
||||
this->webServer->send(status, "application/json", response);
|
||||
});
|
||||
this->webServer->addHandler(upgradeHandler);
|
||||
|
||||
|
||||
// backup
|
||||
this->webServer->on("/api/backup/save", HTTP_GET, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
return this->webServer->send(401);
|
||||
}
|
||||
}
|
||||
|
||||
JsonDocument networkSettingsDoc;
|
||||
networkSettingsToJson(networkSettings, networkSettingsDoc);
|
||||
|
||||
JsonDocument settingsDoc;
|
||||
settingsToJson(settings, settingsDoc);
|
||||
JsonDocument doc;
|
||||
|
||||
doc["network"] = networkSettingsDoc;
|
||||
doc["settings"] = settingsDoc;
|
||||
doc.shrinkToFit();
|
||||
|
||||
this->webServer->sendHeader(F("Content-Disposition"), F("attachment; filename=\"backup.json\""));
|
||||
this->bufferedWebServer->send(200, "application/json", doc);
|
||||
});
|
||||
|
||||
this->webServer->on("/api/backup/restore", HTTP_POST, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
return this->webServer->send(401);
|
||||
}
|
||||
}
|
||||
|
||||
String plain = this->webServer->arg(0);
|
||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/backup/restore %d bytes: %s"), plain.length(), plain.c_str());
|
||||
|
||||
if (plain.length() < 2) {
|
||||
this->webServer->send(406);
|
||||
return;
|
||||
|
||||
} else if (plain.length() > 2048) {
|
||||
this->webServer->send(413);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError dErr = deserializeJson(doc, plain);
|
||||
plain.clear();
|
||||
|
||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||
this->webServer->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
if (doc["settings"] && jsonToSettings(doc["settings"], settings)) {
|
||||
fsSettings.update();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (doc["network"] && jsonToNetworkSettings(doc["network"], networkSettings)) {
|
||||
fsNetworkSettings.update();
|
||||
tNetwork->setStaCredentials(networkSettings.sta.ssid, networkSettings.sta.password, networkSettings.sta.channel);
|
||||
tNetwork->setUseDhcp(networkSettings.useDhcp);
|
||||
tNetwork->setStaticConfig(
|
||||
networkSettings.staticConfig.ip,
|
||||
networkSettings.staticConfig.gateway,
|
||||
networkSettings.staticConfig.subnet,
|
||||
networkSettings.staticConfig.dns
|
||||
);
|
||||
tNetwork->reconnect();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
this->webServer->send(changed ? 201 : 200);
|
||||
});
|
||||
|
||||
// network
|
||||
this->webServer->on("/api/network/settings", HTTP_GET, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
return this->webServer->send(401);
|
||||
}
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
networkSettingsToJson(networkSettings, doc);
|
||||
this->bufferedWebServer->send(200, "application/json", doc);
|
||||
});
|
||||
|
||||
this->webServer->on("/api/network/settings", HTTP_POST, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
return this->webServer->send(401);
|
||||
}
|
||||
}
|
||||
|
||||
String plain = this->webServer->arg(0);
|
||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/network/settings %d bytes: %s"), plain.length(), plain.c_str());
|
||||
|
||||
if (plain.length() < 2) {
|
||||
this->webServer->send(406);
|
||||
return;
|
||||
|
||||
} else if (plain.length() > 512) {
|
||||
this->webServer->send(413);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError dErr = deserializeJson(doc, plain);
|
||||
plain.clear();
|
||||
|
||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||
this->webServer->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonToNetworkSettings(doc, networkSettings)) {
|
||||
this->webServer->send(201);
|
||||
|
||||
fsNetworkSettings.update();
|
||||
tNetwork->setStaCredentials(networkSettings.sta.ssid, networkSettings.sta.password, networkSettings.sta.channel);
|
||||
tNetwork->setUseDhcp(networkSettings.useDhcp);
|
||||
tNetwork->setStaticConfig(
|
||||
networkSettings.staticConfig.ip,
|
||||
networkSettings.staticConfig.gateway,
|
||||
networkSettings.staticConfig.subnet,
|
||||
networkSettings.staticConfig.dns
|
||||
);
|
||||
tNetwork->reconnect();
|
||||
|
||||
} else {
|
||||
this->webServer->send(200);
|
||||
}
|
||||
});
|
||||
|
||||
this->webServer->on("/api/network/status", HTTP_GET, [this]() {
|
||||
bool isConnected = tNetwork->isConnected();
|
||||
|
||||
JsonDocument doc;
|
||||
doc["hostname"] = networkSettings.hostname;
|
||||
doc["mac"] = tNetwork->getStaMac();
|
||||
doc["isConnected"] = isConnected;
|
||||
doc["ssid"] = tNetwork->getStaSsid();
|
||||
doc["signalQuality"] = isConnected ? NetworkTask::rssiToSignalQuality(tNetwork->getRssi()) : 0;
|
||||
doc["channel"] = isConnected ? tNetwork->getStaChannel() : 0;
|
||||
doc["ip"] = isConnected ? tNetwork->getStaIp().toString() : "";
|
||||
doc["subnet"] = isConnected ? tNetwork->getStaSubnet().toString() : "";
|
||||
doc["gateway"] = isConnected ? tNetwork->getStaGateway().toString() : "";
|
||||
doc["dns"] = isConnected ? tNetwork->getStaDns().toString() : "";
|
||||
doc.shrinkToFit();
|
||||
|
||||
this->bufferedWebServer->send(200, "application/json", doc);
|
||||
});
|
||||
|
||||
this->webServer->on("/api/network/scan", HTTP_GET, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
this->webServer->send(401);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto apCount = WiFi.scanComplete();
|
||||
if (apCount <= 0) {
|
||||
WiFi.scanNetworks(true, true);
|
||||
|
||||
if (apCount == WIFI_SCAN_RUNNING || apCount == WIFI_SCAN_FAILED) {
|
||||
this->webServer->send(202);
|
||||
|
||||
} else if (apCount == 0) {
|
||||
this->webServer->send(200, "application/json", "[]");
|
||||
|
||||
} else {
|
||||
this->webServer->send(500);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
for (short int i = 0; i < apCount; i++) {
|
||||
String ssid = WiFi.SSID(i);
|
||||
doc[i]["ssid"] = ssid;
|
||||
doc[i]["signalQuality"] = NetworkTask::rssiToSignalQuality(WiFi.RSSI(i));
|
||||
doc[i]["channel"] = WiFi.channel(i);
|
||||
doc[i]["hidden"] = !ssid.length();
|
||||
doc[i]["encryptionType"] = WiFi.encryptionType(i);
|
||||
}
|
||||
doc.shrinkToFit();
|
||||
|
||||
this->bufferedWebServer->send(200, "application/json", doc);
|
||||
|
||||
WiFi.scanNetworks(true, true);
|
||||
});
|
||||
|
||||
|
||||
// settings
|
||||
this->webServer->on("/api/settings", HTTP_GET, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
return this->webServer->send(401);
|
||||
}
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
settingsToJson(settings, doc);
|
||||
this->bufferedWebServer->send(200, "application/json", doc);
|
||||
});
|
||||
|
||||
this->webServer->on("/api/settings", HTTP_POST, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
return this->webServer->send(401);
|
||||
}
|
||||
}
|
||||
|
||||
String plain = this->webServer->arg(0);
|
||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/settings %d bytes: %s"), plain.length(), plain.c_str());
|
||||
|
||||
if (plain.length() < 2) {
|
||||
this->webServer->send(406);
|
||||
return;
|
||||
|
||||
} else if (plain.length() > 2048) {
|
||||
this->webServer->send(413);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError dErr = deserializeJson(doc, plain);
|
||||
plain.clear();
|
||||
|
||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||
this->webServer->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonToSettings(doc, settings)) {
|
||||
fsSettings.update();
|
||||
this->webServer->send(201);
|
||||
|
||||
} else {
|
||||
this->webServer->send(200);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// vars
|
||||
this->webServer->on("/api/vars", HTTP_GET, [this]() {
|
||||
JsonDocument doc;
|
||||
varsToJson(vars, doc);
|
||||
|
||||
doc["system"]["version"] = PROJECT_VERSION;
|
||||
doc["system"]["buildDate"] = __DATE__ " " __TIME__;
|
||||
doc["system"]["uptime"] = millis() / 1000ul;
|
||||
doc["system"]["freeHeap"] = getFreeHeap();
|
||||
doc["system"]["totalHeap"] = getTotalHeap();
|
||||
doc["system"]["maxFreeBlockHeap"] = getMaxFreeBlockHeap();
|
||||
doc["system"]["resetReason"] = getResetReason();
|
||||
doc["system"]["mqttConnected"] = tMqtt->isConnected();
|
||||
doc.shrinkToFit();
|
||||
|
||||
this->bufferedWebServer->send(200, "application/json", doc);
|
||||
});
|
||||
|
||||
this->webServer->on("/api/vars", HTTP_POST, [this]() {
|
||||
if (this->isNeedAuth()) {
|
||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||
return this->webServer->send(401);
|
||||
}
|
||||
}
|
||||
|
||||
String plain = this->webServer->arg(0);
|
||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/vars %d bytes: %s"), plain.length(), plain.c_str());
|
||||
|
||||
if (plain.length() < 2) {
|
||||
this->webServer->send(406);
|
||||
return;
|
||||
|
||||
} else if (plain.length() > 1024) {
|
||||
this->webServer->send(413);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError dErr = deserializeJson(doc, plain);
|
||||
plain.clear();
|
||||
|
||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||
this->webServer->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonToVars(doc, vars)) {
|
||||
this->webServer->send(201);
|
||||
|
||||
} else {
|
||||
this->webServer->send(200);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// not found
|
||||
this->webServer->onNotFound([this]() {
|
||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Page not found, uri: %s"), this->webServer->uri().c_str());
|
||||
|
||||
if (tNetwork->isApEnabled()) {
|
||||
this->onCaptivePortal();
|
||||
|
||||
} else {
|
||||
this->webServer->send(404, "text/plain", F("Page not found"));
|
||||
}
|
||||
});
|
||||
|
||||
this->webServer->serveStatic("/favicon.ico", LittleFS, "/static/favicon.ico", PORTAL_CACHE);
|
||||
this->webServer->serveStatic("/static", LittleFS, "/static", PORTAL_CACHE);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// web server
|
||||
if (!this->stateWebServer()) {
|
||||
this->startWebServer();
|
||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Started"));
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::esp_yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
// dns server
|
||||
if (!this->stateDnsServer() && this->stateWebServer() && tNetwork->isApEnabled() && tNetwork->hasApClients() && millis() - this->dnsServerChangeState >= this->changeStateInterval) {
|
||||
this->startDnsServer();
|
||||
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Started: AP up"));
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::esp_yield();
|
||||
#endif
|
||||
|
||||
} else if (this->stateDnsServer() && (!tNetwork->isApEnabled() || !this->stateWebServer()) && millis() - this->dnsServerChangeState >= this->changeStateInterval) {
|
||||
this->stopDnsServer();
|
||||
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Stopped: AP down"));
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::esp_yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (this->stateDnsServer()) {
|
||||
this->dnsServer->processNextRequest();
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::esp_yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (this->stateWebServer()) {
|
||||
this->webServer->handleClient();
|
||||
}
|
||||
}
|
||||
|
||||
bool isNeedAuth() {
|
||||
return !tNetwork->isApEnabled() && settings.portal.useAuth && strlen(settings.portal.password);
|
||||
}
|
||||
|
||||
void onCaptivePortal() {
|
||||
const String uri = this->webServer->uri();
|
||||
|
||||
if (uri.equals("/connecttest.txt")) {
|
||||
this->webServer->sendHeader(F("Location"), F("http://logout.net"));
|
||||
this->webServer->send(302);
|
||||
|
||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to http://logout.net with 302 code"));
|
||||
|
||||
} else if (uri.equals("/wpad.dat")) {
|
||||
this->webServer->send(404);
|
||||
|
||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Send empty page with 404 code"));
|
||||
|
||||
} else if (uri.equals("/success.txt")) {
|
||||
this->webServer->send(200);
|
||||
|
||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Send empty page with 200 code"));
|
||||
|
||||
} else {
|
||||
String portalUrl = "http://" + tNetwork->getApIp().toString() + '/';
|
||||
|
||||
this->webServer->sendHeader("Location", portalUrl.c_str());
|
||||
this->webServer->send(302);
|
||||
|
||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to portal page with 302 code"));
|
||||
}
|
||||
}
|
||||
|
||||
bool stateWebServer() {
|
||||
return this->webServerEnabled;
|
||||
}
|
||||
|
||||
void startWebServer() {
|
||||
if (this->stateWebServer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->webServer->begin();
|
||||
this->webServerEnabled = true;
|
||||
this->webServerChangeState = millis();
|
||||
|
||||
::yield();
|
||||
}
|
||||
|
||||
void stopWebServer() {
|
||||
if (!this->stateWebServer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->webServer->handleClient();
|
||||
this->webServer->stop();
|
||||
this->webServerEnabled = false;
|
||||
this->webServerChangeState = millis();
|
||||
|
||||
::yield();
|
||||
}
|
||||
|
||||
bool stateDnsServer() {
|
||||
return this->dnsServerEnabled;
|
||||
}
|
||||
|
||||
void startDnsServer() {
|
||||
if (this->stateDnsServer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->dnsServer->start(53, "*", tNetwork->getApIp());
|
||||
this->dnsServerEnabled = true;
|
||||
this->dnsServerChangeState = millis();
|
||||
|
||||
::yield();
|
||||
}
|
||||
|
||||
void stopDnsServer() {
|
||||
if (!this->stateDnsServer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->dnsServer->processNextRequest();
|
||||
this->dnsServer->stop();
|
||||
this->dnsServerEnabled = false;
|
||||
this->dnsServerChangeState = millis();
|
||||
|
||||
::yield();
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,35 @@
|
||||
struct NetworkSettings {
|
||||
char hostname[25] = HOSTNAME_DEFAULT;
|
||||
bool useDhcp = true;
|
||||
|
||||
struct {
|
||||
char ip[16] = "192.168.0.100";
|
||||
char gateway[16] = "192.168.0.1";
|
||||
char subnet[16] = "255.255.255.0";
|
||||
char dns[16] = "192.168.0.1";
|
||||
} staticConfig;
|
||||
|
||||
struct {
|
||||
char ssid[33] = AP_SSID_DEFAULT;
|
||||
char password[65] = AP_PASSWORD_DEFAULT;
|
||||
byte channel = 1;
|
||||
} ap;
|
||||
|
||||
struct {
|
||||
char ssid[33] = STA_SSID_DEFAULT;
|
||||
char password[65] = STA_PASSWORD_DEFAULT;
|
||||
byte channel = 0;
|
||||
} sta;
|
||||
} networkSettings;
|
||||
|
||||
struct Settings {
|
||||
bool debug = DEBUG_BY_DEFAULT;
|
||||
char hostname[80] = "opentherm";
|
||||
|
||||
struct {
|
||||
bool useAuth = false;
|
||||
char login[13] = PORTAL_LOGIN_DEFAULT;
|
||||
char password[33] = PORTAL_PASSWORD_DEFAULT;
|
||||
} portal;
|
||||
|
||||
struct {
|
||||
byte inPin = OT_IN_PIN_DEFAULT;
|
||||
@@ -16,11 +45,11 @@ struct Settings {
|
||||
} opentherm;
|
||||
|
||||
struct {
|
||||
char server[80];
|
||||
unsigned short port = 1883;
|
||||
char user[32];
|
||||
char password[32];
|
||||
char prefix[80] = "opentherm";
|
||||
char server[81] = MQTT_SERVER_DEFAULT;
|
||||
unsigned short port = MQTT_PORT_DEFAULT;
|
||||
char user[33] = MQTT_USER_DEFAULT;
|
||||
char password[33] = MQTT_PASSWORD_DEFAULT;
|
||||
char prefix[33] = MQTT_PREFIX_DEFAULT;
|
||||
unsigned short interval = 5;
|
||||
} mqtt;
|
||||
|
||||
@@ -107,6 +136,7 @@ struct Variables {
|
||||
bool flame = false;
|
||||
bool fault = false;
|
||||
bool diagnostic = false;
|
||||
bool externalPump = false;
|
||||
} states;
|
||||
|
||||
struct {
|
||||
@@ -124,16 +154,12 @@ struct Variables {
|
||||
float dhw = 0.0f;
|
||||
} temperatures;
|
||||
|
||||
struct {
|
||||
bool enable = false;
|
||||
unsigned long lastEnableTime = 0;
|
||||
} externalPump;
|
||||
|
||||
struct {
|
||||
bool heatingEnabled = false;
|
||||
byte heatingMinTemp = DEFAULT_HEATING_MIN_TEMP;
|
||||
byte heatingMaxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||
byte heatingSetpoint = 0;
|
||||
unsigned long extPumpLastEnableTime = 0;
|
||||
byte dhwMinTemp = DEFAULT_DHW_MIN_TEMP;
|
||||
byte dhwMaxTemp = DEFAULT_DHW_MAX_TEMP;
|
||||
byte maxModulation;
|
||||
|
||||
@@ -1,559 +0,0 @@
|
||||
#define WM_MDNS
|
||||
#include <WiFiManager.h>
|
||||
#include <UnsignedIntParameter.h>
|
||||
#include <UnsignedShortParameter.h>
|
||||
#include <CheckboxParameter.h>
|
||||
#include <HeaderParameter.h>
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
extern "C" {
|
||||
#include "lwip/etharp.h"
|
||||
}
|
||||
#endif
|
||||
|
||||
WiFiManager wm;
|
||||
WiFiManagerParameter* wmHostname;
|
||||
WiFiManagerParameter* wmMqttServer;
|
||||
UnsignedShortParameter* wmMqttPort;
|
||||
WiFiManagerParameter* wmMqttUser;
|
||||
WiFiManagerParameter* wmMqttPassword;
|
||||
WiFiManagerParameter* wmMqttPrefix;
|
||||
UnsignedIntParameter* wmMqttPublishInterval;
|
||||
|
||||
UnsignedIntParameter* wmOtInPin;
|
||||
UnsignedIntParameter* wmOtOutPin;
|
||||
UnsignedIntParameter* wmOtMemberIdCode;
|
||||
CheckboxParameter* wmOtDhwPresent;
|
||||
CheckboxParameter* wmOtSummerWinterMode;
|
||||
CheckboxParameter* wmOtHeatingCh2Enabled;
|
||||
CheckboxParameter* wmOtHeatingCh1ToCh2;
|
||||
CheckboxParameter* wmOtDhwToCh2;
|
||||
CheckboxParameter* wmOtDhwBlocking;
|
||||
CheckboxParameter* wmOtModSyncWithHeating;
|
||||
|
||||
UnsignedIntParameter* wmOutdoorSensorPin;
|
||||
UnsignedIntParameter* wmIndoorSensorPin;
|
||||
#if USE_BLE
|
||||
WiFiManagerParameter* wmOutdoorSensorBleAddress;
|
||||
#endif
|
||||
|
||||
CheckboxParameter* wmExtPumpUse;
|
||||
UnsignedIntParameter* wmExtPumpPin;
|
||||
UnsignedShortParameter* wmExtPumpPostCirculationTime;
|
||||
UnsignedIntParameter* wmExtPumpAntiStuckInterval;
|
||||
UnsignedShortParameter* wmExtPumpAntiStuckTime;
|
||||
|
||||
HeaderParameter* wmMqttHeader;
|
||||
HeaderParameter* wmOtHeader;
|
||||
HeaderParameter* wmOtFlagsHeader;
|
||||
HeaderParameter* wmSensorsHeader;
|
||||
HeaderParameter* wmExtPumpHeader;
|
||||
|
||||
|
||||
extern EEManager eeSettings;
|
||||
#if USE_TELNET
|
||||
extern ESPTelnetStream TelnetStream;
|
||||
#endif
|
||||
|
||||
const char S_WIFI[] PROGMEM = "WIFI";
|
||||
const char S_WIFI_SETTINGS[] PROGMEM = "WIFI.SETTINGS";
|
||||
|
||||
|
||||
class WifiManagerTask : public LeanTask {
|
||||
public:
|
||||
WifiManagerTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {
|
||||
wmHostname = new WiFiManagerParameter("hostname", "Hostname", settings.hostname, 80);
|
||||
wm.addParameter(wmHostname);
|
||||
|
||||
wmMqttHeader = new HeaderParameter("MQTT");
|
||||
wm.addParameter(wmMqttHeader);
|
||||
|
||||
wmMqttServer = new WiFiManagerParameter("mqtt_server", "Server", settings.mqtt.server, 80);
|
||||
wm.addParameter(wmMqttServer);
|
||||
|
||||
wmMqttPort = new UnsignedShortParameter("mqtt_port", "Port", settings.mqtt.port, 6);
|
||||
wm.addParameter(wmMqttPort);
|
||||
|
||||
wmMqttUser = new WiFiManagerParameter("mqtt_user", "Username", settings.mqtt.user, 32);
|
||||
wm.addParameter(wmMqttUser);
|
||||
|
||||
wmMqttPassword = new WiFiManagerParameter("mqtt_password", "Password", settings.mqtt.password, 32, "type=\"password\"");
|
||||
wm.addParameter(wmMqttPassword);
|
||||
|
||||
wmMqttPrefix = new WiFiManagerParameter("mqtt_prefix", "Prefix", settings.mqtt.prefix, 32);
|
||||
wm.addParameter(wmMqttPrefix);
|
||||
|
||||
wmMqttPublishInterval = new UnsignedIntParameter("mqtt_publish_interval", "Publish interval (sec)", settings.mqtt.interval, 3);
|
||||
wm.addParameter(wmMqttPublishInterval);
|
||||
|
||||
wmOtHeader = new HeaderParameter("OpenTherm");
|
||||
wm.addParameter(wmOtHeader);
|
||||
|
||||
wmOtInPin = new UnsignedIntParameter("ot_in_pin", "GPIO IN", settings.opentherm.inPin, 2);
|
||||
wm.addParameter(wmOtInPin);
|
||||
|
||||
wmOtOutPin = new UnsignedIntParameter("ot_out_pin", "GPIO OUT", settings.opentherm.outPin, 2);
|
||||
wm.addParameter(wmOtOutPin);
|
||||
|
||||
wmOtMemberIdCode = new UnsignedIntParameter("ot_member_id_code", "Master Member ID", settings.opentherm.memberIdCode, 5);
|
||||
wm.addParameter(wmOtMemberIdCode);
|
||||
|
||||
wmOtFlagsHeader = new HeaderParameter("OpenTherm flags");
|
||||
wm.addParameter(wmOtFlagsHeader);
|
||||
|
||||
wmOtDhwPresent = new CheckboxParameter("ot_dhw_present", "DHW present", settings.opentherm.dhwPresent);
|
||||
wm.addParameter(wmOtDhwPresent);
|
||||
|
||||
wmOtSummerWinterMode = new CheckboxParameter("ot_summer_winter_mode", "Summer/winter mode", settings.opentherm.summerWinterMode);
|
||||
wm.addParameter(wmOtSummerWinterMode);
|
||||
|
||||
wmOtHeatingCh2Enabled = new CheckboxParameter("ot_heating_ch2_enabled", "CH2 enabled", settings.opentherm.heatingCh2Enabled);
|
||||
wm.addParameter(wmOtHeatingCh2Enabled);
|
||||
|
||||
wmOtHeatingCh1ToCh2 = new CheckboxParameter("ot_heating_ch1_to_ch2", "Heating CH1 to CH2", settings.opentherm.heatingCh1ToCh2);
|
||||
wm.addParameter(wmOtHeatingCh1ToCh2);
|
||||
|
||||
wmOtDhwToCh2 = new CheckboxParameter("ot_dhw_to_ch2", "DHW to CH2", settings.opentherm.dhwToCh2);
|
||||
wm.addParameter(wmOtDhwToCh2);
|
||||
|
||||
wmOtDhwBlocking = new CheckboxParameter("ot_dhw_blocking", "DHW blocking", settings.opentherm.dhwBlocking);
|
||||
wm.addParameter(wmOtDhwBlocking);
|
||||
|
||||
wmOtModSyncWithHeating = new CheckboxParameter("ot_mod_sync_with_heating", "Modulation sync with heating", settings.opentherm.modulationSyncWithHeating);
|
||||
wm.addParameter(wmOtModSyncWithHeating);
|
||||
|
||||
wmSensorsHeader = new HeaderParameter("Sensors");
|
||||
wm.addParameter(wmSensorsHeader);
|
||||
|
||||
wmOutdoorSensorPin = new UnsignedIntParameter("outdoor_sensor_pin", "Outdoor sensor GPIO", settings.sensors.outdoor.pin, 2);
|
||||
wm.addParameter(wmOutdoorSensorPin);
|
||||
|
||||
wmIndoorSensorPin = new UnsignedIntParameter("indoor_sensor_pin", "Indoor sensor GPIO", settings.sensors.indoor.pin, 2);
|
||||
wm.addParameter(wmIndoorSensorPin);
|
||||
|
||||
#if USE_BLE
|
||||
wmOutdoorSensorBleAddress = new WiFiManagerParameter("ble_address", "BLE sensor address", settings.sensors.indoor.bleAddresss, 17);
|
||||
wm.addParameter(wmOutdoorSensorBleAddress);
|
||||
#endif
|
||||
|
||||
wmExtPumpHeader = new HeaderParameter("External pump");
|
||||
wm.addParameter(wmExtPumpHeader);
|
||||
|
||||
wmExtPumpUse = new CheckboxParameter("ext_pump_use", "Use external pump<br>", settings.externalPump.use);
|
||||
wm.addParameter(wmExtPumpUse);
|
||||
|
||||
wmExtPumpPin = new UnsignedIntParameter("ext_pump_pin", "Relay GPIO", settings.externalPump.pin, 2);
|
||||
wm.addParameter(wmExtPumpPin);
|
||||
|
||||
wmExtPumpPostCirculationTime = new UnsignedShortParameter("ext_pump_ps_time", "Post circulation time (min)", (settings.externalPump.postCirculationTime / 60), 5);
|
||||
wm.addParameter(wmExtPumpPostCirculationTime);
|
||||
|
||||
wmExtPumpAntiStuckInterval = new UnsignedIntParameter("ext_pump_as_interval", "Anti stuck interval (days)", (settings.externalPump.antiStuckInterval / 86400), 7);
|
||||
wm.addParameter(wmExtPumpAntiStuckInterval);
|
||||
|
||||
wmExtPumpAntiStuckTime = new UnsignedShortParameter("ext_pump_as_time", "Anti stuck time (min)", (settings.externalPump.antiStuckTime / 60), 5);
|
||||
wm.addParameter(wmExtPumpAntiStuckTime);
|
||||
}
|
||||
|
||||
WifiManagerTask* addTaskForDisable(AbstractTask* task) {
|
||||
this->tasksForDisable.push_back(task);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool connected = false;
|
||||
unsigned long lastArpGratuitous = 0;
|
||||
unsigned long lastReconnecting = 0;
|
||||
std::vector<AbstractTask*> tasksForDisable;
|
||||
|
||||
const char* getTaskName() {
|
||||
return "WifiManager";
|
||||
}
|
||||
|
||||
/*int getTaskCore() {
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
int getTaskPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
#ifdef WOKWI
|
||||
WiFi.begin("Wokwi-GUEST", "", 6);
|
||||
#endif
|
||||
|
||||
wm.setDebugOutput(settings.debug, (wm_debuglevel_t) WM_DEBUG_MODE);
|
||||
wm.setTitle(PROJECT_NAME);
|
||||
wm.setCustomHeadElement(PSTR(
|
||||
"<style>"
|
||||
".bheader + br {display: none;}"
|
||||
".bheader {margin: 1.25em 0 0.5em 0;padding: 0;border-bottom: 2px solid #000;font-size: 1.5em;}"
|
||||
"</style>"
|
||||
));
|
||||
wm.setCustomMenuHTML(PSTR(
|
||||
"<style>.wrap h1 {display: none;} .wrap h3 {display: none;} .nh {margin: 0 0 1em 0;} .nh .logo {font-size: 1.8em; margin: 0.5em; text-align: center;} .nh .links {text-align: center;}</style>"
|
||||
"<div class=\"nh\">"
|
||||
"<div class=\"logo\">" PROJECT_NAME "</div>"
|
||||
"<div class=\"links\"><a href=\"" PROJECT_REPO "\" target=\"_blank\">Repo</a> | <a href=\"" PROJECT_REPO "/issues\" target=\"_blank\">Issues</a> | <a href=\"" PROJECT_REPO "/releases\" target=\"_blank\">Releases</a> | <small>v" PROJECT_VERSION " (" __DATE__ ")</small></div>"
|
||||
"</div>"
|
||||
));
|
||||
|
||||
std::vector<const char *> menu = {"custom", "wifi", "param", "sep", "info", "update", "restart"};
|
||||
wm.setMenu(menu);
|
||||
|
||||
//wm.setCleanConnect(true);
|
||||
wm.setRestorePersistent(false);
|
||||
wm.setHostname(settings.hostname);
|
||||
wm.setWiFiAutoReconnect(false);
|
||||
wm.setAPClientCheck(true);
|
||||
wm.setConfigPortalBlocking(false);
|
||||
wm.setSaveParamsCallback(saveParamsCallback);
|
||||
wm.setPreOtaUpdateCallback([this] {
|
||||
for (AbstractTask* task : this->tasksForDisable) {
|
||||
if (task->isEnabled()) {
|
||||
task->disable();
|
||||
}
|
||||
}
|
||||
});
|
||||
wm.setConfigPortalTimeout(wm.getWiFiIsSaved() ? 180 : 0);
|
||||
wm.setDisableConfigPortal(false);
|
||||
|
||||
wm.autoConnect(AP_SSID, AP_PASSWORD);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (connected && WiFi.status() != WL_CONNECTED) {
|
||||
connected = false;
|
||||
|
||||
if (wm.getWebPortalActive()) {
|
||||
wm.stopWebPortal();
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*wm.setCaptivePortalEnable(true);
|
||||
|
||||
if (!wm.getConfigPortalActive()) {
|
||||
wm.startConfigPortal(AP_SSID, AP_PASSWORD);
|
||||
}*/
|
||||
|
||||
#if USE_TELNET
|
||||
TelnetStream.stop();
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::yield();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI), F("Disconnected"));
|
||||
}
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED && !wm.getConfigPortalActive()) {
|
||||
if (millis() - this->lastReconnecting > 5000) {
|
||||
Log.sinfoln(FPSTR(S_WIFI), F("Reconnecting..."));
|
||||
|
||||
WiFi.reconnect();
|
||||
this->lastReconnecting = millis();
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected && WiFi.status() == WL_CONNECTED) {
|
||||
connected = true;
|
||||
|
||||
wm.setConfigPortalTimeout(180);
|
||||
if (wm.getConfigPortalActive()) {
|
||||
wm.stopConfigPortal();
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
wm.setCaptivePortalEnable(false);
|
||||
if (!wm.getWebPortalActive()) {
|
||||
wm.startWebPortal();
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_TELNET
|
||||
TelnetStream.begin(23, false);
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::yield();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI), F("Connected. IP: %s, RSSI: %hhd"), WiFi.localIP().toString().c_str(), WiFi.RSSI());
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (connected && millis() - lastArpGratuitous > 60000) {
|
||||
stationKeepAliveNow();
|
||||
lastArpGratuitous = millis();
|
||||
|
||||
::yield();
|
||||
}
|
||||
#endif
|
||||
|
||||
wm.process();
|
||||
}
|
||||
|
||||
static void saveParamsCallback() {
|
||||
bool changed = false;
|
||||
bool needRestart = false;
|
||||
|
||||
if (strcmp(wmHostname->getValue(), settings.hostname) != 0) {
|
||||
changed = true;
|
||||
needRestart = true;
|
||||
strcpy(settings.hostname, wmHostname->getValue());
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New hostname: %s"), settings.hostname);
|
||||
}
|
||||
|
||||
if (strcmp(wmMqttServer->getValue(), settings.mqtt.server) != 0) {
|
||||
changed = true;
|
||||
strcpy(settings.mqtt.server, wmMqttServer->getValue());
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New mqtt.server: %s"), settings.mqtt.server);
|
||||
}
|
||||
|
||||
if (wmMqttPort->getValue() != settings.mqtt.port) {
|
||||
changed = true;
|
||||
settings.mqtt.port = wmMqttPort->getValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New mqtt.port: %hu"), settings.mqtt.port);
|
||||
}
|
||||
|
||||
if (strcmp(wmMqttUser->getValue(), settings.mqtt.user) != 0) {
|
||||
changed = true;
|
||||
strcpy(settings.mqtt.user, wmMqttUser->getValue());
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New mqtt.user: %s"), settings.mqtt.user);
|
||||
}
|
||||
|
||||
if (strcmp(wmMqttPassword->getValue(), settings.mqtt.password) != 0) {
|
||||
changed = true;
|
||||
strcpy(settings.mqtt.password, wmMqttPassword->getValue());
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New mqtt.password: %s"), settings.mqtt.password);
|
||||
}
|
||||
|
||||
if (strcmp(wmMqttPrefix->getValue(), settings.mqtt.prefix) != 0) {
|
||||
changed = true;
|
||||
strcpy(settings.mqtt.prefix, wmMqttPrefix->getValue());
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New mqtt.prefix: %s"), settings.mqtt.prefix);
|
||||
}
|
||||
|
||||
if (wmMqttPublishInterval->getValue() != settings.mqtt.interval) {
|
||||
changed = true;
|
||||
settings.mqtt.interval = wmMqttPublishInterval->getValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New mqtt.interval: %du"), settings.mqtt.interval);
|
||||
}
|
||||
|
||||
if (wmOtInPin->getValue() != settings.opentherm.inPin) {
|
||||
changed = true;
|
||||
needRestart = true;
|
||||
settings.opentherm.inPin = wmOtInPin->getValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.inPin: %hhu"), settings.opentherm.inPin);
|
||||
}
|
||||
|
||||
if (wmOtOutPin->getValue() != settings.opentherm.outPin) {
|
||||
changed = true;
|
||||
needRestart = true;
|
||||
settings.opentherm.outPin = wmOtOutPin->getValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.outPin: %hhu"), settings.opentherm.outPin);
|
||||
}
|
||||
|
||||
if (wmOtMemberIdCode->getValue() != settings.opentherm.memberIdCode) {
|
||||
changed = true;
|
||||
settings.opentherm.memberIdCode = wmOtMemberIdCode->getValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.memberIdCode: %du"), settings.opentherm.memberIdCode);
|
||||
}
|
||||
|
||||
if (wmOtDhwPresent->getCheckboxValue() != settings.opentherm.dhwPresent) {
|
||||
changed = true;
|
||||
settings.opentherm.dhwPresent = wmOtDhwPresent->getCheckboxValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.dhwPresent: %s"), settings.opentherm.dhwPresent ? "on" : "off");
|
||||
}
|
||||
|
||||
if (wmOtSummerWinterMode->getCheckboxValue() != settings.opentherm.summerWinterMode) {
|
||||
changed = true;
|
||||
settings.opentherm.summerWinterMode = wmOtSummerWinterMode->getCheckboxValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.summerWinterMode: %s"), settings.opentherm.summerWinterMode ? "on" : "off");
|
||||
}
|
||||
|
||||
if (wmOtHeatingCh2Enabled->getCheckboxValue() != settings.opentherm.heatingCh2Enabled) {
|
||||
changed = true;
|
||||
settings.opentherm.heatingCh2Enabled = wmOtHeatingCh2Enabled->getCheckboxValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.heatingCh2Enabled: %s"), settings.opentherm.heatingCh2Enabled ? "on" : "off");
|
||||
|
||||
if (settings.opentherm.heatingCh1ToCh2) {
|
||||
settings.opentherm.heatingCh1ToCh2 = false;
|
||||
wmOtHeatingCh1ToCh2->setValue(false);
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.heatingCh1ToCh2: %s"), settings.opentherm.heatingCh1ToCh2 ? "on" : "off");
|
||||
}
|
||||
|
||||
if (settings.opentherm.dhwToCh2) {
|
||||
settings.opentherm.dhwToCh2 = false;
|
||||
wmOtDhwToCh2->setValue(false);
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.dhwToCh2: %s"), settings.opentherm.dhwToCh2 ? "on" : "off");
|
||||
}
|
||||
}
|
||||
|
||||
if (wmOtHeatingCh1ToCh2->getCheckboxValue() != settings.opentherm.heatingCh1ToCh2) {
|
||||
changed = true;
|
||||
settings.opentherm.heatingCh1ToCh2 = wmOtHeatingCh1ToCh2->getCheckboxValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.heatingCh1ToCh2: %s"), settings.opentherm.heatingCh1ToCh2 ? "on" : "off");
|
||||
|
||||
if (settings.opentherm.heatingCh2Enabled) {
|
||||
settings.opentherm.heatingCh2Enabled = false;
|
||||
wmOtHeatingCh2Enabled->setValue(false);
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.heatingCh2Enabled: %s"), settings.opentherm.heatingCh2Enabled ? "on" : "off");
|
||||
}
|
||||
|
||||
if (settings.opentherm.dhwToCh2) {
|
||||
settings.opentherm.dhwToCh2 = false;
|
||||
wmOtDhwToCh2->setValue(false);
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.dhwToCh2: %s"), settings.opentherm.dhwToCh2 ? "on" : "off");
|
||||
}
|
||||
}
|
||||
|
||||
if (wmOtDhwToCh2->getCheckboxValue() != settings.opentherm.dhwToCh2) {
|
||||
changed = true;
|
||||
settings.opentherm.dhwToCh2 = wmOtDhwToCh2->getCheckboxValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.dhwToCh2: %s"), settings.opentherm.dhwToCh2 ? "on" : "off");
|
||||
|
||||
if (settings.opentherm.heatingCh2Enabled) {
|
||||
settings.opentherm.heatingCh2Enabled = false;
|
||||
wmOtHeatingCh2Enabled->setValue(false);
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.heatingCh2Enabled: %s"), settings.opentherm.heatingCh2Enabled ? "on" : "off");
|
||||
}
|
||||
|
||||
if (settings.opentherm.heatingCh1ToCh2) {
|
||||
settings.opentherm.heatingCh1ToCh2 = false;
|
||||
wmOtHeatingCh1ToCh2->setValue(false);
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.heatingCh1ToCh2: %s"), settings.opentherm.heatingCh1ToCh2 ? "on" : "off");
|
||||
}
|
||||
}
|
||||
|
||||
if (wmOtDhwBlocking->getCheckboxValue() != settings.opentherm.dhwBlocking) {
|
||||
changed = true;
|
||||
settings.opentherm.dhwBlocking = wmOtDhwBlocking->getCheckboxValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.dhwBlocking: %s"), settings.opentherm.dhwBlocking ? "on" : "off");
|
||||
}
|
||||
|
||||
if (wmOtModSyncWithHeating->getCheckboxValue() != settings.opentherm.modulationSyncWithHeating) {
|
||||
changed = true;
|
||||
settings.opentherm.modulationSyncWithHeating = wmOtModSyncWithHeating->getCheckboxValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New opentherm.modulationSyncWithHeating: %s"), settings.opentherm.modulationSyncWithHeating ? "on" : "off");
|
||||
}
|
||||
|
||||
if (wmOutdoorSensorPin->getValue() != settings.sensors.outdoor.pin) {
|
||||
changed = true;
|
||||
needRestart = true;
|
||||
settings.sensors.outdoor.pin = wmOutdoorSensorPin->getValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New sensors.outdoor.pin: %hhu"), settings.sensors.outdoor.pin);
|
||||
}
|
||||
|
||||
if (wmIndoorSensorPin->getValue() != settings.sensors.indoor.pin) {
|
||||
changed = true;
|
||||
needRestart = true;
|
||||
settings.sensors.indoor.pin = wmIndoorSensorPin->getValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New sensors.indoor.pin: %hhu"), settings.sensors.indoor.pin);
|
||||
}
|
||||
|
||||
#if USE_BLE
|
||||
if (strcmp(wmOutdoorSensorBleAddress->getValue(), settings.sensors.indoor.bleAddresss) != 0) {
|
||||
changed = true;
|
||||
strcpy(settings.sensors.indoor.bleAddresss, wmOutdoorSensorBleAddress->getValue());
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New sensors.indoor.bleAddresss: %s"), settings.sensors.indoor.bleAddresss);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (wmExtPumpUse->getCheckboxValue() != settings.externalPump.use) {
|
||||
changed = true;
|
||||
settings.externalPump.use = wmExtPumpUse->getCheckboxValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New externalPump.use: %s"), settings.externalPump.use ? "on" : "off");
|
||||
}
|
||||
|
||||
if (wmExtPumpPin->getValue() != settings.externalPump.pin) {
|
||||
changed = true;
|
||||
needRestart = true;
|
||||
settings.externalPump.pin = wmExtPumpPin->getValue();
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New externalPump.pin: %hhu"), settings.externalPump.pin);
|
||||
}
|
||||
|
||||
if ((wmExtPumpPostCirculationTime->getValue() * 60) != settings.externalPump.postCirculationTime) {
|
||||
changed = true;
|
||||
settings.externalPump.postCirculationTime = wmExtPumpPostCirculationTime->getValue() * 60;
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New externalPump.postCirculationTime: %hu"), settings.externalPump.postCirculationTime);
|
||||
}
|
||||
|
||||
if ((wmExtPumpAntiStuckInterval->getValue() * 86400) != settings.externalPump.antiStuckInterval) {
|
||||
changed = true;
|
||||
settings.externalPump.antiStuckInterval = wmExtPumpAntiStuckInterval->getValue() * 86400;
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New externalPump.antiStuckInterval: %du"), settings.externalPump.antiStuckInterval);
|
||||
}
|
||||
|
||||
if ((wmExtPumpAntiStuckTime->getValue() * 60) != settings.externalPump.antiStuckTime) {
|
||||
changed = true;
|
||||
settings.externalPump.antiStuckTime = wmExtPumpAntiStuckTime->getValue() * 60;
|
||||
|
||||
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New externalPump.antiStuckTime: %hu"), settings.externalPump.antiStuckTime);
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return;
|
||||
} else if (needRestart) {
|
||||
vars.actions.restart = true;
|
||||
}
|
||||
|
||||
eeSettings.update();
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
/**
|
||||
* @brief
|
||||
* https://github.com/arendst/Tasmota/blob/e6515883f0ee5451931b6280ff847b117de5a231/tasmota/tasmota_support/support_wifi.ino#L1196
|
||||
*/
|
||||
static void stationKeepAliveNow(void) {
|
||||
for (netif* interface = netif_list; interface != nullptr; interface = interface->next) {
|
||||
if (
|
||||
(interface->flags & NETIF_FLAG_LINK_UP)
|
||||
&& (interface->flags & NETIF_FLAG_UP)
|
||||
&& interface->num == STATION_IF
|
||||
&& (!ip4_addr_isany_val(*netif_ip4_addr(interface)))
|
||||
) {
|
||||
etharp_gratuitous(interface);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
#define PROJECT_NAME "OpenTherm Gateway"
|
||||
#define PROJECT_VERSION "1.4.0-rc.5"
|
||||
#define PROJECT_VERSION "1.4.0-rc.9"
|
||||
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
|
||||
#define AP_SSID "OpenTherm Gateway"
|
||||
#define AP_PASSWORD "otgateway123456"
|
||||
|
||||
#define EMERGENCY_TIME_TRESHOLD 120000
|
||||
#define MQTT_RECONNECT_INTERVAL 15000
|
||||
@@ -38,10 +36,58 @@
|
||||
#define USE_BLE false
|
||||
#endif
|
||||
|
||||
#ifndef HOSTNAME_DEFAULT
|
||||
#define HOSTNAME_DEFAULT "opentherm"
|
||||
#endif
|
||||
|
||||
#ifndef AP_SSID_DEFAULT
|
||||
#define AP_SSID_DEFAULT "OpenTherm Gateway"
|
||||
#endif
|
||||
|
||||
#ifndef AP_PASSWORD_DEFAULT
|
||||
#define AP_PASSWORD_DEFAULT "otgateway123456"
|
||||
#endif
|
||||
|
||||
#ifndef STA_SSID_DEFAULT
|
||||
#define STA_SSID_DEFAULT ""
|
||||
#endif
|
||||
|
||||
#ifndef STA_PASSWORD_DEFAULT
|
||||
#define STA_PASSWORD_DEFAULT ""
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_BY_DEFAULT
|
||||
#define DEBUG_BY_DEFAULT false
|
||||
#endif
|
||||
|
||||
#ifndef PORTAL_LOGIN_DEFAULT
|
||||
#define PORTAL_LOGIN_DEFAULT ""
|
||||
#endif
|
||||
|
||||
#ifndef PORTAL_PASSWORD_DEFAULT
|
||||
#define PORTAL_PASSWORD_DEFAULT ""
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_SERVER_DEFAULT
|
||||
#define MQTT_SERVER_DEFAULT ""
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_PORT_DEFAULT
|
||||
#define MQTT_PORT_DEFAULT 1883
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_USER_DEFAULT
|
||||
#define MQTT_USER_DEFAULT ""
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_PASSWORD_DEFAULT
|
||||
#define MQTT_PASSWORD_DEFAULT ""
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_PREFIX_DEFAULT
|
||||
#define MQTT_PREFIX_DEFAULT "opentherm"
|
||||
#endif
|
||||
|
||||
#ifndef OT_IN_PIN_DEFAULT
|
||||
#define OT_IN_PIN_DEFAULT 0
|
||||
#endif
|
||||
|
||||
125
src/main.cpp
125
src/main.cpp
@@ -1,9 +1,12 @@
|
||||
#include <Arduino.h>
|
||||
#include "defines.h"
|
||||
#include "strings.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <EEManager.h>
|
||||
#include <FileData.h>
|
||||
#include <LittleFS.h>
|
||||
#include <TinyLogger.h>
|
||||
#include "Settings.h"
|
||||
#include <utils.h>
|
||||
|
||||
#if USE_TELNET
|
||||
#include "ESPTelnetStream.h"
|
||||
@@ -19,29 +22,34 @@
|
||||
|
||||
#include <Task.h>
|
||||
#include <LeanTask.h>
|
||||
#include "WifiManagerTask.h"
|
||||
#include "NetworkTask.h"
|
||||
#include "MqttTask.h"
|
||||
#include "OpenThermTask.h"
|
||||
#include "SensorsTask.h"
|
||||
#include "RegulatorTask.h"
|
||||
#include "PortalTask.h"
|
||||
#include "MainTask.h"
|
||||
|
||||
// Vars
|
||||
EEManager eeSettings(settings, 60000);
|
||||
FileData fsNetworkSettings(&LittleFS, "/network.conf", 'n', &networkSettings, sizeof(networkSettings), 1000);
|
||||
FileData fsSettings(&LittleFS, "/settings.conf", 's', &settings, sizeof(settings), 60000);
|
||||
#if USE_TELNET
|
||||
ESPTelnetStream TelnetStream;
|
||||
ESPTelnetStream TelnetStream;
|
||||
#endif
|
||||
|
||||
// Tasks
|
||||
WifiManagerTask* tWm;
|
||||
NetworkTask* tNetwork;
|
||||
MqttTask* tMqtt;
|
||||
OpenThermTask* tOt;
|
||||
SensorsTask* tSensors;
|
||||
RegulatorTask* tRegulator;
|
||||
PortalTask* tPortal;
|
||||
MainTask* tMain;
|
||||
|
||||
|
||||
void setup() {
|
||||
LittleFS.begin();
|
||||
|
||||
Log.setLevel(TinyLogger::Level::VERBOSE);
|
||||
Log.setServiceTemplate("\033[1m[%s]\033[22m");
|
||||
Log.setLevelTemplate("\033[1m[%s]\033[22m");
|
||||
@@ -52,46 +60,87 @@ void setup() {
|
||||
int sec = time % 60;
|
||||
int min = time % 3600 / 60;
|
||||
int hour = time / 3600;
|
||||
|
||||
|
||||
return tm{sec, min, hour};
|
||||
});
|
||||
|
||||
|
||||
#if USE_SERIAL
|
||||
Serial.begin(115200);
|
||||
Serial.println("\n\n");
|
||||
Log.addStream(&Serial);
|
||||
Serial.begin(115200);
|
||||
Log.addStream(&Serial);
|
||||
#endif
|
||||
|
||||
#if USE_TELNET
|
||||
TelnetStream.setKeepAliveInterval(500);
|
||||
Log.addStream(&TelnetStream);
|
||||
TelnetStream.setKeepAliveInterval(500);
|
||||
Log.addStream(&TelnetStream);
|
||||
#endif
|
||||
|
||||
EEPROM.begin(eeSettings.blockSize());
|
||||
uint8_t eeSettingsResult = eeSettings.begin(0, 's');
|
||||
if (eeSettingsResult == 0) {
|
||||
Log.sinfoln("MAIN", F("Settings loaded"));
|
||||
Log.print("\n\n\r");
|
||||
|
||||
if (strcmp(SETTINGS_VALID_VALUE, settings.validationValue) != 0) {
|
||||
Log.swarningln("MAIN", F("Settings not valid, reset and restart..."));
|
||||
eeSettings.reset();
|
||||
delay(5000);
|
||||
ESP.restart();
|
||||
}
|
||||
// network settings
|
||||
switch (fsNetworkSettings.read()) {
|
||||
case FD_FS_ERR:
|
||||
Log.swarningln(FPSTR(L_NETWORK_SETTINGS), F("Filesystem error, load default"));
|
||||
break;
|
||||
case FD_FILE_ERR:
|
||||
Log.swarningln(FPSTR(L_NETWORK_SETTINGS), F("Bad data, load default"));
|
||||
break;
|
||||
case FD_WRITE:
|
||||
Log.sinfoln(FPSTR(L_NETWORK_SETTINGS), F("Not found, load default"));
|
||||
break;
|
||||
case FD_ADD:
|
||||
case FD_READ:
|
||||
Log.sinfoln(FPSTR(L_NETWORK_SETTINGS), F("Loaded"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (eeSettingsResult == 1) {
|
||||
Log.sinfoln("MAIN", F("Settings NOT loaded, first start"));
|
||||
// settings
|
||||
switch (fsSettings.read()) {
|
||||
case FD_FS_ERR:
|
||||
Log.swarningln(FPSTR(L_SETTINGS), F("Filesystem error, load default"));
|
||||
break;
|
||||
case FD_FILE_ERR:
|
||||
Log.swarningln(FPSTR(L_SETTINGS), F("Bad data, load default"));
|
||||
break;
|
||||
case FD_WRITE:
|
||||
Log.sinfoln(FPSTR(L_SETTINGS), F("Not found, load default"));
|
||||
break;
|
||||
case FD_ADD:
|
||||
case FD_READ:
|
||||
Log.sinfoln(FPSTR(L_SETTINGS), F("Loaded"));
|
||||
|
||||
} else if (eeSettingsResult == 2) {
|
||||
Log.serrorln("MAIN", F("Settings NOT loaded (error)"));
|
||||
if (strcmp(SETTINGS_VALID_VALUE, settings.validationValue) != 0) {
|
||||
Log.swarningln(FPSTR(L_SETTINGS), F("Not valid, set default and restart..."));
|
||||
fsSettings.reset();
|
||||
delay(5000);
|
||||
ESP.restart();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Log.setLevel(settings.debug ? TinyLogger::Level::VERBOSE : TinyLogger::Level::INFO);
|
||||
|
||||
tWm = new WifiManagerTask(true, 0);
|
||||
Scheduler.start(tWm);
|
||||
|
||||
tMqtt = new MqttTask(false, 100);
|
||||
tNetwork = (new NetworkTask(true, 500))
|
||||
->setHostname(networkSettings.hostname)
|
||||
->setStaCredentials(
|
||||
#ifdef WOKWI
|
||||
"Wokwi-GUEST", nullptr, 6
|
||||
#else
|
||||
strlen(networkSettings.sta.ssid) ? networkSettings.sta.ssid : nullptr,
|
||||
strlen(networkSettings.sta.password) ? networkSettings.sta.password : nullptr,
|
||||
networkSettings.sta.channel
|
||||
#endif
|
||||
)->setApCredentials(
|
||||
strlen(networkSettings.ap.ssid) ? networkSettings.ap.ssid : nullptr,
|
||||
strlen(networkSettings.ap.password) ? networkSettings.ap.password : nullptr,
|
||||
networkSettings.ap.channel
|
||||
);
|
||||
Scheduler.start(tNetwork);
|
||||
|
||||
tMqtt = new MqttTask(false, 500);
|
||||
Scheduler.start(tMqtt);
|
||||
|
||||
tOt = new OpenThermTask(false, 1000);
|
||||
@@ -103,21 +152,17 @@ void setup() {
|
||||
tRegulator = new RegulatorTask(true, 10000);
|
||||
Scheduler.start(tRegulator);
|
||||
|
||||
tPortal = new PortalTask(true, 0);
|
||||
Scheduler.start(tPortal);
|
||||
|
||||
tMain = new MainTask(true, 100);
|
||||
Scheduler.start(tMain);
|
||||
|
||||
tWm
|
||||
->addTaskForDisable(tMain)
|
||||
->addTaskForDisable(tMqtt)
|
||||
->addTaskForDisable(tOt)
|
||||
->addTaskForDisable(tSensors)
|
||||
->addTaskForDisable(tRegulator);
|
||||
|
||||
Scheduler.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
vTaskDelete(NULL);
|
||||
#endif
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
vTaskDelete(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
23
src/strings.h
Normal file
23
src/strings.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM
|
||||
#endif
|
||||
|
||||
const char L_SETTINGS[] PROGMEM = "SETTINGS";
|
||||
const char L_NETWORK[] PROGMEM = "NETWORK";
|
||||
const char L_NETWORK_SETTINGS[] PROGMEM = "NETWORK.SETTINGS";
|
||||
const char L_PORTAL_WEBSERVER[] PROGMEM = "PORTAL.WEBSERVER";
|
||||
const char L_PORTAL_DNSSERVER[] PROGMEM = "PORTAL.DNSSERVER";
|
||||
const char L_PORTAL_CAPTIVE[] PROGMEM = "PORTAL.CAPTIVE";
|
||||
const char L_MAIN[] PROGMEM = "MAIN";
|
||||
const char L_MQTT[] PROGMEM = "MQTT";
|
||||
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_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";
|
||||
939
src/utils.h
Normal file
939
src/utils.h
Normal file
@@ -0,0 +1,939 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
double roundd(double value, uint8_t decimals = 2) {
|
||||
if (decimals == 0) {
|
||||
return (int)(value + 0.5);
|
||||
|
||||
} else if (abs(value) < 0.00000001) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double multiplier = pow10(decimals);
|
||||
value += 0.5 / multiplier * (value < 0 ? -1 : 1);
|
||||
return (int)(value * multiplier) / multiplier;
|
||||
}
|
||||
|
||||
size_t getFreeHeap() {
|
||||
return ESP.getFreeHeap();
|
||||
}
|
||||
|
||||
size_t getTotalHeap() {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
return ESP.getHeapSize();
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
return 81920;
|
||||
#else
|
||||
return 99999;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t getMaxFreeBlockHeap() {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
return ESP.getMaxAllocHeap();
|
||||
#else
|
||||
return ESP.getMaxFreeBlockSize();
|
||||
#endif
|
||||
}
|
||||
|
||||
String getResetReason() {
|
||||
String value;
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
value = ESP.getResetReason();
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
switch(esp_reset_reason()) {
|
||||
case ESP_RST_POWERON:
|
||||
value = F("Reset due to power-on event");
|
||||
break;
|
||||
|
||||
case ESP_RST_EXT:
|
||||
value = F("Reset by external pin");
|
||||
break;
|
||||
|
||||
case ESP_RST_SW:
|
||||
value = F("Software reset via esp_restart");
|
||||
break;
|
||||
|
||||
case ESP_RST_PANIC:
|
||||
value = F("Software reset due to exception/panic");
|
||||
break;
|
||||
|
||||
case ESP_RST_INT_WDT:
|
||||
value = F("Reset (software or hardware) due to interrupt watchdog");
|
||||
break;
|
||||
|
||||
case ESP_RST_TASK_WDT:
|
||||
value = F("Reset due to task watchdog");
|
||||
break;
|
||||
|
||||
case ESP_RST_WDT:
|
||||
value = F("Reset due to other watchdogs");
|
||||
break;
|
||||
|
||||
case ESP_RST_DEEPSLEEP:
|
||||
value = F("Reset after exiting deep sleep mode");
|
||||
break;
|
||||
|
||||
case ESP_RST_BROWNOUT:
|
||||
value = F("Brownout reset (software or hardware)");
|
||||
break;
|
||||
|
||||
case ESP_RST_SDIO:
|
||||
value = F("Reset over SDIO");
|
||||
break;
|
||||
|
||||
case ESP_RST_UNKNOWN:
|
||||
default:
|
||||
value = F("unknown");
|
||||
break;
|
||||
|
||||
}
|
||||
#else
|
||||
value = F("unknown");
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void networkSettingsToJson(const NetworkSettings& src, JsonVariant dst) {
|
||||
dst["hostname"] = src.hostname;
|
||||
|
||||
dst["useDhcp"] = src.useDhcp;
|
||||
dst["staticConfig"]["ip"] = src.staticConfig.ip;
|
||||
dst["staticConfig"]["gateway"] = src.staticConfig.gateway;
|
||||
dst["staticConfig"]["subnet"] = src.staticConfig.subnet;
|
||||
dst["staticConfig"]["dns"] = src.staticConfig.dns;
|
||||
|
||||
dst["ap"]["ssid"] = src.ap.ssid;
|
||||
dst["ap"]["password"] = src.ap.password;
|
||||
dst["ap"]["channel"] = src.ap.channel;
|
||||
|
||||
dst["sta"]["ssid"] = src.sta.ssid;
|
||||
dst["sta"]["password"] = src.sta.password;
|
||||
dst["sta"]["channel"] = src.sta.channel;
|
||||
|
||||
//dst.shrinkToFit();
|
||||
}
|
||||
|
||||
bool jsonToNetworkSettings(const JsonVariantConst src, NetworkSettings& dst) {
|
||||
bool changed = false;
|
||||
|
||||
// hostname
|
||||
if (!src["hostname"].isNull()) {
|
||||
String value = src["hostname"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.hostname)) {
|
||||
strcpy(dst.hostname, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// use dhcp
|
||||
if (src["useDhcp"].is<bool>()) {
|
||||
dst.useDhcp = src["useDhcp"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
// static config
|
||||
if (!src["staticConfig"]["ip"].isNull()) {
|
||||
String value = src["staticConfig"]["ip"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.staticConfig.ip)) {
|
||||
strcpy(dst.staticConfig.ip, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["staticConfig"]["gateway"].isNull()) {
|
||||
String value = src["staticConfig"]["gateway"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.staticConfig.gateway)) {
|
||||
strcpy(dst.staticConfig.gateway, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["staticConfig"]["subnet"].isNull()) {
|
||||
String value = src["staticConfig"]["subnet"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.staticConfig.subnet)) {
|
||||
strcpy(dst.staticConfig.subnet, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["staticConfig"]["dns"].isNull()) {
|
||||
String value = src["staticConfig"]["dns"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.staticConfig.dns)) {
|
||||
strcpy(dst.staticConfig.dns, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ap
|
||||
if (!src["ap"]["ssid"].isNull()) {
|
||||
String value = src["ap"]["ssid"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.ap.ssid)) {
|
||||
strcpy(dst.ap.ssid, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["ap"]["password"].isNull()) {
|
||||
String value = src["ap"]["password"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.ap.password)) {
|
||||
strcpy(dst.ap.password, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["ap"]["channel"].isNull()) {
|
||||
unsigned char value = src["ap"]["channel"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 12) {
|
||||
dst.ap.channel = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ap
|
||||
if (!src["sta"]["ssid"].isNull()) {
|
||||
String value = src["sta"]["ssid"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.sta.ssid)) {
|
||||
strcpy(dst.sta.ssid, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["sta"]["password"].isNull()) {
|
||||
String value = src["sta"]["password"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.sta.password)) {
|
||||
strcpy(dst.sta.password, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["sta"]["channel"].isNull()) {
|
||||
unsigned char value = src["sta"]["channel"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 12) {
|
||||
dst.sta.channel = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
||||
dst["debug"] = src.debug;
|
||||
|
||||
if (!safe) {
|
||||
dst["portal"]["useAuth"] = src.portal.useAuth;
|
||||
dst["portal"]["login"] = src.portal.login;
|
||||
dst["portal"]["password"] = src.portal.password;
|
||||
|
||||
dst["opentherm"]["inPin"] = src.opentherm.inPin;
|
||||
dst["opentherm"]["outPin"] = src.opentherm.outPin;
|
||||
dst["opentherm"]["memberIdCode"] = src.opentherm.memberIdCode;
|
||||
dst["opentherm"]["dhwPresent"] = src.opentherm.dhwPresent;
|
||||
dst["opentherm"]["summerWinterMode"] = src.opentherm.summerWinterMode;
|
||||
dst["opentherm"]["heatingCh2Enabled"] = src.opentherm.heatingCh2Enabled;
|
||||
dst["opentherm"]["heatingCh1ToCh2"] = src.opentherm.heatingCh1ToCh2;
|
||||
dst["opentherm"]["dhwToCh2"] = src.opentherm.dhwToCh2;
|
||||
dst["opentherm"]["dhwBlocking"] = src.opentherm.dhwBlocking;
|
||||
dst["opentherm"]["modulationSyncWithHeating"] = src.opentherm.modulationSyncWithHeating;
|
||||
|
||||
dst["mqtt"]["server"] = src.mqtt.server;
|
||||
dst["mqtt"]["port"] = src.mqtt.port;
|
||||
dst["mqtt"]["user"] = src.mqtt.user;
|
||||
dst["mqtt"]["password"] = src.mqtt.password;
|
||||
dst["mqtt"]["prefix"] = src.mqtt.prefix;
|
||||
dst["mqtt"]["interval"] = src.mqtt.interval;
|
||||
}
|
||||
|
||||
dst["emergency"]["enable"] = src.emergency.enable;
|
||||
dst["emergency"]["target"] = roundd(src.emergency.target, 2);
|
||||
dst["emergency"]["useEquitherm"] = src.emergency.useEquitherm;
|
||||
dst["emergency"]["usePid"] = src.emergency.usePid;
|
||||
|
||||
dst["heating"]["enable"] = src.heating.enable;
|
||||
dst["heating"]["turbo"] = src.heating.turbo;
|
||||
dst["heating"]["target"] = roundd(src.heating.target, 2);
|
||||
dst["heating"]["hysteresis"] = roundd(src.heating.hysteresis, 2);
|
||||
dst["heating"]["minTemp"] = src.heating.minTemp;
|
||||
dst["heating"]["maxTemp"] = src.heating.maxTemp;
|
||||
dst["heating"]["maxModulation"] = src.heating.maxModulation;
|
||||
|
||||
dst["dhw"]["enable"] = src.dhw.enable;
|
||||
dst["dhw"]["target"] = src.dhw.target;
|
||||
dst["dhw"]["minTemp"] = src.dhw.minTemp;
|
||||
dst["dhw"]["maxTemp"] = src.dhw.maxTemp;
|
||||
|
||||
dst["pid"]["enable"] = src.pid.enable;
|
||||
dst["pid"]["p_factor"] = roundd(src.pid.p_factor, 3);
|
||||
dst["pid"]["i_factor"] = roundd(src.pid.i_factor, 3);
|
||||
dst["pid"]["d_factor"] = roundd(src.pid.d_factor, 1);
|
||||
dst["pid"]["dt"] = src.pid.dt;
|
||||
dst["pid"]["minTemp"] = src.pid.minTemp;
|
||||
dst["pid"]["maxTemp"] = src.pid.maxTemp;
|
||||
|
||||
dst["equitherm"]["enable"] = src.equitherm.enable;
|
||||
dst["equitherm"]["n_factor"] = roundd(src.equitherm.n_factor, 3);
|
||||
dst["equitherm"]["k_factor"] = roundd(src.equitherm.k_factor, 3);
|
||||
dst["equitherm"]["t_factor"] = roundd(src.equitherm.t_factor, 3);
|
||||
|
||||
dst["sensors"]["outdoor"]["type"] = src.sensors.outdoor.type;
|
||||
dst["sensors"]["outdoor"]["pin"] = src.sensors.outdoor.pin;
|
||||
dst["sensors"]["outdoor"]["offset"] = roundd(src.sensors.outdoor.offset, 2);
|
||||
|
||||
dst["sensors"]["indoor"]["type"] = src.sensors.indoor.type;
|
||||
dst["sensors"]["indoor"]["pin"] = src.sensors.indoor.pin;
|
||||
dst["sensors"]["indoor"]["bleAddresss"] = src.sensors.indoor.bleAddresss;
|
||||
dst["sensors"]["indoor"]["offset"] = roundd(src.sensors.indoor.offset, 2);
|
||||
|
||||
if (!safe) {
|
||||
dst["externalPump"]["use"] = src.externalPump.use;
|
||||
dst["externalPump"]["pin"] = src.externalPump.pin;
|
||||
dst["externalPump"]["postCirculationTime"] = roundd(src.externalPump.postCirculationTime / 60, 0);
|
||||
dst["externalPump"]["antiStuckInterval"] = roundd(src.externalPump.antiStuckInterval / 86400, 0);
|
||||
dst["externalPump"]["antiStuckTime"] = roundd(src.externalPump.antiStuckTime / 60, 0);
|
||||
}
|
||||
|
||||
//dst.shrinkToFit();
|
||||
}
|
||||
|
||||
void safeSettingsToJson(const Settings& src, JsonVariant dst) {
|
||||
settingsToJson(src, dst, true);
|
||||
}
|
||||
|
||||
bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false) {
|
||||
bool changed = false;
|
||||
|
||||
if (src["debug"].is<bool>()) {
|
||||
dst.debug = src["debug"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
if (!safe) {
|
||||
// portal
|
||||
if (src["portal"]["useAuth"].is<bool>()) {
|
||||
dst.portal.useAuth = src["portal"]["useAuth"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["portal"]["login"].isNull()) {
|
||||
String value = src["portal"]["login"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.portal.login)) {
|
||||
strcpy(dst.portal.login, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["portal"]["password"].isNull()) {
|
||||
String value = src["portal"]["password"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.portal.password)) {
|
||||
strcpy(dst.portal.password, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// opentherm
|
||||
if (!src["opentherm"]["inPin"].isNull()) {
|
||||
unsigned char value = src["opentherm"]["inPin"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 50) {
|
||||
dst.opentherm.inPin = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["opentherm"]["outPin"].isNull()) {
|
||||
unsigned char value = src["opentherm"]["outPin"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 50) {
|
||||
dst.opentherm.outPin = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["opentherm"]["memberIdCode"].isNull()) {
|
||||
unsigned int value = src["opentherm"]["memberIdCode"].as<unsigned int>();
|
||||
|
||||
if (value >= 0 && value < 65536) {
|
||||
dst.opentherm.memberIdCode = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (src["opentherm"]["dhwPresent"].is<bool>()) {
|
||||
dst.opentherm.dhwPresent = src["opentherm"]["dhwPresent"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["opentherm"]["summerWinterMode"].is<bool>()) {
|
||||
dst.opentherm.summerWinterMode = src["opentherm"]["summerWinterMode"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["opentherm"]["heatingCh2Enabled"].is<bool>()) {
|
||||
dst.opentherm.heatingCh2Enabled = src["opentherm"]["heatingCh2Enabled"].as<bool>();
|
||||
|
||||
if (dst.opentherm.heatingCh2Enabled) {
|
||||
dst.opentherm.heatingCh1ToCh2 = false;
|
||||
dst.opentherm.dhwToCh2 = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["opentherm"]["heatingCh1ToCh2"].is<bool>()) {
|
||||
dst.opentherm.heatingCh1ToCh2 = src["opentherm"]["heatingCh1ToCh2"].as<bool>();
|
||||
|
||||
if (dst.opentherm.heatingCh1ToCh2) {
|
||||
dst.opentherm.heatingCh2Enabled = false;
|
||||
dst.opentherm.dhwToCh2 = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["opentherm"]["dhwToCh2"].is<bool>()) {
|
||||
dst.opentherm.dhwToCh2 = src["opentherm"]["dhwToCh2"].as<bool>();
|
||||
|
||||
if (dst.opentherm.dhwToCh2) {
|
||||
dst.opentherm.heatingCh2Enabled = false;
|
||||
dst.opentherm.heatingCh1ToCh2 = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["opentherm"]["dhwBlocking"].is<bool>()) {
|
||||
dst.opentherm.dhwBlocking = src["opentherm"]["dhwBlocking"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["opentherm"]["modulationSyncWithHeating"].is<bool>()) {
|
||||
dst.opentherm.modulationSyncWithHeating = src["opentherm"]["modulationSyncWithHeating"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
// mqtt
|
||||
if (!src["mqtt"]["server"].isNull()) {
|
||||
String value = src["mqtt"]["server"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.mqtt.server)) {
|
||||
strcpy(dst.mqtt.server, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["mqtt"]["port"].isNull()) {
|
||||
unsigned short value = src["mqtt"]["port"].as<unsigned short>();
|
||||
|
||||
if (value >= 0 && value <= 65536) {
|
||||
dst.mqtt.port = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["mqtt"]["user"].isNull()) {
|
||||
String value = src["mqtt"]["user"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.mqtt.user)) {
|
||||
strcpy(dst.mqtt.user, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["mqtt"]["password"].isNull()) {
|
||||
String value = src["mqtt"]["password"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.mqtt.password)) {
|
||||
strcpy(dst.mqtt.password, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["mqtt"]["prefix"].isNull()) {
|
||||
String value = src["mqtt"]["prefix"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.mqtt.prefix)) {
|
||||
strcpy(dst.mqtt.prefix, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["mqtt"]["interval"].isNull()) {
|
||||
unsigned short value = src["mqtt"]["interval"].as<unsigned short>();
|
||||
|
||||
if (value >= 3 && value <= 60) {
|
||||
dst.mqtt.interval = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// emergency
|
||||
if (src["emergency"]["enable"].is<bool>()) {
|
||||
dst.emergency.enable = src["emergency"]["enable"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["emergency"]["target"].isNull()) {
|
||||
double value = src["emergency"]["target"].as<double>();
|
||||
|
||||
if (value > 0 && value < 100) {
|
||||
dst.emergency.target = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (src["emergency"]["useEquitherm"].is<bool>()) {
|
||||
if (dst.sensors.outdoor.type != 1) {
|
||||
dst.emergency.useEquitherm = src["emergency"]["useEquitherm"].as<bool>();
|
||||
|
||||
} else {
|
||||
dst.emergency.useEquitherm = false;
|
||||
}
|
||||
|
||||
if (dst.emergency.useEquitherm && dst.emergency.usePid) {
|
||||
dst.emergency.usePid = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["emergency"]["usePid"].is<bool>()) {
|
||||
if (dst.sensors.indoor.type != 1) {
|
||||
dst.emergency.usePid = src["emergency"]["usePid"].as<bool>();
|
||||
|
||||
} else {
|
||||
dst.emergency.usePid = false;
|
||||
}
|
||||
|
||||
if (dst.emergency.usePid && dst.emergency.useEquitherm) {
|
||||
dst.emergency.useEquitherm = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
// heating
|
||||
if (src["heating"]["enable"].is<bool>()) {
|
||||
dst.heating.enable = src["heating"]["enable"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["heating"]["turbo"].is<bool>()) {
|
||||
dst.heating.turbo = src["heating"]["turbo"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["heating"]["target"].isNull()) {
|
||||
double value = src["heating"]["target"].as<double>();
|
||||
|
||||
if (value > 0 && value < 100) {
|
||||
dst.heating.target = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["heating"]["hysteresis"].isNull()) {
|
||||
double value = src["heating"]["hysteresis"].as<double>();
|
||||
|
||||
if (value >= 0 && value <= 5) {
|
||||
dst.heating.hysteresis = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["heating"]["minTemp"].isNull()) {
|
||||
unsigned char value = src["heating"]["minTemp"].as<unsigned char>();
|
||||
|
||||
if (value >= vars.parameters.heatingMinTemp && value <= vars.parameters.heatingMaxTemp) {
|
||||
dst.heating.minTemp = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["heating"]["maxTemp"].isNull()) {
|
||||
unsigned char value = src["heating"]["maxTemp"].as<unsigned char>();
|
||||
|
||||
if (value >= vars.parameters.heatingMinTemp && value <= vars.parameters.heatingMaxTemp) {
|
||||
dst.heating.maxTemp = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["heating"]["maxModulation"].isNull()) {
|
||||
unsigned char value = src["heating"]["maxModulation"].as<unsigned char>();
|
||||
|
||||
if (value > 0 && value <= 100) {
|
||||
dst.heating.maxModulation = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// dhw
|
||||
if (src["dhw"]["enable"].is<bool>()) {
|
||||
dst.dhw.enable = src["dhw"]["enable"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["dhw"]["target"].isNull()) {
|
||||
unsigned char value = src["dhw"]["target"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 100) {
|
||||
dst.dhw.target = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["dhw"]["minTemp"].isNull()) {
|
||||
unsigned char value = src["dhw"]["minTemp"].as<unsigned char>();
|
||||
|
||||
if (value >= vars.parameters.dhwMinTemp && value <= vars.parameters.dhwMaxTemp) {
|
||||
dst.dhw.minTemp = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["dhw"]["maxTemp"].isNull()) {
|
||||
unsigned char value = src["dhw"]["maxTemp"].as<unsigned char>();
|
||||
|
||||
if (value >= vars.parameters.dhwMinTemp && value <= vars.parameters.dhwMaxTemp) {
|
||||
dst.dhw.maxTemp = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pid
|
||||
if (src["pid"]["enable"].is<bool>()) {
|
||||
dst.pid.enable = src["pid"]["enable"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["pid"]["p_factor"].isNull()) {
|
||||
double value = src["pid"]["p_factor"].as<double>();
|
||||
|
||||
if (value > 0 && value <= 1000) {
|
||||
dst.pid.p_factor = roundd(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["pid"]["i_factor"].isNull()) {
|
||||
double value = src["pid"]["i_factor"].as<double>();
|
||||
|
||||
if (value >= 0 && value <= 100) {
|
||||
dst.pid.i_factor = roundd(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["pid"]["d_factor"].isNull()) {
|
||||
double value = src["pid"]["d_factor"].as<double>();
|
||||
|
||||
if (value >= 0 && value <= 100000) {
|
||||
dst.pid.d_factor = roundd(value, 1);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["pid"]["dt"].isNull()) {
|
||||
unsigned short value = src["pid"]["dt"].as<unsigned short>();
|
||||
|
||||
if (value >= 30 && value <= 600) {
|
||||
dst.pid.dt = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["pid"]["maxTemp"].isNull()) {
|
||||
unsigned char value = src["pid"]["maxTemp"].as<unsigned char>();
|
||||
|
||||
if (value > 0 && value <= 100 && value > dst.pid.minTemp) {
|
||||
dst.pid.maxTemp = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["pid"]["minTemp"].isNull()) {
|
||||
unsigned char value = src["pid"]["minTemp"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 100 && value < dst.pid.maxTemp) {
|
||||
dst.pid.minTemp = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// equitherm
|
||||
if (src["equitherm"]["enable"].is<bool>()) {
|
||||
dst.equitherm.enable = src["equitherm"]["enable"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["equitherm"]["n_factor"].isNull()) {
|
||||
double value = src["equitherm"]["n_factor"].as<double>();
|
||||
|
||||
if (value > 0 && value <= 10) {
|
||||
dst.equitherm.n_factor = roundd(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["equitherm"]["k_factor"].isNull()) {
|
||||
double value = src["equitherm"]["k_factor"].as<double>();
|
||||
|
||||
if (value >= 0 && value <= 10) {
|
||||
dst.equitherm.k_factor = roundd(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["equitherm"]["t_factor"].isNull()) {
|
||||
double value = src["equitherm"]["t_factor"].as<double>();
|
||||
|
||||
if (value >= 0 && value <= 10) {
|
||||
dst.equitherm.t_factor = roundd(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sensors
|
||||
if (!src["sensors"]["outdoor"]["type"].isNull()) {
|
||||
unsigned char value = src["sensors"]["outdoor"]["type"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 2) {
|
||||
dst.sensors.outdoor.type = value;
|
||||
|
||||
if (dst.sensors.outdoor.type == 1) {
|
||||
dst.emergency.useEquitherm = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["sensors"]["outdoor"]["pin"].isNull()) {
|
||||
unsigned char value = src["sensors"]["outdoor"]["pin"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 50) {
|
||||
dst.sensors.outdoor.pin = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["sensors"]["outdoor"]["offset"].isNull()) {
|
||||
double value = src["sensors"]["outdoor"]["offset"].as<double>();
|
||||
|
||||
if (value >= -10 && value <= 10) {
|
||||
dst.sensors.outdoor.offset = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["sensors"]["indoor"]["type"].isNull()) {
|
||||
unsigned char value = src["sensors"]["indoor"]["type"].as<unsigned char>();
|
||||
|
||||
if (value >= 1 && value <= 3) {
|
||||
dst.sensors.indoor.type = value;
|
||||
|
||||
if (dst.sensors.indoor.type == 1) {
|
||||
dst.emergency.usePid = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["sensors"]["indoor"]["pin"].isNull()) {
|
||||
unsigned char value = src["sensors"]["indoor"]["pin"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 50) {
|
||||
dst.sensors.indoor.pin = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_BLE
|
||||
if (!src["sensors"]["indoor"]["bleAddresss"].isNull()) {
|
||||
String value = src["sensors"]["indoor"]["bleAddresss"].as<String>();
|
||||
|
||||
if (value.length() < sizeof(dst.sensors.indoor.bleAddresss)) {
|
||||
strcpy(dst.sensors.indoor.bleAddresss, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!src["sensors"]["indoor"]["offset"].isNull()) {
|
||||
double value = src["sensors"]["indoor"]["offset"].as<double>();
|
||||
|
||||
if (value >= -10 && value <= 10) {
|
||||
dst.sensors.indoor.offset = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!safe) {
|
||||
// external pump
|
||||
if (src["externalPump"]["use"].is<bool>()) {
|
||||
dst.externalPump.use = src["externalPump"]["use"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["externalPump"]["pin"].isNull()) {
|
||||
unsigned char value = src["externalPump"]["pin"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 50) {
|
||||
dst.externalPump.pin = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["externalPump"]["postCirculationTime"].isNull()) {
|
||||
unsigned short value = src["externalPump"]["postCirculationTime"].as<unsigned short>();
|
||||
|
||||
if (value >= 0 && value <= 120) {
|
||||
dst.externalPump.postCirculationTime = value * 60;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["externalPump"]["antiStuckInterval"].isNull()) {
|
||||
unsigned int value = src["externalPump"]["antiStuckInterval"].as<unsigned int>();
|
||||
|
||||
if (value >= 0 && value <= 366) {
|
||||
dst.externalPump.antiStuckInterval = value * 86400;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["externalPump"]["antiStuckTime"].isNull()) {
|
||||
unsigned short value = src["externalPump"]["antiStuckTime"].as<unsigned short>();
|
||||
|
||||
if (value >= 0 && value <= 20) {
|
||||
dst.externalPump.antiStuckTime = value * 60;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool safeJsonToSettings(const JsonVariantConst src, Settings& dst) {
|
||||
return jsonToSettings(src, dst, true);
|
||||
}
|
||||
|
||||
void varsToJson(const Variables& src, JsonVariant dst) {
|
||||
dst["tuning"]["enable"] = src.tuning.enable;
|
||||
dst["tuning"]["regulator"] = src.tuning.regulator;
|
||||
|
||||
dst["states"]["otStatus"] = src.states.otStatus;
|
||||
dst["states"]["emergency"] = src.states.emergency;
|
||||
dst["states"]["heating"] = src.states.heating;
|
||||
dst["states"]["dhw"] = src.states.dhw;
|
||||
dst["states"]["flame"] = src.states.flame;
|
||||
dst["states"]["fault"] = src.states.fault;
|
||||
dst["states"]["diagnostic"] = src.states.diagnostic;
|
||||
dst["states"]["externalPump"] = src.states.externalPump;
|
||||
|
||||
dst["sensors"]["modulation"] = roundd(src.sensors.modulation, 2);
|
||||
dst["sensors"]["pressure"] = roundd(src.sensors.pressure, 2);
|
||||
dst["sensors"]["dhwFlowRate"] = src.sensors.dhwFlowRate;
|
||||
dst["sensors"]["faultCode"] = src.sensors.faultCode;
|
||||
dst["sensors"]["rssi"] = src.sensors.rssi;
|
||||
dst["sensors"]["uptime"] = millis() / 1000ul;
|
||||
|
||||
dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2);
|
||||
dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2);
|
||||
dst["temperatures"]["heating"] = roundd(src.temperatures.heating, 2);
|
||||
dst["temperatures"]["dhw"] = roundd(src.temperatures.dhw, 2);
|
||||
|
||||
dst["parameters"]["heatingEnabled"] = src.parameters.heatingEnabled;
|
||||
dst["parameters"]["heatingMinTemp"] = src.parameters.heatingMinTemp;
|
||||
dst["parameters"]["heatingMaxTemp"] = src.parameters.heatingMaxTemp;
|
||||
dst["parameters"]["heatingSetpoint"] = src.parameters.heatingSetpoint;
|
||||
dst["parameters"]["dhwMinTemp"] = src.parameters.dhwMinTemp;
|
||||
dst["parameters"]["dhwMaxTemp"] = src.parameters.dhwMaxTemp;
|
||||
|
||||
//dst.shrinkToFit();
|
||||
}
|
||||
|
||||
bool jsonToVars(const JsonVariantConst src, Variables& dst) {
|
||||
bool changed = false;
|
||||
|
||||
// tuning
|
||||
if (src["tuning"]["enable"].is<bool>()) {
|
||||
dst.tuning.enable = src["tuning"]["enable"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["tuning"]["regulator"].isNull()) {
|
||||
unsigned char value = src["tuning"]["regulator"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 1) {
|
||||
dst.tuning.regulator = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// temperatures
|
||||
if (!src["temperatures"]["indoor"].isNull()) {
|
||||
double value = src["temperatures"]["indoor"].as<double>();
|
||||
|
||||
if (settings.sensors.indoor.type == 1 && value > -100 && value < 100) {
|
||||
dst.temperatures.indoor = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["temperatures"]["outdoor"].isNull()) {
|
||||
double value = src["temperatures"]["outdoor"].as<double>();
|
||||
|
||||
if (settings.sensors.outdoor.type == 1 && value > -100 && value < 100) {
|
||||
dst.temperatures.outdoor = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// actions
|
||||
if (src["actions"]["restart"].is<bool>() && src["actions"]["restart"].as<bool>()) {
|
||||
dst.actions.restart = true;
|
||||
}
|
||||
|
||||
if (src["actions"]["resetFault"].is<bool>() && src["actions"]["resetFault"].as<bool>()) {
|
||||
dst.actions.resetFault = true;
|
||||
}
|
||||
|
||||
if (src["actions"]["resetDiagnostic"].is<bool>() && src["actions"]["resetDiagnostic"].as<bool>()) {
|
||||
dst.actions.resetDiagnostic = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
Reference in New Issue
Block a user