Par autonome, il faut comprendre que l’immobilisation d’un ordinateur de bureau ou d’un P.C. portable ne sera plus nécessaire. Par contre, pour ce prototype il n’est pas envisagé une indépendance totale, car vu la consommation du capteur j’ai écarté un dispositif qui intègrerait une batterie rechargeable pour des raisons de coût. On se contentera de brancher l’appareil sur le secteur au moyen d’un petit adaptateur USB et le tout est joué. Comme nous nous « amusons » avec de la programmation Arduino, je vais vous proposer pas moins de quatre versions simples, et enfin la version de luxe présentée dans un coffret et utilisant un afficheur graphique.
La version ultra simple à LEDs.
Coté investissement on peut difficilement faire moins onéreux. L’information relative au niveau de pollution sera indiquée sous forme d’une rampe lumineuse. Naturellement il serait envisageable de faire au strictement plus économique sans le capteur météorologique, mais nous allons directement passer à des versions opérationnelles munies du DHT11. Dans les divers exemples qui vont suivre, nous allons juste faire varier le dispositif d’affichage du résultat. Le but de ces expériences est bien entendu de se promener dans la gestion des périphériques bien adaptés à Arduino et surtout de passer en revue certaines techniques classiques pour le développement électronique et la mise au point logicielle.
Avant de passer au développement des démonstrateurs qui illustrent ce propos, nous devons définir le matériel, c’est à dire les composants électroniques couplés au microcontrôleur, et surtout à la répartition des broches d’Entrées/Sorties, phase cruciale lors de l’élaboration d’un projet, aussi simple que celui-ci fut-il. Il faut affecter les broches en pensant « au futur ». En effet, on va « greffer » sur notre platine d’expérimentation divers périphériques, puis écrire les lignes de codes pour les mettre en Å“uvre. Si l’on choisit judicieusement notre répartition et que ces broches restent réutilisables, il sera alors bien plus simple de récupérer directement les séquences spécifiques. Aussi nous allons dans un premier temps définir le projet, puis répartir les broches et justifier les affectations. Cette approche est très générale et doit plus ou moins se retrouver pour toutes nos réalisations ludiques. (Ou professionnelles !) L’idée de base consiste à utiliser une rampe de diodes électroluminescentes telle que celle de la Fig.17 sous la forme ici d’un petit module que j’utilisais à l’époque où je programmais en « ASSEMBLEUR ». À cette période lointaine les LEDs n’avaient absolument pas les rendements de leurs semblables actuels, ce qui explique que sur ce module les
résistances de limitation du courant font 390Ω. Avec les composants actuels on peut aisément intercaler plusieurs kilo-ohms sans problème. Aussi, sur le schéma de la Fig.19 je n’indique pas les valeurs des résistances R, vous déterminerez ces dernières en fonction des composants que vous aurez approvisionnés. Le projet pour cette version est globalement représenté sur la Fig.18 et précise que la rampe lumineuse verticale pourra aussi-bien visualiser le taux de C02 que la température ou le taux d’hygrométrie. Pouvoir visualiser ces trois paramètres avec une seule rampe lumineuse de visualisation implique de facto deux impératifs :
• Disposer d’un moyen signalant la nature de l’information en cours de visualisation : Notre choix se fait sur une LED tricolore par exemple.
• Pouvoir manuellement sélectionner l’affichage du CO2, de la température ou de l’hygrométrie. Un simple bouton poussoir convient.
Affectation de Entrées/Sorties de la carte Arduino NANO.
L’attribution des E/S d’une carte microcontrôleur est une phase cruciale qui va induire des conséquences importante pour toute la suite du projet. Si les choix initiaux sont judicieux, nous allons gagner un temps considérable lors de l’élaboration du programme d’exploitation. Aussi, sans être exhaustif, on peut déjà citer plusieurs critères de choix :

Justification du choix des affectations des broches d’E/S.
Priorité oblige, la bibliothèque de gestion du capteur de CO2 impose l’entré analogique A2 pour gérer le MQ135. Nous n’avons pas vraiment le choix. Même critère pour le capteur météorologique qui réclame D2 pour dialoguer avec le DHT11. Sachant qu’en version ultime on prévoit un afficheur graphique OLED, on laisse disponible A4 et A5. Un petit bruiteur passif est envisagé, D11 est alors employée pour trois raisons. D’une part j’ai cherché à utiliser des broches voisines pour les huit LEDs de la rampe lumineuse. (Mon expérience en programmation tend à croire que généralement ça facilite l’utilisation des opérateurs logiques.) La deuxième raison c’est que souvent j’utilise ce type de bruiteur, ainsi le dessin du circuit imprimé en sera facilité. Enfin D11 est une sortie binaire pouvant générer de la PWM. Les huit broches pour piloter la rampe de LEDs sont affectées dans l’ordre des couleurs, présumant du fait que ce choix facilitera l’étude du circuit imprimé. (Hypothèse à confirmer …) Pour la LED triple, A0, A1 et A3 sont munies de résistances individuelles. Ainsi il sera facile d’en « doser » leurs luminosités, et surtout il sera possible en fin d’études d’opter pour trois témoins indépendants. Ce choix final sera influencé par la répartition des éléments sur le pupitre de pilotage, sachant qu’une LED triple permet des couleurs tel que le blanc ou le violet infaisable sur trois éléments séparés. Ces trois broches d’entrées analogiques sont affectées au témoin triple car elles peuvent aussi fonctionner e
n sorties binaires. Il ne reste plus qu’à ajouter une petite plaque à essais et d’effectuer comme montré sur la Fig.20 les divers branchements avant de passer à l’aspect programmation. Noter que A7 ne pouvant fonctionner qu’en entrée analogique sera précieuse plus avant pour imposer manuellement des valeurs à la place des capteur et ainsi détailler une technique possible de vérification d’un programme.
Concrétiser le schéma de la Fig.19 consiste à récupérer dans mes outils de développement la petite platine numérotée 11 sur la Fig.20 dont seul le bouton poussoir 8 est utilisé. Le B.P. et le micro-Switch 12 sont ignorés. On retrouve en 1 la platine d’essai de la Fig.2 avec bien dégagé le capteur de CO2 en 6 et caché en 3 le capteur météorologique. En 2 non mentionnée sur la Fig.19 la LED rouge en parallèle sur la LED Arduino de D13 n’a pas été enlevée. (On a ainsi conservé les branchements donnés en Fig.12) On retrouve en 4 la carte Arduino NANO embrochée sur la plaque 1. Dominant le paquet de fils et reposant sur ces derniers, se trouve en 5 le module à LEDs de la Fig.17 bien dégagé. La plaquette 10 a été ajoutée pour supporter en 7 le petit bruiteur passif, et en 9 la LED triple et ses trois résistances R de 10kΩ peu visibles sur la photographie qui servent à limiter le courant. Noter que c’est sur 10 que se trouve la résistance de polarisation du bouton poussoir 8. (De valeur 1kΩ volontairement faible pour minimiser les risques de parasitage.) Le décor est en place, il ne reste plus qu’à écrire le programme de vérification de cette « circuiterie ».
Programmer avec méthode : Commencer par vérifier le matériel.
Lorsque l’on va développer le programme d’exploitation de cette version du détecteur de CO2 on ne veut pas risquer des pertes de temps à rechercher une erreur logicielle, alors que l’un (Ou plusieurs !) des composants à été mal branché. Aussi, concevoir un programme qui permet de vérifier rapidement l’ensemble du câblage est à mon sens un incontournable. Invoquer « avec méthode » commence par choisir des identificateurs « évidents », la première qualité d’un logiciel quel qu’il soit est sa LISIBILITÉ. Envisager que votre « production » doit être compréhensible par « tous », et surtout par vous si vous devez reprendre l’ouvrage quelques semaines ou quelques années plus tard. Un skech comportera inévitablement des calculs non évidents, ou des astuces d’optimisation. Dans ce cas il sera primordial de les expliciter avec des commentaires. Éviter autant que possible les séquences très longues imposant des défilements du listage sur l’écran de l’ordinateur. Idéalement, une procédure ou une fonction ne devrait « jamais » dépasser en longueur la possibilité d’affichage de l’écran pour avoir toutes les instructions simultanément visibles. Pour illustrer ces conseils nous allons utiliser le démonstrateur P04_Test_materiel_V1.ino dont je ne vous explique pas à quoi servent les diverses procédures … sauf la première qui est particulière et inclus une technique visuelle de mise au point très utile en programmation.
Bien que ce ne soit pas une obligation du tout, il est fortement recommandé de placer toute fonction ou procédure avant celle qui y fait appel. Ainsi, en déverminage, on sait que si une séquence est invoquée, il faut la rechercher en amont. Cette recommandation qui est une obligation en langage PASCAL par exemple fait gagner un temps fou quand on dévermine ou quand on améliore un programme « long » c’est à dire incluant des centaines d’instructions.
Un petit Cœur logiciel qui bat discrètement.
Stéthoscope à l’oreille, le programmeur doit pouvoir vérifier à tout moment que le processeur ne tombe pas dans une séquence trop longue ou pire, dans une boucle infinie. Logiquement, sauf cas particulier d’une action ponctuelle comme l’attente d’un clic sur un B.P, la boucle de base doit « tourner » sans fin et en général à grande vitesse pour rafraichir le résultat des traitements sur des périphériques quelconques.
Par ailleurs, une bonne pratique consiste à mesurer le temps d’un cycle dans void loop() pour vérifier la rapidité du « frame rate ».
Supposons que l’on désire voir « battre le petit cÅ“ur » une fois par demi-seconde. La première solution qui vient à l’esprit consiste comme sur l’organigramme de la Fig.21 à placer dans la boucle de base une instruction de type delay(T) avec T = 0.5S – la durée des autres traitements. Bien qu’élémentaire, cette approche est franchement à proscrire car durant T le programme ne fait rien et les affichages sont inertes. c’est précisément pour résoudre facile
ment ce type de problème que le microcontrôleur ATmega328 contient en interne un compteur binaire qui s’incrémente régulièrement et qui par l’instruction millis() retourne la valeur du temps qui s’est écoulé depuis le dernier RESET. La valeur retournée est en millisecondes. Ce compteur a forcément une taille limitée et de ce fait recycle au bout de 49 jours si aucun RESET n’intervient durant cette période. La fonction millis() utilise le TIMER 0 qui de ce fait devient indisponible pour d’autres usages. Considérons le démonstrateur P04_Test_materiel_V1.ino dont la première instruction fait appel à une procédure facile à analyser. On se contente de vérifier si depuis le dernier « battement de cÅ“ur » il s’est écoulé le délai désiré. Si OUI on inverser D13 et on réarme un nouveau délai, dans le cas contraire on ne fait rien. Le test sur les durées est très rapide et ne ralentit pas la boucle de base. Par contre, dans notre cas on constate que dans le « battement du cÅ“ur » on insère une temporisation de 10mS car dans le cas contraire l’impulsion sur le bruiteur est trop rapide et le « tic /tac » est peu audible.
ATTENTION : La durée de prise en compte (Ici une demi-seconde) ne sera respectée que si les temps d’exécution des autres instructions de la boucle sont constants et de faibles durées. Hors ici pour avoir le temps d’observer l’éclairage de toutes les LEDs on introduit dans la boucle onze temporisations qui cumulent 850mS. Une mesure de la période entre Tic et Tac indique 0,862S et nous sommes loin des 0,5S programmées. C’est donc dans les temporisations de type delay(T) que l’ATmega328 se tourne les pouces pendant les 850mS et ne travaille effectivement dans du traitement binaire que durant 12mS ! Nous sommes dans un exemple typique de très mauvaise utilisation d’un microprocesseur. Heureusement qu’ici on ne fait que vérifier le matériel.
La gestion du bouton poussoir et l’antiparasitage.
Bien que dans ce démonstrateur on ne va prendre en compte qu’un seul bouton poussoir, ce thème reste directement utilisable pour plusieurs touches ou un clavier multiplexé. La première source de parasitage dans la lecture d’une entrée analogique vient avec le « bruit alimentation ». Considérons la Fig.22 qui montre l’évolution de la tension alimentation au cours de temps. En théorie l’adaptateur secteur fournit un +5V bien filtré et continu représenté par le trait rouge horizontal. Dans la réalité, à cette tension continue se superposent des variations très rapides que les techniciens nomment « herbe » ou « bruit blanc ». Sur la Fig.22 cette perturbation est considérablement exagérée. (Ou alors l’adaptateur secteur doit être mis à la poubelle !) Même si l’alimentation est parfaite, le +5V du bloc USB fournit la polarisation par la ligne électrique dans laquelle se trouve la résistance de 1kΩ. Cette ligne dans un environnement
électromagnétique très encombré capte les parasites hertziens et sera forcément affectée par moment de petites impulsions parasites. Plus la résistance sera de valeur faible, moins ces phénomènes seront présents. Dans le schéma de la Fig.19, l’action a pour effet de faire un court-circuit entre A6 et GND. Donc quand on cliquera sur le bouton poussoir, A6 se retrouvera au potentiel nul. Pour minimiser l’influence des parasites captés par la ligne de polarisation, il suffit de prendre comme seuil de décision la moitié de la tension alimentation. Tout ce qui est au dessus de 2,5V sera considéré comme un état « 1 » et tout ce qui est en dessous comme un état logique « 0« . On a réglé le problème de « l’herbe » mais il reste à éliminer les rebonds du contact électrique. Quand on bascule la mécanique d’un inverseur ou celle d’un bouton poussoir, on engendre le mouvement de pièces métalliques qui viennent en contact. Électriquement on génère un état « 0 » sur une entrée analogique ou binaire. On imagine assez bien une séquence telle que celle représentée par l’organigramme de la Fig.23 pour traiter ce périphérique associé au dialogue Homme/Machine. Et bien cette séquence est pratiquement
inutilisable. En effet, lorsque deux pièces mécaniques sont « bousculées » l’une contre l’autre, il y a des rebonds mécaniques et l’évolution de l’état logique sur l’entrée concernée ressemble à celle de la Fig.24 correspondant à un élément de très bonne qualité. Ici, le contact rebondit quatre fois lorsque l’élément est activé, les transitoires verts sont représentés par des « durées nulles » car ils se font très rapidement. Si on ne tient pas compte de ce phénomène, alors le logiciel va croire que l’action sur le composant a été déclenchée cinq fois et les traiter en conséquence. Dans la pratique, un interrupteur de qualité va présenter entre dix et vingt « ricochets ». Un composant médiocre peut en générer jusqu’à deux cents voir plus ! Aussi, il
faut systématiquement faire de l’anti-rebonds. Parmi les techniques ordinaires, on peut adopter celle de la Fig.25 dans laquelle le traitement est effectué après le relâcher du bouton poussoir. Mais il est aussi intéressant d’effectuer le traitement dès que l’état logique « 0 » est stabilisé, et ne se préoccuper du relâcher qu’après. Cette deuxième approche n’est en général pas plus pénalisante en taille de programme, et peut faire gagner un peu de temps en exécution si le composant met du temps à se stabiliser car ce délai sera en partie « masqué » par la durée du traitement. Cet organigramme n’explique pas comment se fait l’anti-rebonds. En analysant le code source, on constate que l’on utilise un COMPTEUR. Chaque fois qu’une mesure signale un état « 0 » il est incrémenté. Si durant le comptage on constate un état « 1 » ,c’est qu’il y a rebond et le COMPTEUR est remis à zéro. Il faut atteindre un nombre de lecture stables NB_tests_antirebonds fois pour considérer que le contact électrique est stabilisé. Pour attendre le relâcher stabilisé, on procède de façon strictement identique. La valeur de NB_tests_antirebonds est trouvée expérimentalement. Plus le composant est médiocre, plus il faut l’augmenter au détriment naturellement du temps pris par le traitement des « ricochets ». On peut « juguler » un peu ces faux contacts en plaçant un condensateur entre l’entrée logique et GND mais je ne le fais que dans des cas particuliers car ce sont des composants en plus à ajouter à la circuiterie électronique.
L’optimisation logicielle.
Philosophiquement, je considère qu’un petit démonstrateur comme le précédent n’est pas vraiment un programme. C’est avec ses 2000 Octets un petit « bricolage ». (Même si pour le développer je me suis cogné à des séquences pouvant se montrer réticentes de façon agassive.) À mon sens, je parle de vrai programme quand il va falloir aligner on nombre considérable d’instructions, et que le code finira par saturer la mémoire disponible et qu’il faudra chercher désespérément de la place. Ce n’est pas quand le bateau coule qu’il faut apprendre à nager. Aussi, y compris pour des tout petits démonstrateurs, il faut optimiser l’optimalisation des séquences déjà ultra condensées. En résumé, optimiser le code est plus qu’un réflexe de programmeur et cette facette doit virer à l’obsession.
En fonction de la séquence en cours de rédaction, le programmeur peut vouloir :
• Minimiser la taille du code objet qui encombrera la mémoire,
• Minimiser le temps d’exécution d’une séquence car c’est un critère prioritaire dans le contexte.
Considérons la procédure void Eteindre_la_rampe_lumineuse() du démonstrateur P04_Test_materiel_V1.ino qui à la compilation engendre un code objet d’exactement 2000 Octets. Transformer cette procédure en remarques et validez celle de même identificateur qui se trouve juste au dessus. Cette fois le résultat à téléverser passe à 2018 cellules mémoire occupées soit comme précisé une taille augmentée de dix huit Octets.
Conclusion : La séquence qui consiste à forcer une à une les huit sorties à « 0 » semble plus « naturelle ». Mais en remplaçant ces huit instructions par deux boucles, on optimise manifestement la taille du programme. ATTENTION : Traiter individuellement huit affectations d’état de sortie est plus rapide que d’utiliser des boucles qui doivent créer un compteur local et le gérer. Pour s’en convaincre, on commence par remplacer le contenu de la boucle de base par :

La sortie LED 13 permet d’avoir un signal qui autorise la mesure précise de la fréquence et de la période de l’exécution de void loop(). On trouve respectivement 8764Hz et 114µS. Puis on modifie cette boucle de base par :
La Fréquence de l’exécution de void loop() diminue à 8492Hz et 118µS. Il est évident que ce n’est pas catastrophique, mais il ne faut surtout jamais perdre de vue que les boucles de type for sont consommatrice de temps, surtout si le nombre de « rotations » est très important.
Utilisation de la PWM ou de l’instruction tone ?
Quitter le programme SANS LE SAUVEGARDER et téléverser une fois de plus le démonstrateur P04_Test_materiel_V1.ino pour retrouver le logiciel initial. Considérons alors la dernière procédure dans laquelle on repère immédiatement de longues lignes de remarques du genre
//@@@@@@@@@@@@@@@@. C’est un artifice pour attirer immédiatement le regard dans un listage. J’utilise systématiquement cette sorte d’étiquette dans mes programmes pour des lignes de code dont on est amené à changer « régulièrement » le contenu. Par exemple en tête de programme on trouve un texte qui précise la date de validation d’un logiciel. Si plus tard on le modifie, pour mettre à jour cette référence il faut la retrouver facilement. Lorsque vous compilez P04 la taille du programme fait exactement 2000 Octets. Dans cette version, lorsque l’on clique sur le B.P. on entend un son dont on ne peut pas modifier la fréquence de répétition imposée à 490 Hz avec une période T ≈ 2042 µS pour PWM D3, D9, D10 et D11. Pour les sorties PWM D5 et D6 la fréquence de répétition est de 976 Hz avec une période T ≈ 1025 µS. (Pulse Width Modulation) Dans une instruction telle que analogWrite(Sortie_PWM, N) le rapport cyclique est directement proportionnel à la valeur N passée en paramètre et Sortie_PWM fait référence à la broche de sortie utilisée. N impose la durée à l’état « 1« , donc influence directement le rapport cyclique.
![]()
Étant limité à deux tonalités au maximum, il est naturel de chercher à enrichir les possibilités musicales du bruiteur installé. C’est précisément la finalité de l’instruction tone dont la mise en Å“uvre est particulièrement conviviale. Pour s’en rendre facilement compte, dans la procédure void Traite_activation_du_BP() transformer les deux lignes qui gèrent la PWM en remarques, puis valider les deux instructions qui se servent des procédures tone et notone. Ces lignes sont très faciles à repérer car elles sont repérées avec des //@@@@@@@@@@@@@@@@. Maintenant quand on clique sur le B.P. la note générée est bien plus efficace. La fréquence générée de 3000Hz a été choisie car elle n’est ni trop grave ni trop aigüe, toutefois amusez-vous à proposer d’autres tonalités. On ne peut rêver plus simple pour « faire de la musique ». Toutefois, la taille du programme augmente de 1326 Octets d’un coup et 21 Octets de plus dans les données dynamiques.
Conclusion : Générer une tonalité musicale peut se faire de plusieurs façons. Utiliser de la PWM est parfait pour gérer la luminosité d’une LED par exemple en modifiant le rapport cyclique. Par contre, pour créer une note musicale on est limité à une seule fréquence relativement basse. Si on désire n’utiliser qu’un seul BIP sonore, on peut se contenter d’un BUZZER actif et d’une procédure élémentaire telle que : (Un BUZZER actif génère du 3000 Hz quand il est soumis à +5Vcc)

Dans cette procédure qui réalise un BIP sonore sur un bruiteur passif la fréquence sera d’autant plus élevée que T est de faible valeur. La durée du BIP est directement proportionnelle à MAX. Cette procédure ne consomme que 52 Octets de programme et rien de plus dans la mémoire dynamique.
Résumé : Pour générer des tonalités par un bruiteur il existe un nombre infini de possibilités qui n’est limité que par notre manque d’imagination. Si l’on désire se faire plaisir est obtenir facilement des tonalités différentes, l’emploi d’un BUZZER passif s’impose. Si de plus on dispose de largement trop de place en mémoire de programme pour y loger notre code, ne pas hésiter à privilégier tone et notone. Si au contraire on cherche désespérément à loger le code objet qui dépasse l’espace disponible, passer éventuellement à un BUZZER actif et se contenter que d’un seul BIP possible.
Nous avons mis en place et vérifié le matériel et commencé à écrire des procédures qui vont intégrer le programme d’exploitation. Il ne reste plus qu’a utiliser tous ces éléments pour agencer une première version du détecteur de CO2 sachant avec pertinence que le but final ne sera pas forcément de posséder le petit instrument, mais de s’amuser et se perfectionner en programmation.
La suite est ici.