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

Интересно, а вам доплачивают за сэкономленные строки? Пихать по пять операторов на строку - это неуважение к тем, кто будет это читать.
Да и обилие магических чисел в коде его не красит… Какой-то индийский недокод получается

А вы свой код покажите и общество сравнит.

это потом, а кушать хочется уже сейчас, готов продаться в рабство на месяц за 60к, для написания кода, результат не гарантирую, могу и к вам пойти в проект)))

Анализирую код от BABOS из сообщения 612 для Mega. Вижу такое:

   // === 2. Приём данных от nano ===
  if (Serial1.available()) {
    DataFromNano temp;
    if (Serial1.readBytes((uint8_t*)&temp, sizeof(temp))) {
      uint8_t crc = crc8(&temp, sizeof(temp));
      if (crc == 0) {
        rxData = temp;
        updateArrays();
        
        // === 3. Отображение ===
        if (rxData.mistakeOutput > 0) {
          String msg;
          switch(rxData.mistakeOutput) {
            case 1: msg = "CRC error from Mega"; break;
            case 2: msg = "Stop: CRC errors"; break;
            case 3: msg = "Stop: Cycle error"; break;
            case 4: msg = "Stop: Overheat!"; break;
            case 5: msg = "Stop: Low pressure!"; break;
            case 6: msg = "Connection LOST!"; break;
            default: msg = "Unknown error";
          }
          showMistake(msg);
        } else {
          if (kindDisplay == 1) {
            getAllLastDataToDisplay();
          } else {
            getMainDisplay();
            getGraphics(245, RED, t1_Graph);
            getGraphics(245, YELLOW, t2_Graph);
            getGraphics(245, GREEN, t3_Graph);
            getGraphics(245, MAGENTA, t4_Graph);
            getGraphics(245, CYAN, t5_Graph);
            getGraphics(245, RED, t6_Graph);
            getGraphics(245, YELLOW, t7_Graph);
            getGraphics(245, GREEN, t8_Graph);
            getGraphics(245, MAGENTA, t9_Graph);
            getGraphics(245, CYAN, t10_Graph);
            getGraphics(475, RED, Water_Graph);
            getGraphics(475, YELLOW, Electr_Graph);
          }
        }
      }
    }
  }

либо я чего-то не понимаю…
Строка if (crc == 0) { переводится как “если нет ошибки CRC”. Я вижу - что должно сделаться, если ошибки CRC нет.
Но я не вижу - что должно сделаться, если всё-таки ошибка CRC будет. Я так понимаю - в этом случае контроллер ошибку никак не зафиксирует?
Надо бы зафиксировать… наверное.

Ну это будет с налогами 120к.
А ты на столько отработаешь?
Или ты уверен в том, что работодатель деньги из воздуха синтезирует?
Сидеть на ОПЕ ровно за чужие деньги - маловероятно, что получится…

Ну вы же не знаете, может бабос уже давно открыл самозанятость, так что для работодателя по налогам будет 0

Сомневаюсь Я, что бабос достиг такого уровня познания “дзена”
Инфантильность…

а вдруг ))))

Давай у него спросим, дабы интриги ну… Не здесЯ)))

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

// В глобальные переменные добавьте:
uint16_t crcErrorCount = 0;
uint32_t lastCrcErrorTime = 0;

// В loop():
if (crc == 0) {
    // ... обработка ...
    crcErrorCount = 0; // Сброс при успешном приёме
} else {
    crcErrorCount++;
    lastCrcErrorTime = millis();
    Serial.print("⚠️ CRC Error #");
    Serial.println(crcErrorCount);
    
    if (crcErrorCount >= 5) {
        // Если 5 ошибок подряд - показываем предупреждение
        showMistake("CRC errors: Check wiring!");
        crcErrorCount = 0;
    }
}


при чем в коде нано, есть тоже функция crc но толку от нее если например нано повиснет… я сам не полностью еще понимаю работу кода)))

Шаг 1: MEGA формирует пакет
MEGA берёт 3 параметра управления:

Целевая температура (например, 22.0°C)

