Monthly Archives: September 2014

Détecteur de mouvement (PIR) du Raspberry Pi intégré à la ZiBASE – Part 1/2

Dans cet article, je vais vous montrer comment intégrer, comme périphérique virtuel sur la ZiBASE, un détecteur de mouvement connecté à un Raspberry Pi. Nous allons voir l’ensemble des étapes , depuis la connexion du détecteur (PIR) au RaspBerry Pi , la création du script python , la gestion du service pir et le scénario sur la ZiBASE qui va nous permettre d’alimenter un détecteur virtuel, qui pourra ensuite être intégré à une alarme Zibase ou être utilisé dans d’autres scénarios.zibasepi-motion-plate-forme

Pour l’installation du module, j’ai suivi l’excellent article en anglais de Matt Hawkins intitulé  Cheap PIR Sensors and the Raspberry Pi – Part 1 dont je reprends les grandes lignes pour montrer comment brancher et faire fonctionner ce merveilleux petit module.

Le matériel nécessairepir

Nous allons utiliser comme détecteur un module PIR 5V (Passive Infra Red) que l’on trouvera facilement dans une boutique en ligne pour quelques euros. Je l’ai personnellement trouvé pour $2.40 chez www.dx.com. Nous allons le  connecter à un Raspberry Pi  (dans mon cas, j’utilise un Raspberry Pi B+ qui tourne sous Linux Raspbian version Wheezy du 20/06/2014)

Branchement du module PIR20140916_173043

Voici un schéma représentant les trois sorties du module et leurs branchements sur les pattes GPIO sur Raspberry Pi. Le module a deux potentiomètres qui permettent d’ajuster ses performances. Un pour la sensibilité (Sx) et le second (Tx) pour le réglage du temps où la sortie (pin OUT) du module reste à 3V lors de la détection d’un mouvement.

PIR Module

Module PIR

20140916_173058

Paquets requis pour la gestion GPIO du Raspberry Pi

Pour la bonne éxécution du script python, il est nécessaire auparavant d’installer les différents paquets suivants:

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

Le script Python

J’ai repris le script donné en exemple par Matt, et l’ai adapté afin qu’il puisse tourner comme daemon linux et qu’il envoie une requête http à la ZiBASE lors de la détection de mouvement.

Les fichiers sont disponibles au téléchargement sur le site https://github.com/onlinux/Raspberry-Pi-PIR. Vous pourrez y télécharger l’ensemble du projet:

pi@raspi $ wget https://github.com/onlinux/Raspberry-Pi-PIR/archive/master.zip 

pi@raspi ~ $ unzip master.zip 
Archive: master.zip
b2cffd544e4ca4ec420723e08b7fb15508b8d83c
 creating: Raspberry-Pi-PIR-master/
 extracting: Raspberry-Pi-PIR-master/README.md 
 creating: Raspberry-Pi-PIR-master/logrotate.d/
 inflating: Raspberry-Pi-PIR-master/logrotate.d/pir 
 inflating: Raspberry-Pi-PIR-master/pir 
 inflating: Raspberry-Pi-PIR-master/pir.py 
pi@raspi ~ $
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/python
#
# pir.py
#
 
# Import required Python libraries
import time
import RPi.GPIO as GPIO
import requests
import logging
import signal
import sys
import threading
 
 
URL_ZIBASE = 'http://192.168.0.100/cgi-bin/domo.cgi?CMD=LM%2049'
MOTION_ALARM_DELAY =  60
 
logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', filename='/var/log/pir.log',level=logging.DEBUG)
 
def handler(signum = None, frame = None):
    logging.debug (' Signal handler called with signal '+ str(signum) )
    time.sleep(1)  #here check if process is done
    logging.debug( ' Wait done')
    sys.exit(0)   
 
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]:
    signal.signal(sig, handler)
 
# Each request  gets its own thread
class RequestThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        try:
           result = requests.get(url = URL_ZIBASE)
           logging.debug(" %s -> %s" % (threading.current_thread(), result))
        except requests.ConnectionError, e:
           logging.warning(' %s CONNECTION ERROR %s' % (threading.current_thread(), e) )
 
 
# Use BCM GPIO references
# instead of physical pin numbers
GPIO.setmode(GPIO.BCM)
 
# Define GPIO to use on Pi
GPIO_PIR = 7
 
logging.info( "PIR Module Holding Time Test (CTRL-C to exit)")
 
# Set pin as input
GPIO.setup(GPIO_PIR,GPIO.IN)
 
Current_State  = 0
Previous_State = 0
 
