Détecteur de mouvement PIR

Ce tutoriel explique comment mettre en œuvre un détecteur de mouvement analogique PIR avec MicroPython. Ce capteur analyse le rayonnement infrarouge dans son champ de vision et en déduit une présence ou un mouvement. Il peut servir notamment pour gérer l’éclairage automatique d’un lieu ou encore dans un système d’alarme deux exemples couverts par ce tutoriel.

Le capteur de mouvement (PIR motion sensor) :


PIR motion sensor


Crédit image : Seeed Studio

Pour commencer : Programmer un éclairage automatique

Dans un premier temps, nous allons réaliser un système qui allume une LED pendant quelques temps lorsqu’un mouvement est détecté. Ce sera l’occasion d’expliquer et d’utiliser le mécanisme des interruptions externes du microcontrôleur STM3WB55RG.

Matériel requis et montage

  1. Une carte d’extension de base Grove
  2. La carte NUCLEO-WB55
  3. Un module capteur de mouvement infrarouge PIR Grove
  4. Un module Grove LED (peu importe la couleur de la LED). Ne vous trompez pas sur la polarité de la LED lorsque vous la brancherez dans le module ; la patte la plus longue va dans le connecteur “+” !

Le module capteur PIR est branché sur le connecteur D4 du Grove base shield et le module LED sur le connecteur D8.

Les interruptions

Le mécanisme des interruptions donne aux microcontrôleurs l’aptitude à réagir à des évènements extérieurs de façon presque instantanée. Un circuit intégré spécialisé, appelé contrôleur d’interruptions, contenu dans le microcontrôleur (le STM32WB55 en l’occurrence), reste à l’écoute des signaux en provenance de certaines broches spécifiées par le programmeur.

Si une activité (front de tension montant et/ou descendant) est détectée sur l’une des ces broches, le contrôleur d’interruptions suspend le programme courant exécuté par le microcontrôleur et lui communique un autre programme, qu’il devra exécuter en priorité. Ce programme “injecté”, pour répondre à l’urgence d’une situation imprévue, s’appelle la routine de service de l’interruption (ISR pour Interrupt Service Routine en anglais). Une fois qu’il a exécuté l’ISR, le microcontrôleur reprend le cours de l’exécution du programme suspendu.

Dans le code MicroPython qui suit, le capteur PIR est géré avec une interruption qui survient lorsque le signal en provenance du capteur PIR passe de 0V à 3.3V. En cas de mouvement, l’interruption provoque l’affichage prioritaire du message “Mouvement détecté” sur le terminal de l’USB User et la LED de la carte s’allume pendant un laps de temps donné (une seconde). Puis le système d’alarme se réarme.

Précision importante : Le module PIR Motion sensor de Grove que nous avons sélectionné renvoie un signal non nul pendant une seconde après qu’il ait détecté un mouvement. Si d’autres mouvements surviennent pendant cette seconde, il ne les distinguera pas de celui qui l’a activé en premier. Vous ne pourrez donc pas augmenter la fréquence de détection au-delà de 1Hz.

Le code MicroPython

Vous pouvez télécharger les scripts MicroPython de ce tutoriel (entre autres) en cliquant ici.

Editez maintenant le script main.py du périphérique PYBFLASH :

# Objet du script : Mettre en œuvre un capteur de mouvement PIR
# Le module PIR Motion sensor de Grove que nous avons sélectionné renvoie un signal non nul 
# pendant une seconde après qu'il ait détecté un mouvement. Si d'autres mouvements surviennent
# pendant cette seconde, il ne les distinguera pas de celui qui l'a activé en premier. 

from machine import Pin
from time import sleep

# Variable globale qui sera modifiée par la routine de service de l'interruption
motion = False

# Routine de service de l'interruption
# Cette fonction ne fait qu'une chose : elle donne la valeur "True" à la variable globale "motion".
def handle_interrupt(pin):
	global motion
	motion = True

led = Pin('D8', Pin.OUT) # Broche de la LED

pir = Pin('D4', Pin.IN) # Broche du capteur PIR

# On "attache" l'interruption à la broche du capteur PIR.
# Cela signifie que le gestionnaire d'interruptions contenu dans le STM32WB55 va "surveiller" 
# la tension sur la broche D4. Si cette tension augmente et passe de 0 à 3.3V (IRQ_RISING)
# alors le gestionnaire d'interruption forcera le STM32WB55 à exécuter la fontion "handle_interrupt".

pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)

while True:
	if motion: # si motion = True alors, cela signifie que l'interruption a eue lieu.
		print('Mouvement détecté!')
		led.value(1) # Allume la LED
		sleep(1) # Temporise pendant 1 seconde
		led.value(0) # Eteint la LED
		motion = False
		print('En attente...')
	# Place le microcontrôleur en sommeil en attendant la prochaine interruption
	pyb.wfi() 

Manipulation

Lancez le script avec la combinaison de touches [CTRL]-[D] dans votre terminal série.
Lorsque vous bougez devant le capteur PIR, la LED s’allume et s’éteint au bout d’une seconde.

Pour aller plus loin : Temporiser sans bloquer

Vous avez sans doute remarqué que nous faisons très souvent usage d’instructions de temporisation telles que “sleep()” ou encore “sleep_ms()”. Dans certaines situations, ces instructions ne sont pas souhaitables, car elles sont bloquantes : lorsqu’elles sont appelées, le script est suspendu et ne fait rien d’autre. Bien que l’exemple du capteur PIR, qui a une latence d’une seconde, ne soit pas idéal pour en démontrer l’utilité, sachez qu’il existe des “astuces” pour temporiser certaines parties d’un programme sans pour autant le bloquer. On peut par exemple suivre le temps écoulé et activer certaines actions en fonction de sa valeur. Ceci permet, par exemple, de faire clignoter simultanément plusieurs LED à des fréquences différentes. Pour réaliser ces temporisations non bloquantes, on utilise les fonctions “time.ticks_ms()” et “time.ticks_diff()”.

On utilise par ailleurs la méthode pyb.wfi() pour placer le microcontrôleur en mode économie d’énergie entre deux interruptions. Cette fonction doit être utilisée dans des scripts qui font aussi appel à des interruptions car elles sont nécessaires pour sortir sur commande le microcontrôleur de son sommeil. Notez bien que d’autres interruptions de MicroPython, qui ont lieu toutes les millisecondes, vont également réveiller le microcontrôleur.

Le code MicroPython

Vous pouvez télécharger les scripts MicroPython de ce tutoriel (entre autres) en cliquant ici.

Voici l’adaptation du code précédent avec ces instructions :

# Objet du script : Mettre en œuvre un capteur de mouvement PIR avec une temporisation non blocante.
# Le module PIR Motion sensor de Grove que nous avons sélectionné renvoie un signal non nul pendant une seconde après qu'il ait
# détecté un mouvement. Si d'autres mouvements surviennent pendant cette seconde, il ne les distinguera pas de celui qui l'a 
# activé en premier.
# On utilise par ailleurs la méthode "pyb.wfi()" pour placer le microcontrôleur en mode économie d'énergie entre deux interruptions.
# Cette fonction doit être utilisée dans des scripts qui font aussi appel à des interruptions car elles sont nécessaires pour
# sortir sur commande le microcontrôleur de son sommeil.

from machine import Pin
import time # Bibliothèque pour gérer les temporisations

# Routine de service de l'interruption
# Cette fonction ne fait qu'une chose : elle donne la valeur "True" à la variable globale "motion".

def handle_interrupt(pin):
	global motion
	motion = 1 # On signale le mouvement

led = Pin('D8', Pin.OUT) # Broche de la LED signalant l'alarme

pir = Pin('D4', Pin.IN) # Broche du capteur PIR

# On "attache" l'interruption à la broche du capteur PIR.
# Cela signifie que le gestionnaire d'interruptions contenu dans le STM32WB55 va "surveiller" 
# la tension sur la broche D4. Si cette tension augmente et passe de 0 à 3.3V (IRQ_RISING)
# alors le gestionnaire d'interruption forcera le STM32WB55 à exécuter la fontion "handle_interrupt".

pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)

motion = 0 # Variable globale qui sera modifiée par la rouine de service de l'interruption
start = 0 # Variable globale pour mesurer le temps écoulé 

while True:
	
	# On mesure le temps en millisecondes écoulées depuis le démarrage de MicroPython
	now = time.ticks_ms()

	if led.value() == 0 and motion == 1 : # Si la LED est éteinte et que l'interruption a eue lieu...
		print('Mouvement détecté!') 
		# On mémorise le le temps en millisecondes écoulées depuis le démarrage de MicroPython
		start = time.ticks_ms()
		led.value(1) # On allume la LED
	
	# Si la LED est allumée et que 1 seconde s'est écoulée depuis la dernière détection de mouvement...
	elif led.value() == 1 and time.ticks_diff(now, start) > 999: 
		print('En attente...')
		led.value(0) # On éteint la LED
		motion = 0 # On réinitialise l'indicateur d'occurence d'interruption
		
	# Place le microcontrôleur en sommeil en attendant la prochaine interruption
	pyb.wfi()

Pour aller encore plus loin : Programmer une alarme de mouvement

La suite logique de ce tutoriel consiste à programmer une alarme de mouvement associant un buzzer au capteur PIR, elle est présentée ici.