Sysdroid
Je propose ici de concevoir une carte qui, reliée à un Rapberry pi, permet de contrôler un ventilateur en fonction de la température du CPU, et d’afficher tout un tas d’information système (utilisation du CPU, de la RAM, de l’espace disque, température du CPU …) sous forme de barre sur une petite matrice de Leds 8*8 avec un bouton poussoir pour switcher l’affichage de l’écran. Un second bouton poussoir permet l’extinction du Rapsberry pi.
La raison pour laquelle j’ai opté pour afficher les informations sur une matrice de Leds, et non pas sur un écran LCD ou autre, est que je souhaitais réaliser un circuit le plus miniaturisé possible, qui utilise le moins de PINs du GPIO possible, le moins de ressources possibles, et sans utilisation du port I2C, afin qu’ils puissent être utilisés pour des projets qui fonctionnent en parallèle de cette carte.
Fonctionnalités
Un premier écran va afficher sous forme de 3 barres verticales (doublées) de 0 à 100%:
- l’utilisation globale des CPUs
- L’utilisation de la RAM
- La température du CPU entre 30°C (0%) et 80°C (100%)
Un second écran affiche l’utilisation des 4 CPUs
Un troisième écran affiche l’utilisation de l’espace disque (aucune led allumée = 0%, toutes les leds allumées = 100%)
Pour switcher d’un écran à l’autre: on appuie sur un petit bouton poussoir.
Pour éteindre le système: on appuie sur un autre bouton poussoir “OFF”
Enfin la carte permet de contrôler l’alimentation d’un ventilateur en fonction de la température du CPU. Un petit interrupteur à glissière permet de définir l’alimentation du ventilateur en 3.3V ou en 5V.
Circuit électronique
Conçu sous Kicad, je vais mettre en évidence 3 fonctionnalités que je présente par ordre de difficulté.
Les boutons poussoirs on/off
C’est le même principe que mon tutoriel piOff qui explique comment gérer l’extinction d’un Raspberry pi à l’aide d’un bouton poussoir. Le bouton poussoir qui commande l’affichage de la matrice de leds est associé au pin33, et celui qui commande l’extinction est relié au pin40: j’utilise donc 2 pins pour gérer mes 2 boutons poussoirs, et 4 résistances de 10kΩ.
Le contrôle d’un ventilateur
Le contrôle d’un ventilateur m’a fait hésiter entre 3 solutions dont 2 se sont avérées rédhibitoires. Je voulais absolument pouvoir contrôler la tension d’alimentation du ventilateur pour lui permettre de tourner moins vite et faire moins de bruit: le petit ventilateur que j’utilise sur mon rpi3 est inaudible en 3.3V et bruyant en 5V.
La première solution que j’ai trouvée sur internet pour faire diminuer le voltage d’un ventilateur, c’est de lui ajouter une résistance en série. Un ventilateur c’est un moteur à courant continu, donc pour tourner il exige une certaine puissance en dessous de laquelle il ne démarre pas. Ajouter une résistance en série consiste à faire absorber par la résistance la moitié de la puissance par exemple, l’autre moitié fera tourner le ventilateur à bas régime. Le problème c’est qu’il faut une toute petite résistance de l’ordre de 40Ω à peine et qu’elle va dissiper toute la puissance absorbée sous forme de chaleur, tellement de chaleur qu’elle devient vite bouillante au toucher et qu’une résistance 1/4W ne suffit pas: il faut facile des 1/2W minimum. Je trouve l’idée plutôt ridicule en fait: on veut activer un ventilateur pour évacuer la chaleur du proc, mais pour éviter qu’il ne tourne trop vite on va faire “bouillir” une résistance en série qui elle même dégage beaucoup de chaleur … Solution à proscrire.
La seconde solution consiste à utiliser un signal de type pulsations à fréquence carrée (on parle de signal PWM = Pulse-With-Modulation) qui alimente le collecteur d’un transistor comme sur le schéma ci-dessus. Les rpi peuvent facilement générer de tels signaux et c’est un bon moyen pour simuler une tension modulée par la largeur des pulsions. Le principal problème avec cette technique, qui fonctionne parfaitement bien pour moduler la vitesse du ventilateur soit-dit en passant, c’est qu’elle génère un bruit de sifflement très désagréable dans le ventilateur … Plus la fréquence est haute, plus le signal génère un sifflement aiguë (insupportable au delà de 1000hz), et plus il est faible et plus on entend des toc-toc-toc. J’ai eu beau faire des essais dans tous les sens: c’est insupportable….
Troisième solution: on oublie la modulation par pulsation et on utilise un petit bouton à glissière qui permet de switcher l’alimentation entre le +3.3V et le 5V. Le ventilateur est commandé par un transistor dont le collecteur est piloté par un pin du rpi via une résistance de protection de 1kΩ. On peut ajouter une petite led 3mm en parallèle (avec une résistance de 470Ω) du ventilateur, et elle s’allumera donc si le ventilateur fonctionne. En fonction du voltage choisi (3.3v ou 5v) la led va s’éclairer plus ou moins fort: c’est la solution que j’ai retenue, qui nécessite donc 1 pin du rpi (pin 31), 2 résistances (1kΩ et 470Ω), 1 transistor NPN, une petite led 3mm et un petit bouton à glissière 3pin de type ON-ON. Pour brancher le ventilateur sur la carte, il suffit de prévoir un connecteur 2pin mâle. Le pgm python va ensuite commander le pin31 du Raspberry défini en mode OUT, pour le mettre HIGH ou LOW en fonction de la température du CPU.
L’affichage sur une matrice de Leds 8*8
Attention là c’est de la haute voltige… Il faut d’abord comprendre le fonctionnement d’une matrice de leds, les tutoriels de Freenove sont fabuleux pour bien comprendre. Cette petite bestiole est composée de 2 rangées de 8 pins l’une en face de l’autre. Les pins sont numérotés PIN1 à PIN8 de gauche à droite sur la rangée du bas (là où l’on peut voir un label, un peu effacé sur mon modèle), et PIN9 à PIN16 de l’autre côté en sens inverse (le PIN1 est en face du PIN16, le PIN2 est en face du PIN15, …, PIN8 en face du PIN9).
Chaque Led est couplée à une ligne et une colonne. Le type de matrice que j’utilise a un circuit interne dit “Common Anode” qui signifie que les anodes des leds sont connectées aux lignes, tandis que les cathodes sont connectées aux colonnes. Pour allumer une led, il faut donc relier sa colonne “c” à la masse (ou LOW) et sa ligne “l” au +3.3v (ou HIGH) sans oublier d’ajouter une résistance de protection de 220Ω sans quoi vous détruiriez la led. On pourrait espérer que les pin 1 à 8 sont les lignes et les pin 9 à 16 sont les colonnes mais se serait trop simple: vous avez dans le schéma ci-dessus les correspondances entre les PIN, les lignes et les colonnes. Par exemple pour allumer la 3ème led (en comptant 1 à partir de la gauche) de la 1ère ligne (qui est celle du haut): il faut mettre le +3.3v/HIGH sur le pin9 et la masse/LOW sur le pin4, le tout avec une résistance 220Ω en série. Pour l’éteindre, il faut remettre un signal LOW sur le pin9 .
Mais alors si je veux allumer cette 3ème led de la 1ère ligne et éteindre la 4ème leds de la même ligne, en même temps: comment je fais vu que dans un cas il me faut mettre HIGH sur le pin9, et dans l’autre LOW sur le pin9 ???
Pour contourner ce problème on ne va pas activer toutes les colonnes en même temps: seule une colonne n’est activée à un instant t. De ce fait, seules 8 leds d’une seule colonne peuvent être allumées en même temps. Pour donner l’illusion que les 64 leds sont toutes allumées en même temps: on active la première colonne (signal LOW sur la colonne: les leds qui seront allumées sur cette colonne seront celles qui ont leur signal HIGH au niveau des lignes), puis on l’éteint (signal HIGH sur la colonne: toutes les leds de la colonne seront éteintes, peu importe l’état HIGH ou LOW de chaque ligne), et on active ensuite la seconde colonne, et ainsi de suite à haute fréquence: la persistance rétinienne de l’œil humain va donner l’illusion qu’elles sont allumées toutes en même temps. La consommation électrique s’en retrouve réduite à l’alimentation de 8 leds maxi en même temps au lieu de 64.
Faire un dessin sur une matrice de 8*8 leds consiste donc à allumer/éteindre au maximum 8 leds de chaque colonne qui sont activées/désactivées l’une après l’autre à tour de rôle, à haute fréquence.
Convertisseur série–>parallèle 8bits
Si je ne vous ai pas perdu, maintenant vous comprenez qu’il faut pouvoir avec le rpi commander l’activation / désactivation de 8 colonnes et de 8 lignes pour allumer les leds de la matrice. A priori on peut s’en sortir avec 16 pins du GPIO, mais le cahier des charges c’est d’utiliser le minimum de pin possible. C’est ici qu’un très utile convertisseur série–>parallèle 74HC595 (je n’invente rien, on trouve cela dans tous les bons manuels d’électronique et tutos) entre en jeux: son rôle est de transformer un code binaire de 8 bits envoyés en série depuis un pin (une série de 1 et de 0 envoyés les un après les autres) en 1 code binaire de 8 bits parallèles: et BAM avec 1 pin je créé 1 code binaire sur 8 bits qui va me servir à générer mes codes binaires 0/1 (LOW/HIGH) à destination de mes lignes et de mes colonnes. En fait il faut un peu plus que 1 pin puisque 2 autres vont être nécessaires pour jouer le rôle de l’horloge et le rôle de validation, explications:
Le 74HC595 est un circuit-intégré 16 broches dont les pin sont :
- GND et VCC: alimentation (3.3V ou 5V selon la brillance des leds souhaitée: j’ai opté pour du 3.3V)
- QA à QH: sorties binaires 8 bits.
- SER: entrée série c’est ici qu’on envoie des 1 et des 0 en série.
- OE: Output Enable: si HIGH, alors les sorties QA à QH sont toutes en HIGH, sinon elles sont définies en mode output: je vais donc relier ce pin à la masse dans mon circuit.
- RCLK: à chaque fois que ce pin bascule à l’état HIGH la sortie parallèle QA à QH est mise à jour, on parle de “latch_pin”.
- SRCLK: à chaque fois que ce pin bascule à l’état HIGH il va pousser l’entrée SER vers la sortie parallèle qui est décalée d’un bit vers la droite (on appelle cela “shifter” la sortie). Ce pin joue le rôle d’horloge, on l’appelle aussi “clock_pin”.
- SRCLR: la sortie parallèle est mise à zéro quand ce pin est LOW. N’ayant pas besoin de cette fonctionnalité, je vais relier en dur ce pin au +3.3V sur mon circuit.
- QH’: quand ce pin est relié à l’entrée SER d’un autre 74HC595 cela permet de générer un code 16 bits. On peut donc les associer en série pour générer des codes binaires de 16, 24 ou 32 bits etc ….
Voilà pourquoi il faut utiliser 2 circuits 74HC595 pour contrôler les 8 lignes + 8 colonnes d’une matrice de 64 leds. On parle de montage série à 2 étages. Ils n’utiliseront que 3 pin du rpi: un premier (pin 11) qui va envoyer la série de HIGH/LOW au pin “SER” du premier étage 74HC595 , un second (pin 13) qui va jouer le rôle de validation “latch_pin”, et un troisième (pin 15) qui va jouer le rôle d’horloge “clok_pin”. Ces deux derniers sont utilisés pour les 2 circuits 74HC595.
Le programme python consiste alors à définir dans un premier temps le dessin qu’on veut afficher avec 8 codes binaires ou hexadécimaux qui représentent les 1 et les 0 à afficher sur chaque colonne. Dans un premier temps on va “pousser” le code d’une colonne au premier étage en activant 8 fois le clock_pin, et ensuite on va pousser le code correspondant à l’activation de la colonne de la même façon (rappel: LOW sur celle colonne et HIGH sur toutes les autres) ce qui va pousser les 8 premiers bits envoyés vers le second étage. Les sorties parallèles du 1er étage sont donc reliées aux colonnes de la matrice de leds, tandis que celles du 2nd étage sont reliées aux lignes. Les signaux sont alors “confirmés” avec le “latch_pin” pour générer les 2 codes binaires parallèles, et miracle une colonne de la led s’allume avec le bon dessin. Ensuite on passe à la colonne suivante avec un nouveau code pour les lignes sans tarder, jusqu’à recommencer depuis la première et recommencer à l’infini.
Puisque la 1ère ligne de la matrice est celle du haut, pour se simplifier la vie on va disposer la matrice de leds à l’envers 1ère ligne vers le bas donc (les pin 9 à 15) et considérer ainsi que le code hexadécimal 0x01 (= 0000 0001) va allumer la 1ère led ainsi en bas, et 0x0F (= 1000 0000) la led en haut. le code 0xFF (= 1111 1111) va allumer toutes les leds d’une colonne. Avec de tels codes héxa on peut dessiner à peut prêt tout ce qu’on veut en fait limité à une définition de 8*8 pixels: c’est largement suffisant pour représenter mes barres de 0 à 100% par paliers donc de 1/8.
Conclusion pour alimenter la matrice de 8*8 Leds: 3 pin du rpi suffisent (pin 11, 13 et 15), couplés à 2 étages de 74HC595 et 8 résistances de 220Ω. J’utilise 2 autres pin pour mes 2 boutons poussoirs (pin 33 et 40), et encore un autre (pin 31)pour contrôler le ventilateur: 6 pin du rpi au total sont utilisés par cette carte.
Matériel nécessaire
- Une matrice de leds standards 8*8 anode commune (taille 20mm) + 2 barrettes simple rangée de 8 pins femelle, pas 2,54 mm
- 2 circuits intégrés 74HC595 + 2 supports 16 pin
- 8 résistances 220Ω (elles sont planquées sous la matrice de Leds)
- 4 résistance 10kΩ + 1 de 1kΩ + 1 de 470Ω
- 2 boutons poussoirs 6*6 mm
- 1 led verte 3mm
- 1 transistor NPN type T092
- 1 interrupteur miniature à glissière ON-ON 3 pin (8mm de long max), pas 2,54mm
- 1 connecteur 2 pins mâle coudé (pour y brancher le ventilateur)
- 1 connecteur double rangées 2*20 pin et une nappe 40 broches GPIO pour Raspberry
- 4 petites entretoises + boulons en plastique taille 2,5 mm.
- Un circuit imprimé (taille mini 5 cm x 5.5 cm !) à commander à partir des fichiers GERBER fourni sur ma page Github.
Assemblage du circuit
Il faut commencer par souder toutes les résistances: les 8 résistances de 220Ω sous le support de matrice de Leds (R6, R7, R8, R3, R5, R4, R9, R10), puis les 4 de 10kΩ (R1, R2, R11, R13), celle de 1kΩ (R12) à côté du transistor et celle de 470Ω à côté de l’interrupteur à glissière
Souder ensuite les petits boutons poussoir (attention au sens car ils sont carré ! il faut que l’interrupteur relie les deux broches du bas vers les 2 du haut: à tester avec un ampèremètre avant de souder), la led (patte la plus longue à droite: celle qui est connectée à la résistance de 470Ω), le transistor (dans le bon sens: méplat vers le support GPIO 2*20pin)
Souder ensuite le petit interrupteur à glissière et le connecteur coudé 2 broches.
Pour souder les 2 supports de circuit intégrés il y a une petite “galère” à gérer (voir sur la 3ème photo): en effet j’ai disposé un trou d’entretoise trop proche d’un bord du support qui tombe pile dessus! Ce n’est pas grave il suffit de sectionner ce bord en plastique avec une pince coupante, et de bien limer car l’écrou en plastique rentre de justesse (il faudra aussi le limer un peu). Si l’autre bord du circuit intégré casse (ça m’est arrivé) ce n’est pas grave, ça n’empêchera pas de le souder correctement. Avant de le souder il faut s’assurer que l’écrou de l’entretoise en plastique se loge bien sinon il faut la limer un peu (pas directement sur le PCB hein ?? sinon vous détruiriez les pistes): une simple lime à ongles suffit. Quand le support entre bien dans les trous prévus avec l’entretoise vissée sur son écrou: hop vous pouvez souder ce support. Il faut ensuite sectionner à ras de l’écrou la vis en plastique qui dépasse.
Il reste à souder les 2 barrettes 8 pins femelles qui servent de support pour la matrice de leds, puis a double rangée 2*20 pins mâle pour le GPIO du Raspberry. Vous pouvez alors disposer les 2 74HC95 et la matrice de leds sur son support au dessus des résistances: le circuit est opérationnel!
Programmes Python3
programmes
3 programmes python3 permettent la lecture et l’affichage des informations sur la matrice de led ainsi que la gestion des boutons poussoirs.
sysdroid.py: ce programme est composé de deux classes Sysdroid et Readsys
-
la classe Readsys est un thread qui va lire toutes les 30 secondes toutes les informations à afficher (CPU, RAM, espace disque, température CPU) et va préparer en conséquence tous les écrans d’affichage sous forme de codes hexadécimaux correspondant aux niveaux 0 à 100% par palliers de 1/8ème.
-
La classe Sysdroid est aussi un thread qui va instancier une classe Readsys, et gérer les boutons poussoirs, gérer l’activation/désactivation du ventilateur, afficher le bon écran sur la matrice de Leds. Des courts messages informatifs sont scrollés sur la matrice de leds avant affichage d’un écran.
sysdroid_msg.py: ce programme est composé d’une seule classe ScrollMsg qui prépare des messages textes pouvant être scrollés sur la matrice de leds. Ces messages sont préparés à partir d’un dictionnaire où chaque lettre alphabétique est associé aux codes binaires à envoyer à la matrice en définition 5*3 chaque caractère.
sysdroid_main.py: il s’agit de l’application principale à exécuter pour faire démarrer le système. Elle est composée d’une classe Application qui prend comme paramètre la température minimale et maximale qui contrôle l’arrêt/relance du ventilateur (par défaut 40°C et 50°C) , la vitesse de scrolling des messages et un paramètre qui active/désactive les prints (par défaut désactivés)
Il y a plusieurs façons d’exécuter ce programme selon le cas de figure.
-
Si vous l’utilisez sans exploiter le GPIO, rien de plus simple il suffit de relier la carte au rpi avec un connecteur souple, et d’exécuter le programme python sysdroid_main.py.
-
Si en revanche vous souhaitez exploiter le GPIO (sachant que les pin 11, 13, 15, 31, 33 et 40 sont déjà utilisés) il faut juste utiliser un réplicateur GPIO, et créer votre propre programme en partant du modèle sysdroid_main.py qui instancie la classe Sysdroid. Dans la boucle loop c’est là qu’il faut ajouter votre code et supprimer le time.Sleep(1) qui met en veille la boucle pendant 1 seconde (sinon une boucle infinie sans rien faire va saturer un processeur pour rien)
Quelques exemple pour lancer le Sysdroid:
- appl=Application() # options par défaut
- appl=Application(verbose=True) # active les print à l’écran
- appl=Application(tFanMin=44, tFanMax=60) # T° mini et maxi de contrôle du ventilateur différentes de celles par défaut
téléchargement des programmes
vous pouvez télécharger les 3 programmes, ainsi que les fichiers GERBER nécessaires à la conception du PCB sur ma page Github