Código:
#!/usr/bin/env python
# -*- coding: utf8 -*-
# Incluimos los paquetes necesarios, GPIO para el control del cableado, MFRC522 es un script a parte que controla el lector de tarjetas NFC,
# signal lo usaremos para una salida "ordenada" del programa y subprocess para poder llamar a programas de linux
import RPi.GPIO as GPIO
import MFRC522
import signal
import subprocess
# Esto no es estrictamente necesario, pero si cerramos la aplicación (pulsando Ctrl+C), queremos "limpiar" el estado del cableado
def end_read(signal,frame):
GPIO.cleanup()
exit()
# Esta clase es para facilitar el uso del "5 way switch" que uso, así es más fácil definir un "botón", especificando que pin del GPIO
# de la raspi usará, y a qué función habrá que llamar cuando sea pulsado
class Button():
id = 0
runner = None
param = None
already_pressed = False
# Aquí el constructor del botón, le pasamos (además de "self" que es obligatorio en python):
# un "id" que representa al número del pin en el GPIO de la raspi
# un "runner" es decir, la función a la que llamaremos si se detecta que el botón está pulsado
# un parámetro para dicha función (esto está así porque las funciones que yo he definido tienen un parámetro)
def __init__(self, id, runner, param):
# Especificamos que el pin es de "input" y que lo hemos conectado a GND en el cableado
# (por lo tanto, se "activará en LOW")
GPIO.setup(id, GPIO.IN, GPIO.PUD_UP)
self.id = id
self.runner = runner
self.param = param
def check(self):
# Verificamos si el pin específico está en LOW y si no se había activado justo antes
# (el típico mecanismo para que si mantenemos la pulsación no lo detecte, solo nos interesa la primera hasta soltarlo)
if GPIO.input(self.id) == GPIO.LOW:
if not self.already_pressed:
# Aquí es donde llamamos a la función que le especificamos
self.runner(self.param)
self.already_pressed = True
else:
self.already_pressed = False
# Una función muy simple, hace una llamada asíncrona al sistema para ejecutar un comando de linux que suba o baje el volumen
# (según el parámetro que le pasamos ("up") sea True o False
# Lo que hace es ejecutar el comando "amixer -c 1 set PCM 10%+" por ejemplo para subir el volumen
# En mi caso hace falta el parámetro "-c 1" porque estoy usando una tarjeta de sonido USB (la integrada en una raspi "no-zero"
# sería -c 0 (la "por defecto") en cuyo caso no haría falta ese parámetro
def volume(up):
amixer = ["amixer", "-c", "1", "set", "PCM", ""]
amixer[5] = up and "10%+" or "10%-"
subprocess.Popen(amixer)
# Aquí cambiamos la canción actual de reproducción hacia delante o hacia atrás (en función del parámetro "next" sea True o False)
def change_song(next):
global song
global pid
global mpg321
global tag
# Si se llama a esta función sin que hayamos seleccionado un disco, o si ya estamos en la primera o última canción, a lo mejor
# no hay que hacer nada, así que salimos
if tag == "" or (next and song == lengths[tag]) or (not next and song == 1):
return
# Incrementamos o decrementamos nuestro contador de canciones, según el parámetro de entrada
song += (next and 1 or -1)
# Si estaba sonando alguna canción, matamos el proceso (es decir, terminamos la reproducción)
if pid != None:
pid.terminate()
pid = None
# Formamos nuestro string para la llamada al sistema, con dos parámetros, el primero es el nombre del disco y el segundo la canción
# (dentro de cada carpeta de los discos, las canciones son 1.mp3, 2.mp3...)
mpg321[6] = "/root/lp/%s/%s.mp3" % (tag, str(song))
print mpg321
pid = subprocess.Popen(mpg321)
# Aquí almacenaremos información sobre la llamada al sistema que hace que se ejecute mpg321 (el reproductor de música que usamos)
pid = None
# El número de canción que está sonando (o va a sonar)
song = 1
# La cadena con los parámetros para llamar al reproductor de música externo
# Al igual que con el control de volumen, tenemos que especificar que use la segunda tarjeta de sonido (porque uso una pinchada al USB)
# lo especificamos con "-a hw:1"
mpg321 = ["/usr/bin/mpg321", "-q", "-o", "alsa", "-a", "hw:1", ""]
# Cuando leo una pegatina NFC, cojo parte de su tag, y para las pegatinas que tengo, sus tags son 188, 225, 250 y 251
# Cada uno de esos tags se corresponde con un directorio donde están las canciones
# Como nota curiosa, esto es para un regalo y sus discos favoritos son "El concierto de Brandenburgo" de Bach, "A love supreme" de
# John Coltrane y "In rock" de Deep Purple
tags = {188: "bach1", 225: "bach2", 250: "love", 251: "rock"}
# Aquí indicamos cuantas canciones tiene cada directorio / disco
lengths = {"bach1": 9, "bach2": 9, "love": 4, "rock": 7}
# El disco seleccionado actualmente
tag = ""
# Mecanismo para evitar falsos negativos al leer tags. El lector, cuando tiene un tag apoyado, a veces dice que no hay nada,
# y en seguida vuelve a decir que hay un tag. Usamos esta variable para evitar que el programa piense que el usuario ha
# retirado el tag realmente
noTag = False
´
# Hasta aquí definición de funciones, clases y variables, ahora empieza el "main"
# Decimos al sistema que si pulsamos Ctrl+C, llame a la función end_read para hacer un apagado ordenado
signal.signal(signal.SIGINT, end_read)
# Activamos el lector NFC
MIFAREReader = MFRC522.MFRC522()
# Creamos una lista de botones (podríamos crear una variable para cada uno, pero así es más cómodo)
# Le pasamos tres parámetros a cada uno: El pin en el que "escucha", la función a la que llamar si se activa, y el parámetro
# a pasarle a esa función
# Los cuatro botones serían: "subir volumen, bajar volumen, canción previa, canción siguiente"
buttons = [Button(35, volume, True), Button(36, volume, False), Button(37, change_song, False), Button(38, change_song, True)]
# El programa está en un bucle infinito.
# Sería buena idea meter como primera linea del bucle un "time.sleep(1)" o algo así para que no esté a tope comiendo CPU, aunque
# si la raspberry sólo va a hacer eso, la carga de CPU es muy baja
while True:
# Aquí chequeamos cada uno de los botones, recorriendo la lista de los mismos
# No hay que especificar llamar a ninguna funcion ni nada aquí, porque cada uno ya sabe lo que tiene que hacer
for button in buttons:
button.check()
# Si habíamos hecho alguna llamada a linux (mpg321 para reproducir musica), pero ésta ya ha terminado, significa que la canción
# terminó sola (porque si la cortamos nosotros, habríamos puesto la variable "pid" a None) y entonces tenemos que pasar a la siguiente
if pid and pid.poll() == 0:
pid = None
change_song(True)
# Leemos un tag NFC
(status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
# Si el tipo leído es 0, es porque no hay tag, o puede ser un "falso negativo"
if TagType == 0:
# Aqui verificamos si en la iteración anterior dio también TagType == 0, por lo tanto no es un falso negativo, y realmente no hay tag
if noTag:
# Así que si estaba sonando una canción, la paramos
if pid != None:
pid.terminate()
pid = None
tag = ""
else:
noTag = True
else:
noTag = False
# Si hemos leído una tarjeta correcta
if status == MIFAREReader.MI_OK:
# Intentamos obtener su UID (para ver que tag es, el valor que nos interesa estará dentro de la variable uid, en concreto
# estará en uid[2]) Recuerda esos numeritos de arriba: 188, 225...
(status,uid) = MIFAREReader.MFRC522_Anticoll()
if status == MIFAREReader.MI_OK:
# Si el tag que acabamos de leer es diferente del tag actual, significa que hemos puesto otro disco y habrá que cambiar
# de canción, pero si fuese el mismo, significa que el tag sigue encima, por lo que no habría que hacer nada
if tag != tags[uid[2]]:
song = 1
tag = tags[uid[2]]
mpg321[6] = "/root/lp/%s/%s.mp3" % (tag, str(song))
pid = subprocess.Popen(mpg321)
Marcadores