<< 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):
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.
/* * 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 :-).
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
There is a mistake in the
ReplyDeleteOrganoLuces.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.
you must change in: #include ...
ReplyDeleteI 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
@Nicu FLORICA
ReplyDeleteA pleasure that you have served as inspiration!
This comment has been removed by the author.
ReplyDeletei keep getting this error message and I don't know what to do!
ReplyDeleteArduino: 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.
@Kerem
ReplyDeleteIt is possible that this forum can help:
http://www.devopsview.com/blog/arduino/arduino-yun-fix_fft/
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
ReplyDeleteshould this pose a problem? if so, what modifications would you advise?
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!
ReplyDelete@Kerem
ReplyDeleteHow did you fix your code? I keep getting the same error message as you did.
@Kerem
ReplyDeleteThe 330 Ohm resistor is T1, and the 360 Ohm is T2.
Hello and thanks for the code
ReplyDeletebut 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.
@Simei Steiner
ReplyDeleteHave you added the 'fix_fft' library?
This comment has been removed by the author.
ReplyDelete@Arduino GuayYes I did it.
ReplyDeleteThanks for sharing this! Finally it compiles without errors, I will try connecting my arduino to a microphone tomorrow..
ReplyDeleteI've got error message:
ReplyDelete"
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?
I also have this error. Did someone fix it? Thanks
ReplyDeleteArduino: 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.
@Anonymous
ReplyDeleteIn 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 = {
@Anonymous
ReplyDeleteIn 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 = {
hi, quite new here. could you teach me how to add fix_fft.h in the library? vincent_lim89@live.com
ReplyDeleteHello!
ReplyDeleteThis is really a nice software. I "ported" it to Digispark, I only had to change the analog input to P4 (from reasons I do not know analogRead(0) defines P5, which is Reset, so analogRead(2) reads from P4 and P5 is left open. The Leds are on P0, 1 and 2. Took some time and https://digistump.com/wiki/digispark/tutorials/basics but now it works ;-)
In addition to my last post:
ReplyDeletehttps://www.youtube.com/watch?v=E-_1xuJtCcw&feature=youtu.be&ab_channel=hansgruber
What are you using to drive the large light bulbs from the breadboard?
ReplyDelete@Ricardo Cortez
ReplyDeleteIn the next chapter ....
Arduino light organ (III) - Power stage