//WS
// Coil Winder Arduino MEGA 2560 R3
// Укладчик 5000p/r или при шаге винта 5мм = 1000p/mm (Oriental Motor ARD-K ARM66SAK)
// 1мм = 1000p
// Двигатель катушки Panasonic Minas A5 200W 1000p/r

#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);  // Подключено по I2C ноги 20 и 21

// Клава
const byte ROWS = 4;
const byte COLS = 4;
char hexaKeys[ROWS][COLS] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};
byte rowPins[ROWS] = { 31, 33, 35, 37 };
byte colPins[COLS] = { 39, 41, 43, 45 };
Keypad keypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
// Клава

volatile uint16_t w = 0;      // Кол-во намотанных витков в слое
uint16_t w_temp = 0;
volatile uint16_t wt = 0;     // Кол-во витков всего.
uint16_t wt_temp = 0;
uint16_t wl = 0;              // Кол-во витков в слое расчетное.
uint16_t wl_temp = 0;
unsigned long pd = 0;         // Кол-во импульсов укладчика на один диаметр.
unsigned long pg = 0;         // Погрешность, остаток от кол-ва импульсов на все витки и кол-вом импульсов на всю ширину намотки
unsigned long pl = 0;         // Кол-во импульсов укладчика на один слой.
unsigned long pd10 = 0;       // Кол-во импульсов укладчика на 1/10 диаметра.
unsigned long bck = 0;        // Переменная для расчета возврата каретки без учета погрешности
unsigned long bk = 0;         // Переменная для цикла возврата каретки укладчика
uint8_t Lr = 0;               // Кол-во намотанных слоев
uint8_t Lr_temp = 0;
float wdt = 0;                // Ширина намотки
float d = 0;                  // Диаметр провода
uint16_t ii = 0;              // Переменная для цикла pd10
uint16_t iii = 0;             // Переменная для цикла компенсации погрешности в слое
uint16_t prev_iii = 0;
uint8_t iu = 0;               // Переменная для цикла ручного управления укладчиком
uint8_t ik = 0;               // Переменная для цикла ручного управления катушкой
volatile uint8_t cont = 0;    // Переменная счетчика витков, max value 255, энкодер 100p/r.
volatile uint8_t prev_cont = 0;
uint8_t acc = 1;              // Переменная для Non Stop режима. Снижение скорости вначале и конце слоя. Зависит от режима скорости, обычный 7.5 об/сек или высокий 15 об/сек.

// Переменные для ввода диаметра провода и ширины
static int a[3] = { 0, 0, 0 };
static int t = 0;
static int b[4] = { 0, 0, 0, 0};
static int p = 0;
// Переменные для ввода диаметра провода и ширины

// Флаги
bool f_dir = 0;       // Флаг направления укладчика
bool f_auto = 0;      // Флаг нажатия кнопки "С" старт, режим auto/manual
bool f_ukl = 0;       // Флаг укладчик = 0 / катуха = 1
bool f_complete = 0;  // Флаг завершения намотки слоя
bool f_servo = 0;     // Флаг скорости катушки, Low = 0, High = 1.
bool f_ready = 0;     // Флаг для вкл/выкл серво привод катушки Servo-On/Off;
bool f_pg = 0;        // Флаг для погрешности
bool f_back = 0;      // Флаг возврата каретки укладчика для отключения энкодеров во время работы цикла
bool q = 0;           // Флаг ввода ширины намотки
bool i = 0;           // Флаг ввода диаметра провода
bool f_highspd = 0;   // Флаг скорости вращения катушки. 7.5об/сек или 15 об/сек.
bool f_revers = 0;    // Флаг реверса катушки для смотки провода
bool f_nstp = 0;      // Флаг для режима полной автонамотки
bool f_acc = 1;       // Флаг включения / отключения режима замедления в начале и конце слоя в режиме Non Stop
bool f_pgnstp = 0;    // Флаг включения / отключения учета погрешности в конце слоя
// Флаги

