Reproducción de melodía con buzzer pasivo


Hola de nuevo. Después de tanto tiempo he vuelto a tener la osadía de asomar la cabeza por el blog, como prometí en su momento en la cuenta de twitter.

Todos estos días he estado ocupado –distraído, mejor dicho– con temas de universidad, trabajo, etcétera etcétera. Pero me complace mucho volver con la noticia de que estreno nueva sección. Como anuncié en su momento, adquirí por internet un pack de “aprendizaje” con la placa Arduino UNO. Pues bien, ha llovido desde entonces. Lo que parecía un caprichito pasajero se ha convertido en una inmersión total en el mundo de electrónica. Condensadores, resistencias, integrados, transistores, diodos, etc. Todo eso y más son las cosas que he comprado, víctima de mi nueva enfermedad: la adicción a la electrónica.

Prometí una serie de sketches de Arduino bastante simples, que en su momento pensaba que eran… ¿como decirlo? ¿Currados? No sé.

El caso es que desde aquel post he realizado gran cantidad de proyectos diferentes y de distintas magnitudes, por supuesto no tan simples como estos primeros. Es más, no únicamente he trabajado con Arduino. También trabajé con las placas MSP430 de Texas Instruments, Intel Galileo, Waspmote de Libelium, etc. Así que obviamente no publicaré los proyectos simples. Aunque de esa primera partida inicial sí que hay un proyecto que me gustaría compartir con vosotros. No sé si es por que estoy orgulloso de ello o porque al estar íntimamente relacionado con mi infancia Nintendero-gamer me despierta gran simpatía. El caso es que este podría decirse que fue, de los primeros, el proyecto más completo que hice. Todo en un único fichero .ino, pero empleando funciones y con una estructura plenamente desarrollada por mí. Y con un trabajillo de investigación detrás.

Os hablo de la reproducción de una melodía con un buzzer pasivo. La melodía en concreto es la de Pueblo Paleta de las versiones Amarillo, Rojo y Azul de Pokémon. Qué nostalgia.

Como muchos de vosotros sabréis, el buzzer pasivo consiste literalmente en un zumbador que, al recibir cierto nivel de tensión, emite un pitido. En este caso funciona bajo 5V, como la mayoría de accesorios que se conectan a Arduino. Pero, ¿cómo emitir melodías si este zumbador únicamente emite un pitido? La respuesta es sencilla: con simplemente producir el pitido a una frecuencia determinada es posible emitir una nota u otra.

Aquí entra el trabajo de investigación.

En primer lugar se investigan las diferentes notas existentes en la escala musical (se va a trabajar en clave de sol). Googleando un poco, se encuentran tablas como la que se muestra a continuación.

notasyfrecsbg

Basta con recordar el valor de frecuencia que corresponde a cada nota para poder reproducir la melodía.

Junto con esto, se busca una partitura con la melodía de Pallet Town, en este caso, para poder conocer todas las notas que la componen. Además se deberán tener en cuenta los silencios entre cada una de las notas.

Un error de novato que tuve al realizar el código fue emplear PWM para modular las notas. Mi intención era variar el ciclo de trabajo para lograr la diferente escala. Pero cuál fue mi sorpresa al probar el código y ver que lo único que variaba era la intensidad de las notas. IMPORTANTE: PWM varía el ciclo de trabajo, es decir, el porcentaje de tiempo de un determinado tiempo fijo que la señal se mantiene en alto. En ningún momento varía la frecuencia. De los errores se aprende, y esto mismo aprendí yo al intentar hacerlo de este modo.

Una vez solucionado esto, se puede ver que se hace necesaria la creación de una función encargada de generar la “vibración”. Para ello se ha diseñado la función reproducirNotas que podéis ver en el código a continuación. Esta función implementa el proceso de vibración. Únicamente se ha de especificar la nota a reproducir (mediante un índice), la duración de la misma y la duración del silencio que la sigue (que de no existir puede ser 0 perfectamente). De esta forma, el formato de la partitura ahora se adapta a un formato de conjuntos nota+silencio que se van sucediendo en el tiempo. Alternando de la forma adecuada es posible lograr con exactitud la melodía que se indica en la partitura.

Y bueno, no tengo mucho más que añadir. Aquí a continuación os dejo el código comentado. Podéis probar con la melodía que os guste, simplemente tenéis que extraer de la partitura la secuencia de pares nota/silencio. Espero que os resulte interesante. Me parece que llegó a ser mi tercer proyecto con Arduino, con lo cual no está nada mal. Aunque hay que dar parte del mérito a Mr.Sandman, que ayudó en el proceso de búsqueda de información y estuvo ahí para supervisar el proceso mecánico de la traducción. Pasaos por su blog, también tiene algunas cosillas muy interesantes.

Código:

int frecuencias[] = {   262,277,294,311,330,349,370,392,415,440,466,494,523,554,587,   622,659,698,740,784,831,880,932,988}; //Array de frecuencias equivalentes a las notas://DO,#DO,RE,#RE,MI,FA,#FA,SOL,#SOL,LA,#LA,SI,DO2,#DO2,RE2,#RE2,//MI2,FA2,#FA2,SOL2,#SOL2,LA2,#LA2,SI2  void setup() {   pinMode(4, OUTPUT); //Determinación del pin 4 como salida (autor del sonido en sí)}   void reproducirNotas(int i,float figura,float silencio){  //i: indice del array de frecuencias; figura: factor a multiplicar según la figura (negra, blanca, corchea...) //silencio: factor a multiplicar según el silencio long j = 0; //indice no entero long semiperiodo = 1000000/(frecuencias[i])/2; //obtención del semiperiodo (en microsegundos, de ahi el 1000000) como mitad del periodo (1/frec) long numCiclos = frecuencias[i]*(500*figura)/1000; //obtencion del numero de ciclos (500 es el tiempo al que se le aplica el factor de figura) while(j<numCiclos){    digitalWrite(4,HIGH); //ciclo en alto durante primer semiperiodo   delayMicroseconds(semiperiodo);    digitalWrite(4,LOW); //ciclo en alto durante segundo semiperiodo   delayMicroseconds(semiperiodo);    j++;  }  silencio=silencio*500; //500 tiempo al que se le aplica el factor de silencio if(silencio>0){ //si existe silencio, aplicarlo   delay(silencio);  } }   void loop() {   //secuencia obtenida para PalletTown  reproducirNotas(14,0.25,0.25);   reproducirNotas(12,0.25,0.25);   reproducirNotas(11,0.25,0.25);   reproducirNotas(9,0.25,0.25);   reproducirNotas(19,0.25,0.25);   reproducirNotas(16,0.25,0.25);   reproducirNotas(18,0.25,0.25);   reproducirNotas(16,0.25,0.25);   reproducirNotas(14,1.25,0.5);   reproducirNotas(11,0.25,0.25);   reproducirNotas(7,0.25,0.25);   reproducirNotas(7,0.25,0.25);   reproducirNotas(9,0.25,0.25);   reproducirNotas(11,0.25,0.25);   reproducirNotas(12,1.75,0.75);   reproducirNotas(6,0.25,0.25);   reproducirNotas(7,0.25,0.25);   reproducirNotas(9,0.25,0.25);   reproducirNotas(11,1.25,0.25);   reproducirNotas(12,0.25,0);   reproducirNotas(11,0.25,0);   reproducirNotas(9,1.75,0.25);   reproducirNotas(14,0.25,0.25);   reproducirNotas(12,0.25,0.25);   reproducirNotas(11,0.25,0.25);   reproducirNotas(14,0.25,0.25);   reproducirNotas(19,0.25,0.25);   reproducirNotas(18,0.25,0.25);   reproducirNotas(17,0.25,0.25);   reproducirNotas(19,0.25,0.25);   reproducirNotas(16,1.25,0.25);   reproducirNotas(14,2.25,0.25);   reproducirNotas(12,0.25,0.25);   reproducirNotas(11,0.25,0.25);   reproducirNotas(9,0.25,0.25);   reproducirNotas(7,0.25,0.25);   reproducirNotas(14,0.25,0.25);   reproducirNotas(12,0.25,0.25);   reproducirNotas(11,0.25,0.25);   reproducirNotas(9,0.25,0.25);   reproducirNotas(7,1.75,0.75);   reproducirNotas(7,0.25,0.25);   reproducirNotas(9,0.25,0.25);   reproducirNotas(11,0.25,0.25);   reproducirNotas(12,1.75,0.25);   reproducirNotas(14,1.25,0.25);   reproducirNotas(12,0.25,0.25);   reproducirNotas(11,1.75,0.75);   reproducirNotas(7,0.25,0.25);   reproducirNotas(9,0.25,0.25);   reproducirNotas(11,0.25,0.25);   reproducirNotas(12,0.75,0.25);   reproducirNotas(12,0.75,0.25);   reproducirNotas(14,1.25,0.25);   reproducirNotas(12,0.25,0);   reproducirNotas(14,0.25,0);   reproducirNotas(11,1.75,0.75);   reproducirNotas(11,0.25,0.25);   reproducirNotas(9,0.25,0.25);   reproducirNotas(7,0.25,0.25);   reproducirNotas(9,1.75,0.25);   reproducirNotas(4,0.75,0.25);   reproducirNotas(11,0.75,0.25);   reproducirNotas(9,1.75,0.25);   reproducirNotas(7,0.75,0.25);   reproducirNotas(4,0.75,0.25);   reproducirNotas(6,1.75,0.25);   reproducirNotas(7,0.75,0.25);   reproducirNotas(11,0.75,0.25);   reproducirNotas(11,1.75,0.25);   reproducirNotas(9,1.75,0.25);   delay(250); //250 ms entre reproducciones}
Y bueno, os paso también una imagen de cómo realizar el conexionado para que el código os funcione a la primera. Aunque en este caso no sea de una complejidad elevada, pero bueno… Nunca está de más documentar un poco.

PalletTownBuzzer_bb

La verdad es que tengo que reconocer que me encanta formar parte de esta comunidad opensource de Arduino. Y me complace mucho compartir este código (y los otros muchos que espero publicar) con vosotros, porque igualmente agradezco mucho que exista gran cantidad de código abierto publicado en internet que a mí me puede servir de gran utilidad. Ojalá todas las compañías emplearan esta estrategia porque es la mejor forma, con diferencia, de crear conocimiento colectivo en esta red tan masiva que es internet.

Pero bueno, sermones a parte, me encanta haber compartido esto con vosotros por si os sirve de ayuda. Siento no haber comentado mucho el código en el post, pero el propio código cuenta con mis comentarios, que creo que son suficientes. De todas formas, si tuvierais alguna duda más sobre el tema me la podéis hacer llegar a wemento@gmail.com, a la cuenta de Twitter o escribirlo en este mismo post en un comentario. Intentaré responder con la mayor brevedad y precisión posibles.

Espero que os guste. Yo, personalmente, estoy encantado de comenzar esta nueva sección del blog.

Salu2.

Comentarios