<< Organo de luces (I) - IntroducciónOrgano de luces (III) - Etapa de potencia >>Obtención de la señal
Como comentaba en el
post anterior, en mi caso voy a a utilizar un micrófono como fuente de señal, esto evita tener que conectar la fuente directamente y proporciona más libertad de movimiento.
Yo he utilizado un módulo de Dealextreme (como no), el
SKU: 135533, que es un módulo detector de sonido con una salida digital y una analógica. Aquí utilizaremos esta última.
Como el nivel de la señal que entrega es muy pequeño, he utilizado un vetusto
u741 (pero funcional) que había por un cajón, para adecuar la señal a la entrada del
Arduino.
También es posible utilizar directamente un micrófono
electret , pero habrá que amplificarlo adecuadamente (podéis encontrar infinidad de esquemas en internet), o bien utilizar un módulo ya amplificado como
este El esquema de montaje es este (clik en las imágenes para ampliar):
Y el montaje este:
Algunos detalles:
- La resistencia de 1k para alimentar el módulo reduce en gran manera el ruido en la alimentación producido por el Arduino, que se traslada luego a la salida.
- El u714 está en una configuración básica de amplificador, a su salida se obtiene una señal de aproximadamente 1Vpp con un offset de 5V/2 ~ 2.5V.
- Como la entradas analógicas de Arduino necesitan señales de 0 a 5V, el divisor de tensión formado por R1/R2 crean un offset de aprox 0.5V para que la señal tenga un rango del 0 a 1 V aprox.
- Para poder medir con mayor precisión, programaremos la referencia del conversor analógico del Arduino como INTERNAL, que tiene un valor de 1,1V en vez de los 5V de defecto.
El código, puedes descargarlo aquí:
Código /*
* Organo de luces psicodelicas
* Organo de luces de tres canales , utilizando la FFT
* Autor: Jose Daniel Herrera
* Fecha: 05/09/2012
* http://arduino-guay.blogspot.com.es
*/
#include "fix_fft.h"
#define MUESTRAS 128 // Numero de muestras para el cálculo de la FFT
#define LOGM 7 // Logaritmo en base 2 del número de muestras
#define BAJOS_MEDIOS 7 // Nº de banda para el corte entre Bajos y Medios
#define MEDIOS_AGUDOS 35 // Nº de banda para el corte entre Medios y Agudos
#define BPIN 9 // Pin de salida Bajos
#define MPIN 10 // Pin de salida Medios
#define APIN 11 // Pin de salida Agudos
#define MAX_PASADAS 10 // Nº de pasadas para el cálculo de los límites
char data[MUESTRAS]; // Array con los valores muestreados (parte real)
char im[MUESTRAS]; // Array con los valores muestreados (parte imaginaria)
unsigned char salida[MUESTRAS/2]; // Valores obtenidos de la FFT (espectro de 64 bandas)
unsigned char bajos,medios,agudos; // Valores calculados para cada canal
byte pasada, // nº de pasada para el cáculo de los límites
acumBajos,acumMedios,acumAgudos, // acumuladores de veces que se supera el límite
limBajos,limMedios,limAgudos; // límites calculados para cada canal
/*
* Funcion que aplica una ventana de Hann a los datos muestreados para reducir el
* efecto de las discontinuidades en los extremos
*/
void aplicaVentana (char *vData) {
double muestrasMenosUno = (double(MUESTRAS) - 1.0);
// Como la ventana es simétrica , se calcula para la mitad y se aplica el factor por los dos extremos
for (uint8_t i = 0; i < MUESTRAS/2 ; i++) {
double indiceMenosUno = double(i);
double ratio = (indiceMenosUno / muestrasMenosUno);
double factorPeso = 0.5 * (1.0 - cos(6.28 * ratio));
vData[i] *= factorPeso;
vData[MUESTRAS - (i + 1)] *= factorPeso;
}
}
void setup() {
// Configuramos el prescaler a 32 -> 16Mhz/32 = 500 KHz
// como cada conversion son 13 ciclos 500/13 ~ 38.4KHz
// Es decir podemos medir en teoria hasta unos 19KHz,
// que para este proyecto sobra.
bitWrite(ADCSRA,ADPS2,1);
bitWrite(ADCSRA,ADPS1,0);
bitWrite(ADCSRA,ADPS0,1);
// Como la señal es muy baja,utilizamos la referencia interna
// de 1.1 V en vez de la de defecto de 5 V.
analogReference(INTERNAL);
// Salidas para los canales de Bajos,Medios y Agudos
pinMode(BPIN,OUTPUT);
pinMode(MPIN,OUTPUT);
pinMode(APIN,OUTPUT);
// Variables para el cálculo de los límites
pasada = 0;
acumBajos = acumMedios = acumAgudos = 0;
limBajos = limMedios = limAgudos = 50;
}
void loop() {
// Realizamos el muestreo
for( int i=0; i < MUESTRAS; i++) {
data[i] = analogRead(0)/4 -128; //Convertimos de 0..1024 a -128..127
im[i] = 0; // parte imaginaria = 0
}
// Aplicamos la ventana de Hann
aplicaVentana (data);
// Calculamos la FFT
fix_fft(data,im,LOGM,0);
// Sólo nos interesan los valores absolutos, no las fases, asi que
// calculamos el módulos de los vectores re*re + im*im.
// Dado que los valores son pequeños utilizamos el cuadrado
for (int i=0; i < MUESTRAS/2; i++){
salida[i] = data[i] * data[i] + im[i] * im[i];
}
// Ahora repartimos las bandas entre las 3 salidas
// En vez de sacar la media, utilizo sólo el valor máximo de
// una banda
bajos = 0;
for (int i=2; i < BAJOS_MEDIOS; i++){
bajos += salida[i];
}
bajos = bajos/2;
medios = 0;
for (int i=BAJOS_MEDIOS ; i < MEDIOS_AGUDOS; i++){
medios += salida[i];
}
medios = medios/2;
agudos = 0;
for (int i=MEDIOS_AGUDOS; i < MUESTRAS/2; i++){
agudos += salida[i];
}
agudos = agudos/2;
// Calculamos si el canal correspondiente
// supera le límite para encenderlo
int siBajos = bajos > limBajos;
int siMedios = medios > limMedios;
int siAgudos = agudos > limAgudos;
digitalWrite(BPIN,siBajos ? HIGH : LOW);
digitalWrite(MPIN,siMedios? HIGH : LOW);
digitalWrite(APIN,siAgudos? HIGH : LOW);
// Utilizamos las veces que se supera para
// recalcular el límite y evitar que con los
// cambios de volumen los canales se saturen
// o no funcionen.
acumBajos += siBajos;
acumMedios += siMedios;
acumAgudos += siAgudos;
if ( ++pasada > MAX_PASADAS ) {
pasada = 0;
limBajos = 20 + acumBajos*5;
limMedios = 20 + acumMedios*5;
limAgudos = 20 + acumAgudos*5;
acumBajos = 0;
acumMedios = 0;
acumAgudos = 0;
}
}
Necesitaréis la librería fix_fft que podéis descargar de aquí :
fix_fft El código
El código está comentado para poderlo seguir fácilmente, pero veamos básicamente su funcionamiento.
Esta parte contiene una dosis de matemática que puede ser algo aburrida para algunos, si no te interesa simplemente pasa de ella :-).
Inicialización
Se configura como antes hemos dicho la referencia analógica como
INTERNAL, para medir tensiones del orden de 1V. También se configura el prescaler a 32 (el prescaler divide la frecuencia de reloj del micro para realizar las conversiones A/D entre otras cosas), para conseguir un a frecuencia de muestreo de unos 38 KHz, lo cual es más que suficiente para este proyecto. Luego se configuran los pines de salida.
Bucle
Se realiza un muestreo de 128 muestras, se pasa pon una función de ventana (
Hann), para minimizar los efectos de las discontinuidades al principio y al final del muestreo y luego se aplica la transformada rápida de Fourier con la llamada a fix_fft.
Después de esto en data tenemos los valores de las 64 bandas (Nmuestras/2) .
Como nos interesan en valor absoluto y no las partes reales e imaginarias por separado, calculamos el módulo, y puesto que el valor es algo pequeño lo dejamos al cuadrado.
A continuación sólo queda agrupar los valores en tres bandas (graves, medios y agudos) y en función de si superan un límite activar las salidas.
El límite varía en función de la cantidad de veces que cada canal se enciende, lo que proporciona una cierta realimentación y evita que con los cambios de volumen un canal se sature o no se encienda.
La agrupación de las bandas y los límites los he obtenido probando, podéis realizar vuestras pruebas para obtener otros resultados.
Si te ha gustado no te pierdas el próximo post para ver como conectarlo a bombillas de verdad :