Контроллер твердотопливного котла на скору руку

FreeSky
Offline
Зарегистрирован: 18.01.2016

Грубо - это банальное термореле для управления вентилятором, что нагнетает воздух в котел. Насосами не управляет - насосы у меня включены напрямую в зимний период постоянно. Только функционал заточен конкретно под угольный/твердотопливный котел.
Режимы: Открыть топку / Остановка котла / Растопка котла / Работа в поддержке температуры / Тление на низкой температуре (поддержка ее) с последующим выходом на полноценную работу в указанное время.
Установки: Дата-время / Температура работы / Температура тления / Вредя до которого будет осуществляться тление
При невозможности достичь заданной температуры в течение 20 мин (или 40 мин при растопке) котел переходит в режим остановки.
При невключении вентилятора в течении 15 минут (и 30 минут в режиме останова, если температура выше 30гр.), происходит кратковременное включение вентилятора на 5 сек для продувки от образовавшегося газа (если не продувать газ будет периодически взрываться в котле!!!)
Датчик температуы один 18B20, модуль часов. Отображение всех режимов на экране, Кириллица (8 символов хватило!!!), Сохранение всех настроек в энергонезависимой памяти, а также текущего режима работы - если свет моргнет, котел продолжит работу в том же режиме что был до этого. Защита от перегрева с последующим отключением котла.
Родной контроллер просто подгорать и глючить уже начал, вот и сделал свой, с тем функционалом, которого мне там не хватало).
Если интересно - скетч ниже, за стиль написания не ругайте - пытался сделать все максимально линейно, без выкрутасов, чтоб скрытых ошибок было меньше - котел все-таки как никак!!!

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include "RTClib.h"
#include <EEPROM.h>


LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 (0x3f) for a 16 chars and 2 line display

OneWire ds(10); // На Пине 10 висит датчик температуры

RTC_DS1307 rtc; //Создаем объект rtc - ЧАСЫ

int Temp=0;
int deltaTemp = EEPROM.read(0);
int rabotaTemp = EEPROM.read(1);
int tlenieTemp = EEPROM.read(2);
int tlenieDoHour = EEPROM.read(3);
int tlenieDoMinute = EEPROM.read(4);
int tleniePereskok = EEPROM.read(5);
int rabotaMode = EEPROM.read(7);
int criticalTemp=88; // Температура аварии
long lastTimeOn = 0; // Время, когда произошло включение вентилятора
long curTimeOn = 0;
long deltaTimeOn = 1200000; // 20 минут - Миллисекунды - разделить на 1000 - будут секунды
long lastTimeOff = 0; // Время, когда произошло выключение вентилятора
long curTimeOff = 0;
long deltaTimeOff = 900000; // 15 минут - Миллисекунды - разделить на 1000 - будут секунды
int releStatus = 0; // 0 - выключен вентилятор на данный момент, 1 - включен вентилятор на данный момент

// ОБРАБОТКА КЛАВИАТУРЫ
char KeyPressed = 'N'; // Текущая нажатая кнопка клавиатуры
int key2=2; // Пин клавиатуры
int key3=3; // Пин клавиатуры
int key4=4; // Пин клавиатуры
int key5=5; // Пин клавиатуры
int relePin=13; // Пин реле вентилятора

void setup()
{

pinMode (relePin, OUTPUT); // управление реле вентилятора
digitalWrite (relePin, LOW); // на всякий случай выключим вентилятор
  
 rtc.begin(); // Инициализируем Часы в формате 24 часа

// Прописываем в экран русские буквы

 byte rus_d[8] = // Д - матрица
 { B00110,
   B01010,
   B01010,
   B01010,
   B01010,
   B11111,
   B10001,
   B00000, };
   
 byte rus_i[8] = // И - матрица
 { B10001,
   B10001,
   B10011,
   B10101,
   B11001,
   B10001,
   B10001,
   B00000, };
   
 byte rus_ya[8] = // Я - матрица
 { B01111,
   B10001,
   B10001,
   B01111,
   B00101,
   B01001,
   B10001,
   B00000, };
   
 byte rus_b[8] = // Б - матрица
 { B11110,
   B10000,
   B10000,
   B11110,
   B10001,
   B10001,
   B11110,
   B00000, };
   
 byte rus_p[8] = // П - матрица
 { B11111,
   B10001,
   B10001,
   B10001,
   B10001,
   B10001,
   B10001,
   B00000, };
   
 byte rus_yy[8] = // Ы - матрица
 { B10001,
   B10001,
   B10001,
   B11101,
   B10011,
   B10011,
   B11101,
   B00000, };
   
 byte rus_mya[8] = // Ь - матрица
 { B10000,
   B10000,
   B10000,
   B11110,
   B10001,
   B10001,
   B11110,
   B00000, };
   
 byte rus_l[8] = // Л - матрица
 { B00110,
   B01001,
   B01001,
   B01001,
   B01001,
   B01001,
   B10001,
   B00000, };

 lcd.init(); // Включаем дисплей
 lcd.createChar(0, rus_d); // Д - прописываем символ
 lcd.createChar(1, rus_i); // И - прописываем символ
 lcd.createChar(2, rus_ya); // Я - прописываем символ
 lcd.createChar(3, rus_b); // Б - прописываем символ
 lcd.createChar(4, rus_p); // П - прописываем символ
 lcd.createChar(5, rus_yy); // Ы - прописываем символ
 lcd.createChar(6, rus_mya); // Ь - прописываем символ
 lcd.createChar(7, rus_l); // Л - прописываем символ
 lcd.backlight();
 lcd.clear();

// ОБРАБОТКА КЛАВИАТУРЫ
// Для включения всех подтягивающих резисторов!!! 
pinMode (key2, INPUT);
digitalWrite (key2, HIGH); // подтягивающий резистор клавиатура
pinMode (key3, INPUT);
digitalWrite (key3, HIGH); // подтягивающий резистор клавиатура
pinMode (key4, INPUT);
digitalWrite (key4, HIGH); // подтягивающий резистор клавиатура
pinMode (key5, INPUT);
digitalWrite (key5, HIGH); // подтягивающий резистор клавиатура

}

void loop()
{
 printTemp();    // Выводим температуру
 printTime();
 printRabotaTemp();
 lcd.setCursor(0, 1);
 if (rabotaMode == 2) { lcd.print ("KOTE\7 OCTAHOB\7EH"); }
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'S') { menuSetup(); }
if (rabotaMode != 5) { // Если работает любой режим кроме тления то меняем температуру котла
     if (KeyPressed == 'U' && rabotaTemp < 85) { rabotaTemp++;
     EEPROM.write(1, rabotaTemp);
     delay (100); }
     if (KeyPressed == 'D' && rabotaTemp > 25) { rabotaTemp--;
     EEPROM.write(1, rabotaTemp);
     delay (100); }
   } else { // Если на данный момент идет тление по времени от и до,то меняем температуру тления
     if (KeyPressed == 'U' && tlenieTemp < 85) { tlenieTemp++;
     EEPROM.write(2, tlenieTemp);
     delay (100); }
     if (KeyPressed == 'D' && tlenieTemp > 25) { tlenieTemp--;
     EEPROM.write(2, tlenieTemp);
     delay (100); }
   }
if (KeyPressed == 'O') { menuRabotaMode(); }
if (KeyPressed == 'R') { openDoor(); }


// delay (200);
}




void printTemp()
{
byte data[2];
ds.reset(); 
ds.write(0xCC);
ds.write(0x44);
delay(200);
ds.reset();
ds.write(0xCC);
ds.write(0xBE);
data[0] = ds.read(); 
data[1] = ds.read();
Temp = (data[1]<< 8)+data[0];
Temp = Temp>>4;
lcd.setCursor(0, 0);
if (Temp >= 0) { lcd.print ("+"); }
if (Temp > -10 && Temp < 10) { lcd.print ("0"); }
lcd.print (Temp);
lcd.print (char(223));
lcd.print (" ");
if (Temp >= criticalTemp or Temp == 0) {
   digitalWrite (13, LOW); // Отключаем вентилятор
   releStatus = 0;
   rabotaMode = 0;
   EEPROM.write(7 , 2); // ОСТАНОВ КОТЛА ПОСЛЕ ВЫХОДА ИЗ АВАРИИ
   lcd.setCursor (0,1); 
   lcd.print ("! A B A P \1 \2 ! ");
}
deltaTemp = EEPROM.read(0);
if (rabotaMode == 0 && Temp < criticalTemp && Temp != 0) { rabotaMode = 2; } // Выход по падении температыры при аварии в останов котла
if (rabotaMode == 2) { // остановка котла
    digitalWrite (13, LOW); // Отключаем вентилятор
    if (releStatus == 0){ // Если до этого уже был выключен вентилятор
       // провести сравнение и вероятно устроить продувку
           curTimeOff = millis(); // Текущее значение часов
           if ( (lastTimeOff+(deltaTimeOff*2)) < curTimeOff) { // Не разу не включался вентилятор за двойное время
           lastTimeOff = curTimeOff; // Сбрасываем время включения вентилятора на данный момент
             if (Temp > 30) { // Если температура выше 30 гр, то даже в режиме останова продуем котел.
              lcd.setCursor (0,1); 
              lcd.print ("OCTAHOB \4PO");
              lcd.print (char(0));
              lcd.print ("YBKA");
              digitalWrite (13, HIGH);
              delay (5000); // Продувка котла 5 секунд
             }
           digitalWrite (13,LOW); }
       } else { // Если это первое выключение вентилятора при достижении температуры
       releStatus = 0;
       lastTimeOff = millis(); } // Сохраняем текущее время когда выключили вентилятор.
}
  
if (rabotaMode == 4) { // Если котел находится в режиме работы по температуре
  if ( Temp >= rabotaTemp ) { // Если температура равна или выше заданной выключаем вентилятор
    digitalWrite (13, LOW); // Отключаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("PA\3OTA          ");
    if (releStatus == 0){ // Если до этого уже был выключен вентилятор
       // провести сравнение и вероятно устроить продувку
           curTimeOff = millis(); // Текущее значение часов
           if ( (lastTimeOff+deltaTimeOff) < curTimeOff) { // Не разу не включался вентилятор
           lastTimeOff = curTimeOff; // Сбрасываем время включения вентилятора на данный момент
           lcd.setCursor (0,1); 
           lcd.print ("PA\3OTA  \4PO");
           lcd.print (char(0));
           lcd.print ("YBKA");
           digitalWrite (13, HIGH);
           delay (5000); // Продувка котла 5 секунд
           digitalWrite (13,LOW); }
       } else { // Если это первое выключение вентилятора при достижении температуры
       releStatus = 0;
       lastTimeOff = millis(); } // Сохраняем текущее время когда выключили вентилятор.
  }
  if ( (rabotaTemp-Temp) >= deltaTemp ) { // Если температура ниже порогового значения включаем вентилятор
    digitalWrite (13, HIGH); // Включаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("PA\3OTA    PA3");
       lcd.print (char(0));
       lcd.print ("YB"); 
    if (releStatus == 1){ // Если до этого уже был включен вентилятор
       // провести сравнение и вероятно уйти в режим остановки котла
           curTimeOn = millis(); // Текущее значение часов
           if ( (lastTimeOn+deltaTimeOn) < curTimeOn) { // Не произошло разогрева котла за указанное время
           rabotaMode = 2;
           EEPROM.write(7 , rabotaMode); }
       } else { // Если это первое включение вентилятора при достижении температуры
       releStatus = 1;
       lastTimeOn = millis(); } // Сохраняем текущее время когда включили вентилятор.
  }
  if (releStatus == 0){ // Тот случай когда просто выключен вентилятор и температура в пределах дельты
        digitalWrite (13, LOW); // Отключаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("PA\3OTA          ");
  }
}

if (rabotaMode == 5) { // Если котел находится в режиме работы по времени
  if ( Temp >= tlenieTemp ) { // Если температура равна или выше заданной выключаем вентилятор
    digitalWrite (13, LOW); // Отключаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("T\7EH\1E          ");
    if (releStatus == 0){ // Если до этого уже был выключен вентилятор
       // провести сравнение и вероятно устроить продувку
           curTimeOff = millis(); // Текущее значение часов
           if ( (lastTimeOff+deltaTimeOff) < curTimeOff) { // Не разу не включался вентилятор
           lastTimeOff = curTimeOff; // Сбрасываем время включения вентилятора на данный момент
           lcd.setCursor (0,1); 
           lcd.print ("T\7EH\1E  \4PO");
           lcd.print (char(0));
           lcd.print ("YBKA");
           digitalWrite (13, HIGH);
           delay (5000); // Продувка котла 5 секунд
           digitalWrite (13,LOW); }
       } else { // Если это первое выключение вентилятора при достижении температуры
       releStatus = 0;
       lastTimeOff = millis(); } // Сохраняем текущее время когда выключили вентилятор.
  }
  if ( (tlenieTemp-Temp) >= deltaTemp ) { // Если температура ниже порогового значения включаем вентилятор
    digitalWrite (13, HIGH); // Включаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("T\7EH\1E    PA3");
       lcd.print (char(0));
       lcd.print ("YB"); 
    if (releStatus == 1){ // Если до этого уже был включен вентилятор
       // провести сравнение и вероятно уйти в режим остановки котла
           curTimeOn = millis(); // Текущее значение часов
           if ( (lastTimeOn+deltaTimeOn) < curTimeOn) { // Не произошло разогрева котла за указанное время
           rabotaMode = 2;
           EEPROM.write(7 , rabotaMode); }
       } else { // Если это первое включение вентилятора при достижении температуры
       releStatus = 1;
       lastTimeOn = millis(); } // Сохраняем текущее время когда включили вентилятор.
  }
  if (releStatus == 0){ // Тот случай когда просто выключен вентилятор и температура в пределах дельты
        digitalWrite (13, LOW); // Отключаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("T\7EH\1E          ");
  }
}

if (rabotaMode == 3) { // Если котел находится в режиме розжига
  if ( Temp >= rabotaTemp ) { // Если температура равна или выше заданной переходим в режим Работы по температуре
     rabotaMode = 4;  }
  if ( Temp < rabotaTemp ) { // Если температура ниже значения включаем вентилятор
    digitalWrite (13, HIGH); // Включаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("PACTO\4KA  PA3");
       lcd.print (char(0));
       lcd.print ("YB"); 
    if (releStatus == 1){ // Если до этого уже был включен вентилятор
       // провести сравнение и вероятно уйти в режим остановки котла
           curTimeOn = millis(); // Текущее значение часов
           if ( (lastTimeOn+(deltaTimeOn*2)) < curTimeOn) { // Не произошло разогрева котла за двойное время
           rabotaMode = 2;
           EEPROM.write(7 , rabotaMode); }
       } else { // Если это первое включение вентилятора при достижении температуры
       releStatus = 1;
       lastTimeOn = millis(); } // Сохраняем текущее время когда включили вентилятор.
  }
}

}

void printRabotaTemp()
{
lcd.setCursor(11, 0);
lcd.print (' ');
if (rabotaMode != 5) {
 if (Temp > rabotaTemp) { lcd.print (char(127)); } else { lcd.print (char(126)); }
 lcd.print (rabotaTemp);
 } else {
 if (Temp > tlenieTemp) { lcd.print (char(127)); } else { lcd.print (char(126)); }
 lcd.print (tlenieTemp); }   
lcd.print (char(223));
}



void GetKey()
{
 KeyPressed = 'N'; // Ничего не нажато!!!   
 if (digitalRead (key2) == LOW) {
  KeyPressed = 'X';
 delay (100);
    if (digitalRead (key2) == LOW) {
      delay (100);
            if (digitalRead (key2) == LOW) {
              delay (100);
                    if (digitalRead (key2) == LOW) {
                      delay (100);
                                if (digitalRead (key2) == LOW) {
                                  delay (100);
                                          if (digitalRead (key2) == LOW) {
                                            KeyPressed = 'R'; // R - Длительное нажание X
                                          }
                                }
                    }
            }
    }
  return; }
 if (digitalRead (key3) == LOW) { 
  KeyPressed = 'D';
  return; }
 if (digitalRead (key4) == LOW) {
   KeyPressed = 'U';
   return; }
 if (digitalRead (key5) == LOW) {
   KeyPressed = 'O';
 delay (100);
    if (digitalRead (key5) == LOW) {
      delay (100);
            if (digitalRead (key5) == LOW) {
              delay (100);
                    if (digitalRead (key5) == LOW) {
                      delay (100);
                                if (digitalRead (key5) == LOW) {
                                  delay (100);
                                          if (digitalRead (key5) == LOW) {
                                            KeyPressed = 'S'; // S - Длительное нажание O
                                          }
                                }
                    }
            }
    }
   return; }
}

void printTime()
{
 DateTime now = rtc.now(); // Считываем время
 lcd.setCursor(5, 0);
 if (now.hour() < 10) { lcd.print ('0'); }
 lcd.print(now.hour());
 lcd.blink_on();
 lcd.print(':');
 lcd.blink_off();
 if (now.minute() < 10) { lcd.print ('0'); }
 lcd.print(now.minute());
 lcd.print(" ");

 
 if (rabotaMode == 5) { // Если активирован режим тления
     if ( tleniePereskok == 0 && ( now.minute() + ( now.hour() * 60 ) ) >= ( tlenieDoMinute + ( tlenieDoHour * 60) ) ) { // Если без перескока и текущее время больше установленного, то тогда выход из тления в работу
     rabotaMode = 4;
     EEPROM.write(7, rabotaMode);
     }
     
if (tleniePereskok == 1 && now.minute() == 0 && now.hour() == 0 ) { tleniePereskok = 0; } // Если полночь то скидываем перескок через полночь
 
 }
}

void menuSetup()
{
 rabotaMode = 6;
 int menuSetupMode = 1;
 lcd.setCursor(5, 0);
 lcd.print (" YCTAHOBKA ");
 lcd.setCursor (0,1);
 lcd.print (char(0));
 lcd.print ("ATA "); 
 lcd.print ("BPEM\2           ");
 delay (300);
 while (1 == 1) { 
 printTemp();    // Выводим температуру
 lcd.setCursor(5, 0);
 lcd.print (" YCTAHOBKA ");
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') {
    rabotaMode = EEPROM.read(7);
    return; }
 if (KeyPressed == 'U' && menuSetupMode == 5) {
     menuSetupMode = 1;
     KeyPressed = 'N'; }
 if (KeyPressed == 'U' && menuSetupMode < 5) {
     menuSetupMode++;
     KeyPressed = 'N'; }
 if (KeyPressed == 'D' && menuSetupMode == 1) {
     menuSetupMode = 5;
     KeyPressed = 'N'; }
 if (KeyPressed == 'D' && menuSetupMode > 1) {
     menuSetupMode--;
     KeyPressed = 'N'; }
 lcd.setCursor (0,1);
 if (menuSetupMode == 1) { lcd.print (char(0));
     lcd.print ("ATA "); 
     lcd.print ("BPEM\2           "); }
 if (menuSetupMode == 2) { lcd.print (char(0));
     lcd.print ("E\7\6TA TEM\4P BK\7"); }
 if (menuSetupMode == 3) { lcd.print ("TEM\4EPAT. PA\3OT\5"); }
 if (menuSetupMode == 4) { lcd.print ("TEM\4EPAT. T\7EH\1\2"); }
 if (menuSetupMode == 5) { lcd.print ("BPEM\2 T\7EH\1\2 ");
     lcd.print (char(0));
     lcd.print ("O "); }
 if (KeyPressed == 'O' && menuSetupMode == 1) { menuSetupData(); }
 if (KeyPressed == 'O' && menuSetupMode == 2) { menuSetupDelta(); }
 if (KeyPressed == 'O' && menuSetupMode == 3) { menuSetupRabota(); }
 if (KeyPressed == 'O' && menuSetupMode == 4) { menuSetupTlenie(); }
 if (KeyPressed == 'O' && menuSetupMode == 5) { menuSetupTlenieDO(); }

  delay (100); // задержка против дребезга кнопок 
 }
}

void menuSetupData() // Установка даты и времени
{
    DateTime now = rtc.now(); // считываем время и дату для установки.
    int setupYear = now.year();
    int setupMonth = now.month();
    int setupDay = now.day();
    // setupWeek = now.dayOfTheWeek();
    int setupHour = now.hour();
    int setupMinute = now.minute();
    // setupSecond = now.second();
    int menuSetupDataMode = 1;
//    delay (200); // задержка против дребезга кнопок 
 while (1 == 1) {
 printTemp();    // Выводим температуру
 lcd.setCursor (0,1);
 if (setupDay < 10) { lcd.print ('0'); }
 lcd.print (setupDay);
 lcd.print ("/");
 if (setupMonth < 10) { lcd.print ('0'); }
 lcd.print (setupMonth);
 lcd.print ("/");
 lcd.print (setupYear);
 lcd.print (" ");
 if (setupHour < 10) { lcd.print ('0'); }
 lcd.print (setupHour);
 lcd.print (":");
 if (setupMinute < 10) { lcd.print ('0'); }
 lcd.print (setupMinute);
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 if (KeyPressed == 'O' && menuSetupDataMode == 5) { 
    rtc.adjust( DateTime(setupYear, setupMonth, setupDay, setupHour, setupMinute,0) );
    return; }
 if (KeyPressed == 'O' && menuSetupDataMode < 5) { menuSetupDataMode++; }
 lcd.blink();
 if (menuSetupDataMode == 1) {
   lcd.setCursor (1,1);
   if (KeyPressed == 'U' && setupDay < 31) { setupDay++; }
   if (KeyPressed == 'U' && setupDay == 31) { setupDay = 1; }
   if (KeyPressed == 'D' && setupDay > 1) { setupDay--; }
   if (KeyPressed == 'D' && setupDay == 1) { setupDay = 31; }
 }
 if (menuSetupDataMode == 2) {
   lcd.setCursor (4,1);
   if (KeyPressed == 'U' && setupMonth < 12) { setupMonth++; }
   if (KeyPressed == 'U' && setupMonth == 12) { setupMonth = 1; }
   if (KeyPressed == 'D' && setupMonth > 1) { setupMonth--; }
   if (KeyPressed == 'D' && setupMonth == 1) { setupMonth = 12; }
 }
 if (menuSetupDataMode == 3) {
   lcd.setCursor (9,1);
   if (KeyPressed == 'U') { setupYear++; }
   if (KeyPressed == 'D' && setupYear > 2000) { setupYear--; }
  }
 if (menuSetupDataMode == 4) {
   lcd.setCursor (12,1);
   if (KeyPressed == 'U' && setupHour < 23) { setupHour++; }
   if (KeyPressed == 'U' && setupHour == 23) { setupHour = 0; }
   if (KeyPressed == 'D' && setupHour > 0) { setupHour--; }
   if (KeyPressed == 'D' && setupHour == 0) { setupHour = 23; }
 }
 if (menuSetupDataMode == 5) {
   lcd.setCursor (15,1); 
   if (KeyPressed == 'U' && setupMinute < 59) { setupMinute++; }
   if (KeyPressed == 'U' && setupMinute == 59) { setupMinute = 0; }
   if (KeyPressed == 'D' && setupMinute > 0) { setupMinute--; }
   if (KeyPressed == 'D' && setupMinute == 0) { setupMinute = 59; }
 }
 delay (100); // задержка против дребезга кнопок 
 }
}

