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. Commençons par installer le programme fortune :

Vous disposez maintenant de l’outil, allez testons-le :

L’outil utilise une source par défaut. Personnellement, les blagues ne me font pas rire systématiquement… de l’humour anglais sans doute… Fortune utilise deux fichiers pour chaque source de citations :

  1. un fichier texte listant les citations séparées par une ligne avec le caractère % ;
  2. un fichier .dat généré par le programme strfile pour l’accès aléatoire aux données.

Je vous livre ici le gisement de blagues Chuck Norris sous forme de fichier texte. Je l’ai volontairement épuré de tout caractères accentués pour m’affranchir d’un algorithme de substitution pour l’affichage sur le LCD HD44780 qui ne permet pas nativement leur affichage.

Créer le fichier source :

Ici, le seul moyen que j’ai trouvé pour créer un fichier source valide, c’est par le biais d’un copier-coller de l’ensemble du texte à travers l’utilitaire Putty. Une fois le gisement chargé et enregistré, lancez la commande :

La commande strfile permet de créer un fichier de données contenant une structure permettant l’accès aléatoire aux chaînes de caractères enregistrées dans le fichier source. Oui, vous avez bien lu : 9692 strings ! Copiez les deux fichiers dans à l’endroit adéquat :

On peut maintenant afficher une blague Chuck Norris :

Le système est maintenant prêt. Le script Python pourra lancer la commande système.

Configuration du système pour l’utilisation du port série

Les permissions de l’utilisateur

Pour pouvoir utiliser le port série, l’utilisateur pi (ou celui que vous souhaitez utiliser) doit disposer des permissions nécessaires. Vérifier que pi est membre du groupe dialout (ce groupe octroie les droits d’utiliser le port série) :

Si l’utilisateur pi n’est pas membre du groupe dialout, y remédier :

Le serial port est maintenant ouvert pour l’utilisateur pi mais encore inaccessible car utilisé par le système.

Libérer le port série /dev/ttyAMA0

Le port série du Raspberry se caractérise par deux signaux : un signal de transmission TxD (GPIO 14/pin 8) et un signal de réception RxD (GPIO 15/pin 10). Pour connecter un matériel série, il faut connecter le TxD sur le RxD de l’un, respectivement sur le RxD et le TxD de l’autre. Il est également nécessaire de connecter les masses des deux matériels ensemble.

On parle également de port UART, composant Braodcom UART… UART est un acronyme pour Universal Asynchronous Receiver Transmitter (émetteur-récepteur asynchrone universel). Le problème à résoudre est le suivant : le Raspberry envoie les données en parallèle (autant de fils que de bits de données) alors qu’on souhaite en série. Il faut donc transformer ces données parallèles pour les transmettre via une liaison série qui utilise un seul fil pour faire passer tous les bits de données, c’est le rôle du composant UART.

Le composant UART Broadcom est désigné par /dev/ttyAMA0 sous Raspbian. Par défaut, le système Raspbian utilise ce port série, des modifications sont donc nécessaires pour demander au système de libérer le /dev/ttyAMA0 et pouvoir ainsi y accéder.

Le port série permet donc d’envoyer des données entre le Raspberry et un autre matériel. Il existe deux cas d’usage :

  1. connecter un PC via un câble USB-to-serial pour accéder à un terminal avec Putty configuré sur Serial Line (utile pour se connecter si la vidéo et le réseau ne sont pas disponibles) ;
  2. connecter un périphérique possédant une interface série, afin de contrôler un autre matériel depuis le Raspberry.

Par défaut, Raspbian utilise le port série pour envoyer les informations console (premier cas d’usage). Pour le vérifier, tapez la commande :

Le fichier /proc/cmdline contient la commande avec les arguments passés au noyau lors du démarrage. Si on affiche cette ligne de commande :

On constate effectivement qu’elle contient l’argument « console=ttyAMA0,115200 ». Pour ne plus fournir cet argument au démarrage du noyau, éditez le fichier /boot/cmdline.txt :

La ligne de commande comporte la séquence « console=ttyAMA0,115200 » qui indique au noyau d’envoyer l’affichage des messages lors du boot sur le port série. Selon la version de votre environnement, il se peut que la ligne comporte également la séquence « kgdboc=ttyAMA0,115200 » qui active le débugging du noyau.

Vous devez supprimer toutes les références à ttyAMA0, ce qui doit vous donner la ligne suivante :

Attention, une erreur de syntaxe dans la commande du fichier /boot/cmdline.txt peut empêcher le boot du système.

Sur le Raspberry Pi 3, /dev/ttyAMA0 désigne désormais le port série connecté au Bluetooth. Le port série miniUART qui nous intéresse ici est maintenant disponible dans /dev/ttyS0. Sur les systèmes les plus récents (c’est notamment le cas sur la Raspbian Jessie 8.0), c’est désormais /dev/serial0 qui désigne le port série miniUART sur les GPIOs pins 8 et 10, quel que soit le modèle du Raspberry.

Vous pouvez obtenir les informations de votre distribution à l’aide de la commande :

Ensuite, un prompt de login apparaît sur le port série après le démarrage. Ceci est paramétré dans le fichier /etc/inittab. Editez ce fichier :

et commentez la ligne :

Il ne reste plus qu’à redémarrer pour appliquer les modifications. Une fois redémarré, le port /dev/ttyAMA0 peut être utilisé comme un port série classique sans trafic parasitant le matériel connecté sur le port série. Après le redémarrage, on peut observer que la commande de démarrage du noyau ne comporte plus le paramètre « console=ttyAMA0,115200 » et que par conséquent, aucun processus n’utilise ttyAMA0 :

/etc/inittab n’existe plus depuis Raspbian Jessie. Inittab faisait partie de sysvinit qui a été remplacé par systemd. Serial getty est maintenant une service comme un autre.

Il faut donc stopper le service serial-Getty, voire même le désactiver pour empêcher son lancement au prochain redémarrage :

Développement du script Python

Les intérêts du langage Python

Il existe de nombreux langages de haut niveau (C, C++, C#, Java,…). Python est un puissant langage de programmation orienté objet. En tant que langage interprété, il est particulièrement adapté sur des systèmes embarqués tels que le Raspberry. D’ailleurs, l’environnement Python est nativement installé sur la distribution Raspbian. De syntaxe claire, Python est un excellent langage de programmation pour les débutants comme pour les experts. Très populaire, Python dispose de nombreuses bibliothèques (en plus des fonctions natives) permettant d’obtenir rapidement le résultat attendu en un minimum de lignes de code.

La bibliothèque Rpi.GPIO

Pour pouvoir configurer, lire et écrire sur les GPIOs du Raspberry depuis un script Python, il est nécessaire d’installer la librairie RPi.GPIO avec la commande :

La librairie installée, il suffira de l’importer dans le script Python à l’aide de la directive suivante :

Avec cet import, l’ensemble des fonctions définies dans la librairie sont accessible depuis le script.

La librairie HD44780.py

Le script Python HD44780.py présenté ci-dessous est issu de deux sources d’informations :

  1. le tutoriel du site www.raspberrypi-spy.co.uk détaillant l’utilisation de l’écran une bibliothèque en python ;
  2. un guide d’utilisation de l’écran LCD avec Python, qui m’a notamment permis de comprendre comment générer et charger des « custom symbol » dans la CGRAM.

Le module Python HD44780.py permet de définir les fonctions nécessaires pour utiliser l’écran LCD HD44780, à savoir :

  • la fonction send_string(message, line) qui permet d’afficher une message sur la ligne 1 ou 2 ;
  • l’ensemble des fonctions de « bas niveau » qui permettent d’initialiser les GPIOs, d’envoyer un octets de données (en deux fois un quartets) sur les 4 lignes DATA de l’écran, de pulser la ligne Enable pour cadencer la transmission des données ou encore pour initialiser l’écran LCD.

Les modules Python nécessaires pour utiliser l’imprimante

Pour pouvoir utiliser le port série, installer le module Python python-serial avec la commande :

Si vous souhaitez imprimer des images, le module python-imaging-tk est également nécessaire :

La librairie printer.py

Comme évoqué plus haut, le langage Python est très populaire est de nombreuses librairies sont disponibles. Bonne nouvelle ! Il existe un module pour exploiter l’imprimante. Vous pouvez la télécharger en clonant le répertoire git avec la commande :

Il est nécessaire d’effectuer quelques modifications/contrôles :

 

Vous pouvez utilisez la recherche dans l’éditeur nano avec « Ctrl+w » pour accéder rapidement aux paramètres SERIALPORT et BAUDRATE pour vérifier que :

  1. ligne 38 : le port série est correctement configuré : ‘/dev/ttyAMA0’ ;
  2. ligne 56 : la vitesse de transmission des données correspond à la vitesse de l’imprimante : 9 600 Bauds (ou 19 200 selon l’imprimante).

Il est possible de jouer sur la qualité de l’impression à l’aide de 3 paramètres :

  1. heatTime par défaut à 150 : temps de chauffe pour marquer le papier, plus le paramètre est élevé, plus l’impression sera dense et lente ;
  2. heatInterval par défaut à 2 : temps entre chaque impulsion de chauffe (plusieurs pour une même lignes), plus le paramètre est élevé, plus l’impression sera nette et lente ;
  3. heatingDots par défaut à 7 : quantité de points pour imprimer, plus le paramètre est élevé, plus l’impression sera rapide et la consommation de courant importante.

En explorant les différentes fonctions de cette librairie, vous découvrirez le potentiel de l’imprimante :

  • imprimer du texte selon deux tailles de polices (jusqu’à 32 caractères par ligne avec la font A et 40 avec la font B, plus petite), en gras, en inverse vidéo, alignement gauche/droite/centré/justifié, upside down et souligné ;
  • imprimer des images (largeur idéale : 384 pixels) ;
  • imprimer des codes barres.

Il existe d’autres fonctions comme la mise en veille, le test de présence de papier (pour cette fonction, la broche TX de l’imprimante doit être connectée).

A ce stade, vous pouvez tester l’envoi de données vers l’imprimante avec la commande :

Vous devriez obtenir l’impression d’un ticket démontrant les possibilités de l’imprimante.

Le script principal chucknorris.py

Comme l’indiquent les premières lignes, le code principal du script Python chucknorris.py comporte une série d’import parmi lesquels évidemment HD44780 et printer.

Je pense avoir suffisamment documenté les lignes de code pour qu’un lecteur parvienne à s’y retrouver sans difficultés. Chaque instruction ne sera pas commentée plus en détails. Des articles dédiées au développement Python viendront compléter les notions utilisées dans ce script.

Le scénario nominal

Pour avoir une vision d’ensemble du script, je vous propose la modélisation du workflow d’exécution du scénario nominal. La programmation parallèle est réalisée avec l’instanciation d’objet héritant de la classe Thread.

Workflow d'exécution du script Python
Workflow d’exécution du script Python (Cliquez pour agrandir)

Et voilà ! Je pense qu’on a fait le tour du sujet. Je suis bien entendu disponible pour répondre aux questions que vous voudrez bien me poser, alors laissez-moi un commentaire si des précisions vous sont nécessaires.

Ah ! J’oubliais… pour que le script soit lancé automatiquement à chaque démarrage de la machine, je vous invite à lire l’article sur le paramétrage pour exécuter un script au boot du système.

2 réflexions sur « Chuck Norris Machine : script Python et GPIOs »

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *