Посоветуйте датчик температуры

Любой автомобильный, корабельный или иной промышленный датчик температуры масла или ОЖ - это всегда NTC в корпусе с резьбой. Промышленные единственно что отличаются на одно и двухвыводные для подключения в двигателях с землёй на корпусе и с изолированной землей (пароходики морские всякие).

я бы не был так категоричен :grinning_face:
у “вагов” встречается и такое

ds18b20 до 125`С

Их даже можно прямо гирляндой развешивать, если верно подобрать подтягивающий резистор.

А кроме ntc, ds18b20 вот еще варианты

  • термопара К. Но там уже нужен свой модуль/адаптер MAX6675. Зато диапазон температур типа K 0-800 градусов.

-термосопротивление PT100( MAX31865) -50 +200`C

У мотора пробовать придется на помехоустойчивость от системы зажигания. Параллельно ntc даже электролит можно повесить.

Я использую KTY81 для измерения температуры теплоносителя. До 150 С.

У тех же E-Kits вы можете спокойно купить как дешманский китай, так и вполне оригинальные DS18B20
У них же компания SmartModule какупает их для сборки своих термодатчиков в готовом виде (сразу в гильзах герметичные и с фирменным же силиконовым термостойким кабелем).
По названиям контакты гуглятся без проблем.

Здравствуйте.

Решил не создавать новой темы. Эта тема - по названию, вроде бы подходит. Прошу прощения у автора топика, если что…

В общем - вероятно мой вопрос связан “посоветуйте датчик”, а может и нет…

Много времени назад, ещё на старом форуме мною была создана ветка Принять значение температуры с контроллера на контроллер | Аппаратная платформа Arduino . Тот набор оборудования, может в несколько изменённом состоянии, успешно работает до сих пор.

В настоящее время появилась новая задача. Бьюсь над ней уже порядка 3 месяца, самостоятельно решить не могу. Обращаюсь к вам. Задача - выполнить автоматизацию теплового насоса. Тепловой насос воздух/вода, на базе б/у сплит системы. В общем то, система уже работает и показывает удовлетворительные результаты, но проблема в том, что не реже чем раз в сутки наступает критическая ошибка, приводящая к останову. По-моему, эта ошибка связана с датчиками температуры DS18B20 (но не факт).

Напишу, что может быть источником проблемы, на мой взгляд. Датчики температуры. Всего их пока что участвует 6 шт. Три из них - находятся в наружном блоке, в экстремальных условиях (температура измеряемой среды может меняться от -20 до +70 град. С за пару минут).

Вот в таком виде

крепил датчики к трубкам вот так

Сначала это была обычная витая пара. Длина от датчиков до контроллера примерно 5 м. Потом проложил новую витую пару, экранированную, по совету с форума две пары выделил - одна пара для питания, вторая для сигнал/земля. На конце линии конденсатор (электролит и керамика), подтягивающий резистор также на конце линии. Помех стало меньше, но полностью проблема не ушла.

В общем, - пока кратко и сумбурно. Если будут уточняющие вопросы - расскажу более развёрнуто.

Так выглядит водомер, участвующий в показаниях

так выглядит “доска” с микроконтроллером

так выглядит сегодняшняя ошибка на экране дисплея (шаг по горизонтали - один час, некорректное показание - улетевшее в район 50 град. С. После перетыка питания шины - пошло в линию)

код

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

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

#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0
#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin

// Assign human-readable names to some common 16-bit color values:
#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; //=b00000000 - для указания, в каком состоянии должны быть реле
//бит "0"-для управления (и отображения состояния) вентилятора наружного блока (on/off)
//бит "1"-для управления (и отображения состояния) 4-х ходовым клапаном наружного блока (on/off)
//бит "2"-для управления (и отображения состояния) компрессора наружного блока (on/off)
//бит "3"-для управления (и отображения состояния) насоса (on/off)


//адреса для датчиков температуры
//0xDB00000EB43A8C28, {0x28, 0x8C, 0x3A, 0xB4, 0xE, 0x0, 0x0, 0xDB} //1 (оригинал)
//0x3A00000076A76F28, {0x28, 0x6F, 0xA7, 0x76, 0x0, 0x0, 0x0, 0x3A} //2
//0xAB00000075289528, {0x28, 0x95, 0x28, 0x75, 0x0, 0x0, 0x0, 0xAB} //3
//0x2900000072CDFF28, {0x28, 0xFF, 0xCD, 0x72, 0x0, 0x0, 0x0, 0x29} //4
//0xF9000000739C4F28, {0x28, 0x4F, 0x9C, 0x73, 0x0, 0x0, 0x0, 0xF9} //5
//0xAC00000072FB2C28, {0x28, 0x2C, 0xFB, 0x72, 0x0, 0x0, 0x0, 0xAC} //6
//0x2D00000072D1FC28, {0x28, 0xFC, 0xD1, 0x72, 0x0, 0x0, 0x0, 0x2D} //7
//0x760000007697D528, {0x28, 0xD5, 0x97, 0x76, 0x0, 0x0, 0x0, 0x76} //8
//0x5F00000072E5EA28, {0x28, 0xEA, 0xE5, 0x72, 0x0, 0x0, 0x0, 0x5F} //9
//0x58000000768CEF28, {0x28, 0xEF, 0x8C, 0x76, 0x0, 0x0, 0x0, 0x58} //10
//0xC400000077899C28, {0x28, 0x9C, 0x89, 0x77, 0x0, 0x0, 0x0, 0xC4} //11


//массивы для графиков
uint8_t tIsparitel[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; //флаг о начале работы режима Defrost
//счётчики для срабатывания
uint8_t tKompressorCounter = 0;//для подсчёта количества корректных замеров перед отключением и включением вентилятора при высокой температуре на выходе из компрессора
uint8_t HeatOptionCout = 0; //счётчик для смены режима на приостанов нагрева (когда прогрелась вода перед ППТО)
uint8_t HeatOptionPauseCout = 0; //счётчик для смены режима окончания приостанова нагрева
uint8_t defrostCount = 0; //счётчик для перехода в режим Defrost
uint8_t StopMaxTemp = 0; //счётчик для перехода в режим Stop при повышенной температуре компрессора
uint8_t StopMaxElectr = 0; //счётчик для перехода в режим Stop при повышенной электрической нагрузке
uint8_t StopMinTempHeater = 0; //счётчик для перехода в режим Stop при низкой температуре в точке Defrost
uint8_t StopNo4xvalve = 0; //счётчик для перехода в режим Stop при неправильном положении 4х ходового клапана в режиме нагрев
uint8_t StopNoElectric = 0; //счётчик для перехода в режим Stop при отсутствии электропитания наружного блока

uint8_t StopKind = 0; //указатель причины, почему произошёл останов
// 1 - малая температура в точке Defrost в режиме нагрева
// 2 - высокая температура на выходе компрессора
// 3 - большая потребляемая электрическая мощность
// 4 - малая температура в точке Defrost в режиме нагрева
// 5 - отключение электроэнергии
// 6 - поступили некорректные температуры
// 7 - нет расхода воды
// 8 - у контроллера отсутствует актуальный режим (все флаги режимов опущены)
uint8_t MistakeTemp[6] = {}; //массив для фиксации количества ошибок по температуре подряд
boolean getMistakeFlag = false;//флаг для начала отслеживания ошибок температур


//засекание времени
uint32_t HeatOptionPauseStartTime;//для отсчёта времени и прокрутки насоса в режиме паузы прогрева
uint32_t StartHeatTime;//для фиксации времени начала режима работы "нагрев"
uint32_t StopTime;//для фиксации времени останова
uint32_t DefrostTime;//для фиксации времени начала режима работы Defrost


//переменные температур, изначально ставим 0*С
int16_t  Temper[6] =
{
  0,     //KompressorC   0
  0,     //WaterInC      1
  0,     //WaterOutC     2
  0,     //temp3C        3
  0,     //DefrostC      4
  0      //IsparitelC    5
};

//для выявления ошибок температур
int16_t  TemperPrev[6];//предыдущее показание температуры


//соответствие адресов датчиков определённым температурам
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}  //IsparitelC    5
};

//функция сброса шины с датчиками температуры
void resetTemp()
{
  ds.reset();
  ds.write(0xCC); //Обращение ко всем датчикам
  ds.write(0x44); //Команда на конвертацию
}

//функция считывания значения температуры с датчиков по шине
void gettingTemp()
{
  uint8_t i;
  int16_t temp;
  for (i = 0; i < 6; i++)  //Перебор количества датчиков
  {
    ds.reset();
    ds.select(ADDR_DS18B20[i]);
    ds.write(0xBE); //Считывание значения с датчика
    temp = (ds.read() | ds.read() << 8); //Принимаем два байта температуры
    Temper[i] = (temp * 10) >> 4; //целое в десятых *C (214=>21,4*C)
  }
}

//функция для выявления некорректных значений с датчиков температуры
void tempMistake (uint8_t a, int16_t b, int16_t c, uint8_t d) //номер датчика в массиве, минимальное значение, максимальное значение, максимальное изменение
{
  if (Temper[a] >= b and Temper[a] <= c and abs(Temper[a] - TemperPrev[a]) <= d)//если нормальное значение температуры
  {
    TemperPrev[a] = Temper[a];
    if (MistakeTemp[a] <= 6)
      MistakeTemp[a] = 0;
  }
  else if (MistakeTemp[a] > 6 and StopFlag == false)
  {
    StopFlag = true;
    StopTime = millis();
    StopKind = 6;//зафиксирована причина останова
  }
  else
  {
    Temper[a] = TemperPrev[a];
    if (StopFlag == false)
      MistakeTemp[a]++;
  }
}


//функция для вычисления расхода воды по водомеру с герконом
void voda_tick() {
  //Serial.println("proverka");
  boolean current_status = digitalRead(pinVoda_tick);
  if (stateCheck_voda and !current_status) {
    tmr = millis(); //зафиксировали время срабатывания прерывания
    gWaterFlag = true;
    stateCheck_voda = current_status;
  }
  if (!stateCheck_voda and current_status)stateCheck_voda = current_status;
  if (current_status)gWaterFlag = false;
  if (!current_status and millis() - tmr >= 1000 and gWaterFlag == true)
  {
    WaterCountTimer = millis() - WaterStartTimer;
    WaterStartTimer = millis();
    gWaterFlag = false;
    if (FirstImpulsWater == true)SecondImpulsWater = true;//если поднят флаг о уже полученном первом импульсе с водомера, то поднимается флаг о получении второго импульса
    FirstImpulsWater = true;//поднят флаг о том, что пришёл первый импульс с водомера, при начале вычислений расхода воды
  }
}


//функция для вычисления потребляемой электрической мощности, по импульсному выходу электросчётчика
void electr_tick() {
  //Serial.println("proverka");
  boolean current_status = digitalRead(pinElectr_tick);
  if (stateCheck_electr and !current_status) {
    tmr2 = millis(); //зафиксировали время срабатывания прерывания
    ElectrFlag = true;
    stateCheck_electr = current_status;
  }
  if (!stateCheck_electr and current_status)stateCheck_electr = current_status;
  if (current_status)ElectrFlag = false;
  if (!current_status and millis() - tmr2 >= 50 and ElectrFlag == true)
  {
    ElectrCountTimer = millis() - ElectrStartTimer;
    ElectrStartTimer = millis();
    ElectrFlag = false;
  }
}


//функция для вывода значений на дисплей
void getDataInt100(int16_t a)//вывод значений на дисплей у которых делится на 100
{
  //Serial.println("proverka");
  if (a >= -9 and a < 0)
  {
    tft.print("-0.0");
    tft.print(abs(a));
  }
  else if (a<9 and a >= 0)
  {
    tft.print("0.0");
    tft.print(a);
  }
  else if (a <= -10 and 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)//вывод значений на дисплей у которых делится на 10
{
  //Serial.println("proverka");
  if (a >= -9 and a < 0)
  {
    tft.print("-0.");
    tft.print(abs(a));
  }
  else if (a<9 and 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)//границы для последнего значения в массиве для графиков, типа uint8_t
{
  //Serial.println("proverka");
  int16_t LastInMassive;//ввожу временную переменную для вычисления последнего значения массива для графиков
  if (a < -400)
    LastInMassive = 0;
  else if (a >= -400 and a < 500)
    LastInMassive = round ((a + 400) / 5);
  else if (a >= 500 and a < 1250)
    LastInMassive = round (180 + (a - 500) / 10);
  else if (a >= 1250)
    LastInMassive = 255;
  return LastInMassive;
}


//функция для создания массива с последующим выводом на дисплей графиков
int32_t getDataForUintMassiv32(int32_t a)//границы для последнего значения в массиве для графиков, типа uint8_t
{
  //Serial.println("proverka");
  int32_t LastInMassive;//ввожу временную переменную для вычисления последнего значения массива для графиков
  if (a < -400)
    LastInMassive = 0;
  else if (a >= -400 and a < 0)
    LastInMassive = round ((a + 400) / 10);
  else if (a >= 0 and a <= 160)
    LastInMassive = round (40 + a);
  else if (a > 160 and a < 710)
    LastInMassive = round (200 + (a - 160) / 10);
  else if (a >= 710)
    LastInMassive = 255;
  return LastInMassive;
}


//функция для вычерчивания графиков
void getGraphics(uint16_t c, uint16_t d, uint8_t g[320]) //процедура построения графиков
{
  //Serial.println("proverka");
  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()//вычерчивание основного дисплея
{
  //Serial.println("proverka");
  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 or i == 5 or i == 6 or i == 7 or i == 9 or i == 10)
      tft.drawFastHLine(0, (5 + 20 * i), 320, DARKGREEN);
    else if (i == 1 or i == 2 or i == 3 or 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.setCursor(0, 22); // Устанавливаем курсор (X = , Y = )
  tft.setTextSize(1); // Указываем размер символов в строке от 1 до 3
  tft.setTextColor(WHITE, BLACK); // Указываем цвет текста
  tft.print("90");

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

  //надписи назначения измерений датчиков
  tft.setCursor(0, 0); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(RED); // Указываем цвет текста
  tft.print("Kompressor:");

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

  tft.setCursor(125, 10); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(CYAN); // Указываем цвет текста
  tft.print("Defrost:");

  tft.setCursor(220, 10); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(RED); // Указываем цвет текста
  tft.print("Isparitel:");

  tft.setCursor(120, 0); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(YELLOW); // Указываем цвет текста
  tft.print("Water in:");

  tft.setCursor(220, 0); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(GREEN); // Указываем цвет текста
  tft.print("Water out:");

  tft.setCursor(5, 210); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(RED); // Указываем цвет текста
  tft.print("Heat power:");

  tft.setCursor(152, 210); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(YELLOW); // Указываем цвет текста
  tft.print("COP:");

  tft.setCursor(218, 210); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(GREEN); // Указываем цвет текста
  tft.print("Electr pow:");

  tft.setCursor(22, 220); // Устанавливаем курсор (X = , Y = )
  tft.setTextColor(MAGENTA); // Указываем цвет текста
  tft.print("G water:");
}



void setup()
{ // put your setup code here, to run once:
  //Serial.println("proverka");
  Serial.begin(9600);

  tft.reset();
  tft.begin(38022); //указание модели экрана
  tft.invertDisplay(true); //инверсия цветов - т.к. по умолчанию инвертирует цвета
  //tft.setRotation(1); //поворот экрана на 90 градусов

  //для водомера
  pinMode(18, INPUT_PULLUP);// подключили импульсный выход на D18
  stateCheck_voda = digitalRead(pinVoda_tick);

  //для электросчётчика
  pinMode(19, INPUT_PULLUP);// подключили импульсный выход на D19
  stateCheck_electr = digitalRead(pinElectr_tick);

  //присвоение массивам значений, чтобы на графике сначала были нули
  for (int i = 0; i < 320; i++) {
    tIsparitel[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; //опущен флаг о начале работы режима Defrost
  StartHeatFlag = true;//поднят флаг о том, что начался переход в режим "нагрев"
  StartHeatTime = millis();//фиксация времени начала перехода в режим "нагрев"
  Timer = millis(); //для запуска цикла

  delay(1000);  //задержка 1 сек
}


void loop()// put your main code here, to run repeatedly:
{
  voda_tick();//переход на функцию по подсчёту расхода воды
  electr_tick();//переход на функцию по подсчёту электрической мощности


  //первый шаг выполнения по таймеру
  if (millis() - Timer >= 3000 and millis() - Timer < 5000 and Flag1 == true)//если от 3 до 5 сек и флаг1 поднят
  {
    resetTemp();//направление на процедуру по сбросу шины с датчиками
    Flag1 = false;
  }

  //второй шаг выполнения по таймеру
  if (millis() - Timer >= 5000 and millis() - Timer < 7000 and Flag2 == true)//если от 5 до 7 сек и флаг2 поднят
  {
    gettingTemp();//направление на процедуру по получению значений температуры с шины с датчиками
    Flag2 = false;
  }

  //третий шаг выполнения по таймеру
  if (millis() - Timer >= 7000 and millis() - Timer < 9000 and Flag3 == true)//если от 7 до 9 сек и флаг3 поднят
  {
    if (getMistakeFlag == false)//при первом получении значений температур
    {
      for (uint8_t i = 0; i < 6; i++)
      {
        TemperPrev[i] = Temper[i];
      }
      getMistakeFlag = true;
    }

    //отсылка на процедуру проверки корректности полученных температур
    tempMistake (0, -100, 1200, 200); // - датчик на выходе из компрессора - (номер датчика в массиве, минимальное значение, максимальное значение, максимальное изменение)
    tempMistake (1, 0, 600, 100); // - датчик на входе воды в ППТО - (номер датчика в массиве, минимальное значение, максимальное значение, максимальное изменение)
    tempMistake (2, 0, 900, 50); // - датчик на выходе воды из ППТО - (номер датчика в массиве, минимальное значение, максимальное значение, максимальное изменение)
    tempMistake (3, -400, 800, 400); // - датчик на входе в компрессор - (номер датчика в массиве, минимальное значение, максимальное значение, максимальное изменение)
    tempMistake (4, -400, 600, 300); // - датчик в точке Defrost в нижней части ППТО - (номер датчика в массиве, минимальное значение, максимальное значение, максимальное изменение)
    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 and millis() - WaterStartTimer > 400000)//если за 400 секунд работы насоса не пришёл сигнал с геркона водомера
    {
      StopFlag = true;
      StopTime = millis();
      StopKind = 7;//зафиксирована причина останова
    }
    else if (SecondImpulsWater == false)//если ещё не произошло второе срабатывание геркона водомера
    {
      gWaterReal = gWaterRealPrew;
      FlagWater = true;//поднят флаг для подсветки значения неизвестного расхода
    }
    else
    {
      gWaterReal = round (3600000 / WaterCountTimer); //итоговое значение в м3/ч умноженные на 100
      gWaterRealPrew = gWaterReal;
      FlagWater = false;//сброшен флаг подсветки значения
    }


    //расчёт электрической мощности
    if (millis() - ElectrStartTimer > 60000) //если нет данных с счётчика в течение 1 мин.
      ElectrPowReal = 0;
    else
      ElectrPowReal = 72000 / ElectrCountTimer; //значение в кВт умноженные на 100


    //расчёт полезной тепловой мощности
    heatPowerReal = ((int32_t)Temper[2] - (int32_t)Temper[1]) * gWaterReal * 1163 / 10000; //тепловая мощность на теплообменнике в кВт умноженная на 100


    //расчёт СОР
    if (ElectrPowReal < 10)
      COPreal = 0;
    else
      COPreal = 100 * heatPowerReal / ElectrPowReal; //вычисление COP, умноженное на 100


    //подсчёт среднеарифметической тепловой мощности и электроэнергии
    countHeat = countHeat + heatPowerReal;
    countElectr = countElectr + ElectrPowReal;
    countHeatAndElectr++;


    //останов вентилятора при перегреве компрессора
    if (HeatOptionFlag == true and Temper[0] >= 850 and tOverheatReal >= 300 and digitalRead(48) == LOW)//если высокая температура на выходе из компрессора и перегрев с запасом и вентилятор включён
    {
      tKompressorCounter++;
      if (tKompressorCounter >= 4)//если сработал счётчик
      {
        bitClear(relay, 0);//остановить вентилятор
        tKompressorCounter = 0;
      }
    }
    else if (HeatOptionFlag == true and (Temper[0] <= 750 or tOverheatReal <= 200) and digitalRead(48) == HIGH)//если остыл компрессор или перегрев мал и вентилятор выключен
    {
      tKompressorCounter++;
      if (tKompressorCounter >= 2)//если сработал счётчик
      {
        bitSet(relay, 0);//включить вентилятор
        tKompressorCounter = 0;
      }
    }
    else
      tKompressorCounter = 0;


    //смена режимов
    if (HeatOptionFlag == true and Temper[1] >= 360)//если температура воды перед теплообменником достигла 36 град.
    {
      HeatOptionCout++;
      if (HeatOptionCout >= 6)//если устоялся режим
      {
        HeatOptionFlag = false;
        HeatOptionPauseFlag = true;
        HeatOptionCout = 0;
        HeatOptionPauseStartTime = millis();
        bitClear(relay, 2);//остановить компрессор
        bitClear(relay, 0);//остановить вентилятор
      }
    }
    else
      HeatOptionCout = 0;

    if (HeatOptionPauseFlag == true and digitalRead(49) == LOW and millis() - HeatOptionPauseStartTime >= 120000) //если началась пауза обогрева и 4х ход. клапан включён и вышло время
      bitClear(relay, 1);//перевести четырёхходовой клапан в штатное положение (режим "охлаждение")

    if (HeatOptionPauseFlag == true and digitalRead(51) == HIGH and millis() - HeatOptionPauseStartTime >= 960000) //если прошло время 15 мин. после остановки насоса
    {
      if (digitalRead(51) == HIGH)//если насос выключен
      {
        WaterStartTimer = millis();//фиксирование времени начала отсчёта расхода
        FirstImpulsWater = false;//ожидание первого импульса от водомера
        SecondImpulsWater = false;//ожидание второго импульса от водомера
      }
      bitSet(relay, 3);//включить насос
      HeatOptionPauseStartTime = millis();
    }

    if (HeatOptionPauseFlag == true and digitalRead(51) == LOW and millis() - HeatOptionPauseStartTime >= 60000) //если прошло время 1 мин. после включения насоса
    {
      bitClear(relay, 3);//выключить насос
      HeatOptionPauseStartTime = millis();
    }


    if (HeatOptionPauseFlag == true and Temper[1] <= 280)//если в режиме паузы обогрева температура воды перед теплообменником низкая
    {
      HeatOptionPauseCout++;
      if (HeatOptionPauseCout >= 5)//если устоялся режим
      {
        HeatOptionPauseFlag = false;
        HeatOptionPauseCout = 0;
        StartHeatTime = millis();//фиксация времени начала перехода в режим "нагрев"
        StartHeatFlag = true;//поднят флаг о том, что начался переход в режим "нагрев"
      }
    }
    else
      HeatOptionPauseCout = 0;


    //режим оттайки
    if (HeatOptionFlag == true and Temper[5] < -100)
    {
      defrostCount++;
      if (defrostCount >= 6)
      {
        HeatOptionFlag = false;
        DefrostOptionFlag = true;
        DefrostTime = millis();
        defrostCount = 0;
      }
    }
    else
      defrostCount = 0;


    //защита от неправильного положения 4х ходового клапана
    if (HeatOptionFlag == true and digitalRead(49) == HIGH)//если 4х ходовый клапан в неправильном положении
    {
      StopNo4xvalve++;
      if (StopNo4xvalve >= 3)
      {
        bitClear(relay, 2);//остановить компрессор
        StopFlag = true;//переход в режим останов
        StopTime = millis();
        StopKind = 1; //зафиксирована причина остановки
        StopNo4xvalve = 0;
      }
    }
    else
      StopNo4xvalve = 0;


    //защита от превышения температуры на выходе из компрессора
    if ((StartHeatFlag == true or HeatOptionFlag == true or HeatOptionPauseFlag == true or DefrostOptionFlag == true) and Temper[0] >= 1000)
    {
      StopMaxTemp++;
      if (StopMaxTemp >= 5)
      {
        StopFlag = true;
        StopTime = millis();
        StopKind = 2;//зафиксирована причина останова
        StopMaxTemp = 0;
      }
    }
    else
      StopMaxTemp = 0;

    //защита от превышения электрической мощности
    if ((StartHeatFlag == true or HeatOptionFlag == true or HeatOptionPauseFlag == true or DefrostOptionFlag == true) and ElectrPowReal >= 151)
    {
      StopMaxElectr++;
      if (StopMaxElectr >= 6)
      {
        StopFlag = true;
        StopTime = millis();
        StopKind = 3;//зафиксирована причина останова
        StopMaxElectr = 0;
      }
    }
    else
      StopMaxElectr = 0;


    //защита от замерзания теплообменника (кроме режима Defrost)
    if ((StartHeatFlag == true or HeatOptionFlag == true or HeatOptionPauseFlag == true) and Temper[4] <= 50)
    {
      StopMinTempHeater++;
      if (StopMinTempHeater >= 3)
      {
        StopFlag = true;
        StopTime = millis();
        StopKind = 4;//зафиксирована причина останова
        StopMinTempHeater = 0;
      }
    }
    else
      StopMinTempHeater = 0;

    //при отключении электроэнергии
    if (digitalRead(50) == LOW and ElectrPowReal <= 10)//если включён компрессор и электрическая мощность очень мала
    {
      StopNoElectric++;
      if (StopNoElectric >= 6)
      {
        StopFlag = true;
        StopTime = millis();
        StopKind = 5;//зафиксирована причина останова
        StopNoElectric = 0;
      }
    }
    else
      StopNoElectric = 0;


    //режим старта обогрева
    if (StartHeatFlag == true and  millis() - StartHeatTime >= 20000 and Temper[1] >= 50)//если начат переход в режим "нагрев" и при этом прошло время после старта перехода и температура воды перед ППТО более 5 град.
    {
      if (digitalRead(51) == HIGH)//если насос выключен
      {
        WaterStartTimer = millis();//фиксирование времени начала отсчёта расхода
        FirstImpulsWater = false;//ожидание первого импульса от водомера
        SecondImpulsWater = false;//ожидание второго импульса от водомера
      }
      bitSet(relay, 3);//включить насос
    }

    if (StartHeatFlag == true and digitalRead(51) == LOW and millis() - StartHeatTime >= 30000 and Temper[0] <= 850)//если начат переход в режим "нагрев" и при этом прошло время после старта перехода и температура компрес. менее 85 град.
      bitSet(relay, 0);//включить вентилятор

    if (StartHeatFlag == true and digitalRead(51) == LOW and digitalRead(48) == LOW and  millis() - StartHeatTime >= 40000)//если начат переход в режим "нагрев" и при этом прошло время после старта перехода
      bitSet(relay, 1);//переключить четырёхходовой клапан в режим "нагрев"

    if (StartHeatFlag == true and digitalRead(51) == LOW and digitalRead(48) == LOW and digitalRead(49) == LOW and  millis() - StartHeatTime >= 45000)//если начат переход в режим "нагрев" и при этом прошло время после старта перехода и включён насос и четырёхходовой клапан в режим "нагрев"
      bitSet(relay, 2);//включить компрессор

    //переход в режим обогрева после старта
    if (StartHeatFlag == true and digitalRead(51) == LOW and digitalRead(48) == LOW and digitalRead(50) == LOW and digitalRead(49) == LOW)//если начат переход в режим "нагрев" и все требуемые для этого устройства включены
    {
      StartHeatFlag = false;//окончен переход в режим "нагрев"
      HeatOptionFlag = true;//действует режим "нагрев"
    }


    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);//выключить насос
    }

    if (DefrostOptionFlag == true)
    {
      if (digitalRead(51) == HIGH)//если насос выключен
      {
        WaterStartTimer = millis();//фиксирование времени начала отсчёта расхода
        FirstImpulsWater = false;//ожидание первого импульса от водомера
        SecondImpulsWater = false;//ожидание второго импульса от водомера
      }
      bitSet(relay, 3);//включить насос
      bitClear(relay, 2);//остановить компрессор
      bitSet(relay, 0);//включить вентилятор если температура воздуха на улице больше +3 град. С - то вентилятор не отключать
      if (millis() - DefrostTime >= 120000)
        bitClear(relay, 1);//перевести четырёхходовой клапан в штатное положение (режим "охлаждение")
      if (millis() - DefrostTime >= 900000 and 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); // Устанавливаем курсор (X = , Y = )
    if (MistakeTemp[0] > 0)
      tft.setTextColor(CYAN, MAGENTA); // Указываем цвет текста
    else
      tft.setTextColor(RED); // Указываем цвет текста
    getDataInt10(Temper[0]);

    tft.setCursor(181, 0); // Устанавливаем курсор (X = , Y = )
    if (MistakeTemp[1] > 0)
      tft.setTextColor(CYAN, MAGENTA); // Указываем цвет текста
    else
      tft.setTextColor(YELLOW); // Указываем цвет текста
    getDataInt10(Temper[1]);

    tft.setCursor(289, 0); // Устанавливаем курсор (X = , Y = )
    if (MistakeTemp[2] > 0)
      tft.setTextColor(CYAN, MAGENTA); // Указываем цвет текста
    else
      tft.setTextColor(GREEN); // Указываем цвет текста
    getDataInt10(Temper[2]);

    tft.setCursor(74, 10); // Устанавливаем курсор (X = , Y = )
    if (MistakeTemp[3] > 0)
      tft.setTextColor(CYAN, MAGENTA); // Указываем цвет текста
    else
      tft.setTextColor(MAGENTA); // Указываем цвет текста
    getDataInt10(tOverheatReal);

    tft.setCursor(181, 10); // Устанавливаем курсор (X = , Y = )
    if (MistakeTemp[4] > 0)
      tft.setTextColor(CYAN, MAGENTA); // Указываем цвет текста
    else
      tft.setTextColor(CYAN); // Указываем цвет текста
    getDataInt10(Temper[4]);

    tft.setCursor(289, 10); // Устанавливаем курсор (X = , Y = )
    if (MistakeTemp[5] > 0)
      tft.setTextColor(CYAN, MAGENTA); // Указываем цвет текста
    else
      tft.setTextColor(RED); // Указываем цвет текста
    getDataInt10(Temper[5]);

    tft.setCursor(74, 210); // Устанавливаем курсор (X = , Y = )
    tft.setTextColor(RED); // Указываем цвет текста
    getDataInt100(heatPowerReal);

    tft.setCursor(181, 210); // Устанавливаем курсор (X = , Y = )
    tft.setTextColor(YELLOW); // Указываем цвет текста
    getDataInt100(COPreal);

    tft.setCursor(289, 210); // Устанавливаем курсор (X = , Y = )
    tft.setTextColor(GREEN); // Указываем цвет текста
    getDataInt100(ElectrPowReal);

    tft.setCursor(74, 220); // Устанавливаем курсор (X = , Y = )
    if (FlagWater == true)
      tft.setTextColor(CYAN, MAGENTA); // Указываем цвет текста
    else
      tft.setTextColor(MAGENTA); // Указываем цвет текста
    getDataInt100(gWaterReal);

    tft.setCursor(100, 470); // Устанавливаем курсор (X = , Y = )
    tft.setTextColor(RED); // Указываем цвет текста
    getDataInt100(countHeat / countHeatAndElectr);

    tft.setCursor(200, 470); // Устанавливаем курсор (X = , Y = )
    tft.setTextColor(GREEN); // Указываем цвет текста
    getDataInt100(countElectr / countHeatAndElectr);

    /*
        tft.setCursor(200, 460); // Устанавливаем курсор (X = , Y = )
        tft.setTextColor(GREEN,BLACK); // Указываем цвет текста
        tft.print(Temper[3]);
    */

    tft.setCursor(152, 220); // Устанавливаем курсор (X = , Y = )
    tft.setTextColor(RED); // Указываем цвет текста
    if (StopFlag == true)//если режим "останов" активен
    {
      tft.print("Stop");
      if (StopKind == 1)
        tft.print(" No 4x valve");
      else if (StopKind == 2)
        tft.print(" maxT Kompr");
      else if (StopKind == 3)
        tft.print(" maxW Electr");
      else if (StopKind == 4)
        tft.print(" minT Heater");
      else if (StopKind == 5)
        tft.print(" No Electr");
      else if (StopKind == 6)
        tft.print(" Err Temp");
      else if (StopKind == 7)
        tft.print(" No G_water");
      else if (StopKind == 8)
        tft.print(" No Option");
    }
    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); // Устанавливаем курсор (X = , Y = )
    if (bitRead(relay, 2) == 1)
      tft.setTextColor(RED); // Указываем цвет текста
    else
      tft.setTextColor(BLUE); // Указываем цвет текста
    tft.print("Kompressor");

    tft.setCursor(0, 450); // Устанавливаем курсор (X = , Y = )
    if (bitRead(relay, 3) == 1)
      tft.setTextColor(RED); // Указываем цвет текста
    else
      tft.setTextColor(BLUE); // Указываем цвет текста
    tft.print("Pump");

    tft.setCursor(0, 460); // Устанавливаем курсор (X = , Y = )
    if (bitRead(relay, 1) == 1)
      tft.setTextColor(RED); // Указываем цвет текста
    else
      tft.setTextColor(BLUE); // Указываем цвет текста
    tft.print("4 way valve");

    tft.setCursor(0, 470); // Устанавливаем курсор (X = , Y = )
    if (bitRead(relay, 0) == 1)
      tft.setTextColor(RED); // Указываем цвет текста
    else
      tft.setTextColor(BLUE); // Указываем цвет текста
    tft.print("Fan");

    Flag3 = false;
  }


  //четвёртый шаг выполнения по таймеру
  if (millis() - Timer >= 9000 and millis() - Timer < 10000 and Flag4 == true)//если от 9 до 10 сек и флаг4 поднят
  {
    if (bitRead(relay, 0) == 1) //если нулевой бит равен единице
      digitalWrite(48, LOW);//включить вентилятор
    else if (bitRead(relay, 0) == 0) //если нулевой бит равен нулю
      digitalWrite(48, HIGH);//выключить вентилятор

    if (bitRead(relay, 1) == 1) //если первый бит равен единице
      digitalWrite(49, LOW);//переключить четырёхходовой клапан в режим "нагрев"
    else if (bitRead(relay, 1) == 0) //если первый бит равен нулю
      digitalWrite(49, HIGH);//переключить четырёхходовой клапан в режим "оттайка"

    if (bitRead(relay, 2) == 1) //если второй бит равен единице
      digitalWrite(50, LOW);//включить компрессор
    else if (bitRead(relay, 2) == 0) //если второй бит равен нулю
      digitalWrite(50, HIGH);//выключить компрессор

    if (bitRead(relay, 3) == 1) //если третий бит равен единице
      digitalWrite(51, LOW);//включить насос
    else if (bitRead(relay, 3) == 0) //если третий бит равен нулю
      digitalWrite(51, HIGH);//выключить насос

    Flag4 = false;
  }


  //пятый шаг выполнения по таймеру
  if (millis() - Timer >= 10000)//если больше 10 сек
  {
    Timer = millis(); //запускаем отсчёт нового цикла

    //(график периодически)
    if (countGetGrafic >= 9)//между циклами 90 сек
    {
      //перенос значений в массиве
      for (uint16_t x = 0; x < 319; x++)
      {
        tIsparitel[x] = tIsparitel[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]);
      tIsparitel[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; 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, tIsparitel);
      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;
  }
}

Собственно, почему решил написать…

Устал самостоятельно искать причину сбоев. Менял провода, ставил экранированные. Внедрил два блока питания, внедрил опторазвязку от датчиков… Несколько раз менял код получения температуры - полностью устранить проблему не могу. Обращаюсь сюда с надеждой выяснить - как мне устранить проблему? В голове уже крайние мысли - смена датчиков (на профильном форуме по тепловым насосам - упоминались NTC). Тогда - на какие именно и как их правильно подключать.

PPeterr контроль ошибок через crc пробовать будите ?

Для проверки, напишите функцию- заглушку, в которой вместо опроса датчиков просто переменной присваивается определённое значение температуры, и, физически отключите датчики.
P.S.

А как у вас реализована задержка для работы АЦП датчиков?

P.P.S.
Возможно, что код слишком “громоздкий” для Уно/Нано и просто не хватает памяти. Как вариант - проверить на Меге.

P.P.P.S

Вопрос снят, увидел.

Пробовал.

Ранее какое-то время работал код с содержанием проверки crc, аналогично коду из этого сообщения: Почистил скетч примера работы с DS18B20 из библы OneWire.. | Аппаратная платформа Arduino

при этом проблема так же присутствовала, вот в таком виде бывала:

позже я решил, раз проверка crc мне не помогла исключить проблему - то лучше убрать её (проверку) из кода.

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

Выполнено - на Меге.

DS18B20 нужно закупать у проверенных поставщиков, но и это не уберегает от подделок и глюков.

У меня был опыт сборки устройств измерения влажности, основанных на двух таких датчиках в герметичной гильзе (один влажный, второй сухой). Собрал я в общей сложности штук 30 их за пару лет, датчики мне закупал заказчик. Так вот, бывало некоторые устройства начинали глючить через какое-то время, периодически показывали не ту температуру. Решалась эта проблема только заменой датчика на новый. Иногда глючные датчики попадались реже, иногда чаще.

Если нет требований по точности, мне кажется можно взять ntc-резистор, как уже тут советовали, откалибровать его и пользоваться. Точность можно поднять увеличением количества измерений.

Конечно. Напишите функцию - замену для датчиков. Из неё передавайте в основной код значения температуры, к примеру, увеличивая каждый раз на 1 .
Если косяк останется - виноваты не датчики.

Ну, тогда надо брать какой-нибудь blue-pill, или ESP32. У вас код написан в среде Ардуино, возможно, получится перенести.
Но это на случай, если не выявите явных косяков в коде. Я лишь бегло глянул, сейчас нет времени на это, может ещё кто что-нибудь подскажет.

Ещё вариант проверки - запустите код без дисплея(есс-но закоментить всё лишнее) оставить только сериал, и, проверьте работу датчиков

Вроде как экранированную витую не рекомендуют. Обычную. Почитайте рекомендации Dallas uLan. Хотя, если расстояния не большие…
А потом, что мешает контролировать аномально резкие изменения температуры и реагировать на них?

Вот тоже, не совсем понятно - что за ошибка? К какому останову? МК зависает, перезагружается? Или штатная авария, отрабатывает согласно коду?

Покупал на “Озоне”, недорого (что-то вроде 400 руб. за 10 шт.)

Но перед использованием я подвергал их испытаниям и подбирал наилучшие (близкие) из десятка. Рабочие были все.

Вот такая испытательная конструкция

помещалась вот так в банку

эта банка помещалась в морозилку (-1300 - это -13 град. С, -1700 это -17 град. С)

затем в кастрюлю с кипящей водой (7000 - это +70 град. С)

Понял. Спасибо.

Но в этом случае - у меня не будут срабатывать реле и сильноточные цепи. Есть большая вероятность, что сбои происходят - в том числе из-за них. В спокойном состоянии (без срабатываний реле и контактора) - ошибок не прилетает (не было зафиксировано).

Аномально резкие изменения фиксирую и реагирую. По этому условию - и происходит останов.

Напишите так,чтобы срабатывали