Planet RaspFR

Bienvenue sur ce planet dédié au Raspberry Pi
mais aussi autres micro-ordinateurs et aux imprimantes 3D de type DIY.

Si vous voulez ajouter votre blog, n'hésitez pas à nous contacter.
Nous vous donnons rendez-vous sur le forum RaspFR

Le bouton poussoir un composant banal, ô combien, étonnant.

figure06Le bouton poussoir (BP) existe sous de nombreuses tailles, formes et technologies. Il est la principale Interface de communication entre l’Homme et la Machine (IHM). Son emploi est si courant que la plus part des utilisateurs ne soupçonnent même pas les phénomènes liées à sa mise en œuvre. Le bouton poussoir voit son origine dans la sphère de l’électromécanique.

Le BP est un « escargot » en terme de vitesse (à cause de l’humain qui l’actionne) par rapport au circuit électronique (analogique, logique ou numérique) qu’il commande. Les humains capables d’actionner un BP en moins de 10 ms sont rares.

NIVEAU_debutant

Introduction

Le vocable bouton poussoir regroupe également les interrupteurs et autres actionneurs qui ne sont qu’une forme particulière de ce composant.
figure02
La photo représente quelques spécimens de BP, interrupteurs, inverseurs et autres encodeurs mécaniques. On trouve également des BP à commande magnétique ou sous ampoule de verre/plastique avec des liquides conducteurs et autres contacts de relais. Les contacts peuvent à ouverture ou fermeture de circuit. La qualité de ces organes de commande est également très importante dans la mise en œuvre. Le prix, de ces composants, peut aller de 1 à 20 en fonction de la qualité. Malheureusement, les deux ne sont pas toujours liés.

Théorie

 Un peu de pseudo-théorie, le processeur ou plus exactement le SoC (System on Chip) qui équipe la platine Raspberry Pi intègre un nombre important d’entrées/sorties. Ces ports GPIO (General Purpose Input/Output) permettent à notre SoC d’interagir avec d’autres périphériques ou circuits électroniques.
figure03
Dans ce schéma de principe, on retrouve principalement la fonction sortie (output) ou la fonction entrée (input).
Lorsqu’on active la fonction entrée sur une broche du GPIO, on a une broche connectée à rien c’est à dire « en l’air ». Cette entrée est alors sensible aux perturbations ou aux bruits ambiants. L’entrée, en question, peut être soit au niveau 1 (+3.3V), au niveau 0 (0V) ou carrément indéfinie. Pour éviter ce phénomène, on peut fixer le potentiel d’entrée de la broche à 1 ou 0 par l’intermédiaire d’une résistance de soutirage.
Cette résistance (50 kΩ sur le schéma) est connectée à la pin GPIO via des transistors mosfet utilisés en commutateur et pilotées par les entrées pull-up et pull-down du schéma (la représentation des mosfet n’est pas un sinogramme).
Si on relie la résistance au +3.3V notre entrée présente un niveau 1 sur son entrée et un niveau 0 dans le cas contraire.

 Remarques :
– La valeur de 50 kΩ est un ordre de grandeur, car dans la pratique, cette valeur est comprise entre 40 et 100 kΩ,
On considère que le niveau 1 correspond à un niveau de tension supérieure à 2V (difficile de trouver une documentation satisfaisante sur le sujet),
– On considère que le niveau 0 correspond à un niveau de tension inférieure à 1.2V,
– Entre ces deux niveaux de tension, on se trouve dans un « no man’s land » donc à éviter.

Nota : Les deux diodes connectées à la broche GPIO ne sont pas des diodes de protection de l’entrée mais des diodes parasites (aléas de la fabrication). Les broches ne tolèrent pas une tension de 5V. Si l’on applique une tension supérieure à 3.3V sur une entrée, on risque, au mieux, la destruction de l’entrée/sortie et, au pire, la destruction de notre raspberry Pi.

Schémas électroniques

En fonction de ce que l’on a abordé dans le chapitre précédent, il est possible d’opter pour quatre configuration de schéma. Dans tous les cas, notre cher bouton poussoir sera raccordé à la broche 22 du GPIO.

figure04a

 Figure 4a

Dans ce schéma de connexion, le BP est connecté entre l’entrée GPIO22 et la masse. Le potentiel de repos de la broche d’entrée est fixé par la résistance interne de soutirage au +3.3V.
Lorsque le BP n’est pas appuyé le potentiel de l’entrée se trouve à +3.3V.
Lorsque le BP est appuyé le potentiel de l’entrée se trouve à 0V.

Dans ce schéma de connexion, le BP est connecté entre l’entrée GPIO22 et l’alimentation 3.3V. Le potentiel de repos de la broche d’entrée est fixé par la résistance interne de soutirage à la masse.
Lorsque le BP n’est pas appuyé le potentiel de l’entrée se trouve à 0V.
Lorsque le BP est appuyé le potentiel de l’entrée se trouve à +3.3V.
figure04bFigure 4b
figure04cFigure 4c

Dans ce schéma de connexion, le BP est connecté entre l’entrée GPIO22 et la masse. Le potentiel de repos de la broche d’entrée est fixé par la résistance externe reliée au +3.3V. La résistance de soutirage interne n’est pas activée.
Lorsque le BP n’est pas appuyé le potentiel de l’entrée se trouve à +3.3V.
Lorsque le BP est appuyé le potentiel de l’entrée se trouve à 0V.

Dans ce schéma de connexion, le BP est connecté entre l’entrée GPIO22 et l’alimentation +3.3V. Le potentiel de repos de la broche d’entrée est fixé par la résistance externe reliée à la masse. La résistance de soutirage interne n’est pas activée.
Lorsque le BP n’est pas appuyé le potentiel de l’entrée se trouve à 0V.
Lorsque le BP est appuyé le potentiel de l’entrée se trouve à +3.3V.
figure04dFigure 4d

L’utilisation des schémas des figures 4c et 4d est préférable dans un milieu à fortes perturbations électromagnétiques car il présente une meilleure immunité aux bruits. Il est, en effet, plus facile de perturber une entrée reliée à un potentiel de référence avec une résistance de 50 ou 100 kΩ qu’avec une résistance de 4.7 ou 10kΩ.
L‘utilisation des montage des figures 4a et 4c semble plus opportun. En effet, si l’on déporte le BP (entrée et masse), il y a moins de risques de court circuit accidentel qu’avec les schémas 4b et 4d (entrée et +3.3V).

Pour nos essais, nous avons opté pour un schéma dérivant de la figure 4b. En effet, si vous vous levez le matin avec sieur Murphy à vos côtés, il est nécessaire de prendre quelques précautions. Si accidentellement, vous programmez notre broche en sortie, alors l’appuie sur le BP risque de provoquer des dégâts irréversibles (court-circuit). On peut se protéger de se risque en insérant une résistance de 1kΩ en série avec l’entrée du port GPIO qui limitera le courant à une valeur non destructrice pour le SoC. L’auteur utilise ce type de câblage lors des essais de programmation directe des registres internes du SoC, car on a souvent des surprises.

En reliant le BP au +3.3V, on aura également une vision plus digeste des actions. En appuyant sur le BP (1), la broche d’entrée se retrouve au niveau +3.3V soit un niveau logique 1. Si l’on fait l’inverse (figures 4a et 4c), il faudra faire une gymnastique intellectuelle car un appuie sur le BP (1), on aura l’entrée qui va passer au niveau 0V, soit un 0 logique.

figure05Figure 5

Toutes ces explications pour arriver à un schéma aussi simple !

L’avantage de cette présentation, c’est qu’elle liste toutes les possibilités de raccordement de notre bouton poussoir.

Schéma de câblage

figure01Le câblage est simple et demande que peu de composants. On peut le réaliser avec un cobbler comme sur la photo ci-contre ou par une connexion directe sur le port GPIO du raspberry moyennant un petit montage volant (ne pas oublier l’isolation).
Le cobbler est un accessoire qui permet de déporter les pins du GPIO vers une plaque d’essai (breadboard).

Cahier des charges

Tout programmeur qui se respecte doit disposer ou établir un cahier des charges qui défini les différentes opérations à réaliser. Dans notre cas, il sera plus que simple.
Notre cahier des charges :

1) lire l’état de l’entrée,
2) si BP appuyé alors afficher un message dans la console ,
3) attente x ms,
4) continuer en 1) (on répète indéfiniment la boucle)

Organigramme

figure07La représentation graphique ci- contre représente le déroulement de notre programme. Il comporte les différentes étapes qui permettront une transcription dans un langage donné.
La première phase consiste à initialiser la pin GPIO22 en entrée pour lire l’état du BP.
La phase suivante consiste à lire l’état de l’entrée donc du BP. Si c’est le cas, on affiche dans la console le message « BP appuyé », sinon on continue.
Pour finir, on a une petite attente avant de reboucler sur la phase lecture BP.

Programme

Les différents programmes abordés dans la suite peuvent être exécuter sur toutes les versions du raspberry Pi (type A, B, jusqu’au RPI3 y compris le zéro).
La saisie des programmes se fait via un éditeur de texte graphique (par exemple geany) ou en mode console (par exemple nano).

L’accès au raspberry Pi peut se faire via son interface écran HDMI / clavier ou bien par l’intermédiaire d’un connexion à distance (mode console ou graphique). L’auteur utilise une connexion console via une liaison ssh.

Avertissement :
Tous les programmes de cette présentation ont une vocation découverte ou initiatique, donc faire découvrir le monde merveilleux de la communication avec le milieu externe de notre Raspberry Pi via son port GPIO.
Certains programmes peuvent présenter une charge cpu importante voir atteindre 100 %. Ce type de fonctionnement ne présente aucun danger pour notre carte. Toutes les remarques sur le sujet des saga blink restent valables.

Pour nos premiers tests, nous utiliserons le langage Python avec la bibliothèque RPi.GPIO. L’approche multi-langages se fera dans les chapitres suivants de ce didacticiel.

Pour saisir ce programme, il faut saisir dans la console :

nano push01.py

Cette commande ouvre un fichier texte vide appelé push01.py (pour la sauvegarde faire ctrl+o et pour sortir ctrl+x).
Le contenu du programme et de ces commentaires sont représentés ci-dessous.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme classique lecture entrée GPIO avec la bibliothèque RPi.GPIO
utilisation de la fonction GPIO.input()
Bouton poussoir raccordé entre GPIO22 et +3.3V 
(avec résistance de protection de 1k en série)
nom programme       : push01.py
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 18/08/2016
date de mise à jour : 18/08/2016
version             : 1.0
auteur              : icarePetibles
référence           : 
"""
#-------------------------------------------------------------------------------
# Bibliothèques
#-------------------------------------------------------------------------------
import RPi.GPIO as GPIO                 #bibliothèque RPi.GPIO
import time                             #bibliothèque time
#-------------------------------------------------------------------------------
pin = 22                                #broche utilisé en entrée
#temps = 1                              #valeur attente en msec
#temps = 10
temps = 100
#temps = 100
#temps = 1000

GPIO.setwarnings(False)                 #désactive le mode warning
GPIO.setmode(GPIO.BCM)                  #utilisation des numéros de ports du
                                        #processeur
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
                                        #mise en entrée du port GPIO 22
                                        #et activation résistance soutirage
                                        #au ground
if __name__ == '__main__':
     """
     Programme par défaut
     """
     print("Début du programme")        #IHM
     print("Sortie par ctrl-c\n")       #IHM
     try:
         while True:                    #boucle infinie
             entree = GPIO.input(pin)   #lecture entrée
             if (entree == True):       #si touche appuyée
                 print("BP appuyé")     #IHM
             time.sleep(temps / 1000)   #attente en msec 
     except KeyboardInterrupt:          #sortie boucle par ctrl-c
         GPIO.cleanup()                 #libère toutes les ressources
         print("\nFin du programme\n")  #IHM

Les lignes 20 et 21 importent les bibliothèques nécessaires à notre programme. L’initialisation des différents paramètres est effectuée dans les lignes 23 à 41.
Le programme principal s’effectue dans le module while True: (boucle sans fin). A la ligne 44, on lit l’entrée de notre broche et si la valeur lue (ligne 46) est à 1 (3.3V ou True) alors on affiche dans la console le message « BP appuyé » (ligne 46). Enfin ligne 47, on attend quelques millis-secondes avant de boucler.

Par rapport à notre organigramme, le programme gère une sortie « propre » de la boucle sans fin.

Exécution du programme

Pour exécuter ce programme, il faut lancer la commande suivante dans la console Linux :

python3 push01.py

Pour sortir de la boucle infinie, il suffit de faire un Ctrl+c au clavier.

Le programme affiche dans la console les messages suivants :

Début du programme
Sortie par ctrl-c

Si vous essayer ce programme en appuyant sur le bouton poussoir, vous constaterez qu’il fonctionne mais fort mal. En effet, chaque appui sur le BP affiche plusieurs lignes indiquant que le BP a été actionné (même pour un appuie unique).
Vous pouvez également tester ce programme en changeant les valeurs de l’attente (lignes 24 à 27). Certaines valeurs donneront un résultat qui semble acceptable mais on risque, dans ces cas, de ne pas détecter toutes les actions sur le BP.

Commentaires

Mais pourquoi notre programme fonctionne-t-il si mal ?
Nous avons deux phénomènes qui viennent perturber notre programme lorsque l’on cherche à lire un contact mécanique.

Le premier est que le programme exécute très rapidement par rapport à notre échelle de temps de pauvre humain. Vous avez pu mettre en évidence ce paramètre lors des différents essais de notre programme.

Le deuxième phénomène est causé par notre bouton poussoir qui n’est pas parfait. D’un point de vue théorique, on avait admis que notre BP avait deux états (non actionné ou actionné). La réalité est tout autre, pour passé de l’état repos à l’état appuyé, il passe par une multitude de rebondissements. Il en va de même pour le passage de l’état appuyé à l’état repos.

Bouton poussoir réel

figure08La figure ci-contre représente un passage réel d’un bouton poussoir de l’état repos à l’état actionné. On constate que les pics générés par les rebondissements entre dans la zone de basculement de l’entrée.
D’un point de vue physique, ce phénomène de rebondissements est logique puisqu’il faut absorber l’énergie du contact mobile lors de sa frappe sur la partie fixe. Sans entrée dans la théorie des chocs, on se trouve devant une situation que vous connaissez bien. En effet, une balle rebondie plusieurs fois lorsqu’elle tombe au sol, de même, lorsque vous frappez avec un marteau sur une enclume, il rebondit.

Maintenant que faire pour remédier à ce problème ?

Les solutions pour remédier aux rebondissements des BP suscitent de nombreux débats, depuis plus de 30 ans, sans avoir réussi à départager les protagonistes.
Deux clans s’affrontent, d’un côté du ring les «softeux» et le l’autre côté du ring les «hardeux».
Pour les softeux, on a un problème, il faut le traiter, et pour les hardeux, on a un problème, il faut l’éliminer.
Pas facile de trancher puisque cela fonctionne dans les deux cas.

Pour reprendre notre comparaison précédente, on se trouve dans la situation suivante :
– les softeux ferment les yeux pendant que la balle ou le marteau rebondissent,
– les hardeux utilisent une balle ou un marteau sans inertie.

Côté soft :
Pour faire simple, on lit deux fois l’entrée correspondant au BP avec un temps d’attente entre les deux lectures. Ce temps d’attente correspond au temps nécessaire pour stabiliser les contacts du bouton poussoir (bounce time).

Côté hard :
On met un condensateur (entre 10 et 100nF) en parallèle sur le BP et on lit l’entrée.

Nota : Il y a une scission dans le clan des hardeux entre ceux qui veulent mettre une résistance en série avec le condensateur pour limiter le courant (de charge ou de décharge) et ceux qui trouvent cela inutile. Personnellement, je trouve cette résistance inutile car ce n’est pas un composant parfait. Le schéma équivalent du condensateur est composé d’un condensateur parfait et de deux résistances (une en parallèle avec le condensateur et l’autre en série). Si l’on ajoute à ces résistances toutes les résistances parasites du circuit la limitation de courant se fera naturellement.

Remarque : Nous n’avons pas abordé, volontairement, toutes les techniques de traitement des anti-rebonds utilisés en logique classique (bascules RS, monostables, triggers de Schmitt, etc…).

Maintenant que vous savez dans quel camp se trouve l’auteur et pour ne pas passer pour un sectaire ou un dogmatique, on utilisera le traitement logiciel des rebonds du bouton poussoir.

 Structure des programmes

Un petit rappel sur la structure des programmes sur notre ordinateur ou carte embarquée.

Sur notre ordinateur de bureau ou nano-ordinateur un programme « classique » peut être représenté par l’organigramme ci-contre.

Le programme démarre par une phase d’initialisation puis, on entre dans le traitement de l’applicatif (traitement de texte, dessin, navigateur internet, messagerie, etc…).

A tout moment, notre programme vérifie si une action de sortie de l’applicatif est générée. Si c’est le cas, on quitte le programme en libérant les ressources mises en œuvre.

figure09Figure 9
figure10Figure 10 Dans le cas d’une application de type domotique ou contrôle de processus, le programme s’exécute de manière continu sans condition de sortie.

La figure ci-contre représente un organigramme simplifié de la structure du programme.

Notre programme s’exécute dans une boucle infinie et traite les événements pour lesquels il a été développé.
Pour un applicatif de production tout se passe bien, mais pour des tests initiatiques ou d’apprentissage, il faut avoir la possibilité de sortir « proprement » de la boucle infinie sans laisser les ressources dans un état indéfini.

En effet, une broche du GPIO qui reste en sortie peut provoquer un court-circuit fanal à notre entrées/sortie ou au Raspberry Pi. Lorsqu’on relance le logiciel, on risque également d’avoir un message d’erreur parce que l’on alloue une ressource qui est encore occupée.

Dans la suite de nos différentes « saga« , nous adopterons l’organigramme ci-dessous.

figure11Figure 11 : Organigramme saisie BP

La partie gauche de l’organigramme ressemble étrangement à l’organigramme de la figure 10 avec un contrôle de la boucle infinie (run = true). Si cette variable run passe de true (vrai) à false (faux), on sort de la boucle en libérant les ressources.
Pour changer la valeur de cette variable, nous utiliserons un thread (créé dans la phase initialisation) qui permettra une saisie clavier, en occurrence l’appuie sur la touche <Entrée> du clavier. Pour le fonctionnement des threads, on pourra consulter le chapitre 6 de la saga blink, la suite.
Le passage d’un programme selon l’organigramme de la figure 11, à un programme selon l’organigramme de la figure 10 sera commenter dans le paragraphe suivant.

Programme (anti-rebond logiciel)

Pour la suite, nous continuons à utiliser le langage Python avec la bibliothèque RPi.GPIO.

Organigramme

figure12Dans un premier temps, on initialise le port GPIO ainsi que les variables et le thread. L’étape suivante consiste à lire l’état du BP et de le sauvegarder. Le premier test vérifie etat1 est différent de etatBouton. Si c’est le cas on attend que les régimes transitoires soient terminées. Puis on a une seconde lecture de l’état du BP avec sauvegarde de l’état. Ensuite, on compare etat1 et etat2, si les deux variables sont égales alors on a un appuie BP (etatBouton = etat2).
Et pour finir, si etatBouton = True alors on affiche notre message et on remet notre variable etatBouton à False.
En faisant <Entrée> au clavier, on sort de la boucle de scrutation. A la sortie, on libère les ressources et l’on supprime le thread.

Programme

Pour saisir ce programme, il faut saisir dans la console :

nano push05.py

Cette commande ouvre un fichier texte vide appelé push05.py (pour la sauvegarde faire ctrl+o et pour sortir ctrl+x).
Le contenu du programme et de ces commentaires sont représentés ci-dessous.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme classique lecture entrée GPIO avec la bibliothèque RPi.GPIO
utilisation anti-rebond logiciel
Bouton poussoir raccordé entre GPIO22 et +3.3V 
(avec résistance de protection de 1k en série)
nom programme       : push05.py
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 18/08/2016
date de mise à jour : 18/08/2016
version             : 1.0
auteur              : icarePetibles
référence           : 
"""
#-------------------------------------------------------------------------------
# Bibliothèques
#-------------------------------------------------------------------------------
import RPi.GPIO as GPIO                 #bibliothèque RPi.GPIO
import time                             #bibliothèque time
from threading import *                 #bibliothèque thread
#-------------------------------------------------------------------------------
pin = 22                                #broche utilisé en entrée
debounce = 100                          #attente anti-rebond
etatBouton = False                      #bouton au repos

GPIO.setwarnings(False)                 #désactive le mode warning
GPIO.setmode(GPIO.BCM)                  #utilisation des numéros de ports du
                                        #processeur
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
                                        #mise en entrée du port GPIO 22
                                        #et activation résistance soutirage
                                        #au ground
enMarche = True                         #sortie boucle

class Saisie(Thread):
    """Lanceur de fonction"""
    def __init__(self, fonction):
        Thread.__init__(self, None, fonction)
        self._fonction = fonction
        self.running = True
        
    def run(self):                      #lance le programme
        while self.running:
            self._fonction()            #notre fonction programme
    
    def stop(self):                     #arrêt du programme
        self.running = False
        
def saisieClavier():
    """Saisie <Entrée>"""
    global enMarche
    while enMarche:                     #tant que en marche
        input()                         #attente saisie clavier
        sortie = True                   #on sort
        enMarche = False                #sortie boucle
    
if __name__ == '__main__':
    """
    Programme par défaut
    """
    print("Début du programme")         #IHM
    print("Sortie par <Entrée>\n")      #IHM
    thread_1 = Saisie(saisieClavier)    #création thread
    thread_1.start()                    #démarre le thread
    while enMarche:                     #boucle infinie contrôlée
        lecture1 = GPIO.input(pin)      #première lecture entrée
        etat1 = lecture1                #mémorise la lecture
        if(etat1 != etatBouton):        #si première lecture différente
                                        #etatBouton
            time.sleep(debounce / 1000) #attente fin rebondissement contact
        lecture2 = GPIO.input(pin)      #deuxième lesture entrée
        etat2 = lecture2                #mémorise la lecture
        if(etat1 == etat2):             #si etat1 = etat2
            etatBouton = etat2          #alors changement état BP
                
        if(etatBouton == True):         #si BP appuyé
            print("BP appyué")          #alors affiche message
            etatBouton = False          #BP relaché

    thread_1.stop()                     #arrêt thread
    GPIO.cleanup()                      #libère toutes les ressources
    print("Fin du programme")           #IHM

Dans le programme ci-dessus, on retrouve les différentes phases de notre organigramme.

Exécution du programme

Pour exécuter ce programme, il faut lancer la commande suivante dans la console Linux :

python3 push05.py

Pour sortir de la boucle infinie, il suffit de faire un <Entrée> au clavier.

C’est magique, à chaque appuie sur notre BP, on a l’affichage du message « BP appuyé ».

Commentaires

