Analizador del espectro sonoro de 32 bandas
Este proyecto ha sido desarrollado por el grupo 7 de la asignatura Diseño de Sistemas Empotrados, conformado por Eric Martínez Gamero, durante el primer cuatrimestre del curso 2021-2022.
INTRODUCCIÓN
El analizador LED del espectro de audio de 32 bandas permite visualizar las fluctuaciones de frecuencia de una señal analógica de, por ejemplo, una canción.
El display está compuesto por cuatro módulos de 8×8 LEDs rojos, que se iluminan en función de dichas fluctuaciones y siguen un patrón determinado por el modo de visualización seleccionado.
La idea original proviene de la entrada del blog oficial de Arduino publicada por el usuario shajeeb en 2019: https://create.arduino.cc/projecthub/shajeeb/32-band-audio-spectrum-visualizer-analyzer-902f51?ref=platform&ref_id=424_trending___&offset=22
COMPONENTES
Componente | Número | Precio |
Placa Arduino Uno | 1 | Provista por la URJC |
Placa de inserción | 1 | Provista por la URJC |
Cable de audio estéreo | 2 | En propiedad |
Cables DuPont | 17 | 8 € |
Matriz LED 8×32 MAX7219 | 1 | 10 € |
Socket hembra para jack de 3.5» | 1 | 1.37 € |
Splitter de audio 1 a 2 para jack de 3.5» | 1 | 0.50 € |
Resistencias de 4.7k ohm | 3 | 4.33 € |
Resistencias de 100k ohm | 2 | 4.33 € |
Resistencia de 10k ohm | 1 | 4.33 € |
Condensador cerámico 100nF | 2 | 0.61 € |
Botón | 1 | Provisto por la URJC |
Conector pila 9V | 1 | 5 € |
Pila alcalina 9V | 1 | 3 € |
HERRAMIENTAS
Herramienta | Número | Precio |
Soldador de estaño | 1 | 24 € |
Polímetro | 1 | En propiedad |
HARDWARE
El sistema recibe la señal analógica de un dispositivo móvil gracias a un cable de audio estéreo. Este cable está conectado a un socket hembra que separa los canales izquierdo, derecho y tierra. Con el soldador de estaño se fijaron los pines del socket a tres cables que van a la placa de inserción.
Por otro lado, se instaló en la placa un botón para posibilitar el cambio entre los tres modos de visualización del display.
Respecto a la salida del sistema, esta se visualiza a través de una matriz LED 8×32 conectada mediante cables jumper a la placa Arduino Uno y constituida por cuatro módulos 8×8.
En cuanto a la fuente de alimentación, se optó por utilizar el jack de alimentación con adaptador DCAC para conectar una batería de 9V.
ESQUEMA DE CONEXIONES
SOFTWARE
Para la implementación del proyecto a nivel software se han utilizado las librerías de Arduino MD_MAX72xx (https://www.arduino.cc/reference/en/libraries/md_max72xx/) y arduinoFFT (https://www.arduino.cc/reference/en/libraries/arduinofft/).
La librería arduinoFFT permite calcular la frecuencia de una señal muestrea implementando el algoritmo de la transformada rápida de Fourier en coma flotante. Esto permite el tratamiento digital de señales de audio.
Por otro lado, la librería MD_MAX72XX implementa funciones para trabajar con matrices LED 8×8. Es necesario realizar un test inicial sobre la matriz comprada para conocer el tipo de hardware y poder especificarlo para la librería.
En cuanto a los modos de visualización del display, estos se encuentran codificados en binario en arrays de 9 elementos. El primer modo de visualización, por ejemplo, se podría ilustrar como:
La primera línea de setup() es importante, ya que pone el conversor analógico digital de la Arduino Uno en free running mode. Así se permite que el ADC se mantenga realizando conversiones consecutivas, reduciendo latencias.
Sobre loop(), el siguiente esquema representa el proceso que se lleva a cabo cada iteración:
Como se puede ver, antes de volver al inicio hay que comprobar si se ha pulsado el botón para cambiar de modo. La función cambioModo() implementa esta funcionalidad.
CÓDIGO
#include <arduinoFFT.h> // librería que permite calcular la frecuencia de una señal muestreada #include <MD_MAX72xx.h> // librería que permite utilizar una matriz LED como pantalla direccionable por píxeles #define MUESTRAS 64 // debe ser potencia de dos para el algoritmo de muestreo #define TIPO_HARDWARE MD_MAX72XX::ICSTATION_HW // tipo de display para que la librería MD_MAX72XX lo reconozca #define MODULOS 4 // número de módulos LED independientes #define CLK_PIN 13 // pin CLOCK del display #define DATA_PIN 11 // pin DATA del display #define CS_PIN 10 // pin CONTROL del display #define COLUMNAS 32 // número de columnas en el display (muestras / 2) #define FILAS 8 // número de filas en el display // modos de visualización - cada LED puesto a 1 en binario se iluminará int MODO[]= {0, 128, 192, 224, 240, 248, 252, 254, 255}; // por defecto será el patrón estándar int MODO_1[]= {0, 128, 192, 224, 240, 248, 252, 254, 255}; // patrón estándar int MODO_2[]= {0, 128, 64, 32, 16, 8, 4, 2, 1}; // solo parte superior int MODO_3[]= {0, 1, 3, 7, 15, 31, 63, 127, 255}; // patrón estándar del revés int MODO_4[]= {0, 1, 2, 4, 8, 16, 32, 64, 128}; // solo parte superior del revés double vReal[MUESTRAS]; double vImag[MUESTRAS]; char val[COLUMNAS]; int columnaDisplay, valorDisplay; const int BOTON_PIN = 5; // pin del botón int estado = HIGH; // el valor actual leído del pin int estadoPrevio = LOW; // el valor previo leído del pin int modo = 1; MD_MAX72XX mx = MD_MAX72XX(TIPO_HARDWARE, CS_PIN, MODULOS); // objeto display arduinoFFT FFT = arduinoFFT(); // objeto fft void setup() { ADCSRA = 0b11100101; // poner conversor analógico digital en modo de funcionamiento libre y poner prescaler a 32 ADMUX = 0b00000000; // usar el pin A0 y referencia de voltaje externa pinMode(BOTON_PIN, INPUT); mx.begin(); // inicialización del display delay(50); // espera para obtener voltaje de referencia estable } void loop() { // muestreo for(int i=0; i<MUESTRAS; i++) { while(!(ADCSRA & 0x10)); // esperar que ADC termine la conversión actual ADCSRA = 0b11110101 ; // limpiar registro ADCSRA para que ADC pueda realizar la siguiente conversión (0xf5) int valor = ADC - 512 ; // leer del ADC vReal[i] = valor/8; vImag[i] = 0; } // configuración de FFT FFT.Windowing(vReal, MUESTRAS, FFT_WIN_TYP_HAMMING, FFT_FORWARD); FFT.Compute(vReal, vImag, MUESTRAS, FFT_FORWARD); FFT.ComplexToMagnitude(vReal, vImag, MUESTRAS); // reorganizar resultado FFT para adaptarse al número de columnas int step = (MUESTRAS/2) / COLUMNAS; int c = 0; for(int i=0; i < (MUESTRAS/2); i++) { val[c] = 0; for (int k = 0 ; k < step ; k++) val[c] = val[c] + vReal[i+k]; val[c] = val[c]/step; c++; } // enviar el valor medido a cada columna del display for(int i=0; i < COLUMNAS; i++) { val[i] = constrain(val[i],0,80); // restringir valor entre 0 y 80 val[i] = map(val[i], 0, 80, 0, FILAS); // mapear el valor entre 0 y número de filas valorDisplay = MODO[val[i]]; columnaDisplay = 31-i; mx.setColumn(columnaDisplay, valorDisplay); // poner todos los LEDs de la columna en el nuevo estado } cambioModo (); // comprueba si el botón se ha presionado para cambiar de modo } void cambioModo() { int leido = digitalRead(BOTON_PIN); if (leido == HIGH && estadoPrevio == LOW) // al presionar el botón cambiar de modo { switch (modo) { case 1: // del modo 1 al 2 modo = 2; for (int i=0 ; i<=8 ; i++ ) { MODO[i] = MODO_2[i]; } break; case 2: // del modo 2 al 3 modo = 3; for (int i=0 ; i<=8 ; i++ ) { MODO[i] = MODO_3[i]; } break; case 3: // del modo 3 al 4 modo = 4; for (int i=0 ; i<=8 ; i++ ) { MODO[i] = MODO_4[i]; } break; case 4: // del modo 4 al 1 modo = 1; for (int i=0 ; i<=8 ; i++ ) { MODO[i] = MODO_1[i]; } break; } } estadoPrevio = leido; }