Publicado el Deja un comentario

Control de un motor paso a paso de lazo cerrado usando ESP32 

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

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("------------------------");  
    }  
  }  
}  

Resultados 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *