Use platformio and more updates

This commit is contained in:
Yurii
2023-09-16 22:00:07 +03:00
parent 493490895c
commit 4f305a6459
19 changed files with 1479 additions and 1267 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,22 @@
extern MqttTask* tMqtt;
extern SensorsTask* tSensors;
extern OpenThermTask* tOt;
class MainTask : public Task {
class MainTask: public Task {
public:
MainTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
MainTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
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() {
static unsigned long lastHeapInfo = 0;
static unsigned short minFreeHeapSize = 65535;
if (eeSettings.tick()) {
INFO("Settings updated (EEPROM)");
}
@@ -20,12 +26,27 @@ protected:
tMqtt->enable();
}
if ( firstFailConnect != 0 ) {
firstFailConnect = 0;
}
vars.states.rssi = WiFi.RSSI();
} else {
if (tMqtt->isEnabled()) {
tMqtt->disable();
}
vars.states.emergency = true;
if (settings.emergency.enable && !vars.states.emergency) {
if (firstFailConnect == 0) {
firstFailConnect = millis();
}
if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) {
vars.states.emergency = true;
INFO("Emergency mode enabled");
}
}
}
if (!tSensors->isEnabled() && settings.outdoorTempSource == 2) {
@@ -34,6 +55,12 @@ protected:
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
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();
}
}
}
};

View File

@@ -8,15 +8,17 @@ PubSubClient client(espClient);
HomeAssistantHelper haHelper;
class MqttTask : public Task {
class MqttTask: public Task {
public:
MqttTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
MqttTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
protected:
unsigned long lastReconnectAttempt = 0;
unsigned short int reconnectAttempts = 0;
unsigned long firstFailConnect = 0;
void setup() {
DEBUG("[MQTT] Started");
client.setServer(settings.mqtt.server, settings.mqtt.port);
client.setCallback(__callback);
haHelper.setPrefix(settings.mqtt.prefix);
@@ -38,15 +40,21 @@ protected:
publishHaEntities();
publishNonStaticHaEntities(true);
reconnectAttempts = 0;
firstFailConnect = 0;
lastReconnectAttempt = 0;
} else {
INFO("Failed to connect to MQTT server\n");
if (!vars.states.emergency && ++reconnectAttempts >= EMERGENCY_TRESHOLD) {
vars.states.emergency = true;
INFO("Emergency mode enabled");
if (settings.emergency.enable && !vars.states.emergency) {
if (firstFailConnect == 0) {
firstFailConnect = millis();
}
if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) {
vars.states.emergency = true;
INFO("Emergency mode enabled");
}
}
forceARP();
@@ -117,6 +125,11 @@ protected:
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>())) {
settings.heating.target = round(doc["heating"]["target"].as<float>() * 10) / 10;
flag = true;
@@ -221,7 +234,7 @@ protected:
DEBUG("Received restart message...");
Scheduler.delay(10000);
DEBUG("Restart...");
eeSettings.updateNow();
ESP.restart();
}
@@ -272,6 +285,7 @@ protected:
// heating
haHelper.publishSwitchHeating(false);
haHelper.publishSwitchHeatingTurbo();
//haHelper.publishNumberHeatingTarget(false);
haHelper.publishNumberHeatingHysteresis();
haHelper.publishSensorHeatingSetpoint(false);
@@ -305,6 +319,7 @@ protected:
haHelper.publishBinSensorFault();
haHelper.publishBinSensorDiagnostic();
haHelper.publishSensorFaultCode();
haHelper.publishSensorRssi();
// sensors
haHelper.publishSensorModulation(false);
@@ -383,6 +398,7 @@ protected:
doc["emergency"]["useEquitherm"] = settings.emergency.useEquitherm;
doc["heating"]["enable"] = settings.heating.enable;
doc["heating"]["turbo"] = settings.heating.turbo;
doc["heating"]["target"] = settings.heating.target;
doc["heating"]["hysteresis"] = settings.heating.hysteresis;
@@ -420,6 +436,7 @@ protected:
doc["states"]["fault"] = vars.states.fault;
doc["states"]["diagnostic"] = vars.states.diagnostic;
doc["states"]["faultCode"] = vars.states.faultCode;
doc["states"]["rssi"] = vars.states.rssi;
doc["sensors"]["modulation"] = vars.sensors.modulation;
doc["sensors"]["pressure"] = vars.sensors.pressure;
@@ -454,7 +471,7 @@ protected:
if (settings.debug) {
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("\n");

View File

@@ -1,107 +1,133 @@
#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:
OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
OpenThermTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
protected:
void setup() {
ot.begin(handleInterrupt, responseCallback);
ot.setHandleSendRequestCallback(sendRequestCallback);
ot = new CustomOpenTherm(settings.opentherm.inPin, settings.opentherm.outPin);
ot->begin(handleInterrupt, responseCallback);
ot->setHandleSendRequestCallback(sendRequestCallback);
#ifdef LED_OT_RX_PIN
pinMode(LED_OT_RX_PIN, OUTPUT);
#endif
}
void loop() {
static byte currentHeatingTemp, currentDHWTemp = 0;
byte newHeatingTemp, newDHWTemp = 0;
unsigned long localResponse;
setMasterMemberIdCode();
DEBUG_F("Slave member id code: %u \n", vars.parameters.slaveMemberIdCode);
if ( setMasterMemberIdCode() ) {
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(
settings.heating.enable && pump,
settings.dhw.enable
} else {
WARN("Slave member id failed");
}
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;
}
vars.states.heating = ot.isCentralHeatingActive(localResponse);
vars.states.dhw = ot.isHotWaterActive(localResponse);
vars.states.flame = ot.isFlameOn(localResponse);
vars.states.fault = ot.isFault(localResponse);
vars.states.diagnostic = ot.isDiagnostic(localResponse);
INFO_F("Heating enabled: %d\r\n", heatingEnable);
setMaxModulationLevel(heatingEnable ? 100 : 0);
/*if (vars.dump_request.value)
{
testSupportedIDs();
vars.dump_request.value = false;
}*/
/*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;
}*/
vars.states.heating = ot->isCentralHeatingActive(localResponse);
vars.states.dhw = ot->isHotWaterActive(localResponse);
vars.states.flame = ot->isFlameOn(localResponse);
vars.states.fault = ot->isFault(localResponse);
vars.states.diagnostic = ot->isDiagnostic(localResponse);
yield();
// Команды чтения данных котла
if (millis() - prevUpdateNonEssentialVars > 30000) {
if (millis() - prevUpdateNonEssentialVars > 60000) {
updateSlaveParameters();
updateMasterParameters();
// crash?
DEBUG_F("Master type: %u, version: %u \n", vars.parameters.masterType, vars.parameters.masterVersion);
DEBUG_F("Slave type: %u, version: %u \n", vars.parameters.slaveType, vars.parameters.slaveVersion);
DEBUG_F("Master type: %u, version: %u\r\n", vars.parameters.masterType, vars.parameters.masterVersion);
DEBUG_F("Slave type: %u, version: %u\r\n", vars.parameters.slaveType, vars.parameters.slaveVersion);
updateMinMaxDhwTemp();
updateMinMaxHeatingTemp();
if (settings.outdoorTempSource == 0) {
updateOutsideTemp();
}
if (vars.states.fault) {
updateFaultCode();
ot->sendBoilerReset();
}
if ( vars.states.diagnostic ) {
ot->sendServiceReset();
}
updatePressure();
prevUpdateNonEssentialVars = millis();
yield();
}
updateHeatingTemp();
updateDHWTemp();
updateModulationLevel();
updatePressure();
if ( settings.dhw.enable || settings.heating.enable || heatingEnable ) {
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;
if (newDHWTemp != currentDHWTemp) {
byte newDHWTemp = settings.dhw.target;
if (settings.dhw.enable && newDHWTemp != currentDHWTemp) {
if (newDHWTemp < vars.parameters.dhwMinTemp || newDHWTemp > 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;
} else {
WARN("Failed set DHW temp");
}
}
//
// Температура отопления
if (fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) {
INFO_F("Set heating temp = %u \n", vars.parameters.heatingSetpoint);
if (heatingEnable && fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) {
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;
} else {
WARN("Failed set heating temp");
}
}
@@ -111,35 +137,51 @@ protected:
float halfHyst = settings.heating.hysteresis / 2;
if (pump && vars.temperatures.indoor - settings.heating.target + 0.0001 >= halfHyst) {
pump = false;
} else if (!pump && vars.temperatures.indoor - settings.heating.target - 0.0001 <= -(halfHyst)) {
pump = true;
}
} else if (!pump) {
pump = true;
}
}
void static IRAM_ATTR handleInterrupt() {
ot.handleInterrupt();
ot->handleInterrupt();
}
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) {
static byte attempt = 0;
switch (status) {
case OpenThermResponseStatus::TIMEOUT:
if (++attempt > OPENTHERM_OFFLINE_TRESHOLD) {
if (vars.states.otStatus && ++attempt > OPENTHERM_OFFLINE_TRESHOLD) {
vars.states.otStatus = false;
attempt = OPENTHERM_OFFLINE_TRESHOLD;
}
break;
case OpenThermResponseStatus::SUCCESS:
attempt = 0;
vars.states.otStatus = true;
if (!vars.states.otStatus) {
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;
default:
break;
}
@@ -148,9 +190,15 @@ protected:
protected:
bool pump = true;
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) {
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) {
//WARN(buffer);
DEBUG(buffer);
@@ -159,267 +207,7 @@ protected:
}
}
/*
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() {
bool setMasterMemberIdCode() {
//=======================================================================================
// Эта группа элементов данных определяет информацию о конфигурации как на ведомых, так
// и на главных сторонах. Каждый из них имеет группу флагов конфигурации (8 бит)
@@ -430,40 +218,88 @@ protected:
// с "кодом идентификатора участника", который идентифицирует производителя устройства.
//=======================================================================================
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0)); // 0xFFFF
if (!ot.isValidResponse(response)) {
return;
}
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0)); // 0xFFFF
/*uint8_t flags = (response & 0xFFFF) >> 8;
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
);*/
vars.parameters.slaveMemberIdCode = response >> 0 & 0xFF;
ot.sendRequest(ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, vars.parameters.slaveMemberIdCode));
if (ot->isValidResponse(response)) {
vars.parameters.slaveMemberIdCode = response & 0xFF;
} else if ( settings.opentherm.memberIdCode <= 0 ) {
return false;
}
response = ot->sendRequest(ot->buildRequest(
OpenThermRequestType::WRITE,
OpenThermMessageID::MConfigMMemberIDcode,
settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode
));
return ot->isValidResponse(response);
}
void updateMasterParameters() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, 0x013F));
if (!ot.isValidResponse(response)) {
return;
bool setMaxModulationLevel(byte value) {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MaxRelModLevelSetting, (unsigned int)(value * 256)));
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.masterVersion = response & 0xFF;
return true;
}
void updateSlaveParameters() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0));
if (!ot.isValidResponse(response)) {
return;
bool updateSlaveParameters() {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0));
if (!ot->isValidResponse(response)) {
return false;
}
vars.parameters.slaveType = (response & 0xFFFF) >> 8;
vars.parameters.slaveVersion = response & 0xFF;
return true;
}
void updateMinMaxDhwTemp() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::TdhwSetUBTdhwSetLB, 0));
if (!ot.isValidResponse(response)) {
return;
bool updateMinMaxDhwTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::TdhwSetUBTdhwSetLB, 0));
if (!ot->isValidResponse(response)) {
return false;
}
byte minTemp = response & 0xFF;
@@ -472,14 +308,17 @@ protected:
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
vars.parameters.dhwMinTemp = minTemp;
vars.parameters.dhwMaxTemp = maxTemp;
return true;
}
return false;
}
void updateMinMaxHeatingTemp() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::MaxTSetUBMaxTSetLB, 0));
if (!ot.isValidResponse(response)) {
return;
bool updateMinMaxHeatingTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::MaxTSetUBMaxTSetLB, 0));
if (!ot->isValidResponse(response)) {
return false;
}
byte minTemp = response & 0xFF;
@@ -488,55 +327,80 @@ protected:
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
vars.parameters.heatingMinTemp = minTemp;
vars.parameters.heatingMaxTemp = maxTemp;
return true;
}
return false;
}
void updateOutsideTemp() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0));
if (ot.isValidResponse(response)) {
vars.temperatures.outdoor = ot.getFloat(response);
bool updateOutsideTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0));
if (!ot->isValidResponse(response)) {
return false;
}
vars.temperatures.outdoor = ot->getFloat(response);
return true;
}
void updateHeatingTemp() {
unsigned long response = ot.sendRequest(ot.buildGetBoilerTemperatureRequest());
if (ot.isValidResponse(response)) {
vars.temperatures.heating = ot.getFloat(response);
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() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tdhw, 0));
if (ot.isValidResponse(response)) {
vars.temperatures.dhw = ot.getFloat(response);
bool updateDHWTemp() {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermMessageType::READ, OpenThermMessageID::Tdhw, 0));
if (!ot->isValidResponse(response)) {
return false;
}
vars.temperatures.dhw = ot->getFloat(response);
return true;
}
void updateFaultCode() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0));
bool updateFaultCode() {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0));
if (ot.isValidResponse(response)) {
vars.states.faultCode = response & 0xFF;
if (!ot->isValidResponse(response)) {
return false;
}
vars.states.faultCode = response & 0xFF;
return true;
}
void updateModulationLevel() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0));
bool updateModulationLevel() {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0));
if (ot.isValidResponse(response)) {
vars.sensors.modulation = ot.getFloat(response);
if (!ot->isValidResponse(response)) {
return false;
}
float modulation = ot->f88(response);
if (!vars.states.flame) {
vars.sensors.modulation = 0;
} else {
vars.sensors.modulation = modulation;
}
return true;
}
void updatePressure() {
unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0));
bool updatePressure() {
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0));
if (ot.isValidResponse(response)) {
vars.sensors.pressure = ot.getFloat(response);
if (!ot->isValidResponse(response)) {
return false;
}
vars.sensors.pressure = ot->getFloat(response);
return true;
}
};

