CAJA DE RITMOS CON ECUALIZADOR
SISTEMAS EMPOTRADOS Y DE TIEMPO REAL
PROYECTO FINAL
Carmen Navacerrada Álvarez Ignacio Lázaro Solera Alejandro Rubio Barrena
ÍNDICE
Descripción del proyecto
pág 3
Figura 1. Caja de ritmos
pág 3
Material utilizado y presupuesto
pág 4
Figura 2. Componentes utilizados para el proyecto Diagramas
pág 4 pág 5
Figura 3. Esquema general de la placa y los nueve botones
pág 5
Figura 4. Esquema detallado con Arduino
pág 6
Dificultades
pág 7
Tiempo dedicado al proyecto
pág 7
Valores añadidos
pág 7
Figura 5. Medidas
pág 9
Opinión personal
pág 10
Bibliografía
pág 11
Anexo
pág 12
Código del proyecto
pág 12
Código del proyecto extra
pág 20
2
DESCRIPCIÓN DEL PROYECTO El proyecto consiste en la creación de una caja de ritmos y un ecualizador. Se trata de una caja de madera que tiene incrustados nueve botones de diferentes colores. Cada botón estará asociado a un sonido, largo o corto. La melodía se reproducirá por unos altavoces conectados al ordenador. La frecuencia generada por la música dará lugar a las ondas que se moverán en el ecualizador de la pantalla.
Figura 1. Caja de ritmos
3
MATERIAL UTILIZADO Y PRESUPUESTO -
Arduino 1.
Proporcionado por la universidad.
-
Tres botones rojos.
4.5 €
-
Tres botones azules.
4.5 €
-
Tres botones amarillos.
4.5 €
-
Nueve resistencias de 10K.
Proporcionado por la universidad.
-
Placa de inserción.
Proporcionado por la universidad.
-
Tres metros de cable.
0.5 €
-
Ordenador portátil.
-
-
Lámina de contrachapado.
4.5 €
-
Spray de pintura negra.
2.5 €
-
Clemas.
1.5 €
-
Destornillador de clemas.
6.5 €
-
Alicates.
-
-
Altavoces.
Total: 29 €
Figura 2. Componentes utilizados para el proyecto
4
DIAGRAMAS
Figura 3. Esquema general de la placa y los nueve botones
5
Figura 4. Esquema detallado con Arduino
6
DIFICULTADES Inicialmente el proyecto iba a realizarse sin necesidad del ordenador. Para ello compramos un Music Shield que permitía la reproducción de canciones desde una tarjeta SD, y una matriz de leds para realizar el ecualizador. Cuando nos llegó el Music Shield nos dimos cuenta de que usaba todos los pines de Arduino, por lo que decidimos utilizar una interfaz gráfica que simulase los botones. Para ello utilizamos Processing en lugar del entorno de programación de Arduino. Vimos que Processing disponía de una librería que permitía la reproducción de sonidos, por lo que desechamos el Music Shield y preferimos reproducir las canciones desde el ordenador y seguir con la idea de crear físicamente la caja de ritmos con los botones.
TIEMPO DEDICADO AL PROYECTO Búsqueda de la idea y pedido de los materiales – En horario de clase. Desarrollo de proyecto – 5 días. 5 horas/día. Elaboración de la memoria – 3 horas. Creación del vídeo y subida a Internet – 3 horas. Estimación total del tiempo dedicado al proyecto – 40 horas.
VALORES AÑADIDOS Processing ofrece un sinfín de posibilidades a la hora de realizar tu proyecto Arduino. Una de ellas es poder controlar Arduino desde el teclado o ratón a través de un ordenador.
La idea de la caja de ritmos en un principio, fue implementada en Processing a través de una interfaz gráfica conectada al teclado numérico. A continuación se explica el proceso de desarrollo de dicha aplicación.
Una vez hayáis echado un vistazo al proyecto, podréis descargar sin problema la aplicación y probarla en vuestro ordenador.
7
La idea era poder controlar el Arduino desde el teclado y a la vez ofrecer una interfaz gráfica que mostrará cualquier tipo de interacción.
Gracias a unos tutoriales de Processing encontrados en Youtube pudimos aprender como dibujar un array gráficamente (una ventana con varios cuadrados alineados). Más tarde echando un poco de imaginación encontramos la forma de poder dibujar una matriz. Además buscamos información de añadir color a la interfaz. Después solo tuvimos que buscar procedimientos que usa Processing para controlar cuando se pulsa una tecla y el proyecto estuvo listo. Una de las partes más importantes de este proyecto, además de la interfaz, es el tratamiento de los sonidos en Processing. Gracias a la librería Minim, esto fue posible.
Desarrollo Esta parte fue la más difícil y elaborada. La idea era dibujar una matriz pero Processing al parecer no tenía mucha compatibilidad con las matrices. Sin embargo una matriz es un conjunto de arrays, por tanto con 3 arrays de 3 posiciones, o simplemente con un array de 9, se puede dibujar una matriz (realizando los bucles adecuadamente). En Processing para dibujar hay que tener en cuenta el tamaño de la ventana y el tamaño y posición de lo que se quiere dibujar dentro. Se trabaja con pixeles. Simplemente se trata de hacer pruebas y realizar los cálculos necesarios para que cuando ejecutemos el proyecto, se muestre por pantalla como esperamos.
8
Figura 4. Medidas
9
OPINIÓN PERSONAL Personalmente, el proyecto ha tenido sus pros y sus contras. La valoración general es positiva. La creación de un proyecto de estas características y la posibilidad de manejar hardware nos ha parecido interesante y entretenido. Nos ha resultado complicado elegir componentes, lo cual ha sido un problema significativo ya que era necesario tenerlos para empezar a trabajar en el proyecto. Además una vez recibidos no hemos encontrado suficiente información para su uso. La búsqueda de la alternativa del uso de Processing ha sido muy positiva, era más sencillo de utilizar ya que había muchos tutoriales en internet. En definitiva, tras superar los problemas en cuanto a los materiales, y la decisión definitiva del proyecto, el montaje ha sido largo pero fructífero. Ha sido una experiencia divertida y muy creativa.
10
BIBLIOGRAFÍA Tutoriales de Processing – Arduino en Youtube: http://www.youtube.com/watch?v=8OHs1VqIzVU http://www.youtube.com/watch?v=QjvVPpjGWF8&feature=relmfu Página oficial de Processing: http://processing.org/ Librería de audio Minim: http://code.compartmental.net/tools/minim/ Página de efectos de sonido: http://free-loops.com/
11
CÓDIGO DEL PROYECTO import processing.serial.*; import cc.arduino.*; import ddf.minim.*; import ddf.minim.signals.*; import ddf.minim.analysis.*; import ddf.minim.effects.*;
Serial port; Arduino arduino; AudioInput in; Minim minim; FFT fftLin;
float vector[]=new float[6]; // float magnitud=4; float volumen; int volumenCaptado;
// DECLARACIÓN DE CANCIONES
AudioPlayer songAmarilloUno; AudioPlayer songAmarilloDos; AudioPlayer songAmarilloTres;
AudioPlayer songAzulUno; AudioPlayer songAzulDos; AudioPlayer songAzulTres;
AudioPlayer songRojoUno; AudioPlayer songRojoDos; AudioPlayer songRojoTres;
// DECLARACIÓN DE PINES
int amarilloUno=2; int amarilloDos=3;
12
int amarilloTres=4; int azulUno=5; int azulDos=6; int azulTres=7; int rojoUno=8; int rojoDos=9; int rojoTres=10;
int contador1=0; int contador2=0; int contador3=0;
void setup(){
println(Serial.list()); if (port==null) port = new Serial(this, Serial.list()[1], 9600); size(512, 700, P3D); volumen=1;
arduino=new Arduino(this,Arduino.list()[2],57600); minim= new Minim(this); in = minim.getLineIn(Minim.STEREO, 2048); fftLin = new FFT(in.bufferSize(), in.sampleRate()); fftLin.linAverages(240); rectMode(CORNERS);
// INDICAMOS QUE TODOS LOS PINES SON DE ENTRADA
arduino.pinMode(amarilloUno,Arduino.INPUT); arduino.pinMode(amarilloDos,Arduino.INPUT); arduino.pinMode(amarilloTres,Arduino.INPUT);
arduino.pinMode(azulUno,Arduino.INPUT); arduino.pinMode(azulDos,Arduino.INPUT); arduino.pinMode(azulTres,Arduino.INPUT);
arduino.pinMode(rojoUno,Arduino.INPUT);
13
arduino.pinMode(rojoDos,Arduino.INPUT); arduino.pinMode(rojoTres,Arduino.INPUT);
// INICIALIZAMOS LAS CANCIONES
songAmarilloUno = minim.loadFile("1.mp3"); songAmarilloDos = minim.loadFile("2.mp3"); songAmarilloTres = minim.loadFile("3.mp3");
songAzulUno = minim.loadFile("4.mp3"); songAzulDos = minim.loadFile("5.mp3"); songAzulTres = minim.loadFile("6.mp3");
songRojoUno = minim.loadFile("7.mp3"); songRojoDos = minim.loadFile("8.mp3"); songRojoTres = minim.loadFile("9.mp3");
}
void draw(){
//ECUALIZADOR
background(0); fftLin.forward(in.mix);
noStroke(); int w = int(width/6);
for(int i = 0; i < 120; i++){ if (i<=1){ if (vector[0]<2*fftLin.getAvg(i)) vector[0]=2*fftLin.getAvg(i); fill(255,0,0); // Los multiplicadores cumplen la función de ajustar la respuesta visual. En cada barra del ecualizador es diferente }
14
else if (i<=5){ if (vector[1]<1.2*fftLin.getAvg(i)) vector[1]=1.2*fftLin.getAvg(i); fill(255,0,0); } else if (i<=12){ if (vector[2]<1.6*fftLin.getAvg(i)) vector[2]=1.6*fftLin.getAvg(i); fill(0,0,255); } else if (i<=22){ if (vector[3]<1.75*fftLin.getAvg(i)) vector[3]=1.75*fftLin.getAvg(i); fill(0,0,255); } else if (i<=55){ if (vector[4]<2*fftLin.getAvg(i)) vector[4]=2*fftLin.getAvg(i); fill(255,255,0); } if (i<=160){ vector[5]=i*fftLin.getAvg(i)/300+vectormedias[5]; fill(255,255,0); } if (i==119){ for (byte k=0;k<6;k++){ delay(10); // El número que hay a continuación es el nivel real que se recibe como input. if (in.mix.level()>0.001){ volumen=volumen*0.998+in.mix.level()*0.002*10; // Se multiplica por 10 para que sea cercano a 1 volumenCaptado=int(magnitud*vector[k]/volumen); //Manda la cantidad de volumen captado a Arduino y a las barras. if (volumenCaptado>300) volumenCaptado=300; volumenCaptado=volumenCaptado/10; port.write((k<<5)|byte(volumenCaptado)); rect(k*w, height, k*w + w, height - magnitud*vector[k]/volumen);
15
// Dibuja las barras en la ventana de Processing } else{ // Entra por esta rama cuando hay un volumen muy bajo volumen=0.4; port.write((k<<5)); delay(100); }
vector[k]=0; // Igualamos a 0 para la nueva iteración
} }
}
//BOTON AMARILLO 1
if ((arduino.digitalRead(amarilloUno)==Arduino.HIGH)){
contador1++; delay(200); if (contador1%2==1){ songAmarilloUno = minim.loadFile("1.mp3"); songAmarilloUno.play(); } else { songAmarilloUno.close(); } }
//BOTON AMARILLO 2
if ((arduino.digitalRead(amarilloDos)==Arduino.HIGH)){ contador2++; delay(200);
16
if (contador2%2==1){ songAmarilloDos = minim.loadFile("2.mp3"); songAmarilloDos.play(); } else { songAmarilloDos.close(); } }
//BOTON AMARILLO 3
if ((arduino.digitalRead(amarilloTres)==Arduino.HIGH)){ contador3++; delay(200); if (contador3%2==1){ songAmarilloTres = minim.loadFile("3.mp3"); songAmarilloTres.play(); } else { songAmarilloTres.close(); } }
// BOTON AZUL 1
if ((arduino.digitalRead(azulUno)==Arduino.HIGH)){ songAzulUno.close(); delay(5); songAzulUno = minim.loadFile("4.mp3"); songAzulUno.play(); }
// BOTON AZUL 2
if ((arduino.digitalRead(azulDos)==Arduino.HIGH)){ songAzulDos.close();
17
delay(10); songAzulDos = minim.loadFile("5.mp3"); songAzulDos.play(); }
// BOTON AZUL 3
if ((arduino.digitalRead(azulTres)==Arduino.HIGH)){ songAzulTres.close(); delay(15); songAzulTres = minim.loadFile("6.mp3"); songAzulTres.play(); }
// BOTON ROJO 1
if ((arduino.digitalRead(rojoUno)==Arduino.HIGH)){ songRojoUno.close(); delay(15); songRojoUno = minim.loadFile("7.mp3"); songRojoUno.play(); }
// BOTON ROJO 2
if ((arduino.digitalRead(rojoDos)==Arduino.HIGH)){ songRojoDos.close(); delay(15); songRojoDos = minim.loadFile("8.mp3"); songRojoDos.play(); }
// BOTON ROJO 3
if ((arduino.digitalRead(rojoTres)==Arduino.HIGH)){ songRojoTres.close(); delay(15); songRojoTres = minim.loadFile("9.mp3");
18
songRojoTres.play(); }
}
void stop() { // Cerrarmos las librerías minim in.close(); // Y paramos antes de salir minim.stop(); }
19
CÓDIGO DEL PROYECTO EXTRA //MATRIZ DE BOTONES (TECLADO_NUMERICO)
import processing.serial.*; import cc.arduino.*; import ddf.minim.*; import ddf.minim.signals.*; import ddf.minim.analysis.*; import ddf.minim.effects.*;
Arduino arduino; Minim minim; AudioPlayer song1; AudioPlayer song2; AudioPlayer song3; AudioPlayer song4; AudioPlayer song5; AudioPlayer song6; AudioPlayer song7; AudioPlayer song8; AudioPlayer song9;
color off=color(0,0,0);
color blanco =color(255,255,255); color rojo=color(255,0,0); color azul=color(0,0,255); color amarillo = color(255,255,0);
int[] values={Arduino.LOW,Arduino.LOW,Arduino.LOW,Arduino.LOW,Arduino.LOW,Arduino.LOW,Arduino.LO W,Arduino.LOW,Arduino.LOW};
20
/*En este apartado se realizan los import necesarios para poder trabajar con Arduino, el puerto serie y la librería minim. Además se declaran las variables de las canciones y los colores (esos tres campos son los de la escala RGB, se puede mirar en Wikipedia) . Por último se declara el array que va a contener a los cuadrados (en abstracto). */
void setup(){
size(500,500); arduino=new Arduino(this,Arduino.list()[2],57600); minim= new Minim(this);
//INICIALIZAMOS LAS CANCIONES
song1 = minim.loadFile("1.mp3"); song2 = minim.loadFile("2.mp3"); song3 = minim.loadFile("3.mp3"); song4 = minim.loadFile("4.mp3"); song5 = minim.loadFile("5.mp3"); song6 = minim.loadFile("6.mp3"); song7 = minim.loadFile("7.mp3"); song8 = minim.loadFile("8.mp3"); song9 = minim.loadFile("9.mp3");
}
/* size(500,500); size sirve para dibujar la ventana arduino=new Arduino(this,Arduino.list()[2],57600) Muy importante. Con esto inicializamos nuestra variable arduino y le indicamos en que Puerto y a qué velocidad se va a comunicar processing con arduino. (El número que va entre corchetes puede variar en función del usb en el que hayais conectado el Arduino).*/
void draw(){
background(off); stroke(blanco); //Sirve para pintar el marco de loscuadrados del mismo color
21
//PINTAMOS LA PRIMERA FILA ( 1,2,3) (abajo según teclado numérico)
for(int i=0 ; i<=2 ;i++){ if(values[i]==Arduino.HIGH){ fill(amarillo); //Para pintar el cuadrado por dentro (de amarillo) rect((i*150)+51,350,100,100); // rect(x, y, width, height) } else{ fill(off); rect((i*150)+51,350,100,100); } }
//PINTAMOS LA SEGUNDA FILA (4,5,6) for(int i=0 ; i<=2 ;i++){ if(values[i+3]==Arduino.HIGH){ fill(azul); //Para pintar el cuadrado por dentro (de azul) rect((i*150)+51,200,100,100); // rect(x, y, width, height) } else{ fill(off); rect((i*150)+51,200,100,100); } }
//PINTAMOS LA TERCERA FILA (7,8,9) for(int i=0 ; i<=2 ;i++){ if(values[i+6]==Arduino.HIGH){ fill(rojo); //Para pintar el cuadrado por dentro (de rojo) rect((i*150)+51,50,100,100); // rect(x, y, width, height) } else{ fill(off); rect((i*150)+51,50,100,100);
22
} }
}
/*Aquí se muestran tres bucles que son los que dibuja verdaderamente la matriz y los que controlan en la interfaz cuando se debe colorear un cuadrado(botón pulsado) o cuando se debe dejar en negro(botón sin pulsar). Otro dato es que la matriz está dibujada de abajo a arriba y de izquierda a derecha. Si tenéis algún problema con algún bucle simplemente mirar el dibujo en el apartado de desarrollo y lo entenderéis. Si el valor de la posición de array es Arduino.HIGH ,habrá que dibujar el cuadrado (fill() ) y dibujarlo con la funcion rect ( x,y, width,height). En el caso del primer bucle a medida que vayamos recibiendo las posiciones v[0],v[1] o v[2], se va a situar en el pixel donde empieza el cuadrado, y por tanto lo dibujará. El 150 es para situarme en el eje correspondiente de las Y , y el 51 para situarme en el de las x (según multipliquemos).*/
void keyPressed(){
if (key=='1'){ if(values[0]==Arduino.LOW){ values[0]= Arduino.HIGH; song1 = minim.loadFile("1.mp3"); song1.play(); } else{ values[0]= Arduino.LOW; song1.close(); } }
if (key=='2'){ if(values[1]==Arduino.LOW){ values[1] = Arduino.HIGH; song2=minim.loadFile("2.mp3"); song2.play();
23
} else{ values[1] = Arduino.LOW; song2.close(); } }
if (key=='3'){ if(values[2]==Arduino.LOW){ values[2] = Arduino.HIGH; song3=minim.loadFile("3.mp3"); song3.play(); } else{ values[2] = Arduino.LOW; song3.close(); } }
if (key=='4'){ if(values[3]==Arduino.LOW){ values[3] = Arduino.HIGH; song4=minim.loadFile("4.mp3"); song4.play(); } else{ values[3] = Arduino.LOW; song4.close(); } }
if (key=='5'){ if(values[4]==Arduino.LOW){ values[4] = Arduino.HIGH; song5=minim.loadFile("5.mp3");
24
song5.play(); } else{ values[4] = Arduino.LOW; song5.close(); } }
if (key=='6'){ if(values[5]==Arduino.LOW){ values[5] = Arduino.HIGH; song6=minim.loadFile("6.mp3"); song6.play(); } else{ values[5] = Arduino.LOW; song6.close();
} }
if (key=='7'){ if(values[6]==Arduino.LOW){ values[6] = Arduino.HIGH; song7=minim.loadFile("7.mp3"); song7.play();
} else{ values[6] = Arduino.LOW; song7.close(); } }
if (key=='8'){ if(values[7]==Arduino.LOW){ values[7] = Arduino.HIGH;
25
song8=minim.loadFile("8.mp3"); song8.play(); } else{ values[7] = Arduino.LOW; song8.close(); } }
if (key=='9'){ if(values[8]==Arduino.LOW){ values[8] = Arduino.HIGH; song9=minim.loadFile("9.mp3"); song9.play();
} else{ values[8] = Arduino.LOW; song9.close(); } }
}
/*Esta parece ser la parte más larga, pero no os asustéis pues es la más sencilla. Se trata de 9 bucles que hacen exactamente los mismo, por tanto explicaré solo uno. Esto se ha hecho así porque no es posible hacer un array de canciones. Además usar una sola variable para cargar en ella todas las canciones haría que hubiera algún tipo de delay. Si pulsamos la tecla 1, si el valor de la posición 0 del array esta en LOW(apagado) , pasará a estar HIGH, se cargará la canción y se pondrá en funcionamiento. En caso contrario (posición 0 del array = HIGH), la posición del array se pondrá a LOW y la canción parará.*/
26