From 8a4b598161c04da2510aeb440dfd89680bcf2908 Mon Sep 17 00:00:00 2001 From: Yurii Date: Sun, 26 Nov 2023 00:17:47 +0300 Subject: [PATCH] many changes * migrate from jandrassy/TelnetStream to lennarthennigs/ESP Telnet * ability to turn on/off output logs to telnet and serial * memory optimization * added OT parameter DHW blocking * changed algorithm for setting OpenThermMessageID::MConfigMMemberIDcode * refactoring --- README.md | 6 +-- platformio.ini | 13 ++++-- src/MainTask.h | 31 ++++++++----- src/MqttTask.h | 28 ++++++------ src/OpenThermTask.h | 104 ++++++++++++++++++++++++++---------------- src/RegulatorTask.h | 31 +++++++------ src/SensorsTask.h | 17 +++---- src/Settings.h | 1 + src/WifiManagerTask.h | 29 +++++++++--- src/defines.h | 4 ++ src/main.cpp | 42 +++++++++-------- 11 files changed, 188 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index d8389ab..100af53 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ | Boiler | Master Member ID | Notes | | --- | --- | --- | | BAXI ECO Nova | default | Pressure sensor not supported, modulation level not stable | -| BAXI Ampera | 1028 | Pressure sensor not supported, only heating (DHW not tested) | +| BAXI Ampera | 4 | Pressure sensor not supported, only heating (DHW not tested) | | [Remeha Calenta Ace 40C](https://github.com/Laxilef/OTGateway/issues/1#issuecomment-1726081554) | default | - | | [Baxi Nuvola DUO-TEC HT 16](https://github.com/Laxilef/OTGateway/issues/3#issuecomment-1751061488) | default | - | | [AEG GBA124](https://github.com/Laxilef/OTGateway/issues/3#issuecomment-1765857609) | default | Pressure sensor not supported | @@ -220,11 +220,11 @@ In Google you can find instructions for tuning the PID controller. ## Dependencies - [ESP8266Scheduler](https://github.com/nrwiersma/ESP8266Scheduler) (for ESP8266) - [ESP32Scheduler](https://github.com/laxilef/ESP32Scheduler) (for ESP32) -- [NTPClient](https://github.com/arduino-libraries/NTPClient) + - [ArduinoJson](https://github.com/bblanchon/ArduinoJson) - [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library) - [PubSubClient](https://github.com/knolleary/pubsubclient) -- [TelnetStream](https://github.com/jandrassy/TelnetStream) +- [ESPTelnet](https://github.com/LennartHennigs/ESPTelnet) - [EEManager](https://github.com/GyverLibs/EEManager) - [GyverPID](https://github.com/GyverLibs/GyverPID) - [GyverBlinker](https://github.com/GyverLibs/GyverBlinker) diff --git a/platformio.ini b/platformio.ini index 34c40b0..ba95843 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,16 +11,16 @@ [env] framework = arduino lib_deps = - arduino-libraries/NTPClient@^3.2.1 + ;arduino-libraries/NTPClient@^3.2.1 bblanchon/ArduinoJson@^6.20.0 ihormelnyk/OpenTherm Library@^1.1.4 knolleary/PubSubClient@^2.8 - jandrassy/TelnetStream@^1.2.4 + lennarthennigs/ESP Telnet@^2.1.2 gyverlibs/EEManager@^2.0 gyverlibs/GyverPID@^3.3 gyverlibs/GyverBlinker@^1.0 milesburton/DallasTemperature@^3.11.0 - laxilef/TinyLogger@^1.0.2 + laxilef/TinyLogger@^1.0.3 https://github.com/Laxilef/WiFiManager/archive/refs/heads/patch-1.zip ;https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2 build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -mtext-section-literals @@ -66,6 +66,7 @@ build_flags = -D SENSOR_INDOOR_PIN_DEFAULT=14 -D LED_STATUS_PIN=13 -D LED_OT_RX_PIN=15 + -D USE_SERIAL=0 -D USE_TELNET=1 [env:d1_mini_lite] @@ -82,6 +83,7 @@ build_flags = -D SENSOR_INDOOR_PIN_DEFAULT=14 -D LED_STATUS_PIN=13 -D LED_OT_RX_PIN=15 + -D USE_SERIAL=0 -D USE_TELNET=1 [env:d1_mini_pro] @@ -98,6 +100,7 @@ build_flags = -D SENSOR_INDOOR_PIN_DEFAULT=14 -D LED_STATUS_PIN=13 -D LED_OT_RX_PIN=15 + -D USE_SERIAL=0 -D USE_TELNET=1 [env:s2_mini] @@ -114,6 +117,7 @@ build_flags = -D SENSOR_INDOOR_PIN_DEFAULT=7 -D LED_STATUS_PIN=11 -D LED_OT_RX_PIN=12 + -D USE_SERIAL=0 -D USE_TELNET=1 [env:s3_mini] @@ -130,6 +134,7 @@ build_flags = -D SENSOR_INDOOR_PIN_DEFAULT=12 -D LED_STATUS_PIN=11 -D LED_OT_RX_PIN=10 + -D USE_SERIAL=0 -D USE_TELNET=1 [env:c3_mini] @@ -146,6 +151,7 @@ build_flags = -D SENSOR_INDOOR_PIN_DEFAULT=1 -D LED_STATUS_PIN=4 -D LED_OT_RX_PIN=5 + -D USE_SERIAL=0 -D USE_TELNET=1 [env:nodemcu_32s] @@ -162,6 +168,7 @@ build_flags = -D SENSOR_INDOOR_PIN_DEFAULT=13 -D LED_STATUS_PIN=2 ; 18 -D LED_OT_RX_PIN=19 + -D USE_SERIAL=0 -D USE_TELNET=1 ;-D WOKWI=1 ;-D USE_TELNET=0 diff --git a/src/MainTask.h b/src/MainTask.h index 2ade73e..3ca854d 100644 --- a/src/MainTask.h +++ b/src/MainTask.h @@ -3,6 +3,11 @@ extern MqttTask* tMqtt; extern SensorsTask* tSensors; extern OpenThermTask* tOt; +extern EEManager eeSettings; +#if USE_TELNET + extern ESPTelnetStream TelnetStream; +#endif + class MainTask : public Task { public: @@ -42,11 +47,15 @@ protected: void loop() { if (eeSettings.tick()) { - Log.sinfoln("MAIN", "Settings updated (EEPROM)"); + Log.sinfoln("MAIN", PSTR("Settings updated (EEPROM)")); } + #if USE_TELNET + TelnetStream.loop(); + #endif + if (vars.actions.restart) { - Log.sinfoln("MAIN", "Restart signal received. Restart after 10 sec."); + Log.sinfoln("MAIN", PSTR("Restart signal received. Restart after 10 sec.")); eeSettings.updateNow(); restartSignalTime = millis(); vars.actions.restart = false; @@ -83,7 +92,7 @@ protected: if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) { vars.states.emergency = true; - Log.sinfoln("MAIN", "Emergency mode enabled"); + Log.sinfoln("MAIN", PSTR("Emergency mode enabled")); } } } @@ -98,15 +107,15 @@ protected: #endif heap(); - #if USE_TELNET - yield(); + // anti memory leak + for (Stream* stream : Log.getStreams()) { + stream->flush(); - // anti memory leak - TelnetStream.flush(); - while (TelnetStream.available() > 0) { - TelnetStream.read(); + while (stream->available()) { + stream->read(); + yield(); } - #endif + } if (restartSignalTime > 0 && millis() - restartSignalTime > 10000) { restartSignalTime = 0; @@ -128,7 +137,7 @@ protected: } if (millis() - lastHeapInfo > 60000 || minFreeHeapSizeDiff > 0) { - Log.sverboseln("MAIN", "Free heap size: %u of %u bytes, min: %u bytes (diff: %u bytes)", freeHeapSize, heapSize, minFreeHeapSize, minFreeHeapSizeDiff); + Log.sverboseln("MAIN", PSTR("Free heap size: %u of %u bytes, min: %u bytes (diff: %u bytes)"), freeHeapSize, heapSize, minFreeHeapSize, minFreeHeapSizeDiff); lastHeapInfo = millis(); } } diff --git a/src/MqttTask.h b/src/MqttTask.h index 8f4c96b..f5e4f6b 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -24,7 +24,7 @@ protected: } void setup() { - Log.sinfoln("MQTT", "Started"); + Log.sinfoln("MQTT", PSTR("Started")); client.setCallback(__callback); client.setBufferSize(1024); @@ -39,11 +39,11 @@ protected: void loop() { if (!client.connected() && millis() - lastReconnectAttempt >= MQTT_RECONNECT_INTERVAL) { - Log.sinfoln("MQTT", "Not connected, state: %i, connecting to server %s...", client.state(), settings.mqtt.server); + Log.sinfoln("MQTT", PSTR("Not connected, state: %i, connecting to server %s..."), client.state(), settings.mqtt.server); client.setServer(settings.mqtt.server, settings.mqtt.port); if (client.connect(settings.hostname, settings.mqtt.user, settings.mqtt.password)) { - Log.sinfoln("MQTT", "Connected"); + Log.sinfoln("MQTT", PSTR("Connected")); client.subscribe(getTopicPath("settings/set").c_str()); client.subscribe(getTopicPath("state/set").c_str()); @@ -54,7 +54,7 @@ protected: lastReconnectAttempt = 0; } else { - Log.swarningln("MQTT", "Failed to connect to server"); + Log.swarningln("MQTT", PSTR("Failed to connect to server")); if (settings.emergency.enable && !vars.states.emergency) { if (firstFailConnect == 0) { @@ -63,7 +63,7 @@ protected: if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) { vars.states.emergency = true; - Log.sinfoln("MQTT", "Emergency mode enabled"); + Log.sinfoln("MQTT", PSTR("Emergency mode enabled")); } } @@ -76,7 +76,7 @@ protected: if (vars.states.emergency) { vars.states.emergency = false; - Log.sinfoln("MQTT", "Emergency mode disabled"); + Log.sinfoln("MQTT", PSTR("Emergency mode disabled")); } client.loop(); @@ -630,16 +630,16 @@ protected: } if (settings.debug) { - Log.strace("MQTT.MSG", "Topic: %s\r\n> ", topic); + Log.strace("MQTT.MSG", PSTR("Topic: %s\r\n> "), topic); for (unsigned int i = 0; i < length; i++) { if ( payload[i] == 10 ) { - Log.print("\r\n> "); + Log.print(PSTR("\r\n> ")); } else { Log.print((char) payload[i]); } } - Log.print("\r\n\n"); + Log.print(PSTR("\r\n\n")); for (Stream* stream : Log.getStreams()) { stream->flush(); @@ -654,22 +654,22 @@ protected: case DeserializationError::EmptyInput: case DeserializationError::IncompleteInput: case DeserializationError::InvalidInput: - errMsg = "invalid input"; + errMsg = PSTR("invalid input"); break; case DeserializationError::NoMemory: - errMsg = "no memory"; + errMsg = PSTR("no memory"); break; case DeserializationError::TooDeep: - errMsg = "too deep"; + errMsg = PSTR("too deep"); break; default: - errMsg = "failed"; + errMsg = PSTR("failed"); break; } - Log.swarningln("MQTT.MSG", "No deserialization: %s", errMsg); + Log.swarningln("MQTT.MSG", PSTR("No deserialization: %s"), errMsg); return; } diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index a8b51a1..3162210 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -2,6 +2,8 @@ #include CustomOpenTherm* ot; +extern EEManager eeSettings; + class OpenThermTask : public Task { public: @@ -46,11 +48,11 @@ protected: unsigned long localResponse; if (setMasterMemberIdCode()) { - Log.straceln("OT", "Slave member id code: %u", vars.parameters.slaveMemberIdCode); - Log.straceln("OT", "Master member id code: %u", settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode); + Log.straceln("OT", PSTR("Slave member id code: %u"), vars.parameters.slaveMemberIdCode); + Log.straceln("OT", PSTR("Master member id code: %u"), settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode); } else { - Log.swarningln("OT", "Slave member id failed"); + Log.swarningln("OT", PSTR("Slave member id failed")); } bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && pump && isReady(); @@ -69,11 +71,11 @@ protected: false, heatingCh2Enabled, settings.opentherm.summerWinterMode, - false + settings.opentherm.dhwBlocking ); if (!ot->isValidResponse(localResponse)) { - Log.swarningln("OT", "Invalid response after setBoilerStatus: %s", ot->statusToString(ot->getLastResponseStatus())); + Log.swarningln("OT", PSTR("Invalid response after setBoilerStatus: %s"), ot->statusToString(ot->getLastResponseStatus())); return; } @@ -100,8 +102,8 @@ protected: updateSlaveParameters(); updateMasterParameters(); - Log.straceln("OT", "Master type: %u, version: %u", vars.parameters.masterType, vars.parameters.masterVersion); - Log.straceln("OT", "Slave type: %u, version: %u", vars.parameters.slaveType, vars.parameters.slaveVersion); + Log.straceln("OT", PSTR("Master type: %u, version: %u"), vars.parameters.masterType, vars.parameters.masterVersion); + Log.straceln("OT", PSTR("Slave type: %u, version: %u"), vars.parameters.slaveType, vars.parameters.slaveVersion); // DHW min/max temp if (settings.opentherm.dhwPresent) { @@ -109,17 +111,17 @@ protected: if (settings.dhw.minTemp < vars.parameters.dhwMinTemp) { settings.dhw.minTemp = vars.parameters.dhwMinTemp; eeSettings.update(); - Log.snoticeln("OT.DHW", "Updated min temp: %d", settings.dhw.minTemp); + Log.snoticeln("OT.DHW", PSTR("Updated min temp: %d"), settings.dhw.minTemp); } if (settings.dhw.maxTemp > vars.parameters.dhwMaxTemp) { settings.dhw.maxTemp = vars.parameters.dhwMaxTemp; eeSettings.update(); - Log.snoticeln("OT.DHW", "Updated max temp: %d", settings.dhw.maxTemp); + Log.snoticeln("OT.DHW", PSTR("Updated max temp: %d"), settings.dhw.maxTemp); } } else { - Log.swarningln("OT.DHW", "Failed get min/max temp"); + Log.swarningln("OT.DHW", PSTR("Failed get min/max temp")); } if (settings.dhw.minTemp >= settings.dhw.maxTemp) { @@ -135,17 +137,17 @@ protected: if (settings.heating.minTemp < vars.parameters.heatingMinTemp) { settings.heating.minTemp = vars.parameters.heatingMinTemp; eeSettings.update(); - Log.snoticeln("OT.HEATING", "Updated min temp: %d", settings.heating.minTemp); + Log.snoticeln("OT.HEATING", PSTR("Updated min temp: %d"), settings.heating.minTemp); } if (settings.heating.maxTemp > vars.parameters.heatingMaxTemp) { settings.heating.maxTemp = vars.parameters.heatingMaxTemp; eeSettings.update(); - Log.snoticeln("OT.HEATING", "Updated max temp: %d", settings.heating.maxTemp); + Log.snoticeln("OT.HEATING", PSTR("Updated max temp: %d"), settings.heating.maxTemp); } } else { - Log.swarningln("OT.HEATING", "Failed get min/max temp"); + Log.swarningln("OT.HEATING", PSTR("Failed get min/max temp")); } if (settings.heating.minTemp >= settings.heating.maxTemp) { @@ -193,10 +195,10 @@ protected: if (vars.actions.resetFault) { if (vars.states.fault) { if (ot->sendBoilerReset()) { - Log.sinfoln("OT", "Boiler fault reset successfully"); + Log.sinfoln("OT", PSTR("Boiler fault reset successfully")); } else { - Log.serrorln("OT", "Boiler fault reset failed"); + Log.serrorln("OT", PSTR("Boiler fault reset failed")); } } @@ -208,10 +210,10 @@ protected: if (vars.actions.resetDiagnostic) { if (vars.states.diagnostic) { if (ot->sendServiceReset()) { - Log.sinfoln("OT", "Boiler diagnostic reset successfully"); + Log.sinfoln("OT", PSTR("Boiler diagnostic reset successfully")); } else { - Log.serrorln("OT", "Boiler diagnostic reset failed"); + Log.serrorln("OT", PSTR("Boiler diagnostic reset failed")); } } @@ -227,7 +229,7 @@ protected: newDhwTemp = constrain(newDhwTemp, settings.dhw.minTemp, settings.dhw.maxTemp); } - Log.sinfoln("OT.DHW", "Set temp = %u", newDhwTemp); + Log.sinfoln("OT.DHW", PSTR("Set temp = %u"), newDhwTemp); // Записываем заданную температуру ГВС if (ot->setDhwTemp(newDhwTemp)) { @@ -235,12 +237,12 @@ protected: dhwSetTempTime = millis(); } else { - Log.swarningln("OT.DHW", "Failed set temp"); + Log.swarningln("OT.DHW", PSTR("Failed set temp")); } if (settings.opentherm.dhwToCh2) { if (!ot->setHeatingCh2Temp(newDhwTemp)) { - Log.swarningln("OT.DHW", "Failed set ch2 temp"); + Log.swarningln("OT.DHW", PSTR("Failed set ch2 temp")); } } } @@ -248,7 +250,7 @@ protected: // // Температура отопления if (heatingEnabled && (needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001)) { - Log.sinfoln("OT.HEATING", "Set temp = %u", vars.parameters.heatingSetpoint); + Log.sinfoln("OT.HEATING", PSTR("Set temp = %u"), vars.parameters.heatingSetpoint); // Записываем заданную температуру if (ot->setHeatingCh1Temp(vars.parameters.heatingSetpoint)) { @@ -256,12 +258,12 @@ protected: heatingSetTempTime = millis(); } else { - Log.swarningln("OT.HEATING", "Failed set temp"); + Log.swarningln("OT.HEATING", PSTR("Failed set temp")); } if (settings.opentherm.heatingCh1ToCh2) { if (!ot->setHeatingCh2Temp(vars.parameters.heatingSetpoint)) { - Log.swarningln("OT.HEATING", "Failed set ch2 temp"); + Log.swarningln("OT.HEATING", PSTR("Failed set ch2 temp")); } } } @@ -343,7 +345,7 @@ protected: } void static printRequestDetail(OpenThermMessageID id, OpenThermResponseStatus status, unsigned long request, unsigned long response, byte attempt) { - Log.straceln("OT", "OT REQUEST ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s", id, request, response, attempt, ot->statusToString(status)); + Log.straceln("OT", PSTR("OT REQUEST ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s"), id, request, response, attempt, ot->statusToString(status)); } bool setMasterMemberIdCode() { @@ -361,29 +363,53 @@ protected: if (ot->isValidResponse(response)) { vars.parameters.slaveMemberIdCode = response & 0xFF; - /*uint8_t flags = (response & 0xFFFF) >> 8; - Log.strace( + uint8_t flags = (response & 0xFFFF) >> 8; + Log.straceln( "OT", - "MasterMemberIdCode:\r\n DHW present: %u\r\n Control type: %u\r\n Cooling configuration: %u\r\n DHW configuration: %u\r\n Pump control: %u\r\n CH2 present: %u\r\n Remote water filling function: %u\r\n Heat/cool mode control: %u\r\n Slave MemberID Code: %u\r\n", - flags & 0x01, - flags & 0x02, - flags & 0x04, - flags & 0x08, - flags & 0x10, - flags & 0x20, - flags & 0x40, - flags & 0x80, - response & 0xFF - );*/ + PSTR("MasterMemberIdCode:\r\n DHW present: %u\r\n Control type: %u\r\n Cooling configuration: %u\r\n DHW configuration: %u\r\n Pump control: %u\r\n CH2 present: %u\r\n Remote water filling function: %u\r\n Heat/cool mode control: %u\r\n Slave MemberID Code: %u\r\n Raw: %u"), + (bool) (flags & 0x01), + (bool) (flags & 0x02), + (bool) (flags & 0x04), + (bool) (flags & 0x08), + (bool) (flags & 0x10), + (bool) (flags & 0x20), + (bool) (flags & 0x40), + (bool) (flags & 0x80), + response & 0xFF, + response + ); - } else if (settings.opentherm.memberIdCode <= 0) { + }/* else if (settings.opentherm.memberIdCode <= 0) { + return false; + }*/ + + unsigned int responseId = response & 0xFF; + unsigned int responseFlags = (response & 0xFFFF) >> 8; + + unsigned int configId = settings.opentherm.memberIdCode & 0xFF; + unsigned int configFlags = (settings.opentherm.memberIdCode & 0xFFFF) >> 8; + + unsigned int request = 0; + if (configId || settings.opentherm.memberIdCode > 65535) { + request |= configId; + } else { + request |= responseId; + } + + if (configFlags || settings.opentherm.memberIdCode > 65535) { + request |= configFlags << 8; + } else { + request |= responseFlags << 8; + } + + if (!request) { return false; } response = ot->sendRequest(ot->buildRequest( OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, - settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode + request )); return ot->isValidResponse(response); diff --git a/src/RegulatorTask.h b/src/RegulatorTask.h index c174e12..0915870 100644 --- a/src/RegulatorTask.h +++ b/src/RegulatorTask.h @@ -6,6 +6,7 @@ Equitherm etRegulator; GyverPID pidRegulator(0, 0, 0); PIDtuner pidTuner; + class RegulatorTask : public LeanTask { public: RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {} @@ -33,7 +34,7 @@ protected: if (settings.heating.turbo) { settings.heating.turbo = false; - Log.sinfoln("REGULATOR", "Turbo mode auto disabled"); + Log.sinfoln("REGULATOR", PSTR("Turbo mode auto disabled")); } newTemp = getEmergencyModeTemp(); @@ -43,7 +44,7 @@ protected: if (settings.heating.turbo) { settings.heating.turbo = false; - Log.sinfoln("REGULATOR", "Turbo mode auto disabled"); + Log.sinfoln("REGULATOR", PSTR("Turbo mode auto disabled")); } newTemp = getTuningModeTemp(); @@ -57,7 +58,7 @@ protected: if (settings.heating.turbo && (fabs(settings.heating.target - vars.temperatures.indoor) < 1 || (settings.equitherm.enable && settings.pid.enable))) { settings.heating.turbo = false; - Log.sinfoln("REGULATOR", "Turbo mode auto disabled"); + Log.sinfoln("REGULATOR", PSTR("Turbo mode auto disabled")); } newTemp = getNormalModeTemp(); @@ -86,7 +87,7 @@ protected: prevEtResult = etResult; newTemp += etResult; - Log.sinfoln("REGULATOR.EQUITHERM", "New emergency result: %u (%f)", (int)round(etResult), etResult); + Log.sinfoln("REGULATOR.EQUITHERM", PSTR("New emergency result: %u (%f)"), (int) round(etResult), etResult); } else { newTemp += prevEtResult; @@ -105,11 +106,11 @@ protected: if (fabs(prevHeatingTarget - settings.heating.target) > 0.0001) { prevHeatingTarget = settings.heating.target; - Log.sinfoln("REGULATOR", "New target: %f", settings.heating.target); + Log.sinfoln("REGULATOR", PSTR("New target: %f"), settings.heating.target); if (settings.equitherm.enable && settings.pid.enable) { pidRegulator.integral = 0; - Log.sinfoln("REGULATOR.PID", "Integral sum has been reset"); + Log.sinfoln("REGULATOR.PID", PSTR("Integral sum has been reset")); } } @@ -121,7 +122,7 @@ protected: prevEtResult = etResult; newTemp += etResult; - Log.sinfoln("REGULATOR.EQUITHERM", "New result: %u (%f)", (int)round(etResult), etResult); + Log.sinfoln("REGULATOR.EQUITHERM", PSTR("New result: %u (%f)"), (int) round(etResult), etResult); } else { newTemp += prevEtResult; @@ -139,7 +140,7 @@ protected: prevPidResult = pidResult; newTemp += pidResult; - Log.sinfoln("REGULATOR.PID", "New result: %d (%f)", (int)round(pidResult), pidResult); + Log.sinfoln("REGULATOR.PID", PSTR("New result: %d (%f)"), (int) round(pidResult), pidResult); } else { newTemp += prevPidResult; @@ -168,7 +169,7 @@ protected: tunerInit = false; tunerRegulator = 0; tunerState = 0; - Log.sinfoln("REGULATOR.TUNING", "Stopped"); + Log.sinfoln("REGULATOR.TUNING", PSTR("Stopped")); } if (!vars.tuning.enable) { @@ -178,7 +179,7 @@ protected: if (vars.tuning.regulator == 0) { // @TODO дописать - Log.sinfoln("REGULATOR.TUNING.EQUITHERM", "Not implemented"); + Log.sinfoln("REGULATOR.TUNING.EQUITHERM", PSTR("Not implemented")); return 0; } else if (vars.tuning.regulator == 1) { @@ -188,7 +189,7 @@ protected: : settings.heating.target; if (tunerInit && pidTuner.getState() == 3) { - Log.sinfoln("REGULATOR.TUNING.PID", "Finished"); + Log.sinfoln("REGULATOR.TUNING.PID", PSTR("Finished")); for (Stream* stream : Log.getStreams()) { pidTuner.debugText(stream); } @@ -199,7 +200,7 @@ protected: tunerState = 0; if (pidTuner.getAccuracy() < 90) { - Log.swarningln("REGULATOR.TUNING.PID", "Bad result, try again..."); + Log.swarningln("REGULATOR.TUNING.PID", PSTR("Bad result, try again...")); } else { settings.pid.p_factor = pidTuner.getPID_p(); @@ -211,7 +212,7 @@ protected: } if (!tunerInit) { - Log.sinfoln("REGULATOR.TUNING.PID", "Start..."); + Log.sinfoln("REGULATOR.TUNING.PID", PSTR("Start...")); float step; if (vars.temperatures.indoor - vars.temperatures.outdoor > 10) { @@ -221,7 +222,7 @@ protected: } float startTemp = step; - Log.sinfoln("REGULATOR.TUNING.PID", "Started. Start value: %f, step: %f", startTemp, step); + Log.sinfoln("REGULATOR.TUNING.PID", PSTR("Started. Start value: %f, step: %f"), startTemp, step); pidTuner.setParameters(NORMAL, startTemp, step, 20 * 60 * 1000, 0.15, 60 * 1000, 10000); tunerInit = true; tunerRegulator = 1; @@ -231,7 +232,7 @@ protected: pidTuner.compute(); if (tunerState > 0 && pidTuner.getState() != tunerState) { - Log.sinfoln("REGULATOR.TUNING.PID", "Log:"); + Log.sinfoln("REGULATOR.TUNING.PID", PSTR("Log:")); for (Stream* stream : Log.getStreams()) { pidTuner.debugText(stream); } diff --git a/src/SensorsTask.h b/src/SensorsTask.h index 048b89e..8241aa1 100644 --- a/src/SensorsTask.h +++ b/src/SensorsTask.h @@ -1,6 +1,7 @@ #include #include + class SensorsTask : public LeanTask { public: SensorsTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {} @@ -59,7 +60,7 @@ protected: outdoorSensor->requestTemperatures(); startConversionTime = millis(); - Log.serrorln("SENSORS.OUTDOOR", "Could not read temperature data (no response)"); + Log.serrorln("SENSORS.OUTDOOR", PSTR("Could not read temperature data (no response)")); } if (!completed) { @@ -68,10 +69,10 @@ protected: float rawTemp = outdoorSensor->getTempCByIndex(0); if (rawTemp == DEVICE_DISCONNECTED_C) { - Log.serrorln("SENSORS.OUTDOOR", "Could not read temperature data (not connected)"); + Log.serrorln("SENSORS.OUTDOOR", PSTR("Could not read temperature data (not connected)")); } else { - Log.straceln("SENSORS.OUTDOOR", "Raw temp: %f", rawTemp); + Log.straceln("SENSORS.OUTDOOR", PSTR("Raw temp: %f"), rawTemp); if (emptyOutdoorTemp) { filteredOutdoorTemp = rawTemp; @@ -85,7 +86,7 @@ protected: if (fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099) { vars.temperatures.outdoor = filteredOutdoorTemp + settings.sensors.outdoor.offset; - Log.sinfoln("SENSORS.OUTDOOR", "New temp: %f", filteredOutdoorTemp); + Log.sinfoln("SENSORS.OUTDOOR", PSTR("New temp: %f"), filteredOutdoorTemp); } } @@ -116,7 +117,7 @@ protected: indoorSensor->requestTemperatures(); startConversionTime = millis(); - Log.serrorln("SENSORS.INDOOR", "Could not read temperature data (no response)"); + Log.serrorln("SENSORS.INDOOR", PSTR("Could not read temperature data (no response)")); } if (!completed) { @@ -125,10 +126,10 @@ protected: float rawTemp = indoorSensor->getTempCByIndex(0); if (rawTemp == DEVICE_DISCONNECTED_C) { - Log.serrorln("SENSORS.INDOOR", "Could not read temperature data (not connected)"); + Log.serrorln("SENSORS.INDOOR", PSTR("Could not read temperature data (not connected)")); } else { - Log.straceln("SENSORS.INDOOR", "Raw temp: %f", rawTemp); + Log.straceln("SENSORS.INDOOR", PSTR("Raw temp: %f"), rawTemp); if (emptyIndoorTemp) { filteredIndoorTemp = rawTemp; @@ -142,7 +143,7 @@ protected: if (fabs(vars.temperatures.indoor - filteredIndoorTemp) > 0.099) { vars.temperatures.indoor = filteredIndoorTemp + settings.sensors.indoor.offset; - Log.sinfoln("SENSORS.INDOOR", "New temp: %f", filteredIndoorTemp); + Log.sinfoln("SENSORS.INDOOR", PSTR("New temp: %f"), filteredIndoorTemp); } } diff --git a/src/Settings.h b/src/Settings.h index 2177ff7..9cf1b5a 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -11,6 +11,7 @@ struct Settings { bool heatingCh2Enabled = true; bool heatingCh1ToCh2 = false; bool dhwToCh2 = false; + bool dhwBlocking = false; } opentherm; struct { diff --git a/src/WifiManagerTask.h b/src/WifiManagerTask.h index 75845d3..2d4d634 100644 --- a/src/WifiManagerTask.h +++ b/src/WifiManagerTask.h @@ -3,7 +3,6 @@ #include #include - WiFiManager wm; WiFiManagerParameter* wmHostname; WiFiManagerParameter* wmMqttServer; @@ -20,12 +19,18 @@ CheckboxParameter* wmOtSummerWinterMode; CheckboxParameter* wmOtHeatingCh2Enabled; CheckboxParameter* wmOtHeatingCh1ToCh2; CheckboxParameter* wmOtDhwToCh2; +CheckboxParameter* wmOtDhwBlocking; UnsignedIntParameter* wmOutdoorSensorPin; UnsignedIntParameter* wmIndoorSensorPin; SeparatorParameter* wmSep1; SeparatorParameter* wmSep2; +extern EEManager eeSettings; +#if USE_TELNET + extern ESPTelnetStream TelnetStream; +#endif + class WifiManagerTask : public Task { public: @@ -109,6 +114,9 @@ protected: wmOtDhwToCh2 = new CheckboxParameter("ot_dhw_to_ch2", "Opentherm DHW to CH2", settings.opentherm.dhwToCh2); wm.addParameter(wmOtDhwToCh2); + wmOtDhwBlocking = new CheckboxParameter("ot_dhw_blocking", "Opentherm DHW blocking", settings.opentherm.dhwBlocking); + wm.addParameter(wmOtDhwBlocking); + wmSep2 = new SeparatorParameter(); wm.addParameter(wmSep2); @@ -150,7 +158,7 @@ protected: TelnetStream.stop(); #endif - Log.sinfoln("WIFI", "Disconnected"); + Log.sinfoln("WIFI", PSTR("Disconnected")); } else if (!connected && WiFi.status() == WL_CONNECTED) { connected = true; @@ -167,10 +175,10 @@ protected: } #if USE_TELNET - TelnetStream.begin(); + TelnetStream.begin(23, false); #endif - Log.sinfoln("WIFI", "Connected. IP: %s, RSSI: %d", WiFi.localIP().toString().c_str(), WiFi.RSSI()); + Log.sinfoln("WIFI", PSTR("Connected. IP: %s, RSSI: %d"), WiFi.localIP().toString().c_str(), WiFi.RSSI()); } #if defined(ESP8266) @@ -295,6 +303,11 @@ protected: } } + if (wmOtDhwBlocking->getCheckboxValue() != settings.opentherm.dhwBlocking) { + changed = true; + settings.opentherm.dhwBlocking = wmOtDhwBlocking->getCheckboxValue(); + } + if (wmOutdoorSensorPin->getValue() != settings.sensors.outdoor.pin) { changed = true; needRestart = true; @@ -317,7 +330,7 @@ protected: Log.sinfo( "WIFI", - "New settings:\r\n" + PSTR("New settings:\r\n" " Hostname: %s\r\n" " Mqtt server: %s:%d\r\n" " Mqtt user: %s\r\n" @@ -332,8 +345,9 @@ protected: " OT heating ch2 enabled: %d\r\n" " OT heating ch1 to ch2: %d\r\n" " OT DHW to ch2: %d\r\n" + " OT DHW blocking: %d\r\n" " Outdoor sensor pin: %d\r\n" - " Indoor sensor pin: %d\r\n", + " Indoor sensor pin: %d\r\n"), settings.hostname, settings.mqtt.server, settings.mqtt.port, @@ -349,11 +363,12 @@ protected: settings.opentherm.heatingCh2Enabled, settings.opentherm.heatingCh1ToCh2, settings.opentherm.dhwToCh2, + settings.opentherm.dhwBlocking, settings.sensors.outdoor.pin, settings.sensors.indoor.pin ); - eeSettings.updateNow(); + eeSettings.update(); } static void arpGratuitous() { diff --git a/src/defines.h b/src/defines.h index dd3087b..d6c256e 100644 --- a/src/defines.h +++ b/src/defines.h @@ -26,6 +26,10 @@ #define WM_DEBUG_MODE WM_DEBUG_NOTIFY #endif +#ifndef USE_SERIAL + #define USE_SERIAL true +#endif + #ifndef USE_TELNET #define USE_TELNET true #endif diff --git a/src/main.cpp b/src/main.cpp index dc14293..6f67fc7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,25 +1,24 @@ #include #include "defines.h" #include -#include #include #include #include "Settings.h" -EEManager eeSettings(settings, 60000); +#if USE_TELNET + #include "ESPTelnetStream.h" +#endif #if defined(ESP32) #include - #include - #include #elif defined(ESP8266) #include - #include - #include #elif #error Wrong board. Supported boards: esp8266, esp32 #endif +#include +#include #include "WifiManagerTask.h" #include "MqttTask.h" #include "OpenThermTask.h" @@ -27,6 +26,12 @@ EEManager eeSettings(settings, 60000); #include "RegulatorTask.h" #include "MainTask.h" +// Vars +EEManager eeSettings(settings, 60000); +#if USE_TELNET + ESPTelnetStream TelnetStream; +#endif + // Tasks WifiManagerTask* tWm; MqttTask* tMqtt; @@ -37,34 +42,35 @@ MainTask* tMain; void setup() { + Log.setLevel(TinyLogger::Level::VERBOSE); + #if USE_SERIAL + Serial.begin(115200); + Log.addStream(&Serial); + Serial.println("\n\n"); + #endif + #if USE_TELNET - TelnetStream.begin(); - Log.begin(&TelnetStream, TinyLogger::Level::VERBOSE); - delay(1000); - #else - Serial.begin(115200); - Log.begin(&Serial, TinyLogger::Level::VERBOSE); - Serial.println("\n\n"); + Log.addStream(&TelnetStream); #endif //Log.setNtpClient(&timeClient); EEPROM.begin(eeSettings.blockSize()); uint8_t eeSettingsResult = eeSettings.begin(0, 's'); if (eeSettingsResult == 0) { - Log.sinfoln("MAIN", "Settings loaded"); + Log.sinfoln("MAIN", PSTR("Settings loaded")); if (strcmp(SETTINGS_VALID_VALUE, settings.validationValue) != 0) { - Log.swarningln("MAIN", "Settings not valid, reset and restart..."); + Log.swarningln("MAIN", PSTR("Settings not valid, reset and restart...")); eeSettings.reset(); delay(1000); ESP.restart(); } } else if (eeSettingsResult == 1) { - Log.sinfoln("MAIN", "Settings NOT loaded, first start"); + Log.sinfoln("MAIN", PSTR("Settings NOT loaded, first start")); } else if (eeSettingsResult == 2) { - Log.serrorln("MAIN", "Settings NOT loaded (error)"); + Log.serrorln("MAIN", PSTR("Settings NOT loaded (error)")); } Log.setLevel(settings.debug ? TinyLogger::Level::VERBOSE : TinyLogger::Level::INFO); @@ -84,7 +90,7 @@ void setup() { tRegulator = new RegulatorTask(true, 10000); Scheduler.start(tRegulator); - tMain = new MainTask(true, 50); + tMain = new MainTask(true, 10); Scheduler.start(tMain); Scheduler.begin();