Режим работы (OFF / AUTO / MANUAL)

Ручная команда (если режим MANUAL)

Упаковывает их в пакет из 5 байт:

Первые 2 байта: целевая температура

3-й байт: режим

4-й байт: ручная команда

5-й байт: ПУСТОЕ МЕСТО ДЛЯ CRC

Шаг 2: MEGA вычисляет CRC
Берёт первые 4 байта (все данные, КРОМЕ последнего пустого байта)

Пропускает их через математическую функцию CRC-8

Получает контрольную сумму (число от 0 до 255)

Записывает это число в 5-й (последний) байт

Шаг 3: MEGA отправляет полный пакет
Отправляет все 5 байт по проводу в NANO:

[температура] [режим] [команда] [CRC]

Делает это каждые 30 секунд

ЧАСТЬ 2: ПРИЁМ ПАРАМЕТРОВ В NANO
Шаг 4: NANO получает 5 байт
Читает из порта все 5 байт во временную переменную

Теперь у него есть: [температура] [режим] [команда] [CRC]

Шаг 5: NANO проверяет целостность
Берёт ВСЕ 5 байт (включая CRC)

Снова пропускает через ТУ ЖЕ САМУЮ функцию CRC-8

Смотрит на результат:

Если результат = 0 → данные не повреждены, можно использовать

Если результат ≠ 0 → данные повреждены, использовать НЕЛЬЗЯ

Почему это работает:

При отправке мы вычислили CRC от 4 байт и дописали его 5-м

Если мы теперь вычислим CRC от ВСЕХ 5 байт, математически должно получиться 0

Если хоть один бит изменился, получится НЕ 0

Шаг 6: NANO принимает решение
Если CRC верный (результат = 0):

✅ Применяет новые параметры:

Меняет целевую температуру

Меняет режим работы

Запоминает команду

Сбрасывает счётчик потери связи

Сбрасывает флаг "потеря связи"

Обнуляет код ошибки

Если CRC неверный (результат ≠ 0):

❌ ИГНОРИРУЕТ полученные параметры (не применяет их!)

Устанавливает код ошибки = 1 ("CRC error from Mega")

ОСТАВЛЯЕТ старые параметры (которые работали до этого)

НЕ обновляет время последнего приёма

Шаг 7: Важный момент с потерей связи
NANO постоянно следит: сколько прошло времени с последнего успешного приёма

Если прошло более 5 секунд → увеличивает счётчик пропусков

Если пропусков стало 3 подряд (15 секунд):

Устанавливает флаг "потеря связи"

Устанавливает код ошибки = 6 ("Connection LOST!")

ПЕРЕХОДИТ НА ПОСЛЕДНИЕ ВАЛИДНЫЕ ПАРАМЕТРЫ (которые были до потери)

ЧАСТЬ 3: ОТПРАВКА ДАННЫХ ОТ NANO К MEGA
Шаг 8: NANO собирает все данные
Берёт 10 температур от датчиков DS18B20

Добавляет давление

Добавляет время (часы, минуты, день, месяц)

Добавляет расход воды

Добавляет потребляемую мощность

Добавляет состояние всех реле (включены/выключены)

Добавляет код ошибки (если есть)

Упаковывает всё это в пакет из 31 байта:

20 байт: 10 температур (по 2 байта каждая)

1 байт: давление

4 байта: время (часы, минуты, день, месяц)

1 байт: расход воды

1 байт: мощность

2 байта: состояние реле

1 байт: код ошибки

1 байт: ПУСТОЕ МЕСТО ДЛЯ CRC

Шаг 9: NANO вычисляет CRC
Берёт первые 30 байт (ВСЕ данные, КРОМЕ последнего пустого байта)

Пропускает через функцию CRC-8

Получает контрольную сумму

Записывает её в последний (31-й) байт

Шаг 10: NANO отправляет полный пакет
Отправляет все 31 байт по проводу в MEGA

Делает это каждые 10 секунд

Отправляет ВСЕГДА, даже если есть ошибка (код ошибки будет в пакете)

