martes, 28 de agosto de 2012

Mostrando canción en LCD con Arduino (II)

En el post anterior conseguimos mostrar en el LCD conectado al Arduino cadenas de caracteres enviadas desde la conexión serie del mismo. Nos falta la parte correspondiente al RPi, es decir, generar las cadenas correspondientes a la canción que está sonando en MPD y enviarla al Arduino por puerto serie. Para ello vamos a utilizar Python, un lenguaje de programación que nos permite hacer mucho con muy poco, como veremos a continuación.

Conectando el Arduino y el Raspberry Pi por puerto serie

Lo primero que debemos hacer es establecer una comunicación serie entre el Arduino y nuestro RPi. Para ello utilizaremos el puerto USB, que es una conexión serie al fin y al cabo, así que conectamos un cable USB entre el RPi (HUB USB) y el Arduino. Si todo va bien, el Arduino debe encenderse al igual que cuando lo conectábamos a un PC convencional.

Recordemos además que en el programa que escribimos en el post anterior, el Arduino enviaba por el puerto serie una cadena “INITOK” después de inicializarse, y otra cadena “Loop” en cada iteración de la función loop del programa, así que si todo ha salido bien, el RPi debe de estar recibiéndolas. Comprobémoslo:

  • Desconectamos el Arduino del RPi.
  • Escribimos en una terminal: ls /dev/tty*
  • Nos saldrá algo parecido a esta imagen:
  • image

  • Conectamos el Arduino al RPi y esperamos unos segundos.
  • Volvemos a escribir en la terminal ls /dev/tty*
  • Nos saldrá algo parecido a esta imagen:
  • image

    Si nos fijamos, en la segunda imagen hay una nueva entrada (en mi caso /dev/ttyUSB0). Ésta es la conexión serie de nuestro RPi. Para poder monitorizar esta conexión vamos a instalar un pequeño programa llamado minicom, para ello, en la terminal escribimos:

  • sudo apt-get install minicom
  • Y ahora lo configuramos para que monitorice nuestra interfaz serie. De nuevo en la terminal:

  • sudo minicom –s
  • Nos saldrá una interfaz de configuración en modo texto. Básicamente tenemos que cambiar el dispositivo a monitorizar, que en nuestro caso es /dev/ttyUSB0 y la velocidad de la conexión, que en el Arduino la establecimos a 9600 baudios.

    Para ello seleccionamos “configuración de la puerta serial” como aparece en la imagen:

    image

    Y lo configuramos modificando el Dispositivo serial al que nos haya salido antes (en mi caso /dev/ttyUSB0) y el Bps/Paridad/Bits para establecerlo a 9600 8N1:

    image

    Guardamos la configuración como dfl, que es la configuración que cargará minicom por defecto y salimos de minicom.

    Ahora sólo queda ejecutar minicom sin parámetros para comprobar que todo ha salido bien, y deberíamos ver los envíos del Arduino (podemos cerrar minicom pulsando Control+A Q):

    image

    Reproduciendo música con MPD/MPC y obteniendo datos de la canción

    En este momento voy a hacer un inciso para explicar por encima el manejo de MPD y MPC. En anteriores posts dijimos que MPD era un demonio que permitía reproducir todo tipo de audio, y MPC era un cliente que facilitaba el manejo de MPD. Para probar nuestro programa, vamos a añadir una radio online a MPD, vamos a reproducirla, y vamos a obtener los datos de la emisora y de la canción. En mi caso he optado por una radio que he descubierto hace poco y me ha gustado mucho, ya que tiene buena música y ninguna publicidad: Radio Paradise.

    MPD necesita la dirección del stream que queramos reproducir. Buscando en la página de Radio Paradise encontramos varios streams según la calidad de reproducción que queramos en la pestaña “Listen”. En mi caso he elegido la opción de 128 Kbps con el códec AAC+, que da mayor calidad de el bitrate equivalente en MP3. El link en este caso es:

    http://www.radioparadise.com/musiclinks/rp_128aac.m3u

    ¿Cómo reproducir esto en MPD? Muy sencillo: primero lo añadimos a la lista de reproducción escribiendo en consola:

    mpc load http://www.radioparadise.com/musiclinks/rp_128aac.m3u

    Y a continuación iniciamos la reproducción escribiendo:

    mpc play

    Si tenemos el RPi conectado a internet y a unos altavoces externos debería empezar a sonar la radio. De todas maneras podemos comprobar qué esta sonando escribiendo simplemente:

    mpc

    image

    Existen muchos otros comandos para MPC: podemos pausar y reanudar la reproducción con mpc toggle, pararla con mpc stop, eliminar la lista de reproducción actual con mpc clear, eliminar todas los elementos de la lista menos el actual con mpc crop, etc. Para más información se puede consultar el manual con man mpc.

    Un comando que nos interesa, sin embargo, es mpc current, que muestra información sobre la canción actualmente en reproducción. Además puede configurarse con máscaras, así, si escribimos en la terminal:

    mpc current –f “%name%”

    Obtendremos el nombre de la emisora de radio. Si escribimos:

    mpc current –f “%title%”

    Obtenemos el artista y la canción que está sonando en ese momento. Perfecto para nuestros propósitos.

    image

    Lamentablemente, las máscaras de artista y álbum no devuelven nada. Supongo que depende de lo bien (o mal en este caso) que lo hayan implementado en cada emisora. De todas formas para una primera prueba nos sobra.

    Integrando todo en Python

    Ya que tenemos la conexión serie entre el Arduino y el RPi funcionando correctamente y el MPD configurado y reproduciendo música, usaremos Python para obtener el artista y la canción que esté sonando en MPD y la enviaremos al Arduino. En mi caso he optado por mostrar el artista en la línea superior del display y la canción en la línea superior.

    Python viene instalado por defecto en Raspbian, pero no así la librería PySerial, que necesitaremos para enviar y recibir información de forma sencilla por la conexión serie del RPi. La podemos instalar escribiendo en la terminal:

    sudo apt-get install python-serial

    Solo queda crear el programa en Python. Copio el código del programa y lo comento a continuación:

      1: import serial, subprocess, time, shlex
      2: 
      3: ser = serial.Serial('/dev/ttyUSB0', 9600)
      4: 
      5: serial_out = ""
      6: serial_in = ""
      7: com_radio = 'mpc current -f "%name%"'
      8: com_song = 'mpc current -f "%title%"'
      9: 
     10: 
     11: while True:
     12: 	args = shlex.split(com_song)
     13: 	str_song = subprocess.check_output(args)
     14: 	list = str_song.split(' - ')
     15: 
     16: 	serial_out = "UP "
     17: 	serial_out += list[0]
     18: 	serial_out += '\n'
     19: 	ser.write(serial_out)
     20: 	print "RPi->Arduino:",serial_out
     21: 
     22: 	time.sleep(1)
     23: 
     24: 	serial_out = "DW "
     25: 	serial_out += list[1]
     26: 	serial_out += '\n'
     27: 	ser.write(serial_out)
     28: 	print "RPi->Arduino:",serial_out
     29: 	
     30: 	time.sleep(5)

     


    En la línea 1 importamos las librerías que usaremos en el programa. En concreto usaremos serial para la conexión serie con el Arduino, subproccess para poder invocar a MPC desde el programa y recoger el artista y la canción que está sonando en cada momento, time para poder establecer un tiempo de espera entre comprobaciones y shlex para ayudarnos a manipular cadenas de caracteres.


    En la línea 3 abrimos la comunicación serie. Tenemos que indicar el dispositivo con el que nos queremos comunicar (en mi caso /dev/ttyUSB0) y la velocidad de transmisión, que son los 9600 baudios.


    En las líneas 5 a 8 declaramos las cadenas que vamos a utilizar para enviar y recibir información (serial_in y serial_out) y las cadenas que vamos a utilizar para obtener el nombre de la estación, el artista y el título de la canción (com_radio y com_song)


    En las líneas 11 a 30 tenemos un bucle infinito. En él comprobamos el nombre de la canción y lo enviamos al Arduino: en la línea 12 separamos la cadena de comandos necesaria para obtener el artista y canción para adaptarlo al formato que necesita Python y lo guardamos en args que es una lista de los parámetros que ejecutaremos.


    En la línea 13 ejecutamos un subproceso y almacenamos el resultado en str_song. Es exactamente igual que si ejecutásemos el contenido de com_song en el terminal y almacenásemos lo que devuelve el terminal en la cadena str_song. En este caso se estamos pidiendo el campo title a MPC, que ya hemos visto antes que nos devuelve una cadena con [nombredelartista] – [nombredelacancion]. Eso mismo es lo que contendrá str_song.


    En la línea 14 lo que hacemos es separar esa cadena en una lista en la que el primer elemento sea el nombre del artista, y el segundo el nombre de la canción. Para ello usamos el método split y le especificamos que nos separe cuando encuentre un espacio, un guión y un espacio.


    En las líneas 16 a 20 construimos la cadena que enviaremos al Arduino: como queríamos mostrar el artista en la línea superior del display, tendremos que escribir una cadena que comience con “UP “, concatenar el nombre del artista (que esta en list[0]) y añadir un carácter de fin de línea. Por último, en la línea 19 enviamos la cadena que hemos construido al Arduino. Como veis, la sintaxis de Python es muy sencilla, clara y concisa; y permite abstraernos de muchos detalles de bajo nivel.


    En la línea 22 esperamos un segundo para dar tiempo a que el Arduino reciba y procese el artista (antes de que me lo digáis, se que es una chapuza, y más cuando el propio Arduino está respondiendo con un OK cuando recibe los datos correctamente, lo cambiaré más adelante).


    En las líneas 24 a 28 hacemos exactamente el mismo proceso pero con la canción y lo enviamos también al Arduino. En ambos casos imprimimos las cadenas que estamos enviando por la terminal para detectar posibles errores.


    En la última línea simplemente esperamos 5 segundos antes de volver a comprobar si ha cambiado la canción para evitar estar comprobando todo el tiempo.


    Probando todo


    Ya sólo queda probar que todo funcione correctamente, así que comprobamos que MPC esté reproduciendo, que el Arduino esté conectado y que el display esté mostrando Arduino Radio v0.0. Podemos también arrancar el minicom para monitorizar qué se envía y se recibe por el puerto serie. Sólo queda ejecutar en programa de Python (yo lo he llamado radio.py) con:


    python radio.py


    image




    IMG_0412


    Parece que efectivamente todo funciona perfectamente.


    Con esto ya tenemos la base de lo que será nuestra radio. En resumen, hemos conseguido configurar el RPi, controlar desde línea de comandos un reproductor de audio, añadir streams de radios online y reproducirlos, obtener los metadatos de las canciones que están sonando y enviarlos a un LCD controlado por un Arduino mediante conexión serie. No está nada mal. Ahora toca pulir los códigos y ampliar funcionalidades. En concreto, mi objetivo ahora es conseguir que al pulsar un botón del LCDKeypad (el shield del Arduino) podamos detener y reanudar la reproducción de la radio. ¡Nos vemos en el siguiente post!

    6 comentarios:

    1. Hola excelente proyecto.

      Tengo un problema con el codigo python radio.py, me devuelve un error al ejecutar el script en la linea 25, en
      serial_out += list[1]
      IndexError: list index out of range

      Alguna idea?

      ResponderEliminar
    2. Hola,

      El error que te da es porque list[1] no existe. Lo más probable es que cuando un poco antes llenamos la lista con:
      list = str_song.split(' - ')

      El formato no sea ARTISTA - CANCION.
      El método split de Python lo que hace es separar una cadena en varias cuando encuentra un delimitador. En mi caso, como la cadena que recibía desde MPD era ARTISTA - CANCION, lo que hacía con split es poner en list[0] el artista y en list[1] la canción. Seguramente si en vez de la emisora que he puesto yo utilizas otra, el formato no sea el mismo y por eso te falle. Incluso podría ser que la emisora haya cambiado el formato y por eso te falle.

      De todas formas, si quieres evitar que por eso te falle todo, basta con que no uses list[1]. Así que podrías modificar la línea que te da error por:
      serial_out += "Desconocido"
      A ver qué tal te funciona.

      Prueba también a ver qué te devuelve la terminal al hacer un:
      mpc current -f "%title%"

      Porque la raíz del problema es que la cadena que te devuelve ese comando no es la que el programa se espera y de ahí el fallo.

      Un saludo!

      ResponderEliminar
    3. Gracias! solucionado, era un problema de la emisora que estaba probando.

      Otra cuestion, como podria adaptar tu codigo de arduino a un lcd 2x16 y seis pulsadores independientes. Me gustaria seguir tu proyecto, pero no tengo el lcd shield, con lo que la libreria LCDShield no me sirve, y estoy un poco perdido.

      Un saludo!!

      ResponderEliminar
    4. Ten en cuenta que LCDShield hereda de LiquidCrystal (creo que se llama así la librería estándar de Arduino para controlar los lcd) y usa las mismas funciones, así que en el caso de la pantalla lo único que tienes que hacer es añadir esa librería en lugar de la LCDShield, inicializar la pantalla (lo que sabrás hacer si has hecho cualquier ejemplo) y dejar el resto igual, ya que el resto de métodos y funciones son las mismas.

      En cuanto a los botones, he visto el código de LCDShield y realmente lo único que hace es declararlos y poco más, lo ideal sería hacerse una pequeña librería donde se puedan usar las mismas funciones. De todas maneras no planeo utilizar el LCDShield para el programa final, ya que quiero hacer una carcasa y demás, y el uso del LCDShield me limita la colocación de la pantalla y botones (además me estoy planteando utilizar una pantalla de 20x4, que la maneja la misma librería pero hay más espacio para mostrar cosas), el problema es que se me han acabado las vacaciones y con ellas el tiempo (como verás el último post tiene ya casi un mes), por lo que va la cosa más lenta.

      Prueba si quieres lo de la pantalla, que creo que es bastante sencillo hacerlo funcionar así.

      ResponderEliminar
    5. La pantalla la tengo funcionando, ahora me queda la botonera. Intentare sacarlo por mi mismo, y si no pues lo vere con la actualizacion final de tu proyecto.

      Has pensado en asignar cada boton a una radio? las canciones-radios del playlist estan numeradas, y con el comando 'mpc play (numero de la estacion radio)' puedes saltar directamente a esa.

      Un saludo!

      ResponderEliminar
    6. De hecho, he pensado utilizar un par de botones para cambiar de emisora. Mi idea (que no se qué tal saldrá) es tener una pequeña BBDD en SQLite con la información de cada emisora, para poderla cargar en la playlist de MPC al inicio y que también almacene el nombre y la máscara que tengo que usar para obtener el artista y la canción (que en cada una puede ser diferente, como te ha pasado a tí) Por otro lado he pensado en hacer un pequeño servidor web (quizás con web.py) que lance una página donde se puedan consultar algunas estadísticas y añadir/modificar/eliminar emisoras, de ahí la base de datos.

      ResponderEliminar