DTG printer / принтер планшетный

я в курсе, но ему она не нужна, если она не по технологии стелс

Да и я, как бы , не нуждаюсь)).

Мне было интересно проверить , как я это понимаю.
И, поищу ещё, может найду что-нибудь путёвое на тему использования принтера для “печати печатных” плат

пока с алика жду комплектующие для оси Z и длинные шлейфы для платы и платы для понижения напряжения 5- 1.8 и 5-3.3 в для датчика бумаги решил еще раз подумать как можно добавить джойстик в код и его не испортить .

как помните , одной строчки int joystickValue = analogRead(JOYSTICK_PIN); хватает , что бы сломать ту часть кода которая отвечает за прерывания .
что бы код работал нужно в функции void loop() не было намека на порт который отвечает за аналоговые входы .
и как понимаете это сделать в чистую не получится . может быть если я перейду на код с pid регуляторами . но пока я этого не хочу .
а вот сделать код джойстика через одно место и это место включается через другое такое же место это я справился . и джойстик в коде и работает и не мешает но есть одна особенность его нужно включать и выключать с помощью кнопки button2 . в коде сами гляните .
был второй вариант не менее экзотичный .
убрать строки в void loop() зависимости мотора от энкодера .
и тут новый сюрприз . а их некуда убрать как только в цикл энкодера их засунуть но тогда ломается весь подсчет мотор энкодера . печать настраиваешь коэффициентом , подбираемый для соотношения между мотором и энкодером а счетчик ломается и можно настроить только печать например на деталь определенного размера и она отпечатается а вторая будет в два раза больше или меньше и она отпечатается искаженная .
код от Arduman и от Дим-мычъ они немного разные но работают одинаково хорошо есть отличия в подсчете и точности но если перенести обновления позиции мотора в void loop() то все будет одинаково ровно как и вынести эти строчки из void loop() .
поэтому выхода и не осталось как включать и отключать функцию джойстика с кнопки .
это не очень удобно перед печатью нажимать на кнопку отключения этой функции а если забыл и стол пополз на печать нужно быстро нажать на эту кнопку .
когда доделаю ось Z и плату принтера уберу в короб к плате ардуино и сделаю включения датчика бумаги с другой логикой я продолжу тесты и возможно откажусь от джойстика а протяжку стола подвешу на кнопки .

Вот пояснения к коду для большей ясности:
В этом коде реализуется управление шаговым мотором с помощью энкодера, кнопок и джойстика. Основные функции включают обработку прерываний для считывания данных с энкодера, выполнение шагов мотора, а также управление состоянием различных компонентов .
печать работает правильно без искажений и 100% повторяемостью в программе Corel PHOTO-PAINT в других возможно будет не так .
но нужно много еще тестиить .

// Определяем пины для энкодера и других компонентов. Используется внутренняя подтяжка.
int16_t enc, motorPos; // Переменные для хранения значений энкодера и позиции мотора
float Kmotor = 0.2945; // Коэффициент, подбираемый для соотношения между мотором и энкодером

// Определяем пины для различных компонентов
#define ENC_A       2 // Пин для первой фазы энкодера
#define ENC_B       3 // Пин для второй фазы энкодера
#define ENABLE_PIN  8 // Пин для включения/выключения драйвера мотора
#define STEP_PIN    4 // Пин для подачи сигнала на шаговый мотор
#define DIR_PIN     7 // Пин для определения направления вращения мотора
#define limit_1     9 // Пин для первого лимитного переключателя
#define limit_2    10 // Пин для второго лимитного переключателя
#define button1     12 // Пин для первой кнопки вкл. откл датчик бумаги и драйвер мотора
#define button2     5  // Пин для второй кнопки вкл. откл JOYSTICK
#define mosfet      6  // Пин для управления MOSFET (включает датчик бумаги)
#define JOYSTICK_PIN A0 // Пин для джойстика

bool joystickEnabled = true; // Переменная для отслеживания состояния джойстика
int knopka = 0; // Счетчик для состояния кнопки
int T = 10; // Переменная для временной задержки

void setup() {
    Serial.begin(115200); // Инициализация последовательного порта
    pinMode(ENC_A, INPUT_PULLUP); // Установка пина энкодера A как вход с подтяжкой
    pinMode(ENC_B, INPUT_PULLUP); // Установка пина энкодера B как вход с подтяжкой
    pinMode(ENABLE_PIN, OUTPUT); // Установка пина включения драйвера как выход
    pinMode(STEP_PIN, OUTPUT); // Установка пина шага как выход
    pinMode(DIR_PIN, OUTPUT); // Установка пина направления как выход
    pinMode(limit_1, INPUT_PULLUP); // Установка первого лимитного переключателя как вход с подтяжкой
    pinMode(limit_2, INPUT_PULLUP); // Установка второго лимитного переключателя как вход с подтяжкой
    pinMode(button1, INPUT_PULLUP); // Установка первой кнопки как вход с подтяжкой
    pinMode(button2, INPUT_PULLUP); // Установка второй кнопки как вход с подтяжкой
    pinMode(mosfet, OUTPUT); // Установка пина MOSFET как выход
    digitalWrite(ENABLE_PIN, LOW); // Включаем драйвер мотора
    PCIFR = PCIF2; // Сбрасываем флаг прерывания
    PCICR = 1 << PCIE2; // Разрешаем прерывание для группы пинов
    PCMSK2 = 1 << PCINT18 | 1 << PCINT19; // Выбираем входы для срабатывания прерывания
}

// Обработчик прерывания для энкодера
ISR(PCINT2_vect) {
    static char EncPrev = 0; // Предыдущее состояние энкодера
    static char EncPrevPrev = 0; // Пред-предыдущее состояние энкодера
    char EncCur = 0; // Текущее состояние энкодера

    // Считываем состояние фаз энкодера
    if (!(PIND & (1 << PD2))) { EncCur = 1; } // Проверка первой фазы
    if (!(PIND & (1 << PD3))) { EncCur |= 2; } // Проверка второй фазы

    // Проверяем, изменилось ли состояние энкодера
    if (EncCur != EncPrev) {
        // Если предыдущее состояние было 3 и текущее состояние не равно пред-предыдущему
        if (EncPrev == 3 && EncCur != EncPrevPrev) {
            if (EncCur == 2) 
                enc++; // Увеличиваем значение энкодера
            else                          
                enc--; // Уменьшаем значение энкодера
        }

        EncPrevPrev = EncPrev; // Сохраняем пред-предыдущее состояние
        EncPrev = EncCur; // Сохраняем предыдущее состояние
    }
}

// Функция для выполнения шага мотора 
void Step(bool Dir) {
    if (Dir) { 
        digitalWrite(DIR_PIN, 0); // Устанавливаем направление шага вперед
        motorPos++; // Увеличиваем позицию мотора
    } else { 
        digitalWrite(DIR_PIN, 1); // Устанавливаем направление шага назад
        motorPos--; // Уменьшаем позицию мотора
    }
    // Выполняем шаг:
    digitalWrite(STEP_PIN, HIGH); // Активируем шаг
    delayMicroseconds(1); // Задержка для выполнения шага
    digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
}

void loop() {
    // Обновляем позицию мотора в зависимости от энкодера
    int16_t deltaPos = enc - ((int16_t) motorPos * Kmotor);
    if (deltaPos >= 1) Step(1); // Если требуется шаг вперед
    if (deltaPos <= -1) Step(0); // Если требуется шаг назад

    handleButton(); // Проверяем состояние кнопки
    if (joystickEnabled) {
        handleJoystick(); // Обрабатываем джойстик только если разрешено
    }

    // Проверка состояния первого лимитного переключателя
    if (!digitalRead(limit_1)) { // Если переключатель активирован
        digitalWrite(mosfet, HIGH); // Включаем датчик бумаги
        PCICR = 0; // Запрещаем прерывание от энкодера
        digitalWrite(ENABLE_PIN, LOW); // Отключаем драйвер мотора
        digitalWrite(DIR_PIN, LOW); // Устанавливаем направление
        for (int i = 0; i < 2400; i++) { // Дистанция отъезда от переключателя
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(140); // Задержка для скорости отъезда
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
        PCICR = 1 << PCIE2; // Разрешаем прерывание от энкодера
        enc = 0; // Сбрасываем значение энкодера
        motorPos = 0; // Сбрасываем позицию мотора
    }

    // Проверка состояния второго лимитного переключателя
    if (!digitalRead(limit_2)) { // Если переключатель активирован
        digitalWrite(mosfet, LOW); // Отключаем датчик бумаги
        PCICR = 0; // Запрещаем прерывание от энкодера
        digitalWrite(ENABLE_PIN, LOW); // Отключаем драйвер мотора
        digitalWrite(DIR_PIN, HIGH); // Устанавливаем направление
        for (int i = 0; i < 1200; i++) { // Дистанция отъезда от переключателя
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(200); // Задержка для скорости отъезда
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
        PCICR = 1 << PCIE2; // Разрешаем прерывание от энкодера
        enc = 0; // Сбрасываем значение энкодера
        motorPos = 0; // Сбрасываем позицию мотора
    }

    // Проверка состояния первой кнопки
    if (digitalRead(button1) == LOW && knopka == 0) {
        delay(50); // Небольшая задержка для устранения дребезга
        knopka++; // Увеличиваем счетчик нажатий
        digitalWrite(mosfet, !digitalRead(mosfet)); // Переключаем состояние MOSFET

        // Управление драйвером мотора
        if (digitalRead(mosfet) == HIGH) {
            digitalWrite(ENABLE_PIN, HIGH); // Выключаем драйвер мотора
        } else {
            digitalWrite(ENABLE_PIN, LOW); // Включаем драйвер мотора
        }
    }

    // Если кнопка отпущена
    if (digitalRead(button1) == HIGH && knopka == 1) {
        knopka = 0; // Сбрасываем счетчик
    }

    // Проверка состояния второй кнопки
    if (!digitalRead(button2)) { // Если кнопка нажата
        digitalWrite(ENABLE_PIN, LOW); // Отключаем драйвер мотора
        digitalWrite(DIR_PIN, HIGH); // Устанавливаем направление
        digitalWrite(STEP_PIN, HIGH); // Активируем шаг
        delayMicroseconds(20); // Задержка для выполнения шага
        digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
    }
}

void handleButton() {
    static bool lastButtonState = HIGH; // Предыдущее состояние кнопки
    bool currentButtonState = digitalRead(button2); // Текущее состояние кнопки

    // Проверяем нажатие кнопки
    if (lastButtonState == HIGH && currentButtonState == LOW) {
        joystickEnabled = !joystickEnabled; // Переключаем состояние джойстика
        delay(200); // Задержка для предотвращения дребезга кнопки
    }
    lastButtonState = currentButtonState; // Обновляем предыдущее состояние
}

void handleJoystick() {
    // Обработка джойстика
    int joystickValue = analogRead(JOYSTICK_PIN); // Считываем значение с джойстика

    if (joystickValue > 650) { // Если джойстик отклонен вправо
        T = map(joystickValue, 650, 1048, 100, 0); // Мапируем значение для скорости
        digitalWrite(DIR_PIN, HIGH); // Устанавливаем направление
        for (int i = 0; i < 4; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
    } 
    else if (joystickValue < 480) { // Если джойстик отклонен влево
        T = map(joystickValue, 480, 0, 100, 0); // Мапируем значение для скорости
        digitalWrite(DIR_PIN, LOW); // Устанавливаем направление
        for (int i = 0; i < 4; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
    }       
}

Мой код, по сути, тоже от Arduman, только переделанный.
Если научились его запускать, можете попробовать прикрепить джойстик.
Нужно в прерывании поднимать флаг, а в loop() проверять, и, если флаг поднят - вызывать функцию джойстика, а , после неё, сразу сбрасывать флаг, чтобы она отработала только один раз между прерываниями.
На замену analogRead() написал быструю функцию ADCfast_8bit():

Прописать в сетапе:

Спойлер
 ADMUX = (1 << REFS0) | (1 << ADLAR) ;// опорное напряжение AVCC, ADC0
  DIDR0 = 1 << ADC0D; 
  ADCSRA = (1 << ADEN) | (1 << ADPS0)| (1 << ADPS2);// 1/32  500КHz 

И в основной код:

Спойлер
uint8_t ADCfast_8bit()
 {
  uint8_t H, L;
  ADCSRA |= (1 << ADSC); // запускаем преобраз-ние
      while (ADCSRA & (1 << ADSC));
       L = ADCL;
       H = ADCH;
       return H ;
 }


void handleJoystick() {
    // Обработка джойстика
    uint8_t T;
    uint8_t joystickValue =  ADCfast_8bit();// Считываем значение с джойстика

    if (joystickValue >= 162) { // Если джойстик отклонен вправо
        T = (uint8_t)map(joystickValue, 162, 255, 100, 0);//650, 1048, 100, 0); // Мапируем значение для скорости
        digitalWrite(DIR_PIN, HIGH); // Устанавливаем направление
        for (uint8_t  i = 0; i < 4; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
			Serial.println(T);
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
    } 
    else if (joystickValue < 119) { // Если джойстик отклонен влево
        T = (uint8_t)map(joystickValue, 119 , 0, 100, 0); // Мапируем значение для скорости
        digitalWrite(DIR_PIN, LOW); // Устанавливаем направление
        for (uint8_t i = 0; i < 4; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
			Serial.println(T);
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
    }       
}

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

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

ps .
не удержался . сделал несколько отпечатков на точность размеров . тест пройден . малые и большие соответствуют 100% .
и теперь он не ляпает раньше времени . что это значит позже обьясню .

//Энкодер на пинах 2, 3. Используется внутренняя подтяжка.
// volatile int enc, motor_position;
  float K = 3.39; //ПОДБИРАЕМ СООТНОШЕНИЕ МОТОР/ЭНКОДЕР
  volatile int32_t enc = 0;
  volatile int32_t motoPos = 0;
  volatile int32_t motoPosPrev = 0;
  volatile float temp = 0.0;
  volatile float rem = 0.0;
  bool joystickEnabled = true; // Переменная для отслеживания состояния джойстика
  int knopka=0;
  int T = 10;
  
// Определяем пины для различных компонентов
#define ENC_A       2 // Пин для первой фазы энкодера
#define ENC_B       3 // Пин для второй фазы энкодера
#define ENABLE_PIN  8 // Пин для включения/выключения драйвера мотора
#define STEP_PIN    4 // Пин для подачи сигнала на шаговый мотор
#define DIR_PIN     7 // Пин для определения направления вращения мотора
#define limit_1     9 // Пин для первого лимитного переключателя
#define limit_2    10 // Пин для второго лимитного переключателя
#define button1     12 // Пин для первой кнопки
#define button2     5  // Пин для второй кнопки
#define mosfet      6  // Пин для управления MOSFET (включает датчик бумаги)
#define JOYSTICK_PIN A0 // Пин для джойстика

void setup() {
    Serial.begin(115200); // Инициализация последовательного порта
    pinMode(ENC_A, INPUT_PULLUP); // Установка пина энкодера A как вход с подтяжкой
    pinMode(ENC_B, INPUT_PULLUP); // Установка пина энкодера B как вход с подтяжкой
    pinMode(ENABLE_PIN, OUTPUT); // Установка пина включения драйвера как выход
    pinMode(STEP_PIN, OUTPUT); // Установка пина шага как выход
    pinMode(DIR_PIN, OUTPUT); // Установка пина направления как выход
    pinMode(limit_1, INPUT_PULLUP); // Установка первого лимитного переключателя как вход с подтяжкой
    pinMode(limit_2, INPUT_PULLUP); // Установка второго лимитного переключателя как вход с подтяжкой
    pinMode(button1, INPUT_PULLUP); // Установка первой кнопки как вход с подтяжкой
    pinMode(button2, INPUT_PULLUP); // Установка второй кнопки как вход с подтяжкой
    pinMode(mosfet, OUTPUT); // Установка пина MOSFET как выход
    digitalWrite(ENABLE_PIN, LOW); // Включаем драйвер мотора
  ADMUX = (1 << REFS0) | (1 << ADLAR) ;// опорное напряжение AVCC, ADC0
  DIDR0 = 1 << ADC0D; 
  ADCSRA = (1 << ADEN) | (1 << ADPS0)| (1 << ADPS2);// 1/32  500КHz
  PCIFR=PCIF2; PCICR=1<<PCIE2;               //разрешить прерывание
  PCMSK2=1<<PCINT18 | 1<<PCINT19;            //выбрать вход на котором сработает прерывание 
}



ISR(PCINT2_vect){

  static char EncPrev=0;                           //предыдущее состояние энкодера
  static char EncPrevPrev=0;                       //пред-предыдущее состояние энкодера
    char EncCur = 0;
    if(!(PIND & (1 << PD2))){EncCur  = 1;}         //опрос фазы 1 энкодера
    if(!(PIND & (1 << PD3))){ EncCur |= 2;}        //опрос фазы 2 энкодера

    if(EncCur != EncPrev)                          //если состояние изменилось,
    { if(EncPrev == 3 &&                             //если предыдущее состояние 3
          EncCur != EncPrevPrev )                      //и текущее и пред-предыдущее не равны,
      { 
        if(EncCur == 2) 
            enc++;
          else                          
            enc--;      
      

      }
      EncPrevPrev = EncPrev;                           //сохранение пред-предыдущего состояния
      EncPrev = EncCur;                                //сохранение предыдущего состояния
    }
}
   

void Step(bool dir, uint8_t steps) //шаг в + или -, кол-во шагов
{
  digitalWrite(DIR_PIN,dir);
  while(steps--)
  {
  digitalWrite(STEP_PIN, HIGH);
  delayMicroseconds(1);
  digitalWrite(STEP_PIN, LOW);
  delayMicroseconds(1);
  }
}

void moveToTarget() {
    temp = enc * K;//
   motoPos = temp;
   rem += temp - motoPos;
  if(rem >= 1.0)
   {
     motoPos++;
     rem -= 1.0;
   }
   
   if(motoPos > motoPosPrev)Step(false,(uint8_t)(motoPos - motoPosPrev));
   if(motoPos < motoPosPrev)Step(true,(uint8_t)(motoPosPrev - motoPos)); 
  motoPosPrev = motoPos;
}


void loop() {

moveToTarget();   
handleButton(); // Проверяем состояние кнопки
    if (joystickEnabled) {
        handleJoystick(); // Обрабатываем джойстик только если разрешено
    }

    if (!digitalRead(limit_1)) {	                      //pin коцевика задий
      digitalWrite(mosfet, HIGH);                     // включим датчик бумаги
       digitalWrite(ENABLE_PIN, LOW);        
      digitalWrite(DIR_PIN, LOW);
	    for (int i = 0; i < 2200; i++) {           //дистация отьезда от коцевика        
      digitalWrite(STEP_PIN, HIGH);
      delayMicroseconds(100);                  // скорость отьеда от limit
      digitalWrite(STEP_PIN, LOW);
      
      }
        
      enc =0 ;
      motoPos = 0 ;
    }


   // Проверка состояния второго лимитного переключателя
    if (!digitalRead(limit_2)) { // Если переключатель активирован
        digitalWrite(mosfet, LOW); // Отключаем датчик бумаги
        digitalWrite(ENABLE_PIN, LOW); // Отключаем драйвер мотора
        digitalWrite(DIR_PIN, HIGH); // Устанавливаем направление
        for (int i = 0; i < 1200; i++) { // Дистанция отъезда от переключателя
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(200); // Задержка для скорости отъезда
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
        enc = 0; // Сбрасываем значение энкодера
        motoPos = 0; // Сбрасываем позицию мотора
        digitalWrite(ENABLE_PIN, HIGH);
    }

    if(digitalRead(button1)==LOW&&knopka==0){
      digitalWrite(ENABLE_PIN, HIGH);
       delay(50);
       knopka++;
       digitalWrite(mosfet, !digitalRead(mosfet));   // вкл. или откл  mosfet и датчик бумаги
       }
       if(digitalRead(button1)==HIGH&&knopka==1){
       knopka=0;
     }


}

void handleButton() {
    static bool lastButtonState = HIGH; // Предыдущее состояние кнопки
    bool currentButtonState = digitalRead(button2); // Текущее состояние кнопки

    // Проверяем нажатие кнопки
    if (lastButtonState == HIGH && currentButtonState == LOW) {
        joystickEnabled = !joystickEnabled; // Переключаем состояние джойстика
        delay(200); // Задержка для предотвращения дребезга кнопки
        // Управление драйвером мотора
        if (digitalRead(joystickEnabled) == HIGH) {
            digitalWrite(ENABLE_PIN, LOW); // Выключаем драйвер мотора
        } else {
            digitalWrite(ENABLE_PIN, HIGH); // Включаем драйвер мотора
        }
    }
    lastButtonState = currentButtonState; // Обновляем предыдущее состояние
}


uint8_t ADCfast_8bit()
 {
  uint8_t H, L;
  ADCSRA |= (1 << ADSC); // запускаем преобраз-ние
      while (ADCSRA & (1 << ADSC));
       L = ADCL;
       H = ADCH;
       return H ;
 }

void handleJoystick() {
    // Обработка джойстика
    uint8_t T;
    uint8_t joystickValue =  ADCfast_8bit();// Считываем значение с джойстика

    if (joystickValue >= 162) { // Если джойстик отклонен вправо
        T = (uint8_t)map(joystickValue, 162, 255, 100, 0);//650, 1048, 100, 0); // Мапируем значение для скорости
        digitalWrite(DIR_PIN, HIGH); // Устанавливаем направление
        for (uint8_t  i = 0; i < 4; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
			Serial.println(T);
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
    } 
    else if (joystickValue < 119) { // Если джойстик отклонен влево
        T = (uint8_t)map(joystickValue, 119 , 0, 100, 0); // Мапируем значение для скорости
        digitalWrite(DIR_PIN, LOW); // Устанавливаем направление
        for (uint8_t i = 0; i < 4; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
			Serial.println(T);
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
        }
    }       
}

Возможно, частота слишком большая, не тянет без ОУ.
Попробуйте 41стр (#691) заменить на

ADCSRA = (1 << ADEN) | (1 << ADPS1)| (1 << ADPS2);// 1/64  250КHz

Частота ADC понизится, но, всё равно будет вдвое выше, чем у analogRead();

По поводу остального кода - это не мой вариант. Получился гибрид варианта от Ardumana и моего. Мой вариант был - полностью в прерывании.
Но, как говорится , работает - не трожь)))

вроде так же быо .

я пробовал изначальный вариант но размеры сбиваются . там по подробней нужно рассказать.
я выкинул лишнее . код положил в loop раз они с джостиком не конфликтуют

код
тут работает все верно и точно принтер оставил на этом коде

//Энкодер на пинах 2, 3. Используется внутренняя подтяжка.
// Определяем переменные для энкодера и позиции мотора
float K = 3.39; //ПОДБИРАЕМ СООТНОШЕНИЕ МОТОР/ЭНКОДЕР
volatile int32_t enc = 0; // Счетчик импульсов от энкодера
volatile int32_t motoPos = 0; // Текущая позиция мотора
volatile int32_t motoPosPrev = 0; // Предыдущая позиция мотора
volatile float temp = 0.0; // Временная переменная для расчета
volatile float rem = 0.0; // Остаток для корректировки позиции
int knopka=0; // Счетчик нажатий кнопки
  
// Определяем пины для различных компонентов
#define ENC_A       2 // Пин для первой фазы энкодера
#define ENC_B       3 // Пин для второй фазы энкодера
#define ENABLE_PIN  8 // Пин для включения/выключения драйвера мотора
#define STEP_PIN    4 // Пин для подачи сигнала на шаговый мотор
#define DIR_PIN     7 // Пин для определения направления вращения мотора
#define limit_1     9 // Пин для первого лимитного переключателя
#define limit_2    10 // Пин для второго лимитного переключателя
#define button1     12 // Пин для первой кнопки
#define button2     5  // Пин для второй кнопки
#define mosfet      6  // Пин для управления MOSFET (включает датчик бумаги)

void setup() {
    Serial.begin(115200); // Инициализация последовательного порта
    pinMode(ENC_A, INPUT_PULLUP); // Установка пина энкодера A как вход с подтяжкой
    pinMode(ENC_B, INPUT_PULLUP); // Установка пина энкодера B как вход с подтяжкой
    pinMode(ENABLE_PIN, OUTPUT); // Установка пина включения драйвера как выход
    pinMode(STEP_PIN, OUTPUT); // Установка пина шага как выход
    pinMode(DIR_PIN, OUTPUT); // Установка пина направления как выход
    pinMode(limit_1, INPUT_PULLUP); // Установка первого лимитного переключателя как вход с подтяжкой
    pinMode(limit_2, INPUT_PULLUP); // Установка второго лимитного переключателя как вход с подтяжкой
    pinMode(button1, INPUT_PULLUP); // Установка первой кнопки как вход с подтяжкой
    pinMode(button2, INPUT_PULLUP); // Установка второй кнопки как вход с подтяжкой
    pinMode(mosfet, OUTPUT); // Установка пина MOSFET как выход
    digitalWrite(ENABLE_PIN, LOW); // Включаем драйвер мотора
  ADMUX = (1 << REFS0) | (1 << ADLAR) ;// опорное напряжение AVCC, ADC0
  DIDR0 = 1 << ADC0D;                 // Отключаем цифровую часть АЦП для пина ADC0
  //ADCSRA = (1 << ADEN) | (1 << ADPS0)| (1 << ADPS2);//  1/32  500КHz
  ADCSRA = (1 << ADEN) | (1 << ADPS1)| (1 << ADPS2);// Включаем АЦП с делителем 1/64  250КHz
  PCIFR=PCIF2; PCICR=1<<PCIE2;               //разрешить прерывание
  PCMSK2=1<<PCINT18 | 1<<PCINT19;            //выбрать вход на котором сработает прерывание 
}

ISR(PCINT2_vect){

  static char EncPrev=0;                           //предыдущее состояние энкодера
  static char EncPrevPrev=0;                       //пред-предыдущее состояние энкодера
    char EncCur = 0;                               // Текущее состояние энкодера
    if(!(PIND & (1 << PD2))){EncCur  = 1;}         //опрос фазы 1 энкодера
    if(!(PIND & (1 << PD3))){ EncCur |= 2;}        //опрос фазы 2 энкодера

    if(EncCur != EncPrev)                          //если состояние изменилось,
    { if(EncPrev == 3 &&                             //если предыдущее состояние 3
          EncCur != EncPrevPrev )                      //и текущее и пред-предыдущее не равны,
      { 
        if(EncCur == 2) 
            enc++; else enc--;        //Увеличиваем иначе уменьшаем счетчик импульсов
    

      }
      EncPrevPrev = EncPrev;                           //сохранение пред-предыдущего состояния
      EncPrev = EncCur;                                //сохранение предыдущего состояния
    }
}
   
// Функция для выполнения шагов мотора
void Step(bool dir, uint8_t steps) //шаг в + или -, кол-во шагов
{
  digitalWrite(DIR_PIN,dir); // Устанавливаем направление вращения
  while(steps--)  // Выполняем заданное количество шагов
  {
  digitalWrite(STEP_PIN, HIGH); // Активируем шаг
  delayMicroseconds(1); // Задержка для формирования сигнала
  digitalWrite(STEP_PIN, LOW);  // Деактивируем шаг
  delayMicroseconds(1);
  }
}




void loop() {
handleJoystick();
temp = enc * K; // Вычисляем временную позицию мотора
motoPos = temp; // Обновляем текущую позицию мотора
rem += temp - motoPos; // Вычисляем остаток для корректировки позиции
    if (rem >= 1.0) // Если остаток превышает 1
     { 
        motoPos++; // Увеличиваем позицию мотора
        rem -= 1.0; // Уменьшаем остаток
    }
   //перемещения мотора к целевой позиции
   if(motoPos > motoPosPrev)Step(false,(uint8_t)(motoPos - motoPosPrev));
   if(motoPos < motoPosPrev)Step(true,(uint8_t)(motoPosPrev - motoPos)); 
  motoPosPrev = motoPos; // Обновляем предыдущую позицию


  


    // Проверка состояния первого лимитного переключателя
    if (!digitalRead(limit_1)) {                  // Если переключатель активирован
        digitalWrite(mosfet, HIGH);               // Включаем датчик бумаги
        digitalWrite(ENABLE_PIN, LOW);            // Включаем драйвер мотора
        digitalWrite(DIR_PIN, LOW);               // Устанавливаем направление
	    for (int i = 0; i < 2200; i++) {           //дистация отьезда от коцевика        
        digitalWrite(STEP_PIN, HIGH);             // Активируем шаг
        delayMicroseconds(100);                  // Задержка для скорости отъезда
        digitalWrite(STEP_PIN, LOW);             // Деактивируем шаг
     }
       enc = 0;                                 // Сбрасываем значение энкодера
        motoPos = 0;                            // Сбрасываем позицию мотора
    }


   // Проверка состояния второго лимитного переключателя
    if (!digitalRead(limit_2)) {                   // Если переключатель активирован
        digitalWrite(mosfet, LOW);                 // Отключаем датчик бумаги
        digitalWrite(ENABLE_PIN, LOW);             // Включаем драйвер мотора
        digitalWrite(DIR_PIN, HIGH);                // Устанавливаем направление
        for (int i = 0; i < 1200; i++) {            // Дистанция отъезда от переключателя
            digitalWrite(STEP_PIN, HIGH);           // Активируем шаг
            delayMicroseconds(200);                 // Задержка для скорости отъезда
            digitalWrite(STEP_PIN, LOW);            // Деактивируем шаг
        }
        enc = 0;                                    // Сбрасываем значение энкодера
        motoPos = 0;                               // Сбрасываем позицию мотора
        //digitalWrite(ENABLE_PIN, HIGH);
    }

// Обработка нажатия первой кнопки
    if (digitalRead(button1) == LOW && knopka == 0) {
        delay(50);                             // Задержка для предотвращения дребезга
        knopka++; // Увеличиваем счетчик нажатий
        digitalWrite(mosfet, !digitalRead(mosfet)); // Переключаем состояние MOSFET и датчика бумаги
    }
    if (digitalRead(button1) == HIGH && knopka == 1) {
        knopka = 0;                                   // Сбрасываем счетчик нажатий
    }

}



// Быстрое считывание 8-битного значения с АЦП
uint8_t ADCfast_8bit() {
    uint8_t H, L;
    ADCSRA |= (1 << ADSC);                         // Запускаем преобразование
    while (ADCSRA & (1 << ADSC));                  // Ждем завершения преобразования
    L = ADCL;                                      // Считываем младший байт
    H = ADCH;                                      // Считываем старший байт
    return H;                                      // Возвращаем старший байт
}

void handleJoystick() {
    // Обработка джойстика
    uint8_t T;                                          // Переменная для задержки
    uint8_t joystickValue =  ADCfast_8bit();            // Считываем значение с джойстика

    if (joystickValue >= 170) {                             // Если джойстик отклонен вправо
        T = (uint8_t)map(joystickValue, 170, 255, 255, 0);  // Мапируем значение для скорости
        digitalWrite(ENABLE_PIN, LOW);                      // Включаем драйвер мотора
        digitalWrite(DIR_PIN, HIGH);                       // Устанавливаем направление
        for (uint8_t  i = 0; i < 2; i++) {                  // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH);                  // Активируем шаг
            delayMicroseconds(T);                          // Задержка для скорости
			      Serial.println(joystickValue);                //joystickValue в последовательный порт
            digitalWrite(STEP_PIN, LOW);                  // Деактивируем шаг
        }
    } 
    else if (joystickValue < 120) {                           // Если джойстик отклонен влево
        T = (uint8_t)map(joystickValue, 120 , 0, 255, 0);    // Мапируем значение для скорости
        digitalWrite(ENABLE_PIN, LOW);                        // Включаем драйвер мотора
        digitalWrite(DIR_PIN, LOW);                           // Устанавливаем направление
        for (uint8_t i = 0; i < 1; i++) {                      // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH);                     // Активируем шаг
            delayMicroseconds(T);                             // Задержка для скорости
			      Serial.println(joystickValue);             //Выводим значение joystickValue в  порт
            digitalWrite(STEP_PIN, LOW);                   // Деактивируем шаг
        }
    }       
}

1 лайк

Отрадно это слышать)))
Своего рода победа, поздравляю!

1 лайк

В этом коде, немного изменил под себя джойстик ,теперь он имеет 11 скоростей в каждую сторону но всё равно он остаётся музыкальным
пока с неправильными данными Но работает хорошо

добавлены:

переменная lastStepTime: Эта переменная хранит время последнего шага.
условие в функции Step: После выполнения шагов обновляется lastStepTime.
теперь мотор сам отключается если он начинает бездействовать, я поставил время бездействия 3 секунды и так же сам включается если его затрагивают какие-нибудь инициализаторы системы . энкодер ,кнопки ,джойстик

добавлены:

Переменная negativeSteps для отслеживания количества отрицательных шагов.
переменная positiveSteps, которая увеличивается каждый раз, когда энкодер делает положительный шаг. Когда значение positiveSteps достигает то количество шагов которое мне нужно, мотор включается, включается MOSFET датчик бумаги а счетчик сбрасывается. стол поехал на печать

добавлены:
Serial.println для отслеживания показаний энкодера теперь можно с точностью посмотреть сколько сделано шагов
В какую сторону Когда и где он остановился Куда продолжил идти И сколько ещё сделает шагов

вся система остаётся стабильной печать проходит очень хорошо . размеры печати соответствуют эталону повторяемость 100%

теперь принтер печатает так как я планировал изначально я его отправляю в нулевую точку а он ждёт команды на печать
мы нажимаем печать он отправляется прямиком на печать вперёд а не делая зигзаги

но я не уверен что мне это нравится
мне больше нравится тот вариант который был который родился в процессе написания этого кода;
Зачем мне его отправлять в нулевую точку когда я нажму на печать и он сам зигзагами сикам раком доберётся до нужного места и также сделать 100% правильно

В общем меня устраивает оба варианта сегодняшний код и вчерашний который я Выложил.

// Эндодер на пинах 2, 3. Используется внутренняя подтяжка.
// Определяем переменные для энкодера и позиции мотора
float K = 3.39; // ПОДБИРАЕМ СООТНОШЕНИЕ МОТОР/ЭНКОДЕР
volatile int32_t enc = 0; // Счетчик импульсов от энкодера
volatile int32_t motoPos = 0; // Текущая позиция мотора
volatile int32_t motoPosPrev = 0; // Предыдущая позиция мотора
volatile float temp = 0.0; // Временная переменная для расчета
volatile float rem = 0.0; // Остаток для корректировки позиции
int knopka = 0; // Счетчик нажатий кнопки
volatile int positiveSteps = 0; // Счетчик положительных шагов
volatile int negativeSteps = 0; // Счетчик отрицательных шагов

// Определяем пины для различных компонентов
#define ENC_A       2 // Пин для первой фазы энкодера
#define ENC_B       3 // Пин для второй фазы энкодера
#define ENABLE_PIN  8 // Пин для включения/выключения драйвера мотора
#define STEP_PIN    4 // Пин для подачи сигнала на шаговый мотор
#define DIR_PIN     7 // Пин для определения направления вращения мотора
#define limit_1     9 // Пин для первого лимитного переключателя
#define limit_2    10 // Пин для второго лимитного переключателя
#define button1     12 // Пин для первой кнопки
#define button2     5  // Пин для второй кнопки
#define mosfet      6  // Пин для управления MOSFET (включает датчик бумаги)
unsigned long lastStepTime = 0; // Эта переменная хранит время последнего шага.
const unsigned long timeout = 3000; // Время тайм-аута в миллисекундах ( 3 секуны )

void setup() {
    Serial.begin(115200); // Инициализация последовательного порта
    pinMode(ENC_A, INPUT_PULLUP); // Установка пина энкодера A как вход с подтяжкой
    pinMode(ENC_B, INPUT_PULLUP); // Установка пина энкодера B как вход с подтяжкой
    pinMode(ENABLE_PIN, OUTPUT); // Установка пина включения драйвера как выход
    pinMode(STEP_PIN, OUTPUT); // Установка пина шага как выход
    pinMode(DIR_PIN, OUTPUT); // Установка пина направления как выход
    pinMode(limit_1, INPUT_PULLUP); // Установка первого лимитного переключателя как вход с подтяжкой
    pinMode(limit_2, INPUT_PULLUP); // Установка второго лимитного переключателя как вход с подтяжкой
    pinMode(button1, INPUT_PULLUP); // Установка первой кнопки как вход с подтяжкой
    pinMode(button2, INPUT_PULLUP); // Установка второй кнопки как вход с подтяжкой
    pinMode(mosfet, OUTPUT); // Установка пина MOSFET как выход
    digitalWrite(ENABLE_PIN, LOW); // Включаем драйвер мотора
    PCIFR = PCIF2; PCICR = 1 << PCIE2; // Разрешить прерывание
    PCMSK2 = 1 << PCINT18 | 1 << PCINT19; // Выбрать вход на котором сработает прерывание 
    lastStepTime = millis(); // Инициализируем время последнего шага
}

ISR(PCINT2_vect) {
    static char EncPrev = 0;                           // Предыдущее состояние энкодера
    static char EncPrevPrev = 0;                       // Пред-предыдущее состояние энкодера
    char EncCur = 0;                                   // Текущее состояние энкодера
    if (!(PIND & (1 << PD2))) { EncCur = 1; }          // Опрос фазы 1 энкодера
    if (!(PIND & (1 << PD3))) { EncCur |= 2; }          // Опрос фазы 2 энкодера

    if (EncCur != EncPrev) {                           // Если состояние изменилось
        if (EncPrev == 3 && EncCur != EncPrevPrev) {  // Если предыдущее состояние 3
            if (EncCur == 2) {
                enc++;                                // Увеличиваем счетчик импульсов
                positiveSteps++;                      // Увеличиваем счетчик положительных шагов enc
                //Serial.println(positiveSteps++);
            } else {
                enc--;                               // Уменьшаем счетчик импульсов
                negativeSteps++;                     // Увеличиваем счетчик отрицательных шагов enc
                //Serial.println(negativeSteps++);
            }
            lastStepTime = millis();                 // Обновляем время последнего шага
        }
        EncPrevPrev = EncPrev;                       // Сохранение пред-предыдущего состояния
        EncPrev = EncCur;                           // Сохранение предыдущего состояния
    }
}

// Функция для выполнения шагов мотора
void Step(bool dir, uint8_t steps) {                // Шаг в + или -, кол-во шагов
    digitalWrite(DIR_PIN, dir);                     // Устанавливаем направление вращения
    while (steps--) {                               // Выполняем заданное количество шагов
        digitalWrite(STEP_PIN, HIGH);               // Активируем шаг
        delayMicroseconds(1);                        // Задержка для формирования сигнала
        digitalWrite(STEP_PIN, LOW);                   // Деактивируем шаг
        delayMicroseconds(1);
        lastStepTime = millis();                    // Обновляем время последнего шага
    }
}

void loop() {
  handleJoystick();
    temp = enc * K;                               // Вычисляем временную позицию мотора
    motoPos = temp;                               // Обновляем текущую позицию мотора
    rem += temp - motoPos;                       // Вычисляем остаток для корректировки позиции
    if (rem >= 1.0) {                            // Если остаток превышает 1
        motoPos++;                              // Увеличиваем позицию мотора
        rem -= 1.0;                               // Уменьшаем остаток
    }
    
    // Проверка на включение мотора при  шагах
    if (negativeSteps >= 9900) {                        //Мои зачеия 9900 
        digitalWrite(mosfet, HIGH);                    // Включаем MOSFET
        if (positiveSteps >= 1120) {                    //Мои зачеия 1120
      digitalWrite(ENABLE_PIN, LOW);                 // Включаем мотор
      negativeSteps = 0;                       // Сбрасываем счетчик отрицательных шагов
      positiveSteps = 0;                        // Сбрасываем счетчик положительных шагов
    }
    }
    
    // Перемещения мотора к целевой позиции
    if (motoPos > motoPosPrev) Step(false, (uint8_t)(motoPos - motoPosPrev));
    if (motoPos < motoPosPrev) Step(true, (uint8_t)(motoPosPrev - motoPos)); 
    motoPosPrev = motoPos;                             // Обновляем предыдущую позицию

    // Проверка на активацию кнопок
    if (digitalRead(button1) == LOW || digitalRead(button2) == LOW) {
        digitalWrite(ENABLE_PIN, LOW);                 // Включаем мотор
        lastStepTime = millis();                      // Обновляем время последнего шага
    }

    // Проверка на отключение мотора
    if (digitalRead(ENABLE_PIN) == LOW && (millis() - lastStepTime) > timeout) {
        digitalWrite(ENABLE_PIN, HIGH); // Отключаем драйвер мотора
        //Serial.println("мотор вык.");
    }


  


    // Проверка состояния первого лимитного переключателя
    if (!digitalRead(limit_1)) {                  // Если переключатель активирован
        //digitalWrite(mosfet, HIGH);               // Включаем датчик бумаги
        digitalWrite(ENABLE_PIN, LOW);            // Включаем драйвер мотора
        digitalWrite(DIR_PIN, LOW);               // Устанавливаем направление
	    for (int i = 0; i < 2300; i++) {           //дистация отьезда от коцевика        
        digitalWrite(STEP_PIN, HIGH);             // Активируем шаг
        delayMicroseconds(100);                  // Задержка для скорости отъезда
        digitalWrite(STEP_PIN, LOW);             // Деактивируем шаг
     }
       enc = 0;                                 // Сбрасываем значение энкодера
        motoPos = 0;                            // Сбрасываем позицию мотора
        positiveSteps = 0;                    // Сбрасываем счетчик положительных шагов
        negativeSteps = 0;                    // Сбрасываем счетчик отрицательных шагов
    }


   // Проверка состояния второго лимитного переключателя
    if (!digitalRead(limit_2)) {                   // Если переключатель активирован
        digitalWrite(mosfet, LOW);                 // Отключаем датчик бумаги
        digitalWrite(ENABLE_PIN, LOW);             // Включаем драйвер мотора
        digitalWrite(DIR_PIN, HIGH);                // Устанавливаем направление
        for (int i = 0; i < 1200; i++) {            // Дистанция отъезда от переключателя
            digitalWrite(STEP_PIN, HIGH);           // Активируем шаг
            delayMicroseconds(200);                 // Задержка для скорости отъезда
            digitalWrite(STEP_PIN, LOW);            // Деактивируем шаг
        }
        enc = 0;                                    // Сбрасываем значение энкодера
        motoPos = 0;                               // Сбрасываем позицию мотора
        //digitalWrite(ENABLE_PIN, HIGH);
    }

// Обработка нажатия первой кнопки
    if (digitalRead(button1) == LOW && knopka == 0) {
        delay(50);                             // Задержка для предотвращения дребезга
        knopka++;                               // Увеличиваем счетчик нажатий
        digitalWrite(mosfet, !digitalRead(mosfet)); // Переключаем состояние MOSFET и датчика бумаги
    }
    if (digitalRead(button1) == HIGH && knopka == 1) {
        knopka = 0;                                   // Сбрасываем счетчик нажатий
    }
    
    if (!digitalRead(button2)) {          
      digitalWrite(ENABLE_PIN, LOW);
	}
}


// Быстрое считывание бит. значения с АЦП
int ADCfast() {
  ADMUX = (1 << REFS0) | (1 << ADLAR) ;              // опорное напряжение AVCC, ADC0
  DIDR0 = 1 << ADC0D;                               // Отключаем цифровую часть АЦП для пина ADC0
  ADCSRA = (1 << ADEN) | (1 << ADPS1)| (1 << ADPS2);// Включаем АЦП с делителем 1/64  250КHz
    int H, L;
    ADCSRA |= (1 << ADSC);                         // Запускаем преобразование
    while (ADCSRA & (1 << ADSC));                  // Ждем завершения преобразования
    L = ADCL;                                      // Считываем младший байт
    H = ADCH;                                      // Считываем старший байт
    return H;                                      // Возвращаем старший байт
}

void handleJoystick() {
    int T;
    int joystickValue = ADCfast();                // Считываем значение с джойстика
    

    if (joystickValue > 145) {                             // Если джойстик отклонен вправо
        T = (int)map(joystickValue, 145, 256, 255, 0);        // Мапируем значение для скорости
        digitalWrite(DIR_PIN, HIGH);                       // Устанавливаем направление
        digitalWrite(ENABLE_PIN, LOW);                     // Включаем драйвер мотора
        for (int i = 0; i < 4; i++) {
        digitalWrite(STEP_PIN, HIGH);                      // Активируем шаг
        delayMicroseconds(T);                              // Задержка для скорости
        digitalWrite(STEP_PIN, LOW);                       // Деактивируем шаг
        delayMicroseconds(T);                              // Задержка для скорости   
    }
    lastStepTime = millis();                  // После выполнения шагов обновляется lastStepTime
    }
    else if (joystickValue < 135) {                        // Если джойстик отклонен влево
        T = (int)map(joystickValue, 135, 0, 255, 0);           // Мапируем значение 
        digitalWrite(DIR_PIN, LOW);                        // Устанавливаем направление
        digitalWrite(ENABLE_PIN, LOW);                     // Включаем драйвер мотора
        for (int i = 0; i < 4; i++) {
        digitalWrite(STEP_PIN, HIGH);                      // Активируем шаг
        delayMicroseconds(T);                              // Задержка для скорости
        digitalWrite(STEP_PIN, LOW);                       // Деактивируем шаг
        delayMicroseconds(T);                              // Задержка для скорости
    }
    lastStepTime = millis();                             // Обновляем время последнего шага 
    }
    //Serial.println(joystickValue);                     // Значение joystickValue в порт 
     
      
}

Решил весь код перенести на плату esp32 , так как реализации всех задумок не хватало около 8 пинов .
кое-что же проверил энкодеры и разные энкодеры проверил, моторы, всё работает
код для энкодера
Пины: Я выбрал пины 32 и 33, которые поддерживают прерывания на ESP32. Вы можете выбрать другие пины, если они вам больше подходят.
Прерывания: Вместо использования PCIFR, PCICR и PCMSK2 для настройки прерываний, используется функция attachInterrupt(), которая более удобна для ESP32.
ISR: Функция обработки прерывания теперь помечена как IRAM_ATTR, что помогает избежать проблем с задержками при выполнении прерываний на ESP32.

// Энкодер на пинах 34, 35. Используется внутренняя подтяжка.
volatile int16_t enc = 0; // Объявляем как volatile

#define ENC_A  34
#define ENC_B  35

void IRAM_ATTR handleEncoder() {
    static char EncPrev = 0; // Предыдущее состояние энкодера  
    static char EncPrevPrev = 0; // Пред-предыдущее состояние энкодера  
    char EncCur = 0;  

    if (digitalRead(ENC_A) == LOW) { EncCur |= 1; } // Опрос фазы 1 энкодера  
    if (digitalRead(ENC_B) == LOW) { EncCur |= 2; } // Опрос фазы 2 энкодера  

    if (EncCur != EncPrev) { // Если состояние изменилось  
        if (EncPrev == 3 && EncCur != EncPrevPrev) { // Если предыдущее состояние 3  
            if (EncCur == 2)
                enc++; 
                Serial.println(enc);				
            else
                enc--;  
        }  
        EncPrevPrev = EncPrev; // Сохранение пред-предыдущего состояния  
        EncPrev = EncCur; // Сохранение предыдущего состояния  
    }
}

void setup() {
    Serial.begin(115200);
    pinMode(ENC_A, INPUT_PULLUP);
    pinMode(ENC_B, INPUT_PULLUP);

    attachInterrupt(digitalPinToInterrupt(ENC_A), handleEncoder, CHANGE);
    attachInterrupt(digitalPinToInterrupt(ENC_B), handleEncoder, CHANGE);
}

void loop() {
    // Здесь можно добавлять код для работы с переменной enc
}

Один из пинов , как раз я о нём сегодня и расскажу мне Понадобился чтобы я мог не с программы запускать печать а с кнопки за станком

ESP32 не может эмулировать клавиатуру через Wi-Fi напрямую, поэтому мы будем использовать HTTP-запрос для отправки команды на компьютер, который будет слушать эти запросы и выполнять соответствующее действие.

Для реализации данной задачи вам понадобится использовать библиотеку для работы с Wi-Fi на ESP32 и библиотеку для отправки HTTP-запросов.
#include <WiFi.h>
#include <HTTPClient.h>

На компьютере вы можете использовать сервер, который будет принимать запросы от ESP32 и выполнять нужные действия, например, нажимать клавиши.
Я работаю в программе Corel PHOTO-PAINT и кнопка печать вызывается ctrl+p
На esp32 я взял кнопку D4 , нажимаем D4 , и на компьютере срабатывает ctrl+p в программе Corel PHOTO-PAINT и печать пошла.

Код для ESP32, который отправляет HTTP-запрос при нажатии кнопки, подключенной к пину D4:
Замените your_SSID и your_PASSWORD на данные вашей Wi-Fi сети.

#include <WiFi.h>
#include <HTTPClient.h>

const char *ssid = "имя пользователя сети"; // замените на ваш SSID
const char *password = "11111111"; // замените на ваш пароль

const int buttonPin = 4; // кнопка подключена к D4
bool lastButtonState = HIGH; // предыдущее состояние кнопки

void setup() {
  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP); // настройка пина кнопки
  WiFi.begin(ssid, password); // подключение к Wi-Fi

  // Ожидание подключения к Wi-Fi
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Подключение к WiFi...");
  }
  Serial.println("Подключено к WiFi");
}

void loop() {
  bool currentButtonState = digitalRead(buttonPin);
  
  // Проверка нажатия кнопки
  if (lastButtonState == HIGH && currentButtonState == LOW) {
    sendKeyPress(); // отправка команды
    delay(200); // задержка для устранения дребезга
  }
  
  lastButtonState = currentButtonState;
}

void sendKeyPress() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin("http://192.168.1.5:5000/send-key"); // замените на IP-адрес вашего компьютера и порт
    http.addHeader("Content-Type", "application/json");

    String jsonPayload = "{\"key\": \"ctrl+p\"}"; // JSON с данными
    int httpResponseCode = http.POST(jsonPayload); // отправка POST-запроса

    if (httpResponseCode > 0) {
      String response = http.getString(); // получение ответа
      Serial.println(httpResponseCode); // вывод кода ответа
      Serial.println(response); // вывод ответа
    } else {
      Serial.print("Ошибка: ");
      Serial.println(httpResponseCode);
    }
    http.end(); // завершение запроса
  } else {
    Serial.println("WiFi не подключен");
  }
}