ЧАСТЬ 4: ПРИЁМ ДАННЫХ В MEGA
Шаг 11: MEGA получает 31 байт
Читает из порта все 31 байт во временную переменную

Теперь у него есть: [10 температур] [давление] ... [код ошибки] [CRC]

Шаг 12: MEGA проверяет целостность
Берёт ВСЕ 31 байт (включая CRC)

Пропускает через ТУ ЖЕ САМУЮ функцию CRC-8

Смотрит на результат:

Если результат = 0 → данные целые

Если результат ≠ 0 → данные повреждены

Шаг 13: MEGA принимает решение (ВОТ ЗДЕСЬ ПРОБЛЕМА!)
Если CRC верный (результат = 0):

✅ Сохраняет данные в рабочую переменную

✅ Обновляет массивы для графиков

✅ Проверяет код ошибки от NANO:

Если код ошибки > 0 → показывает сообщение об ошибке на дисплее

Если код ошибки = 0 → обновляет дисплей с новыми данными

Если CRC неверный (результат ≠ 0):

❌ НИЧЕГО НЕ ДЕЛАЕТ!

❌ Не сохраняет данные

❌ Не обновляет дисплей

❌ Не показывает ошибку

❌ Не увеличивает счётчик ошибок

❌ Не записывает в лог

ПРОСТО ИГНОРИРУЕТ ВЕСЬ ПАКЕТ!

ЧАСТЬ 5: ЧТО ВИДИТ ПОЛЬЗОВАТЕЛЬ
При нормальной работе:
Дисплей обновляется каждые 10 секунд

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

Если нажать кнопку, температура меняется

При ошибке MEGA → NANO (CRC повреждён):
NANO: Не меняет параметры, устанавливает код ошибки = 1

NANO: Отправляет данные с кодом ошибки = 1

MEGA: Получает данные (CRC от NANO верный!), видит код ошибки = 1

MEGA: Показывает на дисплее "CRC error from Mega"

Пользователь видит: красный экран с ошибкой

При ошибке NANO → MEGA (CRC повреждён):
NANO: Отправляет данные (CRC вычислен, но по пути испортился)

MEGA: Получает искажённые 31 байт

MEGA: Вычисляет CRC от искажённых данных → результат НЕ 0

MEGA: ИГНОРИРУЕТ пакет

Дисплей: остаётся с ПРЕДЫДУЩИМИ данными (10 секунд назад)

Пользователь видит: старые показания, но НЕТ НИКАКОЙ ОШИБКИ!

Пользователь думает: всё работает нормально, а на самом деле данные не обновляются

При потере связи:
MEGA: Не отправляет параметры (обрыв провода)

NANO: Через 15 секунд понимает, что связи нет

NANO: Устанавливает код ошибки = 6

NANO: Переходит на последние валидные параметры

NANO: Продолжает отправлять данные с кодом = 6

MEGA: Получает данные (CRC от NANO верный!), видит код = 6

MEGA: Показывает на дисплее "Connection LOST!"

Пользователь видит: красный экран с ошибкой


бабос любит конверты, и если конверты значит доплата +43% должна быть обязательна, (30 +13%-22) а другие 13% я уплачу сам!)))
может и ошибаюсь, или что то путаю… или что то изменилось… но это вы платите налоги за работников а не я)))

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

тут и считать не надо, так как нарушается главный принцип: “где найти мне пару миллионов я бы все их бабам раздарил

где же мои хейтеры… кто бы мне дизлайков накидал…

На вас ещё надежда есть, в силу возраста, а вот туть уже старческое. Тут уже никак, ибо - вера! Ну, может и Наташа, хто их знаить :grinning_face:
А …ть, опять повёлся на этих. Тады для сурьёзности: @PPeterr возьмите Ардуину Мега2560 с большим дисплеем на SPI и тачем, и засуньте всё это в клетку Фарадея в металлический корпус. И не надо ни УНО, ни Нано.

Металлический корпус - как защита, от наводок - так понимаю.
Дисплей (тем более с тачем) - нужен в удобном для подхода месте. То есть - метров 5 от модулей реле, через стену.
Поэтому мне не совсем понятно, где тогда располагать “металлический ящик с Mega и дисплеем-тачскрин”.

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

