Piano 32 notas

Autores: David Paredes Gómez

Raúl Rodríguez López -Rey

Introducción

El proyecto como su titulo indica, consiste en la realización de un piano con un Arduino Uno que permita tocar 32 notas. 16 agudas y 16 graves.

Para cambiar entre escalas hay un potenciometro. Además, cuenta con una tecla que al pulsarla hace de una especie de pedal para activar los sostenidos de las notas. Para diferenciar las escalas y las notas que estamos pulsando, hay un lcd 16×2 que se encarga de decirnoslo

Materiales y costes

Para llevar a cabo este proyecto hemos usado:

  • Placa Arduino Uno 27 €
  • Altavoz: De 8 Ω que vale 2,5 €
  • 9 botones de 1,5 €
  • 2 potenciometros 1,6€ cada uno
  • Pantalla lcd 16×2 10 €
  • Madera
  • Cables 13,6€ el manojo
  • Pistola de pegamento termofusible 16€
  • Resistencias 3,7 €
  • Protoplaca 12 €

Cabe destacar que la mayoría de componentes nos lo ha proporcionado la Universidad

Implementación

La implementación esta hecha con madera para realizar la caja y las teclas.

Se usarán 9 teclas, las 7 primeras son la escala Do,Re,Mi,Fa,Sol,La,Si la 8 tecla será el do sostenido ( por un tema de ahorrar espacio y poder hacer sonar el máximo numero de notas) y el 9 será el que activara los sostenidos. Si habéis estado atentos, esto hará que la ultima nota sostenida , si’, será la 7 tecla, por lo que la 7 y la 8 cuando se pulsa el pedal(tecla 9) se denominaran Do# y Re#

Se pondrá el panel lcd con sus respectivos pines, y usaremos el otro potenciometro para darle el valor al contraste.

El altavoz se conecta con el lado positivo al Arduino y con el negativo a tierra.

El potenciometro se conecta la pata central al Arduino y las otras al la salida 5v del Arduino y a tierra respectivamente.Esto hará que dependiendo del valor del potenciometro, estará activa la escala aguda o la grave.

Circuito

Se disponen 9 botones con sus resistencias(las resistencias no son necesarias , las usamos porque son más pequeñas que los cables más cortos que teníamos y así quedaba más ordenado) en la protoplaca, un lado al Arduino y otro al positivo. Se pone el potenciometro con la pata central al Arduino y las otras dos a tierra y al positivo respectivamente. Y para acabar con la parte del sonido, se pone el altavoz, se protege con una resistencia si no se quiere que suene tal alto, aunque para la presentación es mejor quitarla.
Parte de los botones y del potenciometro para que se vea mejor

Por ultimo, tenemos el lcd 16×2, se conectan sus puertos correspondientes a tierra y al positivo, el v0 a un potenciometro para controlar el contraste y por ultimo d4-d7, junto con RS y E al Arduino. Estos últimos será la manera de pasar los datos a la pantalla.

Parte del lcd y altavoz

Software

Antes de pasar a explicar el código, vamos a explicar lo básico para entender como funciona el micro-chip ATMEGA 328 (para mas información mirar su datasheet). Dejo por aquí el video de donde hemos sacado las imágenes y de donde aprendimos todo esto sobre el micro-chip: https://www.youtube.com/watch?v=Hzhaoz2j3lA

La imagen que encontramos arriba es una foto del ARDUINO UNO que utiliza un micro-chip ATMEGA328. Los puertos que están pintados en rojo son los puertos que utilizamos normalmente con ARDUINO y en los que pone PORT C,D y B son del micro-chip ATMEGA328, a los que podemos acceder directamente.

Como ARDUINO es secuencial, no tiene programación concurrente, a la hora de hacer un piano y tocar varias notas a la vez, es imposible que estas se ejecuten simultáneamente , siempre se van a ejecutar después de una nota que haya sonado antes, por lo tanto, para engañar al oído humano y que parezca que varias notas suenan a la vez lo primero que necesitamos es la mayor velocidad posible a la hora de ejecutar instrucciones y accediendo a los registros del micro-chip conseguimos esta velocidad ya que no tiene que estar ejecutando cada vez que entre en el LOOP funciones como digitalRead() o digitalWrite().

El micro-chip tiene 3 partes diferenciables, cada uno con sus registros importantes, pero aquí solo vamos a ver los registros DDRX, PORTX y PINX.

DDRX: Es un registro de 8 bits que sirve para establecer el pueto como INPUT u OUPUT, dependiendo de si pones en el valor del registro un 0 o un 1.

PORTX: Es un registro de 8 bits que sirve para establecer el valor del puerto en HIGH o LOW, dependiendo de si tiene un 0 o un 1 en valor del registro.

PINX: Es un registro de 8 bits que guarda el valor de entrada de cada PIN para cada puerto, es decir, podemos comprobar las señales de HIGH y LOW en el momento con este registro.

Vamos a ver un ejemplo con DDRX:

La B que aparece en DDRB es porque pertenece al puerto B que abarca en el ARDUINO UNO de los puertos 8 al 13

Aquí tenemos un registro de 8 bits en el que el penúltimo bit esta puesto en 1 por lo que ese puerto esta establecido como OUTPUT y los demás al tener un 0 como INPUT, y la forma en la que se especifica en código es la que se muestra en la imagen.

Veamos ahora un ejemplo de PORTX:

Al igual que antes la B que aparece en PORTB es porque pertenece al puerto B.

Tenemos de nuevo un registro de 8 bits en el que todos los valores están con un 0 indicando que ese valor es LOW excepto el penúltimo que tiene un 1 indicando que es HIGH, la forma que se indica en el código es como se muestra en la imagen. Ej:

void loop(){

PORTB:B00000010;

}

El PINX no ponemos ejemplo porque se utiliza en el código y se entiende perfectamente.

A continuación les dejamos el código que hemos usado:

//ALGUNAS NOTAS DE PITCHES.H

#define NOTE_B0 31

#define NOTE_C1 33

#define NOTE_CS1 35

#define NOTE_D1 37

#define NOTE_DS1 39

#define NOTE_E1 41

#define NOTE_F1 44

#define NOTE_FS1 46

#define NOTE_G1 49

#define NOTE_GS1 52

#define NOTE_A1 55

#define NOTE_AS1 58

#define NOTE_B1 62

#define NOTE_C2 65

#define NOTE_CS2 69

#define NOTE_D2 73

#define NOTE_DS2 78

#define NOTE_E2 82

#define NOTE_F2 87

#define NOTE_FS2 93

#define NOTE_G2 98

#define NOTE_GS2 104

#define NOTE_A2 110

#define NOTE_AS2 117

#define NOTE_B2 123

#define NOTE_C3 131

#define NOTE_CS3 139

#define NOTE_D3 147

#define NOTE_DS3 156

#define NOTE_E3 165

#define NOTE_F3 175

#define NOTE_FS3 185

#define NOTE_G3 196

#define NOTE_GS3 208

#define NOTE_A3 220

#define NOTE_AS3 233

//DECLARACION DE PUERTOS
int altavoz = 8;
int potenciometro = A2;
unsigned long previousMicros=0;
unsigned long previousMicros2=0;
unsigned long previousMicros3=0;
unsigned long previousMicros4=0;
unsigned long previousMicros5=0;
unsigned long previousMicros6=0;
unsigned long previousMicros7=0;
unsigned long previousMicros8=0;
unsigned long currentMicros;
int button=A1;
int button2=A3;
int button3=A4;
int button4=A5;
int button5=7;
int button6=6;
int button7=5;
int button8=4;
int pedal=2;
int buttonState1, buttonState2, buttonState3 , buttonState4 , buttonState5 , buttonState6 , buttonState7 , buttonState8, pedalState ;
int buttonStatePotenciometro=0;

//NOTAS
int frec1=0;int frec2=0;int frec3=0;int frec4=0;int frec5=0;int frec6=0;int frec7=0;int frec8=0;

String escala=»»;
String sostenido=»»;
bool sos=false;

// CONSTRUCTOR PARA LA PANTALLA LCD 16X2
#include LiquidCrystal lcd(12, 11, 13, 10, 9, 3);

void setup(){
lcd.begin(16, 2);//inicializador pantalla LCD 16×2

pinMode(altavoz,OUTPUT);
pinMode(button5,INPUT);
pinMode(button6,INPUT);
pinMode(button7,INPUT);
pinMode(button8,INPUT);
pinMode(pedal,INPUT);
//DDRD &= !B11110100; //Puerto D7 D6 D5 D4 D2 como INPUT
//DDRB |= B00000001; //Puerto D8 Establecido como OUTPUT

}

void loop(){
pedalState = digitalRead(pedal); //Lee el estado del botón correspondiente al pedal
buttonStatePotenciometro = analogRead(potenciometro); //Lee el valor del potenciometro para saber en que octava estamos
currentMicros=micros(); //Cuenta el tiempo desde que se enciende la placa

//Comprobamos en que octava estamos
//AQUI SE PUEDE CAMBIAR LAS FRECUENCIAS POR LOS #DEFINE QUE ESTAN ARRIBA SI SE QUIERE CAMBIAR LOS SONIDOS
//SOLO HAY QUE SUSTITUIR CADA VARIABLE DE FREC POR LA NOTA QUE SE QUIERE
if(buttonStatePotenciometro>500){
if(pedalState==LOW){
frec1=127;frec2=134;frec3=142;frec4=150;frec5=159;frec6=169;frec7=179;frec8=190;
escala=»grave»;
sostenido=»natural»;
sos=false;
}
else{
frec1=201;frec2=213;frec3=225;frec4=239;frec5=253;frec6=268;frec7=284;frec8=301;
escala=»grave»;
sostenido=»sostenido»;
sos=true;
}
}
else{
if(pedalState==LOW){frec1=1012; frec2=1073;frec3=1136;frec4=1204;frec5=1276;frec6=1351;frec7=1432;frec8=1517;
escala=»aguda»;
sostenido=»natural»;
sos=false;
}
else{
frec1=1607;frec2=1703;frec3=1804;frec4=1911;frec5=2025;frec6=2145;frec7=2272;frec8=2408;
escala=»aguda»;
sostenido=»sostenido»;
sos=true;
}
}

//PRIMER BOTÓN
if(PINC & B000010){ //Comprobamos si el primer boton esta en HIGH directamente desde el registro
//Escribimos la nota en el LCD
lcd.clear();
if(sos==false){
lcd.setCursor(2,0);
lcd.print(«Escala «+escala);
lcd.setCursor(3,1);
lcd.print(«Do «+sostenido);
}else{
lcd.setCursor(2,0);
lcd.print(«Escala «+escala);
lcd.setCursor(3,1);
lcd.print(«Re «+sostenido);
}
//Hacemos que el tono de este botón se ejecute cada 0,002 segundos
if(currentMicros – previousMicros >= 2000){
previousMicros = currentMicros;
tone(altavoz,frec1,20); //Hace sonar la frecuencia de frec1 (cambiar frec1 si se quiere otra nota)
}
}

//SEGUNDO BOTON
if(PINC & B001000){
//Escribimos la nota en el LCD
lcd.clear();
if(sos==false){
lcd.setCursor(2,0);
lcd.print(«Escala «+escala);
lcd.setCursor(3,1);
lcd.print(«Re «+sostenido);
}else{
lcd.setCursor(2,0);
lcd.print(«Escala «+escala);
lcd.setCursor(3,1);
lcd.print(«Mi «+sostenido);
}
if(currentMicros – previousMicros2 >= 1000){ //se ejecuta cada 0,001 segundos
Serial.println(«HOLA2143234»);
//tone(altavoz2,1804,100);
tone(altavoz,frec2,20); //Hace sonar la frecuencia de frec1 (cambiar frec2 si se quiere otra nota)
}
}

//TERCERO BOTON
if(PINC & B010000){
//Escribimos la nota en el LCD
lcd.clear();
if(sos==false){
lcd.setCursor(2,0);
lcd.print(«Escala «+escala);
lcd.setCursor(3,1);
lcd.print(«Mi «+sostenido);
}else{
lcd.setCursor(2,0);
lcd.print(«Escala «+escala);
lcd.setCursor(3,1);
lcd.print(«Fa «+sostenido);
}
if(currentMicros – previousMicros3 >= 1500){ //se ejecuta cada 0,0015 segundos
previousMicros3 = currentMicros;

  tone(altavoz,frec3,20); //Hace sonar la frecuencia de frec1 (cambiar frec3 si se quiere otra nota)
}

}

//CUARTO BOTON
if(PINC & B100000){
  //Escribimos la nota en el LCD
  lcd.clear();
  if(sos==false){
    lcd.setCursor(2,0);
    lcd.print("Escala "+escala);
    lcd.setCursor(3,1);
    lcd.print("Fa "+sostenido);
  }else{
    lcd.setCursor(2,0);
    lcd.print("Escala "+escala);
    lcd.setCursor(3,1);
    lcd.print("Sol "+sostenido);
    }
  if(currentMicros - previousMicros4 >= 2500){ //se ejecuta cada 0,0025 segundos
     previousMicros4 = currentMicros;

    tone(altavoz,frec4,20); //Hace sonar la frecuencia de frec4 (cambiar frec4 si se quiere otra nota)
  }
}

//QUINTO BOTON
if(PIND & B10000000){
  //Escribimos la nota en el LCD
  lcd.clear();
if(sos==false){
  lcd.setCursor(2,0);
  lcd.print("Escala "+escala);
  lcd.setCursor(3,1);
  lcd.print("Sol "+sostenido);
}else{
  lcd.setCursor(2,0);
  lcd.print("Escala "+escala);
  lcd.setCursor(3,1);
  lcd.print("La "+sostenido);
  }
if(currentMicros - previousMicros5 >= 2700){ //se ejecuta cada 0,0027 segundos
   previousMicros5 = currentMicros; 

  tone(altavoz,frec5,20); //Hace sonar la frecuencia de frec5 (cambiar frec5 si se quiere otra nota)
}
}

//SEXTO BOTON
if(PIND & B01000000){
//Escribimos la nota en el LCD
lcd.clear();
if(sos==false){
lcd.setCursor(2,0);
lcd.print(«Escala «+escala);
lcd.setCursor(3,1);
lcd.print(«La «+sostenido);
}else{
lcd.setCursor(2,0);
lcd.print(«Escala «+escala);
lcd.setCursor(3,1);
lcd.print(«Si «+sostenido);
}
if(currentMicros – previousMicros6 >= 1200){ //se ejecuta cada 0,0012 segundos
previousMicros6 = currentMicros;

  tone(altavoz,frec6,20); //Hace sonar la frecuencia de frec6 (cambiar frec6 si se quiere otra nota)
}
}

//SEPTIMO BOTON
if(PIND & B00100000){
  //Escribimos la nota en el LCD
  lcd.clear();
if(sos==false){
  lcd.setCursor(2,0);
  lcd.print("Escala "+escala);
  lcd.setCursor(3,1);
  lcd.print("Si "+sostenido);
}else{
  lcd.setCursor(2,0);
  lcd.print("Escala "+escala);
  lcd.setCursor(3,1);
  lcd.print("Do #");
  }
if(currentMicros - previousMicros7 >= 1700){ //se ejecuta cada 0,0017 segundos
   previousMicros7 = currentMicros;
  tone(altavoz,frec7,20); //Hace sonar la frecuencia de frec7 (cambiar frec7 si se quiere otra nota)
}
}

//OCTAVO BOTON
if(PIND & B00010000){
  //Escribimos la nota en el LCD
  lcd.clear();
if(sos==false){
  lcd.setCursor(2,0);
  lcd.print("Escala "+escala);
  lcd.setCursor(3,1);
  lcd.print("Do sostenido");
}else{
  lcd.setCursor(2,0);
  lcd.print("Escala "+escala);
  lcd.setCursor(3,1);
  lcd.print("Re #");
  }
if(currentMicros - previousMicros8 >= 900){ //se ejecuta cada 0,0009 segundos
   previousMicros8 = currentMicros;
  tone(altavoz,frec8,20); //Hace sonar la frecuencia de frec8 (cambiar frec8 si se quiere otra nota)
}
}

}

