Ir al contenido

Control de motor paso a paso con un ESP32 PLC

Cómo controlar un motor paso a paso con un driver y las salidas PWM del ESP32 PLC
15 de junio de 2026 por
Control de motor paso a paso con un ESP32 PLC
Boot & Work Corp. S.L, Ricard Franch Argullol

Guía completa: Cómo elegir un controlador industrial

Para controlar un motor paso a paso con un ESP32 PLC, conecta un driver como el TB6600 entre el PLC y el motor. Una salida PWM del PLC envía los pulsos de paso (PUL), una segunda salida fija el sentido (DIR) y una tercera habilita el driver (ENA). La frecuencia de los pulsos fija la velocidad: rpm = frecuencia × 60 / (200 × micropasos) en un motor de 1,8°. En los ESP32 PLC 21, 42 y 58 las salidas Q0.x salen de un chip PCA9685, así que la frecuencia de paso va de 24 Hz a 1525 Hz: de 3,6 a 228,9 rpm con 2 micropasos. El PLC no cuenta los pulsos, así que la posición es en lazo abierto y sale del tiempo que duran los pulsos.

Esta guía muestra el cableado, la configuración del driver y dos sketches de Arduino para el montaje que usamos en nuestro laboratorio, un motor NEMA 17 17HS16-2004S y un driver de micropasos TB6600: uno mueve el motor a velocidad constante y el otro añade una rampa de aceleración.

¿Qué velocidad puede dar un ESP32 PLC a un motor paso a paso?

La velocidad depende de los micropasos del driver y del rango de frecuencia de la salida PWM. En los ESP32 PLC 21, 42 y 58, de Q0.0 a Q0.7 son salidas de un chip PWM PCA9685 en la dirección I2C 0x40, con un reloj de 25 MHz y un contador de 12 bits. El paquete de placas ESP32 de Industrial Shields (versión 2.7.1) acepta frecuencias de 24 Hz a 1525 Hz en estas salidas (el core rechaza 1526 Hz; si pides 1525 Hz obtienes la frecuencia más alta que da el chip, 1525,9 Hz). Con un motor de 200 pasos:

MicropasosPulsos por vueltaVelocidad mínima (24 Hz)Velocidad máxima (1525,9 Hz)
1 (paso completo)2007,2 rpm457,8 rpm
24003,6 rpm228,9 rpm
48001,8 rpm114,4 rpm
816000,9 rpm57,2 rpm
1632000,45 rpm28,6 rpm
3264000,22 rpm14,3 rpm

Dos detalles del PCA9685 cambian la forma de escribir el programa:

  • La frecuencia va a saltos. El chip solo puede dar 25 MHz / (4096 × (prescaler + 1)), con un prescaler entero. Cerca del máximo los saltos son grandes: 1525,9; 1220,7; 1017,3; 871,9 y 762,9 Hz son valores seguidos. Si pides 333 Hz obtienes 339 Hz, así que el motor gira un 1,8 % más rápido de lo pedido. Los sketches de abajo calculan la frecuencia real y la usan para medir el tiempo del movimiento.
  • La frecuencia es la misma para todo el chip. Cambiarla para Q0.0 la cambia para todas las salidas PWM de ese PCA9685. Usa las otras salidas del chip como señales de encendido y apagado, como DIR y ENA aquí, o para cargas que no dependan de la frecuencia.

En el ESP32 PLC 14, de Q0.0 a Q0.3 son pines directos del ESP32 con su propio PWM LEDC, así que el límite de 24 a 1525 Hz del PCA9685 no se aplica. Los sketches de este artículo están escritos para las salidas del PCA9685.

Cómo funcionan un motor paso a paso y su driver

Un motor paso a paso gira en pasos angulares fijos en lugar de girar de forma continua. El 17HS16-2004S tiene un ángulo de paso de 1,8°, es decir, 200 pasos por vuelta. Cada pulso que recibe el driver mueve el motor un paso, o una fracción de paso en modo micropaso, así que el número de pulsos fija la posición y la frecuencia de los pulsos fija la velocidad.

El PLC no puede alimentar las bobinas del motor directamente. El driver recibe tres señales lógicas y conmuta la corriente de las bobinas:

  • PUL (pulso): un pulso por paso o micropaso.
  • DIR (sentido): horario o antihorario.
  • ENA (habilitación): con las entradas cableadas como en esta guía, una señal alta deshabilita el driver y deja libre el motor, y una señal baja lo habilita.

Especificaciones del motor NEMA 17 17HS16-2004S

EspecificaciónValor
TipoBipolar
Par de retención0,45 Nm
Corriente nominal2 A por fase
Resistencia de fase1,1 Ω
Inductancia2,6 mH
Ángulo de paso1,8° (200 pasos por vuelta)

Configuración del driver TB6600

El driver tiene seis interruptores DIP. De S1 a S3 eligen los micropasos y de S4 a S6 la corriente. La tabla impresa en el driver de esta prueba da estas combinaciones:

MicropasosPulsos por vueltaS1S2S3
1200ONONOFF
2 (2/A)400ONOFFON
4800ONOFFOFF
81600OFFONOFF
163200OFFOFFON
326400OFFOFFOFF

Para el motor de 2 A, fija la corriente en 2,0 A (2,2 A de pico): S4 ON, S5 OFF, S6 OFF. Los sketches usan 2 micropasos. Otras versiones del driver pueden llevar otra tabla, así que sigue siempre la etiqueta de tu driver.

Cableado del ESP32 PLC al driver TB6600

DesdeHastaFunción
Q0.0PUL+Pulsos de paso
Q0.1DIR+Sentido
Q0.2ENA+Habilitación
GND del PLCPUL-, DIR-, ENA-Común de las señales
Motor azul / rojoA- / A+Bobina A
Motor negro / verdeB- / B+Bobina B
Fuente de alimentación de 24 VdcVdc del PLC y VCC del driverAlimentación

Las salidas del ESP32 PLC toman su tensión de QVdc. En este montaje QVdc va a 5 V y COM(-) a GND, así que las señales llegan al driver a 5 V. La misma fuente de 24 Vdc alimenta el PLC y el driver. Comprueba los colores del cable de tu motor en su datasheet: cambian según el fabricante.

Esquema de cableado: fuente de 24 V al ESP32 PLC y al TB6600, salidas Q0.0, Q0.1 y Q0.2 a PUL+, DIR+ y ENA+, y las bobinas del motor a A y B
Cableado de la fuente de alimentación, el ESP32 PLC, el driver TB6600 y el motor NEMA 17.

Sketch de Arduino: velocidad constante

Instala el paquete de placas ESP32 de Industrial Shields en el IDE de Arduino y elige tu modelo de ESP32 PLC. El paquete ya incluye analogWriteSetFrequency() y analogWrite() para las salidas del PCA9685, así que no hace falta ninguna librería más. La primera versión de este artículo usaba la librería Adafruit PWM Servo Driver sobre el mismo chip; con el paquete actual no es necesaria.

La función moveRevs() recibe las vueltas (en negativo para girar al otro lado) y la velocidad en rpm. Fija la frecuencia, arranca los pulsos con un ciclo de trabajo del 50 % (2048 de 4095), espera el tiempo que necesita la frecuencia real para enviar los pulsos y los para.

// Stepper motor at constant speed with an ESP32 PLC and a TB6600 driver
// Q0.0 -> PUL+, Q0.1 -> DIR+, Q0.2 -> ENA+, GND -> PUL-, DIR-, ENA-

#define STEP_PIN Q0_0
#define DIR_PIN  Q0_1
#define ENA_PIN  Q0_2

#define MOTOR_STEPS 200                         // 1.8 degree motor
#define MICROSTEPS  2                           // DIP switches of the driver
#define STEPS_REV   (MOTOR_STEPS * MICROSTEPS)  // 400 pulses per turn

// Q0.x outputs come from a PCA9685 (25 MHz clock, 12-bit).
// The chip can only make the frequencies 25 MHz / (4096 x (prescaler + 1)).
// This returns the real frequency that analogWriteSetFrequency() will set.
float realFrequency(uint32_t hz) {
  uint32_t prescaler = (((25000000UL << 4) / (4096UL * hz)) >> 4) - 1;
  return 25000000.0 / (4096.0 * (prescaler + 1));
}

