Photobooth – La machine à selfies

Photobooth : la machine à selfies
Photobooth : la machine à selfies

Une soirée à thème très prochainement ? Un mariage se profile dans quelques mois ? Le photobooth est la machine idéale pour animer vos évènements festifs. L’idée est la suivante…

Vous célébrez un évènement avec vos invités dans un espace de convivialité. A l’écart, dans une pièce isolée, le photobooth est disposé autour d’une décoration à thème. Avec les accessoires de déguisement mis à disposition au sein de cet espace intime, les conditions sont ainsi réunies pour favoriser la créativité des protagonistes et les inciter à se mettre en scène.

En s’affranchissant du tarif abordable de 0.10€, l’utilisateur de cette machine à « selfies » à l’ancienne repart avec une photo instantanément imprimée sur papier thermique… mais ce n’est pas tout !

Les photos prises par le photobooth seront exportées vers un autre Raspberry chargé de les projeter sur écran afin de partager les clichés (en couleur et haute résolution) avec l’ensemble des convives !

C’est un projet ambitieux mais très enrichissant : les notions impliquées sont denses et les difficultés à surmonter sont assez complexes. Le challenge technique est de taille : de nombreuses lignes de code Python nous attendent alors démarrez le Raspberry et ouvrez le terminal en grand : il va y avoir du pulse sur les GPIOs !

Avant de se lancer, voici un aperçu des photos imprimées :

Photos imprimées sur papier thermique
Photos imprimées sur papier thermique

Eu égard au coût de revient de l’imprimante et du papier thermique, la qualité d’impression est tout à fait acceptable.

Fonctionnement

Afin de percevoir le fonctionnement du photobooth, le tableau ci-dessous fournit une description des différents écrans affichés sur l’écran HDMI au fil de l’utilisation de la machine.

Enchainement des écrans du Photobooth
Enchainement des écrans du Photobooth

Le matériel

Le Photobooth vu de l'intérieur
Le Photobooth vu de l’intérieur

La photo ci-contre illustre l’agencement des composants à l’intérieur du photobooth.

Le matériel à mettre en œuvre est le suivant :

  • Un monnayeur CH-926 ;
  • Un écran LCD 800×480 HDMI ;
  • Un écran LCD HD44780 20×04 avec interface I2C ;
  • Une PiCaméra ;
  • Un ruban CSI de 30 cm (plus long que celui fourni en standard avec la RaspiCam) ;
  • Une imprimante thermique liaison série ;
  • Un gros bouton ;
  • Un relais ;
  • Une alimentation 5V 10A ;
  • Un convertisseur USB 5V-12V ;
  • Un convertisseur HDMI-VGA ;
  • Des câbles dupont, une multiprise, visserie,…
  • Des pièces imprimées en 3D :
  • Un support pour fixer le Raspberry à l’intérieur du photobooth ;
  • Un support pour loger le module camera en façade.
  • Du bois MDF, des vis, des boulons et des écrous,…

Commençons par le monnayeur.

Le monnayeur

Monnayeur CH-928
Cette photo présente le modèle JY-323 qui ne sait reconnaître que 3 pièces différentes mais c’est bien le modèle CH-928 (jusqu’à 6 pièces différentes) qui a été utilisé pour le projet qui nous intéresse dans cet article.

Le modèle utilisé (CH-928) permet de reconnaître jusqu’à 8 pièces différentes. D’après la documentation, le périphérique se base sur le matériau, le poids et la taille de la pièce pour la reconnaître. Avec un taux d’identification annoncé à 99.5%, le monnayeur est a priori précis, précision qui dépendra de l’échantillon utilisé lors de la programmation… Oui, le monnayeur doit « apprendre » à discerner les pièces pour les reconnaître. Cet apprentissage est réalisé en insérant un échantillon de pièces.

Pour configurer les pièces à reconnaître, le monnayeur doit être programmé par un « entrainement » de reconnaissance avec un échantillon d’au moins 15 samples distincts. Par exemple, pour lui apprendre les pièces de 2 euros, il est recommandé d’utiliser 15 pièces de 2 euros différentes.
La programmation du monnayeur est réalisée à l’aide des boutons « Add » et « Minus » et de l’écran LED à deux digits présent sur le monnayeur. Le tableau ci-dessous détaille les paramètres à configurer lors du processus de programmation

IDPIECESECHANTILLON
(nb pieces)
PULSES
(nb_pulses)
PRECISIONCONVERSION
PULSES-CREDIT €
01 à 8 types de piècesPréco.* : 15 à 30 samplesDe 1 à 50.1 à 30
Préco.** : entre 5 et 10
Opération dans le code Python
10.10 €1517credit = nb_pulses/10
20.20 €1527credit = nb_pulses/10
30.50 €1557credit = nb_pulses/10
41.00 €15107credit = nb_pulses/10
52.00 €15207credit = nb_pulses/10
* La quantité de pièces de chaque type influe sur la précision du monnayeur
** La précision 1 est la plus précise

L’échantillon indique le nombre de pièces distinctes utilisées pour programmer le monnayeur. Le nombre de fronts montants envoyés sur la ligne COIN du monnayeur est comptabilisé et stocké dans la variable nb_pulses. Le monnayeur ainsi programmé, la conversion nb_pulses/credit est réalisée avec une simple division par 10 (credit = nb_pulses/10).

La quantité de l’échantillon est importante. Lors des premiers essais, je ne disposais que de 4 pièces de 2 euros distinctes. Avec un échantillon aussi réduit, l’apprentissage n’est pas optimal et il peut parfois arriver qu’une même pièce de 2 euros ne soit pas reconnue et donc rejetée à l’avant du monnayeur.

Algorithme de traitement des séquences de pulses sur la ligne coin

Tout d’abord, précisons que le switch NO/NC permet de définir l’état par défaut de la ligne COIN (fil blanc) :

  • NO (Normally Opened) : ligne COIN est maintenu à l’état HIGH (5V) et passe nb_pulses fois à LOW (0V) si une pièce est reconnue, puis retour à HIGH ;
  • NC (Normally Closed) : ligne COIN est maintenue à l’état LOW (0V) et passe nb_pulses fois à HIGH (5V) si une pièce est reconnue, puis retour à LOW.

Le bouton SPEED à 3 positions permet quant à lui d’influer sur le « output signal » d’après la documentation succincte fournie.

Je n’ai pas su trouvé s’il s’agissait de faire varier la longueur des pulses envoyés sur la ligne COIN ou le laps entre chaque pulse (ou même les deux). Si quelqu’un sait, merci de laisser un petit commentaire pour partager l’info…

Quoi qu’il en soit, les 3 vitesses disponibles sont les suivantes :

  1. SLOW : longueur de pulse = 100ms ;
  2. MEDIUM : longueur de pulse = 50ms ;
  3. FAST : longueur de pulse = 30ms.

Le schéma ci-dessous illustre la variation de tension sur la ligne COIN du monnayeur.

Pulses sur la ligne COIN du monnayeur
Pulses sur la ligne COIN du monnayeur

Lors des premiers essais dans le développement de la librairie COINSELECTOR.py, j’avais envisagé de reconnaître chaque pièce insérée à l’aide de l’algorithme suivant :

  1. A chaque pulse détecté :
    • Incrémentation de la variable nb_pulses ;
    • Stockage de du timestamp du dernier pulse dans last_pulse ;
  2. Dans une boucle infinie test des deux conditions suivantes :
    • nb_pulse > 0 ?
    • laps de temps écoulé depuis le précédent pulse supérieur à 100ms (configuration du swich output signal du monnayeur sur la vitesse SLOW) ?
    • Les deux conditions vérifiées alors identification de la pièce : coin = nb_pulse /10 puis nb_pulses = 0

Techniquement, cet algorithme fonctionne. Mais d’un point de vue fonctionnel, des résultats inattendus se produiront si l’utilisateur insère trop rapidement deux pièces consécutives. En effet, comme le montre le schéma ci-dessus, si la pièce de 0.20 € est insérée immédiatement après la pièce de 0.10 €, alors le temps tx sera suffisamment court pour que le traitement d’identification de la pièce ne soit pas déclenché après l’insertion de la première pièce mais seulement une fois la seconde insérée. Dans ce cas, l’algorithme conduira à nb_pulses = 3 et le code nous indiquera qu’une pièce de 0.30 € a été insérée !

Malgré une série de tests, je ne suis pas parvenu à identifier le seuil (le laps de temps écoulé entre 2 pulses) adéquat pour discerner à coup sur les séquences de pulses. A nouveau, si un lecteur peut apporter ses lumières…

Après réflexion, je réalise que faire la différence entre deux pièces de 0.10 € et une pièce de 0.20 € n’a pas de réel intérêt pour le projet. L’essentiel est d’incrémenter le crédit accumulé selon chaque pièce insérée et l’algorithme s’en trouve d’autant simplifié : il suffit d’ajouter la valeur 0.10 à la variable credit à chaque pulse détecté sur la ligne COIN ! Voici donc l’algorithme retenu :

A chaque pulse détecté : Incrémentation de la variable credit de 0.10.

Alimentation du monnayeur et tension du signal COIN

Alimentation : fournir l’énergie nécessaire…

Le monnayeur fonctionne en 12V. Il sera alimenté à l’aide d’un transformateur 5V (USB) – 12V (DC Jack) présenté dans la photo ci-dessous.

Convertisseur de tension 5V vers 12V USB
Convertisseur de tension 5V vers 12V USB

Il est possible de connecter l’extrémité USB sur le Raspberry mais un éclair apparaîtra sur l’écran (même avec un condensateur 1000μF) à l’insertion d’une pièce dans le monnayeur. Cela signifie une instabilité de la puissance électrique attendue par le Raspberry lorsque le monnayeur est actionné. Il est donc préférable d’utiliser une alimentation plus puissante type un chargeur de téléphone de 2A. Mes premiers essais ont montré que 1 A ne suffisait pas : en cas de sous-alimentation, même une pièce normalement reconnue par le monnayeur sera rejetée en façade en émettant 3 bips.

Signal COIN : abaisser les 5V à 3.3V pour les GPIOs du Raspberry

Pont diviseur de tension pour abaisser les 5V de la ligne COIN du monnayeur à 3.3V pour la GPIO en INPUT du Raspberry
Pont diviseur de tension pour abaisser les 5V de la ligne COIN du monnayeur à 3.3V pour la GPIO en INPUT du Raspberry

En plaçant le switch NO/NC sur NO, on découvre à l’aide d’un multimètre que la ligne COIN envoie du 5V.

Or, le Raspberry attend du 3.3v sur les GPIO en INPUT : il est donc nécessaire de créer un pont diviseur de tension pour abaisser les 5V à 3.3V.

La formule permettant de définir la valeur des résistances est Vout = (Vin x R2) / (R1 + R2).

Avec Vout = 3.3v et Vin = 5v, on arrive à déterminer que les valeurs 5 kΩ et 10kΩ respectivement pour R1 et R2 conviennent parfaitement. Pour abaisser le signal du monnayeur à 3.3V sur la GPIO 21, le câblage à réaliser est illustré par le schéma ci-dessus.

Pour réaliser le pont diviseur de tension ainsi que toutes les connexions des GPIOs, nous allons utiliser un module de prototypage qui s’enfiche sur les GPIO du Raspberry : le prototype HAT

HAT est un acronyme pour Hardware Attached on the Top.

Le Prototype HAT

La quantité de connexions à réaliser justifie l’ajout d’un module de prototypage.

De gauche à droite : le prototype HAT avec le fils soudés et deux momentary switch, le Raspberry Pi 3 et un support pour Raspberry imprimée en 3D
De gauche à droite : le prototype HAT avec le fils soudés et deux momentary switch, le Raspberry Pi 3 et un support pour Raspberry imprimée en 3D

Le prototype HAT (carte rouge à gauche sur la photo ci-dessus) est une simple plaque PCB facilitant la soudure de composants.

PCB est un acronyme pour Printed Circuit Board

Une fois les fils soudés, j’ai sécurisant l’ensemble avec de la colle à chaud. L’élément noir (à droite sur la photo ci-dessus) est la partie inférieure d’un boitier pour Raspberry que j’ai imprimé en 3D pour faciliter l’intégration de l’ensemble dans la partie supérieure du photobooth, comme l’illustre la photo suivante :

Raspberry surmonté du prototype HAT, le tout fixé dans la partie supérieure du photobooth à l'aide du support imprimé en 3D
Raspberry surmonté du prototype HAT, le tout fixé dans la partie supérieure du photobooth à l’aide du support imprimé en 3D

Le modèle .stl du boitier Raspberry est téléchargeable à l’adresse suivante : https://www.thingiverse.com/thing:922740

On distingue par ailleurs sur le HAT deux boutons (jaune et bleu). Ces deux interrupteurs momentanés ont été ajoutés pour faciliter des opérations de maintenance sur le Raspberry, sans qu’un clavier ne soit nécessaire :

  • Bouton JAUNE : sortie du script Python photobooth.py pour pouvoir reprendre la main sur le terminal ;
  • Bouton BLEU : arrêt du Raspberry.

Pour ces deux opérations, le code Python prévoit que l’action demandée soit confirmée par un appui sur le gros bouton rouge dans un délai de 5s. Sans confirmation, le script revient à son fil d’exécution nominal une fois le délai écoulé. Voyons dès à présent le gros bouton rouge…

Le bouton rouge

Gros bouton rouge
Gros bouton rouge

Les cas d’utilisation

Le gros bouton rouge type buzzer est un « momentary switch » c’est-à-dire que le changement d’état est actif uniquement le temps de l’appui sur le bouton. Il est doté d’une LED qui permet d’allumer le bouton. Pour suggérer à l’utilisateur d’appuyer sur le bouton rouge, le clignotement de sa LED sera activé. L’utilisateur sera invité à actionner le buzzer dans les 3 situations suivantes :

IDCONTEXTEBLINK LEDDELAITRAITEMENT EN CAS D’APPUITRAITEMENT SANS APPUI ET DELAI ECOULE
1Crédit inséré ≥ 0.10 € (prix fixé)OUI15 sCrédit retranché de 0.10 €
Mise à jour du l’affichage LCD 20x04
Démarrage d’une session photo…
Crédit retranché de 0.10 €
Mise à jour du l’affichage LCD 20x04
Démarrage automatique d’une session photo…
2Affichage à l’écran de la photo prise avec un bandeau invitant à appuyer sur le bouton rouge NON5sImpression de la photo sur l’imprimante thermiqueRetour fil d’exécution nominal du script
3Bouton jaune actionnéOUI5 sSortie du script et retour au terminalRetour fil d’exécution nominal du script
4Bouton bleu actionnéOUI5 sArrêt du RaspberryRetour fil d’exécution nominal du script

Côté Python, c’est la fonction récursive waitForRedButtonPush() qui est appelée pour gérer l’attente d’un éventuel appui sur le bouton rouge.

Comme vous pourrez le comprendre à la lecture des instructions de la fonction waitForRedButtonPush(delai), le traitement est prévu pour éliminer les faux-positifs. En effet, dans la première version de la fonction, j’observais un phénomène inattendu : le crédit de la machine était parfois incrémenté à l’allumage/extinction de la lumière de la pièce dans laquelle se trouvait le photobooth ! Cela signifie que la ligne COIN du monnayeur était pulsée sans qu’aucune pièce ne soit insérée ! Ce bug est évidemment inacceptable ! Comment faire fortune avec une telle machine !?

En reprenant un montage et un code simpliste destiné à afficher « PULSE DETECTE » à chaque front montant envoyé sur une GPIO, je suis parvenu à comprendre qu’il s’agissait de très courtes interférences électriques induites par le changement d’état de l’interrupteur de la pièce.

Ainsi, il faut réaliser un déparasitage logicielle pour être en mesurer de définir si le pulse est généré par :

  • une interférence : dans ce cas, il sera très court (inférieur à 100 ms) ;
  • un réel signal issu du monnayeur suite à l’insertion d’une pièce.

La fonction waitForRedButtonPush(delai) est donc nécessairement récursive pour pouvoir, à chaque faux-positif écarté, se rappeler elle-même en décrémentant le délai. Le délai doit être décrémenté car en cas de perturbations fréquentes, la fonction récursive pourrait se rappeler constamment avec un délai de 20 s sans que le timeout ne soit jamais atteint, ce qui conduirait, au cas où l’utilisateur n’appuierait pas sur le bouton rouge à une boucle infinie.

Alimentation de la LED

Une LED devient passante à partir d’un certain seuil de tension. Au-delà de ce seuil, la LED laisse passer tout le courant qui lui parvient : il est donc nécessaire de la protéger par une résistance pour ne pas être endommagée. Une résistance de protection est déjà présente sur la LED, on pourra donc l’alimenter directement. Cette LED fonctionne en 12v : les GPIO n’envoient que du 3.3v, on ne pourra donc pas les utiliser directement pour faire clignoter la LED. Il faudra donc utiliser la source 12v obtenue depuis le rehausseur de tension 5v->12v. Et pour le clignotement, nous allons devoir utiliser un interrupteur dont l’ouverture/fermeture peut être commandée à l’aide d’un signal envoyé depuis la GPIO 18. C’est justement le rôle d’un relais…

Le relais

Le relais est un interrupteur dont on commande l’état à l’aide d’un signal.

Connexion du relais pour commander la LED 12V du bouton rouge
Connexion du relais pour commander la LED 12V du bouton rouge

Par défaut, quand la broche Signal est à l’état LOW, la fiche Common de l’interrupteur est en contact avec la fiche NO. Lorsque la broche SIGNAL passe à l’état HIGH, la fiche Common est mise en contact avec la fiche NC.

Remarque : si le câblage Vcc et GND inversé, alors seule le LED du module relais s’allume lors du changement d’état du signal de commande sur la GPIO 18 mais le relais ne switch pas.

Problème d’alimentation du Raspberry

Au départ, j’envisageais d’alimenter la Raspberry avec l’alimentation 5V 10A à l’aide de câbles Dupont soudés sur le Prototype HAT. Ainsi alimenté, je constatais ponctuellement l’affichage d’un éclair jaune en haut à droite de l’écran signalant un problème d’énergie (notamment lorsque l’alimentation est instable) sur le Raspberry. Cet éclair apparaissait notamment au démarrage ou à l’exécution de la commande iwlist dont l’exécution exige de l’énergie pour scanner les hotspots wifis disponibles. En comparant la tension entre la sortie de l’alimentation, la sortie des fils dupont et la sortie aux broches +5V et GND du Prototype HAT, en constate une diminution de la tension au fur et à mesure que l’on s’éloigne de l’alimentation.

L’explication est la suivante : que ce soit les câbles Dupont ou le prototype HAT, les conducteurs sont fins et de fait, imposent une forte résistance entrainant une baisse de tension. Pour rappel, la relation entre la tension, la résistance et l’intensité est régie par la formule U = R x I. L’idée peut être vulgarisée à l’aide de la métaphore suivante : « peu de cuivre donc peu « d’espace » pour faire circuler les électrons qui se gênent mutuellement pour circuler ».

Remarques :

  • Chaque câble branché en série ajoute sa résistance au total, c’est-à-dire que ce n’est pas le plus petit qui fait goulot, c’est bien une addition ;
  • Plus le câble est fin, plus il est résistant. Plus le câble est long, plus il est résistant ;
  • Pour pouvoir déceler ces chutes de tensions, les tensions doivent être mesurées à l’aide d’un voltmètre lorsqu’un courant circule effectivement (Raspberry en fonctionnement), c’est-à-dire qu’un composant appelle des électrons (comme le dit la formule U = R x I). Dans le cas contraire, les électrons ne circulent pas et ne se « gênent » pas et les atténuations de tensions ne pourront donc pas être mises en évidence.

Face à ce problème, je me résous à utiliser un chargeur mini USB classique 5v 2.5A.

L’écran HDMI pourra quant à lui être alimenté directement depuis l’alimentation 5V 10A via des câbles dupont sans aucun problème.

La gestion des INPUTS sur les GPIOs

Les résistances Pull-up/Pull-down

Si une GPIO configurée en entrée n’est connectée à rien, alors elle sera « flottante ». Cela signifie que l’état de la GPIO est instable puisqu’elle n’est connectée à rien jusqu’à l’appui d’un switch réalisant le contact à la masse ou au +3.3v. Dans cet état instable, les interférences électriques induites par l’alimentation secteur influent fortement la valeur de la GPIO.

Pour éviter que la GPIO ne « flotte », il est nécessaire de la fixer à une valeur stable. Pour cela, on utilise une résistance (10Kohms est couramment utilisée) et deux options sont envisageables :

  1. Résistance pull-down : connectée à la masse par l’intermédiaire de la résistance, la GPIO est maintenue à l’état bas ;
  2. Résistance pull-up : connectée au +3.3v par l’intermédiaire de la résistance, la GPIO est maintenue à l’état haut.
Résistance pull-up et résistance pull-down permettant de maintenir une GPIO en INPUT à un état stable
Résistance pull-up et résistance pull-down permettant de maintenir une GPIO en INPUT à un état stable

Il est également possible de mettre en place une résistance pull-up/pull-down de manière logicielle puisque le module RPi.GPIO permet de configurer les GPIO du Raspberry à l’aide des instructions suivantes :

La méthode (basique) du polling

Si vous avez lu l’article sur la machine à blagues Chuck NORRIS, pour lire l’état des GPIOs en entrée, nous avions utilisé la méthode de polling qui consiste à tester à intervalles réguliers, l’état du GPIO à un instant précis. Dans le script Python, les instructions afférentes sont les suivantes :

Cette méthode est perfectible car un changement d’état sur la GPIO peut potentiellement échapper au test si le script Python sonde l’état de la broche au « mauvais moment », c’est-à-dire juste avant ou juste après le pulse électrique. Par ailleurs, lorsque l’on souhaite attendre l’appui sur un switch, le polling s’inscrit dans une boucle infinie.

Malgré l’instruction time.sleep(0.01), la méthode de polling peut s’avérer lourde pour la ressource CPU.

La méthode des interruptions

L’autre méthode, plus élégante et surtout plus efficace consiste à utiliser les interruptions, déclenchées à la détection des fronts (edge) sur le GPIO. Plutôt que de mesurer la valeur de la GPIO, nous allons ici nous intéresser aux changements d’états, appelés évènements. Ces évènements désignant le passage d’un état à l’autre, deux cas de figure se distinguent :

  1. De HIGH vers LOW : falling edge ;
  2. De LOW vers HIGH : rising edge.

Pour ne pas manquer un appui sur le bouton pendant que le script est occupé à exécuter un autre traitement, 3 fonctions sont à notre disposition :

  1. wait_for_edge() ;
  2. event_detected() ;
  3. une fonction de « callback », exécutée dans un thread parallèle au script principal lorsqu’un front est détecté.
La fonction wait_for_edge()

La fonction wait_for_edge() permet de bloquer l’exécution du script jusqu’à ce qu’un edge soit détecté. Nous avons eu recours à cette fonction au sein de la fonction waitForRedButtonPush(delai) (voir paragraphe sur le Bouton rouge ci-dessus) pour attendre l’appui :

On en conviendra, c’est nettement plus élégant que la boucle while de la méthode de polling évoquée ci-dessus et surtout la ressource CPU est épargnée.

Remarques :

  1. La méthode permet de détecter :
    • Les fronts montants : GPIO.RISING ;
    • Les fronts descendants : GPIO.FALLING ;
    • Les fronts montants ET descendants : GPIO.BOTH.
  2. Le paramètre timeout permet de fixer un délai (en ms) au bout duquel le script passera à l’instruction suivante : dans ce cas, le retour de la fonction wait_for_edge() est None ;
  3. Le paramètre bouncetime permet d’éviter le phénomène de rebond : à la détection d’un edge, les fronts suivants seront ignorés pendant le délai spécifié pour éviter le parasitage.

Le phénomène de rebond est parasitage lié à la génération de plusieurs edge alors que le switch n’est appuyé qu’une seule fois. Il est également possible d’utiliser un condensateur pour déparasiter les rebonds.

La fonction event_detected()

La fonction add_event_detected() joue le même rôle que la boucle while de polling mais elle apporte la garantie de détection de chaque évènement, même à un moment où le CPU serait occupé.

Les fonctions Threaded callback

La librairie RPi.GPIO permet de déclencher l’exécution de fonctions callback en parallèle du script principal (dans un nouveau thread), immédiatement après la détection d’un front sur la GPIO en input. Les lignes suivantes extraites du script photobooth.py permettent de définir les fonctions (stopPhotobooth() et haltPhotobooth()) à appeler sur détection de fronts montants sur les GPIO connectées aux switchs jaune et bleu :

En dupliquant les lignes ci-dessus, il est possible d’ajouter plusieurs fonctions de callback sur une même GPIO. Un seul thread étant créé pour l’exécution des fonctions de callback, celles-ci s’exécuteront séquentiellement (et non pas de manière concurrente) dans l’ordre de où elles sont ajoutées.

Enfin, il est possible de désactiver la détection de front sur une GPIO à l’aide de l’instruction :

Quelle que soit l’issue de la fonction waitForRedButtonPush(delai), cette instruction est exécutée pour libérer la GPIO RED_BUTTON et éviter les erreurs du type :

En effet, la fonction waitForRedButtonPush(delai) commence par activer la détection des edges sur le GPIO RED_BUTTON avec l’instruction :

Lorsque que cette fonction s’appelle récursivement, la nouvelle instance de la fonction active également la détection de front alors que l’instance appelante l’avait déjà activé sur le même GPIO.

Enfin, précisons que la fonction add_event_detect est également utilisée dans la librairie COINSELECTOR.py pour capter les pulses sur la ligne COIN du monnayeur. Exécutée dans un thread concurrent, la fonction de callback coinEventHandler(self, pin_number) (voir extrait de code ci-dessous) permet de gérer l’insertion de pièce par un utilisateur à n’importe quel instant, parallèlement à l’exécution du script principal.

 

L’écran LCD HD44780 20×04 et l’interface I2C

Nous avons déjà abordé le fonctionnement de l’écran LCD HD44780 dans un précédent article (voir la machine à blagues Chuck NORRIS), nous ne nous y attarderons donc pas davantage pour se concentrer sur l’interface I2C. En effet, l’écran peut être interfacé de deux manières pour la transmission de données (indépendamment de l’alimentation et du contraste de l’écran) :

  1. en reliant directement les broches Data sur les GPIOs du Raspberry, ce qui suppose d’hypothéquer 4 GPIOs en mode 4 bits voire 8 GPIOs en mode 8 bits ;
  2. par l’intermédiaire d’un module I2C n’exigeant que deux lignes pour les données : SDL et SDA.

Pour ce projet, les connexions sont déjà nombreuses sur les GPIOs et nous retenons l’utilisation d’un module I2C pour simplifier l’interfaçage de l’écran LCD HD44780 20×04 (20 colonnes, 4 lignes).

Ecran LCD HD44780 avec module I2C
Ecran LCD HD44780 avec module I2C

Un bus

Puisqu’il s’agit d’utiliser le bus I2C, commençons par rappeler ce que signifie ce mot galvaudé. Un bus est un dispositif de transmission de données partagé entre plusieurs composants par l’intermédiaire d’un canal de transmission commun. Le concept englobe donc à la fois :

  • le matériel sur lequel repose la communication ;
  • le protocole qui régit les échanges.

On distingue généralement un bus d’une part d’une liaison point à point, qui ne concerne que deux composants qui en ont l’usage exclusif, et d’autre part d’un réseau, qui implique des participants indépendants entre eux, c’est-à-dire pouvant fonctionner de manière autonome, et qui comprend plusieurs canaux permettant des communications simultanées.

Comme l’illustre le schéma ci-dessous, un bus comporte 3 sous-ensembles :

  1. Les lignes de données pour envoyer les données à transmettre ;
  2. Les lignes d’adresse pour indiquer quel composant doit émettre ou recevoir l’information présente sur le bus de données ;
  3. Les lignes de contrôle pour indiquer l’opération à effectuer.
Bus d'échanges de données
Bus d’échanges de données

Maintenant que l’on sait à peu près de quoi on parle, revenons aux GPIOs. Sur certaines d’entre elles, le Raspberry propose l’activation de bus/protocole de communication, à savoir :

  • I2C (Inter-Integrated Circuit) : il s’agit d’un bus série synchrone bidirectionnel half-duplex. Plusieurs équipements (master/slave) peuvent être connectés au bus, à l’aide de 2 lignes :
    • SDA (Serial Data Line) : données bidirectionnelles (GPIO n°3) ;
    • SCL (Serial Clock Line) : horloge de synchronisation bidirectionnelle (GPIO n°5) ;
  • SPI (Serial Peripheral Interface) : bus série synchrone full-duplex. La communication entre plusieurs équipements (schéma master/slaves) est réalisée à l’aide de 4 signaux logiques :
    • SCLK : Serial Clock (GPIO n°23) ;
    • MOSI : Master Output, Slave Input (GPIO n°19) ;
    • MISO : Master Input, Slave Output (GPIO n°21) ;
    • SS : Slave Select.

Parmi ces deux des bus série fonctionnant selon le schéma maitre/esclave, nous allons utiliser le bus I2C. Commençons tout d’abord par l’activer à l’aide de l’utilitaire raspi-config dans le menu « 5 Interfacing Options » puis redémarrer le Raspberry. Pour la transmission des données, le protocole de communication I2C s’appuie sur l’adresse des composants connectés au bus. Nous devons donc déterminer l’adresse de l’écran. Une fois l’ensemble d’outil I2C installé à l’aide de la commande sudo apt-get install i2c-tools, nous disposons de l’utilitaire i2cdetect permettant de détecter les esclaves présents sur le bus I2C :

Scan du bus I2C pour déterminer les adresses des périphériques présents sur le bus
Scan du bus I2C pour déterminer les adresses des périphériques présents sur le bus

L’adresse I2C du LCD est donc dans notre cas : 3F. Cette adresse sera utilisée dans la librairie Python RPi_I2C_driver.py destinée à piloter l’écran, dans laquelle on adaptera la ligne suivante :

La librairie RPi_I2C_driver.py

Commençons par télécharger la libraire RPi_I2C_driver.py (disponible à l’adresse https://github.com/emcniece/rpi-lcd). L’exemple fourni (example.py) permet de comprendre sans grande difficulté comment exploiter les fonctionnalités de la librairie.

Cette librairie s’appuyant sur le module smbus (import smbus), il devra être installé à l’aide de la commande :

L’affichage à obtenir est le suivant :

Informations affichées sur l'écran LCD HD44780 20x04
Informations affichées sur l’écran LCD HD44780 20×04

L’extrait de code ci-dessous illustre ainsi comment afficher les chaines sur les différentes lignes, charger en mémoire un « custom symbol » (ici, le symbole €) pour ensuite l’afficher sur les digits adéquats.

L’utilisateur pouvant introduire des pièces à n’importe quel moment, l’affichage de la dernière ligne CREDIT sera prise en charge par un thread destiné à actualiser en continu le crédit courant. Un zoom sur le code afférent est fourni ci-dessous.

Les variables moneybox et mylcd étant initialement créées dans le script principal, il est nécessaire de les déclarer avec le préfixe global dans la classe checkAndUpdateCredit(threading.Thread) afin qu’elles puissent être manipulées par le thread.

L’IMPRESSION THERMIQUE

Rappels sur la connexion et résolution des problèmes électriques

Comme nous l’avions déjà fait avec la machine à blagues Chuck NORRIS (voir le numéro 2 du magazine « L’officiel PC »), l’impression des photos sera réalisée à l’aide d’une imprimante série. La distribution Raspbian ayant évolué depuis, l’interfaçage est nettement plus simple : la désactivation du login shell et des messages du noyau envoyés sur le port série est désormais possible via l’outil raspi-config.

Pour « libérer » le port série, lancer l’utilitaire à l’aide de la commande sudo raspi-config, puis naviguer dans les menus 5 Interfacing options > P6 serial.

Pour tester le bon fonctionnement de l’imprimante, il est possible d’imprimer un « self-test ticket » sur lequel sera renseigné la table de caractère ainsi qu’un certain nombre d’informations techniques relatives au périphérique. Pour cela, il suffit de maintenir le bouton « feed paper » enfoncé à la mise sous tension. Lors du tout premier test, l’imprimante était provisoirement reliée à l’alimentation 5V à l’aide de câbles Dupont.

Comparés à ceux fournis avec l’imprimante, les câbles Dupont sont nettement plus fins et leur résistance électrique de facto, plus importante (voir détails précédemment évoqués dans le chapitre « Problème d’alimentation du raspberry »). Comme le montre l’image ci-dessous (partie gauche), la capacité de chauffe du périphérique ainsi alimenté est atténuée et le résultat dégradé.

Capacité de chauffe de l'imprimante réduite avec des câbles d'alimentation trop fins
Capacité de chauffe de l’imprimante réduite avec des câbles d’alimentation trop fins

Heureusement, la connectique fournie avec l’imprimante permet d’obtenir un qualité d’impression satisfaisante (à droite sur la photo ci-dessus) et le problème est rapidement résolu.

En revanche, il est possible qu’un autre phénomène vienne perturber l’impression selon le matériel que vous utilisez… Plutôt que de formuler de longues phrases sibyllines, je vous propose d’observer l’image ci-dessous…

Perturbations de la ligne TX par des interférences induites par l'alimentation low-cost de Raspberry
Perturbations de la ligne TX par des interférences induites par l’alimentation low-cost de Raspberry

Sur cette photo se trouvent les impressions obtenues à l’aide de la librairie py-thermal-printer (disponible à l’adresse https://github.com/luopio/py-thermal-printer) dans deux contextes différents :

  • A gauche : le raspberry était alimenté avec l’alimentation noire sans marque ;
  • A droite : le raspberry était alimenté avec l’alimentation blanche de marque SAMSUNG.

Identifier l’origine du phénomène n’étant pas nécessairement triviale, je vous livre l’explication sans détour… Si l’impression est dégradée (caractères inattendus et image décalée) cela signifie que la transmission des données (à imprimer) sur la ligne TX (entre le Raspberry et l’imprimante) n’est pas fiable. Et si la transmission n’est pas fiable, c’est parce qu’elle subit des perturbations (sur la ligne TX et GND) dues aux interférences émises… par l’alimentation (sans marque et de qualité moyenne) du Raspberry !

Remarque : éloigner l’imprimante de l’alimentation du Raspberry suffit à supprimer les interférences, c’est d’ailleurs pour cela que le problème n’était pas survenu sur la machine à blague Chuck NORRIS. Mais pour ce projet, tout doit être intégré à l’intérieur du photobooth et nous sommes contraints de recourir à une alimentation de qualité (tel qu’un chargeur SAMSUNG) pour résoudre le problème.

Utiliser une librairie Python 2 depuis un script Python 3 : le module PRINT.py

Pour exploiter le potentiel de l’imprimante série depuis le script Python, nous allons installer les librairies suivantes :

Téléchargeons la librairie mise à disposition par Adafruit à l’aide de la commande :

Parmi les fichiers présents dans le répertoire Python-Thermal-Printer créé à l’issue de la commande précédente, nous allons plus particulièrement nous intéresser au fichier Adafruit_Thermal.py. C’est ce fichier que nous allons utiliser depuis notre script photobooth.py. Cette bibliothèque est développée en Python 2 et l’absence de shebang indique que c’est l’interpréteur /usr/bin/python (sous-entendu, version 2). Le script photobooth.py est codé quant à lui avec la version 3 du langage et son exécution fait appel fait appel à l’interpréteur en version 3 comme le précise le shebang # !/usr/bin/python3.

Remarque : Le shebang est l’information placée sur la toute première ligne du fichier (exemple : # !/usr/bin/python) permettant d’indiquer le chemin de l’interpréteur à utiliser pour exécuter les instructions.

Ainsi, si l’on importe directement la librairie Adafruit_Thermal.py dans le script photobooth.py, le code de la librairie sera également parsé avec l’interpréteur version 3, ce qui conduira naturellement à la levée d’erreurs puisque du code Python 2 ne se conformes pas se conforme pas aux exigences de syntaxe introduites par Python 3. L’instruction import Adafruit_Thermal.py en début du script photobooth.py ne peut donc pas être envisagée. Pour s’affranchir d’un laborieux travail d’évolution de la librairie vers la version 3 du langage, nous allons contourner ce problème en créant un module PRINT.py écrit en Python 2 dans lequel sera importé le fichier Adafruit_Thermal.py. Le code chargé de l’impression (en utilisant les fonctionnalités de la librairie) sera déporté dans ce module. Enfin, il suffira d’appeler le script PRINT.py depuis photobooth.py avec l’interpréteur Python 2 à l’aide de l’instruction :

L’instruction subprocess.call (contrairement à subprocess.popen(), subprocess.call() bloque l’exécution du script appelant jusqu’au la fin du processus appelé) permet de créer un nouveau processus chargé d’exécuter la commande passée en paramètres : ici, il s’agit de lancer l’interpréteur Python auquel on fournit en argument le script PRINT.py, auquel on fournit en argument, le nom du fichier à imprimer.

Lancer le script photobooth.py au démarrage

Editer le fichier /etc/rc.local à l’aide de la commande :

Et ajouter (avant la ligne exit 0) la ligne suivante :

Le caractère « & » permet de lancer le script autostartphotobooth.sh en tâche de fond. Editons dès à présent le script autostartphotobooth.sh (nano autostartphotobooth.sh) et ajoutons les lignes suivantes :

Dans cet ultime chapitre, nous allons passer en revue un certain nombre de notions relatives à l’affichage et à la manipulation des images. Tout d’abord, pour un affichage sur l’écran HDMI, les modifications suivantes seront apportées dans le fichier /boot/config.txt :

La gestion des images

Dans cet ultime chapitre, nous allons passer en revue un certain nombre de notions relatives à l’affichage et à la manipulation des images. Tout d’abord, pour un affichage sur l’écran HDMI, les modifications suivantes seront apportées dans le fichier /boot/config.txt :

L’affichage graphique est réalisé à l’aide de la bibliothèque Pygame qui sera installée à l’aide de la commande suivante :

Assez simple à aborder, nous ne nous attarderons pas sur l’utilisation de Pygame. La lecture des deux premiers cours OpenClassroom sur le sujet vous suffira pour mettre en place rapidement l’affichage rudimentaire utilisé dans le photobooth. Intéressons-nous tout de même rapidement aux lignes suivantes extraites du script photobooth.py :

Une fois la fenêtre Pygame (il s’agit d’un objet de classe Surface) affichée en plein écran, le terminal sera masqué et les messages affichés sur la sortie standard ne seront plus visibles. Passée en paramètre au script photobooth.py, l’option –debug permettra de désactiver l’affichage de la fenêtre Pygame sans occulter les messages affichés par les instructions print() au fil de l’exécution du script. Cette option se révèlera bien utile pour débuguer le code.

Côté camera, la capture d’image fera l’objet d’ajustements au fil de l’exécution d’une session photo pour améliorer l’expérience utilisateur. Lorsque la caméra est à sa résolution maximum (s’agissant de ma PiCamera v2 : 2592×1944), le ratio largeur/hauteur (≈ 1.3) ne correspond pas à celui de l’écran (≈ 1.6) et l’affichage du flux vidéo laissent apparaître des bandes noires assez peu esthétiques.

Les ajustements opérés chronologiquement sont les suivants :

  1. la prévisualisation de la camera sera réalisée :
    • à la résolution de l’écran (800×480) afin que le flux vidéo utilise la totalité de l’écran ;
    • avec un « horizontal flip », permettant de réaliser l’affichage en reproduisant l’effet d’un miroir, plus naturel à l’utilisation du photobooth ;
  2. les clichés seront capturés avec la résolution maximum de la caméra pour des photos de qualité (l’adaptation est réalisée juste avant la capture, pendant l’affichage de l’écran SMILE ;
  3. un copie miniature de l’image capturée sera réalisée à la résolution 800×480 pour affichage à l’écran ;
  4. pour finir, l’image haute résolution est envoyée au script PRINT.py pour impression une fois les manipulations effectuées :
    • redimensionnement de l’image à 384×288 (384 étant la largeur maximum d’impression) ;
    • éclaircissement de l’image pour un meilleur rendu sur le papier thermique.

Extrait de la fonction shooting() du script photobooth.py :

Extrait du script PRINT.py :

Voilà pour la première partie de ce projet qui claque sa mère ! Rendez-vous dans quelques semaines pour la phase 2 où il sera question d’afficher un slideshow des photos prises par le photobooth.

Shopping List Machine : Python, Code-barres EAN13, SQLite et imprimante thermique

A l’ère du « drive », l’intérêt de cette Shopping List Machine peut laisser dubitatif mais une chose est sûre : au travers de la réalisation de ce projet, vous en apprendrez beaucoup sur des notions variées. Les concepts impliqués sont denses et ne pourront être que survolés mais l’essentiel nécessaire pour se lancer est ici synthétisé.

La machine Shopping List composée d’un Raspberry, d’un scanner sans fil, d’une enceinte et d’une imprimante thermique

Mais avant d’entrer dans les détails techniques, formulons les exigences de la Shopping List Machine…

Continuer la lecture de « Shopping List Machine : Python, Code-barres EAN13, SQLite et imprimante thermique »

Caméra de vidéosurveillance avec motion

Motion
Un système de vidéosurveillance avec motion

On a vu comment ajouter la vision au Raspberry Pi dans l’article dédié au module Pi caméra. On dispose donc d’un périphérique de capture d’images/vidéos qu’on peut utiliser à l’aide de commandes éventuellement insérées dans un script shell ou Python : c’est chouette. Mais il y a encore plus chouette : créer un système de vidéosurveillance avec motion.

Les usages sont multiples et je ne vais pas philosopher là-dessus, je suis certain que vous saurez trouver l’utilité de ce système. En tout cas, à la fin de ce tutoriel, vous serez en mesure :

  • de visualiser en temps réel le streaming vidéo de votre Raspberry Pi depuis votre smartphone ;
  • déclencher l’enregistrement vidéo et/ou être alerté par mail sur détection de mouvements.

Motion est l’outil qui va permettre de diffuser le flux vidéo en streaming, de capturer des images et des vidéos provenant d’une ou plusieurs caméras.

Installation de motion

Commençez tout d’abord par mettre à jour Raspbian avec la commande :

Maintenant, installons motion et les paquets nécessaires :

Pour demander au noyau le chargement d’un module (le drivers broadcom (bcm) vidéo for linux (v4l)) , taper la commande :

Vous devriez normalement disposer du matériel /dev/video0. Pour que le module bcm2835-v4l2 soit automatiquement chargé au démarrage par le noyau, ajoutons le au fichier /etc/modules :

Oui, c’est une commande élégante pour ajouter bcm2835-v4l2 en fin de fichier /etc/modules.

Configuration de motion

Tout d’abord, pour que motion puisse s’éxécuter en tant que processus d’arrière plan, éditez le fichier /etc/default/motion et modifiez la ligne suivante à yes :

Ce paramètre modifié, motion pourra être lancé et l’essentiel de la configuration est à réaliser dans le fichier /etc/motion/motion.conf. Editez le fichier de configuration :

Dans ce fichier, on pourra apporter les modifications suivantes

Les principales modifications portent sur les paramètres suivants :

  • Décommenter la ligne logfile /tmp/motion.log pour enregistrer les logs ;
  • Effectuer une rotation de l’image selon les besoin (0, 90, 180 et 270), ici : rotate 180 ;
  • Ajuster la largeur/hauteur en pixels de l’image : width 1280 et height 720 ;
  • Le port d’écoute du serveur web offrant le flux viédo : stream_port 8081 ;
  • Ne pas restreindre l’accès au streaming à la machine locale uniquement :  stream_localhost off ;
  • Activer l’authentification Digest : stream_auth_method 2 ;
  • Configurer les credentials (login/password) : stream_authentication username:password ;
  • Afficher un texte en bas à gauche : text_left P3TCH CAMERA %t.

Pour lancer motion, taper la commande :

Désormais, un point rouge s’allume sur le module caméra… ça tourne ! Il est possible de se connecter à l’URL : http://192.168.1.123:8081. Après authentification, on visualise le flux vidéo en tant réel.

Si vous souhaitez désativer la LED rouge qui s’allume au démarrage de motion, c’est dans le fichier /boot/config.txt que ça se passe avec le paramètre : disable_camera_led=1.

Vous pourrez découvrir que le fichier de configuration permet un paramétrage assez fin de la caméra camera les zones de détection, la luminosité, les paramètres des enregistrements,… le site officiel Motion est exhaustif sur les options du fichier de configuration.

Remarque : A ce stade, cela fonctionne mais vous devriez rencontrer une erreur au premier mouvement détecté par la caméra. En observant les logs renseignés dans le fichier /tmp/motion.log, on s’apercoit qu’il s’agit d’un problème de permission lors de l’accès au répertoire /var/lib/motion (voir extrait ci-dessous).

Pour résoudre ce problème, il faut rajouter les droits adéquats à l’utilisateur sous lequel tourne motion. Pour cela, tapez la commande :

C’est donc l’utilisateur pi qui est utilisé. La commande suivante nous renseigne sur les permissions du répertoire en question :

Ajoutez à « other » le droit en écriture avec la commande :

Désormais, motion ne s’interrompera plus suite à la détection de mouvements puisqu’il pourra écrire dans le répertoire adéquat les vidéos d’enregistrements.

 

 

Chuck Norris Machine : script Python et GPIOs

Les concepts matériels étant présentés dans l’article 1/2, il est temps d’aborder le côté logiciel du projet. La lecture préliminaire du premier article est recommandée pour comprendre plus facilement le développement du script Python qui sera détaillé ici pour utiliser les GPIOs du Raspberry Pi.

L’utilitaire fortune au cœur du système

Avant d’entrer dans les détails du code, commençons par aborder la commande fortune sur laquelle s’appuie le script Python. La commande fortune affiche un message choisi au hasard parmi une source de citations. Il peut s’agir de proverbes, citations de célébrités , de blagues ayant attrait à l’informatique ou la programmation. On peut évidemment créer sa propre source. Continuer la lecture de « Chuck Norris Machine : script Python et GPIOs »

Chuck Norris Machine : LCD HD44780 et thermal printer

Guidé par le magazine « Linux Inside – Raspberry pour les débutants » dans la découverte de ce nano ordinateur, l’un des tutoriels proposait l’utilisation d’un écran LCD pour afficher les informations météo de sa ville…  Ce tutoriel me paraissant accessible, je me suis donc orienté sur l’utilisation de l’écran LCD HD44780 1602 (16 caractères x 2 lignes) pour la première expérimentation des GPIOs de mon Raspberry Pi.

L’art. 1/2 s’intéresse aux aspects « bas niveau » du reverse engineering de la Chuck Norris Joke Machine. Ici sont détaillées toutes les notions nécessaires avant d’aborder, dans le second article, le code python qui pilotera la machine.

Bon, la météo c’est chouette mais pas passionnant et on se lasse assez vite du contenu affiché. En revanche, une bonne blague Chuck Norris, c’est nettement plus drôle (si tant est que les blagues Chuck Norris vous fassent marrer) et davantage adapté pour promouvoir son projet une fois finalisé qu’une température/hygrométrie. Parallèlement, j’ai découvert l’utilitaire « fortune » disponible sur les systèmes de type Unix et l’idée était lancée… nom de code : « Chucky Project ».

(Rétro)Spécification des exigences

La devise : Laugh and go ! Pour ce projet, il est question d’afficher, une blague Chuck Norris choisie aléatoirement parmi un gisement de Chuck Norris Fact. Selon la qualité de la blague, l’utilisateur peut (ou non) en demander l’impression. Continuer la lecture de « Chuck Norris Machine : LCD HD44780 et thermal printer »