1. Introduction

Qui n'a jamais rêvé de pouvoir, depuis son lit ou depuis n'importe quel endroit dans la maison commander la fermeture de ses stores lorsqu'il commence à pleuvoir, dans le but de ne pas avoir à nettoyer toutes les fenêtres le lendemain? Ou de les ouvrir le matin alors que les oiseaux commencent à chanter sans avoir besoin de sortir ne serait-ce qu'un pied du chaleureux duvet?

Maintenant cela sera possible grâce à ce projet visant à commander le déplacement des stores électriques depuis n'importe quel périphérique connecté au réseau domestique. En effet, la commande des relais permettant le mouvement des stores sera confiée à un RaspberryPi, faisant office de mini-serveur web.

DSC_0007.jpg

2. Matériel et méthodes

Il faudra reprendre le matériel du projet précédent en ajoutant au Raspberry une connexion au réseau, qu'elle soit par câble ou sans-fil. Pour connecter un adaptateur Wi-Fi USB au Raspberry, il est conseillé d'utiliser un concentrateur USB doté d'une alimentation propre car les adaptateurs étant très gourmands, le Raspberry pourrait faiblir, voire s'éteindre.

Côté nouveauté, il faudra installer Apache2 sur le RaspberryPi, pour le transformer en serveur web, PHP, pour la conception des pages, et bootstrap qui permet une personnalisation des pages internet et qui va être utilisé pour personnaliser les boutons de commande, ainsi que les drivers allant avec votre antenne Wi-Fi, si vous avez choisi ce moyen de connexion au réseau.

2.1 Principe du projet

Pour commencer, il faut tout d'abord exposer le principe de ce projet. Le schéma ci-dessous vous éclairera.

schemaReseau.PNG

Figure 2.1 | Schéma représentatif du réseau domestique

Le RaspberryPi est considéré ici comme un serveur. Il est connecté à un routeur au réseau domestique, tous les autres périphériques connectés sur celui-ci pourront y accéder en entrant dans un navigateur, l'adresse de l'hôte ainsi que la ressource cherchée. Une fois arrivé sur la page souhaitée, celle-ci comprendra 3 boutons par store, afin de le monter, l'arrêter ou le descendre. En appuyant sur un des boutons, cela va créer un fichier ayant pour nom l'action souhaitée ainsi que l'identité du store dans un répertoire cible. En parallèle, un script python va tourner en boucle, vérifiant à chaque tour, si un bouton physique est appuyé, mais aussi si un fichier existe dans ce répertoire cible. Si le fichier en question existe ou si un bouton est pressé, alors le RaspberryPi va exécuter les étapes pour bouger les stores.

2.2 Mise en place de la page web

  1. Remarque préliminaire: La compatibilité avec tous les navigateurs n'est pas certifiée. Firefox a été utilisé lors de ce projet.

Dans ce chapitre, nous allons voir étape par étape comment faire pour mettre en place la commande à distance des stores. La page internet va se présenter comme suit:

pageBouton.PNG

Figure 2.2 | Page internet comprenant le boutons de commande des stores

La page va comporter du PHP, ainsi que bootstrap concernant l'apparence des boutons.

2.2.1 La fonction de la page web avec PHP

Pour commencer il faut installer Apache2 et PHP.

sudo apt-get install apache2 php5

Ceci étant fait, les pages web ou ressources que vous voulez mettre à dispositions seront dans le répertoire /var/www/. Il y a ici déjà un fichier HTML d'exemple. Nous allons modifier ce fichier et le renommer en index.php. (les fichiers nommés "index" sont automatiquement affichés lors du chargement de la page, à l'instar d'une page nommée différemment.)

Maintenant concernant le contenu du fichier index.php, il faut commencer par insérer des styles pour que le positionnement des boutons soit directement dans le head soit dans un fichier main.css sans oublier le tag <link>. Personnellement j'ai opté pour le tag <style type="text/css"> sachant que je n'ai pas eu besoin de mettre beaucoup d'éléments de présentation.

<style type="text/css">
	.element { width: 130px; margin-left:2em; float: left;}
	.element .title { font-size:1.2em; font-weight:bold; text-align:center;}
	
	button {width: 150%;}
</style>

Le .element sera le style pour les boutons.

Dans un tag PHP: On définit une chaine de caractères ayant pour but de stocker le chemin d'accès d'écriture des fichiers "commandes", en n'oubliant pas de mettre une barre oblique après le dernier répertoire. Choisissez donc un répertoire ou créez en un si vous voulez que vos fichiers se situent à un endroit précis. Pour définir le chemin d'accès:

define('PATH', '/chemin_dacces_au_repertoire_cible/');