// Turns the motor 'revs' turns (negative = other direction) at 'rpm'.
// Returns the number of pulses sent, calculated from the real frequency.
float moveRevs(float revs, float rpm) {
  uint32_t hz = (uint32_t)(rpm * STEPS_REV / 60.0 + 0.5);
  if (hz < 24 || hz > 1525) {
    Serial.println("Speed out of range for this microstep setting");
    return 0;
  }
  float realHz = realFrequency(hz);
  unsigned long runMs = (unsigned long)(fabs(revs) * STEPS_REV * 1000.0 / realHz + 0.5);

  analogWriteSetFrequency(STEP_PIN, hz);
  digitalWrite(DIR_PIN, revs >= 0 ? HIGH : LOW);
  digitalWrite(ENA_PIN, LOW);                   // LOW = driver enabled
  analogWrite(STEP_PIN, 2048);                  // 50 % duty: pulses start
  delay(runMs);
  analogWrite(STEP_PIN, 0);                     // pulses stop
  digitalWrite(ENA_PIN, HIGH);                  // HIGH = driver disabled

  Serial.print("Requested Hz: "); Serial.print(hz);
  Serial.print("  real Hz: ");    Serial.print(realHz);
  Serial.print("  time ms: ");    Serial.println(runMs);
  return runMs * realHz / 1000.0;
}

void setup() {
  Serial.begin(115200);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(ENA_PIN, OUTPUT);
  digitalWrite(ENA_PIN, HIGH);
}

void loop() {
  moveRevs(1.5, 50);    // 1.5 turns forward at 50 rpm
  delay(500);
  moveRevs(-1.5, 50);   // and back
  delay(500);
}

Con 2 micropasos, 50 rpm piden 333 Hz. El PCA9685 da 339,08 Hz, así que 1,5 vueltas tardan 1769 ms en lugar de 1800 ms.

Sketch de Arduino: rampa de aceleración

Una carga con inercia puede perder pasos si el motor arranca o para a toda velocidad. Este sketch sigue un perfil trapezoidal: cada 30 ms calcula la velocidad que permiten la aceleración desde el inicio y la frenada hasta el final, y actualiza la frecuencia. Cuenta los pulsos enviados a partir de la frecuencia real de cada periodo, así que para cuando llega al objetivo.

// Stepper motor with acceleration ramp, ESP32 PLC + TB6600 driver
// Same wiring as the constant speed example

#define STEP_PIN Q0_0
#define DIR_PIN  Q0_1
#define ENA_PIN  Q0_2

#define MOTOR_STEPS 200
#define MICROSTEPS  2
#define STEPS_REV   (MOTOR_STEPS * MICROSTEPS)

#define MIN_HZ      24        // lowest PCA9685 frequency
#define MAX_HZ      1525      // highest PCA9685 frequency
#define UPDATE_MS   30        // speed update period

float realFrequency(uint32_t hz) {
  uint32_t prescaler = (((25000000UL << 4) / (4096UL * hz)) >> 4) - 1;
  return 25000000.0 / (4096.0 * (prescaler + 1));
}

// Moves 'revs' turns with a trapezoidal profile.
// maxRpm: cruise speed. accel: acceleration in pulses per second squared.
void moveRamp(float revs, float maxRpm, float accel) {
  float target = fabs(revs) * STEPS_REV;          // pulses to send
  float maxHz = maxRpm * STEPS_REV / 60.0;
  if (maxHz > MAX_HZ) maxHz = MAX_HZ;
  if (maxHz < MIN_HZ) maxHz = MIN_HZ;

  float done = 0;                                  // pulses sent so far
  uint32_t lastHz = 0;
  float realHz = 0;

  digitalWrite(DIR_PIN, revs >= 0 ? HIGH : LOW);
  digitalWrite(ENA_PIN, LOW);

  while (done < target) {
    float left = target - done;
    // speed allowed by the acceleration (from start) and the braking (to end)
    float vUp = sqrt(2.0 * accel * done);
    float vDown = sqrt(2.0 * accel * left);
    float v = min(maxHz, min(vUp, vDown));
    if (v < MIN_HZ) v = MIN_HZ;

    uint32_t hz = (uint32_t)(v + 0.5);
    if (hz != lastHz) {
      analogWriteSetFrequency(STEP_PIN, hz);
      if (lastHz == 0) analogWrite(STEP_PIN, 2048);
      lastHz = hz;
      realHz = realFrequency(hz);
    }

    // last period: wait only the time the remaining pulses need
    unsigned long waitMs = UPDATE_MS;
    float pulses = realHz * UPDATE_MS / 1000.0;
    if (pulses > left) {
      waitMs = (unsigned long)(left * 1000.0 / realHz + 0.5);
      pulses = left;
    }
    delay(waitMs);
    done += pulses;
  }

  analogWrite(STEP_PIN, 0);
  digitalWrite(ENA_PIN, HIGH);
}

void setup() {
  Serial.begin(115200);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(ENA_PIN, OUTPUT);
  digitalWrite(ENA_PIN, HIGH);
}

void loop() {
  moveRamp(2, 120, 2000);    // 2 turns, up to 120 rpm, 2000 pulses/s2
  delay(500);
  moveRamp(-2, 120, 2000);
  delay(500);
}

Cada cambio de frecuencia es una escritura I2C al PCA9685, así que la velocidad cambia a saltos cada 30 ms y no pulso a pulso. Con una aceleración alta los saltos son grandes y el movimiento es menos suave. Baja la aceleración o actualiza más a menudo si el motor vibra o pierde pasos.

Qué hemos comprobado y qué debes comprobar tú

Los dos sketches compilan a un fichero objeto sin avisos con el paquete ESP32 2.7.1 de Industrial Shields, para el ESP32 PLC 21 y el ESP32 PLC 19R (solo prueba de compilador, no una compilación completa con el IDE de Arduino). También hemos ejecutado su lógica de tiempos en un PC con salidas simuladas: 1,5 vueltas a 50 rpm dan 599,8 de 600 pulsos, y una rampa de 2 vueltas da 800,0 de 800 pulsos. Estas versiones nuevas aún no se han probado con un motor, así que pruébalas primero en tu máquina a baja velocidad.

  • La posición es en lazo abierto. Se puede perder un pulso cuando el PWM arranca o para, y el error se suma en muchos movimientos. Usa un sensor de origen en una entrada del PLC para poner a cero la posición al inicio de cada ciclo.
  • Si el motor vibra sin girar, baja la velocidad o la aceleración, o sube la corriente del driver hasta la corriente nominal del motor.
  • Si necesitas más velocidad, usa menos micropasos: el límite de frecuencia es el mismo y cada pulso mueve más el motor.

Preguntas frecuentes sobre motores paso a paso y el ESP32 PLC

¿Puede un ESP32 PLC mover un motor paso a paso sin driver?

No. Las salidas del PLC dan señales lógicas. Un driver como el TB6600 conmuta la corriente de las bobinas del motor y hace los micropasos.

¿Qué salidas del ESP32 PLC pueden enviar los pulsos de paso?

Cualquier salida PWM. En los ESP32 PLC 21, 42 y 58, de Q0.0 a Q0.7 son salidas del PCA9685 de 24 Hz a 1525 Hz. Usa las otras salidas del mismo chip para DIR, ENA o cargas de encendido y apagado, porque la frecuencia es compartida.

¿Cómo calculo la velocidad del motor?

rpm = frecuencia × 60 / pulsos por vuelta. Con 2 micropasos un motor de 200 pasos necesita 400 pulsos por vuelta, así que 1000 Hz son 150 rpm. Recuerda que el PCA9685 redondea la frecuencia hacia arriba, al siguiente valor que puede dar.

¿Sabe el ESP32 PLC la posición del motor?

No con estos sketches. Calculan la posición a partir del tiempo y la frecuencia. Para conocer la posición, añade un sensor de origen, o usa una entrada de encoder si la máquina necesita lazo cerrado.

¿Puedo usar la librería Adafruit PWM Servo Driver?

Controla el mismo PCA9685, pero el paquete de Industrial Shields ya inicializa el chip y tiene analogWriteSetFrequency(). Usar una sola interfaz para el chip evita que dos partes del código le escriban configuraciones distintas.

Llévalo a la práctica

Motores paso a paso en máquinas reales: una cinta de etiquetado de botellas, un equipo de investigación con láser y el driver para tu propio prototipo.

Buscar en nuestro blog

Control de motor paso a paso con un ESP32 PLC
Boot & Work Corp. S.L, Ricard Franch Argullol 15 de junio de 2026
Compartir esta publicación
Etiquetas

¿Estás buscando tu Controlador Lógico Programable ideal?

Echa un vistazo a esta comparativa de producto de varios controladores industriales basados en Arduino.

Comparamos entradas, salidas, comunicaciones y otras especificaciones con las de los equipos de otras marcas destacadas.

Comparativa de PLCs