Robot móvil controlado por Wi-Fi en tiempo real
Auto controlado a distancia con arquitectura de dos placas: una ESP32-S3 crea la red Wi-Fi y reenvía comandos TCP por UART a una STM32 con FreeRTOS, que maneja los motores y el servo de dirección y frena ante obstáculos. Se opera desde una interfaz en PyQt5.
Trabajo integrador individual de Microcontroladores y Electrónica en Potencia (UNCuyo, 2025).
- STM32
- C
- ESP32
- ESP-IDF
- FreeRTOS
- PyQt5
- TCP
- UART
- PWM

El proyecto y su origen
El objetivo era un vehículo que reciba comandos desde una interfaz gráfica en una PC y los ejecute en tiempo real: control de motores DC, detección de obstáculos con un sensor ultrasónico, una pantalla OLED para el estado y control inalámbrico por Wi-Fi.
La idea viene de una arquitectura similar con la que trabajé en la ENIB (Brest). No tenía el mismo hardware, así que lo rediseñé con lo que tenía: una NUCLEO-F446RE y una ESP32-S3 WROOM. La ESP32 era nueva para mí, así que tuve que aprender ESP-IDF y PlatformIO sobre la marcha. No tiene fin industrial; es una base para robótica móvil y sistemas embebidos.
Arquitectura: dos placas y una PC
Dividí el trabajo en dos para que el control en tiempo real no compita con la red:
- PC: interfaz en PyQt5 que envía comandos de texto por Wi-Fi/TCP.
- ESP32-S3 (ESP-IDF, FreeRTOS): crea su propio punto de acceso Wi-Fi, corre un servidor TCP y reenvía cada comando a la STM32 por UART.
- STM32 NUCLEO-F446RE (FreeRTOS): recibe los comandos por UART con DMA y maneja el resto: PWM del TIM3 y GPIO para los motores DC a través de un DRV8833, PWM del TIM12 para el servo de dirección MF90, TIM2 para disparar y medir el HC-SR04, e I²C para la OLED SSD1306.
- Alimentación: dos bancos de dos celdas 18650 en serie y dos reguladores step-down XL4016, uno de 5 V para la NUCLEO y la ESP32 y otro de 6 V para el driver de motores y el servo.

Cómo se construyó
Lo fui armando por etapas y validé cada una antes de pasar a la siguiente:
- Pruebas unitarias de cada módulo por separado (sensor ultrasónico, servo, motores DC).
- Control básico de movimiento: motores y servo manejados desde la NUCLEO con comandos escritos por UART2, y luego una interfaz simple para enviarlos.
- Integración de la ESP32 para el control por Wi-Fi. Su cámara y las muchas variantes de la placa me obligaron a revisar con cuidado los pines y la alimentación.
- Un primer módulo de cámara con un panel de video en la interfaz y un detector simple de objetos rojos que dibujaba un bounding box. Lo dejé de lado al final: no estaba ligado al control embebido, así que quedó como trabajo futuro.
- Diseño físico: un chasis bajado de Thingiverse e impreso a mayor escala. El montaje de las piezas fue lo difícil, sobre todo el servo (ver la sección de problemas).
- Pruebas primero en una base fija y luego en el piso, siempre enviando los comandos por UART desde la PC.
- Pantalla OLED, que además permitió usar el bus I²C.
- Integración final de la alimentación y pruebas completas con control remoto por Wi-Fi.


Firmware de la STM32 con FreeRTOS
Cada función es una tarea, y las interrupciones solo liberan semáforos para mantenerse cortas:
- Los comandos llegan por UART2/UART4 (115200 baudios) con recepción por DMA. El callback libera un semáforo binario que despierta a CmdProcessing_Task (prioridad por encima de la normal).
- Los comandos son líneas de texto: vXXX fija la velocidad de −100 a 100 (el signo es el sentido) y sXXX fija el ángulo del servo. Todo lo demás se ignora. Los comandos válidos van a la cola de motores o de servo.
- Motor_Task (prioridad normal) toma de la cola de motores y aplica sentido y ciclo de trabajo PWM. Servo_Task (baja) convierte el ángulo en un pulso de 1 a 2 ms.
- UltraSonic_Task (prioridad alta, para que la seguridad gane): el TIM2 emite un disparo de 10 µs cada 100 ms (unos 10 Hz) y dos canales de captura miden el eco. Si el eco dura menos de 1160 µs (unos 20 cm) manda un comando de parada a la cola de motores.
- OLED_Task (baja) muestra la distancia, o "Obstaculo" cuando frena.
El manejo del ultrasonido es propio, sin librerías externas; el driver de la OLED es una librería SSD1306 de código abierto.

Pasarela ESP32 e interfaz en PC
El firmware de la ESP32 está dividido en módulos: main inicializa la memoria y la UART y lanza la tarea de Wi-Fi; wifi_manager configura el punto de acceso; tcp_server escucha en el puerto 1234 y arranca una tarea de recepción por cliente, que separa el texto por saltos de línea; uart_comm reenvía cada línea a la NUCLEO con un semáforo y una tarea de envío dedicada.
La interfaz en PC es PyQt5 con una separación MVC interna (vista, controlador, modelo). Un worker serie permite hablar directo con la placa por UART, que es como probé antes del Wi-Fi, y un worker TCP se conecta a la ESP32 como cliente. La interfaz también tiene un panel de video que quedó del intento con la cámara.
Demostración
Teleoperación del prototipo desde la interfaz de la PC.
Cifras
Medido:
- NUCLEO más periféricos (incluidos OLED y sensor): unos 110 mA a 3,3 V con los motores a máxima velocidad.
- ESP32: unos 52 mA a 5 V, con picos mayores al iniciar el Wi-Fi.
- Auto completo en reposo, sin motores ni carga: unos 100 mA.
Estimado:
- Autonomía: de 3 a 4 horas en reposo y de 1,5 a 2,5 horas con los motores en marcha.
- Alcance del Wi-Fi: de 15 a 20 m en interiores.
Otros valores de diseño: precisión del HC-SR04 de ±1 cm entre 2 y 200 cm, muestreado a unos 10 Hz; un divisor de 200 Ω y 100 Ω baja la señal de eco de 5 V a unos 3,3 V; filtrado de 470 µF y 100 nF en la rama de 5 V, doble en la de 6 V, más 470 µF en el driver de motores y en el servo.
Problemas y próximos pasos
- Servo: su torque era muy bajo para girar las ruedas con firmeza. Fijarlo con alambre dejó algo de deslizamiento, y me costó hacer coincidir el ángulo físico natural del servo con el ángulo digital de marcha recta.
- Alimentación: aparecieron problemas de alimentación al sacar los cables USB. Agregué un segundo XL4016 y dividí el circuito en dos ramas independientes, 5 V para la lógica y 6 V para motores y servo, con lo que las pruebas en el piso quedaron estables.
- TCP: desconexiones esporádicas al enviar muchos comandos seguidos. No impide que el auto funcione, pero es lo principal por corregir.
- Sin encoders: la velocidad es a lazo abierto, así que no hay control PID.
Siguiente: encoders y PID, un mejor servidor TCP en la ESP32, una interfaz móvil o web, la cámara con IA sobre el stream de video y más sensores para conducción autónoma. En una versión comercial hipotética cambiaría el HC-SR04 por un sensor láser VL53L0X, usaría una Raspberry Pi para Wi-Fi y visión, motores Pololu de 12 V y una batería LiPo 4S.