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:
| Micropasos | Pulsos por vuelta | Velocidad mínima (24 Hz) | Velocidad máxima (1525,9 Hz) |
|---|---|---|---|
| 1 (paso completo) | 200 | 7,2 rpm | 457,8 rpm |
| 2 | 400 | 3,6 rpm | 228,9 rpm |
| 4 | 800 | 1,8 rpm | 114,4 rpm |
| 8 | 1600 | 0,9 rpm | 57,2 rpm |
| 16 | 3200 | 0,45 rpm | 28,6 rpm |
| 32 | 6400 | 0,22 rpm | 14,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ón | Valor |
|---|---|
| Tipo | Bipolar |
| Par de retención | 0,45 Nm |
| Corriente nominal | 2 A por fase |
| Resistencia de fase | 1,1 Ω |
| Inductancia | 2,6 mH |
| Ángulo de paso | 1,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:
| Micropasos | Pulsos por vuelta | S1 | S2 | S3 |
|---|---|---|---|---|
| 1 | 200 | ON | ON | OFF |
| 2 (2/A) | 400 | ON | OFF | ON |
| 4 | 800 | ON | OFF | OFF |
| 8 | 1600 | OFF | ON | OFF |
| 16 | 3200 | OFF | OFF | ON |
| 32 | 6400 | OFF | OFF | OFF |
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
| Desde | Hasta | Función |
|---|---|---|
| Q0.0 | PUL+ | Pulsos de paso |
| Q0.1 | DIR+ | Sentido |
| Q0.2 | ENA+ | Habilitación |
| GND del PLC | PUL-, DIR-, ENA- | Común de las señales |
| Motor azul / rojo | A- / A+ | Bobina A |
| Motor negro / verde | B- / B+ | Bobina B |
| Fuente de alimentación de 24 Vdc | Vdc del PLC y VCC del driver | Alimentació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.

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.
Más sobre el ESP32 PLC
- ESP32 PLC: prueba de las salidas PWM
- Cómo elegir un controlador industrial
- Aplicaciones industriales: 50 ejemplos reales por sector
- Secuenciado seguro de electroválvulas en un ESP32 PLC con una máquina de estados
Mira todos los modelos de la familia ESP32 PLC.
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.
Caso de estudio
Etiqueta botellas en movimiento, aunque cambie la velocidad de la línea.
Un encoder, una fotocélula y dos motores paso a paso en un solo PLC basado en Arduino.
Lee el caso de estudio
Caso de éxito
Un láser muestra la fibra. Un PLC gira la madera.
Un PLC gira una muestra de madera en pasos de 3,6° con un motor paso a paso, 100 posiciones por vuelta, sin perder ningún paso.
Lee el caso de éxito
Producto
Controlador de motor paso a paso, 4 A, de 9 a 42 V
Un driver para motores paso a paso de 2 y 4 fases, para combinar con las salidas de un PLC.
Ver el producto



