TECLADO MUSICAL CON ARDUINO
MIEMBROS DEL GRUPO
- RAFAEL CASTILLO MUÑOZ
- YAGO NAVARRETE MARTÍNEZ
- ALEJANDRO OREJUDO FRAILE
Memoria del proyecto: https://drive.google.com/file/d/11tYAj70_MnBSN8GRIZ7tetQalvNR9PHA/view?usp=sharing
IDEA GENERAL
El proyecto diseñado en Arduino para la asignatura de Diseño de Sistemas Empotrados tiene como nombre “Teclado musical”. Como su propio nombre indica, se trata de un pequeño teclado implementado en Arduino, mediante el cual se pueden tocar piezas musicales.
Inicialmente el proyecto iba a ser un teclado interactivo diseñado para aprender a tocar canciones. El teclado reproduciría una canción aleatoria, que el usuario debería repetir satisfactoriamente mediante una serie de botones. La idea fue descartada posteriormente, implementando en su lugar diversas funcionalidades adicionales al teclado para compensar. Se mantuvo el concepto base de reproducir distintas notas mediante pulsadores, esencia de cualquier teclado, pero se incorporaron un sistema para cambiar la escala, la opción de poder grabar y reproducir una secuencia de notas elegidas por el usuario y un sistema que modifica la velocidad de reproducción en función de la luz ambiente.
El proyecto es por lo tanto un teclado divertido, sencillo y fácil de usar, que permite a cualquier usuario pasar un buen rato sin tener conocimientos musicales previos. El objetivo buscado era que resultase intuitivo, siendo un teclado musical accesible a todos los públicos.
El proyecto es por lo tanto un teclado divertido, sencillo y fácil de usar, que permite a cualquier usuario pasar un buen rato sin tener conocimientos musicales previos. El objetivo buscado era que resultase intuitivo, siendo un teclado musical accesible a todos los públicos.
HARDWARE
El hardware empleado para el desarrollo del proyecto es muy básico. Se han usado una placa Arduino UNO R3, una protoboard, 9 botones (7 para el teclado y 2 para funcionalidades extra), un zumbador pequeño, un led, dos resistencias de 220 y 1k ohmios respectivamente, una fotorresistencia, un potenciómetro de 1k ohmios y varios cables jumpers para realizar las interconexiones.
En primer lugar, para la implementación del piano se han conectado siete botones distintos a diferentes pines digitales, desde el pin 8 al pin 2 (Se han dejado libres el pin 0 y 1 para permitir depurar el código usando el puerto serie). Cada uno de estos envía una señal eléctrica al Arduino al ser pulsados, que lee dicho input y transforma cada señal en una nota concreta distinta. Para ello envía una señal output al zumbador, que vibra en una frecuencia y escala determinada por el valor de la señal de salida que recibe. Se han conectado mediante resistencias pullup internas del chip Atmega de la placa Arduino tanto por falta de resistencias adecuadas como para facilitar el uso de los botones al no introducir cableado por la parte delantera.
En segundo lugar, se ha conectado el zumbador a la placa. El lado positivo se ha conectado al pin digital 11 y el lado negativo a tierra. Será este pin el encargado de enviar las señales con las distintas frecuencias para hacer vibrar el zumbador.
En tercer lugar, se ha incorporado un controlador de escala. Se trata de un potenciómetro conectado al pin analógico 0 del Arduino. Los valores analógicos, a diferencia de los digitales, se caracterizan por poseer un rango de valores y no solo dos estados. Es gracias a ello que el input obtenido mediante el potenciómetro permite la modificación de las notas reproducidas a diferentes escalas. Se han implementado 4 escalas distintas, pero esto es fácilmente modificable (ver software).
En cuarto lugar, se controla una pequeña función de grabación incorporada por medio de dos botones. El primero de ellos, conectado al pin 9, reproduce la última melodía guardada al ser pulsado, mientras que el segundo, conectado al pin 12, permite grabar una melodía de hasta 30 segundos, registrando las notas correspondientes a los botones pulsados.
El botón de grabar, activa la grabadora implementada en el sistema, y enciende un LED, conectado al pin 13, que indica que se está llevando a cabo correctamente y permite apreciar cuando el Arduino está registrando las pulsaciones del teclado. Tras eso, se guardarán los inputs de las teclas pulsadas durante 30 segundos o hasta que se pulse de nuevo el botón de grabar.
Cuando el Arduino detecta una pulsación en el botón de reproducir la melodía guardada, este accede a su memoria EEPROM, lee las frecuencias almacenadas y las envía automáticamente al zumbador para que reproduzca las notas correspondientes, de forma similar a si se estuviese pulsando los botones del teclado.
Por último, nuestro proyecto incorpora también un LDR, que envía otra señal analógica al Arduino en función de la luz ambiente. Dicha señal es usada para establecer el tiempo de reproducción de cada nota (y silencio) al reproducir la canción grabada. El rango establecido está entre 100 ms y 1 segundo por nota. Cuanto mayor es la cantidad de luz que recibe la fotorresistencia, menor es el tiempo de cada nota.
Listado de materiales
SOFTWARE
En segundo lugar, se va a explicar el software implementado para el proyecto. Lo primero que se preparó fue una serie de constantes usadas para cada una de las notas que se pueden tocar con cada tecla (con cada pulsador). Cada nota se identifica según su frecuencia, y para cada nota se almacenan cuatro valores en un array, que son su frecuencia en diferentes escalas. También se implementa el máximo de escalas, así como la duración base y los valores máximos y mínimos de duración usada para el control con LDR.
Tras eso, se preparan todos los componentes hardware que se usarán, asignando cada uno de ellos a un pin digital o analógico. BUZZER reproduce los sonidos, RECORDER activa o desactiva la grabación, PLAYER activa la reproducción, LED controla el led que indica que se está grabando, PIANO es cada una de las teclas, y posteriormente se preparan el potenciómetro y LDR para cada pin analógico.
Entonces, se inicializan ciertos valores a utilizar posteriormente. Al comienzo del código se incluye la librería EEPROM, utilizada para controlar la memoria interna del sistema Arduino. Esta librería posee ciertos métodos usados principalmente para almacenar en memoria una serie de variables o estructuras que se almacenan incluso entre reinicios del sistema. Para usarla, se declara la variable mem_pointer.
Se almacena el tiempo de reproducción de cada nota y los diferentes modos en los que puede estar el sistema: piano, grabación o reproducción. Los valores recorder_prev y recorder_curr se utilizan para controlar el sistema de grabación, ya que la grabación almacena hasta un máximo de 30 segundos, pero se puede detener antes si así se desea.
A continuación en el Setup() se declara cada pin como entrada o salida respectivamente. Se ha usado el término INPUT_PULLUP debido a que la placa protoboard que se ha utilizado posee resistencias internas pull-up, y por tanto los valores de entrada y salida LOW y HIGH se invierten, y los comandos cambian.
Así, comienza el bucle principal del programa Loop(), el cual está compuesto por un simple conjunto de líneas de código. Se han preparado muchos métodos usados en ese bucle que se explicarán a continuación.
- SetSpeed: se encarga de analizar la luz ambiente por medio del LDR, obteniendo una señal que posteriormente se mapea y se guarda en la variable note_delay usando map. Este método controlará la duración de las notas dependiendo de la luz ambiente, y dicha duración solo se verá reflejada a la hora de reproducir una canción grabada.
- SetScale: utiliza el potenciómetro de manera similar al LDR. De ambos dos métodos se toman sus entradas por los pines analógicos, razón por la cual se usa analogRead(). En el caso de SetScale, la función se utiliza para controlar en qué escala se encuentran las notas que se van a reproducir al usar el piano. Dicha escala es independiente de la reproducción. Las notas almacenadas durante la grabación no variarán según el potenciómetro, sino que siempre sonarán conforme se hayan grabado.
- Piano: en el caso de que se lea que cualquier tecla, es decir, un pulsador, ha sido accionado, se reproduce en el buzzer una nota musical que depende la tecla tocada, reproduciendo una nota u otra, así como de la escala seleccionada por medio del potenciómetro. La reproducción de la nota dura tanto como el tiempo que estés pulsando.
Tras eso, dentro de este método se comprueba si se ha pulsado el botón de grabar para la próxima iteración del bucle loop() principal. La comprobación se hace por medio de recorder_curr y recorder_prev. Esto se ha implementado de esta manera por si se deja pulsado el botón y empieza a cambiar de modo constantemente. Así solo cambia en el instante en el que se pulsa, ya que, si se está grabando y se vuelve a pulsar el botón, se deja de grabar.
Tras esto simplemente se comprueba si el botón de reproducir se ha activado, en cuyo caso se declara mode como 2 para que loop en la siguiente iteración entre en player, es decir, en reproducción. - Grabación: en primer lugar, se enciende el led preparado en el pin 13. La memoria no puede grabar más de una canción a la vez, debido a su limitado espacio, por lo que, si se guarda una nueva, escribiéndose en memoria, la anterior es eliminada.
Usando comandos de la librería EEPROM, en este caso put, se almacenará la nota pulsada en el punto de memoria indicado por el puntero mem_pointer. Si se pulsan varias notas a la vez, se guardará la de mayor frecuencia. De nuevo se examina por medio de recoder_curr el modo en el que se encuentra el sistema. En el caso de que se detecte que se ha pulsado de nuevo el botón de grabación, se dejará de grabar, pero también se dejará de grabar si se ha llegado al punto máximo de memoria especificado. Cuando ya no se va a grabar, se apaga el led. - Player: reproduce la canción almacenada en memoria anteriormente. Usará la variable auxiliar top_pointer para recorrer la memoria junto a mem_pointer, y conseguirá las notas almacenadas con EEPROM.put(). El valor de la duración de las notas se comprueba constantemente tras reproducir cada nota, para tener en cuenta si la luz ambiente varía y por tanto deben variar la duración de cada nota. Cuando se reproducen todas las notas, se pasa a modo piano. Además, noTone se utiliza para cuando el valor leído es 0, es decir que en ese momento no se ha tocado ninguna nota.
Código fuente
#include <EEPROM.h> const float DO[] = {130.81, 261.63, 523.25, 1046.50}; const float RE[] = {146.83, 293.66, 587.33, 1174.66}; const float MI[] = {164.81, 329.63, 659.25, 1318.51}; const float FA[] = {174.61, 349.23, 698.46, 1396.91}; const float SOL[] = {196.00, 392.00, 783.99, 1567.98}; const float LA[] = {220.00, 440.00, 880.00, 1760.00}; const float SI[] = {246.94, 493.88, 987.77, 1975.53}; const int total_scales = 4; const int min_delay = 50; const int max_delay = 400; const int max_time = 30; int t = 50; int BUZZER = 11; int RECORDER = 12; int PLAYER = 9; int LED = 13; int PIANO[] = {8,7,6,5,4,3,2}; int POTENTIOMETER = A0; int LDR = A1; int mem_pointer = 0; int scale = 0; // Escala (0-3) int note_delay = 1; // Tiempo de reproducción de cada nota (ms). int mode = 0; // Modo (0 - Piano, 1 - Grabar, 2 - Reproducir) int recorder_prev; int recorder_curr; //////////////////////////////////////// //////////////// SET UP //////////////// //////////////////////////////////////// void setup() { pinMode(13, OUTPUT); Serial.begin(9600); pinMode(RECORDER, INPUT_PULLUP); pinMode(PLAYER, INPUT_PULLUP); for(int i = 0; i < 7;i++) { pinMode(PIANO[i], INPUT_PULLUP); } recorder_prev = 0; recorder_curr = 0; } //////////////////////////////////////// ///////////////// LOOP ///////////////// //////////////////////////////////////// void loop() { SetSpeed(); SetScale(); switch(mode){ case 0: piano(); break; case 1: record(); break; case 2: play(); break; default: break; } } //////////////////////////////////////// /////////////// METODOS //////////////// //////////////////////////////////////// ///////////// PIANO /////////////////// void piano(){ // REPRODUCIR NOTAS: while(digitalRead(PIANO[0]) == LOW) { tone(BUZZER, DO[scale], t); } while(digitalRead(PIANO[1]) == LOW) { tone(BUZZER, RE[scale], t); } while(digitalRead(PIANO[2]) == LOW) { tone(BUZZER, MI[scale], t); } while(digitalRead(PIANO[3]) == LOW) { tone(BUZZER, FA[scale], t); } while(digitalRead(PIANO[4]) == LOW) { tone(BUZZER, SOL[scale], t); } while(digitalRead(PIANO[5]) == LOW) { tone(BUZZER, LA[scale], t); } while(digitalRead(PIANO[6]) == LOW) { tone(BUZZER, SI[scale], t); } // COMPROBAR BOTÓN DE GRABAR if(digitalRead(RECORDER) == LOW){ recorder_curr = 1; } else{ recorder_curr = 0; } if(recorder_curr == 1 && recorder_prev == 0){ mode = 1; } recorder_prev = recorder_curr; // COMPROBAR BOTÓN DE REPRODUCIR: if(digitalRead(PLAYER) == LOW){ mode = 2; } // DELAY: delay(t); } ////////////// GRABADORA /////////////// void record(){ // ENCIENDE EL LED digitalWrite(13, HIGH); mem_pointer = 0; t = 250; while(mode == 1){ float note = 0.0f; if(digitalRead(PIANO[0]) == LOW) { tone(BUZZER, DO[scale], t); note = DO[scale]; } if(digitalRead(PIANO[1]) == LOW) { tone(BUZZER, RE[scale], t); note = RE[scale]; } if(digitalRead(PIANO[2]) == LOW) { tone(BUZZER, MI[scale], t); note = MI[scale]; } if(digitalRead(PIANO[3]) == LOW) { tone(BUZZER, FA[scale], t); note = FA[scale]; } if(digitalRead(PIANO[4]) == LOW) { tone(BUZZER, SOL[scale], t); note = SOL[scale]; } if(digitalRead(PIANO[5]) == LOW) { tone(BUZZER, LA[scale], t); note = LA[scale]; } if(digitalRead(PIANO[6]) == LOW) { tone(BUZZER, SI[scale], t); note = SI[scale]; } // Escribir en memoria EEPROM la frecuencia de la nota pulsada (si se // han pulsado varias se guardará la de mayor frecuencia: EEPROM.put(mem_pointer, note); mem_pointer += sizeof(float); // Cambiar a modo grabación (solo en el instante en el que se pulsa, // al volver a pulsar se acaba la grabación). if(digitalRead(RECORDER) == LOW){ recorder_curr = 1; } else{ recorder_curr = 0; } // Se guarda una nota cada 250 ms (1/4 segs), por lo que la capacidad // máxima de canción será de 30 segundos (definido por max_time): if(recorder_curr == 1 && recorder_prev == 0 || mem_pointer > sizeof(float) * max_time * 4){ mode = 0; EEPROM.put(mem_pointer, -1.0f); mem_pointer += sizeof(float); } recorder_prev = recorder_curr; delay(t); } digitalWrite(13, LOW); t = 50; } ////////////// REPRODUCTOR /////////////// void play(){ float note = 0.0f; mem_pointer = 0; while(note >= 0.0f){ // Reproduce la nota almacenada en memoria: EEPROM.get(mem_pointer, note); if(note == 0.0f){ noTone(BUZZER); }else if (note > 0.0f){ tone(BUZZER,note, note_delay); } delay(note_delay); // Pasa el puntero de memoria a la siguiente nota: mem_pointer += sizeof(float); // El ritmo de la canción puede cambiar dinámicamente con la luz que // recibe el LDR: SetSpeed(); } //Al acabar la canción se pasa a modo piano (se deja un delay para evitar // que se entre en la función muchas veces si no hay canción grabada). delay(1000); mode = 0; } ////////////////////////////////// ////// Métodos auxiliares: /////// ////////////////////////////////// // -- Establece la escala mediante el potenciómetro: void SetScale(){ int n = analogRead(POTENTIOMETER); scale = map(n, 0, 1024, 0, total_scales); } // -- Establece la velocidad de reproducción mediante el LDR: void SetSpeed(){ int n = analogRead(LDR); note_delay = max_delay + min_delay - map(n, 0, 1024, min_delay, max_delay); }
CASOS DE USO
Como ya se ha comentado con anterioridad, el punto fuerte del proyecto es su sencillez, que permite a cualquier usuario, esté familiarizado o no con términos musicales, pasarlo en grande. Se han añadido al teclado multitud de funciones interactivas, que permiten a su dueño jugar con el producto durante horas, puesto que gracias a la elevada cantidad de posibilidades los resultados obtenidos son casi infinitos.
Piano
En primer lugar, el sistema posee un total de siete botones. Cada uno de ellos reproduce una nota almacenada mediante un zumbador. Todo teclado que se precie debe tener teclas que reproduzcan sonidos cuando sean pulsadas, y nuestra innovadora implementación no es menos. El control de este “modo libre” es fluido e intuitivo. Si se mantiene pulsado un botón, la nota se reproduce hasta que se levanta el dedo del pulsador.
Cambio de escala
Obviamente todo buen teclado musical trae también opciones algo más avanzadas para expertos en el ámbito. En el caso de nuestro diseño, se ha implementado un controlador de escala que modifica la frecuencia de las notas en función de cuanto gire la rueda de un potenciómetro el usuario. Esto permite tocar diversas canciones que requieran escalas específicas, o sencillamente jugar un rato produciendo sonidos más agudos o graves a pesar de tocar siempre tu tecla favorita.
Grabadora
Se ha incorporado al producto también una función de grabadora, controlada mediante dos botones adicionales. Uno de ellos graba hasta 30 segundos de la melodía tocada por el usuario, mientras que el otro reproduce la última melodía grabada. Esto permite al usuario grabar su canción favorita en memoria, pudiendo mantenerla guardada hasta que decida sobrescribirla volviendo a pulsar el botón de grabar. Para una mayor comodidad para el usuario, cuando se está grabando una melodía se enciende un indicador visual en forma de LED. También se puede detener el proceso de grabación cuando se desee, volviendo a pulsar el botón de grabar mientras el LED está encendido.
Velocidad de reproducción
Se añadió también un controlador de la velocidad de reproducción de la canción guardada en memoria. Gracias a estar conectado a un sensor lumínico, cuando el ambiente es oscuro la melodía se reproduce lentamente. Esta funcionalidad es ideal para un uso enfocado a la creación de música terrorífica, con la que crear una atmósfera para morirse de miedo. Cuando la sala está iluminada, la melodía se reproduce a una velocidad normal, no entorpeciendo así a los usuarios que la utilicen sin este fin.
PROBLEMAS Y SOLUCIONES
Resumen de los diversos problemas encontrados con cada una de las partes del proyecto, explicando detalladamente las soluciones encontradas a los mismos. Cabe destacar que la realización del proyecto se ha llevado a cabo de manera no presencial, imposibilitando la implementación hardware de manera conjunta entre todos los miembros del grupo. Dicha tarea ha recaído en manos de Alejandro Orejudo, responsable de la caja de Arduino otorgada por la universidad. El resto de trabajos se han realizado de manera conjunta entre los distintos miembros del grupo, incluida la logística detrás de la colocación de cada componente hardware.
Hardware
En cuanto al hardware el principal problema encontrado fue la tardanza en la entrega de los materiales por parte de las empresas de reparto a causa de las nevadas. A pesar de no requerir componentes muy específicos para el proyecto, algunos elementos necesarios no se encontraban incorporados en la caja de Arduino proporcionada por la universidad. Se había avisado de la necesidad de encargar material adicional para la realización del proyecto, y aunque el encargo se realizó antes de navidades, los componentes no llegaron a tiempo. En su lugar se tuvo que pedir prestado material a compañeros, así como realizar la compra de este en tiendas físicas. Por suerte se pudo solventar este problema a tiempo para la entrega.
Se quiso utilizar en un primer acercamiento al proyecto el display de texto incorporado en la caja para mostrar el nombre de la canción que se estaba reproduciendo, cuando la idea del proyecto no era definitiva. Dicho componente tuvo que ser descartado, pues el número de pines no era suficiente para conectarlo junto a los botones.
En cuanto a problemas con los componentes, un par de botones dejaron de funcionar tras estar haciendo pruebas con ellos. El pin 10 no respondía de forma adecuada al botón de grabar. Esto se solucionó usando el pin 12.
Inicialmente se usó un potenciómetro de 10k, pero era demasiado incómodo y difícil de manipular y costaba demasiado girarlo para cambiar la escala, había que dar demasiadas vueltas. Similar a esto, en un principio se usó un zumbador más grande, pero los cables de cobre no se conectaban bien a la placa y fallaba al reproducir sonidos con frecuencias menores a 230.
Software
En primer lugar, se desconocía si la implementación de una grabadora iba a ser posible. Se conocía la posibilidad de guardar en memoria una cantidad limitada de datos, pero no cómo iba a ser posible almacenar distintos valores de manera ordenada para el caso concreto de nuestro proyecto. Se solucionó este problema mediante el uso de la librería EEPROM, incorporada en Arduino para estos casos. Se trata de una memoria no volátil, ideal para la funcionalidad de la grabadora del teclado.
Casos de uso
Se trató inicialmente de implementar un controlador de volumen. Esta idea se cambió por un sistema para modificar la escala de las notas finalmente, debido a la difícil implementación que planteaba, al no conocer si esto era posible en el zumbador del que se disponía. No obstante, se reutilizaron el potenciómetro y las funciones de código ya implementadas para añadir una función que permite modificar la escala.