mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 02:34:29 +05:00
Use platformio and more updates
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
|
|||||||
BIN
assets/BOM.xlsx
Normal file
BIN
assets/BOM.xlsx
Normal file
Binary file not shown.
BIN
assets/equitherm calc.xlsx
Normal file
BIN
assets/equitherm calc.xlsx
Normal file
Binary file not shown.
Binary file not shown.
106
lib/CustomOpenTherm/CustomOpenTherm.h
Normal file
106
lib/CustomOpenTherm/CustomOpenTherm.h
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <OpenTherm.h>
|
||||||
|
|
||||||
|
extern SchedulerClass Scheduler;
|
||||||
|
|
||||||
|
class CustomOpenTherm : public OpenTherm {
|
||||||
|
private:
|
||||||
|
unsigned long send_ts = millis();
|
||||||
|
void(*handleSendRequestCallback)(unsigned long, unsigned long, OpenThermResponseStatus status, byte attempt);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false) : OpenTherm(inPin, outPin, isSlave) {}
|
||||||
|
void setHandleSendRequestCallback(void(*handleSendRequestCallback)(unsigned long, unsigned long, OpenThermResponseStatus status, byte attempt)) {
|
||||||
|
this->handleSendRequestCallback = handleSendRequestCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long sendRequest(unsigned long request, byte attempts = 5, byte _attempt = 0) {
|
||||||
|
_attempt++;
|
||||||
|
while (send_ts > 0 && millis() - send_ts < 200) {
|
||||||
|
Scheduler.yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long _response;
|
||||||
|
if (!sendRequestAync(request)) {
|
||||||
|
_response = 0;
|
||||||
|
} else {
|
||||||
|
while (!isReady()) {
|
||||||
|
Scheduler.yield();
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
|
||||||
|
_response = getLastResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handleSendRequestCallback != NULL) {
|
||||||
|
handleSendRequestCallback(request, _response, getLastResponseStatus(), _attempt);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_ts = millis();
|
||||||
|
if (getLastResponseStatus() == OpenThermResponseStatus::SUCCESS || _attempt >= attempts) {
|
||||||
|
return _response;
|
||||||
|
} else {
|
||||||
|
return sendRequest(request, attempts, _attempt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long setBoilerStatus(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2, bool summerWinterMode, bool dhwBlocking) {
|
||||||
|
return sendRequest(buildSetBoilerStatusRequest(enableCentralHeating, enableHotWater, enableCooling, enableOutsideTemperatureCompensation, enableCentralHeating2, summerWinterMode, dhwBlocking));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2, bool summerWinterMode, bool dhwBlocking) {
|
||||||
|
unsigned int data = enableCentralHeating | (enableHotWater << 1) | (enableCooling << 2) | (enableOutsideTemperatureCompensation << 3) | (enableCentralHeating2 << 4) | (summerWinterMode << 5) | (dhwBlocking << 6);
|
||||||
|
data <<= 8;
|
||||||
|
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Status, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setBoilerTemperature(float temperature) {
|
||||||
|
unsigned int data = temperatureToData(temperature);
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data));
|
||||||
|
return isValidResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setBoilerTemperature2(float temperature) {
|
||||||
|
unsigned int data = temperatureToData(temperature);
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TsetCH2, data));
|
||||||
|
return isValidResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendBoilerReset() {
|
||||||
|
unsigned int data = 1;
|
||||||
|
data <<= 8;
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::Command, data));
|
||||||
|
return isValidResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendServiceReset() {
|
||||||
|
unsigned int data = 10;
|
||||||
|
data <<= 8;
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::Command, data));
|
||||||
|
return isValidResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendWaterFilling() {
|
||||||
|
unsigned int data = 2;
|
||||||
|
data <<= 8;
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::Command, data));
|
||||||
|
return isValidResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// converters
|
||||||
|
float f88(unsigned long response) {
|
||||||
|
const byte valueLB = response & 0xFF;
|
||||||
|
const byte valueHB = (response >> 8) & 0xFF;
|
||||||
|
|
||||||
|
float value = (int8_t) valueHB;
|
||||||
|
return value + (float)valueLB / 256.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t s16(unsigned long response) {
|
||||||
|
const byte valueLB = response & 0xFF;
|
||||||
|
const byte valueHB = (response >> 8) & 0xFF;
|
||||||
|
|
||||||
|
int16_t value = valueHB;
|
||||||
|
return ((value << 8) + valueLB);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
#pragma once
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#if defined(EQUITHERM_INTEGER)
|
#if defined(EQUITHERM_INTEGER)
|
||||||
@@ -11,6 +10,13 @@ typedef float datatype;
|
|||||||
|
|
||||||
class Equitherm {
|
class Equitherm {
|
||||||
public:
|
public:
|
||||||
|
datatype targetTemp = 0;
|
||||||
|
datatype indoorTemp = 0;
|
||||||
|
datatype outdoorTemp = 0;
|
||||||
|
float Kn = 0.0;
|
||||||
|
float Kk = 0.0;
|
||||||
|
float Kt = 0.0;
|
||||||
|
|
||||||
Equitherm() {}
|
Equitherm() {}
|
||||||
|
|
||||||
// kn, kk, kt
|
// kn, kk, kt
|
||||||
@@ -26,13 +32,6 @@ public:
|
|||||||
_maxOut = max_output;
|
_maxOut = max_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
datatype targetTemp = 0;
|
|
||||||
datatype indoorTemp = 0;
|
|
||||||
datatype outdoorTemp = 0;
|
|
||||||
float Kn = 0.0;
|
|
||||||
float Kk = 0.0;
|
|
||||||
float Kt = 0.0;
|
|
||||||
|
|
||||||
// возвращает новое значение при вызове
|
// возвращает новое значение при вызове
|
||||||
datatype getResult() {
|
datatype getResult() {
|
||||||
datatype output = getResultN() + getResultK() + getResultT();
|
datatype output = getResultN() + getResultK() + getResultT();
|
||||||
@@ -40,6 +39,9 @@ public:
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _minOut = 20, _maxOut = 90;
|
||||||
|
|
||||||
// температура контура отопления в зависимости от наружной температуры
|
// температура контура отопления в зависимости от наружной температуры
|
||||||
datatype getResultN() {
|
datatype getResultN() {
|
||||||
float a = (-0.21 * Kn) - 0.06; // a = -0,21k — 0,06
|
float a = (-0.21 * Kn) - 0.06; // a = -0,21k — 0,06
|
||||||
@@ -56,9 +58,6 @@ public:
|
|||||||
|
|
||||||
// Расчет поправки (ошибки) термостата
|
// Расчет поправки (ошибки) термостата
|
||||||
datatype getResultT() {
|
datatype getResultT() {
|
||||||
return (targetTemp - indoorTemp) * Kt;
|
return constrain((targetTemp - indoorTemp), -2, 2) * Kt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
int _minOut = 20, _maxOut = 90;
|
|
||||||
};
|
};
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is needed by the Arduino IDE because the ino file needs to be named as the directory name.
|
|
||||||
Don't worry, the Arduino compiler will "merge" all files, including src/main.cpp
|
|
||||||
*/
|
|
||||||
28
platformio.ini
Normal file
28
platformio.ini
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:d1_mini_pro]
|
||||||
|
platform = espressif8266
|
||||||
|
board = d1_mini_pro
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
nrwiersma/ESP8266Scheduler@^1.0
|
||||||
|
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
|
||||||
|
gyverlibs/EEManager@^2.0
|
||||||
|
gyverlibs/GyverPID@^3.3
|
||||||
|
gyverlibs/microDS18B20@^3.10
|
||||||
|
https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2
|
||||||
|
build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
|
||||||
|
upload_speed = 921600
|
||||||
|
monitor_speed = 115200
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,22 @@
|
|||||||
extern MqttTask* tMqtt;
|
extern MqttTask* tMqtt;
|
||||||
extern SensorsTask* tSensors;
|
extern SensorsTask* tSensors;
|
||||||
|
extern OpenThermTask* tOt;
|
||||||
|
|
||||||
class MainTask: public Task {
|
class MainTask: public Task {
|
||||||
public:
|
public:
|
||||||
MainTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
|
MainTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
unsigned long lastHeapInfo = 0;
|
||||||
|
unsigned long firstFailConnect = 0;
|
||||||
|
unsigned short minFreeHeapSize = 65535;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(LED_STATUS_PIN, OUTPUT);
|
||||||
|
//pinMode(LED_OT_RX_PIN, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
static unsigned long lastHeapInfo = 0;
|
|
||||||
static unsigned short minFreeHeapSize = 65535;
|
|
||||||
|
|
||||||
if (eeSettings.tick()) {
|
if (eeSettings.tick()) {
|
||||||
INFO("Settings updated (EEPROM)");
|
INFO("Settings updated (EEPROM)");
|
||||||
}
|
}
|
||||||
@@ -20,12 +26,27 @@ protected:
|
|||||||
tMqtt->enable();
|
tMqtt->enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( firstFailConnect != 0 ) {
|
||||||
|
firstFailConnect = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vars.states.rssi = WiFi.RSSI();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (tMqtt->isEnabled()) {
|
if (tMqtt->isEnabled()) {
|
||||||
tMqtt->disable();
|
tMqtt->disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.emergency.enable && !vars.states.emergency) {
|
||||||
|
if (firstFailConnect == 0) {
|
||||||
|
firstFailConnect = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) {
|
||||||
vars.states.emergency = true;
|
vars.states.emergency = true;
|
||||||
|
INFO("Emergency mode enabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tSensors->isEnabled() && settings.outdoorTempSource == 2) {
|
if (!tSensors->isEnabled() && settings.outdoorTempSource == 2) {
|
||||||
@@ -34,6 +55,12 @@ protected:
|
|||||||
tSensors->disable();
|
tSensors->disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tOt->isEnabled() && settings.opentherm.inPin > 0 && settings.opentherm.outPin > 0 && settings.opentherm.inPin != settings.opentherm.outPin) {
|
||||||
|
tOt->enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ledStatus();
|
||||||
|
|
||||||
#ifdef USE_TELNET
|
#ifdef USE_TELNET
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
@@ -58,4 +85,57 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ledStatus() {
|
||||||
|
static byte blinkLeft = 0;
|
||||||
|
static bool ledOn = false;
|
||||||
|
static unsigned long changeTime = 0;
|
||||||
|
|
||||||
|
byte errNo = 0;
|
||||||
|
if (!vars.states.otStatus) {
|
||||||
|
errNo = 1;
|
||||||
|
} else if (vars.states.fault) {
|
||||||
|
errNo = 2;
|
||||||
|
} else if (vars.states.emergency) {
|
||||||
|
errNo = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errNo == 0) {
|
||||||
|
if (!ledOn) {
|
||||||
|
digitalWrite(LED_STATUS_PIN, true);
|
||||||
|
ledOn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blinkLeft > 0) {
|
||||||
|
blinkLeft = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (blinkLeft == 0) {
|
||||||
|
if (ledOn) {
|
||||||
|
digitalWrite(LED_STATUS_PIN, false);
|
||||||
|
ledOn = false;
|
||||||
|
changeTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() - changeTime >= 3000) {
|
||||||
|
blinkLeft = errNo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blinkLeft > 0 && millis() - changeTime >= 500) {
|
||||||
|
if (ledOn) {
|
||||||
|
digitalWrite(LED_STATUS_PIN, false);
|
||||||
|
ledOn = false;
|
||||||
|
blinkLeft--;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
digitalWrite(LED_STATUS_PIN, true);
|
||||||
|
ledOn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeTime = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@@ -14,9 +14,11 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
unsigned long lastReconnectAttempt = 0;
|
unsigned long lastReconnectAttempt = 0;
|
||||||
unsigned short int reconnectAttempts = 0;
|
unsigned long firstFailConnect = 0;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
DEBUG("[MQTT] Started");
|
||||||
|
|
||||||
client.setServer(settings.mqtt.server, settings.mqtt.port);
|
client.setServer(settings.mqtt.server, settings.mqtt.port);
|
||||||
client.setCallback(__callback);
|
client.setCallback(__callback);
|
||||||
haHelper.setPrefix(settings.mqtt.prefix);
|
haHelper.setPrefix(settings.mqtt.prefix);
|
||||||
@@ -38,16 +40,22 @@ protected:
|
|||||||
publishHaEntities();
|
publishHaEntities();
|
||||||
publishNonStaticHaEntities(true);
|
publishNonStaticHaEntities(true);
|
||||||
|
|
||||||
reconnectAttempts = 0;
|
firstFailConnect = 0;
|
||||||
lastReconnectAttempt = 0;
|
lastReconnectAttempt = 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
INFO("Failed to connect to MQTT server\n");
|
INFO("Failed to connect to MQTT server\n");
|
||||||
|
|
||||||
if (!vars.states.emergency && ++reconnectAttempts >= EMERGENCY_TRESHOLD) {
|
if (settings.emergency.enable && !vars.states.emergency) {
|
||||||
|
if (firstFailConnect == 0) {
|
||||||
|
firstFailConnect = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) {
|
||||||
vars.states.emergency = true;
|
vars.states.emergency = true;
|
||||||
INFO("Emergency mode enabled");
|
INFO("Emergency mode enabled");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
forceARP();
|
forceARP();
|
||||||
lastReconnectAttempt = millis();
|
lastReconnectAttempt = millis();
|
||||||
@@ -117,6 +125,11 @@ protected:
|
|||||||
flag = true;
|
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<float>() || doc["heating"]["target"].is<int>())) {
|
if (!doc["heating"]["target"].isNull() && (doc["heating"]["target"].is<float>() || doc["heating"]["target"].is<int>())) {
|
||||||
settings.heating.target = round(doc["heating"]["target"].as<float>() * 10) / 10;
|
settings.heating.target = round(doc["heating"]["target"].as<float>() * 10) / 10;
|
||||||
flag = true;
|
flag = true;
|
||||||
@@ -272,6 +285,7 @@ protected:
|
|||||||
|
|
||||||
// heating
|
// heating
|
||||||
haHelper.publishSwitchHeating(false);
|
haHelper.publishSwitchHeating(false);
|
||||||
|
haHelper.publishSwitchHeatingTurbo();
|
||||||
//haHelper.publishNumberHeatingTarget(false);
|
//haHelper.publishNumberHeatingTarget(false);
|
||||||
haHelper.publishNumberHeatingHysteresis();
|
haHelper.publishNumberHeatingHysteresis();
|
||||||
haHelper.publishSensorHeatingSetpoint(false);
|
haHelper.publishSensorHeatingSetpoint(false);
|
||||||
@@ -305,6 +319,7 @@ protected:
|
|||||||
haHelper.publishBinSensorFault();
|
haHelper.publishBinSensorFault();
|
||||||
haHelper.publishBinSensorDiagnostic();
|
haHelper.publishBinSensorDiagnostic();
|
||||||
haHelper.publishSensorFaultCode();
|
haHelper.publishSensorFaultCode();
|
||||||
|
haHelper.publishSensorRssi();
|
||||||
|
|
||||||
// sensors
|
// sensors
|
||||||
haHelper.publishSensorModulation(false);
|
haHelper.publishSensorModulation(false);
|
||||||
@@ -383,6 +398,7 @@ protected:
|
|||||||
doc["emergency"]["useEquitherm"] = settings.emergency.useEquitherm;
|
doc["emergency"]["useEquitherm"] = settings.emergency.useEquitherm;
|
||||||
|
|
||||||
doc["heating"]["enable"] = settings.heating.enable;
|
doc["heating"]["enable"] = settings.heating.enable;
|
||||||
|
doc["heating"]["turbo"] = settings.heating.turbo;
|
||||||
doc["heating"]["target"] = settings.heating.target;
|
doc["heating"]["target"] = settings.heating.target;
|
||||||
doc["heating"]["hysteresis"] = settings.heating.hysteresis;
|
doc["heating"]["hysteresis"] = settings.heating.hysteresis;
|
||||||
|
|
||||||
@@ -420,6 +436,7 @@ protected:
|
|||||||
doc["states"]["fault"] = vars.states.fault;
|
doc["states"]["fault"] = vars.states.fault;
|
||||||
doc["states"]["diagnostic"] = vars.states.diagnostic;
|
doc["states"]["diagnostic"] = vars.states.diagnostic;
|
||||||
doc["states"]["faultCode"] = vars.states.faultCode;
|
doc["states"]["faultCode"] = vars.states.faultCode;
|
||||||
|
doc["states"]["rssi"] = vars.states.rssi;
|
||||||
|
|
||||||
doc["sensors"]["modulation"] = vars.sensors.modulation;
|
doc["sensors"]["modulation"] = vars.sensors.modulation;
|
||||||
doc["sensors"]["pressure"] = vars.sensors.pressure;
|
doc["sensors"]["pressure"] = vars.sensors.pressure;
|
||||||
@@ -454,7 +471,7 @@ protected:
|
|||||||
|
|
||||||
if (settings.debug) {
|
if (settings.debug) {
|
||||||
DEBUG_F("MQTT received message\n\r Topic: %s\n\r Data: ", topic);
|
DEBUG_F("MQTT received message\n\r Topic: %s\n\r Data: ", topic);
|
||||||
for (int i = 0; i < length; i++) {
|
for (unsigned int i = 0; i < length; i++) {
|
||||||
DEBUG_STREAM.print((char)payload[i]);
|
DEBUG_STREAM.print((char)payload[i]);
|
||||||
}
|
}
|
||||||
DEBUG_STREAM.print("\n");
|
DEBUG_STREAM.print("\n");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "lib/CustomOpenTherm.h"
|
#include <new>
|
||||||
|
#include <CustomOpenTherm.h>
|
||||||
|
|
||||||
CustomOpenTherm ot(OPENTHERM_IN_PIN, OPENTHERM_OUT_PIN);
|
CustomOpenTherm* ot;
|
||||||
|
|
||||||
class OpenThermTask: public Task {
|
class OpenThermTask: public Task {
|
||||||
public:
|
public:
|
||||||
@@ -8,100 +9,125 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setup() {
|
void setup() {
|
||||||
ot.begin(handleInterrupt, responseCallback);
|
ot = new CustomOpenTherm(settings.opentherm.inPin, settings.opentherm.outPin);
|
||||||
ot.setHandleSendRequestCallback(sendRequestCallback);
|
|
||||||
|
ot->begin(handleInterrupt, responseCallback);
|
||||||
|
ot->setHandleSendRequestCallback(sendRequestCallback);
|
||||||
|
|
||||||
|
#ifdef LED_OT_RX_PIN
|
||||||
|
pinMode(LED_OT_RX_PIN, OUTPUT);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
static byte currentHeatingTemp, currentDHWTemp = 0;
|
static byte currentHeatingTemp, currentDHWTemp = 0;
|
||||||
byte newHeatingTemp, newDHWTemp = 0;
|
|
||||||
unsigned long localResponse;
|
unsigned long localResponse;
|
||||||
|
|
||||||
setMasterMemberIdCode();
|
if ( setMasterMemberIdCode() ) {
|
||||||
DEBUG_F("Slave member id code: %u \n", vars.parameters.slaveMemberIdCode);
|
DEBUG_F("Slave member id code: %u\r\n", vars.parameters.slaveMemberIdCode);
|
||||||
|
DEBUG_F("Master member id code: %u\r\n", settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode);
|
||||||
|
|
||||||
localResponse = ot.setBoilerStatus(
|
} else {
|
||||||
settings.heating.enable && pump,
|
WARN("Slave member id failed");
|
||||||
settings.dhw.enable
|
}
|
||||||
|
|
||||||
|
bool heatingEnable = (vars.states.emergency || settings.heating.enable) && pump && isReady();
|
||||||
|
localResponse = ot->setBoilerStatus(
|
||||||
|
heatingEnable,
|
||||||
|
settings.dhw.enable,
|
||||||
|
false, false, true, false, false
|
||||||
);
|
);
|
||||||
|
if (!ot->isValidResponse(localResponse)) {
|
||||||
if (!ot.isValidResponse(localResponse)) {
|
WARN_F("Invalid response after setBoilerStatus: %s\r\n", ot->statusToString(ot->getLastResponseStatus()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.states.heating = ot.isCentralHeatingActive(localResponse);
|
INFO_F("Heating enabled: %d\r\n", heatingEnable);
|
||||||
vars.states.dhw = ot.isHotWaterActive(localResponse);
|
setMaxModulationLevel(heatingEnable ? 100 : 0);
|
||||||
vars.states.flame = ot.isFlameOn(localResponse);
|
|
||||||
vars.states.fault = ot.isFault(localResponse);
|
|
||||||
vars.states.diagnostic = ot.isDiagnostic(localResponse);
|
|
||||||
|
|
||||||
/*if (vars.dump_request.value)
|
vars.states.heating = ot->isCentralHeatingActive(localResponse);
|
||||||
{
|
vars.states.dhw = ot->isHotWaterActive(localResponse);
|
||||||
testSupportedIDs();
|
vars.states.flame = ot->isFlameOn(localResponse);
|
||||||
vars.dump_request.value = false;
|
vars.states.fault = ot->isFault(localResponse);
|
||||||
}*/
|
vars.states.diagnostic = ot->isDiagnostic(localResponse);
|
||||||
|
yield();
|
||||||
|
|
||||||
|
|
||||||
/*if ( ot.isValidResponse(localResponse) ) {
|
|
||||||
vars.SlaveMemberIDcode.value = localResponse >> 0 & 0xFF;
|
|
||||||
uint8_t flags = (localResponse & 0xFFFF) >> 8 & 0xFF;
|
|
||||||
vars.dhw_present.value = flags & 0x01;
|
|
||||||
vars.control_type.value = flags & 0x02;
|
|
||||||
vars.cooling_present.value = flags & 0x04;
|
|
||||||
vars.dhw_tank_present.value = flags & 0x08;
|
|
||||||
vars.pump_control_present.value = flags & 0x10;
|
|
||||||
vars.ch2_present.value = flags & 0x20;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Команды чтения данных котла
|
// Команды чтения данных котла
|
||||||
if (millis() - prevUpdateNonEssentialVars > 30000) {
|
if (millis() - prevUpdateNonEssentialVars > 60000) {
|
||||||
updateSlaveParameters();
|
updateSlaveParameters();
|
||||||
updateMasterParameters();
|
updateMasterParameters();
|
||||||
// crash?
|
|
||||||
DEBUG_F("Master type: %u, version: %u \n", vars.parameters.masterType, vars.parameters.masterVersion);
|
DEBUG_F("Master type: %u, version: %u\r\n", vars.parameters.masterType, vars.parameters.masterVersion);
|
||||||
DEBUG_F("Slave type: %u, version: %u \n", vars.parameters.slaveType, vars.parameters.slaveVersion);
|
DEBUG_F("Slave type: %u, version: %u\r\n", vars.parameters.slaveType, vars.parameters.slaveVersion);
|
||||||
|
|
||||||
updateMinMaxDhwTemp();
|
updateMinMaxDhwTemp();
|
||||||
updateMinMaxHeatingTemp();
|
updateMinMaxHeatingTemp();
|
||||||
|
|
||||||
if (settings.outdoorTempSource == 0) {
|
if (settings.outdoorTempSource == 0) {
|
||||||
updateOutsideTemp();
|
updateOutsideTemp();
|
||||||
}
|
}
|
||||||
if (vars.states.fault) {
|
if (vars.states.fault) {
|
||||||
updateFaultCode();
|
updateFaultCode();
|
||||||
|
ot->sendBoilerReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( vars.states.diagnostic ) {
|
||||||
|
ot->sendServiceReset();
|
||||||
}
|
}
|
||||||
updatePressure();
|
|
||||||
|
|
||||||
prevUpdateNonEssentialVars = millis();
|
prevUpdateNonEssentialVars = millis();
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
updateHeatingTemp();
|
|
||||||
updateDHWTemp();
|
updatePressure();
|
||||||
|
if ( settings.dhw.enable || settings.heating.enable || heatingEnable ) {
|
||||||
updateModulationLevel();
|
updateModulationLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( settings.dhw.enable ) {
|
||||||
|
updateDHWTemp();
|
||||||
|
} else {
|
||||||
|
vars.temperatures.dhw = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( settings.heating.enable || heatingEnable ) {
|
||||||
|
updateHeatingTemp();
|
||||||
|
} else {
|
||||||
|
vars.temperatures.heating = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Температура ГВС
|
// Температура ГВС
|
||||||
newDHWTemp = settings.dhw.target;
|
byte newDHWTemp = settings.dhw.target;
|
||||||
if (newDHWTemp != currentDHWTemp) {
|
if (settings.dhw.enable && newDHWTemp != currentDHWTemp) {
|
||||||
if (newDHWTemp < vars.parameters.dhwMinTemp || newDHWTemp > vars.parameters.dhwMaxTemp) {
|
if (newDHWTemp < vars.parameters.dhwMinTemp || newDHWTemp > vars.parameters.dhwMaxTemp) {
|
||||||
newDHWTemp = constrain(newDHWTemp, vars.parameters.dhwMinTemp, vars.parameters.dhwMaxTemp);
|
newDHWTemp = constrain(newDHWTemp, vars.parameters.dhwMinTemp, vars.parameters.dhwMaxTemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO_F("Set DHW temp = %u \n", newDHWTemp);
|
INFO_F("Set DHW temp = %u\r\n", newDHWTemp);
|
||||||
|
|
||||||
// Записываем заданную температуру ГВС
|
// Записываем заданную температуру ГВС
|
||||||
if (ot.setDHWSetpoint(newDHWTemp)) {
|
if (ot->setDHWSetpoint(newDHWTemp)) {
|
||||||
currentDHWTemp = newDHWTemp;
|
currentDHWTemp = newDHWTemp;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
WARN("Failed set DHW temp");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Температура отопления
|
// Температура отопления
|
||||||
if (fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) {
|
if (heatingEnable && fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) {
|
||||||
INFO_F("Set heating temp = %u \n", vars.parameters.heatingSetpoint);
|
INFO_F("Setting heating temp = %u \n", vars.parameters.heatingSetpoint);
|
||||||
|
|
||||||
// Записываем заданную температуру
|
// Записываем заданную температуру
|
||||||
if (ot.setBoilerTemperature(vars.parameters.heatingSetpoint)) {
|
if (ot->setBoilerTemperature(vars.parameters.heatingSetpoint)) {
|
||||||
currentHeatingTemp = vars.parameters.heatingSetpoint;
|
currentHeatingTemp = vars.parameters.heatingSetpoint;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
WARN("Failed set heating temp");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,35 +137,51 @@ protected:
|
|||||||
float halfHyst = settings.heating.hysteresis / 2;
|
float halfHyst = settings.heating.hysteresis / 2;
|
||||||
if (pump && vars.temperatures.indoor - settings.heating.target + 0.0001 >= halfHyst) {
|
if (pump && vars.temperatures.indoor - settings.heating.target + 0.0001 >= halfHyst) {
|
||||||
pump = false;
|
pump = false;
|
||||||
|
|
||||||
} else if (!pump && vars.temperatures.indoor - settings.heating.target - 0.0001 <= -(halfHyst)) {
|
} else if (!pump && vars.temperatures.indoor - settings.heating.target - 0.0001 <= -(halfHyst)) {
|
||||||
pump = true;
|
pump = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (!pump) {
|
} else if (!pump) {
|
||||||
pump = true;
|
pump = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void static IRAM_ATTR handleInterrupt() {
|
void static IRAM_ATTR handleInterrupt() {
|
||||||
ot.handleInterrupt();
|
ot->handleInterrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void static sendRequestCallback(unsigned long request, unsigned long response, OpenThermResponseStatus status, byte attempt) {
|
void static sendRequestCallback(unsigned long request, unsigned long response, OpenThermResponseStatus status, byte attempt) {
|
||||||
printRequestDetail(ot.getDataID(request), status, request, response, attempt);
|
printRequestDetail(ot->getDataID(request), status, request, response, attempt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void static responseCallback(unsigned long result, OpenThermResponseStatus status) {
|
void static responseCallback(unsigned long result, OpenThermResponseStatus status) {
|
||||||
static byte attempt = 0;
|
static byte attempt = 0;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case OpenThermResponseStatus::TIMEOUT:
|
case OpenThermResponseStatus::TIMEOUT:
|
||||||
if (++attempt > OPENTHERM_OFFLINE_TRESHOLD) {
|
if (vars.states.otStatus && ++attempt > OPENTHERM_OFFLINE_TRESHOLD) {
|
||||||
vars.states.otStatus = false;
|
vars.states.otStatus = false;
|
||||||
attempt = OPENTHERM_OFFLINE_TRESHOLD;
|
attempt = OPENTHERM_OFFLINE_TRESHOLD;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OpenThermResponseStatus::SUCCESS:
|
case OpenThermResponseStatus::SUCCESS:
|
||||||
attempt = 0;
|
attempt = 0;
|
||||||
|
if (!vars.states.otStatus) {
|
||||||
vars.states.otStatus = true;
|
vars.states.otStatus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LED_OT_RX_PIN
|
||||||
|
{
|
||||||
|
digitalWrite(LED_OT_RX_PIN, true);
|
||||||
|
unsigned long ts = millis();
|
||||||
|
while (millis() - ts < 2) {}
|
||||||
|
digitalWrite(LED_OT_RX_PIN, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -148,9 +190,15 @@ protected:
|
|||||||
protected:
|
protected:
|
||||||
bool pump = true;
|
bool pump = true;
|
||||||
unsigned long prevUpdateNonEssentialVars = 0;
|
unsigned long prevUpdateNonEssentialVars = 0;
|
||||||
|
unsigned long startupTime = millis();
|
||||||
|
|
||||||
|
|
||||||
|
bool isReady() {
|
||||||
|
return millis() - startupTime > 60000;
|
||||||
|
}
|
||||||
|
|
||||||
void static printRequestDetail(OpenThermMessageID id, OpenThermResponseStatus status, unsigned long request, unsigned long response, byte attempt) {
|
void static printRequestDetail(OpenThermMessageID id, OpenThermResponseStatus status, unsigned long request, unsigned long response, byte attempt) {
|
||||||
sprintf(buffer, "OT REQUEST ID: %4d Request: %8x Response: %8x Attempt: %2d Status: %s", id, request, response, attempt, ot.statusToString(status));
|
sprintf(buffer, "OT REQUEST ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s", id, request, response, attempt, ot->statusToString(status));
|
||||||
if (status != OpenThermResponseStatus::SUCCESS) {
|
if (status != OpenThermResponseStatus::SUCCESS) {
|
||||||
//WARN(buffer);
|
//WARN(buffer);
|
||||||
DEBUG(buffer);
|
DEBUG(buffer);
|
||||||
@@ -159,267 +207,7 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
bool setMasterMemberIdCode() {
|
||||||
bool getBoilerTemp()
|
|
||||||
{
|
|
||||||
unsigned long response;
|
|
||||||
return sendRequest(ot.buildGetBoilerTemperatureRequest(),response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getDHWTemp()
|
|
||||||
{
|
|
||||||
unsigned long response;
|
|
||||||
unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tdhw, 0);
|
|
||||||
return sendRequest(request,response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getOutsideTemp()
|
|
||||||
{
|
|
||||||
unsigned long response;
|
|
||||||
unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0);
|
|
||||||
return sendRequest(request,response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setDHWTemp(float val)
|
|
||||||
{
|
|
||||||
unsigned long request = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::TdhwSet, ot.temperatureToData(val));
|
|
||||||
unsigned long response;
|
|
||||||
return sendRequest(request,response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getFaultCode()
|
|
||||||
{
|
|
||||||
unsigned long response;
|
|
||||||
unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0);
|
|
||||||
return sendRequest(request,response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getModulationLevel() {
|
|
||||||
unsigned long response;
|
|
||||||
unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0);
|
|
||||||
return sendRequest(request,response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getPressure() {
|
|
||||||
unsigned long response;
|
|
||||||
unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0);
|
|
||||||
return sendRequest(request,response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sendRequest(unsigned long request, unsigned long& response)
|
|
||||||
{
|
|
||||||
send_newts = millis();
|
|
||||||
if (send_newts - send_ts < 200) {
|
|
||||||
// Преждем чем слать что то - надо подождать 100ms согласно специфиикации протокола ОТ
|
|
||||||
delay(200 - (send_newts - send_ts));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = ot.sendRequestAync(request);
|
|
||||||
if(!result) {
|
|
||||||
WARN("Не могу отправить запрос");
|
|
||||||
WARN("Шина " + ot.isReady() ? "готова" : "не готова");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
while (!ot.isReady())
|
|
||||||
{
|
|
||||||
ot.process();
|
|
||||||
yield(); // This is local Task yield() call which allow us to switch to another task in scheduler
|
|
||||||
}
|
|
||||||
send_ts = millis();
|
|
||||||
response = ot_response;
|
|
||||||
//printRequestDetail(ot.getDataID(request), request, response);
|
|
||||||
|
|
||||||
return true; // Response is global variable
|
|
||||||
}
|
|
||||||
|
|
||||||
void testSupportedIDs()
|
|
||||||
{
|
|
||||||
// Basic
|
|
||||||
unsigned long request;
|
|
||||||
unsigned long response;
|
|
||||||
OpenThermMessageID id;
|
|
||||||
//Command
|
|
||||||
id = OpenThermMessageID::Command;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
//ASFlags
|
|
||||||
id = OpenThermMessageID::ASFflags;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//TrOverride
|
|
||||||
id = OpenThermMessageID::TrOverride;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//TSP
|
|
||||||
id = OpenThermMessageID::TSP;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//TSPindexTSPvalue
|
|
||||||
id = OpenThermMessageID::TSPindexTSPvalue;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//FHBsize
|
|
||||||
id = OpenThermMessageID::FHBsize;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//FHBindexFHBvalue
|
|
||||||
id = OpenThermMessageID::FHBindexFHBvalue;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//MaxCapacityMinModLevel
|
|
||||||
id = OpenThermMessageID::MaxCapacityMinModLevel;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//TrSet
|
|
||||||
id = OpenThermMessageID::TrSet;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::WRITE, id, ot.temperatureToData(21));
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//RelModLevel
|
|
||||||
id = OpenThermMessageID::RelModLevel;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//CHPressure
|
|
||||||
id = OpenThermMessageID::CHPressure;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//DHWFlowRate
|
|
||||||
id = OpenThermMessageID::DHWFlowRate;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//DayTime
|
|
||||||
id = OpenThermMessageID::DayTime;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//Date
|
|
||||||
id = OpenThermMessageID::Date;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//Year
|
|
||||||
id = OpenThermMessageID::Year;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//TrSetCH2
|
|
||||||
id = OpenThermMessageID::TrSetCH2;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::WRITE, id, ot.temperatureToData(21));
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//Tr
|
|
||||||
id = OpenThermMessageID::Tr;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::WRITE, id, ot.temperatureToData(21));
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//Tret
|
|
||||||
id = OpenThermMessageID::Tret;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//Texhaust
|
|
||||||
id = OpenThermMessageID::Texhaust;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//Hcratio
|
|
||||||
id = OpenThermMessageID::Hcratio;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//RemoteOverrideFunction
|
|
||||||
id = OpenThermMessageID::RemoteOverrideFunction;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//OEMDiagnosticCode
|
|
||||||
id = OpenThermMessageID::OEMDiagnosticCode;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//BurnerStarts
|
|
||||||
id = OpenThermMessageID::BurnerStarts;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//CHPumpStarts
|
|
||||||
id = OpenThermMessageID::CHPumpStarts;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//DHWPumpValveStarts
|
|
||||||
id = OpenThermMessageID::DHWPumpValveStarts;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//DHWBurnerStarts
|
|
||||||
id = OpenThermMessageID::DHWBurnerStarts;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//BurnerOperationHours
|
|
||||||
id = OpenThermMessageID::BurnerOperationHours;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//CHPumpOperationHours
|
|
||||||
id = OpenThermMessageID::CHPumpOperationHours;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//DHWPumpValveOperationHours
|
|
||||||
id = OpenThermMessageID::DHWPumpValveOperationHours;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
|
|
||||||
//DHWBurnerOperationHours
|
|
||||||
id = OpenThermMessageID::DHWBurnerOperationHours;
|
|
||||||
request = ot.buildRequest(OpenThermRequestType::READ, id, 0);
|
|
||||||
if(sendRequest(request,response))
|
|
||||||
printRequestDetail(id, ot.getLastResponseStatus(), request, response);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
void setMasterMemberIdCode() {
|
|
||||||
//=======================================================================================
|
//=======================================================================================
|
||||||
// Эта группа элементов данных определяет информацию о конфигурации как на ведомых, так
|
// Эта группа элементов данных определяет информацию о конфигурации как на ведомых, так
|
||||||
// и на главных сторонах. Каждый из них имеет группу флагов конфигурации (8 бит)
|
// и на главных сторонах. Каждый из них имеет группу флагов конфигурации (8 бит)
|
||||||
@@ -430,40 +218,88 @@ protected:
|
|||||||
// с "кодом идентификатора участника", который идентифицирует производителя устройства.
|
// с "кодом идентификатора участника", который идентифицирует производителя устройства.
|
||||||
//=======================================================================================
|
//=======================================================================================
|
||||||
|
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0)); // 0xFFFF
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0)); // 0xFFFF
|
||||||
if (!ot.isValidResponse(response)) {
|
/*uint8_t flags = (response & 0xFFFF) >> 8;
|
||||||
return;
|
DEBUG_F(
|
||||||
|
"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
|
||||||
|
);*/
|
||||||
|
|
||||||
|
if (ot->isValidResponse(response)) {
|
||||||
|
vars.parameters.slaveMemberIdCode = response & 0xFF;
|
||||||
|
|
||||||
|
} else if ( settings.opentherm.memberIdCode <= 0 ) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.parameters.slaveMemberIdCode = response >> 0 & 0xFF;
|
response = ot->sendRequest(ot->buildRequest(
|
||||||
ot.sendRequest(ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, vars.parameters.slaveMemberIdCode));
|
OpenThermRequestType::WRITE,
|
||||||
|
OpenThermMessageID::MConfigMMemberIDcode,
|
||||||
|
settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode
|
||||||
|
));
|
||||||
|
|
||||||
|
return ot->isValidResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateMasterParameters() {
|
bool setMaxModulationLevel(byte value) {
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, 0x013F));
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MaxRelModLevelSetting, (unsigned int)(value * 256)));
|
||||||
if (!ot.isValidResponse(response)) {
|
|
||||||
return;
|
return ot->isValidResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setOpenThermVersionMaster() {
|
||||||
|
unsigned long response;
|
||||||
|
response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::OpenThermVersionSlave, 0));
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// INFO_F("Opentherm version slave: %f\n", ot->getFloat(response));
|
||||||
|
|
||||||
|
response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::WRITE_DATA, OpenThermMessageID::OpenThermVersionMaster, response));
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// INFO_F("Opentherm version master: %f\n", ot->getFloat(response));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updateMasterParameters() {
|
||||||
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, 0x013F));
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.parameters.masterType = (response & 0xFFFF) >> 8;
|
vars.parameters.masterType = (response & 0xFFFF) >> 8;
|
||||||
vars.parameters.masterVersion = response & 0xFF;
|
vars.parameters.masterVersion = response & 0xFF;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateSlaveParameters() {
|
bool updateSlaveParameters() {
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0));
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0));
|
||||||
if (!ot.isValidResponse(response)) {
|
if (!ot->isValidResponse(response)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.parameters.slaveType = (response & 0xFFFF) >> 8;
|
vars.parameters.slaveType = (response & 0xFFFF) >> 8;
|
||||||
vars.parameters.slaveVersion = response & 0xFF;
|
vars.parameters.slaveVersion = response & 0xFF;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateMinMaxDhwTemp() {
|
bool updateMinMaxDhwTemp() {
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::TdhwSetUBTdhwSetLB, 0));
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::TdhwSetUBTdhwSetLB, 0));
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
if (!ot.isValidResponse(response)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
byte minTemp = response & 0xFF;
|
byte minTemp = response & 0xFF;
|
||||||
@@ -472,14 +308,17 @@ protected:
|
|||||||
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
|
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
|
||||||
vars.parameters.dhwMinTemp = minTemp;
|
vars.parameters.dhwMinTemp = minTemp;
|
||||||
vars.parameters.dhwMaxTemp = maxTemp;
|
vars.parameters.dhwMaxTemp = maxTemp;
|
||||||
}
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateMinMaxHeatingTemp() {
|
return false;
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::MaxTSetUBMaxTSetLB, 0));
|
}
|
||||||
|
|
||||||
if (!ot.isValidResponse(response)) {
|
bool updateMinMaxHeatingTemp() {
|
||||||
return;
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::MaxTSetUBMaxTSetLB, 0));
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte minTemp = response & 0xFF;
|
byte minTemp = response & 0xFF;
|
||||||
@@ -488,55 +327,80 @@ protected:
|
|||||||
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
|
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
|
||||||
vars.parameters.heatingMinTemp = minTemp;
|
vars.parameters.heatingMinTemp = minTemp;
|
||||||
vars.parameters.heatingMaxTemp = maxTemp;
|
vars.parameters.heatingMaxTemp = maxTemp;
|
||||||
}
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateOutsideTemp() {
|
return false;
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0));
|
|
||||||
|
|
||||||
if (ot.isValidResponse(response)) {
|
|
||||||
vars.temperatures.outdoor = ot.getFloat(response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateHeatingTemp() {
|
bool updateOutsideTemp() {
|
||||||
unsigned long response = ot.sendRequest(ot.buildGetBoilerTemperatureRequest());
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0));
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
if (ot.isValidResponse(response)) {
|
return false;
|
||||||
vars.temperatures.heating = ot.getFloat(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vars.temperatures.outdoor = ot->getFloat(response);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updateHeatingTemp() {
|
||||||
|
unsigned long response = ot->sendRequest(ot->buildGetBoilerTemperatureRequest());
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vars.temperatures.heating = ot->getFloat(response);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void updateDHWTemp() {
|
bool updateDHWTemp() {
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tdhw, 0));
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermMessageType::READ, OpenThermMessageID::Tdhw, 0));
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
if (ot.isValidResponse(response)) {
|
return false;
|
||||||
vars.temperatures.dhw = ot.getFloat(response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateFaultCode() {
|
vars.temperatures.dhw = ot->getFloat(response);
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0));
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updateFaultCode() {
|
||||||
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0));
|
||||||
|
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (ot.isValidResponse(response)) {
|
|
||||||
vars.states.faultCode = response & 0xFF;
|
vars.states.faultCode = response & 0xFF;
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateModulationLevel() {
|
bool updateModulationLevel() {
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0));
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0));
|
||||||
|
|
||||||
if (ot.isValidResponse(response)) {
|
if (!ot->isValidResponse(response)) {
|
||||||
vars.sensors.modulation = ot.getFloat(response);
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePressure() {
|
float modulation = ot->f88(response);
|
||||||
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0));
|
if (!vars.states.flame) {
|
||||||
|
vars.sensors.modulation = 0;
|
||||||
if (ot.isValidResponse(response)) {
|
} else {
|
||||||
vars.sensors.pressure = ot.getFloat(response);
|
vars.sensors.modulation = modulation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updatePressure() {
|
||||||
|
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0));
|
||||||
|
|
||||||
|
if (!ot->isValidResponse(response)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vars.sensors.pressure = ot->getFloat(response);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "lib/Equitherm.h"
|
#include <Equitherm.h>
|
||||||
#include <GyverPID.h>
|
#include <GyverPID.h>
|
||||||
#include <PIDtuner.h>
|
#include <PIDtuner.h>
|
||||||
|
|
||||||
@@ -21,13 +21,25 @@ protected:
|
|||||||
|
|
||||||
void setup() {}
|
void setup() {}
|
||||||
void loop() {
|
void loop() {
|
||||||
byte newTemp;
|
byte newTemp = vars.parameters.heatingSetpoint;
|
||||||
|
|
||||||
if (vars.states.emergency) {
|
if (vars.states.emergency) {
|
||||||
|
if (settings.heating.turbo) {
|
||||||
|
settings.heating.turbo = false;
|
||||||
|
|
||||||
|
INFO("[REGULATOR] Turbo mode auto disabled");
|
||||||
|
}
|
||||||
|
|
||||||
newTemp = getEmergencyModeTemp();
|
newTemp = getEmergencyModeTemp();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (vars.tuning.enable || tunerInit) {
|
if (vars.tuning.enable || tunerInit) {
|
||||||
|
if (settings.heating.turbo) {
|
||||||
|
settings.heating.turbo = false;
|
||||||
|
|
||||||
|
INFO("[REGULATOR] Turbo mode auto disabled");
|
||||||
|
}
|
||||||
|
|
||||||
newTemp = getTuningModeTemp();
|
newTemp = getTuningModeTemp();
|
||||||
|
|
||||||
if (newTemp == 0) {
|
if (newTemp == 0) {
|
||||||
@@ -36,6 +48,12 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!vars.tuning.enable) {
|
if (!vars.tuning.enable) {
|
||||||
|
if (settings.heating.turbo && (fabs(settings.heating.target - vars.temperatures.indoor) < 1 || (settings.equitherm.enable && settings.pid.enable))) {
|
||||||
|
settings.heating.turbo = false;
|
||||||
|
|
||||||
|
INFO("[REGULATOR] Turbo mode auto disabled");
|
||||||
|
}
|
||||||
|
|
||||||
newTemp = getNormalModeTemp();
|
newTemp = getNormalModeTemp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,13 +74,13 @@ protected:
|
|||||||
|
|
||||||
// if use equitherm
|
// if use equitherm
|
||||||
if (settings.emergency.useEquitherm && settings.outdoorTempSource != 1) {
|
if (settings.emergency.useEquitherm && settings.outdoorTempSource != 1) {
|
||||||
float etResult = getEquithermTemp();
|
float etResult = getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp);
|
||||||
|
|
||||||
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
|
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
|
||||||
prevEtResult = etResult;
|
prevEtResult = etResult;
|
||||||
newTemp += etResult;
|
newTemp += etResult;
|
||||||
|
|
||||||
INFO_F("[REGULATOR][EQUITHERM] New emergency result: %u (%f) \n", (byte) round(etResult), etResult);
|
INFO_F("[REGULATOR][EQUITHERM] New emergency result: %u (%f) \n", (int)round(etResult), etResult);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
newTemp += prevEtResult;
|
newTemp += prevEtResult;
|
||||||
@@ -81,19 +99,23 @@ protected:
|
|||||||
|
|
||||||
if (fabs(prevHeatingTarget - settings.heating.target) > 0.0001) {
|
if (fabs(prevHeatingTarget - settings.heating.target) > 0.0001) {
|
||||||
prevHeatingTarget = settings.heating.target;
|
prevHeatingTarget = settings.heating.target;
|
||||||
|
|
||||||
INFO_F("[REGULATOR] New target: %f \n", settings.heating.target);
|
INFO_F("[REGULATOR] New target: %f \n", settings.heating.target);
|
||||||
|
|
||||||
|
if (settings.equitherm.enable && settings.pid.enable) {
|
||||||
|
pidRegulator.integral = 0;
|
||||||
|
INFO_F("[REGULATOR][PID] Integral sum has been reset");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if use equitherm
|
// if use equitherm
|
||||||
if (settings.equitherm.enable) {
|
if (settings.equitherm.enable) {
|
||||||
float etResult = getEquithermTemp();
|
float etResult = getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp);
|
||||||
|
|
||||||
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
|
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
|
||||||
prevEtResult = etResult;
|
prevEtResult = etResult;
|
||||||
newTemp += etResult;
|
newTemp += etResult;
|
||||||
|
|
||||||
INFO_F("[REGULATOR][EQUITHERM] New result: %u (%f) \n", (byte) round(etResult), etResult);
|
INFO_F("[REGULATOR][EQUITHERM] New result: %u (%f) \n", (int)round(etResult), etResult);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
newTemp += prevEtResult;
|
newTemp += prevEtResult;
|
||||||
@@ -102,13 +124,16 @@ protected:
|
|||||||
|
|
||||||
// if use pid
|
// if use pid
|
||||||
if (settings.pid.enable) {
|
if (settings.pid.enable) {
|
||||||
float pidResult = getPidTemp();
|
float pidResult = getPidTemp(
|
||||||
|
settings.equitherm.enable ? -30 : vars.parameters.heatingMinTemp,
|
||||||
|
settings.equitherm.enable ? 30 : vars.parameters.heatingMaxTemp
|
||||||
|
);
|
||||||
|
|
||||||
if (fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) {
|
if (fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) {
|
||||||
prevPidResult = pidResult;
|
prevPidResult = pidResult;
|
||||||
newTemp += pidResult;
|
newTemp += pidResult;
|
||||||
|
|
||||||
INFO_F("[REGULATOR][PID] New result: %u (%f) \n", (byte) round(pidResult), pidResult);
|
INFO_F("[REGULATOR][PID] New result: %u (%f) \n", (int)round(pidResult), pidResult);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
newTemp += prevPidResult;
|
newTemp += prevPidResult;
|
||||||
@@ -147,7 +172,9 @@ protected:
|
|||||||
|
|
||||||
} else if (vars.tuning.regulator == 1) {
|
} else if (vars.tuning.regulator == 1) {
|
||||||
// PID tuner
|
// PID tuner
|
||||||
float defaultTemp = settings.equitherm.enable ? getEquithermTemp() : settings.heating.target;
|
float defaultTemp = settings.equitherm.enable
|
||||||
|
? getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp)
|
||||||
|
: settings.heating.target;
|
||||||
|
|
||||||
if (tunerInit && pidTuner.getState() == 3) {
|
if (tunerInit && pidTuner.getState() == 3) {
|
||||||
INFO(F("[REGULATOR][TUNING][PID] Finished"));
|
INFO(F("[REGULATOR][TUNING][PID] Finished"));
|
||||||
@@ -203,7 +230,7 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float getEquithermTemp() {
|
float getEquithermTemp(int minTemp, int maxTemp) {
|
||||||
if (vars.states.emergency) {
|
if (vars.states.emergency) {
|
||||||
etRegulator.Kt = 0;
|
etRegulator.Kt = 0;
|
||||||
etRegulator.indoorTemp = 0;
|
etRegulator.indoorTemp = 0;
|
||||||
@@ -214,13 +241,17 @@ protected:
|
|||||||
etRegulator.indoorTemp = round(vars.temperatures.indoor);
|
etRegulator.indoorTemp = round(vars.temperatures.indoor);
|
||||||
etRegulator.outdoorTemp = round(vars.temperatures.outdoor);
|
etRegulator.outdoorTemp = round(vars.temperatures.outdoor);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (settings.heating.turbo) {
|
||||||
|
etRegulator.Kt = 10;
|
||||||
} else {
|
} else {
|
||||||
etRegulator.Kt = settings.equitherm.t_factor;
|
etRegulator.Kt = settings.equitherm.t_factor;
|
||||||
|
}
|
||||||
etRegulator.indoorTemp = vars.temperatures.indoor;
|
etRegulator.indoorTemp = vars.temperatures.indoor;
|
||||||
etRegulator.outdoorTemp = vars.temperatures.outdoor;
|
etRegulator.outdoorTemp = vars.temperatures.outdoor;
|
||||||
}
|
}
|
||||||
|
|
||||||
etRegulator.setLimits(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp);
|
etRegulator.setLimits(minTemp, maxTemp);
|
||||||
etRegulator.Kn = settings.equitherm.n_factor;
|
etRegulator.Kn = settings.equitherm.n_factor;
|
||||||
// etRegulator.Kn = tuneEquithermN(etRegulator.Kn, vars.temperatures.indoor, settings.heating.target, 300, 1800, 0.01, 1);
|
// etRegulator.Kn = tuneEquithermN(etRegulator.Kn, vars.temperatures.indoor, settings.heating.target, 300, 1800, 0.01, 1);
|
||||||
etRegulator.Kk = settings.equitherm.k_factor;
|
etRegulator.Kk = settings.equitherm.k_factor;
|
||||||
@@ -229,12 +260,12 @@ protected:
|
|||||||
return etRegulator.getResult();
|
return etRegulator.getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
float getPidTemp() {
|
float getPidTemp(int minTemp, int maxTemp) {
|
||||||
pidRegulator.Kp = settings.pid.p_factor;
|
pidRegulator.Kp = settings.pid.p_factor;
|
||||||
pidRegulator.Ki = settings.pid.i_factor;
|
pidRegulator.Ki = settings.pid.i_factor;
|
||||||
pidRegulator.Kd = settings.pid.d_factor;
|
pidRegulator.Kd = settings.pid.d_factor;
|
||||||
|
|
||||||
pidRegulator.setLimits(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp);
|
pidRegulator.setLimits(minTemp, maxTemp);
|
||||||
pidRegulator.input = vars.temperatures.indoor;
|
pidRegulator.input = vars.temperatures.indoor;
|
||||||
pidRegulator.setpoint = settings.heating.target;
|
pidRegulator.setpoint = settings.heating.target;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ protected:
|
|||||||
if (outdoorSensor.online()) {
|
if (outdoorSensor.online()) {
|
||||||
if (outdoorSensor.readTemp()) {
|
if (outdoorSensor.readTemp()) {
|
||||||
float rawTemp = outdoorSensor.getTemp();
|
float rawTemp = outdoorSensor.getTemp();
|
||||||
INFO_F("[SENSORS][DS18B20] Raw temp: %f \n", rawTemp);
|
DEBUG_F("[SENSORS][DS18B20] Raw temp: %f \n", rawTemp);
|
||||||
|
|
||||||
if (emptyOutdoorTemp) {
|
if (emptyOutdoorTemp) {
|
||||||
filteredOutdoorTemp = rawTemp;
|
filteredOutdoorTemp = rawTemp;
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ struct Settings {
|
|||||||
byte outdoorTempSource = 0;
|
byte outdoorTempSource = 0;
|
||||||
char hostname[80] = "opentherm";
|
char hostname[80] = "opentherm";
|
||||||
|
|
||||||
|
struct {
|
||||||
|
byte inPin = 5;
|
||||||
|
byte outPin = 4;
|
||||||
|
unsigned int memberIdCode = 4;
|
||||||
|
} opentherm;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
char server[80];
|
char server[80];
|
||||||
int port = 1883;
|
int port = 1883;
|
||||||
@@ -21,6 +27,7 @@ struct Settings {
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = true;
|
bool enable = true;
|
||||||
|
bool turbo = false;
|
||||||
float target = 40.0f;
|
float target = 40.0f;
|
||||||
float hysteresis = 0.5f;
|
float hysteresis = 0.5f;
|
||||||
} heating;
|
} heating;
|
||||||
@@ -39,9 +46,9 @@ struct Settings {
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = false;
|
bool enable = false;
|
||||||
float n_factor = 0.67f;
|
float n_factor = 0.7f;
|
||||||
float k_factor = 1.0f;
|
float k_factor = 3.0f;
|
||||||
float t_factor = 5.0f;
|
float t_factor = 2.0f;
|
||||||
} equitherm;
|
} equitherm;
|
||||||
|
|
||||||
} settings;
|
} settings;
|
||||||
@@ -61,6 +68,7 @@ struct Variables {
|
|||||||
bool fault = false;
|
bool fault = false;
|
||||||
bool diagnostic = false;
|
bool diagnostic = false;
|
||||||
byte faultCode = 0;
|
byte faultCode = 0;
|
||||||
|
int8_t rssi = 0;
|
||||||
} states;
|
} states;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
// #include <WiFiClient.h>
|
|
||||||
#define WM_MDNS
|
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
//#include <ESP8266mDNS.h>
|
|
||||||
//#include <WiFiUdp.h>
|
|
||||||
|
|
||||||
// Wifimanager
|
// Wifimanager
|
||||||
WiFiManager wm;
|
WiFiManager wm;
|
||||||
WiFiManagerParameter* wmHostname;
|
WiFiManagerParameter* wmHostname;
|
||||||
|
WiFiManagerParameter* wmOtInPin;
|
||||||
|
WiFiManagerParameter* wmOtOutPin;
|
||||||
|
WiFiManagerParameter* wmOtMemberIdCode;
|
||||||
WiFiManagerParameter* wmMqttServer;
|
WiFiManagerParameter* wmMqttServer;
|
||||||
WiFiManagerParameter* wmMqttPort;
|
WiFiManagerParameter* wmMqttPort;
|
||||||
WiFiManagerParameter* wmMqttUser;
|
WiFiManagerParameter* wmMqttUser;
|
||||||
@@ -25,6 +24,18 @@ protected:
|
|||||||
wmHostname = new WiFiManagerParameter("hostname", "Hostname", settings.hostname, 80);
|
wmHostname = new WiFiManagerParameter("hostname", "Hostname", settings.hostname, 80);
|
||||||
wm.addParameter(wmHostname);
|
wm.addParameter(wmHostname);
|
||||||
|
|
||||||
|
sprintf(buffer, "%d", settings.opentherm.inPin);
|
||||||
|
wmOtInPin = new WiFiManagerParameter("ot_in_pin", "Opentherm pin IN", buffer, 1);
|
||||||
|
wm.addParameter(wmOtInPin);
|
||||||
|
|
||||||
|
sprintf(buffer, "%d", settings.opentherm.outPin);
|
||||||
|
wmOtOutPin = new WiFiManagerParameter("ot_out_pin", "Opentherm pin OUT", buffer, 1);
|
||||||
|
wm.addParameter(wmOtOutPin);
|
||||||
|
|
||||||
|
sprintf(buffer, "%d", settings.opentherm.memberIdCode);
|
||||||
|
wmOtMemberIdCode = new WiFiManagerParameter("ot_member_id_code", "Opentherm member id code", buffer, 5);
|
||||||
|
wm.addParameter(wmOtMemberIdCode);
|
||||||
|
|
||||||
wmMqttServer = new WiFiManagerParameter("mqtt_server", "MQTT server", settings.mqtt.server, 80);
|
wmMqttServer = new WiFiManagerParameter("mqtt_server", "MQTT server", settings.mqtt.server, 80);
|
||||||
wm.addParameter(wmMqttServer);
|
wm.addParameter(wmMqttServer);
|
||||||
|
|
||||||
@@ -46,15 +57,22 @@ protected:
|
|||||||
|
|
||||||
wm.setHostname(settings.hostname);
|
wm.setHostname(settings.hostname);
|
||||||
wm.setWiFiAutoReconnect(true);
|
wm.setWiFiAutoReconnect(true);
|
||||||
|
wm.setAPClientCheck(true);
|
||||||
wm.setConfigPortalBlocking(false);
|
wm.setConfigPortalBlocking(false);
|
||||||
wm.setSaveParamsCallback(saveParamsCallback);
|
wm.setSaveParamsCallback(saveParamsCallback);
|
||||||
wm.setConfigPortalTimeout(300);
|
wm.setConfigPortalTimeout(180);
|
||||||
wm.setDisableConfigPortal(false);
|
//wm.setDisableConfigPortal(false);
|
||||||
|
|
||||||
wm.autoConnect(AP_SSID);
|
wm.autoConnect(AP_SSID, AP_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
static unsigned short prevCounterLiters = 0;
|
||||||
|
|
||||||
|
/*if (WiFi.status() != WL_CONNECTED && !wm.getWebPortalActive() && !wm.getConfigPortalActive()) {
|
||||||
|
wm.autoConnect(AP_SSID);
|
||||||
|
}*/
|
||||||
|
|
||||||
if (connected && WiFi.status() != WL_CONNECTED) {
|
if (connected && WiFi.status() != WL_CONNECTED) {
|
||||||
connected = false;
|
connected = false;
|
||||||
INFO("[wifi] Disconnected");
|
INFO("[wifi] Disconnected");
|
||||||
@@ -62,6 +80,10 @@ protected:
|
|||||||
} else if (!connected && WiFi.status() == WL_CONNECTED) {
|
} else if (!connected && WiFi.status() == WL_CONNECTED) {
|
||||||
connected = true;
|
connected = true;
|
||||||
|
|
||||||
|
if (wm.getConfigPortalActive()) {
|
||||||
|
wm.stopConfigPortal();
|
||||||
|
}
|
||||||
|
|
||||||
INFO_F("[wifi] Connected. IP address: %s, RSSI: %d\n", WiFi.localIP().toString().c_str(), WiFi.RSSI());
|
INFO_F("[wifi] Connected. IP address: %s, RSSI: %d\n", WiFi.localIP().toString().c_str(), WiFi.RSSI());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,14 +95,34 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void static saveParamsCallback() {
|
void static saveParamsCallback() {
|
||||||
strcpy(settings.hostname, (*wmHostname).getValue());
|
strcpy(settings.hostname, wmHostname->getValue());
|
||||||
strcpy(settings.mqtt.server, (*wmMqttServer).getValue());
|
settings.opentherm.inPin = atoi(wmOtInPin->getValue());
|
||||||
settings.mqtt.port = atoi((*wmMqttPort).getValue());
|
settings.opentherm.outPin = atoi(wmOtOutPin->getValue());
|
||||||
strcpy(settings.mqtt.user, (*wmMqttUser).getValue());
|
settings.opentherm.memberIdCode = atoi(wmOtMemberIdCode->getValue());
|
||||||
strcpy(settings.mqtt.password, (*wmMqttPassword).getValue());
|
strcpy(settings.mqtt.server, wmMqttServer->getValue());
|
||||||
strcpy(settings.mqtt.prefix, (*wmMqttPrefix).getValue());
|
settings.mqtt.port = atoi(wmMqttPort->getValue());
|
||||||
|
strcpy(settings.mqtt.user, wmMqttUser->getValue());
|
||||||
|
strcpy(settings.mqtt.password, wmMqttPassword->getValue());
|
||||||
|
strcpy(settings.mqtt.prefix, wmMqttPrefix->getValue());
|
||||||
|
|
||||||
INFO_F("Settings\nHostname: %s, Server: %s, port: %d, user: %s, pass: %s\n", settings.hostname, settings.mqtt.server, settings.mqtt.port, settings.mqtt.user, settings.mqtt.password);
|
INFO_F(
|
||||||
|
"New settings:\r\n"
|
||||||
|
" Hostname: %s\r\n"
|
||||||
|
" OT in pin: %d"
|
||||||
|
" OT out pin: %d"
|
||||||
|
" OT member id code: %d"
|
||||||
|
" Mqtt server: %s:%d\r\n"
|
||||||
|
" Mqtt user: %s\r\n"
|
||||||
|
" Mqtt pass: %s\r\n",
|
||||||
|
settings.hostname,
|
||||||
|
settings.opentherm.inPin,
|
||||||
|
settings.opentherm.outPin,
|
||||||
|
settings.opentherm.memberIdCode,
|
||||||
|
settings.mqtt.server,
|
||||||
|
settings.mqtt.port,
|
||||||
|
settings.mqtt.user,
|
||||||
|
settings.mqtt.password
|
||||||
|
);
|
||||||
eeSettings.updateNow();
|
eeSettings.updateNow();
|
||||||
INFO(F("Settings saved"));
|
INFO(F("Settings saved"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
#define OT_GATEWAY_VERSION "1.0.7"
|
#define OT_GATEWAY_VERSION "1.2.1"
|
||||||
#define AP_SSID "OpenTherm Gateway"
|
#define AP_SSID "OpenTherm Gateway"
|
||||||
|
#define AP_PASSWORD "otgateway123456"
|
||||||
#define USE_TELNET
|
#define USE_TELNET
|
||||||
|
|
||||||
#define EMERGENCY_TRESHOLD 10
|
#define EMERGENCY_TIME_TRESHOLD 120000
|
||||||
#define MQTT_RECONNECT_INTERVAL 5000
|
#define MQTT_RECONNECT_INTERVAL 5000
|
||||||
#define MQTT_KEEPALIVE 30
|
#define MQTT_KEEPALIVE 30
|
||||||
|
|
||||||
#define OPENTHERM_IN_PIN 4
|
|
||||||
#define OPENTHERM_OUT_PIN 5
|
|
||||||
#define OPENTHERM_OFFLINE_TRESHOLD 10
|
#define OPENTHERM_OFFLINE_TRESHOLD 10
|
||||||
|
|
||||||
#define DS18B20_PIN 2
|
#define DS18B20_PIN 2
|
||||||
@@ -16,6 +15,9 @@
|
|||||||
#define DS_CHECK_CRC true
|
#define DS_CHECK_CRC true
|
||||||
#define DS_CRC_USE_TABLE true
|
#define DS_CRC_USE_TABLE true
|
||||||
|
|
||||||
|
#define LED_STATUS_PIN 13
|
||||||
|
#define LED_OT_RX_PIN 15
|
||||||
|
|
||||||
#define CONFIG_URL "http://%s/"
|
#define CONFIG_URL "http://%s/"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <OpenTherm.h>
|
|
||||||
|
|
||||||
extern SchedulerClass Scheduler;
|
|
||||||
|
|
||||||
class CustomOpenTherm : public OpenTherm {
|
|
||||||
private:
|
|
||||||
unsigned long send_ts = millis();
|
|
||||||
void(*handleSendRequestCallback)(unsigned long, unsigned long, OpenThermResponseStatus status, byte attempt);
|
|
||||||
|
|
||||||
public:
|
|
||||||
CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false) : OpenTherm(inPin, outPin, isSlave) {}
|
|
||||||
void setHandleSendRequestCallback(void(*handleSendRequestCallback)(unsigned long, unsigned long, OpenThermResponseStatus status, byte attempt)) {
|
|
||||||
this->handleSendRequestCallback = handleSendRequestCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long sendRequest(unsigned long request, byte attempts = 5, byte _attempt = 0) {
|
|
||||||
_attempt++;
|
|
||||||
while (send_ts > 0 && millis() - send_ts < 200) {
|
|
||||||
Scheduler.yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long _response;
|
|
||||||
if (!sendRequestAync(request)) {
|
|
||||||
_response = 0;
|
|
||||||
} else {
|
|
||||||
while (!isReady()) {
|
|
||||||
Scheduler.yield();
|
|
||||||
process();
|
|
||||||
}
|
|
||||||
|
|
||||||
_response = getLastResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handleSendRequestCallback != NULL) {
|
|
||||||
handleSendRequestCallback(request, _response, getLastResponseStatus(), _attempt);
|
|
||||||
}
|
|
||||||
|
|
||||||
send_ts = millis();
|
|
||||||
if (getLastResponseStatus() == OpenThermResponseStatus::SUCCESS || _attempt >= attempts) {
|
|
||||||
return _response;
|
|
||||||
} else {
|
|
||||||
return sendRequest(request, attempts, _attempt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <TelnetStream.h>
|
#include <TelnetStream.h>
|
||||||
@@ -52,7 +53,7 @@ void setup() {
|
|||||||
tMqtt = new MqttTask(false);
|
tMqtt = new MqttTask(false);
|
||||||
Scheduler.start(tMqtt);
|
Scheduler.start(tMqtt);
|
||||||
|
|
||||||
tOt = new OpenThermTask(true);
|
tOt = new OpenThermTask(false);
|
||||||
Scheduler.start(tOt);
|
Scheduler.start(tOt);
|
||||||
|
|
||||||
tSensors = new SensorsTask(false, DS18B20_INTERVAL);
|
tSensors = new SensorsTask(false, DS18B20_INTERVAL);
|
||||||
|
|||||||
Reference in New Issue
Block a user