Dispositivos sonoros Arduino

Vamos a realizar un proyecto Arduino con sonido. No se trata del típico zumbador que suena como un gato atropellado. Vamos a evolucionar nuestros programas para convertir nuestros sistema en un verdadero reproductor MP3.

Nuestro objetivo es crear una caja capaz de detectar mediante un sensor PIR la presencia de una persona para reproducir una canción tal y como nosotros le indiquemos.

Nuestro sistema

Nuestro sistema se compone de:

  • Arduino Nano
  • Reproductor MP3 DFPlayer Mini
  • Amplificador
  • Altavoz

El sistema que vamos a utilizar es una placa diseñada en la que integraremos todos estos módulos para dejar nuestro sistema lo más compacto posible.

Nuestra primer objetivo, será el de soldar todos los pines sobre la placa, para posteriormente conectar cada módulo en su lugar.

Introducción Arduino

Para empezar, indicamos algunos enlaces que pueden ser de utilidad para comenzar a instalar los módulos necesarios y empezar con nuestro taller.

Para instalar la librería que necesitamos, una vez que hayamos descargado e instalado Arduino, solamente tendremos que acceder a la sección de Programa->Incluir Librería-> Gestionar Librería

 

Preparar la tarjeta SD con música

Para poder escuchar las canciones, primero hay que hacer una busqueda de los archivos mp3 que queremos reproducir.

Para ello tenemos varias opciones.

Las opciones son muy variadas, pero lo más importante a tener en cuenta es lo siguiente. La tarjeta SD es un formato de almacenamiento mediante archivos y estos archivos tienen un nombre específico y están contenidos en unas carpetas determinadas.

A través de esta librería se pueden acceder a través de archivos y ficheros mediante unas determinadas funciones, pero hay veces que pueden no funcionar correctamente. Para que funcionen sin problema, vamos a renombrar todos los archivos de música con un número, indicativo de la posición ordenada dentro de toda la lista de reproducción.

  • 0001.mp3
  • 0002.mp3
  • 0003.mp3
  • ….
  • 9999.mp3

Puede haber hasta un total de 9999 canciones en una carpeta, dentro de 10 carpetas distintas. Pero nuestra recomendación es que las canciones estén ubicadas en la raiz de la tarjeta. Es decir que no estén contenidas en ninguna carpeta o en todo caso estar contenidas en una carpeta con el nombre “mp3

En caso de estar contenidas en carpetas ditintas, éstas han de estar nombradas con un número a su vez y su máximo es de 10. Pero en un principio dejaremos los archivos fuera de ellas en una primera prueba.

De todas maneras, no hace falta llenar la tarjeta por completo, así que podremos guardar otros archivos que no sean necesariamente de música.

Este proceso de buscar, encontrar y ordenar canciones en una lista lleva más tiempo del que nos podemos imaginar aunque parezca mentira, pero el resultado merecerá la pena.

Programación

Nuestro primer programa sera el de reproducir una canción cualquiera. Para realizar este proceso, solamente tendremos que acceder a nuestra página de ArduBlockly y arrastrar los bloques para crear nuestro código.

 

Control por Monitor Serie.

Antes de nada, hemos de comentar que este control por monitor serie, puede ser el mismo que podriamos aplicar si tuvieramos un módulo Bluetooth que se comunique de forma inalámbrica; pero este caso lo veremos en otro taller.

Para realizar el control por monitor serie lo que vamos a hacer es ejecutar sus funciones cuando le definamos una letra en la lectura.

Por ejemplo.

  • Play –> w
  • Next –> d
  • Previous –>a
  • Pause –>  s
  • Stop –> x
  • Random –> r

Pero además de eso, vamos a desarrollar el algoritmo para elegir una canción en concreto de toda la lista mediante un parámetro numérico y otros caracteres para leer datos de nuestro modulo mp3.

  • Play Song number –> e10 (Ejecuta la canción número 10 )
  • Set Volume –> c10 ( Pondremos el volumen a 10 de un máximo de 30 )

 

#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>

char serialData;
int nsong;
int v;

SoftwareSerial comm(10,11);
DFRobotDFPlayerMini mp3;
String DFPlayerStatus(uint8_t type, int value){
  String error;
  switch (type) {
    case TimeOut:
      error = "Time Out!";
      break;
    case WrongStack:
      error = "Stack Wrong!";
      break;
    case DFPlayerCardInserted:
      error = "Card Inserted!";
      break;
    case DFPlayerCardRemoved:
      error = "Card Removed!";
      break;
    case DFPlayerCardOnline:
      error = "Card Online!";
      break;
    case DFPlayerPlayFinished:
      error = "Number:";
      error += value;
      error += " Play Finished!";
      break;
    case DFPlayerError:
      //Serial.print("DFPlayerError:");
      switch (value) {
        case Busy:
          error = "Card not found";
          break;
        case Sleeping:
          error = "Sleeping";
          break;
        case SerialWrongStack:
          error = "Get Wrong Stack";
          break;
        case CheckSumNotMatch:
          error = "Check Sum Not Match";
          break;
        case FileIndexOut:
          error = "File Index Out of Bound";
          break;
        case FileMismatch:
          error = "Cannot Find File";
          break;
        case Advertise:
          error = "In Advertise";
          break;
        default:
          error = "DFPlayerError: Unknown";
          break;
      }
      break;
    default:
      break;
  }
  return error;
}

void setup() {
  Serial.begin(9600);
  comm.begin(9600);

  mp3.begin(comm);
  serialData = (char)((' '));

}

void loop() {
  if (mp3.available()) {
    Serial.println((DFPlayerStatus(mp3.readType(),mp3.read())));
  }
  if (Serial.available()) {
    serialData = (Serial.read());
    if (serialData == ('w')) {
      Serial.println("Play Song");
      mp3.start();
    } else if (serialData == ('q')) {
      Serial.println("Connecting...");
      if (mp3.begin(comm)) {
        Serial.println("Success");
      } else {
        Serial.println("Please insert the SD card or check the connection");
      }
    } else if (serialData == ('a')) {
      Serial.println("Previous Song");
      mp3.previous();
    } else if (serialData == ('s')) {
      Serial.println("Pause");
      mp3.pause();
    } else if (serialData == ('d')) {
      Serial.println("Next Song");
      mp3.next();
    } else if (serialData == ('x')) {
      Serial.println("Stop");
      mp3.stop();
    } else if (serialData == ('r')) {
      Serial.println("Random");
      mp3.randomAll();
    } else if (serialData == ('e')) {
      nsong = (int)(((Serial.readString()).toInt()));
      Serial.print("Play Song Number");
      Serial.println(nsong);
      mp3.play(nsong);
    } else if (serialData == ('c')) {
      v = (int)(((Serial.readString()).toInt()));
      Serial.print("Set Volume (Max 30) to ");
      Serial.println(v);
      mp3.volume(v);
    }
  }

}

Detectar Sensor

Por una parte vamos a leer cuales son las lecturas desde el pin analógico 4. Esta respuesta la podemos ver reflejada en una gráfica mediante el Serial Plotter de Arduino.

Una vez que verifiquemos que nuestro sensor funciona correctamente, podemos hacer que nuestro módulo MP3 se active en función de la lectura, en este caso reproduciremos una canción al azar.

Finalmente, este modelo nos servirá para crear la programación a prueba de errores y así saber que si el módulo no encuentra ningún error, entonces podremos usarlo.


#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>

int sensor;
String status;

SoftwareSerial comm(10,11);
DFRobotDFPlayerMini mp3;
String DFPlayerStatus(uint8_t type, int value){
  String error;
  switch (type) {
    case TimeOut:
      error = "Time Out!";
      break;
    case WrongStack:
      error = "Stack Wrong!";
      break;
    case DFPlayerCardInserted:
      error = "Card Inserted!";
      break;
    case DFPlayerCardRemoved:
      error = "Card Removed!";
      break;
    case DFPlayerCardOnline:
      error = "Card Online!";
      break;
    case DFPlayerPlayFinished:
      error = "Number:";
      error += value;
      error += " Play Finished!";
      break;
    case DFPlayerError:
      //Serial.print("DFPlayerError:");
      switch (value) {
        case Busy:
          error = "Card not found";
          break;
        case Sleeping:
          error = "Sleeping";
          break;
        case SerialWrongStack:
          error = "Get Wrong Stack";
          break;
        case CheckSumNotMatch:
          error = "Check Sum Not Match";
          break;
        case FileIndexOut:
          error = "File Index Out of Bound";
          break;
        case FileMismatch:
          error = "Cannot Find File";
          break;
        case Advertise:
          error = "In Advertise";
          break;
        default:
          error = "DFPlayerError: Unknown";
          break;
      }
      break;
    default:
      break;
  }
  return error;
}

boolean isStringEmpty(String msg) {
  if (msg.length() == 0) {
    return true;
  } else {
    return false;
  }
}


void setup() {
  Serial.begin(9600);
  comm.begin(9600);
  pinMode(4, INPUT);

  mp3.begin(comm);
  sensor = (int)(0);
  status = (String)("");

}

void loop() {
  sensor = (analogRead(4));
  status = (DFPlayerStatus(mp3.readType(),mp3.read()));
  if (!isStringEmpty(String(status))) {
    Serial.println(status);
  } else {
    if (sensor == true) {
      if (mp3.available()) {
        mp3.randomAll();
      }
    } else {
    }
  }

}

Tiempos

Una de las cuestiones más importantes a tener en cuenta con este tipo de sensor de presencia, es la de saber medir los tiempos entre un evento y otro.

En este caso, si utilizamos una canción de 3 minutos y tiene que reproducirse toda la canción completa hasta la próxima persona que pase, entonces quizás no nos funcione como nosotros queremos.

Es por ello que podríamos determinar el siguiente evento, en función de la respuesta de estado. Pero eso lo veremos más adelante.

 

Sensor PIR

