/*Программа написана Федоровым Александром HOME LABORATORY ©
   https://fedoroffalexander.wixsite.com/smart,
  Написано для Антона Кузнецова. Июнь 2022.
  Программа написана на основе свободно распространяемых в Интернет
  библиотек и кода. Распространяется свободно для частного и коммерческого
  испоьзования. Код не гарантирует надежной работы устройств управления.
  Ответственность за использование этого кода лежит на его пользователе.
*/
/*Раскоментировать для Энкодера
#define pin_CLK 16
#define pin_DT  17
#define pin_Btn 32
*/
#include "Wire.h"
#include <LiquidCrystal_I2C.h> //https://wiki.iarduino.ru/page/working_with_character_lcd_displays
#include "time.h"
#include <TaskScheduler.h> //https://github.com/arkhipenko/TaskScheduler
#include "RTClib.h"
#include <Preferences.h> //http://developer.alexanderklimov.ru/arduino/esp32/preferences.php
#include <SD.h> //https://www.mischianti.org/2021/03/28/how-to-use-sd-card-with-esp32-2/
#include "SdFat.h"
/*Раскоментировать для Энкодера #include "GyverEncoder.h"
unsigned long CurrentTime, LastTime;
  enum eEncoderState {eNone, eLeft, eRight, eButton};
  uint8_t EncoderA, EncoderB, EncoderAPrev;
  int8_t counter;
  bool ButtonPrev;
*/
#define SPI_SPEED SD_SCK_MHZ(4)
#define CS_PIN 5
#define UP 1
#define DOWN 2
#define ENTER 3
#define ERASESCREEN true
#define NOERASESCREEN false
#define OffUpDown 3
#define VERT_PIN 2 //дойстик
#define SEL_PIN  15 //дойстик
#define IN_MOTOR_STOP 27
#define IN_MOTOR_UP 13
#define IN_MOTOR_DOWN 33
#define OUT_MOTOR_STOP 26
#define OUT_MOTOR_UP 25
#define OUT_MOTOR_DOWN 12
//**********РАЗОБРАТЬ
#define IN_AMPER 35 //Аналог. Ток с сдатчика VP
#define OUT_PUSH 5 //Режим подъема
#define IN_HEGH 34 //высота подъема. 1 тик в минуту
int activScreen = 0;
int menuPositionMain = 1; int menuPositionMain2 = 1; int menuPositionManual = 0;
int menuPositionSettings = 0; int menuPositionDigitalSettings = 0;
int selectColor = 0;
String ManualOnOff = "OFF"; String ManualUPOnOff = "OFF"; 
String AutoOnOff = "OFF";
bool ClickManualUPOnOff = false; bool ClickManualDOWNOnOff = false;
bool ClickSettingsOnOff = false; bool ClickManualModes = false;
int ManualModes=0;
String ManualDOWNOnOff = "OFF";  bool ClickAutoOnOff = false;
bool edit = false;
int counter=0;
String Time = ""; String Date = "";
float power = 2.00; //Потребляемая мощность
String dimensionPower = ""; //Единицы измерения мощности
int vp = 0; //Управляющие сигналы с датчиков ограничения движения 0/1
int pr = 2; //Управляющие сигналы с датчиков ограничения движения 0/1
int heigh; //Высота подъема/спуска
String VP; String PR; String H; String MODES;
int upMax=0; int downMax=10; //Максимальные точки подъема\спуска
int depthUpMax, depthDownMax; //Глубина подъема\спуска
float depth; //Текущая глубина
int _cycle = 0;
int cycle = 3; //Количество подъемов\спусков
int pauseCycle = 2; //пауза между циклами
int dd=11; int mm=22; int gg=33; int hh=44; int mnt=55; //Время дата
volatile unsigned long redrowSkreen = 0;
int positionDigital = 1;
int positionCursor = 2;
int tenPosition, hundredPosition, onePosition;
int positionMin=13; int positionMax=18; 
int positionCycl=15; int positionPause=19;
int positionDD=6; int positionMM=12; int positionG=19;
int positionHH=6; int position_MM=12;
float AcsValue=0.0,Samples=0.0,AvgAcs=0.0,AcsValueF=0.0;
bool cycleOK=false;
String modes[4] = {"MANUAL   ", "HALF-AUTO", "AUTO     ", "PUSH     "};
LiquidCrystal_I2C lcd(0x27, 20, 4);
RTC_DS1307 rtc;
DateTime now;
SdFat sd;
File root;
Preferences preferences;
//https://cplusplus.com/reference/ctime/tm/
//Ссылка на структуру системного времени. Обязательно нужно выставлять вручную
//Или если использовать Wi Fi, автоматическая синхронизация с NTP при старте один раз
//struct tm timeinfo; 
Scheduler userScheduler; //Управление задачами по таймеру
//Раз в минуту вызываем getTime(). Получаем системное время
void getTime(); //Декларируем функцию обработки
void drowScreen(); //Декларируем функцию разрешения перерисовки экрана
void printDirectory(File dir, int numTabs); //Декларируем функцию чтения директории SD
void saveSettings();
void readSettings();
Task taskTime( TASK_SECOND * 60 , TASK_FOREVER, &getTime );
Task taskDrow( TASK_SECOND * 1 , TASK_FOREVER, &drowScreen ); //TASK_MILLISECOND
/*Раскоментировать для энкодер
eEncoderState GetEncoderState() {
  // Считываем состояние энкодера
  eEncoderState Result = eNone;
  CurrentTime = millis();
  
  if (CurrentTime - LastTime >= 5) {
  
    // Считываем не чаще 1 раза в 5 мс для уменьшения ложных срабатываний
    LastTime = CurrentTime;
    if (digitalRead(pin_Btn) == LOW ) 
    {
      if (ButtonPrev) {
        Result = eButton; // Нажата кнопка
        Serial.println("ENTER");
         _switch = eButton;
        ButtonPrev = 0;
      }
    }
    else 
    {
      ButtonPrev = 1;
      EncoderA = digitalRead(pin_DT);
      EncoderB = digitalRead(pin_CLK);
      
      if ((!EncoderA) && (EncoderAPrev))  // Сигнал A изменился с 1 на 0
      { 
        if (EncoderB) {
          Result = eRight;    // B=1 => энкодер вращается по часовой
          _switch = eRight
          Serial.println("DOWN");
        }
        else          {
          Result = eLeft;     // B=0 => энкодер вращается против часовой
           Serial.println("UP");
            _switch = eLeft;
        }
      }
      EncoderAPrev = EncoderA; // запомним текущее состояние
    }
  }
  return Result;
}
*/
void getTime() {
  //Не забыть установить RTC
  now = rtc.now();
  hh=now.hour(); mnt=now.minute(); dd=now.day(); mm=now.month(); gg=now.year();
  Time = String(hh)+":"+String(mnt); 
  Date = String(dd)+"/"+String(mm)+"/"+String(gg);
}
void drowScreen() { 
  redrowSkreen++;
  //Serial.print("."); 
}
void helloScreen(bool erase) {
  if(erase) lcd.clear();
  lcd.setCursor(5, 1); lcd.print("ELEVATOR");
  lcd.setCursor(2, 2); lcd.print("Project V 1.0.0");
  activScreen = 0;
}
void manualScreen(bool erase, int UpDown) {
  //Очистить если true
  if(erase) {
    lcd.clear();
    menuPositionManual = 0;
  }
  if(UpDown == UP) {
    menuPositionManual--;
    if(menuPositionManual == 2) menuPositionManual=1;
  }
  if(UpDown == DOWN) {
    menuPositionManual++; 
    if(menuPositionManual == 2) menuPositionManual=3;
  } 
  
  lcd.setCursor(2, 0); lcd.print("UP");
  lcd.setCursor(7, 0); lcd.print(ManualUPOnOff);
  //lcd.setCursor(10, 0); lcd.print(Date);
  lcd.setCursor(2, 1); lcd.print("DOWN");
  //lcd.setCursor(11, 1); lcd.print(Time);
  lcd.setCursor(7, 1); lcd.print(ManualDOWNOnOff);
  lcd.setCursor(16, 0); lcd.print(VP);
  lcd.setCursor(16, 1); lcd.print(PR);
  lcd.setCursor(2, 2); lcd.print(H);
  lcd.setCursor(0, 3); lcd.print(dimensionPower);
  lcd.setCursor(11, 3); lcd.write(255); lcd.write(255);lcd.write(255);lcd.write(255);lcd.write(255);lcd.write(255);lcd.write(255);lcd.write(255);lcd.write(255);
  lcd.setCursor(11, 3); lcd.print(MODES);
     
  if(UpDown == DOWN) {
    
    if(menuPositionManual == 3) lcd.setCursor(0, 1);
    if(menuPositionManual == 0 or menuPositionManual == 1) lcd.setCursor(0, menuPositionManual-1);
    lcd.print(" ");
    if(menuPositionManual > 3) {
      activScreen = 4;
      settingsScreen(ERASESCREEN, OffUpDown); 
      return;
    }
  }
  if(UpDown == UP) {
    Serial.println(menuPositionManual);
    if(menuPositionManual == 1) { lcd.setCursor(9, 3); lcd.print(" "); }
    if(menuPositionManual == 0 or menuPositionManual == 1) {lcd.setCursor(0, menuPositionManual+1); lcd.print(" ");}
    
    if(menuPositionManual < 0) {
      activScreen = 4;
      settingsScreen(ERASESCREEN, OffUpDown); 
      return;
    }
  }
  if(menuPositionManual == 0 or menuPositionManual == 1) {
    lcd.setCursor(0, menuPositionManual);
  }
  if(menuPositionManual == 3) lcd.setCursor(9, menuPositionManual);
  
  lcd.print(char(126)); //lcd.print(">");
  activScreen = 3;
}
void settingsScreen(bool erase, int UpDown) {
  //Очистить если true
  if(erase) {
    lcd.clear();
    menuPositionSettings = 0;
  }
  if(UpDown == UP) menuPositionSettings--;
  if(UpDown == DOWN) menuPositionSettings++;  
  lcd.setCursor(2, 0); lcd.print("MinMax="); lcd.setCursor(10, 0); lcd.print(String(upMax)); 
  lcd.setCursor(14, 0); lcd.print("/");
  lcd.setCursor(15, 0); lcd.print(String(downMax));
  lcd.setCursor(2, 1); lcd.print("CyclPause="); lcd.setCursor(13, 1);lcd.print(String(cycle)); 
  lcd.setCursor(16, 1); lcd.print("/");
  lcd.setCursor(17, 1); lcd.print(String(pauseCycle));
  
  lcd.setCursor(2, 2); lcd.print("DD= "); lcd.setCursor(5, 2); lcd.print(String(dd));
  lcd.setCursor(8, 2); lcd.print("MM= "); lcd.setCursor(11, 2); lcd.print(String(mm));
  lcd.setCursor(14, 2); lcd.print("G="); lcd.setCursor(16, 2); lcd.print(String(gg));
  lcd.setCursor(2, 3); lcd.print("HH= "); lcd.setCursor(5, 3); lcd.print(String(hh));
  lcd.setCursor(8, 3); lcd.print("MM=");  lcd.setCursor(11, 3); lcd.print(String(mnt));
 
  //"DOWN"
  if(UpDown == DOWN) {
    lcd.setCursor(0, menuPositionSettings-1);
    lcd.print(" ");
    if(menuPositionSettings > 3) {
      activScreen = 2;
      manualScreen(ERASESCREEN, OffUpDown);
      return;
    }
  }
  if(UpDown == UP) {
    lcd.setCursor(0, menuPositionSettings+1);
    lcd.print(" ");
    if(menuPositionSettings < 0) {
      activScreen = 2;
      manualScreen(ERASESCREEN, OffUpDown);
      return;
    }
  }
  lcd.setCursor(0, menuPositionSettings);
  lcd.print(char(126)); //lcd.print(">");
  activScreen = 4;
}
//Обработка кода "кнопки"
void reciveBtnCode() {
  // Enter-0 up-4095 down-0
  //************Закоментарить до фразы "ЗАКОМЕНТИРОВАТЬ" ЭТО ДЛЯ РАБОТЫ ДЖОЙСТИКА В ЭМУЛЯТОРЕ
  int _case = 0; int _switch = 0;
  _case = analogRead(VERT_PIN);
  if ( _case == 4095) { _switch = UP; Serial.printf("UP %d\n",_case); }
  if ( _case == 0) { _switch = DOWN; Serial.printf("DOWN %d\n",_case); }
  _case = digitalRead(SEL_PIN);
  if ( _case == 0 ) { _switch = ENTER; Serial.printf("ENTER %d\n",_case); }
  //************************************************************"ЗАКОМЕНТИРОВАТЬ"
  
  if ( _switch == 0 ) return; // Закоментировать для Энкодер
  switch ( _switch ) {
    case UP:
      // UP**************
      if(edit == false) {
        if(activScreen == 3) { manualScreen(NOERASESCREEN, UP); break; }
        if(activScreen == 4) { settingsScreen(NOERASESCREEN, UP); break; }
      }
      if(edit == true) {
        if(activScreen == 4) {
          if(menuPositionSettings == 0) { 
            if(menuPositionDigitalSettings==1) { ++upMax; lcd.noBlink();
              if(upMax > 9999) upMax = 0;
              lcd.setCursor(10, 0); lcd.print("    ");  
              settingsScreen(NOERASESCREEN, OffUpDown); lcd.setCursor(10, 0); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==2) { ++downMax; lcd.noBlink();
              if(downMax > 9999) downMax = 0;
              lcd.setCursor(15, 0); lcd.print("    ");  
              settingsScreen(NOERASESCREEN, OffUpDown); lcd.setCursor(15, 0); lcd.blink(); break; 
            } 
          }
          
          if(menuPositionSettings == 1) { 
            if(menuPositionDigitalSettings==1) { ++cycle; lcd.noBlink();
              if(cycle > 9999) cycle = 0;
              lcd.setCursor(13, 1); lcd.print("   ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(13, 1); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==2) { ++pauseCycle; lcd.noBlink();
              if(pauseCycle > 9999) pauseCycle = 0;
              lcd.setCursor(17, 1); lcd.print("   ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(17, 1); lcd.blink(); break; 
            } 
          }
          if(menuPositionSettings == 2) { 
            if(menuPositionDigitalSettings==1) { ++dd; lcd.noBlink();
              if(dd > 31) dd = 1;
              lcd.setCursor(5, 2); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(5, 2); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==2) { ++mm; lcd.noBlink();
              if(mm > 12) mm = 1;
              lcd.setCursor(11, 2); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(11, 2); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==3) { ++gg; lcd.noBlink();
              lcd.setCursor(16, 2); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(16, 2); lcd.blink(); break; 
            }  
          }
          //Не забыть переустановить время в RTC
          if(menuPositionSettings == 3) { 
            if(menuPositionDigitalSettings==1) { ++hh; lcd.noBlink();
              if(hh > 24) hh = 1;
              lcd.setCursor(5, 3); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); lcd.setCursor(5, 3); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==2) { ++mnt; lcd.noBlink();
              if(mnt > 59) mnt = 0;
              lcd.setCursor(11, 3); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); lcd.setCursor(11, 3); lcd.blink(); break; 
            } 
          }
        }
      }
    break;
   
    case ENTER:
      //"ENTER" ВЫБОР
      //Экран MANUAL UP **********************************************
      //**************************************************************
      if(activScreen == 3) {
          if(menuPositionManual == 0) {
            ClickManualUPOnOff = !ClickManualUPOnOff;
            if(ClickManualUPOnOff == true) { 
              ManualUPOnOff = "ON "; ManualDOWNOnOff = "OFF";
              digitalWrite(OUT_MOTOR_UP,HIGH); digitalWrite(OUT_MOTOR_DOWN,LOW);
              manualScreen(NOERASESCREEN, OffUpDown);
              ClickManualDOWNOnOff = !ClickManualDOWNOnOff; break;
            }
            if(ClickManualUPOnOff == false) { 
              ManualUPOnOff = "OFF"; 
              digitalWrite(OUT_MOTOR_UP,LOW);
              manualScreen(NOERASESCREEN, OffUpDown); break;
            }
          }
          
          if(menuPositionManual == 1) {
            ClickManualDOWNOnOff = !ClickManualDOWNOnOff;
            if(ClickManualDOWNOnOff == true) { 
              ManualDOWNOnOff = "ON "; ManualUPOnOff = "OFF"; 
              digitalWrite(OUT_MOTOR_DOWN,HIGH); digitalWrite(OUT_MOTOR_UP,LOW);
              manualScreen(NOERASESCREEN, OffUpDown);
              ClickManualUPOnOff = !ClickManualUPOnOff; break;
            }
            if(ClickManualDOWNOnOff == false) { 
              ManualDOWNOnOff = "OFF"; 
              digitalWrite(OUT_MOTOR_DOWN,LOW);
              manualScreen(NOERASESCREEN, OffUpDown); break;
            }
          }
          if(menuPositionManual == 3) {
            //"MANUAL", "HALF-AUTO", "AUTO", "PUSH"
            ManualModes++;
            if(ManualModes > 4) ManualModes=1;
            if(ManualModes == 1) { MODES=modes[ManualModes-1]; manualScreen(NOERASESCREEN, OffUpDown); break; }
            if(ManualModes == 2) { MODES=modes[ManualModes-1]; manualScreen(NOERASESCREEN, OffUpDown); break; }
            if(ManualModes == 3) { MODES=modes[ManualModes-1]; cycleOK = !cycleOK;AutoOnOff = "ON "; manualScreen(NOERASESCREEN, OffUpDown); break; }
            if(ManualModes == 4) { MODES=modes[ManualModes-1]; manualScreen(NOERASESCREEN, OffUpDown); break; }
          }
        break;
      }
      
      //Экран SETTINGS************************************************** 
      if(activScreen == 4) {
        delay(100);
        //Маркер строчек Экрана SETTINGS menuPositionDigitalSettings
        if(menuPositionSettings == 0) {
          if(menuPositionDigitalSettings == 0) edit = !edit; 
          menuPositionDigitalSettings++;
          //Serial.println(menuPositionDigitalSettings);
          if(edit == true) { 
            if(menuPositionDigitalSettings==1) { lcd.noBlink(); lcd.setCursor(10, 0); lcd.blink(); break; }
            if(menuPositionDigitalSettings==2) { lcd.noBlink(); lcd.setCursor(15, 0); lcd.blink(); break; }
            if(menuPositionDigitalSettings > 2) { edit = !edit; /*menuPositionDigitalSettings=0; lcd.noBlink(); break;*/ }
          }
          if(edit == false) { menuPositionDigitalSettings=0; lcd.noBlink(); saveSettings(); break; }
        }
        if(menuPositionSettings == 1) {
          if(menuPositionDigitalSettings == 0) edit = !edit; 
          menuPositionDigitalSettings++;
          //Serial.println(menuPositionDigitalSettings);
          if(edit == true) { 
            if(menuPositionDigitalSettings==1) { lcd.noBlink(); lcd.setCursor(13, 1); lcd.blink(); break; }
            if(menuPositionDigitalSettings==2) { lcd.noBlink(); lcd.setCursor(17, 1); lcd.blink(); break; }
            if(menuPositionDigitalSettings > 2) { edit = !edit; /*menuPositionDigitalSettings=0; lcd.noBlink(); break;*/ }
          }
          if(edit == false) { menuPositionDigitalSettings=0; lcd.noBlink(); saveSettings(); break; }
        }
        if(menuPositionSettings == 2) {
          if(menuPositionDigitalSettings == 0) edit = !edit; 
          menuPositionDigitalSettings++;
          //Serial.println(menuPositionDigitalSettings);
          if(edit == true) { 
            if(menuPositionDigitalSettings==1) { lcd.noBlink(); lcd.setCursor(5, 2); lcd.blink(); break; }
            if(menuPositionDigitalSettings==2) { lcd.noBlink(); lcd.setCursor(11, 2); lcd.blink(); break; }
            if(menuPositionDigitalSettings==3) { lcd.noBlink(); lcd.setCursor(16, 2); lcd.blink(); break; }
            if(menuPositionDigitalSettings > 3) edit = !edit; 
          }
          if(edit == false) { menuPositionDigitalSettings=0; lcd.noBlink(); saveSettings(); break; }
        }
        
        if(menuPositionSettings == 3) {
          if(menuPositionDigitalSettings == 0) edit = !edit; 
          menuPositionDigitalSettings++;
          //Serial.println(menuPositionDigitalSettings);
          if(edit == true) { 
            if(menuPositionDigitalSettings==1) { lcd.noBlink(); lcd.setCursor(5, 3); lcd.blink(); break; }
            if(menuPositionDigitalSettings==2) { lcd.noBlink(); lcd.setCursor(11, 3); lcd.blink(); break; }
            if(menuPositionDigitalSettings > 2) { edit = !edit; /*menuPositionDigitalSettings=0; lcd.noBlink(); break;*/ }
          }
          if(edit == false) { menuPositionDigitalSettings=0; lcd.noBlink(); saveSettings(); break; }
        }
      }
     
    break;
 
    case DOWN:
      //"MINUS" DOWN
    if(edit == false) {
      if(activScreen == 3) { manualScreen(NOERASESCREEN, DOWN); break;}
      if(activScreen == 4) { settingsScreen(NOERASESCREEN, DOWN); break;}
      
    }
    if(edit == true) {
        if(activScreen == 4) {
          if(menuPositionSettings == 0) { 
            if(menuPositionDigitalSettings==1) { --upMax; lcd.noBlink();
              if(upMax < 0) upMax = 9999;
              lcd.setCursor(10, 0); lcd.print("    ");  
              settingsScreen(NOERASESCREEN, OffUpDown); lcd.setCursor(10, 0); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==2) { --downMax; lcd.noBlink();
              if(downMax < 0) downMax = 9999;
              lcd.setCursor(15, 0); lcd.print("    ");  
              settingsScreen(NOERASESCREEN, OffUpDown); lcd.setCursor(15, 0); lcd.blink(); break; 
            } 
          }
          
          if(menuPositionSettings == 1) { 
            if(menuPositionDigitalSettings==1) { --cycle; lcd.noBlink();
              if(cycle < 0) cycle = 9999;
              lcd.setCursor(13, 1); lcd.print("   ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(13, 1); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==2) { --pauseCycle; lcd.noBlink();
              if(pauseCycle < 0 ) pauseCycle = 9999;
              lcd.setCursor(17, 1); lcd.print("   ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(17, 1); lcd.blink(); break; 
            } 
          }
          if(menuPositionSettings == 2) { 
            if(menuPositionDigitalSettings==1) { --dd; lcd.noBlink();
              if(dd < 1) dd = 31;
              lcd.setCursor(5, 2); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(5, 2); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==2) { --mm; lcd.noBlink();
              if(mm <1) mm = 12;
              lcd.setCursor(11, 2); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(11, 2); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==3) { --gg; lcd.noBlink();
              lcd.setCursor(16, 2); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); 
              lcd.setCursor(16, 2); lcd.blink(); break; 
            }  
          }
          //Не забыть переустановить время в RTC
          if(menuPositionSettings == 3) { 
            if(menuPositionDigitalSettings==1) { --hh; lcd.noBlink();
              if(hh < 1) hh = 24;
              lcd.setCursor(5, 3); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); lcd.setCursor(5, 3); lcd.blink(); break; 
            }
            if(menuPositionDigitalSettings==2) { --mnt; lcd.noBlink();
              if(mnt <0 ) mnt = 59;
              lcd.setCursor(11, 3); lcd.print("  ");  
              settingsScreen(NOERASESCREEN, OffUpDown); lcd.setCursor(11, 3); lcd.blink(); break; 
            } 
          }
        }
      }
    break;
  }
  
}
//******************************************************************************
//Рабочее пространство для разработчика. В код МЕНЮ лазить не надо!!!
//Все переменные автоматически отобразятся в МЕНЮ
//float upMax, downMax; //Максимальные точки подъема\спуска
//int vp, pr; //Управляющие сигналы с датчиков ограничения движения 0/1
//float heigh; //Высота подъема/спуска
//String VP; String PR; String H;
//float depthUpMax, depthDownMax; //Глубина подъема\спуска
//float power; //Потребляемая мощность
//String dimensionPower = "kWh"; //Единицы измерения мощности
//int cycle = 10; //Количество подъемов\спусков
void developer() {
  //Прием данных с пинов********************
  AcsValue = analogRead(IN_AMPER);     //Read current sensor values   
  AcsValue = (2.5 - (AcsValue * (3.3 / 1024.0)) )/0.185;
  dimensionPower = String(AcsValue)+"A  ";
  
  //*****************************************
  //Двигатель ВВЕРХ
  if( digitalRead(IN_MOTOR_UP) == HIGH ) {
    digitalWrite(OUT_MOTOR_UP,HIGH);
    digitalWrite(OUT_MOTOR_DOWN,LOW);
     //menuPositionManual = 0;
    ClickManualUPOnOff != ClickManualUPOnOff; //щелкнуть выключатель
    ManualUPOnOff = "ON "; ManualDOWNOnOff = "OFF"; //Нарисовать выключатели
    if(activScreen == 3) manualScreen(NOERASESCREEN, OffUpDown);
    return;
  }
  //Двигатель ВНИЗ
  if( digitalRead(IN_MOTOR_DOWN) == HIGH ) {
    digitalWrite(OUT_MOTOR_DOWN,HIGH);
    digitalWrite(OUT_MOTOR_UP,LOW);
    //menuPositionManual = 1;
    ClickManualDOWNOnOff != ClickManualDOWNOnOff; //щелкнуть выключатель
    ManualDOWNOnOff = "ON "; ManualUPOnOff = "OFF"; //Нарисовать выключатели
    if(activScreen == 3) manualScreen(NOERASESCREEN, OffUpDown);
    return;
  }
  //Сигналы с Верхнего-Нижнего датчиков и кнопки Стоп*****************
  if( digitalRead(IN_MOTOR_STOP) == HIGH ) {
    digitalWrite(OUT_MOTOR_STOP,HIGH);
    digitalWrite(OUT_MOTOR_UP,LOW);
    digitalWrite(OUT_MOTOR_DOWN,LOW);
    delay(500);
    digitalWrite(OUT_MOTOR_STOP,LOW);
    if ( ManualUPOnOff == "ON ") {
      ClickManualUPOnOff != ClickManualUPOnOff; //щелкнуть выключатель
      ManualUPOnOff = "OFF"; //Нарисовать выключатели
      VP = "VP-"+String("1");
      PR = "PR-"+String("0");
      H = "H-"+String(heigh)+"M  "; 
      if(activScreen == 3) manualScreen(NOERASESCREEN, OffUpDown);
      return;
    }
    if ( ManualDOWNOnOff == "ON ") {
      ClickManualDOWNOnOff != ClickManualDOWNOnOff; //щелкнуть выключатель
      ManualDOWNOnOff = "OFF"; //Нарисовать выключатели
      PR = "PR-"+String("1");
      VP = "VP-"+String("0");
      H = "H-"+String(heigh)+"M  ";
      if(activScreen == 3) manualScreen(NOERASESCREEN, OffUpDown);
      return;
    }
  }
  //*****************************************************************
  //Serial.println(AutoOnOff);
  if(AutoOnOff == "ON ") {
    
    //Число циклов cycle не завершено !cycleOK
    if(cycleOK == true and _cycle < cycle) {
      //Serial.printf("cycleOK=%d _cycle=%d ManualUPOnOff=%s ManualDOWNOnOff=%s\n",cycleOK,_cycle,ManualUPOnOff,ManualDOWNOnOff);
      //Двигаемся ВНИЗ
      if(ManualUPOnOff == "OFF" and heigh < downMax-1) { 
        ManualDOWNOnOff = "ON ";
        /*Serial.println("_DOWN");*/ 
        heigh++;
        manualScreen(NOERASESCREEN, OffUpDown);
      }
      else  { ManualDOWNOnOff = "OFF"; /*AutoOnOff = "OFF"; */ }
      //нижнее положение достигнуто или счетчик heigh
         
      //Двигаемся ВВЕРХ
      if(ManualDOWNOnOff == "OFF" and heigh > upMax) { 
        ManualUPOnOff = "ON ";  
        //Serial.println("_UP");
        heigh--; 
        manualScreen(NOERASESCREEN, OffUpDown);
      }
      //верхнее положение достигнуто или счетчик heigh
      else   ManualDOWNOnOff = "ON ";   
      if(heigh <= 1 and ManualUPOnOff == "OFF") { _cycle++; }
      //Serial.printf("IF cycleOK=%d _cycle=%d ManualUPOnOff=%s ManualDOWNOnOff=%s\n",cycleOK,_cycle,ManualUPOnOff,ManualDOWNOnOff);
      //Посчитать. ЦИКЛы закончились. Все выключить.
      if(_cycle >= cycle) {
        Serial.println("PAUSE");
        taskDrow.enable();
        _cycle=0;
        cycleOK = !cycleOK;
        ManualUPOnOff = "OFF"; ManualDOWNOnOff = "OFF";
        //AutoOnOff = "OFF";
        manualScreen(NOERASESCREEN, OffUpDown);
      }
      
    }
    //Serial.printf("heigh=%d cycleOK=%d _cycle=%d ManualUPOnOff=%s ManualDOWNOnOff=%s\n",cycleOK,_cycle,ManualUPOnOff,ManualDOWNOnOff,heigh);
    //Число циклов завершено cycleOK. Пауза. Включить AUTO в секундах
    /*if( redrowSkreen >= pauseCycle) {
      Serial.println(redrowSkreen);
      redrowSkreen = 0;
      cycleOK = !cycleOK;
      AutoOnOff = "ON ";
      taskDrow.disable();
    }*/
    //manualScreen(NOERASESCREEN, OffUpDown);
  }
  //***************************************************************
  if( ManualUPOnOff == "ON " or  ManualDOWNOnOff == "ON " ) {
    //Формирование строк из данных при обработки логики ON/OFF. Показывать/остановить показ 
    //Здесь обработка логики включения\выключения пинов
    if(digitalRead(IN_HEGH)) {
      if(ManualUPOnOff == "ON ") heigh--;
      if(ManualDOWNOnOff == "ON ") heigh++;
    }
    
    //Проверить. Уперлись в датчик или перекрутили по счетчику
    if(heigh <= upMax or heigh >= downMax or digitalRead(IN_MOTOR_STOP) == HIGH) {
      digitalWrite(OUT_MOTOR_DOWN,LOW);
      digitalWrite(OUT_MOTOR_UP,LOW);
      ClickManualUPOnOff != ClickManualUPOnOff; //щелкнуть выключатель
      ManualUPOnOff = "OFF"; ManualDOWNOnOff = "OFF"; //Нарисовать выключатели
      AutoOnOff = "OFF"; //выключить если включено AUTO 
      if(heigh >= downMax) {
        digitalWrite(OUT_MOTOR_UP,HIGH);
        heigh--;
        //delay(1000); //Время на отъезд от датчика
        digitalWrite(OUT_MOTOR_UP,LOW);
      }
      if(heigh <= upMax) {
        digitalWrite(OUT_MOTOR_DOWN,HIGH);
        heigh++;
        //delay(1000); //Время на отъезд от датчика
        digitalWrite(OUT_MOTOR_DOWN,LOW);
      }
    }
    dimensionPower = String(AcsValue)+"A  ";
    H = "H-"+String(heigh)+"M  ";
    VP = "VP-"+String("0");
    PR = "PR-"+String("0");
    if(activScreen == 3) manualScreen(NOERASESCREEN, OffUpDown); 
    return;
  }
}
//******************************************************************************
void setup() {
  Serial.begin(115200);
  lcd.init(); // This is the I2C LCD object initialization. 
  lcd.backlight(); //  Включаем подсветку LCD дисплея
  //https://microcontrollerslab.com/save-data-esp32-flash-permanently-preferences-library/
  preferences.begin("memory", false); //Инициализация записи в EEPROM
  //preferences.clear();
  //Если ключа нет сохраняем предустановленные параметры
  if(preferences.getUInt("upMax", 0) == 0) saveSettings();
  else  
    readSettings();
  //SD карта. Подключение файловой системы
  /*if (!sd.begin(CS_PIN, SPI_SPEED)) {
    if (sd.card()->errorCode()) {
      Serial.println("SD initialization failed.");
    } else if (sd.vol()->fatType() == 0) {
      Serial.println("Can't find a valid FAT16/FAT32 partition.");
    } else {
      Serial.println("Can't determine error type");
    }
    return;
  }
  Serial.println("Files on card:");
  Serial.println("   Size    Name");
  sd.ls(LS_R | LS_SIZE);*/
  //******************************************************
  //Файл Config
  Serial.print("Initializing SD card... ");
  if (!SD.begin(CS_PIN)) {
    Serial.println("Card initialization failed!");
    while (true);
  }
  Serial.println("initialization done.");
  //Serial.println("Files in the card:");
  //root = SD.open("/");
  //SD.remove("/Config.txt");
  //root.close();
  /*
  root = SD.open("/Config.txt", FILE_WRITE); //Файл не существует. Создать и записать параметры
  //Проверка на готовом файле
  root.println(5); root.println(6); 
  root.println(10); root.println(90);
  if(!root) {
    root.println(upMax); root.println(downMax); 
    root.println(cycle); root.println(pauseCycle);
    //Проверка на вновь созданном файле
    //root.println(5); root.println(6); 
    //root.println(10); root.println(90);
    Serial.println("Creat Config.txt file");
    root.close();
  }
   else
    Serial.println("File Config.txt read...");
  root = SD.open("/Config.txt", FILE_READ);
  if(root) {
    String str = root.readStringUntil('\n'); upMax = str.toInt(); str="";
    str = root.readStringUntil('\n'); cycle = str.toInt(); str="";
    str = root.readStringUntil('\n'); pauseCycle = str.toInt();
    Serial.printf("%d %d %d %d\n",upMax,downMax,cycle,pauseCycle);
    Serial.println("Files on card:");
    Serial.println("   Size    Name");
    root.close();
  }
  else
    Serial.println("No file");
  
  root = SD.open("/");
  printDirectory(root, 0);
  Serial.println("");
  root.close();
  */
  //******************************************************
  if (! rtc.begin()) Serial.println("Couldn't find RTC");
  
  pinMode(VERT_PIN, INPUT); // Enter(0)-2048 up-4095 down-0
  pinMode(SEL_PIN, INPUT_PULLUP); //1-кнопка отпущена 0-нажата
  pinMode(IN_HEGH, INPUT); //высота подема. 1 тик 1 метр
  //digitalWrite(IN_HEGH,LOW);
  pinMode(IN_MOTOR_STOP, INPUT);    digitalWrite(IN_MOTOR_STOP,LOW);
  pinMode(IN_MOTOR_UP, INPUT);      digitalWrite(IN_MOTOR_UP,LOW);
  pinMode(IN_MOTOR_DOWN, INPUT);    digitalWrite(IN_MOTOR_DOWN, LOW);
  pinMode(OUT_MOTOR_STOP, OUTPUT);  digitalWrite(OUT_MOTOR_STOP,LOW);
  pinMode(OUT_MOTOR_UP, OUTPUT);    digitalWrite(OUT_MOTOR_UP,LOW);
  pinMode(OUT_MOTOR_DOWN, OUTPUT);  digitalWrite(OUT_MOTOR_DOWN,LOW);
  helloScreen(ERASESCREEN);
  delay(1000);
  //getLocalTime(&timeinfo);
  //hh=timeinfo.tm_hour; mnt=timeinfo.tm_min;
  now = rtc.now();
  dd=now.day(); mm=now.month(); gg=now.year();
  hh=now.hour(); mnt=now.minute();
  Time = String(hh)+":"+String(mnt); 
  Date = String(dd)+"/"+String(mm)+"/"+String(gg);
  
  userScheduler.init();
  userScheduler.addTask( taskTime );
  userScheduler.addTask( taskDrow );
  
  //mainScreen(ERASESCREEN, OffUpDown);
  //Поднять элеватор наверх. Мотор UP
  heigh = 10; //Для теста. Высота подъема/спуска
  MODES="SETUP    ";
  digitalWrite(OUT_MOTOR_UP,HIGH);
  ClickManualUPOnOff != ClickManualUPOnOff; //щелкнуть выключатель
  ManualUPOnOff = "ON "; ManualDOWNOnOff = "OFF"; //Нарисовать выключатели
  manualScreen(ERASESCREEN, OffUpDown); //нарисовать экран
  //Выключить по счетчику высоты и/или дачику vp (пин 27)
  while(heigh >= upMax and digitalRead(IN_MOTOR_STOP) == LOW) {
    if(digitalRead(IN_HEGH) == HIGH) --heigh;
    H = "H-"+String(heigh)+"M  ";
    vp = digitalRead(IN_MOTOR_STOP); //на один пин заведены СТОП, VP, PR
    VP = "VP-"+String(vp); 
    PR = "PR-"+String("0");
    //https://www.engineersgarage.com/acs712-current-sensor-with-arduino/
    AcsValue = analogRead(IN_AMPER);     //Read current sensor values   
    AcsValue = (2.5 - (AcsValue * (3.3 / 1024.0)) )/0.185;
    dimensionPower = String(AcsValue)+"A  ";
    manualScreen(NOERASESCREEN, OffUpDown); //нарисовать экран 
  }
  digitalWrite(OUT_MOTOR_UP,LOW); //STOP мотор вверх
  //Serial.println(heigh);
  //Проверить. Уперлись в верхний датчик или перекрутили вверх по счетчику
  if(heigh <= 0 or digitalRead(IN_MOTOR_STOP) == HIGH) {
    digitalWrite(OUT_MOTOR_DOWN,HIGH); //Включить мотор DOWN
    //delay(1000); //Время на отъезд от верхнего датчика
    ClickManualUPOnOff != ClickManualUPOnOff; //щелкнуть выключатель
    ManualUPOnOff = "OFF"; ManualDOWNOnOff = "ON "; //Нарисовать выключатели
    if(digitalRead(IN_HEGH) == HIGH) heigh++ ; //pin34
    vp = digitalRead(IN_MOTOR_STOP); //на один пин27 заведены СТОП, VP, PR
    if(vp == 1) heigh = 0; //уперлись в верхний датчик. Сброс счетчика высоты
    VP = "VP-"+String(vp); //36
    PR = "PR-"+String("0"); //39
    H = "H-"+String(heigh)+"M  ";
    digitalWrite(OUT_MOTOR_DOWN,LOW);
    manualScreen(NOERASESCREEN, OffUpDown); //нарисовать экран 
  }
  //Еще раз все выключили
  digitalWrite(OUT_MOTOR_DOWN,LOW);
  digitalWrite(OUT_MOTOR_UP,LOW);
  ClickManualUPOnOff != ClickManualUPOnOff; //щелкнуть выключатель
  ManualUPOnOff = "OFF"; ManualDOWNOnOff = "OFF"; //Нарисовать выключатели
  manualScreen(NOERASESCREEN, OffUpDown); //нарисовать экран 
  taskTime.enable();
  //taskDrow.enable();
  //taskDrow.disable();
 
}
void loop() {
  //redrowSkreen = false;
  //DateTime now = rtc.now();
  //unsigned long m1 = millis();
  //Serial.println(digitalRead(IN_HEGH));
  reciveBtnCode(); //Закоментировать для Энкодер
  //eEncoderState GetEncoderState(); //раскоментировать для Эенкодер
  //if(millis() - m1 != 0) Serial.printf("Btn %d millis\n", millis() - m1);
  //Не трогать, обновления экранов
  
  //redrowSkreen = true; 
  //Serial.println(redrowSkreen);
  /*if ( edit == false and redrowSkreen == true ) {
    //redrowSkreen = false;
    //unsigned long m = millis();
    if(activScreen == 3) manualScreen(NOERASESCREEN, OffUpDown);
    if(activScreen == 4) settingsScreen(NOERASESCREEN, OffUpDown);
    //if(millis() - m != 0) 
    //  Serial.printf("Screen %d millis\n", millis() - m);
    //redrowSkreen != redrowSkreen;
  }*/
  developer();
  userScheduler.execute();
  delay(100); // this speeds up the simulation
}
void printDirectory(File dir, int numTabs) {
  while (true) {
    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}
void saveSettings() {
  preferences.begin("memory", false); 
  preferences.putUInt("upMax", upMax);
  preferences.putUInt("downMax", downMax);
  preferences.putUInt("cycle", cycle);
  preferences.putUInt("pauseCycle", pauseCycle);
  preferences.end();
  Serial.printf("Save Memory %d %d %d %d\n",upMax,downMax,cycle,pauseCycle);
}
void readSettings() {
  preferences.begin("memory", false); 
  upMax=preferences.getUInt("upMax", upMax);
  downMax=preferences.getUInt("downMax", downMax);
  cycle=preferences.getUInt("cycle", cycle);
  pauseCycle=preferences.getUInt("pauseCycle", pauseCycle);
  preferences.end();
  Serial.printf("Read Memory %d %d %d %d\n",upMax,downMax,cycle,pauseCycle);
}