2023-01-30 11:33:41 +03:00
# include "Global.h"
# include "classes/IoTItem.h"
2024-11-20 21:13:12 +03:00
# include "GyverPID.h"
2023-01-30 11:33:41 +03:00
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 ;
2024-11-20 21:13:12 +03:00
int _direction = 0 ;
2023-01-30 11:33:41 +03:00
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 ) ;
2024-11-20 21:13:12 +03:00
jsonRead ( parameters , " direction " , _direction ) ;
2023-01-30 11:33:41 +03:00
}
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 )
2024-11-20 21:13:12 +03:00
{
if ( _direction )
{
tmp - > setValue ( " 0 " , true ) ;
}
else
{
tmp - > setValue ( " 1 " , true ) ;
}
}
2023-01-30 11:33:41 +03:00
}
if ( pv < = sp - _gist & & enable )
{
tmp = findIoTItem ( _rele ) ;
if ( tmp )
2024-11-20 21:13:12 +03:00
{
if ( _direction )
{
tmp - > setValue ( " 1 " , true ) ;
}
else
{
tmp - > setValue ( " 0 " , true ) ;
}
}
2023-01-30 11:33:41 +03:00
}
}
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 )
2024-11-20 21:13:12 +03:00
{
if ( _direction )
{
tmp - > setValue ( " 0 " , true ) ;
}
else
{
tmp - > setValue ( " 1 " , true ) ;
}
}
2023-01-30 11:33:41 +03:00
}
if ( pv2 < = sp - _gist & & enable )
{
tmp = findIoTItem ( _rele ) ;
if ( tmp )
2024-11-20 21:13:12 +03:00
{
if ( _direction )
{
tmp - > setValue ( " 1 " , true ) ;
}
else
{
tmp - > setValue ( " 0 " , true ) ;
}
}
2023-01-30 11:33:41 +03:00
}
}
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 { } ;
}
2024-11-20 21:13:12 +03:00
~ ThermostatGIST ( ) { } ;
2023-01-30 11:33:41 +03:00
} ;
2024-11-20 21:13:12 +03:00
GyverPID * regulator = nullptr ;
GyverPID * instanceregulator ( float _KP , float _KI , float _KD , int interval , boolean setDirection , int setLimitsMIN , int setLimitsMAX )
{
if ( ! regulator )
{ // Если библиотека ранее инициализировалась, то просто вернем указатель
// Инициализируем библиотеку
regulator = new GyverPID ( _KP , _KI , _KD , interval ) ; // коэф. П, коэф. И, коэф. Д, период дискретизации dt (с )
regulator - > setDirection ( setDirection ) ; // направление регулирования (NORMAL/REVERSE). ПО УМОЛЧАНИЮ СТОИТ NORMAL
regulator - > setLimits ( setLimitsMIN , setLimitsMAX ) ; // пределы. ПО УМОЛЧАНИЮ СТОЯТ 0 И 100
SerialPrint ( " i " , F ( " ThermostatPID " ) , " _KP: " + String ( _KP ) + " _KI: " + String ( _KI ) + " _KD: " + String ( _KD ) + " interval: " + String ( interval ) + " _setLimitsMIN: " + String ( setLimitsMIN ) + " _setLimitsMAX: " + String ( setLimitsMAX ) + " Direction: " + String ( setDirection ) ) ;
// GyverPID regulator(_KP, _KI, _KD, interval);
}
return regulator ;
}
2023-01-30 11:33:41 +03:00
class ThermostatPID : public IoTItem
{
private :
2024-11-20 21:13:12 +03:00
String _set_id ; // заданная температура
String _term_id ; // термометр
boolean _setDirection ;
float _int , _KP , _KI , _KD ,
sp , pv ,
2023-01-30 11:33:41 +03:00
pv_last = 0 , // предыдущая температура
ierr = 0 , // интегральная погрешность
dt = 0 ; // время между измерениями
String _rele ; // реле
String interim ;
int enable = 1 ;
2024-11-20 21:13:12 +03:00
int interval , _setLimitsMIN , _setLimitsMAX ;
2023-01-30 11:33:41 +03:00
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 ) ;
jsonRead ( parameters , " rele " , _rele ) ;
2024-11-20 21:13:12 +03:00
// GyverPID
jsonRead ( parameters , " setDirection " , _setDirection ) ;
jsonRead ( parameters , " setLimitsMIN " , _setLimitsMIN ) ;
jsonRead ( parameters , " setLimitsMAX " , _setLimitsMAX ) ;
// в процессе работы можно менять коэффициенты
// instanceregulator(_KP, _KI, _KD, interval)->Kp = _KP;
// instanceregulator(_KP, _KI, _KD, interval)->Ki = _KI;
// instanceregulator(_KP, _KI, _KD, interval)->Kd = _KD;
2023-01-30 11:33:41 +03:00
}
protected :
//===============================================================
// Вычисляем температуру контура отпления, коэффициенты ПИД регулятора
//===============================================================
2024-11-20 21:13:12 +03:00
/*
2023-01-30 11:33:41 +03:00
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 ;
}
2024-11-20 21:13:12 +03:00
*/
2023-01-30 11:33:41 +03:00
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 ( ) ) ;
}
2024-11-20 21:13:12 +03:00
if ( enable )
2023-10-08 18:19:08 +03:00
{
2024-11-20 21:13:12 +03:00
// regEvent(pid(sp, pv, pv_last, ierr, _int), "ThermostatPID", false, true);
// instanceregulator(_KP, _KI, _KD, interval,_setDirection,_setLimitsMIN,_setLimitsMAX)->setDirection(_setDirection); // направление регулирования (NORMAL/REVERSE). ПО УМОЛЧАНИЮ СТОИТ NORMAL
// instanceregulator(_KP, _KI, _KD, interval,_setDirection,_setLimitsMIN,_setLimitsMAX)->setLimits(_setLimitsMIN, _setLimitsMAX); // пределы. ПО УМОЛЧАНИЮ СТОЯТ 0 И 100
// instanceregulator(_KP, _KI, _KD, interval)->setMode(1);
instanceregulator ( _KP , _KI , _KD , interval , _setDirection , _setLimitsMIN , _setLimitsMAX ) - > setpoint = sp ;
instanceregulator ( _KP , _KI , _KD , interval , _setDirection , _setLimitsMIN , _setLimitsMAX ) - > input = pv ;
value . valD = instanceregulator ( _KP , _KI , _KD , interval , _setDirection , _setLimitsMIN , _setLimitsMAX ) - > getResult ( ) ;
SerialPrint ( " i " , F ( " ThermostatPID " ) , " _KP: " + String ( _KP ) + " _KI: " + String ( _KI ) + " _KD: " + String ( _KD ) + " interval: " + String ( interval ) + " _setLimitsMIN: " + String ( _setLimitsMIN ) + " _setLimitsMAX: " + String ( _setLimitsMAX ) + " Direction: " + String ( _setDirection ) ) ;
SerialPrint ( " i " , F ( " ThermostatPID " ) , " setpoint: " + String ( sp ) + " input: " + String ( pv ) ) ;
regEvent ( value . valD , " ThermostatPID " , false , true ) ;
2023-10-08 18:19:08 +03:00
}
2024-11-20 21:13:12 +03:00
else
2023-01-30 11:33:41 +03:00
{
2024-11-20 21:13:12 +03:00
value . valD = 0 ;
regEvent ( value . valD , " ThermostatPID " , false , true ) ;
2023-10-08 18:19:08 +03:00
}
2023-01-30 11:33:41 +03:00
pv_last = pv ;
}
2023-10-08 18:19:08 +03:00
// временное решение
2023-09-05 00:20:03 +03:00
unsigned long currentMillis ;
unsigned long prevMillis ;
unsigned long difference ;
2023-10-08 18:19:08 +03:00
2023-01-30 11:33:41 +03:00
void loop ( )
{
if ( enableDoByInt )
{
currentMillis = millis ( ) ;
difference = currentMillis - prevMillis ;
2024-11-20 21:13:12 +03:00
if ( _rele ! = " " & & enable & & value . valD * interval / 100 > difference / 1000 & & releState = = 0 )
2023-01-30 11:33:41 +03:00
{
releState = 1 ;
tmp = findIoTItem ( _rele ) ;
if ( tmp )
tmp - > setValue ( " 1 " , true ) ;
}
2024-11-20 21:13:12 +03:00
if ( _rele ! = " " & & enable & & value . valD * interval / 100 < difference / 1000 & & releState = = 1 )
2023-01-30 11:33:41 +03:00
{
releState = 0 ;
tmp = findIoTItem ( _rele ) ;
if ( tmp )
tmp - > setValue ( " 0 " , true ) ;
}
2024-11-20 21:13:12 +03:00
if ( difference > = interval * 1000 )
2023-01-30 11:33:41 +03:00
{
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 ;
2024-11-20 21:13:12 +03:00
if ( enable = = 0 )
{
delete regulator ;
regulator = nullptr ;
// instanceregulator(_KP, _KI, _KD, interval, _setDirection, _setLimitsMIN, _setLimitsMAX);
}
}
}
if ( command = = " setLimitsMIN " )
{
if ( param . size ( ) )
{
_setLimitsMIN = param [ 0 ] . valD ;
// delete regulator;
// regulator = nullptr;
// instanceregulator(_KP, _KI, _KD, interval, _setDirection, _setLimitsMIN, _setLimitsMAX);
}
}
if ( command = = " setLimitsMAX " )
{
if ( param . size ( ) )
{
_setLimitsMAX = param [ 0 ] . valD ;
// delete regulator;
// regulator = nullptr;
// instanceregulator(_KP, _KI, _KD, interval, _setDirection, _setLimitsMIN, _setLimitsMAX);
2023-01-30 11:33:41 +03:00
}
}
if ( command = = " KP " )
{
if ( param . size ( ) )
{
_KP = param [ 0 ] . valD ;
2024-11-20 21:13:12 +03:00
delete regulator ;
regulator = nullptr ;
instanceregulator ( _KP , _KI , _KD , interval , _setDirection , _setLimitsMIN , _setLimitsMAX ) ;
2023-01-30 11:33:41 +03:00
}
}
if ( command = = " KI " )
{
if ( param . size ( ) )
{
_KI = param [ 0 ] . valD ;
2024-11-20 21:13:12 +03:00
delete regulator ;
regulator = nullptr ;
instanceregulator ( _KP , _KI , _KD , interval , _setDirection , _setLimitsMIN , _setLimitsMAX ) ;
2023-01-30 11:33:41 +03:00
}
}
if ( command = = " KD " )
{
if ( param . size ( ) )
{
_KD = param [ 0 ] . valD ;
2024-11-20 21:13:12 +03:00
delete regulator ;
regulator = nullptr ;
instanceregulator ( _KP , _KI , _KD , interval , _setDirection , _setLimitsMIN , _setLimitsMAX ) ;
}
}
if ( command = = " setDirection " )
{
if ( param . size ( ) )
{
_setDirection = param [ 0 ] . valD ;
delete regulator ;
regulator = nullptr ;
instanceregulator ( _KP , _KI , _KD , interval , _setDirection , _setLimitsMIN , _setLimitsMAX ) ;
2023-01-30 11:33:41 +03:00
}
}
}
return { } ;
}
2024-11-20 21:13:12 +03:00
~ ThermostatPID ( )
{
delete regulator ;
regulator = nullptr ;
} ;
2023-01-30 11:33:41 +03:00
} ;
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 )
{
2023-10-08 18:19:08 +03:00
// jsonRead(parameters, "set_id", _set_id);
// jsonRead(parameters, "term_id", _term_id);
2023-01-30 11:33:41 +03:00
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 " ) ;
}
}
2024-11-20 21:13:12 +03:00
~ ThermostatETK ( ) { } ;
2023-01-30 11:33:41 +03:00
} ;
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 " ) ;
}
}
2024-11-20 21:13:12 +03:00
~ ThermostatETK2 ( ) { } ;
2023-01-30 11:33:41 +03:00
} ;
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 ;
2024-11-20 21:13:12 +03:00
}