volatile bool d2 = 0;  // Переменная опроса состояния входа D2 для энкодера счетчика.
volatile bool d5 = 0;  // Переменная опроса состояния входа D5 для энкодера счетчика.
volatile bool d3 = 0;  // Переменная опроса состояния входа D3 для энкодера ручного управления.
volatile bool d4 = 0;  // Переменная опроса состояния входа D4 для энкодера ручного управления.

void setup() {
  lcd.init();
  lcd.backlight();
  lcd.clear();
  keypad.addEventListener(keypadEvent);
  cli();  // Запрещаем все прерывания.

  DDRE &= ~(1 << 3);                      // Определяем контакт платы 5 как вход (D5 PE3) CountB
  DDRE &= ~(1 << 4);                      // Определяем контакт платы 2 как вход (D2 PE4) CountA
  DDRE &= ~(1 << 5);                      // Определяем контакт платы 3 как вход (D3 PE5) EncA
  DDRG &= ~(1 << 5);                      // Определяем контакт платы 4 как вход (D4 PG5) EncB
  DDRH |= (1 << 3);                       // Определяем контакт платы 6 как выход (D6 PH3) PUL+K
  DDRH |= (1 << 4);                       // Определяем контакт платы 7 как выход (D7 PH4) DIR+K
  DDRH |= (1 << 5);                       // Определяем контакт платы 8 как выход (D8 PH5) PUL+U
  DDRH |= (1 << 6);                       // Определяем контакт платы 9 как выход (D9 PH6) DIR+U
  DDRB |= (1 << 7);                       // Определяем контакт платы 13 как выход (D13 PB7) STgen
  DDRB |= (1 << 6);                       // Определяем контакт платы 12 как выход (D12 PB6) Servo-On
  DDRB |= (1 << 5);                       // Определяем контакт платы 11 как выход (D11 PB5) High Speed. 300rpm или 900rpm катушки.
  DDRB |= (1 << 4);                       // Определяем контакт платы 10 как выход (D10 PB4) ACC
  SREG |= (1 << 7);                       // Разрешаем все прерывания.
  EICRB |= (1 << ISC40) | ~(1 << ISC41);  // Прерывание по событию CHANGE. ISC40 = 1; ISC41 = 0;
  EICRB |= (1 << ISC50) | ~(1 << ISC51);  // Прерывание по событию CHANGE. ISC50 = 1; ISC51 = 0;
  EIMSK |= (1 << INT4);                   // Разрешаем прерывание INT4 на второй ноге ардуины (PE4).
  EIMSK |= (1 << INT5);                   // Разрешаем прерывание INT5 на третьей ноге ардуины (PE5).

  sei();  // Разрешаем все прерывания.

  digitalWrite(12, HIGH);  // Включаем Servo-On при старте

  lcd.setCursor(0, 0);
  lcd.print("A Wire");
  lcd.setCursor(0, 1);
  lcd.print("B Width");
  lcd.setCursor(0, 3);
  lcd.print("D Direction");
  lcd.setCursor(0, 2);
  lcd.print("C");
  lcd.setCursor(2, 2);
  lcd.print("Start");
  lcd.setCursor(10, 2);
  lcd.print("Lr:");
  lcd.setCursor(13, 2);
  lcd.print(Lr);  // Вывод количества намотанных слоев
  lcd.setCursor(17, 0);
  lcd.print("Ukl");
  lcd.setCursor(16, 2);
  lcd.print(w);  // Вывод количества намотанных витков в слое
  lcd.setCursor(16, 1);
  lcd.print(wt);  // Вывод количества намотанных витков всего
  lcd.setCursor(16, 3);
  lcd.print(wl);  // Вывод количества расчетных витков в слое
}

void loop() {
  char key = keypad.getKey();

  // Расчет
  pd = d * 1000;        // Кол-во импульсов на один диаметр провода
  pd10 = pd / 10;       // Кол-во импульсов укладчика на 1/10 диаметра.
  wl = wdt / d;         // Кол-во витков в слое с округлением до меньшего.
  pl = wdt * 1000;      // Кол-во импульсов на всю ширину
  pg = pl - (wl * pd);  // Погрешность, остаток от кол-ва импульсов на все витки в слое и кол-вом импульсов на всю ширину намотки
  
  if(f_pgnstp == 0){bck = pl - pg;} // Расчет значения переменной для возврата каретки с учетом погрешности или без
    else{bck = pl;}
  // Расчет


  if (wl_temp != wl) {
    lcd.setCursor(16, 3);
    lcd.print("    ");
    lcd.setCursor(16, 3);
    lcd.print(wl);  // Вывод расчетного кол-ва витков в слое.
    wl_temp = wl;
  }

  if (w_temp != w) {
    lcd.setCursor(16, 2);
    lcd.print("    ");
    lcd.setCursor(16, 2);
    lcd.print(w);  // Вывод количества намотанных витков в слое
    w_temp = w;
  }

  if (wt_temp != wt) {
    lcd.setCursor(16, 1);
    lcd.print("    ");
    lcd.setCursor(16, 1);
    lcd.print(wt);  // Вывод количества намотанных витков всего
    wt_temp = wt;
  }


  // Замедление скорости вращения катушки в начале и конце слоя при Non Stop режиме
  if(f_nstp == 1){
    if (f_acc == 1 && (wl - w <= acc || w <= acc)){PORTB |= 1<<4;}   // Если выбран режим Non Stop, переключаем генератор в режим разгона и замедления в начале и конце слоя.
      else {PORTB &= ~(1<<4);}
    if (f_highspd == 0 && d < 0.5){acc = 2; }   // Если диаметр провода меньше 0,5мм и выбран низкий режим скорости, то начало разгона и замедления 2 витка.
    if (f_highspd == 0 && d >= 0.5){acc = 1; }  // Если диаметр провода больше или равен 0,5мм и выбран низкий режим скорости, то начало разгона и замедления 1 виток.
    if (f_highspd == 1 && d < 0.5){acc = 2;}   // Если диаметр провода меньше 0,5мм и выбран высокий режим скорости, то начало разгона и замедления 2 витка.
    if (f_highspd == 1 && d >= 0.5){acc = 1; }  // Если диаметр провода больше или равен 0,5мм и выбран высокий режим скорости, то начало разгона и замедления 1 виток.
   }
  // Замедление скорости вращения катушки в начале и конце слоя при Non Stop режиме

  // Замедление скорости вращения катушки в конце слоя в авто режиме
  if(f_nstp == 0){
    if (f_acc == 1 && wl - w <= acc ){PORTB |= 1<<4;}   // Переключаем генератор в режим замедления в конце слоя.
      else {PORTB &= ~(1<<4);}
    if (f_highspd == 0 && d < 0.5){acc = 3; }   // Если диаметр провода меньше 0,5мм и выбран низкий режим скорости, то начало замедления 3 витка до конца слоя.
    if (f_highspd == 0 && d >= 0.5){acc = 2; }  // Если диаметр провода больше или равен 0,5мм и выбран низкий режим скорости, то начало замедления 2 витка до конца слоя.
    if (f_highspd == 1 && d < 0.5){acc = 5;}   // Если диаметр провода меньше 0,5мм и выбран высокий режим скорости, то начало замедления 5 витков до конца слоя.
    if (f_highspd == 1 && d >= 0.5){acc = 3; }  // Если диаметр провода больше или равен 0,5мм и выбран высокий режим скорости, то начало замедления 3 витка до конца слоя.
  }
  // Замедление скорости вращения катушки в конце слоя в авто режиме


  // Обработка после завершения намотки слоя
  if (w == wl && wl > 0 && f_complete == 1) {
    if (f_nstp == 0) {PORTB &= ~(1 << 7);}  // Выключаем GEN на катушку OFF LOW 
    // Погрешность в конце слоя
    if(f_pgnstp == 1){
      while (iii < pg) {
        PORTH |= (1 << 5);     //PIN8 PUL+U = HIGH
        delayMicroseconds(2);  
        PORTH &= ~(1 << 5);    //PIN8 PUL+U = LOW
        delayMicroseconds(2);
        iii++;
      }
    }
    // Погрешность в конце слоя
    if (f_nstp == 1){w = 0;} // Сброс кол-ва витков в слое в полностью авто режиме
    Lr++;
    f_dir = !f_dir;
    if (f_nstp == 0) {
      f_complete = 0; // Флаг завершения намотки слоя
      f_auto = 0;
      } 
    iii = 0;
    if (f_nstp == 0){
     lcd.setCursor(2, 2);
     lcd.print("Start");
    }
  }

  if (wl - w == 1 && f_complete == 0){ //Если после завершения намотки слоя вы решили отмотать витки обратно
    Lr--;
    f_dir = !f_dir;
    f_complete = 1;
  }
  // Обработка после завершения намотки слоя

  if (Lr_temp != Lr) {
    lcd.setCursor(13, 2);
    lcd.print("  ");
    lcd.setCursor(13, 2);
    lcd.print(Lr);  // Вывод количества слоев
    Lr_temp = Lr;
  }

  // Отображение направления укладчика
  if (f_dir == 1) {
    PORTH |= (1 << 6);  // DIR на укладчик HIGH D9
    lcd.setCursor(12, 3);
    lcd.print("<--");
  } else {
    PORTH &= ~(1 << 6);  // DIR на укладчик LOW D9
    lcd.setCursor(12, 3);
    lcd.print("-->");
  }
  // Отображение направления укладчика

  if (wl > 0) {  // Если расчетных витков больше нуля, введен диаметр провода и ширина намотки, то разрешаем авто старт
    f_ready = 1;
  }
  if (w >= wl && wl > 0) {  //После намотки полного слоя запрещаем авто старт до сброса кол-ва витков в слое
    f_ready = 0;
  }
}

// Ручное управление
void manual() {
  // Ручное управление укладчиком
  if (f_ukl == 0 && f_auto == 0) {
    d3 = PINE & (1 << PINE5);
    d4 = PING & (1 << PING5);
    if (d3 == HIGH && d4 == HIGH) {
      PORTH |= (1 << 6);  // Выбираем направление укладчика. Сигнал DIR = HIGH D9
    }
    delayMicroseconds(3);
    if (d3 == HIGH && d4 == LOW) {
      PORTH &= ~(1 << 6);  // Выбираем направление укладчика. Сигнал DIR = LOW D9
    }
    delayMicroseconds(3);
    while (iu < 6) {         //При 1 = 12,5 оборотов энкодера(400p/r) при 5000p/r двигателя.
      PORTH |= (1 << 5);     // Пишем в порт D8 высокий уровень сигнала PUL
      delayMicroseconds(3);  
      PORTH &= ~(1 << 5);    // Пишем в порт D8 низкий уровень сигнала PUL
      delayMicroseconds(3);
      iu++;
    }
    iu = 0;
  }
  // Ручное управление укладчиком

  // Ручное управление катушкой
  if (f_ukl == 1 && f_auto == 0) {
    d3 = PINE & (1 << PINE5);
    d4 = PING & (1 << PING5);
    if (d3 == HIGH && d4 == HIGH) {
      PORTH &= ~(1 << 4);  // Выбираем направление катушки. Сигнал DIR = HIGH D7 
    }
    delayMicroseconds(5);
    if (d3 == HIGH && d4 == LOW) {
      PORTH |= (1 << 4);  // Выбираем направление катушки. Сигнал DIR = LOW D7 
    }
    delayMicroseconds(5);
    while (ik < 3) {         //Двигатель 1000p/r плюс редуктор 4:1, итого 4000p/r катушки. Энкодер 400p/r. При счетчике равном ik = 3 имеем 4000/1200=3,3 оборота энкодера на оборот катушки.
      PORTH |= (1 << 3);     // Пишем в порт D6 высокий уровень сигнала PUL
      delayMicroseconds(5);  
      PORTH &= ~(1 << 3);    // Пишем в порт D6 низкий уровень сигнала PUL
      delayMicroseconds(5);
      ik++;
    }
    ik = 0;
  }
  // Ручное управление катушкой
}
// Ручное управление


// Пачка импульсов на укладчик, одна десятая от всего на один шаг/оборот
void step() {
  // Остаток от деления на 10. После 10-ти импульсов счетчика (36 градусов поворота катухи)
  // Выдаем пачку импульсов на укладчик в кол-ве 1/10 от посчитанного на один диаметр провода.
  if (f_back == 0 && prev_cont != cont && wl > 0) {
    while (ii < pd10) {
      PORTH |= (1 << 5);     //PIN8 PUL+U = HIGH
      delayMicroseconds(2);  
      PORTH &= ~(1 << 5);    //PIN8 PUL+U = LOW
      delayMicroseconds(2);
      ii++;
    }
    if (ii == pd10) {
      ii = 0;
      prev_cont = cont;
    }
  }
}
// Пачка импульсов на укладчик, одна десятая от всего на один шаг/оборот


// Прерывание для ручного управления катушкой и укладчиком
ISR(INT5_vect) {
  d3 = PINE & (1 << PINE5);
  d4 = PING & (1 << PING5);
  if (d3 == HIGH && d4 == HIGH && f_back == 0) {
    PORTH |= (1 << 6);  // Выбираем направление укладчика. Сигнал DIR = HIGH D9
  }
  if (d3 == HIGH && d4 == LOW && f_back == 0) {
    PORTH &= ~(1 << 6);  // Выбираем направление укладчика. Сигнал DIR = LOW D9
  }
  if (f_back == 0) { manual(); }
}
// Прерывание для ручного управления катушкой и укладчиком


// Укладчик - счетчик
ISR(INT4_vect) {
  d2 = PINE & (1 << PINE4);
  d5 = PINE & (1 << PINE3);
  if (d2 == HIGH && d5 == HIGH && f_back == 0) {
    if (f_dir == 1) { PORTH |= (1 << 6); }   // PIN9 DIR+U = HIGH сразу в прерывании меняем направление укладчика.
    if (f_dir == 0) { PORTH &= ~(1 << 6); }  // PIN9 DIR+U = LOW сразу в прерывании меняем направление укладчика.
    if (cont == 100) {
      cont = 0;
      w++;
      wt++;
    }
    cont++;
  }
  if (d2 == HIGH && d5 == LOW && f_back == 0) {
    if (f_dir == 1) { PORTH &= ~(1 << 6); }  // PIN9 DIR+U = LOW сразу в прерывании меняем направление укладчика.
    if (f_dir == 0) { PORTH |= (1 << 6); }   // PIN9 DIR+U = HIGH сразу в прерывании меняем направление укладчика.
    if (cont == 0 && w > 0) {
      cont = 100;
      if (w > 0) { w--; }
      if (wt > 0) { wt--; }
    }
    if (cont > 0) { cont--; }
  }
  if (cont % 10 == 0) {step();}
}
// Укладчик - счетчик


// Клава
void keypadEvent(KeypadEvent key) {
  switch (keypad.getState()) {
    case PRESSED:

      // Выбор ручного управления укладчиком или катухой
      if (key == '*' && f_auto == 0 && f_ukl == 0) {
        key = 0;
        q = 0;
        i = 0;
        f_ukl = 1;
        lcd.setCursor(17, 0);
        lcd.print("Kat");
      }

      if (key == '*' && f_auto == 0 && f_ukl == 1) {
        key = 0;
        q = 0;
        i = 0;
        f_ukl = 0;
        lcd.setCursor(17, 0);
        lcd.print("Ukl");
      }
      // Выбор ручного управления укладчиком или катухой


      // Сброс кол-ва витков в слое
      if (key == '#' && f_auto == 0) {
        key = 0;
        q = 0;
        i = 0;
        cont = 0;
        w = 0;
        f_complete = 1;
        iii = 0;
        f_pg = 0;
        lcd.setCursor(16, 2);
        lcd.print("    ");
        lcd.setCursor(16, 2);
        lcd.print(w);
      }
      // Сброс кол-ва витков в слое


      // Start / Pause
      if (key == 'C' && f_auto == 0 && f_ready == 1 && wl > 0) {
        key = 0;
        q = 0;
        i = 0;
        f_auto = 1;
        lcd.setCursor(9, 0);
        lcd.print(" ");
        lcd.setCursor(9, 1);
        lcd.print(" ");
        lcd.setCursor(2, 2);
        lcd.print("Stop ");
        PORTB |= (1 << 7);                          // GEN на катушку D13 HIGH
        if (f_revers == 0) { PORTH |= (1 << 4); }   // DIR на катушку D7 HIGH
        if (f_revers == 1) { PORTH &= ~(1 << 4); }  // DIR на катушку D7 LOW
      }

      if (key == 'C' && f_auto == 1) {
        key = 0;
        q = 0;
        i = 0;
        f_auto = 0;
        lcd.setCursor(2, 2);
        lcd.print("Start");
        PORTB &= ~(1 << 7);  // GEN на катушку LOW
      }
      // Start / Pause


      // Direction
      if (key == 'D' && f_auto == 0) {
        key = 0;
        q = 0;
        i = 0;
        f_dir = !f_dir;
      }
      // Direction


      // Сброс ранее набранных значений диаметра и ширины
      if (key == 'A' && f_auto == 0) {
        key = 0;
        a[0] = 0;
        a[1] = 0;
        a[2] = 0;
        t = 0;
        i = 1;
        q = 0;
        f_complete = 1;
        lcd.setCursor(10, 0);
        lcd.print("     ");
        lcd.setCursor(10, 0);
        lcd.print(d, 2);
        lcd.setCursor(10, 1);
        lcd.print(wdt,1);
      }

      if (key == 'B' && f_auto == 0) {
        key = 0;
        b[3] = 0;
        b[2] = 0;
        b[1] = 0;
        b[0] = 0;
        p = 0;
        i = 0;
        q = 1;
        wdt = 0;
        lcd.setCursor(10, 1);
        lcd.print("     ");
        lcd.setCursor(10, 1);
        lcd.print(wdt,1);
      }
      // Сброс ранее набранных значений диаметра и ширины


      // Ввод диаметра провода с клавы начало
      if (i == 1) {
        lcd.setCursor(9, 0);
        lcd.print("*");
        lcd.setCursor(9, 1);
        lcd.print(" ");
        if (key == 'C') { key = 0; }
        if (key) {
          a[t] = key - 48;
          t++;
          if (t == 3) {
            t = 0;
          }
        }
        d = a[0] + 0.1 * a[1] + 0.01 * a[2];
        lcd.setCursor(10, 0);
        lcd.print(d, 2);
      }
      // Ввод диаметра провода с клавы  конец


      // Ввод ширины катухи начало
      if (q == 1) {
        lcd.setCursor(9, 1);
        lcd.print("*");
        lcd.setCursor(9, 0);
        lcd.print(" ");
        if (key == 'C') { key = 0; }
        if (key) {
          b[p] = key - 48;
          p++;
          if (p == 4) {
            p = 0;
          }
        }
        wdt = (b[0] + 0.1 * b[1] + 0.01 * b[2] + 0.001 * b[3]) * 100.001;
        lcd.setCursor(10, 1);
        lcd.print(wdt,1);
      }
      // Ввод ширины катухи конец

      break;


    case HOLD:

      // Возврат каретки в начальное положение слоя
      if (key == 'D' && f_auto == 0) {
        f_back = 1;  // Флаг возврата каретки укладчика для отключения энкодеров во время работы цикла
        cli();       // Выключить прерывания
        if (f_dir == 1) {
          digitalWrite(9, LOW);
          delayMicroseconds(10);  //Задержка после сигнала DIR
        } else {
          digitalWrite(9, HIGH);
          delayMicroseconds(10);  //Задержка после сигнала DIR
        }
        for (bk = 0; bk <= bck; bk++) {
          PORTH |= (1 << 5);  //Контакт платы 8 как выход (D8 PH5) PUL+U, пишем в него HIGH
          delayMicroseconds(3);
          PORTH &= ~(1 << 5);  //Контакт платы 8 как выход (D8 PH5) PUL+U, пишем в него LOW
          delayMicroseconds(3);
          if (bk == bck) {
            f_back = 0;  // Флаг возврата каретки укладчика
          }
        }
        sei();  // Включить прерывания
      }
      // Возврат каретки в начальное положение слоя


      // Сброс всех значений счетчиков
      if (key == '#' && f_auto == 0) {
        w = 0;
        wt = 0;
        Lr = 0;
        cont = 0;
        f_complete = 1;
        lcd.setCursor(13, 2);
        lcd.print("  ");
        lcd.setCursor(13, 2);
        lcd.print(Lr);
        lcd.setCursor(16, 1);
        lcd.print("    ");
        lcd.setCursor(16, 1);
        lcd.print(wt);
        lcd.setCursor(16, 2);
        lcd.print("    ");
        lcd.setCursor(16, 2);
        lcd.print(w);
      }
      // Сброс всех значений счетчиков


      // Servo=On/Off
      if (key == 'C' && f_auto == 0 && f_servo == 0 && wl == 0) {
        key = 0;
        lcd.setCursor(15, 2);
        lcd.print("S");
        f_servo = 1;
        digitalWrite(12, LOW);
      }

      if (key == 'C' && f_auto == 0 && f_servo == 1 && wl == 0) {
        key = 0;
        lcd.setCursor(15, 2);
        lcd.print(" ");
        f_servo = 0;
        digitalWrite(12, HIGH);
      }
      // Servo=On/Off

      // High Speed On/Off
      if (key == '0' && f_highspd == 0) {
        key = 0;
        lcd.setCursor(15, 3);
        lcd.print("H");
        f_highspd = 1;
        digitalWrite(11, HIGH);
      }

      if (key == '0' && f_highspd == 1) {
        key = 0;
        lcd.setCursor(15, 3);
        lcd.print(" ");
        f_highspd = 0;
        digitalWrite(11, LOW);
      }
      // High Speed On/Off


      // Реверс катушки  On/Off
      if (key == '*' && f_revers == 0 && f_auto == 0) {
        key = 0;
        lcd.setCursor(15, 1);
        lcd.print("R");
        f_revers = 1;
      }

      if (key == '*' && f_revers == 1 && f_auto == 0) {
        key = 0;
        lcd.setCursor(15, 1);
        lcd.print(" ");
        f_revers = 0;
      }
      // Реверс катушки On/Off

      // Полный авто режим Non Spot
      if (key == 'A' && f_nstp == 0 && f_auto == 0) {
        key = 0;
        lcd.setCursor(15, 0);
        lcd.print("N");
        f_nstp = 1;
      }

      if (key == 'A' && f_nstp == 1 && f_auto == 0) {
        key = 0;
        lcd.setCursor(15, 0);
        lcd.print(" ");
        f_nstp = 0;
      }
      // Полный авто режим Non Stop

      // Включение / отключение режима замедления в начале и конце слоя в режиме Non Stop
      if (key == '1' && f_acc == 1 && f_auto == 0) {
        key = 0;
        lcd.setCursor(8, 0);
        lcd.print("A");
        f_acc = 0;
      }

      if (key == '1' && f_acc == 0 && f_auto == 0) {
        key = 0;
        lcd.setCursor(8, 0);
        lcd.print(" ");
        f_acc = 1;
      }
      // Включение / отключение режима замедления в начале и конце слоя в режиме Non Stop

      // Включение / отключение учета погрешности в конце слоя при режиме Non Stop
      if (key == 'B' && f_pgnstp == 0 && f_auto == 0) {
        key = 0;
        lcd.setCursor(8, 1);
        lcd.print("P");
        f_pgnstp = 1;
      }

      if (key == 'B' && f_pgnstp == 1 && f_auto == 0) {
        key = 0;
        lcd.setCursor(8, 1);
        lcd.print(" ");
        f_pgnstp = 0;
      }
      // Включение / отключение учета погрешности в конце слоя при режиме Non Stop
      break;
  }
}
// Клава