jueves, 19 de enero de 2017

Controlar un timbre, zumbador o buzzer activo con Python

En este post os voy a mostrar como usar los puertos GPIO y un pequeño programa en Python para controlar un timbre piezoeléctrico activo (también llamado zumbador o buzzer). aquí os dejo una imagen del que he usado yo.



Para ello voy a usar una placa protoboard, cables de conexión, 1 timbre activo, 1 resistencia (opcional, según el modelo de timbre que tengáis) y mi RaspberryPi. Si sois nuevos en esto os aconsejo leer los anteriores posts (del uso de los GPIO en el apartado Programación/GPIO).


1. Organización del circuito

En primer lugar vamos a ver como nos debe quedar nuestro circuito para poder controlar un timbre. Aquí os dejo una imagen para que veáis la distribución que he usado, tened cuidado y respetad la posición de los polos positivo y negativo del timbre (generalmente está indicado, en caso contrario la conexión más larga indica el polo positivo)



si no habéis leído los anteriores post sobre leds os recomiendo hacerlo (sobretodo el primero, en el que detallo el uso de las conexiones y hay un apartado al final sobre el uso de las resistencias, aquí está el link). 
Bien, como podéis ver uso el pin número 6 (cable azul para tierra, etiquetado como GROUND) y el pin número 12 (cable rojo para el positivo, etiquetado como GPIO18). En mi caso conecto el timbre directamente si ninguna resistencia. Si vuestro timbre requiere resistencia o no estáis seguros os recomiendo  usar una resistencia de 470 omnios (colocadla en el cable positivo antes del timbre). Si habéis puesto una resistencia y el timbre no suena o suena muy bajo, sustituid la resistencia por una menor. 


2. Código Python

Para empezar vamos a crear un pequeño programa en python que al ejecutarlo encienda el timbre y suene. Si no sabéis como crear un archivo con un programa en python os recomiendo leer los post sobre programación python del apartado Programación/GPIO del blog.

En primer lugar abrimos el IDLE de Python en nuestra RaspberryPi (yo uso Python3), una vez abierto creamos un nuevo fichero (en el menú File-New File) y introducimos el siguiente código

# importamos la libreria GPIO
import RPi.GPIO as GPIO
# desactivamos mensajes de error
GPIO.setwarnings(False)
# indicamos el uso de  la identificacion BCM para los GPIO
GPIO.setmode(GPIO.BCM)
# indicamos que el GPIO18 es de salida de corriente
GPIO.setup(18,GPIO.OUT)
# damos corriente al pin
GPIO.output(18, True)

guardamos el archivo en una ubicación conocida (en el menú File-Save As), si es necesario apuntad la ruta por si la necesitáis usar más tarde. 

Podemos ejecutar directamente este pequeño programa presionando la tecla F5 (también se puede ejecutar des de el menú Run-Run Module)

Si todo es correcto se abre una nueva ventana de IDLE de python y empieza la ejecución, 
el timbre debería empezar a sonar de forma continuada (si no suena revisad el circuito, si es correcto y usáis una resistencia, poned una menor).
Sí, ya se que molesta bastante el ruido. Para parar el timbre en la nueva pantalla del IDLE que se ha abierto escribid la línea de código siguiente

GPIO.output(18, False)

presionad Intro en el teclado (el sonido debería parar), de esta forma indicamos que finalice la salida de corriente positiva en el puerto GPIO18.

Muy bien, ya sabemos encender un timbre y pararlo, aunque no hemos aprendido nada nuevo. Vamos a cambiar un poco el código y vamos a controlar el timbre para que en lugar de un sonido continuo indefinido dure un período de tiempo determinado. Para ello usaremos un bucle que durará unos segundos.

# importamos la libreria GPIO
import RPi.GPIO as GPIO
# importamos la libreria time
import time
# desactivamos mensajes de error
GPIO.setwarnings(False)
# indicamos el uso de  la identificacion BCM para los GPIO
GPIO.setmode(GPIO.BCM)
# indicamos que el GPIO18 es de salida de corriente
GPIO.setup(18,GPIO.OUT)
# solicitamos al usuario que introduzca los segundos de duracion
segundos_d=int(input("duracion del sonido (segundos): " ))
# guardo en una variable el momento actual en segundos
inicio=time.time()
# guardo en una variable el momento actual sumando los segundos de duracion
final=time.time()+segundos_d
# enviamos la orden de encender el timbre
GPIO.output(18, True)
# bucle, mientras la primera variable no supere a la segunda se repite el bucle
while inicio<=final:    
    # actualizo la variable para controlar el tiempo que ha pasado
    inicio=time.time()
else:
    # si la primera variable no es inferior a la segunda 
    # se ha superado el tiempo y paramos el sonido
    GPIO.output(18, False)

El código es fácil de entender. Lo que he añadido es que a través de un input (si usáis Python2 tenéis que usar raw_input) solicito al usuario que introduzca los segundos de duración del timbre (y los transformo en un integer, de manera que el valor ha de ser 1, 2, 3 ...). Luego guardo en la variable inicio la fecha y hora actuales con time.time(), esto proporciona el valor de la fecha actual en segundos, luego guardo en la variable final el mismo valor sumando los segundos que ha introducido el usuario.
A continuación activo el timbre. Seguidamente creo un bucle con un while que se reproduce mientras no hayan pasado los segundos indicados (dentro del bucle, actualizo la variable inicio para controlar el tiempo transcurrido), una vez se ha superado el tiempo indicado con la orden else (si no se cumple la condición del bucle) finalizo el sonido.

Si ejecutáis el código el programa pedirá que introduzcáis los segundos de duración del timbre, luego presionad Intro en el teclado y a sonar (se parará una vez transcurridos los segundos indicados).

Ahora vamos a darle otra opción al sonido del timbre, ya que un sonido constante es muy simple, vamos a transformar ese sonido en un sonido intermitente añadiendo unas lineas de código,

# importamos la libreria GPIO
import RPi.GPIO as GPIO
# importamos la libreria time
import time
# desactivamos mensajes de error
GPIO.setwarnings(False)
# indicamos el uso de  la identificacion BCM para los GPIO
GPIO.setmode(GPIO.BCM)
# indicamos que el GPIO18 es de salida de corriente
GPIO.setup(18,GPIO.OUT)
# solicitamos al usuario que introduzca los segundos de duracion total
segundos_d=int(input("duracion del sonido (segundos): " ))
# solicitamos al usuario el tiempo del sonido activo
tiempo_sonido=float(input("duracion del sonido intermitente (segundos): "))
# solicitamos al usuario el tiempo del sonido en pausa
tiempo_silencio=float(input("duracion del silencio intermitente (segundos): "))
# guardo en una variable el momento actual en segundos
inicio=time.time()
# guardo en una variable el momento actual mas los segundos de duracion
final=time.time()+segundos_d
# mientras la primera variable no supere a la segunda se repite el bucle
while inicio<final:
    # enviamos la orden de encender el timbre
    GPIO.output(18, True)
    # con esta orden mantenemos el sonido el tiempo indicado
    time.sleep(tiempo_sonido)
    # enviamos la orden de apagar el timbre
    GPIO.output(18, False)
    # con esta orden mantenemos el silencio el tiempo indicado
    time.sleep(tiempo_silencio)
    # actualizo la variable para controlar el tiempo que ha pasado
    inicio=time.time()
else:
    # si la primera variable no es inferior a la segunda 
    # se ha superado el tiempo y paramos el sonido

    GPIO.output(18, False)

con este código, al ejecutarlo ahora el programa nos pide tres valores, la duración total del sonido del timbre (en segundos y tiene que ser un valor entero 1, 2, 3, ...), luego nos pide el tiempo que dura el timbre activado (que al ser un valor float puede tener un valor entero o decimal por ejemplo 1, 2, 3 o 0.1, 0.2, 0.3), en tercer lugar nos pide el tiempo que dura la pausa del timbre (que al ser un valor float puede tener un valor entero o decimal por ejemplo 1, 2, 3 o 0.1, 0.2, 0.3). Una vez introducidos los valores y hacer Intro con el teclado el sonido intermitente empieza a sonar. Fijaos que ahora he trasladado la activación del sonido dentro del bucle while, donde además he añadido la orden time.sleep(x), esta orden detiene con un "delay" la ejecución del código durante el tiempo indicado, de esta forma activado y desactivando el sonido, generamos un sonido intermitente, hasta que finaliza el tiempo total del sonido.

Espero que no os hayáis perdido hasta ahora! la verdad es que el código se puede mejorar un poco, ahora que ya tenemos un timbre que podemos controlar, vamos a estructurar correctamente el código, creando una función que generará el sonido del timbre, y un bucle infinito que ejecutará esta función de forma repetida cada vez que finalice la acción de hacer sonar el timbre. Aunque parezca complicado, es muy sencillo

# importamos la libreria GPIO
import RPi.GPIO as GPIO
# importamos la libreria time
import time
# desactivamos mensajes de error
GPIO.setwarnings(False)
# indicamos el uso de  la identificacion BCM para los GPIO
GPIO.setmode(GPIO.BCM)
# indicamos que el GPIO18 es de salida de corriente
GPIO.setup(18,GPIO.OUT)
# definimos una funcion que recibe tres parametros y que genera con un
# bucle el sonido intermitente
def encender_timbre(tiempo_total,tiempo_activo,tiempo_pausa):
    # guardo en una variable el momento actual en segundos
    inicio=time.time()
    # guardo en una variable el momento actual más los segundos de duracion
    final=time.time()+tiempo_total
    # mientras la primera variable no supere a la segunda se repite el bucle
    while inicio<final:
        # enviamos la orden de encender el timbre
        GPIO.output(18, True)
        # con esta orden mantenemos el sonido el tiempo indicado
        time.sleep(tiempo_activo)
        # enviamos la orden de apagar el timbre
        GPIO.output(18, False)
        # con esta orden mantenemos el silencio el tiempo indicado
        time.sleep(tiempo_pausa)
        # actualizo la variable para controlar el tiempo que ha pasado
        inicio=time.time()
    else:
        # si la primera variable no es inferior a la segunda 
        # se ha superado el tiempo y paramos el sonido
        GPIO.output(18, False)
#
# creamos bucle infinito que solicita los datos al usuario para ejecutar
# la funcion
while(True):
    # solicitamos al usuario que introduzca los segundos de duracion
    segundos_d=int(input("duracion del sonido (segundos): " ))
    # solicitamos al usuario el tiempo del sonido activo
    tiempo_sonido=float(input("duracion del sonido intermitente (segundos): "))
    # solicitamos al usuario el tiempo del sonido en pausa
    tiempo_silencio=float(input("duracion del silencio intermitente (segundos): "))
    # llamamos a la funcion que enciende el timbre con los valores
    encender_timbre(segundos_d,tiempo_sonido,tiempo_silencio)

como podéis ver he creado la función encender_timbre(x,y,z) que ejecuta el código necesario para hacer sonar el timbre, y al final he añadido un bucle que pide al usuario los datos necesarios y ejecuta la función, y una vez acabada la ejecución vuelve a pedir los datos, ahora lo podéis ejecutar todas las veces que queráis.

Espero que os haya gustado (acepto críticas), hasta pronto!