#include <Wire.h>
#include <EEPROM.h>
#include <BleGamepad.h> 
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "esp32-hal-timer.h"
#include <RotaryEncoder.h>

//#include "INA219_WE.h"
//INA219_WE ina219(0x40); 

Adafruit_SSD1306 display(128, 64, &Wire, -1);
BleGamepad bleGamepad("FS G36", "UA", 100);
RotaryEncoder encoder(12, 14, RotaryEncoder::LatchMode::TWO03);

//Profiles 1  
  
  //Profiles
  #define t_sp 1//не міняти число!
  #define t_mp 2//не міняти число!
  #define r_off 0 // = off
  #define r_on 12 // = on
  #define get_name 20
  #define get_name_a_f 21
  #define change_letter 22
  #define delete_letter 23
  
  //*prof()
  #define direct_p_sp 20
  #define direct_p_mp 21
  #define act_dir 22
  #define activate_p_sp 23
  #define activate_p_mp 24
  #define eeprom_initialization 25
  #define rerecording 26// test
  
  uint32_t tmrProf = 0;
  byte defButton[11] = {0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};// test
  
  class Profiles {
    
    public:
      Profiles(int deZoH, byte senHor, int deZoV, byte senVer, char* nameProf, byte tpProf, byte nabProf)
       : deadZoneHor(deZoH),
         sensitivityHor(senHor),
         deadZoneVer(deZoV),
         sensitivityVer(senVer),
         tape(tpProf),
         namber(nabProf),
         status(r_off),
         buttonArray{0, 18, 19, 15, 5, 25, 16, 17, 26, 33, 13, 23, 27} // test
          {     //при глюках замінити на memcpy(buttonArray, defaultButtonArray, sizeof(buttonArray));
         
            strncpy(nameArray, nameProf, 20); //даємо назву профілю через конструктор
            nameArray[20] = '\0';
            
            letterIndicator = [this]() -> byte {//переміна зберігає собі кількість букв в назві потрібно для зміни імен
              
              for (int i = 19; i >= 0; i--) {
                if (this->nameArray[i] != ' ') {
                  return i + 1;
                }
              }
              
              return 20;
              
            }();
         
          }
     
      int setDeZoHor(int deZoH) {//set
       
       if (deadZoneHor != deZoH) {sensitivityHor = 0;}
       deadZoneHor = deZoH; 
       
       return 0;
      } 
      
      byte setSenHor(byte senHor) {
       
       sensitivityHor = senHor;
       
       return 0;
      }
      
      int setDeZoVer(int deZoV) {
       
        if (deadZoneVer != deZoV) {sensitivityVer = 0;}
        deadZoneVer = deZoV;
       
       return 0;  
      }
      
      byte setSenVer(byte senVer) {
       
       sensitivityVer = senVer;
       
       return 0;
      }
      
      byte setStatus(byte st) {
      
      status = st;
      
       return 0;
      }
    
      const char* nameProfiles(byte acceptedCommand, char Letter = ' '){
       
        char baldness[] = "";//baldness ​беззмістовність  
        
        if (status == r_on && tmrProf < millis()){
         
         tmrProf = millis()+600;
         Flag = !Flag;
        
        }
        
        else if (status == r_off && !Flag){
         
         Flag = 1;
         
        }
        
        switch (acceptedCommand) {
        
          case get_name: 
            
            return nameArray;
          
          break;
          
          case get_name_a_f: 
           
           if (Flag) {
            
            return nameArray;
           
           } 
           else { 
            
            return baldness;
           
           }
           
           break;
          
          case change_letter: 
            
            if (letterIndicator != 20){
             
             nameArray[letterIndicator] = Letter;
             letterIndicator++;
            
            }
           
           break;
          
          case delete_letter:
             
            if (letterIndicator){
             
             letterIndicator--;
             nameArray[letterIndicator] = ' ';
             
            }
           
           break;
          
         return baldness;
        
        }
      
       return baldness;
      
      }
      
      
      byte centroName() {
        
        for (int i = 19; i >= 0; i--) {
         
          if (nameArray[i] != ' '){
           
           return 61-i*3;
           break;
          }
        
        }
        
       return 0;
      
      }
      
      byte getNamberTape(){
       
       return tape * 10 +namber;//логіка роботи тип може бути 1 або 2 множимо на 10 щоб не збігалася числа
      
      }
      
      void resetButtons(){
       
        for (int i = 0; i < sizeof(defButton); i++) {
              
         buttonArray[i] = defButton[i];
               
        }
      
      }
      
      void checkName() {//якщо немає імені то вставляє власне ім'я залежно від номеру профіля
        
        bool allSpaces = true; // Припускаємо, що всі символи - пробіли
        
        for(int i = 0; i < 20; i++) { // Останній символ не перевіряємо
            if(nameArray[i] != ' ') {
                allSpaces = false;
                break;
            }
        }

        if(allSpaces) {
            snprintf(nameArray, 21, "My profile %d", namber); // Форматуємо нову назву
        }
      
      }

      void copyFrom(const Profiles& other) {
      
        deadZoneHor = other.deadZoneHor;
        sensitivityHor = other.sensitivityHor;
        deadZoneVer = other.deadZoneVer;
        sensitivityVer = other.sensitivityVer;
        tape = other.tape;
        namber = other.namber;
        status = other.status;
        memcpy(buttonArray, other.buttonArray, sizeof(buttonArray));
        memcpy(nameArray, other.nameArray, sizeof(nameArray)); 
      }

      bool operator!=(const Profiles& other) const {
          
        return (deadZoneHor != other.deadZoneHor) ||
                (sensitivityHor != other.sensitivityHor) ||
                (deadZoneVer != other.deadZoneVer) ||
                (sensitivityVer != other.sensitivityVer) ||
                (tape != other.tape) ||
                (namber != other.namber) ||
                (status != other.status) ||
                (memcmp(buttonArray, other.buttonArray, sizeof(buttonArray)) != 0) ||
                (memcmp(nameArray, other.nameArray, sizeof(nameArray)) != 0); 
      }
      
      // prof(act_dir)->sensitivityHor;
      char nameArray[21];
      byte buttonArray[13]; 
      byte Flag;
      int deadZoneHor;//(primary початкова) початок руху
      byte sensitivityHor;
      int deadZoneVer;
      byte sensitivityVer;
      byte tape;
      byte namber;
      byte status;
      byte letterIndicator;
    
    private:

  };
  
  // Ініціалізуємо статичну змінну класу Profiles
 
  
  Profiles profilSeve(0, 0, 0, 0, "0", 0, 0);
  
  Profiles profilSpOne(2000, 100, 2556, 92, "CodMW", t_sp, 1);
  Profiles profilSpTwo(166, 120, 1965, 112, "Batl3", t_sp, 2);
  Profiles profilSpThree(2000, 100, 2556, 92, "SP 33", t_sp, 3);
  Profiles profilSpFour(166, 120, 1965, 112, "SP 44", t_sp, 4);
  Profiles profilSpFive(2000, 100, 2556, 92, "SP 55", t_sp, 5);
  Profiles profilSpSix(166, 120, 1965, 112, "SP 66", t_sp, 6);
  Profiles profilSpSeven(2000, 100, 2556, 92, "SP 77", t_sp, 7);
  
  Profiles profilMpOne(2000, 100, 2556, 92, "05461               ", t_mp, 1);
  Profiles profilMpTwo(166, 120, 1965, 112, "Xmen   74           ", t_mp, 2);
  Profiles profilMpThree(2000, 100, 2556, 92, "Mp 3                ", t_mp, 3);
  Profiles profilMpFour(166, 120, 1965, 112, "Mp 4                ", t_mp, 4);
  Profiles profilMpFive(2000, 100, 2556, 92, "Mp 5                ", t_mp, 5);
  Profiles profilMpSix(166, 120, 1965, 112, "Mp 6                ", t_mp, 6);
  Profiles profilMpSeven(2000, 100, 2556, 92, "Mp 7                ", t_mp, 7);

  
  Profiles *prof(byte acceptedCommand, byte number = 0){ 
   
    
   Profiles *profArray[] = {&profilSeve, &profilSpOne, &profilSpTwo, &profilSpThree, &profilSpFour, &profilSpFive, &profilSpSix, &profilSpSeven,
                       
   &profilMpOne, &profilMpTwo, &profilMpThree, &profilMpFour, &profilMpFive, &profilMpSix, &profilMpSeven};
   
    static byte acting = []() -> byte {//одноразова інт пер лямбда функція оскільки метод не повертає значення потрібно зробити так
      
      byte temp;
      int tempProfSize = 7 * sizeof(Profiles);
      int tempProfSizebutArr = 7 * sizeof(profArray[1]->buttonArray);
      
      EEPROM.get(7 * sizeof(Profiles) + 1, temp); // Читаємо значення з EEPROM
      
      return temp; // Повертаємо прочитане значення
    
    }();
   
    if (acceptedCommand == act_dir) {//скерування до активного профілю
    
     return profArray[acting];
   
    } 
    
    else if (acceptedCommand == direct_p_sp) {//скеровує до стандартного профіля Для певних настройок
    
     return profArray[number];
   
    }
    
    else if (acceptedCommand == direct_p_mp) {//скеровує до власного профіля Для певних настройок
    
     return profArray[number+7];
   
    }  
   
    else if (acceptedCommand == activate_p_sp || acceptedCommand == activate_p_mp) {//вибраний профіль активується

      for (int i = 1; i < 15; i++){ 
    
       profArray[i]->setStatus(r_off);

      }
  
      if (acceptedCommand == activate_p_sp ) {
       
       profArray[number] -> setStatus(r_on);
       acting = number;
      
      }
      
      else  {
      
       profArray[number+7] -> setStatus(r_on);
       acting = number+7;
      
      }
      
      EEPROM.put(7 * sizeof(Profiles) + 1, acting);
      EEPROM.commit();
      //Serial.println(sizeof(Profiles));
      //Serial.println(EEPROM.get(0, acting));
    } 
    
    else if (acceptedCommand == eeprom_initialization) {
      
      int tempProfSize = 7 * sizeof(Profiles);
      int tempProfSizebutArr = 7 * sizeof(profArray[1]->buttonArray);
      int sizebutArr = sizeof(profArray[1]->buttonArray);
     
     //Serial.println("sizeof(Profiles)");
     //Serial.println(sizeof(Profiles));
 
      if (acting == 255 || Ae_BatR(13)) {// test ( Ae_BatR(13) ) Якщо правда то це самий перший запуск потрібно зберегти в профілі і перемінну енергонезалежну пам'ять подальші збереження будуть виконуватися при виході з настройок якщо вибрати так
  
        acting = 8; 
        
        EEPROM.put(0 * sizeof(Profiles), profilMpOne);
        EEPROM.put(1 * sizeof(Profiles), profilMpTwo);
        EEPROM.put(2 * sizeof(Profiles), profilMpThree);
        EEPROM.put(3 * sizeof(Profiles), profilMpFour);
        EEPROM.put(4 * sizeof(Profiles), profilMpFive);
        EEPROM.put(5 * sizeof(Profiles), profilMpSix);
        EEPROM.put(6 * sizeof(Profiles), profilMpSeven);

        // Зберігаємо buttonArray кожного профілю окремо
        EEPROM.put(tempProfSize + 0 * sizebutArr, profArray[1]->buttonArray);
        EEPROM.put(tempProfSize + 1 * sizebutArr, profArray[2]->buttonArray);
        EEPROM.put(tempProfSize + 2 * sizebutArr, profArray[3]->buttonArray);
        EEPROM.put(tempProfSize + 3 * sizebutArr, profArray[4]->buttonArray);
        EEPROM.put(tempProfSize + 4 * sizebutArr, profArray[5]->buttonArray);
        EEPROM.put(tempProfSize + 5 * sizebutArr, profArray[6]->buttonArray);
        EEPROM.put(tempProfSize + 6 * sizebutArr, profArray[7]->buttonArray);

        // Зберігаємо значення acting після всіх Profiles та їх buttonArray
        EEPROM.put(tempProfSize + tempProfSizebutArr, acting);
        EEPROM.commit();
        
        /*
        for (int i = 0; i <= 6; i++){ //варіант з циклом for можливо буде працювати не в симуляторі
        
          EEPROM.put(i * sizeof(Profiles), profArray[i+8]);
          EEPROM.put(tempProfSize + i * sizebutArr, profArray[i+1]->buttonArray);
          Serial.println(i);
       
        }
       
       EEPROM.put(tempProfSize + tempProfSizebutArr, acting);
       EEPROM.commit();
       */
      }
      
      else{//якщо Дані були вже збережені раніше то повертаємо їх з енергонезалежної пам'яті
        /*
        delay(500);
        for (int i = 0; i <= 6; i++){ 
        
          EEPROM.get(i * sizeof(Profiles), profArray[i+8]);
          EEPROM.get(tempProfSize + i * sizebutArr, profArray[i+1]->buttonArray);
         
        }
        */
        
        EEPROM.get(0 * sizeof(Profiles), profilMpOne);
        EEPROM.get(1 * sizeof(Profiles), profilMpTwo);
        EEPROM.get(2 * sizeof(Profiles), profilMpThree);
        EEPROM.get(3 * sizeof(Profiles), profilMpFour);
        EEPROM.get(4 * sizeof(Profiles), profilMpFive);
        EEPROM.get(5 * sizeof(Profiles), profilMpSix);
        EEPROM.get(6 * sizeof(Profiles), profilMpSeven);
        
        EEPROM.get(tempProfSize + 0 * sizebutArr, profArray[1]->buttonArray);
        EEPROM.get(tempProfSize + 1 * sizebutArr, profArray[2]->buttonArray);
        EEPROM.get(tempProfSize + 2 * sizebutArr, profArray[3]->buttonArray);
        EEPROM.get(tempProfSize + 3 * sizebutArr, profArray[4]->buttonArray);
        EEPROM.get(tempProfSize + 4 * sizebutArr, profArray[5]->buttonArray);
        EEPROM.get(tempProfSize + 5 * sizebutArr, profArray[6]->buttonArray);
        EEPROM.get(tempProfSize + 6 * sizebutArr, profArray[7]->buttonArray);
        
      
      }
      /*
      for (int i = 0; i <= 6; i++){ 
       
       EEPROM.get(i * sizeof(Profiles), *profArray[i+8]);  //*profArray[i+8]???
       Serial.println(i);
      
      }
      */
      
      profArray[acting] -> setStatus(r_on);
 
    }  
  
   return 0;
  }
 
//P

//Additional_elements
  
  bool Vibrgyro;
  // СПІЛЬНІ КОНСТАНТИ ПІД ХОД ДЛЯ БАГ ФУНК
  #define no_action 0 // НЕМА ДІЇ
  #define no_data 0 // немає даних
  #define no_indication 0   // немає вказівки
  #define off 0 
  #define get_status 1               // получити настройки
  #define yes 1
  #define save_settings 4           // зберехти настройки
  #define reset 11
  #define on 12
  #define click 13
  #define execute_code 14// execution without comman - виконання без команди
  #define gyro 15
  #define profil 16
  #define centering 17
  
  #define melody_start 1
  #define melody_off_power 2

  #define x_axis_hor 1
  #define y_axis_ver 2
  #define z_axis_ver 3


  #define get_y 2
  #define get_z 3
  #define get_vibr 4
 
  //Ae_BatR();
  //Піни карта
  //#define b_mein 25
  #define b_emul_dpad 1
  #define b_handle 4
  #define b_triger_r 23
  #define b_analog_l 33
  #define b_on_off 14
  #define b_emul_dpad 26
  #define b_sw 32 // кнопка енко
  #define p_dt 12
  #define p_clk 14
 
  #define thandle_touch_button 27
  
  //Mf_EncoderButton();
  //#define click 13
  #define press_b_e 20
  #define hold_b_e 21
  #define reset_status 23
  #define take_but_pos 24
  #define button_pressed 25
  #define button_released 26
  #define releas_b_e 27
  #define completed_work 28
  #define status_allowed 29
  
  //Md_Display(byte Receiving) {
  
  #define info_display 20
  #define menu 21 
  #define confirm_selection 22 

  #define lang_eng 25
  #define lang_ukr 26 
  #define click_in_dis_on 27

  //Md_MainDisplayENG();
  
  #define menu_off 21
  #define menu_start 22
  
  //byte Md_addres();
  
  //#define click 13
  #define back 20
  #define address_entered 21
  #define get_add 22
  #define int_add 23
  #define add_status 24
  #define int_add_last 25
  #define address_deleted 26
  #define click_reset_off 27
  
  //int Md_cursor();
  //#define reset 11
  #define selection_inversion 21
  #define freeze_position 22
  #define get_pos 23
  #define return_pos 24
  #define cursor_type_one 25
  #define cursor_type_two 26
 
  byte noData[1];
  byte posOne[] = {1};
  byte posTwo[] = {2};
  byte posThree[] = {3};
  byte posFour[] = {4};
  byte posFive[] = {5};
  byte posSix[] = {6};
  byte posEight[] = {7};
  
  //Mf_encoderRotationSpeed()
  //Ae_encoderInterrupt()
  #define rotated_r1 1
  #define rotated_l1 -1
  #define rotated_r2 2
  #define rotated_l2 -2
  #define check_rotation 20
  #define get_rotation1 21
  #define get_rotation2 22
  volatile uint32_t tmr;
  volatile int8_t seveEncPos;
  
  //MdAf_confirmYesNo()
  //MdAf_consenDisplay()
  #define choice_yes 20
  #define choice_no 21
  
  
  //MdAf_profileStorage()
  #define copy_profile 20
  #define return_save 21
  #define check_changes 22
  #define recorded_changes 23//зафіксовані зміни
  
  //Mf_Buttons()
  //#define reset 11
  #define next_animation 20
  
  //Ae_displayRefresh()
  //#define reset 11
  #define clear_display 20
  #define print_display 21
  
  // прототипи
  //int8_t Md_addres(int8_t addNumb = 0, int8_t acceptedCommand = 0, int8_t acceptedData = 0);
  byte Md_p_Buttons(byte receivedData = 0, byte selectedButton = 0);
  int Md_movingButtons(int acceptedCommand = 0);  
  byte Mf_EncoderButton(byte acceptedCommand = 0);
  byte Md_Display(byte Receiving = 0);
  byte Ae_autoShutdown(byte acceptedCommand = 0);
  byte Ae_displayRefresh(byte acceptedCommand = 0);
  
  byte Ae_BatR(byte Taking) {
   
   return !digitalRead(Taking);
 
  }
 
  byte Ae_autoShutdown(byte acceptedCommand){
   
    static uint32_t tmr = 300000;
   
    if (acceptedCommand == reset) { 
      
      tmr = 300000 + millis();// 5 hv
      
    }
    
    else if (tmr < millis()){
      
      delay(400); 
      esp_deep_sleep_start();
    
    }
   
   return 0;
  
  }
  
  
  void Ae_buttonOnOff(){

   if (!digitalRead(13)) { 
    
    delay(400); 
    esp_deep_sleep_start(); 
   
   }
  
   delay(100);

  }
  
  byte Ae_displayRefresh(byte acceptedCommand){
    /*
    static byte clearRefresh = 1; 
    
    if(acceptedCommand == reset && !clearRefresh) {
      
      Serial.println("reset");
      clearRefresh = 1; 

    }
    
    else if(clearRefresh) {
      
      if(acceptedCommand == clear_display && clearRefresh % 2 != 0) {
       
       display.clearDisplay();
      
      } 

      else if(acceptedCommand == print_display && clearRefresh % 2 == 0) {
      
       display.display();
      
      } 
    
      clearRefresh++;
     
      if(clearRefresh == 101) {
      
       clearRefresh = 0;
      
      }
    
    }
    */
     
      if(acceptedCommand == clear_display) {
       
       display.clearDisplay();
      
      } 

      else if(acceptedCommand == print_display) {
       
       display.display();
      
      } 
    
    return 0;
  
  }
  
  void gyroStart(){
    /*
    mpu.initialize();
    delay(10);
    mpu.dmpInitialize();
    mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
    mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
    
    static byte flag = []() -> byte {
      byte temp;
      EEPROM.get(7 * sizeof(Profiles) + 1, temp); 
      
      return !temp; 
    }();
   
    */
    static byte flag = 1;//TECT
    long offsets[6];
   
   
    if(Ae_BatR(b_triger_r) && Ae_BatR(b_analog_l)){
    
      byte temp = 10;
     
      while(temp > 0){
        
        display.clearDisplay();
        
        display.setCursor(0, 16);
        display.println("  Calibration starts");
         display.println();
        display.print  ("          ");
        display.println(temp);
        
        display.display();
        Ae_buttonOnOff(); 
        temp--;
        delay(1000);
        
      }
      
      display.clearDisplay();
      display.setCursor(2, 16);
      display.println(" Wait for completion");
      display.display();
      delay(2000);//TE
      /*
      mpu.setXAccelOffset(0);
      mpu.setYAccelOffset(0);
      mpu.setZAccelOffset(0);
      mpu.setXGyroOffset(0);
      mpu.setYGyroOffset(0);
      mpu.setZGyroOffset(0);
      delay(10);
      
      mpu.CalibrateAccel(60);//не забудьте додати рестарт при помилках калібровки
      mpu.CalibrateGyro(120);
      delay(10);
      
      offsets[0] = mpu.getXAccelOffset();
      offsets[1] = mpu.getYAccelOffset();
      offsets[2] = mpu.getZAccelOffset();
      offsets[3] = mpu.getXGyroOffset();
      offsets[4] = mpu.getYGyroOffset();
      offsets[5] = mpu.getZGyroOffset();
      */
      flag = 1;
      /*
      EEPROM.put(7 * sizeof(Profiles) + 1, flag);
      EEPROM.put(7 * sizeof(Profiles) + 2, offsets);
      EEPROM.commit();
      */

      display.clearDisplay();
      display.setCursor(4, 16);
      display.println("Calibration complete");
      display.display();
    
    }
     
    else if(!flag){
    
     display.clearDisplay();
     
     display.setCursor(1, 16);
     display.println("Gyroscope calibration");
     display.println("     not done");
    
     display.display();
     
     while(1){
      
      Ae_buttonOnOff(); 
     
     }
    
    }
    
    else if(flag){
      /*
      EEPROM.get(7 * sizeof(Profiles) + 2, offsets);
    
      mpu.setXAccelOffset(offsets[0]);
      mpu.setYAccelOffset(offsets[1]);
      mpu.setZAccelOffset(offsets[2]);
      mpu.setXGyroOffset(offsets[3]);
      mpu.setYGyroOffset(offsets[4]);
      mpu.setZGyroOffset(offsets[5]);
      */
    }

  }
  
  void startAnimation(){
    
    display.clearDisplay();
    display.invertDisplay(0);
    display.setCursor(-2, 28);
    display.println("        FS G36      ");
    display.display();
    delay(500);
    display.clearDisplay();
    display.display();
    
  }
  
  void Ae_encoderInterrupt() {
    
    encoder.tick(); // Оновлення стану енкодер
  
  }

//Ae

//Main_display
  
  // add1-----------------------------------------------------------------------------------------------------------
    
    void Md_MainDisplay() {// головна фунція виклик решта функцій дисплея
      
      switch (Md_addres(1, int_add)) {
        
        case no_action: 
         
         MdId_BluetoothPrint(); 
         MdAf_typeNumberPrint(); 
         MdId_battery();

         Md_ItemOne();
        
         break;//1
       
        case 1:
          
          Md_Profile();
         
         break;//a2
        
        case 2:
          
          //qr-код ссылка на інструкцію
         
         break;//a2
        
        
        case address_entered:
          
            if (Md_addres(1, get_add) == 3) {
             
             Md_Display(info_display);
             Md_cursor(posOne, return_pos);
             Md_addres(1, back);         
            
            }
            
            else{
            
             Md_cursor(posOne, return_pos);
            
            } 
          
          break;
        
         
      }
     
    }
    
    void Md_ItemOne(){
      
        byte Lines[] = {22, 32, 43};
        Md_cursor(Lines, sizeof(Lines));
        
        display.setCursor(0, 22);
        display.println("  Profile");
        display.setCursor(0, 32);
        display.println("  Instruction (!)");
        display.setCursor(0, 43);
        display.println("  Exit menu ");
    
    }
  //
  // add2-----------------------------------------------------------------------------------------------------------
    
    void Md_Profile() {
      
      if (prof(act_dir)->tape == t_sp) {//в залежності від типу профілю вид меню міняється
      
        switch (Md_addres(2, int_add)) {
          
          case no_action: 
            
            Md_p_Profile();
           
           break;
          
          case 1:
           
            Md_ProfileSettings();
          
           break;//a3
          
          case 2:// QR code 
          
           break;
          
          case 3:
            
            Md_SelectProfile();
           
           break;//a3 
          
          case address_entered:
          
            if (Md_addres(2, get_add) == 4) {
   
             Md_addres(1, back);           
            
            }
            
            
            else if (Md_addres(2, get_add) == 1) {
             
             MdAf_profileStorage(copy_profile);////копіюємо профіль при вдоході в настройки  для подальшої перевірки на зміни при виході 
          
            } 
            
            Md_cursor(posOne, return_pos);
         
           break;
          
        }
      }   
      
      else {
    
        switch (Md_addres(2, int_add)) {
          
          case no_action: 
           
            Md_p_Profile();
          
           break;
          
          case 1://a3
            
            Md_ProfileSettings();
           
           break;
          
          case 2: //a3 
            
            Md_SelectProfile();
          
           break;
          
          case address_entered:
            
            if (Md_addres(2, get_add) == 3) {
  
              Md_addres(1, back);           
            
            }
            
            
            else if (Md_addres(2, get_add) == 1) {
            
              MdAf_profileStorage(copy_profile);////копіюємо профіль при вдоході в настройки  для подальшої перевірки на зміни при виході 
          
            } 
            
            Md_cursor(posOne, return_pos);
        
           break;
            
        }
      }   
    
    } 
   
    void Md_p_Profile() {
        
      byte LinesSp[] = {31, 39, 49, 57};
      byte LinesMp[] = {35, 49, 57};
      
      if (prof(act_dir)->tape == t_sp) {      
        
        Md_cursor(LinesSp, sizeof( LinesSp));
      
      } 
      
      else {
        
        Md_cursor(LinesMp, sizeof(LinesMp));
      
      }
      
      display.setCursor(3, 1);//стрілки по боках активний профіль
      display.write(25);
      display.setCursor(118, 1);
      display.write(25);
      
      display.drawRoundRect(0, 0, 127, 10, 2, WHITE);// РАМКА АКТИВ ПРОФ НАЗВА ГРИ
      display.drawRect(0, 9, 127, 19, WHITE);
      display.drawRect(10, 29, 51, 19, WHITE);// РАМКА НАСТРОЙКИ КЮАР КОД
      
      display.setCursor(21, 1);
      display.print("Active profile");
      
      display.setCursor(98, 36);       //РИСКА В лыво РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
      display.println("_" );
      display.setCursor(93, 36);
      display.println("_" );
      display.setCursor(91, 36);// СТРЫЛКА ВВЕРХ РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
      display.write(24);
      display.setCursor(91, 34);
      display.write(30);
      
      display.drawCircle(113, 42, 11, WHITE);// КРУГ НАВК Sp
      
      display.setCursor(105, 39);
      
      if (prof(act_dir)->tape == t_sp) { 
       
       display.print("Sp");
       display.println(prof(act_dir)->namber);
      
      }
      else {
       
       display.print("Mp");
       display.println(prof(act_dir)->namber);
      
      }
      
      display.setCursor(prof(act_dir)->centroName(), 15);//функція centroName() в залежності від кількості ім'я вранці
      display.println(prof(act_dir)->nameProfiles(get_name));
      
      display.setCursor(0, 32);       //РИСКА В ПРАВО РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
      display.println("          _" );
      display.setCursor(4, 32);
      display.println("          _" );
      
      display.setCursor(66, 32);// СТРЫЛКА ВВЕРХ РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
      display.write(24);
      display.setCursor(66, 30);
      display.write(30);
      
      
      
      if (prof(act_dir)->tape == t_sp) {      
       
       display.setCursor(0, 31);
       display.println("  Settings   " );
       display.println("  QR code");
      
      } 
      else { 
       
       display.setCursor(0, 35);
       display.println("  Settings   " );
      
      }
      
      display.setCursor(0, 49);
      display.println(" Select profile ");
      display.println(" Back" );
      
    }
    
   
    /*
    void Md_p_Settings() {
     
     display.setCursor(0, 0);
     display.println(" Hibernation ");
     display.println(" Sounds ");
     display.println(" Gamepad mode ");
     display.println(" Back" );
     
    }
   */
  //        
  // add3-----------------------------------------------------------------------------------------------------------

    void Md_SelectProfile() {
    
      switch (Md_addres(3, int_add)) {
        
        case no_action: 
          
          Md_p_SelectProfile();
         
         break;
        
        case 1: 
          
          Md_StandardProfiles();
         
         break;
        
        case 2: 
         
          Md_MyProfiles();
         
         break;
        
        case address_entered: 
         
          if (Md_addres(3, get_add) == 3) {
           
           if (prof(act_dir)->tape == t_sp) {  
            
            Md_cursor(posThree, return_pos);
           
           }
           
           else{
            
            Md_cursor(posTwo, return_pos);
           
           }
           
           Md_addres(2, back); 
          
          }
           
          else{ 

           Md_cursor(posOne, return_pos);
          
          } 
         
         break;
      
      }
    }
    
    void Md_p_SelectProfile() {
      
      byte Lines[] = {16, 24, 32};
      Md_cursor(Lines, sizeof(Lines));
    
      display.setCursor(0, 16);
      display.println(" Standard profiles" );
      display.println(" My profiles");
      display.println(" Back" );
    
    }
  
    void Md_ProfileSettings() {
      
      if (prof(act_dir)->tape == t_sp) { //вид пунктiv меню якщо профіль стандартний
        
        switch (Md_addres(3, int_add)) {
          
          case no_action: 
            
            Md_p_ProfileSettings();
           
           break;
          
          case 1:
            
            Md_Buttons();
           
           break;//Buttons
          
          case 2:// якщо були зміни то повернення відбудеться через функцію MdAf_profileStorage
            
            if (MdAf_profileStorage(check_changes) == recorded_changes) {//перевірка на наявність змін
          
              switch (MdAf_consenDisplay()) {//якщо є зміни запуститься дисплей підтвердження при виході з настройок
                
                case choice_yes:
                  
                  MdAf_profileStorage(reset);//якщо виберемо так профіль повторно скопірується комадою ресеті функція MdAf_profileStorage  перестане видавати recorded_changes
                  Md_addres(2, back);
                  
                 break;
                
                case choice_no:
                
                  MdAf_profileStorage(return_save);//якщо виберемо Ні функція dAf_profileStorage перестане видавати recorded_changes і  умова перестане  виконуватися
                  Md_addres(2, back);
                 break;
              
              }
            }else{
              
              Md_addres(2, back);
              
            }
            
            break;
       
          case address_entered: 
          
           Md_cursor(posOne, return_pos);
         
           break;
        }
      }
        
      else { // вид пунктів якщо вибрано власний профіль
 
        switch (Md_addres(3, int_add)) {
          
          case no_action: //a2

            Md_p_ProfileSettings();//відображення пунктів настройки
  
            break;
          
          case 1://Gyroscope //a4
            
            Md_gyroscopeSettings();
            
            break;
          
          case 2://Buttons
            
            Md_Buttons();
            
            break;
          
          case 3://Name
          
            Md_changeNameProfile();
          
            break;

          case 4:// якщо були зміни то повернення в попередній пункт меню відбудеться через функцію MdAf_profileStorage
            
            if (MdAf_profileStorage(check_changes) == recorded_changes) {//перевірка на наявність змін
          
              switch (MdAf_consenDisplay()) {//якщо є зміни запуститься дисплей підтвердження при виході з настройок
                
                case choice_yes:
                  
                  MdAf_profileStorage(reset);//якщо виберемо так перестане видавати recorded_changes
                  Md_addres(2, back);
                  
                 break;
                
                case choice_no:
                
                  MdAf_profileStorage(return_save);//якщо виберемо Ні функція dAf_profileStorage перестане видавати recorded_changes і  умова перестане  виконуватися
                  Md_addres(2, back);
                 break;
              
              }
            }
            
            else{//якщо змін не було то просто повертаємося
              
              Md_addres(2, back);
              
            }
            
            break;
          
          case address_entered: 
    
            Md_cursor(posOne, return_pos);
          
            break;
          
        }
      }
    }
       
    void  Md_p_ProfileSettings() {
      
      byte LinesSp[] = {16, 24};
      byte LinesMp[] = {16, 24, 32, 50};
      
      if (prof(act_dir)->tape == t_sp) {      
        
        Md_cursor(LinesSp, sizeof(LinesSp));
        display.setCursor(0, 16);
        display.println(" Buttons" );//не добавляти пункт reset не потрібно
        display.println(" Back" );
      
      } else {
        Md_cursor(LinesMp, sizeof(LinesMp));

        display.drawRoundRect(9, 14, 59, 28, 0, WHITE);
        
        display.setCursor(0, 16);
        display.println("  Gyroscope" );
        display.println("  Buttons" );
        display.println("  Name" );
        
        display.setCursor(0, 50);
        display.println(" Back" );
      }

    }
  //                
  // add4-----------------------------------------------------------------------------------------------------------
    
    void Md_StandardProfiles(){
    
      
      switch (Md_addres(4, int_add)) {
        
        case no_action: 
        
          Md_p_StandardProfiles();//відображення списку профілів вибір
        
         break;
        
        case address_entered: 
         
          if (Md_addres(4, get_add) == 8) {
          
            Md_cursor(posOne, return_pos);
            Md_addres(3, back);
          
          }
          
          else{

           
            if (prof(act_dir)->getNamberTape() != prof(direct_p_sp, Md_addres(4, get_add))->getNamberTape()) {
           
             prof(activate_p_sp, Md_addres(4, get_add));
             Md_cursor(posOne, return_pos);
             Md_addres(2, back); 
            
            }
          
            else{Md_addres(4, back); }
          
          }

         break;
 
      }
    }
    
    void Md_p_StandardProfiles(){
      
      byte Lines[] = {0, 8, 16, 24, 32, 40, 48, 56};
      Md_cursor(Lines, sizeof(Lines));
    
      for (int i = 0; i < 7; i++) {//виводим імена профілів діючий профіль мигає
      
       display.setCursor(6, i*8);
       display.print(prof(direct_p_sp, i+1)->nameProfiles(get_name_a_f));
      
      }
      
      display.setCursor(0, 56);
      display.println(" Back" );
    
    }
    
    void Md_MyProfiles(){
     
      switch (Md_addres(4, int_add)){//третій параметр забороняє вибирати діючий профіль
        
        case no_action: 
           
           Md_p_MyProfiles();
          
          break;
        
        case address_entered: 
         
          if (Md_addres(4, get_add) == 8) {//повертаємося в передній пункт
          
            Md_cursor(posOne, return_pos);
            Md_addres(3, back);
          
          }
          
           else{//якщо вибраний профіль не є діючий виконуємо заміну якщо так просто повертаємося

            if (prof(act_dir)->getNamberTape() != prof(direct_p_mp, Md_addres(4, get_add))->getNamberTape()) {
           
             prof(activate_p_mp, Md_addres(4, get_add));
             Md_cursor(posOne, return_pos);
             Md_addres(2, back);//не міняти двоєчку будуть глюки
            
            }
          
            else{Md_addres(4, back);}
          
          }

         break;
        
      }
    }
    
    void Md_p_MyProfiles() {
      
      byte Lines[] = {0, 8, 16, 24, 32, 40, 48, 56};
      Md_cursor(Lines, sizeof(Lines));
    
      for (int i = 0; i < 7; i++) {
      
       display.setCursor(6, i*8);
       display.print(prof(direct_p_mp, i+1)->nameProfiles(get_name_a_f));
      
      }
      
      display.setCursor(6, 56);
      display.println("Back" );
    
    }
   
    void Md_gyroscopeSettings(){//a3
      
      int seveData;//використовую щоб було більш зрозуміліше
      
      if (Md_addres(4, add_status) == address_deleted){//відновляємо у виконанні функції Gf_GyroAnalogR після виходу з настройок мертвої зони (якщо адрес видалився)
        /*
        timerAlarmEnable(timer);
        */
      }
      
      if (Md_addres(4, get_add) != 5){//зроблено для того щоб курсор не міняв свій вигляд при виході
        
       Md_p_gyroscopeSettings();
       
      }
      
      switch (Md_addres(4, int_add_last)) {//4
        
        case no_action:
        
         break;
        
        case 1:
          
          seveData = Mf_Magnifier(prof(act_dir)->deadZoneHor, 0, 9999, 100);
          prof(act_dir)->setDeZoHor(seveData);
         
         break;
        
        case 2:
          
          seveData = Mf_Magnifier(prof(act_dir)->sensitivityHor, 0, 100, 10);
          prof(act_dir)->setSenHor(seveData);
         
         break;
        
        case 3:
          
          seveData = Mf_Magnifier(prof(act_dir)->deadZoneVer, 0, 9999, 100);
          prof(act_dir)->setDeZoVer(seveData);
        
         break;
        
        case 4:
          
          seveData = Mf_Magnifier(prof(act_dir)->sensitivityVer, 0, 100, 10);
          prof(act_dir)->setSenVer(seveData);        
         
         break;
        
        case 5:
          
          Md_cursor(posOne, return_pos);
          Md_addres(3, back);   
         
         break;
        
        case address_entered: 
         
          if (Md_addres(4, get_add) == 1 || Md_addres(4, get_add) == 3) {//зупиняємо виконання функції Gf_GyroAnalogR  для того щоб настроїти мертву зону
           /*
           timerAlarmDisable(timer);
           */ 
          }
        
         break;
      }
    
    }
  
    void Md_p_gyroscopeSettings(){
      
      static uint32_t tmr;
      static byte dashFlag;//прапорець м'ягання
      
      if (tmr < millis()) {
        
        tmr = millis() + 500;
        dashFlag = ! dashFlag;
      
      }
     
      byte Lines[] = {5, 15, 34, 44, 55};
      Md_cursor(Lines, sizeof(Lines));
    
      display.setCursor(8, 5);
      display.println("Dead zone ");
      display.setCursor(8, 15);
      display.println("Sensitivity");
      display.setCursor(8, 34);
      display.println("Dead zone ");
      display.setCursor(8, 44);
      display.println("Sensitivity");
      display.setCursor(8, 55);
      display.println("Back");
      
      display.setCursor(84, 10);
      display.println("H");
    
      display.setCursor(84, 39);
      display.println("V");
      
      display.setCursor(102, 4);
      display.println(prof(act_dir)->deadZoneHor);
    
      display.setCursor(104, 17);
      display.println(prof(act_dir)->sensitivityHor);
      
      display.setCursor(102, 33);
      display.println(prof(act_dir)->deadZoneVer);
      
      display.setCursor(104, 46);
      display.println(prof(act_dir)->sensitivityVer);
      
      
      display.drawRoundRect(6, 2, 69, 23, 3, WHITE); //РАМКА НАВКОЛО Dead zone Sensitivity H
      display.drawRoundRect(6, 31, 69, 23, 0, WHITE);//РАМКА НАВКОЛО Dead zone Sensitivity V
      
      //H
      if((Md_cursor(noData, get_pos) == 1 || Md_cursor(noData, get_pos) == 2) && !Md_addres(4, get_add)){// РАМКА  НВКОЛО H
       
       if(dashFlag){display.drawRoundRect(79, 6, 15, 15, 0, WHITE);}
      
      }
      
      else{display.drawRoundRect(79, 6, 15, 15, 0, WHITE);}
      
      //V
      if((Md_cursor(noData, get_pos) == 3 || Md_cursor(noData, get_pos) == 4) && !Md_addres(4, get_add)){// РАМКА  НВКОЛО V
       
       if(dashFlag){display.drawCircle(86, 42, 7, WHITE);}
      
      }
      
      else{display.drawCircle(86, 42, 7, WHITE);}   
      
      
      if(Md_addres(4, get_add) == 1){ // число рамка DZ H
       
       if(dashFlag){display.drawRoundRect(99, 2, 29, 11, 4, WHITE);}
      
      }
      
      else{display.drawRoundRect(99, 2, 29, 11, 4, WHITE);}
      
      if(Md_addres(4, get_add) == 2){ // число рамка S H
      
       if(dashFlag){display.drawRect(101, 15, 23, 11, WHITE);}
      
      }
      
      else{display.drawRect(101, 15, 23, 11, WHITE);}
      
      if(Md_addres(4, get_add) == 3){ // число рамка DZ V
      
       if(dashFlag){display.drawRect(99, 31, 29, 11, WHITE);}
      
      }
      
      else{display.drawRect(99, 31, 29, 11, WHITE);}

      if(Md_addres(4, get_add) == 4){ // число рамка S V
       
       if(dashFlag){display.drawRoundRect(101, 44, 23, 11, 4, WHITE);}
      
      }
      
      else{display.drawRoundRect(101, 44, 23, 11, 4, WHITE);} 
       
    }

    void Md_Buttons(){//замінити if на Switch в кінці при використанні свіч глюки

      Md_p_Buttons();
     
      if(Md_addres(no_data, add_status) == address_deleted){//якщо адрес був видалений скидає функцію
        
        Md_p_Buttons(reset);
        Md_movingButtons(reset);
      
      }  

      switch (Md_addres(4, int_add_last)) {
        
        case no_action:

         break;
        
        case 1:
          
          Md_movingButtons();
             
         break;
        
        case 2:
          
          switch (MdAf_confirmYesNo(37, 46)) {
            
            case no_action:

             break;
           
            case choice_yes:
              
              Md_addres(4, back);
              prof(act_dir)->resetButtons();

             break;
           
            case choice_no:

              Md_addres(4, back);
             
             break;
          
          }
          
         break;
        
        case 3:
           
          Md_cursor(posTwo, return_pos);
          Md_addres(3, back);
         
         break;
        
        case address_entered:
          
          Serial.print("address_entered");
          if(Md_addres(4, get_add) == 1){
          
           Md_p_Buttons(next_animation); 
          
          }
          
          else if(Md_addres(4, get_add) == 2){
           
           
           Md_addres(no_data, click_reset_off);//потрібно для того щоб Клік працював тепер в функції MdAf_confirmYesNo(37, 46)
          
          }
         
         break;

      }
      
    }
    
    int Md_movingButtons(int acceptedCommand){//не закінчено тест кнопки опитуються не повністю
      
      static unsigned long tmr;//таймер для захисту від тремтіння контактів кнопки
      static int8_t buttonFlag;//затвор кнопки захист від тремтіння
      static int8_t sequenceNumber;//при першому натиску зберігаємо порядковий номер піна-кнопки яка спрацювала


      if (acceptedCommand == reset) { 
       
       buttonFlag = sequenceNumber = 0;
       
       return 0;
      
      }
      
      else if (!buttonFlag && tmr < millis()) { //кнопка одноразово спрацьовує з захистом
            
        for (int i = 3; i <= 4; i++) {
          
          if (Ae_BatR(prof(act_dir)->buttonArray[i])) {
            
            tmr = millis()+500;
            buttonFlag = 1;
            Md_p_Buttons(next_animation, i); 
            
            if(!sequenceNumber){//якщо була натиснута перша кнопка зберігаємо порядковий номер
              
              sequenceNumber = i;
            
            }
            
            else{//якщо натиснули другу кнопку міняємо місцями елементи масиву-кнопки
              
              int temp = prof(act_dir)->buttonArray[sequenceNumber];// переміщення даних між елементами масиву Напряги мозги і поймеш
              prof(act_dir)->buttonArray[sequenceNumber] = prof(act_dir)->buttonArray[i];
              prof(act_dir)->buttonArray[i] = temp;
              buttonFlag = sequenceNumber = 0;
              Md_addres(4, back); 
              
            }

            break;
          
          }
        }
      }
      
      else if (buttonFlag && tmr < millis() && digitalRead(prof(act_dir)->buttonArray[sequenceNumber])) {//захист від брязки до першої кнопки захист натиску другої кнопки не потрібен програма завершує роботу
      
        buttonFlag = 0;
          
      }  
     
     return 0;
    
    }
    
    void Md_resetButtons(){///масив буде потрібно змінити в кінці
                
      const byte defaultButton[] = {0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};

      for (int i = 0; i < sizeof(defaultButton); i++) {
        
        prof(act_dir)->buttonArray[i] = defaultButton[i];
      
      }

    }

    byte Md_p_Buttons(byte receivedData, byte selectedButton){
      
      //бегуща стрілка. анімація. кнопка один збереження. крапка 2 збереження. анімація кнопка ліва. анімація кнопка права
      static byte runningArrow, animation, buttonOne, buttonTwo, accumulationL, accumulationR;
       
      static unsigned long tmr; //таймер для виконання анімації
      
      const char *buttonNames[] = {//масив імена кнопок виводим при натиску
        " ",
        " ",
        " ",
        "A",
        "B",
        "X",
        "Y",
        "LB",
        "RB",
        "LT",
        "RT",
        "LA",
        "RA",
      };
      
      display.drawRect(6, 5, 31, 31, WHITE); // квадрат L
      display.drawRect(90, 5, 31, 31, WHITE);// квадрат R
      
      display.drawCircle(21, 16, 8, WHITE);//кружки над кнопками
      display.drawCircle(105, 16, 8, WHITE);
      
      display.drawRect(12, 26+accumulationL, 19, 5, WHITE);//ліва кнопка accumulationL збільшення зменшення анімація кнопки
      display.fillRect(9, 30, 25, 4, WHITE);
      
      display.drawRect(96, 26+accumulationR, 19, 5, WHITE);//ПРАВА кнопка accumulationR збільшення зменшення анімація кнопки
      display.fillRect(93, 30, 25, 4, WHITE);
      
      byte Lines[] = {40, 48, 56};//передаємо координати переміщення функції
      Md_cursor(Lines, sizeof(Lines));
      
      display.setCursor(0, 40);//понятно
      display.println(" Move buttons");
      display.println(" Reset");      
      display.println(" Back");
      
      if (!receivedData && !animation) {//якщо фаус то закінчуємо роботу функції економія ресурсів
        
        return 0;
      
      }
      
      if (receivedData == reset) {
       
       animation = accumulationL = accumulationR = runningArrow = 0;
      
      }  
      
      else if (receivedData == next_animation) {//в коді викликаємо функцію переключаємо анімацію
        
        accumulationL = accumulationR = runningArrow = 0;
        
        if (animation != 3) {
         
         animation++;
        
        }
        if (animation == 2) {
         
         buttonOne = selectedButton;
        
        }else if (animation == 3) {
        
         buttonTwo = selectedButton;
         tmr = millis()+1000;
        
        }
      
      }
     
      if (animation == 1 && tmr < millis()) { //анімація кнопок за допомогою перемінної
       
        accumulationL++;
        
        if (accumulationL == 4) {
        
         accumulationL = 0;
        
        }
 
      }
      
      else if (animation == 2 && tmr < millis()) {
   
        accumulationR++;
        runningArrow++;
        
        if (accumulationR == 4 ) {
        
         accumulationR = 0;
        
        }
      
        if (runningArrow == 8 ) {
        
         runningArrow = 0;
        
        }
      
      }
      
      else if (animation == 3 && tmr < millis()) {//відображення другої кнопки завершення роботи з запізнення
       
        animation = 0;
    
      }
      
      if (animation == 1 ) {
        
        if (accumulationL){//знак запитання мигає якщо кнопка не натиснута
         
         display.setCursor(19, 13); 
         display.println('?');
        
        }
      
      } 

      else if (animation == 2 ) {
        
        display.setCursor(22-strlen(buttonNames[buttonOne])*3, 13);//strlen автоцентровка в колі може бути одна або дві букви
        display.println(buttonNames[buttonOne]);
       
        if(accumulationR){//знак запитання мигає якщо кнопка не натиснута
        
         display.setCursor(103, 13); 
         display.println('?');
       
        }
       
        for (int i = 0; i < 8; ++i) {//відображаємо кутові складки
        
         display.setCursor(40+i*6, 13);
         display.write(62);
      
        } 
      
        display.setCursor(40+runningArrow*6, 13);//трикутник переміщається по углових скобках анімація
        display.write(16);
      
      }
      
      else if (animation == 3 ) {//завершення програми відображення натиснутих кнопок
        
       display.setCursor(22-strlen(buttonNames[buttonOne])*3, 13);
       display.println(buttonNames[buttonOne]);
      
       display.setCursor(106-strlen(buttonNames[buttonTwo])*3, 13);
       display.println(buttonNames[buttonTwo]);

      }
      
      if (tmr < millis()) {//всі дії виконуються по таймеру
       
       tmr = millis()+400; 
      
      }

      return 0; 
      
    }
     
    void Md_changeNameProfile(){
      
      static int8_t AxisX, AxisY, delFlag;//XYнаведення на вибрану букву delFlag видалення за допомогою отримання
      static uint32_t tmr;//використовується для видалення утриманняm
      static int8_t magnitude = 1;//костилі використовується для вибору пробілу і виходу
     
      char letterBig[6][10] = { 
        
        {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'},
        {'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'},
        {'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', '-'},
        {25 , 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '.', 27 },
        {' ', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>'}
        
      };
      
      char letterSmall[6][10] = {
       
        {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'},
        {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'},
        {'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '-'},
        {24 , 'z', 'x', 'c', 'v', 'b', 'n', 'm', '.', 27 },
        {' ', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>'}
       
      };

      static char (*ptrLetter)[10] = letterSmall;//вказівник вказує в залежності від настройки на великі або малі букви
       
      if (Mf_EncoderButton() == click ) {//після кліку виконується видалення додавання букви
         
        if (ptrLetter[AxisY][AxisX] == 25) {
         
         ptrLetter = letterSmall;
        
        }   
        
        else if (ptrLetter[AxisY][AxisX] == 24) {
         
         ptrLetter = letterBig;
        
        }   
        
        else if (ptrLetter[AxisY][AxisX] == 27) {
          
          prof(act_dir)->nameProfiles(delete_letter);                          
           
          if (!prof(act_dir)->letterIndicator) {
              
            ptrLetter = letterBig;
            
          }
          
        }
        
        else if (ptrLetter[AxisY][AxisX] == '>') {
         
          AxisX = AxisY = delFlag = 0;
          ptrLetter = letterSmall;
          Md_cursor(posThree, return_pos);
          Md_addres(3, back);
        
        }
        
        else{ 
          
          if (!prof(act_dir)->letterIndicator) {//якщо вводиться перша буква всі букви стають малими одноразовo
              
            ptrLetter = letterSmall;
            
          }
           
          prof(act_dir)->nameProfiles(change_letter, ptrLetter[AxisY][AxisX]);
        }
        
      }   
        
      if (Mf_EncoderButton(take_but_pos) == button_pressed){//якщо утримувати кнопку можна зробити дві дії видалення букви утриманням або переміщення по осі Y
      
        if (Mf_encoderRotationSpeed(check_rotation)) {//переміщення курсу вверх-вниз якщо кнопка отримується
          
          delFlag = 1;//забороняємо видалення утриманням коли почались оберти енкод
          Mf_EncoderButton(reset);//Клік не повернеться скидаємо функцію
        
          switch (Mf_encoderRotationSpeed(get_rotation1)) {
              
            case rotated_r1: 
              
              AxisY--;
              
              if (AxisY < 0) {
          
              AxisY = 4;
          
              }
            
            break; 
            
            case rotated_l1: 
        
              AxisY++;
              
              if (AxisY > 4) {
          
              AxisY = 0;
          
              }
            
            break;   
            
          }
        }
         
        else if(ptrLetter[AxisY][AxisX] == 27 && !delFlag) {//якщо не було обертів під час отримання кнопки І ми навелились на знак видалення і минув час починається видалення букв
         
          if (tmr < millis()) {
            
            Mf_EncoderButton(reset);
            tmr = millis()+300;
            
            prof(act_dir)->nameProfiles(delete_letter);
            
            
            if (!prof(act_dir)->letterIndicator) {
                
              ptrLetter = letterBig;
              
            }
          
          }

        }
     
      }
       
      else if (Mf_encoderRotationSpeed(check_rotation)){//звичайний режим роботи курсор рухається вправо і вниз і повторно починає свої дії якщо дійти до кінця  і наоборот
      
        switch (Mf_encoderRotationSpeed(get_rotation1)) {
          
          case 0: 
          
            break;
          
          case rotated_r1: 
            
            AxisX -= magnitude;
            
            break;  
          
          case rotated_l1: 
            
            AxisX += magnitude;
            
            break;   
        
        }

        if (AxisX < 0) {//тут відбувається обмеження мінімального і максимального значення перемінних X Y також змінюється амплітуда якщо y = 4
          
          magnitude = 1;
          AxisX = 9;
          AxisY--;
        
        }
        
        else if (AxisX > 9) {
          
          magnitude = 1;
          AxisX = 0;
          AxisY++;
        
        }
        
        if (AxisY < 0) {
          
          AxisY = 4;
        
        }
        
        else if (AxisY > 4 ) {
          
          AxisY = 0;
        
        } 
        
        if (AxisY == 4) {//якщо y вказує на останній рядок магнітуда збільшується до 9 так щоб після двох обертів ми покидали нижню частину
          
          if (AxisX != 0 && AxisX != 9) {//користуємо якщо X не є дев'яткою або нулем після того як переміщалися вверх-вниз курсором
            
            if (AxisX >= 7) {
            
             AxisX = 9;
            
            }
            
            else{

             AxisX = 0;
            
            }
      
          }
          
          magnitude = 9;//якщо перетворити на 9 після нуля буде дев'ятка а після дев'ятки буде перебільшення і скидання курсова наверх після оберту енкодером
          
        }
    
      }
      
      else{//захист від випадкового видалення коли хочемо почати переміщатися вверх-вниз коли знаходимося на пункті видалення
        
        tmr = millis()+400;
        delFlag = 0;
      
      }
     
      Md_p_changeNameProfile(ptrLetter, AxisX, AxisY);//віалізуємо програму
     
    }
    
    byte Md_p_changeNameProfile(char letter[][10], byte AxisX, byte AxisY){
      
      char str[] = {24, 25, 27, '>' , ' '};//маси використовується для мигання необхідної рамки
      static byte flagBlink;
      static uint32_t tmr;
      
      
      
      display.setCursor(114, 54);//back
      display.print('s'); 
      display.drawPixel(120, 60, WHITE); //крапка
      
     
      for (int i = 0; i <= 4; i++) {//виводимо рамку навколо букви якщо курсор рамку не вказує на пункти
       
       if(letter[AxisY][AxisX] == str[i]){
        
        break;
       
       }
       
       if(i == 4){//якщо елемент не дорівнює забороненим знакам то виводимо рамку
      
        display.drawRect(4+AxisX*12, 12+AxisY*10, 9, 11, WHITE);//рамка курса 10 вверх вниз, вліво вправо 12 
       }
      
      }

      if((letter[AxisY][AxisX] != 24 && letter[AxisY][AxisX] != 25) || flagBlink){//кружок стрілка вниз вверх
       
       display.drawCircle(8, 47, 5, WHITE);
      
      }
      //принцип роботи якщо навелись на додатковий знак то починаємо ним мигати за допомогою флажка
      if(letter[AxisY][AxisX] != 27 || flagBlink){// знак видалення 
        
        display.drawCircle(116, 47, 4, WHITE);
      
      }
      
      if(letter[AxisY][AxisX] != '>' || flagBlink){//рамка s.
       
       display.drawRoundRect(112, 54, 9, 9, 0, WHITE);
      
      }
      

      display.fillRect(36, 54, 54, 6, WHITE);//пробіл
      
      if(letter[AxisY][AxisX] == ' '){//додаткова рамка якщо навели на пробіл
       
       display.drawRect(34, 52, 58, 10, WHITE);
      
      }
      
      if(tmr < millis()){//мигння рамкою
        
        tmr = millis()+400;
        flagBlink = !flagBlink;
        
      }
       
      if(!flagBlink && prof(act_dir)->letterIndicator != 20){//перестає мигати якщо 20 символів також мигаємо за допомогою flagBlink
       
        display.drawRect(2+6*prof(act_dir)->letterIndicator, 0, 1, 9, WHITE);
      
      }
      
      display.setCursor(2, 1);//виводив ім'я профіля
      display.print(prof(act_dir)->nameProfiles(get_name));
      
      for (int i = 0; i < 4; i++) {//виводим масив букв на дисплей
        
        for (int j = 0; j < 10; j++) {
          
          display.setCursor(6+j*12, 14+i*10);
          display.print(letter[i][j]);
          
        }
      }
      
     return 0; 
    
    }
  
  //
  // Md_Additional functions-------------------------------------------------------------------------------------------
    
    byte MdAf_consenDisplay(){ //викликається для підтвердження вибору
      
      static int8_t flagChoice;
      
      switch (Mf_encoderRotationSpeed(get_rotation1)) {//керування рамкою вліво вправо
       
        case no_action: 
          
          break;
        
        case rotated_l1: 
          
          if (flagChoice > -2) {
           
           flagChoice--;
          
          }
          
         break;
        
        case rotated_r1: 
          
          if (flagChoice < 2) {
           
           flagChoice++;
          
          } 
          
         break;
      }
      
      if ((flagChoice == -2 || flagChoice == 2) && Mf_EncoderButton() == click) {// V1 якщо відбувся Клік виконується дії в залежності від вибору
        
        if (flagChoice == -2) {//якщо вибрали ні виконуються дії//вихід виконується в любому випадку з збереженими настройками або ні
           
          flagChoice = 0;
          return choice_no;
        
        }
        
        else if (flagChoice == 2) {
           
          flagChoice = 0;
          return choice_yes;
        
        }
        
      }
            
      MdAf_p_consenDisplay(flagChoice);//візуальна частина коду
    
     return 0;
    
    }
      
    int8_t MdAf_p_consenDisplay(int8_t acceptedFlag){
      
      static uint32_t tmr;
      static int8_t FlagFrame;
    
      display.drawRoundRect(20, 8, 87, 22, 0, WHITE); //рамка навколо слова зверху
      display.setCursor(25, 15); 
      display.println("Save settings");
      if (tmr < millis()) {// виконує механія рамкою навколо слова так ні
        
        tmr = millis() + 400;
        FlagFrame = !FlagFrame;
      
      }
      
      if (FlagFrame) {//виводим рамку навколо слова і мигаємо нею
       
       display.drawRect(52+acceptedFlag*18, 39, 21, 13, WHITE);//переміна приймає число від -2 до +2 І в залежності від числа міняє координати рамки
      
      }

      for (int i=39; i <= 75; i+=18){//відображаємо стрілки вліво вправо
       
       display.setCursor(i,42);
       display.write(27);display.write(26); 
      
      }
      
      display.setCursor(21, 42);
      display.println("NO");
      display.setCursor(18, 42);
      display.println("            YES");
      
      return 0;
    
    }
    
    byte MdAf_profileStorage(byte acceptedCommand){//функція викликається якщо Md_addres(3, get_add) == 1
     
      switch (acceptedCommand) {//false якщо повертаємо збережені настройки
        
        case return_save:
           
          prof(act_dir)->copyFrom(profilSeve);//це означає що ми вибрали ні і повертаємо настройки
          
         break;
       
        case copy_profile:
           
          profilSeve.copyFrom(*prof(act_dir));//копіювання діючий профіль 
          
         break;
        
        case check_changes:
  
          if (profilSeve != *prof(act_dir)) {//провіряємо зміни
            
           return recorded_changes;
            
          }
          
         break;
        
        case reset://прост  спосіб скинути функцію перестане відправляти recorded_changes виконані зміни збережуться
           
          prof(act_dir)->checkName();//якщо видалені всі букви То встановлюється стандартне ім'я профілю
          profilSeve.copyFrom(*prof(act_dir));
          
          if (prof(act_dir)->tape == t_mp) {//якщо ми настроювали власний профіль то перезаписуємо настройки в енергонезалежну пам'ять
            Serial.println("t_mp s");
            EEPROM.put((prof(act_dir)->namber - 1) * sizeof(Profiles), *(prof(act_dir)));
            EEPROM.commit();
          
          }
          
          else if (prof(act_dir)->tape == t_sp) {
           
           int tempProfSize = 7 * sizeof(Profiles);
           Serial.println("t_mp s");
           EEPROM.put(tempProfSize + (prof(act_dir)->namber - 1) * sizeof(prof(act_dir)->buttonArray), prof(act_dir)->buttonArray);
           EEPROM.commit();
          }
         
         break;
      }
      
      return 0;
    }
    
    void MdAf_typeNumberPrint(){
      
      display.setCursor(49, 0);
      if (prof(act_dir)->tape == t_sp) { 
       
       display.print("(Sp");
       display.print(prof(act_dir)->namber);
       
      }
      else {
       
       display.print("(Mp");
       display.print(prof(act_dir)->namber);
      }
      display.print(")");
    }
    
    byte MdAf_confirmYesNo(byte azixX, byte azixY){
     
     //функцію можна відобразити в любому місці біля слова рецепт після завершення потрібно скинути вручну
      static byte flagChoice;
       
      if (Mf_encoderRotationSpeed(get_rotation1)) {
      
       flagChoice = !flagChoice;
      
      }
      
      if (Mf_EncoderButton() == click) {
        
        byte temp = flagChoice;      
        flagChoice = 0; 
        
        if (!temp) {
         Serial.print("yes");
         return choice_no;
        
        }else{
         
         return choice_yes;
        
        }

      }
      
      display.setCursor(azixX+4, azixY+4);
      display.println("NO   YES");
      
      
      if(!flagChoice) {
      
       display.drawRect(azixX+2, azixY+2, 15, 11, WHITE);//NO
      
      }else{
      
       display.drawRect(azixX+32, azixY+2, 21, 11, WHITE);//YES
      
      }
      
      display.drawRect(37, 46, 55, 15, WHITE);//рамка навколо так ні
      //MdAf_p_confirmYesNo(flagChoice, 37, 46);
    
      return 0;
    
    } 

  // Md_Af
  // Menu_functions 5 
 
    byte Md_cursor(byte* arr, byte dataCommand) { 
      
      static int8_t permissionArrow, Point = 1, workPermit = 1;
          
      switch (dataCommand) {
        
        case return_pos: 
            
          Point = arr[0];
          permissionArrow = 0;
          
        break;
        
        case cursor_type_two: 
            
          permissionArrow = 1;
          
        break;
        
        case cursor_type_one: 
            
          permissionArrow = 0;
           
        break;
        
        case get_pos: 
          
          return Point; 
        
        break;
        
        default:

          if(!permissionArrow){
          
            switch (Mf_encoderRotationSpeed(get_rotation1)) {
              
              case rotated_l1: 
                
                Point--;
                
                if (Point == 0) {
                  
                  Point = dataCommand;
                
                }
                
                break;
              
              case rotated_r1: 
                
                Point++;
                
                if (dataCommand < Point) {
                
                 Point = 1;
                
                }
                
                break;
            }
          
          } 
          
          if (!permissionArrow) {
            
            display.setCursor(0, arr[Point-1]);  //21,8 максимум символів
            display.write(26);
          
          }
          
          else if (permissionArrow) {
            
            display.setCursor(0, arr[Point-1]);  //21,8 максимум символів
            display.write(16);
          
          }   
          
          
      } 
    
     return 0;
    
    } 
      
    int8_t Md_addres(int8_t addNumb, int8_t acceptedCommand) {

      static byte adr[19], modeSwitch = 1, namb = 1, workPermit = 1, del;

      switch (acceptedCommand) {
        
        case back:
          
          Md_cursor(noData, cursor_type_one);
          workPermit = modeSwitch = 1;
          
          for (int i=18; i >= 1; i--){//команда back виконує видалення всіх адресів до числа вказаного в addNumb
            
            adr[i] = 0;
            
            if (i == addNumb) {
              
              namb = addNumb;
              break;
            
            }
          
          }
        
         break;
        
        case get_add:
          
          return adr[addNumb];
        
        break;
        
        case add_status://якщо адрес видалявся потрібно іноді обнуляти функції
          
          if (del){
          
            del = 0;
            return address_deleted;
          
          }
        
         break;        
        
        case click_reset_off://заборон кнопці нкодера скидати адрес кнопку перенаправ. для викорис в іншій фун
          
          workPermit = 0;
          
         break; 
        
        case int_add://при тій команді відбувається набір адреса з подальшим набором при натиску кнопки енкодера
          
          if (!adr[addNumb] && Mf_EncoderButton() == click){//набір адреса
          
            adr[namb] = Md_cursor(noData, get_pos);//позиція курс означає набраний адрес
            namb++;

          return address_entered;
          
          }

         break; 
        
        case int_add_last://набір адреса з подальшим скиданням при натиску 
          
          if (workPermit && Mf_EncoderButton() == click){//набір адреса
          
            if (modeSwitch){ 
              
              adr[namb] = Md_cursor(noData, get_pos);//позиція курс означає набраний адрес
              Md_cursor(noData, cursor_type_two);//міняємо вид курсора
              modeSwitch = 0;//міняємо флажок наступний кліп буде означати скидання адресу
              
              return address_entered;
            
            }
            
            else if (!modeSwitch){
          
              adr[namb] = 0;
              del = modeSwitch = 1;
              Md_cursor(noData, cursor_type_one);//міняємо вид курсора
              
            }
          }  
          
         break; 
      
      }
      
      return adr[addNumb];//функція постійно повертає адрес вказаний в параметрах

    }
  
    int8_t Mf_encoderRotationSpeed(int8_t acceptedCommand) {
      
      static uint32_t tmr;
      static int8_t seveEncPos;
   
      if (!tmr && encoder.getPosition()) {
       
       tmr = millis() + 100; 
      
      }
      
      else if (tmr && tmr < millis()) {
        
        tmr = 0;
        encoder.setPosition(0);
      
      }
      
      
      if (acceptedCommand == check_rotation) {//по проходженню часу перевіряємо наявність обертів їх кількість
        
        return constrain(abs(encoder.getPosition()), 1, 2);
        
      }
      
      else if (acceptedCommand == get_rotation2 && abs(encoder.getPosition()) > 1) {//перевіряємо збільшого до меншого при подальші викликики не призведуть до повернення даних перемінна encoder.getPosition() обновляється
        
        
        acceptedCommand = constrain(encoder.getPosition(), -2, 2);
        encoder.setPosition(0);
        return acceptedCommand; 
      
      }
      
      else if (acceptedCommand == get_rotation1) {
        
        
        acceptedCommand = constrain(encoder.getPosition(), -1, 1);
        encoder.setPosition(0); 
        return acceptedCommand; 
      
      }

      return 0;
    
    }
    
    byte Mf_EncoderButton(byte acceptedCommand) {

      static byte saveAction = releas_b_e;
      static byte functionState = status_allowed;
      static uint32_t tmr;
      
      if (acceptedCommand){//в Kodi можемо зробити reset щоб не поверталося утримання hold_b_e або click
      
      
        if (acceptedCommand == reset){//в Kodi можемо зробити reset щоб не поверталося утримання hold_b_e або click
        
         functionState = reset_status;
        
        }
        
        else if (acceptedCommand == take_but_pos){ //викликаємо стан кнопки якщо кнопка нажата то можемо наприклад скинути функцію і вона не поверне hold_b_e або click
        
        if (Ae_BatR(b_sw)){
        
          return button_pressed;
        
        }
        
        return button_released;

        }
      
      }
      
      if (functionState == status_allowed){
      
      
        if (Ae_BatR(b_sw)){
        
          if (saveAction == releas_b_e) {
           
           Ae_autoShutdown(reset); 
           saveAction = press_b_e; 
           tmr = millis() + 500; 
           return press_b_e;
          
          }
          
          else if (saveAction == press_b_e && tmr < millis()) {
           
           saveAction = completed_work; 
           return hold_b_e;
          
          }
        
        }
        
         
        else if (saveAction && !Ae_BatR(b_sw)) {
          
          if (saveAction == press_b_e){
            
            Ae_displayRefresh(reset);
            saveAction = releas_b_e; 
            return click;
        
          }
           
          saveAction = releas_b_e; 
      
        }
        
      }
      
      else if (functionState == reset_status && !Ae_BatR(b_sw)) {
      
       saveAction = releas_b_e;
       functionState = status_allowed; 
      
      }
      
      return 0;
        
    }  
  
    int Mf_Magnifier(int Numeric, int Min, int Max, int Speed) {
   
      if (Mf_encoderRotationSpeed(check_rotation)) {//encoder.getPosition() перевіряємо наявність даних якщо дані є виконуємо перевірку кіль обер
      
        switch (Mf_encoderRotationSpeed(get_rotation2)) {//якщо функція поверне два оберта то 1 не повертається
          
          case rotated_r2:
            
            Numeric += Speed;
            
           break;
          
          case rotated_l2:
            
            Numeric -= Speed;
            
           break;
        
        } 
      
        switch (Mf_encoderRotationSpeed(get_rotation1)) {
          
          case rotated_r1:
            
            Numeric++;
            
          break;
          
          case rotated_l1:
            
            Numeric--;
            
          break;
        
        }
      }
      
      if (Numeric > Max) {
        
        Numeric = Min;
      
      } 
      
      else if (Numeric < Min) {
        
        Numeric = Max;
      
      }
     
     return Numeric;
    
    }

  // Mf
//Md

//MdId
  
  void MdId_BluetoothPrint() {
      
      const byte bluArr[9] = {
        0b00100,
        0b00110,
        0b10101,
        0b01110,
        0b00100,
        0b01110,
        0b10101,
        0b00110,
        0b00100
      };
      
      static uint32_t tmr;
      static byte Flag;
      
      display.drawCircle(6, 6, 6, WHITE);//кружок
      //display.setCursor(4, 3);
      
      if (bleGamepad.isConnected()) {

        display.drawBitmap(1, 2, bluArr, 8, 9, WHITE);
      
      }
      
      else{
        
        if (tmr < millis()) {
         
         Flag = !Flag;
         tmr = millis() + 1000;
        
        }
        
        if (Flag) {
         
         display.drawBitmap(1, 2, bluArr, 8, 9, WHITE);
        
        }

      }
  
  }

  void MdId_battery() {
    
    const float batLevels[] = {4.15, 4.0, 3.82, 3.76, 3.65, 3.6, 3.5, 3.4, 3.3, 3.0};
    /*
    float shuntVoltage_mV = ina219.getShuntVoltage_mV();
    float busVoltage_V = ina219.getBusVoltage_V();
    float loadVoltage_V  = busVoltage_V + (shuntVoltage_mV / 1000);
    */
    static float loadVoltage_V = 3.0;// test
    static byte chargeLevel;
    static uint32_t tmr;
    
    if (tmr < millis()) { 
      
      tmr = millis() + 60000;
     
      for (int i = 0; i <= 9 ; i++) {
    
        if (loadVoltage_V >= batLevels[i]) {
         
         if (chargeLevel != map(i, 0, 9, 0, 11)) {
          
          Ae_displayRefresh(reset);
      
         }
          
          chargeLevel = map(i, 0, 9, 0, 11);
         
         break;
        
        }
      
      }
    
    }
    
    MdId_p_battery(chargeLevel);  
  
  }

  byte MdId_p_battery(byte receivedData) {

    display.drawRect(111, 2, 1, 4, WHITE);//картинка батареї
    display.drawRect(112, 0, 16, 8, WHITE);
    
    display.fillRect(114+receivedData, 2, 12-receivedData, 4, WHITE);//шкала заряду батареї
   
   return 0;
  
  }

//MI

//infoDisplay
   
  void infoDisplay() {

    MdId_BluetoothPrint(); 
    MdAf_typeNumberPrint();
    MdId_battery();

    display.drawRect(0, 14, 127, 19, WHITE);// РАМКА НАВКОЛО НАЗВИ

    display.setCursor(prof(act_dir)->centroName(), 20);// функція centroName() в залежності від кількості ім'я вранці
    display.print(prof(act_dir)->nameProfiles(get_name));
    
    display.setCursor(44,44);
    display.write(26);
    
    display.drawRect(50, 42, 27, 11, WHITE);
    display.setCursor(52,44);
    display.print("Menu");

  }

//Id

//Display
  
  byte Md_Display(byte Receiving) { 
 
    static byte Regime = info_display; 
    //static byte Regime = menu;
   
    Ae_displayRefresh(clear_display);
 
    if (Receiving) {
    
     Regime = Receiving;
    
    }
    
    switch (Regime) {
    
      case info_display: 
          
        if (Mf_EncoderButton() == click) {
     
          Md_Display(menu);
        
        }
        
        else if (Mf_EncoderButton() == hold_b_e) {//по плану має виключати дисплей при утриманні ?
     
         display.clearDisplay();
         display.display();
        
        }
        
        else{
        
         infoDisplay();
        
        }
      
       break;
    
      case menu:
        
        Md_MainDisplay();
      
       break;

    }  
   
    Ae_displayRefresh(print_display);
     
   return 0;
  
  } 

//D

//Gamepad_function
 
  void IRAM_ATTR Gf_GyroAnalogR() {

    //static uint32_t tmr;
    volatile static int GiroDedZone;
    volatile static int rightStickX;
    volatile static int rightStickY;
    static int x; //x
    static int z;  //z
    /*
    
    */
    //Serial.println(" 11111");
    //if (Ae_BatR(b_handle) && tmr < millis()) {  
      //if (Ae_BatR(b_handle) && bleGamepad.isConnected()) { 
    
    if (Ae_BatR(b_handle)) { 
      
      Ae_autoShutdown(reset);
      /*
      Quaternion q;
      VectorFloat gravity;
      VectorInt16 gyro;
      volatile static float ypr[3];
      */
      /*
      if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
        
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGyro(&gyro, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        //tmr = millis() + 11;
      
      }
      */
      
      
      if (abs(x) > 1) {
        
        GiroDedZone = map(z, -150, 150, -30, 30);  
        GiroDedZone = abs(GiroDedZone);
 
        if (x < -GiroDedZone) {
          
          x = map(x, -1, -prof(act_dir)->sensitivityHor, -prof(act_dir)->deadZoneHor, -32767);
          x = constrain(x, -32767, -1);
          bleGamepad.setZ(x);
        
        }
        
        else if (x > GiroDedZone) {
          
          x = map(x, 1, prof(act_dir)->sensitivityHor, prof(act_dir)->deadZoneHor, 32767);
          x = constrain(x, 1, 32767);
          bleGamepad.setZ(x);
        
        }
      
      } 
      
      else{

       bleGamepad.setZ(0); 
      
      }
      
      if (abs(z) > 1) {
        
        GiroDedZone = map(x, -150, 150, -30, 30);  
        GiroDedZone = abs(GiroDedZone);
 
        if (z < -GiroDedZone) {
          
          z = map(z, -1, -prof(act_dir)->sensitivityHor, prof(act_dir)->deadZoneHor, 32767);
          z = constrain(z, -32767, -1);
          bleGamepad.setRZ(z);
        
        }
        
        else if (z > GiroDedZone) {
          
          z = map(z, 1, prof(act_dir)->sensitivityHor, -prof(act_dir)->deadZoneHor, -32767);
          z = constrain(z, 1, 32767);
          bleGamepad.setRZ(z);
        
        }
      
      } 
      
      else{

       bleGamepad.setRZ(0); 
      
      }  
     
     bleGamepad.sendReport();
    
    } 
    
    else if (rightStickX || rightStickY){
          
      rightStickX = rightStickY = 0;
      bleGamepad.setRightThumb(0, 0);
      bleGamepad.sendReport();
    
    }    

  }
  
  void Gf_AnLeftDpad(){
  
    static bool flag;
    int AxisX = constrain(analogRead(39), 45, 4050);
    int AxisY = constrain(analogRead(36), 45, 4050);
    
    
    if ((AxisX < 1800 || AxisX > 2200) || (AxisY < 1800 || AxisY > 2200)) {
    
      if (!Ae_BatR(b_emul_dpad)) {
      
        if (flag) {
          
          flag = 0;
          bleGamepad.setHat1(HAT_CENTERED);
        
        }
        
        AxisX = map(AxisX, 45, 4050, -32767, 32767);
        AxisY = map(AxisY, 45, 4050, -32767, 32767);
        bleGamepad.setLeftThumb(AxisX, AxisY); 
    
      }
  
      else{ 
      
        if (!flag) {
        
         flag = 1;
         bleGamepad.setLeftThumb(0, 0); 
      
        }
       
        if (AxisX < 1800 || AxisX > 2200) {
          
          AxisX = constrain(AxisX, 1800, 2200);
          AxisX = map(AxisX, 1800, 2200, 2, 4);
        
        }
        
        else{

         AxisX = 0;
        
        }
        
        if (AxisY < 1800 || AxisY > 2200) {
          
          AxisY = constrain(AxisY, 1800, 2200);
          AxisY = map(AxisY, 1800, 2200, 1, 6);
        
        }
        
        else{

         AxisY = 0;
        
        }
        
        const byte dpadArr[] = {0, 1, 3, 2, 7, 8, 5, 0, 4, 0, 6};
        bleGamepad.setHat1(dpadArr[AxisX+AxisY]);
      
      }
        
    }    
        
    else{
     
     bleGamepad.setLeftThumb(0, 0); 
     bleGamepad.setHat1(0);
    
    }    
        
  }      
 
  void Gf_gamepadButtons(){

    if (Ae_BatR(prof(act_dir)->buttonArray[1])) {
     
     bleGamepad.pressSelect();
    
    }
    
    else{

     bleGamepad.releaseSelect();
   
    }
    
    if (Ae_BatR(prof(act_dir)->buttonArray[2])) {
     
     bleGamepad.pressStart();
    
    }
    
    else{

     bleGamepad.releaseStart();
   
    }
    
   
    if (Ae_BatR(prof(act_dir)->buttonArray[11])) {
     
     bleGamepad.setLeftTrigger(32767);       
    
    }
    
    else{

     bleGamepad.setLeftTrigger(0);
   
    }
    
    if (Ae_BatR(prof(act_dir)->buttonArray[12])) {
     
     bleGamepad.setRightTrigger(32767);
    
    }
    
    else{

     bleGamepad.setRightTrigger(0);
   
    }
   
    for (int i = 3; i <= 12; i++) {
          
      if (Ae_BatR(prof(act_dir)->buttonArray[i])) {
       
       bleGamepad.press(i);      
      
      }      
      
      else{
        
       bleGamepad.release(i);
      
      }
    
    }
  
  }

//Gf

void setup() {
  
  Serial.begin(9600);// В ФІНАЛІ НЕЗАБУТИ ЗАКОМЕНТУВАТИ!!!
  bleGamepad.begin();
  EEPROM.begin(512);
  prof(eeprom_initialization);//ініціалізація з енергонезалежної пам'яті профілів
  
  pinMode(3, OUTPUT);//подаємо високий сигнал на транзистор щоб заживити  модулі
  digitalWrite(3, HIGH);
  
  pinMode(32, INPUT_PULLUP);
  pinMode(15, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);
  /*
  byte pinsWithPullup[] = {33, 13, 27, 26, 25, 16, 17, 5, 23, 2, 18, 19, 32, 15, 4, 12, 14};//добавити 1

  for (int i = 0; i < sizeof(pinsWithPullup); i++) {
   
    pinMode(pinsWithPullup[i], INPUT_PULLUP);

  }
  
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 0);
  
  hw_timer_t * timer = NULL;
  timer = timerBegin(0, 80, true); // Таймер 0, прескалер 80 (для ESP32), true для повторення таймера
  timerAttachInterrupt(timer, &Gf_GyroAnalogR, true); // Встановлюємо функцію, яка буде викликана таймером
  timerAlarmWrite(timer, 11000, true); // Встановлюємо час спрацьовування таймера (11 мілісекунд)
  timerAlarmEnable(timer); // Вмикаємо таймер
  
  ina219.setADCMode(BIT_MODE_12); // Устанавливаем разрешение АЦП 12 бит
  ina219.setPGain(PG_40); // Ток в пределах 400 мА
  ina219.setBusRange(BRNG_16); // Напряжение до 16 В
  
  */
  
  attachInterrupt(digitalPinToInterrupt(p_dt), Ae_encoderInterrupt, CHANGE);
  attachInterrupt(digitalPinToInterrupt(p_clk), Ae_encoderInterrupt, CHANGE);
  

  //----------Display--------------
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextSize(1);
  display.setTextColor(WHITE);
  //gyroStart();//при потребі проводимо калібровку якщо калібровка виконана виконуємо старт функції гіроскопа
  //startAnimation();//анімація при включенні до робити в кінці

}


void loop() {
  
 
  //Ae_autoShutdown();
  //Ae_buttonOnOff();
  //Ae_batteryCheck();
  Md_Display();
  
  /*
  if (bleGamepad.isConnected()) {
    
    //Gf_GyroAnalogR();
    Gf_AnLeftDpad();
    Gf_gamepadButtons();
    bleGamepad.sendReport();
  
  }
*/
}//void loop()