void menuSetupDelta() // Установка гистерезиса срабатывания котла
{
 int setupDelta = EEPROM.read(0);
 lcd.setCursor (0,1);
 lcd.print (char(0));
 lcd.print ("E\7\6TA TEM\4: ");
 if (setupDelta <10) { lcd.print ('0'); }
 lcd.print (setupDelta);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 delay (400);

 while (1 == 1) {
 printTemp();    // Выводим температуру
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 lcd.setCursor (0,1);
 lcd.print (char(0));
 lcd.print ("E\7\6TA TEM\4: ");
 if (setupDelta <10) { lcd.print ('0'); }
 lcd.print (setupDelta);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 if (KeyPressed == 'U' && setupDelta < 10) { setupDelta++; }
 if (KeyPressed == 'D' && setupDelta > 1) { setupDelta--; }
 if (KeyPressed == 'O') {
     EEPROM.write(0, setupDelta);
     return; }
 delay (200); // задержка против дребезга кнопок 

 }
}

void menuSetupRabota() // Установка температуры работы котла
{
 int setupRabota = EEPROM.read(1);
 lcd.setCursor (0,1);
 lcd.print ("PA\3OTA TEM\4: ");
 if (setupRabota <10) { lcd.print ('0'); }
 lcd.print (setupRabota);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 delay (300);

 while (1 == 1) {
 printTemp();    // Выводим температуру
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 lcd.setCursor (0,1);
 lcd.print ("PA\3OTA TEM\4: ");
 if (setupRabota <10) { lcd.print ('0'); }
 lcd.print (setupRabota);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 if (KeyPressed == 'U' && setupRabota < 85) { setupRabota++; }
 if (KeyPressed == 'D' && setupRabota > 25) { setupRabota--; }
 if (KeyPressed == 'O') {
     EEPROM.write(1, setupRabota);
     rabotaTemp = setupRabota;
     return; }
 delay (100); // задержка против дребезга кнопок 

 }
}

void menuSetupTlenie() // Установка температуры при тлении котла
{
 int setupTlenie = EEPROM.read(2);
 lcd.setCursor (0,1);
 lcd.print ("T\7EH\1E TEM\4: ");
 if (setupTlenie <10) { lcd.print ('0'); }
 lcd.print (setupTlenie);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 delay (300);

 while (1 == 1) {
 printTemp();    // Выводим температуру
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 lcd.setCursor (0,1);
 lcd.print ("T\7EH\1E TEM\4: ");
 if (setupTlenie <10) { lcd.print ('0'); }
 lcd.print (setupTlenie);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 if (KeyPressed == 'U' && setupTlenie < 85) { setupTlenie++; }
 if (KeyPressed == 'D' && setupTlenie > 25) { setupTlenie--; }
 if (KeyPressed == 'O') {
     EEPROM.write(2, setupTlenie);
     tlenieTemp = setupTlenie;
     return; }
 delay (100); // задержка против дребезга кнопок 

 }
}


void menuSetupTlenieDO() // Установка времени до которого будет продолжаться тление
{
 int setupTlenieDoHour = EEPROM.read(3);
 int setupTlenieDoMinute = EEPROM.read(4);

 lcd.setCursor (0,1);
 lcd.print ("T\7EH\1E ");
 lcd.print (char(0));
 lcd.print ("O: ");
 if (setupTlenieDoHour <10) { lcd.print ('0'); }
 lcd.print (setupTlenieDoHour);
 lcd.print (':');
 if (setupTlenieDoMinute <10) { lcd.print ('0'); }
 lcd.print (setupTlenieDoMinute);
 lcd.print (' ');
 lcd.blink();
 lcd.setCursor (12,1);
 delay (300);
 int setupTlenieDoMode = 1; // 1- устанавливаем часы, 2- устанавливаем минуты
 while (1 == 1) {
 printTemp();    // Выводим температуру
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 lcd.setCursor (0,1);
 lcd.print ("T\7EH\1E ");
 lcd.print (char(0));
 lcd.print ("O: ");
 if (setupTlenieDoHour <10) { lcd.print ('0'); }
 lcd.print (setupTlenieDoHour);
 lcd.print (':');
 if (setupTlenieDoMinute <10) { lcd.print ('0'); }
 lcd.print (setupTlenieDoMinute);
 lcd.print (' ');
 lcd.blink();
 if ( setupTlenieDoMode == 1) { lcd.setCursor (12,1); } else { lcd.setCursor (15,1); } 
 if ( setupTlenieDoMode == 2 && KeyPressed == 'O') {
    EEPROM.write(3 , setupTlenieDoHour);
    EEPROM.write(4 , setupTlenieDoMinute);
    return; } 
 if ( setupTlenieDoMode == 1 && KeyPressed == 'O') { setupTlenieDoMode = 2; }
 
 if (setupTlenieDoMode == 1 && KeyPressed == 'U' && setupTlenieDoHour < 23) { setupTlenieDoHour++; }
 if (setupTlenieDoMode == 1 && KeyPressed == 'U' && setupTlenieDoHour >= 23) { setupTlenieDoHour = 0; }
 if (setupTlenieDoMode == 1 && KeyPressed == 'D' && setupTlenieDoHour > 0) { setupTlenieDoHour--; }
 if (setupTlenieDoMode == 1 && KeyPressed == 'D' && setupTlenieDoHour == 0) { setupTlenieDoHour = 23; }

 if (setupTlenieDoMode == 2 && KeyPressed == 'U' && setupTlenieDoMinute < 59) { setupTlenieDoMinute++; }
 if (setupTlenieDoMode == 2 && KeyPressed == 'U' && setupTlenieDoMinute >= 59) { setupTlenieDoMinute = 0; }
 if (setupTlenieDoMode == 2 && KeyPressed == 'D' && setupTlenieDoMinute > 0) { setupTlenieDoMinute--; }
 if (setupTlenieDoMode == 2 && KeyPressed == 'D' && setupTlenieDoMinute == 0) { setupTlenieDoMinute = 59; }

 delay (100); // задержка против дребезга кнопок 

 }
}

void menuRabotaMode()
{
 rabotaMode = 6;
 int rabotaModeTmp = EEPROM.read(7);
 if ( rabotaModeTmp == 2 ) { rabotaModeTmp = 3; }
 lcd.setCursor(5, 0);
 lcd.print ("B\1");
 lcd.print (char(0));
 lcd.print (" PA\3OT\5 ");
 delay (300);
 while (1 == 1) { 
 printTemp();    // Выводим температуру
 lcd.setCursor(5, 0);
 lcd.print ("B\1");
 lcd.print (char(0));
 lcd.print (" PA\3OT\5 ");
 lcd.setCursor (0,1);
 if (rabotaModeTmp == 1) { lcd.print ("OTKP\5T\6 TO\4KY   "); }
 if (rabotaModeTmp == 2) { lcd.print ("OCTAHOBKA KOT\7A "); }
 if (rabotaModeTmp == 3) { lcd.print ("PACTO\4KA KOT\7A  "); }
 if (rabotaModeTmp == 4) { lcd.print ("\4O TEM\4EPATYPE  "); }
 if (rabotaModeTmp == 5) { lcd.print ("T\7EH\1E ");
    lcd.print (char(0));
    lcd.print ("O BPEMEH"); }
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') {
     rabotaMode = EEPROM.read(7);
     return; }
 if (KeyPressed == 'U' && rabotaModeTmp >=5 ) {
     rabotaModeTmp = 1;
     KeyPressed = 'N'; }
 if (KeyPressed == 'U' && rabotaModeTmp < 5) { rabotaModeTmp++; }
 if (KeyPressed == 'D' && rabotaModeTmp > 1) { rabotaModeTmp--; }
 if (KeyPressed == 'D' && rabotaModeTmp == 1) { rabotaModeTmp = 5; }
 if (KeyPressed == 'O' && rabotaModeTmp == 1) {
     rabotaModeTmp = 1;
     openDoor();
     return; }
 if (KeyPressed == 'O' && rabotaMode > 1) {
     if (Temp < 30 && rabotaModeTmp == 4) {rabotaModeTmp = 3;}
     if (rabotaModeTmp == 5) {
       tleniePereskok = 0;
       tlenieDoHour = EEPROM.read(3);
       tlenieDoMinute = EEPROM.read(4);
       DateTime now = rtc.now();
       if ( ( now.minute() + ( now.hour() * 60 ) ) > ( tlenieDoMinute + ( tlenieDoHour * 60) ) ) {
         tleniePereskok = 1;
         EEPROM.write(5 , tleniePereskok);
       }
     }
     rabotaMode = rabotaModeTmp;
     EEPROM.write(7 , rabotaMode);
     return; }
 
 }
}
void openDoor()
{
 rabotaMode = 1;
 printTemp();    // Выводим температуру
 printTime();
 lcd.setCursor (11,0);
 lcd.print ("\4AY3A");
 lcd.setCursor (0,1);
 lcd.print ("  \4PO");
 lcd.print (char(0));
 lcd.print ("YB TO\4K\1  ");
 digitalWrite (13, HIGH);
 delay (3000);
 digitalWrite (13, LOW);
 lcd.setCursor (0,1);
 lcd.print (" OTKP\5T\6 TO\4KY ");
 GetKey();
 while (KeyPressed == 'N') {
 printTemp();    // Выводим температуру
 printTime();
 GetKey(); }
 rabotaMode = EEPROM.read(7);
}

 

bwn
Offline
Зарегистрирован: 25.08.2014

Вариант, всадить фальшпанель прямо в бокс, понравился. +5.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Я не уверен, скорее всего не заработает, но я бы  попробовал проверить,  а вдруг:

#define Б \3

 

FreeSky
Offline
Зарегистрирован: 18.01.2016

Сам бы никогда не додумался, просто пилиэтилен толстый, из которого обычно половинки корпусов делаю закончился, остался только 8мм, которого только на панель и хватало, вот и пошел по магазин в поисках подходящей коробочки. Оказалось, что мини-щитки под пакетники - самое то!!! И стоят не дорого и вид очень приличный и типоразмеров много!
Вот заказал gsm-модуль, жду когда придет, надо будет его к контроллеру прикрутить, чтоб sms-ки слал, когда уголь закончился )

MacSim
Offline
Зарегистрирован: 28.11.2012

Гуд!

Что за котел используете?

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

....Надо бы его усовершенствовать:

для контроля горения используют инфракрасные датчики (пламени), глаз такой смотрит на пламя;                                                        шнек с движком для подсыпки угля, весы для взвешивания порции, или объемный дозатор;                                                            датчик тяги- термосопротивление на трубу, как у газовах котлов (в трубе если тяги нет, все продукты горения начинают проходить через термосопротивление); датчик со(не знаю есть ли такие)

 

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

FreeSky
Offline
Зарегистрирован: 18.01.2016

Котел - обычный твердотопливный (дрова/уголь) с принудительным нагнетанием воздуха - сзади стоит вентилятор, спереди поддува вообще нет - вот такой - http://www.belkomin.com/wp-content/uploads/2013/05/tis-pro-plus-uni_pdf1.pdf

Родной блок управления управлял и насосами и вентилятором. Как показала практика - насосы в целях безопасности должны быть включены всегда в зимний период и лучше всего через серьезный бесперебойник (часа 3 чтоб могли работать). Поэтому мой контролле управляет только вентилятором, нагнетающим воздух в котел. Вдобавок ко всему сделал ночной режим "тление" - чтобы можно было поставить загруженный и растопленный котел тлеть ночью на маленькой температуре, 35 гразусов, например, а утром он по часам , у меня в 7:30, автоматически выходит на нормальный рабочий режим 60 гр. К приходу всех на работу уже тепло )

Датчик только один - DS18B20 - стоит в котле - считывает температу теплоносителя. Всяких там ИК, СО, яги и т.п. думаю быть не должно вообще, т.к. автоматика управления котлом вещь критичная в плане безопасности и должна быть максимально проста. Упала температура - поддали воздуха. Температура стала в норму - выключили воздух. Температура длительное время не растет при включенном вентиляторе - все топливо прогорело - выключаемся.

Автоподачу угля у меня не организовать - т.к. места очень мало в котельной. Да и уголь - это не дрова, если на улице не -30, то закинуть котел надо всего 2-3 раза в сутки.

jonik007
Offline
Зарегистрирован: 20.09.2016

Дайте, пожалуйста, полный список, что использовано из андурино для создания данной системы.

Спасибо, заранее!

slayer73
Offline
Зарегистрирован: 05.04.2016

Здравствуйте, а можно ли посмотреть схему подключения ардуино?

slayer73
Offline
Зарегистрирован: 05.04.2016

Заранее благодарен, мой Мейл: anazarov.chita@mail.ru

Govemail
Offline
Зарегистрирован: 29.10.2018

мож подкоректировать скейч под ардуино вот этот http://arduino.ru/forum/proekty/blok-upravleniya-oborotami-medogonki-lcd-nokia-1202

dmitron1036
Offline
Зарегистрирован: 10.01.2016

оффтоп: что вы думаете об измерении температуры дыма?

думаю автоматизировать свою кирпичную печку....есть всё кроме денег....

айдер
Offline
Зарегистрирован: 30.12.2018

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

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

айдер пишет:

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

ну дак зашли деньги в аркестр

айдер
Offline
Зарегистрирован: 30.12.2018

какая цифра подойдет аркестру?

айдер
Offline
Зарегистрирован: 30.12.2018

просьба частная не коммерческая.

айдер
Offline
Зарегистрирован: 30.12.2018

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

айдер
Offline
Зарегистрирован: 30.12.2018

Доброго времени суток.Чего молчим?

dmitron1036
Offline
Зарегистрирован: 10.01.2016

Я себе вот тоже поставил немножко автоматики:

https://youtu.be/WKGTGtQIwkk?t=82

Ардуины там пока нет. коплю опыт.

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

явно не хватает датчика температуры дыма в  трубе...   как минимум определять что котел  затух и не дуть туда ничего.  Контроль температуры дает много для понимания  насколько эффетивно сжигается топливо, не улетат и в трубу драгоценное  тепло и т.д.

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

айдер пишет:

Доброго времени суток.Чего молчим?

Вот мне интересно (чисто для себя) - вы действительно считаете,  что достаточно человеку сказать "очень хороший проект", что бы он немедленно кинулся выполнять ваши хотелки?

айдер
Offline
Зарегистрирован: 30.12.2018

Ну так ведь не бесплатно! 

FreeSky
Offline
Зарегистрирован: 18.01.2016

Всем здрасте!

Выполнять заказы в плане написания скетчев для ардуино - это далеко не мой профиль работы. Контроллер делал в первую очередь для себя сам по двум причинам. То что фирменный стоит 300 евро. На мой взгляд немного дороговато. Тем более когда за вечер можно сделать самому такой же. И вторая причина, что я промахнулся с размерами котла и купил большего размера чем мне нужен. Поэтому если ставить температуру в котле, как рекомендовано 80 градусов, то у меня в помещении будет под 30 зимой.

Минимально выставляемая температура на родном контроллере была 60 градусов, что тоже было для меня много. Поэтому я в своем сделал нижнию границу 50, что конечно как бы нельзя и не совсем хорошо для котла, но у меня нет другого выхода. Менять котел не вариант. Когда топились углем, то да, на таких низких температурах конечно шло дикое образование конденсата и труба забивалась за сезон. Сейчас перешли на торф и таких проблем с ним нет.

Помимо этого я реализовал режим тления до утра и утреннего разогрева помещения. С ночи загружаешь котел. Выставляешь время пробуждения. И всю ночь он будет держать температуру например 35 градусов. Тоесть практически еле тлеть. Топливо при этом расходуется конечно жуть как не экономично. Но зато не надо прибегать раньше всех на работу и растапливать котел в 7 утра. (Котел у меня отапливает мое производство 420 кв.м.) В указанное время котел выйдет из спячки и остатками топлива вполне себе разогреет помещение к приходу сотрудников. Ну а дальше, кто-нибудь его загрузит топливом. Задача чтобы к приходу было уже тепло и не приходилось сидеть в холодном помещении час, пока оно прогреется с ночи. Это спец режим, для того случая, когда нет истопника, который приходит раньше всех на работу.

Этим контроллером мы пользуемся уже 3-й год и как показала практика добавлять датчики туда нет смысла.

Твердотопливные котлы - крайне инерционные системы и устраивать там контроль температуры газов в трубе нет никакого практического смысла. Все отлично работает и без этих датчиков.

Датчик температуры помещения - тоже не нужен, так как у системы отопления огромная инертность и к тому моменту как в помещении установится требуемая температура, котел уже кипеть начнет. В реальности надо будет его глушить намного раньше достижения заданной температуры в помещении. Эту задержку надо вычислять опытным путем под каждое помещение, что нереально в условиях удаленного написания кода. Датчик температурыв помещении применяется как правило  газовых системах отопления. И там где объем теплоносителя маленький. Между установленной температурой на котле и той которую вы получите в помещении есть прямая корреляция. Тоесть например если вы на котле установили 70 градусов то в помещении получите 20, если на котле поставили 75, то в помещении будет 22-23. И это при том, что котел будет включаться/выключаться и температура в нем будет прыгать от 65 до 70, а в помещении при этом практически не будет перепадов. Если же на улице прихватит мороз сильный, то и в помещении упадет температура и надо будет просто котел перевести в более высокий режим для тех же 20 градусов в помещении. Это я про то, что для твердотопливного котла осуществлять управление будет крайне затруднительно по датчику температуры в помещении. Т.к. как уже сказал будет очень большая задержка между включением котла и набором температуры в помещении.

Определение того что котел затух происходит очень просто. Допустим произошло падение температуры ниже установленной. Котел включил вентилятор нагнал воздуха и поднял температуру до нормы и выключился до следующего падения. Но он не просто включает вентилятор. Он еще контролирует сколько времени этот вентилятор включен. И если в течении определенного времени (сколько не помню - смотрите выше в программе) не произойдет повышение температуры до заданной, то считаем что в котле закончилось топливо и переводим его в режим выключен. Но даже в данном режиме котел периодически на короткое время будет включать вентилятор, чтобы продувать топку от скапливающегося там угарного газа. Т.к. При открытии будет БУХ!!!)))

Из деталей надо
- Ардуина любая
- Дисплейный модуль 1602 с I2C шиной
- Цифровой датчик температуры 18B20
- Модуль часов DS1307
- Реле на 5в для коммутации 220в (включение вентилятора)
- Блок питания 5-9в
- Пленочная клавиатура 1х4 или 4 отдельные кнопки
- Корпус

Непосредственно что куда подключать можно посмотреть в самом скетче. Как правило я стараюсь хорошо комментировать свои программы, чтобы потом самому можно было в них разобраться. Выполнять коммерческие заказы я не готов пока и не хочу. Просто делюсь своей разработкой со всеми из простых гуманистических побуждений. Никаких скрытых багов или намеренно внесенных ошибок/опечаток в программе нет. Она полностью рабочая. Единственно можно переписать процедуру опроса кнопок. Т.к. мне не совсем нравится отклик кнопок. Если будут реальные повторы проекта, то для них я могу переписать скетч с другим опросом клавиш, как это я сейчас делаю обычно во всех своих устройствах.

Если есть вопросы по существу - задавайте - отвечу.

АндрейT
Offline
Зарегистрирован: 08.02.2019

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

У меня котлом в данный момент управляет термостат W1209
китайский. На одном реле висит вентилятор на 220вольт,
на втором привод заслонки поддувала на 12 вольт.
Работают синхронно.
Соответственно безопасности-никакой.
Если температура упала,заслонка открылась и вентилятор будет дуть до посинения...
Ваш проект в этом смысле,то что надо.

Я его повторил,пока на столе.(это первое что делаю на ардуино)
Всё работает.
Как Вы и сказали,кнопки немного туповаты.

И в общем вопрос,как можно подключить привод заслонки
(с установками температуры открытия и закрытия)?
 И второй вопрос,вывести например на пин 12 сигнал тревоги?

С ув. Андрей.

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

для кнопок берите библиотеку типа титановый велосипед. Там все типы нажатий, нету delay. Что Клапа зря пыхтел чтоли. 

FreeSky
Offline
Зарегистрирован: 18.01.2016

АндрейT пишет:

Я его повторил,пока на столе.(это первое что делаю на ардуино)
Всё работает.
Как Вы и сказали,кнопки немного туповаты.

Кнопки завтра переделаю полностью, как и обещал.

АндрейT пишет:

И в общем вопрос,как можно подключить привод заслонки
(с установками температуры открытия и закрытия)?

Опишите алгоритм, как вы себе представляете должна работать заслонка. Привод вентилятора, я так понимаю оставим как есть на данный момент. Что должна делать заслонка? Когда открываться? Когда закрываться? Связана как-нибудь с вентилятором или должна жить своей собственной жизнью? И вообще подробно опишите своими словами сам принцип одновременной работы и заслонки и вентилятора.

АндрейT пишет:

И второй вопрос,вывести например на пин 12 сигнал тревоги?

12 пин при работе находится в нуле, если произошла авария - то на нем будет +5в, пока температура не спадет ниже критической (88 Градусов). Если будет обрыв сигнала датчика или показание его температуры равно 0 (глюк или реально 0) - то это тоже будет расценено как авария.

Добовлены строки 39, 47, 46, 219, 220, 221

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include "RTClib.h"
#include <EEPROM.h>


LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 (0x3f) for a 16 chars and 2 line display

OneWire ds(10); // На Пине 10 висит датчик температуры

RTC_DS1307 rtc; //Создаем объект rtc - ЧАСЫ

int Temp=0;
int deltaTemp = EEPROM.read(0);
int rabotaTemp = EEPROM.read(1);
int tlenieTemp = EEPROM.read(2);
int tlenieDoHour = EEPROM.read(3);
int tlenieDoMinute = EEPROM.read(4);
int tleniePereskok = EEPROM.read(5);
int rabotaMode = EEPROM.read(7);
int criticalTemp=88; // Температура аварии
long lastTimeOn = 0; // Время, когда произошло включение вентилятора
long curTimeOn = 0;
long deltaTimeOn = 1200000; // 20 минут - Миллисекунды - разделить на 1000 - будут секунды
long lastTimeOff = 0; // Время, когда произошло выключение вентилятора
long curTimeOff = 0;
long deltaTimeOff = 900000; // 15 минут - Миллисекунды - разделить на 1000 - будут секунды
int releStatus = 0; // 0 - выключен вентилятор на данный момент, 1 - включен вентилятор на данный момент

// ОБРАБОТКА КЛАВИАТУРЫ
char KeyPressed = 'N'; // Текущая нажатая кнопка клавиатуры
int key2=2; // Пин клавиатуры
int key3=3; // Пин клавиатуры
int key4=4; // Пин клавиатуры
int key5=5; // Пин клавиатуры

int relePin=13; // Пин реле вентилятора
int alarmPin=12; // Пин аварии внешний

void setup()
{

pinMode (relePin, OUTPUT); // управление реле вентилятора
digitalWrite (relePin, LOW); // на всякий случай выключим вентилятор
pinMode (alarmPin, OUTPUT); // внешняя линия сигнализации об аварии
digitalWrite (alarmPin, LOW); // Устанавливаем в состояние ВЫКЛ линии сигнализации об аварии 
  
 rtc.begin(); // Инициализируем Часы в формате 24 часа

// Прописываем в экран русские буквы

 byte rus_d[8] = // Д - матрица
 { B00110,
   B01010,
   B01010,
   B01010,
   B01010,
   B11111,
   B10001,
   B00000, };
   
 byte rus_i[8] = // И - матрица
 { B10001,
   B10001,
   B10011,
   B10101,
   B11001,
   B10001,
   B10001,
   B00000, };
   
 byte rus_ya[8] = // Я - матрица
 { B01111,
   B10001,
   B10001,
   B01111,
   B00101,
   B01001,
   B10001,
   B00000, };
   
 byte rus_b[8] = // Б - матрица
 { B11110,
   B10000,
   B10000,
   B11110,
   B10001,
   B10001,
   B11110,
   B00000, };
   
 byte rus_p[8] = // П - матрица
 { B11111,
   B10001,
   B10001,
   B10001,
   B10001,
   B10001,
   B10001,
   B00000, };
   
 byte rus_yy[8] = // Ы - матрица
 { B10001,
   B10001,
   B10001,
   B11101,
   B10011,
   B10011,
   B11101,
   B00000, };
   
 byte rus_mya[8] = // Ь - матрица
 { B10000,
   B10000,
   B10000,
   B11110,
   B10001,
   B10001,
   B11110,
   B00000, };
   
 byte rus_l[8] = // Л - матрица
 { B00110,
   B01001,
   B01001,
   B01001,
   B01001,
   B01001,
   B10001,
   B00000, };

 lcd.init(); // Включаем дисплей
 lcd.createChar(0, rus_d); // Д - прописываем символ
 lcd.createChar(1, rus_i); // И - прописываем символ
 lcd.createChar(2, rus_ya); // Я - прописываем символ
 lcd.createChar(3, rus_b); // Б - прописываем символ
 lcd.createChar(4, rus_p); // П - прописываем символ
 lcd.createChar(5, rus_yy); // Ы - прописываем символ
 lcd.createChar(6, rus_mya); // Ь - прописываем символ
 lcd.createChar(7, rus_l); // Л - прописываем символ
 lcd.backlight();
 lcd.clear();

// ОБРАБОТКА КЛАВИАТУРЫ
// Для включения всех подтягивающих резисторов!!! 
pinMode (key2, INPUT);
digitalWrite (key2, HIGH); // подтягивающий резистор клавиатура
pinMode (key3, INPUT);
digitalWrite (key3, HIGH); // подтягивающий резистор клавиатура
pinMode (key4, INPUT);
digitalWrite (key4, HIGH); // подтягивающий резистор клавиатура
pinMode (key5, INPUT);
digitalWrite (key5, HIGH); // подтягивающий резистор клавиатура

}

void loop()
{
 printTemp();    // Выводим температуру
 printTime();
 printRabotaTemp();
 lcd.setCursor(0, 1);
 if (rabotaMode == 2) { lcd.print ("KOTE\7 OCTAHOB\7EH"); }
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'S') { menuSetup(); }
if (rabotaMode != 5) { // Если работает любой режим кроме тления то меняем температуру котла
     if (KeyPressed == 'U' && rabotaTemp < 85) { rabotaTemp++;
     EEPROM.write(1, rabotaTemp);
     delay (100); }
     if (KeyPressed == 'D' && rabotaTemp > 25) { rabotaTemp--;
     EEPROM.write(1, rabotaTemp);
     delay (100); }
   } else { // Если на данный момент идет тление по времени от и до,то меняем температуру тления
     if (KeyPressed == 'U' && tlenieTemp < 85) { tlenieTemp++;
     EEPROM.write(2, tlenieTemp);
     delay (100); }
     if (KeyPressed == 'D' && tlenieTemp > 25) { tlenieTemp--;
     EEPROM.write(2, tlenieTemp);
     delay (100); }
   }
if (KeyPressed == 'O') { menuRabotaMode(); }
if (KeyPressed == 'R') { openDoor(); }


// delay (200);
}




void printTemp()
{
byte data[2];
ds.reset(); 
ds.write(0xCC);
ds.write(0x44);
delay(200);
ds.reset();
ds.write(0xCC);
ds.write(0xBE);
data[0] = ds.read(); 
data[1] = ds.read();
Temp = (data[1]<< 8)+data[0];
Temp = Temp>>4;
lcd.setCursor(0, 0);
if (Temp >= 0) { lcd.print ("+"); }
if (Temp > -10 && Temp < 10) { lcd.print ("0"); }
lcd.print (Temp);
lcd.print (char(223));
lcd.print (" ");
if (Temp >= criticalTemp or Temp == 0) {
   digitalWrite (13, LOW); // Отключаем вентилятор
   releStatus = 0;
   rabotaMode = 0;
   EEPROM.write(7 , 2); // ОСТАНОВ КОТЛА ПОСЛЕ ВЫХОДА ИЗ АВАРИИ
   lcd.setCursor (0,1); 
   lcd.print ("! A B A P \1 \2 ! ");
   digitalWrite (alarmPin, HIGH); // Устанавливаем в состояние ВКЛЮЧЕНО внешнюю линию сигнализации об аварии
} else {
   digitalWrite (alarmPin, LOW); // Если аварии нет, то и линия сигнализации неактивна
}
deltaTemp = EEPROM.read(0);
if (rabotaMode == 0 && Temp < criticalTemp && Temp != 0) { rabotaMode = 2; } // Выход по падении температыры при аварии в останов котла
if (rabotaMode == 2) { // остановка котла
    digitalWrite (13, LOW); // Отключаем вентилятор
    if (releStatus == 0){ // Если до этого уже был выключен вентилятор
       // провести сравнение и вероятно устроить продувку
           curTimeOff = millis(); // Текущее значение часов
           if ( (lastTimeOff+(deltaTimeOff*2)) < curTimeOff) { // Не разу не включался вентилятор за двойное время
           lastTimeOff = curTimeOff; // Сбрасываем время включения вентилятора на данный момент
             if (Temp > 30) { // Если температура выше 30 гр, то даже в режиме останова продуем котел.
              lcd.setCursor (0,1); 
              lcd.print ("OCTAHOB \4PO");
              lcd.print (char(0));
              lcd.print ("YBKA");
              digitalWrite (13, HIGH);
              delay (5000); // Продувка котла 5 секунд
             }
           digitalWrite (13,LOW); }
       } else { // Если это первое выключение вентилятора при достижении температуры
       releStatus = 0;
       lastTimeOff = millis(); } // Сохраняем текущее время когда выключили вентилятор.
}
  
if (rabotaMode == 4) { // Если котел находится в режиме работы по температуре
  if ( Temp >= rabotaTemp ) { // Если температура равна или выше заданной выключаем вентилятор
    digitalWrite (13, LOW); // Отключаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("PA\3OTA          ");
    if (releStatus == 0){ // Если до этого уже был выключен вентилятор
       // провести сравнение и вероятно устроить продувку
           curTimeOff = millis(); // Текущее значение часов
           if ( (lastTimeOff+deltaTimeOff) < curTimeOff) { // Не разу не включался вентилятор
           lastTimeOff = curTimeOff; // Сбрасываем время включения вентилятора на данный момент
           lcd.setCursor (0,1); 
           lcd.print ("PA\3OTA  \4PO");
           lcd.print (char(0));
           lcd.print ("YBKA");
           digitalWrite (13, HIGH);
           delay (5000); // Продувка котла 5 секунд
           digitalWrite (13,LOW); }
       } else { // Если это первое выключение вентилятора при достижении температуры
       releStatus = 0;
       lastTimeOff = millis(); } // Сохраняем текущее время когда выключили вентилятор.
  }
  if ( (rabotaTemp-Temp) >= deltaTemp ) { // Если температура ниже порогового значения включаем вентилятор
    digitalWrite (13, HIGH); // Включаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("PA\3OTA    PA3");
       lcd.print (char(0));
       lcd.print ("YB"); 
    if (releStatus == 1){ // Если до этого уже был включен вентилятор
       // провести сравнение и вероятно уйти в режим остановки котла
           curTimeOn = millis(); // Текущее значение часов
           if ( (lastTimeOn+deltaTimeOn) < curTimeOn) { // Не произошло разогрева котла за указанное время
           rabotaMode = 2;
           EEPROM.write(7 , rabotaMode); }
       } else { // Если это первое включение вентилятора при достижении температуры
       releStatus = 1;
       lastTimeOn = millis(); } // Сохраняем текущее время когда включили вентилятор.
  }
  if (releStatus == 0){ // Тот случай когда просто выключен вентилятор и температура в пределах дельты
        digitalWrite (13, LOW); // Отключаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("PA\3OTA          ");
  }
}

if (rabotaMode == 5) { // Если котел находится в режиме работы по времени
  if ( Temp >= tlenieTemp ) { // Если температура равна или выше заданной выключаем вентилятор
    digitalWrite (13, LOW); // Отключаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("T\7EH\1E          ");
    if (releStatus == 0){ // Если до этого уже был выключен вентилятор
       // провести сравнение и вероятно устроить продувку
           curTimeOff = millis(); // Текущее значение часов
           if ( (lastTimeOff+deltaTimeOff) < curTimeOff) { // Не разу не включался вентилятор
           lastTimeOff = curTimeOff; // Сбрасываем время включения вентилятора на данный момент
           lcd.setCursor (0,1); 
           lcd.print ("T\7EH\1E  \4PO");
           lcd.print (char(0));
           lcd.print ("YBKA");
           digitalWrite (13, HIGH);
           delay (5000); // Продувка котла 5 секунд
           digitalWrite (13,LOW); }
       } else { // Если это первое выключение вентилятора при достижении температуры
       releStatus = 0;
       lastTimeOff = millis(); } // Сохраняем текущее время когда выключили вентилятор.
  }
  if ( (tlenieTemp-Temp) >= deltaTemp ) { // Если температура ниже порогового значения включаем вентилятор
    digitalWrite (13, HIGH); // Включаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("T\7EH\1E    PA3");
       lcd.print (char(0));
       lcd.print ("YB"); 
    if (releStatus == 1){ // Если до этого уже был включен вентилятор
       // провести сравнение и вероятно уйти в режим остановки котла
           curTimeOn = millis(); // Текущее значение часов
           if ( (lastTimeOn+deltaTimeOn) < curTimeOn) { // Не произошло разогрева котла за указанное время
           rabotaMode = 2;
           EEPROM.write(7 , rabotaMode); }
       } else { // Если это первое включение вентилятора при достижении температуры
       releStatus = 1;
       lastTimeOn = millis(); } // Сохраняем текущее время когда включили вентилятор.
  }
  if (releStatus == 0){ // Тот случай когда просто выключен вентилятор и температура в пределах дельты
        digitalWrite (13, LOW); // Отключаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("T\7EH\1E          ");
  }
}

if (rabotaMode == 3) { // Если котел находится в режиме розжига
  if ( Temp >= rabotaTemp ) { // Если температура равна или выше заданной переходим в режим Работы по температуре
     rabotaMode = 4;  }
  if ( Temp < rabotaTemp ) { // Если температура ниже значения включаем вентилятор
    digitalWrite (13, HIGH); // Включаем вентилятор
       lcd.setCursor (0,1); 
       lcd.print ("PACTO\4KA  PA3");
       lcd.print (char(0));
       lcd.print ("YB"); 
    if (releStatus == 1){ // Если до этого уже был включен вентилятор
       // провести сравнение и вероятно уйти в режим остановки котла
           curTimeOn = millis(); // Текущее значение часов
           if ( (lastTimeOn+(deltaTimeOn*2)) < curTimeOn) { // Не произошло разогрева котла за двойное время
           rabotaMode = 2;
           EEPROM.write(7 , rabotaMode); }
       } else { // Если это первое включение вентилятора при достижении температуры
       releStatus = 1;
       lastTimeOn = millis(); } // Сохраняем текущее время когда включили вентилятор.
  }
}

}

void printRabotaTemp()
{
lcd.setCursor(11, 0);
lcd.print (' ');
if (rabotaMode != 5) {
 if (Temp > rabotaTemp) { lcd.print (char(127)); } else { lcd.print (char(126)); }
 lcd.print (rabotaTemp);
 } else {
 if (Temp > tlenieTemp) { lcd.print (char(127)); } else { lcd.print (char(126)); }
 lcd.print (tlenieTemp); }   
lcd.print (char(223));
}



void GetKey()
{
 KeyPressed = 'N'; // Ничего не нажато!!!   
 if (digitalRead (key2) == LOW) {
  KeyPressed = 'X';
 delay (100);
    if (digitalRead (key2) == LOW) {
      delay (100);
            if (digitalRead (key2) == LOW) {
              delay (100);
                    if (digitalRead (key2) == LOW) {
                      delay (100);
                                if (digitalRead (key2) == LOW) {
                                  delay (100);
                                          if (digitalRead (key2) == LOW) {
                                            KeyPressed = 'R'; // R - Длительное нажание X
                                          }
                                }
                    }
            }
    }
  return; }
 if (digitalRead (key3) == LOW) { 
  KeyPressed = 'D';
  return; }
 if (digitalRead (key4) == LOW) {
   KeyPressed = 'U';
   return; }
 if (digitalRead (key5) == LOW) {
   KeyPressed = 'O';
 delay (100);
    if (digitalRead (key5) == LOW) {
      delay (100);
            if (digitalRead (key5) == LOW) {
              delay (100);
                    if (digitalRead (key5) == LOW) {
                      delay (100);
                                if (digitalRead (key5) == LOW) {
                                  delay (100);
                                          if (digitalRead (key5) == LOW) {
                                            KeyPressed = 'S'; // S - Длительное нажание O
                                          }
                                }
                    }
            }
    }
   return; }
}

void printTime()
{
 DateTime now = rtc.now(); // Считываем время
 lcd.setCursor(5, 0);
 if (now.hour() < 10) { lcd.print ('0'); }
 lcd.print(now.hour());
 lcd.blink_on();
 lcd.print(':');
 lcd.blink_off();
 if (now.minute() < 10) { lcd.print ('0'); }
 lcd.print(now.minute());
 lcd.print(" ");

 
 if (rabotaMode == 5) { // Если активирован режим тления
     if ( tleniePereskok == 0 && ( now.minute() + ( now.hour() * 60 ) ) >= ( tlenieDoMinute + ( tlenieDoHour * 60) ) ) { // Если без перескока и текущее время больше установленного, то тогда выход из тления в работу
     rabotaMode = 4;
     EEPROM.write(7, rabotaMode);
     }
     
if (tleniePereskok == 1 && now.minute() == 0 && now.hour() == 0 ) { tleniePereskok = 0; } // Если полночь то скидываем перескок через полночь
 
 }
}

void menuSetup()
{
 rabotaMode = 6;
 int menuSetupMode = 1;
 lcd.setCursor(5, 0);
 lcd.print (" YCTAHOBKA ");
 lcd.setCursor (0,1);
 lcd.print (char(0));
 lcd.print ("ATA "); 
 lcd.print ("BPEM\2           ");
 delay (300);
 while (1 == 1) { 
 printTemp();    // Выводим температуру
 lcd.setCursor(5, 0);
 lcd.print (" YCTAHOBKA ");
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') {
    rabotaMode = EEPROM.read(7);
    return; }
 if (KeyPressed == 'U' && menuSetupMode == 5) {
     menuSetupMode = 1;
     KeyPressed = 'N'; }
 if (KeyPressed == 'U' && menuSetupMode < 5) {
     menuSetupMode++;
     KeyPressed = 'N'; }
 if (KeyPressed == 'D' && menuSetupMode == 1) {
     menuSetupMode = 5;
     KeyPressed = 'N'; }
 if (KeyPressed == 'D' && menuSetupMode > 1) {
     menuSetupMode--;
     KeyPressed = 'N'; }
 lcd.setCursor (0,1);
 if (menuSetupMode == 1) { lcd.print (char(0));
     lcd.print ("ATA "); 
     lcd.print ("BPEM\2           "); }
 if (menuSetupMode == 2) { lcd.print (char(0));
     lcd.print ("E\7\6TA TEM\4P BK\7"); }
 if (menuSetupMode == 3) { lcd.print ("TEM\4EPAT. PA\3OT\5"); }
 if (menuSetupMode == 4) { lcd.print ("TEM\4EPAT. T\7EH\1\2"); }
 if (menuSetupMode == 5) { lcd.print ("BPEM\2 T\7EH\1\2 ");
     lcd.print (char(0));
     lcd.print ("O "); }
 if (KeyPressed == 'O' && menuSetupMode == 1) { menuSetupData(); }
 if (KeyPressed == 'O' && menuSetupMode == 2) { menuSetupDelta(); }
 if (KeyPressed == 'O' && menuSetupMode == 3) { menuSetupRabota(); }
 if (KeyPressed == 'O' && menuSetupMode == 4) { menuSetupTlenie(); }
 if (KeyPressed == 'O' && menuSetupMode == 5) { menuSetupTlenieDO(); }

  delay (100); // задержка против дребезга кнопок 
 }
}

void menuSetupData() // Установка даты и времени
{
    DateTime now = rtc.now(); // считываем время и дату для установки.
    int setupYear = now.year();
    int setupMonth = now.month();
    int setupDay = now.day();
    // setupWeek = now.dayOfTheWeek();
    int setupHour = now.hour();
    int setupMinute = now.minute();
    // setupSecond = now.second();
    int menuSetupDataMode = 1;
//    delay (200); // задержка против дребезга кнопок 
 while (1 == 1) {
 printTemp();    // Выводим температуру
 lcd.setCursor (0,1);
 if (setupDay < 10) { lcd.print ('0'); }
 lcd.print (setupDay);
 lcd.print ("/");
 if (setupMonth < 10) { lcd.print ('0'); }
 lcd.print (setupMonth);
 lcd.print ("/");
 lcd.print (setupYear);
 lcd.print (" ");
 if (setupHour < 10) { lcd.print ('0'); }
 lcd.print (setupHour);
 lcd.print (":");
 if (setupMinute < 10) { lcd.print ('0'); }
 lcd.print (setupMinute);
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 if (KeyPressed == 'O' && menuSetupDataMode == 5) { 
    rtc.adjust( DateTime(setupYear, setupMonth, setupDay, setupHour, setupMinute,0) );
    return; }
 if (KeyPressed == 'O' && menuSetupDataMode < 5) { menuSetupDataMode++; }
 lcd.blink();
 if (menuSetupDataMode == 1) {
   lcd.setCursor (1,1);
   if (KeyPressed == 'U' && setupDay < 31) { setupDay++; }
   if (KeyPressed == 'U' && setupDay == 31) { setupDay = 1; }
   if (KeyPressed == 'D' && setupDay > 1) { setupDay--; }
   if (KeyPressed == 'D' && setupDay == 1) { setupDay = 31; }
 }
 if (menuSetupDataMode == 2) {
   lcd.setCursor (4,1);
   if (KeyPressed == 'U' && setupMonth < 12) { setupMonth++; }
   if (KeyPressed == 'U' && setupMonth == 12) { setupMonth = 1; }
   if (KeyPressed == 'D' && setupMonth > 1) { setupMonth--; }
   if (KeyPressed == 'D' && setupMonth == 1) { setupMonth = 12; }
 }
 if (menuSetupDataMode == 3) {
   lcd.setCursor (9,1);
   if (KeyPressed == 'U') { setupYear++; }
   if (KeyPressed == 'D' && setupYear > 2000) { setupYear--; }
  }
 if (menuSetupDataMode == 4) {
   lcd.setCursor (12,1);
   if (KeyPressed == 'U' && setupHour < 23) { setupHour++; }
   if (KeyPressed == 'U' && setupHour == 23) { setupHour = 0; }
   if (KeyPressed == 'D' && setupHour > 0) { setupHour--; }
   if (KeyPressed == 'D' && setupHour == 0) { setupHour = 23; }
 }
 if (menuSetupDataMode == 5) {
   lcd.setCursor (15,1); 
   if (KeyPressed == 'U' && setupMinute < 59) { setupMinute++; }
   if (KeyPressed == 'U' && setupMinute == 59) { setupMinute = 0; }
   if (KeyPressed == 'D' && setupMinute > 0) { setupMinute--; }
   if (KeyPressed == 'D' && setupMinute == 0) { setupMinute = 59; }
 }
 delay (100); // задержка против дребезга кнопок 
 }
}

void menuSetupDelta() // Установка гистерезиса срабатывания котла
{
 int setupDelta = EEPROM.read(0);
 lcd.setCursor (0,1);
 lcd.print (char(0));
 lcd.print ("E\7\6TA TEM\4: ");
 if (setupDelta <10) { lcd.print ('0'); }
 lcd.print (setupDelta);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 delay (400);

 while (1 == 1) {
 printTemp();    // Выводим температуру
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 lcd.setCursor (0,1);
 lcd.print (char(0));
 lcd.print ("E\7\6TA TEM\4: ");
 if (setupDelta <10) { lcd.print ('0'); }
 lcd.print (setupDelta);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 if (KeyPressed == 'U' && setupDelta < 10) { setupDelta++; }
 if (KeyPressed == 'D' && setupDelta > 1) { setupDelta--; }
 if (KeyPressed == 'O') {
     EEPROM.write(0, setupDelta);
     return; }
 delay (200); // задержка против дребезга кнопок 

 }
}

void menuSetupRabota() // Установка температуры работы котла
{
 int setupRabota = EEPROM.read(1);
 lcd.setCursor (0,1);
 lcd.print ("PA\3OTA TEM\4: ");
 if (setupRabota <10) { lcd.print ('0'); }
 lcd.print (setupRabota);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 delay (300);

 while (1 == 1) {
 printTemp();    // Выводим температуру
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 lcd.setCursor (0,1);
 lcd.print ("PA\3OTA TEM\4: ");
 if (setupRabota <10) { lcd.print ('0'); }
 lcd.print (setupRabota);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 if (KeyPressed == 'U' && setupRabota < 85) { setupRabota++; }
 if (KeyPressed == 'D' && setupRabota > 25) { setupRabota--; }
 if (KeyPressed == 'O') {
     EEPROM.write(1, setupRabota);
     rabotaTemp = setupRabota;
     return; }
 delay (100); // задержка против дребезга кнопок 

 }
}

void menuSetupTlenie() // Установка температуры при тлении котла
{
 int setupTlenie = EEPROM.read(2);
 lcd.setCursor (0,1);
 lcd.print ("T\7EH\1E TEM\4: ");
 if (setupTlenie <10) { lcd.print ('0'); }
 lcd.print (setupTlenie);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 delay (300);

 while (1 == 1) {
 printTemp();    // Выводим температуру
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 lcd.setCursor (0,1);
 lcd.print ("T\7EH\1E TEM\4: ");
 if (setupTlenie <10) { lcd.print ('0'); }
 lcd.print (setupTlenie);
 lcd.print (char(223));
 lcd.blink();
 lcd.setCursor (14,1);
 if (KeyPressed == 'U' && setupTlenie < 85) { setupTlenie++; }
 if (KeyPressed == 'D' && setupTlenie > 25) { setupTlenie--; }
 if (KeyPressed == 'O') {
     EEPROM.write(2, setupTlenie);
     tlenieTemp = setupTlenie;
     return; }
 delay (100); // задержка против дребезга кнопок 

 }
}


