Emulación de Zumo32u4 (puertos E/S y LCD)


El robot Zumo 32U4, un modelo compacto, programable y listo para usar, basado en el microcontrolador ATmega32U4 y compatible con Arduino. Mide menos de 10×10 cm, pesa unos 160 g sin baterías y está especialmente diseñado para competiciones Mini-Sumo. Incorpora motores con reductoraencoderspantalla OLEDsensores de línea y proximidad, una IMU (acelerómetro, giroscopio y magnetómetro), además de botones, LEDs y un buzzer para interacción.

El robot se programa directamente desde el entorno Arduino, sin necesidad de montaje previo, y se puede ampliar mediante sus líneas de E/S y alimentación, lo que lo hace ideal tanto para educación como para competición robótica.

En este blog nos hemos propuesto emular el Zumo 32U4 con hardware más sencillo y económico de un modo general https://soloelectronicos.com/2025/12/10/emulacion-de-zumo32u4-con-un-arduino/ (por ejemplo, con un Arduino Uno o Nano, controlador L298N, motores DC, pantalla LCD, LEDs y pulsadores).

También se ha tratado el sonido, un tema especialmente atractivo por su «sencillez» y resultados https://soloelectronicos.com/2025/12/11/emulacion-zumo-de-sonidos-con-atmega/

Esta emulación ofrece varias ventajas:

  • Económica y accesible, permitiendo disponer de más unidades para enseñanza o experimentación.
  • Formativa, ya que obliga a conectar y comprender cada componente (drivers, PWM, control de motor, entradas y salidas).
  • Compatible y útil para desarrollo de código, permitiendo probar lógicas de control sin necesitar el robot original.
  • Flexible y robusta, ideal para investigación o docencia, ya que se pueden introducir fallos o modificar el hardware sin riesgo.

Para emular el Zumo 32U4 con un ATmega328P y poder usar las clases de la librería Zumo32U4 (buttons, motors, buzzer, LEDs), como ya se ha hablado, necesitamos replicar su “interfaz lógica”: mismos pines Arduino que espera la librería, pero cableados a nuestro hardware equivalente.​ Hemos hablado de motores, pulsadores,leds y el sonido .

En este ultimo post vamos a resumir todos lo dispositivos fácilmente emulables ( leds, motores, pulsadores, buzzer ) a la vez que vamos a intentar sopesar el tema de la pantalla y su emulación con un Arduino Mega.

RESUMEN DE LA EMULACION DE ZUMO TRATADA EN POST ANTERIORES

1. Qué espera la librería Zumo32U4:La documentación indica, a nivel de pines Arduino:

  • Botón A → pin digital 14 (A0)
  • Botón B → pin digital 4
  • Botón C → pin digital 17 (A3)
  • LED amarillo usuario → pin 13
  • LED rojo usuario → mismo pin que botón C (17/A3)
  • Motores:
    • Motor derecho velocidad (PWM) → pin 9
    • Motor derecho dirección → pin 15 (A1)
    • Motor izquierdo velocidad (PWM) → pin 10
    • Motor izquierdo dirección → pin 16 (A2)
  • Buzzer → pin 6

Las clases de la librería (Zumo32U4ButtonA/B/CZumo32U4MotorsZumo32U4Buzzer, etc.) asumen estos números de pin.

2. Estrategia con ATmega328P (Uno “emulado”): como un ATmega328P no tiene el mismo mapeo interno, tenemos dos opciones:

  1. No tocar la librería y cablear tus periféricos a los mismos números de pin Arduino que usa el Zumo.
  2. Modificar la librería para cambiar números de pin a otros de nuestro gusto.

Lo más sencillo es la opción 1, es decir cablear de esta forma en una placa tipo Uno/Nano​

La asignación recomendada (para no tocar la librería), suponiendo que compilamos como si fuera un Zumo32U4 (core 32U4) es complicado en un 328P, así que la forma práctica es reusar los números de pin lógicos y adaptar el hardware:

Botones (normalmente abiertos a GND, INPUT_PULLUP)

  • Botón A:
    • Zumo: pin 14 (A0) → en tu 328P: usa A0.
  • Botón B:
    • Zumo: pin 4 → en tu 328P: usa D4.
  • Botón C:
    • Zumo: pin 17 (A3) → en tu 328P: usa A3.

Cableado:

  • Un terminal del pulsador → pin (A0, D4, A3).
  • Otro terminal → GND.
  • En código se usará INPUT_PULLUP, lógica: LOW = pulsado.

Motores (con L298N)

  • Motor derecho:
    • Velocidad (PWM): Zumo pin 9 → usa D9.
    • Dirección: Zumo pin 15 (A1) → usa A1 (o D3/D5 si prefieres digital puro).
  • Motor izquierdo:
    • Velocidad (PWM): Zumo pin 10 → usa D10.
    • Dirección: Zumo pin 16 (A2) → usa A2.

Ejemplo lógico:

  • L298N IN1/IN2 ← A1/D9 (derecha).
  • L298N IN3/IN4 ← A2/D10 (izquierda).

LEDs externos

  • LED amarillo:
    • Zumo: pin 13 → usa D13 (LED integrado + LED externo en serie o separado).
  • LED rojo:
    • Zumo: comparte pin con botón C (17/A3) → en una emulación es más cómodo usar otro pin y luego adaptar la librería, pero si quieres ser fiel:
      • LED rojo + resistor → A3.
      • Botón C comparte A3 y se detecta por lectura del pin.

Buzzer

  • Zumo: pin 6
  • Para compatibilidad con la clase Zumo32U4Buzzer, tendrías que:
    • O bien mover tu buzzer a D6.
    • O bien modificar la librería para que use pin 7.

3. Uso práctico con ATmega328P: la librería Zumo32U4 está pensada para un ATmega32U4 (Leonardo/Micro-like). Compilarla tal cual sobre un core Uno (328P) nos va a dar problemas porque internamente usa características específicas del 32U4 (USB, Timer4, etc.). Para nuestro objetivo (emulación docente):

  • Más viable:
    • Copiar solo las ideas/clases y escribir nuestras propias mini-clases equivalentes para 328P:
      • class ButtonA { pinMode(A0, INPUT_PULLUP); bool isPressed() { return !digitalRead(A0); } };
      • class Motors { … } que use los pines de L298N.
      • class Buzzer { tone(7, f); … } con el pin 7.
  • Si insistes en usar exactamenteZumo32U4.h:
    • Compila con un core de placa 32U4 (por ejemplo, seleccionar “Arduino Leonardo”) aunque el hardware real sea un 328P no es correcto eléctricamente.
    • O crea una “board definition” personalizada para 328P que imite los nombres de pines y timers del 32U4 (trabajo considerable).

4. Resumen de pines para una emulacion (versión directa 328P): para dejarlo claro, si ignoramos la librería y solo queremos el mismo comportamiento:

  • Botón A: A0 (NO a GND, INPUT_PULLUP).
  • Botón B: D4 (NO a GND).
  • Botón C: A3 (NO a GND).
  • Motor derecho: PWM D9, dirección A1.
  • Motor izquierdo: PWM D10, dirección A2.
  • LED amarillo: D13.
  • LED rojo: D12 (o A3 si quieres imitar el multiplexado).
  • Buzzer: D7 (y usas tone(7, f)).

La librería Zumo32U4 solo funciona con microcontroladores ATmega32U4 y no con ATmega328P (Arduino Uno u otros). Es recomendable por tanto seleccionar una placa basada en 32U4 (como A-Star 32U4) en el menú Boards para usar esa librería.

Opciones para seguir con un ATmega328P (Arduino Uno):

  1. No usar la librería Zumo32U4 directamente, porque está diseñada específicamente para 32U4 y usa características hardware exclusivas ( ES JUSTAMENTE LO QUE SE HA TRATADO EN ESTE POST Y POST ANTERIOES).
  2. Crear tu propia implementación “simulada” de las clases Button, Motor, Led y Buzzer usando pines ATmega328P (tal como definimos los mapeos) para emular la funcionalidad básica.
  3. Cambiar a usar una placa basada en ATmega32U4 (como Arduino Leonardo, Micro o la A-Star 32U4) si quieres usar la librería oficial.

DISPLAY EMULADO

Centrémonos a ahora en la emulación de un Display OLED Zumo32U4 con LCD I2C ATmega328P

El Zumo32U4 tiene un display OLED 1.3″ monocromo (128×64 píxeles) conectado por I2C. EL Típico u LCD I2C es perfecto para emularlo. Es cierto que en versiones posteriores se ha sustituido por una pantalla OLED, pero por simplificar nos centraremos en el LCD dado que requiere menos lineas GPIO.

Debemos en el entorno Arduino importar la librería correspondiente:

Librería Display Zumo32U4 → LiquidCrystal_I2C

Zumo32U4Tu LCD I2C
Zumo32U4OLEDLiquidCrystal_I2C
I2C: SDA A4, SCL A5Igual: A4, A5
Texto 21×4 caracteresTu LCD 16×2/20×4

Y respecto a los pines I2C (Fijos – No cambies)

Display I2C LCD:
SDA → A4 (todos Arduino)
SCL → A5 (todos Arduino) 
VCC → 5V
GND → GND

No debemos ante de nada olvidar la instalación de la Librería LCD I2C

Arduino IDE → Tools → Manage Libraries
Buscar: **"LiquidCrystal I2C"** → Frank de Brabander
Instalar → Reiniciar IDE

Asimismo, es crucial encontrar Dirección I2C de tu Display, para lo cual una vez todo conectado podemos usar el siguiente código en nuestro entorno Arduino:

// Scanner I2C - Ejecuta UNA vez
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("I2C Scanner");
}
void loop() {
for(byte i=8; i<120; i++) {
Wire.beginTransmission(i);
if (Wire.endTransmission() == 0) {
Serial.print("I2C: 0x"); Serial.println(i, HEX);
}
}
delay(5000);
}

Las direcciones típicas son 0x27 o 0x3F. Cambia en LiquidCrystal_I2C lcd(0x27, 16, 2);

Veamos ahora finalmente ver un ejemplo de código que usa un display con tres leds y dos pulsadores descritos estos últimos en post anteriores.

Código Completo: Emulación Display + LEDs + Botones

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Emulación Zumo32U4 display (16x2 típico)
LiquidCrystal_I2C lcd(0x27, 16, 2); // Cambia 0x27 por tu dirección I2C

#define BUTTON_A_PIN A0
#define BUTTON_B_PIN 4
#define LED_YELLOW_PIN 13
#define LED_RED_PIN 12

void setup() {
// Inicializar LCD I2C (emula Zumo OLED)
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Zumo32U4 EMU");
lcd.setCursor(0, 1);
lcd.print("Display OK!");
delay(2000);

// Botones y LEDs
pinMode(BUTTON_A_PIN, INPUT_PULLUP);
pinMode(BUTTON_B_PIN, INPUT_PULLUP);
pinMode(LED_YELLOW_PIN, OUTPUT);
pinMode(LED_RED_PIN, OUTPUT);
}

void loop() {
bool btnA = digitalRead(BUTTON_A_PIN) == LOW;
bool btnB = digitalRead(BUTTON_B_PIN) == LOW;

// LED ROJO: ON cuando botón A NO pulsado
digitalWrite(LED_RED_PIN, !btnA);

// LED AMARILLO: ON cuando botón B pulsado
digitalWrite(LED_YELLOW_PIN, btnB);

// EMULAR DISPLAY ZUMO32U4
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("BtnA:"); lcd.print(btnA ? "ON " : "OFF");
lcd.print(" BtnB:"); lcd.print(btnB ? "ON " : "OFF");

lcd.setCursor(0, 1);
lcd.print("Red:"); lcd.print(digitalRead(LED_RED_PIN) ? "ON " : "OFF");
lcd.print("Yel:"); lcd.print(digitalRead(LED_YELLOW_PIN) ? "ON" : "OFF");

delay(200); // Refresh 5Hz
}

Este código simula parte de la interfaz del robot Zumo 32U4 usando un Arduino con una pantalla LCD I2C y algunos elementos básicos como LEDs y botones. En la función setup(), se inicializa la pantalla LCD para mostrar un mensaje de bienvenida (“Zumo32U4 EMU – Display OK!”) y se configuran los pines: dos entradas con resistencia pull-up para los botones (A y B) y dos salidas para los LEDs rojo y amarillo. De este modo, se prepara el entorno para imitar la interacción que tendría un Zumo real con su pantalla y entradas físicas.

En el bucle principal loop(), el programa lee el estado de los botones y ajusta los LEDs de forma inversa o directa según el botón pulsado. Luego actualiza continuamente el LCD para mostrar en tiempo real el estado de los botones y de los LEDs, lo que permite verificar visualmente la interacción. Con un retardo de 200 ms (frecuencia de unas 5 actualizaciones por segundo), esta implementación emula de manera sencilla cómo el Zumo muestra información y responde a los mandos del usuario, sirviendo como práctica educativa de entrada/salida digital y manejo de pantallas I2C.

PROYECTO FINAL

Y ahora ya finalmente al proyecto anterior le añadimos la gestión de los motores junto a todo lo demás:

BOTONES:
A0 ← Btn A → GND
D4 ← Btn B → GND
A3 ← Btn C → GND

LEDS:
D13 ← LED Amarillo → 220Ω → GND
D12 ← LED Rojo → 220Ω → GND

BUZZER:
D6 ← Buzzer → GND

L298N:
D3 ← ENA (Izq)
A2 ← IN1 (Izq Dir)
D11 ← ENB (Der)
A1 ← IN3 (Der Dir)
OUT1,2 → Motor Izq
OUT3,4 → Motor Der

A continuación mostramos un código funcional que ejemplariza le emulación de la gestión del display LCD, pulsadores, leds y botones:

<Wire.h>
<LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F, 16, 2);

BUTTON_A_PIN A0 // AVANZAR
BUTTON_B_PIN 4 // RETROCEDER
BUTTON_C_PIN A3 // PARAR
LED_YELLOW_PIN 13
LED_RED_PIN 12
BUZZER_PIN 6

// ✅ PINS L298N COMPLETOS
M1_ENA_PIN 3 // Enable Motor Izq (PWM)
M1_DIR_PIN A2 // Dirección Motor Izq
M2_ENB_PIN 11 // Enable Motor Der (PWM)
M2_DIR_PIN A1 // Dirección Motor Der

bool lastBtnA = false, lastBtnB = false, lastBtnC = false;
String estado = «PARADO»;

void setup() {
Wire.begin();
lcd.init(); lcd.backlight(); lcd.clear();
lcd.setCursor(0,0); lcd.print(«Zumo32U4 v3.0»);
lcd.setCursor(0,1); lcd.print(«Motores OK»);
delay(2000);

// Botones con pull-up
pinMode(BUTTON_A_PIN, INPUT_PULLUP);
pinMode(BUTTON_B_PIN, INPUT_PULLUP);
pinMode(BUTTON_C_PIN, INPUT_PULLUP);

// LEDs
pinMode(LED_YELLOW_PIN, OUTPUT);
pinMode(LED_RED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);

// ✅ MOTORES – TODOS LOS PINES
pinMode(M1_ENA_PIN, OUTPUT);
pinMode(M1_DIR_PIN, OUTPUT);
pinMode(M2_ENB_PIN, OUTPUT);
pinMode(M2_DIR_PIN, OUTPUT);

// INICIALIZAR PINES EN LOW
digitalWrite(M1_ENA_PIN, LOW);
digitalWrite(M1_DIR_PIN, LOW);
digitalWrite(M2_ENB_PIN, LOW);
digitalWrite(M2_DIR_PIN, LOW);

parar(); // Estado inicial
}

void loop() {
bool btnA = digitalRead(BUTTON_A_PIN) == LOW;
bool btnB = digitalRead(BUTTON_B_PIN) == LOW;
bool btnC = digitalRead(BUTTON_C_PIN) == LOW;

// Detectar pulsación única (flanco ↓)
bool btnA_pressed = btnA && !lastBtnA;
bool btnB_pressed = btnB && !lastBtnB;
bool btnC_pressed = btnC && !lastBtnC;

// CONTROL MOTORES
if (btnA_pressed) {
avanzar();
tone(BUZZER_PIN, 1000, 200);
}
if (btnB_pressed) {
retroceder();
tone(BUZZER_PIN, 800, 200);
}
if (btnC_pressed) {
parar();
tone(BUZZER_PIN, 500, 300);
}

// LEDs indicadores
digitalWrite(LED_YELLOW_PIN, (estado == «AVANZAR»));
digitalWrite(LED_RED_PIN, (estado == «RETROCEDER»));

// DISPLAY
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(«Estado: «); lcd.print(estado);

lcd.setCursor(0, 1);
lcd.print(«A:»); lcd.print(btnA ? «ON » : «OFF»);
lcd.print(«B:»); lcd.print(btnB ? «ON » : «OFF»);
lcd.print(«C:»); lcd.print(btnC ? «ON» : «OFF»);

// Actualizar estados
lastBtnA = btnA;
lastBtnB = btnB;
lastBtnC = btnC;

delay(50);
}

// ✅ FUNCIONES MOTORES CORREGIDAS
void avanzar() {
digitalWrite(M1_DIR_PIN, HIGH); // Izq adelante
digitalWrite(M2_DIR_PIN, LOW); // Der adelante
analogWrite(M1_ENA_PIN, 180); // Velocidad suave
analogWrite(M2_ENB_PIN, 180);
estado = «AVANZAR»;
}

void retroceder() {
digitalWrite(M1_DIR_PIN, LOW); // Izq atrás
digitalWrite(M2_DIR_PIN, HIGH); // Der atrás
analogWrite(M1_ENA_PIN, 180); // Velocidad suave
analogWrite(M2_ENB_PIN, 180);
estado = «RETROCEDER»;
}

void parar() {
// ✅ APAGAR COMPLETAMENTE AMBOS MOTORES
analogWrite(M1_ENA_PIN, 0); // Motor 1 OFF
analogWrite(M2_ENB_PIN, 0); // Motor 2 OFF
digitalWrite(M1_DIR_PIN, LOW); // Direcciones LOW
digitalWrite(M2_DIR_PIN, LOW);
estado = «PARADO»;
}

Este código implementa una emulación de control de robot tipo Zumo usando un Arduino, una pantalla LCD I2C, tres botones, dos LEDs, un buzzer y un módulo de potencia L298N para dos motores DC. En setup() se inicializa la LCD en la dirección I2C 0x3F, se muestra un mensaje de arranque, se configuran los botones con INPUT_PULLUP, los LEDs, el buzzer y todos los pines del L298N como salidas, dejando los motores apagados inicialmente mediante parar(). Además, se usa una variable de estado (estado) que mantiene si el “robot” está AVANZAR, RETROCEDER o PARADO y que luego se mostrará en pantalla.​

BotónAcciónLEDDisplaySonido
AAvanzarAmarillo ON«AVANZAR»1000Hz
BRetrocederRojo ON«RETROCEDER»800Hz
CPARAROFF«PARADO»500Hz

En loop() se leen los tres botones (A=avanzar, B=retroceder, C=parar) con lógica de flanco descendente para que cada pulsación cuente solo una vez, y según el botón se llama a avanzar(), retroceder() o parar(), disparando también un tono distinto en el buzzer con tone() como feedback sonoro. Las funciones de motor fijan la dirección con los pines M1_DIR_PIN y M2_DIR_PIN y la velocidad mediante PWM en M1_ENA_PIN y M2_ENB_PIN, tal y como se hace habitualmente al controlar el L298N con Arduino. Los LEDs indican el modo (amarillo al avanzar, rojo al retroceder) y la LCD se refresca continuamente para mostrar el estado actual y si cada botón está ON/OFF, creando una interfaz sencilla pero completa de prueba y depuración del “robot” emulado.