Сожго там мегу - купи есп32 и буит те щасЬе!!!
это да, что бы потом изучать как ее программировать, и убедиться что для силовой электроники все таки лучше ардуино, или стм, ну или я не понял что она реально даст… в текущем случае если перейдет на нее
BABOS, мне больше всего понравился вариант кода из сообщения 115. В первую очередь потому - что там довольно подробно расписано в комментариях. Сегодня же, наибольший эффект (отказ нормально работать) показал отладочный код из сообщения 149.
Я пытаюсь сейчас сопоставить - насколько похожи (одинаковы) эти два кода.
У меня мысли - попробовать аккуратно постепенно подключать сильноточную нагрузку у реле (по очереди) и выявить, на каком именно реле происходит сбой.
Но я не могу пока точно сам себе ответить - а одинаковы ли вообще эти два кода?
Может, сможете мне подсказать - в чём у них разница? Отличие только в том, что у отладочного много “Serial.println()”, или ещё в чем-то?
Из сообщения 115:
// ========== 19. ФУНКЦИЯ ЧТЕНИЯ ТЕМПЕРАТУРЫ С ДАТЧИКА ==========
// Возвращает true, если CRC прошёл, и записывает температуру в *dest
bool readDS18B20(byte *addr, int16_t *dest) {
uint8_t data[9];
ds.reset();
ds.select(addr);
ds.write(0xBE);
for (uint8_t i = 0; i < 9; i++) {
data[i] = ds.read();
}
if (dallas_crc8(data, 8) != data[8]) {
return false; // Данные повреждены
}
int16_t raw = (int16_t)((data[1] << 8) | data[0]);
*dest = (raw * 5) / 8; // Правильная формула для int16_t
return true;
}
Из сообщения 149:
// ========== 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; // Преобразуем в десятые доли градуса
}
Во-первых, разница с этим пресловутым отрицательным (raw * 5) / 8 и (raw * 10) >> 4. Якобы - какой-то из них не показывает нормально отрицательные значения… но у меня в обоих вариантов - по этой части проблемы не было замечено. Я так понял - вариант (raw * 5) / 8 более правильный. Но тогда почему - в более позднем сообщении опять появилось (raw * 10) >> 4? Может - было ближе, “под рукой”…
Во-вторых - с самой ошибкой. В первом варианте - функция возвращает true/false. Во втором - какие-то данные…
В первом варианте - убрались вылетающие значения “G water”, во втором варианте - они присутствовали (как я смог для себя уяснить). Мне важно, чтобы “G water” не вылетал…
Короче - тут вижу какую-то разницу, но насколько она существенна - осознать не могу.
Из сообщения 115:
// ========== 21. ФУНКЦИЯ ПОЛУЧЕНИЯ ТЕМПЕРАТУР ==========
void gettingTemp() {
int16_t temp;
for (uint8_t i = 0; i < 6; i++) {
if (readDS18B20(ADDR_DS18B20[i], &temp)) {
// CRC прошёл — данные достоверны
Temper[i] = temp;
TemperPrev[i] = temp;
if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0;
} else {
// CRC не прошёл — оставляем предыдущее значение
// Temper[i] не меняется
}
}
}
Из сообщения 149:
// ========== 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; // Причина: ошибка датчика
}
}
}
}
}
Комментировать не могу. Но на мой взгляд, более понятный и простой - верхний вариант, из сообщения 115.
Что-то я уже “мозг сломал” - не могу понять, где (в каком случае) в коде из сообщения 115 работает счётчик MistakeTemp[a]++.
По коду - эта запись встречается только в одном месте
void tempMistake(uint8_t a, int16_t b, int16_t c, int16_t d) {
// Если значение в норме — сбрасываем счётчик ошибок
if (Temper[a] >= b && Temper[a] <= c && abs(Temper[a] - TemperPrev[a]) <= d) {
MistakeTemp[a] = 0;
return;
}
// Ошибка! Откатываем на предыдущее значение
Temper[a] = TemperPrev[a];
if (StopFlag == false) {
MistakeTemp[a]++;
if (MistakeTemp[a] > 6) {
StopFlag = true;
StopTime = millis();
StopKind = 6;
}
}
}
То есть - счётчик начнёт увеличиваться только лишь если не выполнится условие Temper[a] >= b && Temper[a] <= c && abs(Temper[a] - TemperPrev[a]) <= d
Но, при проверке значения, если CRC не прошёл - то остаётся предыдущее значение:
// ========== 21. ФУНКЦИЯ ПОЛУЧЕНИЯ ТЕМПЕРАТУР ==========
void gettingTemp() {
int16_t temp;
for (uint8_t i = 0; i < 6; i++) {
if (readDS18B20(ADDR_DS18B20[i], &temp)) {
// CRC прошёл — данные достоверны
Temper[i] = temp;
TemperPrev[i] = temp;
if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0;
} else {
// CRC не прошёл — оставляем предыдущее значение
// Temper[i] не меняется
}
}
}
Таким образом, получается: пришло неправильное значение (CRC не прошёл) - значение подменилось предыдущим, и в процедуре tempMistake оно принимается как корректное… в итоге получается - что получено некорректное значение (CRC не прошёл) - а счётчик ошибок не увеличился. Верно?
Ой,… похоже я запутался со 115, 149-ым сообщением… похоже - спать пора.
И обязательно на релюшках и тиристорах КУ202Н и ниипЭт_ваащпЭ.
Напиши мне веб-морду, образец-пример дам, но на есп32.
ТСу продадим за деньги, тебе дошик а мне семечки - пойдет???
только ради интереса… скидывайте ТЗ)))
возьмите код за основу из 115, дальше могут быть ошибки, и дело вроде не в коде, а в электронике
вы проверили экранизацию проводов ? опять же не факт что поможет, но мы же варианты перебираем ?)))
может опять только время потеряем, а может и поможет… при включении реле катушка реле вроде может создавать импульс который провода воспринимают за сигнал…
а дальше может еще кто что подскажет, или уже подсказал… (я не сдаю дельный совет или нет, и был ли он вообще, тут куча текста))) )и опять же может сработает, может нет))) надо пробовать
единственное что там вот эту строку лучше закомментировать if (MistakeTemp[a] > 0) return; // ← ВОТ ЭТА СТРОКА - БАГ!
и еще если надо… надо сделать сброс счетчика при норме… я не знаю в каких он там местах должен сбрасываться…
/*
ПРОГРАММА ДЛЯ МОНИТОРИНГА ТЕПЛОВОГО НАСОСА
ФИНАЛЬНАЯ ВЕРСИЯ СО ВСЕМИ ИСПРАВЛЕНИЯМИ
*/
// ========== 1. ПОДКЛЮЧАЕМ БИБЛИОТЕКИ ==========
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
#include <OneWire.h>
OneWire ds(21);
// ========== 2. НАСТРОЙКИ ПИНОВ ДЛЯ ДИСПЛЕЯ ==========
#define LCD_CS A3
#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;
// ========== 6. АДРЕСА ДАТЧИКОВ ТЕМПЕРАТУРЫ ==========
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},
{0x28, 0xEF, 0x8C, 0x76, 0x0, 0x0, 0x0, 0x58},
{0x28, 0x8C, 0x3A, 0xB4, 0xE, 0x0, 0x0, 0xDB}
};
// ========== 7. МАССИВЫ ДЛЯ ГРАФИКОВ ==========
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 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;
boolean Flag2 = true;
boolean Flag3 = true;
boolean Flag4 = true;
uint8_t countGetGrafic = 9;
uint32_t Timer;
// ========== 11. МАССИВЫ ДЛЯ ГРАФИКОВ СОСТОЯНИЯ РЕЛЕ ==========
int16_t trend_fan[20];
int16_t trend_kompressor[20];
int16_t trend_pump[20];
int16_t trend_4way_valve[20];
// ========== 12. ПЕРЕМЕННЫЕ ДЛЯ РАСХОДОМЕРА ВОДЫ ==========
uint32_t tmr;
boolean gWaterFlag = false;
uint32_t WaterStartTimer = 0;
uint32_t WaterCountTimer;
// ========== 13. ПЕРЕМЕННЫЕ ДЛЯ ЭЛЕКТРОСЧЁТЧИКА ==========
uint32_t tmr2;
boolean ElectrFlag = false;
uint32_t ElectrStartTimer = 0;
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 ==========
uint8_t dallas_crc8(const uint8_t *data, uint8_t len) {
uint8_t crc = 0;
for (uint8_t i = 0; i < len; i++) {
uint8_t inbyte = data[i];
for (uint8_t j = 0; j < 8; j++) {
uint8_t mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix) crc ^= 0x8C;
inbyte >>= 1;
}
}
return crc;
}
// ========== 19. ФУНКЦИЯ ЧТЕНИЯ ТЕМПЕРАТУРЫ С ДАТЧИКА (ИСПРАВЛЕНО) ==========
bool readDS18B20(byte *addr, int16_t *dest) {
uint8_t data[9];
ds.reset();
ds.select(addr);
ds.write(0xBE);
for (uint8_t i = 0; i < 9; i++) {
data[i] = ds.read();
}
if (dallas_crc8(data, 8) != data[8]) {
return false;
}
int16_t raw = (int16_t)((data[1] << 8) | data[0]);
*dest = (raw * 5) / 8; // Правильная формула (работает с отрицательными)
return true;
}
// ========== 20. ФУНКЦИЯ ЗАПУСКА ИЗМЕРЕНИЯ ТЕМПЕРАТУРЫ ==========
void resetTemp() {
ds.reset();
ds.write(0xCC);
ds.write(0x44);
}
// ========== 21. ФУНКЦИЯ ПОЛУЧЕНИЯ ТЕМПЕРАТУР ==========
void gettingTemp() {
int16_t temp;
for (uint8_t i = 0; i < 6; i++) {
if (readDS18B20(ADDR_DS18B20[i], &temp)) {
Temper[i] = temp;
TemperPrev[i] = temp;
if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0;
}
}
}
// ========== 22. ПРОВЕРКА ГРАНИЦ ТЕМПЕРАТУР (ИСПРАВЛЕНО) ==========
void tempMistake(uint8_t a, int16_t b, int16_t c, int16_t d) {
// Если значение в норме — сбрасываем счётчик ошибок
if (Temper[a] >= b && Temper[a] <= c && abs(Temper[a] - TemperPrev[a]) <= d) {
MistakeTemp[a] = 0;
return;
}
// Ошибка! Откатываем на предыдущее значение
Temper[a] = TemperPrev[a];
if (StopFlag == false) {
MistakeTemp[a]++;
if (MistakeTemp[a] > 6) {
StopFlag = true;
StopTime = millis();
StopKind = 6;
}
}
}
// ========== 23. ОБРАБОТЧИК ИМПУЛЬСОВ СЧЁТЧИКА ВОДЫ (ИСПРАВЛЕНО) ==========
void voda_tick() {
boolean current_status = digitalRead(pinVoda_tick);
if (stateCheck_voda && !current_status) {
tmr = millis();
gWaterFlag = true;
stateCheck_voda = current_status;
return;
}
if (!stateCheck_voda && current_status) {
stateCheck_voda = current_status;
return;
}
if (current_status) {
gWaterFlag = false;
return;
}
if (!current_status && (millis() - tmr >= 1000) && gWaterFlag == true) {
if (WaterStartTimer == 0) {
WaterStartTimer = millis();
gWaterFlag = false;
return;
}
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;
return;
}
if (!stateCheck_electr && current_status) {
stateCheck_electr = current_status;
return;
}
if (current_status) {
ElectrFlag = false;
return;
}
if (!current_status && (millis() - tmr2 >= 50) && ElectrFlag == true) {
if (ElectrStartTimer == 0) {
ElectrStartTimer = millis();
ElectrFlag = false;
return;
}
ElectrCountTimer = millis() - ElectrStartTimer;
ElectrStartTimer = millis();
ElectrFlag = false;
}
}
// ========== 25. ФУНКЦИИ ВЫВОДА ЧИСЕЛ НА ДИСПЛЕЙ ==========
void getDataInt100(int16_t a) {
if (a >= -9 && a < 0) {
tft.print("-0.0"); tft.print(abs(a));
} else if (a < 9 && a >= 0) {
tft.print("0.0"); tft.print(a);
} else if (a <= -10 && a > -100) {
tft.print("-0."); tft.print(abs(a % 100));
} else {
tft.print(a / 100); tft.print(".");
if (abs(a % 100) < 10) tft.print("0");
tft.print(abs(a % 100));
}
}
void getDataInt10(int16_t a) {
if (a >= -9 && a < 0) {
tft.print("-0."); tft.print(abs(a));
} else if (a < 9 && a >= 0) {
tft.print("0."); tft.print(a);
} else {
tft.print(a / 10); tft.print(".");
tft.print(abs(a % 10));
}
}
// ========== 26. ПРЕОБРАЗОВАНИЕ ЧИСЕЛ ДЛЯ ГРАФИКА ==========
int16_t getDataForUintMassiv(int16_t a) {
int16_t LastInMassive;
if (a < -400) LastInMassive = 0;
else if (a >= -400 && a < 500) LastInMassive = round((a + 400) / 5);
else if (a >= 500 && a < 1250) LastInMassive = round(180 + (a - 500) / 10);
else LastInMassive = 255;
return LastInMassive;
}
int32_t getDataForUintMassiv32(int32_t a) {
int32_t LastInMassive;
if (a < -400) LastInMassive = 0;
else if (a >= -400 && a < 0) LastInMassive = round((a + 400) / 10);
else if (a >= 0 && a <= 160) LastInMassive = round(40 + a);
else if (a > 160 && a < 710) LastInMassive = round(200 + (a - 160) / 10);
else LastInMassive = 255;
return LastInMassive;
}
// ========== 27. ОТРИСОВКА ГРАФИКОВ ==========
void getGraphics(uint16_t c, uint16_t d, uint8_t g[320]) {
int16_t oldX = 0;
int16_t oldY = g[0];
for (int16_t x = 1; x < 320; x++) {
int16_t nxt_x = x;
tft.drawLine(oldX, (c - oldY), nxt_x, (c - g[x]), d);
oldY = g[x];
oldX = nxt_x;
}
}
// ========== 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.print("MCUSR=0x");
Serial.println(MCUSR, HEX);
if (MCUSR & (1 << PORF)) Serial.println("RESET: Power-on reset");
if (MCUSR & (1 << EXTRF)) Serial.println("RESET: External reset (пин RESET)");
if (MCUSR & (1 << BORF)) Serial.println("RESET: Brown-out (просадка питания)");
if (MCUSR & (1 << WDRF)) Serial.println("RESET: Watchdog (зависание)");
MCUSR = 0;
tft.reset();
tft.begin(38022);
tft.invertDisplay(true);
pinMode(18, INPUT_PULLUP);
stateCheck_voda = digitalRead(pinVoda_tick);
pinMode(19, INPUT_PULLUP);
stateCheck_electr = digitalRead(pinElectr_tick);
for (int i = 0; i < 320; i++) {
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;
StartHeatFlag = true;
StartHeatTime = millis();
Timer = millis();
Serial.println("=== SETUP COMPLETE ===");
delay(1000);
}
// ========== 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);
tempMistake(1, 0, 600, 100);
tempMistake(2, 0, 900, 50);
tempMistake(3, -400, 800, 400);
tempMistake(4, -400, 600, 300);
tempMistake(5, -400, 800, 200);
tOverheatReal = (Temper[3] - Temper[5]);
// Расход воды
if (digitalRead(51) == HIGH) {
gWaterReal = 0;
gWaterRealPrew = gWaterReal;
FlagWater = false;
} else if (digitalRead(51) == LOW && millis() - WaterStartTimer > 400000) {
StopFlag = true; StopTime = millis(); StopKind = 7;
} else if (SecondImpulsWater == false) {
gWaterReal = gWaterRealPrew; FlagWater = true;
} else {
gWaterReal = round(3600000 / WaterCountTimer);
gWaterRealPrew = gWaterReal; FlagWater = false;
}
// Электрическая мощность
if (millis() - ElectrStartTimer > 60000) ElectrPowReal = 0;
else ElectrPowReal = 72000 / ElectrCountTimer;
// Тепловая мощность и COP
heatPowerReal = ((int32_t)Temper[2] - (int32_t)Temper[1]) * gWaterReal * 1163 / 10000;
if (ElectrPowReal < 10) COPreal = 0;
else COPreal = 100 * heatPowerReal / ElectrPowReal;
countHeat = countHeat + heatPowerReal;
countElectr = countElectr + ElectrPowReal;
countHeatAndElectr++;
// Управление вентилятором при перегреве
if (HeatOptionFlag == true && Temper[0] >= 850 && tOverheatReal >= 300 && digitalRead(48) == LOW) {
tKompressorCounter++;
if (tKompressorCounter >= 4) { bitClear(relay, 0); tKompressorCounter = 0; }
} else if (HeatOptionFlag == true && (Temper[0] <= 750 || tOverheatReal <= 200) && digitalRead(48) == HIGH) {
tKompressorCounter++;
if (tKompressorCounter >= 2) { bitSet(relay, 0); tKompressorCounter = 0; }
} else tKompressorCounter = 0;
// Режимы нагрева
if (HeatOptionFlag == true && Temper[1] >= 360) {
HeatOptionCout++;
if (HeatOptionCout >= 6) {
HeatOptionFlag = false; HeatOptionPauseFlag = true; HeatOptionCout = 0;
HeatOptionPauseStartTime = millis(); bitClear(relay, 2); bitClear(relay, 0);
}
} else HeatOptionCout = 0;
if (HeatOptionPauseFlag == true && digitalRead(49) == LOW && millis() - HeatOptionPauseStartTime >= 120000)
bitClear(relay, 1);
if (HeatOptionPauseFlag == true && digitalRead(51) == HIGH && millis() - HeatOptionPauseStartTime >= 960000) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis(); FirstImpulsWater = false; SecondImpulsWater = false;
}
bitSet(relay, 3); HeatOptionPauseStartTime = millis();
}
if (HeatOptionPauseFlag == true && digitalRead(51) == LOW && millis() - HeatOptionPauseStartTime >= 60000) {
bitClear(relay, 3); HeatOptionPauseStartTime = millis();
}
if (HeatOptionPauseFlag == true && Temper[1] <= 280) {
HeatOptionPauseCout++;
if (HeatOptionPauseCout >= 5) {
HeatOptionPauseFlag = false; HeatOptionPauseCout = 0;
StartHeatTime = millis(); StartHeatFlag = true;
}
} else HeatOptionPauseCout = 0;
// Defrost
if (HeatOptionFlag == true && Temper[5] < -100) {
defrostCount++;
if (defrostCount >= 6) {
HeatOptionFlag = false; DefrostOptionFlag = true; DefrostTime = millis(); defrostCount = 0;
}
} else defrostCount = 0;
// Защиты
if (HeatOptionFlag == true && digitalRead(49) == HIGH) {
StopNo4xvalve++;
if (StopNo4xvalve >= 3) {
bitClear(relay, 2); StopFlag = true; StopTime = millis(); StopKind = 1; StopNo4xvalve = 0;
}
} else StopNo4xvalve = 0;
if ((StartHeatFlag || HeatOptionFlag || HeatOptionPauseFlag || DefrostOptionFlag) && Temper[0] >= 1000) {
StopMaxTemp++;
if (StopMaxTemp >= 5) { StopFlag = true; StopTime = millis(); StopKind = 2; StopMaxTemp = 0; }
} else StopMaxTemp = 0;
if ((StartHeatFlag || HeatOptionFlag || HeatOptionPauseFlag || DefrostOptionFlag) && ElectrPowReal >= 151) {
StopMaxElectr++;
if (StopMaxElectr >= 6) { StopFlag = true; StopTime = millis(); StopKind = 3; StopMaxElectr = 0; }
} else StopMaxElectr = 0;
if ((StartHeatFlag || HeatOptionFlag || HeatOptionPauseFlag) && Temper[4] <= 50) {
StopMinTempHeater++;
if (StopMinTempHeater >= 3) { StopFlag = true; StopTime = millis(); StopKind = 4; StopMinTempHeater = 0; }
} else StopMinTempHeater = 0;
if (digitalRead(50) == LOW && ElectrPowReal <= 10) {
StopNoElectric++;
if (StopNoElectric >= 6) { StopFlag = true; StopTime = millis(); StopKind = 5; StopNoElectric = 0; }
} else StopNoElectric = 0;
// Start heat
if (StartHeatFlag && millis() - StartHeatTime >= 20000 && Temper[1] >= 50) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis(); FirstImpulsWater = false; SecondImpulsWater = false;
}
bitSet(relay, 3);
}
if (StartHeatFlag && digitalRead(51) == LOW && millis() - StartHeatTime >= 30000 && Temper[0] <= 850) bitSet(relay, 0);
if (StartHeatFlag && digitalRead(51) == LOW && digitalRead(48) == LOW && millis() - StartHeatTime >= 40000) bitSet(relay, 1);
if (StartHeatFlag && digitalRead(51) == LOW && digitalRead(48) == LOW && digitalRead(49) == LOW && millis() - StartHeatTime >= 45000) bitSet(relay, 2);
if (StartHeatFlag && digitalRead(51) == LOW && digitalRead(48) == LOW && digitalRead(50) == LOW && digitalRead(49) == LOW) {
StartHeatFlag = false; HeatOptionFlag = true;
}
// Stop
if (StopFlag) {
StartHeatFlag = false; HeatOptionFlag = false; HeatOptionPauseFlag = false; DefrostOptionFlag = false;
bitClear(relay, 2); bitClear(relay, 0);
if (millis() - StopTime >= 120000) bitClear(relay, 1);
if (millis() - StopTime >= 180000) bitClear(relay, 3);
}
// Defrost
if (DefrostOptionFlag) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis(); FirstImpulsWater = false; SecondImpulsWater = false;
}
bitSet(relay, 3); bitClear(relay, 2); bitSet(relay, 0);
if (millis() - DefrostTime >= 120000) bitClear(relay, 1);
if (millis() - DefrostTime >= 900000 && Temper[5] >= 20) {
DefrostOptionFlag = false; StartHeatTime = millis(); StartHeatFlag = true;
}
}
// Вывод на дисплей
tft.fillRect(74, 0, 30, 20, BLACK);
tft.fillRect(181, 0, 30, 20, BLACK);
tft.fillRect(289, 0, 30, 20, BLACK);
tft.fillRect(74, 210, 30, 20, BLACK);
tft.fillRect(181, 210, 30, 10, BLACK);
tft.fillRect(289, 210, 30, 10, BLACK);
tft.fillRect(152, 220, 60, 10, BLACK);
tft.fillRect(100, 470, 30, 8, BLACK);
tft.fillRect(200, 470, 30, 8, BLACK);
tft.setCursor(74, 0);
tft.setTextColor((MistakeTemp[0] > 0) ? CYAN : RED);
getDataInt10(Temper[0]);
tft.setCursor(181, 0);
tft.setTextColor((MistakeTemp[1] > 0) ? CYAN : YELLOW);
getDataInt10(Temper[1]);
tft.setCursor(289, 0);
tft.setTextColor((MistakeTemp[2] > 0) ? CYAN : GREEN);
getDataInt10(Temper[2]);
tft.setCursor(74, 10);
tft.setTextColor((MistakeTemp[3] > 0) ? CYAN : MAGENTA);
getDataInt10(tOverheatReal);
tft.setCursor(181, 10);
tft.setTextColor((MistakeTemp[4] > 0) ? CYAN : CYAN);
getDataInt10(Temper[4]);
tft.setCursor(289, 10);
tft.setTextColor((MistakeTemp[5] > 0) ? CYAN : RED);
getDataInt10(Temper[5]);
tft.setCursor(74, 210); tft.setTextColor(RED); getDataInt100(heatPowerReal);
tft.setCursor(181, 210); tft.setTextColor(YELLOW); getDataInt100(COPreal);
tft.setCursor(289, 210); tft.setTextColor(GREEN); getDataInt100(ElectrPowReal);
tft.setCursor(74, 220);
tft.setTextColor(FlagWater ? CYAN : MAGENTA);
getDataInt100(gWaterReal);
tft.setCursor(100, 470); tft.setTextColor(RED);
getDataInt100(countHeat / countHeatAndElectr);
tft.setCursor(200, 470); tft.setTextColor(GREEN);
getDataInt100(countElectr / countHeatAndElectr);
tft.setCursor(152, 220); tft.setTextColor(RED);
if (StopFlag == true) {
tft.print("Stop");
switch (StopKind) {
case 1: tft.print(" No 4x valve"); break;
case 2: tft.print(" maxT Kompr"); break;
case 3: tft.print(" maxW Electr"); break;
case 4: tft.print(" minT Heater"); break;
case 5: tft.print(" No Electr"); break;
case 6: tft.print(" Err Temp"); break;
case 7: tft.print(" No G_water"); break;
case 8: tft.print(" No Option"); break;
}
} else if (StartHeatFlag == true) {
tft.print("Start heat");
} else if (HeatOptionFlag == true) {
tft.print("Heat option");
} else if (HeatOptionPauseFlag == true) {
tft.print("Pause heat");
} else if (DefrostOptionFlag == true) {
tft.print("Defrost");
} else {
StopFlag = true; StopTime = millis(); StopKind = 8;
}
tft.setCursor(0, 440);
tft.setTextColor(bitRead(relay, 2) ? RED : BLUE); tft.print("Kompressor");
tft.setCursor(0, 450);
tft.setTextColor(bitRead(relay, 3) ? RED : BLUE); tft.print("Pump");
tft.setCursor(0, 460);
tft.setTextColor(bitRead(relay, 1) ? RED : BLUE); tft.print("4 way valve");
tft.setCursor(0, 470);
tft.setTextColor(bitRead(relay, 0) ? RED : BLUE); tft.print("Fan");
Flag3 = false;
}
// Шаг 4: управление реле (9-10 сек)
if (millis() - Timer >= 9000 && millis() - Timer < 10000 && Flag4 == true) {
digitalWrite(48, bitRead(relay, 0) ? LOW : HIGH);
digitalWrite(49, bitRead(relay, 1) ? LOW : HIGH);
digitalWrite(50, bitRead(relay, 2) ? LOW : HIGH);
digitalWrite(51, bitRead(relay, 3) ? LOW : HIGH);
Flag4 = false;
}
// Шаг 5: обновление графиков (каждые 10 сек)
if (millis() - Timer >= 10000) {
Timer = millis();
if (countGetGrafic >= 9) {
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 < 19; 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;
}
}
а можете этот потестировать, и если норм, взять его за основу, и лучше наверное начать новую тему, а то тут зафлудили ее, негодяи…
Похоже на это
Дело в том, что в том эксперименте что я наблюдал вчера (перезагрузка МК при попытке записи в серийный порт лога) - мне сейчас по памяти кажется, что МК перезагрузился как раз в момент срабатывания реле 4-ёх ходового клапана. А сам этот 4-ёх ходовый клапан - управляется электромагнитной катушкой
у меня есть подозрения, что там тоже надо ставить что-то типа RC цепочки, которую NMi рекомендовал для катушки контактора.
У меня вот какие мысли. Надо взять код, который BABOS рекомендует к настоящему времени, как актуальный (для управления оборудованием). И в этот код добавить вывод информации в серийный порт, по аналогии с тем кодом, в котором быстро наступала перезагрузка (предварительно - в момент срабатывания реле). И постепенно (подключая нагрузку поочереди к каждому из реле) - выявить, при работе какого именно реле срабатывает “reset”.
Спасибо за код. Но у меня есть вопросы:
- счётчик
MistakeTemp[a]++при получении значения, которое не пройдёт проверку CRC - увеличится? Будет ли отсутствие корректности CRC воспринято как ошибка?
Пока что - мне по коду кажется, что в этом случае счётчик не увеличится. А это неверно - на мой взгляд.
Речь идёт про счётчик MistakeTemp[a]++, верно?
Счётчик MistakeTemp[a]++ должен сбрасываться на ноль, если подряд от конкретного датчика - пришло не более 6 некорректных значений. То есть - назначение этого счётчика - исключить случайно вылетевшие некорректные значения. Если за 6 раз (то есть за 1 минуту) - пришло хотя бы одно корректное значение - то сбросить счётчик на ноль. Я просто хотел таким образом - не останавливать процесс, если датчик стал присылать некорректные значения, но в течение не более 1 минуты.
вооот! нашли что то)))) нет, не будет
void gettingTemp() {
int16_t temp;
for (uint8_t i = 0; i < 6; i++) {
if (readDS18B20(ADDR_DS18B20[i], &temp)) {
// CRC прошёл — данные достоверны
Temper[i] = temp;
TemperPrev[i] = temp;
if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0;
} else {
// CRC не прошёл — это ошибка!
// Temper[i] не меняем (остаётся предыдущее значение)
if (StopFlag == false) {
MistakeTemp[i]++;
if (MistakeTemp[i] > 6) {
StopFlag = true;
StopTime = millis();
StopKind = 6;
}
}
}
}
}
замените, и верно сделано ?))) Теперь ошибка CRC увеличивает счётчик MistakeTemp[i], и после 6 ошибок подряд будет аварийная остановка, и лучше если вы еще код повнимательнее посмотрите…
В gettingTemp():
cpp
if (readDS18B20(ADDR_DS18B20[i], &temp)) {
// CRC прошёл — сбрасываем счётчик
if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0; // <-- СБРОС при норме
} else {
// CRC не прошёл — увеличиваем счётчик
MistakeTemp[i]++;
if (MistakeTemp[i] > 6) { ... } // <-- ОСТАНОВКА при 7 ошибках подряд
}
В tempMistake():
cpp
if (Temper[a] >= b && Temper[a] <= c && abs(Temper[a] - TemperPrev[a]) <= d) {
MistakeTemp[a] = 0; // <-- СБРОС при норме
return;
}
// Иначе увеличиваем счётчик...
Оба механизма сбрасывают счётчик при первом же корректном значении. Если за 6 циклов пришло хотя бы одно нормальное значение — счётчик обнуляется, остановки не будет. Только 6 ошибок подряд приводят к остановке.
Это ровно то, что вы хотели: фильтр случайных выбросов длительностью до 1 минуты.
Вопрос такой.
Набор функций для вывода текста на дисплеи имеет такой вариант:
void setTextColor(uint16_t color, uint16_t backgroundcolor);
(это из руководства пользователя).
То есть - пишется текст определённым цветом (color) на фоне другого цвета (backgroundcolor). И мне нравится этим пользоваться. Особенно - для вывода некорректных значений.
А как при записи подобным образом tft.setTextColor((MistakeTemp[0] > 0) ? CYAN : RED); применить вариант написания текста на фоне. То есть - чтобы при условии MistakeTemp[0] > 0 текст бы выводился не просто цветом CYAN, но и ещё - на фоне (например) MAGENTA (backgroundcolor)?
tft.setTextColor(CYAN, MAGENTA); //???
tft.setTextColor(CYAN, (MistakeTemp[0] > 0) ? MAGENTA : BLACK);
Да, в этом заключался вопрос. Попробую. Спасибо.
У меня было:
if (MistakeTemp[0] > 0)
tft.setTextColor(CYAN, MAGENTA); // Указываем цвет текста
else
tft.setTextColor(RED); // Указываем цвет текста
А как всё же быть со сбросом таймера Timer = 0 в случае перезагрузки (нештатной) контроллера?
BABOS, я совместил Ваш код (крайний), который Вы советовали для тестирования с тем кодом, который при опытах вызывал перезагрузку МК (при подключённых сильноточных проводах) - то есть добавил “Serial.print” в монитор порта
Спойлер
/*
ПРОГРАММА ДЛЯ МОНИТОРИНГА ТЕПЛОВОГО НАСОСА
ФИНАЛЬНАЯ ВЕРСИЯ СО ВСЕМИ ИСПРАВЛЕНИЯМИ
*/
// ========== 1. ПОДКЛЮЧАЕМ БИБЛИОТЕКИ ==========
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
#include <OneWire.h>
OneWire ds(21);
// ========== 2. НАСТРОЙКИ ПИНОВ ДЛЯ ДИСПЛЕЯ ==========
#define LCD_CS A3
#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;
// ========== 6. АДРЕСА ДАТЧИКОВ ТЕМПЕРАТУРЫ ==========
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},
{0x28, 0xEF, 0x8C, 0x76, 0x0, 0x0, 0x0, 0x58},
{0x28, 0x8C, 0x3A, 0xB4, 0xE, 0x0, 0x0, 0xDB}
};
// ========== 7. МАССИВЫ ДЛЯ ГРАФИКОВ ==========
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 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;
boolean Flag2 = true;
boolean Flag3 = true;
boolean Flag4 = true;
uint8_t countGetGrafic = 9;
uint32_t Timer;
// ========== 11. МАССИВЫ ДЛЯ ГРАФИКОВ СОСТОЯНИЯ РЕЛЕ ==========
int16_t trend_fan[20];
int16_t trend_kompressor[20];
int16_t trend_pump[20];
int16_t trend_4way_valve[20];
// ========== 12. ПЕРЕМЕННЫЕ ДЛЯ РАСХОДОМЕРА ВОДЫ ==========
uint32_t tmr;
boolean gWaterFlag = false;
uint32_t WaterStartTimer = 0;
uint32_t WaterCountTimer;
// ========== 13. ПЕРЕМЕННЫЕ ДЛЯ ЭЛЕКТРОСЧЁТЧИКА ==========
uint32_t tmr2;
boolean ElectrFlag = false;
uint32_t ElectrStartTimer = 0;
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 ==========
uint8_t dallas_crc8(const uint8_t *data, uint8_t len) {
uint8_t crc = 0;
for (uint8_t i = 0; i < len; i++) {
uint8_t inbyte = data[i];
for (uint8_t j = 0; j < 8; j++) {
uint8_t mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix) crc ^= 0x8C;
inbyte >>= 1;
}
}
return crc;
}
// ========== 19. ФУНКЦИЯ ЧТЕНИЯ ТЕМПЕРАТУРЫ С ДАТЧИКА (ИСПРАВЛЕНО) ==========
bool readDS18B20(byte *addr, int16_t *dest) {
uint8_t data[9];
ds.reset();
ds.select(addr);
ds.write(0xBE);
for (uint8_t i = 0; i < 9; i++) {
data[i] = ds.read();
}
if (dallas_crc8(data, 8) != data[8]) {
return false;
}
int16_t raw = (int16_t)((data[1] << 8) | data[0]);
*dest = (raw * 5) / 8; // Правильная формула (работает с отрицательными)
return true;
}
// ========== 20. ФУНКЦИЯ ЗАПУСКА ИЗМЕРЕНИЯ ТЕМПЕРАТУРЫ ==========
void resetTemp() {
ds.reset();
ds.write(0xCC);
ds.write(0x44);
}
// ========== 21. ФУНКЦИЯ ПОЛУЧЕНИЯ ТЕМПЕРАТУР ==========
void gettingTemp() {
int16_t temp;
for (uint8_t i = 0; i < 6; i++) {
if (readDS18B20(ADDR_DS18B20[i], &temp)) {
// CRC прошёл — данные достоверны
Temper[i] = temp;
TemperPrev[i] = temp;
if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0;
} else {
// CRC не прошёл — это ошибка!
// Temper[i] не меняем (остаётся предыдущее значение)
if (StopFlag == false) {
MistakeTemp[i]++;
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) {
// Если значение в норме — сбрасываем счётчик ошибок
if (Temper[a] >= b && Temper[a] <= c && abs(Temper[a] - TemperPrev[a]) <= d) {
MistakeTemp[a] = 0;
return;
}
// Ошибка! Откатываем на предыдущее значение
Temper[a] = TemperPrev[a];
if (StopFlag == false) {
MistakeTemp[a]++;
if (MistakeTemp[a] > 6) {
StopFlag = true;
StopTime = millis();
StopKind = 6;
}
}
}
// ========== 23. ОБРАБОТЧИК ИМПУЛЬСОВ СЧЁТЧИКА ВОДЫ (ИСПРАВЛЕНО) ==========
void voda_tick() {
boolean current_status = digitalRead(pinVoda_tick);
if (stateCheck_voda && !current_status) {
tmr = millis();
gWaterFlag = true;
stateCheck_voda = current_status;
return;
}
if (!stateCheck_voda && current_status) {
stateCheck_voda = current_status;
return;
}
if (current_status) {
gWaterFlag = false;
return;
}
if (!current_status && (millis() - tmr >= 1000) && gWaterFlag == true) {
if (WaterStartTimer == 0) {
WaterStartTimer = millis();
gWaterFlag = false;
return;
}
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;
return;
}
if (!stateCheck_electr && current_status) {
stateCheck_electr = current_status;
return;
}
if (current_status) {
ElectrFlag = false;
return;
}
if (!current_status && (millis() - tmr2 >= 50) && ElectrFlag == true) {
if (ElectrStartTimer == 0) {
ElectrStartTimer = millis();
ElectrFlag = false;
return;
}
ElectrCountTimer = millis() - ElectrStartTimer;
ElectrStartTimer = millis();
ElectrFlag = false;
}
}
// ========== 25. ФУНКЦИИ ВЫВОДА ЧИСЕЛ НА ДИСПЛЕЙ ==========
void getDataInt100(int16_t a) {
if (a >= -9 && a < 0) {
tft.print("-0.0"); tft.print(abs(a));
} else if (a < 9 && a >= 0) {
tft.print("0.0"); tft.print(a);
} else if (a <= -10 && a > -100) {
tft.print("-0."); tft.print(abs(a % 100));
} else {
tft.print(a / 100); tft.print(".");
if (abs(a % 100) < 10) tft.print("0");
tft.print(abs(a % 100));
}
}
void getDataInt10(int16_t a) {
if (a >= -9 && a < 0) {
tft.print("-0."); tft.print(abs(a));
} else if (a < 9 && a >= 0) {
tft.print("0."); tft.print(a);
} else {
tft.print(a / 10); tft.print(".");
tft.print(abs(a % 10));
}
}
// ========== 26. ПРЕОБРАЗОВАНИЕ ЧИСЕЛ ДЛЯ ГРАФИКА ==========
int16_t getDataForUintMassiv(int16_t a) {
int16_t LastInMassive;
if (a < -400) LastInMassive = 0;
else if (a >= -400 && a < 500) LastInMassive = round((a + 400) / 5);
else if (a >= 500 && a < 1250) LastInMassive = round(180 + (a - 500) / 10);
else LastInMassive = 255;
return LastInMassive;
}
int32_t getDataForUintMassiv32(int32_t a) {
int32_t LastInMassive;
if (a < -400) LastInMassive = 0;
else if (a >= -400 && a < 0) LastInMassive = round((a + 400) / 10);
else if (a >= 0 && a <= 160) LastInMassive = round(40 + a);
else if (a > 160 && a < 710) LastInMassive = round(200 + (a - 160) / 10);
else LastInMassive = 255;
return LastInMassive;
}
// ========== 27. ОТРИСОВКА ГРАФИКОВ ==========
void getGraphics(uint16_t c, uint16_t d, uint8_t g[320]) {
int16_t oldX = 0;
int16_t oldY = g[0];
for (int16_t x = 1; x < 320; x++) {
int16_t nxt_x = x;
tft.drawLine(oldX, (c - oldY), nxt_x, (c - g[x]), d);
oldY = g[x];
oldX = nxt_x;
}
}
// ========== 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.print("MCUSR=0x");
Serial.println(MCUSR, HEX);
if (MCUSR & (1 << PORF)) Serial.println("RESET: Power-on reset");
if (MCUSR & (1 << EXTRF)) Serial.println("RESET: External reset (пин RESET)");
if (MCUSR & (1 << BORF)) Serial.println("RESET: Brown-out (просадка питания)");
if (MCUSR & (1 << WDRF)) Serial.println("RESET: Watchdog (зависание)");
MCUSR = 0;
tft.reset();
tft.begin(38022);
tft.invertDisplay(true);
pinMode(18, INPUT_PULLUP);
stateCheck_voda = digitalRead(pinVoda_tick);
pinMode(19, INPUT_PULLUP);
stateCheck_electr = digitalRead(pinElectr_tick);
for (int i = 0; i < 320; i++) {
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;
StartHeatFlag = true;
StartHeatTime = millis();
Timer = millis();
Serial.println("=== SETUP COMPLETE ===");
delay(1000);
}
// ========== 30. ГЛАВНЫЙ ЦИКЛ ==========
void loop() {
// ===== ДИАГНОСТИКА ВРЕМЕНИ =====
Serial.print("lm=");
Serial.print(millis());
Serial.print(" T=");
Serial.println(Timer);
// ================================
voda_tick();
electr_tick();
// Шаг 1: запуск конвертации (3-5 сек)
if (millis() - Timer >= 3000 && millis() - Timer < 5000 && Flag1 == true) {
Serial.println(">>> Step 1: resetTemp");
resetTemp();
Flag1 = false;
}
// Шаг 2: чтение температур (5-7 сек)
if (millis() - Timer >= 5000 && millis() - Timer < 7000 && Flag2 == true) {
Serial.println(">>> Step 2: gettingTemp");
gettingTemp();
Flag2 = false;
}
// Шаг 3: расчёты и логика (7-9 сек)
if (millis() - Timer >= 7000 && millis() - Timer < 9000 && Flag3 == true) {
Serial.println(">>> Step 3 START");
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);
tempMistake(5, -400, 800, 200);
tOverheatReal = (Temper[3] - Temper[5]);
// Расход воды
if (digitalRead(51) == HIGH) {
gWaterReal = 0;
gWaterRealPrew = gWaterReal;
FlagWater = false;
} else if (digitalRead(51) == LOW && millis() - WaterStartTimer > 400000) {
StopFlag = true; StopTime = millis(); StopKind = 7;
} else if (SecondImpulsWater == false) {
gWaterReal = gWaterRealPrew; FlagWater = true;
} else {
gWaterReal = round(3600000 / WaterCountTimer);
gWaterRealPrew = gWaterReal; FlagWater = false;
}
// Электрическая мощность
if (millis() - ElectrStartTimer > 60000) ElectrPowReal = 0;
else ElectrPowReal = 72000 / ElectrCountTimer;
// Тепловая мощность и COP
heatPowerReal = ((int32_t)Temper[2] - (int32_t)Temper[1]) * gWaterReal * 1163 / 10000;
if (ElectrPowReal < 10) COPreal = 0;
else COPreal = 100 * heatPowerReal / ElectrPowReal;
countHeat = countHeat + heatPowerReal;
countElectr = countElectr + ElectrPowReal;
countHeatAndElectr++;
// Управление вентилятором при перегреве
if (HeatOptionFlag == true && Temper[0] >= 850 && tOverheatReal >= 300 && digitalRead(48) == LOW) {
tKompressorCounter++;
if (tKompressorCounter >= 4) { bitClear(relay, 0); tKompressorCounter = 0; }
} else if (HeatOptionFlag == true && (Temper[0] <= 750 || tOverheatReal <= 200) && digitalRead(48) == HIGH) {
tKompressorCounter++;
if (tKompressorCounter >= 2) { bitSet(relay, 0); tKompressorCounter = 0; }
} else tKompressorCounter = 0;
// Режимы нагрева
if (HeatOptionFlag == true && Temper[1] >= 360) {
HeatOptionCout++;
if (HeatOptionCout >= 6) {
HeatOptionFlag = false; HeatOptionPauseFlag = true; HeatOptionCout = 0;
HeatOptionPauseStartTime = millis(); bitClear(relay, 2); bitClear(relay, 0);
}
} else HeatOptionCout = 0;
if (HeatOptionPauseFlag == true && digitalRead(49) == LOW && millis() - HeatOptionPauseStartTime >= 120000)
bitClear(relay, 1);
if (HeatOptionPauseFlag == true && digitalRead(51) == HIGH && millis() - HeatOptionPauseStartTime >= 960000) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis(); FirstImpulsWater = false; SecondImpulsWater = false;
}
bitSet(relay, 3); HeatOptionPauseStartTime = millis();
}
if (HeatOptionPauseFlag == true && digitalRead(51) == LOW && millis() - HeatOptionPauseStartTime >= 60000) {
bitClear(relay, 3); HeatOptionPauseStartTime = millis();
}
if (HeatOptionPauseFlag == true && Temper[1] <= 280) {
HeatOptionPauseCout++;
if (HeatOptionPauseCout >= 5) {
HeatOptionPauseFlag = false; HeatOptionPauseCout = 0;
StartHeatTime = millis(); StartHeatFlag = true;
}
} else HeatOptionPauseCout = 0;
// Defrost
if (HeatOptionFlag == true && Temper[5] < -100) {
defrostCount++;
if (defrostCount >= 6) {
HeatOptionFlag = false; DefrostOptionFlag = true; DefrostTime = millis(); defrostCount = 0;
}
} else defrostCount = 0;
// Защиты
if (HeatOptionFlag == true && digitalRead(49) == HIGH) {
StopNo4xvalve++;
if (StopNo4xvalve >= 3) {
bitClear(relay, 2); StopFlag = true; StopTime = millis(); StopKind = 1; StopNo4xvalve = 0;
}
} else StopNo4xvalve = 0;
if ((StartHeatFlag || HeatOptionFlag || HeatOptionPauseFlag || DefrostOptionFlag) && Temper[0] >= 1000) {
StopMaxTemp++;
if (StopMaxTemp >= 5) { StopFlag = true; StopTime = millis(); StopKind = 2; StopMaxTemp = 0; }
} else StopMaxTemp = 0;
if ((StartHeatFlag || HeatOptionFlag || HeatOptionPauseFlag || DefrostOptionFlag) && ElectrPowReal >= 151) {
StopMaxElectr++;
if (StopMaxElectr >= 6) { StopFlag = true; StopTime = millis(); StopKind = 3; StopMaxElectr = 0; }
} else StopMaxElectr = 0;
if ((StartHeatFlag || HeatOptionFlag || HeatOptionPauseFlag) && Temper[4] <= 50) {
StopMinTempHeater++;
if (StopMinTempHeater >= 3) { StopFlag = true; StopTime = millis(); StopKind = 4; StopMinTempHeater = 0; }
} else StopMinTempHeater = 0;
if (digitalRead(50) == LOW && ElectrPowReal <= 10) {
StopNoElectric++;
if (StopNoElectric >= 6) { StopFlag = true; StopTime = millis(); StopKind = 5; StopNoElectric = 0; }
} else StopNoElectric = 0;
// Start heat
if (StartHeatFlag && millis() - StartHeatTime >= 20000 && Temper[1] >= 50) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis(); FirstImpulsWater = false; SecondImpulsWater = false;
}
bitSet(relay, 3);
}
if (StartHeatFlag && digitalRead(51) == LOW && millis() - StartHeatTime >= 30000 && Temper[0] <= 850) bitSet(relay, 0);
if (StartHeatFlag && digitalRead(51) == LOW && digitalRead(48) == LOW && millis() - StartHeatTime >= 40000) bitSet(relay, 1);
if (StartHeatFlag && digitalRead(51) == LOW && digitalRead(48) == LOW && digitalRead(49) == LOW && millis() - StartHeatTime >= 45000) bitSet(relay, 2);
if (StartHeatFlag && digitalRead(51) == LOW && digitalRead(48) == LOW && digitalRead(50) == LOW && digitalRead(49) == LOW) {
StartHeatFlag = false; HeatOptionFlag = true;
}
// Stop
if (StopFlag) {
StartHeatFlag = false; HeatOptionFlag = false; HeatOptionPauseFlag = false; DefrostOptionFlag = false;
bitClear(relay, 2); bitClear(relay, 0);
if (millis() - StopTime >= 120000) bitClear(relay, 1);
if (millis() - StopTime >= 180000) bitClear(relay, 3);
}
// Defrost
if (DefrostOptionFlag) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis(); FirstImpulsWater = false; SecondImpulsWater = false;
}
bitSet(relay, 3); bitClear(relay, 2); bitSet(relay, 0);
if (millis() - DefrostTime >= 120000) bitClear(relay, 1);
if (millis() - DefrostTime >= 900000 && Temper[5] >= 20) {
DefrostOptionFlag = false; StartHeatTime = millis(); StartHeatFlag = true;
}
}
// Вывод на дисплей
tft.fillRect(74, 0, 30, 20, BLACK);
tft.fillRect(181, 0, 30, 20, BLACK);
tft.fillRect(289, 0, 30, 20, BLACK);
tft.fillRect(74, 210, 30, 20, BLACK);
tft.fillRect(181, 210, 30, 10, BLACK);
tft.fillRect(289, 210, 30, 10, BLACK);
tft.fillRect(152, 220, 60, 10, BLACK);
tft.fillRect(100, 470, 30, 8, BLACK);
tft.fillRect(200, 470, 30, 8, BLACK);
tft.setCursor(74, 0);
tft.setTextColor((MistakeTemp[0] > 0) ? CYAN : RED);
getDataInt10(Temper[0]);
tft.setCursor(181, 0);
tft.setTextColor((MistakeTemp[1] > 0) ? CYAN : YELLOW);
getDataInt10(Temper[1]);
tft.setCursor(289, 0);
tft.setTextColor((MistakeTemp[2] > 0) ? CYAN : GREEN);
getDataInt10(Temper[2]);
tft.setCursor(74, 10);
tft.setTextColor((MistakeTemp[3] > 0) ? CYAN : MAGENTA);
getDataInt10(tOverheatReal);
tft.setCursor(181, 10);
tft.setTextColor((MistakeTemp[4] > 0) ? CYAN : CYAN);
getDataInt10(Temper[4]);
tft.setCursor(289, 10);
tft.setTextColor((MistakeTemp[5] > 0) ? CYAN : RED);
getDataInt10(Temper[5]);
tft.setCursor(74, 210); tft.setTextColor(RED); getDataInt100(heatPowerReal);
tft.setCursor(181, 210); tft.setTextColor(YELLOW); getDataInt100(COPreal);
tft.setCursor(289, 210); tft.setTextColor(GREEN); getDataInt100(ElectrPowReal);
tft.setCursor(74, 220);
tft.setTextColor(FlagWater ? CYAN : MAGENTA);
getDataInt100(gWaterReal);
tft.setCursor(100, 470); tft.setTextColor(RED);
getDataInt100(countHeat / countHeatAndElectr);
tft.setCursor(200, 470); tft.setTextColor(GREEN);
getDataInt100(countElectr / countHeatAndElectr);
tft.setCursor(152, 220); tft.setTextColor(RED);
if (StopFlag == true) {
tft.print("Stop");
switch (StopKind) {
case 1: tft.print(" No 4x valve"); break;
case 2: tft.print(" maxT Kompr"); break;
case 3: tft.print(" maxW Electr"); break;
case 4: tft.print(" minT Heater"); break;
case 5: tft.print(" No Electr"); break;
case 6: tft.print(" Err Temp"); break;
case 7: tft.print(" No G_water"); break;
case 8: tft.print(" No Option"); break;
}
} else if (StartHeatFlag == true) {
tft.print("Start heat");
} else if (HeatOptionFlag == true) {
tft.print("Heat option");
} else if (HeatOptionPauseFlag == true) {
tft.print("Pause heat");
} else if (DefrostOptionFlag == true) {
tft.print("Defrost");
} else {
StopFlag = true; StopTime = millis(); StopKind = 8;
}
tft.setCursor(0, 440);
tft.setTextColor(bitRead(relay, 2) ? RED : BLUE); tft.print("Kompressor");
tft.setCursor(0, 450);
tft.setTextColor(bitRead(relay, 3) ? RED : BLUE); tft.print("Pump");
tft.setCursor(0, 460);
tft.setTextColor(bitRead(relay, 1) ? RED : BLUE); tft.print("4 way valve");
tft.setCursor(0, 470);
tft.setTextColor(bitRead(relay, 0) ? RED : BLUE); tft.print("Fan");
Flag3 = false;
Serial.println(">>> Step 3 END");
}
// Шаг 4: управление реле (9-10 сек)
if (millis() - Timer >= 9000 && millis() - Timer < 10000 && Flag4 == true) {
Serial.println(">>> Step 4: Relays");
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) {
Serial.println(">>> Step 5: Graphics reset, Cycle END");
Timer = millis();
if (countGetGrafic >= 9) {
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 < 19; 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;
}
}
Планирую идти, пробовать, смотреть с ним - в каких случаях будут сбои и что пропишет монитор порта.
