Raspberry Pi : Plusieurs fonctions avec un seul bouton

Dans un des premiers articles de hardware-libre, j’ai expliqué comment ajouter un bouton d’extinction ou de reboot au Raspberry Pi.

Nous allons aujourd’hui améliorer ce système en rajoutant une possibilité bien pratique : des fonctions différentes selon la durée d’appui sur le bouton.

Pour faire suite à l’article précédent, nous aurons désormais 2 fonctions :
- reboot sur un appui entre 1 et 4 secondes,
- arrêt complet sur un appui long de plus de 4 secondes
Le code fourni ici rajoute une autre possibilité d’appui très court, entre 0.2 et 1 seconde, mais nous l’utiliserons simplement afin d’éviter un reboot en cas d’appui malencontreux.

Bien que ce ne soit pas obligatoire, nous allons modifier un peu le circuit. La raison est qu’en ayant le bouton directement entre le GPIO et le GND, si une erreur dans un script configure le GPIO en sortie, et qu’on le presse à ce moment là, on va créer un court-circuit au niveau du GPIO, qui risque de griller.
Nous allons donc supprimer complètement ce risque en rajoutant simplement une résistance de pull-up. Voici un petit schéma rapide de cette modification :
butt-schem
La résistance de pull-up permet de limiter le courant si le GPIO est malencontreusement configuré en sortie, et le bouton pressé.

Coté code, les commentaires sont je pense suffisant pour expliquer le tout :

#!/usr/bin/env python2.7

from time import sleep
import subprocess
import RPi.GPIO as GPIO

# On choisit le GPIO 23 (pin 16) pour notre bouton
CHANNEL = 23 

# On definit nos durees
long_press = 1
very_long_press = 4

# on met RPi.GPIO en mode notation BCM
GPIO.setmode(GPIO.BCM)

# on initialise le GPIO 23 en mode entree
GPIO.setup(CHANNEL, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# notre fonction extinction
def shutdown():
	subprocess.call(['shutdown -h now "Arret du systeme par bouton GPIO" &'], shell=True)

# notre fonction reboot
def reboot():
	subprocess.call(['sudo reboot "Reboot du systeme par bouton GPIO" &'], shell=True)

# notre fonction de gestion du bouton
def system_button(CHANNEL):
	# cette variable servira a stocker le temps de pression
	button_press_timer = 0

	while True:
			if (GPIO.input(CHANNEL) == False) : # le bouton a ete presse...
				button_press_timer += 0.2 # ... on enregistre le temps que cela dure

			else: # le bouton a ete relache, on compte combien de temps cela a dure
				if (button_press_timer > very_long_press) :
					print "very long press : ", button_press_timer
					shutdown()

				elif (button_press_timer > long_press) :
					print "long press : ", button_press_timer
					reboot()

				elif (button_press_timer > 0.2):
					print "short press : ", button_press_timer

				button_press_timer = 0
			# on attend 0.2 secondes avant la boucle suivante afin de reduire la charge sur le CPU
			sleep(0.2)

# on met le bouton en ecoute par interruption, detection falling edge sur le canal choisi, et un debounce de 200 millisecondes
GPIO.add_event_detect(CHANNEL, GPIO.FALLING, callback=system_button, bouncetime=200)

# ici vous pouvez mettre du code qui sera execute normalement, sans influence de la fonction bouton
try:
	while True:
		# faites ce qui vous plait
		sleep (2)

# on reinitialise les ports GPIO en sortie de script
except KeyboardInterrupt:
	GPIO.cleanup()
GPIO.cleanup()

Vous remarquerez qu’on a introduit le module subprocess à la place du module os pour appeler nos commandes shutdown et reboot, ce qui est bien plus efficace « pythoniquement » parlant. Je vous invite à suivre ce lien pour en apprendre plus à ce sujet.
L’option « bouncetime=200″ de la fonction « GPIO.add_event_detect », quant à elle, effectue un premier filtrage du bouton (pour éviter par exemple les micro-relachements lors d’un appui).

Pour améliorer le système, on pourrait également par exemple rajouter une led,
- allumée pour signaler que le Pi est démarré et prêt,
- clignotement rapide pour signaler un reboot,
- clignotement lent pour signaler une extinction,
……
A vous de faire preuve d’imagination, tout est possible ;)

Edit :
- En cas d’erreur du type « TypeError: set_callback() takes at most 3 arguments (4 given) » lors de l’execution du script, il vous faudra installer / mettre à jour le composant RPi.GPIO :

sudo apt-get update
sudo apt-get install python-dev
sudo apt-get install python-rpi.gpio

- En cas d’erreur avec la fonction reboot (usage: reboot [-n] [-w] [-d] [-f] [-h] [-i] …), il suffit de modifier la ligne 26 :

subprocess.call(['sudo reboot -f "Reboot du systeme par bouton GPIO" &'], shell=True)

ou encore :

subprocess.call(['sudo shutdown -r now "Reboot du systeme par bouton GPIO" &'], shell=True)

Merci à Djeremaille ;)