Música guardada en tarjeta MicroSD
Se trata de reproducir un archivo de música guardado en una tarjeta MicroSD. El altavoz es de 8 Ohms y también podría utilizarse un pequeño amplificador LM386.
El tarjetero para la MicroSD tiene 6 conexiones, dos para la alimentación a 5 voltios y 4 de señal, CS (El pin de Arduino al que está conectado es el que se debe definir en el programa), SCK, MOSI y MISO. Si se utiliza la librería SD.h el pin 11 está conectado a MOSI, el 12 a MISO y el 13 a SCK. En este ejemplo CS está conectado al pin 10, pero se puede variar esta conexión poniéndolo en otro pin.
Una vez montado el circuito y antes de programar, se debe preparar la tarjeta MicroSD y el archivo de audio que se pretende reproducir en el altavoz.
La tarjeta MicroSD se ha de formatear con un Sistema de Archivos FAT o FAT32.
El archivo de audio conviene que tenga una duración menor de un minuto ya sea en formato .mp3 o .wav. Se pueden encontrar aquí archivos mp3.
Para preparar el archivo de audio antes de cargarlo en la tarjeta MicroSD se debe asegurar que algunos de sus parámetros son los correctos. Para ello se puede utilizar el programa Wav Sample Rate Converter, Audacity o similar (COOL EDIT PRO), el cual permite convertir el archivo .mp3 (si es que esa es su extensión) en un archivo .wav o, si ya es un archivo .wav, permite modificarle ciertas características importantes.
En el caso del Wav Sample Rate Converter hacemos click en el botón Open Media File y definimos el Wave Format con estas características:
Samples per second (Hz): Custom define 16000
Channels: Mono
Bits per sample: 8
Se puede usar el botón Browse para elegir el nombre del nuevo archivo y la carpeta en la que se guardará. Haciendo click en Convert el archivo estará ya convertido y listo para guardar en la tarjeta MicroSD para después reproducirlo con Arduino.
A continuación se muestra el programa del IDE de Arduino que utiliza la librería TMRpcm que se habrá de descargar e instalar en el IDE de Arduino.
#include <SD.h>#include <SPI.h>#include <TMRpcm.h>#define pinSD 10 //define el pin para seleccionar la tarjeta SDTMRpcm tmrpcm; //Se crea un objeto de la librería TMRpcmvoid setup(){tmrpcm.speakerPin = 9; //Se define el pin en el que está conectado el altavozSerial.begin(9600); //Se inicia la comunicación serieif (!SD.begin(pinSD)) { //Si no detecta la tarjetaSerial.println("Fallo en la tarjeta SD"); //Aviso de que algo no anda bienreturn; //No hacer nada si no se pudo leer la tarjeta}}void loop(){Serial.println("HOLA MUNDO!!!!"); //Imprime algo :)/*Usa la función .play para reproducir el archivo.wav que hayas guardado en tu tarjeta SD*/tmrpcm.play("holamund.wav");delay(30000); //Espera medio minuto para volver a reproducirlo}Trabajando con el programa COOL EDIT PRO
El sonido que queremos reproducir en Arduino lo podemos obtener de otro archivo, para llevarlo al programa podemos hacer copiar y pegar, o incluso podemos grabarlo mediante el micrófono. Lo importante es configurar el formato del archivo final.
Una vez elegidas estas opciones pegamos o grabamos el sonido deseado.
Guardamos los cambios, eligiendo el formato Windows PCM - WAV.
El siguiente montaje utiliza un amplificador de audio LM386.
Ejecutar contenido RTTTL desde el IDE de Arduino
Trabajando desde el IDE de Arduino y utilizando la librería PlayRtttl.hpp podemos reproducir una melodía guardada en la matriz StarWarsInRam utilizando el siguiente programa.
#include <Arduino.h>
#include <PlayRtttl.hpp>
const int TONE_PIN = 9;char StarWarsInRam[] ="StarWars:d=32,o=5,b=45,l=2,s=N:p,f#,f#,f#,8b.,8f#.6,e6,d#6,c#6,8b.6,16f#.6,e6,d#6,c#6,8b.6,16f#.6,e6,d#6,e6,8c#6";void setup() {}void loop() {playRtttlBlocking(TONE_PIN, StarWarsInRam);delay(10000);}
Si en lugar de una matriz disponemos de la variable de texto melodia podemos cargar su contenido en la matriz melodia1, que es la que se ejecutará posteriormente en playRtttlBlocking. El programa que lo hace posible lo podemos ver a continuación de la siguiente manera.
#include <Arduino.h>#include <PlayRtttl.hpp>const int TONE_PIN = 9;String melodia = "StarWars:d=32,o=5,b=45,l=2,s=N:p,f#,f#,f#,8b.,8f#.6,e6,d#6,c#6,8b.6,16f#.6,e6,d#6,c#6,8b.6,16f#.6,e6,d#6,e6,8c#6";char melodia1[] = "";void setup() {int cant_caract = melodia.length() + 1;char char_array[cant_caract];melodia.toCharArray(melodia1, cant_caract);}void loop() {playRtttlBlocking(TONE_PIN, melodia1);delay(10000);}
Este programa devuelve por la Consola la longitud del archivo inicio.txt. Primeramente el valor lo guarda en la variable largo.
File myFile;
int largo;void setup(){Serial.begin(9600);Serial.print("Iniciando SD ...");if (!SD.begin(10)) {Serial.println("No se pudo inicializar");return;}Serial.println("inicializacion exitosa");myFile = SD.open("inicio.txt");//abrimos el archivoif (myFile) {Serial.println("inicio.txt:");largo = (myFile.size());Serial.println(largo);myFile.close(); //cerramos el archivo} else {Serial.println("Error al abrir el archivo");}}void loop(){}
Este programa envía el contenido del archivo inicio.txt por la Consola del puerto serie.
#include <SD.h>
File myFile;void setup(){Serial.begin(9600);Serial.print("Iniciando SD ...");if (!SD.begin(10)) {Serial.println("No se pudo inicializar");return;}Serial.println("inicializacion exitosa");myFile = SD.open("inicio.txt");//abrimos el archivoif (myFile) {Serial.println("inicio.txt:");while (myFile.available()) {Serial.write(myFile.read());}myFile.close(); //cerramos el archivo} else {Serial.println("Error al abrir el archivo");}}void loop(){}
Este otro programa hace lo mismo que el anterior pero de otra manera.
#include <SD.h>#include <SPI.h>#include <SD.h>File myFile;String linea = "";void setup() {Serial.begin(9600);if (!SD.begin(10)) {Serial.println("No se pudo inicializar la tarjeta SD");return;}Serial.println("Tarjeta SD lista.");myFile = SD.open("inicio.txt");if (myFile) {Serial.println("Contenido de datos.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir datos.txt");}}void loop() {}
El siguiente programa permite guardar el contenido de la variable de tipo File archivo1, que se ha obtenido del archivo inicio.txt de la tarjeta MicroSD, en la variable de texto Contenido. Es esta variable de texto la que se muestra en la Consola del monitor serie y posteriormente en el loop cada 10 segundos.
#include <SD.h>String Contenido = "";String textodelarchivo(File archivos) {while (archivos.available()) {Contenido += (char)archivos.read();}return Contenido;}void setup() {Serial.begin(9600);while (!Serial) { // Espere a que el puerto serie se conecte. Necesario para USB nativo.}if (!SD.begin(10)) {Serial.println("No se pudo inicializar");return;}File archivo1 = SD.open("inicio.txt", FILE_READ);if (archivo1) {String Contenido = textodelarchivo(archivo1);Serial.println(Contenido);archivo1.close();} else {Serial.println("Error abriendo el archivo");}}void loop() {Serial.println(Contenido);delay(10000);}
Este programa lee el contenido del archivo inicio.txt, lo introduce en la variable de texto linea y posteriormente en la matriz melodia1. Con el contenido de esta matriz se ejecuta en playRtttlBlocking para generar en el altavoz conectado al pin 9 de Arduino la melodía que guardaba el archivo de la tarjeta Micro SD en formato RTTTL.
#include <SPI.h>#include <SD.h>#include <PlayRtttl.hpp>const int TONE_PIN = 9;int cant_caract;File myFile;String linea = "";char melodia1[] = "";void setup() {Serial.begin(9600);if (!SD.begin(10)) {Serial.println("No se pudo inicializar la tarjeta SD");return;}Serial.println("Tarjeta SD lista.");myFile = SD.open("inicio.txt");if (myFile) {Serial.println("Contenido de datos.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir datos.txt");}}void loop() {cant_caract = linea.length() + 1;char char_array[cant_caract];linea.toCharArray(melodia1, cant_caract);Serial.println(melodia1);playRtttlBlocking(TONE_PIN, melodia1);delay(10000);}
Con el mismo programa estructurado de esta otra manera también funciona.
#include <SPI.h>#include <SD.h>#include <PlayRtttl.hpp>const int TONE_PIN = 9;int cant_caract;File myFile;String linea = "";char melodia1[] = "";void setup() {Serial.begin(9600);if (!SD.begin(10)) {Serial.println("No se pudo inicializar la tarjeta SD");return;}Serial.println("Tarjeta SD lista.");myFile = SD.open("inicio.txt");if (myFile) {Serial.println("Contenido de datos.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}cant_caract = linea.length() + 1;char char_array[cant_caract];linea.toCharArray(melodia1, cant_caract);Serial.println(melodia1);myFile.close();} else {Serial.println("No se pudo abrir datos.txt");}}void loop() {playRtttlBlocking(TONE_PIN, melodia1);delay(10000);}
Ahora otra versión pero con todo el código de lectura del archivo en el loop.
#include <SPI.h>#include <SD.h>#include <PlayRtttl.hpp>const int TONE_PIN = 9;int cant_caract;File myFile;String linea = "";char melodia1[] = "";void setup() {Serial.begin(9600);if (!SD.begin(10)) {Serial.println("No se pudo inicializar la tarjeta SD");return;}Serial.println("Tarjeta SD lista.");}void loop() {myFile = SD.open("inicio.txt");if (myFile) {Serial.println("Contenido de datos.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}cant_caract = linea.length() + 1;char char_array[cant_caract];linea.toCharArray(melodia1, cant_caract);myFile.close();} else {Serial.println("No se pudo abrir datos.txt");}playRtttlBlocking(TONE_PIN, melodia1);delay(10000);}
Con esta misma idea, para dos archivos de texto, inicio.txt y piso.txt podemos utilizar el siguiente programa. En el loop se ejecutan sucesivamente las dos melodías.
#include <SD.h>#include <PlayRtttl.hpp>const int TONE_PIN = 9;File myFile;String linea = "";int cant_caract;char melodia1[] = "";char melodia2[] = "";void setup() {Serial.begin(9600);if (!SD.begin(10)) {Serial.println("No se pudo inicializar la tarjeta SD");return;}Serial.println("Tarjeta SD lista.");}void loop() {myFile = SD.open("inicio.txt");if (myFile) {Serial.println("Contenido de inicio.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir inicio.txt");}cant_caract = linea.length() + 1;char char_array[cant_caract];linea.toCharArray(melodia1, cant_caract);playRtttlBlocking(TONE_PIN, melodia1);delay(10000);myFile = SD.open("piso.txt");if (myFile) {Serial.println("Contenido de piso.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir piso.txt");}cant_caract = linea.length() + 1;linea.toCharArray(melodia2, cant_caract);playRtttlBlocking(TONE_PIN, melodia2);delay(10000);}
Sobre la base del programa anterior se leen tres archivos, inicio.txt, piso.txt y llegada.txt y se reproducen sus melodías.
#include <SD.h>#include <PlayRtttl.hpp>const int TONE_PIN = 9;File myFile;String linea = "";int cant_caract;char melodia1[] = "";char melodia2[] = "";char melodia3[] = "";void setup() {Serial.begin(9600);if (!SD.begin(10)) {Serial.println("No se pudo inicializar la tarjeta SD");return;}Serial.println("Tarjeta SD lista.");}void loop() {myFile = SD.open("inicio.txt");if (myFile) {Serial.println("Contenido de inicio.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir inicio.txt");}cant_caract = linea.length() + 1;char char_array[cant_caract];linea.toCharArray(melodia1, cant_caract);playRtttlBlocking(TONE_PIN, melodia1);delay(10000);myFile = SD.open("piso.txt");if (myFile) {Serial.println("Contenido de piso.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir piso.txt");}cant_caract = linea.length() + 1;linea.toCharArray(melodia2, cant_caract);playRtttlBlocking(TONE_PIN, melodia2);delay(10000);myFile = SD.open("llegada.txt");if (myFile) {Serial.println("Contenido de llegada.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir piso.txt");}cant_caract = linea.length() + 1;linea.toCharArray(melodia3, cant_caract);playRtttlBlocking(TONE_PIN, melodia3);delay(10000);}
En el siguiente programa la lectura de los diferentes archivos y la ejecución de la melodía correspondiente se hace mediante tres funciones nuevas, void inicio(), void piso() y void() llegada. Cada una de estas funciones es llamada desde el loop en el momento conveniente.
#include <SD.h>#include <PlayRtttl.hpp>const int TONE_PIN = 9;File myFile;String linea = "";int cant_caract;char melodia1[] = "";char melodia2[] = "";char melodia3[] = "";void setup() {Serial.begin(9600);if (!SD.begin(10)) {Serial.println("No se pudo inicializar la tarjeta SD");return;}Serial.println("Tarjeta SD lista.");}void inicio(){myFile = SD.open("inicio.txt");if (myFile) {Serial.println("Contenido de inicio.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir inicio.txt");}cant_caract = linea.length() + 1;//char char_array[cant_caract];linea.toCharArray(melodia1, cant_caract);playRtttlBlocking(TONE_PIN, melodia1);delay(10000);}void piso(){myFile = SD.open("piso.txt");if (myFile) {Serial.println("Contenido de piso.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir piso.txt");}cant_caract = linea.length() + 1;linea.toCharArray(melodia2, cant_caract);playRtttlBlocking(TONE_PIN, melodia2);delay(10000);}void llegada(){myFile = SD.open("llegada.txt");if (myFile) {Serial.println("Contenido de llegada.txt:");while (myFile.available()) {linea = myFile.readStringUntil('\n');Serial.println(linea);}myFile.close();} else {Serial.println("No se pudo abrir piso.txt");}cant_caract = linea.length() + 1;linea.toCharArray(melodia3, cant_caract);playRtttlBlocking(TONE_PIN, melodia3);delay(10000);}void loop() {inicio();piso();llegada();}
Programa estable, a diferencia de los anteriores, para leer el contenido de archivos .txt
El siguiente programa tiene tres bloques en el setup que leen el contenido de tres archivos .txt y lo guardan en tres variables tipo char para posteriormente ejecutar las melodías correspondientes cuando convenga.
#include <SPI.h>#include <SD.h>#include <PlayRtttl.hpp>const int pinCS = 10; // Pin CS de la tarjeta SDFile archivo;const int TAM_MAX = 200; // Tamaño máximo esperado del archivo (ajusta según necesidad)char contenido[TAM_MAX]; // Variable para guardar el contenidochar contenido1[TAM_MAX]; // Variable para guardar el contenidochar contenido2[TAM_MAX]; // Variable para guardar el contenidovoid setup() {Serial.begin(9600);Serial.println("Inicializando tarjeta SD...");if (!SD.begin(pinCS)) {Serial.println("Error: No se pudo inicializar la tarjeta SD");while (true); // Detener ejecución}Serial.println("Tarjeta SD inicializada correctamente");//********************************//********************************archivo = SD.open("inicio.txt");if (archivo) {Serial.println("Leyendo archivo...");int i = 0;while (archivo.available() && i < TAM_MAX - 1) {contenido[i] = archivo.read(); // Guardar cada carácteri++;}contenido[i] = '\0'; // Final de cadenaarchivo.close();Serial.println("Contenido leído:");Serial.println(contenido); // Imprimir todo el contenido guardado en la variable} else {Serial.println("Error: No se pudo abrir datos.txt");}//***********************************************************//***********************************************************archivo = SD.open("piso.txt");if (archivo) {Serial.println("Leyendo archivo...");int i = 0;while (archivo.available() && i < TAM_MAX - 1) {contenido1[i] = archivo.read(); // Guardar cada carácteri++;}contenido1[i] = '\0'; // Final de cadenaarchivo.close();Serial.println("Contenido leído:");Serial.println(contenido1); // Imprimir todo el contenido guardado en la variable} else {Serial.println("Error: No se pudo abrir datos.txt");}//***********************************************************//*********************************************************archivo = SD.open("llegada.txt");if (archivo) {Serial.println("Leyendo archivo...");int i = 0;while (archivo.available() && i < TAM_MAX - 1) {contenido2[i] = archivo.read(); // Guardar cada carácteri++;}contenido2[i] = '\0'; // Final de cadenaarchivo.close();Serial.println("Contenido leído:");Serial.println(contenido2); // Imprimir todo el contenido guardado en la variable} else {Serial.println("Error: No se pudo abrir datos.txt");}//***********************************************************//***********************************************************}void loop() {playRtttlBlocking(9, contenido);delay(1000);playRtttlBlocking(9, contenido1);delay(1000);playRtttlBlocking(9, contenido2);delay(1000);
}
El programa anterior mejorado
En el programa anterior todos los archivos .txt se convierten en un archivo tipo char con 200 caracteres, que es lo que indica la variable numérica TAM_MAX, sea cual fuere la longitud del archivo .txt. En este nuevo programa esta deficiencia se corrige recogiendo en la variable longitud la longitud del archivo tipo File.
#include <SPI.h>#include <SD.h>#include <PlayRtttl.hpp>const int pinCS = 10; // Pin CS de la tarjeta SDFile archivo;int longitud;void setup() {Serial.begin(9600);Serial.println("Inicializando tarjeta SD...");if (!SD.begin(pinCS)) {Serial.println("Error: No se pudo inicializar la tarjeta SD");while (true); // Detener ejecución}Serial.println("Tarjeta SD inicializada correctamente");}void sonido1(){archivo = SD.open("inicio.txt");if (archivo) {Serial.println("Leyendo archivo...");longitud = archivo.size();char contenido[longitud]; // Variable para guardar el contenidoint i = 0;while (archivo.available() && i < longitud) {contenido[i] = archivo.read(); // Guardar cada carácteri++;}contenido[i] = '\0'; // Final de cadenaarchivo.close();Serial.println(contenido); // Imprimir todo el contenido guardado en la variableplayRtttlBlocking(9, contenido);} else {Serial.println("Error: No se pudo abrir datos.txt");}}void sonido2(){archivo = SD.open("piso.txt");if (archivo) {Serial.println("Leyendo archivo...");longitud = archivo.size();char contenido1[longitud]; // Variable para guardar el contenidoint i = 0;while (archivo.available() && i < longitud) {contenido1[i] = archivo.read(); // Guardar cada carácteri++;}contenido1[i] = '\0'; // Final de cadenaarchivo.close();Serial.println(contenido1); // Imprimir todo el contenido guardado en la variableplayRtttlBlocking(9, contenido1);} else {Serial.println("Error: No se pudo abrir datos.txt");}}void sonido3(){archivo = SD.open("llegada.txt");if (archivo) {Serial.println("Leyendo archivo...");longitud = archivo.size();char contenido2[longitud]; // Variable para guardar el contenidoint i = 0;while (archivo.available() && i < longitud) {contenido2[i] = archivo.read(); // Guardar cada carácteri++;}contenido2[i] = '\0'; // Final de cadenaarchivo.close();Serial.println(contenido2); // Imprimir todo el contenido guardado en la variableplayRtttlBlocking(9, contenido2);} else {Serial.println("Error: No se pudo abrir datos.txt");}}void loop() {sonido3();sonido1();sonido2();}
Archivos con contenido RTTTL en SteamakersBlocks
Utilizando el entorno de programación SteamakersBlocks podemos recoger la información guardada en un archivo en una tarjeta microSD para poder reproducir una melodía RTTTL en Arduino. El programa es el siguiente. En Inicializar configuramos el puerto serie y la tarjeta SD. A continuación eliminamos el archivo estado.txt para volver a cargar contenido con el siguiente bloque. En este archivo guardamos el código de la sintonía de los Simpsons.
En el Bucle se leen sucesivamente los diferentes bytes del archivo, se guardan en una variable de texto llamada musica y se reproducen en el Zumbador conectado al pin 9 de Arduino.
En el archivo estado.txt el contenido es:
:d=4,o=5,b=160:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p
Con este programa conseguimos después de unos 22 segundos que se reproduzca una vez la sintonía de los Simpsons.
A continuación se puede ver otro programa que lee directamente el contenido del archivo estado.txt para reproducir sus bytes en el Zumbador.
Ahora la sintonía se reproduce continuamente, con unos 9 segundos en silencio entre las dos melodías.
Curiosamente este programa solamente funciona después de cargar el programa anterior y que haya sonado una vez la melodía, el que graba en el archivo el código RTTTL. Si una vez grabado se reinicia con el botón de RESET de la placa Arduino si que funciona bien, pero si se desconecta el cable de alimentación deja de funcionar, aunque se vuelva a cargar el programa. Si se desconecta el cable y se vuelve a conectar muy rápido si que sigue funcionando. Por todo ello este programa NO ES MUY SEGURO Y NO TIENE MAYOR UTILIDAD.
Intérprete de código ASCII en SteamakersBlocks
El contenido del archivo de texto es alfanumérico, pero cuando SteamakersBlocks lee su contenido proporciona el código ASCII de cada uno de sus bytes, por lo que para poder volver a utilizar estos caracteres originales hemos de transformar los códigos ASCII en sus correspondientes signos alfanuméricos.
Para conseguirle he hecho un subprograma que asigna un carácter alfanúmero a su correspondiente símbolo ASCII utilizando tantas estructuras condicionales como símbolos hay. Se puede encontrar este programa en SteamakersBlocks.
La función ASCII no se ve completa y tiene una estructura condicional para cada signo alfanumérico. En el Bucle se establece una estructura de repetición que repite las operaciones que se encuentran en su interior tantas veces como bytes tenga el archivo estado.txt. Al inicio del proceso se pone la variable i a 0 y también se borra la variable de texto Texto. Cada byte del archivo se guarda en una variable de texto llamada ASCII. Después se envía este valor a la función ASCII que devuelve dentro de esta variable el signo alfanumérico correspondiente.
A continuación este carácter alfanumérico se añade a la variable de texto Texto. Una vez fuera de la estructura de repetición se envía todo el contenido de la variable Texto para que el zumbador interprete esa melodía RTTL.
En el archivo estado.txt el contenido es:
:d=4,o=5,b=160:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p









No hay comentarios:
Publicar un comentario