Reproducir una melodia
Este ejemplo hace uso un altavoz Piezoeléctrico para reproducir melodías. Aprovechamos la capacidad de los procesadores para producir señales PWM para reproducir música. Hay más información acerca de como funciona PWM escrita por David Cuartielles aqui e incluso en el antiguo curso de K3
Un Piezoeléctrico no es más que un dispositivo que puede ser usado para reproducir tonos y para detectarlos. En nuestro ejemplo conectamos el Piezoeléctrico al pin número 9, que soporta la funcionalidad de escribir señales PWM, y no simplemente un valor HIGH o LOW.
El primer ejemplo envía una onda cuadrada al Piezoeléctrico, mientras que el segundo ejemplo hará uso de la funcionalidad de PWM para controlar el volumen a través del cambio de la anchura del pulso.
Por otro lado recordar que los Piezoeléctricos tienen polaridad, normalmente los dispositivos comerciales tienen un cable rojo y otro negro indicando como conectarlo al circuito. Conectamos el negro a la masa y el rojo a la salida. Algunas veces es posible obtener elementos Piezoeléctricos sin la carcasa de plástico, estos serán parecidos a un disco metálico.
Ejemplo de conexión de un piezoeléctrioco al pin 9
Ejemplo 1: Reproducir una melodía
/* Play Melody
*-----------
*
* Programa para reproducir una simple melodía
*
*
* Los tonos son creados por rápidos pulsos de intervalos on y off
* al atavoz usando PWM, para crear la marca de frecuencias
*
*
* Cada nota tiene una frecuencia, creada variando el periodo
* de la vibración, medida en microsegundos. Utilizaremos la modulación de
* la anchura del pulso (PWM) para crear esa vibración.
*
* Calculamos la anchura del pulso para que sea la mitad del período;
* establecemos el altavoz a HIGH durante 'longitud del pulso' microsegundos,
* entonces establecemos a LOW 'longitud del pulso' microsegundos.
* Esto crea una vibración de la frecuencia deseada
*
* (cleft) 2005 D. Cuartielles for K3
* Refactoring and comments 2006 clay.shirky@nyu.edu
* See NOTES in comments at end for possible improvements
*/
// TONES ==========================================
// comenzamos definiendo la relación entre
// nota, periodo y frecuencia
#define c 3830 // 261 Hz
#define d 3400 // 294 Hz
#define e 3038 // 329 Hz
#define f 2864 // 349 Hz
#define g 2550 // 392 Hz
#define a 2272 // 440 Hz
#define b 2028 // 493 Hz
#define C 1912 // 523 Hz
// Definimos una nota especial 'R', para representar reposo
#define R 0
// SETUP ============================================
// Instale el altavoz en un pin PWM (digital 9, 10 or 11)
int speakerOut = 9;
// ¿Quieres depurar en la salida serie? 1 para si, 0 para no
int DEBUG = 1;
void setup() {
pinMode(speakerOut, OUTPUT);
if (DEBUG) {
Serial.begin(9600); // Activa la salida serie si quieres depurar
}
}
// MELODY and TIMING =======================================
// melody[] es un array de notas, acompañado por beats[],
// el cual establece la longitud relativa de cada nota
int melody[] = { C, b, g, C, b, e, R, C, c, g, a, C };
int beats[] = { 16, 16, 16, 8, 8, 16, 32, 16, 16, 16, 8, 8 };
int MAX_COUNT = sizeof(melody) / 2; // Longitud de la melodia para el bucle (loop)
// Establece el tiempo total
long tempo = 10000;
// Establece la longitud de la pausa entre notas
int pause = 1000;
// Variable del bucle (loop) para incremento de la longitud del reposo
int rest_count = 100; //<-BLETCHEROUS HACK; See NOTES
// Inicializa las variable principales
int tone = 0;
int beat = 0;
long duration = 0;
// PLAY TONE ==============================================
// Activa el altavoz para reproducir un tono de una duración particular
void playTone() {
long elapsed_time = 0;
if (tone > 0) { //Si no es un reposo (R), mientras el tono
//tiene que reproducirse (menos tiempo que 'duration'),
//establecemos el altavoz a HIGH y LOW
while (elapsed_time < duration) {
digitalWrite(speakerOut,HIGH);
delayMicroseconds(tone / 2);
// DOWN
digitalWrite(speakerOut, LOW);
delayMicroseconds(tone / 2);
// Controlamos cuanto tiempo estuvo activo
elapsed_time += (tone);
}
}
else { // Señal de reposo (R); bucle de tiempo
for (int j = 0; j < rest_count; j++) { // Ver nota en rest_count
delayMicroseconds(duration);
}
}
}
// LET THE WILD RUMPUS BEGIN =============================
void loop() {
// Set up a counter to pull from melody[] and beats[]
for (int i=0; i<MAX_COUNT; i++) {
tone = melody[i];
beat = beats[i];
duration = beat * tempo; //Establecemos el tiempo
playTone();
// Una pausa entre notas ...
delayMicroseconds(pause);
if (DEBUG) { // Si depuración, reportamos loop, tone, beat y duration
Serial.print(i);
Serial.print(":");
Serial.print(beat);
Serial.print(" ");
Serial.print(tone);
Serial.print(" ");
Serial.println(duration);
}
}
}
/*
* NOTAS
* El programa propone mantener un tono durante 'duration' microsegundos.
* Menuda mentira! Lo mantiene por lo menos 'duration' microsegundos,
* cualquier consumo de tiempo extra ocurrido por incremantar elapsed_time
* (podría
* ser mayor de 3K microsegundos) añade duración extra a loop
* y a las dos digitalWrites()
*
* As a result, a tone of 'duration' plays much more slowly than a rest
* of 'duration.' rest_count creates a loop variable to bring 'rest'
* beats in line with 'tone' beats of the same length.
*
* Como resultado, un tono de 'duration' sonará mucho más lento
* que un reposo de 'duration'. rest_count crea una variable loop
* para almacenar 'rest' pulsos de acuerdo con los pulsos 'tone'
* de la misma anchura.
*
* rest_count será afectado por la arquitectura y la velocidad del chip,
* asi como por cualquier operación del programa. Comportamiento pasados
* no garantizan funcionamientos futuros.
*
*
*
* Podría probar estas cosas:
*
*
* Añadir código para permitir al programador especificar cuantas veces debe
* repetirse la melodia antes de parar.
*
* Añadir otra actava
*
* Mover tempo, pause y rest_count a las declaraciones del #define
*
* Reescribir para incluir control de volumen, usando analogWrite,
* como en el segundo programa http://www.arduino.cc/en/Tutorial/PlayMelody
*
* Añadir código para poder establecer tempo por potenciómetro
* u otro dispositivo de entrada
*
* Añadir código para establecer tempo o volumen por comunicación serie
* (Requiere 0005 o superior)
*
* Agregar código para crear la compensación de tono
* (el más alto o el más bajo) a
* través del potenciómetro etc.
*
* Reemplazar la melodia al azar
*/
Segunda versión, con control de volumen usando analogWrite()
/* Play Melody
* -----------
*
*
* Programa para reproducir melodias almacenadas en un array, requiere
* conocimientos sobre sincronización y reproducción de tonos
*
*
* El cálculo de los tonos se hace siguiendo la operación matemática:
*
* timeHigh = 1/(2 * toneFrequency) = period / 2
*
*
* donde los diferentes tonos están descritos en la tabla:
*
* note frequency period PW (timeHigh)
* c 261 Hz 3830 1915
* d 294 Hz 3400 1700
* e 329 Hz 3038 1519
* f 349 Hz 2864 1432
* g 392 Hz 2550 1275
* a 440 Hz 2272 1136
* b 493 Hz 2028 1014
* C 523 Hz 1912 956
*
* (cleft) 2005 D. Cuartielles for K3
*/
int ledPin = 13;
int speakerOut = 9;
byte names[] = {'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C'};
int tones[] = {1915, 1700, 1519, 1432, 1275, 1136, 1014, 956};
byte melody[] = "2d2a1f2c2d2a2d2c2f2d2a2c2d2a1f2c2d2a2a2g2p8p8p8p";
// count length: 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
// 10 20 30
int count = 0;
int count2 = 0;
int count3 = 0;
int MAX_COUNT = 24;
int statePin = LOW;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
analogWrite(speakerOut, 0);
for (count = 0; count < MAX_COUNT; count++) {
statePin = !statePin;
digitalWrite(ledPin, statePin);
for (count3 = 0; count3 <= (melody[count*2] - 48) * 30; count3++) {
for (count2=0;count2<8;count2++) {
if (names[count2] == melody[count*2 + 1]) {
analogWrite(speakerOut,500);
delayMicroseconds(tones[count2]);
analogWrite(speakerOut, 0);
delayMicroseconds(tones[count2]);
}
if (melody[count*2 + 1] == 'p') {
// make a pause of a certain size
analogWrite(speakerOut, 0);
delayMicroseconds(500);
}
}
}
}
}