try:
 
  logging.info('Waiting for PIR to settle ...')
 
  # Loop until PIR output is 0
  while GPIO.input(GPIO_PIR)==1:
    Current_State  = 0
 
  logging.info('  Ready')
 
  # Loop until users quits with CTRL-C
  while True :
 
    # Read PIR state
    Current_State = GPIO.input(GPIO_PIR)
 
    if Current_State==1 and Previous_State==0:
      # PIR is triggered
      start_time=time.time()
      logging.info(' Motion detected!')
      # Record previous state
      Previous_State=1
      RequestThread().start()
 
    elif Current_State==0 and Previous_State==1:
      # PIR has returned to ready state
      stop_time=time.time()
      elapsed_time=int(stop_time-start_time)
      logging.info(" (Elapsed time : " + str(elapsed_time) + " secs)")
      Previous_State=0
      logging.info(' Going to sleep for %s seconds' % (MOTION_ALARM_DELAY))
      time.sleep(MOTION_ALARM_DELAY)
      logging.info('  Ready')
 
    time.sleep(1)	
 
finally:
	logging.info( "  Reset GPIO settings & Quit")
	# Reset GPIO settings
	GPIO.cleanup()

Le script dans les détails

Définition de la requête http vers la ZiBASE

16
URL_ZIBASE = 'http://192.168.0.100/cgi-bin/domo.cgi?CMD=LM%2049'

Nous définissons ici la requête http qui va être envoyée vers la zibase. Ma zibase a pour adresse IP sur mon réseau local (lan) 192.168.0.100. Il faudra bien sûr changer cette adresse et l’adapter à votre installation. Ensuite le paramètre CMD=LM 49 signifie ‘lancer l’exécution du scénario numéro 49′. Nous reviendrons plus tard sur cette notion de numéro de scénario lorsque nous aborderons la conception du scénario sur notre zibase.

 Durée de latence entre deux détections ou MOTION_ALARM_DELAY

17
MOTION_ALARM_DELAY =  60

Je définis ici, la durée en secondes ou le détecteur ignorera la détection de nouveaux mouvements. Une minute par défaut. En fait cela permet surtout de soulager la taille de la log. Ici, il n’y a pas de notion de durée de vie de la batterie comme dans les détecteurs classiques autonomes, donc si on le souhaite on peut très bien définir ce paramètre égale à 1 mais je n’en vois vraiment pas l’intérêt.

Configuration de la log

19
logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', filename='/var/log/pir.log',level=logging.DEBUG)

Nous définissons ici le nom du fichier log du daemon (/var/log/pir.log) ou tous les messages seront inscrits avec pour niveau d’information ‘DEBUG’.
Nous verrons plus tard, comment définir une entrée dans logrotate.d qui permet d’avoir une gestion des fichiers log automatisée.

Gestion des signaux envoyés au daemon

21
22
23
24
25
26
27
28
def handler(signum = None, frame = None):
    logging.debug (' Signal handler called with signal '+ str(signum) )
    time.sleep(1)  #here check if process is done
    logging.debug( ' Wait done')
    sys.exit(0) 
 
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]:
    signal.signal(sig, handler)


93
94
95
96
finally:
	logging.info( "  Reset GPIO settings & Quit")
	# Reset GPIO settings
	GPIO.cleanup()

Les signaux de terminaison de processus sont interceptés, ce qui permet à notre script de se terminer gracieusement et surtout de laisser une situation propre au niveau de la gestion des GPIO  du Raspberry Pi ( appel à la commande  GPIO.cleanup())

Notion de Thread

31
32
33
34
35
36
37
38
39
40
# Each request  gets its own thread
class RequestThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        try:
           result = requests.get(url = URL_ZIBASE)
           logging.debug(" %s -> %s" % (threading.current_thread(), result))
        except requests.ConnectionError, e:
           logging.warning(' %s CONNECTION ERROR %s' % (threading.current_thread(), e) )

Nous allons lancer la requête http vers la Zibase dans un thread, ce qui permet à notre script de ne pas être bloquant lors d’un problème de connexion à la zibase (problème réseau ou zibase déconnectée). Il m’est arrivé de voir la connexion refusée par la zibase, peut être était-ce lors du reboot après enregistrement d’un nouveau périphérique. Si la requête n’était pas exécutée dans un thread, lors d’un problème de connexion, le daemon serait tué!

Boucle de gestion de la détection de mouvement

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# Use BCM GPIO references
# instead of physical pin numbers
GPIO.setmode(GPIO.BCM)
 
# Define GPIO to use on Pi
GPIO_PIR = 7
 
