модуль SmartBoiler v2

This commit is contained in:
Mit4el
2024-02-26 23:31:12 +03:00
parent 0baf03fccf
commit 8e7d2d6a14
7 changed files with 2167 additions and 457 deletions

View File

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

View 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);
}
*/
}

View 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

View File

@@ -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": "Максимальная мощность котла при включении на поеднем Шаге Мощности",
"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()"
] ]
} }
] ]

View File

@@ -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 Возможность управления циркуляционным насосом

View File

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