<< Arduino light organ (I) - Introduction             Arduino light organ (III) - Power stage >>

Obtaining the signal

As I mentioned in the previous post, in my case I am going to use a microphone as a signal source, this eliminates the need to connect directly and provides more freedom of movement.

I've used a Dealextreme module, the SKU: 135533, a sound detector module with digital output and analog. Here we use the latter.
As the level of the signal delivery is very small, I used an ancient u741 (but functional) that had a drawer, to adjust the input signal to the Arduino.
It is also possible to directly use an electret microphone, but we have to amplify it properly (you can find plenty of schemes on the internet), or use a module, and amplified as this


The scheme: (click images to enlarge):

Arduino light organ scheme

And the assembly:

Arduino light organ assembly



Some details:

  • The resistor of 1k to power the module greatly reduces the noise produced by feeding Arduino, which then moves to the output.. 
  • The u714 is a basic configuration of the amplifier, the output signal is obtained approximately 1 Vpp with an offset of 5V / 2 ~ 2.5V.
  • As the Arduino analog input signals need 0 to 5V, the voltage divider formed by R1/R2 creates an offset of approximately 0.5V so that the signal has a range of 0-1 V approx.
  • To measure more accurately, we program the Arduino analog reference as "INTERNAL", which has a value of 1.1 V instead of the default 5V.
The code (you can download it here):

/*
*  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;
   }
 }

You need the fix_fft library that you can download from here:   fix_fft

The code

The code is commented so that it can easily follow, but basically see its operation.
This part contains a dose of mathematics that can be a little boring for some, if not interested simply passes it :-).

Setup

The analog reference is set to 'INTERNAL' to measure voltages in the range of 1V. Also the prescaler is set to 32 (the prescaler divides the clock frequency of the micro to perform 'A/D' conversions among other things), to achieve a sampling frequency of about 38 KHz, which is more than sufficient for this project. Then configure the output pins.

Loop

Is sampled 128 samples, then it passed to a window function (Hann) to minimize the effects of discontinuities at the beginning and end of the sample and then applies the fast Fourier transform with fix_fft call.

After this in 'data' have the values ​​of the 64 bands (Nmuestras / 2). As we are interested in absolute value and not the real and imaginary parts separately, we calculate the module, and since the value is something small we leave the square

Below is just group the values ​​in three bands (bass, mid and treble) and depending on if they exceed a limit enable outputs.

The threshold varies depending on the number of times that each light channel is activated, which provides some feedback and prevents volume changes with a channel is saturated or not light.
The grouping of bands and the limits I obtained by testing, you can perform other tests to get your results.

If you liked not miss the next post to see how to connect it to true bulbs:
Arduino light organ (III) - Power stage



If you liked it, remember to share it on your favorite social network. Thanks

20 comments

March 10, 2013 at 10:00 PM

There is a mistake in the
OrganoLuces.ino code.
REef #include "fix_fft.h" in the actual ino file has brackets, won't compile.
removing them and adding the "" will fix the problem.

January 27, 2014 at 2:57 PM

you must change in: #include ...
I made this montage with small changes and I'm very satisfied. I changed the sketch and put another channel (first time as negative channel, then I modified in subwoofer channel, see http://nicuflorica.blogspot.ro/2014/01/miniorga-de-lumini-cu-arduino.html

January 27, 2014 at 3:43 PM

@Nicu FLORICA
A pleasure that you have served as inspiration!

February 19, 2015 at 7:13 PM

i keep getting this error message and I don't know what to do!
Arduino: 1.5.8 (Mac OS X), Board: "Arduino Uno"

/Users/Kerem/Documents/Arduino/libraries/Fix_fft/fix_fft.cpp:50:7: error: 'prog_int8_t' does not name a type
const prog_int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = {
^
In file included from /Users/Kerem/Documents/Arduino/libraries/Fix_fft/fix_fft.cpp:1:0:
/Users/Kerem/Documents/Arduino/libraries/Fix_fft/fix_fft.cpp: In function 'int fix_fft(char*, char*, int, int)':
/Users/Kerem/Documents/Arduino/libraries/Fix_fft/fix_fft.cpp:199:28: error: 'Sinewave' was not declared in this scope
wr = pgm_read_word_near(Sinewave + j+N_WAVE/4);
^
/Users/Kerem/Documents/Arduino/libraries/Fix_fft/fix_fft.cpp:209:28: error: 'Sinewave' was not declared in this scope
wi = -pgm_read_word_near(Sinewave + j);
^
Error compiling.

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.

February 19, 2015 at 8:31 PM

@Kerem
It is possible that this forum can help:
http://www.devopsview.com/blog/arduino/arduino-yun-fix_fft/

February 26, 2015 at 1:13 AM

i've gotten my code work, i've run a "blink" to see if the output LEDs are working and they are, but for some reason i can not get an input signal in. i'm using this sound sensor: http://www.seeedstudio.com/wiki/Grove_-_Sound_Sensor
should this pose a problem? if so, what modifications would you advise?

March 31, 2015 at 6:47 PM

hola, es posible cambiar la sensibilidad de las luces? no se como puedo cambiarlas usando el "prescalar"...o es posible que hay otro método para hacer esto? gracias!

James
April 17, 2015 at 10:32 PM

@Kerem
How did you fix your code? I keep getting the same error message as you did.

April 20, 2015 at 9:03 AM

@Kerem
The 330 Ohm resistor is T1, and the 360 Ohm is T2.

April 27, 2015 at 11:41 AM

Hello and thanks for the code

but i get an error:

undefined reference to `fix_fft(char*, char*, int, int)'

what can i do to solve the problem. I know its maybe something with pointers.

April 28, 2015 at 1:11 PM

@Simei Steiner
Have you added the 'fix_fft' library?

April 30, 2015 at 11:51 AM

@Arduino GuayYes I did it.

July 28, 2015 at 4:05 PM

Thanks for sharing this! Finally it compiles without errors, I will try connecting my arduino to a microphone tomorrow..

Anonymous
August 10, 2015 at 11:07 PM

I've got error message:
"
C:\Program Files (x86)\Arduino\libraries\Fix_fft\fix_fft.cpp:50:7: error: 'prog_int8_t' does not name a type
const prog_int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = {
^
In file included from C:\Program Files (x86)\Arduino\libraries\Fix_fft\fix_fft.cpp:1:0:
C:\Program Files (x86)\Arduino\libraries\Fix_fft\fix_fft.cpp: In function 'int fix_fft(char*, char*, int, int)':
C:\Program Files (x86)\Arduino\libraries\Fix_fft\fix_fft.cpp:199:28: error: 'Sinewave' was not declared in this scope
wr = pgm_read_word_near(Sinewave + j+N_WAVE/4);
^
C:\Program Files (x86)\Arduino\libraries\Fix_fft\fix_fft.cpp:209:28: error: 'Sinewave' was not declared in this scope
wi = -pgm_read_word_near(Sinewave + j);"

What a Sinewave? How can I fix this?

Anonymous
October 18, 2015 at 12:41 AM

I also have this error. Did someone fix it? Thanks

Arduino: 1.6.4 (Windows 7), Board: "Arduino Nano, ATmega328"

C:\Users\Joao\Documents\Arduino\libraries\Fix_fft\fix_fft.cpp:50:7: error: 'prog_int8_t' does not name a type
const prog_int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = {
^
In file included from C:\Users\Joao\Documents\Arduino\libraries\Fix_fft\fix_fft.cpp:1:0:
C:\Users\Joao\Documents\Arduino\libraries\Fix_fft\fix_fft.cpp: In function 'int fix_fft(char*, char*, int, int)':
C:\Users\Joao\Documents\Arduino\libraries\Fix_fft\fix_fft.cpp:199:28: error: 'Sinewave' was not declared in this scope
wr = pgm_read_word_near(Sinewave + j+N_WAVE/4);
^
C:\Users\Joao\Documents\Arduino\libraries\Fix_fft\fix_fft.cpp:209:28: error: 'Sinewave' was not declared in this scope
wi = -pgm_read_word_near(Sinewave + j);
^
Error compiling.

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.

October 18, 2015 at 1:59 PM

@Anonymous
In version 1.6.0, the "prog_int8_t" is no longer used. Replace it with "int8_t". (In your fix_fft.cpp)

Replace
const prog_int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = {

For
const int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = {

October 18, 2015 at 1:59 PM

@Anonymous
In version 1.6.0, the "prog_int8_t" is no longer used. Replace it with "int8_t". (IN your fix_fft.cpp)

Replace
const prog_int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = {

For
const int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = {

October 4, 2016 at 5:56 AM

hi, quite new here. could you teach me how to add fix_fft.h in the library? vincent_lim89@live.com

Post a Comment

Topics

Popular Posts

Versión española

Versión española
Arduino es Guay!!

Follow in Facebook

Powered by Blogger.