Cela évitera aussi de devoir changer le chemin d'accès à plusieurs endroits. Suite à cela, dans le body nous allons définir 3 chaînes de caractères (string) dit "états", qui seront les états du mouvement des stores.

define('ACTION_UP',     'up');
define('ACTION_DOWN',	'down');
define('ACTION_STOP',	'stop');

Nous allons maintenant définir un tableau (array) nommé aMyMotor qui va associer une identification (p. ex. 1, 2, 3, etc...) à un nom (p. ex. Store salon, store chambre, store garage). Plus tard nous allons créer une fonction qui génère des boutons monter/stop/descendre pour chaque store ajouté à aMyMotor. Ainsi si vous deviez ajouter un store, il suffit de l'ajouter dans ce tableau, plutôt que de réécrire tout les boutons.

Par convention et par compréhension, lors de la création du nom d'une variable, une lettre minuscule correspondant au type de variable est préférable. (p. ex. array -> a, int -> i, string -> s) Nous allons ensuite créer une fonction php qui va créer un fichier ayant pour nom l'action souhaitée et le numéro du store, qui seront spécifiés comme requête dans le lien.

function fWriteFile($sAction, $iMotor) {}

Et qui va retourner une valeur True si la fonction s'est exécutée normalement. Le principe de cette fonction est le suivant: on va créer un nom de fichier en deux partie, d'abord l'action du store (p. ex. up, down, stop) suivit d'un trait de soulignement et le numéro d'identité du store. Pour cela il faut utiliser des conditions if. Néanmoins, en PHP, il existe une fonction (switch) qui permet de contrôler la même variable, ou chaîne de caractère pour différent cas.