Como decía antes, ARDUINO es secuencial y no podemos tocar varias notas a la vez, teniendo el código a mano puedo explicar como hemos conseguido que se ejecuten no 2 sonidos a la vez si no varios sonidos a la vez. Contamos con la función micros() que es como un contador que se inicia cuando la placa le llega corriente, por lo que vamos a hacer que cada tecla de nuestro proyecto se ejecute cada x momento, de tal manera que por ejemplo un sonido se escucha cada 0.001 s y el siguiente cada 0.003 y aunque realmente no se ejecuten a la vez, se puede engañar al oído con estos tiempos tan pequeños y que parezca que suenen simultáneamente como si estuviéramos tocando un piano de verdad.

Casos de Uso

  • Potenciómetro por debajo de 500 escala grave
  • Potenciómetro por encima de 500 escala aguda
  • En cada escala y al pulsar la tecla 9 se accede a los sostenidos ( la 8 ya es sostenido de por si y al pulsarla junto a la tecla 9 marca la nota y # que es lo siguiente a sostenido)
  • Poder pulsar dos o mas teclas a la vez gracias a que accedemos directamente a los pines y podemos controlar mejor los tiempos haciendo que parezca que suenan a la vez

Problemas encontrados y soluciones

El principal ha sido poder tocar varias teclas a la vez , ya que no se puede o es muy difícil el uso de threads para poder ejecutar varios sonidos a la vez, ademas de que se necesitaría otro altavoz para poder acceder en la sección critica a los dos recursos a la vez. Por ello, buscamos información de como controlar los tiempos para que cuando no se esta ejecutando una acción lo haga otra. Encontramos que se podían acceder directamente a los puertos mediante el controlador Artmega 328p. Esto permite acceder a los registros de los puertos y así saltarnos los digitalWrites y reads que no hacen más que ejecutar una rutina interna que consume más milisegundos.

Otros problemas:

  • Soldar los botones, estaban hechos en parte de plástico, en una zona muy cercana a las patillas de contacto. Al intentar fundir el estaño se deshacía el botón. Para solucionarlo, usamos un cable encima de la tecla y otro cable conectado a otra chincheta en la caja donde va a pulsar la tecla. Al bajar la tecla , el cable hace contacto con la chincheta simulando un botón y encima es bastante más barato
  • Los cables, demasiados cables y difícil de maniobrar por la condición de la caja del piano, teníamos que ir con pinzas.
  • Pitidos sin que tocásemos los botones( video continuación ) . La solución fue empezar de cero

https://youtube.com/shorts/PqGeQRUKwxs?feature=share

  • La alineación de las teclas

Reparto de tareas

Los dos( David y Raúl) hemos hecho todas las partes , participando ambos en cada una. No hemos hecho una separación de tareas. Quedábamos para hacerlo de manera presencial y estábamos pendiente del mismo problema.

Video con todo el proyecto

También te podría gustar...

1 respuesta

  1. ANCOCEN dice:

    ¿PORQUE NO ME DEJA USAR EL CODIGO

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *