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​.

Ambilight para nuestro PC


Ambilight es una tecnología diseñada para mejorar la experiencia visual  analizando las señales entrantes y produciendo una  luz lateral ambiental adecuada al contenido que se está visualizando en la pantalla un resultado bastante atractivo , el cual  además de la sensación de estar viendo una pantalla aun mayor.

Hasta hace muy poco este efecto solo se podía conseguir si comprábamos un TV que contara con ese sistema y no había otra opción, pero recientemente  con la aparición de placas con suficiente capacidad computacional, se puede emular gracias al uso por ejemplo de una Raspberry Pi .  Aun mas sencillo  y facil es hacerlo a través una placa Arduino UNO (o incluso Arduino nano), un ordenador,y una tira de 50 leds para iluminar una televisión de 47 pulgadas..!Y todo sin tener que soldar nada!.

 

 

Antes de empezar  con el montaje ,  la tira de  leds   RGB   direccionable es muy importante que este basada en el chip  ws2801 (LEDs WS2801) pues el menos no nos  dará  ningún tipo de problemas usando una Placa Arduino,  siendo además la mas utilizada para este tipo de montajes.

Existen tiras basadas en el chips WS2801   en formato «luces de navidad», pero lo mas habitual es adquirirla en forma de cinta autoadhesiva.

Un ejemplo de tira es  esta  que puede comprarse en Amazon por menos de 27€

tira de leds.png

Una peculiaridad  de esta tiras ,es que se pueden cortar según la longitud que se requieran, así como además que también es posible ampliarlas gracias a  los conectores que llevan en cada extremo, pudiendo  unirse  entre ellas hasta donde se necesite.

conector.png

Asimismo, para alimentar dicha tira  también  necesitaremos aparte  una fuente de alimentación  dimensionada para el números de leds que vayamos a adquirir , como puede ser una fuente de  5v y 2A  (para 50 leds)

La tira de leds por simplicidad la conectaremos a una placa  Arduino UNO , el cual puede adquirirse en Amazon por menos de 10€

Arduino UNO comparado a la versión anterior, usa el  Chip alternativo Atmega 16U2 8U2, lo que quiere decir una tasa más alta de transferencia y memoria.Ademas esta versión cuenta con la interfaz SDA y SCL .

Los datos de LED y las líneas de reloj los conectaremos  a la salida SPI de Arduino,es decir  los datos SPI salen del pin digital 11 y  el reloj es el pin digital 13.

Los LED deben ser alimentados externamente fuera de la linea de +5V de  Arduino 5V, pues podrían estropear el regulador de este . La masa o  tierra, por el contrario, si debe ser conectada a  la masa de Arduino.

Normalmente las tiras de leds WS01  ,suelen tiene 6 cables : tres de ellos lo  conectaremos los pines (11,13 Y GND) del Arduino, y los otros dos  conectaremos  a la fuente de 5V.

La forma de conectarlos todo esto es según el siguiente esquema :

  • El cable VERDE proveniente del pin SD de la tira de leds al pin 11 del Arduino Uno.
  • El cable ROJO proveniente del pin CK  de al tira de leds al  pin 13 del Arduino Uno.
  • El cable NEGRO proveniente del pin  GND de la tira de leds al pin GND del Arduino Uno.
  • El cable AZUL proveniente del pin +5V de al tira de leds lo dejaremos sin conectar
  • El cable Rojo grueso en paralelo con el azul  proveniente de la tira de leds a la conexión +5v de la fuente auxiliar
  • El cable NEGRO en paralelo con el  negro  proveniente del pin  GND de la tira de leds al GND de la fuente auxiliar
arduino.png

 

Conectamos pues  la tira de leds  por un lado a una fuente de 5V /2amp .  y por el otro a Arduino , por uno de los extremos y las otras 2 o 3 tiras con los adaptadores macho hembra adecuados   a continuación siguiendo la flecha  de las tiras  haciendo un rectángulo que rodeara nuestro monitor o TV .  Evidentemente en uno de los extremos de inicio es donde haremos las conexiones  y todas la demás se harán por medio de los  conectares .

Hemos de tener cuidado ya que uno de los extremos de la tira de luces es pues para conectar la primea tira al arduino y a la fuente :de esta forma, en cada extremo quedan sueltos los cables opuestos (normalmente el cable rojo es el positivo y el azul el negativo.) que conectaremos también entre si para dar alimentación a  los leds ( aunque los conectores también den energía  ya que llevan las 4 conexiones incluida los 5v y GND). 

 

 SOFTWARE EN EL ARDUINO

Para gobernar , la tira de leds la conectaremos a  un   Arduino   que  ademas  hará de «puente» entre el ordenador host y la tira basado en WS2801 . Los datos de LED se transmiten, y  no se almacenan en búfer, lo que significa que si  hay mas código en Arduino  podrían generar demoras debido a la RAM limitada del Arduino,pero no obstante el algoritmo ejerce cierto esfuerzo para evitar las pérdidas de buffer

 El protocolo de cierre WS2801, basado en retardo, podría desencadenarse inadvertidamente si el bus USB o la CPU  está desbordada con otras tareas. Este código almacena datos entrantes en serie e introduce pausas intencionadas si hay una amenaza del buffer  lleno prematuro.

El costo de esta complejidad es algo que  reduce el rendimiento, pero la ganancia es muy buena  evitando  la mayoría de los fallos visuales  incluso aunque finalmente una función de carga en el bus USB y  host CPU, quede  fuera de  control.

 

Si no lo tenemos, descargaremos el software de arduino (Página oficial de arduino) y lo instalamos.

Conectamos el arduino uno a nuestro pc con el cable usb. Si pide los drivers, se pueden encontrarlo en la carpeta arduino-1.0.4\drivers.

Descargaremos  esta biblioteca: fastled biblioteca descarga, la cual  importaremos  al Arduino IDE.

Ahora toca cargar el sketch para lo cual  descaremos el código Adalight para las luces  aqui 

Descomprimireos el archivo y  añadimos los archivos que acabamos de descargar en la carptea Mis documentos/ Arduino  y ng

Arrancaremos el software de arduino y  configuramos en el ide la placa Arduino en Herramientas –>Placa Arduino Uno ( o la placa que tengamos)   sin  olvidar el puerto de comunicaciones

Iremos a  File> Sketchbook> Arduino> Adalight  y uan vez cargado el sketch debemos ajustar el numero de leds  (88 en nuestro casoo) que  tengamos en la instalación  así como la velocidad máxima (500000 )

#define NUM_LEDS 88 // Max LED count
#define LED_PIN 11 // arduino output pin - probably not required for WS2801
#define GROUND_PIN 10 // probably not required for WS2801
#define BRIGHTNESS 255 // maximum brightness
#define SPEED 500000 // virtual serial port speed, must be the same in boblight_config

Ahora ya podemos   compilar el software( botón primero que  pone un v de verificar). 

adalight.PNG

 Si no ha habido errores ahora podemos subir  el sw pulsando el botón de Upload ( flechita a la derecha  en el software de Arduino.

Al contrario de lo que sucede  con el sketch LedlIght donde se iluminan las luces  de 3 colores rojo, verde y azul si todo ha ido bien, si tenemos conectadas los leds al arduino y a la fuente externa, cuando carguemos este  código dentro del Arduino solo lucirá el primer led de la cadena lo cual significará que estamos en buen camino.

IMG_20170221_170329.jpg

 El código dentro de Arduino es no volátil, así que no se borrará aunque desconecte la tarjeta.

 Software en el PC

Una vez tenemos el sw de Adalight en un Arduino, toca instalar el programa de captura que envíe las señales correspondiente a nuestro Arduino.

Entre los programas de captura ambibox es el mejor especialmente con windows 10, ya que no solo tiene la capacidad para capturar su escritorio sino de poner un fondo personalizable, convertir la tira en luces psicodelicas en función del audio,fondo variable automático ,plugins, etc.

Se puede encontrar aqui, tanto el software como el add-on para XBMC.

 Una vez   descargado , durante la instalación se puede seleccionar  la opción de instalación completa , marcando además la opción de descarga e instalación de playclaw.

Empezamos la configuración, pulsamos sobre el botón de mas ajustes:

more

En la parte inferior, como vemos seleccionaremos como Device  Adalight , elegiremos  el puerto de comunicaciones ( el mismo al que este conectado el Arduino) y en el numero de zonas, coloremos  el numero de leds total que tengamos instalados ( en el ejemplo 88).

Asimismo no olvidar orden de colores, lo cual podemos obtener   fijando un color mediante el selector de Mode:Static Background, pinchando en el color ( aparecerá la paleta), pinchando en el check de Use baclight   y seleccionando en el combo order of colors la opción adecuada   hasta que el color de los leds sea similar al de paleta ( en mi caso es BGR). 

fondo.PNG

En la siguiente imagen podemos ver el aspecto de las primeras pruebas.

IMG_20170221_204134.jpg

En este programa no olvidar  en salvar cada cambio en «Save Setting»  pues si no lo hacemos perderemos cualquier cambio que hagamos.

Con las nuevas opciones ya podemos avanzar en la  configuración de nuestra instalación para lo cual seleccionaremos en Mode: Screen capture.

 capturawindiow.PNG
Acto seguido configuramos la ubicación de los leds, pulsando  sobre SHOW AREAS OF CAPTURE y sobre el asistente de configuración, elegimos si queremos una instalación de 3 lados o 4. También  es importante la cantidad de leds que tenemos en cada lado de la TV especialmente horizontal o verticalmente.
Marcamos asimismo el orden de los leds, de izq->der o de der->izq.
Con esto ultimo ya tenemos nuestro software listo para funcionar.

2017-02-21_20h59_23.png.
Este programa además tiene unas opciones muy interesantes, en esta pantalla:

adicional.png

Podemos configurar muchos parámetros de cada led, aplicar correcciones de color y gamma ,brillo ,etc

También podemos activar un servidor web para controlar el software desde el teléfono.

servidor

 El siguiente paso es instalar el add-on para el XBMC.Para ello lo descomprimimos y lo ponemos  en la ruta:»Users/Username/AppData/Roaming/XBMC/addons«.

Ahora en el  apartado de add-on (en el  menú de la izquierda) se puede configurar un poco el comportamiento, aquí cada cual que lo puede personalizar a su gusto.

Una solución para que funcione a pantalla completa es usando el software playclaw.
Para ello, se pueden  es crear 2 perfiles dentro de ambibox, uno para el escritorio y otro para XBMC.
En este ultimo el sistema de captura que elijo es playclaw de modo que cuando se inicie un video en XBMC  dará la opción de elegir que perfil cargar, de modo que se  puede  elegir el perfil XBMC y asi  cuando se  salga de XBMC se  puede vplber   al perfil de escritorio.
Por supuesto se debe tener corriendo el software playclaw para que esto funcione.

 Por ultimo  hay  un  modo  que haya las delicias de los que les guste la música : el modo Color music , el cual permite modular las luces en función  de lo que se este escuchando por el canal de sonido principal. 

musica.PNG

 Obviamente si queremos que las luces acompañen a la imagen de video de la pantalla principal el modo de captura de pantalla elegido será  [Software] Screen capture  y el Método  Windows 8  (aunque tengamos Windows 10 instalado en nuestro equipo).

windows8.png