mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-30 03:49:13 +03:00
модуль SmartBoiler v2
This commit is contained in:
@@ -3,13 +3,13 @@
|
|||||||
#define SLAVE true
|
#define SLAVE true
|
||||||
#define TIMEOUT_TRESHOLD 5
|
#define TIMEOUT_TRESHOLD 5
|
||||||
|
|
||||||
namespace _Boiler
|
namespace _Boiler_v2
|
||||||
{
|
{
|
||||||
// команды/установки от термостата
|
// команды/установки от термостата
|
||||||
struct SetpointBoiler
|
struct SetpointBoiler
|
||||||
{
|
{
|
||||||
uint8_t cmd_chEnable = 0;
|
bool cmd_chEnable = 0;
|
||||||
uint8_t cmd_dhwEnable = 0;
|
bool cmd_dhwEnable = 0;
|
||||||
float TSetCH = 0;
|
float TSetCH = 0;
|
||||||
float TSetDhw = 0;
|
float TSetDhw = 0;
|
||||||
} set;
|
} set;
|
||||||
@@ -22,33 +22,36 @@ namespace _Boiler
|
|||||||
bool gas_fault = 0;
|
bool gas_fault = 0;
|
||||||
bool air_fault = 0;
|
bool air_fault = 0;
|
||||||
bool water_overtemp = 0;
|
bool water_overtemp = 0;
|
||||||
uint8_t fault_code = 0;
|
int fault_code = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// текущее реальное состояние котла
|
// текущее реальное состояние котла
|
||||||
struct StateBoiler
|
struct StateBoiler
|
||||||
{
|
{
|
||||||
uint8_t stateCH = 0;
|
bool antiFreezOn = false;
|
||||||
uint8_t stateDHW = 0;
|
bool stateCH = 0;
|
||||||
uint8_t fl_flame = 0;
|
bool stateDHW = 0;
|
||||||
uint8_t currentRele = 0;
|
bool fl_flame = 0;
|
||||||
uint8_t fl_fail = 0;
|
int currentRele = 0;
|
||||||
|
bool fl_fail = 0;
|
||||||
failCode fCode;
|
failCode fCode;
|
||||||
float RelModLevel = 0;
|
float RelModLevel = 0;
|
||||||
float Tboiler = -40;
|
float Tboiler = -40;
|
||||||
float Tret = 0;
|
float Tret = 0;
|
||||||
float Tdhw = 0;
|
float Tdhw = 0;
|
||||||
float Toutside = 0;
|
float Toutside = 0;
|
||||||
bool r[3] = {0, 0, 0};
|
// bool r[3] = {0, 0, 0};
|
||||||
|
int numStepOn;
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
// конфигурация котла
|
// конфигурация котла
|
||||||
struct ConfigBoiler
|
struct ConfigBoiler
|
||||||
{
|
{
|
||||||
|
bool autoPower = true; // если false то управление только из сценария или веба
|
||||||
int antiFreez;
|
int antiFreez;
|
||||||
bool pump = false; // 1- наличие реле насоса СО, 0 - мы не управляем насосом СО (в протоколе ОТ нет)
|
// bool pump = false; // 1- наличие реле насоса СО, 0 - мы не управляем насосом СО (в протоколе ОТ нет)
|
||||||
bool changeRele = false;
|
bool changeRele = false;
|
||||||
// bool dhw = false; // 1- есть реле(трехходовой) ГВС
|
bool dhw = false; // 1- есть поддержка ГВС, по наличию реле(трехходовой)
|
||||||
bool ctrlType = false; // 0 - модуляция, 1- вкл/выкл
|
bool ctrlType = false; // 0 - модуляция, 1- вкл/выкл
|
||||||
bool confDhw = false; // 1 - бак, 0 - проточная //TODO ПОКА НЕ ЗНАЮ ЧТО ДЕЛАТЬ
|
bool confDhw = false; // 1 - бак, 0 - проточная //TODO ПОКА НЕ ЗНАЮ ЧТО ДЕЛАТЬ
|
||||||
bool pumpControlMaster = false; // в протоколе ОТ: мастер управляет насосом ????????????????????? //TODO Команды кправления насосом от мастера не помню
|
bool pumpControlMaster = false; // в протоколе ОТ: мастер управляет насосом ????????????????????? //TODO Команды кправления насосом от мастера не помню
|
||||||
@@ -62,17 +65,18 @@ namespace _Boiler
|
|||||||
int gistCH;
|
int gistCH;
|
||||||
|
|
||||||
int countRele = 0;
|
int countRele = 0;
|
||||||
int relePwr[3]={0,0,0};
|
// int relePwr[3]={0,0,0};
|
||||||
int prcOnekWt = 0; // процент одного киловата из общей мощности всех тэнев, расчитывается для модуляции
|
int prcOnekWt = 0; // процент одного киловата из общей мощности всех тэнев, расчитывается для модуляции
|
||||||
// int rele2Pwr = 0;
|
// int rele2Pwr = 0;
|
||||||
// int rele3Pwr = 0;
|
// int rele3Pwr = 0;
|
||||||
|
int numStepDhw;
|
||||||
|
float maxKW;
|
||||||
} conf;
|
} conf;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long timeout_count = 0;
|
unsigned long timeout_count = 0;
|
||||||
|
|
||||||
int _debug = 0;
|
bool _debug = 0;
|
||||||
bool _telegram = false;
|
bool _telegram = false;
|
||||||
unsigned long ot_response = 0;
|
unsigned long ot_response = 0;
|
||||||
|
int SlaveMemberIDcode = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
812
src/modules/exec/SmartBoiler/OpenTherm.cpp
Normal file
812
src/modules/exec/SmartBoiler/OpenTherm.cpp
Normal file
@@ -0,0 +1,812 @@
|
|||||||
|
/*
|
||||||
|
OpenTherm.cpp - OpenTherm Communication Library For Arduino, ESP8266
|
||||||
|
Copyright 2018, Ihor Melnyk
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "OpenTherm.h"
|
||||||
|
|
||||||
|
OpenTherm::OpenTherm(int inPin, int outPin, bool isSlave) : status(OpenThermStatus::NOT_INITIALIZED),
|
||||||
|
inPin(inPin),
|
||||||
|
outPin(outPin),
|
||||||
|
isSlave(isSlave),
|
||||||
|
response(0),
|
||||||
|
responseStatus(OpenThermResponseStatus::NONE),
|
||||||
|
responseTimestamp(0),
|
||||||
|
handleInterruptCallback(NULL),
|
||||||
|
processResponseCallback(NULL)
|
||||||
|
{
|
||||||
|
imitFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTherm::begin(void (*handleInterruptCallback)(void), void (*processResponseCallback)(unsigned long, OpenThermResponseStatus))
|
||||||
|
{
|
||||||
|
pinMode(inPin, INPUT);
|
||||||
|
pinMode(outPin, OUTPUT);
|
||||||
|
if (handleInterruptCallback != NULL)
|
||||||
|
{
|
||||||
|
this->handleInterruptCallback = handleInterruptCallback;
|
||||||
|
attachInterrupt(digitalPinToInterrupt(inPin), handleInterruptCallback, CHANGE);
|
||||||
|
}
|
||||||
|
activateBoiler();
|
||||||
|
status = OpenThermStatus::READY;
|
||||||
|
this->processResponseCallback = processResponseCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTherm::begin(void (*handleInterruptCallback)(void))
|
||||||
|
{
|
||||||
|
begin(handleInterruptCallback, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRAM_ATTR OpenTherm::isReady()
|
||||||
|
{
|
||||||
|
return status == OpenThermStatus::READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IRAM_ATTR OpenTherm::readState()
|
||||||
|
{
|
||||||
|
return digitalRead(inPin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTherm::setActiveState()
|
||||||
|
{
|
||||||
|
digitalWrite(outPin, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTherm::setIdleState()
|
||||||
|
{
|
||||||
|
digitalWrite(outPin, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTherm::activateBoiler()
|
||||||
|
{
|
||||||
|
setIdleState();
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTherm::sendBit(bool high)
|
||||||
|
{
|
||||||
|
if (high)
|
||||||
|
setActiveState();
|
||||||
|
else
|
||||||
|
setIdleState();
|
||||||
|
delayMicroseconds(500);
|
||||||
|
if (high)
|
||||||
|
setIdleState();
|
||||||
|
else
|
||||||
|
setActiveState();
|
||||||
|
delayMicroseconds(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::sendRequestAync(unsigned long request)
|
||||||
|
{
|
||||||
|
// Serial.println("Request: " + String(request, HEX));
|
||||||
|
noInterrupts();
|
||||||
|
const bool ready = isReady();
|
||||||
|
interrupts();
|
||||||
|
|
||||||
|
if (!ready)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
status = OpenThermStatus::REQUEST_SENDING;
|
||||||
|
response = 0;
|
||||||
|
responseStatus = OpenThermResponseStatus::NONE;
|
||||||
|
// Prevent switching to other tasks as there is a delay within sendBit
|
||||||
|
#ifdef ESP32
|
||||||
|
// vTaskSuspendAll();
|
||||||
|
#endif
|
||||||
|
sendBit(HIGH); // start bit
|
||||||
|
for (int i = 31; i >= 0; i--)
|
||||||
|
{
|
||||||
|
sendBit(bitRead(request, i));
|
||||||
|
}
|
||||||
|
sendBit(HIGH); // stop bit
|
||||||
|
setIdleState();
|
||||||
|
#ifdef ESP32
|
||||||
|
// xTaskResumeAll();
|
||||||
|
#endif
|
||||||
|
status = OpenThermStatus::RESPONSE_WAITING;
|
||||||
|
responseTimestamp = micros();
|
||||||
|
if (imitFlag)
|
||||||
|
ImitationResponse(request);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long OpenTherm::sendRequest(unsigned long request)
|
||||||
|
{
|
||||||
|
if (!sendRequestAync(request))
|
||||||
|
return 0;
|
||||||
|
while (!isReady())
|
||||||
|
{
|
||||||
|
process();
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::sendResponse(unsigned long request)
|
||||||
|
{
|
||||||
|
status = OpenThermStatus::REQUEST_SENDING;
|
||||||
|
response = 0;
|
||||||
|
responseStatus = OpenThermResponseStatus::NONE;
|
||||||
|
// Prevent switching to other tasks as there is a delay within sendBit
|
||||||
|
#ifdef ESP32
|
||||||
|
// vTaskSuspendAll();
|
||||||
|
#endif
|
||||||
|
sendBit(HIGH); // start bit
|
||||||
|
for (int i = 31; i >= 0; i--)
|
||||||
|
{
|
||||||
|
sendBit(bitRead(request, i));
|
||||||
|
}
|
||||||
|
sendBit(HIGH); // stop bit
|
||||||
|
setIdleState();
|
||||||
|
#ifdef ESP32
|
||||||
|
// xTaskResumeAll();
|
||||||
|
#endif
|
||||||
|
status = OpenThermStatus::READY;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long OpenTherm::getLastResponse()
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenThermResponseStatus OpenTherm::getLastResponseStatus()
|
||||||
|
{
|
||||||
|
return responseStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRAM_ATTR OpenTherm::handleInterrupt()
|
||||||
|
{
|
||||||
|
if (isReady())
|
||||||
|
{
|
||||||
|
if (isSlave && readState() == HIGH)
|
||||||
|
{
|
||||||
|
status = OpenThermStatus::RESPONSE_WAITING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long newTs = micros();
|
||||||
|
if (status == OpenThermStatus::RESPONSE_WAITING)
|
||||||
|
{
|
||||||
|
if (readState() == HIGH)
|
||||||
|
{
|
||||||
|
status = OpenThermStatus::RESPONSE_START_BIT;
|
||||||
|
responseTimestamp = newTs;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error start bit / Ошибка стартового бита
|
||||||
|
status = OpenThermStatus::RESPONSE_INVALID;
|
||||||
|
responseTimestamp = newTs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (status == OpenThermStatus::RESPONSE_START_BIT)
|
||||||
|
{
|
||||||
|
if ((newTs - responseTimestamp < 750) && readState() == LOW)
|
||||||
|
{
|
||||||
|
status = OpenThermStatus::RESPONSE_RECEIVING;
|
||||||
|
responseTimestamp = newTs;
|
||||||
|
responseBitIndex = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error Start_bit LOW 750mks / Ошибка стартового бита по тылу (нет LOW через 750мкс)
|
||||||
|
status = OpenThermStatus::RESPONSE_INVALID;
|
||||||
|
responseTimestamp = newTs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (status == OpenThermStatus::RESPONSE_RECEIVING)
|
||||||
|
{
|
||||||
|
// unsigned long bitDuration = newTs - responseTimestamp;
|
||||||
|
// В новой спецификации стоповый бит не обязателен. Если не дождались, всё равно попробуем разобрать
|
||||||
|
if ((newTs - responseTimestamp) > 750 && (newTs - responseTimestamp) < 1300)
|
||||||
|
{
|
||||||
|
if (responseBitIndex < 32)
|
||||||
|
{
|
||||||
|
response = (response << 1) | !readState();
|
||||||
|
responseTimestamp = newTs;
|
||||||
|
responseBitIndex++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // stop bit
|
||||||
|
status = OpenThermStatus::RESPONSE_READY;
|
||||||
|
responseTimestamp = newTs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTherm::process()
|
||||||
|
{
|
||||||
|
noInterrupts();
|
||||||
|
OpenThermStatus st = status;
|
||||||
|
unsigned long ts = responseTimestamp;
|
||||||
|
interrupts();
|
||||||
|
|
||||||
|
if (st == OpenThermStatus::READY)
|
||||||
|
return;
|
||||||
|
unsigned long newTs = micros();
|
||||||
|
if (st != OpenThermStatus::NOT_INITIALIZED && st != OpenThermStatus::DELAY && (newTs - ts) > 1000000)
|
||||||
|
{
|
||||||
|
status = OpenThermStatus::READY;
|
||||||
|
responseStatus = OpenThermResponseStatus::TIMEOUT;
|
||||||
|
if (processResponseCallback != NULL)
|
||||||
|
{
|
||||||
|
processResponseCallback(response, responseStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (st == OpenThermStatus::RESPONSE_INVALID)
|
||||||
|
{
|
||||||
|
status = OpenThermStatus::DELAY;
|
||||||
|
responseStatus = OpenThermResponseStatus::INVALID;
|
||||||
|
if (processResponseCallback != NULL)
|
||||||
|
{
|
||||||
|
processResponseCallback(response, responseStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (st == OpenThermStatus::RESPONSE_READY)
|
||||||
|
{
|
||||||
|
status = OpenThermStatus::DELAY;
|
||||||
|
responseStatus = (isSlave ? isValidRequest(response) : isValidResponse(response)) ? OpenThermResponseStatus::SUCCESS : OpenThermResponseStatus::INVALID;
|
||||||
|
// Error msgType (READ_ACK | WRITE_ACK) is Header
|
||||||
|
if (processResponseCallback != NULL)
|
||||||
|
{
|
||||||
|
processResponseCallback(response, responseStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (st == OpenThermStatus::DELAY)
|
||||||
|
{
|
||||||
|
if ((newTs - ts) > 100000)
|
||||||
|
{
|
||||||
|
status = OpenThermStatus::READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::parity(unsigned long frame) // odd parity
|
||||||
|
{
|
||||||
|
byte p = 0;
|
||||||
|
while (frame > 0)
|
||||||
|
{
|
||||||
|
if (frame & 1)
|
||||||
|
p++;
|
||||||
|
frame = frame >> 1;
|
||||||
|
}
|
||||||
|
return (p & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenThermMessageType OpenTherm::getMessageType(unsigned long message)
|
||||||
|
{
|
||||||
|
OpenThermMessageType msg_type = static_cast<OpenThermMessageType>((message >> 28) & 7);
|
||||||
|
return msg_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenThermMessageID OpenTherm::getDataID(unsigned long frame)
|
||||||
|
{
|
||||||
|
return (OpenThermMessageID)((frame >> 16) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long OpenTherm::buildRequest(OpenThermMessageType type, OpenThermMessageID id, unsigned int data)
|
||||||
|
{
|
||||||
|
unsigned long request = data;
|
||||||
|
if (type == OpenThermMessageType::WRITE_DATA)
|
||||||
|
{
|
||||||
|
request |= 1ul << 28;
|
||||||
|
}
|
||||||
|
request |= ((unsigned long)id) << 16;
|
||||||
|
if (parity(request))
|
||||||
|
request |= (1ul << 31);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
unsigned long OpenTherm::buildRequestID(OpenThermMessageType type, unsigned int id, unsigned int data)
|
||||||
|
{
|
||||||
|
unsigned long request = data;
|
||||||
|
if (type == OpenThermMessageType::WRITE_DATA)
|
||||||
|
{
|
||||||
|
request |= 1ul << 28;
|
||||||
|
}
|
||||||
|
request |= ((unsigned long)id) << 16;
|
||||||
|
if (parity(request))
|
||||||
|
request |= (1ul << 31);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long OpenTherm::buildResponse(OpenThermMessageType type, OpenThermMessageID id, unsigned int data)
|
||||||
|
{
|
||||||
|
unsigned long response = data;
|
||||||
|
response |= ((unsigned long)type) << 28;
|
||||||
|
response |= ((unsigned long)id) << 16;
|
||||||
|
if (parity(response))
|
||||||
|
response |= (1ul << 31);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::isValidResponse(unsigned long response)
|
||||||
|
{
|
||||||
|
if (parity(response))
|
||||||
|
return false;
|
||||||
|
byte msgType = (response << 1) >> 29;
|
||||||
|
return msgType == READ_ACK || msgType == WRITE_ACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::isValidRequest(unsigned long request)
|
||||||
|
{
|
||||||
|
if (parity(request))
|
||||||
|
return false;
|
||||||
|
byte msgType = (request << 1) >> 29;
|
||||||
|
return msgType == READ_DATA || msgType == WRITE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTherm::end()
|
||||||
|
{
|
||||||
|
if (this->handleInterruptCallback != NULL)
|
||||||
|
{
|
||||||
|
detachInterrupt(digitalPinToInterrupt(inPin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *OpenTherm::statusToString(OpenThermResponseStatus status)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case NONE:
|
||||||
|
return "NONE";
|
||||||
|
case SUCCESS:
|
||||||
|
return "SUCCESS";
|
||||||
|
case INVALID:
|
||||||
|
return "INVALID";
|
||||||
|
case TIMEOUT:
|
||||||
|
return "TIMEOUT";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *OpenTherm::messageTypeToString(OpenThermMessageType message_type)
|
||||||
|
{
|
||||||
|
switch (message_type)
|
||||||
|
{
|
||||||
|
case READ_DATA:
|
||||||
|
return "READ_DATA";
|
||||||
|
case WRITE_DATA:
|
||||||
|
return "WRITE_DATA";
|
||||||
|
case INVALID_DATA:
|
||||||
|
return "INVALID_DATA";
|
||||||
|
case RESERVED:
|
||||||
|
return "RESERVED";
|
||||||
|
case READ_ACK:
|
||||||
|
return "READ_ACK";
|
||||||
|
case WRITE_ACK:
|
||||||
|
return "WRITE_ACK";
|
||||||
|
case DATA_INVALID:
|
||||||
|
return "DATA_INVALID";
|
||||||
|
case UNKNOWN_DATA_ID:
|
||||||
|
return "UNKNOWN_DATA_ID";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// building requests
|
||||||
|
|
||||||
|
unsigned long OpenTherm::buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2, bool enableSummerMode, bool dhwBlock)
|
||||||
|
{
|
||||||
|
unsigned int data = enableCentralHeating | (enableHotWater << 1) | (enableCooling << 2) | (enableOutsideTemperatureCompensation << 3) | (enableCentralHeating2 << 4) | (enableSummerMode << 5) | (dhwBlock << 6);
|
||||||
|
data <<= 8;
|
||||||
|
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Status, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long OpenTherm::buildSetBoilerTemperatureRequest(float temperature)
|
||||||
|
{
|
||||||
|
unsigned int data = temperatureToData(temperature);
|
||||||
|
return buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long OpenTherm::buildGetBoilerTemperatureRequest()
|
||||||
|
{
|
||||||
|
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tboiler, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsing responses
|
||||||
|
bool OpenTherm::isFault(unsigned long response)
|
||||||
|
{
|
||||||
|
return response & 0x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::isCentralHeatingActive(unsigned long response)
|
||||||
|
{
|
||||||
|
return response & 0x2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::isHotWaterActive(unsigned long response)
|
||||||
|
{
|
||||||
|
return response & 0x4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::isFlameOn(unsigned long response)
|
||||||
|
{
|
||||||
|
return response & 0x8;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::isCoolingActive(unsigned long response)
|
||||||
|
{
|
||||||
|
return response & 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::isDiagnostic(unsigned long response)
|
||||||
|
{
|
||||||
|
return response & 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t OpenTherm::getUInt(const unsigned long response) const
|
||||||
|
{
|
||||||
|
const uint16_t u88 = response & 0xffff;
|
||||||
|
return u88;
|
||||||
|
}
|
||||||
|
|
||||||
|
float OpenTherm::getFloat(const unsigned long response) const
|
||||||
|
{
|
||||||
|
const uint16_t u88 = getUInt(response);
|
||||||
|
const float f = (u88 & 0x8000) ? -(0x10000L - u88) / 256.0f : u88 / 256.0f;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int OpenTherm::temperatureToData(float temperature)
|
||||||
|
{
|
||||||
|
if (temperature < 0)
|
||||||
|
temperature = 0;
|
||||||
|
if (temperature > 100)
|
||||||
|
temperature = 100;
|
||||||
|
unsigned int data = (unsigned int)(temperature * 256);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic requests
|
||||||
|
|
||||||
|
unsigned long OpenTherm::setBoilerStatus(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2, bool enableSummerMode, bool dhwBlock)
|
||||||
|
{
|
||||||
|
return sendRequest(buildSetBoilerStatusRequest(enableCentralHeating, enableHotWater, enableCooling, enableOutsideTemperatureCompensation, enableCentralHeating2, enableSummerMode, dhwBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::setBoilerTemperature(float temperature)
|
||||||
|
{
|
||||||
|
unsigned long response = sendRequest(buildSetBoilerTemperatureRequest(temperature));
|
||||||
|
return isValidResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
float OpenTherm::getBoilerTemperature()
|
||||||
|
{
|
||||||
|
unsigned long response = sendRequest(buildGetBoilerTemperatureRequest());
|
||||||
|
return isValidResponse(response) ? getFloat(response) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float OpenTherm::getReturnTemperature()
|
||||||
|
{
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tret, 0));
|
||||||
|
return isValidResponse(response) ? getFloat(response) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenTherm::setDHWSetpoint(float temperature)
|
||||||
|
{
|
||||||
|
unsigned int data = temperatureToData(temperature);
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TdhwSet, data));
|
||||||
|
return isValidResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
float OpenTherm::getDHWTemperature()
|
||||||
|
{
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tdhw, 0));
|
||||||
|
return isValidResponse(response) ? getFloat(response) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float OpenTherm::getModulation()
|
||||||
|
{
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0));
|
||||||
|
return isValidResponse(response) ? getFloat(response) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float OpenTherm::getPressure()
|
||||||
|
{
|
||||||
|
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0));
|
||||||
|
return isValidResponse(response) ? getFloat(response) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char OpenTherm::getFault()
|
||||||
|
{
|
||||||
|
return ((sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0)) >> 8) & 0xff);
|
||||||
|
}
|
||||||
|
int8_t flame_timer = 0;
|
||||||
|
void OpenTherm::ImitationResponse(unsigned long request)
|
||||||
|
{
|
||||||
|
|
||||||
|
// unsigned long response;
|
||||||
|
unsigned int data = getUInt(request);
|
||||||
|
OpenThermMessageType msgType;
|
||||||
|
byte ID;
|
||||||
|
OpenThermMessageID id = getDataID(request);
|
||||||
|
uint8_t flags;
|
||||||
|
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case OpenThermMessageID::Status:
|
||||||
|
// Статус котла получен
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
static int8_t flame = 0;
|
||||||
|
flame_timer++;
|
||||||
|
if (flame_timer > 10)
|
||||||
|
flame = 1;
|
||||||
|
if (flame_timer > 20)
|
||||||
|
{
|
||||||
|
flame_timer = 0;
|
||||||
|
flame = 0;
|
||||||
|
}
|
||||||
|
static int8_t fault = 0;
|
||||||
|
// fault = 1 - fault;
|
||||||
|
data = (bool)fault | (true << 1) | (true << 2) | ((bool)flame << 3) | (false << 4);
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::SConfigSMemberIDcode:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::SlaveVersion:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::MasterVersion:
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::RelModLevel:
|
||||||
|
static float RelModLevel = 10;
|
||||||
|
// RelModLevel = RelModLevel > 100 ? 10 : RelModLevel + 1;
|
||||||
|
if (flame_timer < 11)
|
||||||
|
{
|
||||||
|
RelModLevel = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RelModLevel = RelModLevel == 0 ? 10 : RelModLevel + 1;
|
||||||
|
}
|
||||||
|
// data = RelModLevel;
|
||||||
|
data = temperatureToData(RelModLevel);
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Tboiler:
|
||||||
|
// Получили температуру котла
|
||||||
|
static float Tboiler = 40;
|
||||||
|
Tboiler = Tboiler > 60 ? 40 : Tboiler + 1;
|
||||||
|
data = temperatureToData(Tboiler);
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Tdhw:
|
||||||
|
// Получили температуру ГВС
|
||||||
|
static float Tdhw = 60;
|
||||||
|
Tdhw = Tdhw > 80 ? 60 : Tdhw + 1;
|
||||||
|
data = temperatureToData(Tdhw);
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Toutside:
|
||||||
|
// Получили внешнюю температуру
|
||||||
|
static float Toutside = -10;
|
||||||
|
Toutside = Toutside > 10 ? -10 : Toutside + 1;
|
||||||
|
data = temperatureToData(Toutside);
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::ASFflags:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpenThermMessageID::TdhwSetUBTdhwSetLB:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::MaxTSetUBMaxTSetLB:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpenThermMessageID::OEMDiagnosticCode:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpenThermMessageID::OpenThermVersionSlave:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpenThermMessageID::CHPressure:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::DHWFlowRate:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::DayTime:
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Date:
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Year:
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpenThermMessageID::Tret:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Tstorage:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Tcollector:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::TflowCH2:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Tdhw2:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Texhaust:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::TheatExchanger:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::BoilerFanSpeed:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::ElectricBurnerFlame:
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::BurnerStarts:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::CHPumpStarts:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::DHWPumpValveStarts:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::DHWBurnerStarts:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::BurnerOperationHours:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::CHPumpOperationHours:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::DHWPumpValveOperationHours:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::DHWBurnerOperationHours:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::RBPflags:
|
||||||
|
//
|
||||||
|
// Pre-Defined Remote Boiler Parameters
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::TdhwSet:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::TSet:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::MaxTSet:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::Hcratio:
|
||||||
|
//
|
||||||
|
if (getMessageType(request) == OpenThermMessageType::READ_DATA)
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
else
|
||||||
|
msgType = OpenThermMessageType::WRITE_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::TSP:
|
||||||
|
//
|
||||||
|
// Transparent Slave Parameters
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::FHBsize:
|
||||||
|
//
|
||||||
|
// Fault History Data
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::MaxCapacityMinModLevel:
|
||||||
|
//
|
||||||
|
// Boiler Sequencer Control
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::TrOverride:
|
||||||
|
//
|
||||||
|
// Remote override room setpoint
|
||||||
|
//
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
case OpenThermMessageID::RemoteOverrideFunction:
|
||||||
|
msgType = OpenThermMessageType::READ_ACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
msgType = OpenThermMessageType::UNKNOWN_DATA_ID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
response = buildResponse(msgType, id, data);
|
||||||
|
status = OpenThermStatus::RESPONSE_READY;
|
||||||
|
responseStatus = OpenThermResponseStatus::SUCCESS;
|
||||||
|
/*
|
||||||
|
if (processResponseCallback != NULL)
|
||||||
|
{
|
||||||
|
processResponseCallback(response, OpenThermResponseStatus::SUCCESS);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
208
src/modules/exec/SmartBoiler/OpenTherm.h
Normal file
208
src/modules/exec/SmartBoiler/OpenTherm.h
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
OpenTherm.h - OpenTherm Library for the ESP8266/Arduino platform
|
||||||
|
https://github.com/ihormelnyk/OpenTherm
|
||||||
|
http://ihormelnyk.com/pages/OpenTherm
|
||||||
|
Licensed under MIT license
|
||||||
|
Copyright 2018, Ihor Melnyk
|
||||||
|
|
||||||
|
Frame Structure:
|
||||||
|
P MGS-TYPE SPARE DATA-ID DATA-VALUE
|
||||||
|
0 000 0000 00000000 00000000 00000000
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OpenTherm_h
|
||||||
|
#define OpenTherm_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
enum OpenThermResponseStatus : uint8_t
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
SUCCESS,
|
||||||
|
INVALID,
|
||||||
|
TIMEOUT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OpenThermMessageType : uint8_t
|
||||||
|
{
|
||||||
|
/* Master to Slave */
|
||||||
|
READ_DATA = B000,
|
||||||
|
READ = READ_DATA, // for backwared compatibility
|
||||||
|
WRITE_DATA = B001,
|
||||||
|
WRITE = WRITE_DATA, // for backwared compatibility
|
||||||
|
INVALID_DATA = B010,
|
||||||
|
RESERVED = B011,
|
||||||
|
/* Slave to Master */
|
||||||
|
READ_ACK = B100,
|
||||||
|
WRITE_ACK = B101,
|
||||||
|
DATA_INVALID = B110,
|
||||||
|
UNKNOWN_DATA_ID = B111
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef OpenThermMessageType OpenThermRequestType; // for backwared compatibility
|
||||||
|
|
||||||
|
enum OpenThermMessageID : uint8_t
|
||||||
|
{
|
||||||
|
Status, // flag8 / flag8 Master and Slave Status flags.
|
||||||
|
TSet, // f8.8 Control setpoint ie CH water temperature setpoint (°C)
|
||||||
|
MConfigMMemberIDcode, // flag8 / u8 Master Configuration Flags / Master MemberID Code
|
||||||
|
SConfigSMemberIDcode, // flag8 / u8 Slave Configuration Flags / Slave MemberID Code
|
||||||
|
Command, // u8 / u8 Remote Command
|
||||||
|
ASFflags, // / OEM-fault-code flag8 / u8 Application-specific fault flags and OEM fault code
|
||||||
|
RBPflags, // flag8 / flag8 Remote boiler parameter transfer-enable & read/write flags
|
||||||
|
CoolingControl, // f8.8 Cooling control signal (%)
|
||||||
|
TsetCH2, // f8.8 Control setpoint for 2e CH circuit (°C)
|
||||||
|
TrOverride, // f8.8 Remote override room setpoint
|
||||||
|
TSP, // u8 / u8 Number of Transparent-Slave-Parameters supported by slave
|
||||||
|
TSPindexTSPvalue, // u8 / u8 Index number / Value of referred-to transparent slave parameter.
|
||||||
|
FHBsize, // u8 / u8 Size of Fault-History-Buffer supported by slave
|
||||||
|
FHBindexFHBvalue, // u8 / u8 Index number / Value of referred-to fault-history buffer entry.
|
||||||
|
MaxRelModLevelSetting, // f8.8 Maximum relative modulation level setting (%)
|
||||||
|
MaxCapacityMinModLevel, // u8 / u8 Maximum boiler capacity (kW) / Minimum boiler modulation level(%)
|
||||||
|
TrSet, // f8.8 Room Setpoint (°C)
|
||||||
|
RelModLevel, // f8.8 Relative Modulation Level (%)
|
||||||
|
CHPressure, // f8.8 Water pressure in CH circuit (bar)
|
||||||
|
DHWFlowRate, // f8.8 Water flow rate in DHW circuit. (litres/minute)
|
||||||
|
DayTime, // special / u8 Day of Week and Time of Day
|
||||||
|
Date, // u8 / u8 Calendar date
|
||||||
|
Year, // u16 Calendar year
|
||||||
|
TrSetCH2, // f8.8 Room Setpoint for 2nd CH circuit (°C)
|
||||||
|
Tr, // f8.8 Room temperature (°C)
|
||||||
|
Tboiler, // f8.8 Boiler flow water temperature (°C)
|
||||||
|
Tdhw, // f8.8 DHW temperature (°C)
|
||||||
|
Toutside, // f8.8 Outside temperature (°C)
|
||||||
|
Tret, // f8.8 Return water temperature (°C)
|
||||||
|
Tstorage, // f8.8 Solar storage temperature (°C)
|
||||||
|
Tcollector, // f8.8 Solar collector temperature (°C)
|
||||||
|
TflowCH2, // f8.8 Flow water temperature CH2 circuit (°C)
|
||||||
|
Tdhw2, // f8.8 Domestic hot water temperature 2 (°C)
|
||||||
|
Texhaust, // s16 Boiler exhaust temperature (°C)
|
||||||
|
TheatExchanger, // f8.8 Boiler heat exchanger temperature (°C)
|
||||||
|
BoilerFanSpeed, // u16 Boiler fan speed Setpiont and actual value
|
||||||
|
ElectricBurnerFlame, // f8.8?? Electric current through burner flame (mюA)
|
||||||
|
TdhwSetUBTdhwSetLB = 48, // s8 / s8 DHW setpoint upper & lower bounds for adjustment (°C)
|
||||||
|
MaxTSetUBMaxTSetLB, // s8 / s8 Max CH water setpoint upper & lower bounds for adjustment (°C)
|
||||||
|
HcratioUBHcratioLB, // s8 / s8 OTC heat curve ratio upper & lower bounds for adjustment
|
||||||
|
TdhwSet = 56, // f8.8 DHW setpoint (°C) (Remote parameter 1)
|
||||||
|
MaxTSet, // f8.8 Max CH water setpoint (°C) (Remote parameters 2)
|
||||||
|
Hcratio, // f8.8 OTC heat curve ratio (°C) (Remote parameter 3)
|
||||||
|
RemoteOverrideFunction = 100, // flag8 / - Function of manual and program changes in master and remote room setpoint.
|
||||||
|
OEMDiagnosticCode = 115, // u16 OEM-specific diagnostic/service code
|
||||||
|
BurnerStarts, // u16 Number of starts burner
|
||||||
|
CHPumpStarts, // u16 Number of starts CH pump
|
||||||
|
DHWPumpValveStarts, // u16 Number of starts DHW pump/valve
|
||||||
|
DHWBurnerStarts, // u16 Number of starts burner during DHW mode
|
||||||
|
BurnerOperationHours, // u16 Number of hours that burner is in operation (i.e. flame on)
|
||||||
|
CHPumpOperationHours, // u16 Number of hours that CH pump has been running
|
||||||
|
DHWPumpValveOperationHours, // u16 Number of hours that DHW pump has been running or DHW valve has been opened
|
||||||
|
DHWBurnerOperationHours, // u16 Number of hours that burner is in operation during DHW mode
|
||||||
|
OpenThermVersionMaster, // f8.8 The implemented version of the OpenTherm Protocol Specification in the master.
|
||||||
|
OpenThermVersionSlave, // f8.8 The implemented version of the OpenTherm Protocol Specification in the slave.
|
||||||
|
MasterVersion, // u8 / u8 Master product version number and type
|
||||||
|
SlaveVersion, // u8 / u8 Slave product version number and type
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OpenThermStatus : uint8_t
|
||||||
|
{
|
||||||
|
NOT_INITIALIZED,
|
||||||
|
READY,
|
||||||
|
DELAY,
|
||||||
|
REQUEST_SENDING,
|
||||||
|
RESPONSE_WAITING,
|
||||||
|
RESPONSE_START_BIT,
|
||||||
|
RESPONSE_RECEIVING,
|
||||||
|
RESPONSE_READY,
|
||||||
|
RESPONSE_INVALID
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenTherm
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false);
|
||||||
|
volatile OpenThermStatus status;
|
||||||
|
void begin(void (*handleInterruptCallback)(void));
|
||||||
|
void begin(void (*handleInterruptCallback)(void), void (*processResponseCallback)(unsigned long, OpenThermResponseStatus));
|
||||||
|
bool isReady();
|
||||||
|
unsigned long sendRequest(unsigned long request);
|
||||||
|
bool sendResponse(unsigned long request);
|
||||||
|
bool sendRequestAync(unsigned long request);
|
||||||
|
unsigned long buildRequest(OpenThermMessageType type, OpenThermMessageID id, unsigned int data);
|
||||||
|
unsigned long buildRequestID(OpenThermMessageType type, unsigned int id, unsigned int data);
|
||||||
|
unsigned long buildResponse(OpenThermMessageType type, OpenThermMessageID id, unsigned int data);
|
||||||
|
unsigned long getLastResponse();
|
||||||
|
OpenThermResponseStatus getLastResponseStatus();
|
||||||
|
const char *statusToString(OpenThermResponseStatus status);
|
||||||
|
void handleInterrupt();
|
||||||
|
void process();
|
||||||
|
void end();
|
||||||
|
|
||||||
|
bool parity(unsigned long frame);
|
||||||
|
OpenThermMessageType getMessageType(unsigned long message);
|
||||||
|
OpenThermMessageID getDataID(unsigned long frame);
|
||||||
|
const char *messageTypeToString(OpenThermMessageType message_type);
|
||||||
|
bool isValidRequest(unsigned long request);
|
||||||
|
bool isValidResponse(unsigned long response);
|
||||||
|
|
||||||
|
// requests
|
||||||
|
unsigned long buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater = false, bool enableCooling = false, bool enableOutsideTemperatureCompensation = false, bool enableCentralHeating2 = false, bool enableSummerMode = false, bool dhwBlock = false);
|
||||||
|
unsigned long buildSetBoilerTemperatureRequest(float temperature);
|
||||||
|
unsigned long buildGetBoilerTemperatureRequest();
|
||||||
|
|
||||||
|
// responses
|
||||||
|
bool isFault(unsigned long response);
|
||||||
|
bool isCentralHeatingActive(unsigned long response);
|
||||||
|
bool isHotWaterActive(unsigned long response);
|
||||||
|
bool isFlameOn(unsigned long response);
|
||||||
|
bool isCoolingActive(unsigned long response);
|
||||||
|
bool isDiagnostic(unsigned long response);
|
||||||
|
uint16_t getUInt(const unsigned long response) const;
|
||||||
|
float getFloat(const unsigned long response) const;
|
||||||
|
unsigned int temperatureToData(float temperature);
|
||||||
|
|
||||||
|
// basic requests
|
||||||
|
unsigned long setBoilerStatus(bool enableCentralHeating, bool enableHotWater = false, bool enableCooling = false, bool enableOutsideTemperatureCompensation = false, bool enableCentralHeating2 = false, bool enableSummerMode = false, bool dhwBlock = false);
|
||||||
|
bool setBoilerTemperature(float temperature);
|
||||||
|
float getBoilerTemperature();
|
||||||
|
float getReturnTemperature();
|
||||||
|
bool setDHWSetpoint(float temperature);
|
||||||
|
float getDHWTemperature();
|
||||||
|
float getModulation();
|
||||||
|
float getPressure();
|
||||||
|
unsigned char getFault();
|
||||||
|
|
||||||
|
//Имитация ответов от котла, TRUE - идет имитация ответов котла, в котел так же шлется (лучше его отключить), FALSE - штатная работа
|
||||||
|
void imitation(bool fl) {imitFlag = fl;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool imitFlag;
|
||||||
|
void ImitationResponse(unsigned long request);
|
||||||
|
|
||||||
|
const int inPin;
|
||||||
|
const int outPin;
|
||||||
|
const bool isSlave;
|
||||||
|
|
||||||
|
volatile unsigned long response;
|
||||||
|
volatile OpenThermResponseStatus responseStatus;
|
||||||
|
volatile unsigned long responseTimestamp;
|
||||||
|
volatile byte responseBitIndex;
|
||||||
|
|
||||||
|
int readState();
|
||||||
|
void setActiveState();
|
||||||
|
void setIdleState();
|
||||||
|
void activateBoiler();
|
||||||
|
|
||||||
|
void sendBit(bool high);
|
||||||
|
void (*handleInterruptCallback)();
|
||||||
|
void (*processResponseCallback)(unsigned long, OpenThermResponseStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef ICACHE_RAM_ATTR
|
||||||
|
#define ICACHE_RAM_ATTR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IRAM_ATTR
|
||||||
|
#define IRAM_ATTR ICACHE_RAM_ATTR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // OpenTherm_h
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -7,12 +7,12 @@
|
|||||||
"type": "Reading",
|
"type": "Reading",
|
||||||
"subtype": "BoilerControl",
|
"subtype": "BoilerControl",
|
||||||
"id": "boiler",
|
"id": "boiler",
|
||||||
"widget": "anydataDef",
|
"widget": "anydataWt",
|
||||||
"page": "Boiler",
|
"page": "Boiler",
|
||||||
"descr": "Котёл",
|
"descr": "Котёл",
|
||||||
"int": 60,
|
"int": 1,
|
||||||
"value": "...",
|
"value": "...",
|
||||||
"LogLevel": 0,
|
"debug": 0,
|
||||||
"telegram": 1,
|
"telegram": 1,
|
||||||
"idPID":"PID",
|
"idPID":"PID",
|
||||||
"idTboiler": "Tboiler",
|
"idTboiler": "Tboiler",
|
||||||
@@ -25,15 +25,13 @@
|
|||||||
"idCmdDHW":"CmdDHW",
|
"idCmdDHW":"CmdDHW",
|
||||||
"idSetCH":"SetCH",
|
"idSetCH":"SetCH",
|
||||||
"idCtrlType":"CtrlType",
|
"idCtrlType":"CtrlType",
|
||||||
"rele1_Pwr": 1,
|
|
||||||
"rele2_Pwr": 2,
|
|
||||||
"rele3_Pwr": 4,
|
|
||||||
"changeRele":0,
|
"changeRele":0,
|
||||||
"Pump": 0,
|
"idRelePump": "relePump",
|
||||||
"minCH": 35,
|
"minCH": 35,
|
||||||
"maxCH": 85,
|
"maxCH": 85,
|
||||||
"gistCH": 5,
|
"gistCH": 5,
|
||||||
"antiFreez":10
|
"antiFreez":10,
|
||||||
|
"maxKW": 24
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"global": 0,
|
"global": 0,
|
||||||
@@ -41,10 +39,10 @@
|
|||||||
"type": "Reading",
|
"type": "Reading",
|
||||||
"subtype": "DHWControl",
|
"subtype": "DHWControl",
|
||||||
"id": "dhw",
|
"id": "dhw",
|
||||||
"widget": "anydataDef",
|
"widget": "",
|
||||||
"page": "Boiler",
|
"page": "Boiler",
|
||||||
"descr": "Котёл",
|
"descr": "Котёл",
|
||||||
"int": 60,
|
"int": 1,
|
||||||
"value": "...",
|
"value": "...",
|
||||||
"idTdhw": "TDhw",
|
"idTdhw": "TDhw",
|
||||||
"idReleDhw": "ReleDhw",
|
"idReleDhw": "ReleDhw",
|
||||||
@@ -53,7 +51,23 @@
|
|||||||
"idSetDHW":"SetDHW",
|
"idSetDHW":"SetDHW",
|
||||||
"minDhw": 20,
|
"minDhw": 20,
|
||||||
"maxDhw": 60,
|
"maxDhw": 60,
|
||||||
"gistDhw": 2
|
"gistDhw": 2,
|
||||||
|
"numStepDhw":1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"global": 0,
|
||||||
|
"name": "OpenThermSlave",
|
||||||
|
"type": "Reading",
|
||||||
|
"subtype": "OpenThermSlave",
|
||||||
|
"id": "otslave",
|
||||||
|
"widget": "",
|
||||||
|
"page": "Boiler",
|
||||||
|
"descr": "Котёл",
|
||||||
|
"int": 1,
|
||||||
|
"value": "...",
|
||||||
|
"RX_pin": 13,
|
||||||
|
"TX_pin": 15,
|
||||||
|
"MemberID": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"about": {
|
"about": {
|
||||||
@@ -62,28 +76,26 @@
|
|||||||
"authorGit": "https://github.com/Mit4el",
|
"authorGit": "https://github.com/Mit4el",
|
||||||
"specialThanks": "",
|
"specialThanks": "",
|
||||||
"moduleName": "SmartBoiler",
|
"moduleName": "SmartBoiler",
|
||||||
"moduleVersion": "0.1",
|
"moduleVersion": "2.0",
|
||||||
"usedRam": {
|
"usedRam": {
|
||||||
"esp32_4mb": 15,
|
"esp32_4mb": 15,
|
||||||
"esp8266_4mb": 15
|
"esp8266_4mb": 15
|
||||||
},
|
},
|
||||||
"subTypes": [
|
"subTypes": [
|
||||||
"BoilerControl",
|
"BoilerControl",
|
||||||
"OpenThermSlave"
|
"OpenThermSlave",
|
||||||
|
"DHWControl"
|
||||||
],
|
],
|
||||||
"title": "SmartBoiler",
|
"title": "SmartBoiler",
|
||||||
"moduleDesc": "Модуль для автоматизации электрического котла. Мозги котла с внешним протоколом opentherm",
|
"moduleDesc": "Модуль для автоматизации электрического котла. Мозги котла с внешним протоколом opentherm. Модуль OpenThermSlave_v2 id модулй использует теже, что указаны в BoilerControl_v2. Но так же может работать автономно, если нет модуля BoilerControl_v2, он ищет модули по ID по умолчаию",
|
||||||
"propInfo": {
|
"propInfo": {
|
||||||
"int": "Интервал отправки данных в MQTT и web интерфейс",
|
"int": "Интервал обработки логики и опроса внешних модулей",
|
||||||
"telegram": "1- Будет отправлять в телеграмм оповещения при ошибках котла и пропаже сигнала от котла, остальные необходимо реализовывать через сценарий",
|
"telegram": "1- Будет отправлять в телеграмм оповещения при ошибках котла и пропаже сигнала от котла, остальные необходимо реализовывать через сценарий",
|
||||||
"MemberID": "SlaveMemberIDcode - код производителя котла, кем притворится котёл;) Менять в большинстве случаев не надо",
|
"MemberID": "SlaveMemberIDcode - код производителя котла, кем притворится котёл;) Менять в большинстве случаев не надо",
|
||||||
"idPID":"ID модуля ПИД регулятора, для расчета модуляции и включения тэнов в зависимости от температуры теплоносителя, в модуле TCHSet будет уставка СО, создать TCHSet и указать его в модуле ПИД",
|
"idPID":"ID модуля ПИД регулятора, для расчета модуляции и включения тэнов в зависимости от температуры теплоносителя, в модуле TCHSet будет уставка СО, создать TCHSet и указать его в модуле ПИД",
|
||||||
"idTboiler": "ID датчика температуры подачи котла",
|
"idTboiler": "ID датчика температуры подачи котла",
|
||||||
"idTret": "ID датчика температуры обратки котла",
|
"idTret": "ID датчика температуры обратки котла, только для передачи по opentherm",
|
||||||
"idToutside": "ID датчика уличной температуры",
|
"idToutside": "ID датчика уличной температуры, только для передачи по opentherm",
|
||||||
"rele1_Pwr": "Мощность тэна на первом реле, ID реле должно называться rele1",
|
|
||||||
"rele2_Pwr": "Мощность тэна на первом реле, ID реле должно называться rele2, если нет, то 0 (ноль)",
|
|
||||||
"rele3_Pwr": "Мощность тэна на первом реле, ID реле должно называться rele3, если нет, то 0 (ноль)",
|
|
||||||
"Pupm": "1-есть реле насоса (ID реле должно называться relePump), 0-нет реле насоса, насос управляется котлом без нас",
|
"Pupm": "1-есть реле насоса (ID реле должно называться relePump), 0-нет реле насоса, насос управляется котлом без нас",
|
||||||
"minCH": "Граница установки температуры СО",
|
"minCH": "Граница установки температуры СО",
|
||||||
"maxCH": "Граница установки температуры СО",
|
"maxCH": "Граница установки температуры СО",
|
||||||
@@ -94,7 +106,9 @@
|
|||||||
"minDhw": "Граница установки температуры ГВС",
|
"minDhw": "Граница установки температуры ГВС",
|
||||||
"maxDhw": "Граница установки температуры ГВС",
|
"maxDhw": "Граница установки температуры ГВС",
|
||||||
"changeRele":"Будет менять каждый раз при включении тэн 1->2->3->1...",
|
"changeRele":"Будет менять каждый раз при включении тэн 1->2->3->1...",
|
||||||
"antiFreez":"Режим анти-замерзания, Указывается температура, если опустится ниже указанной, то включится нарев один тэн и нагреет на +5гр от указанной"
|
"antiFreez":"Режим анти-замерзания, Указывается температура, если опустится ниже указанной, то включится нарев один тэн и нагреет на +5гр от указанной",
|
||||||
|
"maxKW": "Максимальная мощность котла при включении на поcледнем Шаге Мощности",
|
||||||
|
"numStepDhw":"На каком Шаге Мощности включать ГВС"
|
||||||
},
|
},
|
||||||
"funcInfo": [
|
"funcInfo": [
|
||||||
{
|
{
|
||||||
@@ -122,7 +136,28 @@
|
|||||||
"name": "DHWEnable",
|
"name": "DHWEnable",
|
||||||
"descr": "включить / выключить ГВС",
|
"descr": "включить / выключить ГВС",
|
||||||
"params": [
|
"params": [
|
||||||
"dhw.DHWEnable(1) - вкл, dhw.DHWEnable(0) - выкл, "
|
"dhw.DHWEnable(1) - вкл, dhw.DHWEnable(0) - выкл "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "addStepPower",
|
||||||
|
"descr": "Добавить Шаг Нагрева: мощность Шага кВт, ID реле на данном шаге",
|
||||||
|
"params": [
|
||||||
|
"bolier.addStepPower(1, 3, rele1) - шаг №1 в 3kW на первом реле, bolier.addStepPower(4, 24, rele1, rele3, rele4) - шаг 4 в 24Квт на 1+3+4 реле "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "onStepPower",
|
||||||
|
"descr": "включить определенный шаг нагрева, указывается номер шага, Включит Ручной Режим! ",
|
||||||
|
"params": [
|
||||||
|
"bolier.onStepPower(2) "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "autoPower",
|
||||||
|
"descr": "включить автоматический режим работы котла (по умолчанию включен) ",
|
||||||
|
"params": [
|
||||||
|
"bolier.autoPower()"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
3.2 Управление модулем из сценария
|
3.2 Управление модулем из сценария
|
||||||
3.3 есть проверка ошибок датчиков (если отвалились датчика, котел не включится)
|
3.3 есть проверка ошибок датчиков (если отвалились датчика, котел не включится)
|
||||||
3.4 Отправка состояния в телеграмм
|
3.4 Отправка состояния в телеграмм
|
||||||
3.998 3.4 TODO Автоматическая отправка состояния в модули для отображения (имена модулей в логах "new")
|
3.998 TODO Автоматическая отправка состояния в модули для отображения (имена модулей в логах "new")
|
||||||
3.999 другой функционал IoTManager ...
|
3.999 другой функционал IoTManager ...
|
||||||
|
|
||||||
4 Возможность управления циркуляционным насосом
|
4 Возможность управления циркуляционным насосом
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
{
|
{
|
||||||
"mark": "iotm",
|
"mark": "iotm",
|
||||||
"config": [
|
"config": [
|
||||||
{
|
|
||||||
"global": 0,
|
|
||||||
"type": "Writing",
|
|
||||||
"subtype": "TelegramLT",
|
|
||||||
"id": "tg",
|
|
||||||
"widget": "",
|
|
||||||
"page": "",
|
|
||||||
"descr": "",
|
|
||||||
"token": "",
|
|
||||||
"chatID": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"global": 0,
|
"global": 0,
|
||||||
"type": "Reading",
|
"type": "Reading",
|
||||||
@@ -146,7 +135,7 @@
|
|||||||
"subtype": "Variable",
|
"subtype": "Variable",
|
||||||
"id": "ModLevel",
|
"id": "ModLevel",
|
||||||
"needSave": 0,
|
"needSave": 0,
|
||||||
"widget": "anydataDef",
|
"widget": "anydataHum",
|
||||||
"page": "Состояние",
|
"page": "Состояние",
|
||||||
"descr": "Модуляция",
|
"descr": "Модуляция",
|
||||||
"int": "0",
|
"int": "0",
|
||||||
@@ -168,117 +157,6 @@
|
|||||||
"int": "0",
|
"int": "0",
|
||||||
"val": "0"
|
"val": "0"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"global": 0,
|
|
||||||
"type": "Reading",
|
|
||||||
"subtype": "VButton",
|
|
||||||
"id": "ReleDhw",
|
|
||||||
"needSave": 0,
|
|
||||||
"widget": "toggle",
|
|
||||||
"page": "ГВС",
|
|
||||||
"descr": "3-хходовой",
|
|
||||||
"int": "0",
|
|
||||||
"val": "0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"global": 0,
|
|
||||||
"type": "Reading",
|
|
||||||
"subtype": "VButton",
|
|
||||||
"id": "CmdDHW",
|
|
||||||
"needSave": 0,
|
|
||||||
"widget": "toggle",
|
|
||||||
"page": "ГВС",
|
|
||||||
"descr": " ВКЛ ГВС",
|
|
||||||
"int": "0",
|
|
||||||
"val": "0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"global": 0,
|
|
||||||
"type": "Reading",
|
|
||||||
"subtype": "Variable",
|
|
||||||
"id": "TDhw",
|
|
||||||
"needSave": 0,
|
|
||||||
"widget": "inputDgt",
|
|
||||||
"page": "ГВС",
|
|
||||||
"descr": "Датчик БКН",
|
|
||||||
"int": "0",
|
|
||||||
"val": "0.0",
|
|
||||||
"map": "1024,1024,1,100",
|
|
||||||
"plus": 0,
|
|
||||||
"multiply": 1,
|
|
||||||
"round": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"global": 0,
|
|
||||||
"type": "Reading",
|
|
||||||
"subtype": "Variable",
|
|
||||||
"id": "SetDHW",
|
|
||||||
"needSave": 0,
|
|
||||||
"widget": "inputDgt",
|
|
||||||
"page": "ГВС",
|
|
||||||
"descr": "Уставка ГВС",
|
|
||||||
"int": "0",
|
|
||||||
"val": "0.0",
|
|
||||||
"map": "1024,1024,1,100",
|
|
||||||
"plus": 0,
|
|
||||||
"multiply": 1,
|
|
||||||
"round": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"global": 0,
|
|
||||||
"type": "Reading",
|
|
||||||
"subtype": "Variable",
|
|
||||||
"id": "StateDHW",
|
|
||||||
"needSave": 0,
|
|
||||||
"widget": "anydataDef",
|
|
||||||
"page": "Состояние",
|
|
||||||
"descr": "Состояние ГВС",
|
|
||||||
"int": "0",
|
|
||||||
"val": "0.0",
|
|
||||||
"map": "1024,1024,1,100",
|
|
||||||
"plus": 0,
|
|
||||||
"multiply": 1,
|
|
||||||
"round": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"global": 0,
|
|
||||||
"type": "Reading",
|
|
||||||
"subtype": "DHWControl",
|
|
||||||
"id": "dhw96",
|
|
||||||
"widget": "nil",
|
|
||||||
"page": "Boiler",
|
|
||||||
"descr": "Котёл",
|
|
||||||
"int": 60,
|
|
||||||
"value": "...",
|
|
||||||
"idTDhw": "TDhw",
|
|
||||||
"idReleDhw": "ReleDhw",
|
|
||||||
"idCmdDHW": "CmdDHW",
|
|
||||||
"idStateDHW": "StateDHW",
|
|
||||||
"idSetDHW": "SetDHW",
|
|
||||||
"minDhw": 20,
|
|
||||||
"maxDhw": 60,
|
|
||||||
"gistDhw": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"global": 0,
|
|
||||||
"needSave": 0,
|
|
||||||
"type": "Writing",
|
|
||||||
"subtype": "ThermostatPID",
|
|
||||||
"id": "PID",
|
|
||||||
"widget": "anydataHum",
|
|
||||||
"page": "Котёл",
|
|
||||||
"descr": "термостат ПИД",
|
|
||||||
"int": "10",
|
|
||||||
"round": 1,
|
|
||||||
"map": "0,100,0,100",
|
|
||||||
"set_id": "SetCH",
|
|
||||||
"term_id": "Tboiler",
|
|
||||||
"term_rezerv_id": "",
|
|
||||||
"rele": "",
|
|
||||||
"KP": 5,
|
|
||||||
"KI": 50,
|
|
||||||
"KD": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"global": 0,
|
"global": 0,
|
||||||
"type": "Reading",
|
"type": "Reading",
|
||||||
@@ -295,17 +173,37 @@
|
|||||||
"multiply": 1,
|
"multiply": 1,
|
||||||
"round": 0
|
"round": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"global": 0,
|
||||||
|
"needSave": 0,
|
||||||
|
"type": "Writing",
|
||||||
|
"subtype": "ThermostatPID",
|
||||||
|
"id": "PID",
|
||||||
|
"widget": "anydataHum",
|
||||||
|
"page": "Котёл",
|
||||||
|
"descr": "термостат",
|
||||||
|
"int": 60,
|
||||||
|
"round": 1,
|
||||||
|
"map": "1,100,1,100",
|
||||||
|
"set_id": "SetCH",
|
||||||
|
"term_id": "Tboiler",
|
||||||
|
"term_rezerv_id": "",
|
||||||
|
"rele": "",
|
||||||
|
"KP": 5,
|
||||||
|
"KI": 50,
|
||||||
|
"KD": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"global": 0,
|
"global": 0,
|
||||||
"type": "Reading",
|
"type": "Reading",
|
||||||
"subtype": "BoilerControl",
|
"subtype": "BoilerControl",
|
||||||
"id": "boiler81",
|
"id": "boiler",
|
||||||
"widget": "nil",
|
"widget": "anydataWt",
|
||||||
"page": "Boiler",
|
"page": "Котёл",
|
||||||
"descr": "Котёл",
|
"descr": "Котёл",
|
||||||
"int": 60,
|
"int": 1,
|
||||||
"value": "...",
|
"value": "...",
|
||||||
"LogLevel": 0,
|
"debug": "0",
|
||||||
"telegram": 1,
|
"telegram": 1,
|
||||||
"idPID": "PID",
|
"idPID": "PID",
|
||||||
"idTboiler": "Tboiler",
|
"idTboiler": "Tboiler",
|
||||||
@@ -318,20 +216,22 @@
|
|||||||
"idCmdDHW": "CmdDHW",
|
"idCmdDHW": "CmdDHW",
|
||||||
"idSetCH": "SetCH",
|
"idSetCH": "SetCH",
|
||||||
"idCtrlType": "CtrlType",
|
"idCtrlType": "CtrlType",
|
||||||
"rele1_Pwr": 1,
|
|
||||||
"rele2_Pwr": 2,
|
|
||||||
"rele3_Pwr": 4,
|
|
||||||
"changeRele": 0,
|
"changeRele": 0,
|
||||||
"Pump": 0,
|
"idRelePump": "relePump",
|
||||||
"minCH": 35,
|
"minCH": 35,
|
||||||
"maxCH": 85,
|
"maxCH": 85,
|
||||||
"gistCH": 5,
|
"gistCH": 5,
|
||||||
"antiFreez": 10
|
"antiFreez": 10,
|
||||||
|
"maxKW": "8"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario=>if onStart then
|
scenario=>if onStart then
|
||||||
{
|
{
|
||||||
tg.sendMsg("SmartBoiler http://" + getIP());
|
boiler.addStepPower(1, 3, "rele1");
|
||||||
|
boiler.addStepPower(2, 5, "rele2", "rele3");
|
||||||
|
boiler.addStepPower(3, 8, "rele1", "rele2", "rele3");
|
||||||
|
#boiler.onStepPower(2);
|
||||||
|
#boiler.autoPower();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user