Поддержу. Промышленное оборудование строится по другим принципам. Это не просто помигать светодиодом, но и диагностика работоспособности светодиода и т.д. ПО тестируется не на удачное выполнение функции, но и восстановление после сбоя. Поэтому хоть и применяются ардункочипы в охранных системах, но код внутри оттестирован, поскольку отвечать производителю оборудования, а не производителю ардуинки.
Я так понимаю - что Вы говорите о том, что такой как я, без должного обучения - не способен написать нормальный код.
Но что хочу возразить на это. Несколько лет назад я умудрился сделать (по сути - “надёргав” куски кода из интернета). Системку, которая следит за обогревом дома, управляя различными органами и получая температуру.
Рядом с нагревательными элементами установлена Ардуино “nano”. Она получает значения с датчиков, отправляет через rx/tx. Потом получает через rx/tx ответ и посылает команду на реле. Код этой “nano” такой:
Спойлер
#include <OneWire.h>
OneWire ds(2);
#define START_CONVERT 0
#define READ_TEMP 1
//библиотеки для часов реального времени
#include "microDS3231.h"
MicroDS3231 rtc;
byte addr1[8]={0x28,0xFF,0x94,0xAE,0xA0,0x16,0x03,0x0E};
byte addr2[8]={0x28,0xFF,0x48,0x83,0xA0,0x16,0x04,0x1F};
byte addr3[8]={0x28,0xFF,0xAA,0xAB,0xA0,0x16,0x05,0x74};
byte addr4[8]={0x28,0xFF,0xEC,0x98,0xA0,0x16,0x04,0x9F};
byte a=1; //переменная для фиксации данных от электроконтактного манометра (минимальное и максимальное давление в системе)
// 1 - давление в рабочем диапазоне
// 2 - минимальное давление
// 3 - максимальное давление
byte output=0; //переменная, непосредственно замыкаюшая и размыкающая пины, управляющие релюшками
//бит "0"-для управления насосом тёплых полов (on/off)
//бит "1"-для управления насосом радиаторов (on/off)
//бит "2"-для управления котлом и котловым насосом одновременно (on/off)
//бит "3"-для управления ТЭН-ом гидроразделителя 2,5 кВт (on/off)
//бит "4"-для управления (и отображения состояния) ТЭНа теплоаккумулятора 7,5 кВт (on/off)
//бит "5"-для управления (и отображения состояния) шарового крана на выходе теплоаккумулятора (откр/закр)
void setup() {
Serial.begin(9600);
//конвентируем
tempProcess(START_CONVERT,addr1);
tempProcess(START_CONVERT,addr2);
tempProcess(START_CONVERT,addr3);
tempProcess(START_CONVERT,addr4);
digitalWrite(3, HIGH);
pinMode(3, OUTPUT);
digitalWrite(4, HIGH);
pinMode(4, OUTPUT);
digitalWrite(5, HIGH);
pinMode(5, OUTPUT);
digitalWrite(6, HIGH);
pinMode(6, OUTPUT);
digitalWrite(7, HIGH);
pinMode(7, OUTPUT);
digitalWrite(8, HIGH);
pinMode(8, OUTPUT);
pinMode(9, INPUT_PULLUP); //пин D9 назначается как "вход подтянутый к питанию" - для сигнализации о минимальном давлении в системе
pinMode(10, INPUT_PULLUP); //пин D10 назначается как "вход подтянутый к питанию" - для сигнализации о максимальном давлении в системе
if (rtc.lostPower()) { // при потере питания
rtc.setTime(COMPILE_TIME); // установить время компиляции
}
//rtc.setTime(SEC, MIN, HOUR, DAY, MONTH, YEAR); // установка времени вручную
//rtc.setTime(0, 34, 21, 26, 9, 2022); // установка времени вручную
delay(1000);
}
void loop()
{
//принимаю байт с управляющими командами через порт:
while (Serial.available())
output=Serial.read();
//Serial.println(output,BIN); //для проверки
if (bitRead(output,0)==0)
digitalWrite(3, HIGH);
else if (bitRead(output,0)==1)
digitalWrite(3, LOW);
if (bitRead(output,1)==0)
digitalWrite(4, HIGH);
else if (bitRead(output,1)==1)
digitalWrite(4, LOW);
if (bitRead(output,2)==0)
digitalWrite(5, HIGH);
else if (bitRead(output,2)==1)
digitalWrite(5, LOW);
if (bitRead(output,3)==0)
digitalWrite(6, HIGH);
else if (bitRead(output,3)==1)
digitalWrite(6, LOW);
if (bitRead(output,4)==0)
digitalWrite(7, HIGH);
else if (bitRead(output,4)==1)
digitalWrite(7, LOW);
if (bitRead(output,5)==0)
digitalWrite(8, HIGH);
else if (bitRead(output,5)==1)
digitalWrite(8, LOW);
delay(8000);//задержка
if (digitalRead(9)==0) //если замкнулся на "землю" пин D9
a=2; //сработал микрик манометра о минимальном давлении в системе
else if (digitalRead(10)==0) //если замкнулся на "землю" пин D10
a=3; //сработал микрик манометра о максимальном давлении в системе
else
a=1; //стрелка манометра находится в рабочем диапазоне (между минимальным и максимальным значением)
int temp1=20;
int temp2=20;
int temp3=20;
int temp4=20;
temp1=tempProcess(READ_TEMP, addr1);//читаем Т1
tempProcess(START_CONVERT,addr1);
Serial.print(temp1);
Serial.print(";");
temp2=tempProcess(READ_TEMP, addr2);//читаем Т2
tempProcess(START_CONVERT,addr2);
Serial.print(temp2);
Serial.print(";");
temp3=tempProcess(READ_TEMP, addr3);//читаем Т3
tempProcess(START_CONVERT,addr3);
Serial.print(temp3);
Serial.print(";");
temp4=tempProcess(READ_TEMP, addr4);//читаем Т4
tempProcess(START_CONVERT,addr4);
Serial.print(temp4);
Serial.print(";");
Serial.print(rtc.getHours());
Serial.print(";");
Serial.print(rtc.getMinutes());
Serial.print(";");
Serial.print(a); //передаётся положение стрелки электроконтактного манометра
Serial.println(";E");
delay(2000);//задержка
}
//==============================
int tempProcess(boolean ch, byte *sensor)
{
int t=0;
if(!ch)
{
ds.reset();
ds.write(0xCC);
ds.write(0x44);
}
else
{
ds.reset();
ds.select(sensor);
ds.write(0xBE);
t = ds.read() | (ds.read()<<8);
return (t*10)>>4;//целое в десятых *C (214=>21,4*C)
}
}
А в комнате стоит другой контроллер, Ардуино “UNO”, который по rx/tx получает значения, обрабатывает и в ответ посылает через rx/tx команду. Код у этой “UNO” такой:
(часть кода не влезла по ограничению количества букв в сообщении)
Спойлер
byte output; //=b00000000
//бит "0"-для управления (и отображения состояния) насоса тёплых полов (on/off)
//бит "1"-для управления (и отображения состояния) насоса радиаторов (on/off)
//бит "2"-для управления (и отображения состояния) котлом (on/off)
//бит "3"-для управления (и отображения состояния) ТЭНа гидроразделителя 2,5 кВт (on/off)
//бит "4"-для управления (и отображения состояния) ТЭНа теплоаккумулятора 7,5 кВт (on/off)
//бит "5"-для управления (и отображения состояния) шарового крана на выходе теплоаккумулятора (откр/закр)
byte prev; //=b00000000
//бит "0"-для регистрации предыдущего состояния насоса полов (вкл/выкл) для фиксирования времени срабатывания
//бит "1"-для регистрации предыдущего состояния насоса радиаторов (вкл/выкл) для фиксирования времени срабатывания
//бит "2"-для регистрации предыдущего состояния котла (вкл/выкл) для фиксирования времени срабатывания
//бит "3"-для контроля появления новых данных в серийном порту (меняет значение один раз за период поступления данных из порта)
//бит "4"-для фиксации момента появления ошибки значения с датчика температуры воды на выходе с теплоаккумулятора (большое отклонение температуры текущего значения по отношению к предыдущему)
//бит "5"-для фиксации момента появления ошибки значения с датчика температуры в комнате (большое отклонение температуры текущего значения по отношению к предыдущему)
//бит "6"-для фиксации момента появления ошибки значения с датчика температуры котловой воды (большое отклонение температуры текущего значения по отношению к предыдущему)
//бит "7"-для фиксации момента появления ошибки значения с датчика температуры на улице (большое отклонение температуры текущего значения по отношению к предыдущему)
byte in1[80]; //массив для графика - температура в комнате (запись значения один раз в 9 минут)
byte in2[240]; //массив для графика - температура воды на выходе из ГР (запись значения один раз в 3 минуты)
char in3[80] = { -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60}; //массив для графика - температура на улице (запись значения один раз в 9 минут)
byte in5[80]; //массив для графика - температура воды на выходе из теплоаккумулятора (запись значения один раз в 9 минут)
int trend_kot[15]; //массив для отображения графика работы котла
int trend_rad_p[15]; //массив для отображения графика работы насоса радиаторов
int trend_floor_p[15]; //массив для отображения графика работы насоса тёплых полов
int trend_tenGR_25[15]; //массив для отображения графика работы ТЭНа гидроразделителя 2,5 кВт
int trend_tenTA_75[15]; //массив для отображения графика работы ТЭНа теплоаккумулятора 7,5 кВт
int trend_shar_kr[15]; //массив для отображения графика работы шарового крана на выходе из теплоаккумулятора
int trend_min_Pr[15]; //массив для отображения графика минимального давления
int trend_max_Pr[15]; //массив для отображения графика максимального давления
int tempRoomPrev = 0; //переменная для фиксации предыдущего значения температуры в комнате
int tempWaterPrev = 0; //переменная для фиксации предыдущего значения температуры котловой воды
int tempOutdoorPrev = 0; //переменная для фиксации предыдущего значения температуры на улице
int tempTAPrev = 0; //переменная для фиксации предыдущего значения температуры воды на выходе из теплоаккумулятора
int tempRoomBefore = 0; //"промежуточное" значение температуры (для обработки на наличие ошибки)
int tempWaterBefore = 0; //"промежуточное" значение температуры (для обработки на наличие ошибки)
int tempOutdoorBefore = 0; //"промежуточное" значение температуры (для обработки на наличие ошибки)
int tempTABefore = 0; //"промежуточное" значение температуры (для обработки на наличие ошибки)
byte hoursLook = 0; //для отображения текущего значения в процедурах
byte minutesLook = 0; //для отображения текущего значения в процедурах
//относится к получению данных из серийного порта:
String inString;
String message;
//массивы с информацией об ошибках
char beforeTempEror[14] = {0, 0, 0, 0, 0, 0, 0, 0}; //массив для записей значений в момент ошибки (показание, предшествовавшее ошибке)
char afterTempEror[14] = {0, 0, 0, 0, 0, 0, 0, 0}; //массив для записей значений в момент ошибки (показание, после выявленной ошибки)
byte hourEror[14]; //массив времени (часы) для фиксации момента ошибки
byte minutEror[14]; //массив времени (минуты) для фиксации момента ошибки
byte kindEror[14]; //массив указатель на источник ошибки
//kindEror=1 - ошибка температуры в помещении
//kindEror=2 - ошибка температуры воды в ГР
//kindEror=3 - ошибка температуры на улице
//kindEror=4 - минимальное давление в системе
//kindEror=5 - ошибка температуры воды на выходе из теплоаккумулятора
//kindEror=6 - максимальное давление в системе
byte option = 0; //переменная для определения (задания), какая опция регулирования активна (в работе) в данный момент:
//0 - "Out of home" режим "вне дома";
//1 - "Heat Pump" режим работы с тепловым насосом;
//2 - "Room temp." режим поддержания заданной температуры воздуха в комнате;
//3 - "Timer" режим отложенного пуска системы. Включается при достижении временем заданного значения;
//32 - "Room temp. by Timer" режим прогрева помещения при срабатывании таймера;
//4 - "Stop" - режим полной остановки, насосов и оборудования (при ненормальном давлении в системе)
//5 - "Floor" - прогрев пола в межсезонье
//6 - "el Room t." - поддержание температуры в помещении только за счёт электричества (без включения дизельного котла)
//7 - "Test Pumps" - режим тестирования насосов (прокрутка всех насосов одновременно, против прикипания)
//8 - "Out H" - опция аналогична опции "вне дома", но в период с 12 до 16 часов происходит нагрев бака-аккумулятора до заданной температуры
//Код для вырисовывания различных меню (окон):
void getMenu_Main() //Главное окно (главное меню дисплея)
{
a = 0;
tft.fillScreen(WHITEGREY); //очистка диспея
getMyButton(2, 268, 60, "C"); //вырисовывание сенсорной кнопки "С"
tft.drawCircle(52, 283, 4, WHITE);//вырисовывание значка градуса
getMyButton(121, 268, 0, ""); //вырисовывание сенсорной кнопки "Меню"
tft.fillRect(156, 278, 47, 3, WHITE);//вырисовывание линий значка "Меню"
tft.fillRect(156, 291, 47, 3, WHITE);//вырисовывание линий значка "Меню"
tft.fillRect(156, 304, 47, 3, WHITE);//вырисовывание линий значка "Меню"
tft.setCursor(10, 5); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK); // Указываем цвет текста
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
tft.print("Opt:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.setCursor(10, 53); // Устанавливаем курсор (X = , Y = )
tft.print("Room:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.setCursor(10, 85); // Устанавливаем курсор (X = , Y = )
tft.print("Outdoor:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.setCursor(10, 117); // Устанавливаем курсор (X = , Y = )
tft.print("Time:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.setCursor(10, 149); // Устанавливаем курсор (X = , Y = )
tft.println("Flo. p.:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println(" Rad. p.:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println(" Kotel:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println(" TEN 2.5:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println(" TEN 7.5:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(" Shar. kr.:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
getIndicationMenu_Main(); //динамические данные главного окна
delay (300);
}
void getIndicationMenu_Main() //процедура вывода динамических данных в главном меню
{
tft.fillRect(100, 5, 110, 16, WHITEGREY);//закрашивание области указания текущей опции
tft.setCursor(100, 5); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK); // Указываем цвет текста
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
getIndicationOption(); //отображаем текущий режим работы (option)
//температуры:
tft.fillRect(140, 46, 75, 102, WHITEGREY);//закрашивание области указания температур
//температура воздуха в помещении:
if (f >= 20) //если с критической ошибкой
getIndicationTemp(140, 46, 3, tempRoomBefore, WHITE, RED); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
else
getIndicationTemp(140, 46, 3, tempRoomBefore, BLUE, WHITEGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
//температура воздуха на улице:
if (m >= 20) //если с критической ошибкой
getIndicationTemp(140, 78, 3, tempOutdoorBefore, WHITE, RED); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
else
getIndicationTemp(140, 78, 3, tempOutdoorBefore, BLACK, WHITEGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
//время:
tft.setCursor(80, 110); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
getTimeDispley(hoursLook);
tft.print(":"); //двоеточие для времени
getTimeDispley(minutesLook);
//отображение состояния насосов, котла, водонагревателя (вкл/выкл):
getIndication(0, 157);
getIndication(1, 173);
getIndication(2, 189);
getIndication(3, 205);
getIndication(4, 221);
getIndication(5, 237);
if (kindEror[9] != 0 or kindEror[13] != 0) //индикация получения значения от датчиков с ошибкой
{
tft.setCursor(90, 248); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
if (kindEror[9] != 0 and kindEror[13] == 0)
tft.setTextColor(WHITE, DARKGREEN); // Указываем цвет текста
else if (kindEror[13] != 0)
tft.setTextColor(WHITE, RED); // Указываем цвет текста
tft.print (" eror ");
}
}
void getIndicationOption() //индикация, какая опция в работе
{
if (option == 0)
tft.print("Out");
else if (option == 1)
tft.print("Heat Pump");
else if (option == 2)
tft.print("Room t.");
else if (option == 3)
{
tft.print("Timer ");
tft.print(b);
tft.print(":");
tft.print(c);
}
else if (option == 32)
tft.print("by Timer");
else if (option == 4)
tft.print("Stop");
else if (option == 5)
tft.print("Floor");
else if (option == 6)
tft.print("el Room t.");
else if (option == 7)
tft.print("Test Pumps");
else if (option == 8)
tft.print("Out H");
}
void getMenu_Temp() //меню (окошко) выставления требуемой температуры воздуха в помещении
{
a = 1;
tft.fillScreen(WHITEGREY); //очистка диспея
getMyButton(2, 268, 15, "Exit"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, 30, "OK"); //вырисовывание сенсорной кнопки
getMyButton(121, 168, 45, "v"); //вырисовывание сенсорной кнопки
getMyButton(121, 116, 45, "^"); //вырисовывание сенсорной кнопки
getLookTemp();
delay (300);
}
void getMenu_Other() //окошко (меню) имеющее сенсорные кнопки (ссылки) на другие требуемые меню
{
a = 2;
tft.fillScreen(WHITEGREY); //очистка диспея
getMyButton(2, 268, 15, "Exit"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, 30, "Out"); //вырисовывание сенсорной кнопки
getMyButton(2, 134, 15, "Graph"); //вырисовывание сенсорной кнопки
getMyButton(121, 134, 15, "Erors"); //вырисовывание сенсорной кнопки
getMyButton(2, 1, 15, "HeatP"); //вырисовывание сенсорной кнопки
getMyButton(121, 1, 15, "Timer"); //вырисовывание сенсорной кнопки
getMyButton(121, 67, 15, "Floor"); //вырисовывание сенсорной кнопки
getMyButton(2, 67, 15, "el tR"); //вырисовывание сенсорной кнопки
getMyButton(2, 201, 15, "TestP"); //вырисовывание сенсорной кнопки
getMyButton(121, 201, 15, "Out H"); //вырисовывание сенсорной кнопки
delay (300);
}
void getMenu_Timer() //окошко (меню) задания времени включения системы (отложенный старт)
{
a = 3;
tft.fillScreen(WHITEGREY); //очистка диспея
getMyButton(2, 268, 15, "Exit"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, 30, "OK"); //вырисовывание сенсорной кнопки
getMyButton(0, 168, 45, "v"); //вырисовывание сенсорной кнопки
getMyButton(0, 116, 45, "^"); //вырисовывание сенсорной кнопки
getMyButton(121, 168, 45, "v"); //вырисовывание сенсорной кнопки
getMyButton(121, 116, 45, "^"); //вырисовывание сенсорной кнопки
getLookDate();
delay (300);
}
void getMenu_Eror() //меню (окошко) отображения ошибок температур
{
a = 7;
tft.fillScreen(BLACK); //очистка диспея
tft.setCursor(0, 0); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(YELLOW); // Указываем цвет текста
tft.println(" Mist.:");
getPrintKindEror (0, 9);
tft.setCursor(0, 184); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(RED); // Указываем цвет текста
tft.println(" Erors:");
getPrintKindEror (10, 13);
getMyButton(2, 268, 15, "Exit"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, 15, "Clear"); //вырисовывание сенсорной кнопки
delay (300);
}
void getPrintKindEror (byte a, byte b)
{
for (byte i = a; i <= b; i++)
{
getTimeDispley(hourEror[i]);
tft.print(":");
getTimeDispley(minutEror[i]);
if (kindEror[i] == 1)
tft.print(" Ro ");
else if (kindEror[i] == 2)
tft.print(" Wa ");
else if (kindEror[i] == 3)
tft.print(" Ou ");
else if (kindEror[i] == 4)
tft.print(" minPr ");
else if (kindEror[i] == 5)
tft.print(" TA ");
else if (kindEror[i] == 6)
tft.print(" maxP ");
tft.print(beforeTempEror[i], DEC);
tft.print(" to ");
tft.println(afterTempEror[i], DEC);
}
}
void drawTrends()
{
tft.fillScreen(BLACK);
//сетка по времени:
for (byte i = 1; i < 12; i++)
tft.drawFastVLine((20 * i - 1), 10, 291, DARKGREY);
//серая сетка по шкале температур:
for (byte i = 0; i < 5; i++)
tft.drawFastHLine(0, (68 + 40 * i), 240, DARKGREY);
//синие горизонтали по шкале температур (от 40 до 70 градусов):
for (byte i = 0; i < 4; i++)
tft.drawFastHLine(0, (18 + 10 * i), 240, BLUE);
//синие горизонтали по шкале температур (от 0 до 40 градусов):
for (byte i = 0; i < 4; i++)
tft.drawFastHLine(0, (88 + 40 * i), 240, BLUE);
tft.setCursor(0, 21); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(RED, BLACK); // Указываем цвет текста
tft.print("60");
tft.setCursor(0, 41); // Устанавливаем курсор (X = , Y = )
tft.print("40");
tft.setCursor(0, 81); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(MAGENTA, BLACK); // Указываем цвет текста
tft.print("30");
tft.setCursor(0, 121); // Устанавливаем курсор (X = , Y = )
tft.print("20");
tft.setCursor(0, 161); // Устанавливаем курсор (X = , Y = )
tft.print("10");
tft.setCursor(0, 201); // Устанавливаем курсор (X = , Y = )
tft.print("0");
//построение графика температуры воздуха на улице
getGraphics(2, 0, 148, MAGENTA, 80, in3, 0);
//построение графика температуры воздуха в комнате
getGraphics(2, in1, 208, GREEN, 80, 0, 0);
//построение графика температуры воды на выходе из теплоаккумулятора
getGraphics(2, in5, 208, CYAN, 80, 0, 0);
//построение графика температуры котловой воды (поверх других графиков)
getGraphics(0, 0, 208, RED, 240, 0, in2);
for (byte i = 0; i < 15; i++)
{
for (byte j = 0; j < 16; j++)
{
if (bitRead(trend_kot[i], j) == 1)
tft.drawPixel ((i * 16 + (15 - j)), 248, YELLOW);
if (bitRead(trend_rad_p[i], j) == 1)
tft.drawPixel ((i * 16 + (15 - j)), 258, YELLOW);
if (bitRead(trend_floor_p[i], j) == 1)
tft.drawPixel ((i * 16 + (15 - j)), 268, YELLOW);
if (bitRead(trend_tenGR_25[i], j) == 1)
tft.drawPixel ((i * 16 + (15 - j)), 278, YELLOW);
if (bitRead(trend_tenTA_75[i], j) == 1)
tft.drawPixel ((i * 16 + (15 - j)), 288, YELLOW);
if (bitRead(trend_shar_kr[i], j) == 1)
tft.drawPixel ((i * 16 + (15 - j)), 298, YELLOW);
if (bitRead(trend_min_Pr[i], j) == 1)
tft.drawPixel ((i * 16 + (15 - j)), 192, YELLOW);
if (bitRead(trend_max_Pr[i], j) == 1)
tft.drawPixel ((i * 16 + (15 - j)), 152, YELLOW);
}
}
tft.setCursor(0, 310); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(1); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(YELLOW); // Указываем цвет текста
tft.print((10 * e / 124) / 10);
tft.print(".");
tft.print((10 * e / 124) % 10);
tft.print(" L ");
tft.print((TEN25_kWh + TEN75_kWh * 3) / 144);
tft.print(" kWh ");
tft.print(k / 10);
tft.print(" C ");
tft.setTextColor(RED); // Указываем цвет текста
tft.print(p / 10);
tft.print(".");
tft.print(p % 10);
tft.print(" L ");
tft.print(Last_kWh);
tft.print(" kWh ");
tft.print(r / 10);
tft.print(" C");
getIndicationDrawTrends();
}
void getGraphics(byte a, byte b[80], byte c, uint16_t d, byte e, char f[80], byte g[240]) //процедура построения графиков
{
byte oldX = a;
if (b == in1 or b == in5)
tft.drawLine(0, (c - b[0]), oldX, (c - b[0]), d); //дополнительный отрезок от Х=0 до Х=2
else if (f == in3)
tft.drawLine(0, (c - f[0]), oldX, (c - f[0]), d); //дополнительный отрезок от Х=0 до Х=2
for (byte x = 1; x < e; x++)
{
byte nxt_x;
if (b == in1 or b == in5 or f == in3)
nxt_x = (3 * x + 2);
else if (g == in2)
nxt_x = x;
if (b == in1 or b == in5)
tft.drawLine(oldX, (c - b[x - 1]), nxt_x, (c - b[x]), d);
else if (f == in3)
tft.drawLine(oldX, (c - f[x - 1]), nxt_x, (c - f[x]), d);
else if (g == in2)
tft.drawLine(oldX, (c - g[x - 1]), nxt_x, (c - g[x]), d);
oldX = nxt_x;
}
}
void getIndicationDrawTrends()
{
tft.fillRect(0, 0, 240, 8, BLACK);//закрашивание области вывода значений в верхней части экрана
//отображение температур с датчиков
//температура в помещении:
if (f >= 20)
getIndicationTemp(0, 0, 1, tempRoomBefore, YELLOW, DARKGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
else
getIndicationTemp(0, 0, 1, tempRoomBefore, GREEN, BLACK); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
//температура на улице:
if (m >= 20)
getIndicationTemp(35, 0, 1, tempOutdoorBefore, YELLOW, DARKGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
else
getIndicationTemp(35, 0, 1, tempOutdoorBefore, MAGENTA, BLACK); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
//температура котловой воды:
if (h >= 20)
getIndicationTemp(70, 0, 1, tempWaterBefore, YELLOW, DARKGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
else
getIndicationTemp(70, 0, 1, tempWaterBefore, RED, BLACK); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
//температура воды на выходе с теплоаккумулятора:
if (g >= 20)
getIndicationTemp(105, 0, 1, tempTABefore, YELLOW, DARKGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
else
getIndicationTemp(105, 0, 1, tempTABefore, CYAN, BLACK); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
tft.setCursor(170, 0); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(WHITE); // Указываем цвет текста
getIndicationOption(); //отображаем текущий режим работы (option)
//отображение состояния исполнительных механизмов (изменение цвета текста)
//состояние котла:
getIndicationTrend(250, 2, "Kot"); //координата начала текста Y=, номер соответствующего бита "output", название тренда
//состояние насоса радиаторов:
getIndicationTrend(260, 1, "Rad"); //координата начала текста Y=, номер соответствующего бита "output", название тренда
//состояние насоса тёплых полов:
getIndicationTrend(270, 0, "Floor"); //координата начала текста Y=, номер соответствующего бита "output", название тренда
//состояние ТЭНа 2,5 кВт:
getIndicationTrend(280, 3, "TEN 25"); //координата начала текста Y=, номер соответствующего бита "output", название тренда
//состояние ТЭНа 7,5 кВт:
getIndicationTrend(290, 4, "TEN 75"); //координата начала текста Y=, номер соответствующего бита "output", название тренда
//состояние шарового крана теплоаккумулятора:
getIndicationTrend(300, 5, "Shar"); //координата начала текста Y=, номер соответствующего бита "output", название тренда
}
void getIndicationTrend(int y3, byte a3, String z3) //отображение состояния исполнительных механизмов при прорисовке трендов
{
tft.setCursor(0, y3); // Устанавливаем курсор (X = , Y = )
if (bitRead(output, a3) == 1) //если шаровой кран открыт
tft.setTextColor(RED); // Указываем цвет текста
else if (bitRead(output, a3) == 0) //если шаровой кран закрыт
tft.setTextColor(BLUE); // Указываем цвет текста
tft.print(z3);
}
void getIndicationTemp(byte x4, int y4, byte a4, int b4, uint16_t c4, uint16_t d4)
{
tft.setCursor(x4, y4); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(a4); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(c4, d4); // Указываем цвет текста
if (b4<0 and b4> -10) //записываю знак "-" в диапазоне от 0 до -1
{
tft.print("-0.");
tft.print(-b4 % 10);
}
else
{
tft.print(b4 / 10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(".");
if (b4 >= 0)
tft.print(b4 % 10);
else if (b4 < 0)
tft.print(-b4 % 10);
}
}
void initTachScr() //инициализация тачскрина и действия по нажатию сенсорных кнопок в различных окнах
{
TSPoint p = ts.getPoint();
// if sharing pins, you'll need to fix the directions of the touchscreen pins
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
// we have some minimum pressure we consider 'valid'
// pressure of 0 means no pressing!
if (p.z > MINPRESSURE && p.z < MAXPRESSURE)
{
int tmp = p.x; //запись значения p.x в переменную tmp
p.x = p.y; //переназначение p.x на p.y
p.y = tmp; //присвоение p.y значения p.x (через tmp)
//Serial.println((String) "X="+p.x+",\tY="+p.y); //Выводим «сырые» показания TouchScreen
// scale from 0->1023 to tft.width i.e. left = 0, rt = width
// most mcufriend have touch (with icons) that extends below the TFT
// screens without icons need to reserve a space for "erase"
p.x = map(p.x, 893, 145, 0, tft.width());
p.y = map(p.y, 930, 135, 0, tft.height());
//Serial.println((String) "("+p.x+","+p.y+")"); //Выводим преобразованные показания TouchScreen
//tft.drawCircle(p.x, p.y, 3, RED); //Прорисовываем окружность диаметром 3 пикселя с центром в точке координат считанных с TouchScreen
}
if (a == 0)
{
if (p.x >= 0 and p.x <= 119 and p.y >= 320 and p.y <= 350)
getMenu_Temp();
if (p.x >= 120 and p.x <= 239 and p.y >= 320 and p.y <= 350)
getMenu_Other();
}
else if (a == 1)
{
if (p.x >= 0 and p.x <= 119 and p.y >= 267 and p.y <= 319)
getMenu_Main();
if (p.x >= 120 and p.x <= 239 and p.y >= 267 and p.y <= 319)
{
option = 2;
to_Tertn_Off_All();
d = 0;
getMenu_Main();
}
if (p.x >= 120 and p.x <= 239 and p.y > 167 and p.y < 220)
{
if (t > 40)
t = t - 5;
else
t = 40;
getLookTemp();
delay(300);
}
if (p.x >= 120 and p.x <= 239 and p.y > 115 and p.y < 167)
{
if (t < 250)
t = t + 5;
else
t = 250;
getLookTemp();
delay(300);
}
}
else if (a == 2)
{
if (p.x >= 0 and p.x <= 119 and p.y >= 267 and p.y <= 319)
getMenu_Main();
if (p.x >= 120 and p.x <= 239 and p.y >= 267 and p.y <= 319)
{
option = 0;
d = 0;
getMenu_Main();
}
if (p.x >= 0 and p.x <= 119 and p.y >= 0 and p.y <= 51)
{
option = 1;
d = 0;
to_Tertn_Off_All();
bitClear(output, 0); //выключить насос тёплых полов
getMenu_Main();
}
if (p.x >= 120 and p.x <= 239 and p.y >= 0 and p.y <= 51)
getMenu_Timer();
if (p.x >= 0 and p.x <= 119 and p.y > 133 and p.y < 184)
{
a = 4;
drawTrends();
delay (300);
}
if (p.x >= 120 and p.x <= 239 and p.y > 133 and p.y < 184)
getMenu_Eror();
if (p.x >= 120 and p.x <= 239 and p.y > 66 and p.y < 117)
{
option = 5; //переход в режим нагрева полов в межсезонье
FloorHour = hoursLook;
d = 0;
getMenu_Main();
}
if (p.x >= 0 and p.x <= 119 and p.y > 66 and p.y < 117)
{
option = 6; //переход в режим поддержания температуры в помещении только за счёт электричества (без включения дизельного котла)
d = 0;
to_Tertn_Off_All();
getMenu_Main();
}
if (p.x >= 0 and p.x <= 119 and p.y > 200 and p.y < 251)
{
option = 7; //переход в режим тестирования насосов (прокрутка всех насосов против прикипания)
d = 0;
to_Tertn_Off_All();
getMenu_Main();
}
if (p.x >= 120 and p.x <= 239 and p.y > 200 and p.y < 251)
{
option = 8; //переход в режим "вне дома" с функцией подогрева бака-аккумулятора в дневное время
d = 0;
to_Tertn_Off_All();
getMenu_Main();
}
}
else if (a == 3)
{
if (p.x >= 0 and p.x <= 119 and p.y >= 267 and p.y <= 319)
getMenu_Other();
if (p.x >= 120 and p.x <= 239 and p.y >= 267 and p.y <= 319)
{
option = 3;
d = 0;
getMenu_Main();
}
if (p.x >= 0 and p.x <= 119 and p.y > 115 and p.y < 167)
{
if (b < 23)
b = b + 1;
else
b = 23;
getLookDate();
delay(300);
}
if (p.x >= 0 and p.x <= 119 and p.y > 167 and p.y < 219)
{
if (b > 0)
b = b - 1;
else
b = 0;
getLookDate();
delay(300);
}
if (p.x >= 120 and p.x <= 239 and p.y > 115 and p.y < 167)
{
if (c < 50)
c = c + 10;
else
c = 50;
getLookDate();
delay(300);
}
if (p.x >= 120 and p.x <= 239 and p.y > 167 and p.y < 219)
{
if (c > 0)
c = c - 10;
else
c = 0;
getLookDate();
delay(300);
}
}
else if (a == 4 and p.x >= 0 and p.x <= 239 and p.y >= 320 and p.y <= 350)
getMenu_Main();
else if (a == 7)
{
if (p.x >= 0 and p.x <= 119 and p.y >= 267 and p.y <= 319)
getMenu_Other();
if (p.x >= 120 and p.x <= 239 and p.y >= 267 and p.y <= 319)
{
for (byte i = 0; i <= 13; i++)
{
beforeTempEror[i] = 0;
afterTempEror[i] = 0;
hourEror[i] = 0;
minutEror[i] = 0;
kindEror[i] = 0;
}
bitClear(prev, 4);
bitClear(prev, 5);
bitClear(prev, 6);
bitClear(prev, 7);
f = 0;
h = 0;
m = 0;
g = 0;
u = 0;
maxPres = 0;
noHeat = 0;
tempRoomPrev = 0;
tempWaterPrev = 0;
tempOutdoorPrev = 0;
tempTAPrev = 0;
getMenu_Eror();
}
}
}
void getErorAll(byte a2, byte b2, byte c2, int d2, int e2, byte f2, byte g2, byte h2) //процедура заполнения массивов ошибок (аварии)
{
for (byte i = a2; i <= b2; i++) //перезаписываются предыдущие ошибки (освобождая место для записи новой ошибки)
{
beforeTempEror[i] = beforeTempEror[i + 1];
afterTempEror[i] = afterTempEror[i + 1];
hourEror[i] = hourEror[i + 1];
minutEror[i] = minutEror[i + 1];
kindEror[i] = kindEror[i + 1];
}
beforeTempEror[c2] = d2;
afterTempEror[c2] = e2;
kindEror[c2] = f2;
hourEror[c2] = g2;
minutEror[c2] = h2;
}
//вырисовывание сенсорных кнопок:
void getMyButton(byte x1, int y1, byte x2, String z1)
{
tft.fillRoundRect (x1, y1, 117, 50, 15, BLUE); //залитый прямоугольник со скруглёнными углами
y1 = y1 + 13;
tft.setCursor(x1 + x2, y1); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(WHITE); // Указываем цвет текста
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print(z1); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
//выводим значение заданной температуры (в меню настройки задания):
void getLookTemp()
{
tft.fillRect (30, 160, 70, 24, WHITEGREY);
getIndicationTemp(30, 160, 3, (t + 50), BLACK, WHITEGREY); //координата X, координата Y, размер текста, название выводимой переменной (225=>22,5), цвет текста, фон текста
}
//выводим значение заданного времени включения по таймеру (в меню таймера):
void getLookDate()
{
tft.fillRect (75, 50, 90, 24, WHITEGREY);
tft.setCursor(75, 50); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK); // Указываем цвет текста
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print(b); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(":"); //значок двоеточия для времени
tft.print(c); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
void getTimeDispley(byte unit) //процедура вывода времени на дисплей
{
if (unit < 10)
{
tft.print("0"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(unit); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
else
tft.print(unit); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
void getIndication(byte bitNomber, byte y) //индикация состояния исполнительных механизмов на дисплее в главном меню
{
tft.fillCircle(140, y, 8, WHITEGREY); //закрашивается место индикации
if (bitRead(output, bitNomber) == 1)
{
tft.drawCircle(140, y, 7, BLACK); //рисуется черная окружность
tft.fillCircle(140, y, 5, RED); //закрашивается центр окружности красным цветом
}
else if (bitRead(output, bitNomber) == 0)
tft.drawCircle(140, y, 7, BLACK); //рисуется черная окружность
}
void timeCounter(byte x) //отсчёт времени при смене режимов
{
if (w != x)//если предыдущий режим был другим
v = 0;
w = x; //значению w присвоился номер текущего режима
if (v < 250) //чтобы значение "v" не сбросилось на "0" при превышении максимального значения для "byte"
v++;
}
void findTempEror(byte a1, int b1, int c1, byte d1, byte e1, byte f1, byte g1, int h1, int k1)
{
//если предыдущее значение было нормальным (флаг не поднят) и поступившее значение нормальное либо если стартовое состояние (флаг не поднят) и стартовое значение:
if (bitRead(prev, a1) == 0 and ((b1 > h1 and b1 < k1 and (b1 - c1) <= 30 and (b1 - c1) >= -30) || c1 == 0))
{
if (a1 == 5)
tempRoomPrev = b1;
else if (a1 == 6)
tempWaterPrev = b1;
else if (a1 == 4)
tempTAPrev = b1;
else if (a1 == 7)
tempOutdoorPrev = b1;
}
//если предыдущее значение было ненормальным (флаг поднят), количество отклонений не превысило значение 9 и пришло нормальное значение:
else if (bitRead(prev, a1) == 1 and d1<10 and b1 > h1 and b1 < k1 and (b1 - c1) <= 30 and (b1 - c1) >= -30)
{
bitClear(prev, a1); //записался бит равный 0 (для сброса счётчика приёма некоректного значения температуры)
if (a1 == 5)
{
tempRoomPrev = b1;
f = 0;
}
else if (a1 == 6)
{
tempWaterPrev = b1;
h = 0;
}
else if (a1 == 4)
{
tempTAPrev = b1;
g = 0;
}
else if (a1 == 7)
{
tempOutdoorPrev = b1;
m = 0;
}
}
//если предыдущее значение было ненормальным (флаг поднят) и пришло ненормальное значение:
else if (bitRead(prev, a1) == 1 and (b1 <= h1 || b1 >= k1 || (b1 - c1) > 30 || (b1 - c1) < -30))
{
if (d1 < 10)
{
if (a1 == 5)
{
tempRoomBefore = c1;
f++;
}
else if (a1 == 6)
{
tempWaterBefore = c1;
h++;
}
else if (a1 == 4)
{
tempTABefore = c1;
g++;
}
else if (a1 == 7)
{
tempOutdoorBefore = c1;
m++;
}
}
else if (d1 >= 10 and d1 < 20) //если пришло 10 отклонившихся значений подряд
{
getErorAll(10, 12, 13, c1 / 10, b1 / 10, e1, f1, g1);
if (a1 == 5)
f = 20;
else if (a1 == 6)
h = 20;
else if (a1 == 4)
g = 20;
else if (a1 == 7)
m = 20;
}
}
//если предыдущее значение было нормальным (флаг не поднят) и пришло ненормальное значение:
else if (bitRead(prev, a1) == 0 and (b1 <= h1 || b1 >= k1 || (b1 - c1) > 30 || (b1 - c1) < -30))
{
getErorAll(0, 8, 9, c1 / 10, b1 / 10, e1, f1, g1);
if (a1 == 5)
{
tempRoomBefore = c1;
f++;
}
else if (a1 == 6)
{
tempWaterBefore = c1;
h++;
}
else if (a1 == 4)
{
tempTABefore = c1;
g++;
}
else if (a1 == 7)
{
tempOutdoorBefore = c1;
m++;
}
bitSet(prev, a1); //записался бит равный 1 (фиксация сильного отклонения температуры)
}
}
void to_Tertn_Off_All() //выключить всё (отключить катушки всех управляющих реле)
{
if (tempWaterBefore < 320) //если вода в гидроразделителе остыла
s++;
else
s = 0;
if (s >= 30)
{
bitClear(output, 0); //выключить насос тёплых полов
s = 0;
}
bitClear(output, 1); //выключить насос радиаторов
bitClear(output, 2); //выключить котёл
bitClear(output, 3); //выключить ТЭН 2,5 кВт
bitClear(output, 4); //выключить ТЭН 7,5 кВт
bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
}
void setup()
{
Serial.begin(9600);
tft.reset();
tft.begin(0); //включение дисплея
tft.fillScreen(WHITEGREY); //очистка диспея
getMenu_Main(); //при загрузке - на экран выводится главное окно (меню)
delay(3000); //задержка 3 сек
}
void loop() //главный повторяющийся цикл
{
initTachScr(); //инициализация тачскрина и действия по нажатию сенсорных кнопок в различных окнах
bitSet(prev, 3); //записался бит номер 3 равный 1 (для фиксирования момента получения данных через серийный порт)
//принимаем строку из порта:
while (Serial.available())
{
char inChar = Serial.read();
inString += inChar;
if (inChar == 'E')
{
//Serial.println(inString); // !!! закомментировать в конечном варианте
message = inString;
inString = "";
bitClear(prev, 3); //записался бит номер 3 равный 0 (для фиксирования момента получения данных через серийный порт)
}
}
byte Index1 = message.indexOf(';'); //разделительный символ
byte Index2 = message.indexOf(';', Index1 + 1); //вычисляем второй символ
byte Index3 = message.indexOf(';', Index2 + 1); //...третий и т.д.
byte Index4 = message.indexOf(';', Index3 + 1);
byte Index5 = message.indexOf(';', Index4 + 1);
byte Index6 = message.indexOf(';', Index5 + 1);
byte Index7 = message.indexOf(';', Index6 + 1);
String Val_1 = message.substring(0, Index1);//что-то перед первым разд. сим.
String Val_2 = message.substring(Index1 + 1, Index2); //что-то за первым разд. сим.
String Val_3 = message.substring(Index2 + 1, Index3); //за вторым и т.д
String Val_4 = message.substring(Index3 + 1, Index4);
String Val_5 = message.substring(Index4 + 1, Index5);
String Val_6 = message.substring(Index5 + 1, Index6);
String Val_7 = message.substring(Index6 + 1, Index7);
int tempRoom = Val_1.toInt();
int tempWater = Val_2.toInt();
int tempTA = Val_3.toInt(); //значение температуры на выходе из теплоакумулятора
int tempOutdoor = Val_4.toInt();
if (Val_4.toInt() == 0) //исключение значения "0"
tempOutdoor = 1;
byte hours = Val_5.toInt();
byte minutes = Val_6.toInt();
byte pressure = Val_7.toInt();
if (bitRead(prev, 3) == 0) //если поступили данные через порт
{
tempRoomBefore = tempRoom; //"промежуточное" значение температуры (для обработки на наличие ошибки)
tempWaterBefore = tempWater; //"промежуточное" значение температуры (для обработки на наличие ошибки)
tempOutdoorBefore = tempOutdoor; //"промежуточное" значение температуры (для обработки на наличие ошибки)
tempTABefore = tempTA; //"промежуточное" значение температуры (для обработки на наличие ошибки)
//Регистрация ошибки с датчиков температуры и пропуск одиночных ошибок
//для температуры в комнате:
findTempEror(5, tempRoomBefore, tempRoomPrev, f, 1, hours, minutes, 0, 400); //номер бита "prev", температура обрабатываемая, температура предыдущая, счётчик, указание на вид ошибки "kindEror", час, минуты, минимально возможное значение, максимально возможное значение
//для температуры котловой воды:
findTempEror(6, tempWaterBefore, tempWaterPrev, h, 2, hours, minutes, 0, 950); //номер бита "prev", температура обрабатываемая, температура предыдущая, счётчик, указание на вид ошибки "kindEror", час, минуты, минимально возможное значение, максимально возможное значение
//для температуры воды на выходе из теплоаккумулятора:
findTempEror(4, tempTABefore, tempTAPrev, g, 5, hours, minutes, 0, 950); //номер бита "prev", температура обрабатываемая, температура предыдущая, счётчик, указание на вид ошибки "kindEror", час, минуты, минимально возможное значение, максимально возможное значение
//для температуры воздуха на улице:
findTempEror(7, tempOutdoorBefore, tempOutdoorPrev, m, 3, hours, minutes, -300, 500); //номер бита "prev", температура обрабатываемая, температура предыдущая, счётчик, указание на вид ошибки "kindEror", час, минуты, минимально возможное значение, максимально возможное значение
tempRoom = tempRoomBefore; //фиксируется значение температуры после обработки на наличие ошибки
tempWater = tempWaterBefore; //фиксируется значение температуры после обработки на наличие ошибки
tempOutdoor = tempOutdoorBefore; //фиксируется значение температуры после обработки на наличие ошибки
tempTA = tempTABefore; //фиксируется значение температуры после обработки на наличие ошибки
hoursLook = hours; //для использования значения в процедурах
minutesLook = minutes; //для использования значения в процедурах
//Регистрация минимального значения давления и переход в режим "Stop"
if (pressure == 2 and option != 4) //если зафиксировано минимальное давление но ещё нет перехода в режим "Stop"
u++;
else
u = 0;
if (pressure == 2 and u >= 3 and option != 4) //если поступило подряд три сигнала о минимальном давлении
{
getErorAll(10, 12, 13, 4, 3, 4, hours, minutes);
option = 4; //переход в режим "Stop"
}
//Регистрация максимального значения давления
if (pressure == 3 and option != 4 and noHeat == 0) //если зафиксировано максимальное давление но ещё нет перехода в режим "Stop" и нет отключения нагревателей
maxPres++;
else
maxPres = 0;
if (pressure == 3 and maxPres >= 30 and noHeat == 0) //если поступило подряд 30 сигналов о максимальном давлении но ещё нет отключения нагревателей
noHeat = 1;
if (noHeat >= 180 and pressure == 3 and option != 4) //если обогрев отключен 30 минут подряд и давление не упало
{
getErorAll(10, 12, 13, 13, 14, 6, hours, minutes);
option = 4; //переход в режим "Stop"
}
else if (noHeat >= 180 and pressure != 3)
noHeat = 0;
//Действия в случае поступления критического количества ошибок с датчиков температуры и давления:
//Температура в помещении. Если флаг поднят и счётчик ошибок превысил значение 9:
if (bitRead(prev, 5) == 1 and f >= 10)
{
option = 0; //переход в режим "вне дома"
tempRoom = 80; //включатся насосы на циркуляцию
}
//Температура котловой воды. Если флаг поднят и счётчик ошибок превысил значение 9:
if (bitRead(prev, 6) == 1 and h >= 10)
{
option = 0; //переход в режим "вне дома"
tempWater = 80; //включатся насосы на циркуляцию
}
//Температура на улице. Если флаг поднят и счётчик ошибок превысил значение 9:
if (bitRead(prev, 7) == 1 and m >= 10)
{
option = 0; //переход в режим "вне дома"
tempOutdoor = 10; //не знаю, какое значение задать
}
//Температура воды на выходе из теплоаккумулятора. Если флаг поднят и счётчик ошибок превысил значение 9:
if (bitRead(prev, 4) == 1 and g >= 10)
{
option = 0; //переход в режим "вне дома"
tempTA = 80; //включатся насосы на циркуляцию
}
//задержка времени при ручном смене режима:
if ((d < 3 and option != 7) or (d < 18 and option == 7))
d++;
//фиксация "срабатывания" тепла, запасённого в теплоаккумуляторе с предыдущих суток:
if ((7 <= hours and hours < 23) and z != 0) //в 7 часов утра счётчик z сбрасывается на ноль, чтобы в 23 часа зафиксировать состояние теплоаккумулятора (осталась ли в нём тепловая энергия)
z = 0;
if (z == 0 and (hours < 7 or hours >= 23)) //момент обращения к теплоаккумулятору, чтобы выяснить его состояние
{
if (tempTA > 320) //если теплоаккумуляторе осталось тепло
z = 1;
else if (tempTA <= 320) //если всё тепло из теплоаккумулятора израсходовано
z = 2;
}
if (z == 1 and tempTA <= 320) //если была фиксация, что в теплоаккумуляторе осталось тепло, а затем было оно израсходовано
z = 2;
//Зависимость температуры уставки (верхняя граница) воды системы отопления от значения температуры наружного воздуха и температуры в помещении при работе котла:
byte tW = (55 - 1 * (tempOutdoor / 10) + ((t + 50) - tempRoom));
//Время (часы) включения ТЭН 7,5 кВт для накопления тепловой энергии в теплоаккумуляторе:
if (r < 60)
hour_TA = 2;
else
hour_TA = (r / 30 + 1);
//Код для программ (обций), алгоритм работы контроллера:
if (option != 1 and option != 2 and option != 32 and option != 4 and option != 6) //режим антизамерзания (работает всегда кроме режимов "Fast heat", "Room temp.", "Room temp. by Timer", "Stop")
{
if (tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) //циркуляция + подогрев
timeCounter(11);
if (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80)) //только циркуляция
timeCounter(12);
if (v >= 30 and (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and w == 11) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and w == 12)))
bitSet(output, 0); //включить насос тёплых полов
if (v >= 30 and (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and w == 11) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and w == 12)))
bitSet(output, 1); //включить насос радиаторов
if (v >= 30 and (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and w == 11) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and w == 12)))
bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
if (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and (tempWater > 350 or tempTA > 350)) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and v >= 30 and w == 12))
bitClear(output, 4); //выключить ТЭН 7,5 кВт
if (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and (tempWater > 350 or tempTA > 350)) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and v >= 30 and w == 12))
bitClear(output, 3); //выключить ТЭН 2,5 кВт
if (v >= 30 and (tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and w == 11 and tempWater <= 150 and tempTA <= 350)
bitSet(output, 2); //включить котёл
else if (((tempRoom <= 50 || tempWater <= 50 || tempTA <= 50) and (tempWater > 350 or tempTA > 350)) or (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80 || tempTA <= 80) and v >= 30 and w == 12))
bitClear(output, 2); //выключить котёл
}
//1. Опция "Out of home" ("вне дома"):
if (option == 0 and tempRoom > 80 and tempWater > 80 and tempTA > 80 and d >= 3)
to_Tertn_Off_All();
//2. Опция "Heat Pump" (тепловой насос):
if (option == 1 and d >= 3) //если выбран режим "Heat Pump"
{
if (tempOutdoor < 180)
{
if (tempRoom >= (t + 50)) //если температура в помещении больше или равна заданной (в любое время)
timeCounter(13);
if ((t + 50 - 5) <= tempRoom and tempRoom < (t + 50))
timeCounter(14);
if (tempRoom < (t + 50 - 5))
timeCounter(15);
if (v >= 30) //если сработал счётчик после смены режима
{
if (w != 13)
bitSet(output, 0); //включить насос тёплых полов
else if (w == 13)
bitClear(output, 0); //выключить насос тёплых полов
if (w == 15)
bitSet(output, 1); //включить насос радиаторов
else if (w == 13 or w == 14)
bitClear(output, 1); //выключить насос радиаторов
if (w != 13)//если не достигнута температура
bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
else if (w == 13)
bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
if (w == 15 and (hours < 7 or hours >= 23))
bitSet(output, 3); //включить ТЭН 2,5 кВт
else if ((7 <= hours and hours < 23) or w != 15)
bitClear(output, 3); //выключить ТЭН 2,5 кВт
}
}
else if (tempOutdoor >= 180)
timeCounter(18);
if (v >= 30 and w == 18)
to_Tertn_Off_All();
}
//3. Опция "Timer". Запуск системы в заданное время (работает в пределах текущих суток). На следующие сутки - поставить нельзя
if (option == 3 and d >= 3) //если выбран режим "таймер"
{
to_Tertn_Off_All();
if (hours >= b and minutes >= c)
option = 32; //переходим в режим срабатывания таймера
}
//4. Опция "Room temp. by Timer" (прогрев помещения тёплыми полами при срабатывании таймера)
if (option == 32)
{
if (tempRoom < (t + 50) and tempOutdoor < 180)
timeCounter(8);
if (v >= 30 and w == 8)
{
bitSet(output, 0); //включить насос тёплых полов
if (tempTA > 350)
bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
else if (hours < 7 or hours >= 23)
{
bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
bitSet(output, 3); //включить ТЭН 2,5 кВт
}
else
{
bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
if (tempWater / 10 < (tW - 10) and tempWater / 10 < 55)
bitSet(output, 2); //включить котёл
}
}
else if (tempRoom >= (t + 50) or tempOutdoor >= 180)
timeCounter(9);
if (v >= 30 and w == 9)
to_Tertn_Off_All();
if (((b == 20 and hours == 0) or (b == 21 and hours == 1) or (b == 22 and hours == 2) or (b == 23 and hours == 3) or (hours >= (b + 4))) and minutes >= c) //если прошло 4 часа
option = 0; //переход в режим "вне дома"
}
//5. Опция "Room temp." (поддержания температуры в комнате):
if (option == 2 and d >= 3)
{
if (tempOutdoor < 180)
{
if (tempRoom >= (t + 50)) //если температура в помещении больше или равна заданной (в любое время)
timeCounter(1);
if (7 <= hours and hours < 23) //в дневное время
{
if ((t + 50 - 5) <= tempRoom and tempRoom < (t + 50))
timeCounter(2);
if ((t + 50 - 10) < tempRoom and tempRoom < (t + 50 - 5))
timeCounter(10);
if (tempRoom <= (t + 50 - 10))
timeCounter(3);
}
if (hours < 7 or hours >= 23) //в ночное время
{
if ((t + 50 - 3) <= tempRoom and tempRoom < (t + 50)) //если температура в комнате ниже чем заданная, но выше либо равна чем заданная минус 0,3 градуса
{
if (w == 2 or w == 10 or w == 3) //для мгновенного перехода в 23 часа с дневного режима на ночной
{
w = 4;
v = 30;
}
timeCounter(4);
}
if ((t + 50 - 10) <= tempRoom and tempRoom < (t + 50 - 3))
{
if (w == 2 or w == 10 or w == 3) //для мгновенного перехода в 23 часа с дневного режима на ночной
{
w = 5;
v = 30;
}
timeCounter(5);
}
if (tempRoom < (t + 50 - 10))
{
if (w == 2 or w == 10 or w == 3) //для мгновенного перехода в 23 часа с дневного режима на ночной
{
w = 6;
v = 30;
}
timeCounter(6);
}
}
if (v >= 30) //если сработал счётчик после смены режима
{
if (w != 1)
bitSet(output, 0); //включить насос тёплых полов
else if (w == 1)
{
if (tempWater / 10 < 28 and bitRead(output, 3) == 0) //если вода в гидроразделителе остыла и ТЭН 2,5 кВт при этом выключен
s++;
else
s = 0;
if (s >= 30)
{
bitClear(output, 0); //выключить насос тёплых полов
s = 0;
}
}
if (w == 10 or w == 3 or w == 6 or (w == 5 and z == 1))
bitSet(output, 1); //включить насос радиаторов
else if (w == 1)
bitClear(output, 1); //выключить насос радиаторов
if ((tempTA > 320 and (w == 2 or w == 10 or w == 3 or (w == 4 and z == 1))) or w == 5 or w == 6)
bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
else
bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
if ((w == 5 and z == 2) or (tempTA < 800 and w == 6))
bitSet(output, 4); //включить ТЭН 7,5 кВт
else if ((w == 4 or w == 1) and (hours >= 23 or hours < hour_TA))
bitClear(output, 4); //выключить ТЭН 7,5 кВт
if (w == 6 or r < 40 or ((w == 4 or w == 5) and (z == 2 or (hours >= hour_TA and hours < 7))))
bitSet(output, 3); //включить ТЭН 2,5 кВт
else if ((((w == 4 and (hours >= 23 or hours < hour_TA)) and z == 1) or w == 1) and r >= 40)
bitClear(output, 3); //выключить ТЭН 2,5 кВт
if ((((w == 2 or w == 10) and tempTA <= 320) or w == 3 or w == 6) and (tempWater / 10 < (tW - 10) and tempWater / 10 < 55))
bitSet(output, 2); //включить котёл
else if (((w == 2 or w == 10) and tempTA > 330) or w == 4 or w == 5 or w == 1)
bitClear(output, 2); //выключить котёл
}
}
else if (tempOutdoor >= 180)
timeCounter(7);
if (v >= 30 and w == 7)
to_Tertn_Off_All();
if (hours < 7 and tempTA < 800 and hours >= hour_TA)
bitSet(output, 4); //включить ТЭН 7,5 кВт
}
//6. Опция "Stop" (выполняется при минимальном или максимальном давлении в системе):
if (option == 4)
{
to_Tertn_Off_All();
// дополнительное задание для моментального отключения насоса тёплых полов:
bitClear(output, 0); //выключить насос тёплых полов
}
//7. Опция "Floor" - прогрев пола в межсезонье:
if (option == 5 and d >= 3)
{
to_Tertn_Off_All();
if (hours == 23 or hours < 5)
FloorHour = 5;
if (hours == FloorHour and minutes < 15)
{
bitSet(output, 0); //включить насос тёплых полов
bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
}
else if (hours == FloorHour and minutes >= 15)
{
bitClear(output, 0); //выключить насос тёплых полов
bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
FloorHour++;
}
if (hours < 7 and hours >= 5 and tempTA < 800)
bitSet(output, 4); //включить ТЭН 7,5 кВт
}
//8. Опция "el Room t." (поддержания температуры в комнате без включения дизельного котла):
if (option == 6 and d >= 3)
{
if (tempOutdoor < 180)
{
if (tempRoom >= (t + 50)) //если температура в помещении больше или равна заданной (в любое время)
timeCounter(1);
if (7 <= hours and hours < 23) //в дневное время
{
if ((t + 50 - 5) <= tempRoom and tempRoom < (t + 50))
timeCounter(2);
if (tempRoom < (t + 50 - 5))
timeCounter(10);
}
if (hours < 7 or hours >= 23) //в ночное время
{
if ((t + 50 - 3) <= tempRoom and tempRoom < (t + 50)) //если температура в комнате ниже чем заданная, но выше либо равна чем заданная минус 0,3 градуса
{
if (w == 2 or w == 10) //для мгновенного перехода в 23 часа с дневного режима на ночной
{
w = 4;
v = 30;
}
timeCounter(4);
}
if (tempRoom < (t + 50 - 3))
{
if (w == 2 or w == 10) //для мгновенного перехода в 23 часа с дневного режима на ночной
{
w = 5;
v = 30;
}
timeCounter(5);
}
}
if (v >= 30) //если сработал счётчик после смены режима
{
if ((w != 1 and (hours < 7 or hours >= 23)) or (w != 1 and (tempTA > 330 and (7 <= hours and hours < 23))))
bitSet(output, 0); //включить насос тёплых полов
else if (w == 1 or (tempTA <= 320 and (7 <= hours and hours < 23)))
{
if (tempWater / 10 < 28 and bitRead(output, 3) == 0) //если вода в гидроразделителе остыла и ТЭН 2,5 кВт при этом выключен
s++;
else
s = 0;
if (s >= 30)
{
bitClear(output, 0); //выключить насос тёплых полов
s = 0;
}
}
if ((w == 10 and tempTA > 330) or (w == 5 and (z == 1 or tempTA > 450)))
bitSet(output, 1); //включить насос радиаторов
else if (w == 1 or ((w == 2 or w == 10 or w == 4) and tempTA < 320) or (w == 5 and tempTA < 400) or ((w == 4 or w == 5) and bitRead(output, 5) == 0))
bitClear(output, 1); //выключить насос радиаторов
if ((tempTA > 320 and (w == 2 or w == 10 or (w == 4 and z == 1))) or w == 5)
bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
else
bitClear(output, 5); //закрыть шаровой кран теплоаккумулятора
if (w == 5 and z == 2)
bitSet(output, 4); //включить ТЭН 7,5 кВт
else if ((w == 4 or w == 1) and (hours >= 23 or hours < hour_TA))
bitClear(output, 4); //выключить ТЭН 7,5 кВт
if (r < 40 or ((w == 4 or w == 5) and (z == 2 or (hours >= hour_TA and hours < 7))))
bitSet(output, 3); //включить ТЭН 2,5 кВт
else if ((((w == 4 and (hours >= 23 or hours < hour_TA)) and z == 1) or w == 1) and r >= 40)
bitClear(output, 3); //выключить ТЭН 2,5 кВт
}
}
else if (tempOutdoor >= 180)
timeCounter(7);
if (v >= 30 and w == 7)
to_Tertn_Off_All();
if (hours < 7 and tempTA < 800 and hours >= hour_TA)
bitSet(output, 4); //включить ТЭН 7,5 кВт
}
//9. Опция "Test Pumps" (тестирование насосов. Прокрутка всех насосов против прикипания):
if (option == 7 and d >= 3)
{
bitSet(output, 0); //включить насос тёплых полов
bitSet(output, 1); //включить насос радиаторов
bitSet(output, 2); //включить котёл
if (d >= 18)
{
bitClear(output, 0); //выключить насос тёплых полов
bitClear(output, 1); //выключить насос радиаторов
bitClear(output, 2); //выключить котёл
option = 0; //переход в режим "вне дома"
}
}
//10. Опция "Out H" ("вне дома", но с подогревом бака-аккумулятора в дневное время):
if (option == 8 and tempRoom > 80 and tempWater > 80 and tempTA > 80 and d >= 3)
{
if ((12 <= hours and hours < 16) and tempTA < 670)
{
bitSet(output, 4); //включить ТЭН 7,5 кВт
if (tempTA > 650)
option = 0; //переход в режим "вне дома"
}
else
to_Tertn_Off_All();
}
//Выполняется всегда:
if (7 <= hours and hours < 23)
{
bitClear(output, 3); //выключить ТЭН 2,5 кВт
if (option != 8)
bitClear(output, 4); //выключить ТЭН 7,5 кВт
}
if (tempTA >= 850)
bitClear(output, 4); //выключить ТЭН 7,5 кВт
if ((tempWater / 10 >= tW and option != 1) || tempWater / 10 >= 65) //если температура воды в системе отопления достигла верхней уставки - выключить котёл (отдельный случай для режима "натоп")
bitClear(output, 2); //выключить котёл
//Если сработал сигнал о необходимости отключения обогрева при превышении давления в системе
if (noHeat > 0 and option != 4)
{
bitClear(output, 2); //выключить котёл
bitClear(output, 3); //выключить ТЭН 2,5 кВт
bitClear(output, 4); //выключить ТЭН 7,5 кВт
bitSet(output, 0); //включить насос тёплых полов
bitSet(output, 1); //включить насос радиаторов
bitSet(output, 5); //открыть шаровой кран теплоаккумулятора
noHeat++;
}
Serial.write(output); //передача исполнительных команд на управляющие реле через МК nano
//формирование массивов для графиков температуры:
q++;
if (q >= 18) //если прошло 180 секунд
{
for (byte x = 0; x < 239; x++)
in2[x] = in2[x + 1]; //массив температуры воды в ГР
if (tempWater > 400)
in2[239] = (120 + tempWater / 10);
else if (tempWater <= 400 and tempWater >= 0)
in2[239] = (tempWater * 10 / 25);
if (n >= 3)
{
for (byte x = 0; x < 79; x++)
{
in1[x] = in1[x + 1]; //массив температуры в помещении
in3[x] = in3[x + 1]; //массив температуры на улице
in5[x] = in5[x + 1]; //массив температуры воды на выходе теплоаккумулятора
}
in1[79] = (tempRoom * 10 / 25);
if (tempOutdoor > 400)
in3[79] = (120 + tempOutdoor / 10 - 60);
else if (tempOutdoor <= 400)
in3[79] = (tempOutdoor * 10 / 25 - 60);
if (tempTA > 400)
in5[79] = (120 + tempTA / 10);
else if (tempTA <= 400)
in5[79] = (tempTA * 10 / 25);
n = 0;
}
if (n < 3)
n++;
//формирование массивов для графиков включения оборудования
for (byte i; i < 15; i++)
{
trend_kot[i] = trend_kot[i] << 1;
bitWrite(trend_kot[i], 0, bitRead(trend_kot[i + 1], 15));
trend_rad_p[i] = trend_rad_p[i] << 1;
bitWrite(trend_rad_p[i], 0, bitRead(trend_rad_p[i + 1], 15));
trend_floor_p[i] = trend_floor_p[i] << 1;
bitWrite(trend_floor_p[i], 0, bitRead(trend_floor_p[i + 1], 15));
trend_tenGR_25[i] = trend_tenGR_25[i] << 1;
bitWrite(trend_tenGR_25[i], 0, bitRead(trend_tenGR_25[i + 1], 15));
trend_tenTA_75[i] = trend_tenTA_75[i] << 1;
bitWrite(trend_tenTA_75[i], 0, bitRead(trend_tenTA_75[i + 1], 15));
trend_shar_kr[i] = trend_shar_kr[i] << 1;
bitWrite(trend_shar_kr[i], 0, bitRead(trend_shar_kr[i + 1], 15));
trend_min_Pr[i] = trend_min_Pr[i] << 1;
bitWrite(trend_min_Pr[i], 0, bitRead(trend_min_Pr[i + 1], 15));
trend_max_Pr[i] = trend_max_Pr[i] << 1;
bitWrite(trend_max_Pr[i], 0, bitRead(trend_max_Pr[i + 1], 15));
}
bitWrite(trend_kot[14], 0, bitRead(output, 2));
bitWrite(trend_rad_p[14], 0, bitRead(output, 1));
bitWrite(trend_floor_p[14], 0, bitRead(output, 0));
bitWrite(trend_tenGR_25[14], 0, bitRead(output, 3));
bitWrite(trend_tenTA_75[14], 0, bitRead(output, 4));
bitWrite(trend_shar_kr[14], 0, bitRead(output, 5));
if (pressure == 2)
bitWrite(trend_min_Pr[14], 0, 1);
else
bitWrite(trend_min_Pr[14], 0, 0);
if (pressure == 3)
bitWrite(trend_max_Pr[14], 0, 1);
else
bitWrite(trend_max_Pr[14], 0, 0);
if (a == 4)
drawTrends();
q = 0;
}
//Расчёт израсходованного топлива, электроэнергии и средней температуры на улице за прошедшие сутки
if (bitRead(output, 2) == 1)
e++;
if (bitRead(output, 2) != bitRead(prev, 2) and bitRead(output, 2) == 0)
e = e - 2; //вычитается время, которое топливо на горелку не подавалось (для каждого пуска котла)
if (j == 30 and k == 0) //для немедленного начала фиксации среднесуточной температуры при компиляции скетча
{
j = hours + 1;
k = tempOutdoor;
}
if (hours == j)
{
k = ((k * j) + tempOutdoor) / (j + 1);
j++;
}
if (bitRead(output, 3) == 1)
TEN25_kWh++;
if (bitRead(output, 4) == 1)
TEN75_kWh++;
if (j >= 24 and hours == 0)
{
p = 10 * e / 124; //количество израсходованного топлива за прошедшие сутки (из условия 2.9 л/ч)
r = k; //значение средней температуры на улице за прошедшие сутки
Last_kWh = ((TEN25_kWh + TEN75_kWh * 3) / 144); //значение в киловатт-часах израсходованной электроэнергии за прошедшие сутки
e = 0;
j = 0;
k = 0;
TEN25_kWh = 0;
TEN75_kWh = 0;
}
//динамические данные в окнах:
if (a == 0) //вывод динамических данных на главном экране
getIndicationMenu_Main();
if (a == 4) //выводим динамические данные на дисплее с графиками
getIndicationDrawTrends();
}
}
Выглядит “UNO” в комнате вот так:
Так вот… эта связка работает успешно несколько лет. И я очень доволен. И не помешало то, что нет образования.
Но сейчас у меня проблема. Когда я попытался внедрить новую систему, по похожему принципу - у меня не получается. Обратился за помощью сюда. Пытаюсь найти причину, по которой Ардуино “Mega” не работает так, как мне того надо.
Поможете - здорово. Не получится - ну и фиг с ним… будет нерешённая задача, на которую убил кучу времени.
Это вАААпсче нипричем. Энти “приблуды” влехкую напишет 12-летний школьнег.
В качестве примера возмите любой код из линукс на гите и … вот там уже не так все просто.
Кстати, есть флпрог волшебный.
вам там в первой теме, куда писали подсказали что я забыл учесть минусовые значения)))
а вы точно проверяли работу вашей системы включив 4 реле ?
включаем 1 2 3 4, затем выключаем, и снова включаем, и так хотя бы минут 5
в целом и на ардуино можно собирать… иногда легко, а иногда надо долго отлаживать, в целом на ней собирать тоже можно, но созданна она для того что бы делится проектами (делать за других))) )
если уже так делали… то можно разбить программу на 2 части, одна будет например датчики отслеживать, другая управлять силовой электроникой, и может еще что можно придумать… только в идеале что бы подсказывать вам, это все надо собрать самим, что лениво)))
Яж уже скока раз повторял - ну дали вам нахалтай, просто так, нате, берите и пользуйтесь всякими BT, wifi, BLE, GPS, NFC и прочие фишки современности но никто не знает даже, во сколько миру обошелся СИНИЙ СВЕТОДИОД!!! епрст!!!
Ну не лечат геморой по интернету.
Ты же не идешь на форум к хирургам с вопросом, что лучше, длинный или толстый а потом обвиняешь всех на хирургическом форуме в том, что все у тебя осталось как и было и все без исключения хирурги триньтарасы и тока ты дарт^анЬян?
Ну с чего ты решил, что кто-то будет думать и делать за тебя? Тебе дали вполне адекватные подсказки, даже код написали тестовый, а ты что? Ты уже впрямую обвиняешь, мол вот раньше было не совсем плохо, а сейчас еще хуже… Ну это не в какие ворота уже…
Сходи к Гайверу.
Сообщение читал там. Но почему-то подумал, что речь идёт про мой изначальный код. А там - всё по отрицательным значениям было корректно. Поэтому - не придал внимания сообщению.
Если же речь идёт - что после Вашего редактирования… то спасибо что обратили моё внимание. Попытаюсь проанализировать.
Но во вчерашнем состоянии - контроллер работал уже с участием Вашей корректировки кода. И на графике отрицательные значения отображались на первый взгдляд корректно:
что там было на табол где вывод цифрами (в моменты отрицательных температур) - не посмотрел.
Ну,… я видел моменты, когда включены все 4 реле (в основном режиме работы - они все 4 и включены).
Вот это вроде мне понятно. Запишу код - и пусть поработает с включением отключением реле. Единственный момен - я отключу при этом силовую часть. И как-бы исчезнут нюансы, связанные с сильноточными цепями. Реле будут замыкаться на разомкнутую нагрузку…
Это и было запланировано в дальнейшем - передача через rx/tx. Но я решил всё делать поэтапно. И подумал - что сначала надо наладить без передачи. Но уже и не знаю что думать… если в этом вся проблема - то готов перекинуться, посылать сигналы на “nano” а потом обрабатывать их на этой “Mega”. Но в голове не укладывается - что при этом проблема может отвалиться
в том то и дело, надо под нагрузкой на это смотреть)))(что бы то что подключено при этом к реле работало, дабы убедиться что тока хватает, или еще что) или я вас не понял…
На всякий случай, код который в настоящее время загружен в обсуждаемый микроконтроллер “Mega”:
Спойлер
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
#include <OneWire.h>
OneWire ds(21); // выбор пина ардуино, на котором висит шина 1-wire
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4
// Цвета
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define WHITEGREY 0xC638
#define DARKGREY 0x7BEF
#define DARKGREEN 0x0C80
#define pinVoda_tick 18
boolean stateCheck_voda;
#define pinElectr_tick 19
boolean stateCheck_electr;
uint8_t relay;
// Адреса датчиков
byte ADDR_DS18B20 [6][8] = {
{0x28, 0xD5, 0x97, 0x76, 0x0, 0x0, 0x0, 0x76}, // KompressorC 0
{0x28, 0x4F, 0x9C, 0x73, 0x0, 0x0, 0x0, 0xF9}, // WaterInC 1
{0x28, 0xFC, 0xD1, 0x72, 0x0, 0x0, 0x0, 0x2D}, // WaterOutC 2
{0x28, 0x95, 0x28, 0x75, 0x0, 0x0, 0x0, 0xAB}, // temp3C 3
{0x28, 0xEF, 0x8C, 0x76, 0x0, 0x0, 0x0, 0x58}, // DefrostC 4
{0x28, 0x8C, 0x3A, 0xB4, 0xE, 0x0, 0x0, 0xDB} // EvaporatorC 5
};
// Массивы для графиков
uint8_t tEvaporator[320];
uint8_t tOverheat[320];
uint8_t tKompressor[320];
uint8_t tWaterIn[320];
uint8_t tWaterOut[320];
uint8_t tDefrost[320];
uint8_t heatPower[320];
uint8_t gWater[320];
uint8_t ElectrPow[320];
uint8_t COP[320];
// Точные значения
int16_t tOverheatReal;
int32_t gWaterReal;
int32_t heatPowerReal;
int32_t ElectrPowReal;
int32_t COPreal;
int32_t gWaterRealPrew;
boolean FlagWater = false;
boolean FirstImpulsWater = false;
boolean SecondImpulsWater = false;
// Средние значения
int32_t countHeatAndElectr = 0;
int32_t countHeat = 0;
int32_t countElectr = 0;
// Флаги цикла
boolean Flag1 = true;
boolean Flag2 = true;
boolean Flag3 = true;
boolean Flag4 = true;
uint8_t countGetGrafic = 9;
uint32_t Timer;
// Графики реле
int16_t trend_fan[20];
int16_t trend_kompressor[20];
int16_t trend_pump[20];
int16_t trend_4way_valve[20];
// Водомер
uint32_t tmr;
boolean gWaterFlag = false;
uint32_t WaterStartTimer;
uint32_t WaterCountTimer;
// Электросчётчик
uint32_t tmr2;
boolean ElectrFlag = false;
uint32_t ElectrStartTimer;
uint32_t ElectrCountTimer;
// Режимы
boolean StartHeatFlag;
boolean StopFlag;
boolean HeatOptionFlag;
boolean HeatOptionPauseFlag;
boolean DefrostOptionFlag;
uint8_t tKompressorCounter = 0;
uint8_t HeatOptionCout = 0;
uint8_t HeatOptionPauseCout = 0;
uint8_t defrostCount = 0;
uint8_t StopMaxTemp = 0;
uint8_t StopMaxElectr = 0;
uint8_t StopMinTempHeater = 0;
uint8_t StopNo4xvalve = 0;
uint8_t StopNoElectric = 0;
uint8_t StopKind = 0;
uint8_t MistakeTemp[6] = {};
boolean getMistakeFlag = false;
uint32_t HeatOptionPauseStartTime;
uint32_t StartHeatTime;
uint32_t StopTime;
uint32_t DefrostTime;
// Переменные температур
int16_t Temper[6] = {0, 0, 0, 0, 0, 0};
int16_t TemperPrev[6];
// ============== CRC8 ДЛЯ DS18B20 ==============
uint8_t dallas_crc8(const uint8_t *data, uint8_t len) {
uint8_t crc = 0;
for (uint8_t i = 0; i < len; i++) {
uint8_t inbyte = data[i];
for (uint8_t j = 0; j < 8; j++) {
uint8_t mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix) crc ^= 0x8C;
inbyte >>= 1;
}
}
return crc;
}
// ============== НОВАЯ ФУНКЦИЯ ЧТЕНИЯ С ПРОВЕРКОЙ CRC ==============
// Возвращает температуру в десятых долях (например, 156 = 15.6°C)
// Возвращает 0x7FFF при ошибке CRC
int16_t readDS18B20(byte *addr) {
uint8_t data[9];
ds.reset();
ds.select(addr);
ds.write(0xBE); // Читать scratchpad
for (uint8_t i = 0; i < 9; i++) {
data[i] = ds.read();
}
// Проверка CRC
if (dallas_crc8(data, 8) != data[8]) {
return 0x7FFF; // Ошибка
}
int16_t raw = (data[1] << 8) | data[0];
return (raw * 10) >> 4; // Десятые доли
}
// ============== СБРОС ШИНЫ (запуск конвертации) ==============
void resetTemp() {
ds.reset();
ds.write(0xCC); // Ко всем датчикам
ds.write(0x44); // Конвертация
}
// ============== ПОЛУЧЕНИЕ ТЕМПЕРАТУР ==============
void gettingTemp() {
for (uint8_t i = 0; i < 6; i++) {
int16_t temp = readDS18B20(ADDR_DS18B20[i]);
if (temp != 0x7FFF) {
// Данные достоверны
Temper[i] = temp;
TemperPrev[i] = temp;
if (MistakeTemp[i] <= 6) MistakeTemp[i] = 0;
} else {
// Ошибка CRC
Temper[i] = TemperPrev[i];
if (StopFlag == false) {
MistakeTemp[i]++;
if (MistakeTemp[i] > 6) {
StopFlag = true;
StopTime = millis();
StopKind = 6;
}
}
}
}
}
// ============== ПРОВЕРКА ГРАНИЦ ТЕМПЕРАТУР ==============
void tempMistake(uint8_t a, int16_t b, int16_t c, int16_t d) {
// Эта функция теперь только для физических проверок,
// так как CRC уже отсеяла помехи.
// Но оставляем на всякий случай.
if (MistakeTemp[a] > 0) return; // Уже есть ошибка CRC
if (Temper[a] >= b && Temper[a] <= c && abs(Temper[a] - TemperPrev[a]) <= d) {
// Норма
} else {
Temper[a] = TemperPrev[a];
if (StopFlag == false) MistakeTemp[a]++;
if (MistakeTemp[a] > 6 && StopFlag == false) {
StopFlag = true;
StopTime = millis();
StopKind = 6;
}
}
}
// ============== ВОДОМЕР ==============
void voda_tick() {
boolean current_status = digitalRead(pinVoda_tick);
if (stateCheck_voda && !current_status) {
tmr = millis();
gWaterFlag = true;
stateCheck_voda = current_status;
}
if (!stateCheck_voda && current_status) stateCheck_voda = current_status;
if (current_status) gWaterFlag = false;
if (!current_status && millis() - tmr >= 1000 && gWaterFlag == true) {
WaterCountTimer = millis() - WaterStartTimer;
WaterStartTimer = millis();
gWaterFlag = false;
if (FirstImpulsWater == true) SecondImpulsWater = true;
FirstImpulsWater = true;
}
}
// ============== ЭЛЕКТРОСЧЁТЧИК ==============
void electr_tick() {
boolean current_status = digitalRead(pinElectr_tick);
if (stateCheck_electr && !current_status) {
tmr2 = millis();
ElectrFlag = true;
stateCheck_electr = current_status;
}
if (!stateCheck_electr && current_status) stateCheck_electr = current_status;
if (current_status) ElectrFlag = false;
if (!current_status && millis() - tmr2 >= 50 && ElectrFlag == true) {
ElectrCountTimer = millis() - ElectrStartTimer;
ElectrStartTimer = millis();
ElectrFlag = false;
}
}
// ============== ВЫВОД ЗНАЧЕНИЙ НА ДИСПЛЕЙ ==============
void getDataInt100(int16_t a) {
if (a >= -9 && a < 0) {
tft.print("-0.0");
tft.print(abs(a));
} else if (a < 9 && a >= 0) {
tft.print("0.0");
tft.print(a);
} else if (a <= -10 && a > -100) {
tft.print("-0.");
tft.print(abs(a % 100));
} else {
tft.print(a / 100);
tft.print(".");
if (abs(a % 100) < 10) tft.print("0");
tft.print(abs(a % 100));
}
}
void getDataInt10(int16_t a) {
if (a >= -9 && a < 0) {
tft.print("-0.");
tft.print(abs(a));
} else if (a < 9 && a >= 0) {
tft.print("0.");
tft.print(a);
} else {
tft.print(a / 10);
tft.print(".");
tft.print(abs(a % 10));
}
}
int16_t getDataForUintMassiv(int16_t a) {
int16_t LastInMassive;
if (a < -400) LastInMassive = 0;
else if (a >= -400 && a < 500) LastInMassive = round((a + 400) / 5);
else if (a >= 500 && a < 1250) LastInMassive = round(180 + (a - 500) / 10);
else LastInMassive = 255;
return LastInMassive;
}
int32_t getDataForUintMassiv32(int32_t a) {
int32_t LastInMassive;
if (a < -400) LastInMassive = 0;
else if (a >= -400 && a < 0) LastInMassive = round((a + 400) / 10);
else if (a >= 0 && a <= 160) LastInMassive = round(40 + a);
else if (a > 160 && a < 710) LastInMassive = round(200 + (a - 160) / 10);
else LastInMassive = 255;
return LastInMassive;
}
void getGraphics(uint16_t c, uint16_t d, uint8_t g[320]) {
int16_t oldX = 0;
int16_t oldY = g[0];
for (int16_t x = 1; x < 320; x++) {
int16_t nxt_x = x;
tft.drawLine(oldX, (c - oldY), nxt_x, (c - g[x]), d);
oldY = g[x];
oldX = nxt_x;
}
}
// ============== ОТРИСОВКА ДИСПЛЕЯ ==============
void getMainDisplay() {
tft.fillScreen(BLACK);
// Сетка по времени
for (uint8_t i = 1; i < 8; i++) {
tft.drawFastVLine((40 * i), 20, 190, DARKGREY);
tft.drawFastVLine((40 * i), 230, 210, DARKGREY);
}
// Сетка по температуре
for (uint8_t i = 0; i < 11; i++) {
if (i == 4 || i == 5 || i == 6 || i == 7 || i == 9 || i == 10)
tft.drawFastHLine(0, (5 + 20 * i), 320, DARKGREEN);
else if (i == 1 || i == 2 || i == 3 || i == 8)
tft.drawFastHLine(0, (5 + 20 * i), 320, BLUE);
}
for (uint8_t i = 0; i < 5; i++)
tft.drawFastHLine(0, (231 + 10 * i), 320, BLUE);
tft.drawFastHLine(0, 275, 320, BLUE);
for (uint8_t i = 0; i < 8; i++)
tft.drawFastHLine(0, (295 + 20 * i), 320, DARKGREEN);
// Подписи
tft.setTextSize(1);
tft.setCursor(0, 22); tft.setTextColor(WHITE, BLACK); tft.print("90");
tft.setCursor(0, 42); tft.print("70");
tft.setCursor(0, 62); tft.print("50");
tft.setCursor(0, 82); tft.print("40");
tft.setCursor(0, 102); tft.print("30");
tft.setCursor(0, 122); tft.print("20");
tft.setCursor(0, 142); tft.print("10");
tft.setCursor(0, 162); tft.print("0");
tft.setCursor(0, 182); tft.print("-10");
tft.setCursor(0, 202); tft.print("-20");
tft.setCursor(0, 238); tft.print("5");
tft.setCursor(0, 258); tft.print("3");
tft.setCursor(0, 272); tft.print("1.6");
tft.setCursor(0, 292); tft.print("1.4");
tft.setCursor(0, 312); tft.print("1.2");
tft.setCursor(0, 332); tft.print("1");
tft.setCursor(0, 352); tft.print("0.8");
tft.setCursor(0, 372); tft.print("0.6");
tft.setCursor(0, 392); tft.print("0.4");
tft.setCursor(0, 412); tft.print("0.2");
tft.setCursor(0, 432); tft.print("0");
// Назначения датчиков
tft.setCursor(0, 0); tft.setTextColor(RED); tft.print("Kompressor:");
tft.setCursor(10, 10); tft.setTextColor(MAGENTA); tft.print("Overheat:");
tft.setCursor(125, 10); tft.setTextColor(CYAN); tft.print("Defrost:");
tft.setCursor(215, 10); tft.setTextColor(RED); tft.print("Evaporator:");
tft.setCursor(120, 0); tft.setTextColor(YELLOW); tft.print("Water in:");
tft.setCursor(220, 0); tft.setTextColor(GREEN); tft.print("Water out:");
tft.setCursor(5, 210); tft.setTextColor(RED); tft.print("Heat power:");
tft.setCursor(152, 210); tft.setTextColor(YELLOW); tft.print("COP:");
tft.setCursor(218, 210); tft.setTextColor(GREEN); tft.print("Electr pow:");
tft.setCursor(22, 220); tft.setTextColor(MAGENTA); tft.print("G water:");
}
// ============== SETUP ==============
void setup() {
Serial.begin(9600);
tft.reset();
tft.begin(38022);
tft.invertDisplay(true);
pinMode(18, INPUT_PULLUP);
stateCheck_voda = digitalRead(pinVoda_tick);
pinMode(19, INPUT_PULLUP);
stateCheck_electr = digitalRead(pinElectr_tick);
for (int i = 0; i < 320; i++) {
tEvaporator[i] = 80;
tOverheat[i] = 80;
tKompressor[i] = 80;
tWaterIn[i] = 80;
tWaterOut[i] = 80;
tDefrost[i] = 80;
heatPower[i] = 40;
gWater[i] = 40;
ElectrPow[i] = 40;
COP[i] = 40;
}
pinMode(48, OUTPUT); digitalWrite(48, HIGH);
pinMode(49, OUTPUT); digitalWrite(49, HIGH);
pinMode(50, OUTPUT); digitalWrite(50, HIGH);
pinMode(51, OUTPUT); digitalWrite(51, HIGH);
StopFlag = false;
HeatOptionFlag = false;
HeatOptionPauseFlag = false;
DefrostOptionFlag = false;
StartHeatFlag = true;
StartHeatTime = millis();
Timer = millis();
delay(1000);
}
// ============== LOOP ==============
void loop() {
voda_tick();
electr_tick();
// Шаг 1: запуск конвертации (3-5 сек)
if (millis() - Timer >= 3000 && millis() - Timer < 5000 && Flag1 == true) {
resetTemp();
Flag1 = false;
}
// Шаг 2: чтение температур (5-7 сек)
if (millis() - Timer >= 5000 && millis() - Timer < 7000 && Flag2 == true) {
gettingTemp();
Flag2 = false;
}
// Шаг 3: расчёты и логика (7-9 сек)
if (millis() - Timer >= 7000 && millis() - Timer < 9000 && Flag3 == true) {
if (getMistakeFlag == false) {
for (uint8_t i = 0; i < 6; i++) {
TemperPrev[i] = Temper[i];
}
getMistakeFlag = true;
}
// Проверка границ (дополнительная, CRC уже отсеяла мусор)
tempMistake(0, -100, 1200, 200);
tempMistake(1, 0, 600, 100);
tempMistake(2, 0, 900, 50);
tempMistake(3, -400, 800, 400);
tempMistake(4, -400, 600, 400);
tempMistake(5, -400, 800, 200);
tOverheatReal = (Temper[3] - Temper[5]);
// Расход воды
if (digitalRead(51) == HIGH) {
gWaterReal = 0;
gWaterRealPrew = gWaterReal;
FlagWater = false;
} else if (digitalRead(51) == LOW && millis() - WaterStartTimer > 400000) {
StopFlag = true;
StopTime = millis();
StopKind = 7;
} else if (SecondImpulsWater == false) {
gWaterReal = gWaterRealPrew;
FlagWater = true;
} else {
gWaterReal = round(3600000 / WaterCountTimer);
gWaterRealPrew = gWaterReal;
FlagWater = false;
}
// Электрическая мощность
if (millis() - ElectrStartTimer > 60000)
ElectrPowReal = 0;
else
ElectrPowReal = 72000 / ElectrCountTimer;
// Тепловая мощность
heatPowerReal = ((int32_t)Temper[2] - (int32_t)Temper[1]) * gWaterReal * 1163 / 10000;
// COP
if (ElectrPowReal < 10)
COPreal = 0;
else
COPreal = 100 * heatPowerReal / ElectrPowReal;
// Средние значения
countHeat = countHeat + heatPowerReal;
countElectr = countElectr + ElectrPowReal;
countHeatAndElectr++;
// Управление вентилятором при перегреве
if (HeatOptionFlag == true && Temper[0] >= 850 && tOverheatReal >= 300 && digitalRead(48) == LOW) {
tKompressorCounter++;
if (tKompressorCounter >= 4) {
bitClear(relay, 0);
tKompressorCounter = 0;
}
} else if (HeatOptionFlag == true && (Temper[0] <= 750 || tOverheatReal <= 200) && digitalRead(48) == HIGH) {
tKompressorCounter++;
if (tKompressorCounter >= 2) {
bitSet(relay, 0);
tKompressorCounter = 0;
}
} else {
tKompressorCounter = 0;
}
// Режимы нагрева
if (HeatOptionFlag == true && Temper[1] >= 360) {
HeatOptionCout++;
if (HeatOptionCout >= 6) {
HeatOptionFlag = false;
HeatOptionPauseFlag = true;
HeatOptionCout = 0;
HeatOptionPauseStartTime = millis();
bitClear(relay, 2);
bitClear(relay, 0);
}
} else {
HeatOptionCout = 0;
}
if (HeatOptionPauseFlag == true && digitalRead(49) == LOW && millis() - HeatOptionPauseStartTime >= 120000)
bitClear(relay, 1);
if (HeatOptionPauseFlag == true && digitalRead(51) == HIGH && millis() - HeatOptionPauseStartTime >= 960000) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis();
FirstImpulsWater = false;
SecondImpulsWater = false;
}
bitSet(relay, 3);
HeatOptionPauseStartTime = millis();
}
if (HeatOptionPauseFlag == true && digitalRead(51) == LOW && millis() - HeatOptionPauseStartTime >= 60000) {
bitClear(relay, 3);
HeatOptionPauseStartTime = millis();
}
if (HeatOptionPauseFlag == true && Temper[1] <= 280) {
HeatOptionPauseCout++;
if (HeatOptionPauseCout >= 5) {
HeatOptionPauseFlag = false;
HeatOptionPauseCout = 0;
StartHeatTime = millis();
StartHeatFlag = true;
}
} else {
HeatOptionPauseCout = 0;
}
// Defrost
if (HeatOptionFlag == true && Temper[5] < -100) {
defrostCount++;
if (defrostCount >= 6) {
HeatOptionFlag = false;
DefrostOptionFlag = true;
DefrostTime = millis();
defrostCount = 0;
}
} else {
defrostCount = 0;
}
// Защиты
if (HeatOptionFlag == true && digitalRead(49) == HIGH) {
StopNo4xvalve++;
if (StopNo4xvalve >= 3) {
bitClear(relay, 2);
StopFlag = true;
StopTime = millis();
StopKind = 1;
StopNo4xvalve = 0;
}
} else {
StopNo4xvalve = 0;
}
if ((StartHeatFlag == true || HeatOptionFlag == true || HeatOptionPauseFlag == true || DefrostOptionFlag == true) && Temper[0] >= 1000) {
StopMaxTemp++;
if (StopMaxTemp >= 5) {
StopFlag = true;
StopTime = millis();
StopKind = 2;
StopMaxTemp = 0;
}
} else {
StopMaxTemp = 0;
}
if ((StartHeatFlag == true || HeatOptionFlag == true || HeatOptionPauseFlag == true || DefrostOptionFlag == true) && ElectrPowReal >= 151) {
StopMaxElectr++;
if (StopMaxElectr >= 6) {
StopFlag = true;
StopTime = millis();
StopKind = 3;
StopMaxElectr = 0;
}
} else {
StopMaxElectr = 0;
}
if ((StartHeatFlag == true || HeatOptionFlag == true || HeatOptionPauseFlag == true) && Temper[4] <= 50) {
StopMinTempHeater++;
if (StopMinTempHeater >= 3) {
StopFlag = true;
StopTime = millis();
StopKind = 4;
StopMinTempHeater = 0;
}
} else {
StopMinTempHeater = 0;
}
if (digitalRead(50) == LOW && ElectrPowReal <= 10) {
StopNoElectric++;
if (StopNoElectric >= 6) {
StopFlag = true;
StopTime = millis();
StopKind = 5;
StopNoElectric = 0;
}
} else {
StopNoElectric = 0;
}
// Start heat
if (StartHeatFlag == true && millis() - StartHeatTime >= 20000 && Temper[1] >= 50) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis();
FirstImpulsWater = false;
SecondImpulsWater = false;
}
bitSet(relay, 3);
}
if (StartHeatFlag == true && digitalRead(51) == LOW && millis() - StartHeatTime >= 30000 && Temper[0] <= 850)
bitSet(relay, 0);
if (StartHeatFlag == true && digitalRead(51) == LOW && digitalRead(48) == LOW && millis() - StartHeatTime >= 40000)
bitSet(relay, 1);
if (StartHeatFlag == true && digitalRead(51) == LOW && digitalRead(48) == LOW && digitalRead(49) == LOW && millis() - StartHeatTime >= 45000)
bitSet(relay, 2);
if (StartHeatFlag == true && digitalRead(51) == LOW && digitalRead(48) == LOW && digitalRead(50) == LOW && digitalRead(49) == LOW) {
StartHeatFlag = false;
HeatOptionFlag = true;
}
// Stop
if (StopFlag == true) {
StartHeatFlag = false;
HeatOptionFlag = false;
HeatOptionPauseFlag = false;
DefrostOptionFlag = false;
bitClear(relay, 2);
bitClear(relay, 0);
if (millis() - StopTime >= 120000) bitClear(relay, 1);
if (millis() - StopTime >= 180000) bitClear(relay, 3);
}
// Defrost
if (DefrostOptionFlag == true) {
if (digitalRead(51) == HIGH) {
WaterStartTimer = millis();
FirstImpulsWater = false;
SecondImpulsWater = false;
}
bitSet(relay, 3);
bitClear(relay, 2);
bitSet(relay, 0);
if (millis() - DefrostTime >= 120000) bitClear(relay, 1);
if (millis() - DefrostTime >= 900000 && Temper[5] >= 20) {
DefrostOptionFlag = false;
StartHeatTime = millis();
StartHeatFlag = true;
}
}
// Очистка старых значений
tft.fillRect(74, 0, 30, 20, BLACK);
tft.fillRect(181, 0, 30, 20, BLACK);
tft.fillRect(289, 0, 30, 20, BLACK);
tft.fillRect(74, 210, 30, 20, BLACK);
tft.fillRect(181, 210, 30, 10, BLACK);
tft.fillRect(289, 210, 30, 10, BLACK);
tft.fillRect(152, 220, 60, 10, BLACK);
tft.fillRect(100, 470, 30, 8, BLACK);
tft.fillRect(200, 470, 30, 8, BLACK);
// Вывод значений на дисплей
tft.setCursor(74, 0);
tft.setTextColor((MistakeTemp[0] > 0) ? CYAN : RED);
getDataInt10(Temper[0]);
tft.setCursor(181, 0);
tft.setTextColor((MistakeTemp[1] > 0) ? CYAN : YELLOW);
getDataInt10(Temper[1]);
tft.setCursor(289, 0);
tft.setTextColor((MistakeTemp[2] > 0) ? CYAN : GREEN);
getDataInt10(Temper[2]);
tft.setCursor(74, 10);
tft.setTextColor((MistakeTemp[3] > 0) ? CYAN : MAGENTA);
getDataInt10(tOverheatReal);
tft.setCursor(181, 10);
tft.setTextColor((MistakeTemp[4] > 0) ? CYAN : CYAN);
getDataInt10(Temper[4]);
tft.setCursor(289, 10);
tft.setTextColor((MistakeTemp[5] > 0) ? CYAN : RED);
getDataInt10(Temper[5]);
tft.setCursor(74, 210); tft.setTextColor(RED); getDataInt100(heatPowerReal);
tft.setCursor(181, 210); tft.setTextColor(YELLOW); getDataInt100(COPreal);
tft.setCursor(289, 210); tft.setTextColor(GREEN); getDataInt100(ElectrPowReal);
tft.setCursor(74, 220);
tft.setTextColor(FlagWater ? CYAN : MAGENTA);
getDataInt100(gWaterReal);
tft.setCursor(100, 470); tft.setTextColor(RED);
getDataInt100(countHeat / countHeatAndElectr);
tft.setCursor(200, 470); tft.setTextColor(GREEN);
getDataInt100(countElectr / countHeatAndElectr);
tft.setCursor(152, 220); tft.setTextColor(RED);
if (StopFlag == true) {
tft.print("Stop");
switch (StopKind) {
case 1: tft.print(" No 4x valve"); break;
case 2: tft.print(" maxT Kompr"); break;
case 3: tft.print(" maxW Electr"); break;
case 4: tft.print(" minT Heater"); break;
case 5: tft.print(" No Electr"); break;
case 6: tft.print(" Err Temp"); break;
case 7: tft.print(" No G_water"); break;
case 8: tft.print(" No Option"); break;
}
} else if (StartHeatFlag == true) {
tft.print("Start heat");
} else if (HeatOptionFlag == true) {
tft.print("Heat option");
} else if (HeatOptionPauseFlag == true) {
tft.print("Pause heat");
} else if (DefrostOptionFlag == true) {
tft.print("Defrost");
} else {
StopFlag = true;
StopTime = millis();
StopKind = 8;
}
// Состояния реле
tft.setCursor(0, 440);
tft.setTextColor(bitRead(relay, 2) ? RED : BLUE);
tft.print("Kompressor");
tft.setCursor(0, 450);
tft.setTextColor(bitRead(relay, 3) ? RED : BLUE);
tft.print("Pump");
tft.setCursor(0, 460);
tft.setTextColor(bitRead(relay, 1) ? RED : BLUE);
tft.print("4 way valve");
tft.setCursor(0, 470);
tft.setTextColor(bitRead(relay, 0) ? RED : BLUE);
tft.print("Fan");
Flag3 = false;
}
// Шаг 4: управление реле (9-10 сек)
if (millis() - Timer >= 9000 && millis() - Timer < 10000 && Flag4 == true) {
digitalWrite(48, bitRead(relay, 0) ? LOW : HIGH);
digitalWrite(49, bitRead(relay, 1) ? LOW : HIGH);
digitalWrite(50, bitRead(relay, 2) ? LOW : HIGH);
digitalWrite(51, bitRead(relay, 3) ? LOW : HIGH);
Flag4 = false;
}
// Шаг 5: обновление графиков (сброс цикла)
if (millis() - Timer >= 10000) {
Timer = millis();
if (countGetGrafic >= 9) {
// Сдвиг массивов
for (uint16_t x = 0; x < 319; x++) {
tEvaporator[x] = tEvaporator[x + 1];
tOverheat[x] = tOverheat[x + 1];
tKompressor[x] = tKompressor[x + 1];
tWaterIn[x] = tWaterIn[x + 1];
tWaterOut[x] = tWaterOut[x + 1];
gWater[x] = gWater[x + 1];
heatPower[x] = heatPower[x + 1];
ElectrPow[x] = ElectrPow[x + 1];
COP[x] = COP[x + 1];
tDefrost[x] = tDefrost[x + 1];
}
// Новые значения
tKompressor[319] = (uint8_t)getDataForUintMassiv(Temper[0]);
tWaterIn[319] = (uint8_t)getDataForUintMassiv(Temper[1]);
tWaterOut[319] = (uint8_t)getDataForUintMassiv(Temper[2]);
tOverheat[319] = (uint8_t)getDataForUintMassiv(tOverheatReal);
tDefrost[319] = (uint8_t)getDataForUintMassiv(Temper[4]);
tEvaporator[319] = (uint8_t)getDataForUintMassiv(Temper[5]);
heatPower[319] = (uint8_t)getDataForUintMassiv32(heatPowerReal);
COP[319] = (uint8_t)getDataForUintMassiv32(COPreal);
ElectrPow[319] = (uint8_t)getDataForUintMassiv32(ElectrPowReal);
gWater[319] = (uint8_t)getDataForUintMassiv32(gWaterReal);
// Графики реле
for (uint8_t i = 0; i < 20; i++) {
trend_fan[i] = trend_fan[i] << 1;
bitWrite(trend_fan[i], 0, bitRead(trend_fan[i + 1], 15));
trend_kompressor[i] = trend_kompressor[i] << 1;
bitWrite(trend_kompressor[i], 0, bitRead(trend_kompressor[i + 1], 15));
trend_pump[i] = trend_pump[i] << 1;
bitWrite(trend_pump[i], 0, bitRead(trend_pump[i + 1], 15));
trend_4way_valve[i] = trend_4way_valve[i] << 1;
bitWrite(trend_4way_valve[i], 0, bitRead(trend_4way_valve[i + 1], 15));
}
bitWrite(trend_fan[19], 0, bitRead(relay, 0));
bitWrite(trend_kompressor[19], 0, bitRead(relay, 2));
bitWrite(trend_pump[19], 0, bitRead(relay, 3));
bitWrite(trend_4way_valve[19], 0, bitRead(relay, 1));
getMainDisplay();
getGraphics(245, RED, tKompressor);
getGraphics(245, YELLOW, tWaterIn);
getGraphics(245, GREEN, tWaterOut);
getGraphics(245, MAGENTA, tOverheat);
getGraphics(245, CYAN, tDefrost);
getGraphics(245, RED, tEvaporator);
getGraphics(475, RED, heatPower);
getGraphics(475, YELLOW, COP);
getGraphics(475, GREEN, ElectrPow);
getGraphics(475, MAGENTA, gWater);
for (uint8_t i = 0; i < 20; i++) {
for (uint8_t j = 0; j < 16; j++) {
if (bitRead(trend_fan[i], j) == 1) tft.drawPixel((i * 16 + (15 - j)), 479, YELLOW);
if (bitRead(trend_kompressor[i], j) == 1) tft.drawPixel((i * 16 + (15 - j)), 449, YELLOW);
if (bitRead(trend_pump[i], j) == 1) tft.drawPixel((i * 16 + (15 - j)), 459, YELLOW);
if (bitRead(trend_4way_valve[i], j) == 1) tft.drawPixel((i * 16 + (15 - j)), 469, YELLOW);
}
}
countGetGrafic = 0;
}
countGetGrafic++;
Flag1 = true;
Flag2 = true;
Flag3 = true;
Flag4 = true;
}
}
Я не могу хаотично включать и выключать компрессор и четырёхходовой клапан. Могу потерять оборудование…
Это если только на время таких экспериментов включать что-то другое, типа утюга… или ещё чего… но именно компрессор - так опасно. А линия (провода от датчиков) - там как раз идут вдоль силового провода компрессора (проходят через стену)
Ты просто фантастический везунчик. Начинай играть в джекпот
Я понимаю, что вот так вот можно спокойно посидеть и покурить на спортивной площадке, но, можно спросить нашего Эфиопского футболиста, сколько пачек в день он выкуривает? Енто я про схему, тут была примерно такая-же тема с зависаниями и ее шатали больше месяца. Дык воот, может дело не в “бабине” ???
тогда возможно сразу попытаться купить реле с опторазвязкой ? не факт что поможет, но просто дополнительный вариант
А чо как мало? Про снабберы и кондюки 10n и оптроны забыл? Давай уже начнем УСЕ ПА ГОСТУ !!!
скачки напряжения могут вешать его мегу… я думаю совет имеет место быть, а может и не поможет…
@PPeterr Всё же для начала определитесь в чём у вас беда! В датчиках, коде, внешних ЭМ помехах? Ибо, судя по последним данным(перезагрузка контроллера в отсутствии WDT) у вас ещё и с электричеством проблемы ![]()
Понятно пока только одно - оптопары и снабберы НЕ нужны.
Между БП 12В поставь диод Шоттки и конденсатор ближе к меге мокрофарад на 100-500 и 16 Вольт.
Входы от счетчика и водомера около меги (ближе) зашунтируй керамикой 1-10nf.
В цепь контактора (или пускателя) либо снаббер, либо RC гаситель.
Пока усе.


