Los motores paso a paso son ampliamente utilizados en proyectos de automatización, CNC, impresoras 3D y robótica debido a su facilidad de control y la precisión que ofrecen al trabajar con pasos definidos. Sin embargo, cuando un motor se controla sin realimentación, el sistema asume que cada paso enviado se ejecuta correctamente, pero en la práctica pueden ocurrir pérdidas de pasos por exceso de carga, vibraciones o aceleraciones bruscas.
Para solucionar este problema, se emplean motores paso a paso con encoder, como el JK57HSN21, que permite monitorear en tiempo real si el motor realmente alcanzó la posición deseada. El encoder envía pulsos al controlador y habilita un sistema de realimentación cerrada, lo que mejora la confiabilidad, evita pérdidas de pasos y asegura movimientos mucho más estables.
Objetivo
En este proyecto aprenderás a controlar un motor paso a paso JK57HSN21 con encoder y el driver HSD57, usando un ESP32 y la librería AccelStepper para gestionar la aceleración, velocidad y sentido de giro. De esta manera, podrás implementar un sistema más robusto y preciso en comparación con el uso de un motor paso a paso tradicional sin encoder.
Componentes Utilizados
- ESP32 (microcontrolador)
- Motor paso a paso JK57HSN21 con encoder
- Servo Driver Híbrido HSD57
- Conversor de nivel lógico (3.3 V a 5 V)
Librería AccelStepper
Esta librería permite al ESP32 controlar el motor de manera más avanzada que solo encender y apagar pulsos. Con ella se puede programar que el motor arranque suave, acelere, mantenga una velocidad y luego frene sin perder pasos. En el proyecto se usará para manejar los movimientos con perfiles de aceleración y para enviar al driver la cantidad de pasos exacta que debe mover el motor.
AccelStepper stepper(DRIVER_INTERFACE, PIN_STEP, PIN_DIR);
Crea un objeto llamado stepper que controlará el motor.
Se indica el tipo de interfaz (DRIVER) y los pines de STEP y DIR.
stepper.setAcceleration(aceleracion);
Define la aceleración del motor en pasos/segundo².
stepper.setCurrentPosition(0);
Asigna la posición actual del motor como 0 (origen), útil para reiniciar la referencia luego de cada ciclo
stepper.setMaxSpeed(pasosPorSegundo);
Establece la velocidad máxima en pasos por segundo.
stepper.run();
Es la función que ejecuta los movimientos paso a paso.
stepper.distanceToGo();
Devuelve la distancia restante (en pasos) que le falta al motor para llegar a su destino.
stepper.moveTo(pasos); Establece la meta de pasos, el movimiento empieza con stepper.run()
stepper.stop();
Ordena detener el motor
Conexiones
Enconder Motor – Encoder Driver
| Cables Encoder | Driver HSD57 |
|---|---|
| Amarillo | EB+ |
| Verde | EB- |
| Negro | EA+ |
| Azul | EA- |
| Rojo | VCC |
| Blanco | GND |
Motor – Driver
| Cable Motor | Driver |
|---|---|
| Negro | A+ |
| Verde | A- |
| Rojo | B+ |
| Azul | B- |
Signal (Driver) – Conversor de Nivel
| Signal (Driver) | Conversor de Nivel |
|---|---|
| PUL+ | HV1 |
| PUL- | GND |
| DIR+ | HV2 |
| DIR- | GND |
| ENA+ | – |
| ENA- | GND |
Obs: ENA+ es opcional (para habilitar o deshabilitar el motor), si no se conecta el motor se encuentra habilitado por defecto (obedece las señales STEP/DIR), y no se puede girar con la mano.
Conversor de nivel – ESP32
| Conversor de Nivel | ESP32 |
|---|---|
| HV | 5V |
| LV | 3.3V |
| LV1 | GPIO26 |
| LV2 | GPIO25 |
| GND | GND |
Diagrama Esquemático
Después de realizar todas las conexiones, el esquema resultante se verá de la siguiente manera:

Configuración del Driver
Para esta demostración, el driver fue configurado mediante los interruptores DIP (SW) en un modo de microstepping de 800 pulsos por revolución, de acuerdo con la tabla de configuración del fabricante.

Se activó el control en lazo cerrado colocando el interruptor SW6 en posición ON.
Para esta experiencia se emplea el motor JK57HSN21, por lo que se configuraron los interruptores SW7 en OFF y SW8 en ON, de acuerdo con las especificaciones del fabricante.
Programación
Se desarrollará un ejemplo básico en el cual el motor se desplazará en un sentido, se detendrá durante un intervalo de tiempo determinado y posteriormente girará en el sentido contrario, repitiendo este ciclo de manera indefinida.
Configuración de hardware:
#include <AccelStepper.h> // ---------------- CONFIGURACIÓN DE HARDWARE ---------------- #define DRIVER_INTERFACE AccelStepper::DRIVER // Pines del ESP32 #define PIN_STEP 26 #define PIN_DIR 25 AccelStepper stepper(DRIVER_INTERFACE, PIN_STEP, PIN_DIR);
Aquí se incluye la librería AccelStepper, se define el tipo de driver, los pines STEP y DIR del ESP32 y se crea el objeto stepper que controlará al motor.
Configuración del motor y parámetros
// ---------------- CONFIGURACIÓN DEL MOTOR --------------- const int pasosPorVuelta = 200; // Motor típico: 1.8° por paso const int microstepping = 4; // Configuración física en el driver // ---------------- PARÁMETROS DE MOVIMIENTO -------------- const int delayEntreMovimientos = 2000; // ms const int rpm = 100; // Velocidad máxima const int aceleracion = 50000; // pasos/s² const float vueltasPorMovimiento = 10; // Vueltas por ciclo // ---------------- VARIABLES DE ESTADO ---------------- bool enMovimiento = false; bool sentidoHorario = true; unsigned long tiempoInicioPausa = 0;
Aquí se define las características del motor (pasos y microstepping), la velocidad, aceleración, número de vueltas y el tiempo de pausa entre movimientos.
Además, las variables de estado controlan si el motor está girando o en pausa.
Setup Inicial
void setup() { Serial.begin(115200); // Comunicación con monitor Serial // Calcular velocidad en pasos/segundo float pasosPorSegundo = (rpm / 60.0) * pasosPorVuelta * microstepping; stepper.setMaxSpeed(pasosPorSegundo); stepper.setAcceleration(aceleracion); stepper.setCurrentPosition(0); }
Aquí se inicializa la comunicación serial, se calcula la velocidad máxima en pasos/segundo y se configuran los parámetros del motor en la librería.
Lógica principal del loop
void loop() { if (enMovimiento) { stepper.run(); if (stepper.distanceToGo() == 0) { enMovimiento = false; tiempoInicioPausa = millis(); stepper.setCurrentPosition(0); } } else { if (millis() - tiempoInicioPausa >= delayEntreMovimientos) { sentidoHorario = !sentidoHorario; // Calcular pasos desde el número de vueltas long pasos = vueltasPorMovimiento * pasosPorVuelta * microstepping; if (!sentidoHorario) pasos = -pasos; stepper.moveTo(pasos); enMovimiento = true;
Este bloque se encarga de:
- Mover el motor paso a paso mientras esté activo (stepper.run()).
- Detectar cuando termina el movimiento y activar la pausa.
- Cambiar el sentido de giro tras la pausa.
- Calcular cuántos pasos debe dar según las vueltas configuradas.
Depuración en Serial
// Datos de depuración float pasosPorSegundo = (rpm / 60.0) * pasosPorVuelta * microstepping; float tiempoEstimado = abs(pasos) / pasosPorSegundo; Serial.println(sentidoHorario ? "Giro horario" : "Giro antihorario"); Serial.print("Vueltas: "); Serial.println(vueltasPorMovimiento); Serial.print("Pasos totales: "); Serial.println(abs(pasos)); Serial.print("Pasos/seg: "); Serial.println(pasosPorSegundo, 1); Serial.print("RPM: "); Serial.println(rpm); Serial.print("Aceleración: "); Serial.println(aceleracion); Serial.print("Tiempo estimado: "); Serial.print(tiempoEstimado, 2); Serial.println(" s"); Serial.println("----------------"); } } }
Aquí se imprimen todos los datos útiles para mostrarlos en el monitor Serial.
Código Completo
#include <AccelStepper.h> // ---------------- CONFIGURACIÓN DE HARDWARE ---------------- #define DRIVER_INTERFACE AccelStepper::DRIVER // Pines del ESP32 #define PIN_STEP 26 #define PIN_DIR 25 AccelStepper stepper(DRIVER_INTERFACE, PIN_STEP, PIN_DIR); // ---------------- CONFIGURACIÓN DEL MOTOR --------------- const int pasosPorVuelta = 200; // Motor típico: 1.8° por paso const int microstepping = 4; // Configuración física en el driver // ---------------- PARÁMETROS DE MOVIMIENTO -------------- const int delayEntreMovimientos = 2000; // ms const int rpm = 100; // Velocidad máxima const int aceleracion = 50000; // pasos/s² const float vueltasPorMovimiento = 10; // Vueltas por ciclo // ---------------- VARIABLES DE ESTADO ---------------- bool enMovimiento = false; bool sentidoHorario = true; unsigned long tiempoInicioPausa = 0; // ---------------- SETUP INICIAL ---------------- void setup() { Serial.begin(115200); // Comunicación con monitor Serial // Calcular velocidad en pasos/segundo float pasosPorSegundo = (rpm / 60.0) * pasosPorVuelta * microstepping; stepper.setMaxSpeed(pasosPorSegundo); stepper.setAcceleration(aceleracion); stepper.setCurrentPosition(0); } // ---------------- LÓGICA PRINCIPAL ---------------- void loop() { if (enMovimiento) { stepper.run(); if (stepper.distanceToGo() == 0) { enMovimiento = false; tiempoInicioPausa = millis(); stepper.setCurrentPosition(0); } } else { if (millis() - tiempoInicioPausa >= delayEntreMovimientos) { sentidoHorario = !sentidoHorario; // Calcular pasos desde el número de vueltas long pasos = vueltasPorMovimiento * pasosPorVuelta * microstepping; if (!sentidoHorario) pasos = -pasos; stepper.moveTo(pasos); enMovimiento = true; // ---------------- DEPURACIÓN EN SERIAL ---------------- float pasosPorSegundo = (rpm / 60.0) * pasosPorVuelta * microstepping; float tiempoEstimado = abs(pasos) / pasosPorSegundo; Serial.println(sentidoHorario ? "Giro horario" : "Giro antihorario"); Serial.print("Vueltas: "); Serial.println(vueltasPorMovimiento); Serial.print("Pasos totales: "); Serial.println(abs(pasos)); Serial.print("Pasos/seg: "); Serial.println(pasosPorSegundo, 1); Serial.print("RPM: "); Serial.println(rpm); Serial.print("Aceleración: "); Serial.println(aceleracion); Serial.print("Tiempo estimado: "); Serial.print(tiempoEstimado, 2); Serial.println(" s"); Serial.println("------------------------"); } } }