Pour rendre notre programme compatible avec la version de la figure 10, il suffit de commenter (# en début de ligne) la ligne 22, les lignes 37 à 57, les lignes 63 à 66 et les lignes 82 à 84.

On aurait pu faire nettement mieux en utilisant une variable test ou debug (True ou False) pour avoir les deux versions dans le même fichier. Nous verrons cette option lors d’un autre chapitre.

Remarque :
Les puristes de python auraient adoptés une structure de la forme ci-dessous avec les exécutions spécifiques (lignes 4 et 5) :

#!/usr/bin/python3
# -*- coding:uft-8 -*-
#
# python3 -O test.py -> debug on
# python3 test.py    -> debug off
#
if __debug__:
    print("Debug ON")
else:
    print("Debug OFF")

Autres solutions

Lorsqu’on consulte les fonctions de notre bibliothèque RPi.GPIO, on constate qu’il existe d’autres possibilités pour lire l’état du BP. On peut explorer les fonctions GPIO.event_detected(), GPIO.wait_for_edge() et GPIO.add_event_detect (avec bounce time).

L’utilisation des bibliothèques permet une programmation plus concise et réduit considérablement le nombre de lignes de code à saisir. Par contre, leur utilisation n’est pas toujours très formateur car elles cachent une partie du code utilisé et l’organigramme correspondant. Il faut néanmoins pas hésiter de consulter et re-consulter la documentation, parce qu’elles contiennent, presque toujours, des fonctions « miracles » pour résoudre vos problèmes.

Langage Python

Si l’on utilise le même organigramme, on aura toujours le même résultat peu importe le langage utilisé. Dans suite, on va mettre en œuvre une particularité de la bibliothèque gpiozero pour détecter l’appuie sur le bouton poussoir. La méthode de la bibliothèque fait un travail similaire à notre organigramme.

gpiozero

Programme

Pour saisir ce programme, il faut saisir dans la console :

nano push25.py

Cette commande ouvre un fichier texte vide appelé push25.py (pour la sauvegarde faire ctrl+o et pour sortir ctrl+x).
Le contenu du programme et de ces commentaires sont représentés ci-dessous.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme classique lecture entrée GPIO avec la bibliothèque gpiozero
utilisation anti-rebond logiciel
Bouton poussoir raccordé entre GPIO22 et +3.3V 
(avec résistance de protection de 1k en série)
nom programme       : push25.py
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 18/08/2016
date de mise à jour : 18/08/2016
version             : 1.0
auteur              : icarePetibles
référence           : 
"""
#-------------------------------------------------------------------------------
# Bibliothèques
#-------------------------------------------------------------------------------
from gpiozero import Button             #bibliothèque gpiozero
import time                             #bibliothèque time
from threading import *                 #bibliothèque thread
#-------------------------------------------------------------------------------
pin = 22                                #broche utilisé en entrée
debounce = 0.1                          #attente anti-rebond

bouton = Button(pin, pull_up=False, bounce_time=debounce)
                                        #instance bouton sur GPIO22 avec
                                        #résistance de soutirage vers la masse
                                        #et temps de rebondissement de 100 ms
enMarche = True                         #sortie boucle

class Saisie(Thread):
    """Lanceur de fonction"""
    def __init__(self, fonction):
        Thread.__init__(self, None, fonction)
        self._fonction = fonction
        self.running = True
        
    def run(self):                      #lance le programme
        while self.running:
            self._fonction()            #notre fonction programme
    
    def stop(self):                     #arrêt du programme
        self.running = False
        
def saisieClavier():
    """Saisie <Entrée>"""
    global enMarche
    while enMarche:                     #tant que en marche
        input()                         #attente saisie clavier
        sortie = True                   #on sort
        enMarche = False                #sortie boucle
    
if __name__ == '__main__':
    """
    Programme par défaut
    """
    print("Début du programme")         #IHM
    print("Sortie par <Entrée>\n")      #IHM
    thread_1 = Saisie(saisieClavier)    #création thread
    thread_1.start()                    #démarre le thread
    while enMarche:                     #boucle infinie contrôlée
        if bouton.is_pressed:           #si BP appuyé
            print("BP appyué")          #alors affiche message
        time.sleep(0.15)                #ralentit le programme

    thread_1.stop()                     #arrêt thread
    bouton.close()                      #libère la ressource
    print("Fin du programme")           #IHM

Notre programme n’a rien de compliqué car le rebondissement de contacts est géré par l’instance bouton (ligne 27).

La class Button possède de nombreuses méthodes (voir documentation : http://gpiozero.readthedocs.io/en/v1.2.0/api_input.html)

Exécution du programme

Pour exécuter ce programme, il faut lancer la commande suivante dans la console Linux :

python3 push25.py

Pour sortir de la boucle infinie, il suffit de faire un <Entrée> au clavier.

Commentaires

Pour rendre notre programme compatible avec la version de la figure 10, il suffit de commenter (# en début de ligne) la ligne 22, les lignes 33 à 53, les lignes 59 à 62 et les lignes 68 à 70.

wiringpi et pigpio

Nous n’avons pas présenté d’exemples utilisant les bibliothèques python pigpio ou wiringpi. Mais cela ne devrait pas vous posez de problèmes pour réaliser ces exercices.

Langage C

Nous utiliserons pour cet exemple (suivant l’organigramme de la figure 11) la bibliothèque wiringPi. Il existe également une bibliothèque bcm2835 qui, bien que très performante, n’est pas très avenante. Nous l’utiliserons certainement pour l’un de nos articles a venir.

Programme

Pour saisir ce programme, il faut saisir dans la console :

nano push10.c

Cette commande ouvre un fichier texte vide appelé push10.c (pour la sauvegarde faire ctrl+o et pour sortir ctrl+x).
Le contenu du programme et de ces commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique lecture entrée GPIO avec la bibliothèque wiringPi
Bouton poussoir raccordé entre GPIO22 et +3.3V
(avec une résistance de protection de 1k en série)
nom programme       : push10.c
os                  : RPi Linux 4.4.13+
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 24/08/2016
date de mise à jour : 24/08/2016
version             : 1.0
auteur              : icarePetibles
référence           : www.wiringpi.com
Remarques           :

Bibliothèques
----------------------------------------------------------------------------- */
#include <stdio.h>                      //entrées/sorties
#include <wiringPi.h>                   //bibliothèque wiringPi
#include <stdlib.h>                     //conversion nombre
#include <string.h>                     //chaîne
#include <pthread.h>                    //thread

#define TEST                            //voir commentaire

#define PIN 3                           //numéro bouton = GPIO22
#define DEBOUNCE 100                    //temps rebondissement

typedef enum                            //pour palier non existance type booléen
    {False=0, True=1}
    Bool;

volatile Bool enMarche = True;          //variable globale contrôle boucle

#ifdef TEST
void* saisieClavier(){                  //attente saisie clavier (thread)
    while(enMarche){                    //en fonctionnement
        getchar();                      //attente saisie clavier
        enMarche = False;               //arrêt
    }
    return 0;                           //valeur retour
}
#endif

int main(void){                         //programme principal
    Bool etatBouton = False;            //variable état du bouton
    Bool lecture1 = False, lecture2 = False;
    Bool etat1 = False, etat2 = False;
    wiringPiSetup();                    //numérotation wiringPi ou type Arduino
    pinMode(PIN, INPUT);                //pin en entrée
    pullUpDnControl(PIN, PUD_DOWN);     //résistance soutirage à la masse
#ifdef TEST
    pthread_t saisie;                   //instance thread
    printf("Début programme\n");        //IHM
    printf("sortie par touche <Entrée>\n\n");
                                        //IHM
    pthread_create(&saisie, NULL, saisieClavier, NULL);
                                        //création thread
#endif
    while(enMarche){                    //tant que l'on fonctionne
        lecture1 = digitalRead(PIN);    //lecture état BP
        etat1 = lecture1;               //sauve le résultat
        if(etat1 != etatBouton)         //si différent de l'état actuel
            delay(DEBOUNCE);            //attente fin rebondissement
        lecture2 = digitalRead(PIN);    //re-lecture état BP
        etat2 = lecture2;               //sauve le résultat
        if(etat1 == etat2)              //compare les 2 lectures sont identiques
            etatBouton = etat2;         //nouvel état

        if(etatBouton == True){         //si BP appuyé
            printf("BP appuyé\n");      //IHM
            etatBouton = False;         //remise à 0
        }
    }
#ifdef TEST
    printf("Fin du programme\n");       //IHM
#endif
    return(0);                          //code sortie
}
//------------------------------------------------------------------------------

Notre programme est en tout point similaire au programme push05.py à la syntaxe près.

Exécution du programme

Pour exécuter ce programme, il faut, avant tout, le compiler par la commande suivante dans la console Linux :

gcc -Wall -o push10 push10.c -lwiringPi -lpthread

Pour l’exécuter, il faut lancer le programme par la commande console :

sudo ./push10

A chaque action sur le bouton poussoir, on verra le message console « BP appuyé ».

Pour sortir de la boucle infinie, il suffit de taper sur la touche <Entrée> du clavier.

Commentaires

Le programme push10 s’exécute conformément à la structure de l’organigramme de la figure 11 pour le rendre exécutable selon l’organigramme de la figure 10, il suffit de commenter la ligne 24.

//#define TEST                //voir commentaire

Puis il faudra recompiler le programme comme décrit ci-dessus.
Toutes les lignes comprises entre les différents #ifdef TEST et #endif ne seront pas compilées.

Les seuls messages affichés dans la console seront « BP appuyé ».
Pour sortir uniquement un ctrl-c viendra à bout de la boucle infernale.

Langage C++

Programme

Pour saisir ce programme, il faut saisir dans la console :

nano push20.cpp

Cette commande ouvre un fichier texte vide appelé push20.cpp (pour la sauvegarde faire ctrl+o et pour sortir ctrl+x).
Le contenu du programme et de ces commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique lecture entrée GPIO avec la bibliothèque wiringPi
Bouton poussoir raccordé entre GPIO22 et +3.3V
(avec une résistance de protection de 1k en série)
nom programme       : push20.cpp
os                  : RPi Linux 4.4.13+
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 24/08/2016
date de mise à jour : 24/08/2016
version             : 1.0
auteur              : icarePetibles
référence           : www.wiringpi.com
Remarques           :

Bibliothèques
----------------------------------------------------------------------------- */
#include <cstdio>                       //entrées/sorties
#include <wiringPi.h>                   //bibliothèque wiringPi
#include <iostream>                     //entrées/sorties standard
#include <sstream>                      //buffer
#include <stdlib.h>                     //conversion nombre
#include <pthread.h>                    //thread

bool lectureBP(int pin, bool etatBouton);
                                        //prototype fonction lecture BP

using namespace std;

#define TEST                            //voir commentaire

#define PIN 3                           //numéro bouton = GPIO22
#define DEBOUNCE 100                    //temps rebondissement

volatile bool enMarche(true);           //variable globale contrôle boucle

#ifdef TEST
void* saisieClavier(void *){            //attente saisie clavier (thread)
    while(enMarche){                    //en fonctionnement
        cin.ignore();                   //attente saisie clavier
        enMarche = false;               //arrêt
    }
    return 0;                           //valeur retour
}
#endif

int main(void){                         //programme principal
    bool etatBouton(false);             //variable état du bouton
    wiringPiSetup();                    //numérotation wiringPi ou type Arduino
    pinMode(PIN, INPUT);                //pin en entrée
    pullUpDnControl(PIN, PUD_DOWN);     //résistance soutirage à la masse
#ifdef TEST
    pthread_t saisie;                   //instance thread
    cout << "Début programme" << endl;  //IHM
    cout << "sortie par la touche <Entrée>" << endl << endl;
                                        //IHM
    pthread_create(&saisie, NULL, saisieClavier, NULL);
                                        //création thread
#endif
    while(enMarche){                    //tant que l'on fonctionne
        if(lectureBP(PIN, etatBouton)){ //lecture BP
            cout << "BP appuyé" << endl;//IHM
            etatBouton = false;         //remise à 0
        }
    }
#ifdef TEST
    cout << "Fin du programme" << endl;
#endif
    return(0);                          //code sortie
}
//------------------------------------------------------------------------------
bool lectureBP(int pin, bool etatBouton){
    bool lecture1(false), lecture2(false);
    bool etat1(false), etat2(false);    //variables booléen et initialisation
    lecture1 = digitalRead(pin);        //lecture état BP
    etat1 = lecture1;                   //sauve le résultat
    if(etat1 != etatBouton)             //si différent de l'état actuel
        delay(DEBOUNCE);                //attente fin rebondissement
    lecture2 = digitalRead(pin);        //re-lecture état BP
    etat2 = lecture2;                   //sauve le résultat
    if(etat1 == etat2)                  //compare les deux lectures
        etatBouton = etat2;             //nouvel état
    return etatBouton;                  //retourne état du bouton
}
//------------------------------------------------------------------------------

Pour éviter la monotonie de tous ces programmes qui se ressemblent (à la syntaxe près), nous avons déporté la lecture du bouton poussoir dans un sous programme. Le fonctionnement reste, bien sur, le même.

Exécution du programme

Pour exécuter ce programme, il faut, avant tout, le compiler par la commande suivante dans la console Linux :

g++ -Wall -o push20 push20.cpp -lwiringPi -lpthread

Pour l’exécuter, il faut lancer le programme par la commande console :

sudo ./push20

A chaque action sur le bouton poussoir, on verra le message console « BP appuyé ».

Pour sortir de la boucle infinie, il suffit de taper sur la touche <Entrée> du clavier.

Commentaires

L’utilisation de la directive TEST est la même que dans le chapitre précédent.

Langage Bash

Nous sommes émerveillés par les possibilités et la puissance des scripts bash. L’usage des scripts ne sera, certainement, pas très courant dans l’exploitation des entrées/sorties GPIO de la carte framboise. Donc juste pour le « fun », un programme similaire au précédent.

Programme

Pour saisir ce programme, il faut saisir dans la console :

nano push30.sh

Cette commande ouvre un fichier texte vide appelé push30.sh (pour la sauvegarde faire ctrl+o et pour sortir ctrl+x).
Le contenu du programme et de ces commentaires sont représentés ci-dessous.

#!/bin/bash
#Programme classique lecture entrée GPIO via driver linux
#Anti-rebond logiciel
#Bouton poussoir raccordé entre GPIO22 et +3.3V
#(avec résistance de protection de 1k en série)
#nom programme       : push30.sh
#logiciel            : bash
#cible               : raspberry Pi
#date de création    : 18/06/2016
#date de mise à jour : 18/07/2016
#version             : 1.0
#auteur              : icarePetibles
#référence           :
#Remarques           : Pour fonctionner, il faut être sous super utilisateur
#                      Il faut rendre le fichier exécutable avec
#                      sudo chmod +x push30.sh
#                      Pour exécuter le fichier, il faut faire
#                      sudo ./push30.sh
#
BPPIN=22                                            #GPIO22
FALSE=0
TRUE=1
DEBOUNCE=0.05                                       #temps rebondissement
etatBouton=0                                        #BP relaché

#vérification si accès root
#if [ $EUID -ne 0 ]                                  #si $EUID différent de 0
if [ $EUID != "0" ]                                 #si $EUID différent de 0
then                                                #alors
    echo "Il faut être root pour exécuter le script. Essaye sudo $0"
    exit                                            #sortie
fi                                                  #fin if

#Procédure de netoyage, désactive l'entrée
cleanup()
{
    PIN=$1                                          #paramètre transmis
    echo $PIN > /sys/class/gpio/unexport            #désactive port
    echo                                            #esthétique
    echo Fin script                                 #message de fin
    exit                                            #sortie
}

#programme principal
echo Début du script                                #IHM
echo ctrl-c pour sortir de la boucle                #IHM
echo
#Setup pin et direction - Cature Control-C SIGHUP SIGKILL
echo $BPPIN > /sys/class/gpio/export                #assigne pin 23
echo in > /sys/class/gpio/gpio$BPPIN/direction      #pin 22 en entrée
trap "cleanup $BPPIN" SIGHUP SIGINT SIGTERM         #capture SIGNAL et lance la
                                                    #la procédure cleanup avec
                                                    #le paramètre 22
while true                                          #boucle infinie
  do                                                #faire
    lecture1=`cat /sys/class/gpio/gpio$BPPIN/value` #lecture entrée BP
    etat1=$lecture1                                 #mémorise la lecture
    if [ $etat1 -ne $etatBouton ]                   #si lecture <> état BP
    then
        sleep $DEBOUNCE                             #attente fin rebondissement
    fi
    lecture2=`cat /sys/class/gpio/gpio$BPPIN/value` #re-lecture entrée BP
    etat2=$lecture2                                 #mémorise la lecture
    if [ $etat1 -eq $etat2 ]                        #si etat1 = etat2
    then
        etatBouton=$etat2                           #alors changement état BP
    fi

    if [ $etatBouton -eq $TRUE ]                    #si BP appuyé
    then
        echo BP appuyé                              #affiche message
        etatBouton=$FALSE                           #BP relâché
    fi
  done                                              #fin do
#Fin du script

Exécution du programme

Pour exécuter ce programme bash, il faut le rendre exécutable avec la commande :

sudo chmod +x push30.sh

Et pour l’exécuter :

sudo ./push30.sh

Pour sortir de la boucle infinie, il suffit de faire un ctrl-c au clavier.

Conclusion

figure13Tout une histoire pour un simple bouton poussoir !!! Par contre, nous avons abordé des sujets complémentaires (schémas, logiciels et concepts) qui nous servirons dans d’autres articles de la « saga ».
Il reste des sujets intéressants autour du BP comme la détection des appuies courts, longs ou incrémentant un compteur à vitesse progression et bien d’autres.
Peut être, une suite dans quelque temps, on ne sait jamais !

Les plus courageux pourront faire un mixte de la « sagaBlink » et « sagaPushButton » pour faire, par exemple :
– une led que l’on allume avec un bouton poussoir et que l’on éteint avec un autre bouton,
– une led que l’on allume et que l’on éteint avec le même bouton poussoir,
– deux leds clignotantes à des fréquences différentes que l’on fait clignoter avec chaque une un BP,
– etc…

Le prochain épisode sera nettement plus intéressant et portera sur la « saga I2C »

A tous présents et à venir. Salut !

Sources

On pourra télécharger l’ensemble des sources sous :
http://psl.ibidouille.net/Images_forum/raspberryPi/sagaPush.zip

Cam-Aero : La météo pour les pilotes grâce au Raspberry Pi

camaero_250pxL’objectif de cam-aero.eu est de permettre aux pilotes d’avoir une confirmation visuelle des METARS/TAF (données météo destinées aux pilotes) dont ils doivent prendre connaissance avant de décoller. L’idée c’est d’équiper gratuitement (pour la partie logicielle/hébergement) tous les acteurs présents sur les terrains d’aviation afin de constituer un réseau couvrant une zone géographique maximale.

cam-aero.eu la météo pour chaque terrain d’aviation

L’équipe

olivier-duqueCam-Aéro est la création d’Olivier Duque, un pilote bien connu dans le milieu de l’aéronautique légère. Il a développé une série d’applications sur iOs et Android dédiées à l’aviation légère. Cela allait des checklists au format informatique jusqu’au suivi complet du vol avec moving map GPS. Antoine Theuret a travaillé avec lui dès le début de Cam-Aéro.  Lorsqu’Olivier est décédé dans un accident aux commandes de son avion, Antoine a repris la suite… Aujourd’hui il s’occupe de l’hébergement et du « cœur » du programme sur le Raspberry Pi. Nicolas Merlet, un de ses amis, l’aide sur la partie frontal web (mosaïque des caméras, replay etc. …).

L’idée d’origine

Fallback or ‘alternate’ content goes here. This content will only be visible if the SWF fails to load. La météo est un élément primordial à prendre en compte lors de la préparation d’un vol, surtout pour les pilotes privés qui doivent voler à vue (hors des nuages et en vue du sol). Pour cela les pilotes utilisent les services de Météo France qui diffuse les METAR (METeorological Aerodrome Report) et TAF (Terminal Aerodrome Forecast) afin de savoir si le vol est possible.

METAR ET TAF… y’a du boulot

Voici un exemple de message METAR pour Brest (LFRB) :

LFRB 241400Z AUTO 05010KT 010V090 9999 BKN006 BKN047 BKN070 16/15 Q1018

C’est un langage codé, une fois traduit cela donne ceci :

  • Le bulletin a été fait le 24 du mois, à 14:00 UTC
  • Vent 10 kt de nord-est, variant entre nord et est
  • Température 16°C
  • Humidité 94%
  • Pression 1018 hPa
  • Visibilité 10 km ou plus
  • Nuages fragmentés à une hauteur de 600 ft
  • Nuages fragmentés à une hauteur de 4700 ft
  • Nuages fragmentés à une hauteur de 7000 ft

Alors ? Vous, est-ce que vous décolleriez ?

decollage600px

Les METAR et TAF sont très précis et le travail des météorologistes est de première qualité. Mais il faut avouer que pour des usagers ponctuels, il est difficile de se faire une représentation mentale précise de la météo que l’on va trouver à l’arrivée. C’est comme cela qu’est née l’idée de Cam-Aéro : associer les données METAR/TAF à une image prise sur l’aérodrome afin de confirmer « visuellement » ce qu’annonce le météorologue.

Météo + images pas cher = Raspberry Pi

Forts de ce premier constat, l’équipe s’est attaquée à une autre problématique : le coût et la simplicité d’installation. Si l’aviation a encore une image de loisir de « riches », force est de constater que certains aéroclubs fonctionnent avec des moyens très faibles. Certains louent le seul avion qu’ils utilisent, n’ont qu’un instructeur bénévole, « vivent » dans un Algeco au bord du terrain et ne comptent que 15 membres. Imaginez le budget royal !

aeroclub_laon
Alors il fallait proposer une solution économique (moins de 100€) et rendre l’installation possible même par un non-informaticien. Pour cela le Raspberry Pi a été comme une évidence ! J’en vois déjà certains ronchonner en disant qu’un Raspberry (qui est ni plus ni moins qu’un ordinateur de bureau) est beaucoup trop puissant pour une utilisation aussi triviale. C’est vrai, mais son coût reste faible et on peut le trouver partout !

camaero_montage_01_600pxAfin de rendre l’installation la plus simple possible, il a été décidé de minimiser les fonctions supportées par le Raspberry Pi et de délocaliser l’hébergement des images et leur archivage sur un serveur dédié. Le Raspberry Pi prend une photo toutes les 5 minutes, récupère la météo et incruste les données dans l’image. Cette image est ensuite envoyé sur le serveur cam-aero.eu. Et c’est TOUT !

camaero_01

Une installation de base on ne peut plus simple. Le boîtier équipé de la caméra Pi est placé derrière une vitre !

Côté hardware on se base sur un Raspberry Pi type B (ou plus récent), le module caméra v1.3 et un ensemble de scripts bash, php et Python. (voir plus bas)

Du point de vue de la structure (club, école de pilotage, atelier …) c’est très simple. Ils achètent (directement via Amazon) le matériel pour 75/80€,  branchent la caméra sans aucune manipulation et le système publie une photo horodatée incrustée du METAR + TAF + Sigmet toutes les 5 minutes. Pour voir l’évolution de la météo, un mode « replay » est aussi accessible.

Le montage de la caméra

camaero_03

Un support de téléphone portable convient également…

Actuellement 30 caméras sont déployées, 3 supplémentaires sont en préparation et devraient être rapidement opérationnelles.

camaero_carte

Cliquez pour voir la carte en direct

En terme de montage : le module est souvent localisé à l’intérieur et est fixé sur une vitre, cette solution évite les problèmes d’humidité et de connectique. Le « truc » c’est d’utiliser un support universel de téléphone pour voiture, il convient parfaitement pour tenir le boîtier du Raspberry Pi 😉

camaero_04

Montage extérieur avec une protection « maison ».

lampadaireDans le cas d’une utilisation en extérieur, Antoine préconise l’installation dans un boîtier de lampadaire halogène de jardin. Le format convient parfaitement et l’isolation est très bonne. Certains clubs ont poussé le vice jusqu’à mettre l’ensemble dans un boîtier de caméra de surveillance factice, le résultat est bluffant ! (note de framboise314 : Je vous en avais présenté un modèle)

camaero_02

Montage dans un boîtier de caméra factice qui du coup devient… une vraie caméra 🙂

Sur la photo ci-dessus, le Raspberry Pi est monté sur le support qui se glisse dans la fausse caméra. A l’origine sur ce support il y a une pile et… une LED clignotante. Le cache qui vient fermer l’arrière de la caméra est sur la droite de la photo.

cam-aero côté technique

Matériel nécessaire

  • Raspberry B, B+ ou B2
  • Module Caméra Raspberry v1.3 ou supérieur – une version grand angle peut aussi être utilisée
  • Alimentation 2A
  • Carte microSD 8Gb
  • Raspbian Jessie

Le logiciel

La prise de vue est effectuée toutes les cinq minutes via un script lancé par crontab. Ensuite on récupère la météo de l’aéroport sur le site de Météo France (la réponse est au format XML, c’est un script PHP qui s’en charge) au format texte.

synoptique_600px

Cliquez pour agrandir

Via imagemagick on incruste le nom de la caméra et ces données météo sur l’image.

llagonne__meteo_20160826Afin de faciliter l’affichage sur les portables et alléger la consommation de data, les images sont transformées en JPeg progressif et les données Exif sont supprimées.

orly_20160826_600px

Cliquez pour agrandir

Ensuite les données sont envoyées en HTTP via une requête POST sur le serveur Cam-Aero. C’est lui qui assure l’authentification, l’enregistrement de l’image et sa publication sur Internet.

perigueux_20160826_600px

Cliquez pour agrandir

A chaque fois qu’une image est postée, les images datant de plus de trois heures sont automatiquement supprimées. Les images plus récentes sont conservées afin de permettre la génération d’un « replay » des trois dernières heures de prises de vue.

camaero_webcams

La page des webcam. Cliquez pour accéder à la page sur cam-aero.eu

 

Le serveur cam-aero.eu
L’hébergement est du OVH Mutualisé Pro. Antoine voulait l’accès SSH, car il a commencé sa carrière comme administrateur système et il a toujours du mal à se passer de la console 😉 .
Note de framboise314 : Ah bon, il y a autre chose que la console ? 😀

Le module cam-aéro est connecté directement au réseau des clubs et écoles de pilotage. Le choix de ne pas avoir d’accès distant (VPN, etc. …) a été fait pour garantir une neutralité maximale vis à vis de leur hôte.

llagonne_20160826_600px

Cliquez pour agrandir

L’idée est que Cam-Aéro se comporte comme n’importe quel PC connecté au réseau : il est client DHCP de la box et ne fait que des requêtes HTTP standard ! Comme ça, pas besoin d’ouverture de port ni de configuration compliquée : on branche et cela fonctionne.

Mise à jour du logiciel

Comme tout doit se faire de façon automatique, il faut pouvoir mettre à jour le logiciel et cela se passe via une télé-distribution deux fois par jour. L’ancien logiciel est supprimé et remplacé par la dernière version à jour.

Et demain… SIGFOX ?

Certains petits aéroports n’ont pas de station de météo. Antoine travaille avec un ami sur une station météo (témpérature, pression atmosphérique, vitesse et direction du vent) basée sur un Arduino et Sigfox pour l’envoi des données.

Conclusion

L’originalité du projet est de s’adresser à des structures qui sont friandes de caméra météo mais bloquées d’une part par le prix élevé et d’autre part (surtout) par la complexité de mise en œuvre (paramétrages, ouverture port Box ADSL, ouverture FTP et sortie de la webcam sur le http).

cam-aero élimine tous ces obstacles en proposant un serveur gratuit pour l’hébergement du service, ainsi qu’un système pratiquement prêt à l’emploi pour les petits aéroclubs (bin oui il faut quand même monter le boîtier et brancher l’alim. secteur 🙂 )

Si vous êtes pilote, membre d’un aéroclub ou simplement intéressé(e) par le sujet, diffusez l’information pour que les aéroclubs soient au courant de l’existence de cam-aero…

Laissez vos remarques, suggestions, propositions de participation (?) soit sur cam-aero, soit dans les commentaires ci-dessous.

Sources

RaspiCamAero : Aéronautique de loisir, caméra sur piste et informations METAR

Migration de serveur

Bonjour à toutes à et à tous,

Je viens de changer de serveur d’hébergement pour MagdiBlog.fr, aussi, veuillez m’excuser pour les coupures qui ont pu avoir lieux ces dernières 24h. Il se peut également que certains commentaires aient été perdus lors de la migration, mais c’est pour la bonne cause, le site devrait être beaucoup réactif à présent 🙂

Je profite de ce billet pour vous annoncer que le projet Pi BOAt n’est pas mort, les articles paraitront à partir de la rentrée 🙂

En outre, je prépare un article sur la partie vidéo, l’utilisation d’objectifs sur la Pi Camera ainsi que sur les différentes manières d’utiliser le module caméra sur le Pi 🙂

Profitez bien des quelques jours d’août qu’il nous reste,

A très bientôt,

Olivier

Cet article Migration de serveur est apparu en premier sur MagdiBlog.

Nantes Digital Week Conférence Raspberry Pi le 19 septembre 2016

conf20160919_250pxA l’occasion de Nantes Digital Week et à l’invitation des Editions ENI, j’animerai une conférence sur… le Raspberry Pi 😉 (vous aviez deviné ?).
La conférence aura lieu le Lundi 19 septembre 2016 au Hub Créatic de Nantes.
La conférence devrait débuter à 17h, toutefois si l’heure venait à évoluer, vous en seriez informé(e)s ici.

digital_week_transparent2

Du 15 au 25 septembre 2016

Toutes les cultures numériques ont rendez-vous à la troisième édition de la Nantes Digital Week, une semaine portée par l’ensemble des acteurs de l’écosystème numérique nantais rassemblant de multiples manifestations et événements variés.

Conférence Raspberry Pi

Dans le cadre de la Nantes Digital Week j’animerai une conférence sur le Raspberry Pi, sa technologie et ses applications à l’invitation des Editions ENI.

hub-creatic_600px

Hub Creatic – Nantes

Le lieu : le Hub Creatic 6 Rue Rose Dieng-Kuntz, 44300 Nantes. Le Hub Creatic est installé au cœur du parc d’innovations de La Chantrerie à Nantes. Cet immeuble de nouvelle génération accueille des entreprises TIC, en incubateur, pépinière ou hôtel d’entreprises.

La conférence aura lieu le 19 septembre 2016 à partir de 17h (si changement d’heure ce sera annoncé ici).

Ça parle de quoi ?

Après une présentation technique du Raspberry Pi 3 et du Raspberry Pi Zero (bin oui il y a des personnes qui ne savent pas forcément ce que c’est) je présenterai des utilisations du Raspberry Pi dans le monde des geek mais aussi dans l’industrie ou les SSII.

Vous êtes sur Nantes ou sa région ?

Ce sera un plaisir de vous rencontrer et d’échanger à l’occasion de cette visite à Nantes !

Sources

Prenez VRAIMENT la main à distance sur votre Raspberry Pi avec x11VNC

Prendre-la-main-250pxSi vous êtes un lecteur habituel du blog, vous vous souvenez sans doute que je vous avais expliqué comment installer et démarrer automatiquement tightvncserver sous Raspbian Jessie.
Alors, me dires-vous, on sait déjà prendre la main à distance sur le Raspberry Pi…. Pourquoi ajouter VRAIMENT dans le titre ? Eh bien cette fois tout ce que vous ferez sur la machine distante sera fidèlement reproduit en local sur le Raspberry Pi !  Vous bougez la souris sur votre PC ? Le curseur se déplace sur l’écran du Raspberry Pi. Vous ouvrez un menu sur l’écran local du Raspberry Pi ? Il s’ouvre aussi sur la machine distante 🙂

NIVEAU_debutant

Cliquez pour avoir une définition de ces niveaux

Prenez la main à distance sur un Raspberry Pi avec x11vnc

Présentation

Comme indiqué plus haut, l’avantage de ce type de prise en main, c’est que tous les mouvements de souris, frappes clavier et affichage écran sont absolument identiques et simultanés sur l’écran local du Raspberry Pi et sur la machine distante sur laquelle vous intervenez.

Imaginez une classe avec 10 Raspberry Pi en service… Vous pouvez vous connecter sur chacun des Raspberry Pi pour voir ce que l’utilisateur est en train de faire, et comment il le fait. De plus si un des participants a un souci, vous pouvez prendre la main à distance sur sa machine et lui montrer comment il faut faire… sans bouger de votre place 🙂

 

Attention !
Ce tutoriel ne fournit pas une connexion sécurisée car il n’utilise pas de solution de communication sécurisée comme SSH. Il ne faudra l’utiliser que sur un réseau local !

Ceci étant dit voilà ce que je vous propose :

  • Voir comment fonctionnent les sessions graphiques sur le Raspberry Pi
  • Installer x11vnc sur le Raspberry Pi
  • Vérifier que ça fonctionne avec RealVNC sous Windows et KDRC sous Linux
  • Pour ceux que ça intéresse, lancer x11VNC automatiquement au démarrage de Raspbian

Ça vous va ?

Alors on y va 😆

Comment ça marche ?

Session graphique locale

principe_session locale_600pxLorsque vous ouvrez une session graphique en local sur un Raspberry Pi sous Raspbian Jessie, les informations à afficher sont envoyées au DISPLAY :0. c’est celui qui est affiché sur l’écran local via HDMI si vous êtes connecté(e) à l’écran de cette façon.

Session graphique distante avec tightvncserver

principe_session locale2_600pxLorsque vous ouvrez une session distante avec tightvncserver, une nouvelle session graphique est ouverte sur le Raspberry Pi. Les images sont envoyées sur le DISPLAY :1.

Ce que vous affichez sur l’écran de la machine distante est différent de ce qui est affiché sur l’écran du Raspberry Pi. Votre souris et votre clavier interagissent avec votre session graphique, mais absolument pas sur la session DISPLAY :0.

C’est un peu comme si vous étiez connecté(e) sur une autre machine

Session graphique distante avec x11vnc

principe_sessionx11_600pxVoilà ce qui nous intéresse ! Avec une machine distante connectée sur le même DISPLAY :0 que le Raspberry Pi en local. Conséquence : toute action d’une machine (clavier, souris, clics sur un menu, une icône…) est instantanément dupliquée sur l’autre machine. Dans un sens et dans l’autre.

Installation de X11vnc sur le Raspberry Pi

Mettez le système à jour

Comme d’habitude, partez d’une machine mise à jour avant de procéder à l’installation de x11vnc :

sudo apt-get update
sudo apt-get upgrade

Installez x11vnc

Vous pouvez maintenant passer à l’installation de x11vnc. Rien de bien compliqué : un simple sudo apt-get install x11vnc fera tout le travail pour vous 🙂

pi@raspberrypi:~ $ sudo apt-get install x11vnc
Lecture des listes de paquets... Fait
Construction de l'arbre des dépendances
Lecture des informations d'état... Fait
Les paquets supplémentaires suivants seront installés&nbsp;:
libvncclient0 libvncserver0 tcl tcl8.6 tk tk8.6 x11vnc-data
Paquets suggérés&nbsp;:
tcl-tclreadline
Les NOUVEAUX paquets suivants seront installés&nbsp;:
libvncclient0 libvncserver0 tcl tcl8.6 tk tk8.6 x11vnc x11vnc-data
0 mis à jour, 8 nouvellement installés, 0 à enlever et 0 non mis à jour.
Il est nécessaire de prendre 1 621 ko dans les archives.
Après cette opération, 3 403 ko d'espace disque supplémentaires seront utilisés.
Souhaitez-vous continuer ? [O/n] o
Réception de&nbsp;: 1 http://mirrordirector.raspbian.org/raspbian/ jessie/main libvncclient0 armhf0.9.9+dfsg2-6.1+deb8u1 [118 kB]
Réception de&nbsp;: 2 http://mirrordirector.raspbian.org/raspbian/ jessie/main libvncserver0 armhf0.9.9+dfsg2-6.1+deb8u1 [174 kB]
.../...
Paramétrage de tk (8.6.0+8+b1) ...
Paramétrage de x11vnc-data (0.9.13-1.2) ...
Paramétrage de x11vnc (0.9.13-1.2) ...
Traitement des actions différées («&nbsp;triggers&nbsp;») pour libc-bin (2.19-18+deb8u4)&nbsp;. ..
pi@raspberrypi:~ $

Une entrée dans le menu

L’installation de x11vnc crée un fichier dans /usr/share/applications. ce fichier s’appelle x11vnc.desktop et il contient les informations destinées à l’insertion d‘une entrée de menu permettant d’accéder à x11vnc :

pi@raspberrypi:/usr/share/applications $ cat x11vnc.desktop
[Desktop Entry]
Name=X11VNC Server
Comment=Share this desktop by VNC
Exec=x11vnc -gui tray=setpass -rfbport PROMPT -bg -o %%HOME/.x11vnc.log.%%VNCDISPLAY
Icon=computer
Terminal=false
Type=Application
StartupNotify=false
#StartupWMClass=x11vnc_port_prompt
Categories=Network;RemoteAccess;

Ce qui produit l’affichage suivant dans le menu

ssvnc_04Si vous souhaitez franciser cette entrée du menu, rien de plus simple, modifiez le fichier x11vnc.desktop en l’ouvrant avec nano et changez le texte de ces deux lignes :

[Desktop Entry]
Name=Serveur X11VNC
Comment=Partager ce bureau avec VNC

Ce qui fait que le texte sera en français dans le menu ainsi que la bulle d’aide qui s’ouvre quand on passe la souris sur le texte :

ssvnc_09

Lancement manuel de x11vnc

Cliquez sur Serveur X11VNC dans le menu pour lancer le programme. Après quelques secondes, une fenêtre s’ouvre :

ssvnc_05Laissez le port sur 5900 (en principe le port 5900 correspond au DISPLAY :0, le port 5901 au DISPLAY :1 etc. Si vous modifiez le port, faites le en connaissance de cause et attendez vous à faire des modifs aussi sur la machine qui va se connecter à distance 🙂

Si vous avez des fichiers à transférer choisissez un mode de transfert. J’ai coché UltraVNC bien que je n’utilise pas cette possibilité… On ne sait jamais.

Validez vos choix en cliquant sur le bouton OK en bas à gauche de la fenêtre.

Encore quelques secondes d’attente (et on voit sur le moniteur CPU que celui-ci bosse !

ssvnc_06Autorisez les connexions distantes (cochez la case Accept Connections) sinon vous risquez d’avoir des soucis pour vous connecter à distance 😉

Dans la zone de texte Password saisissez un mot de passe qui sera demandé lorsque vous vous connecterez à distance. On n’est jamais trop prudent. Sans honte, j’ai choisi raspberry comme mot de passe 😛 (bah si un pirate russe hacke mon raspberry pi il aura fait du boulot pour rien 🙂 ). Vous pouvez laisser ViewOnly Password vide. C’est le mot de passe qui serait demandé pour une connexion en view only (sans possibilité d’interaction).

Cliquez sur Apply pour valiser vos choix puis sur OK.

En haut à droite de la barre de tâches vous voyez apparaître une icône de x11vnc (bon, ils se sont pas forcés pour le côté graphique 😉 )

ssvnc_07ssvnc_10Si vous cliquez sur cette icône, vous ouvrez un menu qui vous permettra de régler certains paramètres de x11vnc.

En particulier vous disposez ici de l’aide de x11vnc mais aussi de la possibilité d’arrêtre x11vnc.

D’autre part l’icône est un indicateur puisque si cette icône disparait cela signifie que x11vnc s’est arrêté de fonctionner pour une raison ou pour une autre.

Suite à ces opérations, x11vnc tourne sur le Raspberry Pi, nous pouvons essayer de nous connecter depuis un poste distant.

Connexion depuis un poste Windows

 

Installation
Si ce n’est pas fait, téléchargez et installer RealVNC Viewer (https://www.realvnc.com/download/viewer/).

Lancez l’exécution de RealVNC sur le PC distant.

viewer_01Saisissez l’adresse et le numéro de port de la machine sur laquelle vous voulez vous connecter (normalement c’est le Raspberry Pi dont on parle depuis le début de cet article 😉 ). Cliquez sur le bouton Connexion.

viewer_02De quoi je mêle ? Ignorez superbement ce message. C’est qui qui commande hein ? Dites lui OK et on n’en parle plus !

viewer_03RealVNC Viewer vous avertit que la connexion ne sera pas chiffrée. Allez on y va, cliquez sur Continuer.

viewer_04Hé non ce n’est pas encore fini 🙂 Entrez le mot de passe que vous avez saisi tout à l’heure dans la fenêtre de x11vnc sur le Raspberry, puis cliquez sur OK.

Allez c’était le clic final !

viewer_05Une superbe fenêtre s’ouvre sur votre écran et vous voici sur la session de votre Raspberry Pi. Pas de croix noire comme lors du premier lancement de tightvncviewer mais vous avez ici la flèche habituelle de Raspbian Jessie. Bougez la souris, ouvrez le menu et observez l’écran d’origine de votre Raspberry Pi…. Alors ?

Tout en haut, au milieu de la fenêtre il ya un rectangle. Si vous mettez le curseur de la souris dessus vous ouvrez un menu qui vous donne accès aux options de RealVNC.

ssvnc_08En particulier une des icônes vous permet d’envoyer CTRL ALT DEL sur la machine distante. Passez le curseur sur les icônes pour obtenir des infos sur l’utilisation de celles-ci.

Connexion depuis un poste Linux

Installation
Si ce n’est pas fait, installez KRDC sur système Linux : sudo apt-get install krdc

Ouvrez KRDC sur le poste Linux.

krdc_01Saisissez l’adresse du Raspberry Pi et le numéro de port sur lequel vous pouvez accéder à x11vnc. Cliquez sur la flèche située à droite de la zone de saisie pour accéder à x11vnc.

krdc_02Une fenêtre de configuration (Host Configuration – KRDC) s’ouvre. Vous pouvez choisir la qualité de transmission. Si votre réseau fonctionne bien, optez pour High Quality. Cochez la case Remember password si vous souhaitez mémoriser le mot de passe.

Lorsque vous avez terminé la configuration, cliquez sur le bouton OK.

krdc_03bDans la fenêtre qui s’ouvre, saisissez le mot de passe qui vous permet d’accéder à la session ouverte sur le Raspberry Pi puis cliquez sur le bouton OK.

krdc_04_600px

Cliquez pour agrandir…

Et voilà votre fenêtre du bureau Raspbian Jessie ouverte dans KRDC…

Bon, il reste un inconvénient… Le lancement de x11vnc est valable pour cette session du bureau. Si vous éteignez le Raspberry Pi, il faudra relancer à chaque fois x11vnc. Pas cool si c’est un Raspberry headless (sans clavier, souris ni écran 🙂 )

Alors il nous reste une dernière étape : automatiser le lancement de x11vnc !

Lancement automatique sous Raspbian Jessie

NIVEAU_avancé

Cliquez pour avoir une définition des niveaux

Créer un fichier mot de passe

Comme il faut saisir le mot de passe à chaque lancement, ce n’est pas pratique avec un Raspberry Pi headless… La solution, c’est de créer un fichier, d’y ranger le mot de passe et d’indiquer à x11vnc où il doit aller récupérer le mot de passe.

Il n’y a pas d’endroit privilégié pour stocker le mot de passe. Il peut être dans /home/pi/.vnc/ ou dans /etc. C’est cette option que j’ai choisie en créant un fichier x11vnc.pass avec l’option -storepasswd.

pi@raspberrypi:~ $ sudo x11vnc -storepasswd /etc/x11vnc.pass
Enter VNC password:
Verify password:
Write password to /etc/x11vnc.pass?  [y]/n y
Password written to: /etc/x11vnc.pass
pi@raspberrypi:~ $ sudo cat /etc/x11vnc.pass
ë▒▒▒▒▒

Ne cherchez pas à lire le mot de passe, il n’est bien entendu pas stocké en clair dans le fichier !

Créer le service pour Raspbian Jessie

Pour permettre le lancement automatique, il faut créer un fichier x11vnc.service dans /lib/systemd/system avec nano.

sudo nano /lib/systemd/system/x11vnc.service

Saisissez les lignes suivantes dans le fichier

[Unit]
Description=Start x11vnc at startup.
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/bin/x11vnc -auth guess -forever -loop -noxdamage -repeat -rfbauth /etc/x11vnc.pass -rfbport 5900 -shared

[Install]
WantedBy=multi-user.target

Maintenant que le service est prêt il faut demander à systemd de le prendre en compte pour qu’il soit lancé au démarrage :

sudo systemctl daemon-reload
sudo systemctl enable x11vnc.service

pi@raspberrypi:/usr/share/applications $ sudo systemctl daemon-reload
pi@raspberrypi:/usr/share/applications $ sudo systemctl enable x11vnc.service
Created symlink from /etc/systemd/system/multi-user.target.wants/x11vnc.service to /lib/systemd/system/x11vnc.service.

Bon, tout s’est bien passé, on peut rebooter le Raspberry Pi, vérifier en se connectant à distance que le lancement automatique de x11vnc a bien été fait.

Conclusion

C’est une demande souvent formulée : « tightvncserver c’est bien mais ce que je voudrais c’est intervenir sur le bureau de Raspbian Jessie, celui qui est ouvert, pas un autre…« 

Dans cette article vous trouverez les bases pour prendre la main à distance sur un Raspberry Pi. Tout n’est pas parfait, vous voudrez sans doute adapter la résolution de l’écran… Bon, je vous laisse un peu de boulot 🙂 Vous ne pensiez pas vous en tirer comme ça ?

Et puis si vous êtes sage, on verra peut-être un de ces jours comment prendre la main à distance mais de façon sécurisée, cette fois… Mais ça… c’est une autre histoire !

Sources

 

 

 

 

Ramdrive sur un Raspberry pi : réduisez l’usure de la carte SD!

Qu’est ce qu’un ramdrive?

Un ramdrive est un espace de stockage qui utilise la RAM (mémoire vive) au lieu du disque dur, SSD, mémoire flash ou autre moyen habituel de stockage. La RAM est plus rapide que tous ces supports, mais en cas de coupure de courant ou si on éteint l’ordinateur, les données sont perdues. Donc, à quoi ça sert, un stockage qui oublie à chaque reboot?

Pourquoi utiliser un ramdrive?

La réponse évidente, c’est la vitesse : si vous avez besoin d’accès ultrarapides, ça peut être une solution. Une seconde réponse intéressera les utilisateurs de raspberry pis : éviter d’user la carte SD du système. La mémoire flash s’use à chaque écriture (c’est simplifié, mais c’est l’idée). Un SSD aura des techniques pour répartir l’usure équitablement et donc ça ne rentrera pas réellement en compte. Mais pour une carte SD, il n’y a pas ces algorithmes avancés. Donc quand on écrit tout le temps au même endroit, on finira par « user » la carte, et elle aura des secteurs défectueux. C’est ici qu’intervient le ramdrive. Pour de nombreux projets à base de Raspberry pi, nous utilisons des capteurs, et nous souhaitons stocker les valeurs des capteurs. Du coup, si à chaque mesure on écrit sur la carte SD, elle lâchera bien vite, d’autant plus vite qu’on écrit souvent, par exemple chaque seconde. Dans ce contexte, je vous propose de créer un ramdrive pour pouvoir écrire dessus en continu, sans se soucier de l’usure, et de copier de temps en temps les données vers la carte SD. En stockant les mesures chaque seconde sur le ramdrive, puis en copiant les données toutes les heures, on écrira 3600 fois moins souvent sur la carte SD!

Voyons maintenant comment réaliser cela.

Comment créer un ramdrive

Les instructions suivantes sont valables sous linux, pas que sous raspbian. Le tutoriel est orienté vers les Raspberry pi, mais ça reste valable pour tous les systèmes Linux (et sans doute énormément d’Unix, et donc, oui, sous MacOS ça devrait aussi fonctionner 🙂 ).

Créer temporairement le ramdrive

Tout d’abord, il faut créer un répertoire qui servira de point de montage. C’est à dire que c’est l’endroit sur le système de fichier ou sera situé le ramdrive. Généralement, sous Linux, les disques sont montés dans le dossier /mnt. Puisque c’est un ramdrive, nous irons dans /mnt/ramdrive.

Pour créer ce répertoire, on utilisera la commande mkdir, en sudo, car l’utilisateur par défaut ne possède pas les droits :

sudo mkdir /mnt/ramdrive

Ce répertoire ayant été créé par root, il faut changer le propriétaire pour que l’utilisateur pi puisse écrire dedans, avec la commande chown :

sudo chown pi /mnt/ramdrive

Ici, nous donnons la propriété du répertoire /mnt/ramdrive à l’utilisateur pi. Ce n’est pas nécessaire pour le fonctionnement du ramdrive à proprement parler, mais si je souhaite qu’un programme lancé en tant qu’utilisateur (pi) puisse écrire dans ce répertoire, c’est une solution. Une autre serait de faire un chmod.

Il faut maintenant monter le ramdrive, en utilisant la commande mount :

sudo mount -t tmpfs -o size=8m tmpfs /mnt/ramdrive

Ici, je monte un système de fichier de type tmpfs, d’une taille de 8Mio dans le répertoire /mnt/ramdrive. Vous pouvez bien sur ajuster ces paramètres à votre convenance. La taille du système de fichier est importante, car la ram du Raspberry pi (ou de l’ordinateur utilisé) n’est pas infinie, et une valeur trop importante aura un impact sur les performances de la machine.

Créer le ramdrive de façon persistante (il sera recréé automatiquement au démarrage)

Si l’on souhaite profiter systématiquement de la présence du ramdrive au démarrage du système, sans avoir à lancer de commande supplémentaire, il suffit de modifier le fichier fstab de façon appropriée, pour que le ramdrive soit automatiquement monté au démarrage. Les données non sauvegardées seront toujours perdues, mais le ramdrive sera créé à chaque fois.

Pour cela, nous devons modifier le fichier fstab, par exemple avec vi ou nano, pour y ajouter la ligne suivante :

tmpfs /mnt/ramdisk tmpfs nodev,nosuid,noexec,nodiratime,size=8M 0 0

Ainsi, à chaque démarrage, nous aurons accès à notre ramdrive.

On pourra alors écrire sur le ramdrive sans problème, sans usure du système, et sans avoir à le créer au préalable.

Conclusions et ressources

En pratique, c’est quelque chose de simple à faire, et qui peut s’avérer utile. Toutefois, il convient de bien prendre en compte le fait que le contenu du ramdrive est perdu à chaque redémarrage du système. Il faut donc sauvegarder périodiquement les données que l’on souhaite conserver sur la mémoire persistante.

Je m’en sers pour mon projet Rlieh, un contrôleur d’aquarium automatisé. Celui ci mesure la température de plusieurs cuves, et la température de l’air, chaque seconde. Je peux donc faire des statistiques détaillées, mais je ne sauvegarde pas tout cela en continu. Chaque seconde, j’écris sur le ramdrive avec le programme qui récupère les données des capteurs, de sorte que d’autres programmes puissent s’en servir. Ainsi, il est facile de rendre les données accessibles pour un  serveur web, ou n’importe quel programme. Je n’archive qu’une partie des données (la température varie relativement peu, et change lentement, donc quelques mesures par heure suffisent; cependant je peux faire toutes sortes de statistiques sur les données completes tant qu’elles sont en mémoire, avant de les vider après X jours.), et donc je minimise encore l’usure de la carte.

Pour la gestion de l’écriture de sauvegarde, il est possible de faire un cron, ou encore de créer un demon unix qui se chargera de cette tâche, comme nous l’avons détaillé dans ce précédent tutoriel sur le wiki.

On peut également créer un partage samba pour pouvoir accéder au ramdrive depuis le réseau, comme détaillé dans notre tutoriel sur ce sujet.

Ce n’est qu’un exemple d’application parmi d’autres, mais c’est une corde à avoir à son arc!

Pour aller plus loin, vous pouvez consulter cet article en anglais sur le même sujet, ou encore celui ci qui explique la différence entre les systèmes tmpfs et ramfs (wikipedia FR : tmpfs, ramfs)

Cet article Ramdrive sur un Raspberry pi : réduisez l’usure de la carte SD! est apparu en premier sur Slog.

Un Raspberry Pi 2 en MediaCenter

dsc00067.jpg

  • Avec l’avènement du Raspberry Pi2, mon vieux médiacenter, lui-même PC recyclé (P4@2,66GHz) fait pas mal grise mine…

Incapable de lire des vidéo h264 qui dépasse le 720p, bruyant, énergivore (environ 80W), couplé à un vieux poste de télévision cathodique qui consomme aussi pas mal (environ 90Watt continu).
Utiliser un RpiPi2 (moins de 5W) donc me permettrait de laisser de côté tous ces inconvénients, cependant j'avais besoin d'une prise HDMI et c'est après avoir récupéré un vieux poste de télévision LCD (HD Ready, 720p et 1080i max, tuner mpeg2 obsolète, 50W en mode éco) que j'ai envisagé de changer mon système de médiacenter.

Entre temps, bonne nouvelle, plus besoin de bidouiller, ma clé wifi TP-Link est désormais reconnue par le kernel des derniers système GNU/Linux désigné pour RpiPi !

  • Voici donc l'histoire d'une migration, ou plutôt d'une lente progression…

1 - Choix du système d'exploitation :

À ma connaissance il y a deux possibilités, utiliser Raspbian Lite et à la main installer le logiciel médiacenter Kodi et tout le reste; Ou alors utiliser une distribution dédiée telle que Openelec ou RaspBMC. La première solution serait calquée sur ma démarche avec l'ancien médiacenter, mais puisqu'on me dit qu'Openelec offrirait de bien meilleures, j'ai donc choisis cette dernière…
Et après une semaine à m'échiner à faire fonctionner le truc, j'ai lâché l'affaire…
Pourtant j'avais réussis à tout faire fonctionner, télécommande, récup de la base, tout ça… sauf une chose, le partage réseau !
Avec ma config je n'ai pas le choix, je suis obligé de passer par un partage ssh, soit en sftp depuis l'interface de Kodi, qui s'évertue à monter ce partage en lecture seule…
Impossible de me rabattre sur le NFS, car mon stockage est chiffré, et NFS ne fonctionne donc pas.
Openelec étant verrouillée de partout, impossible d'installer un soft ou même d'écrire sur le disque, afin de monter un partage sshfs… Merci pour le temps perdu, au revoir !

Donc Raspbian Lite :) Ça je connais, et on va tout faire à la main, quoi de mieux que d'être servis par soit même !??

2 - Installer le Raspbian Lite :

Il existe actuellement 2016-05-10-raspbian-jessie-lite.zip, une version qui est entièrement en ligne de commande, sans environnement de bureau, donc très légère, idéale pour une petite carte SD (environ 800Mio une fois installé)

  • Déployer l'image sur une carte SD depuis un système GNU/Linux, avec la commande DD

(attention, of=/dev/sdx à adapter pour la cible de votre carte SD, en cas d'erreur risque d'effacement d'un disque dur non désiré)

dd bs=4M if=2016-05-10-raspbian-jessie-lite.img of=/dev/sdx; sync
  • Placer la carte SD dans le Raspberry, et le mettre sous tension.

Exécuter raspi-config, et configurer tous les trucs comme on veut, notamment les variables locales (time zone, clavier, etc), et très important, le mémory split pour allouer 512Mo au GPU, sans quoi impossible de lire de la HD 1080 !

raspi-config

Après le reboot, pour rappel :

Login : pi
Pass : raspberry

  • Effectuer les mises à jour :
sudo apt-get install rpi-update
sudo rpi-update
sudo apt-get update
sudo apt-get upgrade
  • Mettre les fichiers temporaires en ram pour économiser sur la durée de vie la la carte SD.
sudo nano /etc/fstab
tmpfs      /tmp		tmpfs	defaults,size=256M	0    0
tmpfs      /var/tmp	tmpfs	defaults,size=256M	0    0
tmpfs      /var/lock	tmpfs	defaults,size=256M	0    0
  • Editer le fichier /etc/network/interfaces et modifier comme suit la section wlan0 :
auto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-ssid "le nom du réseau wi-fi"
wpa-psk "le mot de passe du réseau wi-fi"
  • Relancer ensuite le réseau avec la commande :
service networking restart



3 - Installer Kodi :

sudo apt-get install kodi

Pour lancer Kodi il suffit de taper son nom depuis la console.
Si on veut que Kodi se lance au démarrage du RpiPi2, on peut éditer le fichier /etc/default/kodi, et le renseigner comme suit :

# Set this to 1 to enable startup
ENABLED=1

# The user to run Kodi as
USER=pi

# Adjust niceness of Kodi (decrease for higher priority)
NICE=-5

Sauf que… Pour une raison que j'ignore, une fois le RpiPi2 redémarré, Kodi doit se trouver dans un profil incomplet, car par exemple, son menu d'arrêt ne comporte qu'une seule entrée (exit), au lieu des 3 ou 4 habituelles (exit, reboot, shutdown…)
Ça c'est pas cool, surtout quand on constate qu'en lançant Kodi manuellement ce menu se retrouve bel et bien complet !
Encore un truc pas sec… alors on va faire les choses autrement !

  • On va configurer le login automatique de l'utilisateur sur le RpiPi2[1] en créant un autologin dans systemd :
sudo nano  /etc/systemd/system/getty@tty1.service.d/autologin.conf
  • Le remplir avec ceci :
# Systemd : logue automatiquement la console tty1 avec l utilisateur arcade
[service]
ExecStart=
ExecStart=-/sbin/agetty --autologin arcade --noclear %I 38400 linux
  • Puis enfin lancer ces deux commandes :
sudo systemctl enable getty@tty1.service
sudo sed -i -e "s/sbin\/agetty/sbin\/agetty -a arcade/g" /etc/systemd/system/getty.target.wants/getty@tty1.service
  • Au redémarrage du RpiPi2 plus besoin de se loguer, reste donc à lancer Kodi en éditant le fichier .profile :
nano ~/.profile
  • Et ajouter les lignes suivantes à la fin :
tty=`tty`
# Start Kodi only if login in tty1
if [ $tty = '/dev/tty1' ]; then
        kodi
fi



4 - Montage automatique du partage réseau ssh :

On va utiliser simplement le fstab, et s'arranger pour que le mot de passe ssh ne soit pas demandé.

  • Installation de sshfs :
sudo apt-get instal sshfs
  • Création d'une paire de clés publiques/privées pour l'utilisateur sur le client (le RpiPi2) :

fstab est géré par l'utilisateur root, c'est donc des cléf de celui-ci dont on a va avoir besoin, pour spécifier cela on utilise sudo :

sudo ssh-keygen -t dsa

Laisser le chemin par défaut (/root/.ssh/id_dsa) et laisser la demande de passphrase vide.

  • Copier ensuite la clé publique sur le serveur (la machine ou se trouve le stockage) :
sudo ssh-copy-id -i /root/.ssh/id_dsa.pub -p N°-de-port MONLOGIN@IP-du-serveur
  • Éditer le fstab :
sudo nano /etc/fstab
  • Et ajouter cette ligne :
sshfs#MONLOGIN@IP-du-serveu:/répertoire/distant    /hone/pi/video    fuse    port=N°-de-port,user,noatime,allow_other,_netdev    0    0
  • Ne pas oublier le créer le dossier ~/video :
mkdir /home/pi/video

Et vérifier en montant le contenu de fstab :

sudo mount -a



infrarougeRPI_bb.png

5 - La télécommande infra-rouge :

Jusqu'à présent j'utilisais une télécommande de récupération, couplée à un récepteur infra-rouge connecté sur une entrée de la carte son d'un vieux PC.
Je me suis dit, cette fois-ci, on va utiliser les nouvelles techno, et grâce au HDMI CEC je vais pouvoir piloter le médiacenter exécuté sur le RpiPi2, avec la télécommande de la TV !
Mais en fait non, le téléviseur n'est pas compatible avec cette norme, il faut donc passer par un récepteur infrarouge connecté sur un GPIO. Ici j'ai câblé le GPI018.

  • Installer le soft de gestion de l'infrarouge :
sudo apt-get install lirc
  • Éditer hardware.conf pour régler les paramètres de LIRC :
sudo nano /etc/lirc/hardware.conf
  • Faire correspondre le contenu à ceci :
########################################################
# /etc/lirc/hardware.conf
#
# Arguments which will be used when launching lircd
LIRCD_ARGS="--uinput"
# Don't start lircmd even if there seems to be a good config file
# START_LIRCMD=false
# Don't start irexec, even if a good config file seems to exist.
# START_IREXEC=false
#Try to load appropriate kernel modules
LOAD_MODULES=true
# Run "lircd --driver=help" for a list of supported drivers.
DRIVER="default"
# usually /dev/lirc0 is the correct setting for systems using udev
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"
# Default configuration files for your hardware if any
LIRCD_CONF=""
LIRCMD_CONF=""
########################################################
  • Activer le module sur le RpiPi2 en éditant config.txt
sudo nano /boot/config.txt
  • Dé-commenter la ligne (sans autre argument, c'est le GPIO18 qui sera écouté par le module, sinon pour par exemple le GPIO23, on indiquera dtoverlay=lirc-rpi,gpio_in_pin=23) et redémarrer le RpiPi2.
dtoverlay=lirc-rpi
  • Pour tester si les commandes infrarouge sont bien reçues :
sudo killall lircd
sudo mode2 -d /dev/lirc0
  • À chaque appuie sur une touche de la télécommande, devrait s'afficher ce genre de truc :
space 1223
pulse 281
space 1193
pulse 350
space 1199
pulse 264
space 1901
  • Bien, maintenant on va identifier la télécommande et associer chacun de ses boutons à une touche du clavier. Le programme va demander d'appuyer anarchiquement sur toutes les touches, puis de nommer les touches une à une. La syntaxe à respecter pour les noms est disponible en exécutant :
irrecord --list-namespace

Il est temps de procéder :

sudo irrecord -d /dev/lirc0 /etc/lirc/lircd.conf

J'ai choisis ces touches du clavier, qui correspondent aux raccourcis clavier les plus utile de Kodi, pour la simple raison que ça fonctionnera directement ! Pas la peine de configurer quoi que ce soit dans Kodi !

KEY_Up
KEY_Down
KEY_Right
KEY_Left
KEY_Enter
KEY_C (click droit)
KEY_PageUp
KEY_Esc (retour menu principal)
KEY_Back (retour menu précédent)
KEY_Rewind
KEY_Stop
KEY_PlayPause
KEY_FastForward

  • Reste à redémarrer le service LIRC
sudo /etc/init.d/lirc restart
  • Un moyen simple de savoir si ça marche, c'est de simplement appuyer sur les touches de la télécommande, ça devrait agir dans la console GNU/Linux !

Par exemple la touche correspondant à KEY_C affichera le caractère C, normal quoi, puisque LIRC permet ainsi d'assigner l'entièreté des touches du clavier, et plus encore.
Voici le contenu de lircd.conf, il est toujours temps de le modifier au besoin !

# Please make this file available to others
# by sending it to <lirc@bartelmus.de>
#
# this config file was automatically generated
# using lirc-0.9.1-git(default) on Wed Jul 20 22:41:05 2016
#
# contributed by
#
# brand:                       lircd.conf
# model no. of remote control:
# devices being controlled by this remote:
#

begin remote

  name  lircd.conf
  bits           15
  flags SPACE_ENC|CONST_LENGTH
  eps            30
  aeps          100

  one           304  1799
  zero          304   746
  ptrail        304
  gap          66906
  toggle_bit_mask 0x0

      begin codes
          KEY_Up                    0x43AA 0x4055
          KEY_Down                    0x4012 0x43ED
          KEY_Right                    0x406E 0x4391
          KEY_Left                    0x43AE 0x4051
          KEY_Enter                    0x412A 0x42D5
          KEY_C               0x42BE 0x4141
          KEY_PageUp                    0x409E 0x4361
          KEY_Esc                    0x4072 0x438D
          KEY_Back                    0x3ECE 0x3D31
          KEY_Rewind                    0x404A 0x43B5
          KEY_Stop                    0x424A 0x41B5
          KEY_PlayPause                    0x414A 0x42B5
          KEY_FastForward                    0x434A 0x40B5
      end codes

end remote

Au lancement de Kodi la télécommande devrait fonctionner Si ce n'est pas le cas, ressortir et vérifier que le processus lircd tourne bien.

ps -A | grep lirc



6 - Migration de base de données :

  • Toute la configuration de Kodi est bien rangée dans le dossier caché .kodi dans le home de l'utilisateur.

J'ai donc récupéré le dossier de mon ancien médiacenter afin de le copier sur le nouveau.
Oké donc après redémarrage du RpiPi2, j'ajoute ma source de vidéos, je navigue avec ma télécommande parmi les fichiers média, et … haaaa mais non, où sont passés les status lu/non-lu des fichiers ??? Tout est vierge !
Le dossier .kodi/userdata/Databases est pourtant bien en place…
Ho, une nouvelle base MyVideo93.db a été crée, la mienne MyVideo99.db a été ignorée semble t'il !
Qu'à cela ne tienne, je renomme la mienne en MyVideo93.db :)
Toujours rien, et cette fois le message d'erreur suivant apparait dans le log Kodi : .kodi/temp/kodi.log

10:51:06 9.596940 T:1967722496 ERROR: Can't open the database MyVideos93 as it is a NEWER version than what we were expecting?

Après beaucoup de temps à trifouiller la configuration de Kodi, j'ai fini par comprendre le truc…
Les bases utilisées par Kodi (par défaut des bases SQLite, extention .db stockée dans .kodi/userdata/Databases) sont incompatible pour la simple raison que le Kodi du vieux PC Pentium4 est plus récent (KODI 16.0) que celui du RpiPi2 (KODI 15).
Impossible de downgrader, me voilà bien embêté…

  • Pour résumer en une ligne : Sur le vieux PC médiacenter la base en V16 stokée dans ~/.kodi/userdata/Database/MyVideo99.db est incompatible avec la base sur le RpiPi2 en V15 stokée dans ~/.kodi/userdata/Database/MyVideo93.db.
  • Je veux pourtant récupérer les données de cette base, surtout les fichier lu/non-lu, alors j'ai procédé ainsi :
  • Installer les outils sur un ordinateur :
sudo apt install sqlite3 sqlitebrowser
  • Ouvrir la base en V16 (celle qui contient les données) : MyVideo99.db
sqlite3 MyVideos99.db
  • Dumper une à une les tables depuis SQLite en format SQL pour sauvegarder la structure.

Première commande, rediriger la sortie vers un fichier puis dumper la table depuis SQLite :

sqlite> .output bookmark.sql
  • Seconde commande, dumper :
sqlite> .dump bookmark
  • Faire de même pour les tables : files, path, settings, streamdetails.

On a donc sous la main les fichiers : bookmark.sql, files.sql, path.sql, settings.sql, streamdetails.sql.

  • À la fin on quitte :
sqlite> .quit
  • Ensuite on va importer ces données dans la base en V15 : MyVideo93.db

Ouvrir la base :

sqlite3 MyVideos93.db
  • Tout d'abord effacez la table de la base:
sqlite> drop table bookmark;
  • Puis lire le fichier sauvegardé:
sqlite> .read bookmark.sql
  • Pour vérifier que la table s'est bien importée.
sqlite> select * from bookmark;

Faire de même pour les tables : files, path, settings, streamdetails.

  • À la fin on quitte :
sqlite> .quit

Ok, mais ce n'est pas tout, car une fois la base MyVideo93.db mise en place sur le RpiPi2, toujours rien dans le dossier des vidéos, les status lu/non-lu ne remontent pas…
J'ai fini par comprendre qu'il s'agissait d'un problème avec les chemins utilisés sur l'ancien PC médiacenter qui étaient bien différents sur le nouveau RpiPi2, et que donc il fallait le mettre en conformité avec ce dernier dans la table path.
Sur la base MyVideo93.db, le seul path qui apparaissait était donc : /home/makoto/video/

  • Il suffirait de remplacer tout les anciens path par ce nouveau.

On peut le faire à la main avec le logiciel sqlitebrowser, ou alors par la ligne de commande :

sqlite3 MyVideos93.db
  • La commande va rechercher/remplacer le chemin /home/makoto/video/ par le nouveau !
UPDATE path SET strPath = REPLACE(strPath,'/home/makoto/video', '/home/pi/video');
  • À la fin on quitte
sqlite> .quit

Voilà c'est terminé, une fois la base rangée sur le RpiPi2 à sa place .kodi/userdata/Database/MyVideos93.db je constate que les status lu/non-lu sont tous présents :) Ouf !!!

Ressources :

http://blog.dorian-depriester.fr/linux/raspberry/utiliser-une-telecommande-infrarouge-pour-controler-xbmc
http://kodi.wiki/view/Keymaps
http://kodi.wiki/view/LIRC
http://wiki.openelec.tv/index.php/Guide_To_lirc_rpi_GPIO_Receiver
http://bornslacker.com/raspberry-pi-with-openelec-part-2/
https://anderson69s.com/2015/08/04/raspberry-pi-dupliquer-sa-telecommande-ir/
http://info.geekitude.fr/kodi/accueil
http://www.geekmag.fr/kodi-deplacer-le-profil-et-ses-donnees/
https://doc.ubuntu-fr.org/sqlite#inserer_des_valeurs_dans_la_table
http://kodi.wiki/view/HOW-TO:Update_SQL_databases_when_files_move

Note

[1] Note : je n'ai pas testé le login automatique via « raspi-config »

Les écrans LCD texte et leur conversion I2C

J’ai fait une nouvelle vidéo sur ma chaine youtube, sur les écrans LCD texte classiques, à base de HD44780 (ceux qu’on trouve partout), et sur leur conversion en écrans I2C en utilisant une carte additionnelle qui permet de passer de 6-8 GPIOs occupés à 2 (qui restent utilisables en plus!).

Plus d’explications dans la vidéo :

Cet article Les écrans LCD texte et leur conversion I2C est apparu en premier sur Slog.

La Saga Blink Suite : Elle clignote toujours

figure08La led dans tous ses états. Un petit complément à la saga blink La Saga Blink s’avère nécessaire pour les raisons suivantes :
et nous, les utilisateurs de l’écosystème Arduino, on n’a pas droit au C++
– exploiter le partage des ressources utilisées par les applicatifs de la saga blink,
saluer la « copine clignotante qui vient de nous rejoindre : Plongez dans le Go

Introduction

La présentation s’adresse, toujours, aux débutants expérimentés c’est à dire ceux qui disposent d’un Raspberry Pi opérationnel et qui n’ont pas peur d’utiliser la console pour l’élaboration des programmes. Les experts n’ont pas besoin de cette présentation et la trouveront certainement encore plus inutile que la première.

Par contre, leurs remarques et commentaires sont les bienvenus pour nous permettre d’évoluer.

Base d’expérimentation

Comme il s’agit d’un complément, pour l’instant, tout est identique au chapitre 2 de la « saga Blink ».

Langage C++

Que les experts du C++ me pardonnent car la suite n’est pas conforme au concept C++, mais notre but est de s’amuser et de prendre en mains notre framboise. Ce qui est important pour nous, c’est que cela « tombe en marche ».

Comme la structure de nos programmes C sont compatibles avec C++, il suffit de renommer les fichiers dans un premier temps. Puis, il faudra les recompiler.
Comment ! ; aurais-je fait du « C++ sans que j’en susse rien » (je n’ai pas résisté à la parodie du Bourgeois gentilhomme de Molière).

Une utilisation spécifique du raspberry Pi en C++ fera l’objet d’un autre chapitre.

Langage C++ (boucle infinie)

Voir chapitre 2.4 de la « saga blink ».

wiringPi

Programme

Il suffit de renommer le fichier blink80.c en blink80.cpp avec la commande si dessous :

mv blink80.c blink80.cpp

Exécution du programme

Pour exécuter se programme, il faut d’abord le compiler. La compilation se fait par la commande ci-dessous (en mode console) :

g++ -Wall -o blink80 blink80.cpp -lwiringPi

Le résultat de la compilation est un fichier : blink80

Lancement du programme :

sudo ./blink80

Pour sortir de la boucle infinie, il suffit de faire un Ctrl+c au clavier.

Remarque : On aurait aussi pu utiliser gcc à la place de g++ car le compilateur fait la différence entre les fichier .c et les fichier .cpp (g++ est un alias de gcc).

perso

Programme

Il suffit de renommer le fichier blink50.c en blink50.cpp et ipsGPIO.c en ipsGPIO.cpp avec la commande si dessous :

mv blink50.c blink50.cpp
mv ipsGPIO.c ipsGPIO.cpp

Exécution du programme

Pour exécuter se programme, il faut d’abord le compiler. La compilation se fait par la commande ci-dessous (en mode console) pour les différents fichiers C en créant des fichier objet :

g++ -Wall -c ipsGPIO.cpp
g++ -Wall -c blink50.cpp

Et pour terminer, il faut créer le fichier final via l’éditeur de lien :

g++ -Wall -o blink50 blink50.o ipsGPIO.o

Le résultat de la compilation est un fichier : blink50

Lancement du programme :

sudo ./blink50

Pour sortir de la boucle infinie, il suffit de faire un Ctrl+c au clavier.

Remarque : On peut supprimer les fichiers objets (.o) du répertoire courant par :

rm *.o

Langage C++ (boucle infinie avec sortie)

Voir chapitre 7 de la « saga blink ».

wiringPi

Pour ce programme, on utilise la même méthode pour sortir de la boucle infinie que pour pigpio. La saisie non bloquante d’un caractère clavier est incluse dans un fichier .h. Les experts du C vont voir tout rouge car on ne met pas de code dans un fichier .h, mais cela fonctionne et l’on fera mieux dans le prochain source.

Programme

Il suffit de renommer le fichier blink81.c en blink81.cpp avec la commande si dessous :

mv blink81.c blink81.cpp

Exécution du programme

Pour exécuter se programme, il faut d’abord le compiler. La compilation se fait par la commande ci-dessous (en mode console) :

g++ -Wall -o blink81 blink81.cpp -lwiringPi

Le résultat de la compilation est un fichier : blink81

Lancement du programme :

sudo ./blink81

Pour sortir de la boucle infinie, il suffit de faire un ‘q’ ou ‘Q’ au clavier.

perso

Programme

Il suffit de renommer le fichier blink52.c en blink52.cpp, ipsGPIO.c en ipsGPIO.cpp et saisieCarac.c en saisieCarac.cpp avec la commande si dessous :

mv blink52.c blink52.cpp
mv ipsGPIO.c ipsGPIO.cpp
mv saisieCarac.c saisieCarac.cpp

Exécution du programme

Pour exécuter se programme, il faut d’abord le compiler. La compilation se fait par la commande ci-dessous (en mode console) pour les différents fichiers CPP en créant des fichier objet :

g++ -Wall -c ipsGPIO.cpp
g++ -Wall -c saisieCarac.cpp
g++ -Wall -c blink52.cpp

Et pour terminer, il faut créer le fichier final via l’éditeur de lien :

g++ -Wall -o blink52 blink52.o ipsGPIO.o saisieCarac.o

Le résultat de la compilation est un fichier : blink52

Lancement du programme :

sudo ./blink52

Pour sortir de la boucle infinie, il suffit de faire un Ctrl+c au clavier.

Remarque : On peut supprimer les fichiers objets (.o) du répertoire courant par :

rm *.o

Langage C++ – Plus Classe

Pour la suite de la présentation, nous allons modifier notre cahier des charges et notre organigramme. Le câblage restera en tout point identique à celui du début de la saga. Cette modification nous permettra de mettre en évidence le temps de réaction du programme et la consommation de ressources.

Pour la programmation en C++ et la notion de class, vous trouverez de nombreux tutoriels et cours sur la toile ou dans des ouvrages spécialisés sur le sujet.

Cahier des charges et Organigramme

Notre nouveau cahier des charges se résume à celui du paragraphe 2.3 de la saga blink en y ajoutant une sortie de la boucle infernale.

1) si appuie touche clavier, si appuie alors 1) assignation ports GPIO
2) allumer led, 2)arrêt programme
3) attendre 1 seconde,
4) éteindre led,
5) attendre 1 seconde,
6) continuer en 1)

Nous avons opté pour cet organigramme pour satisfaire les ardeurs des aficionados de la ressource cpu en incluant une attente bloquante. Dans la vraie vie, il faudra trouver le bon compromis entre réactivité/ressource.

figure09
Le première étape consiste à initialiser le port GPIO23 pour qu’elle puisse piloter la led.
L’étape suivante teste si on a appuyé sur une touche clavier (q ou Q). Si la condition est vérifiée, on libère la ressource et l’on quitte le programme.
Puis, on allume la led,
Étape suivante, attendre x secondes,
Après cette attente, on éteint la led,
Suivi d’une nouvelle attente,
Pour finalement recommencer le cycle.

Programme C++

Programme

Ce programme se compose d’un fichier blink15.cpp et de la bibliothèque saisieCarac pour la saisie clavier non bloquante. L’interface avec les ports GPIO est assurée par la bibliothèque wiringPi.

saisieCarac.h et saisieCarac.cpp

Pour saisir le programme saisieCarc.h, il faut faire dans la console :

nano saisieCarac.h

Cette commande ouvre un fichier de texte vide appelé saisieCarac.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme saisie non bloquante caractère au clavier
Fichier entête de saisieCarac.cpp
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 21/07/2016
date de mise à jour : 21/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#ifndef SAISIE_CARAC_h
#define SAISIE_CARAC_h
/* -------------------------------------------------------------------------- */
//prototypes fonctions
int kbhit(void);
/* -------------------------------------------------------------------------- */
#endif

Pour saisir le programme saisieCarc.cpp, il faut faire dans la console :

nano saisieCarac.cpp

Cette commande ouvre un fichier de texte vide appelé saisieCarac.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme saisie non bloquante caractère au clavier
os                  : RPi Linux 4.4.13+
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 22/06/2016
date de mise à jour : 22/06/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
/* -----------------------------------------------------------------------------
Bibliothèques
----------------------------------------------------------------------------- */
#include <stdio.h>                  //bibliothèque standard
#include <termios.h>                //bibliothèque entrées/sorties terminal
#include <unistd.h>                 //bibliothèque constantes symboliques
#include <fcntl.h>                  //bibliothèque descripteur de fichier
#include "saisieCarac.h"            //
/* -------------------------------------------------------------------------- */
int kbhit(void){                    //fonction indiquant si frappe clavier
    struct termios oldt, newt;
    int ch;
    int oldf;

    tcgetattr(STDIN_FILENO, &oldt); //sauve paramètres terminal
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
                                    //nouveaux paramètres terminal
    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

    ch = getchar();                 //lecture caractère

    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
                                    //restaure paramètres terminal
    fcntl(STDIN_FILENO, F_SETFL, oldf);

    if(ch != EOF){
        ungetc(ch, stdin);          //replace le caractère dans le flux stdin
                                    //affiche le caractère dans la console
        return 1;                   //valeur de retour caractère saisie
}
return 0;                          //valeur de retour pas de caractère
}
/* -------------------------------------------------------------------------- */

blink15.cpp

Pour saisir le programme blink15.cpp, il faut faire dans la console :

nano blink15.cpp

Cette commande ouvre un fichier de texte vide appelé blink15.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique LED clignotante
Led rouge sur GPIO23 (4) via une résistance de 330 ohms
os                  : RPi Linux 4.4.13+
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 28/07/2016
date de mise à jour : 28/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : www.wiringpi.com
Remarques           :
----------------------------------------------------------------------------- */
/* -----------------------------------------------------------------------------
Bibliothèques
----------------------------------------------------------------------------- */
#include <stdio.h>                     //utilisé pour printf()
#include <iostream>                    //pour endl
#include "saisieCarac.h"               //entête saisie non bloquante
#include <wiringPi.h>                  //bibliothèque wiringPi
//------------------------------------------------------------------------------
#define LED 4 //numéro led = GPIO23
#define DUREE 1000                     //durée demi-période en millis-sec

using namespace std;                   //espace de nommage

int main(void){                        //programme principale
    int c(0);
    cout << "Led clignotante" << endl; //IHM
    cout << "q ou Q pour quitter :";   //IHM
    wiringPiSetup();                   //numérotation wiringPi ou "pseudo Arduino"
    digitalWrite(LED, LOW);            //led éteinte
    pinMode(LED, OUTPUT);              //pin en sortie
    for(;;){                           //boucle infinie
        if(kbhit())                    //si touche saisie
            c = getchar();             //lecture touche
        if(tolower(c) == 'q')          //si q ou Q
            break;                     //sortie boucle
        digitalWrite(LED, HIGH);       //allume led
        delay (DUREE);                 //attente DUREE
        digitalWrite(LED, LOW);        //éteind led
        delay (DUREE);                 //attente DUREE
    }
    digitalWrite(LED, LOW);            //éteint led
    pinMode(LED, INPUT);               //pin en entrée
    cout << endl << "Fin du programme" << endl;
                                       //IHM
    return(0);                         //code sortie
}
//------------------------------------------------------------------------------

Exécution du programme

Pour exécuter se programme, il faut au préalable le compiler. La compilation se fait par la commande ci-dessous (en mode console) pour les différents fichiers C++ en créant les fichiers objets correspondants.

g++ -Wall -c saisieCarac.cpp
g++ -Wall -c blink15.cpp

Et pour terminer, il faut créer le fichier final via l’éditeur de lien :

g++ -Wall -o blink15 blink15.o saisieCarac.o -lwiringPi

Le résultat de la compilation est un fichier : blink15

Lancement du programme :

sudo ./blink15

Pour sortir de la boucle, il suffit de faire q ou Q au clavier.

Remarque : On peut supprimer les fichiers objets (.o) du répertoire courant par :

rm *.o

Commentaire

La sortie de la boucle infinie va être tributaire de l’instant où l’on entre le caractère q au clavier. En effet, si la saisie de q intervient lorsque le programme exécute la ligne 43, la prise en compte de l’ordre de sortie va intervenir quasiment de suite (temps proche de 0 seconde). Par contre, si la saisie se fait lors de l’exécution de la ligne 39, il faudra attendre le passage par la ligne 35 pour provoquer la sortie (soit environ 2 seconde plus tard). C’est d’ailleurs le principal inconvénient des attentes bloquantes sur les cartes embarquées.

Attendre 2 secondes parce que la température de la piscine est passée sous la température basse et qu’il faut chauffer l’eau n’est pas un problème. En effet, ce temps ne représente rien par rapport à l’inertie thermique de la piscine.

Attendre 2 secondes pour arrêter un dispositif mécanique en mouvement lorsqu’on actionne un fin de course est une autre affaire. Je vous fait grâce, des contraintes mécaniques, des déformations physiques, de la casse matériel et, des éventuelles, atteintes à l’intégrité physique des personnes.

Tout est possible, ce n’est qu’un problème de choix mais il faut le faire en toute connaissance de cause.

Programme C++ avec Class

Programme

Profitons de C++ pour créer une classe qui nous permettra d’instancier notre (nos) led (leds). Pour créer notre classe GPIOClassOut nous utiliserons les drivers Linux (utilisés dans de nombreux exemples précédents) pour accéder aux ports GPIO. Cet technique n’est pas la plus efficace mais elle permet de savoir ce que l’on fait.

Notre programme comporte un fichier blink05.cpp (programme principal) et de deux librairies saisieCarc.h et GPIOClassOut.h.

Comme d’habitude les fichiers saisieCarac.h et saisieCarc.cpp sont des fichiers C. Je sais, ce n’est pas bien mais je n’ai pas d’autres solutions pour l’instant.
Nota : Si l’un des lecteurs à une solution élégante pour saisir un caractère non bloquante au clavier en C++, je suis preneur.

saisieCarac.h et saisieCarac.cpp

Identique aux programmes du chapitre 4.2.1.1

GPIOClassOut.h et GPIOClassOut.cpp

Pour saisir le programme GPIOClassOut.h, il faut faire dans la console :

nano GPIOClassOut.h

Cette commande ouvre un fichier de texte vide appelé GPIOClassOut.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique LED clignotante
Fichier entête de GPIOClassOut.cpp
os                  : RPi Linux 4.4.13+
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 21/07/2016
date de mise à jour : 21/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#ifndef GPIOClassOut_H
#define GPIOClassOut_H

class GPIOClassOut{                     //classe GPIO en sortie
public:                                 //attributs et méthodes publics
    GPIOClassOut();
    void begin(int broche);             //initialise la sortie
    void on();                          //met la sortie à 1 (3.3V)
    void off();                         //met la sortie à 0 (0V)
    void close();                       //ferme la / les sortie(s)
    void toggle();                      //inverse état de la sortie
private:                                //attributs et méthodes privés
    void export_gpio();                 //méthode export
    void gpio_direction();              //méthode direction uniquement sortie
                                        //pour notre exemple
    void unexport_gpio();               //méthode unexport
    char read_gpio();                   //lecture état port sortie
    int _broche;                        //numéro du port
};
#endif

Le fichier entête contient les attributs (variables) et les méthodes (fonctions) de notre class GPIOClassOut.cpp. Certains attributs et certaines méthodes sont déclarés en « public » et elles sont accessibles à partir de notre programme blink.

Tout ce qui est déclaré en « private«  est uniquement à usage interne de la Class.

Pour saisir le programme GPIOClassOut.cpp, il faut faire dans la console :

nano GPIOClassOut.cpp

Cette commande ouvre un fichier de texte vide appelé GPIOClassOut.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique LED clignotante
Fichier bibliothèque class GPIOClassOut.cpp
os                  : RPi Linux 4.4.13+
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 21/07/2016
date de mise à jour : 21/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
/* -----------------------------------------------------------------------------
 Biliothèques
----------------------------------------------------------------------------- */
#include "GPIOClassOut.h"               //entête class
#include <stdlib.h>                     //pour .c_str()
#include <sstream>                      //pour stringstream
#include <fstream>                      //pour la lecture fichier

using namespace std;                    //espace de nommage
/* -------------------------------------------------------------------------- */
/*
Classe GPIOClassOut
*/
GPIOClassOut::GPIOClassOut():
    _broche(0)                          //valeur par défaut
{
}
/*
Initialisation sortie
*/
void GPIOClassOut::begin(int broche){
    _broche = broche;                   //copie dans variable privée
    if(_broche <= 0){                   //si <= 0
        _broche = 23;                   //valeur par défaut
    }
    export_gpio();                      //méthode export
    gpio_direction();                   //méthode direction
}
/*
Exporte la sortie
*/
void GPIOClassOut::export_gpio(){
    string commande;                    //variable commande à transmettre
    stringstream ss;                    //variable de conversion
    string broche;                      //numéro broche en string
    ss << _broche;                      //conversion de int en string, on aurait
    ss >> broche;                       //pu utiliser la méthode .to_string()
                                        //mais nécésitait une directive de
                                        //compilation supplémentaire
    commande = "echo " + broche + " > /sys/class/gpio/export";
                                        //constitution de la commande à
                                        //transmettre
    system(commande.c_str());           //transmission de la commande linux
}
/*
Assigne la sortie
*/
void GPIOClassOut::gpio_direction(){
    string commande;                    //variable commande à transmettre
    stringstream ss;                    //variable de conversion
    string broche;                      //numéro broche en string
    ss << _broche;                      //conversion de int en string
    ss >> broche;                       //conversion de int en string
    commande = "echo out > /sys/class/gpio/gpio" + broche + "/direction";
                                        //constitution de la commande à
                                        //transmettre
    system(commande.c_str());           //transmission de la commande linux
}
/*
Libère la ressource
*/
void GPIOClassOut::unexport_gpio(){
    string commande;                    //variable commande à transmettre
    stringstream ss;                    //variable de conversion
    string broche;                      //numéro broche en string
    ss << _broche;                      //conversion de int en string
    ss >> broche;                       //conversion de int en string
    commande = "echo " + broche + " > /sys/class/gpio/unexport";
                                        //constitution de la commande à
                                        //transmettre
    system(commande.c_str());           //transmission de la commande linux
}
/*
Inverse l'état de la sortie
*/
void GPIOClassOut::toggle(){
    char etat = read_gpio();            //lecture état de la sortie
    if(etat == '1')                     //si état haut
        off();                          //mettre à 0 par méthode off
    else                                //si état bas
        on();                           //mettre à 1 par méthode on
}
/*
Mise à 1 (3.3V) de la sortie
*/
void GPIOClassOut::on(){
    string commande;                    //variable commande à transmettre
    stringstream ss;                    //variable de conversion
    string broche;                      //numéro broche en string
    ss << _broche;                      //conversion de int en string
    ss >> broche;                       //conversion de int en string
    commande = "echo 1 > /sys/class/gpio/gpio" + broche + "/value";
                                        //constitution de la commande à
                                        //transmettre
    system(commande.c_str());           //transmission de la commande linux
}
/*
Mise à 0 (0V) de la sortie
*/
void GPIOClassOut::off(){
    string commande;                    //variable commande à transmettre
    stringstream ss;                    //variable de conversion
    string broche;                      //numéro broche en string
    ss << _broche;                      //conversion de int en string
    ss >> broche;                       //conversion de int en string
    commande = "echo 0 > /sys/class/gpio/gpio" + broche + "/value";
                                        //constitution de la commande à
                                        //transmettre
    system(commande.c_str());           //transmission de la commande linux
}
/*
Libère la ressource
*/
void GPIOClassOut::close(){
    unexport_gpio();                    //méthode unexport
}
/*
Lit l'état de la sortie
*/
char GPIOClassOut::read_gpio(){
    string nomFichier;                  //variable pour le nom fichier
    stringstream ss;                    //variable de conversion
    string broche;                      //numéro broche en string
    ss << _broche;                      //conversion de int en string
    ss >> broche;                       //conversion de int en string
    nomFichier = "/sys/class/gpio/gpio" + broche + "/value";
                                        //constitution du chemin et nom fichier
    ifstream Fp(nomFichier.c_str(), ios::in);
                                        //ouverture fichier
    char valeur = 0;                    //valeur contenu fichier
    Fp.get(valeur);                     //lire et mettre dans valeur
    Fp.close();                         //ferme le fichier
    return valeur;                      //valeur de retour
}
/* -------------------------------------------------------------------------- */

Normalement le programme sur-commenté devrait suffisant pour la compréhension.

blink05.cpp

Pour saisir le programme blink05.cpp, il faut faire dans la console :

nano blink05.cpp

Cette commande ouvre un fichier de texte vide appelé blink05.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique LED clignotante
Led rouge sur GPIO23 via une résistance de 330 ohms
Ajout sortie boucle infinie
os                  : RPi Linux 4.4.13+
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 29/07/2016
date de mise à jour : 29/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
/* -----------------------------------------------------------------------------
 Bibliothèques
----------------------------------------------------------------------------- */
#include "GPIOClassOut.h"               //entête class
#include <iostream>                     //pour cout et endl
#include <unistd.h>                     //pour usleep
#include "saisieCarac.h"                //entête saisie caractère non bloquant
#include <stdio.h>                      //bibliothèque entrées/sorties

#define DUREE 1000000                   //durée demi-période en micro seconde

using namespace std;                    //espace nommage
/* -------------------------------------------------------------------------- */
int main(void){
    int c;
    cout << "LED's clignotantes" << endl;
    cout << "'q' pour sortir" << endl;  //IHM
    GPIOClassOut led;                   //instance led
    led.begin(23);                      //initialise led sur GPIO23
    led.on();                           //allume led
    for(;;){                            //boucle infinie
       if(kbhit())                      //si touche saisie
          c = getchar();                //lecture touche
       if(tolower(c) == 'q')            //test si 'q' ou 'Q'
          break;                        //sortie boucle infernale
       led.toggle();                    //permute état led
       usleep(DUREE);                   //attente pendant DUREE
    }
    led.close();                        //libère la ressource
    cout << "\nFin du programme" << endl;
    return 0;                           //code sortie
}
/* -------------------------------------------------------------------------- */

Exécution du programme

Pour exécuter ce programme, il faut au préalable le compiler. La compilation se fait par la commande ci-dessous (en mode console) pour les différents fichiers C++ en créant les fichiers objets correspondants.

g++ -Wall -c saisieCarac.cpp
g++ -Wall -c GPIOClassOut.cpp&lt;/span&gt;
g++ -Wall -c blink05.cpp

Et pour terminer, il faut créer le fichier final via l’éditeur de lien :

g++ -Wall -o blink05 blink05.o saisieCarac.o -GPIOClassOut

Le résultat de la compilation est un fichier : blink05

Lancement du programme :

sudo ./blink05

Pour sortir de la boucle, il suffit de faire q ou Q au clavier.

Remarque : On peut supprimer les fichiers objets (.o) du répertoire courant par :

rm *.o

Commentaire

La sortie de la boucle infinie va être tributaire de l’instant où l’on entre le caractère q au clavier. En effet, si la saisie de q intervient lorsque le programme exécute la ligne 42, la prise en compte de l’ordre de sortie va intervenir quasiment de suite (temps proche de 0 seconde). Par contre, si la saisie se fait lors de l’exécution de la ligne 40, il faudra attendre le passage par la ligne 36 pour provoquer la sortie (soit environ 1 seconde plus tard). Si l’on avait utilisé les méthodes on() et off() pour faire clignoter notre led, l’attente serait d’environ 2 secondes.

Conclusion saga blink

A force de triturer cette led qui clignote dans tous les sens, nous avons de plus en plus d’idées de mise en œuvre. L’utilisation de leds multicolores ou de leds intelligentes type WS2812b permettrait également de proposer des petits programmes intéressants pour les débutants. Mais le but n’est pas d’écrire un ouvrage de 458 pages sur la led clignotante.

Tout doit avoir une fin. Quoi que !

Les threads

Tous ceux qui veulent en savoir plus, peuvent consulter les différentes documentations du web, ouvrages spécialisés ou l’article de Bud Spencer : Penser ‘Thread’ pour simplifier vos programmes

Je ne vais pas vous faire un cours sur les threads, j’en suis parfaitement incapable. De formation charcutier/zingueur qui aspire à devenir pâtissier/couvreur, on ne peut pas tout assumer.

figure10C’est quoi les threads ? Cela ne se mange pas, même s’ils sont difficiles à digérer. Pour faire simple, notre programme va créer un ou plusieurs « programmes » (threads ou fils ou tâches) qui vont s’exécuter de manière parallèle (en même temps ou quasiment en même temps) que notre programme. Pendant que le thread effectue une tâche plus ou moins bloquante, un autre thread peut assurer une surveillance, par exemple, des entrées ou clavier (voir figure).

Exemples de threads

Dans nos exemples nous n’utiliserons pas les notions d’échanges d’informations entre threads, de blocage, etc… Les mutex ou autres termes pharmaceutiques, tout droit issus du Codex, sont du domaine des spécialistes que nous ne sommes pas. Pour nous la framboise est un fruit ou un objet d’amusement.

Un thread

Pour faire des tests avec ce concept, nous avons opté pour une approche hors ports GPIO. Tous ces programmes pourront être testés sur le Raspberry Pi ou sur votre ordinateur de bureau.

Langage C

Programme

Pour saisir le programme thread01.c, il faut faire dans la console :

nano thread01.c

Cette commande ouvre un fichier de texte vide appelé thread01.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques           :
----------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
//fonction thread
void* fctThread(){
    printf("<thread>\n");
    while(1){                               //boucle infinie
        printf("*");                        //affiche des *
        fflush(stdout);                     //affichage immédiat
        usleep(500000);                     //attente 500 ms
    }
    return NULL;
}
//programme principal
int main(){
    pthread_t monThread;                    //instance thread
    printf("Début programme\n");
    printf("Zone d'initialisation\n");
    printf("\ntouche <Entrée> pour quitter !\n\n");
    pthread_create(&monThread, NULL, fctThread, NULL);
                                            //création du thread
    getchar();                              //attente saisie clavier
    pthread_cancel(monThread);              //supprime le thread
    pthread_join(monThread, NULL);          //attente fin thread
    printf("\nZone libération des ressources\n");
    printf("Fin du programme\n");
    return 0;
}

La fonction lignes 18 à 25 est exécutée par un thread en parallèle du programme main().
On déclare une instance thread à la ligne 29, la création proprement dite du thread est faite ligne 33.
Ligne 35, notre programme main() attend une saisie au clavier.
La ligne 36 se charge de la destruction du thread et l’instruction de la ligne 37 attend la fin du cycle du thread.

Exécution du programme

Pour exécuter se programme, il faut au préalable le compiler. La compilation se fait par la commande ci-dessous (en mode console).

gcc -Wall -o thread01 thread01.c -lpthread

Le résultat de la compilation est un fichier : thread01

Lancement du programme :

./thread01

Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.

Résultat du programme

Vous devriez voir dans votre console un affichage semblable à celui ci :

Début programme
Zone d'initialisation

touche <Entrée> pour quitter !

<thread>
*************************************************************************************************

Zone libération des ressources
Fin du programme

La « Zone d’initialisation » correspond à l’emplacement où l’on peut initialiser les paramètres de nos ports GPIO.
La « Zone libération des ressources » correspond à l’emplacement où l’on libère les ressources de la carte.
<thread> : Message de démarrage du thread.
**********…….*********** : Le résultat de l’exécution du thread, peut être une led qui clignote.

Autre présentation

L’utilisation de la structure précédente fonctionne très bien mais il serait plus simple, d’un point de vue développement, de déporter le code de notre thread dans un fichier annexe.
Dans ce cas, notre programme thread01.c deviendrait un pseudo-ordonnanceur pour la led clignotante.

La nouvelle version se compose des fichiers thread02.c, monProg.h et monProg.c.
Pour saisir le programme monProg.h, il faut faire dans la console :

nano monProg.h

Cette commande ouvre un fichier de texte vide appelé monProg.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
Fichier entête pour monProg.c
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques :
----------------------------------------------------------------------------- */
#ifndef MON_PROG_H
#define MON_PROG_H
//prototypes
void* fctThread();
#endif

Pour saisir le programme monProg.c, il faut faire dans la console :

nano monProg.c

Cette commande ouvre un fichier de texte vide appelé monProg.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
Simulation programme raspberry Pi
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques           :
----------------------------------------------------------------------------- */
#include "monProg.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void* fctThread(){
    while(1){                           //boucle infinie
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        printf("*");                    //affiche des *
        fflush(stdout);                 //affichage immédiat
        usleep(500000);                 //attente 500 ms
    }
    return NULL;
}

Pour saisir le programme thread02.c, il faut faire dans la console :

nano thread02.c

Cette commande ouvre un fichier de texte vide appelé thread02.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques           :
----------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "monProg.h"

//programme principal
int main(){
    pthread_t monThread;                //instance thread
    printf("Début programme\n");
    printf("Zone d'initialisation\n");
    printf("\ntouche <Entrée> pour quitter !\n\n");
    pthread_create(&monThread, NULL, fctThread, NULL);
                                        //création du thread
    getchar();                          //attente saisie clavier
    pthread_cancel(monThread);          //supprime le thread
    pthread_join(monThread, NULL);      //attente fin thread
    printf("\nZone libération des ressources\n");
    printf("Fin du programme\n");
    return 0;
}

Pour l’exécution, il faudra faire :

gcc -Wall -o thread02 thread02.c monProg.c -lpthread

Le résultat sera un fichier : thread02

Lancement du programme :

./thread02

Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.

L’affichage dans la console sera, en tout point, identique au précédent exemple.

Langage C++

Programme

Notre programme est composé par trois fichiers : monProg.h, monProg.cpp et thread05.cpp.
Pour saisir le programme monProg.h, il faut faire dans la console :

nano monProg.h

Cette commande ouvre un fichier de texte vide appelé monProg.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
Fichier entête pour monProg.cpp
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques :
----------------------------------------------------------------------------- */
#ifndef MON_PROG_H
#define MON_PROG_H
//prototypes
void* fctThread(void *);
#endif

Pour saisir le programme monProg.cpp, il faut faire dans la console :

nano monProg.cpp

Cette commande ouvre un fichier de texte vide appelé monProg.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
Simulation programme raspberry Pi
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques :
----------------------------------------------------------------------------- */
#include "monProg.h"
#include <cstdio>
#include <iostream>
#include <unistd.h>

using namespace std;

void* fctThread(void *){
    while(1){ //boucle infinie
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        cout << "*" << flush;
        usleep(500000); //attente 500 ms
    }
    return NULL;
}

Pour saisir le programme thread05.cpp, il faut faire dans la console :

nano thread05.cpp

Cette commande ouvre un fichier de texte vide appelé thread05.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : g++ (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques :
----------------------------------------------------------------------------- */
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include "monProg.h"

using namespace std;

//programme principal
int main(){
    pthread_t monThread;                //instance thread
    cout << "Début programme" << endl;
    cout << "Zone d'initialisation" << endl;
    cout << endl << "touche <Entrée> pour quitter !" << endl << endl;
    pthread_create(&monThread, NULL, fctThread, NULL);
                                        //création du thread
    cin.ignore();                       //attente saisie clavier
    pthread_cancel(monThread);          //supprime le thread
    pthread_join(monThread, NULL);      //attente fin thread
    cout << endl << "Zone libération des ressources" << endl;
    cout << "Fin du programme" << endl;
    return 0;
}
Exécution du programme

Pour exécuter se programme, il faut au préalable le compiler. La compilation se fait par la commande ci-dessous (en mode console).

gcc -Wall -o thread05 thread05.cpp monProg.cpp -lpthread

 

Le résultat de la compilation est un fichier : thread05

Lancement du programme :

./thread05

Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.

Résultat du programme

Vous devriez voir dans votre console un affichage semblable à celui ci :

Début programme
Zone d'initialisation

touche <Entrée> pour quitter !

<thread>
*************************************************************************************************

Zone libération des ressources
Fin du programme

Remarque : On notera la similitude entre les programmes C et C++.

Python

L’exemple ci-dessous utilise la même structure de programme, c’est-à-dire un programme principal (main) qui se charge de lancer le thread et de la détruire lors d’une saisie clavier.

Programme

Pour saisir le programme monProg.py, il faut faire dans la console :

nano monProg.py

Cette commande ouvre un fichier de texte vide appelé monProg.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme de test des threads
Fonction d'affichage des *
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : 
Remarques           : 

"""
import sys
import time

debut = True #variable globale

def fctThread():
    """affichage * dans la console"""
    global debut #transfaire de variable
    while debut: #affichage message au premier passage
        print("<thread>") #signal start thread
        debut = False
 
    sys.stdout.write("*") #affichage *
    sys.stdout.flush() #affichage immédiat
    time.sleep(0.5) #attente 500 msec

Pour saisir le programme thread50.py, il faut faire dans la console :

nano thread50.py

Cette commande ouvre un fichier de texte vide appelé thread50.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme de test des threads
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : 
Remarques           : 

"""
import sys
import time
from threading import Thread
from monProg import *

class Afficheur(Thread):
    """Lanceur de fonction"""
    def __init__(self):
        Thread.__init__(self)
        self.running = True
        self.debut = True
 
    def run(self):
        while self.running:
            fctThread()                 #notre fonction programme
 
    def stop(self):
        self.running = False

if __name__ == '__main__':
    """Programme principal"""
    print("Début programme")
    print("Zone d'initialisation")
    print()
    print("touche <Entrée> pour quitter !")
    print()
 
    thread_1 = Afficheur()              #création du thread
    thread_1.start()                    #lancement du thread
    input()                             #attente saisie clavier
    thread_1.stop()                     #détruit le thread
    thread_1.join()                     #attente fin cycle thread

    print()
    print("Zone libération des ressources")
    print("Fin du programme")
Exécution du programme

Pour exécuter se programme, il suffit de lancer la commande ci-dessous (en mode console).

python3 thread50.py

Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.

Résultat du programme
Début programme
Zone d'initialisation

touche <Entrée> pour quitter !

<thread>
*************************************************************************************************

Zone libération des ressources
Fin du programme

Remarque : On notera la similitude entre les programmes python, C et C++.

Et pour quelques threads de plus

Pour créer deux ou plusieurs threads, il suffit d’étendre ce que l’on a mis en œuvre dans le chapitre suivant. Par exemple, sous la forme :

instance thread_1
instance thread_2
création thread_1
création thread_2
lancement thread_1
lancement thread_2
attente saisie clavier
destruction thread_1
destruction thread_2
attente fin thread_1
attente fin thread_2

Dans ce synoptique, le programme principal lance les deux threads (qui vivent leur vie) et attend une action de l’utilisateur pour sortie du programme.
Dans le cas de carte embarquée, la priorité n’est pas de terminer le programme mais de boucler indéfiniment pour assurer la fonction qui est la sienne. La structure adoptée au paragraphe précédent peut convenir à sortir proprement d’une ou plusieurs boucles sans fin à des fins de débogages.

Pour l’exploitation du raspberry Pi en tant que carte embarquée, il peut être préférable de créer au minimum deux threads. L’un des threads traiterait les actionneurs en sortie (plus en moins rapide) et l’autre l’acquisition des capteurs nécessitant un traitement plus rapide (réactivité, stabilité, sécurité, etc…).

Pour répondre à cette problématique, il nous faudrait une structure de la forme :

instance thread_1
instance thread_2
création thread_1
création thread_2
lancement thread_1
lancement thread_2
attente fin thread_1
attente fin thread_2

Notre programme principal instancie, créé et lance les threads puis attend que cela se passe (jusqu’à l’arrêt des deux threads). Bref, couché sur une plage de sable fin, les doigts de pieds en éventail et entrain de « siroter » une boisson fraîche.

Dans les exemples de ce chapitre, nous allons utiliser ce principe. Un des thread affichera nos petits astérisques (sortie) et l’autre attendra une saisie clavier (entrée).

Langage C

Programme

L’exemple se compose des fichiers thread10.c, monProg2.h et monProg2.c.
Pour saisir le programme thread10.c, il faut faire dans la console :

nano thread10.c

Cette commande ouvre un fichier de texte vide appelé thread10.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "monProg2.h"

volatile char leCarac = '\0';

//programme principal
int main(){
    pthread_t monThread_1, monThread_2; //instances thread
    printf("Début programme\n");
    printf("Zone d'initialisation\n\n");
    pthread_create(&monThread_1, NULL, sortie, NULL);
                                        //création du thread
    pthread_create(&monThread_2, NULL, entree, NULL);
                                        //création du thread
    pthread_join(monThread_1, NULL);    //attente fin thread
    pthread_join(monThread_2, NULL);    //attente fin thread
    printf("\nZone libération des ressources\n");
    printf("Fin du programme\n");
    return 0;
}

A la ligne 19, on déclare une variable globale utilisée dans nos threads.
Ligne 23, on crée instances Threads. La création des threads se fait aux lignes 26 et 28 avec l’appel des fonctions à exécuter sortie() et entree().
Aux lignes 30 et 31, on attend la fin des threads.

Pour saisir le programme monProg2.h, il faut faire dans la console :

nano monProg.h

Cette commande ouvre un fichier de texte vide appelé monProg2.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
Fichier entête pour monProg1.c
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#ifndef MON_PROG2_H
#define MON_PROG2_H
//prototypes
void* sortie();
void* entree();
#endif

Pour saisir le programme monProg2.c, il faut faire dans la console :

nano monProg.c

Cette commande ouvre un fichier de texte vide appelé monProg2.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
Simulation programme raspberry Pi
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#include "monProg2.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

extern volatile char leCarac; //variable globale

//fonction sortie() affiche *
void* sortie(){
    while(leCarac != 'q'){ //tant que différent de q
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        printf("*"); //affiche des *
        fflush(stdout); //affichage immédiat
        usleep(500000); //attente 500 ms
    }
    return NULL;
}
//fonction entrée saisie clavier
void* entree(){
    while(leCarac != 'q'){ //tant que différent de q
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        printf("q pour quitter !\n");
        leCarac = getchar(); //saisie clavier q+<Entrée>
    }
    return NULL;
}

La fonction sortie() affiche nos petites astérisques tant que le leCarc est différent de ‘q’.
La fonction entree() attend une saisie clavier tant que leCarc est différent de ‘q’.

Exécution du programme

Pour l’exécution, il faudra compiler nos différents programmes par :

gcc -Wall -o thread10 thread10.c monProg2.c -lpthread

Le résultat sera un fichier : thread10

Lancement du programme :

./thread10

Pour sortir de la boucle, il suffit de faire q+<Entrée> au clavier.

L’affichage dans la console sera similaire aux exemples précédents.

Résultat du programme

Vous devriez voir dans votre console un affichage semblable à celui ci :

Début programme
Zone d'initialisation

q pour quitter !
*********************************q**

Zone libération des ressources
Fin du programme

La « Zone d’initialisation » correspond à l’emplacement où l’on peut initialiser les paramètres de nos ports GPIO.
La « Zone libération des ressources » correspond à l’emplacement où l’on libère les ressources de la carte.
**********…….*********** : Le résultat de l’exécution du thread, pourquoi pas une led qui clignote.

Langage C++

Programme

L’exemple se compose des fichiers thread30.cpp, monProg2.h et monProg2.cpp.
Pour saisir le programme thread30.cpp, il faut faire dans la console :

nano thread30.cpp

Cette commande ouvre un fichier de texte vide appelé thread30.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include "monProg2.h"

using namespace std;
volatile char leCarac = '\0';           //variable globale pour les threads

//programme principal
int main(){
    pthread_t monThread_1, monThread_2; //instances thread
    cout << "Début programme" << endl;
    cout << "Zone d'initialisation" << endl << endl;
    pthread_create(&monThread_1, NULL, sortie, NULL);
                                        //création du thread
    pthread_create(&monThread_2, NULL, entree, NULL);
                                        //création du thread
    pthread_join(monThread_1, NULL);    //attente fin thread
    pthread_join(monThread_2, NULL);    //attente fin thread
    cout << endl << "Zone libération des ressources" << endl;
    cout << "Fin du programme" << endl;
    return 0;
}

Notre programme est, en tout point, conforme à celui de l’exemple précédent hormis les syntaxes pour l’affichage console.

 Pour saisir le programme monProg2.h, il faut faire dans la console :

nano monProg2.h

Cette commande ouvre un fichier de texte vide appelé monProg2.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
Fichier entête pour monProg1.cpp
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#ifndef MON_PROG2_H
#define MON_PROG2_H
//prototypes
void* sortie(void *);
void* entree(void *);
#endif

Pour saisir le programme monProg2.cpp, il faut faire dans la console :

nano monProg2.cpp

Cette commande ouvre un fichier de texte vide appelé monProg2.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme de test des threads
Simulation programme raspberry Pi
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#include <cstdio>
#include <unistd.h>
#include <iostream>
#include "monProg2.h"

using namespace std;
extern volatile char leCarac;           //variable globale 🙁

//fonction sortie() affiche *
void* sortie(void *){
    while(leCarac != 'q'){              //tant que différent de q
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        cout << "*" << flush;           //affiche immédiatement les *
        usleep(500000);                 //attente 500 ms
    }
    return NULL;
}
//fonction entrée saisie clavier
void* entree(void *){
    while(leCarac != 'q'){              //tant que différent de q
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        cout << "q pour quitter !" << endl;
        leCarac = getchar();            //saisie clavier q+<Entrée>
    }
    return NULL;
}

Les commentaires du programme et ceux des exemples précédents sont suffisants à la compréhension.

Exécution du programme

Pour l’exécution, il faudra compiler nos différents programmes par :

g++ -Wall -o thread30 thread30.cpp monProg2.cpp -lpthread

 Le résultat sera un fichier : thread30

Lancement du programme :

./thread30

Pour sortir de la boucle, il suffit de faire q+<Entrée> au clavier.

L’affichage dans la console sera similaire aux exemples précédents.

Résultat du programme

Vous devriez voir dans votre console un affichage semblable à celui ci :

Début programme
Zone d'initialisation

q pour quitter !
*********************************q**

Zone libération des ressources
Fin du programme

Remarque : Les puristes C++ auraient utilisés la bibliothèque thread à la place de thread.h et chrono à la place unistd.h. Mais je ne suis pas à un blasphème près.

Langage Python

Programme

Notre programme se compose de deux fichiers thread70.py et monProg2.py.
Pour saisir le programme monProg2.py, il faut faire dans la console :

nano monProg2.py

Cette commande ouvre un fichier de texte vide appelé monProg2.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme de test des threads
Fonctions d'affichage des * et saisie clavier
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : 
Remarques           : 

"""
import sys
import time
from thread70 import leCarac            #importe la variable globale

def sortie():
    """affichage * dans la console"""
    global leCarac
    while(leCarac != 'q'):              #boucle tant que leCarac différent de q
        #
        #on pourra mettre dans la boucle notre programme raspberry Pi
        #
        sys.stdout.write("*")           #affiche *
        sys.stdout.flush()              #affichage immédiat
        time.sleep(0.5)                 #attente 500 msec
        
def entree():
    """saisie caractère au clavier"""
    global leCarac
    while(leCarac != 'q'):              #boucle tant que leCarac différent de q
        #
        #on pourra mettre dans la boucle notre programme raspberry Pi
        #
        leCarac = input()               #saisie caractère

Ce fichier contient les deux fonctions exécutées par les threads.

Pour saisir le programme thread70.py, il faut faire dans la console :

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme de test des threads
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :

"""
import sys
import time
from threading import Thread
from monProg2 import *

leCarac = '\0'                          #variables globale

class MonThread(Thread):
    """Lanceur de fonction"""
    def __init__(self, fonction):
        Thread.__init__(self, None, fonction)
        self._fonction = fonction
        self.running = True

    def run(self):
        while self.running:
            self._fonction()            #notre fonction programme

    def stop(self):
        self.running = False

if __name__ == '__main__':
    """Programme principal"""
    print("Début programme")
    print("Zone d'initialisation")
    print()
    print("q pour quitter !")
    print()

    thread_1 = MonThread(sortie)        #création du thread
    thread_2 = MonThread(entree)
    thread_1.start()                    #lancement du thread
    thread_2.start()
    thread_1.stop()                     #détruit le thread
    thread_2.stop()
#    thread_1.join()                    #attente fin cycle thread
    thread_2.join()

    print()
    print("Zone libération des ressources")
    print("Fin du programme")

Lignes 22 à 34, on retrouve notre classe qui permet de créer, lancer et arrêter nos threads.

Exécution du programme

Pour exécuter se programme, il suffit de lancer la commande ci-dessous (en mode console).

python3 thread70.py

Pour sortir de la boucle, il suffit de faire q+<Entrée> au clavier.

Résultat du programme

Vous devriez voir dans votre console un affichage semblable à celui ci :

Début programme
Zone d'initialisation

q pour quitter !
*********************************q**

Zone libération des ressources
Fin du programme

Led clignotante et threads

Revenons à nos moutons ! Après cet interlude sur ma vision des threads, nous allons les mettre en pratique.

Langage C

Pour cet exemple, nous allons utiliser un thread pour commander notre led et sortir de la boucle infinie par la touche <Entrée> du clavier.

Programme

Le programme est composé des fichier thread20.c, monProg3.h et monProg3.c.
Pour saisir le programme thread20.c, il faut faire dans la console :

nano thread20.c

Cette commande ouvre un fichier de texte vide appelé thread20.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique led clignotante
Led rouge sur GPIO23 (4) via une résistance de 330 ohms
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer 
Remarques           :
----------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "monProg3.h"
#include <wiringPi.h>

//programme principal
int main(){
    pthread_t monThread;                //instance thread
    printf("Led clignotante\n");
    //initialisation port
    wiringPiSetup();                    //pseudo-Arduino
    pinMode(LED, OUTPUT);               //broche en sortie
    //fin initialisation port
    printf("\ntouche <Entrée> pour quitter !\n");
    pthread_create(&monThread, NULL, sortie, NULL);
                                        //création du thread
    getchar();                          //attente saisie clavier
    pthread_cancel(monThread);          //supprime le thread
    pthread_join(monThread, NULL);      //attente fin thread
    //libération ressources
    digitalWrite(LED, LOW);             //éteint led
    pinMode(LED, INPUT);                //broche en entrée
    //fin libération ressources
    printf("Fin du programme\n");
    return 0;
}

L’initialisation se fait lignes 26 et 27, et la libération de ressources se fait lignes 36 et 37. Le reste du programme est conforme à la structure du paragraphe 6.1.1.1.4.
La directive LED est déclarée dans le fichier monProg3.h.

Pour saisir le programme monProg3.h, il faut faire dans la console :

nano monProg3.h

Cette commande ouvre un fichier de texte vide appelé monProg3.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique led clignotante
Fichier entête pour monProg3.c
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques           :
----------------------------------------------------------------------------- */
#ifndef MON_PROG3_H
#define MON_PROG3_H
//directives
#define LED 4                           //numéro port led=GPIO23
#define DUREE 500                       //demi-période 500 ms

//prototypes
void* sortie();                         //led clignotante
#endif

Pour saisir le programme monProg3.c, il faut faire dans la console :

nano monProg3.c

Cette commande ouvre un fichier de texte vide appelé monProg3.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique led clignotante
Fonction sortie()
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : Bud Spencer
Remarques           :
----------------------------------------------------------------------------- */
#include "monProg3.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>

void* sortie(){
    while(1){                           //boucle infinie
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        digitalWrite(LED, HIGH);        //allume led
        delay(DUREE);                   //attente
        digitalWrite(LED, LOW);         //éteint led
        delay(DUREE);                   //attente
    }
    return NULL;
}

On retrouve ci-dessus que des choses déjà vus.

Exécution du programme

Pour l’exécution, il faudra faire :

gcc -Wall -o thread20 thread20.c monProg3.c -lpthread -lwiringPi

 

Le résultat sera un fichier : thread20

Lancement du programme :

sudo ./thread20

Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.

Miracle, notre led est toujours en vie et clignote dans toute sa spendeur.

Langage C++

Pour cet exemple, nous allons utiliser deux thread pour commander notre led et sortir de la boucle infinie par la touche q+<Entrée> du clavier.

Programme

Le programme est composé des fichiers thread35.cpp, monProg3.h et monProg3.cpp.
Pour saisir le programme thread35.cpp, il faut faire dans la console :

nano thread35.cpp

Cette commande ouvre un fichier de texte vide appelé thread35.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique led clignotante
Led rouge sur GPIO23 (4) via une résistance 330 ohms
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include "monProg3.h"
#include <wiringPi.h>

using namespace std;
volatile char leCarac = '\0';           //variable globale pour les threads

//programme principal
int main(){
    pthread_t monThread_1, monThread_2; //instances thread
    cout << "Led clignotante" << endl << endl;
    //initialisation
    wiringPiSetup();                    //pseudo-Arduino
    pinMode(LED, OUTPUT);               //en mode sortie
    //fin initialisation
    pthread_create(&monThread_1, NULL, sortie, NULL);
                                        //création du thread
    pthread_create(&monThread_2, NULL, entree, NULL);
                                        //création du thread
    pthread_join(monThread_1, NULL);    //attente fin thread
    pthread_join(monThread_2, NULL);    //attente fin thread
    //libération ressources
    digitalWrite(LED, LOW);             //éteint led
    pinMode(LED, INPUT);                //broche en entrée
    cout << "Fin du programme" << endl;
    return 0;
}

Pour saisir le programme monProg3.h, il faut faire dans la console :

nano monProg3.h

Cette commande ouvre un fichier de texte vide appelé monProg3.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique led clignotante
Fichier entête pour monProg3.cpp
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#ifndef MON_PROG3_H
#define MON_PROG3_H
//directives
#define LED 4                           //numéro port led=GPIO23
#define DUREE 500                       //demi-période en ms
//prototypes
void* sortie(void *);                   //led clignotante
void* entree(void *);                   //saisie clacvier
#endif

Pour saisir le programme monProg3.cpp, il faut faire dans la console :

nano monProg3.cpp

Cette commande ouvre un fichier de texte vide appelé monProg3.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

/* -----------------------------------------------------------------------------
Programme classique led clignotante
Fonctions sortie() et entree()
os                  : RPi Linux 4.4.13+ (Jessie)
logiciel            : gcc (Raspbian 4.9.2-10) 4.9.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           :
Remarques           :
----------------------------------------------------------------------------- */
#include <cstdio>
#include <unistd.h>
#include <iostream>
#include "monProg3.h"
#include <wiringPi.h>

using namespace std;
extern volatile char leCarac;           //variable globale

//fonction sortie() affiche *
void* sortie(void *){
    while(leCarac != 'q'){              //tant que différent de q
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        digitalWrite(LED, HIGH);        //allume led
        delay(DUREE);                   //attente
        digitalWrite(LED, LOW);         //éteint led
        delay(DUREE);                   //attente
    }
    return NULL;
}
//fonction entrée saisie clavier
void* entree(void *){
    while(leCarac != 'q'){              //tant que différent de q
        //
        //On pourra mettre dans la boucle while notre programme raspberry Pi
        //
        cout << "q pour quitter !" << endl;
        leCarac = getchar();            //saisie clavier q+<Entrée>
    }
    return NULL;
}

Exécution du programme

Pour l’exécution, il faudra faire :

g++ -Wall -o thread35 thread35.cpp monProg3.cpp -lpthread -lwiringPi

Le résultat sera un fichier : thread35

Lancement du programme :

sudo ./thread35

Pour sortir de la boucle, il suffit de faire q+<Entrée> au clavier.

Le résultat est toujours le même, notre led bien aimée clignote !

Langage Python

Pour cet exemple, nous utiliserons à nouveau deux thread pour commander notre led et sortir de la boucle infinie par la touche q+<Entrée> du clavier.
Les plus courageux pourront facilement faire la version mono-thread.

Programme

Notre programme se compose de deux fichiers thread75.py et monProg3.py.
Pour saisir le programme monProg3.py, il faut faire dans la console :

nano monProg3.py

Cette commande ouvre un fichier de texte vide appelé monProg3.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme classique led clignotante
Fonctions sortie() et saisie clavier
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : 
Remarques           : 

"""
import sys
import time
from thread75 import leCarac            #importe la variable globale
import RPi.GPIO as GPIO

def sortie():
    """affichage * dans la console"""
    global leCarac
    GPIO.setmode(GPIO.BCM)
    while(leCarac != 'q'):              #boucle tant que leCarac différent de q
        #
        #on pourra mettre dans la boucle notre programme raspberry Pi
        #
        GPIO.output(23, GPIO.HIGH)      #allume led
        time.sleep(1)                   #attente 1 s
        GPIO.output(23, GPIO.LOW)       #éteint led
        time.sleep(1)                   #attente 1 s
 
def entree():
    """saisie caractère au clavier"""
    global leCarac
    while(leCarac != 'q'):              #boucle tant que leCarac différent de q
        #
        #on pourra mettre dans la boucle notre programme raspberry Pi
        #
        leCarac = input()               #saisie caractère

Pour saisir le programme thread35.py, il faut faire dans la console :

nano thread35.py

Cette commande ouvre un fichier de texte vide appelé thread35.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
"""
Programme classique led clignotante
Led rouge sur GPIO23 via une résistance de 330 ohms
logiciel            : python 3.4.2
cible               : raspberry Pi
date de création    : 30/07/2016
date de mise à jour : 30/07/2016
version             : 1.0
auteur              : icarePetibles
référence           : 
Remarques           : 

"""
import sys
import time
from threading import Thread
from monProg3 import *
import RPi.GPIO as GPIO

leCarac = '\0'                          #variables globale

class MonThread(Thread):
    """Lanceur de fonction"""
    def __init__(self, fonction):
        Thread.__init__(self, None, fonction)
        self._fonction = fonction
        self.running = True
        
    def run(self):
        while self.running:
            self._fonction()            #notre fonction programme
            
    def stop(self):
        self.running = False

def init():
    """initialisation ports"""
    GPIO.setwarnings(False)             #désactive le mode alerte
    GPIO.setmode(GPIO.BCM)              #numérotation ports processeur
    GPIO.setup(23, GPIO.OUT)            #GPIO23 en sortie


if __name__ == '__main__':
    """Programme principal"""
    print("Led clignotante")
    #initialisation
    init()                              #initialisation
    #fin initialisation
    print()
    print("q pour quitter !")
    
    thread_1 = MonThread(sortie)        #création du thread
    thread_2 = MonThread(entree)
    thread_1.start()                    #lancement du thread
    thread_2.start()
    thread_1.stop()                     #détruit le thread
    thread_2.stop()
#    thread_1.join()                     #attente fin cycle thread
    thread_2.join()

    #libération des ressources
    GPIO.output(23, GPIO.LOW)           #éteint led
    GPIO.cleanup()                      #remet les pins en entrée
    #fin libération des ressources
    print("Fin du programme")

Exécution du programme

Pour exécuter ce programme, il faut lancer la commande suivante dans la console Linux :

python3 thread35.py

Pour sortir de l’application, il suffit de faire q+<Entrée> au clavier.

Nota : Pour l’interface avec les ports GPIO, on peut utiliser une des autres bibliothèques adorbées dans la saga blink

Conclusion

L’utilisation des threads n’est pas la panacée universel mais l’une des solutions envisageables dans certains cas.
Et si notre belle led clignotante tombait amoureuse d’un beau Thread ! Dans le cadre de notre class GPIOClassOut, on pourrait imaginer de créer, les méthodes blink(), blink(int tempsAllumage, int tempsExtinction) et blinkStop().

Je fais une addiction à la led qui clignote et il est temps que je me soigne en passant à autre chose, par exemple, la « saga Push Button »

A force de parcourir des tutoriels sur la toile, nous avions noté qu’il existait de nombreux « tutos » très bien faits. Par contre, on constate également que souvent, il manquait le petit bout de quelque « chose » pour que tout se déroule bien.
Ce petit quelque « chose«  que les utilisateurs avertis font sans réfléchir mais qui pose de problème aux débutants. Fort de ce constat, j’ai essayé de faire une présentation un peu différente en y intégrant toutes les étapes de la saisie jusqu’à l’exécution des programmes (Pas très écologique en terme de pages).
Faire des recherches pour comprendre est, en soi, une bonne chose mais je préfère la méthode « à force de répéter cela finira par le faire ».

Toutes les remarques, critiques, suggestions et autres approches sont les bienvenues, à partir du moment où l’on peut les partager avec les autres. Les dénigrements sont également acceptés mais dans le respect de l’autre.

A tous présents et à venir. Salut !

Sources

On pourra télécharger l’ensemble des sources sous :
http://psl.ibidouille.net/Images_forum/raspberryPi/sagaBlinkComp.zip

Bootez votre Raspberry Pi 3 sur une clé USB

boot_USB_250pxOn attendait cette possibilité depuis la sortie du Raspberry Pi. Coincés par ce démarrage sur la carte SD imposé par les précédents SoC, on attendait l’arrivée de cette nouvelle.
Eh bien ça y est il est possible de booter sur une clé USB avec le Raspberry Pi 3 ! Bon, ça nécessite quelques manipulations mais après… ça fonctionne.

Booter le Raspberry Pi 3 sur sur un périphérique de stockage USB

Comment démarre le SoC

En fait ce qui nous intéresse ici, ce sont les toutes premières secondes de ce démarrage. Juste après que vous ayez branché la prise micro-usb d’alimentation, ou appuyé sur le bouton reset fraîchement installé sur votre Raspberry Pi. Dans un premier temps, le signal RUN s’établit, le SOC se réveille. Le SOC c’est le System On a Chip (Système Sur une Puce) de Broadcom. Ce SOC BCM2837 contient un processeur ARM cadencé à 1,2 GHz, un GPU VideoCore IV, de la mémoire cache et d’autres babioles qui accompagnent généralement les microprocesseurs (GPIO, timers, BUS divers et variés – I2C – SPI, contrôleur d’interruption – PIC, contrôleur de mémoire -MMU, UART…).

etape_01

A la mise sous tension, seul le GPU est activé. Le processeur ARM reste en veille, la SDRAM est désactivée. Le SOC comporte une ROM (Read Only Memory – Mémoire Morte) dans laquelle le fabricant a programmé le premier étage du bootloader. Lors de son réveil, le GPU est paramétré pour exécuter le programme qui est dans cette ROM. C’est ce qu’il fait et le firmware contenu en ROM a pour seul objectif d’accéder à la carte micro SD, qui doit être formatée en FAT (enfin, au moins la première partition)… SAUF si on a modifié un registre de l’OTP (One Time Programmable memory) qui dit au BCM2837 (celui qui équipe le Raspberry Pi 3) que lorsqu’il démarre, il va déjà voir s’il y a une carte SD opérationnelle. Si ce n’est pas le cas, il peut baguenauder et essayer de démarrer sur l’USB le SPI ou l’Ethernet…

boot_USB_01

Un Raspberry Pi 3 équipé d’une carte HiFiberry DAC + remarquez à droite de limage la carte SD posée sur le socle et pourtant… ça fonctionne !

Donc ce que je vous propose de faire c’est de commencer cette modif, prévoyez un peu de temps quand même…
Ce tutoriel est paru sur le blog raspberrypi.org, en voici une traduction.

 

Attention !
Ce tutoriel  EXPÉRIMENTAL est accessible aux débutants. Cependant vérifiez qu’il n’y a pas de messages d’erreur ni d’avertissements. Ce tutoriel ne fonctionne qu’avec certains modèles de clés USB si votre modèle de clé ne permet pas de booter le Raspberry Pi 3, inutile de m’incendier 🙂

Mise à jour du firmware

Commencez par une mise à jour du systéme

sudo apt-get update
sudo apt-get upgrade

Ensuite on met à jour le firmware dans /boot (start.elf et bootcode.bin) mais avec la version de la branche NEXT (expérimentale) : sudo BRANCH=next rpi-update

pi@raspberrypi:~ $ sudo BRANCH=next rpi-update
*** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom
*** Performing self-update
*** Relaunching after update
*** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom
*** Downloading specific firmware revision (this will take a few minutes)
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 168 0 168 0 0 294 0 --:--:-- --:--:-- --:--:-- 294
100 51.1M 100 51.1M 0 0 413k 0 0:02:06 0:02:06 --:--:-- 468k
*** Updating firmware
*** Updating kernel modules
*** depmod 4.4.15-v7+
*** depmod 4.4.15+
*** Updating VideoCore libraries
*** Using HardFP libraries
*** Updating SDK
*** Running ldconfig
*** Storing current firmware revision
*** Deleting downloaded files
*** Syncing changes to disk
*** If no errors appeared, your firmware was successfully updated to 47d64831ccbc99080f98c1bc8948717c3416cb0f
*** A reboot is needed to activate the new firmware

 Autorisation du boot USB

On va modifier le fichier config.txt pour autoriser le boot USB :

echo program_usb_boot_mode=1 | sudo tee -a /boot/config.txt

Ceci ajoute program_usb_boot_mode=1 à la fin du fichier /boot/config.txt.

On peut maintenant redémarrer le Raspberry Pi et vérifier que le registre 17 de l’OTP a bien été modifié

sudo reboot

et pour vérifier le registre :

pi@raspberrypi:~ $ vcgencmd otp_dump | grep 17:
17:3020000a
pi@raspberrypi:~ $

Vérifiez bien que la valeur du registre 17 est    3020000a   ! (0x3020000a pour ceux qui parlent couramment en hexadécimal 😀 ).

Si vous le souhaitez vous pouvez effacer la ligne program_usb_boot_mode du fichier config.txt (attention de ne pas laisser de ligne blanche à la fin du fichier !) de façon à ce que si vous employez votre carte micro SD sur un autre Raspberry Pi, elle ne programme pas le mode de boot sur USB 😉 Vous pouvez faire la modif avec sudo nano /boot/config.txt, par exemple.

Les clés qui fonctionnent à coup sûr

Les tests ont pour le moment été réalisés avec

  • Sandisk Cruzer Fit 16Go
  • Sandisk Cruzer Blade 16Go
  • Sandisk Cruzer Blade 32Go (merci Michel)
  • Samsung 32Go USB 3.0 drive
  • MeCo 16Go USB 3.0
  • Sandisk Cruise Slice16 Go => celle que j’ai utilisé
  • Disque dur WD Pi Drive 314Go

boot_USB_05

Préparation d’une clé USB déjà partitionnée

Attention !
La clé va être effacée et toutes les données qu’elle contient seront perdue à tout jamais !

Commencez par insérer la clé USB dans un des ports USB du Raspberry Pi. Plutôt que de télécharger à nouveau l’image Raspbian, nous allons copier le système existant sur la carte micro SD du Raspberry Pi. La source (carte SD) sera /dev/mmcblk0 et la destination (clé USB) devrait être /dev/sda en supposant que vous n’ayez pas d’autres périphériques USB connectés.

boot_USB_07

On commence par utiliser parted pour créer une partition FAT32 de 100Mo, suivie d’une partition Linux ext4 qui occupera le reste de la place disponible.

La clé que j’ai utilisé contenait déjà une partition. Si votre clé n’a pas de partition, passez à l’étape suivante.

pi@raspberrypi:~ $ sudo parted /dev/sda
GNU Parted 3.2
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) mktable msdos
Warning: Partition(s) on /dev/sda are being used.
Ignore/Cancel? i
Warning: The existing disk label on /dev/sda will be destroyed and all data on
this disk will be lost. Do you want to continue?
Yes/No? y
Error: Partition(s) 1 on /dev/sda have been written, but we have been unable to
inform the kernel of the change, probably because it/they are in use. As a
result, the old partition(s) will remain in use. You should reboot now before
making further changes.
Ignore/Cancel?

Sortez par CTRL + C et rebootez le Raspberry Pictrl c et reboot

Préparation d’une clé USB non partitionnée

On commence par utiliser parted pour créer une partition FAT32 de 100Mo, suivie d’une partition Linux ext4 qui occupera le reste de la place disponible.

pi@raspberrypi:~ $ sudo parted /dev/sda
GNU Parted 3.2
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) mktable msdos
Warning: The existing disk label on /dev/sda will be destroyed and all data on
this disk will be lost. Do you want to continue?
Yes/No? y
(parted) mkpart primary fat32 0% 100M
(parted) mkpart primary ext4 100M 100%
(parted) print
Model: SanDisk Cruzer Slice (scsi)
Disk /dev/sda: 16,0GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number Start End Size Type File system Flags
1 1049kB 99,6MB 98,6MB primary fat32 lba
2 99,6MB 16,0GB 15,9GB primary ext4 lba

(parted) quit
Information: You may need to update /etc/fstab.

Nous allons maintenant formater les partitions et créer les systèmes de fichiers boot et root (ça peut prendre un certain temps selon la taille de la clé USB)

pi@raspberrypi:~ $ sudo mkfs.vfat -n BOOT -F 32 /dev/sda1
mkfs.fat 3.0.27 (2014-11-12)
pi@raspberrypi:~ $ sudo mkfs.ext4 /dev/sda2
mke2fs 1.42.12 (29-Aug-2014)
En train de créer un système de fichiers avec 3884032 4k blocs et 971040 i-noeuds.
UUID de système de fichiers=545cd535-8405-42eb-aafe-e92300be1f10
Superblocs de secours stockés sur les blocs :
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208

Allocation des tables de groupe : complété
Écriture des tables d'i-noeuds : complété
Création du journal (32768 blocs) : complété
Écriture des superblocs et de l'information de comptabilité du système de
fichiers : complété

Transfert du système Raspbian sur la clé USB

On va monter les systèmes de fichiers nouvellement créés et installer rsync

pi@raspberrypi:~ $ sudo mkdir /mnt/target
pi@raspberrypi:~ $ sudo mount /dev/sda2 /mnt/target/
pi@raspberrypi:~ $ sudo mkdir /mnt/target/boot
pi@raspberrypi:~ $ sudo mount /dev/sda1 /mnt/target/boot/
pi@raspberrypi:~ $ sudo apt-get update; sudo apt-get install rsync
Atteint http://archive.raspberrypi.org jessie InRelease
.../...
Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en
Lecture des listes de paquets... Fait
Lecture des listes de paquets... Fait
Construction de l'arbre des dépendances
Lecture des informations d'état... Fait
rsync est déjà la plus récente version disponible.
rsync passé en « installé manuellement ».
0 mis à jour, 0 nouvellement installés, 0 à enlever et 0 non mis à jour.

Maintenant que les systèmes de fichiers de la clé USB sont montés, on peut copier le boot et le root file system (rfs) dessus… Comptez une demi heure voire plus selon la taille du système ! Vous pouvez aller boire un café, une bière ou une boisson au cola selon votre préférence. Quoi ? De l’eau ? Non, Monsieur chez moi l’eau ne sert qu’à laver les patates 😉

sudo rsync -ax --progress / /boot /mnt/target

boot_USB_06

Régénérer les clés SSH

pi@raspberrypi:~ $ cd /mnt/target
pi@raspberrypi:/mnt/target $ sudo mount --bind /dev dev
pi@raspberrypi:/mnt/target $ sudo mount --bind /sys sys
pi@raspberrypi:/mnt/target $ sudo mount --bind /proc proc
pi@raspberrypi:/mnt/target $ sudo chroot /mnt/target
root@raspberrypi:/# rm /etc/ssh/ssh_host*
root@raspberrypi:/# dpkg-reconfigure openssh-server
Creating SSH2 RSA key; this may take some time ...
2048 7a:f7:33:4c:01:71:a3:fc:c1:2c:b4:9c:3c:3f:4b:19 /etc/ssh/ssh_host_rsa_key.pub (RSA)
Creating SSH2 DSA key; this may take some time ...
1024 28:c6:4b:d1:5e:d9:cb:8c:ca:ad:70:fe:87:08:a1:21 /etc/ssh/ssh_host_dsa_key.pub (DSA)
Creating SSH2 ECDSA key; this may take some time ...
256 f7:f1:9e:3f:6b:13:41:d2:d9:ee:a1:00:e2:ef:ed:db /etc/ssh/ssh_host_ecdsa_key.pub (ECDSA)
Creating SSH2 ED25519 key; this may take some time ...
256 fc:98:8c:c3:52:45:95:c1:67:78:41:0c:00:c6:23:f0 /etc/ssh/ssh_host_ed25519_key.pub (ED25519)
[ ok ] Restarting OpenBSD Secure Shell server: sshd.
root@raspberrypi:/# exit
exit
pi@raspberrypi:/mnt/target $ sudo umount dev
pi@raspberrypi:/mnt/target $ sudo umount sys
pi@raspberrypi:/mnt/target $ sudo umount proc

Modifier cmdline.txt

Il faut maintenant modifier cmdline.txt pour que la clé USB soit le root file system (RFS) à la place de la carte SD :

pi@raspberrypi:/mnt/target $ sudo sed -i "s,root=/dev/mmcblk0p2,root=/dev/sda2," /mnt/target/boot/cmdline.txt

/!\ il n’y a qu’une ligne de code et /mnt/target/boot/cmdline.txt est en un seul bloc

Modifier fstab

pi@raspberrypi:/mnt/target $ sudo sed -i "s,/dev/mmcblk0p,/dev/sda," /mnt/target/etc/fstab

/!\ il n’y a qu’une ligne de code et /mnt/target/etc/fstab est en un seul bloc

Démonter le système de fichier de la clé USB

pi@raspberrypi:/mnt/target $ cd ~
pi@raspberrypi:~ $ sudo umount /mnt/target/boot
pi@raspberrypi:~ $ sudo umount /mnt/target

Arrêter le Raspberry Pi 3

pi@raspberrypi:~ $ sudo poweroff

Maintenant vous pouvez débrancher la prise micro USB d’alimentation et enlever la carte SD

boot_USB_04

Attendez 2,87 secondes (nan c’est une connerie 😉 )

Redémarrer le Raspberry Pi 3

et… reconnectez l’alim… suspens 🙂

ecran_boot

Bon bin chez moi après ce bel écran coloré, ça boote 😀

Raspbian démarre et j’arrive sur le bureau en mode graphique. J’avais installé x11vnc, depuis mon PC je me reconnecte… Ma foi tout ça fonctionne !

Allez une petite verification :

pi@raspberrypi:~ $ df -h
Sys. de fichiers Taille Utilisé Dispo Uti% Monté sur
/dev/root 15G 4,0G 9,8G 29% /
devtmpfs 459M 0 459M 0% /dev
tmpfs 463M 0 463M 0% /dev/shm
tmpfs 463M 6,4M 457M 2% /run
tmpfs 5,0M 4,0K 5,0M 1% /run/lock
tmpfs 463M 0 463M 0% /sys/fs/cgroup
/dev/sda1 93M 20M 73M 22% /boot
tmpfs 93M 0 93M 0% /run/user/1000

et aussi

fdisk -l

/dev/sda1 2048 194559 192512 94M c W95 FAT32 (LBA)
/dev/sda2 194560 31266815 31072256 14,8G 83 Linux

Et avec Ubuntu Mate ?

Là je n’ai pas testé personnellement, c’est Michel qui s’y est collé… Volà ce qu’il en dit :

« Pour Ubuntu Mate 16.10.1 je n’ai rien touché du tout. Ma carte SD était bien chargée d’applications et il a fallu une bonne heure pour faire le transfert de la carte SD vers la clé USB.

Le 1er démarrage un peu long…….20 secondes environ et ensuite un peu plus court.

Aucun fichier n’a été modifié, j’ai uniquement suivi le tuto à la lettre près. »

Si comme Michel vous avez testé d’autres distributions que Raspbian ou d’autres clés n’hésitez pas à ajouter les infos dans les commentaires, je les reprendrai en fin d’article 🙂

Conclusion

Depuis le temps qu’on attendait cette possibilité, elle est enfin disponible. Le Raspberry Pi boote sur autre chose que la carte micro SD !

Certes ceci est encore expérimental mais fonctionne bien. Il faudra tester le boot sur un disque dur classique et sur un SSD mais c’est déjà très encourageant.

boot_USB_02

Le boot Ethernet fonctionne, j’espère le tester bientôt. C’est un peu plus compliqué à mettre en oeuvre et il reste des points à améliorer sur ce démarrage mais c’est déjà fonctionnel 🙂

Sources

Pi 3 booting part I: USB mass storage boot beta

 

Pi 3 booting part II: Ethernet