Ещё понадобится код для сервера на компьютере его мы пишем в терминал нашего нового проекта в pachat

Вы можете использовать Python с библиотекой Flask для создания простого сервера, который будет выполнять нажатие клавиш.

На компьютере уже установлен Python если его нет придётся установить .
Для удобства Я установил PyCharm но можно прямо с Python всё установить для этого создать как и в pachat новый проект и уже с терминала и консоли запустить всё необходимое.
сдаём новый проект в этом проекте открываем терминал для установки библиотеки Flask и pyautogui.
Вводим команды для установки Flask и pyautogui.

pip install Flask
pip install pyautogui

теперь там же открываем консоль и вводим уже отредактированы ваш код

from flask import Flask, request
import pyautogui

app = Flask(__name__)

@app.route('/send-key', methods=['POST'])
def send_key():
    data = request.get_json()
    if data and 'key' in data:
        key = data['key']
        if key == 'ctrl+p':
            pyautogui.hotkey('ctrl', 'p')  # выполнение сочетания клавиш ('p')
            return 'Key pressed', 200
    return 'Invalid request', 400

if __name__ == '__main__':
    app.run(host='192.168.1.5', port=5000)  # запустить сервер
	

#192.168.1.5 вместо этого адреса вам нужно поставить свой адрес компьютера
Запустите этот скрипт на вашем компьютере, и он будет слушать запросы от ESP32.
Настройки

