#include <TFT_eSPI.h>
#include <SPI.h>       // this is needed for display
#include <Wire.h>      // this is needed for FT6206
#include <Adafruit_FT6206.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>
#include "Free_Fonts.h" 
#include <EEPROM.h>                  //Подключаем библиотеку для работы с энергонезависимой памятью
#include <OneWire.h>                 //Подключаем библиотеку для температурного датчика DS18B20
#include <DHTesp.h>                     //Подключаем библиотеку для датчика влажности и температуры




// The FT6206 uses hardware I2C (SCL/SDA)
Adafruit_FT6206 ctp = Adafruit_FT6206();

// The display also uses hardware SPI, plus #9 & #10
#define TFT_CS 15
#define TFT_DC 2
#define TFT_MOSI 23
#define TFT_SCLK 18

TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height

//=====Button PS*
#define axis_X 26                    //Ось Х подключена к Analog 0
#define axis_Y 25                    //Ось Y подключена к Analog 1
#define axis_Z 34                     //Ось Z (кнопка джойстика) подключена к Digital 6
#define pushLED 10                   //Индикаторный светик нахожения в меню и нажатия кнопки (мигает при авариях)
unsigned  long pressTime=0;          //Запоминаем, когда произошло нажатие кнопки
bool prevButtonState;
int val_X, val_Y, val_Z=0;           //Переменные для хранения значений осей
byte buttonState=0;                  //Флаг состояния нажатия кнопки
byte butLongState=0;                 //Флаг "удержания" кнопки, longClick

//=====Blink led*
#define BeepPin 12                   //Пищалка

//=====Moisture*
#define moistIn 39                   //Определяем Аналоговый вход А2 датчика влажности почвы (вилочка такая)
int moistAO=EEPROM.read(9);          //Переменная влажности в % (итоговая), приравниваем, чтобы при старте неполивать :(
int moistTmp=0;                      //Переменная влажности в "попугаях" 0-1023 (промежуточная)
byte autoIrrig=0;                    //Флаг состояния автополива(необходимо поднять при включении помпы)
byte tIrrig=0;                       //Переменная интервала влючения датчика влажности грунта

//=====VoltMeter*
int voltInput=36;                    //Определяем Аналоговый вход A3 для измерения напряжения
float vout=0.0;
float vin=0.0;
float R1=32140.0;                    //Номинал первого резистора, тот которй ПОСЛЕДОВАТЕЛЬНО в цепи измерения
float R2=12810.0;                    //А тут номинал второго резистора, что ПАРАЛЕЛЬНО измерению и к земле
int valueVo=0;

//=====Led_Relay*
#define OUT0 2                       //Используем цифровой ПОРТ 2 включения датчика измернеия влажности почвы
#define OUT1 3                       //Используем цифровой ПОРТ 3 помпочки автополива
//#define OUT2 39                      //Используем аналоговый ПОРТ A6 включения реле вентиляции теплички

//=====Humidity*
#define dhtPin 5                    //Определяем цифровой ПОРТ 5 датчика влажности воздуха в тепличке (хреновая)
#define dhtType DHT11               //Ранее был А6 (он РАСПАЯН и обрезан, красный короткий)
DHTesp dht;
float hmdt;

//=====Termo_Sensor*
int tempPin=4;                      //Определяем порт шины OneWire (IC) для температурного датчика DS18B20                               
OneWire ds(tempPin);                 //Создаем объект для работы с термометром
byte flagDallas=0;                   //Флаг для обработки показаний с датчиков Dallas
byte data[12];
byte addr1[8]={0x28, 0x33, 0x4B, 0xEA, 0x05, 0x00, 0x00, 0x54};     //адрес датчика DS18B20_в парничке
byte addr2[8]={0x28, 0xFF, 0xB5, 0x24, 0x54, 0x14, 0x01, 0xC7};     //адрес датчика DS18B20_на улице
unsigned int raw;                    //Если экранированный кабель, можно подключать до 32 термо-датчиков DS18B20
float temp[2];                       //Температура на уличке \ в парничке

//=====Rtc_Clock*
#define DS1307_I2C_ADDRESS 0x68
byte seconds, minutes, hours, day, date, month, year;
byte decToBcd (byte val){return((val/10*16)+(val%10));}

//=====Timer*
byte IrrigStat=0;                   //Флаг полива, если включена, то 1 конечное-жеж
byte TempStat=0;                    //Флаг вентиляции, если включена, то 1 конечное-жеж
byte isIrrigt=0;                    //Флаг для обработки необходимости включить автополив (по таймеру менюшному)
byte isTemp=0;                      //Флаг для обработки необходимости включить вентиляцию (по томуже таймеру)
byte isNight=0;                     //Если включаем на ночь, т.е. начальное время больше конечного

//=====Menu*
byte winMe;                         //Номер экрана меню
byte setHorClockOn;                 //Часы включения Таймера
byte setMinClockOn;                 //Минуты включения Таймера
byte setHorClockOff;                //Часы вЫключения Таймера
byte setMinClockOff;                //Минуты вЫключения Таймера

byte setTemptGist;                  //Гистерезис температур (по 1/2 в каждую сторону)
byte setTemptIn;                    //Максимальная температура воздуха в парничке для вентиляции
byte setTemptInOnOff;               //Флаг Включения/Выключения НЕОЬХОДИМОСТИ "контроля" температуры воздуха в тепличке

byte setMoistIn;                    //Максимальная влажность воздуха в тепличке для вентиляции
byte setMoistInOnOff;               //Флаг Включения/Выключения НЕОЬХОДИМОСТИ контроля влажности воздуха

byte setSoiltIn;                    //Минимальная влажность почвы в парничке для полива
byte setSoiltTime;                  //Интервал работы датчика влажности грунта
byte setSoiltIrrig;                 //Интервал работы помпочки автополива
byte setSoiltInOnOff;               //Флаг Включения/Выключения НЕОЬХОДИМОСТИ контроля влажности грунта

byte setVoltCt;                     //Минимальная напряжение на батарее для замены
byte setVoltCtOnOff;                //Флаг Включения/Выключения НЕОЬХОДИМОСТИ контрорля напряжения батареечки ценной

//=====Delays*
unsigned long prvMlsTm=0;           //Предыдущее показание миллисекунд для обновления показания часов
unsigned long prvMlsVo=0;           //Предыдущее показание обновления показания вольтметра
unsigned long prvMlsMoist=0;        //Предыдущее показание обновления показания влажности почвы
unsigned long prvMlsIrrig=0;        //Предыдущее показание обновления показания автополива
unsigned long prvMlsTemp=0;         //Предыдущее показание обновления температур
unsigned long prvMlsTimer=0;        //Предыдущее показание обновления таймера
unsigned long prvMlsbLink=0;        //Предыдущее показание миллисекунд для обновления "blink led"
unsigned long tzad=millis();        //Переменная задержки (пока для ds18b20, ds.write)

unsigned long zadM[4];              //Массив для задержек
const long zadTime[]={100,500,750,1000,2000,3000,5000,10000,60000};

const char menuTxt[6][17]={"set Timer Soil >","set Timer Temp >",
                           "set Irrigation >","set TermoRele >>",
                           "set VoltControl>","set Clock >>>>>>"};      //Массив с наименованиями для экрана

void setup(){
  Wire.begin();
    tone(BeepPin, 5040, 1000);
  Serial.begin(9600);                                //Инициализация Serial-порта
  dht.setup(dhtPin, DHTesp::DHT11);                                       //Инициализация датчика влажности DHT-11
    pinMode(voltInput,INPUT);                        //Определяем вход вольтметра
    pinMode(moistIn, INPUT);                         //Определяем вход измерителя влажности почвы
    pinMode(axis_Z, INPUT);                          //Задаем как вход нажатия кнопки джойстика
    digitalWrite(axis_Z,HIGH);                       //ПОдтягиваем внутреннее сопротивнление ардуинки
    pinMode(pushLED,OUTPUT);                         //Определяем светик нажатия и нахождения в меню
    pinMode(OUT0,OUTPUT);                            //Определяем инверсные выходы реле датчика влажности почвы
    pinMode(OUT1,OUTPUT);                            //Определяем инверсные выходы реле помпы автополива
//    pinMode(bLink,OUTPUT);
    digitalWrite(OUT0,HIGH);                         //Определяем инверсный выход в HIGH реле сенсора почвы
    digitalWrite(OUT1,HIGH);                         //Определяем инверсный выход в HIGH реле помпы полива
    digitalWrite(pushLED,LOW);                       //Определяем инверсный выход в HIGH реле помпы полива
//    digitalWrite(bLink,HIGH);
//    tft.initR(INITR_BLACKTAB);     
tft.begin();                  //Initialize a ST7735S chip, black tab
    tft.fillScreen(TFT_BLACK);                    //Clear screen
}  

//========== Обработка нажатия кнопки и джойстика PS
void buttonPS(){
  val_X=analogRead(axis_X);                         //Считываем аналоговое значение оси Х
  val_Y=analogRead(axis_Y);                         //Считываем аналоговое значение оси Y
  val_Z=digitalRead(axis_Z);                        //Считываем цифровое значение оси Z (кнопка)
  val_Z=val_Z^1;                                    //Инвертируем значение
bool buttonState;                                   //Читаем кнопку
buttonState=(digitalRead(axis_Z)==LOW);
    if (buttonState==HIGH){
      digitalWrite(pushLED,HIGH);
  } else {
      digitalWrite(pushLED,LOW);}
    if(!prevButtonState && buttonState){            //Была не нажата, стала нажата
     pressTime=millis();}
    if(!buttonState)pressTime=0;                    //При отпускании кнопки - сразу останавливаем осчет
    if(pressTime&&(millis()-pressTime>1000)){       //Если с момента нажатия прошло более 1- секунды
    butLongState=1;                                 //Поднимаем флаг "удержания" кнопки
    tone(BeepPin, 240, 100);                        //Попискиваем при этом
    pressTime=0;}                                   //Останавливаем отсчет что-бы не переключить несколько раз по одному нажатию 
      prevButtonState=buttonState;                  //Запомнили "прошлое" состояние кнопки
}
 
//========== Обработка Входа в Меню
void setUp(){
    if (butLongState==1){                            //ВХОД В МЕНЮ и проверка блокировки от возврата. Принимаем код клавиши
    digitalWrite(OUT0,HIGH); digitalWrite(OUT1,HIGH);//Отключаем датчик измерения влажности почвы и работу помпы !!!!
        menu();}                                     //Если флаг longClick идем в меню
}
                           