void menuSetupTlenieDO() // Установка времени до которого будет продолжаться тление
{
 int setupTlenieDoHour = EEPROM.read(3);
 int setupTlenieDoMinute = EEPROM.read(4);

 lcd.setCursor (0,1);
 lcd.print ("T\7EH\1E ");
 lcd.print (char(0));
 lcd.print ("O: ");
 if (setupTlenieDoHour <10) { lcd.print ('0'); }
 lcd.print (setupTlenieDoHour);
 lcd.print (':');
 if (setupTlenieDoMinute <10) { lcd.print ('0'); }
 lcd.print (setupTlenieDoMinute);
 lcd.print (' ');
 lcd.blink();
 lcd.setCursor (12,1);
 delay (300);
 int setupTlenieDoMode = 1; // 1- устанавливаем часы, 2- устанавливаем минуты
 while (1 == 1) {
 printTemp();    // Выводим температуру
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') { return; }
 lcd.setCursor (0,1);
 lcd.print ("T\7EH\1E ");
 lcd.print (char(0));
 lcd.print ("O: ");
 if (setupTlenieDoHour <10) { lcd.print ('0'); }
 lcd.print (setupTlenieDoHour);
 lcd.print (':');
 if (setupTlenieDoMinute <10) { lcd.print ('0'); }
 lcd.print (setupTlenieDoMinute);
 lcd.print (' ');
 lcd.blink();
 if ( setupTlenieDoMode == 1) { lcd.setCursor (12,1); } else { lcd.setCursor (15,1); } 
 if ( setupTlenieDoMode == 2 && KeyPressed == 'O') {
    EEPROM.write(3 , setupTlenieDoHour);
    EEPROM.write(4 , setupTlenieDoMinute);
    return; } 
 if ( setupTlenieDoMode == 1 && KeyPressed == 'O') { setupTlenieDoMode = 2; }
 
 if (setupTlenieDoMode == 1 && KeyPressed == 'U' && setupTlenieDoHour < 23) { setupTlenieDoHour++; }
 if (setupTlenieDoMode == 1 && KeyPressed == 'U' && setupTlenieDoHour >= 23) { setupTlenieDoHour = 0; }
 if (setupTlenieDoMode == 1 && KeyPressed == 'D' && setupTlenieDoHour > 0) { setupTlenieDoHour--; }
 if (setupTlenieDoMode == 1 && KeyPressed == 'D' && setupTlenieDoHour == 0) { setupTlenieDoHour = 23; }

 if (setupTlenieDoMode == 2 && KeyPressed == 'U' && setupTlenieDoMinute < 59) { setupTlenieDoMinute++; }
 if (setupTlenieDoMode == 2 && KeyPressed == 'U' && setupTlenieDoMinute >= 59) { setupTlenieDoMinute = 0; }
 if (setupTlenieDoMode == 2 && KeyPressed == 'D' && setupTlenieDoMinute > 0) { setupTlenieDoMinute--; }
 if (setupTlenieDoMode == 2 && KeyPressed == 'D' && setupTlenieDoMinute == 0) { setupTlenieDoMinute = 59; }

 delay (100); // задержка против дребезга кнопок 

 }
}

void menuRabotaMode()
{
 rabotaMode = 6;
 int rabotaModeTmp = EEPROM.read(7);
 if ( rabotaModeTmp == 2 ) { rabotaModeTmp = 3; }
 lcd.setCursor(5, 0);
 lcd.print ("B\1");
 lcd.print (char(0));
 lcd.print (" PA\3OT\5 ");
 delay (300);
 while (1 == 1) { 
 printTemp();    // Выводим температуру
 lcd.setCursor(5, 0);
 lcd.print ("B\1");
 lcd.print (char(0));
 lcd.print (" PA\3OT\5 ");
 lcd.setCursor (0,1);
 if (rabotaModeTmp == 1) { lcd.print ("OTKP\5T\6 TO\4KY   "); }
 if (rabotaModeTmp == 2) { lcd.print ("OCTAHOBKA KOT\7A "); }
 if (rabotaModeTmp == 3) { lcd.print ("PACTO\4KA KOT\7A  "); }
 if (rabotaModeTmp == 4) { lcd.print ("\4O TEM\4EPATYPE  "); }
 if (rabotaModeTmp == 5) { lcd.print ("T\7EH\1E ");
    lcd.print (char(0));
    lcd.print ("O BPEMEH"); }
 GetKey();       // Считываем нажатую кнопку
 if (KeyPressed == 'X') {
     rabotaMode = EEPROM.read(7);
     return; }
 if (KeyPressed == 'U' && rabotaModeTmp >=5 ) {
     rabotaModeTmp = 1;
     KeyPressed = 'N'; }
 if (KeyPressed == 'U' && rabotaModeTmp < 5) { rabotaModeTmp++; }
 if (KeyPressed == 'D' && rabotaModeTmp > 1) { rabotaModeTmp--; }
 if (KeyPressed == 'D' && rabotaModeTmp == 1) { rabotaModeTmp = 5; }
 if (KeyPressed == 'O' && rabotaModeTmp == 1) {
     rabotaModeTmp = 1;
     openDoor();
     return; }
 if (KeyPressed == 'O' && rabotaMode > 1) {
     if (Temp < 30 && rabotaModeTmp == 4) {rabotaModeTmp = 3;}
     if (rabotaModeTmp == 5) {
       tleniePereskok = 0;
       tlenieDoHour = EEPROM.read(3);
       tlenieDoMinute = EEPROM.read(4);
       DateTime now = rtc.now();
       if ( ( now.minute() + ( now.hour() * 60 ) ) > ( tlenieDoMinute + ( tlenieDoHour * 60) ) ) {
         tleniePereskok = 1;
         EEPROM.write(5 , tleniePereskok);
       }
     }
     rabotaMode = rabotaModeTmp;
     EEPROM.write(7 , rabotaMode);
     return; }
 
 }
}
void openDoor()
{
 rabotaMode = 1;
 printTemp();    // Выводим температуру
 printTime();
 lcd.setCursor (11,0);
 lcd.print ("\4AY3A");
 lcd.setCursor (0,1);
 lcd.print ("  \4PO");
 lcd.print (char(0));
 lcd.print ("YB TO\4K\1  ");
 digitalWrite (13, HIGH);
 delay (3000);
 digitalWrite (13, LOW);
 lcd.setCursor (0,1);
 lcd.print (" OTKP\5T\6 TO\4KY ");
 GetKey();
 while (KeyPressed == 'N') {
 printTemp();    // Выводим температуру
 printTime();
 GetKey(); }
 rabotaMode = EEPROM.read(7);
}
АндрейT
Offline
Зарегистрирован: 08.02.2019

Доброго дня.
Перезалил скетч-работает)))
Спасибо большое.
Я как раз закончил собирать всё на плате.
Буду приступать к корпусу и перифирии.

"Опишите алгоритм, как вы себе представляете должна работать заслонка. Привод вентилятора, я так понимаю оставим как есть на данный момент. Что должна делать заслонка? Когда открываться? Когда закрываться? Связана как-нибудь с вентилятором или должна жить своей собственной жизнью? И вообще подробно опишите своими словами сам принцип одновременной работы и заслонки и вентилятора."

На данный момент, заслонка привязяна к вентилятору.
То есть, вентилятор включился-заслонка на поддувале-открылась.
Есть ручное управление и заслонкои и вентилятором.
Часто бывает,что достаточно открыть заслонку,без включения вентилятора,
чтобы температура начала подниматься(естественная тяга).
Если этого недостаточно-включается вентилятор.

В общем алгоритм вижу такой:

1.Растопка-заслонка открывается,вентилятор-выключен.
2.При достижении рабочей температуры (например-60*с)-заслонка закрывается.
3.При падении температуры на дельта-заслонка открывается(то есть, работой котла пока управляет только заслонка)
4.Продувка-работают и заслонка и вентилятор.
  (На второй день топки обычно заслонки уже не хватает(или тяга бывает слабой).
5.Температура упала на дельта,заслонка открылась,но этого недостаточно для набора температуры(температура или не растёт или продолжает падать-включился вентилятор,наверное по времени..?
Или как вариант-температура упала на дельту заслонки-заслонка открылась.
Температура падает дальше-на дельту вентилятора-включился вентилятор,при открытой заслонке)
Температура выросла, заслонка закрылась-вентилятор-отключился.
  (То есть,после выключения вентилятора,заслонка закрывается-во всех случаях).
 6.Остановка котла-заслонка закрыта,вентилятор-выключен.
7.Авария-заслонка закрыта, вентилятор выключен,сигнал тревоги.

Как то так.

Привод заслонки у меня реализован от моторедуктора дворников,через пассик на заслонку.
То есть достаточно подать сигнал на 2-3 секунды на Н мост.
Сигнала надо 2,на открытие и на закрытие.(2 пина)
Хотя можно и с одного реализовать,но это лишние реле,герконы и полевики.

Понимаю, я слишком мого хочу.
Поэтому это даже не просьба.
Просто-хотелка.

В любом случае большое спасибо за скетч.

С ув Андрей.

 

АндрейT
Offline
Зарегистрирован: 08.02.2019

Да, как всё соберу,обязательно сделаю фотки.

FreeSky
Offline
Зарегистрирован: 18.01.2016

Я где-то так себе и представлял сам алгоритм работы.

Более кратко и на мой взгляд более логично.

1. В настройках будет. Время задержки перед включением наддува (например 7 мин.)
2. В настройках будет. Время задержки перед включением наддува при растопке (нарпимер 20 мин.)
3. В настройках будет. Время активности управляющего сигнала заслонки в 1/10 секундах. (например 25 - 2,5 секунды)
4. Растопка. Открываем заслонку. Если за определенное время (20 мин.) температура не выросла до установленной - включаем вентилятор.
5. Работа кота. При падении ниже дельты открываем заслонку. Если за определенное время (7 мин.) температура не выросла до установленной - включаем вентилятор.
6. Продувка перед открытием топки - заслонка открыта и вентилятор включен.
7. Авария - заслонка закрыта и вентилятор выключен. Сигнал тревоги.
8. Заслонка управляется тремя пинами.
9. Первый пин при открытии заслонки становится в +5в на время указанное в п.3 (2,5 сек.). В остальных случаях он всегда в нуле.
10. Второй пин при закрытии заслонки становится в +5в на время указанное в п.3 (2,5 сек.). В остальных случаях он всегда в нуле.
11. Третий пин при закрытой заслонке - 0в, при открытой заслонке +5в. Это либо управление по 1 пину - либо сигнальный пин состояния заслонки.

Если установить время в п.1 и п.2 равное нулю - то котел будет управляться только вентиляторм.
Если установить это время больше 40мин - то котел будет управляться только заслонкой без включения вентилятора наддува.

Если есть какие-то комментарии - напишите пока я еще не переписал скетч.

 

АндрейT
Offline
Зарегистрирован: 08.02.2019

Добрый день.
Вроде всё нормально.
Единственное,при растопке вентилятор включать не надо(и так тянетчерез чур),
но за 20-30 минут температура уже войдет в рабочую,так что он и не включится.))
Ну и режим тления оставте.
Очень удобно,на работу ушел-тление,а к приходу домой-температура поднялась.

Это будет просто "бомба"  ))) в хорошем смысле.

С ув.Андрей.

FreeSky
Offline
Зарегистрирован: 18.01.2016

Серьезно переделал скетч. Теперь котел может управляться одновременно и заслонкой и вентилятором. Или по отдельности заслонкой или вентилятором. Также полностью переписал опрос кнопок - теперь они с большего адекватно реагируют и не тупят особо. Все параметры и значения на которые опирается в работе контроллер теперь можно менять через настройки.
Если установить максимальное время включенного вентилятора в 0 - то котел будет управляться только заслонкой
Если установить максимальное время открытой заслонки в 0 и длительность управляющего сигнала заслонки тоже в 0 - то котел будет работать только от вентилятора.
Длительность управляющего сигнала заслонки - это время на которое будет включаться двигатель привода заслонки чтобы ее полностью открыть или закрыть.

Пины:
2 - КНОПКА [X] (выход)
3 - КНОПКА [ВНИЗ] (меньше)
4 - КНОПКА [ВВЕРХ] (больше)
5 - КНОПКА [O] (меню)
Кнопка считается нажатой, если ее пин соеденится с землей
7 - Сигнал открытия заслонки - при открытии заслонки здесь определенное время будет +5в - Это время и определяется настройкой длительности управляющего сигнала заслонки
8 - Сигнал закрытия заслонки - при закрытии заслонки здесь определенное время будет +5в - Это время и определяется настройкой длительности управляющего сигнала заслонки
9 - Управляющий сигнал заслонки поддувала - +5в когда открыта заслонка и 0в, когда она закрыта - Альтернативный вариант управления
10 - Датчик температуры - его цифровой сигнал OneWire
12 - Внешний сигнал сигнализации аварии - +5в если произошло превышение температуры выше допустимой и 0в, если температура не превышает допустимую или не равна 0 или нет обрыва датчика температуры
13 - Управляющий сигнал вентилятора наддува - +5в когда включен вентилятор и 0в, когда вентилятор выключен
Часы и экран подлючаются на стандартные пины I2C

Кнопки:
X - ВЫХОД - Выход из меню без сохранения, Длительное нажатие в режимах растопки, работы, тления и останова - Продув топки с последующим открытием
ВВЕРХ и ВНИЗ - переход по пунктам меню, изменение значений. В режиме растопки, работы и останова - меняет значение установленной температуры работы котла. В режиме тления - меняет значение установленной температуры тления.
O - МЕНЮ - Вход в меню. Подтверждение введенного значения с сохранением. Выбор пункта меню. Длительное нажатие в режиме растопки, работы, тления и останова - переход к настройкам параметров контроллера.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include <RTClib.h>
#include <EEPROM.h>

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 (0x3f) for a 16 chars and 2 line display
OneWire ds(10); // На Пине 10 висит датчик температуры
RTC_DS1307 rtc; //Создаем объект rtc - ЧАСЫ

int CurTemp=0; // Текущая температура котла
int DeltaTemp = EEPROM.read(0); // Гистерезис включения котла (по умолчанию - 5)
int RabotaTemp = EEPROM.read(1); // Рабочая темперадура котла (по умолчанию - 70)
int TlenieTemp = EEPROM.read(2); // Температура тления (по умолчанию - 35)
int TlenieDoHour = EEPROM.read(3); // Время часов до которого осуществляется тление (по умолчанию - 7)
int TlenieDoMinute = EEPROM.read(4); // Время минут до которого осуществляется тление (по умолчанию - 0)
int RabotaMode = EEPROM.read(5); // Режим работы котла (по умолчанию - 2)
int CriticalTemp = EEPROM.read(6); // Температура аварии (по умолчанию - 88)
long CurTime = millis(); // Текущее время
long LastTimeOn = CurTime; // Время, когда произошло открытие/закрытие заслонки или включение/выключение вентилятора
long SavedTimeOnVent = EEPROM.read(7); // Максимальное время включенного вентилятора в минутах - свыше этого ОСТАНОВ КОТЛА (по умолчанию - 20)
// При использовании только заслонки без вентилятора наддува вышеуказанный параметр установить в 0
long DeltaTimeOnVent = SavedTimeOnVent * 60000; // Максимальное время включенного вентилятора в миллисекундах
// Если вышеуказанное время равно 0 - то котел работает без вентилятора, только с одной заслонкой
long SavedTimeOnZasl = EEPROM.read(8); // Максимальное время открытой заслонки в минутах - свыше этого ВКЛЮЧЕНИЕ ВЕНТИЛЯТОРА (по умолчанию - 10)
// При неиспользуемой заслонке вышеуказанный параметр установить в 0
long DeltaTimeOnZasl = SavedTimeOnZasl * 60000; // Максимальное время открытой заслонки в миллисекундах
// Если вышеуказанное время равно 0 - то котел работает без заслонки, только с вентилятором
long SavedTimeOff = EEPROM.read(9); // Промежутки времени в минутах, через которые котел будет продуваться при простое. (по умолчанию - 15)
long DeltaTimeOff = SavedTimeOff * 60000; // Промежутки времени в миллисекундах, через которые котел будет продуваться при простое
int ZaslTimeOnOff = EEPROM.read(10); // Время на которое активируются линии управления положением заслонки - миллисекунды делённые на 100 (по умолчанию 25 или 2500 миллисекунд)
// При неиспользуемой заслонке вышеуказанный параметр установить в 0
int ZaslVentStatus = 0; // 0 - выключен вентилятор на данный момент, 1 - открыта заслонка на данный момент, 2 - включен вентилятор на данный момент

// ОБРАБОТКА КЛАВИАТУРЫ
char KeyPressed = 'N'; // Текущая нажатая кнопка клавиатуры
int PinKey2=2; // Пин клавиатуры
int PinKey3=3; // Пин клавиатуры
int PinKey4=4; // Пин клавиатуры
int PinKey5=5; // Пин клавиатуры

int vent_ON=13; // Пин реле вентилятора
int Zasl_ON=7; // Пин на котором будет +5v для открытия заслонки (некоторое время)
int Zasl_OFF=8; // Пин на котором будет +5v для закрытия заслонки (некоторое время)
int Zasl_POS=9; // Пин положения заслонки - +5v - открыта, 0 - закрыта
int alarmPin=12; // Пин аварии внешний

void setup() {
  pinMode (vent_ON, OUTPUT); // управление реле вентилятора
  digitalWrite (vent_ON, LOW); // на всякий случай выключим вентилятор
  pinMode (Zasl_ON, OUTPUT); // управляющий сигнал открытия заслонки
  digitalWrite (Zasl_ON, LOW); //  выключим
  pinMode (Zasl_OFF, OUTPUT); // управляющий сигнал закрытия заслонки
  digitalWrite (Zasl_OFF, LOW); //  выключим
  pinMode (Zasl_POS, OUTPUT); // управляющий сигнал положения заслонки
  digitalWrite (Zasl_POS, LOW); //  выключим    
  pinMode (alarmPin, OUTPUT); // внешняя линия сигнализации об аварии
  digitalWrite (alarmPin, LOW); // Устанавливаем в состояние ВЫКЛ линии сигнализации об аварии 
  rtc.begin(); // Инициализируем Часы в формате 24 часа
  
// Прописываем в экран русские буквы
  byte rus_d[8] = {B00110, B01010, B01010, B01010, B01010, B11111, B10001, B00000}; // Д - матрица
  byte rus_i[8] = {B10001, B10001, B10011, B10101, B11001, B10001, B10001, B00000}; // И - матрица
  byte rus_ya[8] = {B01111, B10001, B10001, B01111, B00101, B01001, B10001, B00000}; // Я - матрица
  byte rus_b[8] = {B11110, B10000, B10000, B11110, B10001, B10001, B11110, B00000}; // Б - матрица
  byte rus_p[8] = {B11111, B10001, B10001, B10001, B10001, B10001, B10001, B00000}; // П - матрица
  byte rus_yy[8] = {B10001, B10001, B10001, B11101, B10011, B10011, B11101, B00000}; // Ы - матрица
  byte rus_mya[8] = {B10000, B10000, B10000, B11110, B10001, B10001, B11110, B00000}; // Ь - матрица
  byte rus_l[8] = {B00110, B01001, B01001, B01001, B01001, B01001, B10001, B00000}; // Л - матрица
  lcd.init(); // Включаем дисплей
  lcd.createChar(0, rus_d); // Д - прописываем символ
  lcd.createChar(1, rus_i); // И - прописываем символ
  lcd.createChar(2, rus_ya); // Я - прописываем символ
  lcd.createChar(3, rus_b); // Б - прописываем символ
  lcd.createChar(4, rus_p); // П - прописываем символ
  lcd.createChar(5, rus_yy); // Ы - прописываем символ
  lcd.createChar(6, rus_mya); // Ь - прописываем символ
  lcd.createChar(7, rus_l); // Л - прописываем символ
  lcd.backlight();
  lcd.clear();
  lcd.noBlink();
  
// ОБРАБОТКА КЛАВИАТУРЫ включения всех подтягивающих резисторов!!! 
  pinMode (PinKey2, INPUT); // Кнопка - X - ОТМЕНА, ОТКРЫТЬ ТОПКУ
  digitalWrite (PinKey2, HIGH); // подтягивающий резистор клавиатура
  pinMode (PinKey3, INPUT); // Кнопка - D - ВНИЗ, МЕНЬШЕ
  digitalWrite (PinKey3, HIGH); // подтягивающий резистор клавиатура
  pinMode (PinKey4, INPUT); // Кнопка - U - ВВЕРХ, БОЛЬШЕ
  digitalWrite (PinKey4, HIGH); // подтягивающий резистор клавиатура
  pinMode (PinKey5, INPUT); // Кнопка - O - МЕНЮ, НАСТРОЙКИ, ВВОД
  digitalWrite (PinKey5, HIGH); // подтягивающий резистор клавиатура

  if (EEPROM.read(10) > 99) { // Если не установлены значения в памяти в 10-й ячейке
    resetToDefaults(); // То скидываем все настройки к дефолтным и прописываем их в память
  }
}

void loop() {
  printTemp(); // Выводим температуру в верхнюю строчку
  printTime(); // Выводим время в верхнюю строчку
  printRabotaTemp(); // Выводим установленную температуру работы / тления
  KotelMode(); // Обрабатываем текущий режим работы котла
  GetKey(); // Считываем нажатую кнопку
  if (KeyPressed == 'S') { menuSetup(); }
  if (KeyPressed == 'O') { menuRabotaMode(); }
  if (KeyPressed == 'R') { openDoor(); }
  if (RabotaMode != 5) { // Если работает любой режим кроме тления то меняем температуру котла
    if (KeyPressed == 'U' && RabotaTemp < 85) { RabotaTemp++;
      EEPROM.write(1, RabotaTemp);
    }
    if (KeyPressed == 'D' && RabotaTemp > 25) { RabotaTemp--;
      EEPROM.write(1, RabotaTemp);
    }
  } else { // Если на данный момент идет тление по времени от и до,то меняем температуру тления
    if (KeyPressed == 'U' && TlenieTemp < 85) { TlenieTemp++;
      EEPROM.write(2, TlenieTemp);
    }
    if (KeyPressed == 'D' && TlenieTemp > 25) { TlenieTemp--;
      EEPROM.write(2, TlenieTemp);
    }
  }
}

void menuSetup() { // Выбор режима работы котла
  int menuSetupMode = 1; // Выбранный режим
  while (1 == 1) { 
    printTemp(); // Выводим температуру
    lcd.setCursor(5, 0);
    lcd.print (" YCTAHOBKA ");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') {
      return; // Выходим из выбора режима работы
    } else if (KeyPressed == 'U' ) {
      menuSetupMode++;
    } else if (KeyPressed == 'D' ) {
      menuSetupMode--;
    }
    if (menuSetupMode < 1) {menuSetupMode = 11;}
    if (menuSetupMode > 11) {menuSetupMode = 1;}
    lcd.setCursor (0,1);
    if (menuSetupMode == 1) { lcd.print (char(0));
      lcd.print ("ATA BPEM\2           "); }
    else if (menuSetupMode == 2) { lcd.print (char(0));
      lcd.print ("E\7\6TA TEM\4P BK\7"); }
    else if (menuSetupMode == 3) { lcd.print ("TEM\4EPAT. PA\3OT\5"); }
    else if (menuSetupMode == 4) { lcd.print ("TEM\4EPAT. T\7EH\1\2"); }
    else if (menuSetupMode == 5) { lcd.print ("BPEM\2 T\7EH\1\2 ");
      lcd.print (char(0));
      lcd.print ("O "); }
    else if (menuSetupMode == 6) { lcd.print ("TEM\4EPAT. ABAP\1\1"); }
    else if (menuSetupMode == 7) { lcd.print ("BPEM\2 BK\7 BEHT\1\7"); }
    else if (menuSetupMode == 8) { lcd.print ("BPEM\2 OTKP 3AC\7H"); }
    else if (menuSetupMode == 9) { lcd.print ("\1HTEPBA\7 \4PO");
      lcd.print (char(0));
      lcd.print ("YBA");}
    else if (menuSetupMode == 10) { lcd.print ("Y\4PAB\7H 3AC\7OHK\1"); }
    else if (menuSetupMode == 11) { lcd.print ("C\3POC \4APAMETPOB"); }
    if (KeyPressed == 'O') {
      if (menuSetupMode == 1) { menuSetupData(); }
      else if (menuSetupMode == 2) { menuSetupDelta(); }
      else if (menuSetupMode == 3) { menuSetupRabota(); }
      else if (menuSetupMode == 4) { menuSetupTlenie(); }
      else if (menuSetupMode == 5) { menuSetupTlenieDO(); }
      else if (menuSetupMode == 6) { menuSetupCriticalTemp(); }
      else if (menuSetupMode == 7) { menuSetupDeltaTimeOnVent(); }
      else if (menuSetupMode == 8) { menuSetupDeltaTimeOnZasl(); }
      else if (menuSetupMode == 9) { menuSetupDeltaTimeOff(); }
      else if (menuSetupMode == 10) { menuSetupZaslTimeOnOff(); }
      else if (menuSetupMode == 11) { menuSetupResetDefaults(); }
    }
  }
}

void menuSetupData() { // Установка даты и времени
  DateTime now = rtc.now(); // считываем время и дату для установки.
  int setupYear = now.year();
  int setupMonth = now.month();
  int setupDay = now.day();
  int setupHour = now.hour();
  int setupMinute = now.minute();
  int menuSetupDataMode = 1;
  while (1 == 1) {
    printTemp();    // Выводим температуру
    lcd.setCursor (0,1);
    if (setupDay < 10) { lcd.print ('0'); }
    lcd.print (setupDay);
    lcd.print ("/");
    if (setupMonth < 10) { lcd.print ('0'); }
    lcd.print (setupMonth);
    lcd.print ("/");
    lcd.print (setupYear);
    lcd.print (" ");
    if (setupHour < 10) { lcd.print ('0'); }
    lcd.print (setupHour);
    lcd.print (":");
    if (setupMinute < 10) { lcd.print ('0'); }
    lcd.print (setupMinute);
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; }
    if (KeyPressed == 'O' && menuSetupDataMode == 5) { 
      rtc.adjust( DateTime(setupYear, setupMonth, setupDay, setupHour, setupMinute,0) );
      lcd.noBlink();
      return;
    }
    if (KeyPressed == 'O' && menuSetupDataMode < 5) { menuSetupDataMode++; }
    lcd.blink();
    if (menuSetupDataMode == 1) {
      lcd.setCursor (1,1);
      if (KeyPressed == 'U') { setupDay++; }
      if (KeyPressed == 'D') { setupDay--; }
    }
    if (menuSetupDataMode == 2) {
      lcd.setCursor (4,1);
      if (KeyPressed == 'U') { setupMonth++; }
      if (KeyPressed == 'D') { setupMonth--; }
    }
    if (menuSetupDataMode == 3) {
      lcd.setCursor (9,1);
      if (KeyPressed == 'U') { setupYear++; }
      if (KeyPressed == 'D') { setupYear--; }
    }
    if (menuSetupDataMode == 4) {
      lcd.setCursor (12,1);
      if (KeyPressed == 'U') { setupHour++; }
      if (KeyPressed == 'D') { setupHour--; }
    }
    if (menuSetupDataMode == 5) {
      lcd.setCursor (15,1); 
      if (KeyPressed == 'U') { setupMinute++; }
      if (KeyPressed == 'D') { setupMinute--; }
    }
    if (setupDay < 1) { setupDay=31; }
    if (setupDay > 31) { setupDay=1; }    
    if (setupMonth < 1) { setupMonth=12; }
    if (setupMonth > 12) { setupMonth=1; }           
    if (setupYear < 2019) { setupYear=2019; }
    if (setupHour < 0) { setupHour=23; }
    if (setupHour > 23) { setupHour=0; }       
    if (setupMinute < 0) { setupMinute=59; }
    if (setupMinute > 59) { setupMinute=0; }      
  }
}

void menuSetupDelta() { // Установка гистерезиса срабатывания котла
  while (1 == 1) {
    printTemp();    // Выводим температуру
    GetKey();       // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; }
    lcd.setCursor (0,1);
    lcd.print (char(0));
    lcd.print ("E\7\6TA TEM\4: ");
    if (DeltaTemp <10) { lcd.print ('0'); }
    lcd.print (DeltaTemp);
    lcd.print (char(223));
    if (KeyPressed == 'U') { DeltaTemp++; }
    if (KeyPressed == 'D') { DeltaTemp--; }
    if (DeltaTemp > 10) { DeltaTemp = 1; }
    if (DeltaTemp < 1) { DeltaTemp = 10; }
    if (KeyPressed == 'O') {
      EEPROM.write(0, DeltaTemp);
      return;
    }
  }
}

void menuSetupRabota() { // Установка температуры работы котла
  while (1 == 1) {
    printTemp();    // Выводим температуру
    GetKey();       // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; }
    lcd.setCursor (0,1);
    lcd.print ("PA\3OTA TEM\4: ");
    if (RabotaTemp <10) { lcd.print ('0'); }
    lcd.print (RabotaTemp);
    lcd.print (char(223));
    if (KeyPressed == 'U') { RabotaTemp++; }
    if (KeyPressed == 'D') { RabotaTemp--; }
    if (RabotaTemp > 85) { RabotaTemp = 25; }
    if (RabotaTemp < 25) { RabotaTemp = 85; }
    if (RabotaTemp >= CriticalTemp) { RabotaTemp = CriticalTemp-3; }    
    if (KeyPressed == 'O') {
      EEPROM.write(1, RabotaTemp);
      return;
    }
  }
}

void menuSetupTlenie() { // Установка температуры при тлении котла
  while (1 == 1) {
    printTemp();    // Выводим температуру
    GetKey();       // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; }
    lcd.setCursor (0,1);
    lcd.print ("T\7EH\1E TEM\4: ");
    if (TlenieTemp <10) { lcd.print ('0'); }
    lcd.print (TlenieTemp);
    lcd.print (char(223));
    if (KeyPressed == 'U') { TlenieTemp++; }
    if (KeyPressed == 'D') { TlenieTemp--; }
    if (TlenieTemp > 85) { TlenieTemp = 20; }
    if (TlenieTemp < 20) { TlenieTemp = 85; }
    if (TlenieTemp >= CriticalTemp) { TlenieTemp = CriticalTemp-3; }    
    if (KeyPressed == 'O') {
      EEPROM.write(2, TlenieTemp);
      return;
    }
  }
}

void menuSetupTlenieDO() { // Установка времени до которого будет продолжаться тление
  int setupTlenieDoMode = 1; // 1- устанавливаем часы, 2- устанавливаем минуты
  while (1 == 1) {
  printTemp();    // Выводим температуру
  GetKey();       // Считываем нажатую кнопку
  if (KeyPressed == 'X') {
    lcd.noBlink();
    return; }
  lcd.setCursor (0,1);
  lcd.print ("T\7EH\1E ");
  lcd.print (char(0));
  lcd.print ("O: ");
  if (TlenieDoHour <10) { lcd.print ('0'); }
  lcd.print (TlenieDoHour);
  lcd.print (':');
  if (TlenieDoMinute <10) { lcd.print ('0'); }
  lcd.print (TlenieDoMinute);
  lcd.print (' ');
  lcd.blink();
  if ( setupTlenieDoMode == 1) { lcd.setCursor (12,1); } else { lcd.setCursor (15,1); } 
  if ( setupTlenieDoMode == 2 && KeyPressed == 'O') {
    EEPROM.write(3 , TlenieDoHour);
    EEPROM.write(4 , TlenieDoMinute);
    return;
  } 
  if (setupTlenieDoMode == 1 && KeyPressed == 'O') { setupTlenieDoMode = 2; }
  if (setupTlenieDoMode == 1 && KeyPressed == 'U') { TlenieDoHour++; }
  if (setupTlenieDoMode == 1 && KeyPressed == 'D') { TlenieDoHour--; }
  if (setupTlenieDoMode == 2 && KeyPressed == 'U') { TlenieDoMinute++; }
  if (setupTlenieDoMode == 2 && KeyPressed == 'D') { TlenieDoMinute--; }
  if (TlenieDoHour > 23) {TlenieDoHour = 0;}
  if (TlenieDoHour < 0) {TlenieDoHour = 23;}
  if (TlenieDoMinute > 59) {TlenieDoMinute = 0;}
  if (TlenieDoMinute < 0) {TlenieDoMinute = 59;}  
  }
}

void menuSetupCriticalTemp() { // Установка Критической температуры
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("ABAP\1\2 TEM\4: ");
    lcd.print (CriticalTemp);
    lcd.print (char(223));
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { CriticalTemp++; }
    if (KeyPressed == 'D') { CriticalTemp--; }
    if (CriticalTemp > 95) { CriticalTemp = 75; }
    if (CriticalTemp < 75) { CriticalTemp = 95; }
    if (TlenieTemp >= CriticalTemp) { TlenieTemp = CriticalTemp-3; }  
    if (RabotaTemp >= CriticalTemp) { RabotaTemp = CriticalTemp-3; }    
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(6, CriticalTemp);
      return;
    }
  }
}

void menuSetupDeltaTimeOnVent() { // Установка максимального времени включенного вентилятора
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("BK\7 BEHT: ");
    if (SavedTimeOnVent <10) { lcd.print ('0'); }
    lcd.print (SavedTimeOnVent);
    lcd.print (" M\1H");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { SavedTimeOnVent++; }
    if (KeyPressed == 'D') { SavedTimeOnVent--; }
    if (SavedTimeOnVent > 30) { SavedTimeOnVent = 0; }
    if (SavedTimeOnVent < 0) { SavedTimeOnVent = 30; }
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(7, SavedTimeOnVent);
      DeltaTimeOnVent = SavedTimeOnVent * 60000;
      return;
    }
  }
}

void menuSetupDeltaTimeOnZasl() { // Установка максимального времени открытой заслонки
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("OTKP 3AC\7:");
    if (SavedTimeOnZasl <10) { lcd.print ('0'); }
    lcd.print (SavedTimeOnZasl);
    lcd.print (" M\1H");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { SavedTimeOnZasl++; }
    if (KeyPressed == 'D') { SavedTimeOnZasl--; }
    if (SavedTimeOnZasl > 30) { SavedTimeOnZasl = 0; }
    if (SavedTimeOnZasl < 0) { SavedTimeOnZasl = 30; }
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(8, SavedTimeOnZasl);
      DeltaTimeOnZasl = SavedTimeOnZasl * 60000;
      return;
    }
  }
}

void menuSetupDeltaTimeOff() { // Установка максимального времени простоя котла без включения
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("ABTO\4PO");
    lcd.print (char(0));
    lcd.print ("YB:");
    lcd.print (SavedTimeOff);
    lcd.print ("M\1H");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { SavedTimeOff++; }
    if (KeyPressed == 'D') { SavedTimeOff--; }
    if (SavedTimeOff > 30) { SavedTimeOff = 15; }
    if (SavedTimeOff < 15) { SavedTimeOff = 30; }
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(9, SavedTimeOff);
      DeltaTimeOff = SavedTimeOff * 60000;
      return;
    }
  }
}

void menuSetupZaslTimeOnOff() { // Установка времени на которое активизируются управляющие сигналы заслонки
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("Y\4PB\7 3AC\7: ");
    lcd.print (ZaslTimeOnOff / 10); // выводим десятки
    lcd.print (".");
    lcd.print (ZaslTimeOnOff - (ZaslTimeOnOff / 10)*10); // выводим единицы
    lcd.print ("c");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { ZaslTimeOnOff++; }
    if (KeyPressed == 'D') { ZaslTimeOnOff--; }
    if (ZaslTimeOnOff > 99) { ZaslTimeOnOff = 0; }
    if (ZaslTimeOnOff < 0) { ZaslTimeOnOff = 99; }
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(10, ZaslTimeOnOff);
      return;
    }
  }
}

void menuSetupResetDefaults() { // Сброс всех настроек к заводским
  lcd.setCursor (0,1);
  lcd.print ("C\3POC HET / ");
  lcd.print (char(0));
  lcd.print ("A ?");
  while (1 == 1) {
    printTemp(); // Выводим температуру
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      lcd.setCursor (0,1);
      lcd.print (" -= B\5\4O\7HEH =- ");
      resetToDefaults();
      delay (1500);
      return;
    }
  }
}

void resetToDefaults() {
  DeltaTemp = 5; // Гистерезис включения котла
  EEPROM.write(0, DeltaTemp);
  RabotaTemp = 70; // Рабочая темперадура котла
  EEPROM.write(1, RabotaTemp);
  TlenieTemp = 35; // Температура тления
  EEPROM.write(2, TlenieTemp);
  TlenieDoHour = 7; // Время часов до которого осуществляется тление
  EEPROM.write(3, TlenieDoHour);
  TlenieDoMinute = 0; // Время минут до которого осуществляется тление
  EEPROM.write(4, TlenieDoMinute);
  RabotaMode = 2; // Режим работы котла
  EEPROM.write(5, RabotaMode);
  CriticalTemp = 88; // Температура аварии
  EEPROM.write(6, CriticalTemp);
  CurTime = millis(); // Текущее время
  LastTimeOn = CurTime; // Время, когда произошло открытие/закрытие заслонки или включение/выключение вентилятора
  SavedTimeOnVent = 20; // Максимальное время включенного вентилятора в минутах - свыше этого ОСТАНОВ КОТЛА
  EEPROM.write(7, SavedTimeOnVent);
  DeltaTimeOnVent = SavedTimeOnVent * 60000; // Максимальное время включенного вентилятора в миллисекундах
  SavedTimeOnZasl = 10; // Максимальное время открытой заслонки в минутах - свыше этого ВКЛЮЧЕНИЕ ВЕНТИЛЯТОРА
  EEPROM.write(8, SavedTimeOnZasl);
  DeltaTimeOnZasl = SavedTimeOnZasl * 60000; // Максимальное время открытой заслонки в миллисекундах
  SavedTimeOff = 15; // Промежутки времени в минутах, через которые котел будет продуваться при простое
  EEPROM.write(9, SavedTimeOff);
  DeltaTimeOff = SavedTimeOff * 60000; // Промежутки времени в миллисекундах, через которые котел будет продуваться при простое
  ZaslTimeOnOff = 25; // Время на которое активируются линии управления положением заслонки - миллисекунды делённые на 100
  EEPROM.write(10, ZaslTimeOnOff);
}

void menuRabotaMode() { // Выбор режима работы котла
  int RabotaModeTmp = 1; // Выбранный режим
  while (1 == 1) { 
    lcd.setCursor(5, 0);
    lcd.print ("B\1");
    lcd.print (char(0));
    lcd.print (" PA\3OT\5 ");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') {
      return; // Выходим из выбора режима работы
    } else if (KeyPressed == 'U' ) {
      RabotaModeTmp++;
    } else if (KeyPressed == 'D' ) {
      RabotaModeTmp--;
    }
    if (RabotaModeTmp < 1) {RabotaModeTmp = 6;}
    if (RabotaModeTmp > 6) {RabotaModeTmp = 1;}
    if (RabotaMode == 2 && RabotaModeTmp == 2 && KeyPressed == 'U') {RabotaModeTmp = 3;} // ОСТАНОВКА КОТЛА неактивно при остановленном котле
    if (RabotaMode == 2 && RabotaModeTmp == 2 && KeyPressed == 'D') {RabotaModeTmp = 1;} // ОСТАНОВКА КОТЛА неактивно при остановленном котле
    lcd.setCursor (0,1);
    if (RabotaModeTmp == 1) { lcd.print ("OTKP\5T\6 TO\4KY   "); }
    else if (RabotaModeTmp == 2) { lcd.print ("OCTAHOBKA KOT\7A "); }
    else if (RabotaModeTmp == 3) { lcd.print ("PACTO\4KA KOT\7A  "); }
    else if (RabotaModeTmp == 4) { lcd.print ("\4O TEM\4EPATYPE  "); }
    else if (RabotaModeTmp == 5) { lcd.print ("T\7EH\1E ");
      lcd.print (char(0));
      lcd.print ("O BPEMEH"); }
    else if (RabotaModeTmp == 6) { lcd.print ("YCTAHOBKA \4APMTP"); }
    if (KeyPressed == 'O') {
      if (RabotaModeTmp == 1) {
        openDoor();
        return; }
      else if (RabotaModeTmp == 2) {
        RabotaMode = 2;
        EEPROM.write(5 , RabotaMode);
        return; }
      else if (RabotaModeTmp == 3) {
        RabotaMode = 3;
        EEPROM.write(5 , RabotaMode);
        return; }
      else if (RabotaModeTmp == 4) {
        RabotaMode = 4;
        EEPROM.write(5 , RabotaMode);
        return; }
      else if (RabotaModeTmp == 5) {
        RabotaMode = 5;
        EEPROM.write(5 , RabotaMode);
        return; }
      else if (RabotaModeTmp == 6) {
        menuSetup();
        return;
      }
    }
  }
}

void openDoor() { // Продуваем топку перед открытием
  printTemp();    // Выводим температуру
  printTime();
  lcd.setCursor (11,0);
  lcd.print ("\4AY3A");
  lcd.setCursor (0,1);
  lcd.print ("  \4PO");
  lcd.print (char(0));
  lcd.print ("YB TO\4K\1  ");
  kotelRun(2); // Включаем вентилятор
  delay (4000);
  kotelRun(0); // Выключаем вентилятор
  lcd.setCursor (0,1);
  lcd.print (" OTKP\5T\6 TO\4KY ");
  GetKey();
  while (KeyPressed == 'N') {
    printTemp();    // Выводим температуру
    printTime();
    GetKey();
  }
}

void KotelMode() { // обработка всех режимов работы котла
  CurTime = millis(); // Текущее время
  if (RabotaMode == 0) { // РЕЖИМ АВАРИЯ
    lcd.setCursor (0,1); 
    lcd.print ("! A B A P \1 \2 ! ");
    if (CurTemp < CriticalTemp && CurTemp != 0) { // Выход по падении температуры после аварии в останов котла
      digitalWrite (alarmPin, LOW); // Если аварии нет, то и линия сигнализации неактивна
      RabotaMode = 2;
    }
  }
  if (RabotaMode == 3) { // Котел растапливается
    if (ZaslVentStatus == 0) { // Заслонка еще не открыта
      if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
        kotelRun(1); // Открываем заслонку
      } else { // Время для открытой заслонки 0 или меньше
        kotelRun(2); // Включаем сразу вентилятор
      }
    } else if (ZaslVentStatus == 1) { // Заслонка уже открыта
      if (CurTime - LastTimeOn > DeltaTimeOnZasl) { // Если вышло время для открытой заслонки
        kotelRun(2); // Включаем вентилятор
      }
    } else if (ZaslVentStatus == 2) { // Вентилятор уже включен
      if (CurTime - LastTimeOn > DeltaTimeOnZasl+DeltaTimeOnVent) { // Если вышло время для открытой заслонки и включенного вентилятора
        kotelRun(0); // Выключаем вентилятор
        RabotaMode = 2; // режим Котел остановлен
        EEPROM.write(5,RabotaMode); // RabotaMode = 2 при следующем включении питания
      }
    }
    if (CurTemp>=RabotaTemp) { // Если котел вышел на рабочую температуру
      kotelRun(0); // Выключаем вентилятор
      RabotaMode = 4; // режим Котел в работе
      EEPROM.write(5,RabotaMode); // RabotaMode = 4 при следующем включении питания
    }
    if (ZaslVentStatus == 1) { // Заслонка открыта
      lcd.setCursor (0,1);
      lcd.print ("PACTO\4KA  \4O");
      lcd.print (char(0));
      lcd.print (char(0));
      lcd.print ("YB"); 
    }   
    if (ZaslVentStatus == 2) { // Включен вентилятор
      lcd.setCursor (0,1);
      lcd.print ("PACTO\4KA  PA3");
      lcd.print (char(0));
      lcd.print ("YB"); 
    }     
  }
  if (RabotaMode == 4) { // Котел в работе поддержания температуры
    if ( (RabotaTemp-CurTemp) >= DeltaTemp ) { // Если температура ниже порогового значения
      if (ZaslVentStatus == 0) { // Заслонка еще не открыта
        if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
          kotelRun(1); // Открываем заслонку
        } else { // Время для открытой заслонки 0 или меньше
          kotelRun(2); // Включаем сразу вентилятор
        }
      } else if (ZaslVentStatus == 1) { // Заслонка уже открыта
        if (CurTime - LastTimeOn > DeltaTimeOnZasl) { // Если вышло время для открытой заслонки
          kotelRun(2); // Включаем вентилятор
        }
      } else if (ZaslVentStatus == 2) { // Вентилятор уже включен
        if (CurTime - LastTimeOn > DeltaTimeOnZasl+DeltaTimeOnVent) { // Если вышло время для открытой заслонки и включенного вентилятора
          kotelRun(0); // Выключаем вентилятор
          RabotaMode = 2; // режим Котел остановлен
          EEPROM.write(5,RabotaMode); // RabotaMode = 2 при следующем включении питания
        }
      }    
    }
    if (CurTemp>=RabotaTemp) { // Если котел вышел на рабочую температуру
      if (ZaslVentStatus > 0) { // Заслонка открыта и / или вентилятор включен
        kotelRun(0); // Выключаем вентилятор
      }
    }
    if (ZaslVentStatus == 0) { // Заслонка закрыта - режим - работа
      lcd.setCursor (0,1);
      lcd.print ("PA\3OTA          ");   
    }     
    if (ZaslVentStatus == 1) { // Заслонка открыта
      lcd.setCursor (0,1);
      lcd.print ("PA\3OTA    \4O");
      lcd.print (char(0));
      lcd.print (char(0));
      lcd.print ("YB"); 
    }   
    if (ZaslVentStatus == 2) { // Включен вентилятор
      lcd.setCursor (0,1);
      lcd.print ("PA\3OTA    PA3");
      lcd.print (char(0));
      lcd.print ("YB"); 
    }
    if (CurTime-LastTimeOn > DeltaTimeOff) { // Обрабатываем если котел давно не включался
      lcd.setCursor (0,1); 
      lcd.print ("PA\3OTA  \4PO");
      lcd.print (char(0));
      lcd.print ("YBKA");
      kotelRun(2);
      delay (5000); // Продувка котла 5 секунд
      kotelRun(0);
    }
  }  
  if (RabotaMode == 5) { // Котел в режиме Тление до времени
    if ( (TlenieTemp-CurTemp) >= DeltaTemp ) { // Если температура тления ниже порогового значения
      if (ZaslVentStatus == 0) { // Заслонка еще не открыта
        if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
          kotelRun(1); // Открываем заслонку
        } else { // Время для открытой заслонки 0 или меньше
          kotelRun(2); // Включаем сразу вентилятор
        }
      } else if (ZaslVentStatus == 1) { // Заслонка уже открыта
        if (CurTime - LastTimeOn > DeltaTimeOnZasl) { // Если вышло время для открытой заслонки
          kotelRun(2); // Включаем вентилятор
        }
      } else if (ZaslVentStatus == 2) { // Вентилятор уже включен
        if (CurTime - LastTimeOn > DeltaTimeOnZasl+DeltaTimeOnVent) { // Если вышло время для открытой заслонки и включенного вентилятора
          kotelRun(0); // Выключаем вентилятор
          RabotaMode = 2; // режим Котел остановлен
          EEPROM.write(5,RabotaMode); // RabotaMode = 2 при следующем включении питания
        }
      }    
    }
    if (CurTemp>=TlenieTemp) { // Если котел вышел на температуру тления
      if (ZaslVentStatus > 0) { // Заслонка открыта и / или вентилятор включен
        kotelRun(0); // Выключаем вентилятор
      }
    }
    if (ZaslVentStatus == 0) { // Заслонка закрыта - режим - тление
      lcd.setCursor (0,1);
      lcd.print ("T\7EH\1E ");
      lcd.print (char(0));
      lcd.print ("O: ");
      if (TlenieDoHour <10) { lcd.print ('0'); }
      lcd.print (TlenieDoHour);
      lcd.print (':');
      if (TlenieDoMinute <10) { lcd.print ('0'); }
      lcd.print (TlenieDoMinute);
      lcd.print (' ');
    }     
    if (ZaslVentStatus == 1) { // Заслонка открыта
      lcd.setCursor (0,1);
      lcd.print ("T\7EH\1E    \4O");
      lcd.print (char(0));
      lcd.print (char(0));
      lcd.print ("YB"); 
    }   
    if (ZaslVentStatus == 2) { // Включен вентилятор
      lcd.setCursor (0,1);
      lcd.print ("T\7EH\1E    PA3");
      lcd.print (char(0));
      lcd.print ("YB"); 
    }
    if (CurTime-LastTimeOn > DeltaTimeOff) { // Обрабатываем если котел давно не включался
      lcd.setCursor (0,1); 
      lcd.print ("T\7EH\1E  \4PO");
      lcd.print (char(0));
      lcd.print ("YBKA");
      kotelRun(2);
      delay (5000); // Продувка котла 5 секунд
      kotelRun(0);
    }  
  }
  if (RabotaMode == 2) { // Котел остановлен
    if (ZaslVentStatus > 0) { // Заслонка открыта и / или вентилятор включен
      kotelRun(0); // Выключаем вентилятор
    }
    lcd.setCursor (0,1);
    lcd.print ("KOTE\7 OCTAHOB\7EH");
    if (CurTime-LastTimeOn > DeltaTimeOff*2) { // Обрабатываем если котел давно не включался - в режиме простоя - это двойное время
      if (CurTemp > 30) { // Если температура выше 30 гр, то даже в режиме останова продуем котел.
        lcd.setCursor (0,1); 
        lcd.print ("OCTAHOB \4PO");
        lcd.print (char(0));
        lcd.print ("YBKA");
        kotelRun (2);
        delay (5000); // Продувка котла 5 секунд
        kotelRun (0);
      }
    } 
  }
}

void kotelRun( int KotelRezhim ){ // Управление заслонкой и вентилятором
  if (KotelRezhim == 0){ // Если требуется выключить все
    if (ZaslVentStatus > 0) { // Если до этого была хоть что-то включено
      digitalWrite(vent_ON, LOW); // Выключаем вентилятор
      digitalWrite(Zasl_POS, LOW); // Выключаем сигнальную линию заслонки
      digitalWrite(Zasl_OFF, HIGH); // Активируем ВЫКЛЮЧЕНИЕ заслонки
      delay (ZaslTimeOnOff*100); // Закрываем заслонку
      digitalWrite(Zasl_OFF, LOW); // Деактивируем ВЫКЛЮЧЕНИЕ заслонки
    }
    ZaslVentStatus = 0; // Все выключено
  }
  if (KotelRezhim == 1){ // Если надо включить только саму заслонку
    if (ZaslVentStatus == 2) { // Если был включен вентилятор
      digitalWrite(vent_ON, LOW); // То просто Выключаем вентилятор
    }
    if (ZaslVentStatus == 0) { // Если до этого не была еще открыта заслонка
      digitalWrite(Zasl_POS, HIGH); // Включаем сигнальную линию заслонки
      digitalWrite(Zasl_ON, HIGH); // Активируем ВКЛЮЧЕНИЕ заслонки
      delay (ZaslTimeOnOff*100); // Открываем заслонку
      digitalWrite(Zasl_ON, LOW); // Деактивируем ВКЛЮЧЕНИЕ заслонки
    }
    ZaslVentStatus = 1; // Вентилятор выключен, заслонка открыта
  }
  if (KotelRezhim == 2){ // Если надо включить вентилятор
    if (ZaslVentStatus == 0) { // Если до этого не была открыта заслонка
      digitalWrite(Zasl_POS, HIGH); // Включаем сигнальную линию заслонки
      digitalWrite(Zasl_ON, HIGH); // Активируем ВКЛЮЧЕНИЕ заслонки
      delay (ZaslTimeOnOff*100); // Открываем заслонку
      digitalWrite(Zasl_ON, LOW); // Деактивируем ВКЛЮЧЕНИЕ заслонки
    }
    digitalWrite(vent_ON, HIGH); // Включаем вентилятор
    ZaslVentStatus = 2; // Вентилятор включен, заслонка открыта
  }
  LastTimeOn = millis(); // Сохраняем текущее время когда все включили или выключили
}

void GetKey() {
  if (KeyPressed != 'N') { // Если перед этим было нажата любая клавиша
    delay (200); // Задержка от дребезга и повторного нажатия
  }
  if (KeyPressed == 'R' | KeyPressed == 'S') { // Если перед этим было длительное нажатие клавиш
    delay (350); // Задержка от дребезга и повторного нажатия
  }
  if (KeyPressed == 'O' | KeyPressed == 'X') { // Если перед этим было нажатие клавиш подтверждения или отмены
    delay (250); // Задержка от дребезга и повторного нажатия
  }
  KeyPressed = 'N'; // Ничего не нажато
  if ( digitalRead(PinKey2) == LOW ) {
    KeyPressed = 'X'; // Кнопка ОМЕНА
    int LongKey = 0; // Счетчик длительности удержания кнопки
    for (LongKey=1; LongKey <= 140; LongKey++){
      if ( digitalRead(PinKey2) == HIGH ) {return; } // Отпущена кнопка
      delay (5);
    }
    if ( LongKey == 141 ) { KeyPressed = 'R'; } // Кнопка ОТМЕНА - Длительное нажатие - Продувка топки
    return;
  }
  if ( digitalRead(PinKey3) == LOW ) {
    KeyPressed = 'D'; // Кнопка ВНИЗ 
    return;
  }
  if ( digitalRead(PinKey4) == LOW ) {
    KeyPressed = 'U'; // Кнопка ВВЕРХ
    return;
  }
  if ( digitalRead(PinKey5) == LOW ) {
    KeyPressed = 'O'; // Кнопка РЕЖИМ РАБОТЫ
    int LongKey = 0; // Счетчик длительности удержания кнопки
    for (LongKey=1; LongKey <= 140; LongKey++){
      if ( digitalRead(PinKey5) == HIGH ) {return; } // Отпущена кнопка
      delay (5);
    }
    if ( LongKey == 141 ) { KeyPressed = 'S'; } // Кнопка УСТАНОВКА - Длительное нажатие
    return;
  }
}

void printTemp() { // Считываем температуру с Датчика, Выводим ее в левый верхний угол и Проверяем и устанавливаем режим Авария
  byte data[2];
  ds.reset(); 
  ds.write(0xCC);
  ds.write(0x44);
  delay(200);
  ds.reset();
  ds.write(0xCC);
  ds.write(0xBE);
  data[0] = ds.read(); 
  data[1] = ds.read();
  CurTemp = (data[1]<< 8)+data[0];
  CurTemp = CurTemp>>4;
  lcd.setCursor(0, 0);
  if (CurTemp >= 0) { lcd.print ("+"); }
  if (CurTemp > -10 && CurTemp < 10) { lcd.print ("0"); }
  lcd.print (CurTemp);
  lcd.print (char(223));
  lcd.print (" ");
  if (CurTemp >= CriticalTemp) { // Если температура превысила допустимый порог
    if (RabotaMode != 0) { // Если еще не был установлен режим аварии и это первая сработка
      RabotaMode = 0; // Режим АВАРИЯ !!!
      kotelRun(0); // Останавливаем котел
      digitalWrite (alarmPin, HIGH); // Устанавливаем в состояние ВКЛЮЧЕНО внешнюю линию сигнализации об аварии
      EEPROM.write(5,2); // RabotaMode = 2 при следующем включении питания
    }
  }
}

void printRabotaTemp() { // Выводим установленную рабочую температу в вернюю строчку в зависимости от режима - работа / тление
lcd.setCursor(11, 0);
lcd.print (' ');
if (RabotaMode != 5) {
 if (CurTemp > RabotaTemp) { lcd.print (char(127)); } else { lcd.print (char(126)); }
 lcd.print (RabotaTemp);
 } else {
 if (CurTemp > TlenieTemp) { lcd.print (char(127)); } else { lcd.print (char(126)); }
 lcd.print (TlenieTemp); }   
lcd.print (char(223));
}

void printTime() { // Считываем время и отрабатываем выход по таймеру из режима тления
  DateTime now = rtc.now(); 
  lcd.setCursor(5, 0);
  if (now.hour() < 10) { lcd.print ('0'); }
  lcd.print(now.hour());
  lcd.print(':');
  if (now.minute() < 10) { lcd.print ('0'); }
  lcd.print(now.minute());
  lcd.print(" ");
  if (RabotaMode == 5) { // Если активирован режим тления
  if ( now.hour() == TlenieDoHour && now.minute() == TlenieDoMinute) { // Если текущее время равно установленному, то тогда выход из тления в работу
    RabotaMode = 4;
    EEPROM.write(5, RabotaMode);
  }
 }
}

 

АндрейT
Offline
Зарегистрирован: 08.02.2019
Добрый день.
 
Залил скетч,потестировал.
Не сразу разобрался с логикой управления заслонкой и вентилятором.Получается,если выставляешь,например, время открытия заслонки-1минута, а время работы вентилятора-2 минуты. То вентилятор после включения работает-3минуты и тд.
Я так понимаю за это отвечают строки 638-639.
 
Подключил блоки реле.
Вентилятор включается, а заслонка нет.
Померял напряжения:
пин 13-4,8 v
пин 9--0,16v
Померял напряжения на пинах без нагрузки:
пин 13-4,86 v
пин 9-4,65 v
Думал что то с ардуиной.
Поменял в прошивке местами пины 9 и 13.
Пин 9-вентилятор,пин 13-заслонка.
всё заработало ???
Пробовал несколько раз.
Пропаивал пины тоже.
То же самое и с пинами 7 и 8.
Не пойму в чём дело.
 
А в целом, программа работает,всё как и хотелось.
Очень вам благодарен за скетч.
 
С Ув Андрей.
FreeSky
Offline
Зарегистрирован: 18.01.2016

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

- ИСПРАВИЛ - в скетче выше

Логика работы такая при включении сначала включается на свое время заслонка, если по истечении ее времени температура не поднялась, то только потом вентилятор. У меня вроде работает. Если не работает, то дайте детальное описание или лучше снимите видео.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

FreeSky пишет:

- ИСПРАВИЛ - в скетче выше

Ты б еще научился код длиной более 100 строк под спойлер пхать...

FreeSky
Offline
Зарегистрирован: 18.01.2016

DetSimen пишет:

Ты б еще научился код длиной более 100 строк под спойлер пхать...

[spoiler]

Не умею, если знаешь как, то научи))))

[/spoiler]

АндрейT
Offline
Зарегистрирован: 08.02.2019

Зато скетчи нормально пишет.
И не отказывает в помощи.
А ты это умеешь?
Наверное умеешь,так что не помог?
ТС-респект и уважуха.
Всё уметь никто не может)))

АндрейT
Offline
Зарегистрирован: 08.02.2019

FreeSky
Спасибо большое.
Но сейчас я уже не всостоянии.
С утра с пол четвертого пытался что то насторить,а потом на работу.
Надо отоспаться.
Хотя скорее всего ночью проснусь и займусь)))
В принципе и старую прошивку бы реализовал,там несколько транзисторов и релюшек и управление по
пину 9(13).
Как только опробую скетч-сразу отпишусь.
И сфоткаю мою конструкцию(пока на столе)

P.S. Можно ли делится твоим скетчем(естественно на ссылкой на тебя)?
Я уже давал ссылку несколько раз на этот пост.
Но тогда нового скетча небыло.
Считаю что это лучшая ревлизация проекта для твердотопливного котла.
Я пересмотрел очень много проектов.
Ваша реализация-то что нужно для твердотопливного котла с  управлением заслонкой и вентилятором.
Спасибо.

С Ув.Андрей.

FreeSky
Offline
Зарегистрирован: 18.01.2016

АндрейT пишет:

Всё уметь никто не может)))
...
Спасибо большое.

Спойлер в этом форуме просто не работает))) Его здесь нет от слова вообще)))
Прошивку обязательно перезалей, я в последнем сообщении ее обновил. В прошлый раз пины не были включены в режим OUTPUT, поэтому вместо того чтобы на них появлялось +5в они просто включались к подтягивающим резисторам внутри платы. А вместо 0в, эти резисторы отключались и на пине могло быть все что угодно... Отсюда и возможные глюки.
Нумерацию пинов можешь выставить как душе угодно - строки 042-046, только не залезь на используемые стандартно в I2C и OneWire пины.
Свой котел я бновил этой прошивкой, что выложил тут. Вот вторые сутки он исправно работает. У меня просто указана длительность открытия заслонки 0сек и время на которое она открывается при подаче воздуха 0мин. Поэтому котел сразу врубает вентилятор. Глюки по ошибкам в алгоритме и опискам я вылавливал уже на котле в процессе отладки скетча. Поэтому с большего он должен быть рабочим. Если будет нелогичное поведение - отпишись - исправлю.

АндрейT пишет:

Зато скетчи нормально пишет.

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

айдер пишет:

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

Есть идея как можно добавить сюда датчик температуры в помещении и как он будет работать и управлять котлом. Логика работы. Котел стремиться достигнуть заданную в помещении температуру и поддерживать ее, но при этом не выходить за пределы установленной температуры теплоносителя. Тоесть если например указали температуру в помещении 22 градуса и гестеризис 2 градуса, то при падении до 20 градусов, котел включается и начинает работать пока не наберет в помещении 22 градуса. Но если в процессе работы он выйдет на предел установленной температуры теплоносителя (например это 70 градусов), то тогда естественно остановится, несмотря на то сколько в помещении. Для успешной реализации этого алгоритма на практике нужно задавать высокую температуру теплоносителя 70-75 градусов и большой гистерезис для него - например 10-15 градусов, чтобы практически не происходило запуска котла по температуре теплоносителя. И в тот же момент указывать маленький гистерезис для температуры помещения. Т.к. эта температура очень инертна. Она будет медленно набираться и медленно падать. Подключение 2-х датчиков требует немножко другого, более сложного их опроса.  Вопрос, нужно ли реально это кому-нибудь...

АндрейT
Offline
Зарегистрирован: 08.02.2019

Привет.
Я на Чипмейкере выложу как проект.
Скетч обязательно опробую,может и сегодня под утро.
" Поэтому котел сразу врубает вентилятор"- в этом режиме всё нормально.
но при работе с заслокной,время работы вентилятора сумируется с работой заслонки.Ну тут я уже разобрался(ну мне так кажется)-DeltaTimeOnZasl+DeltaTimeOnVent, оставил только-DeltaTimeOnVent,
все настройки стали выполнятся.(это в предыдущей версии скетча).
ну может я не прав-но работает.
Но это не принципиально.
Скетч-супер.

А у тебя есть еще проекты?Интересно посмотреть.

С Ув.Андрей.
Привет с Донбасса!

АндрейT
Offline
Зарегистрирован: 08.02.2019

Залил скетч-всё работает!

Пошел досыпать)))

FreeSky
Offline
Зарегистрирован: 18.01.2016

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

По времени работы заслонки и вентилятора - исправил. До включения вентилятора, ждем время открытой заслонки, после включения вентилатора, ждем время включенного вентилятора, т.к. счетчик ожидания общий и обнуляется каждый раз при любом действии - открытие, включение, выключение и закрытие. Есдинственно, в режиме растопки время до выхода на рабочуу температуру ждем в 2 раза дольше, перед тем как отключить котел - на случай мокрых дров или плохой укладки. И указание времени заслонки в 0 - это значит заслонку не используем. А указание времени вентилятора в 0 - это значит вентилятор не используем.

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

После выхода из режима Авария, котел теперь возвращается к предыдущему режиму а не в останов как было до этого. Например, если вырубился свет, как следствие стали моторы, поддува нет, но нет и циркуляции. Котел подзакипает. Потом появляется свет. Котел уходит в режим авария и стоит в ожидании. Но насосы то воду пошли гонять уже, активно сбрасывая при этом температуру на батареи. Температура теплоносителя падает до приемлемого значения, котел выходит из аварии и продолжает работать дальше отапливая помещение. То что котел был в режиме авария можно определить по индикации часов. Если вместо разделителя : (двуеточие) будет разделитель ! (восклицательный знак), то это значит что с момента последнего нажатия вами кнопок котел побывал в состоянии аварии. (Некоторые датчики при включении кратковременно выдают температуру около 85 градусов. Если при таком датчике температура аварии будет такая же или ниже - то после каждого включения будет выдавать аварию. Чтобы избежать этого, первые 2 секунды после включения алгоритм определия аварии не активен, давая устояться показаниям датчика.

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

Проверяйте - будут непонятки с логикой работы - исправлю.
Привет всем из Беларуси!)

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include <RTClib.h>
#include <EEPROM.h>

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 (0x3f) for a 16 chars and 2 line display
OneWire ds(10); // На Пине 10 висит датчик температуры
RTC_DS1307 rtc; //Создаем объект rtc - ЧАСЫ

int CurTemp=0; // Текущая температура котла
int DeltaTemp = EEPROM.read(0); // Гистерезис включения котла (по умолчанию - 5)
int RabotaTemp = EEPROM.read(1); // Рабочая темперадура котла (по умолчанию - 70)
int TlenieTemp = EEPROM.read(2); // Температура тления (по умолчанию - 35)
int TlenieDoHour = EEPROM.read(3); // Время часов до которого осуществляется тление (по умолчанию - 7)
int TlenieDoMinute = EEPROM.read(4); // Время минут до которого осуществляется тление (по умолчанию - 0)
int RabotaMode = EEPROM.read(5); // Режим работы котла (по умолчанию - 2)
int CriticalTemp = EEPROM.read(6); // Температура аварии (по умолчанию - 88)
long CurTime = millis(); // Текущее время
long LastTimeOn = CurTime; // Время, когда произошло открытие/закрытие заслонки или включение/выключение вентилятора
long SavedTimeOnVent = EEPROM.read(7); // Максимальное время включенного вентилятора в минутах - свыше этого ОСТАНОВ КОТЛА (по умолчанию - 20)
// При использовании только заслонки без вентилятора наддува вышеуказанный параметр установить в 0
long DeltaTimeOnVent = SavedTimeOnVent * 60000; // Максимальное время включенного вентилятора в миллисекундах
// Если вышеуказанное время равно 0 - то котел работает без вентилятора, только с одной заслонкой
long SavedTimeOnZasl = EEPROM.read(8); // Максимальное время открытой заслонки в минутах - свыше этого ВКЛЮЧЕНИЕ ВЕНТИЛЯТОРА (по умолчанию - 10)
// При неиспользуемой заслонке вышеуказанный параметр установить в 0
long DeltaTimeOnZasl = SavedTimeOnZasl * 60000; // Максимальное время открытой заслонки в миллисекундах
// Если вышеуказанное время равно 0 - то котел работает без заслонки, только с вентилятором
long SavedTimeOff = EEPROM.read(9); // Промежутки времени в минутах, через которые котел будет продуваться при простое. (по умолчанию - 15)
long DeltaTimeOff = SavedTimeOff * 60000; // Промежутки времени в миллисекундах, через которые котел будет продуваться при простое
int ZaslTimeOnOff = EEPROM.read(10); // Время на которое активируются линии управления положением заслонки - миллисекунды делённые на 100 (по умолчанию 25 или 2500 миллисекунд)
// При неиспользуемой заслонке вышеуказанный параметр установить в 0
int ZaslVentStatus = 0; // 0 - выключен вентилятор на данный момент, 1 - открыта заслонка на данный момент, 2 - включен вентилятор на данный момент
int AvariaFlag = EEPROM.read(11); // - Признак того, что происходила авария, сбрасывается при любом нажатии кнопки
int LastDigit = 0; // Используется для определия последней цифры секунды, чтобы проверить ее на четность и моргать разделителем времени в часах

// ОБРАБОТКА КЛАВИАТУРЫ
char KeyPressed = 'N'; // Текущая нажатая кнопка клавиатуры
int PinKey2=2; // Пин клавиатуры
int PinKey3=3; // Пин клавиатуры
int PinKey4=4; // Пин клавиатуры
int PinKey5=5; // Пин клавиатуры

int vent_ON=13; // Пин реле вентилятора
int Zasl_ON=7; // Пин на котором будет +5v для открытия заслонки (некоторое время)
int Zasl_OFF=8; // Пин на котором будет +5v для закрытия заслонки (некоторое время)
int Zasl_POS=9; // Пин положения заслонки - +5v - открыта, 0 - закрыта
int alarmPin=12; // Пин аварии внешний

void setup() {
  pinMode (vent_ON, OUTPUT); // управление реле вентилятора
  digitalWrite (vent_ON, LOW); // на всякий случай выключим вентилятор
  pinMode (Zasl_ON, OUTPUT); // управляющий сигнал открытия заслонки
  digitalWrite (Zasl_ON, LOW); //  выключим
  pinMode (Zasl_OFF, OUTPUT); // управляющий сигнал закрытия заслонки
  digitalWrite (Zasl_OFF, LOW); //  выключим
  pinMode (Zasl_POS, OUTPUT); // управляющий сигнал положения заслонки
  digitalWrite (Zasl_POS, LOW); //  выключим    
  pinMode (alarmPin, OUTPUT); // внешняя линия сигнализации об аварии
  digitalWrite (alarmPin, LOW); // Устанавливаем в состояние ВЫКЛ линии сигнализации об аварии 
  rtc.begin(); // Инициализируем Часы в формате 24 часа
  
// Прописываем в экран русские буквы
  byte rus_d[8] = {B00110, B01010, B01010, B01010, B01010, B11111, B10001, B00000}; // Д - матрица
  byte rus_i[8] = {B10001, B10001, B10011, B10101, B11001, B10001, B10001, B00000}; // И - матрица
  byte rus_ya[8] = {B01111, B10001, B10001, B01111, B00101, B01001, B10001, B00000}; // Я - матрица
  byte rus_b[8] = {B11110, B10000, B10000, B11110, B10001, B10001, B11110, B00000}; // Б - матрица
  byte rus_p[8] = {B11111, B10001, B10001, B10001, B10001, B10001, B10001, B00000}; // П - матрица
  byte rus_yy[8] = {B10001, B10001, B10001, B11101, B10011, B10011, B11101, B00000}; // Ы - матрица
  byte rus_mya[8] = {B10000, B10000, B10000, B11110, B10001, B10001, B11110, B00000}; // Ь - матрица
  byte rus_l[8] = {B00110, B01001, B01001, B01001, B01001, B01001, B10001, B00000}; // Л - матрица
  lcd.init(); // Включаем дисплей
  lcd.createChar(0, rus_d); // Д - прописываем символ
  lcd.createChar(1, rus_i); // И - прописываем символ
  lcd.createChar(2, rus_ya); // Я - прописываем символ
  lcd.createChar(3, rus_b); // Б - прописываем символ
  lcd.createChar(4, rus_p); // П - прописываем символ
  lcd.createChar(5, rus_yy); // Ы - прописываем символ
  lcd.createChar(6, rus_mya); // Ь - прописываем символ
  lcd.createChar(7, rus_l); // Л - прописываем символ
  lcd.backlight();
  lcd.clear();
  lcd.noBlink();
  
// ОБРАБОТКА КЛАВИАТУРЫ включения всех подтягивающих резисторов!!! 
  pinMode (PinKey2, INPUT); // Кнопка - X - ОТМЕНА, ОТКРЫТЬ ТОПКУ
  digitalWrite (PinKey2, HIGH); // подтягивающий резистор клавиатура
  pinMode (PinKey3, INPUT); // Кнопка - D - ВНИЗ, МЕНЬШЕ
  digitalWrite (PinKey3, HIGH); // подтягивающий резистор клавиатура
  pinMode (PinKey4, INPUT); // Кнопка - U - ВВЕРХ, БОЛЬШЕ
  digitalWrite (PinKey4, HIGH); // подтягивающий резистор клавиатура
  pinMode (PinKey5, INPUT); // Кнопка - O - МЕНЮ, НАСТРОЙКИ, ВВОД
  digitalWrite (PinKey5, HIGH); // подтягивающий резистор клавиатура

  if (EEPROM.read(10) > 99) { // Если не установлены значения в памяти в 10-й ячейке
    resetToDefaults(); // То скидываем все настройки к дефолтным и прописываем их в память
  }
  
  if (AvariaFlag == 0) { AvariaFlag = 2; } // Если не было до этого Аварии, то установим признак пропадания электричества при включении
}

void loop() {
  printTemp(); // Выводим температуру в верхнюю строчку
  printTime(); // Выводим время в верхнюю строчку
  printRabotaTemp(); // Выводим установленную температуру работы / тления
  KotelMode(); // Обрабатываем текущий режим работы котла
  GetKey(); // Считываем нажатую кнопку
  if (KeyPressed == 'S') { menuSetup(); }
  if (KeyPressed == 'O') { menuRabotaMode(); }
  if (KeyPressed == 'R') { openDoor(); }
  if (RabotaMode != 5) { // Если работает любой режим кроме тления то меняем температуру котла
    if (KeyPressed == 'U' && RabotaTemp < 85) { RabotaTemp++;
      EEPROM.write(1, RabotaTemp);
    }
    if (KeyPressed == 'D' && RabotaTemp > 25) { RabotaTemp--;
      EEPROM.write(1, RabotaTemp);
    }
  } else { // Если на данный момент идет тление по времени от и до,то меняем температуру тления
    if (KeyPressed == 'U' && TlenieTemp < 85) { TlenieTemp++;
      EEPROM.write(2, TlenieTemp);
    }
    if (KeyPressed == 'D' && TlenieTemp > 25) { TlenieTemp--;
      EEPROM.write(2, TlenieTemp);
    }
  }
}

void menuSetup() { // Выбор режима работы котла
  int menuSetupMode = 1; // Выбранный режим
  while (1 == 1) { 
    printTemp(); // Выводим температуру
    lcd.setCursor(5, 0);
    lcd.print (" YCTAHOBKA ");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') {
      return; // Выходим из выбора режима работы
    } else if (KeyPressed == 'U' ) {
      menuSetupMode++;
    } else if (KeyPressed == 'D' ) {
      menuSetupMode--;
    }
    if (menuSetupMode < 1) {menuSetupMode = 11;}
    if (menuSetupMode > 11) {menuSetupMode = 1;}
    lcd.setCursor (0,1);
    if (menuSetupMode == 1) { lcd.print (char(0));
      lcd.print ("ATA BPEM\2           "); }
    else if (menuSetupMode == 2) { lcd.print (char(0));
      lcd.print ("E\7\6TA TEM\4P BK\7"); }
    else if (menuSetupMode == 3) { lcd.print ("TEM\4EPAT. PA\3OT\5"); }
    else if (menuSetupMode == 4) { lcd.print ("TEM\4EPAT. T\7EH\1\2"); }
    else if (menuSetupMode == 5) { lcd.print ("BPEM\2 T\7EH\1\2 ");
      lcd.print (char(0));
      lcd.print ("O "); }
    else if (menuSetupMode == 6) { lcd.print ("TEM\4EPAT. ABAP\1\1"); }
    else if (menuSetupMode == 7) { lcd.print ("BPEM\2 BK\7 BEHT\1\7"); }
    else if (menuSetupMode == 8) { lcd.print ("BPEM\2 OTKP 3AC\7H"); }
    else if (menuSetupMode == 9) { lcd.print ("\1HTEPBA\7 \4PO");
      lcd.print (char(0));
      lcd.print ("YBA");}
    else if (menuSetupMode == 10) { lcd.print ("Y\4PAB\7H 3AC\7OHK\1"); }
    else if (menuSetupMode == 11) { lcd.print ("C\3POC \4APAMETPOB"); }
    if (KeyPressed == 'O') {
      if (menuSetupMode == 1) { menuSetupData(); }
      else if (menuSetupMode == 2) { menuSetupDelta(); }
      else if (menuSetupMode == 3) { menuSetupRabota(); }
      else if (menuSetupMode == 4) { menuSetupTlenie(); }
      else if (menuSetupMode == 5) { menuSetupTlenieDO(); }
      else if (menuSetupMode == 6) { menuSetupCriticalTemp(); }
      else if (menuSetupMode == 7) { menuSetupDeltaTimeOnVent(); }
      else if (menuSetupMode == 8) { menuSetupDeltaTimeOnZasl(); }
      else if (menuSetupMode == 9) { menuSetupDeltaTimeOff(); }
      else if (menuSetupMode == 10) { menuSetupZaslTimeOnOff(); }
      else if (menuSetupMode == 11) { menuSetupResetDefaults(); }
    }
  }
}

void menuSetupData() { // Установка даты и времени
  DateTime now = rtc.now(); // считываем время и дату для установки.
  int setupYear = now.year();
  int setupMonth = now.month();
  int setupDay = now.day();
  int setupHour = now.hour();
  int setupMinute = now.minute();
  int menuSetupDataMode = 1;
  while (1 == 1) {
    printTemp();    // Выводим температуру
    lcd.setCursor (0,1);
    if (setupDay < 10) { lcd.print ('0'); }
    lcd.print (setupDay);
    lcd.print ("/");
    if (setupMonth < 10) { lcd.print ('0'); }
    lcd.print (setupMonth);
    lcd.print ("/");
    lcd.print (setupYear);
    lcd.print (" ");
    if (setupHour < 10) { lcd.print ('0'); }
    lcd.print (setupHour);
    lcd.print (":");
    if (setupMinute < 10) { lcd.print ('0'); }
    lcd.print (setupMinute);
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; }
    if (KeyPressed == 'O' && menuSetupDataMode == 5) { 
      rtc.adjust( DateTime(setupYear, setupMonth, setupDay, setupHour, setupMinute,0) );
      lcd.noBlink();
      return;
    }
    if (KeyPressed == 'O' && menuSetupDataMode < 5) { menuSetupDataMode++; }
    lcd.blink();
    if (menuSetupDataMode == 1) {
      lcd.setCursor (1,1);
      if (KeyPressed == 'U') { setupDay++; }
      if (KeyPressed == 'D') { setupDay--; }
    }
    if (menuSetupDataMode == 2) {
      lcd.setCursor (4,1);
      if (KeyPressed == 'U') { setupMonth++; }
      if (KeyPressed == 'D') { setupMonth--; }
    }
    if (menuSetupDataMode == 3) {
      lcd.setCursor (9,1);
      if (KeyPressed == 'U') { setupYear++; }
      if (KeyPressed == 'D') { setupYear--; }
    }
    if (menuSetupDataMode == 4) {
      lcd.setCursor (12,1);
      if (KeyPressed == 'U') { setupHour++; }
      if (KeyPressed == 'D') { setupHour--; }
    }
    if (menuSetupDataMode == 5) {
      lcd.setCursor (15,1); 
      if (KeyPressed == 'U') { setupMinute++; }
      if (KeyPressed == 'D') { setupMinute--; }
    }
    if (setupDay < 1) { setupDay=31; }
    if (setupDay > 31) { setupDay=1; }    
    if (setupMonth < 1) { setupMonth=12; }
    if (setupMonth > 12) { setupMonth=1; }           
    if (setupYear < 2019) { setupYear=2019; }
    if (setupHour < 0) { setupHour=23; }
    if (setupHour > 23) { setupHour=0; }       
    if (setupMinute < 0) { setupMinute=59; }
    if (setupMinute > 59) { setupMinute=0; }      
  }
}

void menuSetupDelta() { // Установка гистерезиса срабатывания котла
  while (1 == 1) {
    printTemp();    // Выводим температуру
    GetKey();       // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; }
    lcd.setCursor (0,1);
    lcd.print (char(0));
    lcd.print ("E\7\6TA TEM\4: ");
    if (DeltaTemp <10) { lcd.print ('0'); }
    lcd.print (DeltaTemp);
    lcd.print (char(223));
    if (KeyPressed == 'U') { DeltaTemp++; }
    if (KeyPressed == 'D') { DeltaTemp--; }
    if (DeltaTemp > 10) { DeltaTemp = 1; }
    if (DeltaTemp < 1) { DeltaTemp = 10; }
    if (KeyPressed == 'O') {
      EEPROM.write(0, DeltaTemp);
      return;
    }
  }
}

void menuSetupRabota() { // Установка температуры работы котла
  while (1 == 1) {
    printTemp();    // Выводим температуру
    GetKey();       // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; }
    lcd.setCursor (0,1);
    lcd.print ("PA\3OTA TEM\4: ");
    if (RabotaTemp <10) { lcd.print ('0'); }
    lcd.print (RabotaTemp);
    lcd.print (char(223));
    if (KeyPressed == 'U') { RabotaTemp++; }
    if (KeyPressed == 'D') { RabotaTemp--; }
    if (RabotaTemp > 85) { RabotaTemp = 25; }
    if (RabotaTemp < 25) { RabotaTemp = 85; }
    if (RabotaTemp >= CriticalTemp) { RabotaTemp = CriticalTemp-3; }    
    if (KeyPressed == 'O') {
      EEPROM.write(1, RabotaTemp);
      return;
    }
  }
}

void menuSetupTlenie() { // Установка температуры при тлении котла
  while (1 == 1) {
    printTemp();    // Выводим температуру
    GetKey();       // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; }
    lcd.setCursor (0,1);
    lcd.print ("T\7EH\1E TEM\4: ");
    if (TlenieTemp <10) { lcd.print ('0'); }
    lcd.print (TlenieTemp);
    lcd.print (char(223));
    if (KeyPressed == 'U') { TlenieTemp++; }
    if (KeyPressed == 'D') { TlenieTemp--; }
    if (TlenieTemp > 85) { TlenieTemp = 20; }
    if (TlenieTemp < 20) { TlenieTemp = 85; }
    if (TlenieTemp >= CriticalTemp) { TlenieTemp = CriticalTemp-3; }    
    if (KeyPressed == 'O') {
      EEPROM.write(2, TlenieTemp);
      return;
    }
  }
}

void menuSetupTlenieDO() { // Установка времени до которого будет продолжаться тление
  int setupTlenieDoMode = 1; // 1- устанавливаем часы, 2- устанавливаем минуты
  while (1 == 1) {
  printTemp();    // Выводим температуру
  GetKey();       // Считываем нажатую кнопку
  if (KeyPressed == 'X') {
    lcd.noBlink();
    return; }
  lcd.setCursor (0,1);
  lcd.print ("T\7EH\1E ");
  lcd.print (char(0));
  lcd.print ("O: ");
  if (TlenieDoHour <10) { lcd.print ('0'); }
  lcd.print (TlenieDoHour);
  lcd.print (':');
  if (TlenieDoMinute <10) { lcd.print ('0'); }
  lcd.print (TlenieDoMinute);
  lcd.print (' ');
  lcd.blink();
  if ( setupTlenieDoMode == 1) { lcd.setCursor (12,1); } else { lcd.setCursor (15,1); } 
  if ( setupTlenieDoMode == 2 && KeyPressed == 'O') {
    EEPROM.write(3 , TlenieDoHour);
    EEPROM.write(4 , TlenieDoMinute);
    return;
  } 
  if (setupTlenieDoMode == 1 && KeyPressed == 'O') { setupTlenieDoMode = 2; }
  if (setupTlenieDoMode == 1 && KeyPressed == 'U') { TlenieDoHour++; }
  if (setupTlenieDoMode == 1 && KeyPressed == 'D') { TlenieDoHour--; }
  if (setupTlenieDoMode == 2 && KeyPressed == 'U') { TlenieDoMinute++; }
  if (setupTlenieDoMode == 2 && KeyPressed == 'D') { TlenieDoMinute--; }
  if (TlenieDoHour > 23) {TlenieDoHour = 0;}
  if (TlenieDoHour < 0) {TlenieDoHour = 23;}
  if (TlenieDoMinute > 59) {TlenieDoMinute = 0;}
  if (TlenieDoMinute < 0) {TlenieDoMinute = 59;}  
  }
}

void menuSetupCriticalTemp() { // Установка Критической температуры
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("ABAP\1\2 TEM\4: ");
    lcd.print (CriticalTemp);
    lcd.print (char(223));
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { CriticalTemp++; }
    if (KeyPressed == 'D') { CriticalTemp--; }
    if (CriticalTemp > 95) { CriticalTemp = 75; }
    if (CriticalTemp < 75) { CriticalTemp = 95; }
    if (TlenieTemp >= CriticalTemp) { TlenieTemp = CriticalTemp-3; }  
    if (RabotaTemp >= CriticalTemp) { RabotaTemp = CriticalTemp-3; }    
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(6, CriticalTemp);
      return;
    }
  }
}

void menuSetupDeltaTimeOnVent() { // Установка максимального времени включенного вентилятора
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("BK\7 BEHT: ");
    if (SavedTimeOnVent <10) { lcd.print ('0'); }
    lcd.print (SavedTimeOnVent);
    lcd.print (" M\1H");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { SavedTimeOnVent++; }
    if (KeyPressed == 'D') { SavedTimeOnVent--; }
    if (SavedTimeOnVent > 30) { SavedTimeOnVent = 0; }
    if (SavedTimeOnVent < 0) { SavedTimeOnVent = 30; }
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(7, SavedTimeOnVent);
      DeltaTimeOnVent = SavedTimeOnVent * 60000;
      return;
    }
  }
}

void menuSetupDeltaTimeOnZasl() { // Установка максимального времени открытой заслонки
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("OTKP 3AC\7:");
    if (SavedTimeOnZasl <10) { lcd.print ('0'); }
    lcd.print (SavedTimeOnZasl);
    lcd.print (" M\1H");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { SavedTimeOnZasl++; }
    if (KeyPressed == 'D') { SavedTimeOnZasl--; }
    if (SavedTimeOnZasl > 30) { SavedTimeOnZasl = 0; }
    if (SavedTimeOnZasl < 0) { SavedTimeOnZasl = 30; }
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(8, SavedTimeOnZasl);
      DeltaTimeOnZasl = SavedTimeOnZasl * 60000;
      return;
    }
  }
}

void menuSetupDeltaTimeOff() { // Установка максимального времени простоя котла без включения
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("ABTO\4PO");
    lcd.print (char(0));
    lcd.print ("YB:");
    lcd.print (SavedTimeOff);
    lcd.print ("M\1H");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { SavedTimeOff++; }
    if (KeyPressed == 'D') { SavedTimeOff--; }
    if (SavedTimeOff > 30) { SavedTimeOff = 15; }
    if (SavedTimeOff < 15) { SavedTimeOff = 30; }
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(9, SavedTimeOff);
      DeltaTimeOff = SavedTimeOff * 60000;
      return;
    }
  }
}

void menuSetupZaslTimeOnOff() { // Установка времени на которое активизируются управляющие сигналы заслонки
  while (1 == 1) {
    printTemp(); // Выводим температуру
    lcd.setCursor (0,1);
    lcd.print ("Y\4PB\7 3AC\7: ");
    lcd.print (ZaslTimeOnOff / 10); // выводим десятки
    lcd.print (".");
    lcd.print (ZaslTimeOnOff - (ZaslTimeOnOff / 10)*10); // выводим единицы
    lcd.print ("c");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'U') { ZaslTimeOnOff++; }
    if (KeyPressed == 'D') { ZaslTimeOnOff--; }
    if (ZaslTimeOnOff > 99) { ZaslTimeOnOff = 0; }
    if (ZaslTimeOnOff < 0) { ZaslTimeOnOff = 99; }
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      EEPROM.write(10, ZaslTimeOnOff);
      return;
    }
  }
}

void menuSetupResetDefaults() { // Сброс всех настроек к заводским
  lcd.setCursor (0,1);
  lcd.print ("C\3POC HET / ");
  lcd.print (char(0));
  lcd.print ("A ?");
  while (1 == 1) {
    printTemp(); // Выводим температуру
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') { return; } // Выход без сохранения    
    if (KeyPressed == 'O') { // Выход с сохранением настройки
      lcd.setCursor (0,1);
      lcd.print (" -= B\5\4O\7HEH =- ");
      resetToDefaults();
      delay (1500);
      return;
    }
  }
}

void resetToDefaults() {
  DeltaTemp = 5; // Гистерезис включения котла
  EEPROM.write(0, DeltaTemp);
  RabotaTemp = 70; // Рабочая темперадура котла
  EEPROM.write(1, RabotaTemp);
  TlenieTemp = 35; // Температура тления
  EEPROM.write(2, TlenieTemp);
  TlenieDoHour = 7; // Время часов до которого осуществляется тление
  EEPROM.write(3, TlenieDoHour);
  TlenieDoMinute = 0; // Время минут до которого осуществляется тление
  EEPROM.write(4, TlenieDoMinute);
  RabotaMode = 2; // Режим работы котла
  EEPROM.write(5, RabotaMode);
  CriticalTemp = 85; // Температура аварии
  EEPROM.write(6, CriticalTemp);
  CurTime = millis(); // Текущее время
  LastTimeOn = CurTime; // Время, когда произошло открытие/закрытие заслонки или включение/выключение вентилятора
  SavedTimeOnVent = 20; // Максимальное время включенного вентилятора в минутах - свыше этого ОСТАНОВ КОТЛА
  EEPROM.write(7, SavedTimeOnVent);
  DeltaTimeOnVent = SavedTimeOnVent * 60000; // Максимальное время включенного вентилятора в миллисекундах
  SavedTimeOnZasl = 10; // Максимальное время открытой заслонки в минутах - свыше этого ВКЛЮЧЕНИЕ ВЕНТИЛЯТОРА (10 минут)
  EEPROM.write(8, SavedTimeOnZasl);
  DeltaTimeOnZasl = SavedTimeOnZasl * 60000; // Максимальное время открытой заслонки в миллисекундах
  SavedTimeOff = 15; // Промежутки времени в минутах, через которые котел будет продуваться при простое
  EEPROM.write(9, SavedTimeOff);
  DeltaTimeOff = SavedTimeOff * 60000; // Промежутки времени в миллисекундах, через которые котел будет продуваться при простое
  ZaslTimeOnOff = 25; // Время на которое активируются линии управления положением заслонки - миллисекунды делённые на 100 (25)
  EEPROM.write(10, ZaslTimeOnOff);
}

void menuRabotaMode() { // Выбор режима работы котла
  int RabotaModeTmp = 1; // Выбранный режим
  while (1 == 1) { 
    lcd.setCursor(5, 0);
    lcd.print ("B\1");
    lcd.print (char(0));
    lcd.print (" PA\3OT\5 ");
    GetKey(); // Считываем нажатую кнопку
    if (KeyPressed == 'X') {
      return; // Выходим из выбора режима работы
    } else if (KeyPressed == 'U' ) {
      RabotaModeTmp++;
    } else if (KeyPressed == 'D' ) {
      RabotaModeTmp--;
    }
    if (RabotaModeTmp < 1) {RabotaModeTmp = 6;}
    if (RabotaModeTmp > 6) {RabotaModeTmp = 1;}
    if (RabotaMode == 2 && RabotaModeTmp == 2 && KeyPressed == 'U') {RabotaModeTmp = 3;} // ОСТАНОВКА КОТЛА неактивно при остановленном котле
    if (RabotaMode == 2 && RabotaModeTmp == 2 && KeyPressed == 'D') {RabotaModeTmp = 1;} // ОСТАНОВКА КОТЛА неактивно при остановленном котле
    lcd.setCursor (0,1);
    if (RabotaModeTmp == 1) { lcd.print ("OTKP\5T\6 TO\4KY   "); }
    else if (RabotaModeTmp == 2) { lcd.print ("OCTAHOBKA KOT\7A "); }
    else if (RabotaModeTmp == 3) { lcd.print ("PACTO\4KA KOT\7A  "); }
    else if (RabotaModeTmp == 4) { lcd.print ("\4O TEM\4EPATYPE  "); }
    else if (RabotaModeTmp == 5) { lcd.print ("T\7EH\1E ");
      lcd.print (char(0));
      lcd.print ("O BPEMEH"); }
    else if (RabotaModeTmp == 6) { lcd.print ("YCTAHOBKA \4APMTP"); }
    if (KeyPressed == 'O') {
      if (RabotaModeTmp == 1) {
        openDoor();
        return; }
      else if (RabotaModeTmp == 2) {
        RabotaMode = 2;
        EEPROM.write(5 , RabotaMode);
        return; }
      else if (RabotaModeTmp == 3) {
        RabotaMode = 3;
        EEPROM.write(5 , RabotaMode);
        return; }
      else if (RabotaModeTmp == 4) {
        RabotaMode = 4;
        EEPROM.write(5 , RabotaMode);
        return; }
      else if (RabotaModeTmp == 5) {
        RabotaMode = 5;
        EEPROM.write(5 , RabotaMode);
        return; }
      else if (RabotaModeTmp == 6) {
        menuSetup();
        return;
      }
    }
  }
}

void openDoor() { // Продуваем топку перед открытием
  printTemp();    // Выводим температуру
  printTime();
  lcd.setCursor (11,0);
  lcd.print ("\4AY3A");
  lcd.setCursor (0,1);
  lcd.print ("  \4PO");
  lcd.print (char(0));
  lcd.print ("YB TO\4K\1  ");
  kotelRun(2); // Включаем вентилятор
  delay (4000);
  kotelRun(0); // Выключаем вентилятор
  lcd.setCursor (0,1);
  lcd.print (" OTKP\5T\6 TO\4KY ");
  GetKey();
  while (KeyPressed == 'N') {
    printTemp();    // Выводим температуру
    printTime();
    GetKey();
  }
}

void KotelMode() { // обработка всех режимов работы котла
  CurTime = millis(); // Текущее время
  if (RabotaMode == 0) { // РЕЖИМ АВАРИЯ
    lcd.setCursor (0,1); 
    lcd.print ("! A B A P \1 \2 ! ");
    if (CurTemp < CriticalTemp && CurTemp != 0) { // Выход по падении температуры после аварии в останов котла
      digitalWrite (alarmPin, LOW); // Если аварии нет, то и линия сигнализации неактивна
      RabotaMode = EEPROM.read(5); // Возвращаемся к предыдущему режиму работы
    }
  }
  if (RabotaMode == 3) { // Котел растапливается
    if (ZaslVentStatus == 0) { // Заслонка еще не открыта
      if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
        kotelRun(1); // Открываем заслонку
      } else { // Время для открытой заслонки 0 или меньше
        kotelRun(2); // Включаем сразу вентилятор
      }
    } else if (ZaslVentStatus == 1) { // Заслонка уже открыта
      if (CurTime - LastTimeOn > DeltaTimeOnZasl*2) { // Если вышло время для открытой заслонки
        kotelRun(2); // Включаем вентилятор
      }
    } else if (ZaslVentStatus == 2) { // Вентилятор уже включен
      if (CurTime - LastTimeOn > DeltaTimeOnVent*2) { // Если вышло двойное время для включенного вентилятора
        kotelRun(0); // Выключаем вентилятор
        RabotaMode = 2; // режим Котел остановлен
        EEPROM.write(5,RabotaMode); // RabotaMode = 2 при следующем включении питания
      }
    }
    if (CurTemp>=RabotaTemp) { // Если котел вышел на рабочую температуру
      kotelRun(0); // Выключаем вентилятор
      RabotaMode = 4; // режим Котел в работе
      EEPROM.write(5,RabotaMode); // RabotaMode = 4 при следующем включении питания
    }
    if (ZaslVentStatus == 1) { // Заслонка открыта
      lcd.setCursor (0,1);
      lcd.print ("PACTO\4KA  \4O");
      lcd.print (char(0));
      lcd.print (char(0));
      lcd.print ("YB"); 
    }   
    if (ZaslVentStatus == 2) { // Включен вентилятор
      lcd.setCursor (0,1);
      lcd.print ("PACTO\4KA  PA3");
      lcd.print (char(0));
      lcd.print ("YB"); 
    }     
  }
  if (RabotaMode == 4) { // Котел в работе поддержания температуры
    if ( (RabotaTemp-CurTemp) >= DeltaTemp ) { // Если температура ниже порогового значения
      if (ZaslVentStatus == 0) { // Заслонка еще не открыта
        if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
          kotelRun(1); // Открываем заслонку
        } else { // Время для открытой заслонки 0 или меньше
          kotelRun(2); // Включаем сразу вентилятор
        }
      } else if (ZaslVentStatus == 1) { // Заслонка уже открыта
        if (CurTime - LastTimeOn > DeltaTimeOnZasl) { // Если вышло время для открытой заслонки
          kotelRun(2); // Включаем вентилятор
        }
      } else if (ZaslVentStatus == 2) { // Вентилятор уже включен
        if (CurTime - LastTimeOn > DeltaTimeOnVent) { // Если вышло время для включенного вентилятора
          kotelRun(0); // Выключаем вентилятор
          RabotaMode = 2; // режим Котел остановлен
          EEPROM.write(5,RabotaMode); // RabotaMode = 2 при следующем включении питания
        }
      }    
    }
    if (CurTemp>=RabotaTemp) { // Если котел вышел на рабочую температуру
      if (ZaslVentStatus > 0) { // Заслонка открыта и / или вентилятор включен
        kotelRun(0); // Выключаем вентилятор
      }
    }
    if (ZaslVentStatus == 0) { // Заслонка закрыта - режим - работа
      lcd.setCursor (0,1);
      lcd.print ("PA\3OTA          ");   
    }     
    if (ZaslVentStatus == 1) { // Заслонка открыта
      lcd.setCursor (0,1);
      lcd.print ("PA\3OTA    \4O");
      lcd.print (char(0));
      lcd.print (char(0));
      lcd.print ("YB"); 
    }   
    if (ZaslVentStatus == 2) { // Включен вентилятор
      lcd.setCursor (0,1);
      lcd.print ("PA\3OTA    PA3");
      lcd.print (char(0));
      lcd.print ("YB"); 
    }
    if (CurTime-LastTimeOn > DeltaTimeOff && ZaslVentStatus == 0) { // Обрабатываем если котел давно не включался
      lcd.setCursor (0,1); 
      lcd.print ("PA\3OTA  \4PO");
      lcd.print (char(0));
      lcd.print ("YBKA");
      if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
        kotelRun(1); // Открываем заслонку
      } else { // Время для открытой заслонки 0 или меньше
        kotelRun(2); // Включаем сразу вентилятор
      }
      delay (5000); // Продувка котла 5 секунд
      kotelRun(0);
    }
  }  
  if (RabotaMode == 5) { // Котел в режиме Тление до времени
    if ( (TlenieTemp-CurTemp) >= DeltaTemp ) { // Если температура тления ниже порогового значения
      if (ZaslVentStatus == 0) { // Заслонка еще не открыта
        if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
          kotelRun(1); // Открываем заслонку
        } else { // Время для открытой заслонки 0 или меньше
          kotelRun(2); // Включаем сразу вентилятор
        }
      } else if (ZaslVentStatus == 1) { // Заслонка уже открыта
        if (CurTime - LastTimeOn > DeltaTimeOnZasl) { // Если вышло время для открытой заслонки
          kotelRun(2); // Включаем вентилятор
        }
      } else if (ZaslVentStatus == 2) { // Вентилятор уже включен
        if (CurTime - LastTimeOn > DeltaTimeOnVent) { // Если вышло время для включенного вентилятора
          kotelRun(0); // Выключаем вентилятор
          RabotaMode = 2; // режим Котел остановлен
          EEPROM.write(5,RabotaMode); // RabotaMode = 2 при следующем включении питания
        }
      }    
    }
    if (CurTemp>=TlenieTemp) { // Если котел вышел на температуру тления
      if (ZaslVentStatus > 0) { // Заслонка открыта и / или вентилятор включен
        kotelRun(0); // Выключаем вентилятор
      }
    }
    if (ZaslVentStatus == 0) { // Заслонка закрыта - режим - тление
      lcd.setCursor (0,1);
      lcd.print ("T\7EH\1E ");
      lcd.print (char(0));
      lcd.print ("O: ");
      if (TlenieDoHour <10) { lcd.print ('0'); }
      lcd.print (TlenieDoHour);
      lcd.print (':');
      if (TlenieDoMinute <10) { lcd.print ('0'); }
      lcd.print (TlenieDoMinute);
      lcd.print (' ');
    }     
    if (ZaslVentStatus == 1) { // Заслонка открыта
      lcd.setCursor (0,1);
      lcd.print ("T\7EH\1E    \4O");
      lcd.print (char(0));
      lcd.print (char(0));
      lcd.print ("YB"); 
    }   
    if (ZaslVentStatus == 2) { // Включен вентилятор
      lcd.setCursor (0,1);
      lcd.print ("T\7EH\1E    PA3");
      lcd.print (char(0));
      lcd.print ("YB"); 
    }
    if (CurTime-LastTimeOn > DeltaTimeOff && ZaslVentStatus == 0) { // Обрабатываем если котел давно не включался
      lcd.setCursor (0,1); 
      lcd.print ("T\7EH\1E  \4PO");
      lcd.print (char(0));
      lcd.print ("YBKA");
      if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
        kotelRun(1); // Открываем заслонку
      } else { // Время для открытой заслонки 0 или меньше
        kotelRun(2); // Включаем сразу вентилятор
      }
      delay (5000); // Продувка котла 5 секунд
      kotelRun(0);
    }  
  }
  if (RabotaMode == 2) { // Котел остановлен
    if (ZaslVentStatus > 0) { // Заслонка открыта и / или вентилятор включен
      kotelRun(0); // Выключаем вентилятор
    }
    lcd.setCursor (0,1);
    lcd.print ("KOTE\7 OCTAHOB\7EH");
    if (CurTime-LastTimeOn > DeltaTimeOff*2) { // Обрабатываем если котел давно не включался - в режиме простоя - это двойное время
      if (CurTemp > 30) { // Если температура выше 30 гр, то даже в режиме останова продуем котел.
        lcd.setCursor (0,1); 
        lcd.print ("OCTAHOB \4PO");
        lcd.print (char(0));
        lcd.print ("YBKA");
        if( DeltaTimeOnZasl > 0 ) { // Если время для открытой заслонки больше 0
          kotelRun(1); // Открываем заслонку
        } else { // Время для открытой заслонки 0 или меньше
          kotelRun(2); // Включаем сразу вентилятор
        }
        delay (5000); // Продувка котла 5 секунд
        kotelRun (0);
      }
    } 
  }
}

void kotelRun( int KotelRezhim ){ // Управление заслонкой и вентилятором
  if (KotelRezhim == 0){ // Если требуется выключить все
    if (ZaslVentStatus > 0) { // Если до этого была хоть что-то включено
      digitalWrite(vent_ON, LOW); // Выключаем вентилятор
      digitalWrite(Zasl_POS, LOW); // Выключаем сигнальную линию заслонки
      digitalWrite(Zasl_OFF, HIGH); // Активируем ВЫКЛЮЧЕНИЕ заслонки
      delay (ZaslTimeOnOff*100); // Закрываем заслонку
      digitalWrite(Zasl_OFF, LOW); // Деактивируем ВЫКЛЮЧЕНИЕ заслонки
    }
    ZaslVentStatus = 0; // Все выключено
  }
  if (KotelRezhim == 1){ // Если надо включить только саму заслонку
    if (ZaslVentStatus == 2) { // Если был включен вентилятор
      digitalWrite(vent_ON, LOW); // То просто Выключаем вентилятор
    }
    if (ZaslVentStatus == 0) { // Если до этого не была еще открыта заслонка
      digitalWrite(Zasl_POS, HIGH); // Включаем сигнальную линию заслонки
      digitalWrite(Zasl_ON, HIGH); // Активируем ВКЛЮЧЕНИЕ заслонки
      delay (ZaslTimeOnOff*100); // Открываем заслонку
      digitalWrite(Zasl_ON, LOW); // Деактивируем ВКЛЮЧЕНИЕ заслонки
    }
    ZaslVentStatus = 1; // Вентилятор выключен, заслонка открыта
  }
  if (KotelRezhim == 2){ // Если надо включить вентилятор
    if (ZaslVentStatus == 0) { // Если до этого не была открыта заслонка
      digitalWrite(Zasl_POS, HIGH); // Включаем сигнальную линию заслонки
      digitalWrite(Zasl_ON, HIGH); // Активируем ВКЛЮЧЕНИЕ заслонки
      delay (ZaslTimeOnOff*100); // Открываем заслонку
      digitalWrite(Zasl_ON, LOW); // Деактивируем ВКЛЮЧЕНИЕ заслонки
    }
    digitalWrite(vent_ON, HIGH); // Включаем вентилятор
    ZaslVentStatus = 2; // Вентилятор включен, заслонка открыта
  }
  LastTimeOn = millis(); // Сохраняем текущее время когда все включили или выключили
}

void GetKey() {
  if (KeyPressed != 'N') { // Если перед этим было нажата любая клавиша
    delay (200); // Задержка от дребезга и повторного нажатия
    if (AvariaFlag > 0) { // Если был установлен флаг аварии то по нажатию любой кнопки скидываем его
      AvariaFlag = 0;
      EEPROM.write(11,AvariaFlag); // И прописываем в память
    }
  } // Если кнопок нажато не было - то задержку не делаем
  if (KeyPressed == 'R' | KeyPressed == 'S') { // Если перед этим было длительное нажатие клавиш
    delay (350); // Задержка от дребезга и повторного нажатия
  }
  if (KeyPressed == 'O' | KeyPressed == 'X') { // Если перед этим было нажатие клавиш подтверждения или отмены
    delay (250); // Задержка от дребезга и повторного нажатия
  }
  KeyPressed = 'N'; // Ничего не нажато
  if ( digitalRead(PinKey2) == LOW ) {
    KeyPressed = 'X'; // Кнопка ОМЕНА
    int LongKey = 0; // Счетчик длительности удержания кнопки
    for (LongKey=1; LongKey <= 140; LongKey++){
      if ( digitalRead(PinKey2) == HIGH ) {return; } // Отпущена кнопка
      delay (5);
    }
    if ( LongKey == 141 ) { KeyPressed = 'R'; } // Кнопка ОТМЕНА - Длительное нажатие - Продувка топки
    return;
  }
  if ( digitalRead(PinKey3) == LOW ) {
    KeyPressed = 'D'; // Кнопка ВНИЗ 
    return;
  }
  if ( digitalRead(PinKey4) == LOW ) {
    KeyPressed = 'U'; // Кнопка ВВЕРХ
    return;
  }
  if ( digitalRead(PinKey5) == LOW ) {
    KeyPressed = 'O'; // Кнопка РЕЖИМ РАБОТЫ
    int LongKey = 0; // Счетчик длительности удержания кнопки
    for (LongKey=1; LongKey <= 140; LongKey++){
      if ( digitalRead(PinKey5) == HIGH ) {return; } // Отпущена кнопка
      delay (5);
    }
    if ( LongKey == 141 ) { KeyPressed = 'S'; } // Кнопка УСТАНОВКА - Длительное нажатие
    return;
  }
}

void printTemp() { // Считываем температуру с Датчика, Выводим ее в левый верхний угол и Проверяем и устанавливаем режим Авария
  byte data[2];
  ds.reset(); 
  ds.write(0xCC);
  ds.write(0x44);
  delay(200);
  ds.reset();
  ds.write(0xCC);
  ds.write(0xBE);
  data[0] = ds.read(); 
  data[1] = ds.read();
  CurTemp = (data[1]<< 8)+data[0];
  CurTemp = CurTemp>>4;
  lcd.setCursor(0, 0);
  if (CurTemp >= 0) { lcd.print ("+"); }
  if (CurTemp > -10 && CurTemp < 10) { lcd.print ("0"); }
  lcd.print (CurTemp);
  lcd.print (char(223));
  lcd.print (" ");
  if (CurTemp >= CriticalTemp && CurTime > 2000) { // Если температура превысила допустимый порог и это не сразу же после включения, пока глючит датчик
    if (RabotaMode != 0) { // Если еще не был установлен режим аварии и это первая сработка
      RabotaMode = 0; // Режим АВАРИЯ !!!
      kotelRun(0); // Останавливаем котел
      digitalWrite (alarmPin, HIGH); // Устанавливаем в состояние ВКЛЮЧЕНО внешнюю линию сигнализации об аварии
      AvariaFlag = 1; // Устанавливаем флаг отображения аварии в разделителе времени как ! ( Восклицательный знак вместо : )
      EEPROM.write(11, AvariaFlag); // Прописываем в память что флаг установлен
    }
  }
}

void printRabotaTemp() { // Выводим установленную рабочую температу в вернюю строчку в зависимости от режима - работа / тление
lcd.setCursor(11, 0);
lcd.print (' ');
if (RabotaMode != 5) {
 if (CurTemp > RabotaTemp) { lcd.print (char(127)); } else { lcd.print (char(126)); }
 lcd.print (RabotaTemp);
 } else {
 if (CurTemp > TlenieTemp) { lcd.print (char(127)); } else { lcd.print (char(126)); }
 lcd.print (TlenieTemp); }   
lcd.print (char(223));
}

void printTime() { // Считываем время и отрабатываем выход по таймеру из режима тления
  DateTime now = rtc.now(); 
  lcd.setCursor(5, 0);
  if (now.hour() < 10) { lcd.print ('0'); }
  lcd.print(now.hour());
  LastDigit = now.second() - (now.second()/10)*10;
  if (LastDigit == 0 | LastDigit == 2 | LastDigit == 4 | LastDigit == 6 | LastDigit == 8) { // Если секунда четная
    if (AvariaFlag == 1) { // Если происходила авария
      lcd.print('!'); // В разделителе времени используем восклицательный знак
    } else if (AvariaFlag == 2 ){
      lcd.print('+'); // В разделителе времени используем плюс-минус
    } else { // Если аварии не было и не было отключения питания
      lcd.print(':'); // То разделитель - просто двуеточие как всегда
    }
  } else { // Если секунда нечетная 
    lcd.print(' '); // То разделитель - пробел
  }
  if (now.minute() < 10) { lcd.print ('0'); }
  lcd.print(now.minute());
  lcd.print(" ");
  if (RabotaMode == 5) { // Если активирован режим тления
  if ( now.hour() == TlenieDoHour && now.minute() == TlenieDoMinute) { // Если текущее время равно установленному, то тогда выход из тления в работу
    RabotaMode = 4;
    EEPROM.write(5, RabotaMode);
  }
 }
}

 

АндрейT
Offline
Зарегистрирован: 08.02.2019

Привет всем.

Делаю корпус для контроллера.
Пытаюсь всё всунуть в бп АТХ.
Вмесе с блоком питания.
Поэтому всё в разобраном виде.
Завтра прошивку опробую.

С Ув Андрей.

uragan
Offline
Зарегистрирован: 23.02.2015

С датчиком температуры дымогазов управление горнием будет более правильным. Есть реализация на естественной тяге. И задействованием датчика комнатной температуры.

arduinec
Offline
Зарегистрирован: 01.09.2015

FreeSky пишет:

DetSimen пишет:

Ты б еще научился код длиной более 100 строк под спойлер пхать...

[spoiler]

Не умею, если знаешь как, то научи))))

[/spoiler]

При вставке кода нужно нажать на вкладку Дополнительно и там выбрать Свернуть.
Картинки здесь: http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukomment...

АндрейT
Offline
Зарегистрирован: 08.02.2019

Пока так :

 

FreeSky
Offline
Зарегистрирован: 18.01.2016

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

АндрейT
Offline
Зарегистрирован: 08.02.2019

Привет.
Да,топлюсь углем,в основном семечка.
Заслонку сфотографирую,наверное завтра.
Кстати, в скетче нет одной важной штуки-остановки котла по нижнему порогу температуры.
Вот представьте-время вентилятора на максимуме,ин он дует а температура падает.
У меня котел работает в старт-стопном режиме, и на 3й день топки вентилятору приходится долго раздувать.
Иногда по часу дует( он слабенький).
Можно ввести в скетч отключение при, например-30*с ?

С Ув Андрей.

FreeSky
Offline
Зарегистрирован: 18.01.2016

Смысл перегружать функционалом. Уже и так неподготовленный пользователь путаться в настройках будет.

Котел сейчас отключается если за установленное время не сможет нагнать температуру до заданной.

Допустим время установлено 20 мин. (по умолчанию), Температура 70 градусов. Гистерезис 5гр. Если температура упала ниже 65 градусов. Включится вентилятор и будет дуть пока не нагонит 70 градусов, но не дольше 20 минут. Если за 20 минут температура не поднимется, то котел уйдет в состояние остановлен. Зачем здесь нижняя граница? - На мой взгляд это уже реально излишне. При указанных выше цифрах, котел реально выключится уже при температуре градусов 50-60, если топливо прогорело.

По поводу долго раздува топки - в режиме раздув время отведенное на раздув до останова берется умноженным на 2, тоесть это будет 40 минут в данном примере.

Плюс добавьте сюда еще и время открытой залонки. С которой все начиналось)...

Вентилятор не должен дуть час. Это плохой вентилятор. У меня он включается минут на 5. За это время надувает температуру как надо. И я не сказал бы что он очень мощный. Поставьте тот же любой автомобильный вентилтор от печки салона. На разборках их навалом недорогих будет. Дуют как надо!

АндрейT
Offline
Зарегистрирован: 08.02.2019

Да я уже думал поменять,но как то этот справляется )))
Я кстати на СТО работаю.
Завтра сфоткаю,Вам понятно будет.С ним компактнее и  он в принципе справляется.
Да,пробовал последнюю прошивку, что то ардуинка стала задумываться.
Тормозит с выводом температуры.
И на кнопки хуже реагирует.
Залил предпоследнюю.

А аварийное отключение по нижнему пределу, всётаки надо.
Ну это моё мнение.

Спасибо огромное.Без Вас этого контроллера бы небыло.

С Ув Андрей.

АндрейT
Offline
Зарегистрирован: 08.02.2019

Сейчас пару фоток выложу,моего термостата и заслонки

АндрейT
Offline
Зарегистрирован: 08.02.2019

АндрейT
Offline
Зарегистрирован: 08.02.2019

И вот ещё,в порядке обсуждения, То что часы стали тикать-действительно-класс.
И возврат котла в режим после аварии тоже.
А вот это: "Режим интервальной продувки, когда давно не было включения - если время заслонки стоит ноль - то используем вентилятор, если время заслонки не равно нулю - то используем только заслонку для продувки."
Для моего котла как раз надо чтоб и вентилятор подул.
Всётаки залью последнюю прошивку и пойду подключать к котлу.

С ув Андрей.