Замените http://your_computer_ip:port/send-key на IP-адрес вашего компьютера и порт, на котором работает Flask (по умолчанию 5000).
Убедитесь, что ваш компьютер и ESP32 находятся в одной сети.
Запуск
Запустите сервер на компьютере с помощью Python.
Загрузите код на ESP32 и подключите кнопку к пину D4.
Нажмите кнопку, и ESP32 отправит команду вашему компьютеру.




джойстик с правильными значениями 561
тот который ранее был тоже отлично работает не хуже
выкладываю сюда чтобы не потерять

#define ENABLE_PIN  8 // Пин для включения/выключения драйвера мотора
#define STEP_PIN    4 // Пин для подачи сигнала на шаговый мотор
#define DIR_PIN     7 // Пин для определения направления вращения мотора

volatile int joystickValue = 560; // Глобальная переменная для хранения значения джойстика

void setup() {
    Serial.begin(115200); // Инициализация последовательного порта

    pinMode(ENABLE_PIN, OUTPUT); // Установка пина включения драйвера как выход
    pinMode(STEP_PIN, OUTPUT); // Установка пина шага как выход
    pinMode(DIR_PIN, OUTPUT); // Установка пина направления как выход
    digitalWrite(ENABLE_PIN, LOW); // Включаем драйвер мотора

    // Настройка АЦП
    ADMUX = (1 << REFS0) | (1 << ADLAR); // Опорное напряжение AVCC, ADC0
    DIDR0 = 1 << ADC0D; // Отключаем цифровую часть АЦП для пина ADC0
    ADCSRA = (1 << ADEN) | (1 << ADPS0) | (1 << ADPS2); // Включаем АЦП с делителем 1/32
}

