Прошу помощи в отладке Arduino Mega 2560

Поддержу. Промышленное оборудование строится по другим принципам. Это не просто помигать светодиодом, но и диагностика работоспособности светодиода и т.д. ПО тестируется не на удачное выполнение функции, но и восстановление после сбоя. Поэтому хоть и применяются ардункочипы в охранных системах, но код внутри оттестирован, поскольку отвечать производителю оборудования, а не производителю ардуинки.

Я так понимаю - что Вы говорите о том, что такой как я, без должного обучения - не способен написать нормальный код.
Но что хочу возразить на это. Несколько лет назад я умудрился сделать (по сути - “надёргав” куски кода из интернета). Системку, которая следит за обогревом дома, управляя различными органами и получая температуру.
Рядом с нагревательными элементами установлена Ардуино “nano”. Она получает значения с датчиков, отправляет через rx/tx. Потом получает через rx/tx ответ и посылает команду на реле. Код этой “nano” такой:

Спойлер
#include <OneWire.h>
OneWire ds(2);
#define START_CONVERT 0
#define READ_TEMP 1

//библиотеки для часов реального времени
#include "microDS3231.h"
MicroDS3231 rtc;


byte addr1[8]={0x28,0xFF,0x94,0xAE,0xA0,0x16,0x03,0x0E};
byte addr2[8]={0x28,0xFF,0x48,0x83,0xA0,0x16,0x04,0x1F};
byte addr3[8]={0x28,0xFF,0xAA,0xAB,0xA0,0x16,0x05,0x74};
byte addr4[8]={0x28,0xFF,0xEC,0x98,0xA0,0x16,0x04,0x9F};


byte a=1; //переменная для фиксации данных от электроконтактного манометра (минимальное и максимальное давление в системе)
// 1 - давление в рабочем диапазоне
// 2 - минимальное давление
// 3 - максимальное давление


byte output=0; //переменная, непосредственно замыкаюшая и размыкающая пины, управляющие релюшками
//бит "0"-для управления насосом тёплых полов (on/off)
//бит "1"-для управления насосом радиаторов (on/off)
//бит "2"-для управления котлом и котловым насосом одновременно (on/off)
//бит "3"-для управления ТЭН-ом гидроразделителя 2,5 кВт (on/off)
//бит "4"-для управления (и отображения состояния) ТЭНа теплоаккумулятора 7,5 кВт (on/off)
//бит "5"-для управления (и отображения состояния) шарового крана на выходе теплоаккумулятора (откр/закр)

void setup() {
  Serial.begin(9600);
  
  //конвентируем
  tempProcess(START_CONVERT,addr1);
  tempProcess(START_CONVERT,addr2);
  tempProcess(START_CONVERT,addr3);
  tempProcess(START_CONVERT,addr4);
  
  digitalWrite(3, HIGH);
  pinMode(3, OUTPUT);
  digitalWrite(4, HIGH);
  pinMode(4, OUTPUT);
  digitalWrite(5, HIGH);
  pinMode(5, OUTPUT);
  digitalWrite(6, HIGH);
  pinMode(6, OUTPUT);
  digitalWrite(7, HIGH);
  pinMode(7, OUTPUT);
  digitalWrite(8, HIGH);
  pinMode(8, OUTPUT);

  pinMode(9, INPUT_PULLUP); //пин D9 назначается как "вход подтянутый к питанию" - для сигнализации о минимальном давлении в системе
  pinMode(10, INPUT_PULLUP); //пин D10 назначается как "вход подтянутый к питанию" - для сигнализации о максимальном давлении в системе
  

  if (rtc.lostPower()) {  //  при потере питания
    rtc.setTime(COMPILE_TIME);  // установить время компиляции
  }
  //rtc.setTime(SEC, MIN, HOUR, DAY, MONTH, YEAR); // установка времени вручную
  
  //rtc.setTime(0, 34, 21, 26, 9, 2022); // установка времени вручную

  
  delay(1000);
}

void loop() 
{

//принимаю байт с управляющими командами через порт: 
while (Serial.available())
 output=Serial.read();

 
 //Serial.println(output,BIN); //для проверки

 
if (bitRead(output,0)==0)
 digitalWrite(3, HIGH);
else if (bitRead(output,0)==1) 
 digitalWrite(3, LOW);
if (bitRead(output,1)==0)
 digitalWrite(4, HIGH);
else if (bitRead(output,1)==1) 
 digitalWrite(4, LOW); 
if (bitRead(output,2)==0)
 digitalWrite(5, HIGH);
else if (bitRead(output,2)==1) 
 digitalWrite(5, LOW);
if (bitRead(output,3)==0)
 digitalWrite(6, HIGH);
else if (bitRead(output,3)==1) 
 digitalWrite(6, LOW);
if (bitRead(output,4)==0)
 digitalWrite(7, HIGH);
else if (bitRead(output,4)==1) 
 digitalWrite(7, LOW);
if (bitRead(output,5)==0)
 digitalWrite(8, HIGH);
else if (bitRead(output,5)==1) 
 digitalWrite(8, LOW); 


delay(8000);//задержка


if (digitalRead(9)==0) //если замкнулся на "землю" пин D9
 a=2; //сработал микрик манометра о минимальном давлении в системе
else if (digitalRead(10)==0) //если замкнулся на "землю" пин D10 
 a=3; //сработал микрик манометра о максимальном давлении в системе
else
 a=1; //стрелка манометра находится в рабочем диапазоне (между минимальным и максимальным значением) 

    
  int temp1=20;
  int temp2=20;
  int temp3=20;
  int temp4=20;
  
  temp1=tempProcess(READ_TEMP, addr1);//читаем Т1
  tempProcess(START_CONVERT,addr1);
  Serial.print(temp1);
  Serial.print(";");

  temp2=tempProcess(READ_TEMP, addr2);//читаем Т2
  tempProcess(START_CONVERT,addr2);
  Serial.print(temp2);
  Serial.print(";");

  temp3=tempProcess(READ_TEMP, addr3);//читаем Т3
  tempProcess(START_CONVERT,addr3);
  Serial.print(temp3);
  Serial.print(";");  
  
  temp4=tempProcess(READ_TEMP, addr4);//читаем Т4
  tempProcess(START_CONVERT,addr4);
  Serial.print(temp4);
  Serial.print(";");  


  Serial.print(rtc.getHours());
  Serial.print(";");
  Serial.print(rtc.getMinutes());
  Serial.print(";");
  Serial.print(a); //передаётся положение стрелки электроконтактного манометра 
  Serial.println(";E");

delay(2000);//задержка
 
}





//============================== 
int tempProcess(boolean ch, byte *sensor)
  {
  int t=0;
  if(!ch)
   {
   ds.reset(); 
   ds.write(0xCC);
   ds.write(0x44);
   }
  else
   {
   ds.reset();
   ds.select(sensor);
   ds.write(0xBE);
   t = ds.read() | (ds.read()<<8);
   return  (t*10)>>4;//целое в десятых *C (214=>21,4*C)
   }
  }

А в комнате стоит другой контроллер, Ардуино “UNO”, который по rx/tx получает значения, обрабатывает и в ответ посылает через rx/tx команду. Код у этой “UNO” такой:
(часть кода не влезла по ограничению количества букв в сообщении)

Спойлер

byte output; //=b00000000
//бит "0"-для управления (и отображения состояния) насоса тёплых полов (on/off)
//бит "1"-для управления (и отображения состояния) насоса радиаторов (on/off)
//бит "2"-для управления (и отображения состояния) котлом (on/off)
//бит "3"-для управления (и отображения состояния) ТЭНа гидроразделителя 2,5 кВт (on/off)
//бит "4"-для управления (и отображения состояния) ТЭНа теплоаккумулятора 7,5 кВт (on/off)
//бит "5"-для управления (и отображения состояния) шарового крана на выходе теплоаккумулятора (откр/закр)

byte prev; //=b00000000
//бит "0"-для регистрации предыдущего состояния насоса полов (вкл/выкл) для фиксирования времени срабатывания
//бит "1"-для регистрации предыдущего состояния насоса радиаторов (вкл/выкл) для фиксирования времени срабатывания
//бит "2"-для регистрации предыдущего состояния котла (вкл/выкл) для фиксирования времени срабатывания
//бит "3"-для контроля появления новых данных в серийном порту (меняет значение один раз за период поступления данных из порта)
//бит "4"-для фиксации момента появления ошибки значения с датчика температуры воды на выходе с теплоаккумулятора (большое отклонение температуры текущего значения по отношению к предыдущему)
//бит "5"-для фиксации момента появления ошибки значения с датчика температуры в комнате (большое отклонение температуры текущего значения по отношению к предыдущему)
//бит "6"-для фиксации момента появления ошибки значения с датчика температуры котловой воды (большое отклонение температуры текущего значения по отношению к предыдущему)
//бит "7"-для фиксации момента появления ошибки значения с датчика температуры на улице (большое отклонение температуры текущего значения по отношению к предыдущему)


byte in1[80]; //массив для графика - температура в комнате (запись значения один раз в 9 минут)
byte in2[240]; //массив для графика - температура воды на выходе из ГР (запись значения один раз в 3 минуты)
char in3[80] = { -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60}; //массив для графика - температура на улице (запись значения один раз в 9 минут)
byte in5[80]; //массив для графика - температура воды на выходе из теплоаккумулятора (запись значения один раз в 9 минут)

int trend_kot[15]; //массив для отображения графика работы котла
int trend_rad_p[15]; //массив для отображения графика работы насоса радиаторов
int trend_floor_p[15]; //массив для отображения графика работы насоса тёплых полов
int trend_tenGR_25[15]; //массив для отображения графика работы ТЭНа гидроразделителя 2,5 кВт
int trend_tenTA_75[15]; //массив для отображения графика работы ТЭНа теплоаккумулятора 7,5 кВт
int trend_shar_kr[15]; //массив для отображения графика работы шарового крана на выходе из теплоаккумулятора
int trend_min_Pr[15]; //массив для отображения графика минимального давления
int trend_max_Pr[15]; //массив для отображения графика максимального давления


int tempRoomPrev = 0; //переменная для фиксации предыдущего значения температуры в комнате
int tempWaterPrev = 0; //переменная для фиксации предыдущего значения температуры котловой воды
int tempOutdoorPrev = 0; //переменная для фиксации предыдущего значения температуры на улице
int tempTAPrev = 0; //переменная для фиксации предыдущего значения температуры воды на выходе из теплоаккумулятора

int tempRoomBefore = 0; //"промежуточное" значение температуры (для обработки на наличие ошибки)
int tempWaterBefore = 0; //"промежуточное" значение температуры (для обработки на наличие ошибки)
int tempOutdoorBefore = 0; //"промежуточное" значение температуры (для обработки на наличие ошибки)
int tempTABefore = 0; //"промежуточное" значение температуры (для обработки на наличие ошибки)

byte hoursLook = 0; //для отображения текущего значения в процедурах
byte minutesLook = 0; //для отображения текущего значения в процедурах


//относится к получению данных из серийного порта:
String inString;
String message;

//массивы с информацией об ошибках
char beforeTempEror[14] = {0, 0, 0, 0, 0, 0, 0, 0}; //массив для записей значений в момент ошибки (показание, предшествовавшее ошибке)
char afterTempEror[14] = {0, 0, 0, 0, 0, 0, 0, 0}; //массив для записей значений в момент ошибки (показание, после выявленной ошибки)
byte hourEror[14]; //массив времени (часы) для фиксации момента ошибки
byte minutEror[14]; //массив времени (минуты) для фиксации момента ошибки
byte kindEror[14]; //массив указатель на источник ошибки
//kindEror=1 - ошибка температуры в помещении
//kindEror=2 - ошибка температуры воды в ГР
//kindEror=3 - ошибка температуры на улице
//kindEror=4 - минимальное давление в системе
//kindEror=5 - ошибка температуры воды на выходе из теплоаккумулятора
//kindEror=6 - максимальное давление в системе

byte option = 0; //переменная для определения (задания), какая опция регулирования активна (в работе) в данный момент:
//0 - "Out of home" режим "вне дома";
//1 - "Heat Pump" режим работы с тепловым насосом;
//2 - "Room temp." режим поддержания заданной температуры воздуха в комнате;
//3 - "Timer" режим отложенного пуска системы. Включается при достижении временем заданного значения;
//32 - "Room temp. by Timer" режим прогрева помещения при срабатывании таймера;
//4 - "Stop" - режим полной остановки, насосов и оборудования (при ненормальном давлении в системе)
//5 - "Floor" - прогрев пола в межсезонье
//6 - "el Room t." - поддержание температуры в помещении только за счёт электричества (без включения дизельного котла)
//7 - "Test Pumps" - режим тестирования насосов (прокрутка всех насосов одновременно, против прикипания)
//8 - "Out H" - опция аналогична опции "вне дома", но в период с 12 до 16 часов происходит нагрев бака-аккумулятора до заданной температуры



