По порядку.
Вчера, когда пошёл загружать новый код - предыдущий код продолжал работать (находясь в ошибке по температурам), цикл отрабатывал. Прошло для его рабочего состояния - более суток.
Далее. При загрузке нового кода (из сообщения 115), появилась ошибка. Эта ошибка мучает меня давно, но я уже как-то свыкся с ней. Сейчас подумал - может она имеет значение. Дело в том, что частенько, после очередных корректировок кода (скетча) и попытки его загрузки в контроллер - происходит ситуация, когда уже по логу “загрузка выполнена успешно” появляется сообщение, наподобие “отсутствует ком порт”. Насколько я пытался выяснить в интернете с чем это связано - нашёл, что якобы после загрузки кода идёт проверка контрольных сумм и ответ от контроллера приходит о несоответствии этих контрольных сумм.
При этом - если убрать галочку выделенную красным:
то загрузка кода проходит без сообщения об ошибке, но при этом - на дисплее можно наблюдать сбои, наподобие того, что буквы могут иметь некорректные координаты (расположение на дисплее).
Я уже приловчился, случайно нашёл способ обходить эту проблему. Заметил, что если добавить строчку “Serial.println(…)” - то ошибка при загрузке эта - уходит. Иногда помогает не “Serial.println(…)”, а “Serial.print(…)”. Также - зависит от того, в какой процедуре это находится.
В общем, чтобы загрузить вчерашний код без ошибки, мне понадобилось добавить “Serial.println(“proverka”)” в пункте 27 “ОТРИСОВКА ГРАФИКОВ”
Спойлер
/*
* ПРОГРАММА ДЛЯ МОНИТОРИНГА ТЕПЛОВОГО НАСОСА
*
* Что делает программа:
* 1. Читает температуру с 6 датчиков DS18B20
* 2. Считает расход воды и электричества
* 3. Управляет реле (компрессор, насос, вентилятор, клапан)
* 4. Рисует графики на дисплее
* 5. Защищает систему от ошибок
*/
// ========== 1. ПОДКЛЮЧАЕМ БИБЛИОТЕКИ ==========
#include <MCUFRIEND_kbv.h> // Библиотека для работы с TFT дисплеем
MCUFRIEND_kbv tft; // Создаём объект для управления дисплеем
#include <OneWire.h> // Библиотека для работы с датчиками по протоколу 1-Wire
OneWire ds(21); // Говорим, что датчики подключены к пину 21 Arduino
// ========== 2. НАСТРОЙКИ ПИНОВ ДЛЯ ДИСПЛЕЯ ==========
#define LCD_CS A3 // Пин выбора дисплея (Chip Select)
#define LCD_CD A2 // Пин команд/данных
#define LCD_WR A1 // Пин записи
#define LCD_RD A0 // Пин чтения
#define LCD_RESET A4 // Пин сброса дисплея
// ========== 3. ЗАДАЁМ ЦВЕТА (как краски на палитре) ==========
// Каждый цвет - это число. Дисплей понимает цвета как числа.
#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 // Тёмно-зелёный
// ========== 4. ПИНЫ ДЛЯ СЧЁТЧИКОВ ВОДЫ И ЭЛЕКТРИЧЕСТВА ==========
#define pinVoda_tick 18 // Пин, куда приходят импульсы от счётчика воды
boolean stateCheck_voda; // Переменная для отслеживания состояния пина воды
#define pinElectr_tick 19 // Пин, куда приходят импульсы от электросчётчика
boolean stateCheck_electr; // Переменная для отслеживания состояния пина электричества
// ========== 5. ПЕРЕМЕННАЯ ДЛЯ УПРАВЛЕНИЯ РЕЛЕ ==========
uint8_t relay; // 8-битное число (0-255), каждый бит управляет одним реле
// Бит 0: вентилятор
// Бит 1: 4-ходовой клапан
// Бит 2: компрессор
// Бит 3: насос
// ========== 6. АДРЕСА ДАТЧИКОВ ТЕМПЕРАТУРЫ ==========
// Каждый датчик DS18B20 имеет уникальный адрес (как серийный номер)
// Это массив из 6 датчиков, у каждого адрес из 8 байт
byte ADDR_DS18B20 [6][8] = {
{0x28, 0xD5, 0x97, 0x76, 0x0, 0x0, 0x0, 0x76}, // Датчик на компрессоре
{0x28, 0x4F, 0x9C, 0x73, 0x0, 0x0, 0x0, 0xF9}, // Датчик воды на входе
{0x28, 0xFC, 0xD1, 0x72, 0x0, 0x0, 0x0, 0x2D}, // Датчик воды на выходе
{0x28, 0x95, 0x28, 0x75, 0x0, 0x0, 0x0, 0xAB}, // Дополнительный датчик 3
{0x28, 0xEF, 0x8C, 0x76, 0x0, 0x0, 0x0, 0x58}, // Датчик разморозки
{0x28, 0x8C, 0x3A, 0xB4, 0xE, 0x0, 0x0, 0xDB} // Датчик испарителя
};
// ========== 7. МАССИВЫ ДЛЯ ХРАНЕНИЯ ГРАФИКОВ ==========
// 320 точек по горизонтали (ширина экрана), значения 0-255 по вертикали
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]; // График КОП (эффективности)
// ========== 8. ТОЧНЫЕ ЗНАЧЕНИЯ (с запасом по точности) ==========
// int16_t - целое число от -32768 до 32767
// int32_t - целое число от -2147483648 до 2147483647
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; // Флаг: был ли второй импульс
// ========== 9. СРЕДНИЕ ЗНАЧЕНИЯ ==========
int32_t countHeatAndElectr = 0; // Счётчик для усреднения
int32_t countHeat = 0; // Сумма тепловой мощности для среднего
int32_t countElectr = 0; // Сумма электрической мощности для среднего
// ========== 10. ФЛАГИ ЦИКЛА (управление порядком действий) ==========
boolean Flag1 = true; // Флаг для шага 1 (запуск измерения)
boolean Flag2 = true; // Флаг для шага 2 (чтение температур)
boolean Flag3 = true; // Флаг для шага 3 (расчёты и логика)
boolean Flag4 = true; // Флаг для шага 4 (управление реле)
uint8_t countGetGrafic = 9; // Счётчик для обновления графиков
uint32_t Timer; // Таймер для отслеживания времени
// ========== 11. МАССИВЫ ДЛЯ ГРАФИКОВ СОСТОЯНИЯ РЕЛЕ ==========
int16_t trend_fan[20]; // История включений вентилятора (20 ячеек по 16 бит)
int16_t trend_kompressor[20]; // История включений компрессора
int16_t trend_pump[20]; // История включений насоса
int16_t trend_4way_valve[20]; // История включений 4-ходового клапана
// ========== 12. ПЕРЕМЕННЫЕ ДЛЯ РАСХОДОМЕРА ВОДЫ ==========
uint32_t tmr; // Таймер для защиты от дребезга контактов
boolean gWaterFlag = false; // Флаг: идёт ли измерение
uint32_t WaterStartTimer; // Время начала измерения
uint32_t WaterCountTimer; // Длительность импульса
// ========== 13. ПЕРЕМЕННЫЕ ДЛЯ ЭЛЕКТРОСЧЁТЧИКА ==========
uint32_t tmr2; // Таймер для защиты от дребезга
boolean ElectrFlag = false; // Флаг: идёт ли измерение
uint32_t ElectrStartTimer; // Время начала измерения
uint32_t ElectrCountTimer; // Длительность импульса
// ========== 14. ФЛАГИ РЕЖИМОВ РАБОТЫ ==========
boolean StartHeatFlag; // Режим: начальный нагрев
boolean StopFlag; // Режим: аварийная остановка
boolean HeatOptionFlag; // Режим: рабочий нагрев
boolean HeatOptionPauseFlag; // Режим: пауза между нагревом
boolean DefrostOptionFlag; // Режим: разморозка
// ========== 15. СЧЁТЧИКИ ДЛЯ ЗАЩИТЫ ==========
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; // Флаг первой записи температур
// ========== 16. ВРЕМЕННЫЕ МЕТКИ ==========
uint32_t HeatOptionPauseStartTime; // Время начала паузы
uint32_t StartHeatTime; // Время начала нагрева
uint32_t StopTime; // Время аварийной остановки
uint32_t DefrostTime; // Время начала разморозки
// ========== 17. МАССИВЫ ТЕМПЕРАТУР ==========
int16_t Temper[6] = {0, 0, 0, 0, 0, 0}; // Текущие температуры (в десятых долях)
int16_t TemperPrev[6]; // Предыдущие температуры (для проверки)
// ========== 18. ФУНКЦИЯ ПРОВЕРКИ ЦЕЛОСТНОСТИ ДАННЫХ (CRC) ==========
// CRC - это как контрольная сумма. Если данные повредились при передаче,
// CRC не совпадёт, и мы поймём, что данным нельзя доверять.
uint8_t dallas_crc8(const uint8_t *data, uint8_t len) {
uint8_t crc = 0; // Начальное значение CRC
// Проходим по всем байтам данных
for (uint8_t i = 0; i < len; i++) {
uint8_t inbyte = data[i]; // Берём очередной байт
// Обрабатываем каждый бит байта
for (uint8_t j = 0; j < 8; j++) {
// Проверяем, нужно ли делать XOR (операция "исключающее ИЛИ")
uint8_t mix = (crc ^ inbyte) & 0x01;
crc >>= 1; // Сдвигаем CRC вправо
// Если младший бит был 1, делаем XOR с полиномом
if (mix) crc ^= 0x8C;
inbyte >>= 1; // Сдвигаем байт данных вправо
}
}
return crc; // Возвращаем посчитанный CRC
}
// ========== 19. ФУНКЦИЯ ЧТЕНИЯ ТЕМПЕРАТУРЫ С ДАТЧИКА ==========
// Возвращает температуру в десятых долях (например 156 = 15.6°C)
// Если ошибка - возвращает 0x7FFF (максимальное число)
int16_t readDS18B20(byte *addr) {
uint8_t data[9]; // Массив для 9 байт данных от датчика
// 1. Начинаем общение с датчиком
ds.reset(); // Сброс шины 1-Wire
ds.select(addr); // Выбираем конкретный датчик по адресу
ds.write(0xBE); // Команда "прочитай мне данные"
// 2. Читаем 9 байт от датчика
for (uint8_t i = 0; i < 9; i++) {
data[i] = ds.read(); // Читаем байт
}
// 3. Проверяем CRC (целостность данных)
if (dallas_crc8(data, 8) != data[8]) {
return 0x7FFF; // Данные повреждены, возвращаем ошибку
}
// 4. Собираем температуру из двух байтов
int16_t raw = (data[1] << 8) | data[0]; // Сдвигаем старший байт и объединяем
return (raw * 10) >> 4; // Преобразуем в десятые доли градуса
}
// ========== 20. ФУНКЦИЯ ЗАПУСКА ИЗМЕРЕНИЯ ТЕМПЕРАТУРЫ ==========
// Говорим всем датчикам: "Начинайте измерять температуру!"
void resetTemp() {
ds.reset(); // Сброс шины
ds.write(0xCC); // Команда "обратиться ко всем датчикам"
ds.write(0x44); // Команда "начать измерение температуры"
}
// ========== 21. ФУНКЦИЯ ПОЛУЧЕНИЯ ТЕМПЕРАТУР ==========
// Читаем температуру со всех 6 датчиков
void gettingTemp() {
// Проходим по всем датчикам (от 0 до 5)
for (uint8_t i = 0; i < 6; i++) {
int16_t temp = readDS18B20(ADDR_DS18B20[i]); // Читаем датчик
if (temp != 0x7FFF) {
// Данные достоверны - сохраняем
Temper[i] = temp; // Текущая температура
TemperPrev[i] = temp; // Запоминаем как предыдущую
// Сбрасываем счётчик ошибок (если ошибок было меньше 6)
if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0;
} else {
// Данные повреждены (ошибка CRC)
// Используем предыдущее значение вместо ошибочного
Temper[i] = TemperPrev[i];
// Увеличиваем счётчик ошибок, если система не остановлена
if (StopFlag == false) {
MistakeTemp[i]++; // +1 к ошибкам
// Если ошибок больше 6 - аварийная остановка
if (MistakeTemp[i] > 6) {
StopFlag = true; // Включаем флаг остановки
StopTime = millis(); // Запоминаем время остановки
StopKind = 6; // Причина: ошибка датчика
}
}
}
}
}
// ========== 22. ПРОВЕРКА ГРАНИЦ ТЕМПЕРАТУР ==========
// Проверяем, не выходит ли температура за разумные пределы
void tempMistake(uint8_t a, int16_t b, int16_t c, int16_t d) {
// a - номер датчика (0-5)
// b - минимально допустимая температура
// c - максимально допустимая температура
// d - максимально допустимое изменение за раз
// Если уже есть ошибки CRC - пропускаем проверку
if (MistakeTemp[a] > 0) return;
// Проверяем три условия:
// 1. Температура >= минимума
// 2. Температура <= максимума
// 3. Изменение не больше допустимого
if (Temper[a] >= b && Temper[a] <= c && abs(Temper[a] - TemperPrev[a]) <= d) {
// Всё нормально - ничего не делаем
} else {
// Ошибка! Используем предыдущее значение
Temper[a] = TemperPrev[a];
// Увеличиваем счётчик ошибок
if (StopFlag == false) MistakeTemp[a]++;
// Если ошибок больше 6 - аварийная остановка
if (MistakeTemp[a] > 6 && StopFlag == false) {
StopFlag = true;
StopTime = millis();
StopKind = 6; // Ошибка показаний датчика
}
}
}
// ========== 23. ОБРАБОТЧИК ИМПУЛЬСОВ СЧЁТЧИКА ВОДЫ ==========
// Вызывается часто в loop() для отслеживания импульсов
void voda_tick() {
// Читаем текущее состояние пина (HIGH или LOW)
boolean current_status = digitalRead(pinVoda_tick);
// Если сигнал изменился с HIGH на LOW (передний фронт импульса)
if (stateCheck_voda && !current_status) {
tmr = millis(); // Запоминаем время
gWaterFlag = true; // Ставим флаг: началось измерение
stateCheck_voda = current_status; // Запоминаем новое состояние
}
// Если сигнал изменился с LOW на HIGH (задний фронт)
if (!stateCheck_voda && current_status) {
stateCheck_voda = current_status; // Запоминаем состояние
}
// Если сигнал HIGH - сбрасываем флаг измерения
if (current_status) gWaterFlag = false;
// Если прошло больше 1 секунды и флаг измерения активен
// (защита от дребезга контактов - случайных ложных срабатываний)
if (!current_status && millis() - tmr >= 1000 && gWaterFlag == true) {
// Измеряем время между импульсами
WaterCountTimer = millis() - WaterStartTimer;
WaterStartTimer = millis(); // Запоминаем время этого импульса
gWaterFlag = false; // Сбрасываем флаг
// Отмечаем, что были импульсы
if (FirstImpulsWater == true) SecondImpulsWater = true;
FirstImpulsWater = true;
}
}
// ========== 24. ОБРАБОТЧИК ИМПУЛЬСОВ ЭЛЕКТРОСЧЁТЧИКА ==========
// Работает так же, как и счётчик воды
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;
// Если прошло 50 миллисекунд (защита от дребезга)
if (!current_status && millis() - tmr2 >= 50 && ElectrFlag == true) {
ElectrCountTimer = millis() - ElectrStartTimer; // Измеряем период
ElectrStartTimer = millis(); // Запоминаем время
ElectrFlag = false; // Сбрасываем флаг
}
}
// ========== 25. ФУНКЦИИ ВЫВОДА ЧИСЕЛ НА ДИСПЛЕЙ ==========
// Вывод числа с точностью до сотых (например 1234 = 12.34)
void getDataInt100(int16_t a) {
if (a >= -9 && a < 0) {
// Для чисел от -0.09 до 0
tft.print("-0.0");
tft.print(abs(a));
} else if (a < 9 && a >= 0) {
// Для чисел от 0 до 0.09
tft.print("0.0");
tft.print(a);
} else if (a <= -10 && a > -100) {
// Для чисел от -0.99 до -0.10
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)); // Дробная часть
}
}
// Вывод числа с точностью до десятых (например 123 = 12.3)
void getDataInt10(int16_t a) {
if (a >= -9 && a < 0) {
// Для чисел от -0.9 до 0
tft.print("-0.");
tft.print(abs(a));
} else if (a < 9 && a >= 0) {
// Для чисел от 0 до 0.9
tft.print("0.");
tft.print(a);
} else {
// Для всех остальных чисел
tft.print(a / 10); // Целая часть
tft.print(".");
tft.print(abs(a % 10)); // Дробная часть
}
}
// ========== 26. ПРЕОБРАЗОВАНИЕ ЧИСЕЛ ДЛЯ ГРАФИКА ==========
// Преобразует реальное значение (например -40°C) в позицию на экране (0-255)
int16_t getDataForUintMassiv(int16_t a) {
int16_t LastInMassive;
// Разбиваем на диапазоны для лучшего отображения
if (a < -400) LastInMassive = 0; // Всё что ниже -40°C - в самый низ
else if (a >= -400 && a < 500) LastInMassive = round((a + 400) / 5); // От -40°C до 50°C
else if (a >= 500 && a < 1250) LastInMassive = round(180 + (a - 500) / 10); // До 125°C
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;
}
// ========== 27. ОТРИСОВКА ГРАФИКОВ ==========
// Рисует график по массиву значений
void getGraphics(uint16_t c, uint16_t d, uint8_t g[320]) {
// c - базовая линия по Y (где находится "ноль" графика)
// d - цвет линии
// g - массив с данными (320 точек)
Serial.println("proverka");
int16_t oldX = 0; // Предыдущая X координата
int16_t oldY = g[0]; // Предыдущая Y координата
// Рисуем линию, соединяя все точки графика
for (int16_t x = 1; x < 320; x++) {
int16_t nxt_x = x; // Текущая X координата
// Рисуем линию от предыдущей точки до текущей
tft.drawLine(oldX, (c - oldY), nxt_x, (c - g[x]), d);
oldY = g[x]; // Запоминаем текущую точку как предыдущую
oldX = nxt_x;
}
}
// ========== 28. ОТРИСОВКА ВСЕГО ЭКРАНА ==========
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:");
}
// ========== 29. НАЧАЛЬНАЯ НАСТРОЙКА (ВЫПОЛНЯЕТСЯ ОДИН РАЗ) ==========
void setup() {
Serial.begin(9600); // Запускаем Serial порт для отладки
// Настраиваем дисплей
tft.reset(); // Сбрасываем дисплей
tft.begin(38022); // Инициализируем с ID драйвера
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++) {
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;
}
// Настраиваем пины управления реле как выходы
// И сразу выключаем все реле (HIGH - выключено)
pinMode(48, OUTPUT); digitalWrite(48, HIGH); // Вентилятор
pinMode(49, OUTPUT); digitalWrite(49, HIGH); // 4-ходовой клапан
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); // Ждём 1 секунду для стабилизации
}
// ========== 30. ГЛАВНЫЙ ЦИКЛ (ВЫПОЛНЯЕТСЯ БЕСКОНЕЧНО) ==========
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;
}
// Проверяем каждую температуру на разумные границы
// Параметры: номер датчика, мин.темп, макс.темп, макс.изменение
tempMistake(0, -100, 1200, 200); // Компрессор: от -10°C до 120°C
tempMistake(1, 0, 600, 100); // Вода вход: от 0°C до 60°C
tempMistake(2, 0, 900, 50); // Вода выход: от 0°C до 90°C
tempMistake(3, -400, 800, 400); // Доп.датчик: от -40°C до 80°C
tempMistake(4, -400, 600, 300); // Разморозка: от -40°C до 60°C
tempMistake(5, -400, 800, 200); // Испаритель: от -40°C до 80°C
// Считаем перегрев (разница между датчиками 3 и 5)
tOverheatReal = (Temper[3] - Temper[5]);
// ===== РАСЧЁТ РАСХОДА ВОДЫ =====
if (digitalRead(51) == HIGH) {
// Если насос выключен - расхода нет
gWaterReal = 0;
gWaterRealPrew = gWaterReal;
FlagWater = false;
} else if (digitalRead(51) == LOW && millis() - WaterStartTimer > 400000) {
// Если насос включён, но импульсов нет больше 400 секунд - ошибка
StopFlag = true;
StopTime = millis();
StopKind = 7; // Нет расхода воды
} else if (SecondImpulsWater == false) {
// Ещё нет двух импульсов - используем предыдущее значение
gWaterReal = gWaterRealPrew;
FlagWater = true;
} else {
// Есть два импульса - считаем расход
// Формула: 3600000 / время между импульсами
gWaterReal = round(3600000 / WaterCountTimer);
gWaterRealPrew = gWaterReal;
FlagWater = false;
}
// ===== РАСЧЁТ ЭЛЕКТРИЧЕСКОЙ МОЩНОСТИ =====
if (millis() - ElectrStartTimer > 60000) {
// Если импульсов нет больше 60 секунд - мощность 0
ElectrPowReal = 0;
} else {
// Считаем мощность: 72000 / время между импульсами
ElectrPowReal = 72000 / ElectrCountTimer;
}
// ===== РАСЧЁТ ТЕПЛОВОЙ МОЩНОСТИ =====
// Формула: разница температур * расход воды * 1163 / 10000
heatPowerReal = ((int32_t)Temper[2] - (int32_t)Temper[1]) * gWaterReal * 1163 / 10000;
// ===== РАСЧЁТ КОП (эффективности) =====
if (ElectrPowReal < 10) {
COPreal = 0; // Если электричества почти нет - КОП = 0
} else {
// КОП = тепловая мощность / электрическая мощность * 100
COPreal = 100 * heatPowerReal / ElectrPowReal;
}
// ===== СЧИТАЕМ СРЕДНИЕ ЗНАЧЕНИЯ =====
countHeat = countHeat + heatPowerReal;
countElectr = countElectr + ElectrPowReal;
countHeatAndElectr++; // Счётчик измерений
// ===== УПРАВЛЕНИЕ ВЕНТИЛЯТОРОМ ПРИ ПЕРЕГРЕВЕ =====
if (HeatOptionFlag == true && Temper[0] >= 850 && tOverheatReal >= 300 && digitalRead(48) == LOW) {
// Если температура компрессора >= 85°C и перегрев >= 30°C
tKompressorCounter++; // Увеличиваем счётчик
if (tKompressorCounter >= 4) { // Если 4 раза подряд
bitClear(relay, 0); // Выключаем вентилятор (бит 0 = 0)
tKompressorCounter = 0;
}
} else if (HeatOptionFlag == true && (Temper[0] <= 750 || tOverheatReal <= 200) && digitalRead(48) == HIGH) {
// Если температура <= 75°C или перегрев <= 20°C
tKompressorCounter++;
if (tKompressorCounter >= 2) { // Если 2 раза подряд
bitSet(relay, 0); // Включаем вентилятор (бит 0 = 1)
tKompressorCounter = 0;
}
} else {
tKompressorCounter = 0; // Сбрасываем счётчик
}
// ===== ОКОНЧАНИЕ РЕЖИМА НАГРЕВА =====
if (HeatOptionFlag == true && Temper[1] >= 360) {
// Если вода на входе >= 36°C
HeatOptionCout++;
if (HeatOptionCout >= 6) { // 6 раз подряд
HeatOptionFlag = false; // Выключаем нагрев
HeatOptionPauseFlag = true; // Включаем паузу
HeatOptionCout = 0;
HeatOptionPauseStartTime = millis(); // Запоминаем время
bitClear(relay, 2); // Выключаем компрессор
bitClear(relay, 0); // Выключаем вентилятор
}
} else {
HeatOptionCout = 0;
}
// ===== ЛОГИКА ПАУЗЫ =====
// Выключаем клапан через 2 минуты паузы
if (HeatOptionPauseFlag == true && digitalRead(49) == LOW && millis() - HeatOptionPauseStartTime >= 120000) {
bitClear(relay, 1); // Выключаем 4-ходовой клапан
}
// Включаем насос на 16 минут если нет расхода воды
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) {
// Если вода остыла до 28°C
HeatOptionPauseCout++;
if (HeatOptionPauseCout >= 5) { // 5 раз подряд
HeatOptionPauseFlag = false; // Выключаем паузу
HeatOptionPauseCout = 0;
StartHeatTime = millis();
StartHeatFlag = true; // Начинаем нагрев заново
}
} else {
HeatOptionPauseCout = 0;
}
// ===== ЗАЩИТА: НЕТ КЛАПАНА =====
if (HeatOptionFlag == true && digitalRead(49) == HIGH) {
StopNo4xvalve++;
if (StopNo4xvalve >= 3) { // 3 раза подряд
bitClear(relay, 2); // Выключаем компрессор
StopFlag = true;
StopTime = millis();
StopKind = 1; // Причина: нет 4-ходового клапана
StopNo4xvalve = 0;
}
} else {
StopNo4xvalve = 0;
}
// ===== ЗАЩИТА: ПЕРЕГРЕВ КОМПРЕССОРА =====
if ((StartHeatFlag == true || HeatOptionFlag == true || HeatOptionPauseFlag == true || DefrostOptionFlag == true) && Temper[0] >= 1000) {
StopMaxTemp++;
if (StopMaxTemp >= 5) { // 5 раз подряд >= 100°C
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) { // 6 раз подряд >= 151
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) { // 3 раза подряд <= 5°C
StopFlag = true;
StopTime = millis();
StopKind = 4; // Причина: низкая температура нагревателя
StopMinTempHeater = 0;
}
} else {
StopMinTempHeater = 0;
}
// ===== ЗАЩИТА: НЕТ ЭЛЕКТРИЧЕСТВА =====
if (digitalRead(50) == LOW && ElectrPowReal <= 10) {
StopNoElectric++;
if (StopNoElectric >= 6) { // 6 раз подряд
StopFlag = true;
StopTime = millis();
StopKind = 5; // Причина: нет электричества
StopNoElectric = 0;
}
} else {
StopNoElectric = 0;
}
// ===== ЛОГИКА СТАРТА НАГРЕВА =====
// Через 20 секунд включаем насос
if (StartHeatFlag == true && millis() - StartHeatTime >= 20000 && Temper[1] >= 50) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis();
FirstImpulsWater = false;
SecondImpulsWater = false;
}
bitSet(relay, 3); // Включаем насос
}
// Через 30 секунд включаем вентилятор
if (StartHeatFlag == true && digitalRead(51) == LOW && millis() - StartHeatTime >= 30000 && Temper[0] <= 850) {
bitSet(relay, 0); // Включаем вентилятор
}
// Через 40 секунд включаем клапан
if (StartHeatFlag == true && digitalRead(51) == LOW && digitalRead(48) == LOW && millis() - StartHeatTime >= 40000) {
bitSet(relay, 1); // Включаем 4-ходовой клапан
}
// Через 45 секунд включаем компрессор
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; // Включаем рабочий режим
}
// ===== АВАРИЙНАЯ ОСТАНОВКА =====
if (StopFlag == true) {
StartHeatFlag = false;
HeatOptionFlag = false;
HeatOptionPauseFlag = false;
DefrostOptionFlag = false;
bitClear(relay, 2); // Выключаем компрессор сразу
bitClear(relay, 0); // Выключаем вентилятор сразу
if (millis() - StopTime >= 120000) bitClear(relay, 1); // Клапан через 2 минуты
if (millis() - StopTime >= 180000) bitClear(relay, 3); // Насос через 3 минуты
}
// ===== РЕЖИМ РАЗМОРОЗКИ =====
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); // Выключаем клапан через 2 минуты
// Заканчиваем разморозку через 15 минут или при температуре >= 2°C
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; // Сбрасываем флаг шага 3
}
// ===== ШАГ 4: УПРАВЛЕНИЕ РЕЛЕ (9-10 секунд) =====
if (millis() - Timer >= 9000 && millis() - Timer < 10000 && Flag4 == true) {
// Применяем состояния реле к физическим пинам
// LOW - включено, HIGH - выключено (инверсная логика)
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: ОБНОВЛЕНИЕ ГРАФИКОВ (каждые 10 секунд) =====
if (millis() - Timer >= 10000) {
Timer = millis(); // Сбрасываем таймер
// Обновляем графики каждый 10-й цикл
if (countGetGrafic >= 9) {
// Сдвигаем все графики на 1 точку влево
// (старые данные уходят, освобождая место для новых)
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 = 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, 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;
}
}
при этом, предыдущий код, из из сообщения 58 загружался без появления подобной ошибки.
Когда загружается код без “плясок” наподобие добавления “Serial.println(“proverka”)” - мне радостно на душе. Вчера вот - не получилось этого. Подумал, следует сообщить. Вдруг это что-то значит.
Далее. После загрузки нового кода, некоторое время простоял рядом, понаблюдал
код отработал штатно. Никаких ошибок не наблюдал. По прошествии 1,5 часа его работы - сделал это фото дисплея и пошёл спать (было уже поздно). В голове созрел вопрос: почему в предыдущем варианте кода - я неоднократно наблюдал “вылетевшие” значения по “G water”, а теперь, с этим вариантом кода они отсутствовали? Это просто случайность, “так сошлись звёзды”, или же что-то в этом направлении было изменено в коде?
Утром пошёл смотреть. Прошло 9 часов с момента загрузки кода и запуска. Наблюдаю такую картину:
вид дисплея говорит о том, что пару часов назад была перезагрузка, типа “reset”.
И опять вопросы - почему с предыдущим кодом цикл не останавливался более суток (правда, имея при этом режим “останов по причине ошибок температур”)… и я считал, что избавился от проблем типа “перезагрузка”…, а теперь - опять вижу эту самую проблему, типа “reset”. Мучает вопрос - как к этому относиться? Это “так сошлись звёзды”, или как-то повлиял новый код?