void loop() {
    // Обрабатываем джойстик
    handleJoystick();
}

// Быстрое считывание 10-битного значения с АЦП
uint32_t ADCfast_10bit() {
    uint32_t H, L;
    ADCSRA |= (1 << ADSC); // Запускаем преобразование
    while (ADCSRA & (1 << ADSC)); // Ждем завершения преобразования
    L = ADCL; // Считываем младший байт
    H = ADCH; // Считываем старший байт
    return (H << 2) | (L >> 6); // Возвращаем 10-битное значение
}

void handleJoystick() {
    // Обработка джойстика
    uint32_t T; // Переменная для задержки
    joystickValue = ADCfast_10bit(); // Считываем значение с джойстика

    Serial.println(joystickValue); // Выводим значение joystickValue в последовательный порт

    if (joystickValue >= 570) { // Если джойстик отклонен вправо
        T = (uint32_t)map(joystickValue, 570, 1023, 255, 0); // Мапируем значение для скорости
        digitalWrite(ENABLE_PIN, LOW); // Включаем драйвер мотора
        digitalWrite(DIR_PIN, HIGH); // Устанавливаем направление
        for (uint32_t i = 0; i < 4; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
            delayMicroseconds(T); // Задержка для скорости
        }
    } 
    else if (joystickValue < 550) { // Если джойстик отклонен влево
        T = (uint32_t)map(joystickValue, 550, 0, 255, 0); // Мапируем значение для скорости
        digitalWrite(ENABLE_PIN, LOW); // Включаем драйвер мотора
        digitalWrite(DIR_PIN, LOW); // Устанавливаем направление
        for (uint32_t i = 0; i < 4; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
            delayMicroseconds(T); // Задержка для скорости
        }
    } 


     if (joystickValue >= 1000) { 
       for (uint32_t i = 0; i < 6; i++) { // Выполняем несколько шагов
            digitalWrite(STEP_PIN, HIGH); // Активируем шаг
            delayMicroseconds(T); // Задержка для скорости
            digitalWrite(STEP_PIN, LOW); // Деактивируем шаг
            delayMicroseconds(T); // Задержка для скорости
        }
        }   
}

Здравствуйте Вадим. Посмотрел Ваши видео и попал на эту страницу. Прочитал всю и возник вопрос по ESP32: не могли бы ВЫ подробнее объяснить есть ли разные модели и какую ВЫ выбрали для себя. Ни когда с ними не сталкивался. Можно ли будет использовать Ваш скетч под свою модель принтера?

под любую модель принтера можно использовать заменив
3.39 на свойе число передаточное
Если вы спросили про нажатии кнопки то любую esp32

3,39 это у Вас по энкодеру и валу или по шкиву и ремню?

О esp смотрел видео. Их много всяких разных. Значит самую обычную попробую

это проще сказать условная цифра энкодер на ось Yкоторую подбираешь эксперементально