View File

@@ -1,4 +1,4 @@
#include "lib/Equitherm.h"
#include <Equitherm.h>
#include <GyverPID.h>
#include <PIDtuner.h>
@@ -6,9 +6,9 @@ Equitherm etRegulator;
GyverPID pidRegulator(0, 0, 0, 10000);
PIDtuner pidTuner;
class RegulatorTask : public LeanTask {
class RegulatorTask: public LeanTask {
public:
RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
RegulatorTask(bool _enabled = false, unsigned long _interval = 0): LeanTask(_enabled, _interval) {}
protected:
bool tunerInit = false;
@@ -21,21 +21,39 @@ protected:
void setup() {}
void loop() {
byte newTemp;
byte newTemp = vars.parameters.heatingSetpoint;
if (vars.states.emergency) {
if (settings.heating.turbo) {
settings.heating.turbo = false;
INFO("[REGULATOR] Turbo mode auto disabled");
}
newTemp = getEmergencyModeTemp();
} 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();
if ( newTemp == 0 ) {
if (newTemp == 0) {
vars.tuning.enable = false;
}
}
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();
}
}
@@ -56,13 +74,13 @@ protected:
// if use equitherm
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) {
prevEtResult = 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 {
newTemp += prevEtResult;
@@ -81,19 +99,23 @@ protected:
if (fabs(prevHeatingTarget - settings.heating.target) > 0.0001) {
prevHeatingTarget = 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 (settings.equitherm.enable) {
float etResult = getEquithermTemp();
float etResult = getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp);
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
prevEtResult = 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 {
newTemp += prevEtResult;
@@ -102,13 +124,16 @@ protected:
// if use pid
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) {
prevPidResult = 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 {
newTemp += prevPidResult;
@@ -124,8 +149,8 @@ protected:
}
byte getTuningModeTemp() {
if ( tunerInit && (!vars.tuning.enable || vars.tuning.regulator != tunerRegulator) ) {
if ( tunerRegulator == 0 ) {
if (tunerInit && (!vars.tuning.enable || vars.tuning.regulator != tunerRegulator)) {
if (tunerRegulator == 0) {
pidTuner.reset();
}
@@ -135,19 +160,21 @@ protected:
INFO(F("[REGULATOR][TUNING] Stopped"));
}
if ( !vars.tuning.enable ) {
if (!vars.tuning.enable) {
return 0;
}
if ( vars.tuning.regulator == 0 ) {
if (vars.tuning.regulator == 0) {
// @TODO дописать
INFO(F("[REGULATOR][TUNING][EQUITHERM] Not implemented"));
return 0;
} else if ( vars.tuning.regulator == 1 ) {
} else if (vars.tuning.regulator == 1) {
// 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) {
INFO(F("[REGULATOR][TUNING][PID] Finished"));
@@ -203,8 +230,8 @@ protected:
}
}
float getEquithermTemp() {
if ( vars.states.emergency ) {
float getEquithermTemp(int minTemp, int maxTemp) {
if (vars.states.emergency) {
etRegulator.Kt = 0;
etRegulator.indoorTemp = 0;
etRegulator.outdoorTemp = vars.temperatures.outdoor;
@@ -215,12 +242,16 @@ protected:
etRegulator.outdoorTemp = round(vars.temperatures.outdoor);
} else {
etRegulator.Kt = settings.equitherm.t_factor;
if (settings.heating.turbo) {
etRegulator.Kt = 10;
} else {
etRegulator.Kt = settings.equitherm.t_factor;
}
etRegulator.indoorTemp = vars.temperatures.indoor;
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 = tuneEquithermN(etRegulator.Kn, vars.temperatures.indoor, settings.heating.target, 300, 1800, 0.01, 1);
etRegulator.Kk = settings.equitherm.k_factor;
@@ -229,12 +260,12 @@ protected:
return etRegulator.getResult();
}
float getPidTemp() {
float getPidTemp(int minTemp, int maxTemp) {
pidRegulator.Kp = settings.pid.p_factor;
pidRegulator.Ki = settings.pid.i_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.setpoint = settings.heating.target;

View File

@@ -2,9 +2,9 @@
MicroDS18B20<DS18B20_PIN> outdoorSensor;
class SensorsTask : public LeanTask {
class SensorsTask: public LeanTask {
public:
SensorsTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
SensorsTask(bool _enabled = false, unsigned long _interval = 0): LeanTask(_enabled, _interval) {}
protected:
float filteredOutdoorTemp = 0;
@@ -16,9 +16,9 @@ protected:
if (outdoorSensor.online()) {
if (outdoorSensor.readTemp()) {
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;
emptyOutdoorTemp = false;
@@ -27,8 +27,8 @@ protected:
}
filteredOutdoorTemp = floor(filteredOutdoorTemp * 100) / 100;
if ( fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099 ) {
if (fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099) {
vars.temperatures.outdoor = filteredOutdoorTemp;
INFO_F("[SENSORS][DS18B20] New temp: %f \n", filteredOutdoorTemp);
}

View File

@@ -4,6 +4,12 @@ struct Settings {
byte outdoorTempSource = 0;
char hostname[80] = "opentherm";
struct {
byte inPin = 5;
byte outPin = 4;
unsigned int memberIdCode = 4;
} opentherm;
struct {
char server[80];
int port = 1883;
@@ -21,6 +27,7 @@ struct Settings {
struct {
bool enable = true;
bool turbo = false;
float target = 40.0f;
float hysteresis = 0.5f;
} heating;
@@ -39,9 +46,9 @@ struct Settings {
struct {
bool enable = false;
float n_factor = 0.67f;
float k_factor = 1.0f;
float t_factor = 5.0f;
float n_factor = 0.7f;
float k_factor = 3.0f;
float t_factor = 2.0f;
} equitherm;
} settings;
@@ -61,6 +68,7 @@ struct Variables {
bool fault = false;
bool diagnostic = false;
byte faultCode = 0;
int8_t rssi = 0;
} states;
struct {

View File

@@ -1,21 +1,20 @@
// #include <WiFiClient.h>
#define WM_MDNS
#include <WiFiManager.h>
//#include <ESP8266mDNS.h>
//#include <WiFiUdp.h>
// Wifimanager
WiFiManager wm;
WiFiManagerParameter *wmHostname;
WiFiManagerParameter *wmMqttServer;
WiFiManagerParameter *wmMqttPort;
WiFiManagerParameter *wmMqttUser;
WiFiManagerParameter *wmMqttPassword;
WiFiManagerParameter *wmMqttPrefix;
WiFiManagerParameter* wmHostname;
WiFiManagerParameter* wmOtInPin;
WiFiManagerParameter* wmOtOutPin;
WiFiManagerParameter* wmOtMemberIdCode;
WiFiManagerParameter* wmMqttServer;
WiFiManagerParameter* wmMqttPort;
WiFiManagerParameter* wmMqttUser;
WiFiManagerParameter* wmMqttPassword;
WiFiManagerParameter* wmMqttPrefix;
class WifiManagerTask : public Task {
class WifiManagerTask: public Task {
public:
WifiManagerTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
WifiManagerTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {}
protected:
void setup() {
@@ -25,6 +24,18 @@ protected:
wmHostname = new WiFiManagerParameter("hostname", "Hostname", settings.hostname, 80);
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);
wm.addParameter(wmMqttServer);
@@ -46,15 +57,22 @@ protected:
wm.setHostname(settings.hostname);
wm.setWiFiAutoReconnect(true);
wm.setAPClientCheck(true);
wm.setConfigPortalBlocking(false);
wm.setSaveParamsCallback(saveParamsCallback);
wm.setConfigPortalTimeout(300);
wm.setDisableConfigPortal(false);
wm.setConfigPortalTimeout(180);
//wm.setDisableConfigPortal(false);
wm.autoConnect(AP_SSID);
wm.autoConnect(AP_SSID, AP_PASSWORD);
}
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) {
connected = false;
INFO("[wifi] Disconnected");
@@ -62,6 +80,10 @@ protected:
} else if (!connected && WiFi.status() == WL_CONNECTED) {
connected = true;
if (wm.getConfigPortalActive()) {
wm.stopConfigPortal();
}
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() {
strcpy(settings.hostname, (*wmHostname).getValue());
strcpy(settings.mqtt.server, (*wmMqttServer).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());
strcpy(settings.hostname, wmHostname->getValue());
settings.opentherm.inPin = atoi(wmOtInPin->getValue());
settings.opentherm.outPin = atoi(wmOtOutPin->getValue());
settings.opentherm.memberIdCode = atoi(wmOtMemberIdCode->getValue());
strcpy(settings.mqtt.server, wmMqttServer->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();
INFO(F("Settings saved"));
}

View File

@@ -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_PASSWORD "otgateway123456"
#define USE_TELNET
#define EMERGENCY_TRESHOLD 10
#define EMERGENCY_TIME_TRESHOLD 120000
#define MQTT_RECONNECT_INTERVAL 5000
#define MQTT_KEEPALIVE 30
#define OPENTHERM_IN_PIN 4
#define OPENTHERM_OUT_PIN 5
#define OPENTHERM_OFFLINE_TRESHOLD 10
#define DS18B20_PIN 2
@@ -16,6 +15,9 @@
#define DS_CHECK_CRC true
#define DS_CRC_USE_TABLE true
#define LED_STATUS_PIN 13
#define LED_OT_RX_PIN 15
#define CONFIG_URL "http://%s/"

View File

@@ -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);
}
}
};

View File

@@ -1,64 +0,0 @@
#pragma once
#include <Arduino.h>
#if defined(EQUITHERM_INTEGER)
// расчёты с целыми числами
typedef int datatype;
#else
// расчёты с float числами
typedef float datatype;
#endif
class Equitherm {
public:
Equitherm() {}
// kn, kk, kt
Equitherm(float new_kn, float new_kk, float new_kt) {
Kn = new_kn;
Kk = new_kk;
Kt = new_kt;
}
// лимит выходной величины
void setLimits(int min_output, int max_output) {
_minOut = min_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 output = getResultN() + getResultK() + getResultT();
output = constrain(output, _minOut, _maxOut); // ограничиваем выход
return output;
}
// температура контура отопления в зависимости от наружной температуры
datatype getResultN() {
float a = (-0.21 * Kn) - 0.06; // a = -0,21k — 0,06
float b = (6.04 * Kn) + 1.98; // b = 6,04k + 1,98
float c = (-5.06 * Kn) + 18.06; // с = -5,06k + 18,06
float x = (-0.2 * outdoorTemp) + 5; // x = -0.2*t1 + 5
return (a * x * x) + (b * x) + c; // Tn = ax2 + bx + c
}
// поправка на желаемую комнатную температуру
datatype getResultK() {
return (targetTemp - 20) * Kk;
}
// Расчет поправки (ошибки) термостата
datatype getResultT() {
return (targetTemp - indoorTemp) * Kt;
}
private:
int _minOut = 20, _maxOut = 90;
};

View File

@@ -1,3 +1,4 @@
#include <Arduino.h>
#include "defines.h"
#include <ArduinoJson.h>
#include <TelnetStream.h>
@@ -52,7 +53,7 @@ void setup() {
tMqtt = new MqttTask(false);
Scheduler.start(tMqtt);
tOt = new OpenThermTask(true);
tOt = new OpenThermTask(false);
Scheduler.start(tOt);
tSensors = new SensorsTask(false, DS18B20_INTERVAL);