//Код для вырисовывания различных меню (окон):
void getMenu_Main()  //Главное окно (главное меню дисплея)
{
  a = 0;
  tft.fillScreen(WHITEGREY); //очистка диспея
  getMyButton(2, 268, 60, "C"); //вырисовывание сенсорной кнопки "С"
  tft.drawCircle(52, 283, 4, WHITE);//вырисовывание значка градуса
  getMyButton(121, 268, 0, ""); //вырисовывание сенсорной кнопки "Меню"
  tft.fillRect(156, 278, 47, 3, WHITE);//вырисовывание линий значка "Меню"
  tft.fillRect(156, 291, 47, 3, WHITE);//вырисовывание линий значка "Меню"
  tft.fillRect(156, 304, 47, 3, WHITE);//вырисовывание линий значка "Меню"
  tft.setCursor(10, 5); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(BLACK); // Указываем цвет текста
  tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
  tft.print("Opt:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.setCursor(10, 53); // Устанавливаем курсор (X = , Y = )
  tft.print("Room:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.setCursor(10, 85); // Устанавливаем курсор (X = , Y = )
  tft.print("Outdoor:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.setCursor(10, 117); // Устанавливаем курсор (X = , Y = )
  tft.print("Time:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.setCursor(10, 149); // Устанавливаем курсор (X = , Y = )
  tft.println("Flo. p.:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.println(" Rad. p.:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.println(" Kotel:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.println(" TEN 2.5:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.println(" TEN 7.5:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.print(" Shar. kr.:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  getIndicationMenu_Main(); //динамические данные главного окна
  delay (300);
}


void getIndicationMenu_Main()  //процедура вывода динамических данных в главном меню
{
  tft.fillRect(100, 5, 110, 16, WHITEGREY);//закрашивание области указания текущей опции
  tft.setCursor(100, 5); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(BLACK); // Указываем цвет текста
  tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
  getIndicationOption(); //отображаем текущий режим работы (option)

  //температуры:
  tft.fillRect(140, 46, 75, 102, WHITEGREY);//закрашивание области указания температур

  //температура воздуха в помещении:
  if (f >= 20) //если с критической ошибкой
    getIndicationTemp(140, 46, 3, tempRoomBefore, WHITE, RED); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
  else
    getIndicationTemp(140, 46, 3, tempRoomBefore, BLUE,  WHITEGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста

  //температура воздуха на улице:
  if (m >= 20) //если с критической ошибкой
    getIndicationTemp(140, 78, 3, tempOutdoorBefore, WHITE, RED); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
  else
    getIndicationTemp(140, 78, 3, tempOutdoorBefore, BLACK, WHITEGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста


  //время:
  tft.setCursor(80, 110); // Устанавливаем курсор (X = , Y = )
  tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
  tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
  getTimeDispley(hoursLook);
  tft.print(":"); //двоеточие для времени
  getTimeDispley(minutesLook);

  //отображение состояния насосов, котла, водонагревателя (вкл/выкл):
  getIndication(0, 157);
  getIndication(1, 173);
  getIndication(2, 189);
  getIndication(3, 205);
  getIndication(4, 221);
  getIndication(5, 237);

  if (kindEror[9] != 0 or kindEror[13] != 0) //индикация получения значения от датчиков с ошибкой
  {
    tft.setCursor(90, 248); // Устанавливаем курсор (X = , Y = )
    tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
    if (kindEror[9] != 0 and kindEror[13] == 0)
      tft.setTextColor(WHITE, DARKGREEN); // Указываем цвет текста
    else if (kindEror[13] != 0)
      tft.setTextColor(WHITE, RED); // Указываем цвет текста
    tft.print (" eror ");
  }
}


void getIndicationOption() //индикация, какая опция в работе
{
  if (option == 0)
    tft.print("Out");
  else if (option == 1)
    tft.print("Heat Pump");
  else if (option == 2)
    tft.print("Room t.");
  else if (option == 3)
  {
    tft.print("Timer ");
    tft.print(b);
    tft.print(":");
    tft.print(c);
  }
  else if (option == 32)
    tft.print("by Timer");
  else if (option == 4)
    tft.print("Stop");
  else if (option == 5)
    tft.print("Floor");
  else if (option == 6)
    tft.print("el Room t.");
  else if (option == 7)
    tft.print("Test Pumps");
  else if (option == 8)
    tft.print("Out H");
}


void getMenu_Temp()  //меню (окошко) выставления требуемой температуры воздуха в помещении
{
  a = 1;
  tft.fillScreen(WHITEGREY); //очистка диспея
  getMyButton(2, 268, 15, "Exit"); //вырисовывание сенсорной кнопки
  getMyButton(121, 268, 30, "OK"); //вырисовывание сенсорной кнопки
  getMyButton(121, 168, 45, "v"); //вырисовывание сенсорной кнопки
  getMyButton(121, 116, 45, "^"); //вырисовывание сенсорной кнопки
  getLookTemp();
  delay (300);
}


void getMenu_Other()  //окошко (меню) имеющее сенсорные кнопки (ссылки) на другие требуемые меню
{
  a = 2;
  tft.fillScreen(WHITEGREY); //очистка диспея
  getMyButton(2, 268, 15, "Exit"); //вырисовывание сенсорной кнопки
  getMyButton(121, 268, 30, "Out"); //вырисовывание сенсорной кнопки
  getMyButton(2, 134, 15, "Graph"); //вырисовывание сенсорной кнопки
  getMyButton(121, 134, 15, "Erors"); //вырисовывание сенсорной кнопки
  getMyButton(2, 1, 15, "HeatP"); //вырисовывание сенсорной кнопки
  getMyButton(121, 1, 15, "Timer"); //вырисовывание сенсорной кнопки
  getMyButton(121, 67, 15, "Floor"); //вырисовывание сенсорной кнопки
  getMyButton(2, 67, 15, "el tR"); //вырисовывание сенсорной кнопки
  getMyButton(2, 201, 15, "TestP"); //вырисовывание сенсорной кнопки
  getMyButton(121, 201, 15, "Out H"); //вырисовывание сенсорной кнопки
  delay (300);
}


void getMenu_Timer()  //окошко (меню) задания времени включения системы (отложенный старт)
{
  a = 3;
  tft.fillScreen(WHITEGREY); //очистка диспея
  getMyButton(2, 268, 15, "Exit"); //вырисовывание сенсорной кнопки
  getMyButton(121, 268, 30, "OK"); //вырисовывание сенсорной кнопки
  getMyButton(0, 168, 45, "v"); //вырисовывание сенсорной кнопки
  getMyButton(0, 116, 45, "^"); //вырисовывание сенсорной кнопки
  getMyButton(121, 168, 45, "v"); //вырисовывание сенсорной кнопки
  getMyButton(121, 116, 45, "^"); //вырисовывание сенсорной кнопки
  getLookDate();
  delay (300);
}


void getMenu_Eror()  //меню (окошко) отображения ошибок температур
{
  a = 7;
  tft.fillScreen(BLACK); //очистка диспея
  tft.setCursor(0, 0); // Устанавливаем курсор (X = , Y = )
  tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
  tft.setTextColor(YELLOW); // Указываем цвет текста
  tft.println(" Mist.:");
  getPrintKindEror (0, 9);

  tft.setCursor(0, 184); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(RED); // Указываем цвет текста
  tft.println(" Erors:");
  getPrintKindEror (10, 13);

  getMyButton(2, 268, 15, "Exit"); //вырисовывание сенсорной кнопки
  getMyButton(121, 268, 15, "Clear"); //вырисовывание сенсорной кнопки
  delay (300);
}


void getPrintKindEror (byte a, byte b)
{
  for (byte i = a; i <= b; i++)
  {
    getTimeDispley(hourEror[i]);
    tft.print(":");
    getTimeDispley(minutEror[i]);
    if (kindEror[i] == 1)
      tft.print(" Ro ");
    else if (kindEror[i] == 2)
      tft.print(" Wa ");
    else if (kindEror[i] == 3)
      tft.print(" Ou ");
    else if (kindEror[i] == 4)
      tft.print(" minPr ");
    else if (kindEror[i] == 5)
      tft.print(" TA ");
    else if (kindEror[i] == 6)
      tft.print(" maxP ");
    tft.print(beforeTempEror[i], DEC);
    tft.print(" to ");
    tft.println(afterTempEror[i], DEC);
  }
}


void drawTrends()
{
  tft.fillScreen(BLACK);

  //сетка по времени:
  for (byte i = 1; i < 12; i++)
    tft.drawFastVLine((20 * i - 1), 10, 291, DARKGREY);

  //серая сетка по шкале температур:
  for (byte i = 0; i < 5; i++)
    tft.drawFastHLine(0, (68 + 40 * i), 240, DARKGREY);

  //синие горизонтали по шкале температур (от 40 до 70 градусов):
  for (byte i = 0; i < 4; i++)
    tft.drawFastHLine(0, (18 + 10 * i), 240, BLUE);

  //синие горизонтали по шкале температур (от 0 до 40 градусов):
  for (byte i = 0; i < 4; i++)
    tft.drawFastHLine(0, (88 + 40 * i), 240, BLUE);

  tft.setCursor(0, 21); // Устанавливаем курсор (X = , Y = )
  tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
  tft.setTextColor(RED, BLACK); // Указываем цвет текста
  tft.print("60");

  tft.setCursor(0, 41); // Устанавливаем курсор (X = , Y = )
  tft.print("40");

  tft.setCursor(0, 81); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(MAGENTA, BLACK); // Указываем цвет текста
  tft.print("30");

  tft.setCursor(0, 121); // Устанавливаем курсор (X = , Y = )
  tft.print("20");

  tft.setCursor(0, 161); // Устанавливаем курсор (X = , Y = )
  tft.print("10");

  tft.setCursor(0, 201); // Устанавливаем курсор (X = , Y = )
  tft.print("0");


  //построение графика температуры воздуха на улице
  getGraphics(2, 0, 148, MAGENTA, 80, in3, 0);

  //построение графика температуры воздуха в комнате
  getGraphics(2, in1, 208, GREEN, 80, 0, 0);

  //построение графика температуры воды на выходе из теплоаккумулятора
  getGraphics(2, in5, 208, CYAN, 80, 0, 0);

  //построение графика температуры котловой воды (поверх других графиков)
  getGraphics(0, 0, 208, RED, 240, 0, in2);


  for (byte i = 0; i < 15; i++)
  {
    for (byte j = 0; j < 16; j++)
    {
      if (bitRead(trend_kot[i], j) == 1)
        tft.drawPixel ((i * 16 + (15 - j)), 248, YELLOW);
      if (bitRead(trend_rad_p[i], j) == 1)
        tft.drawPixel ((i * 16 + (15 - j)), 258, YELLOW);
      if (bitRead(trend_floor_p[i], j) == 1)
        tft.drawPixel ((i * 16 + (15 - j)), 268, YELLOW);
      if (bitRead(trend_tenGR_25[i], j) == 1)
        tft.drawPixel ((i * 16 + (15 - j)), 278, YELLOW);
      if (bitRead(trend_tenTA_75[i], j) == 1)
        tft.drawPixel ((i * 16 + (15 - j)), 288, YELLOW);
      if (bitRead(trend_shar_kr[i], j) == 1)
        tft.drawPixel ((i * 16 + (15 - j)), 298, YELLOW);
      if (bitRead(trend_min_Pr[i], j) == 1)
        tft.drawPixel ((i * 16 + (15 - j)), 192, YELLOW);
      if (bitRead(trend_max_Pr[i], j) == 1)
        tft.drawPixel ((i * 16 + (15 - j)), 152, YELLOW);
    }
  }


  tft.setCursor(0, 310); // Устанавливаем курсор (X = , Y = )
  tft.setTextSize(1); // Указываем размер символов в строке от 1 до 3
  tft.setTextColor(YELLOW); // Указываем цвет текста
  tft.print((10 * e / 124) / 10);
  tft.print(".");
  tft.print((10 * e / 124) % 10);
  tft.print(" L ");

  tft.print((TEN25_kWh + TEN75_kWh * 3) / 144);
  tft.print(" kWh ");

  tft.print(k / 10);
  tft.print(" C  ");

  tft.setTextColor(RED); // Указываем цвет текста
  tft.print(p / 10);
  tft.print(".");
  tft.print(p % 10);
  tft.print(" L ");

  tft.print(Last_kWh);
  tft.print(" kWh ");

  tft.print(r / 10);
  tft.print(" C");

  getIndicationDrawTrends();
}


void getGraphics(byte a, byte b[80], byte c, uint16_t d, byte e, char f[80], byte g[240]) //процедура построения графиков
{
  byte oldX = a;
  if (b == in1 or b == in5)
    tft.drawLine(0, (c - b[0]), oldX, (c - b[0]), d); //дополнительный отрезок от Х=0 до Х=2
  else if (f == in3)
    tft.drawLine(0, (c - f[0]), oldX, (c - f[0]), d); //дополнительный отрезок от Х=0 до Х=2
  for (byte x = 1; x < e; x++)
  {
    byte nxt_x;
    if (b == in1 or b == in5 or f == in3)
      nxt_x = (3 * x + 2);
    else if (g == in2)
      nxt_x = x;
    if (b == in1 or b == in5)
      tft.drawLine(oldX, (c - b[x - 1]), nxt_x, (c - b[x]), d);
    else if (f == in3)
      tft.drawLine(oldX, (c - f[x - 1]), nxt_x, (c - f[x]), d);
    else if (g == in2)
      tft.drawLine(oldX, (c - g[x - 1]), nxt_x, (c - g[x]), d);
    oldX = nxt_x;
  }
}


void getIndicationDrawTrends()
{
  tft.fillRect(0, 0, 240, 8, BLACK);//закрашивание области вывода значений в верхней части экрана


  //отображение температур с датчиков
  //температура в помещении:
  if (f >= 20)
    getIndicationTemp(0, 0, 1, tempRoomBefore, YELLOW, DARKGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
  else
    getIndicationTemp(0, 0, 1, tempRoomBefore, GREEN, BLACK); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста

  //температура на улице:
  if (m >= 20)
    getIndicationTemp(35, 0, 1, tempOutdoorBefore, YELLOW, DARKGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
  else
    getIndicationTemp(35, 0, 1, tempOutdoorBefore, MAGENTA, BLACK); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста

  //температура котловой воды:
  if (h >= 20)
    getIndicationTemp(70, 0, 1, tempWaterBefore, YELLOW, DARKGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
  else
    getIndicationTemp(70, 0, 1, tempWaterBefore, RED, BLACK); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста

  //температура воды на выходе с теплоаккумулятора:
  if (g >= 20)
    getIndicationTemp(105, 0, 1, tempTABefore, YELLOW, DARKGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
  else
    getIndicationTemp(105, 0, 1, tempTABefore, CYAN, BLACK); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста


  tft.setCursor(170, 0); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(WHITE); // Указываем цвет текста
  getIndicationOption(); //отображаем текущий режим работы (option)


  //отображение состояния исполнительных механизмов (изменение цвета текста)
  //состояние котла:
  getIndicationTrend(250, 2, "Kot"); //координата начала текста Y=, номер соответствующего бита "output", название тренда

  //состояние насоса радиаторов:
  getIndicationTrend(260, 1, "Rad"); //координата начала текста Y=, номер соответствующего бита "output", название тренда

  //состояние насоса тёплых полов:
  getIndicationTrend(270, 0, "Floor"); //координата начала текста Y=, номер соответствующего бита "output", название тренда

  //состояние ТЭНа 2,5 кВт:
  getIndicationTrend(280, 3, "TEN 25"); //координата начала текста Y=, номер соответствующего бита "output", название тренда

  //состояние ТЭНа 7,5 кВт:
  getIndicationTrend(290, 4, "TEN 75"); //координата начала текста Y=, номер соответствующего бита "output", название тренда

  //состояние шарового крана теплоаккумулятора:
  getIndicationTrend(300, 5, "Shar"); //координата начала текста Y=, номер соответствующего бита "output", название тренда
}


void getIndicationTrend(int y3, byte a3, String z3) //отображение состояния исполнительных механизмов при прорисовке трендов
{
  tft.setCursor(0, y3); // Устанавливаем курсор (X = , Y = )
  if (bitRead(output, a3) == 1) //если шаровой кран открыт
    tft.setTextColor(RED); // Указываем цвет текста
  else if (bitRead(output, a3) == 0) //если шаровой кран закрыт
    tft.setTextColor(BLUE); // Указываем цвет текста
  tft.print(z3);
}


void getIndicationTemp(byte x4, int y4, byte a4, int b4, uint16_t c4, uint16_t d4)
{
  tft.setCursor(x4, y4); // Устанавливаем курсор (X = , Y = )
  tft.setTextSize(a4); // Указываем размер символов в строке от 1 до 3
  tft.setTextColor(c4, d4); // Указываем цвет текста
  if (b4<0 and b4> -10) //записываю знак "-" в диапазоне от 0 до -1
  {
    tft.print("-0.");
    tft.print(-b4 % 10);
  }
  else
  {
    tft.print(b4 / 10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
    tft.print(".");
    if (b4 >= 0)
      tft.print(b4 % 10);
    else if (b4 < 0)
      tft.print(-b4 % 10);
  }
}


void initTachScr() //инициализация тачскрина и действия по нажатию сенсорных кнопок в различных окнах
{
  TSPoint p = ts.getPoint();
  // if sharing pins, you'll need to fix the directions of the touchscreen pins
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  // we have some minimum pressure we consider 'valid'
  // pressure of 0 means no pressing!
  if (p.z > MINPRESSURE && p.z < MAXPRESSURE)
  {
    int tmp = p.x; //запись значения p.x в переменную tmp
    p.x = p.y; //переназначение p.x на p.y
    p.y = tmp; //присвоение p.y значения p.x (через tmp)
    //Serial.println((String) "X="+p.x+",\tY="+p.y); //Выводим «сырые» показания TouchScreen
    // scale from 0->1023 to tft.width  i.e. left = 0, rt = width
    // most mcufriend have touch (with icons) that extends below the TFT
    // screens without icons need to reserve a space for "erase"
    p.x = map(p.x, 893, 145, 0, tft.width());
    p.y = map(p.y, 930, 135, 0, tft.height());
    //Serial.println((String) "("+p.x+","+p.y+")"); //Выводим преобразованные показания TouchScreen
    //tft.drawCircle(p.x, p.y, 3, RED); //Прорисовываем окружность диаметром 3 пикселя с центром в точке координат считанных с TouchScreen
  }

  if (a == 0)
  {
    if (p.x >= 0 and p.x <= 119 and p.y >= 320 and p.y <= 350)
      getMenu_Temp();
    if (p.x >= 120 and p.x <= 239 and p.y >= 320 and p.y <= 350)
      getMenu_Other();
  }
  else if (a == 1)
  {
    if (p.x >= 0 and p.x <= 119 and p.y >= 267 and p.y <= 319)
      getMenu_Main();
    if (p.x >= 120 and p.x <= 239 and p.y >= 267 and p.y <= 319)
    {
      option = 2;
      to_Tertn_Off_All();
      d = 0;
      getMenu_Main();
    }
    if (p.x >= 120 and p.x <= 239 and p.y > 167 and p.y < 220)
    {
      if (t > 40)
        t = t - 5;
      else
        t = 40;
      getLookTemp();
      delay(300);
    }
    if (p.x >= 120 and p.x <= 239 and p.y > 115 and p.y < 167)
    {
      if (t < 250)
        t = t + 5;
      else
        t = 250;
      getLookTemp();
      delay(300);
    }
  }

  else if (a == 2)
  {
    if (p.x >= 0 and p.x <= 119 and p.y >= 267 and p.y <= 319)
      getMenu_Main();
    if (p.x >= 120 and p.x <= 239 and p.y >= 267 and p.y <= 319)
    {
      option = 0;
      d = 0;
      getMenu_Main();
    }
    if (p.x >= 0 and p.x <= 119 and p.y >= 0 and p.y <= 51)
    {
      option = 1;
      d = 0;
      to_Tertn_Off_All();
      bitClear(output, 0); //выключить насос тёплых полов
      getMenu_Main();
    }
    if (p.x >= 120 and p.x <= 239 and p.y >= 0 and p.y <= 51)
      getMenu_Timer();
    if (p.x >= 0 and p.x <= 119 and p.y > 133 and p.y < 184)
    {
      a = 4;
      drawTrends();
      delay (300);
    }
    if (p.x >= 120 and p.x <= 239 and p.y > 133 and p.y < 184)
      getMenu_Eror();
    if (p.x >= 120 and p.x <= 239 and p.y > 66 and p.y < 117)
    {
      option = 5; //переход в режим нагрева полов в межсезонье
      FloorHour = hoursLook;
      d = 0;
      getMenu_Main();
    }
    if (p.x >= 0 and p.x <= 119 and p.y > 66 and p.y < 117)
    {
      option = 6; //переход в режим поддержания температуры в помещении только за счёт электричества (без включения дизельного котла)
      d = 0;
      to_Tertn_Off_All();
      getMenu_Main();
    }
    if (p.x >= 0 and p.x <= 119 and p.y > 200 and p.y < 251)
    {
      option = 7; //переход в режим тестирования насосов (прокрутка всех насосов против прикипания)
      d = 0;
      to_Tertn_Off_All();
      getMenu_Main();
    }
    if (p.x >= 120 and p.x <= 239 and p.y > 200 and p.y < 251)
    {
      option = 8; //переход в режим "вне дома" с функцией подогрева бака-аккумулятора в дневное время
      d = 0;
      to_Tertn_Off_All();
      getMenu_Main();
    }
  }
  else if (a == 3)
  {
    if (p.x >= 0 and p.x <= 119 and p.y >= 267 and p.y <= 319)
      getMenu_Other();
    if (p.x >= 120 and p.x <= 239 and p.y >= 267 and p.y <= 319)
    {
      option = 3;
      d = 0;
      getMenu_Main();
    }
    if (p.x >= 0 and p.x <= 119 and p.y > 115 and p.y < 167)
    {
      if (b < 23)
        b = b + 1;
      else
        b = 23;
      getLookDate();
      delay(300);
    }
    if (p.x >= 0 and p.x <= 119 and p.y > 167 and p.y < 219)
    {
      if (b > 0)
        b = b - 1;
      else
        b = 0;
      getLookDate();
      delay(300);
    }
    if (p.x >= 120 and p.x <= 239 and p.y > 115 and p.y < 167)
    {
      if (c < 50)
        c = c + 10;
      else
        c = 50;
      getLookDate();
      delay(300);
    }
    if (p.x >= 120 and p.x <= 239 and p.y > 167 and p.y < 219)
    {
      if (c > 0)
        c = c - 10;
      else
        c = 0;
      getLookDate();
      delay(300);
    }
  }
  else if (a == 4 and p.x >= 0 and p.x <= 239 and p.y >= 320 and p.y <= 350)
    getMenu_Main();
  else if (a == 7)
  {
    if (p.x >= 0 and p.x <= 119 and p.y >= 267 and p.y <= 319)
      getMenu_Other();
    if (p.x >= 120 and p.x <= 239 and p.y >= 267 and p.y <= 319)
    {
      for (byte i = 0; i <= 13; i++)
      {
        beforeTempEror[i] = 0;
        afterTempEror[i] = 0;
        hourEror[i] = 0;
        minutEror[i] = 0;
        kindEror[i] = 0;
      }
      bitClear(prev, 4);
      bitClear(prev, 5);
      bitClear(prev, 6);
      bitClear(prev, 7);
      f = 0;
      h = 0;
      m = 0;
      g = 0;
      u = 0;
      maxPres = 0;
      noHeat = 0;
      tempRoomPrev = 0;
      tempWaterPrev = 0;
      tempOutdoorPrev = 0;
      tempTAPrev = 0;
      getMenu_Eror();
    }
  }
}


void getErorAll(byte a2, byte b2, byte c2, int d2, int e2, byte f2, byte g2, byte h2) //процедура заполнения массивов ошибок (аварии)
{
  for (byte i = a2; i <= b2; i++) //перезаписываются предыдущие ошибки (освобождая место для записи новой ошибки)
  {
    beforeTempEror[i] = beforeTempEror[i + 1];
    afterTempEror[i] = afterTempEror[i + 1];
    hourEror[i] = hourEror[i + 1];
    minutEror[i] = minutEror[i + 1];
    kindEror[i] = kindEror[i + 1];
  }
  beforeTempEror[c2] = d2;
  afterTempEror[c2] = e2;
  kindEror[c2] = f2;
  hourEror[c2] = g2;
  minutEror[c2] = h2;
}


//вырисовывание сенсорных кнопок:
void getMyButton(byte x1, int y1, byte x2, String z1)
{
  tft.fillRoundRect (x1, y1, 117, 50, 15, BLUE); //залитый прямоугольник со скруглёнными углами
  y1 = y1 + 13;
  tft.setCursor(x1 + x2, y1); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(WHITE); // Указываем цвет текста
  tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
  tft.print(z1); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}


//выводим значение заданной температуры (в меню настройки задания):
void getLookTemp()
{
  tft.fillRect (30, 160, 70, 24, WHITEGREY);
  getIndicationTemp(30, 160, 3, (t + 50), BLACK, WHITEGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
}


//выводим значение заданного времени включения по таймеру (в меню таймера):
void getLookDate()
{
  tft.fillRect (75, 50, 90, 24, WHITEGREY);
  tft.setCursor(75, 50); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(BLACK); // Указываем цвет текста
  tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
  tft.print(b); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  tft.print(":"); //значок двоеточия для времени
  tft.print(c); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}


void getTimeDispley(byte unit) //процедура вывода времени на дисплей
{
  if (unit < 10)
  {
    tft.print("0"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
    tft.print(unit); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  }
  else
    tft.print(unit); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}


void getIndication(byte bitNomber, byte y) //индикация состояния исполнительных механизмов на дисплее в главном меню
{
  tft.fillCircle(140, y, 8, WHITEGREY); //закрашивается место индикации
  if (bitRead(output, bitNomber) == 1)
  {
    tft.drawCircle(140, y, 7, BLACK); //рисуется черная окружность
    tft.fillCircle(140, y, 5, RED); //закрашивается центр окружности красным цветом
  }
  else if (bitRead(output, bitNomber) == 0)
    tft.drawCircle(140, y, 7, BLACK); //рисуется черная окружность
}


void timeCounter(byte x) //отсчёт времени при смене режимов
{
  if (w != x)//если предыдущий режим был другим
    v = 0;
  w = x; //значению w присвоился номер текущего режима
  if (v < 250) //чтобы значение "v" не сбросилось на "0" при превышении максимального значения для "byte"
    v++;
}


void findTempEror(byte a1, int b1, int c1, byte d1, byte e1, byte f1, byte g1, int h1, int k1)
{
  //если предыдущее значение было нормальным (флаг не поднят) и поступившее значение нормальное либо если стартовое состояние (флаг не поднят) и стартовое значение:
  if (bitRead(prev, a1) == 0 and ((b1 > h1 and b1 < k1 and (b1 - c1) <= 30 and (b1 - c1) >= -30) || c1 == 0))
  {
    if (a1 == 5)
      tempRoomPrev = b1;
    else if (a1 == 6)
      tempWaterPrev = b1;
    else if (a1 == 4)
      tempTAPrev = b1;
    else if (a1 == 7)
      tempOutdoorPrev = b1;
  }
  //если предыдущее значение было ненормальным (флаг поднят), количество отклонений не превысило значение 9 и пришло нормальное значение:
  else if (bitRead(prev, a1) == 1 and d1<10 and b1 > h1 and b1 < k1 and (b1 - c1) <= 30 and (b1 - c1) >= -30)
  {
    bitClear(prev, a1); //записался бит равный 0 (для сброса счётчика приёма некоректного значения температуры)
    if (a1 == 5)
    {
      tempRoomPrev = b1;
      f = 0;
    }
    else if (a1 == 6)
    {
      tempWaterPrev = b1;
      h = 0;
    }
    else if (a1 == 4)
    {
      tempTAPrev = b1;
      g = 0;
    }
    else if (a1 == 7)
    {
      tempOutdoorPrev = b1;
      m = 0;
    }
  }
  //если предыдущее значение было ненормальным (флаг поднят) и пришло ненормальное значение:
  else if (bitRead(prev, a1) == 1 and (b1 <= h1 || b1 >= k1 || (b1 - c1) > 30 || (b1 - c1) < -30))
  {
    if (d1 < 10)
    {
      if (a1 == 5)
      {
        tempRoomBefore = c1;
        f++;
      }
      else if (a1 == 6)
      {
        tempWaterBefore = c1;
        h++;
      }
      else if (a1 == 4)
      {
        tempTABefore = c1;
        g++;
      }
      else if (a1 == 7)
      {
        tempOutdoorBefore = c1;
        m++;
      }
    }
    else if (d1 >= 10 and d1 < 20) //если пришло 10 отклонившихся значений подряд
    {
      getErorAll(10, 12, 13, c1 / 10, b1 / 10, e1, f1, g1);
      if (a1 == 5)
        f = 20;
      else if (a1 == 6)
        h = 20;
      else if (a1 == 4)
        g = 20;
      else if (a1 == 7)
        m = 20;
    }
  }
  //если предыдущее значение было нормальным (флаг не поднят) и пришло ненормальное значение:
  else if (bitRead(prev, a1) == 0 and (b1 <= h1 || b1 >= k1 || (b1 - c1) > 30 || (b1 - c1) < -30))
  {
    getErorAll(0, 8, 9, c1 / 10, b1 / 10, e1, f1, g1);
    if (a1 == 5)
    {
      tempRoomBefore = c1;
      f++;
    }
    else if (a1 == 6)
    {
      tempWaterBefore = c1;
      h++;
    }
    else if (a1 == 4)
    {
      tempTABefore = c1;
      g++;
    }
    else if (a1 == 7)
    {
      tempOutdoorBefore = c1;
      m++;
    }
    bitSet(prev, a1); //записался бит равный 1 (фиксация сильного отклонения температуры)
  }
}


void to_Tertn_Off_All() //выключить всё (отключить катушки всех управляющих реле)
{
  if (tempWaterBefore < 320) //если вода в гидроразделителе остыла
    s++;
  else
    s = 0;
  if (s >= 30)
  {
    bitClear(output, 0); //выключить насос тёплых полов
    s = 0;
  }
  bitClear(output, 1); //выключить насос радиаторов
  bitClear(output, 2); //выключить котёл
  bitClear(output, 3); //выключить ТЭН 2,5 кВт
  bitClear(output, 4); //выключить ТЭН 7,5 кВт
  bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
}


void setup()
{
  Serial.begin(9600);
  tft.reset();
  tft.begin(0); //включение дисплея
  tft.fillScreen(WHITEGREY); //очистка диспея
  getMenu_Main(); //при загрузке - на экран выводится главное окно (меню)
  delay(3000);  //задержка 3 сек
}


void loop() //главный повторяющийся цикл
{
  initTachScr(); //инициализация тачскрина и действия по нажатию сенсорных кнопок в различных окнах

  bitSet(prev, 3); //записался бит номер 3 равный 1 (для фиксирования момента получения данных через серийный порт)
  //принимаем строку из порта:
  while (Serial.available())
  {
    char inChar = Serial.read();
    inString += inChar;
    if (inChar == 'E')
    {
      //Serial.println(inString); // !!! закомментировать в конечном варианте
      message = inString;
      inString = "";
      bitClear(prev, 3); //записался бит номер 3 равный 0 (для фиксирования момента получения данных через серийный порт)
    }
  }

  byte Index1 = message.indexOf(';');             //разделительный символ
  byte Index2 = message.indexOf(';', Index1 + 1); //вычисляем второй символ
  byte Index3 = message.indexOf(';', Index2 + 1); //...третий и т.д.
  byte Index4 = message.indexOf(';', Index3 + 1);
  byte Index5 = message.indexOf(';', Index4 + 1);
  byte Index6 = message.indexOf(';', Index5 + 1);
  byte Index7 = message.indexOf(';', Index6 + 1);

  String Val_1 = message.substring(0, Index1);//что-то перед первым разд. сим.
  String Val_2 = message.substring(Index1 + 1, Index2); //что-то за первым разд. сим.
  String Val_3 = message.substring(Index2 + 1, Index3); //за вторым и т.д
  String Val_4 = message.substring(Index3 + 1, Index4);
  String Val_5 = message.substring(Index4 + 1, Index5);
  String Val_6 = message.substring(Index5 + 1, Index6);
  String Val_7 = message.substring(Index6 + 1, Index7);

  int tempRoom = Val_1.toInt();
  int tempWater = Val_2.toInt();
  int tempTA = Val_3.toInt(); //значение температуры на выходе из теплоакумулятора
  int tempOutdoor = Val_4.toInt();
  if (Val_4.toInt() == 0) //исключение значения "0"
    tempOutdoor = 1;
  byte hours = Val_5.toInt();
  byte minutes = Val_6.toInt();
  byte pressure = Val_7.toInt();

  if (bitRead(prev, 3) == 0) //если поступили данные через порт
  {

    tempRoomBefore = tempRoom; //"промежуточное" значение температуры (для обработки на наличие ошибки)
    tempWaterBefore = tempWater; //"промежуточное" значение температуры (для обработки на наличие ошибки)
    tempOutdoorBefore = tempOutdoor; //"промежуточное" значение температуры (для обработки на наличие ошибки)
    tempTABefore = tempTA; //"промежуточное" значение температуры (для обработки на наличие ошибки)


    //Регистрация ошибки с датчиков температуры и пропуск одиночных ошибок
    //для температуры в комнате:
    findTempEror(5, tempRoomBefore, tempRoomPrev, f, 1, hours, minutes, 0, 400); //номер бита "prev", температура обрабатываемая, температура предыдущая, счётчик, указание на вид ошибки "kindEror", час, минуты, минимально возможное значение, максимально возможное значение

    //для температуры котловой воды:
    findTempEror(6, tempWaterBefore, tempWaterPrev, h, 2, hours, minutes, 0, 950); //номер бита "prev", температура обрабатываемая, температура предыдущая, счётчик, указание на вид ошибки "kindEror", час, минуты, минимально возможное значение, максимально возможное значение

    //для температуры воды на выходе из теплоаккумулятора:
    findTempEror(4, tempTABefore, tempTAPrev, g, 5, hours, minutes, 0, 950); //номер бита "prev", температура обрабатываемая, температура предыдущая, счётчик, указание на вид ошибки "kindEror", час, минуты, минимально возможное значение, максимально возможное значение

    //для температуры воздуха на улице:
    findTempEror(7, tempOutdoorBefore, tempOutdoorPrev, m, 3, hours, minutes, -300, 500); //номер бита "prev", температура обрабатываемая, температура предыдущая, счётчик, указание на вид ошибки "kindEror", час, минуты, минимально возможное значение, максимально возможное значение


    tempRoom = tempRoomBefore; //фиксируется значение температуры после обработки на наличие ошибки
    tempWater = tempWaterBefore; //фиксируется значение температуры после обработки на наличие ошибки
    tempOutdoor = tempOutdoorBefore; //фиксируется значение температуры после обработки на наличие ошибки
    tempTA = tempTABefore; //фиксируется значение температуры после обработки на наличие ошибки


    hoursLook = hours; //для использования значения в процедурах
    minutesLook = minutes; //для использования значения в процедурах


    //Регистрация минимального значения давления и переход в режим "Stop"
    if (pressure == 2 and option != 4) //если зафиксировано минимальное давление но ещё нет перехода в режим "Stop"
      u++;
    else
      u = 0;
    if (pressure == 2 and u >= 3 and option != 4) //если поступило подряд три сигнала о минимальном давлении
    {
      getErorAll(10, 12, 13, 4, 3, 4, hours, minutes);
      option = 4; //переход в режим "Stop"
    }

    //Регистрация максимального значения давления
    if (pressure == 3 and option != 4 and noHeat == 0) //если зафиксировано максимальное давление но ещё нет перехода в режим "Stop" и нет отключения нагревателей
      maxPres++;
    else
      maxPres = 0;
    if (pressure == 3 and maxPres >= 30 and noHeat == 0) //если поступило подряд 30 сигналов о максимальном давлении но ещё нет отключения нагревателей
      noHeat = 1;
    if (noHeat >= 180 and pressure == 3 and option != 4) //если обогрев отключен 30 минут подряд и давление не упало
    {
      getErorAll(10, 12, 13, 13, 14, 6, hours, minutes);
      option = 4; //переход в режим "Stop"
    }
    else if (noHeat >= 180 and pressure != 3)
      noHeat = 0;



    //Действия в случае поступления критического количества ошибок с датчиков температуры и давления:
    //Температура в помещении. Если флаг поднят и счётчик ошибок превысил значение 9:
    if (bitRead(prev, 5) == 1 and f >= 10)
    {
      option = 0; //переход в режим "вне дома"
      tempRoom = 80; //включатся насосы на циркуляцию
    }

    //Температура котловой воды. Если флаг поднят и счётчик ошибок превысил значение 9:
    if (bitRead(prev, 6) == 1 and h >= 10)
    {
      option = 0; //переход в режим "вне дома"
      tempWater = 80; //включатся насосы на циркуляцию
    }


    //Температура на улице. Если флаг поднят и счётчик ошибок превысил значение 9:
    if (bitRead(prev, 7) == 1 and m >= 10)
    {
      option = 0; //переход в режим "вне дома"
      tempOutdoor = 10; //не знаю, какое значение задать
    }

    //Температура воды на выходе из теплоаккумулятора. Если флаг поднят и счётчик ошибок превысил значение 9:
    if (bitRead(prev, 4) == 1 and g >= 10)
    {
      option = 0; //переход в режим "вне дома"
      tempTA = 80; //включатся насосы на циркуляцию
    }


    //задержка времени при ручном смене режима:
    if ((d < 3 and option != 7) or (d < 18 and option == 7))
      d++;

    //фиксация "срабатывания" тепла, запасённого в теплоаккумуляторе с предыдущих суток:
    if ((7 <= hours and hours < 23) and z != 0) //в 7 часов утра счётчик z сбрасывается на ноль, чтобы в 23 часа зафиксировать состояние теплоаккумулятора (осталась ли в нём тепловая энергия)
      z = 0;
    if (z == 0 and (hours < 7 or hours >= 23)) //момент обращения к теплоаккумулятору, чтобы выяснить его состояние
    {
      if (tempTA > 320) //если теплоаккумуляторе осталось тепло
        z = 1;
      else if (tempTA <= 320) //если всё тепло из теплоаккумулятора израсходовано
        z = 2;
    }
    if (z == 1 and tempTA <= 320) //если была фиксация, что в теплоаккумуляторе осталось тепло, а затем было оно израсходовано
      z = 2;



    //Зависимость температуры уставки (верхняя граница) воды системы отопления от значения температуры наружного воздуха и температуры в помещении при работе котла:
    byte tW = (55 - 1 * (tempOutdoor / 10) + ((t + 50) - tempRoom));

    //Время (часы) включения ТЭН 7,5 кВт для накопления тепловой энергии в теплоаккумуляторе:
    if (r < 60)
      hour_TA = 2;
    else
      hour_TA = (r / 30 + 1);



    //Код для программ (обций), алгоритм работы контроллера:
    if (option != 1 and option != 2 and option != 32 and option != 4 and option != 6) //режим антизамерзания (работает всегда кроме режимов "Fast heat", "Room temp.", "Room temp. by Timer", "Stop")
    {
      if (tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) //циркуляция + подогрев
        timeCounter(11);

      if (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80)) //только циркуляция
        timeCounter(12);

      if (v >= 30 and (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and w == 11) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and w == 12)))
        bitSet(output, 0); //включить насос тёплых полов

      if (v >= 30 and (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and w == 11) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and w == 12)))
        bitSet(output, 1); //включить насос радиаторов

      if (v >= 30 and (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and w == 11) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and w == 12)))
        bitSet(output, 5); //открыть шаровой кран теплоаккумулятора

      if (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and (tempWater > 350 or tempTA > 350)) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and v >= 30 and w == 12))
        bitClear(output, 4); //выключить ТЭН 7,5 кВт

      if (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and (tempWater > 350 or tempTA > 350)) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and v >= 30 and w == 12))
        bitClear(output, 3); //выключить ТЭН 2,5 кВт

      if (v >= 30 and (tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and w == 11 and tempWater <= 150 and tempTA <= 350)
        bitSet(output, 2); //включить котёл
      else if (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and (tempWater > 350 or tempTA > 350)) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and v >= 30 and w == 12))
        bitClear(output, 2); //выключить котёл
    }



    //1. Опция "Out of home" ("вне дома"):
    if (option == 0 and tempRoom > 80 and tempWater > 80 and tempTA > 80 and d >= 3)
      to_Tertn_Off_All();



    //2. Опция "Heat Pump" (тепловой насос):
    if (option == 1 and d >= 3) //если выбран режим "Heat Pump"
    {
      if (tempOutdoor < 180)
      {
        if (tempRoom >= (t + 50)) //если температура в помещении больше или равна заданной (в любое время)
          timeCounter(13);
        if ((t + 50 - 5) <= tempRoom and tempRoom < (t + 50))
          timeCounter(14);
        if (tempRoom < (t + 50 - 5))
          timeCounter(15);

        if (v >= 30) //если сработал счётчик после смены режима
        {
          if (w != 13)
            bitSet(output, 0); //включить насос тёплых полов
          else if (w == 13)
            bitClear(output, 0); //выключить насос тёплых полов

          if (w == 15)
            bitSet(output, 1); //включить насос радиаторов
          else if (w == 13 or w == 14)
            bitClear(output, 1); //выключить насос радиаторов

          if (w != 13)//если не достигнута температура
            bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
          else if (w == 13)
            bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора

          if (w == 15 and (hours < 7 or hours >= 23))
            bitSet(output, 3); //включить ТЭН 2,5 кВт
          else if ((7 <= hours and hours < 23) or w != 15)
            bitClear(output, 3); //выключить ТЭН 2,5 кВт
        }
      }

      else if (tempOutdoor >= 180)
        timeCounter(18);
      if (v >= 30 and w == 18)
        to_Tertn_Off_All();
    }



    //3. Опция "Timer". Запуск системы в заданное время (работает в пределах текущих суток). На следующие сутки - поставить нельзя
    if (option == 3 and d >= 3) //если выбран режим "таймер"
    {
      to_Tertn_Off_All();
      if (hours >= b and minutes >= c)
        option = 32; //переходим в режим срабатывания таймера
    }



    //4. Опция "Room temp. by Timer" (прогрев помещения тёплыми полами при срабатывании таймера)
    if (option == 32)
    {
      if (tempRoom < (t + 50) and tempOutdoor < 180)
        timeCounter(8);
      if (v >= 30 and w == 8)
      {
        bitSet(output, 0); //включить насос тёплых полов
        if (tempTA > 350)
          bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
        else if (hours < 7 or hours >= 23)
        {
          bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
          bitSet(output, 3); //включить ТЭН 2,5 кВт
        }
        else
        {
          bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
          if (tempWater / 10 < (tW - 10) and tempWater / 10 < 55)
            bitSet(output, 2); //включить котёл
        }
      }
      else if (tempRoom >= (t + 50) or tempOutdoor >= 180)
        timeCounter(9);
      if (v >= 30 and w == 9)
        to_Tertn_Off_All();
      if (((b == 20 and hours == 0) or (b == 21 and hours == 1) or (b == 22 and hours == 2) or (b == 23 and hours == 3) or (hours >= (b + 4))) and minutes >= c) //если прошло 4 часа
        option = 0; //переход в режим "вне дома"
    }



    //5. Опция "Room temp." (поддержания температуры в комнате):
    if (option == 2 and d >= 3)
    {
      if (tempOutdoor < 180)
      {
        if (tempRoom >= (t + 50)) //если температура в помещении больше или равна заданной (в любое время)
          timeCounter(1);

        if (7 <= hours and hours < 23) //в дневное время
        {
          if ((t + 50 - 5) <= tempRoom and tempRoom < (t + 50))
            timeCounter(2);
          if ((t + 50 - 10) < tempRoom and tempRoom < (t + 50 - 5))
            timeCounter(10);
          if (tempRoom <= (t + 50 - 10))
            timeCounter(3);
        }

        if (hours < 7 or hours >= 23) //в ночное время
        {
          if ((t + 50 - 3) <= tempRoom and tempRoom < (t + 50)) //если температура в комнате ниже чем заданная, но выше либо равна чем заданная минус 0,3 градуса
          {
            if (w == 2 or w == 10 or w == 3) //для мгновенного перехода в 23 часа с дневного режима на ночной
            {
              w = 4;
              v = 30;
            }
            timeCounter(4);
          }
          if ((t + 50 - 10) <= tempRoom and tempRoom < (t + 50 - 3))
          {
            if (w == 2 or w == 10 or w == 3) //для мгновенного перехода в 23 часа с дневного режима на ночной
            {
              w = 5;
              v = 30;
            }
            timeCounter(5);
          }
          if (tempRoom < (t + 50 - 10))
          {
            if (w == 2 or w == 10 or w == 3) //для мгновенного перехода в 23 часа с дневного режима на ночной
            {
              w = 6;
              v = 30;
            }
            timeCounter(6);
          }
        }

        if (v >= 30) //если сработал счётчик после смены режима
        {
          if (w != 1)
            bitSet(output, 0); //включить насос тёплых полов
          else if (w == 1)
          {
            if (tempWater / 10 < 28 and bitRead(output, 3) == 0) //если вода в гидроразделителе остыла и ТЭН 2,5 кВт при этом выключен
              s++;
            else
              s = 0;
            if (s >= 30)
            {
              bitClear(output, 0); //выключить насос тёплых полов
              s = 0;
            }
          }

          if (w == 10 or w == 3 or w == 6 or (w == 5 and z == 1))
            bitSet(output, 1); //включить насос радиаторов
          else if (w == 1)
            bitClear(output, 1); //выключить насос радиаторов

          if ((tempTA > 320 and (w == 2 or w == 10 or w == 3 or (w == 4 and z == 1))) or w == 5 or w == 6)
            bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
          else
            bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора

          if ((w == 5 and z == 2) or (tempTA < 800 and w == 6))
            bitSet(output, 4); //включить ТЭН 7,5 кВт
          else if ((w == 4 or w == 1) and (hours >= 23 or hours < hour_TA))
            bitClear(output, 4); //выключить ТЭН 7,5 кВт

          if (w == 6 or r < 40 or ((w == 4 or w == 5) and (z == 2 or (hours >= hour_TA and hours < 7))))
            bitSet(output, 3); //включить ТЭН 2,5 кВт
          else if ((((w == 4 and (hours >= 23 or hours < hour_TA)) and z == 1) or w == 1) and r >= 40)
            bitClear(output, 3); //выключить ТЭН 2,5 кВт

          if ((((w == 2 or w == 10) and tempTA <= 320) or w == 3 or w == 6) and (tempWater / 10 < (tW - 10) and tempWater / 10 < 55))
            bitSet(output, 2); //включить котёл
          else if (((w == 2 or w == 10) and tempTA > 330) or w == 4 or w == 5 or w == 1)
            bitClear(output, 2); //выключить котёл
        }
      }

      else if (tempOutdoor >= 180)
        timeCounter(7);
      if (v >= 30 and w == 7)
        to_Tertn_Off_All();

      if (hours < 7 and tempTA < 800 and hours >= hour_TA)
        bitSet(output, 4); //включить ТЭН 7,5 кВт
    }



    //6. Опция "Stop" (выполняется при минимальном или максимальном давлении в системе):
    if (option == 4)
    {
      to_Tertn_Off_All();
      // дополнительное задание для моментального отключения насоса тёплых полов:
      bitClear(output, 0); //выключить насос тёплых полов
    }



    //7. Опция "Floor" - прогрев пола в межсезонье:
    if (option == 5 and d >= 3)
    {
      to_Tertn_Off_All();
      if (hours == 23 or hours < 5)
        FloorHour = 5;
      if (hours == FloorHour and minutes < 15)
      {
        bitSet(output, 0); //включить насос тёплых полов
        bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
      }
      else if (hours == FloorHour and minutes >= 15)
      {
        bitClear(output, 0); //выключить насос тёплых полов
        bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
        FloorHour++;
      }
      if (hours < 7 and hours >= 5 and tempTA < 800)
        bitSet(output, 4); //включить ТЭН 7,5 кВт
    }



    //8. Опция "el Room t." (поддержания температуры в комнате без включения дизельного котла):
    if (option == 6 and d >= 3)
    {
      if (tempOutdoor < 180)
      {
        if (tempRoom >= (t + 50)) //если температура в помещении больше или равна заданной (в любое время)
          timeCounter(1);

        if (7 <= hours and hours < 23) //в дневное время
        {
          if ((t + 50 - 5) <= tempRoom and tempRoom < (t + 50))
            timeCounter(2);
          if (tempRoom < (t + 50 - 5))
            timeCounter(10);
        }

        if (hours < 7 or hours >= 23) //в ночное время
        {
          if ((t + 50 - 3) <= tempRoom and tempRoom < (t + 50)) //если температура в комнате ниже чем заданная, но выше либо равна чем заданная минус 0,3 градуса
          {
            if (w == 2 or w == 10) //для мгновенного перехода в 23 часа с дневного режима на ночной
            {
              w = 4;
              v = 30;
            }
            timeCounter(4);
          }
          if (tempRoom < (t + 50 - 3))
          {
            if (w == 2 or w == 10) //для мгновенного перехода в 23 часа с дневного режима на ночной
            {
              w = 5;
              v = 30;
            }
            timeCounter(5);
          }
        }

        if (v >= 30) //если сработал счётчик после смены режима
        {
          if ((w != 1 and (hours < 7 or hours >= 23)) or (w != 1 and (tempTA > 330 and (7 <= hours and hours < 23))))
            bitSet(output, 0); //включить насос тёплых полов
          else if (w == 1 or (tempTA <= 320 and (7 <= hours and hours < 23)))
          {
            if (tempWater / 10 < 28 and bitRead(output, 3) == 0) //если вода в гидроразделителе остыла и ТЭН 2,5 кВт при этом выключен
              s++;
            else
              s = 0;
            if (s >= 30)
            {
              bitClear(output, 0); //выключить насос тёплых полов
              s = 0;
            }
          }

          if ((w == 10 and tempTA > 330) or (w == 5 and (z == 1 or tempTA > 450)))
            bitSet(output, 1); //включить насос радиаторов
          else if (w == 1 or ((w == 2 or w == 10 or w == 4) and tempTA < 320) or (w == 5 and tempTA < 400) or ((w == 4 or w == 5) and bitRead(output, 5) == 0))
            bitClear(output, 1); //выключить насос радиаторов

          if ((tempTA > 320 and (w == 2 or w == 10 or (w == 4 and z == 1))) or w == 5)
            bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
          else
            bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора

          if (w == 5 and z == 2)
            bitSet(output, 4); //включить ТЭН 7,5 кВт
          else if ((w == 4 or w == 1) and (hours >= 23 or hours < hour_TA))
            bitClear(output, 4); //выключить ТЭН 7,5 кВт

          if (r < 40 or ((w == 4 or w == 5) and (z == 2 or (hours >= hour_TA and hours < 7))))
            bitSet(output, 3); //включить ТЭН 2,5 кВт
          else if ((((w == 4 and (hours >= 23 or hours < hour_TA)) and z == 1) or w == 1) and r >= 40)
            bitClear(output, 3); //выключить ТЭН 2,5 кВт

        }
      }

      else if (tempOutdoor >= 180)
        timeCounter(7);
      if (v >= 30 and w == 7)
        to_Tertn_Off_All();

      if (hours < 7 and tempTA < 800 and hours >= hour_TA)
        bitSet(output, 4); //включить ТЭН 7,5 кВт
    }


    //9. Опция "Test Pumps" (тестирование насосов. Прокрутка всех насосов против прикипания):
    if (option == 7 and d >= 3)
    {
      bitSet(output, 0); //включить насос тёплых полов
      bitSet(output, 1); //включить насос радиаторов
      bitSet(output, 2); //включить котёл
      if (d >= 18)
      {
        bitClear(output, 0); //выключить насос тёплых полов
        bitClear(output, 1); //выключить насос радиаторов
        bitClear(output, 2); //выключить котёл
        option = 0; //переход в режим "вне дома"
      }
    }


    //10. Опция "Out H" ("вне дома", но с подогревом бака-аккумулятора в дневное время):
    if (option == 8 and tempRoom > 80 and tempWater > 80 and tempTA > 80 and d >= 3)
    {
      if ((12 <= hours and hours < 16) and tempTA < 670)
      {
        bitSet(output, 4); //включить ТЭН 7,5 кВт
        if (tempTA > 650)
          option = 0; //переход в режим "вне дома"
      }
      else
        to_Tertn_Off_All();
    }

    //Выполняется всегда:

    if (7 <= hours and hours < 23)
    {
      bitClear(output, 3); //выключить ТЭН 2,5 кВт
      if (option != 8)
        bitClear(output, 4); //выключить ТЭН 7,5 кВт
    }

    if (tempTA >= 850)
      bitClear(output, 4); //выключить ТЭН 7,5 кВт

    if ((tempWater / 10 >= tW and option != 1) || tempWater / 10 >= 65) //если температура воды в системе отопления достигла верхней уставки - выключить котёл (отдельный случай для режима "натоп")
      bitClear(output, 2); //выключить котёл



    //Если сработал сигнал о необходимости отключения обогрева при превышении давления в системе
    if (noHeat > 0 and option != 4)
    {
      bitClear(output, 2); //выключить котёл
      bitClear(output, 3); //выключить ТЭН 2,5 кВт
      bitClear(output, 4); //выключить ТЭН 7,5 кВт
      bitSet(output, 0); //включить насос тёплых полов
      bitSet(output, 1); //включить насос радиаторов
      bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
      noHeat++;
    }



    Serial.write(output); //передача исполнительных команд на управляющие реле через МК nano



    //формирование массивов для графиков температуры:
    q++;
    if (q >= 18) //если прошло 180 секунд
    {
      for (byte x = 0; x < 239; x++)
        in2[x] = in2[x + 1]; //массив температуры воды в ГР


      if (tempWater > 400)
        in2[239] = (120 + tempWater / 10);
      else if (tempWater <= 400 and tempWater >= 0)
        in2[239] = (tempWater * 10 / 25);


      if (n >= 3)
      {
        for (byte x = 0; x < 79; x++)
        {
          in1[x] = in1[x + 1]; //массив температуры в помещении
          in3[x] = in3[x + 1]; //массив температуры на улице
          in5[x] = in5[x + 1]; //массив температуры воды на выходе теплоаккумулятора
        }

        in1[79] = (tempRoom * 10 / 25);


        if (tempOutdoor > 400)
          in3[79] = (120 + tempOutdoor / 10 - 60);
        else if (tempOutdoor <= 400)
          in3[79] = (tempOutdoor * 10 / 25 - 60);

        if (tempTA > 400)
          in5[79] = (120 + tempTA / 10);
        else if (tempTA <= 400)
          in5[79] = (tempTA * 10 / 25);


        n = 0;
      }

      if (n < 3)
        n++;


      //формирование массивов для графиков включения оборудования
      for (byte i; i < 15; i++)
      {
        trend_kot[i] = trend_kot[i] << 1;
        bitWrite(trend_kot[i], 0, bitRead(trend_kot[i + 1], 15));
        trend_rad_p[i] = trend_rad_p[i] << 1;
        bitWrite(trend_rad_p[i], 0, bitRead(trend_rad_p[i + 1], 15));
        trend_floor_p[i] = trend_floor_p[i] << 1;
        bitWrite(trend_floor_p[i], 0, bitRead(trend_floor_p[i + 1], 15));
        trend_tenGR_25[i] = trend_tenGR_25[i] << 1;
        bitWrite(trend_tenGR_25[i], 0, bitRead(trend_tenGR_25[i + 1], 15));
        trend_tenTA_75[i] = trend_tenTA_75[i] << 1;
        bitWrite(trend_tenTA_75[i], 0, bitRead(trend_tenTA_75[i + 1], 15));
        trend_shar_kr[i] = trend_shar_kr[i] << 1;
        bitWrite(trend_shar_kr[i], 0, bitRead(trend_shar_kr[i + 1], 15));
        trend_min_Pr[i] = trend_min_Pr[i] << 1;
        bitWrite(trend_min_Pr[i], 0, bitRead(trend_min_Pr[i + 1], 15));
        trend_max_Pr[i] = trend_max_Pr[i] << 1;
        bitWrite(trend_max_Pr[i], 0, bitRead(trend_max_Pr[i + 1], 15));
      }

      bitWrite(trend_kot[14], 0, bitRead(output, 2));
      bitWrite(trend_rad_p[14], 0, bitRead(output, 1));
      bitWrite(trend_floor_p[14], 0, bitRead(output, 0));
      bitWrite(trend_tenGR_25[14], 0, bitRead(output, 3));
      bitWrite(trend_tenTA_75[14], 0, bitRead(output, 4));
      bitWrite(trend_shar_kr[14], 0, bitRead(output, 5));
      if (pressure == 2)
        bitWrite(trend_min_Pr[14], 0, 1);
      else
        bitWrite(trend_min_Pr[14], 0, 0);
      if (pressure == 3)
        bitWrite(trend_max_Pr[14], 0, 1);
      else
        bitWrite(trend_max_Pr[14], 0, 0);

      if (a == 4)
        drawTrends();

      q = 0;
    }

    //Расчёт израсходованного топлива, электроэнергии и средней температуры на улице за прошедшие сутки
    if (bitRead(output, 2) == 1)
      e++;
    if (bitRead(output, 2) != bitRead(prev, 2) and bitRead(output, 2) == 0)
      e = e - 2; //вычитается время, которое топливо на горелку не подавалось (для каждого пуска котла)
    if (j == 30 and k == 0) //для немедленного начала фиксации среднесуточной температуры при компиляции скетча
    {
      j = hours + 1;
      k = tempOutdoor;
    }
    if (hours == j)
    {
      k = ((k * j) + tempOutdoor) / (j + 1);
      j++;
    }
    if (bitRead(output, 3) == 1)
      TEN25_kWh++;
    if (bitRead(output, 4) == 1)
      TEN75_kWh++;
    if (j >= 24 and hours == 0)
    {
      p = 10 * e / 124; //количество израсходованного топлива за прошедшие сутки (из условия 2.9 л/ч)
      r = k; //значение средней температуры на улице за прошедшие сутки
      Last_kWh = ((TEN25_kWh + TEN75_kWh * 3) / 144); //значение в киловатт-часах израсходованной электроэнергии за прошедшие сутки
      e = 0;
      j = 0;
      k = 0;
      TEN25_kWh = 0;
      TEN75_kWh = 0;
    }


    //динамические данные в окнах:
    if (a == 0) //вывод динамических данных на главном экране
      getIndicationMenu_Main();
    if (a == 4) //выводим динамические данные на дисплее с графиками
      getIndicationDrawTrends();
  }
}

Выглядит “UNO” в комнате вот так:


Так вот… эта связка работает успешно несколько лет. И я очень доволен. И не помешало то, что нет образования.

Но сейчас у меня проблема. Когда я попытался внедрить новую систему, по похожему принципу - у меня не получается. Обратился за помощью сюда. Пытаюсь найти причину, по которой Ардуино “Mega” не работает так, как мне того надо.
Поможете - здорово. Не получится - ну и фиг с ним… будет нерешённая задача, на которую убил кучу времени.

Это вАААпсче нипричем. Энти “приблуды” влехкую напишет 12-летний школьнег.

В качестве примера возмите любой код из линукс на гите и … вот там уже не так все просто.

Кстати, есть флпрог волшебный.

вам там в первой теме, куда писали подсказали что я забыл учесть минусовые значения)))
а вы точно проверяли работу вашей системы включив 4 реле ?
включаем 1 2 3 4, затем выключаем, и снова включаем, и так хотя бы минут 5
в целом и на ардуино можно собирать… иногда легко, а иногда надо долго отлаживать, в целом на ней собирать тоже можно, но созданна она для того что бы делится проектами (делать за других))) )