El sensor PIR HCSR501 es el que vamos a utilizar para detectar la presencia de una persona y cuando lo hayamos detectado reproduciremos la canción.

El problema que tendremos es que de este sensor, solo queremos detectar el evento o salto de la señal cuando pasa de desactivado a activado. En caso de activar el reproductor cuando el sensor se encuentre activo, entonces ejecutaría la función reproducir continuamente en cada iteración del bucle y eso produciría un comportamiento indeseado.

Por lo que entonces vamos a desarrollar una programación por cambio de estado. Este sensor de movimiento PIR tiene 3 pines, VCC, OUTPUT y GND, 2 potenciómetros para ajustar la sensibilidad y la demora. El retardo se puede configurar entre 5 y 300 segundos mientras que el potenciómetro de sensibilidad ajusta el rango de detección de aproximadamente 3 metros a 7 metros.

*Existe un jumper en el que se supone que una conexión es de disparo único y el otro modo de conexión es de trigger. Pero la experiencia nos ha mostrado que simplemente cambia la lógica. es decir que si se conecta en H, se mantendrá en 0 si no hay ninguna persona y salta en activo cuando detecta que alguien ha pasado. Si se conecta en L, la lógica es inversa.

El modelo de programación sería de la siguiente manera.

Si añadimos los bloques para MP3 dentro de nuestro gestor de eventos y revisando los errores asociados al módulo que pudieran aparecer  tendremos nuestro programa completo.

 

Programación final

Dentro de la programación completa, haremos una pequeña función para ejecutar una canción aleatoria. Por experiencia sabemos que la aleatoriedad no es el punto fuerte de los aparatos electrónico digitales. El resultado de esta pseudoaleatoriedad es que las canciones que se reproducen son exactamente las mismas en el mismo orden aunque aparentemente parezcan ser elegidas de manera distinta.

Para ello, utilizaremos la función de randomSeed, que lo que hace es modificar la referencia con la que se crea la aleatoriedad de la placa. Si el numero del parámetro randomSeed fuera siempre el mismo, la aleatoriedad no sería tan aleatoria, pero al cambiar, sus pautas de aleatoriedad varían.

Para un mejor resultado, haremos que el número de este parámetro en esta función sea la lectura de un pin analógico sin conectar que hará que varíe la señal sin ningún patrón.

randomSeed(analogRead(0));

#include <DFRobotDFPlayerMini.h>
#include <SoftwareSerial.h>

int pin = A5;
 
boolean estado;
SoftwareSerial comm(10,11);
DFRobotDFPlayerMini mp3;
 
int size = 30;
 
void ISRUP(){
   Serial.println( "HIGH" );
    //mp3.randomAll();
    //mp3.play( random( 1, size ) );
}
 
void ISRDOWN(){
  Serial.println( "DOWN" );
  mp3.play( random( 1, size ) );
}
 
String DFPlayerStatus(uint8_t type, int value){
  String error;
  switch (type) {
    case TimeOut:
      error = "Time Out!";
      break;
    case WrongStack:
      error = "Stack Wrong!";
      break;
    case DFPlayerCardInserted:
      error = "Card Inserted!";
      break;
    case DFPlayerCardRemoved:
      error = "Card Removed!";
      break;
    case DFPlayerCardOnline:
      error = "Card Online!";
      break;
    case DFPlayerPlayFinished:
      error = "Number:";
      error += value;
      error += " Play Finished!";
      break;
    case DFPlayerError:
      //Serial.print("DFPlayerError:");
      switch (value) {
        case Busy:
          error = "Card not found";
          break;
        case Sleeping:
          error = "Sleeping";
          break;
        case SerialWrongStack:
          error = "Get Wrong Stack";
          break;
        case CheckSumNotMatch:
          error = "Check Sum Not Match";
          break;
        case FileIndexOut:
          error = "File Index Out of Bound";
          break;
        case FileMismatch:
          error = "Cannot Find File";
          break;
        case Advertise:
          error = "In Advertise";
          break;
        default:
          error = "DFPlayerError: Unknown";
          break;
      }
      break;
    default:
      break;
  }
  return error;
}
 
void setup() {
 
  attachInterrupt(digitalPinToInterrupt(pin), ISRUP, RISING);
  attachInterrupt(digitalPinToInterrupt(pin), ISRDOWN, FALLING);
   
  Serial.begin(9600);
  comm.begin(9600);
 
  mp3.begin(comm); 
  mp3.volume(30);
}
 
void loop() {
    if (mp3.available()) {
      Serial.println((DFPlayerStatus(mp3.readType(),mp3.read())));
    }
     
 
      if (estado != digitalRead(  pin  )) {
         
        if (estado == HIGH) {
          randomSeed(analogRead(A4));
          
          Serial.println( "Change to low" );
          mp3.play( random( 1, size ) );
           
          //mp3.next();
        }else{
          Serial.println( "Change to high" );
        }
        estado = digitalRead( pin );
      }
 
}

Deja un comentario

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

seis − uno =

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.