switch($sAction){
	case "up":
		$sFileName = ACTION_UP.'_';
	break;
	
	case "stop":
		$sFileName = ACTION_STOP.'_';
	break;
	
	case "down":
		$sFileName = ACTION_DOWN.'_';
	break;

Dans cet exemple, la chaîne de caractère sFileName se voit attribuer la valeur de ACTION_*** suivie d'un trait de soulignement.
Ensuite, le numéro id du store voulu doit être ajouté.

$sFileName .= $iMotor;

Maintenant, concernant l'écriture de fichier sur le serveur, je vous conseille de vous renseigner sur cette page concernant l'ouverture et l'écriture du fichier et sur celle-ci pour la fermeture.
Dès lors, l'écriture du fichier se compose comme suit:

$rFile = fopen(PATH . $sFileName, 'a+') or die('The file '.PATH . $sFileName . 'can\'t be created!');

fclose($rFile);

Cette fonction ne doit tourner que si l'action et le numéro du store sont définis dans la barre de recherche. On va donc créer une condition qui va vérifier si les deux arguments ont été précisés dans le lien. Si oui il va appeler la fonction qui crée le fichier cible. Nous allons utiliser la méthode GET qui consiste à prendre l'information d'un formulaire qui est contenu dans l'URL.

if($_GET['action'] && $_GET['motor']){
	fWriteFile($_GET['action'], $_GET['motor']);
}

Ainsi, lorsque l'URL comportera /index.php?action=up&motor=1 à la fin, la méthode GET prendra la valeur de action et la mettra comme premier argument pour la fonction fWriteFile elle prendre également la valeur de motor mais la mettra comme deuxième argument pour la fonction. A présent, nous sommes prêt à passer à la confection des boutons.

2.2.2 Les boutons avec PHP et Bootstrap

Il vous faut tout d'abord télécharger le librairie utile Bootstrap disponible ici. Cette archive est à décompresser dans le même répertoire que votre fichier index.php.

Une petite introduction à Bootstrap est sûrement nécessaire. Bootstrap est un framework dont le but de rendre plus facile et surtout plus rapide le développement web. De ce fait, il ne nous sera pas nécessaire de taper 15 lignes pour rendre nos boutons jolis puisque des expressions toutes faites sont déjà disponibles. Il faut savoir qu'avec les librairies que vous venez de télécharger vous avez à disposition des icônes et toutes sortes de personnalisations. Elles sont répertoriées sur ce site.

Nous avons d'abord besoin d'initier une chaîne de caractères qui va contenir les boutons.

$sContent = '';

Pour commencer, nous allons créer tout simplement une condition "pour chaque" (foreach) avec comme argument le tableau que nous avions créé précédemment. Il conviendra de spécifier deux variables, une de type int pour le numéro d'ID du store et l'autre de type string pour le nom donné au store.

foreach($aMyMotor as $iMotorNum=>$sPlace)

Où:

  • aMyMotor est le tableau déjà défini
  • iMotorNum est le numéro d'ID du store
  • sPlace est le nom du store

Dans cette condition, nous allons ajouter à la chaîne de caractères sContent les tags des boutons, ainsi que leur spécificités.
Un bouton se construit de la manière suivante:

	<button type="button" class="ajouter_les_specifications_du_bouton_ici" 
	  onclick="action_a_effectuer_lors_de_l_appuis_sur_le_bouton">
		<span class="ajouter_une_icone_ici"></span> nom_affiche_sur_le_bouton </button>

Dans la class nous allons ajouter la couleur et la taille du bouton. Voici quelques exemples, ils représentent les tailles et les couleurs basiques.

tableauBouton.PNG

Figure 2.3 | Illustrations des personnalisations de taille et de couleurs

Pour ajouter une icône, vous pouvez voir les entrées correspondantes ici. (p. ex. glyphicon glyphicon-home)

Lors de l’événement onclick, nous voulons que la page soit re-générée, en ayant cette fois les paramètres correspondant au bouton appuyé.
(p. ex. onclick="location.href=\'index.php?action=up&motor="1")
Voilà maintenant nous sommes capable de tout rassembler pour écrire la partie concernant les boutons.

foreach($aMyMotor as $iMotorNum=>$sPlace){
	$sContent .= 
	'<div class="element">
		<div class="title">'.$sPlace.'</div>
		<button type="button" class="btn btn-primary btn-lg" 
		  onclick="location.href=\'index.php?action=up&motor='.$iMotorNum.'\'">
			<span class="glyphicon glyphicon-chevron-up"></span> Monter</button><br />
			
		<button type="button" class="btn btn-danger btn-lg" 
		  onclick="location.href=\'index.php?action=stop&motor='.$iMotorNum.'\'">
			<span class="glyphicon glyphicon-stop"></span> Stop</button><br />
			
		<button type="button" class="btn btn-primary btn-lg" 
		  onclick="location.href=\'index.php?action=down&motor='.$iMotorNum.'\'">
			<span class="glyphicon glyphicon-chevron-down"></span> Descendre</button><br />
	</div>';

Les tag <div> permettent de créer des groupes en HTML. Ils sont donc utiles pour organiser les boutons.

Mais cette condition, aussi jolie soit-elle ne fait rien de concret. Elle ajoute juste une longue chaine de caractère à une variable. Pour l'imprimer sur la page, il faut faire recourt à fonciton echo.

echo $sContent;

2.2.3 Accéder à la page de commande

Lorsque vous connectez le RaspberryPi au réseau, il se voit attribué une adresse IP. Cette adresse va nous servir à nous connecter depuis un autre périphérique. Pour connaitre cette adresse, il faut entrer dans le terminal: ifconfig.

Cela devrait vous donner un résultat du genre:

eth0      Link encap:Ethernet  HWaddr 00:07:E9:D5:E0:5D  
          inet addr:192.168.169.129  Bcast:192.168.169.255  Mask:255.255.255.0
          inet6 addr: 2001:6a8:204::1/48 Scope:Global
          inet6 addr: fe80::207:e9ff:fed5:e05d/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:346293248 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1089423722 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1501808809 (1.3 GiB)  TX bytes:4184566400 (3.8 GiB) 

L’adresse IP est donc écrite, il vous suffit de la recopier en ajoutant le chemin d'accès de votre fichier index.php depuis le répertoire /var/www non-compris.

IPaddress.PNG

2.3 Actualisation du script existant

Dans ce chapitre, nous allons voir étape par étape comment faire pour remanier le script permettant la commande des stores. Pour ce faire, il faut suivre le projet précédent, même si vous avez les bases, pour être sûr que tout fonctionne et que tout soit bien en place.
Tout d'abord, rappelons ce dont nous avons besoin: de vérifier si un fichier existe, et si tel est le cas, l'effacer et ensuite mettre le store en mouvement. Il nous faut donc importer une librairie qui est déjà présente sur le RaspberryPi qui s'appelle os.path. Contrairement au projet précédent, il y a cette fois trois boutons par store, ce qui en fait deux de plus si je compte bien. Il faudra par conséquent ne pas se tromper avec les variable états.

Commençons par importer une nouvelle librairie os.path au début du script python. Les deux nouvelles fonctions que nous allons devoir utiliser sont: os.path.isFile() pour vérifier si le fichier existe et os.path.remove pour supprimer le fichier. La fonction os.path.isFile() retourne une valeur True si le fichier existe et False s'il n'existe pas. Dans la boucle while True:, nous allons ajouter des conditions si. Si tel fichier existe, alors supprime-le, active/désactive les bons relais et change les variables états correspondantes. Voici comment cela va se présenter pour un cas quelconque.

if os.path.isFile(/chemin_d'acces/action_numeroStore):
    os.path.remove(/chemin_d'acces/action_numeroStore);
    GPIO.output(relaisVoulu, True/False );
    etatup/etatdown = True/False;

3. Résultats

Rappelons ce que vous devriez avoir comme page web, elle est disponible sur ce lien. Bien sûr cette page est une démonstration et la fonction d'écriture de fichier est désactivée pour éviter l'apparition d'erreurs étant donné que le dossier de destination d'écriture des fichiers n'existe pas. De plus, cela ne servirait à rien d'avoir des fichiers sur ce serveur.

Concernant le script Python, vous obtiendrez une présentation de la sorte à rajouter aux bons endroits du script.

import os.path
import GPIO as GPIO

while True:

    if (os.path.isfile("/ProjetOCI/webCommands/up_1")):
        os.remove("/ProjetOCI/webCommands/up_1")
        GPIO.output(15, True)
        etat1D = False
        GPIO.output(16, False)
        etat1U = True
        
				
    elif (os.path.isfile("/ProjetOCI/webCommands/down_1")):
        os.remove("/ProjetOCI/webCommands/down_1")
        GPIO.output(16, True)
        etat1U = False
        GPIO.output(15, False)
        etat1D = True
				
    elif os.path.isfile("/ProjetOCI/webCommands/stop_1"):
        os.remove("/ProjetOCI/webCommands/stop_1")
	GPIO.output(16, True)
	etat1U = False
	GPIO.output(15, True)
	etat1D = False

    if (os.path.isfile("/ProjetOCI/webCommands/up_2")):
        os.remove("/ProjetOCI/webCommands/up_2")
        GPIO.output(18, True)
        etat2D = False
        GPIO.output(22, False)
        etat2U = True
 
    elif (os.path.isfile("/ProjetOCI/webCommands/down_2")):
        os.remove("/ProjetOCI/webCommands/down_2")
        GPIO.output(22, True)
        etat2U = False
        GPIO.output(18, False)
        etat2D = True

    elif os.path.isfile("/ProjetOCI/webCommands/stop_2"):
        os.remove("/ProjetOCI/webCommands/stop_2")
	GPIO.output(18, True)
	etat1U = False
	GPIO.output(22, True)
	etat1D = False

4. Discussion

Il n'y a pas de conflit entre les différentes conditions car lorsqu'une condition est engagée, le script va la finir avant de passer à une autre. Il n'y a par conséquent pas la possibilité d'abimer le store en lui envoyant du courant des deux côtés.

Malheureusement, on ne peut pas accéder à la page de commande, car elle est disponible uniquement en réseau local. Par conséquent, si vous oublier un jour de monter ou descendre vos stores en allant au travail, vous ne pourrez pas le faire depuis le-dit lieu. Néanmoins, cela permet d'éviter que des petits malins s'amusent à bouger vos stores lorsque vous ne le voulez pas. Ainsi, seuls les individus connectés aux même réseau que le RaspberryPi pourront le commander à distance.

La technique utilisée est peut-être pas la plus rapide et la plus simple, mais elle est accessible avec relativement peu de connaissance. Le moyen le plus simple aurait été de mélanger plusieurs langages, de l'AJAX, du JQuerry, du PHP et de l'HTML mais comme vous pouvez surement vous en rendre compte, cette méthode demande de certaines connaissances en matière de langage de programmation. C'est pourquoi ce projet propose une alternative viable par rapport aux connaissances acquises. En ne connaissant que les bases de PHP, de Python et d'HTML il est tout de même possible de s'en sortir.

Question budget, vous n'allez pas vous ruiner car lors de ce projet pas le moindre sous n'a été dépensé. (hormis peut-être les frais d'alimentation électrique)

5. Conclusion

Voilà les stores fonctionnels! Amusez-vous bien avec ce nouveau gadget!

Il serait envisageable lors du démarrage du RaspberryPi ou du moins du script commandant les boutons, que celui-ci s'assure que les stores soient en haut en attendant le temps qu'il faudrait aux stores pour monter totalement. Suite à cela, il serait possible de mettre des options sur la page de commande pour que les stores soient ouverts à moitié ou alors qu'ils soient fermés mais avec les lamelles ouvertes. Il faudrait donc mesurer le temps que mets un store pour parcourir la distance entière.

Temps voulu = Temps tôt * %d'ouverture ou de fermeture du store

Cette nouvelle fonction comporte des difficultés, car le RaspberryPi n'a a priori aucun moyen de connaître la portion du store. Il faudrait initialiser une variable qui corresponde à la position du store et la faire changer suivant le temps passer à descendre ou alors à monter.

6. Références