если уже так делали… то можно разбить программу на 2 части, одна будет например датчики отслеживать, другая управлять силовой электроникой, и может еще что можно придумать… только в идеале что бы подсказывать вам, это все надо собрать самим, что лениво)))

Яж уже скока раз повторял - ну дали вам нахалтай, просто так, нате, берите и пользуйтесь всякими BT, wifi, BLE, GPS, NFC и прочие фишки современности но никто не знает даже, во сколько миру обошелся СИНИЙ СВЕТОДИОД!!! епрст!!!

Ну не лечат геморой по интернету.

Ты же не идешь на форум к хирургам с вопросом, что лучше, длинный или толстый а потом обвиняешь всех на хирургическом форуме в том, что все у тебя осталось как и было и все без исключения хирурги триньтарасы и тока ты дарт^анЬян?

Ну с чего ты решил, что кто-то будет думать и делать за тебя? Тебе дали вполне адекватные подсказки, даже код написали тестовый, а ты что? Ты уже впрямую обвиняешь, мол вот раньше было не совсем плохо, а сейчас еще хуже… Ну это не в какие ворота уже…

Сходи к Гайверу.

Сообщение читал там. Но почему-то подумал, что речь идёт про мой изначальный код. А там - всё по отрицательным значениям было корректно. Поэтому - не придал внимания сообщению.
Если же речь идёт - что после Вашего редактирования… то спасибо что обратили моё внимание. Попытаюсь проанализировать.
Но во вчерашнем состоянии - контроллер работал уже с участием Вашей корректировки кода. И на графике отрицательные значения отображались на первый взгдляд корректно:


что там было на табол где вывод цифрами (в моменты отрицательных температур) - не посмотрел.

Ну,… я видел моменты, когда включены все 4 реле (в основном режиме работы - они все 4 и включены).

Вот это вроде мне понятно. Запишу код - и пусть поработает с включением отключением реле. Единственный момен - я отключу при этом силовую часть. И как-бы исчезнут нюансы, связанные с сильноточными цепями. Реле будут замыкаться на разомкнутую нагрузку…

Это и было запланировано в дальнейшем - передача через rx/tx. Но я решил всё делать поэтапно. И подумал - что сначала надо наладить без передачи. Но уже и не знаю что думать… если в этом вся проблема - то готов перекинуться, посылать сигналы на “nano” а потом обрабатывать их на этой “Mega”. Но в голове не укладывается - что при этом проблема может отвалиться

в том то и дело, надо под нагрузкой на это смотреть)))(что бы то что подключено при этом к реле работало, дабы убедиться что тока хватает, или еще что) или я вас не понял…

На всякий случай, код который в настоящее время загружен в обсуждаемый микроконтроллер “Mega”:

Спойлер
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;

#include <OneWire.h>
OneWire ds(21);  // выбор пина ардуино, на котором висит шина 1-wire

#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4

// Цвета
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
#define WHITEGREY  0xC638
#define DARKGREY  0x7BEF
#define DARKGREEN 0x0C80

#define pinVoda_tick        18
boolean stateCheck_voda;

#define pinElectr_tick       19
boolean stateCheck_electr;

uint8_t relay;

// Адреса датчиков
byte ADDR_DS18B20 [6][8] = {
  {0x28, 0xD5, 0x97, 0x76, 0x0, 0x0, 0x0, 0x76}, // KompressorC   0
  {0x28, 0x4F, 0x9C, 0x73, 0x0, 0x0, 0x0, 0xF9}, // WaterInC      1
  {0x28, 0xFC, 0xD1, 0x72, 0x0, 0x0, 0x0, 0x2D}, // WaterOutC     2
  {0x28, 0x95, 0x28, 0x75, 0x0, 0x0, 0x0, 0xAB}, // temp3C        3
  {0x28, 0xEF, 0x8C, 0x76, 0x0, 0x0, 0x0, 0x58}, // DefrostC      4
  {0x28, 0x8C, 0x3A, 0xB4, 0xE, 0x0, 0x0, 0xDB}  // EvaporatorC    5
};

// Массивы для графиков
uint8_t tEvaporator[320];
uint8_t tOverheat[320];
uint8_t tKompressor[320];
uint8_t tWaterIn[320];
uint8_t tWaterOut[320];
uint8_t tDefrost[320];
uint8_t heatPower[320];
uint8_t gWater[320];
uint8_t ElectrPow[320];
uint8_t COP[320];

// Точные значения
int16_t tOverheatReal;
int32_t gWaterReal;
int32_t heatPowerReal;
int32_t ElectrPowReal;
int32_t COPreal;

int32_t gWaterRealPrew;
boolean FlagWater = false;
boolean FirstImpulsWater = false;
boolean SecondImpulsWater = false;

// Средние значения
int32_t countHeatAndElectr = 0;
int32_t countHeat = 0;
int32_t countElectr = 0;

// Флаги цикла
boolean Flag1 = true;
boolean Flag2 = true;
boolean Flag3 = true;
boolean Flag4 = true;
uint8_t countGetGrafic = 9;
uint32_t Timer;

// Графики реле
int16_t trend_fan[20];
int16_t trend_kompressor[20];
int16_t trend_pump[20];
int16_t trend_4way_valve[20];

// Водомер
uint32_t tmr;
boolean gWaterFlag = false;
uint32_t WaterStartTimer;
uint32_t WaterCountTimer;

// Электросчётчик
uint32_t tmr2;
boolean ElectrFlag = false;
uint32_t ElectrStartTimer;
uint32_t ElectrCountTimer;

// Режимы
boolean StartHeatFlag;
boolean StopFlag;
boolean HeatOptionFlag;
boolean HeatOptionPauseFlag;
boolean DefrostOptionFlag;

uint8_t tKompressorCounter = 0;
uint8_t HeatOptionCout = 0;
uint8_t HeatOptionPauseCout = 0;
uint8_t defrostCount = 0;
uint8_t StopMaxTemp = 0;
uint8_t StopMaxElectr = 0;
uint8_t StopMinTempHeater = 0;
uint8_t StopNo4xvalve = 0;
uint8_t StopNoElectric = 0;

uint8_t StopKind = 0;
uint8_t MistakeTemp[6] = {};
boolean getMistakeFlag = false;

uint32_t HeatOptionPauseStartTime;
uint32_t StartHeatTime;
uint32_t StopTime;
uint32_t DefrostTime;

// Переменные температур
int16_t Temper[6] = {0, 0, 0, 0, 0, 0};
int16_t TemperPrev[6];

// ============== CRC8 ДЛЯ DS18B20 ==============
uint8_t dallas_crc8(const uint8_t *data, uint8_t len) {
  uint8_t crc = 0;
  for (uint8_t i = 0; i < len; i++) {
    uint8_t inbyte = data[i];
    for (uint8_t j = 0; j < 8; j++) {
      uint8_t mix = (crc ^ inbyte) & 0x01;
      crc >>= 1;
      if (mix) crc ^= 0x8C;
      inbyte >>= 1;
    }
  }
  return crc;
}

// ============== НОВАЯ ФУНКЦИЯ ЧТЕНИЯ С ПРОВЕРКОЙ CRC ==============
// Возвращает температуру в десятых долях (например, 156 = 15.6°C)
// Возвращает 0x7FFF при ошибке CRC
int16_t readDS18B20(byte *addr) {
  uint8_t data[9];

  ds.reset();
  ds.select(addr);
  ds.write(0xBE); // Читать scratchpad

  for (uint8_t i = 0; i < 9; i++) {
    data[i] = ds.read();
  }

  // Проверка CRC
  if (dallas_crc8(data, 8) != data[8]) {
    return 0x7FFF; // Ошибка
  }

  int16_t raw = (data[1] << 8) | data[0];
  return (raw * 10) >> 4; // Десятые доли
}

// ============== СБРОС ШИНЫ (запуск конвертации) ==============
void resetTemp() {
  ds.reset();
  ds.write(0xCC); // Ко всем датчикам
  ds.write(0x44); // Конвертация
}

// ============== ПОЛУЧЕНИЕ ТЕМПЕРАТУР ==============
void gettingTemp() {
  for (uint8_t i = 0; i < 6; i++) {
    int16_t temp = readDS18B20(ADDR_DS18B20[i]);

    if (temp != 0x7FFF) {
      // Данные достоверны
      Temper[i] = temp;
      TemperPrev[i] = temp;
      if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0;
    } else {
      // Ошибка CRC
      Temper[i] = TemperPrev[i];
      if (StopFlag == false) {
        MistakeTemp[i]++;
        if (MistakeTemp[i] > 6) {
          StopFlag = true;
          StopTime = millis();
          StopKind = 6;
        }
      }
    }
  }
}

// ============== ПРОВЕРКА ГРАНИЦ ТЕМПЕРАТУР ==============
void tempMistake(uint8_t a, int16_t b, int16_t c, int16_t d) {
  // Эта функция теперь только для физических проверок,
  // так как CRC уже отсеяла помехи.
  // Но оставляем на всякий случай.
  if (MistakeTemp[a] > 0) return; // Уже есть ошибка CRC

  if (Temper[a] >= b && Temper[a] <= c && abs(Temper[a] - TemperPrev[a]) <= d) {
    // Норма
  } else {
    Temper[a] = TemperPrev[a];
    if (StopFlag == false) MistakeTemp[a]++;
    if (MistakeTemp[a] > 6 && StopFlag == false) {
      StopFlag = true;
      StopTime = millis();
      StopKind = 6;
    }
  }
}

// ============== ВОДОМЕР ==============
void voda_tick() {
  boolean current_status = digitalRead(pinVoda_tick);
  if (stateCheck_voda && !current_status) {
    tmr = millis();
    gWaterFlag = true;
    stateCheck_voda = current_status;
  }
  if (!stateCheck_voda && current_status) stateCheck_voda = current_status;
  if (current_status) gWaterFlag = false;
  if (!current_status && millis() - tmr >= 1000 && gWaterFlag == true) {
    WaterCountTimer = millis() - WaterStartTimer;
    WaterStartTimer = millis();
    gWaterFlag = false;
    if (FirstImpulsWater == true) SecondImpulsWater = true;
    FirstImpulsWater = true;
  }
}

// ============== ЭЛЕКТРОСЧЁТЧИК ==============
void electr_tick() {
  boolean current_status = digitalRead(pinElectr_tick);
  if (stateCheck_electr && !current_status) {
    tmr2 = millis();
    ElectrFlag = true;
    stateCheck_electr = current_status;
  }
  if (!stateCheck_electr && current_status) stateCheck_electr = current_status;
  if (current_status) ElectrFlag = false;
  if (!current_status && millis() - tmr2 >= 50 && ElectrFlag == true) {
    ElectrCountTimer = millis() - ElectrStartTimer;
    ElectrStartTimer = millis();
    ElectrFlag = false;
  }
}

// ============== ВЫВОД ЗНАЧЕНИЙ НА ДИСПЛЕЙ ==============
void getDataInt100(int16_t a) {
  if (a >= -9 && a < 0) {
    tft.print("-0.0");
    tft.print(abs(a));
  } else if (a < 9 && a >= 0) {
    tft.print("0.0");
    tft.print(a);
  } else if (a <= -10 && a > -100) {
    tft.print("-0.");
    tft.print(abs(a % 100));
  } else {
    tft.print(a / 100);
    tft.print(".");
    if (abs(a % 100) < 10) tft.print("0");
    tft.print(abs(a % 100));
  }
}

void getDataInt10(int16_t a) {
  if (a >= -9 && a < 0) {
    tft.print("-0.");
    tft.print(abs(a));
  } else if (a < 9 && a >= 0) {
    tft.print("0.");
    tft.print(a);
  } else {
    tft.print(a / 10);
    tft.print(".");
    tft.print(abs(a % 10));
  }
}

int16_t getDataForUintMassiv(int16_t a) {
  int16_t LastInMassive;
  if (a < -400) LastInMassive = 0;
  else if (a >= -400 && a < 500) LastInMassive = round((a + 400) / 5);
  else if (a >= 500 && a < 1250) LastInMassive = round(180 + (a - 500) / 10);
  else LastInMassive = 255;
  return LastInMassive;
}

int32_t getDataForUintMassiv32(int32_t a) {
  int32_t LastInMassive;
  if (a < -400) LastInMassive = 0;
  else if (a >= -400 && a < 0) LastInMassive = round((a + 400) / 10);
  else if (a >= 0 && a <= 160) LastInMassive = round(40 + a);
  else if (a > 160 && a < 710) LastInMassive = round(200 + (a - 160) / 10);
  else LastInMassive = 255;
  return LastInMassive;
}

void getGraphics(uint16_t c, uint16_t d, uint8_t g[320]) {
  int16_t oldX = 0;
  int16_t oldY = g[0];
  for (int16_t x = 1; x < 320; x++) {
    int16_t nxt_x = x;
    tft.drawLine(oldX, (c - oldY), nxt_x, (c - g[x]), d);
    oldY = g[x];
    oldX = nxt_x;
  }
}

// ============== ОТРИСОВКА ДИСПЛЕЯ ==============
void getMainDisplay() {
  tft.fillScreen(BLACK);

  // Сетка по времени
  for (uint8_t i = 1; i < 8; i++) {
    tft.drawFastVLine((40 * i), 20, 190, DARKGREY);
    tft.drawFastVLine((40 * i), 230, 210, DARKGREY);
  }

  // Сетка по температуре
  for (uint8_t i = 0; i < 11; i++) {
    if (i == 4 || i == 5 || i == 6 || i == 7 || i == 9 || i == 10)
      tft.drawFastHLine(0, (5 + 20 * i), 320, DARKGREEN);
    else if (i == 1 || i == 2 || i == 3 || i == 8)
      tft.drawFastHLine(0, (5 + 20 * i), 320, BLUE);
  }
  for (uint8_t i = 0; i < 5; i++)
    tft.drawFastHLine(0, (231 + 10 * i), 320, BLUE);
  tft.drawFastHLine(0, 275, 320, BLUE);
  for (uint8_t i = 0; i < 8; i++)
    tft.drawFastHLine(0, (295 + 20 * i), 320, DARKGREEN);

  // Подписи
  tft.setTextSize(1);

  tft.setCursor(0, 22); tft.setTextColor(WHITE, BLACK); tft.print("90");
  tft.setCursor(0, 42); tft.print("70");
  tft.setCursor(0, 62); tft.print("50");
  tft.setCursor(0, 82); tft.print("40");
  tft.setCursor(0, 102); tft.print("30");
  tft.setCursor(0, 122); tft.print("20");
  tft.setCursor(0, 142); tft.print("10");
  tft.setCursor(0, 162); tft.print("0");
  tft.setCursor(0, 182); tft.print("-10");
  tft.setCursor(0, 202); tft.print("-20");

  tft.setCursor(0, 238); tft.print("5");
  tft.setCursor(0, 258); tft.print("3");
  tft.setCursor(0, 272); tft.print("1.6");
  tft.setCursor(0, 292); tft.print("1.4");
  tft.setCursor(0, 312); tft.print("1.2");
  tft.setCursor(0, 332); tft.print("1");
  tft.setCursor(0, 352); tft.print("0.8");
  tft.setCursor(0, 372); tft.print("0.6");
  tft.setCursor(0, 392); tft.print("0.4");
  tft.setCursor(0, 412); tft.print("0.2");
  tft.setCursor(0, 432); tft.print("0");

  // Назначения датчиков
  tft.setCursor(0, 0); tft.setTextColor(RED); tft.print("Kompressor:");
  tft.setCursor(10, 10); tft.setTextColor(MAGENTA); tft.print("Overheat:");
  tft.setCursor(125, 10); tft.setTextColor(CYAN); tft.print("Defrost:");
  tft.setCursor(215, 10); tft.setTextColor(RED); tft.print("Evaporator:");
  tft.setCursor(120, 0); tft.setTextColor(YELLOW); tft.print("Water in:");
  tft.setCursor(220, 0); tft.setTextColor(GREEN); tft.print("Water out:");
  tft.setCursor(5, 210); tft.setTextColor(RED); tft.print("Heat power:");
  tft.setCursor(152, 210); tft.setTextColor(YELLOW); tft.print("COP:");
  tft.setCursor(218, 210); tft.setTextColor(GREEN); tft.print("Electr pow:");
  tft.setCursor(22, 220); tft.setTextColor(MAGENTA); tft.print("G water:");
}

// ============== SETUP ==============
void setup() {
  Serial.begin(9600);

  tft.reset();
  tft.begin(38022);
  tft.invertDisplay(true);

  pinMode(18, INPUT_PULLUP);
  stateCheck_voda = digitalRead(pinVoda_tick);

  pinMode(19, INPUT_PULLUP);
  stateCheck_electr = digitalRead(pinElectr_tick);

  for (int i = 0; i < 320; i++) {
    tEvaporator[i] = 80;
    tOverheat[i] = 80;
    tKompressor[i] = 80;
    tWaterIn[i] = 80;
    tWaterOut[i] = 80;
    tDefrost[i] = 80;
    heatPower[i] = 40;
    gWater[i] = 40;
    ElectrPow[i] = 40;
    COP[i] = 40;
  }

  pinMode(48, OUTPUT); digitalWrite(48, HIGH);
  pinMode(49, OUTPUT); digitalWrite(49, HIGH);
  pinMode(50, OUTPUT); digitalWrite(50, HIGH);
  pinMode(51, OUTPUT); digitalWrite(51, HIGH);

  StopFlag = false;
  HeatOptionFlag = false;
  HeatOptionPauseFlag = false;
  DefrostOptionFlag = false;
  StartHeatFlag = true;
  StartHeatTime = millis();
  Timer = millis();

  delay(1000);
}

// ============== LOOP ==============
void loop() {
  voda_tick();
  electr_tick();

  // Шаг 1: запуск конвертации (3-5 сек)
  if (millis() - Timer >= 3000 && millis() - Timer < 5000 && Flag1 == true) {
    resetTemp();
    Flag1 = false;
  }

  // Шаг 2: чтение температур (5-7 сек)
  if (millis() - Timer >= 5000 && millis() - Timer < 7000 && Flag2 == true) {
    gettingTemp();
    Flag2 = false;
  }

  // Шаг 3: расчёты и логика (7-9 сек)
  if (millis() - Timer >= 7000 && millis() - Timer < 9000 && Flag3 == true) {
    if (getMistakeFlag == false) {
      for (uint8_t i = 0; i < 6; i++) {
        TemperPrev[i] = Temper[i];
      }
      getMistakeFlag = true;
    }

    // Проверка границ (дополнительная, CRC уже отсеяла мусор)
    tempMistake(0, -100, 1200, 200);
    tempMistake(1, 0, 600, 100);
    tempMistake(2, 0, 900, 50);
    tempMistake(3, -400, 800, 400);
    tempMistake(4, -400, 600, 400);
    tempMistake(5, -400, 800, 200);

    tOverheatReal = (Temper[3] - Temper[5]);

    // Расход воды
    if (digitalRead(51) == HIGH) {
      gWaterReal = 0;
      gWaterRealPrew = gWaterReal;
      FlagWater = false;
    } else if (digitalRead(51) == LOW && millis() - WaterStartTimer > 400000) {
      StopFlag = true;
      StopTime = millis();
      StopKind = 7;
    } else if (SecondImpulsWater == false) {
      gWaterReal = gWaterRealPrew;
      FlagWater = true;
    } else {
      gWaterReal = round(3600000 / WaterCountTimer);
      gWaterRealPrew = gWaterReal;
      FlagWater = false;
    }

    // Электрическая мощность
    if (millis() - ElectrStartTimer > 60000)
      ElectrPowReal = 0;
    else
      ElectrPowReal = 72000 / ElectrCountTimer;

    // Тепловая мощность
    heatPowerReal = ((int32_t)Temper[2] - (int32_t)Temper[1]) * gWaterReal * 1163 / 10000;

    // COP
    if (ElectrPowReal < 10)
      COPreal = 0;
    else
      COPreal = 100 * heatPowerReal / ElectrPowReal;

    // Средние значения
    countHeat = countHeat + heatPowerReal;
    countElectr = countElectr + ElectrPowReal;
    countHeatAndElectr++;

    // Управление вентилятором при перегреве
    if (HeatOptionFlag == true && Temper[0] >= 850 && tOverheatReal >= 300 && digitalRead(48) == LOW) {
      tKompressorCounter++;
      if (tKompressorCounter >= 4) {
        bitClear(relay, 0);
        tKompressorCounter = 0;
      }
    } else if (HeatOptionFlag == true && (Temper[0] <= 750 || tOverheatReal <= 200) && digitalRead(48) == HIGH) {
      tKompressorCounter++;
      if (tKompressorCounter >= 2) {
        bitSet(relay, 0);
        tKompressorCounter = 0;
      }
    } else {
      tKompressorCounter = 0;
    }

    // Режимы нагрева
    if (HeatOptionFlag == true && Temper[1] >= 360) {
      HeatOptionCout++;
      if (HeatOptionCout >= 6) {
        HeatOptionFlag = false;
        HeatOptionPauseFlag = true;
        HeatOptionCout = 0;
        HeatOptionPauseStartTime = millis();
        bitClear(relay, 2);
        bitClear(relay, 0);
      }
    } else {
      HeatOptionCout = 0;
    }

    if (HeatOptionPauseFlag == true && digitalRead(49) == LOW && millis() - HeatOptionPauseStartTime >= 120000)
      bitClear(relay, 1);

    if (HeatOptionPauseFlag == true && digitalRead(51) == HIGH && millis() - HeatOptionPauseStartTime >= 960000) {
      if (digitalRead(51) == HIGH) {
        WaterStartTimer = millis();
        FirstImpulsWater = false;
        SecondImpulsWater = false;
      }
      bitSet(relay, 3);
      HeatOptionPauseStartTime = millis();
    }

    if (HeatOptionPauseFlag == true && digitalRead(51) == LOW && millis() - HeatOptionPauseStartTime >= 60000) {
      bitClear(relay, 3);
      HeatOptionPauseStartTime = millis();
    }

    if (HeatOptionPauseFlag == true && Temper[1] <= 280) {
      HeatOptionPauseCout++;
      if (HeatOptionPauseCout >= 5) {
        HeatOptionPauseFlag = false;
        HeatOptionPauseCout = 0;
        StartHeatTime = millis();
        StartHeatFlag = true;
      }
    } else {
      HeatOptionPauseCout = 0;
    }

    // Defrost
    if (HeatOptionFlag == true && Temper[5] < -100) {
      defrostCount++;
      if (defrostCount >= 6) {
        HeatOptionFlag = false;
        DefrostOptionFlag = true;
        DefrostTime = millis();
        defrostCount = 0;
      }
    } else {
      defrostCount = 0;
    }

    // Защиты
    if (HeatOptionFlag == true && digitalRead(49) == HIGH) {
      StopNo4xvalve++;
      if (StopNo4xvalve >= 3) {
        bitClear(relay, 2);
        StopFlag = true;
        StopTime = millis();
        StopKind = 1;
        StopNo4xvalve = 0;
      }
    } else {
      StopNo4xvalve = 0;
    }

    if ((StartHeatFlag == true || HeatOptionFlag == true || HeatOptionPauseFlag == true || DefrostOptionFlag == true) && Temper[0] >= 1000) {
      StopMaxTemp++;
      if (StopMaxTemp >= 5) {
        StopFlag = true;
        StopTime = millis();
        StopKind = 2;
        StopMaxTemp = 0;
      }
    } else {
      StopMaxTemp = 0;
    }

    if ((StartHeatFlag == true || HeatOptionFlag == true || HeatOptionPauseFlag == true || DefrostOptionFlag == true) && ElectrPowReal >= 151) {
      StopMaxElectr++;
      if (StopMaxElectr >= 6) {
        StopFlag = true;
        StopTime = millis();
        StopKind = 3;
        StopMaxElectr = 0;
      }
    } else {
      StopMaxElectr = 0;
    }

    if ((StartHeatFlag == true || HeatOptionFlag == true || HeatOptionPauseFlag == true) && Temper[4] <= 50) {
      StopMinTempHeater++;
      if (StopMinTempHeater >= 3) {
        StopFlag = true;
        StopTime = millis();
        StopKind = 4;
        StopMinTempHeater = 0;
      }
    } else {
      StopMinTempHeater = 0;
    }

    if (digitalRead(50) == LOW && ElectrPowReal <= 10) {
      StopNoElectric++;
      if (StopNoElectric >= 6) {
        StopFlag = true;
        StopTime = millis();
        StopKind = 5;
        StopNoElectric = 0;
      }
    } else {
      StopNoElectric = 0;
    }

    // Start heat
    if (StartHeatFlag == true && millis() - StartHeatTime >= 20000 && Temper[1] >= 50) {
      if (digitalRead(51) == HIGH) {
        WaterStartTimer = millis();
        FirstImpulsWater = false;
        SecondImpulsWater = false;
      }
      bitSet(relay, 3);
    }

    if (StartHeatFlag == true && digitalRead(51) == LOW && millis() - StartHeatTime >= 30000 && Temper[0] <= 850)
      bitSet(relay, 0);

    if (StartHeatFlag == true && digitalRead(51) == LOW && digitalRead(48) == LOW && millis() - StartHeatTime >= 40000)
      bitSet(relay, 1);

    if (StartHeatFlag == true && digitalRead(51) == LOW && digitalRead(48) == LOW && digitalRead(49) == LOW && millis() - StartHeatTime >= 45000)
      bitSet(relay, 2);

    if (StartHeatFlag == true && digitalRead(51) == LOW && digitalRead(48) == LOW && digitalRead(50) == LOW && digitalRead(49) == LOW) {
      StartHeatFlag = false;
      HeatOptionFlag = true;
    }

    // Stop
    if (StopFlag == true) {
      StartHeatFlag = false;
      HeatOptionFlag = false;
      HeatOptionPauseFlag = false;
      DefrostOptionFlag = false;
      bitClear(relay, 2);
      bitClear(relay, 0);
      if (millis() - StopTime >= 120000) bitClear(relay, 1);
      if (millis() - StopTime >= 180000) bitClear(relay, 3);
    }

    // Defrost
    if (DefrostOptionFlag == true) {
      if (digitalRead(51) == HIGH) {
        WaterStartTimer = millis();
        FirstImpulsWater = false;
        SecondImpulsWater = false;
      }
      bitSet(relay, 3);
      bitClear(relay, 2);
      bitSet(relay, 0);
      if (millis() - DefrostTime >= 120000) bitClear(relay, 1);
      if (millis() - DefrostTime >= 900000 && Temper[5] >= 20) {
        DefrostOptionFlag = false;
        StartHeatTime = millis();
        StartHeatFlag = true;
      }
    }

    // Очистка старых значений
    tft.fillRect(74, 0, 30, 20, BLACK);
    tft.fillRect(181, 0, 30, 20, BLACK);
    tft.fillRect(289, 0, 30, 20, BLACK);
    tft.fillRect(74, 210, 30, 20, BLACK);
    tft.fillRect(181, 210, 30, 10, BLACK);
    tft.fillRect(289, 210, 30, 10, BLACK);
    tft.fillRect(152, 220, 60, 10, BLACK);
    tft.fillRect(100, 470, 30, 8, BLACK);
    tft.fillRect(200, 470, 30, 8, BLACK);

    // Вывод значений на дисплей
    tft.setCursor(74, 0);
    tft.setTextColor((MistakeTemp[0] > 0) ? CYAN : RED);
    getDataInt10(Temper[0]);

    tft.setCursor(181, 0);
    tft.setTextColor((MistakeTemp[1] > 0) ? CYAN : YELLOW);
    getDataInt10(Temper[1]);

    tft.setCursor(289, 0);
    tft.setTextColor((MistakeTemp[2] > 0) ? CYAN : GREEN);
    getDataInt10(Temper[2]);

    tft.setCursor(74, 10);
    tft.setTextColor((MistakeTemp[3] > 0) ? CYAN : MAGENTA);
    getDataInt10(tOverheatReal);

    tft.setCursor(181, 10);
    tft.setTextColor((MistakeTemp[4] > 0) ? CYAN : CYAN);
    getDataInt10(Temper[4]);

    tft.setCursor(289, 10);
    tft.setTextColor((MistakeTemp[5] > 0) ? CYAN : RED);
    getDataInt10(Temper[5]);

    tft.setCursor(74, 210); tft.setTextColor(RED); getDataInt100(heatPowerReal);
    tft.setCursor(181, 210); tft.setTextColor(YELLOW); getDataInt100(COPreal);
    tft.setCursor(289, 210); tft.setTextColor(GREEN); getDataInt100(ElectrPowReal);
    tft.setCursor(74, 220);
    tft.setTextColor(FlagWater ? CYAN : MAGENTA);
    getDataInt100(gWaterReal);

    tft.setCursor(100, 470); tft.setTextColor(RED);
    getDataInt100(countHeat / countHeatAndElectr);
    tft.setCursor(200, 470); tft.setTextColor(GREEN);
    getDataInt100(countElectr / countHeatAndElectr);

    tft.setCursor(152, 220); tft.setTextColor(RED);
    if (StopFlag == true) {
      tft.print("Stop");
      switch (StopKind) {
        case 1: tft.print(" No 4x valve"); break;
        case 2: tft.print(" maxT Kompr"); break;
        case 3: tft.print(" maxW Electr"); break;
        case 4: tft.print(" minT Heater"); break;
        case 5: tft.print(" No Electr"); break;
        case 6: tft.print(" Err Temp"); break;
        case 7: tft.print(" No G_water"); break;
        case 8: tft.print(" No Option"); break;
      }
    } else if (StartHeatFlag == true) {
      tft.print("Start heat");
    } else if (HeatOptionFlag == true) {
      tft.print("Heat option");
    } else if (HeatOptionPauseFlag == true) {
      tft.print("Pause heat");
    } else if (DefrostOptionFlag == true) {
      tft.print("Defrost");
    } else {
      StopFlag = true;
      StopTime = millis();
      StopKind = 8;
    }

    // Состояния реле
    tft.setCursor(0, 440);
    tft.setTextColor(bitRead(relay, 2) ? RED : BLUE);
    tft.print("Kompressor");

    tft.setCursor(0, 450);
    tft.setTextColor(bitRead(relay, 3) ? RED : BLUE);
    tft.print("Pump");

    tft.setCursor(0, 460);
    tft.setTextColor(bitRead(relay, 1) ? RED : BLUE);
    tft.print("4 way valve");

    tft.setCursor(0, 470);
    tft.setTextColor(bitRead(relay, 0) ? RED : BLUE);
    tft.print("Fan");

    Flag3 = false;
  }

  // Шаг 4: управление реле (9-10 сек)
  if (millis() - Timer >= 9000 && millis() - Timer < 10000 && Flag4 == true) {
    digitalWrite(48, bitRead(relay, 0) ? LOW : HIGH);
    digitalWrite(49, bitRead(relay, 1) ? LOW : HIGH);
    digitalWrite(50, bitRead(relay, 2) ? LOW : HIGH);
    digitalWrite(51, bitRead(relay, 3) ? LOW : HIGH);
    Flag4 = false;
  }

  // Шаг 5: обновление графиков (сброс цикла)
  if (millis() - Timer >= 10000) {
    Timer = millis();

    if (countGetGrafic >= 9) {
      // Сдвиг массивов
      for (uint16_t x = 0; x < 319; x++) {
        tEvaporator[x] = tEvaporator[x + 1];
        tOverheat[x] = tOverheat[x + 1];
        tKompressor[x] = tKompressor[x + 1];
        tWaterIn[x] = tWaterIn[x + 1];
        tWaterOut[x] = tWaterOut[x + 1];
        gWater[x] = gWater[x + 1];
        heatPower[x] = heatPower[x + 1];
        ElectrPow[x] = ElectrPow[x + 1];
        COP[x] = COP[x + 1];
        tDefrost[x] = tDefrost[x + 1];
      }

      // Новые значения
      tKompressor[319] = (uint8_t)getDataForUintMassiv(Temper[0]);
      tWaterIn[319] = (uint8_t)getDataForUintMassiv(Temper[1]);
      tWaterOut[319] = (uint8_t)getDataForUintMassiv(Temper[2]);
      tOverheat[319] = (uint8_t)getDataForUintMassiv(tOverheatReal);
      tDefrost[319] = (uint8_t)getDataForUintMassiv(Temper[4]);
      tEvaporator[319] = (uint8_t)getDataForUintMassiv(Temper[5]);
      heatPower[319] = (uint8_t)getDataForUintMassiv32(heatPowerReal);
      COP[319] = (uint8_t)getDataForUintMassiv32(COPreal);
      ElectrPow[319] = (uint8_t)getDataForUintMassiv32(ElectrPowReal);
      gWater[319] = (uint8_t)getDataForUintMassiv32(gWaterReal);

      // Графики реле
      for (uint8_t i = 0; i < 20; i++) {
        trend_fan[i] = trend_fan[i] << 1;
        bitWrite(trend_fan[i], 0, bitRead(trend_fan[i + 1], 15));
        trend_kompressor[i] = trend_kompressor[i] << 1;
        bitWrite(trend_kompressor[i], 0, bitRead(trend_kompressor[i + 1], 15));
        trend_pump[i] = trend_pump[i] << 1;
        bitWrite(trend_pump[i], 0, bitRead(trend_pump[i + 1], 15));
        trend_4way_valve[i] = trend_4way_valve[i] << 1;
        bitWrite(trend_4way_valve[i], 0, bitRead(trend_4way_valve[i + 1], 15));
      }
      bitWrite(trend_fan[19], 0, bitRead(relay, 0));
      bitWrite(trend_kompressor[19], 0, bitRead(relay, 2));
      bitWrite(trend_pump[19], 0, bitRead(relay, 3));
      bitWrite(trend_4way_valve[19], 0, bitRead(relay, 1));

      getMainDisplay();

      getGraphics(245, RED, tKompressor);
      getGraphics(245, YELLOW, tWaterIn);
      getGraphics(245, GREEN, tWaterOut);
      getGraphics(245, MAGENTA, tOverheat);
      getGraphics(245, CYAN, tDefrost);
      getGraphics(245, RED, tEvaporator);
      getGraphics(475, RED, heatPower);
      getGraphics(475, YELLOW, COP);
      getGraphics(475, GREEN, ElectrPow);
      getGraphics(475, MAGENTA, gWater);

      for (uint8_t i = 0; i < 20; i++) {
        for (uint8_t j = 0; j < 16; j++) {
          if (bitRead(trend_fan[i], j) == 1) tft.drawPixel((i * 16 + (15 - j)), 479, YELLOW);
          if (bitRead(trend_kompressor[i], j) == 1) tft.drawPixel((i * 16 + (15 - j)), 449, YELLOW);
          if (bitRead(trend_pump[i], j) == 1) tft.drawPixel((i * 16 + (15 - j)), 459, YELLOW);
          if (bitRead(trend_4way_valve[i], j) == 1) tft.drawPixel((i * 16 + (15 - j)), 469, YELLOW);
        }
      }

      countGetGrafic = 0;
    }

    countGetGrafic++;
    Flag1 = true;
    Flag2 = true;
    Flag3 = true;
    Flag4 = true;
  }
}

Я не могу хаотично включать и выключать компрессор и четырёхходовой клапан. Могу потерять оборудование…
Это если только на время таких экспериментов включать что-то другое, типа утюга… или ещё чего… но именно компрессор - так опасно. А линия (провода от датчиков) - там как раз идут вдоль силового провода компрессора (проходят через стену)

Ты просто фантастический везунчик. Начинай играть в джекпот

Я понимаю, что вот так вот можно спокойно посидеть и покурить на спортивной площадке, но, можно спросить нашего Эфиопского футболиста, сколько пачек в день он выкуривает? Енто я про схему, тут была примерно такая-же тема с зависаниями и ее шатали больше месяца. Дык воот, может дело не в “бабине” ???

тогда возможно сразу попытаться купить реле с опторазвязкой ? не факт что поможет, но просто дополнительный вариант

А чо как мало? Про снабберы и кондюки 10n и оптроны забыл? Давай уже начнем УСЕ ПА ГОСТУ !!!

скачки напряжения могут вешать его мегу… я думаю совет имеет место быть, а может и не поможет…

@PPeterr Всё же для начала определитесь в чём у вас беда! В датчиках, коде, внешних ЭМ помехах? Ибо, судя по последним данным(перезагрузка контроллера в отсутствии WDT) у вас ещё и с электричеством проблемы :roll_eyes:

Нарисовал по памяти. Возможно - будет что-то понятно:

И ссылка на фото “доски”: https://forum.arduino.ru/uploads/default/optimized/3X/c/4/c467c66792793326bab58f6570f95f961aae4c7c_2_621x500.jpeg

Понятно пока только одно - оптопары и снабберы НЕ нужны.

Между БП 12В поставь диод Шоттки и конденсатор ближе к меге мокрофарад на 100-500 и 16 Вольт.

Входы от счетчика и водомера около меги (ближе) зашунтируй керамикой 1-10nf.

В цепь контактора (или пускателя) либо снаббер, либо RC гаситель.

Пока усе.