Emulación Zumo de sonidos con ATmega


Como adelantábamos en un post anterior sobre la emulación del robot Zumo32 con un ATMEGA, en este post vamos a tratar un tema tana atractivo con el sonido usando para ello solo el buzzer emulado (ojo porqeu solo disponemos de un unico bit para obtener toda la funcionalidad).

Para los amigos lectores que se pregunten si es equivalente en ambiente Arduino la instrucción tone (puzzer_pin,nota) por buzzer.playNote(nota,1000,15),  NO son equivalentes exactamente, dado que la primera instrucción corresponde al código nativo de Arduino , y el segundo requiere la librería de Zumo32 y por tanto va condicionada a usar un Zumo32 o en su defecto un Arduino Leonardo ( de modo que en nuestro Atmega32 no podremos usar el segundo a no ser que modifiquemos la librería original.

tone(BUZZER_PIN, nota) y buzzer.playNote(nota, 1000, 15) tienen diferencias funcionales críticas:

Diferencias Técnicas

Aspectotone(pin, freq)buzzer.playNote(freq, duration, octave)
Librería✅ Nativa Arduino❌ Zumo32U4Buzzer (solo 32U4)
DuraciónInfinita (hasta noTone())1000ms (automática)
OctavaIgnora parámetroOctava 15 (frecuencia alterada)
VolumenFijoVariable (polifonía Zumo)
Blocking✅ No bloquea❌ Puede bloquear

Por tanto el código  CORRECTO

tone(BUZZER_PIN, notas[notaActual]);  // Reproduce INDEFINIDAMENTE
// ...
noTone(BUZZER_PIN);  // Para manualmente

Funciona perfecto en ATmega328P sin librerías externas.

buzzer.playNote() Sintaxis Completa

cpp// Zumo32U4Buzzer (SOLO ATmega32U4)
buzzer.playNote(NOTE_C(4), 1000);  // Frecuencia C4, 1000ms

Parámetros:
  • notaNOTE_C(4)NOTE_G(5), etc. (predefinidas)
  • duration: ms automático
  • octave: 0-8 (no 15, error en tu ejemplo)

Equivalencia Aproximada (No Exacta)

// ANTES (tu código - CORRECTO)
tone(BUZZER_PIN, 262); // C4 infinito
delay(1000);
noTone(BUZZER_PIN);

// "Similar" a:
buzzer.playNote(NOTE_C(4), 1000); // C4 x 1s automático

Recomendación para la emulacion de Zumo32 con ATmega328P:

Mantén tone() + noTone() – es más preciso y flexible:

// Tu código actual es ÓPTIMO
if (millis() - tiempoInicio >= 1000) {
tone(BUZZER_PIN, notas[notaActual]);
// ... display
// noTone() en pausas
}

A cambio buzzer.playNote() solo para Zumo32U4 real (no para una emulación con ATmega328P pero si funcional para Arduino Leornardo).

​EJEMPLO PRACTICO CON LA EMULACION DEL ZUMO 32 EN UNA PLACA ARDUINO ATMEGA32

Para comprender la potencia de no trabajar con librerías externas donde se supone que esta todo hecho , pero que a cambio no dejan apenas flexibilidad al programador , vamos a ver un ejemplo de como reproducir tres escalas musicales con la emulación de Zumo usando un ATmega328P o en la práctica cualquier Arduino:

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

LiquidCrystal_I2C lcd(0x3F, 16, 2);

#define BUZZER_PIN 6
#define BUTTON_A_PIN A0
#define BUTTON_B_PIN 4
#define BUTTON_C_PIN A3
#define LED_YELLOW_PIN 13
#define LED_RED_PIN 12

bool ledYellowState = false;
bool lastBtnA = false, lastBtnB = false;

// Escala completa C3 (130Hz) → B5 (1976Hz) - 3 octavas
int notas[] = {
  // C3 (65*2=130), D3(73*2=146), E3(82*2=164), F3(87*2=174), G3(98*2=196), A3(110*2=220), B3(123*2=246)
  130, 146, 164, 174, 196, 220, 246,
  // C4 (262), D4(294), E4(330), F4(349), G4(392), A4(440), B4(494)
  262, 294, 330, 349, 392, 440, 494,
  // C5 (523), D5(587), E5(659), F5(698), G5(784), A5(880), B5(988)
  523, 587, 659, 698, 784, 880, 988
};

String nombres[] = {
  "C3","D3","E3","F3","G3","A3","B3",
  "C4","D4","E4","F4","G4","A4","B4",
  "C5","D5","E5","F5","G5","A5","B5"
};

int numNotas = 21;  // 3 octavas × 7 notas
int escalaActual = 0;  // 0,1,2 (3 escalas)

void setup() {
  Wire.begin();
  lcd.init(); lcd.backlight(); lcd.clear();
  lcd.setCursor(0,0); lcd.print("3 Escalas C3-B5");
  lcd.setCursor(0,1); lcd.print("Iniciando...");
  delay(2000);
  
  pinMode(BUTTON_A_PIN, INPUT_PULLUP);
  pinMode(BUTTON_B_PIN, INPUT_PULLUP);
  pinMode(BUTTON_C_PIN, INPUT_PULLUP);
  pinMode(LED_YELLOW_PIN, OUTPUT);
  pinMode(LED_RED_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
}

void loop() {
  bool btnA = digitalRead(BUTTON_A_PIN) == LOW;
  bool btnB = digitalRead(BUTTON_B_PIN) == LOW;
  bool btnC = digitalRead(BUTTON_C_PIN) == LOW;
  
  // Toggle LED Amarillo
  bool btnA_pressed = btnA && !lastBtnA;
  bool btnB_pressed = btnB && !lastBtnB;
  if (btnA_pressed) ledYellowState = true;
  if (btnB_pressed) ledYellowState = false;
  digitalWrite(LED_YELLOW_PIN, ledYellowState);
  digitalWrite(LED_RED_PIN, !btnA);
  
  // REPRODUCCIÓN 3 ESCALAS COMPLETAS C3→B5
  static int notaActual = 0;
  static unsigned long tiempoInicio = 0;
  static bool reproduciendo = true;
  
  if (reproduciendo && millis() - tiempoInicio >= 1000) {
    tone(BUZZER_PIN, notas[notaActual]);
    
    // Mostrar nota actual
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Escala "); lcd.print(escalaActual + 1);
    lcd.print("/3 Nota:");
    
    lcd.setCursor(0, 1);
    lcd.print(nombres[notaActual]);
    lcd.print(" "); lcd.print(notas[notaActual]);
    lcd.print("Hz ");
    
    notaActual++;
    
    if (notaActual >= 7) {  // Fin de escala (7 notas)
      escalaActual++;
      notaActual = 0;
      noTone(BUZZER_PIN);
      delay(500);  // Pausa entre escalas
    }
    
    if (escalaActual >= 3) {  // 3 escalas completas
      reproduciendo = false;
      lcd.clear();
      lcd.setCursor(0,0); lcd.print("3 ESCALAS OK!");
      lcd.setCursor(0,1); lcd.print("C3-B5 Fin");
      noTone(BUZZER_PIN);
    }
    
    tiempoInicio = millis();
  }
  
  lastBtnA = btnA;
  lastBtnB = btnB;
  delay(10);
}

Este código extiende la emulación del Zumo32U4 añadiendo un buzzer musical que reproduce automáticamente 3 escalas completas (C3-B5) usando la función tone(), mientras mantiene el control de LEDs y pulsadores del ejemplo anterior. En setup() inicializa el LCD I2C (0x3F), configura los pines (A0=A, 4=B, A3=C para botones; 13=amarillo, 12=rojo, 6=buzzer) y muestra «3 Escalas C3-B5». Define dos arrays paralelos: notas[] con 21 frecuencias (130Hz C3 hasta 988Hz B5, 3 octavas × 7 notas diatónicas) y nombres[] con sus identificadores («C3″,»D3″,…,»B5»).​

En loop(), primero gestiona los botones con detección de flanco (anti-rebote): A enciende LED amarillo (latch), B apaga LED amarillo (latch), LED rojo sigue inverso de A instantáneamente. Luego, una máquina de estados automática con variables static (notaActual, tiempoInicio, reproduciendo) reproduce las escalas: cada 1000ms llama tone(6, frecuencia) de la nota actual, actualiza el LCD mostrando «Escala X/3 Nota: Nombre frecuenciaHz», avanza notaActual. Al llegar a la 7ª nota (fin escala), pasa a la siguiente escalaActual, pausa 500ms con noTone() y reinicia notaActual=0. Tras 3 escalas (21 notas total), para con reproduciendo=false y mensaje «3 ESCALAS OK!».

La reproducción es no bloqueante (usa millis() para timing), permitiendo que los botones respondan simultáneamente. El delay(10) final estabiliza lecturas sin interferir con los 1000ms de nota. Emula perfectamente el Zumo32U4Buzzer de Pololu, que usa PWM en pin 6 (PD7) para tonos precisos, demostrando control musical preciso en ATmega328P con pines mapeados equivalentes (pin 6 Uno → pin 6/PD7 Zumo).

En el siguiente video podemos ver el código anterior explicado por el autor que escribe estas líneas https://youtube.com/shorts/QYoSJSKwREc?feature=share

En el siguiente ejemplo ahora podemos ver para la misma funcionalidad (a excepción del LCD y los leds) las grandes diferencias del código anterior con el código para el propio robot usando su librería Zumo32U4 en lugar de código nativo.

#include <Wire.h>
#include <Zumo32U4.h>

Zumo32U4Buzzer buzzer;

void setup() {
}

void loop() {
  buzzer.playFrequency(440, 200, 12);
  delay(1000);

  for (int i = 3; i <= 5; i++) {

    buzzer.playFrequency(NOTE_C(i), 1000, 15);
    while (buzzer.isPlaying()) {}
    delay(1000);

    buzzer.playFrequency(NOTE_D(i), 1000, 15);
    while (buzzer.isPlaying()) {}
    delay(1000);

    buzzer.playFrequency(NOTE_E(i), 1000, 15);
    while (buzzer.isPlaying()) {}
    delay(1000);

    buzzer.playFrequency(NOTE_F(i), 1000, 15);
    while (buzzer.isPlaying()) {}
    delay(1000);

    buzzer.playFrequency(NOTE_G(i), 1000, 15);
    while (buzzer.isPlaying()) {}
    delay(1000);

    buzzer.playFrequency(NOTE_A(i), 1000, 15);
    while (buzzer.isPlaying()) {}
    delay(1000);

    buzzer.playFrequency(NOTE_B(i), 1000, 15);
    while (buzzer.isPlaying()) {}
    delay(1000);
  }
}

Este programa como ya adelantábamos usa la librería del Zumo32U4 para que el robot reproduzca tonos musicales con su zumbador integrado. Primero crea un objeto Zumo32U4Buzzer y, en el loop(), genera un pitido inicial de 440 Hz durante 200 ms con volumen 12 y espera 1 segundo. Después entra en un bucle for que recorre las octavas 3, 4 y 5; en cada octava va llamando a buzzer.playFrequency(NOTE_C(i) ... NOTE_B(i)) para reproducir las siete notas de la escala (Do, Re, Mi, Fa, Sol, La, Si) con duración de 1 segundo y volumen 15, esperando a que termine cada nota con while (buzzer.isPlaying()) {} y añadiendo un segundo de pausa entre ellas, de forma que se escuchan tres escalas ascendentes completas en distintas octavas usando las constantes de nota que proporciona la propia librería del Zumo32U4​.

Domina el Control de Motores DC con Arduino y el Módulo L298N: Guía Fácil y Práctica


Realmente hoy en dia es bastante asequible obtener módulos ya montados que realicen funciones auxiliares como por ejemplo controlar un motor DC normal o pasa a paso, realmente por un precio irrisorio (en nuestro caso por unos 4 € en Amazon ) . Un ejemplo de este tipo de módulos, son los controladores de motor basados en el L298N ,siendo este uno de los controladores de motores mas usados con microcontroladores y microprocesadores .

En efecto el Circuito Integrado (CI) L298N es uno de los controladores de motor más populares entre quienes trabajan con proyectos de robótica, automatización y sistemas embebidos. En la imagen superior puede observarse el módulo real, un conjunto compacto que integra el chip L298N acompañado de un gran disipador de calor. Este disipador es necesario porque el circuito puede manejar corrientes relativamente altas (hasta 2A por canal, dependiendo de la disipación térmica), algo habitual al controlar motores de corriente continua (DC) o motores paso a paso. Gracias a esta capacidad, el módulo resulta ideal para aplicaciones con microcontroladores como Arduino, ESP32 o Raspberry Pi, que por sí mismos no pueden suministrar suficiente corriente para mover motores.

Veamos ahora con mas detalle el esquema electrónico de este circuito:

El diagrama esquemático mostrado ofrece una visión más profunda del funcionamiento interno:

  • U1 (L298N): Es el núcleo del circuito. Contiene dos puentes H completos, lo que permite controlar de forma independiente dos motores DC o un motor paso a paso bipolar. Cada puente H puede invertir la polaridad aplicada al motor, logrando así el cambio de sentido de giro.
  • Entradas IN1–IN4 y ENA/ENB: Estas entradas reciben las señales lógicas desde el microcontrolador. Los pines IN1–IN4 determinan la dirección del giro de los motores, mientras que ENA y ENB (enable) controlan si cada puente está activo. En muchos módulos, ENA y ENB están conectados a jumpers que pueden retirarse si se desea controlar la velocidad mediante una señal PWM.
  • Salidas OUT1–OUT4: Se conectan directamente a los terminales de los motores. Cada par de salidas (OUT1/OUT2 y OUT3/OUT4) corresponde a uno de los dos puentes H del chip.
  • Diodos D1–D8: Actúan como diodos de protección o “flyback”, un elemento esencial cuando se trabaja con cargas inductivas como motores. Su función es disipar los picos de tensión inversa generados al interrumpir la corriente del motor, protegiendo los transistores internos del L298N.
  • U2 (78M05): Es un regulador lineal de voltaje que convierte la tensión de entrada del módulo (por ejemplo, 12 V) en 5 V, alimentando la lógica del L298N y, opcionalmente, otros componentes del sistema. Algunos módulos permiten activar o desactivar esta función mediante un jumper para usar fuentes externas de 5 V.
  • Condensadores y resistencias: Estos componentes auxiliares estabilizan la alimentación y amortiguan los transitorios eléctricos, garantizando un funcionamiento estable y reduciendo interferencias.

Este módulo tiene una fuente de alimentación de 5v incorporada, cuando el voltaje de conducción es de 7v-35v,esta fuente es adecuada para la fuente de alimentación. NO ingrese voltaje a la interfaz de suministro de + 5v, sin embargo, el ledding de 5v para uso externo está disponible. Cuando ENA habilita IN1 IN2 control OUT1 OUT2. Cuando ENB habilita IN3 IN4 control OUT3 OUT4

En conjunto, el esquema y el módulo del L298N ilustran cómo un diseño relativamente sencillo es capaz de combinar potencia y control lógico, ofreciendo una solución robusta y económica para el manejo de motores. Esto lo convierte en un componente indispensable para quienes desarrollan vehículos robóticos, cintas transportadoras, sistemas de control de movimiento o proyectos de automatización casera.

Ejemplo esquema de cableado básico

A continuación vamos a ver a un ejemplo práctico de código Arduino, junto con un pequeño esquema de cableado, para controlar un motor DC usando el módulo controlador L298N. Esta combinación es ideal para proyectos de robótica y automatización que requieren mover motores de forma simple y eficiente.

Conexiones recomendadas entre Arduino, módulo L298N y motor DC:

  • IN1 (L298N) → Pin 7 de Arduino.
  • IN2 (L298N) → Pin 6 de Arduino.
  • ENA (L298N) → Jumper (por defecto, motor a máxima velocidad) o Pin 9 de Arduino si quieres usar PWM para velocidad.
  • OUT1/OUT2 (L298N) → Motor DC.
  • +12V (L298N) → Fuente de 12V para el motor.
  • GND (L298N) → GND de Arduino y de la fuente.
  • 5V (L298N) → No se conecta si tu Arduino ya está alimentado por USB o fuente externa.
   Arduino        L298N           Motor
------- ----- -----
GND ----+--- GND
| +----+
+---(-) Fuente | |
(+) Fuente-----+ M +---- OUT1
M |
OUT2------+
D7 ---------- IN1
D6 ---------- IN2
ENA ---------- Jumper (o D9 si usa PWM)

Ejemplo de código Arduino

El siguiente sketch enciende el motor en un sentido, espera 2 segundos, invierte el sentido durante otros 2 segundos, y repite el ciclo:

cpp// Pines de control
const int IN1 = 7;
const int IN2 = 6;
const int ENA = 9; // Opcional, para velocidad con PWM

void setup() {
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(ENA, OUTPUT);
}

void loop() {
  // Girar motor en un sentido (adelante)
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  analogWrite(ENA, 255); // Velocidad máxima
  delay(2000);

  // Detener motor
  analogWrite(ENA, 0);
  delay(500);

  // Girar motor en sentido contrario (atrás)
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  analogWrite(ENA, 255); // Velocidad máxima
  delay(2000);

  // Detener motor
  analogWrite(ENA, 0);
  delay(500);
}

Este código básico permite experimentar fácilmente con el control de dirección y velocidad de motores DC usando el L298N y una placa Arduino. Puedes ajustar el pin ENA y el valor de analogWrite para regular la velocidad mediante modulación por ancho de pulso (PWM), retirando el jumper si lo tuviese puesto.​