Files
IoTManager/src/modules/exec/Thermostat/Thermostat.cpp

472 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "Global.h"
#include "classes/IoTItem.h"
extern IoTGpio IoTgpio;
class ThermostatGIST : public IoTItem
{
private:
String _set_id; // заданная температура
String _term_id; // термометр
String _term_rezerv_id; // резервный термометр
String _rele; // реле
float pv_last = 0; // предыдущая температура
float _gist = 1; // гистерис
float sp, pv, pv2;
String interim;
int enable = 1;
public:
ThermostatGIST(String parameters) : IoTItem(parameters)
{
jsonRead(parameters, "set_id", _set_id);
jsonRead(parameters, "term_id", _term_id);
jsonRead(parameters, "term_rezerv_id", _term_rezerv_id);
jsonRead(parameters, "gist", _gist);
jsonRead(parameters, "rele", _rele);
}
void doByInterval()
{
// заданная температура
IoTItem *tmp = findIoTItem(_set_id);
if (tmp)
{
interim = tmp->getValue();
sp = ::atof(interim.c_str());
}
// термометр
tmp = findIoTItem(_term_id);
if (tmp)
{
interim = tmp->getValue();
pv = ::atof(interim.c_str());
}
if (sp && _rele != "")
{
if (pv > -40 && pv < 120 && pv)
{
if (enable)
{
setValue("штатный режим");
}
// работаем по основному датчику
if (pv >= sp + _gist && enable)
{
tmp = findIoTItem(_rele);
if (tmp)
tmp->setValue("0", true);
}
if (pv <= sp - _gist && enable)
{
tmp = findIoTItem(_rele);
if (tmp)
tmp->setValue("1", true);
}
}
else
{
// резервный термометр
if (_term_rezerv_id != "")
{
tmp = findIoTItem(_term_rezerv_id);
if (tmp)
{
interim = tmp->getValue();
pv2 = ::atof(interim.c_str());
}
// работаем по резервному датчику
if (pv2 > -40 && pv2 < 120 && pv2)
{
if (enable)
{
setValue("резервный датчик");
}
if (pv2 >= sp + _gist && enable)
{
tmp = findIoTItem(_rele);
if (tmp)
tmp->setValue("0", true);
}
if (pv2 <= sp - _gist && enable)
{
tmp = findIoTItem(_rele);
if (tmp)
tmp->setValue("1", true);
}
}
else
{
if (enable)
{
setValue("ошибка резервного датчика");
}
}
}
else
{
if (enable)
{
setValue("ошибка датчика температуры");
}
}
}
}
else
{
// если не заполнены настройки термостата
setValue("ошибка настройки термостата");
}
pv_last = pv;
}
IoTValue execute(String command, std::vector<IoTValue> &param)
{
if (param.size() == 1)
{
if (command == "enable")
{
if (param.size())
{
enable = param[0].valD;
if (enable)
{
setValue("включен");
}
else
{
setValue("выключен");
}
}
}
}
return {};
}
~ThermostatGIST(){};
};
class ThermostatPID : public IoTItem
{
private:
String _set_id; // заданная температура
String _term_id; // термометр
float _int, _KP, _KI, _KD, sp, pv,
pv_last = 0, // предыдущая температура
ierr = 0, // интегральная погрешность
dt = 0; // время между измерениями
String _rele; // реле
String interim;
int enable = 1;
long interval;
IoTItem *tmp;
int releState = 0;
public:
ThermostatPID(String parameters) : IoTItem(parameters)
{
jsonRead(parameters, "set_id", _set_id);
jsonRead(parameters, "term_id", _term_id);
jsonRead(parameters, "int", _int);
jsonRead(parameters, "KP", _KP);
jsonRead(parameters, "KI", _KI);
jsonRead(parameters, "KD", _KD);
jsonRead(parameters, F("int"), interval);
interval = interval * 1000; // интервал проверки в сек
jsonRead(parameters, "rele", _rele);
}
protected:
//===============================================================
// Вычисляем температуру контура отпления, коэффициенты ПИД регулятора
//===============================================================
float pid(float sp, float pv, float pv_last, float &ierr, float dt)
{
float Kc = _KP; // K / %Heater 5
float tauI = _KI; // sec 50
float tauD = _KD; // sec 1
// ПИД коэффициенты
float KP = Kc; // 5
if (tauI == 0)
{
tauI = 50;
}
float KI = Kc / tauI; // 0.1
float KD = Kc * tauD; // 5
// верхняя и нижняя границы уровня нагрева
float ophi = 100;
float oplo = 0;
// вычислить ошибку
float error = sp - pv; // 0
// calculate the integral error
ierr = ierr + KI * error * dt; // 0
// вычислить производную измерения
float dpv = (pv - pv_last) / dt; // 0
// рассчитать выход ПИД регулятора
float P = KP * error; // пропорциональная составляющая
float I = ierr; // интегральная составляющая
float D = -KD * dpv; // дифференциальная составляющая
float op = P + I + D;
// защита от сброса
if ((op < oplo) || (op > ophi))
{
I = I - KI * error * dt;
// выход регулятора, он же уставка для ID-1 (температура теплоносителя контура СО котла)
op = constrain(op, oplo, ophi);
}
ierr = I;
return op;
}
void
doByInterval()
{
// заданная температура
IoTItem *tmp = findIoTItem(_set_id);
if (tmp)
{
interim = tmp->getValue();
sp = ::atof(interim.c_str());
}
// термометр
tmp = findIoTItem(_term_id);
if (tmp)
{
interim = tmp->getValue();
pv = ::atof(interim.c_str());
}
if (sp && pv)
{
value.valD = pid(sp, pv, pv_last, ierr, _int);
value.valS = (String)(int)pid(sp, pv, pv_last, ierr, _int);
regEvent(value.valS, "ThermostatPID", false, true);
}
pv_last = pv;
}
void loop()
{
if (enableDoByInt)
{
currentMillis = millis();
difference = currentMillis - prevMillis;
if (_rele != "" && enable && value.valD * interval / 100000 > difference / 1000 && releState == 0)
{
releState = 1;
tmp = findIoTItem(_rele);
if (tmp)
tmp->setValue("1", true);
}
if (_rele != "" && enable && value.valD * interval / 100000 < difference / 1000 && releState == 1)
{
releState = 0;
tmp = findIoTItem(_rele);
if (tmp)
tmp->setValue("0", true);
}
if (difference >= interval)
{
prevMillis = millis();
this->doByInterval();
}
}
}
IoTValue execute(String command, std::vector<IoTValue> &param)
{
if (param.size() == 1)
{
if (command == "enable")
{
if (param.size())
{
enable = param[0].valD;
}
}
if (command == "KP")
{
if (param.size())
{
_KP = param[0].valD;
}
}
if (command == "KI")
{
if (param.size())
{
_KI = param[0].valD;
}
}
if (command == "KD")
{
if (param.size())
{
_KD = param[0].valD;
}
}
}
return {};
}
~ThermostatPID(){};
};
class ThermostatETK : public IoTItem
{
private:
float pv, sp, outside_temp;
float _iv_k; // эквитермические кривые
String _set_id; // заданная температура
String _term_id; // термометр
String _outside_id; // уличный термометр
String interim;
int enable = 1;
public:
ThermostatETK(String parameters) : IoTItem(parameters)
{
jsonRead(parameters, "set_id", _set_id);
jsonRead(parameters, "term_id", _term_id);
jsonRead(parameters, "iv_k", _iv_k);
jsonRead(parameters, "outside_id", _outside_id);
}
protected:
//===================================================================================================================
// Вычисляем температуру контура отпления, эквитермические кривые
//===================================================================================================================
float curve(float iv_k, float outside_temp)
{
float a = (-0.21 * iv_k) - 0.06; // a = -0,21k — 0,06
float b = (6.04 * iv_k) + 1.98; // b = 6,04k + 1,98
float c = (-5.06 * iv_k) + 18.06; // с = -5,06k + 18,06
float x = (-0.2 * outside_temp) + 5; // x = -0.2*t1 + 5
float temp_n = (a * x * x) + (b * x) + c; // Tn = ax2 + bx + c
// Расчетная температура конура отопления
float op = temp_n; // T = Tn
// Ограничиваем температуру для ID-1
op = constrain(op, 0, 100);
return op;
}
void doByInterval()
{
// уличный термометр
IoTItem *tmp = findIoTItem(_outside_id);
if (tmp)
{
interim = tmp->getValue();
outside_temp = ::atof(interim.c_str());
}
if (_iv_k && outside_temp)
{
value.valD = curve(_iv_k, outside_temp);
regEvent(value.valD, "ThermostatETK");
}
}
~ThermostatETK(){};
};
class ThermostatETK2 : public IoTItem
{
private:
float pv, sp, outside_temp;
float _iv_k; // эквитермические кривые
String _set_id; // заданная температура
String _term_id; // термометр
String _outside_id; // уличный термометр
String interim;
int enable = 1;
public:
ThermostatETK2(String parameters) : IoTItem(parameters)
{
jsonRead(parameters, "set_id", _set_id);
jsonRead(parameters, "term_id", _term_id);
jsonRead(parameters, "iv_k", _iv_k);
jsonRead(parameters, "outside_id", _outside_id);
}
protected:
//===================================================================================================================
// Вычисляем температуру контура отпления, эквитермические кривые с учётом влияния температуры в помещении
//===================================================================================================================
float curve2(float sp, float pv, float iv_k, float outside_temp)
{
// Расчет поправки (ошибки) термостата
float error = sp - pv; // Tt = (Tu — T2) × 5
float temp_t = error * 3.0;
// Поправка на желаемую комнатную температуру
// Температура контура отопления в зависимости от наружной температуры
float a = (-0.21 * iv_k) - 0.06; // a = -0,21k — 0,06
float b = (6.04 * iv_k) + 1.98; // b = 6,04k + 1,98
float c = (-5.06 * iv_k) + 18.06; // с = -5,06k + 18,06
float x = (-0.2 * outside_temp) + 5; // x = -0.2*t1 + 5
float temp_n = (a * x * x) + (b * x) + c; // Tn = ax2 + bx + c
// Расчетная температура конура отопления
float op = temp_n + temp_t; // T = Tn + Tk + Tt
// Ограничиваем температуру для ID-1
op = constrain(op, 0, 100);
return op;
}
void doByInterval()
{
// заданная температура
IoTItem *tmp = findIoTItem(_set_id);
if (tmp)
{
interim = tmp->getValue();
sp = ::atof(interim.c_str());
}
// термометр
tmp = findIoTItem(_term_id);
if (tmp)
{
interim = tmp->getValue();
pv = ::atof(interim.c_str());
}
// уличный термометр
tmp = findIoTItem(_outside_id);
if (tmp)
{
interim = tmp->getValue();
outside_temp = ::atof(interim.c_str());
}
if (sp && pv && _iv_k && outside_temp)
{
value.valD = curve2(sp, pv, _iv_k, outside_temp);
regEvent(value.valD, "ThermostatETK2");
}
}
~ThermostatETK2(){};
};
void *getAPI_Thermostat(String subtype, String param)
{
if (subtype == F("ThermostatGIST"))
{
return new ThermostatGIST(param);
}
else if (subtype == F("ThermostatPID"))
{
return new ThermostatPID(param);
}
else if (subtype == F("ThermostatETK"))
{
return new ThermostatETK(param);
}
else if (subtype == F("ThermostatETK2"))
{
return new ThermostatETK2(param);
}
//}
return nullptr;
}