//=========== Обработка Меню, выбор экрана
void menu(){
  tft.fillScreen(TFT_BLUE);                                  //Clear screen
  tft.setTextSize(1);
  tft.setTextColor(TFT_WHITE, TFT_BLUE);
  byte pos=0;
  while(1){                                                     //Бесконечный цикл
//      bLinkLed();
      butLongState=0;
      buttonPS();
      digitalWrite(pushLED,HIGH);
    tft.setCursor(5,55);
    tft.print(pos+1);                                           //Печатаем номер
    tft.print(". ");
    tft.print(menuTxt[pos]);                                    //Печатаем название
      if (val_Y>923 && pos<5) {pos++;}
  else if (pos==5 && val_Y>923) {pos=0;}
      if (val_Y<123 && pos>0) {pos--;}                          //Уменьшить/увеличить позицию окна меню
  else if (pos==0 && val_Y<123) {pos=5;}                        //Уменьшить/увеличить (циклично)      
      winMe=pos;                                                //Переменная номера таймера
    if (butLongState==1 && pos==0) setOnOff();        //"set Timer Soil >" 0
    if (butLongState==1 && pos==1) setOnOff();        //"set Timer Temp >" 1
    if (butLongState==1 && pos==2) setIrrigation();   //"set Irrigation >" 2
    if (butLongState==1 && pos==3) setTermoRele();    //"set TermoRele >>" 3
    if (butLongState==1 && pos==4) setVoltControl();  //"set VoltControl>" 4
    if (butLongState==1 && pos==5) setClock();        //"set Clock >>>>>>" 5
    if (val_X>923){                                              //Выход из меню с проверкой и установкой блокировки от возврата       
      tft.fillScreen(TFT_BLACK);                              //Clear screen
    tone(BeepPin, 540, 200); break;} 
  }
} 

//=========== Обработка Меню, параметры и настройка таймеров
void setOnOff(){
  byte pos=0;   
  setHorClockOn=  EEPROM.read(winMe*4+1);                //Считываем записанные значения таймеров
  setMinClockOn=  EEPROM.read(winMe*4+2);                //Адрес определяется как номер таймера*4 + четыре ячейки
  setHorClockOff= EEPROM.read(winMe*4+3);
  setMinClockOff= EEPROM.read(winMe*4+4);
    tft.fillScreen(TFT_RED);                          //Clear screen
  while(1){                                              //Бесконечный цикл
//    while(buttonState==LOW){
      butLongState=0;                                    //Обнуляем флаг longClick
      buttonPS();
      digitalWrite(pushLED,HIGH);
      if (millis()-prvMlsTimer>zadTime[7]){                 //Проверяем интервал для обновления 
     prvMlsTimer=millis();
      tft.setTextSize(1);
    tft.setCursor(45,40);
    tft.setTextColor(TFT_YELLOW,TFT_RED);
    tft.print("  On   Off"); 
    tft.setCursor(5,150);
    tft.setTextColor(TFT_GREEN, TFT_RED);
    tft.print("=== push to save ===");}
    tft.setCursor(5,55);
    tft.print(winMe+1,DEC);                                //Печатаем номер программы таймера
    tft.print("|");
    tft.print(pos/3,DEC);                                  //Печатаем позицию настройки параметра (замена blink)
    tft.print(" ->");  
    tft.setCursor(45,55);                                  //Выводим инфу о часах и минутах
     if (setHorClockOn<10) tft.print("0");
    tft.print(setHorClockOn,DEC);
    tft.print(":");
     if (setMinClockOn<10) tft.print("0"); 
    tft.print(setMinClockOn,DEC);  
    tft.print(" ");     
     if (setHorClockOff<10) tft.print("0");
    tft.print(setHorClockOff,DEC);
    tft.print(":");
     if (setMinClockOff<10) tft.print("0");
    tft.print(setMinClockOff,DEC); 

    tft.setCursor(pos,0);                                   //Устанавливаем курсор согласно позиции

    if (val_X>923 && pos==3){                                //Выход из подменю без сохранения       
      tft.fillScreen(TFT_BLACK);                          //Clear screen
    return; winMe=pos;} 

    if (pos<3) pos=3;
    if (val_X<123 && pos<11) pos += 3;                       //Крутим позицию право-лево
    else if (val_X>923 && pos>3) pos -= 3;

    else if (pos==3 && val_Y>1020) setHorClockOn++;          //Крутим значения
    else if (pos==3 && val_Y<10) setHorClockOn--;
    else if (pos==6 && val_Y>1020) setMinClockOn++;
    else if (pos==6 && val_Y<10) setMinClockOn--;    
    else if (pos==9 && val_Y>1020) setHorClockOff++;
    else if (pos==9 && val_Y<10) setHorClockOff--;    
    else if (pos==12 && val_Y>1020) setMinClockOff++;
    else if (pos==12 && val_Y<10) setMinClockOff--; 
    
    if (setHorClockOn>23) setHorClockOn=0;                   //Ограничиваем значения
    else if (setMinClockOn>59) setMinClockOn=0;
    else if (setHorClockOff>23) setHorClockOff=0;
    else if (setMinClockOff>59) setMinClockOff=0;

    if (butLongState==1){                                    //Выход из меню с проверкой и установкой блокировки от возврата
    tft.fillScreen(TFT_BLACK);                            //Clear screen
    break;} 
}                                                            //Конец цикла
   tft.fillScreen(TFT_BLACK);                             //Clear screen
   EEPROM.write(winMe*4+1, setHorClockOn);                   //Записываем НОВЫЕ значения
   EEPROM.write(winMe*4+2, setMinClockOn); 
   EEPROM.write(winMe*4+3, setHorClockOff);
   EEPROM.write(winMe*4+4, setMinClockOff);
   
   tft.setCursor(25,105);
   tft.setTextColor(TFT_WHITE,TFT_BLACK);
   tft.setTextSize(1);
   tft.print("== Saved =="); tone(BeepPin, 3040, 500); 
   delay(zadTime[4]);
}

//=========== Обработка Меню, параметры и настройка автополива (влажность грунта)
void setIrrigation(){
  byte pos=0;   
  setSoiltIn=       EEPROM.read(winMe*4+1);              //Считываем записанные значения 
  setSoiltTime=     EEPROM.read(winMe*4+2);              //Адрес определяется как номер *2 + две ячейки
  setSoiltIrrig=    EEPROM.read(winMe*4+3);
  setSoiltInOnOff=  EEPROM.read(winMe*4+4);
    tft.fillScreen(TFT_RED);                          //Clear screen
  while(1){                                              //Бесконечный цикл
      butLongState=0;                                    //Обнуляем флаг longClick
      buttonPS();
      digitalWrite(pushLED,HIGH);
      tft.setTextSize(1);
      if (millis()-prvMlsTimer>zadTime[7]){                 //Проверяем интервал для обновления 
     prvMlsTimer=millis();
    tft.setCursor(45,40);
    tft.setTextColor(TFT_YELLOW,TFT_RED);
    tft.print("Soil%  Tsens"); 
      tft.setCursor(45,70);
    tft.setTextColor(TFT_YELLOW,TFT_RED);
    tft.print("Tpomp  On/Of"); 
    tft.setCursor(5,150);
    tft.setTextColor(TFT_GREEN, TFT_RED);
    tft.print("=== push to save ===");}
      tft.setCursor(5,55);
    tft.print(winMe+1,DEC);                                //Печатаем номер программы 
    tft.print("|");
    tft.print(pos/3,DEC);                                  //Печатаем позицию настройки параметра (замена blink)
    tft.print(" ->");  
    tft.setCursor(45,55);                                  //Выводим инфу 
     if (setSoiltIn<10) tft.print("0");
    tft.print(setSoiltIn,DEC);
    tft.print(" : ");
     if (setSoiltTime<10) tft.print("0");
    tft.print(setSoiltTime,DEC);
    tft.setCursor(45,85);
     if (setSoiltIrrig<10) tft.print("0");
    tft.print(setSoiltIrrig,DEC);
    tft.print(" : ");
    tft.print(setSoiltInOnOff,DEC);  
    
    tft.setCursor(pos,0);                                   //Устанавливаем курсор согласно позиции

    if (val_X>923 && pos==3){                                //Выход из подменю без сохранения       
      tft.fillScreen(TFT_BLACK);                          //Clear screen
    return;} 

    if (pos<3) pos=3;
    if (val_X<123 && pos<11) pos += 3;                       //Крутим позицию право-лево
    else if (val_X>923 && pos>3) pos -= 3;

    else if (pos==3 && val_Y>1020) setSoiltIn++;            //Крутим значения
    else if (pos==3 && val_Y<10) setSoiltIn--;
    else if (pos==6 && val_Y>1020) setSoiltTime++;
    else if (pos==6 && val_Y<10) setSoiltTime--; 
    else if (pos==9 && val_Y>1020) setSoiltIrrig++;
    else if (pos==9 && val_Y<10) setSoiltIrrig--; 
    else if (pos==12 && val_Y>1020) setSoiltInOnOff++;
    else if (pos==12 && val_Y<10) setSoiltInOnOff--;    
    
    if (setSoiltIn>99) setSoiltIn=0;                        //Ограничиваем значения
    if (setSoiltTime>59) setSoiltTime=0;
    if (setSoiltIrrig>19) setSoiltIrrig=0;
    if (setSoiltInOnOff>1) setSoiltInOnOff=0;

    if (butLongState==1){                                    //Выход из меню с проверкой и установкой блокировки от возврата
    tft.fillScreen(TFT_BLACK);                            //Clear screen
    break;} 
}                                                            //Конец цикла
   tft.fillScreen(TFT_BLACK);                             //Clear screen
   EEPROM.write(winMe*4+1, setSoiltIn);                      //Записываем НОВЫЕ значения
   EEPROM.write(winMe*4+2, setSoiltTime);
   EEPROM.write(winMe*4+3, setSoiltIrrig);
   EEPROM.write(winMe*4+4, setSoiltInOnOff); 
   
   tft.setCursor(25,105);
   tft.setTextColor(TFT_WHITE,TFT_BLACK);
   tft.setTextSize(1);
   tft.print("== Saved =="); tone(BeepPin, 3040, 500);
   delay(zadTime[4]);
}

//=========== Обработка Меню, параметры и настройка термореле (вентиляция)
void setTermoRele(){
  byte pos=0;   
  setTemptIn=       EEPROM.read(winMe*4+1);              //Считываем записанные значения 
  setTemptGist=     EEPROM.read(winMe*4+2);              //Адрес определяется как номер *4 + четыре ячейки
  setTemptInOnOff=  EEPROM.read(winMe*4+3);
    tft.fillScreen(TFT_RED);                          //Clear screen
  while(1){                                              //Бесконечный цикл
//    while(buttonState==LOW){
      butLongState=0;                                    //Обнуляем флаг longClick
      buttonPS();
      digitalWrite(pushLED,HIGH);
    if (millis()-prvMlsTimer>zadTime[7]){                 //Проверяем интервал для обновления 
     prvMlsTimer=millis();
      tft.setTextSize(1);
    tft.setCursor(45,40);
    tft.setTextColor(TFT_YELLOW,TFT_RED);
    tft.print("Temp Gst On/f"); 
    tft.setCursor(5,150);
    tft.setTextColor(TFT_GREEN, TFT_RED);
    tft.print("=== push to save ===");}
    tft.setCursor(5,55);
    tft.print(winMe+1,DEC);                                //Печатаем номер программы 
    tft.print("|");
    tft.print(pos/3,DEC);                                  //Печатаем позицию настройки параметра (замена blink)
    tft.print(" ->");  
    tft.setCursor(45,55);                                  //Выводим инфу о часах и минутах
     if (setTemptIn<10) tft.print("0");
    tft.print(setTemptIn,DEC);
    tft.print("  : ");
    tft.print(setTemptGist,DEC);
    tft.print("  : ");
    tft.print(setTemptInOnOff,DEC);  
    
    tft.setCursor(pos,0);                                   //Устанавливаем курсор согласно позиции

    if (val_X>923 && pos==3){                                //Выход из подменю без сохранения       
      tft.fillScreen(TFT_BLACK);                          //Clear screen
    return;} 

    if (pos<3) pos=3;
    if (val_X<123 && pos<8) pos += 3;                       //Крутим позицию право-лево
    else if (val_X>923 && pos>3) pos -= 3;

    else if (pos==3 && val_Y>1020) setTemptIn++;            //Крутим значения
    else if (pos==3 && val_Y<10) setTemptIn--;
    else if (pos==6 && val_Y>1020) setTemptGist++;
    else if (pos==6 && val_Y<10) setTemptGist--;    
    else if (pos==9 && val_Y>1020) setTemptInOnOff++;
    else if (pos==9 && val_Y<10) setTemptInOnOff--;  
    
    if (setTemptIn>50) setTemptIn=0;                        //Ограничиваем значения
    if (setTemptGist>9) setTemptGist=0;
    if (setTemptInOnOff>1) setTemptInOnOff=0;

    if (butLongState==1){                                    //Выход из меню с проверкой и установкой блокировки от возврата
    tft.fillScreen(TFT_BLACK);                            //Clear screen
    break;} 
}                                                            //Конец цикла
   tft.fillScreen(TFT_BLACK);                             //Clear screen
   EEPROM.write(winMe*4+1, setTemptIn);                      //Записываем НОВЫЕ значения
   EEPROM.write(winMe*4+2, setTemptGist); 
   EEPROM.write(winMe*4+3, setTemptInOnOff);
     
   tft.setCursor(25,105);
   tft.setTextColor(TFT_WHITE,TFT_BLACK);
   tft.setTextSize(1);
   tft.print("== Saved =="); tone(BeepPin, 3040, 500);
   delay(zadTime[4]);
}

//=========== Обработка Меню, параметры и настройка ВольтКонтроля (замена батареи)
void setVoltControl(){
  byte pos=0;   
  setVoltCt=          EEPROM.read(winMe*4+1);              //Считываем записанные значения 
  setVoltCtOnOff=     EEPROM.read(winMe*4+2);              //Адрес определяется как номер *4 + четыре ячейки
     tft.fillScreen(TFT_RED);                          //Clear screen
  while(1){                                              //Бесконечный цикл
//    while(buttonState==LOW){
      butLongState=0;                                    //Обнуляем флаг longClick
      buttonPS();
      digitalWrite(pushLED,HIGH);
    if (millis()-prvMlsTimer>zadTime[7]){                 //Проверяем интервал для обновления 
     prvMlsTimer=millis();
      tft.setTextSize(1);
    tft.setCursor(40,40);
    tft.setTextColor(TFT_YELLOW,TFT_RED);
    tft.print("volt  On/Off"); 
    tft.setCursor(5,150);
    tft.setTextColor(TFT_GREEN, TFT_RED);
    tft.print("=== push to save ===");}
    tft.setCursor(5,55);
    tft.print(winMe+1,DEC);                                //Печатаем номер программы 
    tft.print("|");
    tft.print(pos/3,DEC);                                  //Печатаем позицию настройки параметра (замена blink)
    tft.print(" ->");  
    tft.setCursor(45,55);                                  //Выводим инфу о часах и минутах
     if (setVoltCt<10) tft.print("0");
    tft.print(setVoltCt,DEC);
    tft.print("  :  ");
    tft.print(setVoltCtOnOff,DEC);
    
    tft.setCursor(pos,0);                                   //Устанавливаем курсор согласно позиции

    if (val_X>923 && pos==3){                                //Выход из подменю без сохранения       
      tft.fillScreen(TFT_BLACK);                          //Clear screen
    return;} 

    if (pos<3) pos=3;
    if (val_X<123 && pos<5) pos += 3;                       //Крутим позицию право-лево
    else if (val_X>923 && pos>3) pos -= 3;

    else if (pos==3 && val_Y>1020) setVoltCt++;            //Крутим значения
    else if (pos==3 && val_Y<10) setVoltCt--;
    else if (pos==6 && val_Y>1020) setVoltCtOnOff++;
    else if (pos==6 && val_Y<10) setVoltCtOnOff--;    
    
    if (setVoltCt>15) setVoltCt=7;                        //Ограничиваем значения
    else if (setVoltCt<7) setVoltCt=15;
    if (setVoltCtOnOff>1) setVoltCtOnOff=0;

    if (butLongState==1){                                    //Выход из меню с проверкой и установкой блокировки от возврата
    tft.fillScreen(TFT_BLACK);                            //Clear screen
    break;} 
}                                                            //Конец цикла
   tft.fillScreen(TFT_BLACK);                             //Clear screen
   EEPROM.write(winMe*4+1, setVoltCt);                      //Записываем НОВЫЕ значения
   EEPROM.write(winMe*4+2, setVoltCtOnOff); 
     
   tft.setCursor(25,105);
   tft.setTextColor(TFT_WHITE,TFT_BLACK);
   tft.setTextSize(1);
   tft.print("== Saved =="); tone(BeepPin, 3040, 500);
   delay(zadTime[4]);
}

//=========== Обработка Меню, параметры установки времени
void setClock(){
  byte pos=0;
    tft.fillScreen(TFT_RED);                            //Clear screen
  while(1){                                              //Бесконечный цикл
//    while(buttonState==LOW){
      butLongState=0;                                    //Обнуляем флаг longClick
      buttonPS();
      digitalWrite(pushLED,HIGH);
    if (millis()-prvMlsTimer>zadTime[7]){                 //Проверяем интервал для обновления 
     prvMlsTimer=millis();
      tft.setTextSize(1);
    tft.setCursor(5,150);
    tft.setTextColor(TFT_GREEN, TFT_RED);
    tft.print("=== push to save ===");}
    tft.setCursor(5,55);
    tft.print(winMe+1,DEC);                                //Печатаем номер программы 
    tft.print("|");
    tft.print(pos/3,DEC);                                  //Печатаем позицию настройки параметра (замена blink)
    tft.print(" ->");  
    tft.setCursor(45,55); 
     if (hours<10) tft.print("0");
    tft.print(hours);
    tft.print(":");
     if (minutes<10) tft.print("0"); 
    tft.print(minutes);
      tft.print(":");
     if (seconds<10) tft.print("0"); 
    tft.print(seconds);  
    tft.setCursor(5,75);
      if (day<10) tft.print("0");
    tft.print(day);
      tft.print(" | ");
      if (date<10) tft.print("0");
    tft.print(date);
    tft.print("/");
     if (month<10) tft.print("0");
    tft.print(month);
    tft.print("/");
     if (year<10) tft.print("0");
    tft.print(year);
    tft.setCursor(pos,0);
    
    if (val_X>923 && pos==3){                                //Выход из подменю без сохранения       
      tft.fillScreen(TFT_BLACK);                          //Clear screen
    return;}
    
    if (pos<3) pos=3;
    if (val_X<123 && pos<21) pos += 3;                       //Крутим позицию право-лево
    else if (val_X>923 && pos>3) pos -= 3;
    
    else if (pos==3 && val_Y>1020) hours++;                   //Крутим значения
    else if (pos==3 && val_Y<10) hours--;
    else if (pos==6 && val_Y>1020) minutes++;
    else if (pos==6 && val_Y<10) minutes--;
    else if (pos==9 && val_Y>1020) seconds++;
    else if (pos==9 && val_Y<10) seconds--;
    else if (pos==12 && val_Y>1020) day++;
    else if (pos==12 && val_Y<10) day--;     
    else if (pos==15 && val_Y>1020) date++;
    else if (pos==15 && val_Y<10) date--;    
    else if (pos==18 && val_Y>1020) month++;
    else if (pos==18 && val_Y<10) month--;    
    else if (pos==21 && val_Y>1020) year++;
    else if (pos==21 && val_Y<10) year--;  
    
    if (hours>23) hours=0;
    else if (minutes>59) minutes=0;
    else if (seconds>59) seconds=0;
    else if (day>7) day=1;
    else if (date>31) date=1;
    else if (month>12) month=1;
    else if (year>99) year=0;
        
    if (butLongState==1){                                    //Выход из меню с проверкой и установкой блокировки от возврата
    tft.fillScreen(TFT_BLACK);                            //Clear screen
    break;}
}
   tft.fillScreen(TFT_BLACK);   
   setRtc(seconds, minutes, hours, day, date, month, year); 
   tft.setCursor(25,105);
   tft.setTextColor(TFT_WHITE,TFT_BLACK);
   tft.setTextSize(1);
   tft.print("== Saved =="); tone(BeepPin, 3040, 500);
   delay(zadTime[4]);
}

//========== Обработка таймера включения датчика Влажности почвы, измерение и автополив
void irrigation(){
  if (EEPROM.read(12)==1){                      //Если установен флаг необходимости проверки датчика влажности почвы (в меню)
  if (seconds<EEPROM.read(10)){                 //Сверяем значение текущего времени с интервалом вклчюения датчика
    digitalWrite(OUT0,LOW); tIrrig=1;}          //Включаем реле датчика измерения влажности почвы, поднимаем флаг интервала
  else if (seconds>EEPROM.read(10)){            //Ежели время непришло, реле разомкнуто и некорродирует :) флаг низкий
    digitalWrite(OUT0,HIGH); tIrrig=0;}

//========== Считывание Влажности почвы
  if (tIrrig==1){                                        //Если датчик влажности включен
  if (millis()-prvMlsIrrig>zadTime[1]){                  //Проверяем интервал для обновления 
     prvMlsIrrig=millis();
  moistTmp=analogRead(moistIn);                          //Считываем показатели влажности почвы
  moistAO=map(moistTmp,0,1023,99.9,0);}                  //Преобразуя в процентное соотношение :)
  }
}
//========== Обработка помпы автополива
    if (IrrigStat==1){                                    //Если таймер необходимости автополива в меню (день) задан и
    if (moistAO<EEPROM.read(9)&&tIrrig==0){autoIrrig=1;}  //Если показания влажности почвы МЕНЬШЕ сохраненных 
//  tone(BeepPin, 9040, 100);                             //И датчик влажности отключен, поднимаем флаг необходимости полива
    else if (moistAO>EEPROM.read(9)){autoIrrig=0;}        //Ежели показания ВЫШЕ то ничего неделаем и флаг держим низким
    
    if (autoIrrig==1){                                    //Если флажок необходимости автополива поднят
  digitalWrite(OUT1,LOW); //tone(BeepPin, 5040, 800);     //Включаем помпочку на время из заданного в меню и попискиваем
      if (millis()-prvMlsIrrig>EEPROM.read(11)*1000){     //Проверяем интервал для обновления 
     prvMlsIrrig=millis();
     autoIrrig=0; moistAO=EEPROM.read(9);}}
    if (autoIrrig==0){
  digitalWrite(OUT1,HIGH);}
  }
}

//========== Считывание Влажности воздуха
void humiDity(){
  hmdt=(dht.getHumidity()+11);                           //Reading temperature or humidity takes about 250 milliseconds!
}

//========== Считывание напряжения АКБ
void voltM(){
  valueVo=analogRead(voltInput);                         //Само собой считываем Аналоговый вход (с делителя)
    vout=(valueVo*5.00)/1023.0;                          //Делим именно на 1023, а 5.00 - это опроное "стабилизированное" питание
    vin=vout/0.284983;                                   //vin=vout/(R2/(R1+R2));
    if (vin<0.09) {vin=0.0;}                             //Округляем с высокой точностью
    if (EEPROM.read(18)==1){                             //Если установен флаг необходимости контроля напряжения батареи (в меню)
    if (vin<EEPROM.read(17)){
      bLinkLed();
    tone(BeepPin, 3040, 100);}                           //Пищщим как угорелые и просим заменить батарейку!!!
  }
//    Serial.println(vin);  
}

//=========== Считывание температур
void dallas(){
  ds.reset();
  ds.write(0xCC);                                      //Команда инициации  
  ds.write(0x44);                                      //Start conversion, with parasite power on at the end
    tzad=millis()+750; flagDallas=1;}
  float DS18B20(byte *adres){
  ds.reset();
  ds.select(adres);
  ds.write(0xBE);                                      //Read Scratchpad
    for (byte i=0; i<9; i++) data[i]=ds.read();        //We need 9 bytes
    int raw=(data[1]<<8) | data[0];                    //Переводим в температуру   
    float celsius=(float)raw/16.0;                     //Для ds18b20 делим на "16", для ds18s20 на "2"
    return celsius;
}

//=========== Обработка Термо-Реле
void tempDallas(){
      if (tzad<millis()&&flagDallas==1){  
    temp[0]=DS18B20(addr1);
    temp[1]=DS18B20(addr2);
    prvMlsTemp=millis();
      flagDallas=0;}
//  if (TempStat==1){                                          //Если таймер необходимости вентиляции в меню (день) задан и
//      if (temp[1]>EEPROM.read(13)-(EEPROM.read(14)/2)){      //Если температура в тепличке выше сохранённой в ЕПРОМ и гистерезис пополам
//    digitalWrite(OUT2,LOW);}                                 //Реле вентиляции включается
//      else if (temp[1]<EEPROM.read(13)+(EEPROM.read(14)/2)){ //Если температура в тепличке ниже сохранённой в ЕПРОМ и гистерезис пополам
//    digitalWrite(OUT2,HIGH);}                                //Реле вентиляции включается
//  }
}

//=========== Обработка установки RTC часов
void setRtc(byte seconds, byte minutes, byte hours, byte day, byte date, byte month, byte year){
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0x00);
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.write(decToBcd(day));
  Wire.write(decToBcd(date));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

//=========== Обработка RTC часов
void timeRtc(){
  Wire.beginTransmission(DS1307_I2C_ADDRESS);       //104 is DS3231 device address
  Wire.write(0x00);                                 //Start at register 0
  Wire.endTransmission();
  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);          //Request seven bytes
      if(Wire.available()) { 
    seconds = Wire.read();                          //Get second
    minutes = Wire.read();                          //Get minute
    hours   = Wire.read();                          //Get hour
    day     = Wire.read();
    date    = Wire.read();
    month   = Wire.read();                          //Get month
    year    = Wire.read();
       
    seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111));   //Convert BCD to decimal
    minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111));
    hours   = (((hours & B00110000)>>4)*10 + (hours & B00001111));       //Convert BCD to decimal (assume 24 hour mode)
    day     = (day & B00000111);                                         // 1-7
    date    = (((date & B00110000)>>4)*10 + (date & B00001111));         //Convert BCD to decimal  1-31
    month   = (((month & B00010000)>>4)*10 + (month & B00001111));       //msb7 is century overflow
    year    = (((year & B11110000)>>4)*10 + (year & B00001111));
  }
}

//=========== Обработка Таймеров
void timer(){
  int fulMin=hours*60+minutes;                                 //Переводим часы + минуты к полным минутам
  int fulMinOn1=    EEPROM.read(1)*60+EEPROM.read(2);          //Переводим часы + минуты включения Soil реле к полным минутам
  int fulMinOff1=   EEPROM.read(3)*60+EEPROM.read(4);          //Переводим часы + минуты выключения Soil реле к полным минутам
  int fulMinOn2=    EEPROM.read(5)*60+EEPROM.read(6);          //Переводим часы + минуты включения Temp реле к полным минутам
  int fulMinOff2=   EEPROM.read(7)*60+EEPROM.read(8);          //Переводим часы + минуты выключения Temp реле к полным минутам
 
 //========== Таймер №1 (автополивалочка)        
    if (fulMinOn1>fulMinOff1) {isNight=1;}                   //Если ночное время
    if (isNight==0) {                                        //Если день
    if (fulMin>=fulMinOn1&&fulMin<fulMinOff1) {isIrrigt=1;}  //Проверяем интервал
   else {isIrrigt=0;}                                        //Если необходимо включить автополив
  } else {                                                   //Если ночь
      if(fulMin-fulMinOn1>=0) {isIrrigt=1;}                  //Если больше или равно верхнему значению, то необходимо включить автополив
   else {                                                    //Если необходимо включить автополив
      if(fulMin<fulMinOff1) {isIrrigt=1;}                    //Если меньше нижнего значения, то необходимо включить автополив
   else {isIrrigt=0;}                                        //Если необходимо включить автополив
    }
  }  
    if((isIrrigt==1)&&(IrrigStat==0)){                       //Если автополив еще не включен и выставлен флаг необходимости включить
      {IrrigStat=1;}
  } else {
      if(isIrrigt==0&&IrrigStat==1){
      {IrrigStat=0;}
  }
}

//========== Таймер №2 (вентиялция парничковых газов)
      if (fulMinOn2>fulMinOff2) {isNight=1;}                //Если ночное время
    if (isNight==0){                                        //Если день
    if (fulMin>=fulMinOn2&&fulMin<fulMinOff2) {isTemp=1;}  //Проверяем интервал
   else {isTemp=0;}                                        //Если необходимо включить вентиляцию
  } else {                                                  //Если ночь
      if(fulMin-fulMinOn2>=0) {isTemp=1;}                  //Если больше или равно верхнему значению, то необходимо включить вентиляцию
   else {                                                   //Если необходимо включить вентиляцию
      if(fulMin<fulMinOff2) {isTemp=1;}                    //Если меньше нижнего значения, то необходимо включить вентиляцию
   else {isTemp=0;}                                        //Если необходимо включить вентиляцию
    }
  }  
     if((isTemp==1)&&(TempStat==0)){                      //Если вентиляция еще не включена и выставлен флаг необходимости включить
        {TempStat=1;}
  } else {
      if(isTemp==0&&TempStat==1){
        {TempStat=0;}
    }
  }
}

//=========== Обработка Аврийного индикатора
void bLinkLed(){
  if (millis()-prvMlsbLink>zadTime[1]){               //Проверяем интервал для обновления 
    prvMlsbLink=millis();                             //Установка задержки
      digitalWrite(pushLED,!digitalRead(pushLED));}   //Инверсия значения
}

//=========== Обработки печати и вывода на дисплейчик (часы)
void printTime() {
      if (millis()-prvMlsTm>zadTime[3]){                     //Проверяем интервал для обновления часов
      prvMlsTm=millis();                                     //Вызываем ф-цию вывода времени на экран
    tft.setTextSize(2);
      tft.setCursor(15,2);
    tft.setTextColor(TFT_WHITE,TFT_BLACK);
        if (hours<10) {tft.print(0);tft.print(hours);} else {tft.print(hours);} 
    tft.print(":"); 
      if (minutes<10) {tft.print(0);tft.print(minutes);} else {tft.print(minutes);} 
    tft.print(":"); 
      if (seconds<10) {tft.print(0);tft.print(seconds);} else {tft.print(seconds);}
  }
}

//=========== Обработки печати и вывода на дисплейчик (напряжения, температуры, давление)
void printDin(){
      if (millis()-prvMlsVo>zadTime[6]){                     //Проверяем интервал для обновления показателй динамичных
    prvMlsVo=millis();
    tft.setTextSize(2);
      tft.setCursor(0,40);
    tft.setTextColor(TFT_YELLOW, TFT_BLACK);
    tft.print(temp[0],1);
      tft.setTextSize(3);
    tft.setCursor(55,40);
    tft.setTextColor(TFT_GREEN, TFT_BLACK);
    tft.print(temp[1],1);
    tft.setTextColor(TFT_YELLOW, TFT_BLACK);
      tft.setCursor(55,70);
    tft.print(hmdt,1);
    tft.setTextColor(TFT_RED, TFT_BLACK);
      tft.setCursor(55,100);
    tft.print((moistAO*1.001),1);
    tft.setTextColor(TFT_BLUE, TFT_BLACK);
      tft.setCursor(55,130);
    tft.print(vin,1);
  } 
}

//=========== Обработки печати и вывода на дисплейчик СТАТИКИ
void printStat(){
        if (millis()-prvMlsTimer>zadTime[8]){                 //Проверяем интервал для обновления 
     prvMlsTimer=millis();
    tft.setTextSize(1);
//      tft.setCursor(0,45);
//    tft.setTextColor(TFT_YELLOW);
//    tft.print("Temp C: ");
      tft.setCursor(0,75);
    tft.setTextColor(TFT_YELLOW);
    tft.print("Atmo %: ");
      tft.setCursor(0,105);
    tft.setTextColor(TFT_YELLOW);
    tft.print("Soil %: ");
      tft.setCursor(0,135);
    tft.setTextColor(TFT_YELLOW);
    tft.print("Volt V: ");
    tft.drawLine(0,35, tft.width()-1, 35, TFT_WHITE);
    tft.drawLine(0,65, tft.width()-1, 65, TFT_WHITE);
    tft.drawLine(0,95, tft.width()-1, 95, TFT_WHITE);
    tft.drawLine(0,125, tft.width()-1, 125, TFT_WHITE); 
    tft.drawLine(0,155, tft.width()-1, 155, TFT_WHITE);    
    tft.setTextColor(TFT_YELLOW, TFT_BLACK);
      tft.setTextSize(1);
      tft.setCursor(15,22);
    tft.setTextColor(TFT_GREEN,TFT_BLACK);
    if (date<10) {tft.print(0);tft.print(date);} else {tft.print(date);} 
    tft.print("/");
    if (month==1){tft.print ("Jan");} 
    if (month==2){tft.print ("Feb");} 
    if (month==3){tft.print ("Mar");} 
    if (month==4){tft.print ("Apr");} 
    if (month==5){tft.print ("May");} 
    if (month==6){tft.print ("Jun");} 
    if (month==7){tft.print ("Jul");} 
    if (month==8){tft.print ("Aug");} 
    if (month==9){tft.print ("Sep");} 
    if (month==10){tft.print ("Oct");} 
    if (month==11){tft.print ("Nov");} 
    if (month==12){tft.print ("Dec");} 
    tft.print("/"); 
    tft.print(year+2000); 
    if (day==1){tft.print (" Sun.");} 
    if (day==2){tft.print (" Mon.");} 
    if (day==3){tft.print (" Tue.");} 
    if (day==4){tft.print (" Wed.");} 
    if (day==5){tft.print (" Thu.");} 
    if (day==6){tft.print (" Fri.");} 
    if (day==7){tft.print (" Sat.");}
  }
}

void loop(){
    timeRtc();
    timer();
    buttonPS();
    setUp();
      if (millis()-prvMlsTemp>zadTime[6]&&flagDallas!=1){dallas();}
    tempDallas();
    irrigation();
    voltM();
    humiDity();
    printTime();
    printDin();
    printStat();
}