logging.info( "PIR Module Holding Time Test (CTRL-C to exit)")
 
# Set pin as input
GPIO.setup(GPIO_PIR,GPIO.IN)
 
Current_State  = 0
Previous_State = 0
 
try:
 
  logging.info('Waiting for PIR to settle ...')
 
  # Loop until PIR output is 0
  while GPIO.input(GPIO_PIR)==1:
    Current_State  = 0
 
  logging.info('  Ready')
 
  # Loop until users quits with CTRL-C
  while True :
 
    # Read PIR state
    Current_State = GPIO.input(GPIO_PIR)
 
    if Current_State==1 and Previous_State==0:
      # PIR is triggered
      start_time=time.time()
      logging.info(' Motion detected!')
      # Record previous state
      Previous_State=1
      RequestThread().start()
 
    elif Current_State==0 and Previous_State==1:
      # PIR has returned to ready state
      stop_time=time.time()
      elapsed_time=int(stop_time-start_time)
      logging.info(" (Elapsed time : " + str(elapsed_time) + " secs)")
      Previous_State=0
      logging.info(' Going to sleep for %s seconds' % (MOTION_ALARM_DELAY))
      time.sleep(MOTION_ALARM_DELAY)
      logging.info('  Ready')
 
    time.sleep(1)

Test du script

A ce stade le script est fonctionnel. Pour tester son fonctionnement, il faut lancer le script sous root (sudo) et visualiser la log dans un autre terminal.
Si vous bougez devant votre détecteur de mouvement vous verrez la log réagir.

sudo_pir

tailf pir_log

Si vous tapez CTRL-C dans la fenêtre de lancement du script, dans la log, vous verrez que le signal a été intercepté et a permis au script de terminer proprement en envoyant son ordre GPIO.clean().

tailf pir_log1

 

Elaboration du scénario sur la ZiBASE

Avant de créer un scénario, il faut créer un détecteur virtuel pour qu’il puisse remonter les alertes de notre module PIR connecté au Raspberry PI.

Création du détecteur virtuel

creation_device

Nous devons créer un périphérique virtuel de catégorie détecteurs, type MS13 par exemple (détecteur de mouvement) .  Indiquez le nom de votre choix ( pour moi ce sera PI-MOTION) et lui attribuer un identifiant radio. Cet identifiant radio fictif doit être sous la forme “CS suivi de 9 chiffres’” ex: CS123456789,  pour ma part j’ai choisi CS123456787 car j’ai déjà 2 périphériques virtuels créés ( ouverture  fenêtre et détecteur de présence pour mon PSM02 Philio) ensuite  cliquez sur « Capture d’identifiant » puis  enfin, cliquez sur « Sauvegarder » après avoir coché, bien sûr, la case « Visualiser sur les interfaces  de l’utilisateur».

Nous avons à ce stade créer notre détecteur de présence virtuel. Nous devons maintenant l’alimenter et pour cela nous allons créer un scénario.

Création du scénario

scenario

 

Créez un nouveau scénario, donnez lui un nom ( pour moi ce sera PIR-MOTION-49) . Le nombre 49 correspond au numéro du scénario nouvellement créé. Pour connaître ce numéro qui nous  est nécessaire lors de la requête http dans notre script python, une fois le scénario créé, cliquez sur le bouton “Tester le scénario”, vous verrez ensuite dans le suivi d’activité le déclenchement  du scénario avec son numéro associé (pour moi c’est 49)  entre parenthèses.numero scenario

 

Une fois le numéro du scénario connu , nous pouvons construire l’url qui permettra à notre script pir.py de déclencher le scénario sur la ZiBASE.

Souvenez-vous, ligne 16 du script:

16
URL_ZIBASE = 'http://192.168.0.100/cgi-bin/domo.cgi?CMD=LM%2049'

Modifiez le numéro dans le fichier pir.py  LM%20XX , avec pour XX= votre numéro de script.

Une fois cette étape effectuée, si vous exécutez le script sur le raspberry Pi, celui-ci doit déclencher le scénario sur la ZiBASE qui va alimenter notre détecteur virtuel, et nous pouvons visualiser sur l’interface le déclenchement d’une alerte, si vous bougez devant votre module PIR, bien sûr!

 

pi-motion-plate-forme

Création du service pir

Dans l’article qui suivra PART 2, nous allons voir comment déclarer notre script pir.py comme un service linux, et le paramètrer pour qu’il s’exécute automatiquement au reboot du Raspberry Pi, et aussi paramétrer la gestion de la log avec logrotate.