Спойлер
/*
   ПРОГРАММА ДЛЯ КОНТРОЛЛЕРА MEGA
*/

#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;

// ===================================================
// 1. НАСТРОЙКИ ДИСПЛЕЯ
// ===================================================
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4

// ===================================================
// 2. ЦВЕТА
// ===================================================
#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 DARKGREY  0x7BEF
#define DARKGREEN 0x0C80


// ===================================================
// 4. ПЕРЕМЕННЫЕ
// ===================================================
uint8_t kindDisplay = 1;
uint8_t kindDisplayPrev = 1;
uint16_t mistakes = 0;
//бит 0 - ошибка CRC при передаче данных от Mega к nano, менее нескольких раз подряд
//бит 1 - ошибка CRC при передаче данных от Mega к nano, более нескольких раз подряд
//бит 2 - ошибка потери связи с МК Mega, менее нескольких раз подряд
//бит 3 - ошибка потери связи с МК Mega, более нескольких раз подряд


//бит 15 - ошибка CRC при передаче данных от nano к Mega, менее нескольких раз подряд
//бит 14 - ошибка CRC при передаче данных от nano к Mega, ,более нескольких раз подряд
//бит 13 - ошибка потери связи с МК nano, менее нескольких раз подряд
//бит 12 - ошибка потери связи с МК nano, более нескольких раз подряд
uint8_t mistake_15_Count;//счётчик количества ошибок подряд, mistakes, бит 15
uint8_t mistake_13_Count;//счётчик количества ошибок подряд, mistakes, бит 13

uint16_t output_from_Mega_to_nano = 0;
//0 - дизельный котёл и насос дизельного котла
//1 - ТЭН 2,5 кВт
//2 - ТЭН 7,5 кВт
//3 - шаровой кран
//4 - насос радиаторов
//5 - насос тёплых полов
//6 - вентилятор наружного блока
//7 - закрывание клапана
//8 - открывание клапана
//9 - компрессор
//10 - четырёхходовой клапан
//11 - насос пластинчатого теплообменника
uint8_t actuatorMoveTime;//время в течении которого перемещается клапан (выражено в 0, 1 сек.: 10 = 1 сек.)
uint32_t actuatorTimer;//для выдержки времени между срабатыванием клапана


//-------------- проверка работы реле ---------------
uint8_t cycleCount = 0;
//-------------- (проверка работы реле) ---------------


// Данные от nano
struct DataFromNano {
  int16_t t1, t2, t3, t4, t5, t6, t7, t8, t9, t10;
  uint8_t P;
  uint8_t minutes, hours, days, months;
  uint8_t Water;
  uint8_t Electr;
  uint16_t relaysState;
  uint8_t mistakesFrom_nano;
  uint8_t crc;
} rxData;


// Данные от UNO
struct DataFromUNO {
  uint8_t Display;
} rxDataFromUNO;


// Массивы для графиков
uint8_t t1_Graph[320], t2_Graph[320], t3_Graph[320], t4_Graph[320];
uint8_t t5_Graph[320], t6_Graph[320], t7_Graph[320], t8_Graph[320];
uint8_t t9_Graph[320], t10_Graph[320];
uint8_t Water_Graph[320], Electr_Graph[320];


// ===================================================
// 5. CRC
// ===================================================
uint8_t crc8(const void* data, size_t length, uint8_t crc = 0) {
  const uint8_t* p = (const uint8_t*)data;
  while (length--) {
    uint8_t b = *p++, j = 8;
    while (j--) {
      crc = ((crc ^ b) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1);
      b >>= 1;
    }
  }
  return crc;
}

// ===================================================
// 6. ПРЕОБРАЗОВАНИЕ ДЛЯ ГРАФИКОВ
// ===================================================
int16_t getDataForUintMassiv(int16_t a) {
  if (a < -400) return 0;
  else if (a >= -400 && a < 500) return round((a + 400) / 5);
  else if (a >= 500 && a < 1250) return round(180 + (a - 500) / 10);
  else return 255;
}

uint8_t getDataForUintMassiv8(uint8_t a) {
  if (a <= 160) return round(40 + a);
  else if (a < 255) return round(200 + (a - 160) / 10);
  else return 255;
}

// ===================================================
// 7. ОТРИСОВКА ГРАФИКОВ
// ===================================================
void getGraphics(uint16_t c, uint16_t d, uint8_t g[320]) {
  for (int16_t x = 1; x < 320; x++) {
    tft.drawLine(x - 1, (c - g[x - 1]), x, (c - g[x]), d);
  }
}

// ===================================================
// 8. ОСНОВНОЙ ЭКРАН
// ===================================================
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(220, 10); tft.setTextColor(RED); tft.print("Isparitel:");
  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:");
}

// ===================================================
// 9. ТЕКСТОВЫЙ РЕЖИМ
// ===================================================
void getAllLastDataToDisplay() {
  tft.fillScreen(BLACK);
  tft.setTextSize(1);
  tft.setTextColor(YELLOW);

  tft.setCursor(0, 0); tft.print("tRoom:          "); tft.print(rxData.t1 / 10.0); tft.print(" C");
  tft.setCursor(0, 10); tft.print("tGS:            "); tft.print(rxData.t2 / 10.0); tft.print(" C");
  tft.setCursor(0, 20); tft.print("tBA:            "); tft.print(rxData.t3 / 10.0); tft.print(" C");
  tft.setCursor(0, 30); tft.print("tOutdoor:       "); tft.print(rxData.t4 / 10.0); tft.print(" C");
  tft.setCursor(0, 40); tft.print("tKompressorOut: "); tft.print(rxData.t5 / 10.0); tft.print(" C");
  tft.setCursor(0, 50); tft.print("tWaterIn:       "); tft.print(rxData.t6 / 10.0); tft.print(" C");
  tft.setCursor(0, 60); tft.print("tWaterOut:      "); tft.print(rxData.t7 / 10.0); tft.print(" C");
  tft.setCursor(0, 70); tft.print("tKompressorIn:  "); tft.print(rxData.t8 / 10.0); tft.print(" C");
  tft.setCursor(0, 80); tft.print("tDefrost:       "); tft.print(rxData.t9 / 10.0); tft.print(" C");
  tft.setCursor(0, 90); tft.print("tEvaporator:    "); tft.print(rxData.t10 / 10.0); tft.print(" C");
  tft.setCursor(0, 100); tft.print("Pressure:       ");
  if (rxData.P == 1) tft.print("OK");
  else if (rxData.P == 2) tft.print("HIGH");
  else tft.print("LOW");
  tft.setCursor(0, 110); tft.print("Time:           "); tft.print(rxData.hours); tft.print(":"); tft.print(rxData.minutes);
  tft.setCursor(0, 120); tft.print("Date:           "); tft.print(rxData.days); tft.print("."); tft.print(rxData.months);
  tft.setCursor(0, 130); tft.print("Water flow:     "); tft.print(rxData.Water / 100.0); tft.print(" m3/h");
  tft.setCursor(0, 140); tft.print("Power:          "); tft.print(rxData.Electr / 100.0); tft.print(" W");
  tft.setCursor(0, 150); tft.print("Relays:         "); tft.print(rxData.relaysState, BIN);
  tft.setCursor(0, 160); tft.print("Mistakes        "); tft.print(mistakes, BIN);
}

// ===================================================
// 10. ОТОБРАЖЕНИЕ ОШИБОК
// ===================================================
void showMistakes() {
  tft.fillScreen(BLACK);
  tft.setTextSize(2);
  tft.setTextColor(RED);
  tft.setCursor(0, 80);
  tft.print("❌ ERROR");
  tft.setCursor(0, 120);
  tft.setTextSize(1);
  if (bitRead(mistakes, 0) == HIGH)
    tft.println("CRC error from Mega");
  if (bitRead(mistakes, 1) == HIGH)
    tft.println("Stop: CRC error from Mega");
  if (bitRead(mistakes, 2) == HIGH)
    tft.println("Connection from Mega to nano LOST!");
  if (bitRead(mistakes, 3) == HIGH)
    tft.println("Stop: connection from Mega to nano LOST!");


  if (bitRead(mistakes, 15) == HIGH)
    tft.println("CRC error from nano");
  if (bitRead(mistakes, 14) == HIGH)
    tft.println("Stop: CRC error from nano");
  if (bitRead(mistakes, 13) == HIGH)
    tft.println("Connection from nano to Mega LOST!");
  if (bitRead(mistakes, 12) == HIGH)
    tft.println("Stop: connection from nano to Mega LOST!");

  else if (bitRead(mistakes, 0) == LOW and bitRead(mistakes, 1) == LOW and bitRead(mistakes, 2) == LOW and bitRead(mistakes, 3) == LOW and bitRead(mistakes, 15) == LOW and bitRead(mistakes, 14) == LOW and bitRead(mistakes, 13) == LOW and bitRead(mistakes, 12) == LOW)
    tft.println("Unknown error");
}

// ===================================================
// 11. ОТПРАВКА ПАРАМЕТРОВ НА NANO
// ===================================================
void sendParamsToNano() {
  struct ParamsToNano {
    uint16_t output_from_Mega_to_nano;
    uint8_t actuatorMoveTime;
    uint8_t crc;
  } params;

  params.output_from_Mega_to_nano = output_from_Mega_to_nano;
  params.actuatorMoveTime = actuatorMoveTime;
  params.crc = crc8(&params, sizeof(params) - 1);

  Serial1.write((uint8_t*)&params, sizeof(params));
}

// ===================================================
// 14. ФОРМИРОВАНИЕ КОМАНДЫ РЕГУЛИРУЮЩЕМУ КЛАПАНУ
// ===================================================
void actuatorMove(String a, uint8_t b)
{
  if (millis() - actuatorTimer > 15000)//если после предыдущего перемещения клапана прошло время
  {
    if (a == "DOWN")
      bitSet(output_from_Mega_to_nano, 7);//закрывать регулирующий клапан
    else if (a == "UP")
      bitSet(output_from_Mega_to_nano, 8);//открывать регулирующий клапан
    actuatorMoveTime = b;
    actuatorTimer = millis();
  }
}



// ===================================================
// 13. ФИКСАЦИЯ ОШИБОК ОТ nano
// ===================================================
void updateMistakes()
{
  if (bitRead(rxData.mistakesFrom_nano, 0) == HIGH)
    bitSet(mistakes, 0);
  else if (bitRead(rxData.mistakesFrom_nano, 0) == LOW)
    bitClear(mistakes, 0);

  if (bitRead(rxData.mistakesFrom_nano, 1) == HIGH)
    bitSet(mistakes, 1);
  else if (bitRead(rxData.mistakesFrom_nano, 1) == LOW)
    bitClear(mistakes, 1);

  if (bitRead(rxData.mistakesFrom_nano, 2) == HIGH)
    bitSet(mistakes, 2);
  else if (bitRead(rxData.mistakesFrom_nano, 2) == LOW)
    bitClear(mistakes, 2);

  if (bitRead(rxData.mistakesFrom_nano, 3) == HIGH)
    bitSet(mistakes, 3);
  else if (bitRead(rxData.mistakesFrom_nano, 3) == LOW)
    bitClear(mistakes, 3);
}


// ===================================================
// 13. СМЕНА ИЗОБРАЖЕНИЯ НА ДИСПЛЕЕ
// ===================================================
void selectDisplay()
{
  if (kindDisplay != kindDisplayPrev)
  {
    if (kindDisplay == 1)
      getAllLastDataToDisplay();
    kindDisplayPrev = kindDisplay;
  }
}


// ===================================================
// 13. ОБНОВЛЕНИЕ МАССИВОВ ДЛЯ ГРАФИКОВ
// ===================================================
void updateArrays() {
  for (int i = 0; i < 319; i++) {
    t1_Graph[i] = t1_Graph[i + 1];
    t2_Graph[i] = t2_Graph[i + 1];
    t3_Graph[i] = t3_Graph[i + 1];
    t4_Graph[i] = t4_Graph[i + 1];
    t5_Graph[i] = t5_Graph[i + 1];
    t6_Graph[i] = t6_Graph[i + 1];
    t7_Graph[i] = t7_Graph[i + 1];
    t8_Graph[i] = t8_Graph[i + 1];
    t9_Graph[i] = t9_Graph[i + 1];
    t10_Graph[i] = t10_Graph[i + 1];
    Water_Graph[i] = Water_Graph[i + 1];
    Electr_Graph[i] = Electr_Graph[i + 1];
  }

  t1_Graph[319] = getDataForUintMassiv(rxData.t1);
  t2_Graph[319] = getDataForUintMassiv(rxData.t2);
  t3_Graph[319] = getDataForUintMassiv(rxData.t3);
  t4_Graph[319] = getDataForUintMassiv(rxData.t4);
  t5_Graph[319] = getDataForUintMassiv(rxData.t5);
  t6_Graph[319] = getDataForUintMassiv(rxData.t6);
  t7_Graph[319] = getDataForUintMassiv(rxData.t7);
  t8_Graph[319] = getDataForUintMassiv(rxData.t8);
  t9_Graph[319] = getDataForUintMassiv(rxData.t9);
  t10_Graph[319] = getDataForUintMassiv(rxData.t10);
  Water_Graph[319] = getDataForUintMassiv8(rxData.Water);
  Electr_Graph[319] = getDataForUintMassiv8(rxData.Electr);
}

// ===================================================
// 14. НАЧАЛЬНАЯ НАСТРОЙКА
// ===================================================
void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);  // Связь с nano
  Serial2.begin(9600); // Связь с UNO

  // === Настройка дисплея ===
  tft.reset();
  tft.begin(38022);
  tft.invertDisplay(true);

  // === Инициализация графиков ===
  for (int i = 0; i < 320; i++) {
    t1_Graph[i] = t2_Graph[i] = t3_Graph[i] = t4_Graph[i] = 80;
    t5_Graph[i] = t6_Graph[i] = t7_Graph[i] = t8_Graph[i] = 80;
    t9_Graph[i] = t10_Graph[i] = 80;
    Water_Graph[i] = Electr_Graph[i] = 40;
  }

  delay(1000);
  getMainDisplay();

  // Отправляем начальные параметры
  delay(500);
  sendParamsToNano();
}

// ===================================================
// 15. ГЛАВНЫЙ ЦИКЛ
// ===================================================
void loop() {

  static uint32_t lastGet = 0;//для исчисления времени последних полученных данных

  selectDisplay();//моментальная смена изображения на дисплее

  // === 1. Приём данных от UNO ===
  if (Serial2.available()) {
    DataFromUNO window;
    if (Serial2.readBytes((uint8_t*)&window, sizeof(window))) {
      rxDataFromUNO = window;
      kindDisplay = rxDataFromUNO.Display;
    }
  }



  // === 2. Приём данных от nano ===
  if (Serial1.available()) {
    DataFromNano temp;
    if (Serial1.readBytes((uint8_t*)&temp, sizeof(temp))) {
      uint8_t crc = crc8(&temp, sizeof(temp));
      if (crc == 0) {
        rxData = temp;
        updateArrays();
        updateMistakes();
        bitClear(mistakes, 15);//снять ошибку передачи данных от nano к Mega
        mistake_15_Count = 0;

        // === 3. Отображение ===
        if (mistakes > 0)
          showMistakes;

        else {
          if (kindDisplay == 1) {
            getAllLastDataToDisplay();
          } else {
            getMainDisplay();
            getGraphics(245, RED, t1_Graph);
            getGraphics(245, YELLOW, t2_Graph);
            getGraphics(245, GREEN, t3_Graph);
            getGraphics(245, MAGENTA, t4_Graph);
            getGraphics(245, CYAN, t5_Graph);
            getGraphics(245, RED, t6_Graph);
            getGraphics(245, YELLOW, t7_Graph);
            getGraphics(245, GREEN, t8_Graph);
            getGraphics(245, MAGENTA, t9_Graph);
            getGraphics(245, CYAN, t10_Graph);
            getGraphics(475, RED, Water_Graph);
            getGraphics(475, YELLOW, Electr_Graph);
          }
        }
      }
      else
      {
        bitSet(mistakes, 15);//зафиксирована ошибка передачи данных от nano к Mega
        mistake_15_Count++;
        if (mistake_15_Count > 3)
          bitSet(mistakes, 14);//зафиксирована несколько раз подряд ошибка передачи данных от nano к Mega
        showMistakes;
      }
    }
    bitClear(mistakes, 13);
    mistake_13_Count = 0;
    lastGet = millis();
  }


  // === 4. Периодическая отправка параметров ===
  if (millis() - lastGet > 5000) {//через 5 сек. после получения данных от nano


    //-------------- проверка работы реле ---------------
    cycleCount++;
    if (cycleCount > 24)
      bitSet(output_from_Mega_to_nano, 0); //включить дизельный котёл и насос дизельного котла
    if (cycleCount > 22)
      bitSet(output_from_Mega_to_nano, 1);//включить ТЭН 2,5 кВт
    if (cycleCount > 20)
      bitSet(output_from_Mega_to_nano, 2);//включить ТЭН 7,5 кВт
    if (cycleCount > 18)
      bitSet(output_from_Mega_to_nano, 3);//открыть шаровой кран
    if (cycleCount == 17)
      actuatorMove("DOWN", 20);
    else
      bitClear(output_from_Mega_to_nano, 7);
    if (cycleCount == 15)
      actuatorMove("UP", 20);
    else
      bitClear(output_from_Mega_to_nano, 8);
    if (cycleCount > 12)
      bitSet(output_from_Mega_to_nano, 6);//включить вентилятор наружного блока
    if (cycleCount > 10)
      bitSet(output_from_Mega_to_nano, 4);//включить насос радиаторов
    if (cycleCount > 8)
      bitSet(output_from_Mega_to_nano, 5);//включить насос тёплых полов
    //if (cycleCount > 6)
    //bitSet(output_from_Mega_to_nano, 9);//включить компрессор
    //if (cycleCount > 4)
    //bitSet(output_from_Mega_to_nano, 10);//включить четырёхходовый клапан
    if (cycleCount > 2)
      bitSet(output_from_Mega_to_nano, 11);//включить насос пластинчатого теплообменника

    if (cycleCount > 26)
    {
      cycleCount = 0;
      output_from_Mega_to_nano = 0b0000000000000000;
    }
    //-------------- (проверка работы реле) ---------------



    sendParamsToNano();
  }


  // === 5. Фиксация потери связи ===
  if (millis() - lastGet > 15000) {//если в течение 15 сек. не было связи с nano
    {
      bitSet(mistakes, 13);//зафиксирована потеря связи с nano
      mistake_13_Count++;
      lastGet = millis();//попытка связи с nano
      if (mistake_13_Count > 3)
      {
        bitSet(mistakes, 12);//зафиксирована продолжительная потеря связи с nano
        output_from_Mega_to_nano = 0; //аварийный останов
      }
      showMistakes;
    }
  }
}

Код на МК ещё не проверял.
За основу брал - предложение от BABOS из сообщения 612.
Чувствую - что с помощью ИИ, наверное, это делается легко и просто у других. Я же - пока так, пытаюсь, как могу.

И опутайте все проводами, чтобы ходить и спотыкаться. Хотя и для крыс хорошая развлекуха.

Ну, явно же будет меньше чем Uno+Nano+Mega :slightly_smiling_face:

Провода и монтаж будет стоить дороже чем модульное решение.

Для единичного то изделия это конечно важно :neutral_face:
А вааще стоимость ESP будет явно выше чем провод данных/управления/питания учитывая, что силовые там по-любому должны быть.

Судя по подходу - доработка будет сразу после первого удачного варианта.

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

Стопудово :grinning_face: