Контроллер для пивоварни (моя версия)

maxi_10
Offline
Зарегистрирован: 05.01.2012
Знакомый решил заняться пивоварением и попросил написать для него контроллер для нагревательного бака.
 
Далее фото и код.
 
 
 
 
 
#include <Wire.h>
#include <PID_v1.h>
#include <TimeHelpers.h>
#include <OneWire.h>
#include <LiquidCrystal_I2C.h>
#include <DS1307.h>
#include <RTClib.h>
#include <EEPROM.h>

#define ONE_WIRE_BUS 10 // Шина датчика температуры
#define P_Temp 0        // Поле масива с данными температуры
#define P_Time 1        // Поле масива с данными времени
#define P_Alar 2        // Поле масива с данными сигнализации
#define TEN1 13         // Порт шины нагревательного элемента 1
#define TEN2 6          // Порт шины нагревательного элемента 2
#define BUTON_MENU 4    // Порт входа кнопки меню (притянут к +)
#define BUZZ 8          // Порт выхода на пьезоизлучатель
#define noTrem 150      // Задержка антидребезга

RTC_DS1307 rtc;         // Инициализация Часов точного времени
OneWire  ds(10);        // Инициализация шины датчика измерения температуры
LiquidCrystal_I2C lcd(0x27, 20, 4); // Инициализация дисплея

double Setpoint = EEPROM.read(110); // Создание переменной устанавливаемой температуры (и передача данных из памяти)
double Input;           // Создание переменной входящих данных в ПИД регулятор 
double Output;          // Создание переменной исходящих данных из ПИД регулятора
double consKp = 5, consKi = 50, consKd = 0.5; // Создание переменных констант ПИД регулятора (Обычный режим)(быстрый режим)
//double tochKp = 5,  tochKi = 2,  tochKd = 0.05; // Создание переменных констант ПИД регулятора (точный режим)(медленный режим)

PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT); // Инициализация ПИД регулятора

DateTime now;          // Создания объекта Часов точного времени

byte type_s;           // Создание переменной типа датчика измерения температуры
byte data[12];         // Создание переменной данных датчика температуры
byte addr[8];          // Создание переменной адреса датчика температуры
float celsius;         // Создание переменной значения измерений датчика температуры

int Moschnost;         // Создание переменной мощности нагревательного элемента
int U_D = 5;           // Создание переменной джойстика (движение вверх, вниз)
int L_R = 0;           // Создание переменной джойстика (движение в лево, в право)
boolean Vhod_v_menu = 0; // Создание переменной флага входа в меню
int T_Upr = EEPROM.read(111); // Создание переменной указывающий тип управления (и передача данных из памяти)

int _HR = 0;          // Создание переменной хранения значения часов
int _MI = 0;          // Создание переменной хранения значения минут
int _SE = 0;          // Создание переменной хранения значения секунд
int A_H = 0;          // Создание переменной хранения значения часов при изменении времени
int A_M = 0;          // Создание переменной хранения значения минут при изменении времени
boolean chg_Time = 0; // Создание переменной флага изменения значения времени

#define PunkMenu 20   // Указатель на количество пунктов меню 
boolean Dreft0 = 0;   // Создание переменной флага отрисовки 0 поля меню
boolean Dreft1 = 0;   // Создание переменной флага отрисовки 1 поля меню
boolean Dreft2 = 0;   // Создание переменной флага отрисовки 2 поля меню
boolean Dreft3 = 0;   // Создание переменной флага отрисовки 3 поля меню
boolean Dreft4 = 0;   // Создание переменной флага отрисовки 4 поля меню
boolean Dreft5 = 0;   // Создание переменной флага отрисовки 5 поля меню
boolean Dreft_MAS_0 = 0;   // Создание переменной флага отрисовки 6 поля меню
boolean Dreft_MAS_1 = 0;   // Создание переменной флага отрисовки 7 поля меню
boolean Dreft_MAS_2 = 0;   // Создание переменной флага отрисовки 8 поля меню
boolean Dreft_MAS_3 = 0;   // Создание переменной флага отрисовки 9 поля меню
boolean Dreft_MAS_4 = 0;  // Создание переменной флага отрисовки 10 поля меню

//    Temperatura,Vremya,Alarm                
int Pat[5][3] = {{26,1,0},  // Создание масива данных програмируемых периодов
                 {28,0,0},  // Создание масива данных програмируемых периодов 
                 {30,0,0},  // Создание масива данных програмируемых периодов
                 {40,0,0},  // Создание масива данных програмируемых периодов
                 {50,0,0}}; // Создание масива данных програмируемых периодов
                 
int P_Numb = 0;             // Создание переменной определения пункта масива програмируемых периодов (общего назначения)
int R_Numb = 0;             // Создание переменной определения пункта масива програмируемых периодов (отрисовка меню)
long NeoTime = 0;           // Создание переменной хранения значения unix времени конца выполнения программы
boolean rasTempBol = 0;     // Создание переменной флага необходимости расчета времени
long unixt;                 // Создание переменной хранения текущего unix времени
long Sec;                   // Создание переменной временивыполнения программы
int cur = 1;                // Создание переменной указателя элемента моссива (отрисовка меню)
boolean StartTime = 0;      // Создание переменной начала работы по заданному переоду (Р1 - Р5)
boolean Rabota = 1;         // Создание переменной дополнительная переменная управления переключением периодов (Р1 - Р5)
boolean Alarm = 0;          // Создание переменной значения сигнализации
boolean chgTemp = 0;        // Создание переменной флага изменения значения температуры 
boolean flag = 0;           // Создание переменной флага управления пьезо излучателем

void setup()
{
  rtc.begin();
  lcd.init();
  lcd.backlight();
  pinMode(TEN1, OUTPUT);
  pinMode(TEN2, OUTPUT);
  pinMode(BUZZ, OUTPUT);
  pinMode(BUTON_MENU, INPUT_PULLUP);

// DO_EVERY(250,{   });

  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }
  
  if (Setpoint > 90){Setpoint = 20;}       // на случай если значение в памяти будет отсутствовать или быть неккоректным
  if (T_Upr > 3 || T_Upr < 0){T_Upr = 0;}  // на случай если значение в памяти будет отсутствовать или быть неккоректным
  
  myPID.SetMode(AUTOMATIC);
  lcd.clear();
  tone(BUZZ, 3000, 500);                  // сигнал готовности устройства
}

void loop()
{
  getTemp();
  Input = celsius;                       // глупо... но пусть будет.
  now = rtc.now();
  Pause();
  podderzhTemp();
  Menu();
/*  
    double gap = abs(Setpoint-Input);
    if (gap <= 5){myPID.SetTunings(consKp, consKi, consKd);}
    else         {myPID.SetTunings(tochKp, tochKi, tochKd);} 
*/  
    myPID.Compute();
    if (T_Upr == 0){analogWrite(TEN1, 0);analogWrite(TEN2, 0);}
    if (T_Upr == 1){analogWrite(TEN1, 255);analogWrite(TEN2, 255);}    
    if (T_Upr == 2){analogWrite(TEN1, Output);analogWrite(TEN2, Output);}
    if (T_Upr == 3){
       Setpoint = Pat[P_Numb][P_Temp];
       Alarm =    Pat[P_Numb][P_Alar];
       int tempIN = abs(Input);
       int SetpIN = abs(Setpoint);
       if (SetpIN == tempIN) {StartTime = 1;}
       analogWrite(TEN1, Output);analogWrite(TEN2, Output);
    }   
    _HR = now.hour();
    _MI = now.minute();
    _SE = now.second();
    Moschnost = map(Output, 0, 255, 0, 100);
    if (Vhod_v_menu == 0) {
      lcd.setCursor(0, 0);lcd.print("T.ustC ");
      lcd.print(Setpoint);
      if (T_Upr == 3) {lcd.print("    PREF");}
      if (T_Upr == 2) {lcd.print("    AUTO");}
      if (T_Upr < 2)  {lcd.print("  MANUAL");}
      lcd.setCursor(0, 1);
      lcd.print("T.zidC ");
      lcd.print(Input);
      if (T_Upr == 3) {
        lcd.setCursor(15, 1);
        lcd.print("PAT ");
        lcd.print(P_Numb + 1);
      }else{lcd.setCursor(13, 1);lcd.print("PAT OFF");}     
      lcd.setCursor(0, 2);lcd.print("P.ten% ");
      if (Moschnost <= 100) {lcd.setCursor(7, 2);lcd.print(Moschnost); lcd.print(" ");}
      if (Moschnost < 10)   {lcd.setCursor(7, 2);lcd.print(Moschnost); lcd.print("  ");}
      if (Output <= 255) {lcd.setCursor(14, 2);lcd.print(Output); lcd.print("");}
      if (Output < 100)  {lcd.setCursor(14, 2);lcd.print(Output); lcd.print(" ");}
      if (Output < 10)   {lcd.setCursor(14, 2);lcd.print(Output); lcd.print("  ");}
      lcd.setCursor(0, 3);
      if (T_Upr != 3) {lcd.print("Time:  ");}
      if (_HR < 10) {lcd.print("0");lcd.print(_HR);}else{lcd.print(_HR);}lcd.print(":");
      if (_MI < 10) {lcd.print("0");lcd.print(_MI);}else{lcd.print(_MI);}lcd.print(":");
      if (_SE < 10) {lcd.print("0");lcd.print(_SE);}else{lcd.print(_SE);}
      if (T_Upr == 3) {
        lcd.setCursor(10, 3);lcd.print("Count:");
        if (StartTime != 0) {
          if (Rabota == 1){
            if (Sec < 10){                 lcd.setCursor(16, 3); lcd.print("   ");lcd.print(Sec);}
            if (Sec < 100 && Sec > 10){    lcd.setCursor(16, 3); lcd.print("  "); lcd.print(Sec);}
            if (Sec < 1000 && Sec > 100){  lcd.setCursor(16, 3); lcd.print(" ");  lcd.print(Sec);}
            if (Sec < 10000 && Sec > 1000){lcd.setCursor(16, 3);                  lcd.print(Sec);}
          }
        }else{
          if (Rabota == 1){
          long Pr = Pat[P_Numb][P_Time] * 60;
            if (Pr < 10){                lcd.setCursor(16, 3); lcd.print("   ");lcd.print(Pr);}
            if (Pr < 100 && Pr > 10){    lcd.setCursor(16, 3); lcd.print("  "); lcd.print(Pr);}
            if (Pr < 1000 && Pr > 100){  lcd.setCursor(16, 3); lcd.print(" ");  lcd.print(Pr);}
            if (Pr < 10000 && Pr > 1000){lcd.setCursor(16, 3);                  lcd.print(Pr);} 
          }         
        }
      }else{lcd.setCursor(15, 3);lcd.print("     ");}
    }
}

void Pause(){
  getPressedButton();
  if (analogRead(0) > 1000) {Rabota = 1;Alarm = 0;} // Reset Pause
  if (Rabota == 0) {
    DO_EVERY(1000,{ 
      if (flag == 0)   {lcd.setCursor(13, 1); lcd.print("*");   tone(BUZZ, 1000); flag = 1;}
      else             {lcd.setCursor(13, 1); lcd.print(" "); noTone(BUZZ);       flag = 0;}
    });
  }else{if (T_Upr == 3){lcd.setCursor(13, 1); lcd.print(" "); noTone(BUZZ);       flag = 0;}}
}

void podderzhTemp(){
  if (StartTime == 1){
     rasTemp();
     unixt = now.unixtime();
     Sec = NeoTime - unixt;
     if (Rabota = 1){
       if (Sec <= 0){
         if (Alarm == 1) {
           Rabota = 0;
         }else{ 
           if (P_Numb <= 4){
             rasTempBol = 0;
             StartTime = 0;
             P_Numb++;
             if (Pat[P_Numb][P_Time] == 0){StopRab();}
           }else{StopRab();}
         }
       }
      }
  }
}

void StopRab(){
                   Setpoint = 10;
                   T_Upr = 0;
                   P_Numb = 0;
                   StartTime = 0;
                   Rabota = 1; 
                   Alarm = 0;
                   U_D = 5;
                   L_R = 0;
                   lcd.setCursor(15, 1);lcd.print("  STOP  ");
                   lcd.setCursor(13, 1);lcd.print("PAT OFF");
                   tone(BUZZ, 400, 100);    // сигнал завершения работы всех программ
                   tone(BUZZ, 500, 200);    // сигнал завершения работы всех программ
                   tone(BUZZ, 700, 300);    // сигнал завершения работы всех программ
                   tone(BUZZ, 1000, 200);    // сигнал завершения работы всех программ
                   tone(BUZZ, 6000, 100);    // сигнал завершения работы всех программ
}

void rasTemp(){
  if (rasTempBol == 0){NeoTime = (Pat[P_Numb][P_Time] * 60) + now.unixtime();rasTempBol = 1;}
}

void Menu() {
  getPressedButton();
  if (Vhod_v_menu == 0) {    // выключатель освещения :)
    if (analogRead(1) < 100) {lcd.backlight();}
    if (analogRead(1) > 1000) {lcd.noBacklight();}    
  }

  if (digitalRead(BUTON_MENU) == 0) {
    U_D = 5;
    L_R = 0;
    lcd.backlight();
    if (Vhod_v_menu == 0) {
        Vhod_v_menu = 1;
        ClearNol();
        lcd.print("0.     Setup");
        lcd.setCursor(0, 1);lcd.print("    left or right");
        Dreft0 = 1;
        delay(100);
    } else {
        lcd.clear();
        lcd.setCursor(8, 1);lcd.print("EXIT");
        chg_Time = 0;
        L_R = 0;
        Vhod_v_menu = 0;
        
        if (EEPROM.read(110) != Setpoint) {  // Сохранение значения температуры
          if (T_Upr != 3 && chgTemp == 1){
            EEPROM.write(110, Setpoint); 
            lcd.setCursor(8, 1); 
            lcd.print("SAVE");
          }
        }
        if (EEPROM.read(111) != T_Upr) {   // Сохранение значения режима работы
          if (T_Upr != 3){
            EEPROM.write(111, T_Upr);
            lcd.setCursor(8, 1); 
            lcd.print("SAVE");
          }
        }
        delay(1000);
        lcd.clear();
      }
     }
//----------------------------------------------------------------------------------
  if (Vhod_v_menu == 1) {
    if (L_R == 0) { 
      if (Dreft0 == 0) {
        ClearNol();
        lcd.print("0.     Setup");
        lcd.setCursor(0, 1);lcd.print("    left or right");
        Dreft0 = 1;}} else {Dreft0 = 0;}
//-------------
    if (L_R == 1) {
      if (Dreft1 == 0) {
        chgTemp = 0;
        ClearNol();
        lcd.print("1. Setup Temp");
        lcd.setCursor(0, 2);lcd.print(Setpoint);
        Dreft1 = 1;}} else {Dreft1 = 0;}
//-------------
    if (L_R == 2) {
      if (Dreft2 == 0) {
        ClearNol();
        lcd.print("2. Setup Rele ON-OFF");
        lcd.setCursor(0, 2);
        if (T_Upr == 0) {lcd.print("1-4 OFF   ");}
        if (T_Upr == 1) {lcd.print("2-4 ON    ");}
        if (T_Upr == 2) {lcd.print("3-4 AUTO  ");}
        if (T_Upr == 3) {lcd.print("4-4 PREF  ");}
        Dreft2 = 1;}} else {Dreft2 = 0;}
//-------------    
    if (L_R == 3) {
      if (Dreft3 == 0) {
        A_H = now.hour();
        ClearNol();
        lcd.print("3. Setup Time hour");
        lcd.setCursor(0, 1);
        now = rtc.now();
        lcd.print(now.hour());
        lcd.print(":");
        lcd.print(now.minute());
        lcd.print(":");
        lcd.print(now.second());
          lcd.setCursor(0, 2);
          lcd.print(A_H);
          lcd.print("  hour");        
        Dreft3 = 1;}} else {Dreft3 = 0;}
//-------------        
    if (L_R == 4) {
      if (Dreft4 == 0) {
        A_M = now.minute();
        ClearNol();
        lcd.print("4. Setup Time minute");
        lcd.setCursor(0, 1);
        now = rtc.now();
        lcd.print(now.hour());
        lcd.print(":");
        lcd.print(now.minute());
        lcd.print(":");
        lcd.print(now.second());
          lcd.setCursor(0, 2);
          lcd.print(A_M);
          lcd.print("  minute");        
        Dreft4 = 1;}} else {Dreft4 = 0;}
//-------------        
    if (L_R == 5) {
      if (Dreft5 == 0) {
        ClearNol();
        lcd.print("5. Setup SET Time");
        lcd.setCursor(0, 1);
        now = rtc.now();
        lcd.print(now.hour());
        lcd.print(":");
        lcd.print(now.minute());
        lcd.print(":");
        lcd.print(now.second());
        lcd.print(" - OLD");
        lcd.setCursor(0, 2);
        lcd.print(A_H);
        lcd.print(":");
        lcd.print(A_M);
        lcd.print(":00");
        lcd.print(" - NEW");
        lcd.setCursor(0, 3);
        if (chg_Time == 1){lcd.print("Press UP to SET");}else{lcd.print("Time not changed");}
        Dreft5 = 1;}} else {Dreft5 = 0;}
//-------------                
    if (L_R == 6 || L_R == 11 || L_R == 16) {
      if (Dreft_MAS_0 == 0) {
		if (L_R == 6) {Print_Pat_Screan_Temp();}
		if (L_R == 11) {Print_Pat_Screan_Time();}
		if (L_R == 16) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(1, 3);lcd.print("^^");
		Dreft_MAS_0 = 1;}} else {Dreft_MAS_0 = 0;}
		
    if (L_R == 7 || L_R == 12 || L_R == 17) {
      if (Dreft_MAS_1 == 0) {
		if (L_R == 7) {Print_Pat_Screan_Temp();}
		if (L_R == 12) {Print_Pat_Screan_Time();}
		if (L_R == 17) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(5, 3);lcd.print("^^");
		Dreft_MAS_1 = 1;}} else {Dreft_MAS_1 = 0;}	
		
    if (L_R == 8 || L_R == 13 || L_R == 18) {
      if (Dreft_MAS_2 == 0) {
		if (L_R == 8) {Print_Pat_Screan_Temp();}
		if (L_R == 13) {Print_Pat_Screan_Time();}
		if (L_R == 18) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(9, 3);lcd.print("^^");
		Dreft_MAS_2 = 1;}} else {Dreft_MAS_2 = 0;}
		
    if (L_R == 9 || L_R == 14 || L_R == 19) {
      if (Dreft_MAS_3 == 0) {
		if (L_R == 9) {Print_Pat_Screan_Temp();}
		if (L_R == 14) {Print_Pat_Screan_Time();}
		if (L_R == 19) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(13, 3);lcd.print("^^");
		Dreft_MAS_3 = 1;}} else {Dreft_MAS_3 = 0;}		
		
    if (L_R == 10 || L_R == 15 || L_R == 20) {
      if (Dreft_MAS_4 == 0) {
		if (L_R == 10) {Print_Pat_Screan_Temp();}
		if (L_R == 15) {Print_Pat_Screan_Time();}
		if (L_R == 20) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(17, 3);lcd.print("^^");
		Dreft_MAS_4 = 1;}} else {Dreft_MAS_4 = 0;}
//##################################################################
  
    if (U_D > 5) {
//-------------      
      if (L_R == 1) {
        Setpoint = Setpoint + 0.5;
        chgTemp = 1;
        lcd.setCursor(0, 2);
        lcd.print(Setpoint);
        U_D = 5;
      }
//-------------
      if (L_R == 2) {
        if (T_Upr < 4) {T_Upr = T_Upr + 1;}
          lcd.setCursor(0, 2);
        if (T_Upr == 0) {lcd.print("1-4 OFF   ");}
        if (T_Upr == 1) {lcd.print("2-4 ON    ");}
        if (T_Upr == 2) {lcd.print("3-4 AUTO  ");}
        if (T_Upr == 3) {lcd.print("4-4 PREF  ");}
        U_D = 5;
      }
//-------------
      if (L_R == 3) {
        A_H = A_H + 1;
        chg_Time = 1;
        if (A_H > 23) {A_H = 23;}
        if (A_H < 0) {A_H = 0;}
          lcd.setCursor(0, 2);
          lcd.print(A_H);
          lcd.print("  hour");
        U_D = 5;
      }
//-------------
      if (L_R == 4) {
        A_M = A_M + 1;
        chg_Time = 1;
        if (A_M > 59) {A_M = 59;}
        if (A_M < 0) {A_M = 0;}
          lcd.setCursor(0, 2);
          lcd.print(A_M);
          lcd.print("  minute");
        U_D = 5;
      } 
//-------------
       if (L_R == 5) {
        if (chg_Time == 1){ 
          rtc.adjust(DateTime(2015, 2, 27, A_H, A_M, 0));
          chg_Time = 0;
          lcd.setCursor(0, 0);
          lcd.clear();
          lcd.print("6. Setup SET Time");
          lcd.setCursor(0, 1);
          now = rtc.now();
          lcd.print(now.hour());
          lcd.print(":");
          lcd.print(now.minute());
          lcd.print(":");
          lcd.print(now.second());
          lcd.setCursor(0, 2);
          lcd.print(A_H);
          lcd.print(":");
          lcd.print(A_M);
          lcd.print(":00");
          lcd.setCursor(0, 3);
          lcd.print("Save - ok");
        }
        U_D = 5;
      }
//-------------    
      if (L_R == 6 || L_R == 11 || L_R == 16) {
		if (L_R == 6) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 11) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 16) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(1, 3);lcd.print("^^");
        U_D = 5;
      }		
       if (L_R == 7 || L_R == 12 || L_R == 17) {
		if (L_R == 7) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 12) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 17) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(5, 3);lcd.print("^^");
        U_D = 5;
      }		
       if (L_R == 8 || L_R == 13 || L_R == 18) {
		if (L_R == 8) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 13) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 18) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(9, 3);lcd.print("^^");
        U_D = 5;
      }			
       if (L_R == 9 || L_R == 14 || L_R == 19) {
		if (L_R == 9) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 14) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 19) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(13, 3);lcd.print("^^");
        U_D = 5;
      }		
       if (L_R == 10 || L_R == 15 || L_R == 20) {
		if (L_R == 10) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 15) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 20) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(17, 3);lcd.print("^^");
        U_D = 5;
      }
//----------------------------------------------------------------------------------
    } else if (U_D < 5) {
      if (L_R == 1) {
        Setpoint = Setpoint - 0.5;
        chgTemp = 1;
        lcd.setCursor(0, 2);
        lcd.print(Setpoint);
        U_D = 5;
      }
//-------------
      if (L_R == 2) {
        if (T_Upr > 0) {T_Upr = T_Upr - 1;}
        lcd.setCursor(0, 2);
        if (T_Upr == 0) {lcd.print("1-4 OFF   ");}
        if (T_Upr == 1) {lcd.print("2-4 ON    ");}
        if (T_Upr == 2) {lcd.print("3-4 AUTO  ");}
        if (T_Upr == 3) {lcd.print("4-4 PREF  ");}
        U_D = 5;
      }
//-------------
      if (L_R == 3) {
        A_H = A_H - 1;
        chg_Time = 1;
        if (A_H > 23) {A_H = 23;}
        if (A_H < 0) {A_H = 0;}
          lcd.setCursor(0, 2);
          lcd.print(A_H);
          lcd.print("  hour");
        U_D = 5;
      } 
//-------------
      if (L_R == 4) {
        A_M = A_M - 1;
        chg_Time = 1;
        if (A_M > 59) {A_M = 59;}
        if (A_M < 0) {A_M = 0;}
          lcd.setCursor(0, 2);
          lcd.print(A_M);
          lcd.print("  minute");
        U_D = 5;
      }
//-------------
       if (L_R == 5) {
        if (chg_Time == 1){ 
          A_H = now.hour();
          A_M = now.minute();
          chg_Time = 0;
        lcd.setCursor(0, 0);
        lcd.clear();
        lcd.print("6. Setup SET Time");
        lcd.setCursor(0, 1);
        now = rtc.now();
        lcd.print(now.hour());
        lcd.print(":");
        lcd.print(now.minute());
        lcd.print(":");
        lcd.print(now.second());
        lcd.setCursor(0, 2);
        lcd.print(A_H);
        lcd.print(":");
        lcd.print(A_M);
        lcd.print(":00");
          lcd.setCursor(0, 3);
        lcd.print("Changed - Cancel");
        }
        U_D = 5;
      }

//-------------      
      if (L_R == 6 || L_R == 11 || L_R == 16) {
		if (L_R == 6) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 11) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 16) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(1, 3);lcd.print("^^");
        U_D = 5;
      }		
       if (L_R == 7 || L_R == 12 || L_R == 17) {
		if (L_R == 7) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 12) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 17) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(5, 3);lcd.print("^^");
        U_D = 5;
      }		
       if (L_R == 8 || L_R == 13 || L_R == 18) {
		if (L_R == 8) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 13) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 18) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(9, 3);lcd.print("^^");
        U_D = 5;
      }			
       if (L_R == 9 || L_R == 14 || L_R == 19) {
		if (L_R == 9) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 14) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 19) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(13, 3);lcd.print("^^");
        U_D = 5;
      }		
       if (L_R == 10 || L_R == 15 || L_R == 20) {
		if (L_R == 10) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 15) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 20) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(17, 3);lcd.print("^^");
        U_D = 5;
      }
//-------------
    }
  }
}


void CHG_P_Temp(){
	if (U_D > 5) {
		if (Dreft_MAS_0 == 1) {if (Pat[0][P_Temp] < 100) {Pat[0][P_Temp] += 1;}}
		if (Dreft_MAS_1 == 1) {if (Pat[1][P_Temp] < 100) {Pat[1][P_Temp] += 1;}}
		if (Dreft_MAS_2 == 1) {if (Pat[2][P_Temp] < 100) {Pat[2][P_Temp] += 1;}}
		if (Dreft_MAS_3 == 1) {if (Pat[3][P_Temp] < 100) {Pat[3][P_Temp] += 1;}}
		if (Dreft_MAS_4 == 1) {if (Pat[4][P_Temp] < 100) {Pat[4][P_Temp] += 1;}}}
	else{		
		if (Dreft_MAS_0 == 1) {if (Pat[0][P_Temp] > 0) {Pat[0][P_Temp] -= 1;}}
		if (Dreft_MAS_1 == 1) {if (Pat[1][P_Temp] > 0) {Pat[1][P_Temp] -= 1;}}
		if (Dreft_MAS_2 == 1) {if (Pat[2][P_Temp] > 0) {Pat[2][P_Temp] -= 1;}}
		if (Dreft_MAS_3 == 1) {if (Pat[3][P_Temp] > 0) {Pat[3][P_Temp] -= 1;}}
		if (Dreft_MAS_4 == 1) {if (Pat[4][P_Temp] > 0) {Pat[4][P_Temp] -= 1;}}}
}

void CHG_P_Time(){
	if (U_D > 5) {
		if (Dreft_MAS_0 == 1) {if (Pat[0][P_Time] < 32000) {Pat[0][P_Time] += 1;}}
		if (Dreft_MAS_1 == 1) {if (Pat[1][P_Time] < 32000) {Pat[1][P_Time] += 1;}}
		if (Dreft_MAS_2 == 1) {if (Pat[2][P_Time] < 32000) {Pat[2][P_Time] += 1;}}
		if (Dreft_MAS_3 == 1) {if (Pat[3][P_Time] < 32000) {Pat[3][P_Time] += 1;}}
		if (Dreft_MAS_4 == 1) {if (Pat[4][P_Time] < 32000) {Pat[4][P_Time] += 1;}}}
	else{
		if (Dreft_MAS_0 == 1) {if (Pat[0][P_Time] > 0) {Pat[0][P_Time] -= 1;}}
		if (Dreft_MAS_1 == 1) {if (Pat[1][P_Time] > 0) {Pat[1][P_Time] -= 1;}}
		if (Dreft_MAS_2 == 1) {if (Pat[2][P_Time] > 0) {Pat[2][P_Time] -= 1;}}
		if (Dreft_MAS_3 == 1) {if (Pat[3][P_Time] > 0) {Pat[3][P_Time] -= 1;}}
		if (Dreft_MAS_4 == 1) {if (Pat[4][P_Time] > 0) {Pat[4][P_Time] -= 1;}}}
}

void CHG_P_Alar(){
	if (U_D > 5) {
		if (Dreft_MAS_0 == 1) {Pat[0][P_Alar] = 1;}
		if (Dreft_MAS_1 == 1) {Pat[1][P_Alar] = 1;}
		if (Dreft_MAS_2 == 1) {Pat[2][P_Alar] = 1;}
		if (Dreft_MAS_3 == 1) {Pat[3][P_Alar] = 1;}
		if (Dreft_MAS_4 == 1) {Pat[4][P_Alar] = 1;}}
	else{
		if (Dreft_MAS_0 == 1) {Pat[0][P_Alar] = 0;}
		if (Dreft_MAS_1 == 1) {Pat[1][P_Alar] = 0;}
		if (Dreft_MAS_2 == 1) {Pat[2][P_Alar] = 0;}
		if (Dreft_MAS_3 == 1) {Pat[3][P_Alar] = 0;}
		if (Dreft_MAS_4 == 1) {Pat[4][P_Alar] = 0;}}
}

void Print_Pat_Screan_Temp(){
    ClearNol();
    cur = 1;  
    lcd.print("6.  Setup PAT Temp");	
	for (int i = 1; i <= 5; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 1;
	for (int i = 0; i < 5; i++) {
		R_Numb = i;
		if (cur == 1) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		lcd.print(Pat[R_Numb][P_Temp]);
	}
}
void Print_Pat_Screan_Time(){
    ClearNol();
    cur = 1;
    lcd.print("7.  Setup PAT Time");	
	for (int i = 1; i <= 5; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 1;
	for (int i = 0; i < 5; i++) {
		R_Numb = i;
		if (cur == 1) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		if (Pat[R_Numb][P_Time] < 10){lcd.print(Pat[R_Numb][P_Time]);lcd.print(" ");}else{lcd.print(Pat[R_Numb][P_Time]);}
	}
}
void Print_Pat_Screan_Alarm(){
    ClearNol();
    cur = 1;
    lcd.print("8.  Setup PAT Alarm");	
	for (int i = 1; i <= 5; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 1;
	for (int i = 0; i < 5; i++) {
		R_Numb = i;
		if (cur == 1) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		if (Pat[R_Numb][P_Alar] == 0){lcd.print("OF");}else{lcd.print("ON");}
	}
}

void ClearNol(){
        lcd.clear();        
        lcd.setCursor(0, 0);
}

void getPressedButton() {
  DO_EVERY(noTrem,{if (analogRead(0) < 100) {L_R--;}});
  DO_EVERY(noTrem,{if (analogRead(0) > 1000){L_R++;}});
  DO_EVERY(noTrem,{if (analogRead(1) < 100) {U_D++;}});
  DO_EVERY(noTrem,{if (analogRead(1) > 1000){U_D--;}});
  if (L_R < 0){L_R = PunkMenu;}
  if (L_R > PunkMenu){L_R = 0;}
}

void getTemp() {
  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);
  ds.reset();
  ds.select(addr);
  ds.write(0xBE);
  for ( int i = 0; i < 9; i++) {
    data[i] = ds.read();
  }
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3;
    if (data[7] == 0x10) {
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw & ~7;
    else if (cfg == 0x20) raw = raw & ~3;
    else if (cfg == 0x40) raw = raw & ~1;
  }

  celsius = (float)raw / 16.0;
  return;
}

 

 

maxi_10
Offline
Зарегистрирован: 05.01.2012

Следующяя более оптимизированная версия прошивки

переработанно меню, добавленно управление рециркуляционным насосом, добавленны дополнительные 5 программ (P1 - P9), добавленна возможность изменять интегральный коэфициент ПИД регулятора.

Система протестированна на чайнике, по результатам тестов был удален НЧ фильтр между SSR и UNL.

 

#include <Wire.h>
#include <PID_v1.h>
#include <TimeHelpers.h>
#include <OneWire.h>
#include <LiquidCrystal_I2C.h>
#include <DS1307.h>
#include <RTClib.h>
#include <EEPROM.h>

#define ONE_WIRE_BUS 10 // Шина датчика температуры
#define P_Temp 0        // Поле масива с данными температуры
#define P_Time 1        // Поле масива с данными времени
#define P_Alar 2        // Поле масива с данными сигнализации
#define TEN1 13         // Порт шины нагревательного элемента 1
#define TEN2 6          // Порт шины нагревательного элемента 2
#define NASOS 2         // Порт шины рециркуляционного насоса
#define BUTON_MENU 4    // Порт входа кнопки меню (притянут к +)
#define BUZZ 8          // Порт выхода на пьезоизлучатель
#define noTrem 150      // Задержка антидребезга

RTC_DS1307 rtc;                     // Инициализация Часов точного времени
OneWire  ds(10);                    // Инициализация шины датчика измерения температуры
LiquidCrystal_I2C lcd(0x27, 20, 4); // Инициализация дисплея

double Setpoint = EEPROM.read(110); // Создание переменной устанавливаемой температуры (и передача данных из памяти)
double Input;                       // Создание переменной входящих данных в ПИД регулятор 
double Output;                      // Создание переменной исходящих данных из ПИД регулятора
double consKp = 5;                  // Создание переменных констант ПИД регулятора (Обычный режим)(быстрый режим)
double consKi = EEPROM.read(112);   // Создание переменных констант ПИД регулятора (Обычный режим)(быстрый режим)
double consKd = 1;                  // Создание переменных констант ПИД регулятора (Обычный режим)(быстрый режим)
double KP = 0;                      // Создание переменных констант ПИД регулятора
double KI = 0;                      // Создание переменных констант ПИД регулятора
double KD = 0;                      // Создание переменных констант ПИД регулятора

//double tochKp = 5,  tochKi = 2,  tochKd = 0.05; // Создание переменных констант ПИД регулятора (точный режим)(медленный режим)

PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT); // Инициализация ПИД регулятора

DateTime now;          // Создания объекта Часов точного времени

byte type_s;           // Создание переменной типа датчика измерения температуры
byte data[12];         // Создание переменной данных датчика температуры
byte addr[8];          // Создание переменной адреса датчика температуры
float celsius;         // Создание переменной значения измерений датчика температуры

int Moschnost;         // Создание переменной мощности нагревательного элемента
int U_D = 5;           // Создание переменной джойстика (движение вверх, вниз)
int L_R = 0;           // Создание переменной джойстика (движение в лево, в право)
boolean Vhod_v_menu = 0; // Создание переменной флага входа в меню
int T_Upr = 0; // EEPROM.read(111); // Создание переменной указывающий тип управления (и передача данных из памяти)

int _HR = 0;          // Создание переменной хранения значения часов
int _MI = 0;          // Создание переменной хранения значения минут
int _SE = 0;          // Создание переменной хранения значения секунд
int A_H = 0;          // Создание переменной хранения значения часов при изменении времени
int A_M = 0;          // Создание переменной хранения значения минут при изменении времени
boolean chg_Time = 0; // Создание переменной флага изменения значения времени

#define PunkMenu 35   // Указатель на количество пунктов меню 
boolean Dreft0 = 0;   // Создание переменной флага отрисовки 0 поля меню
boolean Dreft1 = 0;   // Создание переменной флага отрисовки 1 поля меню
boolean Dreft2 = 0;   // Создание переменной флага отрисовки 2 поля меню
boolean Dreft3 = 0;   // Создание переменной флага отрисовки 3 поля меню
boolean Dreft4 = 0;   // Создание переменной флага отрисовки 4 поля меню
boolean Dreft5 = 0;   // Создание переменной флага отрисовки 5 поля меню
boolean Dreft_MAS_0 = 0;   // Создание переменной флага отрисовки 6 поля меню
boolean Dreft_MAS_1 = 0;   // Создание переменной флага отрисовки 7 поля меню
boolean Dreft_MAS_2 = 0;   // Создание переменной флага отрисовки 8 поля меню
boolean Dreft_MAS_3 = 0;   // Создание переменной флага отрисовки 9 поля меню
boolean Dreft_MAS_4 = 0;   // Создание переменной флага отрисовки 10 поля меню
boolean Dreft_MAS_5 = 0;   // Создание переменной флага отрисовки 11 поля меню
boolean Dreft_MAS_6 = 0;   // Создание переменной флага отрисовки 12 поля меню
boolean Dreft_MAS_7 = 0;   // Создание переменной флага отрисовки 13 поля меню
boolean Dreft_MAS_8 = 0;   // Создание переменной флага отрисовки 14 поля меню
boolean Dreft_MAS_9 = 0;   // Создание переменной флага отрисовки 15 поля меню

//    Temperatura,Vremya,Alarm                
int Pat[10][3] = {{30,0,0}, // Создание масива данных програмируемых периодов 0
                 {40,0,0},  // Создание масива данных програмируемых периодов 1
                 {50,0,0},  // Создание масива данных програмируемых периодов 2
                 {60,0,0},  // Создание масива данных програмируемых периодов 3
                 {70,0,0},  // Создание масива данных програмируемых периодов 4 
                 {95,0,0},  // Создание масива данных програмируемых периодов 5
                 {95,0,0},  // Создание масива данных програмируемых периодов 6
                 {95,0,0},  // Создание масива данных програмируемых периодов 7
                 {95,0,0},  // Создание масива данных програмируемых периодов 8
                 {95,0,0}}; // Создание масива данных програмируемых периодов 9
                 
int P_Numb = 0;             // Создание переменной определения пункта масива програмируемых периодов (общего назначения)
int R_Numb = 0;             // Создание переменной определения пункта масива програмируемых периодов (отрисовка меню)
long NeoTime = 0;           // Создание переменной хранения значения unix времени конца выполнения программы
boolean rasTempBol = 0;     // Создание переменной флага необходимости расчета времени
long unixt;                 // Создание переменной хранения текущего unix времени
long Sec;                   // Создание переменной временивыполнения программы
int cur = 1;                // Создание переменной указателя элемента моссива (отрисовка меню)
boolean StartTime = 0;      // Создание переменной начала работы по заданному переоду (Р1 - Р5)
boolean Rabota = 1;         // Создание переменной дополнительная переменная управления переключением периодов (Р1 - Р5)
boolean Alarm = 0;          // Создание переменной значения сигнализации
boolean chgTemp = 0;        // Создание переменной флага изменения значения температуры 
boolean flag = 0;           // Создание переменной флага управления пьезо излучателем

int Povtor_Promyvka  = 5;   // Колличество циклов
int tekPovtor_Promyvka = 0; // Текущее количество циклов проверки
int EndOFNasos = 0;         // отключение насоса

byte Gradus[8] =            // Создание значка градуса
{ B01000,                   // Создание значка градуса
  B10100,                   // Создание значка градуса
  B01000,                   // Создание значка градуса
  B00000,                   // Создание значка градуса
  B00000,                   // Создание значка градуса
  B00000,                   // Создание значка градуса
  B00000,                   // Создание значка градуса
  B00000};                  // Создание значка градуса

void setup()
{
  KP = myPID.GetKp();
  KI = myPID.GetKi();
  KD = myPID.GetKd();
  lcd.createChar(0, Gradus);
  rtc.begin();
  lcd.init();
  lcd.backlight();
  pinMode(TEN1, OUTPUT);
  pinMode(TEN2, OUTPUT);
  pinMode(BUZZ, OUTPUT);
  pinMode(NASOS, OUTPUT);
  pinMode(BUTON_MENU, INPUT_PULLUP);

  digitalWrite(NASOS, LOW);
  analogWrite(TEN1, 0);
  analogWrite(TEN2, 0);
  
// DO_EVERY(250,{   });

  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }
  
  if (Setpoint > 90){Setpoint = 20;}             // на случай если значение в памяти будет отсутствовать или быть неккоректным
  if (consKi < 1 || consKi > 100){consKi = 50;}  // на случай если значение в памяти будет отсутствовать или быть неккоректным
  if (T_Upr > 3 || T_Upr < 0){T_Upr = 0;}        // на случай если значение в памяти будет отсутствовать или быть неккоректным
  
  myPID.SetMode(AUTOMATIC);
  lcd.clear();
  tone(BUZZ, 3000, 500);                  // сигнал готовности устройства
}

void loop()
{
  Prokachka();
  getTemp();
  Input = celsius;                       // глупо... но пусть будет.
  now = rtc.now();
  Pause();
  podderzhTemp();
  Menu();
    myPID.Compute();
    if (T_Upr == 0){analogWrite(TEN1, 0);analogWrite(TEN2, 0);}
    if (T_Upr == 1){analogWrite(TEN1, 255);analogWrite(TEN2, 255);}    
    if (T_Upr == 2){analogWrite(TEN1, Output);analogWrite(TEN2, Output);}
    if (T_Upr == 3){
       Setpoint = Pat[P_Numb][P_Temp];
       Alarm =    Pat[P_Numb][P_Alar];
       int tempIN = abs(Input);
       int SetpIN = abs(Setpoint);
       if (SetpIN == tempIN) {StartTime = 1;}
       analogWrite(TEN1, Output);analogWrite(TEN2, Output);
    }   
    _HR = now.hour();
    _MI = now.minute();
    _SE = now.second();
    Moschnost = map(Output, 0, 255, 0, 100);
    if (Vhod_v_menu == 0) {
      lcd.setCursor(0, 0);lcd.print("T.ust ");
      lcd.print(Setpoint); lcd.print('\0');
      if (T_Upr == 3) {lcd.print("    PREF");}
      if (T_Upr == 2) {lcd.print("    AUTO");}
      if (T_Upr < 2)  {lcd.print("  MANUAL");}
      lcd.setCursor(0, 1);
      lcd.print("T.zid ");
      lcd.print(Input); lcd.print('\0');
      if (T_Upr == 3) {
        lcd.setCursor(15, 1);
        lcd.print("PAT ");
        lcd.print(P_Numb);
      }else{lcd.setCursor(13, 1);lcd.print("PAT OFF");}     
      lcd.setCursor(0, 2);lcd.print("P.ten ");
      if (Moschnost <= 100) {lcd.setCursor(6, 2);lcd.print(Moschnost); lcd.print("% ");}
      if (Moschnost < 10)   {lcd.setCursor(6, 2);lcd.print(Moschnost); lcd.print("%  ");}
      if (Output <= 255) {lcd.setCursor(14, 2);lcd.print(Output); lcd.print("");}
      if (Output < 100)  {lcd.setCursor(14, 2);lcd.print(Output); lcd.print(" ");}
      if (Output < 10)   {lcd.setCursor(14, 2);lcd.print(Output); lcd.print("  ");}
      lcd.setCursor(0, 3);
      if (T_Upr != 3) {lcd.print("Time:  ");}
      if (_HR < 10) {lcd.print("0");lcd.print(_HR);}else{lcd.print(_HR);}lcd.print(":");
      if (_MI < 10) {lcd.print("0");lcd.print(_MI);}else{lcd.print(_MI);}lcd.print(":");
      if (_SE < 10) {lcd.print("0");lcd.print(_SE);}else{lcd.print(_SE);}
      if (T_Upr == 3) {
        lcd.setCursor(10, 3);lcd.print("Count:");
        if (StartTime != 0) {
          if (Rabota == 1){
            if (Sec < 10){                 lcd.setCursor(16, 3); lcd.print("   ");lcd.print(Sec);}
            if (Sec < 100 && Sec > 10){    lcd.setCursor(16, 3); lcd.print("  "); lcd.print(Sec);}
            if (Sec < 1000 && Sec > 100){  lcd.setCursor(16, 3); lcd.print(" ");  lcd.print(Sec);}
            if (Sec < 10000 && Sec > 1000){lcd.setCursor(16, 3);                  lcd.print(Sec);}
          }
        }else{
          if (Rabota == 1){
          long Pr = Pat[P_Numb][P_Time] * 60;
            if (Pr < 10){                lcd.setCursor(16, 3); lcd.print("   ");lcd.print(Pr);}
            if (Pr < 100 && Pr > 10){    lcd.setCursor(16, 3); lcd.print("  "); lcd.print(Pr);}
            if (Pr < 1000 && Pr > 100){  lcd.setCursor(16, 3); lcd.print(" ");  lcd.print(Pr);}
            if (Pr < 10000 && Pr > 1000){lcd.setCursor(16, 3);                  lcd.print(Pr);} 
          }         
        }
      }else{lcd.setCursor(15, 3);lcd.print("     ");}
    }
}

void Pause(){
  getPressedButton();
  if (analogRead(0) > 1000) {Rabota = 1;Alarm = 0;} // Reset Pause
  if (Rabota == 0) {
    DO_EVERY(1000,{ 
      if (flag == 0)   {lcd.setCursor(13, 1); lcd.print("*");   tone(BUZZ, 1500); flag = 1;}
      else             {lcd.setCursor(13, 1); lcd.print(" "); noTone(BUZZ);       flag = 0;}
    });
  }else{if (T_Upr == 3){lcd.setCursor(13, 1); lcd.print(" "); noTone(BUZZ);       flag = 0;}}
}

void podderzhTemp(){
  if (StartTime == 1){
     rasTemp();
     unixt = now.unixtime();
     Sec = NeoTime - unixt;
     if (Rabota = 1){
       if (Sec <= 0){
         if (Alarm == 1) {
           Rabota = 0;
         }else{ 
           if (P_Numb <= 9){
             rasTempBol = 0;
             StartTime = 0;
             P_Numb++;
             if (Pat[P_Numb][P_Time] == 0){StopRab();}
           }else{StopRab();}
         }
       }
      }
  }
}

void StopRab(){
                   Setpoint = 10;
                   T_Upr = 0;
                   P_Numb = 0;
                   StartTime = 0;
                   chg_Time = 0;
                   Rabota = 1; 
                   Alarm = 0;
                   U_D = 5;
                   L_R = 0;
                   tekPovtor_Promyvka = 0;
                   lcd.setCursor(15, 1);lcd.print("  STOP  ");
                   lcd.setCursor(13, 1);lcd.print("PAT OFF");
                   tone(BUZZ, 400, 5000);    // сигнал завершения работы всех программ
                   digitalWrite(NASOS,HIGH); // Включение насоса на 5 минут после завершения программ
                   EndOFNasos = 1;           // Флаг включение насоса на 5 минут после завершения программ  
}

void rasTemp(){
  if (rasTempBol == 0){NeoTime = (Pat[P_Numb][P_Time] * 60) + now.unixtime();rasTempBol = 1;}
}

void Menu() {
  getPressedButton();
  if (Vhod_v_menu == 0) {    // выключатель освещения :)
    if (analogRead(1) < 100)  {lcd.backlight();}
    if (analogRead(1) > 1000) {lcd.noBacklight();}    
  }

  if (digitalRead(BUTON_MENU) == 0) {
    U_D = 5;
    L_R = 0;
    chg_Time = 0;
    EndOFNasos = 0;
    lcd.backlight();
    if (Vhod_v_menu == 0) {
        Vhod_v_menu = 1;
        ClearNol();
        lcd.print("       Setup");
        lcd.setCursor(0, 1);lcd.print("    left or right");
        Dreft0 = 1;
        delay(100);
    } else {
        lcd.clear();
        lcd.setCursor(8, 1);lcd.print("EXIT");
        chg_Time = 0;
        L_R = 0;
        Vhod_v_menu = 0;
        
        if (EEPROM.read(110) != Setpoint) {  // Сохранение значения температуры
          if (T_Upr == 2 && chgTemp == 1){
            EEPROM.write(110, Setpoint); 
            lcd.setCursor(5, 1); 
            lcd.print("SAVE Temp");
            delay(600);
          }
        }
        if (EEPROM.read(111) != T_Upr) {   // Сохранение значения режима работы
          if (T_Upr != 3){
            EEPROM.write(111, T_Upr);
            lcd.setCursor(5, 1); 
            lcd.print("SAVE Mode");
            delay(600);
          }
        }
        if (EEPROM.read(112) != KI) {      // Сохранение значения режима KI PID регулятора
            EEPROM.write(112, KI);
            lcd.setCursor(5, 1);
            lcd.print("SAVE PID ");
            myPID.SetTunings(consKp, KI, consKd);
            delay(600);
        }
        lcd.clear();
      }
     }
//----------------------------------------------------------------------------------
  if (Vhod_v_menu == 1) {
    if (L_R == 0) { 
      if (Dreft0 == 0) {
        ClearNol();
        lcd.print("       Setup");
        lcd.setCursor(0, 1);lcd.print("    left or right");
        Dreft0 = 1;}} else {Dreft0 = 0;}
//-------------
    if (L_R == 1) {
      if (Dreft1 == 0) {
        ReleMode();
        lcd.setCursor(0, 3);lcd.print("    ^^^^      ");
        Dreft1 = 1;}} else {Dreft1 = 0;}
//-------------
    if (L_R == 2) {
      if (Dreft2 == 0) {
        ReleMode();
        lcd.setCursor(0, 3);lcd.print("         ^^^^^");
        Dreft2 = 1;}} else {Dreft2 = 0;}
//-------------    
    if (L_R == 3) {
      if (Dreft3 == 0) {
        A_H = now.hour();
        A_M = now.minute();
        ClearNol();
        PrintTime();
        lcd.setCursor(0, 3);lcd.print("^^");      
        Dreft3 = 1;}} else {Dreft3 = 0; if (chg_Time == 1){rtc.adjust(DateTime(2015, 2, 27, A_H, A_M, 0));}}
//-------------        
    if (L_R == 4) {
      if (Dreft4 == 0) {
        A_H = now.hour();
        A_M = now.minute();
        ClearNol();
        PrintTime();
        lcd.setCursor(0, 3);lcd.print("   ^^");         
        Dreft4 = 1;}} else {Dreft4 = 0; if (chg_Time == 1){rtc.adjust(DateTime(2015, 2, 27, A_H, A_M, 0));}}
//-------------        
    if (L_R == 5) {
      if (Dreft5 == 0) {
        ClearNol();
        KP = myPID.GetKp();
        KI = myPID.GetKi();
        KD = myPID.GetKd();
        lcd.setCursor(0, 0);lcd.print("Setup PID controller");
        lcd.setCursor(0, 1);lcd.print("Kp "); if (KP < 10) {lcd.print("0");lcd.print(KP);}else{lcd.print(KP);}
        lcd.setCursor(0, 2);lcd.print("Ki "); if (KI < 10) {lcd.print("0");lcd.print(KI);}else{lcd.print(KI);} lcd.print(" <==");
        lcd.setCursor(0, 3);lcd.print("Kd "); if (KD < 10) {lcd.print("0");lcd.print(KD);}else{lcd.print(KD);}
      Dreft5 = 1;}} else {Dreft5 = 0;}
//-------------                
    if (L_R == 6 || L_R == 11 || L_R == 16) {
      if (Dreft_MAS_0 == 0) {
		if (L_R == 6) {Print_Pat_Screan_Temp();}
		if (L_R == 11) {Print_Pat_Screan_Time();}
		if (L_R == 16) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(0, 3);lcd.print("^^^^");
		Dreft_MAS_0 = 1;}} else {Dreft_MAS_0 = 0;}
    if (L_R == 7 || L_R == 12 || L_R == 17) {
      if (Dreft_MAS_1 == 0) {
		if (L_R == 7) {Print_Pat_Screan_Temp();}
		if (L_R == 12) {Print_Pat_Screan_Time();}
		if (L_R == 17) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(4, 3);lcd.print("^^^^");
		Dreft_MAS_1 = 1;}} else {Dreft_MAS_1 = 0;}	
    if (L_R == 8 || L_R == 13 || L_R == 18) {
      if (Dreft_MAS_2 == 0) {
		if (L_R == 8) {Print_Pat_Screan_Temp();}
		if (L_R == 13) {Print_Pat_Screan_Time();}
		if (L_R == 18) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(8, 3);lcd.print("^^^^");
		Dreft_MAS_2 = 1;}} else {Dreft_MAS_2 = 0;}
    if (L_R == 9 || L_R == 14 || L_R == 19) {
      if (Dreft_MAS_3 == 0) {
		if (L_R == 9) {Print_Pat_Screan_Temp();}
		if (L_R == 14) {Print_Pat_Screan_Time();}
		if (L_R == 19) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(12, 3);lcd.print("^^^^");
		Dreft_MAS_3 = 1;}} else {Dreft_MAS_3 = 0;}		
    if (L_R == 10 || L_R == 15 || L_R == 20) {
      if (Dreft_MAS_4 == 0) {
		if (L_R == 10) {Print_Pat_Screan_Temp();}
		if (L_R == 15) {Print_Pat_Screan_Time();}
		if (L_R == 20) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(16, 3);lcd.print("^^^^");
		Dreft_MAS_4 = 1;}} else {Dreft_MAS_4 = 0;}

    if (L_R == 21 || L_R == 26 || L_R == 31) {
      if (Dreft_MAS_5 == 0) {
		if (L_R == 21) {Print_Pat_Screan_Temp();}
		if (L_R == 26) {Print_Pat_Screan_Time();}
		if (L_R == 31) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(0, 3);lcd.print("^^^^");
		Dreft_MAS_5 = 1;}} else {Dreft_MAS_5 = 0;}
    if (L_R == 22 || L_R == 27 || L_R == 32) {
      if (Dreft_MAS_6 == 0) {
		if (L_R == 22) {Print_Pat_Screan_Temp();}
		if (L_R == 27) {Print_Pat_Screan_Time();}
		if (L_R == 32) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(4, 3);lcd.print("^^^^");
		Dreft_MAS_6 = 1;}} else {Dreft_MAS_6 = 0;}
    if (L_R == 23 || L_R == 28 || L_R == 33) {
      if (Dreft_MAS_7 == 0) {
		if (L_R == 23) {Print_Pat_Screan_Temp();}
		if (L_R == 28) {Print_Pat_Screan_Time();}
		if (L_R == 33) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(8, 3);lcd.print("^^^^");
		Dreft_MAS_7 = 1;}} else {Dreft_MAS_7 = 0;}
    if (L_R == 24 || L_R == 29 || L_R == 34) {
      if (Dreft_MAS_8 == 0) {
		if (L_R == 24) {Print_Pat_Screan_Temp();}
		if (L_R == 29) {Print_Pat_Screan_Time();}
		if (L_R == 34) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(12, 3);lcd.print("^^^^");
		Dreft_MAS_8 = 1;}} else {Dreft_MAS_8 = 0;}
    if (L_R == 25 || L_R == 30 || L_R == 35) {
      if (Dreft_MAS_9 == 0) {
		if (L_R == 25) {Print_Pat_Screan_Temp();}
		if (L_R == 30) {Print_Pat_Screan_Time();}
		if (L_R == 35) {Print_Pat_Screan_Alarm();}
		lcd.setCursor(16, 3);lcd.print("^^^^");
		Dreft_MAS_9 = 1;}} else {Dreft_MAS_9 = 0;}
//##################################################################
    if (U_D > 5) {
//-------------      
      if (L_R == 1) {
        if (T_Upr < 3) {T_Upr += 1;}
        lcd.setCursor(0, 1);
        lcd.print("    MODE  TEMP");
        lcd.setCursor(0, 2);
        if (T_Upr == 0) {lcd.print("1-4  OFF -----");}
        if (T_Upr == 1) {lcd.print("2-4  ON  -----");}
        if (T_Upr == 2) {lcd.print("3-4 AUTO ");lcd.print(Setpoint);lcd.print('\0');}
        if (T_Upr == 3) {lcd.print("4-4 PREF -----");}
        lcd.setCursor(0, 3);lcd.print("    ^^^^      ");
        U_D = 5;
      }
//-------------
      if (L_R == 2) {
        if (T_Upr == 2) {
        Setpoint += 1;
        chgTemp = 1;
        lcd.setCursor(0, 1);
        lcd.print("    MODE  TEMP");
        lcd.setCursor(0, 2);
        lcd.print("3-4 AUTO ");
        lcd.print(Setpoint);lcd.print('\0');
        lcd.setCursor(0, 3);lcd.print("         ^^^^^");
      }
        U_D = 5;
      }
//-------------
      if (L_R == 3) {
        if (A_H < 23) {A_H += 1;}
          chg_Time = 1;
          PrintTime();
          lcd.setCursor(0, 3);lcd.print("^^");
        U_D = 5;
      }
//-------------
      if (L_R == 4) {
        if (A_M > 0) {A_M += 1;}
          chg_Time = 1;
          PrintTime();
          lcd.setCursor(0, 3);lcd.print("   ^^");
        U_D = 5;
      } 
//-------------
       if (L_R == 5) {
            if (KI < 100) {KI += 1;}else{KI = 0;}
            lcd.setCursor(0, 2);lcd.print("Ki "); if (KI < 10) {lcd.print("0");lcd.print(KI);}else{lcd.print(KI);} lcd.print(" <==");
        U_D = 5;
      }
//-------------    
        Upr_PAT();
//----------------------------------------------------------------------------------
    } else if (U_D < 5) {
      if (L_R == 1) {
        if (T_Upr > 0) {T_Upr -= 1;}
        lcd.setCursor(0, 1);
        lcd.print("    MODE  TEMP");
        lcd.setCursor(0, 2);
        if (T_Upr == 0) {lcd.print("1-4  OFF -----");}
        if (T_Upr == 1) {lcd.print("2-4  ON  -----");}
        if (T_Upr == 2) {lcd.print("3-4 AUTO ");lcd.print(Setpoint);lcd.print('\0');}
        if (T_Upr == 3) {lcd.print("4-4 PREF -----");}
        lcd.setCursor(0, 3);lcd.print("    ^^^^      ");
        U_D = 5;
      }
//-------------
      if (L_R == 2) {
        if (T_Upr == 2) {
        Setpoint -= 1;
        chgTemp = 1;
        lcd.setCursor(0, 1);
        lcd.print("    MODE  TEMP");
        lcd.setCursor(0, 2);
        lcd.print("3-4 AUTO ");
        lcd.print(Setpoint);lcd.print('\0');
        lcd.setCursor(0, 3);lcd.print("         ^^^^^");
      }
        U_D = 5;
      }
//-------------
      if (L_R == 3) {
        if (A_H > 0) {A_H -= 1;}
          chg_Time = 1;
          PrintTime();
          lcd.setCursor(0, 3);lcd.print("^^");
        U_D = 5;
      } 
//-------------
      if (L_R == 4) {
        if (A_M > 0) {A_M -= 1;}
          chg_Time = 1;
          PrintTime();
          lcd.setCursor(0, 3);lcd.print("   ^^");
        U_D = 5;
      }
//-------------
       if (L_R == 5) {
            if (KI > 0) {KI -= 1;} else {KI = 100;}
            lcd.setCursor(0, 2);lcd.print("Ki "); if (KI < 10) {lcd.print("0");lcd.print(KI);}else{lcd.print(KI);} lcd.print(" <==");
         U_D = 5;
      }

//-------------      
        Upr_PAT();
//-------------
    }
  }
}

void Prokachka(){
  int TempINNasos = abs(Input);
  
  if (TempINNasos < 40) {
      NasosPromyvka();                 // 3 запуска длительностью в 1 секунду
    }
  else if (TempINNasos > 40) {
      digitalWrite(NASOS, HIGH);       // постоянная работа
    }
  else if (TempINNasos > 70) {
      digitalWrite(NASOS, LOW);        // отключение
    }
  else if (TempINNasos > 90) {
      NasosKipyachenie();              // вклячатся на 1 минуту с интервалом 10 минут
    }
  if (EndOFNasos == 1) {DO_EVERY(_MIN_(5),{digitalWrite(NASOS,LOW);});EndOFNasos = 0;} // Включение насоса на 5 минут после завершения программ
}
 
void NasosPromyvka(){
  if (tekPovtor_Promyvka < Povtor_Promyvka *2){
    DO_EVERY(3000,{digitalWrite(NASOS,!digitalRead(NASOS)); tekPovtor_Promyvka += 1;});
  } else {digitalWrite(NASOS,LOW);}
/*       
  DO_EVERY(_SEC_(20),{digitalWrite(NASOS,HIGH);});
  DO_EVERY(_SEC_(5),{digitalWrite(NASOS,LOW);}); */
}

void NasosKipyachenie(){
  DO_EVERY(_MIN_(10),{digitalWrite(NASOS,HIGH);});
  DO_EVERY(_MIN_(1),{digitalWrite(NASOS,LOW);});
}

void PrintTime(){
        lcd.setCursor(0, 0); lcd.print("Setup Time");
//        lcd.setCursor(0, 1); lcd.print(now.hour()); lcd.print(":"); lcd.print(now.minute()); lcd.print(":"); lcd.print(now.second());
        lcd.setCursor(0, 2); if (A_H < 10) {lcd.print("0");lcd.print(A_H);}else{lcd.print(A_H);} lcd.print(":"); if (A_M < 10) {lcd.print("0");lcd.print(A_M);}else{lcd.print(A_M);} lcd.print(":00 - New Time");
}

void ReleMode(){
        ClearNol();
        lcd.print("Setup Rele MODE");
        lcd.setCursor(0, 1);
        lcd.print("    MODE  TEMP");
        lcd.setCursor(0, 2);
        if (T_Upr == 0) {lcd.print("1-4  OFF -----");}
        if (T_Upr == 1) {lcd.print("2-4  ON  -----");}
        if (T_Upr == 2) {lcd.print("3-4 AUTO "); Setpoint = EEPROM.read(110); lcd.print(Setpoint);lcd.print('\0');}
        if (T_Upr == 3) {lcd.print("4-4 PREF -----");}
}

void Upr_PAT(){
      if (L_R == 6 || L_R == 11 || L_R == 16) {
		if (L_R == 6) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 11) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 16) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(0, 3);lcd.print("^^^^");
        U_D = 5;
      }		
       if (L_R == 7 || L_R == 12 || L_R == 17) {
		if (L_R == 7) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 12) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 17) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(4, 3);lcd.print("^^^^");
        U_D = 5;
      }		
       if (L_R == 8 || L_R == 13 || L_R == 18) {
		if (L_R == 8) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 13) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 18) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(8, 3);lcd.print("^^^^");
        U_D = 5;
      }			
       if (L_R == 9 || L_R == 14 || L_R == 19) {
		if (L_R == 9) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 14) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 19) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(12, 3);lcd.print("^^^^");
        U_D = 5;
      }		
       if (L_R == 10 || L_R == 15 || L_R == 20) {
		if (L_R == 10) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 15) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 20) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(16, 3);lcd.print("^^^^");
        U_D = 5;
      }
             if (L_R == 21 || L_R == 26 || L_R == 31) {
		if (L_R == 21) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 26) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 31) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(0, 3);lcd.print("^^^^");
        U_D = 5;
      }
             if (L_R == 22 || L_R == 27 || L_R == 32) {
		if (L_R == 22) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 27) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 32) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(4, 3);lcd.print("^^^^");
        U_D = 5;
      }
             if (L_R == 23 || L_R == 28 || L_R == 33) {
		if (L_R == 23) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 28) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 33) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(8, 3);lcd.print("^^^^");
        U_D = 5;
      }
             if (L_R == 24 || L_R == 29 || L_R == 34) {
		if (L_R == 24) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 29) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 34) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(12, 3);lcd.print("^^^^");
        U_D = 5;
      }
             if (L_R == 25 || L_R == 30 || L_R == 35) {
		if (L_R == 25) {
			CHG_P_Temp();
			Print_Pat_Screan_Temp();
		}
		if (L_R == 30) {
			CHG_P_Time();
			Print_Pat_Screan_Time();
		}		
		if (L_R == 35) {
			CHG_P_Alar();
			Print_Pat_Screan_Alarm();
		}
		lcd.setCursor(16, 3);lcd.print("^^^^");
        U_D = 5;
      }
}



void CHG_P_Temp(){
	if (U_D > 5) {
		if (Dreft_MAS_0 == 1) {if (Pat[0][P_Temp] < 100) {Pat[0][P_Temp] += 1;}}
		if (Dreft_MAS_1 == 1) {if (Pat[1][P_Temp] < 100) {Pat[1][P_Temp] += 1;}}
		if (Dreft_MAS_2 == 1) {if (Pat[2][P_Temp] < 100) {Pat[2][P_Temp] += 1;}}
		if (Dreft_MAS_3 == 1) {if (Pat[3][P_Temp] < 100) {Pat[3][P_Temp] += 1;}}
		if (Dreft_MAS_4 == 1) {if (Pat[4][P_Temp] < 100) {Pat[4][P_Temp] += 1;}}
		if (Dreft_MAS_5 == 1) {if (Pat[5][P_Temp] < 100) {Pat[5][P_Temp] += 1;}}
		if (Dreft_MAS_6 == 1) {if (Pat[6][P_Temp] < 100) {Pat[6][P_Temp] += 1;}}
		if (Dreft_MAS_7 == 1) {if (Pat[7][P_Temp] < 100) {Pat[7][P_Temp] += 1;}}
		if (Dreft_MAS_8 == 1) {if (Pat[8][P_Temp] < 100) {Pat[8][P_Temp] += 1;}}
		if (Dreft_MAS_9 == 1) {if (Pat[9][P_Temp] < 100) {Pat[9][P_Temp] += 1;}}}
	else{		
		if (Dreft_MAS_0 == 1) {if (Pat[0][P_Temp] > 0) {Pat[0][P_Temp] -= 1;}}
		if (Dreft_MAS_1 == 1) {if (Pat[1][P_Temp] > 0) {Pat[1][P_Temp] -= 1;}}
		if (Dreft_MAS_2 == 1) {if (Pat[2][P_Temp] > 0) {Pat[2][P_Temp] -= 1;}}
		if (Dreft_MAS_3 == 1) {if (Pat[3][P_Temp] > 0) {Pat[3][P_Temp] -= 1;}}
		if (Dreft_MAS_4 == 1) {if (Pat[4][P_Temp] > 0) {Pat[4][P_Temp] -= 1;}}
		if (Dreft_MAS_5 == 1) {if (Pat[5][P_Temp] > 0) {Pat[5][P_Temp] -= 1;}}
		if (Dreft_MAS_6 == 1) {if (Pat[6][P_Temp] > 0) {Pat[6][P_Temp] -= 1;}}
		if (Dreft_MAS_7 == 1) {if (Pat[7][P_Temp] > 0) {Pat[7][P_Temp] -= 1;}}
		if (Dreft_MAS_8 == 1) {if (Pat[8][P_Temp] > 0) {Pat[8][P_Temp] -= 1;}}
		if (Dreft_MAS_9 == 1) {if (Pat[9][P_Temp] > 0) {Pat[9][P_Temp] -= 1;}}}
}
void CHG_P_Time(){
	if (U_D > 5) {
		if (Dreft_MAS_0 == 1) {if (Pat[0][P_Time] < 32000) {Pat[0][P_Time] += 1;}}
		if (Dreft_MAS_1 == 1) {if (Pat[1][P_Time] < 32000) {Pat[1][P_Time] += 1;}}
		if (Dreft_MAS_2 == 1) {if (Pat[2][P_Time] < 32000) {Pat[2][P_Time] += 1;}}
		if (Dreft_MAS_3 == 1) {if (Pat[3][P_Time] < 32000) {Pat[3][P_Time] += 1;}}
		if (Dreft_MAS_4 == 1) {if (Pat[4][P_Time] < 32000) {Pat[4][P_Time] += 1;}}
		if (Dreft_MAS_5 == 1) {if (Pat[5][P_Time] < 32000) {Pat[5][P_Time] += 1;}}
		if (Dreft_MAS_6 == 1) {if (Pat[6][P_Time] < 32000) {Pat[6][P_Time] += 1;}}
		if (Dreft_MAS_7 == 1) {if (Pat[7][P_Time] < 32000) {Pat[7][P_Time] += 1;}}
		if (Dreft_MAS_8 == 1) {if (Pat[8][P_Time] < 32000) {Pat[8][P_Time] += 1;}}
		if (Dreft_MAS_9 == 1) {if (Pat[9][P_Time] < 32000) {Pat[9][P_Time] += 1;}}}
	else{
		if (Dreft_MAS_0 == 1) {if (Pat[0][P_Time] > 0) {Pat[0][P_Time] -= 1;}}
		if (Dreft_MAS_1 == 1) {if (Pat[1][P_Time] > 0) {Pat[1][P_Time] -= 1;}}
		if (Dreft_MAS_2 == 1) {if (Pat[2][P_Time] > 0) {Pat[2][P_Time] -= 1;}}
		if (Dreft_MAS_3 == 1) {if (Pat[3][P_Time] > 0) {Pat[3][P_Time] -= 1;}}
		if (Dreft_MAS_4 == 1) {if (Pat[4][P_Time] > 0) {Pat[4][P_Time] -= 1;}}
		if (Dreft_MAS_5 == 1) {if (Pat[5][P_Time] > 0) {Pat[5][P_Time] -= 1;}}
		if (Dreft_MAS_6 == 1) {if (Pat[6][P_Time] > 0) {Pat[6][P_Time] -= 1;}}
		if (Dreft_MAS_7 == 1) {if (Pat[7][P_Time] > 0) {Pat[7][P_Time] -= 1;}}
		if (Dreft_MAS_8 == 1) {if (Pat[8][P_Time] > 0) {Pat[8][P_Time] -= 1;}}
		if (Dreft_MAS_9 == 1) {if (Pat[9][P_Time] > 0) {Pat[9][P_Time] -= 1;}}}
}

void CHG_P_Alar(){
	if (U_D > 5) {
		if (Dreft_MAS_0 == 1) {Pat[0][P_Alar] = 1;}
		if (Dreft_MAS_1 == 1) {Pat[1][P_Alar] = 1;}
		if (Dreft_MAS_2 == 1) {Pat[2][P_Alar] = 1;}
		if (Dreft_MAS_3 == 1) {Pat[3][P_Alar] = 1;}
		if (Dreft_MAS_4 == 1) {Pat[4][P_Alar] = 1;}
		if (Dreft_MAS_5 == 1) {Pat[5][P_Alar] = 1;}
		if (Dreft_MAS_6 == 1) {Pat[6][P_Alar] = 1;}
		if (Dreft_MAS_7 == 1) {Pat[7][P_Alar] = 1;}
		if (Dreft_MAS_8 == 1) {Pat[8][P_Alar] = 1;}
		if (Dreft_MAS_9 == 1) {Pat[9][P_Alar] = 1;}}
	else{
		if (Dreft_MAS_0 == 1) {Pat[0][P_Alar] = 0;}
		if (Dreft_MAS_1 == 1) {Pat[1][P_Alar] = 0;}
		if (Dreft_MAS_2 == 1) {Pat[2][P_Alar] = 0;}
		if (Dreft_MAS_3 == 1) {Pat[3][P_Alar] = 0;}
		if (Dreft_MAS_4 == 1) {Pat[4][P_Alar] = 0;}
		if (Dreft_MAS_5 == 1) {Pat[5][P_Alar] = 0;}
		if (Dreft_MAS_6 == 1) {Pat[6][P_Alar] = 0;}
		if (Dreft_MAS_7 == 1) {Pat[7][P_Alar] = 0;}
		if (Dreft_MAS_8 == 1) {Pat[8][P_Alar] = 0;}
		if (Dreft_MAS_9 == 1) {Pat[9][P_Alar] = 0;}}
}

void Print_Pat_Screan_Temp(){
    ClearNol();
    cur = 1;
  if (L_R <= 20){
    lcd.print("Setup PAT Temp");	
	for (int i = 0; i <= 4; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 0;
	for (int i = 0; i < 5; i++) {
		R_Numb = i;
		if (cur == 1) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		lcd.print(Pat[R_Numb][P_Temp]);
	}
  }else{
    lcd.print("Setup PAT Temp");
    cur = 1;
	for (int i = 5; i <= 9; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 0;
	for (int i = 5; i < 10; i++) {
		R_Numb = i;
		if (cur == 1) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		lcd.print(Pat[R_Numb][P_Temp]);
	}  
  }
}
void Print_Pat_Screan_Time(){
        ClearNol();
        cur = 1;  
      if (L_R <= 20){
        lcd.print("Setup PAT Time");	
	for (int i = 0; i <= 4; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 0;
	for (int i = 0; i < 5; i++) {
		R_Numb = i;
		if (cur == 0) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		if (Pat[R_Numb][P_Time] < 10){lcd.print(Pat[R_Numb][P_Time]);lcd.print(" ");}else{lcd.print(Pat[R_Numb][P_Time]);}
	}
      }else{
        cur = 1;
        lcd.print("Setup PAT Time");	
	for (int i = 5; i <= 9; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 0;
	for (int i = 5; i < 10; i++) {
		R_Numb = i;
		if (cur == 0) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		if (Pat[R_Numb][P_Time] < 10){lcd.print(Pat[R_Numb][P_Time]);lcd.print(" ");}else{lcd.print(Pat[R_Numb][P_Time]);}
	}      
      }
}
void Print_Pat_Screan_Alarm(){
    ClearNol();
    cur = 1;  
    if (L_R <= 20){
    lcd.print("Setup PAT Alarm");	
	for (int i = 0; i <= 4; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 0;
	for (int i = 0; i < 5; i++) {
		R_Numb = i;
		if (cur == 0) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		if (Pat[R_Numb][P_Alar] == 0){lcd.print("OFF");}else{lcd.print("ON");}
	}
    }else{
      cur = 1;
    lcd.print("Setup PAT Alarm");	
	for (int i = 5; i <= 9; i++) {
		if (cur == 1) {lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}else{lcd.setCursor(cur, 1);lcd.print("P");lcd.print(i);}
                cur += 4;
	}
        cur = 0;
	for (int i = 5; i < 10; i++) {
		R_Numb = i;
		if (cur == 0) {lcd.setCursor(cur, 2);}else{lcd.setCursor(cur, 2);}
                cur += 4;
		if (Pat[R_Numb][P_Alar] == 0){lcd.print("OFF");}else{lcd.print("ON");}
	}    
    }
}

void ClearNol(){
        lcd.clear();        
        lcd.setCursor(0, 0);
}

void getPressedButton() {
  DO_EVERY(noTrem,{if (analogRead(0) < 100) {L_R--;}});
  DO_EVERY(noTrem,{if (analogRead(0) > 1000){L_R++;}});
  DO_EVERY(noTrem,{if (analogRead(1) < 100) {U_D++;}});
  DO_EVERY(noTrem,{if (analogRead(1) > 1000){U_D--;}});
  if (L_R < 0){L_R = PunkMenu;}
  if (L_R > PunkMenu){L_R = 0;}
}

void getTemp() {
  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);
  ds.reset();
  ds.select(addr);
  ds.write(0xBE);
  for ( int i = 0; i < 9; i++) {
    data[i] = ds.read();
  }
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3;
    if (data[7] == 0x10) {
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw & ~7;
    else if (cfg == 0x20) raw = raw & ~3;
    else if (cfg == 0x40) raw = raw & ~1;
  }

  celsius = (float)raw / 16.0;
  return;
}

 

miroshnik
Offline
Зарегистрирован: 15.03.2015

Добрый день. Проконсультируйте будет ваша прошивка работать на Мега 2560 и экран

TFT 320QUT и что для этого надо. 

 
 
maxi_10
Offline
Зарегистрирован: 05.01.2012

Прошивка работать будет, но для использования указанной вами модели экрана необходимо использоваь другую библиотеку (я использую LiquidCrystal_I2C.h вам она не подайдет), и соответственно поменять больше половины кода отрисовывания менюшек. Одним словом вам будет проще написать с вой код.

maxi_10
Offline
Зарегистрирован: 05.01.2012

 

Предварительный монтаж. В дальнейшем всё будет собранно на одной плате.

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

maxi_10
Offline
Зарегистрирован: 05.01.2012

Датчик температуры будет запаян Оловянно-медным припоем в отверстие 6,5 высверленное в болте. Пустота между датчиком и болтом будет заполнена теплопроводящим клей-герметиком.

maxi_10
Offline
Зарегистрирован: 05.01.2012

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

miroshnik
Offline
Зарегистрирован: 15.03.2015

Да, было бы не плохо научиться писать код. 

Bierman
Offline
Зарегистрирован: 13.05.2014

Пока нравиться! Смущает только здоровый радиатор у пластика, но это все суеверия. Ждем фото общего готовго проджекта!

maxi_10
Offline
Зарегистрирован: 05.01.2012

Почти готовый коньтроллер.

maxi_10
Offline
Зарегистрирован: 05.01.2012

Почти готовый БАК.

maxi_10
Offline
Зарегистрирован: 05.01.2012

Тестовый запуск всей конструкции.

Работа Бака

https://youtu.be/vtRUYCUEHXE

 

Работа контроллера

https://youtu.be/e4MfLSn30cs

 

Прект входит в заключительную стадию отладки и тестирования.

maxi_10
Offline
Зарегистрирован: 05.01.2012

Испытание чиллира.

maxi_10
Offline
Зарегистрирован: 05.01.2012

Итак первая партия домашнего пива прошла 50% отстаивания в бутылках (на следующие выходные буду пробовать)

Ссылки с рецептами.

http://rossibeer.ru/recepty-piva

http://www.castlemalting.com/CastleMaltingBeerRecipes.asp?Language=Russian

maxi_10
Offline
Зарегистрирован: 05.01.2012
Посчитал себестоимость контроллера.
Цены по сайтам ebay.com, chipdip.ru, owen.ru
 
Компанент             Стоимость
корпус                   600
Контроллер            205
реле насоса            64
RTC                        50
DS18b20               100
HD-4022.10U        1100
Джойстик              78
UNL2003               61
LCD 20x4 I2C         375
гайки болты          100
Радиатор               100
Разьемы питания  150
Разьем датчика    140
Кнопка ресет         20
кнопка питания    40
Печатная плата     100
Итого                    3283
Слесарные работы Бесценно
Программное обеспечение Бесценно
 
maxi_10
Offline
Зарегистрирован: 05.01.2012

Котел     5091  http://www.klenmarket.ru/shop/inventory/kitchen-equipment/boilers/boiler-50-l-professional-luxstahl-101307/

Внутренний бак   6950  http://hbpro.ru/showcase/zheleznyj-kit-30l.7/

Насос   4750  http://вортекс-центр.рф/shop/n-gvs/bw152r12ot.html

ТЭН  2100  http://hbpro.ru/showcase/tehn-dlja-klona-20l.18/

Кран шаровой ½ дюйма, ножки для бака, фитинг для насоса (врезка в бак ½ дюйма, переходник американка вн-внеш резьба ½ дюйма, уголок  внутр-внеш резьба ½ дюйма), прокладки ~ 1500-2000

Болт+гайки из нержи для датчика ~ 200 рублей

Чиллер   3500  http://hbpro.ru/showcase/chiller-dlja-klona-20l.24/

Из инструмента пришлось купить тиски, сверла кобальтовые (по нерже) разного диаметра, припой безсвинцовый, термоклей. На это еще где-то 3500 вышло.

 

maxi_10
Offline
Зарегистрирован: 05.01.2012

Итого средняя стоимость проекта 31650 в среднем.

maxi_10
Offline
Зарегистрирован: 05.01.2012

Bierman пишет:

Пока нравиться! Смущает только здоровый радиатор у пластика, но это все суеверия. Ждем фото общего готовго проджекта!

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

maxi_10
Offline
Зарегистрирован: 05.01.2012

Процесс варки пивного сусла. 

https://youtu.be/vjlG784vqak

maxi_10
Offline
Зарегистрирован: 05.01.2012

maxi_10
Offline
Зарегистрирован: 05.01.2012

maxi_10
Offline
Зарегистрирован: 05.01.2012

maxi_10
Offline
Зарегистрирован: 05.01.2012

Первая партия готова!

 

a5021
Offline
Зарегистрирован: 07.07.2013

И как это на вкус?

maxi_10
Offline
Зарегистрирован: 05.01.2012

a5021 пишет:

И как это на вкус?

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

Вкус мягкий, насыщенный, в меру алкогольный (приблизительно 5,5 - 6 градусов).

a5021
Offline
Зарегистрирован: 07.07.2013

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

2099996
2099996 аватар
Offline
Зарегистрирован: 21.07.2015

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

maxi_10
Offline
Зарегистрирован: 05.01.2012

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

готовый на 70% код (профильный режим в проццесе написания)

для простоты проект разбит на 8 файлов (но это не обязательно можно весь код сохранить в один файл)

 

// Last Relise - 05/08/2015 Alpha  //
// Last Relise - 21/08/2015 Betha  //
// Last Relise - 00/00/0000 Relise //
/////////////////////////////////////



#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <EEPROM.h>
#include <Encoder.h>
#include <OneWire.h>
#include <LiquidCrystal_I2C.h>
#include <DS1307.h>
#include <RTClib.h>
#include <PID_v1.h>
#include <IniFile.h>
#include <TimeHelpers.h>

#define DEBUG
// #define ENCODER_DO_NOT_USE_INTERRUPTS

#define ONE_WIRE_BUS 7     // Порт шина датчика температуры
#define SD_SELECT    4     // Порт шина CS SD карты
#define ButtonEnc    8     // Порт кнопка энкодора
#define TEN1         13    // Порт шины нагревательного элемента 1
#define TEN2         6     // Порт шины нагревательного элемента 2
#define NASOS        9     // Порт шины рециркуляционного насоса
#define BUZZ         5     // Порт выхода на пьезоизлучатель
//
#define P_Temp       0     // Поле масива с данными температуры
#define P_Time       1     // Поле масива с данными времени
#define P_Alar       2     // Поле масива с данными сигнализации
#define BobleOff_tim 3000  // Время продувки насоса

//``````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
byte type_s;               // Создание переменной типа датчика измерения температуры
byte data[12];             // Создание переменной данных датчика температуры
byte addr[8];              // Создание переменной адреса датчика температуры
//
long Pos;                  // Создание переменной нового положения энкодера
long position;             // Создание переменной текущего положения энкодера
long NeoTime = 0;           // Создание переменной хранения значения unix времени конца выполнения программы
//
const size_t bufferLen = 30; // Создания константы размера буфера строк читаемых из ini файла
//
char buffer[bufferLen];    // Создания буфера строк читаемых из ini файла
//
double Setpoint;           // Создание переменной устанавливаемой температуры (и передача данных из памяти)
double Input;              // Создание переменной входящих данных в ПИД регулятор 
double Output;             // Создание переменной исходящих данных из ПИД регулятора
double consKp = 5;         // Создание переменных констант ПИД регулятора
double consKi = 75;        // Создание переменных констант ПИД регулятора
double consKd = 1;         // Создание переменных констант ПИД регулятора
//
String FileiniNAMEAn;
String FileiniNAME;
//                           0         1         2         3         4         5         6         7         8         9
const String Prog[10] = {"PROG_1", "PROG_2", "PROG_3", "PROG_4", "PROG_5", "PROG_6", "PROG_7", "PROG_8", "PROG_9", "PROG_10"};
int Pat[10][3]        = {{20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0}, {20,0,0} };
int prog = 0;
int mem1 = 0;
int mem2 = 0;
int mem3 = 0;
int Nasos_Start = 30;
int Nasos_Stop  = 80;
int PID_SPEED = 3;
int UPGRADE = 1000;         // Частота обновления главного экрана в милисикундах
int _HR = 0;                // Создание переменной хранения значения часов
int _MI = 0;                // Создание переменной хранения значения минут
int _SE = 0;                // Создание переменной хранения значения секунд
int A_H = 0;                // Создание переменной хранения значения часов при изменении времени
int A_M = 0;                // Создание переменной хранения значения минут при изменении времени
int maxMenu = 40;           // Отсчет от 0 (количество пунктов -1).
int curMenu = 0;            // Значение текущего пункта меню.
int Moschnost;              // Создание переменной мощности нагревательного элемента
int T_Upr = 0;              // Режим работы.
int P_Numb = 0;             // Создание переменной определения пункта масива програмируемых периодов (общего назначения)
int BobleOff_tec1 = 0;      // Создание переменной хранения времени прошедшего с начала промывки насоса
int BobleOff_tec2 = 0;      // Создание переменной хранения времени прошедшего с начала промывки насоса
int vinti = 0;              // Создание переменной винтика :)
int cardti = 0;             // Создание переменной карточки :)
//
long unixt;                 // Создание переменной хранения текущего unix времени
long Sec;                   // Создание переменной временивыполнения программы
//
float Mic1 = 0;             // Переменные измерения производительности
float Mic2 = 0;             // Переменные измерения производительности
float celsius;              // Создание переменной значения измерений датчика температуры
//
boolean StartTime = false;  // Создание переменной начала работы по заданному переоду (Р1 - Р9)
boolean Rabota = true;      // Создание переменной дополнительная переменная управления переключением периодов (Р1 - Р9)
boolean Alarm = false;      // Создание переменной значения сигнализации
boolean Pause = false;      // Создание переменной паузы в варке
boolean BobleOff = true;    // Создание переменной продувки насоса
boolean BobleOffst = true;  // Создание переменной продувки насоса
boolean rasTempBol = false; // Создание переменной флага необходимости расчета времени
boolean tr_menu = false;    // Признак входа в меню настройки.
boolean dir = false;        // Признак переключения между вводом данных и листанием меню.
boolean keyPres = false;    // Признак исплнния клика по кнопке.
boolean STATUS = false;     // Состояние предохранителя (программа включена/выключенна)
boolean Udate = true;       // Обновление основного экрана
boolean SD_stat = false;    // Определяет наличие или отсутствие SD карты.
//``````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````

uint8_t vint1[8]  = {B00000, B00111, B00101, B11111, B00100, B00100, B00000, B00000,};
uint8_t vint2[8]  = {B00000, B00100, B00100, B11111, B00101, B00111, B00000, B00000,};
uint8_t vint3[8]  = {B00000, B00100, B00100, B11111, B10100, B11100, B00000, B00000,};
uint8_t vint4[8]  = {B00000, B11100, B10100, B11111, B00100, B00100, B00000, B00000,};
uint8_t gradus[8] = {B01000, B10100, B10100, B01000, B00000, B00000, B00000, B00000,};                   
uint8_t card[8]   = {B00000, B00000, B01111, B10101, B10001, B11111, B11111, B11111,};
uint8_t curs[8]   = {B11000, B11100, B11110, B11111, B11110, B11100, B11000, B00000,};

RTC_DS1307 rtc;                                                        // Инициализация Часов точного времени
OneWire  ds(ONE_WIRE_BUS);                                             // Инициализация шины датчика измерения температуры
LiquidCrystal_I2C lcd(0x27, 20, 4);                                    // Инициализация дисплея
DateTime now;                                                          // Создания объекта Часов точного времени
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT); // Инициализация ПИД регулятора
Encoder myEnc(2, 3);                                                   // инициализация энкодера
File root;
File entry;


class BUTTON {
public:
//================================================================
static const byte bounce_              =    50; // длительность отслеживания дребезга.
static const byte doubleclick_         =   200; // длительность отслеживания двойного клика.
static const unsigned int retention_   =  2000; // длительность отслеживания нажатия и удержания.
static const unsigned long timer_      = 60000; // длительность отслеживания неактивности.
//================================================================
boolean click_down;
boolean click_up;
boolean doubleclick;
boolean timer;
boolean retention;
//=================================
unsigned long m;
boolean  p;
boolean  b;
boolean dc;
byte     c;
boolean  t;
boolean  r;
//=================================
byte _pb;
//=================================
BUTTON(byte pb) {
_pb = pb;
pinMode(_pb, INPUT);
digitalWrite(_pb, 1);
//====
click_down      = 0;
click_up        = 0;
doubleclick     = 0;
timer           = 0;
retention       = 0;
//====
m  =      millis();
p  = digitalRead(_pb);
b  =              0;
dc =              0;
c  =              0;
t  =              0;
r  =              0;
//====
}

void read() {
//=======================================================
unsigned long nm =      millis();
boolean       np = digitalRead(_pb);
//=================
boolean nb  = 0;
boolean ndc = 0;
boolean nt  = 0;
boolean nr  = 0;
//================
click_down  = 0;
click_up    = 0;
doubleclick = 0;
timer       = 0;
retention   = 0;
//=================
if (np != p) {p = np; m = nm; }
//=======================================================
if (nm - m > bounce_) {nb = 1;}
if (nm - m > doubleclick_) {ndc = 1;}
if (ndc != dc) {dc = ndc; if (dc == 1) {c = 0;}}
if (nb != b) {b = nb;
if (p == 0 && b == 0) {click_down = 1;
++c;      if (c == 2) {c = 0; doubleclick = 1;}
}
if (p == 1 && b == 1) {click_up = 1;}
}
//=======================================================
if (nm - m > timer_) {nt = 1;}
if (nt != t) {t = nt; if (p == 1 && t == 1) {timer = 1;}}
//=======================================================
if (nm - m > retention_) {nr = 1;}
if (nr != r) {r = nr; if (p == 0 && r == 1) {retention = 1;}}
//=======================================================
}};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

BUTTON BUTTON_01(ButtonEnc);

/////////////////////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
void clearLine(int line){if (line <= 3 && line >= 0){lcd.setCursor(0, line); lcd.print("                   "); lcd.setCursor(0, line);} else {lcd.clear();}}
void Enc_Zero(int ps = 0){ if (ps == 0) {myEnc.write(0);} else {ps = ps*4; myEnc.write(ps);} position = Pos;}
void EnterCHG(){if (tr_menu == true) {if (dir == true) {Enc_Zero(curMenu); menu(curMenu);} else {lcd.setCursor(13, 0); lcd.print("Chenge"); Enc_Zero(0); ChgData();} dir = !dir;}}
void Heat(boolean het = false){if (het){analogWrite(TEN1, Output); analogWrite(TEN2, Output);} else {analogWrite(TEN1, 0); analogWrite(TEN2, 0);}}
/////////////////////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void setup()
{
#ifdef DEBUG
  Serial.begin(115200);
#endif
  
//  SavePAT();
  LoadPAT();
  rtc.begin();
  lcd.init(); lcd.backlight();

  lcd.createChar(1, vint1);
  lcd.createChar(2, vint2);
  lcd.createChar(3, vint3);
  lcd.createChar(4, vint4);
  lcd.createChar(5, gradus);
  lcd.createChar(6, card);
  lcd.createChar(7, curs);

  pinMode(TEN1, OUTPUT); pinMode(TEN2, OUTPUT);
  pinMode(BUZZ, OUTPUT); pinMode(NASOS, OUTPUT);
  pinMode(ButtonEnc, INPUT); digitalWrite(ButtonEnc, HIGH);

  myPID.SetMode(AUTOMATIC);
  lcd.setCursor(0, 0); lcd.print(__DATE__);
  lcd.setCursor(0, 1); lcd.print(__TIME__);
  delay(2000);
  lcd.clear();
  if (!SD.begin(SD_SELECT)) {lcd.setCursor(0, 0); lcd.print("SD init - failed!");} // Тестирование SD карты (карта отсутствует)
  else       {lcd.setCursor(0, 0); lcd.print("SD  init - ok"); root = SD.open("/"); root.rewindDirectory(); SD_stat = true;} // Тестирование SD карты (карта на месте, исправна)
  delay(500); lcd.setCursor(0, 1); lcd.print("CPU init - ok"); // Приятный красивый мультик (несёт только эстетический хорактер)
  delay(500); lcd.setCursor(0, 2); lcd.print("SYS init - ok"); // Приятный красивый мультик (несёт только эстетический хорактер)
  delay(500); lcd.setCursor(0, 3); lcd.print("PID init - ok"); // Приятный красивый мультик (несёт только эстетический хорактер)
  delay(100);  
  lcd.clear();
  
  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }  
}

void loop(){
  Mic1 = millis();
  now = rtc.now(); _HR = now.hour(); _MI = now.minute(); _SE = now.second();
  getTemp(); Input = celsius;                       // Конвертация типов переменных на вход ПИД регулятора.
  DO_EVERY(PID_SPEED,{ myPID.Compute(); });
  Pos = myEnc.read() / 4;
  Moschnost = map(Output, 0, 255, 0, 100);
    
  if (STATUS) {
    Prokachka();
    if (!Pause){
      UPGRADE = 500;
      if (T_Upr == 0) {analogWrite(TEN1, 255); analogWrite(TEN2, 255); Moschnost = 100;}
      if (T_Upr == 1) {          if (Input < Setpoint) {Heat(true);} else {Heat(false); Moschnost = 0;}}
      if (T_Upr == 2) {Heater(); if (Input < Setpoint) {Heat(true);} else {Heat(false); Moschnost = 0;}}
    } else {Heat(false); UPGRADE = 1000;}
  } else   {Heat(false); digitalWrite(NASOS, LOW); Moschnost = 0; UPGRADE = 1000;}
  
//--------------------------------------------------------  
      BUTTON_01.read();
  if (BUTTON_01.timer)       {lcd.noBacklight(); tr_menu = false; lcd.noBlink(); lcd.clear(); Enc_Zero(0); keyPres = false;} 
  if (BUTTON_01.click_down)  {keyPres = true; lcd.backlight();}
  if (BUTTON_01.retention)   {lcd.backlight(); tr_menu = !tr_menu; if (tr_menu == true) {Enc_Zero(0); menu(0); lcd.blink();} else {lcd.noBlink(); lcd.clear(); Enc_Zero(0); SavePAT();} keyPres = false;}
  if (BUTTON_01.doubleclick) {if (!tr_menu && position == 1 && !STATUS){STATUS = true; vinti = 0; BobleOff = true; Enc_Zero(0);} else if (!tr_menu && position == 0 && STATUS) {STATUS = false;} else if (tr_menu == false && position == 2 && STATUS) {Pause = !Pause;} Udate = true;}
  if (BUTTON_01.click_up)    {if (keyPres == true) {EnterCHG();} keyPres = false;}
//--------------------------------------------------------

  if (tr_menu == false){ DO_EVERY(UPGRADE,{Udate = true;}); menu(99); }
  
  if (Pos != position) {position = Pos; lcd.backlight();
    if (tr_menu == false)                {if (position > 2)       {Enc_Zero(0);} else if (position < 0) {Enc_Zero(2);      } Print_ctrl_main();}
    if (tr_menu == true && dir == false) {if (position > maxMenu) {Enc_Zero(0);} else if (position < 0) {Enc_Zero(maxMenu);} menu(position);}
    if (tr_menu == true && dir == true)  {ChgData();}  
  }

//``````````````````````````````````````````
#ifdef DEBUG
  Mic2 = 1000 / (millis() - Mic1); // Вычисление количества операций(циклов) в секунду
  // Переменные и флаги 
  Serial.print(" Status-");      Serial.print(STATUS);
  Serial.print(" Pause-");       Serial.print(Pause);
  // Значения и параметры
  Serial.print(" Output-");      Serial.print(Output); 
  Serial.print(" Input-");       Serial.print(Input);
  Serial.print(" celsius-");     Serial.print(celsius);
  Serial.print(" Setpoint-");    Serial.print(Setpoint);
  Serial.print(" Nasos_Start-"); Serial.print(Nasos_Start);
  Serial.print(" Nasos_Stop-");  Serial.print(Nasos_Stop);
  // Производительность
  Serial.print(" CPU-");         Serial.print(Mic2); Serial.print("Lps");
  // Конец тестовой информации
  Serial.println(" |");
#endif
//``````````````````````````````````````````
} // end Loop
void ChgData(){
        if (curMenu == 0) {if (position >= 0 && position <= 2)    {T_Upr = position; lcd.setCursor(12, 1); switch (T_Upr){case 0:lcd.print("MAN "); break; case 1:lcd.print("AUTO"); break; case 2:lcd.print("PAT "); break;}} else {Enc_Zero(0);} lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 1) {if (position >= 25 && position <= 90)  {Nasos_Start = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(25);} lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 2) {if (position >= 25 && position <= 90)  {Nasos_Stop = position;  lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(25);} lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 3) {lcd.setCursor(6, 1); lcd.print("             "); CHGFile(); lcd.setCursor(6, 1); lcd.print(FileiniNAME); lcd.setCursor(0, 1); lcd.blink();}
        if (curMenu == 4) {ReadINI(); lcd.setCursor(8, 3); if (!SD_stat) {lcd.print("fail");}else{lcd.print("ok");} lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 5) {if (position >= 1 && position <= 100)  {PID_SPEED = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 1); lcd.blink();}
        if (curMenu == 6) {if (position >= 0 && position <= 100)  {consKi = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 7) {if (position >= 0 && position <= 100)  {Setpoint = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 8) {if (position >= 0 && position <= 24)   {A_H = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 1); lcd.blink();}
        if (curMenu == 9) {if (position >= 0 && position <= 59)   {A_M = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);} lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 10){rtc.adjust(DateTime(2015, 8, 20, A_H, A_M, 0)); lcd.setCursor(12, 3); lcd.print("ok"); lcd.setCursor(0, 3); lcd.blink();}

        if (curMenu == 11) {if (position >= 0 && position <= 100)   {Pat[0][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 12) {if (position >= 0 && position <= 32000) {Pat[0][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 13) {if (position >= 0 && position <= 1)     {Pat[0][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 14) {if (position >= 0 && position <= 100)   {Pat[1][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 15) {if (position >= 0 && position <= 32000) {Pat[1][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 16) {if (position >= 0 && position <= 1)     {Pat[1][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 17) {if (position >= 0 && position <= 100)   {Pat[2][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 18) {if (position >= 0 && position <= 32000) {Pat[2][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 19) {if (position >= 0 && position <= 1)     {Pat[2][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 20) {if (position >= 0 && position <= 100)   {Pat[3][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 21) {if (position >= 0 && position <= 32000) {Pat[3][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 22) {if (position >= 0 && position <= 1)     {Pat[3][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 23) {if (position >= 0 && position <= 100)   {Pat[4][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 24) {if (position >= 0 && position <= 32000) {Pat[4][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 25) {if (position >= 0 && position <= 1)     {Pat[4][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 26) {if (position >= 0 && position <= 100)   {Pat[5][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 27) {if (position >= 0 && position <= 32000) {Pat[5][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 28) {if (position >= 0 && position <= 1)     {Pat[5][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 29) {if (position >= 0 && position <= 100)   {Pat[6][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 30) {if (position >= 0 && position <= 32000) {Pat[6][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 31) {if (position >= 0 && position <= 1)     {Pat[6][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 32) {if (position >= 0 && position <= 100)   {Pat[7][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 33) {if (position >= 0 && position <= 32000) {Pat[7][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 34) {if (position >= 0 && position <= 1)     {Pat[7][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 35) {if (position >= 0 && position <= 100)   {Pat[8][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 36) {if (position >= 0 && position <= 32000) {Pat[8][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 37) {if (position >= 0 && position <= 1)     {Pat[8][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
        if (curMenu == 38) {if (position >= 0 && position <= 100)   {Pat[9][0] = position; lcd.setCursor(12, 1); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 1); lcd.blink();} 
        if (curMenu == 39) {if (position >= 0 && position <= 32000) {Pat[9][1] = position; lcd.setCursor(12, 2); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 2); lcd.blink();}
        if (curMenu == 40) {if (position >= 0 && position <= 1)     {Pat[9][2] = position; lcd.setCursor(12, 3); lcd.print(position); lcd.print("     ");} else {Enc_Zero(0);}  lcd.setCursor(0, 3); lcd.blink();}
}
void Heater(){


  
}
void Prokachka(){

  if (BobleOff){
    if (Input <= Nasos_Stop){
      BobleOff_tec1 = millis();
      if (BobleOffst){BobleOff_tec2 = BobleOff_tec1 + BobleOff_tim; BobleOffst = false;}
      if (BobleOff_tec1 <= BobleOff_tec2) {digitalWrite(NASOS, HIGH); Pause = true; 
      } else {digitalWrite(NASOS, LOW); Pause = false; BobleOff = false; BobleOffst = true; BobleOff_tec1 = 0;}
    } else {BobleOff = false;}
   }
  
  if (!Pause){if (Input >= Nasos_Start && Input <= Nasos_Stop) {digitalWrite(NASOS, HIGH);} else {digitalWrite(NASOS, LOW);}} else {if (!BobleOff) {digitalWrite(NASOS, LOW);}}
}

void CHGFile(){
    entry = root.openNextFile(); if (!entry) { root.rewindDirectory(); }
    FileiniNAMEAn = entry.name();
    if (FileiniNAMEAn == "" or FileiniNAMEAn == "SYSTEM~1") {entry = root.openNextFile();}
    else {FileiniNAME = entry.name();}}

void ReadINI(){
if (SD_stat){
  char Str2[FileiniNAME.length()+1]; FileiniNAME.toCharArray(Str2, FileiniNAME.length()+1);
  IniFile ini(Str2);
 
  if (!ini.open()) {clearLine(0); lcd.print("Ini file does not exist"); while (1);}
  if (!ini.validate(buffer, bufferLen)) {clearLine(0); lcd.print("ini file "); lcd.print(ini.getFilename()); lcd.print(" not valid"); while (1);}
//--------------------------------------------------------------------  
  if (ini.open()) { /* clearLine(0); lcd.print("Load: "); lcd.print(ini.getFilename()); */ }
  if (ini.getValue("NAME",  "NAME",      buffer, bufferLen)) {if (curMenu == 4) {clearLine(2); lcd.setCursor(1, 2); lcd.print(buffer);}} //else {clearLine(0); lcd.print("Default Error file");}
  if (ini.getValue("PID",   "consKp",    buffer, bufferLen)) {consKp      = String(buffer).toInt();}
  if (ini.getValue("PID",   "consKi",    buffer, bufferLen)) {consKi      = String(buffer).toInt();}
  if (ini.getValue("PID",   "consKd",    buffer, bufferLen)) {consKd      = String(buffer).toInt();}
  if (ini.getValue("PID",   "PID_SPEED", buffer, bufferLen)) {PID_SPEED   = String(buffer).toInt();}
  if (ini.getValue("NASOS", "start",     buffer, bufferLen)) {Nasos_Start = String(buffer).toInt();}
  if (ini.getValue("NASOS", "stop",      buffer, bufferLen)) {Nasos_Stop  = String(buffer).toInt();}
 
  for (int mesMas=0; mesMas <= 9; mesMas++){
      char Str1[Prog[mesMas].length()+1]; Prog[mesMas].toCharArray(Str1, Prog[mesMas].length()+1);
      if (ini.getValue(Str1, "P_Temp",    buffer, bufferLen)) {Pat[mesMas][0]   = String(buffer).toInt();}
      if (ini.getValue(Str1, "P_Time",    buffer, bufferLen)) {Pat[mesMas][1]   = String(buffer).toInt();}
      if (ini.getValue(Str1, "P_Alar",    buffer, bufferLen)) {Pat[mesMas][2]   = String(buffer).toInt();}
  }
  ini.close();
  entry.close();
//--------------------------------------------------------------------
}}
void LoadPAT(){
  INI_pat_mem();
  for (int pr=50; pr <= 59; pr++){
    prog = pr - 50;
    mem1 = pr;
    mem2 = pr + 10;
    mem3 = pr + 20;
    Pat[prog][0] = EEPROM.read(mem1); Pat[prog][1] = EEPROM.read(mem2); Pat[prog][2] = EEPROM.read(mem3);
  }
  T_Upr       = EEPROM.read(111);
  consKi      = EEPROM.read(112);
  PID_SPEED   = EEPROM.read(113);
  Nasos_Start = EEPROM.read(114);
  Nasos_Stop  = EEPROM.read(115);
  Setpoint    = EEPROM.read(116);
  myPID.SetTunings(consKp, consKi, consKd);
}

void SavePAT(){
  INI_pat_mem();
  for (int pr=50; pr <= 59; pr++){
    prog = pr - 50;
    mem1 = pr;
    mem2 = pr + 10;
    mem3 = pr + 20;
    if (EEPROM.read(mem1) != Pat[prog][0]) {EEPROM.write(mem1, Pat[prog][0]);}
    if (EEPROM.read(mem2) != Pat[prog][1]) {EEPROM.write(mem2, Pat[prog][1]);}
    if (EEPROM.read(mem3) != Pat[prog][2]) {EEPROM.write(mem3, Pat[prog][2]);}
  }
    if (EEPROM.read(111) != T_Upr)       {EEPROM.write(111, T_Upr);                                               } // Сохранение значения режима работы
    if (EEPROM.read(112) != consKi)      {EEPROM.write(112, consKi);    myPID.SetTunings(consKp, consKi, consKd); } // Сохранение значения режима consKi PID регулятора
    if (EEPROM.read(113) != PID_SPEED)   {EEPROM.write(113, PID_SPEED);                                           } // Сохранение значения режима SPEED PID регулятора
    if (EEPROM.read(114) != Nasos_Start) {EEPROM.write(114, Nasos_Start);                                         } // Сохранение значения режима запуска насоса
    if (EEPROM.read(115) != Nasos_Stop)  {EEPROM.write(115, Nasos_Stop);                                          } // Сохранение значения режима отключения насоса
    if (EEPROM.read(116) != Setpoint)    {EEPROM.write(116, Setpoint);                                            } // Сохранение значения установленной температуры
}

void INI_pat_mem(){
  prog = 0; mem1 = 0; mem2 = 0; mem3 = 0;
}
void getTemp() {
  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);
  ds.reset();
  ds.select(addr);
  ds.write(0xBE);
  for ( int i = 0; i < 9; i++) {data[i] = ds.read();}
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3;
    if (data[7] == 0x10) {raw = (raw & 0xFFF0) + 12 - data[6];}
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw & ~7;
    else if (cfg == 0x20) raw = raw & ~3;
    else if (cfg == 0x40) raw = raw & ~1;
  }
  celsius = (float)raw / 16.0;
  return;
}

void menu(int lvl){
  if (lvl == 0 || lvl == 1 || lvl == 2){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 0"); 
      lcd.setCursor(0, 1); lcd.print(" Mode:"); lcd.setCursor(12, 1); switch (T_Upr){case 0:lcd.print("MAN "); break; case 1:lcd.print("AUTO"); break; case 2:lcd.print("PAT "); break;}
      lcd.setCursor(0, 2); lcd.print(" Nasos run:");  lcd.setCursor(12, 2); lcd.print(Nasos_Start);
      lcd.setCursor(0, 3); lcd.print(" Nasos stop:"); lcd.setCursor(12, 3); lcd.print(Nasos_Stop);
      switch (lvl){case 0:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 1:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 2:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 3 || lvl == 4){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 1");
      lcd.setCursor(0, 1); lcd.print(" File:"); lcd.print(FileiniNAME);
      if (!SD_stat) {lcd.setCursor(0, 2); lcd.print(" SD init - failed!");}
      lcd.setCursor(0, 3); lcd.print(" Load"); 
      switch (lvl){case 3:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 4:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 5 || lvl == 6 || lvl == 7 ){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 2");
      lcd.setCursor(0, 1); lcd.print(" PID_SPEED"); lcd.setCursor(12, 1); lcd.print(PID_SPEED);
      lcd.setCursor(0, 2); lcd.print(" consKi");    lcd.setCursor(12, 2); lcd.print(consKi);
      lcd.setCursor(0, 3); lcd.print(" Setpoint");  lcd.setCursor(12, 3); lcd.print(Setpoint);
      switch (lvl){case 5:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 6:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 7:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 8 || lvl == 9 || lvl == 10 ){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 3");
      lcd.setCursor(0, 1); lcd.print(" hour");   lcd.setCursor(12, 1); lcd.print(A_H);
      lcd.setCursor(0, 2); lcd.print(" minute"); lcd.setCursor(12, 2); lcd.print(A_M);
      lcd.setCursor(0, 3); lcd.print(" set clock"); 
      switch (lvl){case 8: lcd.setCursor(0, 1); lcd.blink(); break;
                   case 9: lcd.setCursor(0, 2); lcd.blink(); break;
                   case 10:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
//```````````````````````````````````````````````````````````````````````````````````````````
  if (lvl == 11 || lvl == 12 || lvl == 13){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.0"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[0][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[0][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[0][2]);
      switch (lvl){case 11:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 12:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 13:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 14 || lvl == 15 || lvl == 16){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.1"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[1][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[1][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[1][2]);
      switch (lvl){case 14:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 15:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 16:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 17 || lvl == 18 || lvl == 19){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.2"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[2][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[2][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[2][2]);
      switch (lvl){case 17:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 18:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 19:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 20 || lvl == 21 || lvl == 22){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.3"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[3][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[3][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[3][2]);
      switch (lvl){case 20:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 21:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 22:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 23 || lvl == 24 || lvl == 25){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.4"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[4][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[4][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[4][2]);
      switch (lvl){case 23:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 24:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 25:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 26 || lvl == 27 || lvl == 28){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.5"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[5][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[5][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[5][2]);
      switch (lvl){case 26:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 27:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 28:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 29 || lvl == 30 || lvl == 31){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.6"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[6][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[6][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[6][2]);
      switch (lvl){case 29:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 30:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 31:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 32 || lvl == 33 || lvl == 34){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.7"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[7][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[7][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[7][2]);
      switch (lvl){case 32:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 33:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 34:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 35 || lvl == 36 || lvl == 37){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.8"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[8][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[8][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[8][2]);
      switch (lvl){case 35:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 36:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 37:lcd.setCursor(0, 3); lcd.blink(); break;}
  }
  if (lvl == 38 || lvl == 39 || lvl == 40){ lcd.clear(); curMenu = lvl;
      clearLine(0);
      lcd.setCursor(0, 0); lcd.print(" MENU - 4.9"); 
      lcd.setCursor(0, 1); lcd.print(" P_Temp "); lcd.setCursor(12, 1); lcd.print(Pat[9][0]);
      lcd.setCursor(0, 2); lcd.print(" P_Time "); lcd.setCursor(12, 2); lcd.print(Pat[9][1]);
      lcd.setCursor(0, 3); lcd.print(" P_Alar "); lcd.setCursor(12, 3); lcd.print(Pat[9][2]);
      switch (lvl){case 38:lcd.setCursor(0, 1); lcd.blink(); break;
                   case 39:lcd.setCursor(0, 2); lcd.blink(); break;
                   case 40:lcd.setCursor(0, 3); lcd.blink(); break;}
  }

//```````````````````````````````````````````````````````````````````````````````````````````  
  if (lvl == 99){ 

    if (Udate){ Udate = false;
      lcd.setCursor(0, 0); lcd.print("T.ust "); lcd.print(Setpoint); lcd.print("\5"); Card(); if (STATUS) {Vintik(); lcd.setCursor(17, 0); lcd.print("RUN");} else {lcd.setCursor(15, 0); lcd.print("+ OFF");}
      lcd.setCursor(0, 1); lcd.print("T.zid "); lcd.print(Input); lcd.print("\5"); lcd.setCursor(13, 1); switch (T_Upr){case 0:lcd.print("M"); break; case 1:lcd.print("A"); break; case 2:lcd.print("P"); break;}
      lcd.setCursor(0, 2); lcd.print("P.ten "); lcd.setCursor(13, 2); if (T_Upr == 2) {lcd.print(P_Numb);} else {lcd.print("-");}
     
      if (Moschnost <= 100) {lcd.setCursor(6, 2);lcd.print(Moschnost); lcd.print("% ");}
      if (Moschnost < 10)   {lcd.setCursor(6, 2);lcd.print(Moschnost); lcd.print("%  ");}
      
      Print_ctrl_main();
 
      lcd.setCursor(0, 3); if (_HR < 10) {lcd.print("0"); lcd.print(_HR);} else {lcd.print(_HR);} lcd.print(":"); 
                           if (_MI < 10) {lcd.print("0"); lcd.print(_MI);} else {lcd.print(_MI);} lcd.print(":"); 
                           if (_SE < 10) {lcd.print("0"); lcd.print(_SE);} else {lcd.print(_SE);} 
                           
     lcd.setCursor(13, 3); if (digitalRead(NASOS)) {if (BobleOff) {lcd.print("B");} else {lcd.print("N");}} else {lcd.print("-");}
    }
  }
}

void Print_ctrl_main(){
      if (tr_menu == false && position == 0) {lcd.setCursor(15, 1); lcd.print("\7STOP"); 
                                              lcd.setCursor(15, 2); lcd.print(" WORK");
                                              lcd.setCursor(15, 3); if (Pause) {lcd.print(" NEXT");} else {lcd.print(" PAUS");}}
                                              
      if (tr_menu == false && position == 1) {lcd.setCursor(15, 1); lcd.print(" STOP"); 
                                              lcd.setCursor(15, 2); lcd.print("\7WORK");
                                              lcd.setCursor(15, 3); if (Pause) {lcd.print(" NEXT");} else {lcd.print(" PAUS");}} 
                                              
      if (tr_menu == false && position == 2) {lcd.setCursor(15, 1); lcd.print(" STOP"); 
                                              lcd.setCursor(15, 2); lcd.print(" WORK");
                                              lcd.setCursor(15, 3); if (Pause) {lcd.print("\7NEXT");} else {lcd.print("\7PAUS");}}                                                     
}
void Vintik(){
  lcd.setCursor(15, 0);
  if (!Pause){
    vinti = vinti + 1;
    switch (vinti){case 1:lcd.print("\1"); break;
                   case 2:lcd.print("\2"); break;
                   case 3:lcd.print("\3"); break;
                   case 4:lcd.print("\4"); vinti = 0; break;}
  }
}

void Card(){
  lcd.setCursor(13, 0);
  if (!SD_stat){
    cardti = cardti + 1;
    switch (cardti){case 1:lcd.print("\6"); break;
                    case 2:lcd.print(" "); cardti = 0; break;}
  }else{lcd.print("\6");}
}

maxi_10
Offline
Зарегистрирован: 05.01.2012

Работа с меню осуществляется с помощью энкодера и его кнопки. Находясь на домашнем экране поворот энкодера переключает отдин из 3х пунктов меню (см. скриншот выше). При двойном клике (два key_up до 200ms) активируется соответствующий выбранный пунк. Длительное (2000ms) нажатие переводит в меню настроек (скриншоты в проццесе), далее вращение энкодера переключает по пунктам меню. короткий клик на выбраном пункте меню вводит в состояние настройки параметра, второй короткий клик в меню применяет заданные параметры. Длительный клик находясь в меню запускает процедуру сохранения !только измененых параметров! и выводит на домашний экран.

maxi_10
Offline
Зарегистрирован: 05.01.2012

Отдельное спасибо Клапауций 999 за класс для кнопки, и leshak за <TimeHelpers.h>

 

maxi_10
Offline
Зарегистрирован: 05.01.2012

Формат ini файла с рецептом

[NAME]
# Имя не должно содержать больше 19 символов.
# Исключительно информационное поле на работу контроллера не влияет.
name = Irlandskoe_temnoe

[PID]
# Обязательные параметры PID регулятора
consKp = 5
consKi = 75
consKd = 1
PID_SPEED = 3

[NASOS]
# Температура запуска и остановки насоса.
start = 20
stop = 85

[PROG_1]
# Первая программа
# P_Temp - температура. Значения исчисляются в Градусах цельсия от 0 до 99.
# P_Time - Время поддержания заданной температуры. Значение исчисляются в минутах. Если значение времени равно 0 то выполнение данной программы и всех дальнейших останавливается.
# P_Alar - необходимость паузы после выполнения данной программы. Может принимать только 0 и 1.
P_Temp = 38;
P_Time = 20
P_Alar = 0

[PROG_2]
# Настройки аналогичны первой программе.
P_Temp = 81
P_Time = 20
P_Alar = 0

[PROG_3]
# Настройки аналогичны первой программе.
P_Temp = 64
P_Time = 20
P_Alar = 0

[PROG_4]
# Настройки аналогичны первой программе.
P_Temp = 28
P_Time = 20
P_Alar = 0

[PROG_5]
# Настройки аналогичны первой программе.
P_Temp = 50
P_Time = 0
P_Alar = 0

[PROG_6]
# Настройки аналогичны первой программе.
P_Temp = 46
P_Time = 20
P_Alar = 0

[PROG_7]
# Настройки аналогичны первой программе.
P_Temp = 37
P_Time = 20
P_Alar = 0

[PROG_8]
# Настройки аналогичны первой программе.
P_Temp = 11
P_Time = 20
P_Alar = 0

[PROG_9]
# Настройки аналогичны первой программе.
P_Temp = 44
P_Time = 20
P_Alar = 0

[PROG_10]
# Настройки аналогичны первой программе.
P_Temp = 76
P_Time = 20
P_Alar = 0

 

Клапауций 998
Offline
Зарегистрирован: 12.08.2015

maxi_10 пишет:

Отдельное спасибо Клапауций 999 за класс для кнопки

ок - рад, что не мне одному показалось это нужным.

adsound
Offline
Зарегистрирован: 18.09.2015

Максим! как успехи с новой версией проекта? добил его

ers
Offline
Зарегистрирован: 06.09.2015

maxi_10 пишет:

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

Аналогично. Пересмотрев существующие решения и желания пивоваров, начали разработку новой автоматики с touch LCD и WiFi.

 

Небольшой демо-ролик

За подробностями можно следить тут http://hbpro.ru/threads/wi-fi-kontroller-na-esp8266.233/

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

Здравствуйте! maxi_10, продолжение будет?)) очень ждем) или, если может где-нибудь на другом ресурсе описывали, можно ссылочку?

LanSWAT
Offline
Зарегистрирован: 08.10.2015

Здраствуйте уважаемый maxi_10! Заинтересовал ваш проект. Какова судьба и как с продолжением?

Avtomatika
Offline
Зарегистрирован: 20.07.2016

Здравствуйте, я собрал автоматику на LCD 3.2  на Mega кому интерестно   https://youtu.be/BvQUjaGohdY

falcon_e
Offline
Зарегистрирован: 28.01.2017

День добрый! не могу найти библиотеку <TimeHelpers.h>.  

 

Alex22rus
Offline
Зарегистрирован: 15.10.2017

Привет всем!!! а я с компа варю) https://youtu.be/Ua9WxJoCwpc