lundi 3 mai 2021

ARDUINO : bien débuter


ARDUINO : bien débuter

 

Voici un article que j'aurais dû écrire il y a longtemps. En effet, je constate souvent que la majeure partie des débutants ne connaissent même pas les bases essentielles du développement sur cette plateforme.

Certains se contentent d'acheter une carte et de rechercher sur le WEB un projet qui correspond à leurs attentes, sans apprendre les bases du langage. Ce n'est pas la bonne méthode.

Ce tutoriel n'a pas la prétention d'être exhaustif, mais il met en avant certains points importants, donne des conseils et permettra d'éviter certains pièges. Certains points généralement absents des tutoriels courants sont présentés ici.

Sa lecture est relativement longue, surtout si l'on clique sur les liens proposés. Il ne s'agit pas de tout assimiler d'une seule traite, mais conserver ce document sous le coude comme pense-bête pour y revenir en cas de besoin est une bonne chose.

Les points suivants sont abordés :

  • les différentes cartes
  • l'alimentation
  • les périphériques
  • le prototypage
  • la réalisation finale
  • le logiciel (IDE, drivers)
  • le moniteur série
  • le code (variables, conditions, boucles, fonctions, macros, structures, pointeurs, etc.)
  • les librairies ou bibliothèques
  • trucs et astuces (entrées numériques, analogiques, communication série, le temps, réseau, etc.).

1. Les cartes

Il existe différentes cartes, qui correspondent chacune à un besoin. Elles comportent souvent les composants suivants :

  • un microcontrôleur
  • un oscillateur 
  • un connecteur USB (ou pas)
  • un convertisseur USB / série (ou pas) 
  • un connecteur d'alimentation (ou pas)
  • un régulateur de tension
  • des connecteurs d'entrée / sortie
  • une LED sur l'alimentation
  • une LED réservée à l'utilisateur

Le microcontrôleur exécute les instructions du logiciel (code) écrit par l'utilisateur. Ce logiciel est souvent appelé sketch dans le monde ARDUINO, et il est écrit le plus souvent en C ou C++.

Le microcontrôleur intègre différents types de mémoire :

  • mémoire programme (FLASH)
  • mémoire de travail volatile (RAM)
  • mémoire de stockage permanent de données (EEPROM)

L'oscillateur est une horloge utilisée pour cadencer l'exécution du logiciel.

Le connecteur USB permet l'alimentation de la carte en 5V, la programmation du microcontrôleur, et d'autres usages, grâce au convertisseur USB.

Le connecteur d'alimentation, un JACK 5.5x2.1 généralement, permet d'alimenter la carte sous une tension comprise entre 6.5V et 20V. Il est présent sur certaines cartes (UNO, MEGA, LEONARDO).

Le régulateur de tension abaisse la tension d'alimentation pour l'adapter au microcontrôleur, le plus généralement 5V, ou 3.3V dans certains cas.

Les connecteurs d'entrée / sortie permettent de raccorder différents types de périphériques (capteurs, boutons, LEDs, modules, etc.).

Sur ces connecteurs on trouve également plusieurs broches d'alimentation :

  • VIN ou RAW : alimentation 6.5V - 20V (équivalent au JACK de la UNO)
  • VCC : alimentation 5V ou 3.3V (entrée ou sortie)
  • sortie 3.3V
  • GND

Avant de démarrer, il est indispensable de bien comprendre les différents modes d'alimentation de ces cartes (voir plus loin : 1.4. L'alimentation).

1.1. Les cartes ATMEGA328P

Les cartes équipées d'un microcontrôleur ATMEGA328P ont en commun les caractéristiques suivantes :

  • alimentation 5V (plus rarement 3.3V)
  • oscillateur 16MHz
  • 32 KB de mémoire FLASH pour le code
  • 2 KB de mémoire RAM pour les données volatiles
  • 1 KB de mémoire EEPROM pour les données non volatiles
  • 14 entrée / sorties digitales
  • 6 entrées analogiques

32 KO de mémoire est une quantité ridiculement faible par rapport à la taille de celle d'un ordinateur du type PC, mais on peut faire énormément de choses tout de même, et cela suffit à la majorité des applications.

1.1.1. La UNO

ARDUINO UNO R3 officielle

C'est la carte la plus connue, mais il en existe différentes versions. La carte officielle est équipée d'un microcontrôleur ATMEGA328P 28 pattes DIL (Dual In Line).

ARDUINO UNO clone

La carte UNO dite "clone", que l'on trouve le plus souvent chez les revendeurs chinois, est équipée d'un ATMEGA328P 28 pattes SMD (Surface Mount Device).

Quelle sont les différences ?

Pratiquement aucune si ce n'est le convertisseur USB :

  • un microcontrôleur ATMEGA16U2 pour la carte officielle
  • un convertisseur CH340 pour la carte clone

Cette carte est assez peu adaptée à une réalisation finalisée, sauf si l'on utilise des modules vendus sous forme de cartes dites "shield", enfichées directement sur les connecteurs de la UNO :

Shield Ethernet W5100
Shield communication radio RFM69
Shield de pilotage moteur

Mais sachez toutefois que ces cartes sont difficilement empilables à l'infini, car rien ne dit qu'elles n'utilisent pas les même entrées / sortie de la UNO. Souvent il est plus prudent de se limiter à une seule carte, à moins de bien étudier le schéma.

1.1.2. La NANO

C'est une version miniature de la UNO, équipée d'un ATMEGA328P SMD et d'un connecteur mini-USB. Elle possède 2 entrées analogiques supplémentaires (A6 et A7).

Comme pour la UNO, il existe deux versions, officielle et clone avec un convertisseur USB différent :

  • un convertisseur FT232RL pour la carte officielle
  • un convertisseur CH340 pour la carte clone

1.1.3. La PRO MINI

La PRO MINI est une carte qui ne possède pas de connecteur USB ni de convertisseur USB. Elle est plutôt réservée aux utilisateurs avertis qui désirent développer des systèmes à basse consommation, alimentés sur batterie.

Elle existe en deux versions :

  • 16MHz 5V
  • 8MHz 3.3V

Cet article peut être utile :

ARDUINO : PRO MINI 5V ou 3.3V

Pour la programmer il faudra lui ajouter un convertisseur USB :

Voir ici pour plus de détails :

ARDUINO PRO MINI & basse consommation

1.2. La carte ATMEGA2560

 

Les cartes équipées d'un microcontrôleur ATMEGA2560 ont en commun les caractéristiques suivantes :

  • alimentation 5V
  • oscillateur 16MHz
  • 256 KB de mémoire FLASH pour le code
  • 8 KB de mémoire RAM pour les données volatiles
  • 4 KB de mémoire EEPROM pour les données non volatiles
  • 54 entrée / sorties digitales
  • 16 entrées analogiques

Comme pour la UNO, il existe deux versions, qui se distinguent par leur convertisseur USB :

  • un microcontrôleur ATMEGA16U2 pour la carte officielle
  • un convertisseur CH340 pour la carte clone

Ces cartes seront nécessaires uniquement dans les cas extrêmes :

  • serveur WEB avec stockage sur carte SD et communication par Ethernet
  • projets avec écran TFT grand format
  • etc.

1.3. Les cartes diverses

Il existe bien entendu d'autres cartes :

  • ARDUINO LEONARDO
  • ARDUINO MICRO
  • ARDUINO DUE
  • etc.

Sans oublier les cartes à base d'ESP8266 ou ESP32 qui possèdent un contrôleur WIFI, et éventuellement un écran OLED, ou un contrôleur de charge de batterie :

ESP32 Devkit C

HELTEC WiFi Kit 32
WEMOS D1 LOLIN32

Le but de cet article n'est pas de les énumérer toutes, mais de s'initier à la manipulation des plus courantes.

1.4. L'alimentation

Si un montage simple peut être alimenté avec un simple bloc secteur USB ou JACK, d'autres nécessiteront une alimentation plus conséquente, ou une batterie.

J'ai déjà écrit quelques articles sur ce sujet. Le premier est très important :

ARDUINO : l'alimentation (VCC, VIN, etc.)

Alimentation par bloc secteur

ARDUINO et pile 9V

Alimentation par batterie + panneaux solaires

Alimenter un ARDUINO sur Pile ou Batterie

Consommation d'une carte ARDUINO, ESP8266 ou ESP32  

L'alimentation est un sujet sérieux à ne pas sous estimer.

1.5. Les périphériques

En général, lorsque l'on réalise un projet, on a besoin de dispositifs divers :

  • boutons
  • potentiomètre
  • capteurs
  • LEDs
  • afficheurs
  • etc.

Si un bouton, un potentiomètre ou une LED peuvent se passer de librairie pour être utilisés, la majorité des capteurs ou afficheurs, plus complexes, ne pourront s'en passer.

Un conseil : avant d'acheter quoi que ce soit, ne foncez pas tête baissée et vérifiez qu'il existe une librairie adaptée, spécialement s'il s'agit d'un afficheur TFT (dans ce cas vérifiez également que le vendeur indique bien de quel contrôleur l'écran est équipé).

1.5.1. Les kits

Faut-il acheter un kit comprenant un ARDUINO et différents composants ?

Je dirais non, car souvent les capteurs livrés ne sont pas des modèles très performants, et la plupart ne serviront à rien et vous resteront sur les bras. 

Par contre il est intéressant d'acheter un kit de résistances 1/4W :

Jeu de résistances film métallique 1%

On peut éventuellement compléter par quelques diodes (1N4148, 1N4004), condensateurs (10nF, 100nF, 10µF), transistors (BC547, 2N2222, 2N2907,  2N3904, 2N3906).

1.5.2. Les bus

Un ARDUINO possède deux bus, I2C et SPI.

Le bus I2C permet de connecter plusieurs composants ou modules sur deux fils SDA (data) et SCL (clock). L'adressage est logiciel et chaque composant possède son adresse propre. Certains composants permettent le choix entre plusieurs adresses différentes, grâce à une ou plusieurs broches spéciales, afin d'éviter que deux composants aient la même adresse.

Le bus SPI permet de connecter plusieurs composants ou modules sur quatre fils : MISO (Master Input Slave Output), MOSI (Master Output Slave Input), CLK (clock) et CS (chip select). L'adressage est physique, il faudra prévoir une broche CS par composant. Il est souvent utilisé sur les composants de stockage ou d'échange de données (SD, mémoire FLASH, carte Ethernet), et certains petits écrans TFT.

Ces bus permettent d'économiser des broches d'entrée / sortie. Par exemple il est possible de contrôler un clavier matriciel 16 touches avec un MCP23008 (expander 8 bits I2C), avec simplement 2 entrées / sorties au lieu de 8.
Un afficheur LCD monopolise 6 entrées / sorties de l'ARDUINO. Avec un contrôleur I2C PCF8574, 2 suffiront.

1.5.3. Les organes de commande

Les organes de commande sont très nombreux :

  • boutons
  • interrupteurs
  • commutateurs rotatifs
  • encodeurs rotatifs
  • claviers matriciels
  • télécommandes
  • etc.

1.5.2.1. Encodeur rotatif

Peu de personnes pensent à utiliser un encodeur rotatif :

Ce petit composant permet une foule de choses. Les modèles à cinq broches possèdent de plus un bouton central (actionné par un appui sur l'axe). On peut donc sélectionner une valeur ou une fonction précise en tournant dans le sens désiré, et valider en appuyant.

Voir cet exemple :

Alimentation 3.7V Numérique

1.5.3.2. Télécommande infra-rouge

Penser également à la télécommande infra-rouge. Le module TSOP4838 est un récepteur capable de comprendre les code de la majeure partie des télécommandes du marché (NEC, SONY, SAMSUNG, etc.) :

Il nécessite une librairie nommée IRREMOTE, installable depuis l'IDE.

Voir cet exemple :

Télécommande de Relais par Infra-Rouge 

1.5.3.3. Clavier matriciel

Un clavier matriciel peut être utilisé pour concevoir des IHM (Interface Homme Machine) simples. En général il est nécessaire de lui adjoindre un écran pour le côté visuel (menu, affichage de données).

Même si des librairies existent, elles ne sont pas vraiment à la portée d'un débutant :

https://github.com/neu-rah/ArduinoMenu

J'ai à ce jour implémenté deux projets à base de clavier matriciel :

ARDUINO : Micro-irrigation Automatisée

Minuterie pour Insoleuse de PCB

1.5.3.4. Ecran TFT tactile

C'est en général le premier réflexe, lorsque l'on a une IHM élaborée à concevoir. Ici aussi, il s'agit d'une tâche complexe, difficilement accessible aux débutants.

Il existe des écrans intelligents : le Nextion est le plus connu. Un logiciel permet de dessiner l'interface graphique : https://nextion.tech/nextion-editor/

1.5.3.5. Interface WEB

Depuis pas mal d'années les interfaces WEB se sont invitées à la table des logiciels embarqués. Il s'agit à mon sens de la solution idéale pour concevoir une IHM complexe.

En effet, il est souvent plus intuitif d'utiliser un dispositif comme un navigateur ou un smartphone pour communiquer avec un système embarqué. Cela nécessite bien sûr du matériel adéquat (ESP8266, ESP32), et des connaissances (HTML, JAVASCRIPT) mais l'investissement est rentable.

Voici quelques exemples concrets :

Serveur ESP32 : implémentation

ARDUINO + Ethernet ou ESP32 : comparons

Un logger analogique et digital (version WEB)

1.5.4. Les capteurs

Il existe une foule de capteurs :

  • température
  • humidité
  • pression
  • lumière
  • poids
  • présence 
  • distance
  • anémomètre
  • gyroscope
  • courant
  • etc.

J'ai déjà écrit des article sur certains :

Les capteurs de température, humidité, pression & luminosité

Capteur de présence : le HC-SR501 sous 3.3V, 3.7V et 5V

Thermomètre InfraRouge

Un thermomètre MYSENSORS sur batterie

Un thermomètre / hygromètre MYSENSORS sur batterie

Un thermomètre MYSENSORS pour freezer

Banc de Mesure de Consommation

Il est important de consulter la datasheet du composant ou du module capteur, ne serait-ce que pour vérifier qu'il correspond au besoin. Attention, certains capteurs s'alimentent en 5V, d'autres en 3.3V.

Un capteur de température ou de luminosité I2C peut être alimenté par la sortie 3.3V d'un ARDUINO, et le dialogue I2C peut se faire en 5V. Cela ne pose pas de problème.

1.5.5. La commande de puissance

La commande de charges de puissance ne se fait pas directement à l'aide d'une sortie de l'ARDUINO, comme on le voit parfois. Une sortie d'ARDUINO est limitée à 20mA, tout juste assez pour allumer une LED courante.

En fonction du type de charge (lampe, moteur, pompe, etc.) de la tension nécessaire (5V, 12V, 24V, 230V, etc.) et de la puissance à fournir, l'organe de commande sera différent.

Il faut tenir compte aussi de l'utilisation prévue. On n'utilisera pas un relais lorsque la fréquence de commutation est élevée.

Anecdote : j'ai déjà vu quelques amateurs allumer avec des relais 15 rubans de LEDS sur un escalier. Soyons sérieux, imaginez le bruit infernal que ce montage va produire ! Des transistors MOSFET feront ce travail en silence, et on pourra de plus régler la luminosité en utilisant une technique appelée PWM.

1.5.5.1. Relais

Module relais

Un relais peut être utilisé pour commander tous types de charges, en courant continu comme alternatif. Souvent ces modules sont limités à 250V et 10A, mais il n'est pas interdit de commander un contacteur plus puissant avec un relais :

Contacteur Legrand 25A

Un contacteur est préférable lorsqu'il s'agit d'alimenter un gros moteur ou une pompe 230V. Commander ce type de charge avec un petit module relais 10A est assez déconseillé. Les contacts n'ont pas une résistance suffisante et pourraient rester collés, ou plutôt soudés, à cause des arcs électriques importants générés par la charge inductive.

Il existe des modules relais comportant jusqu'à 16 relais, mais il faut savoir que chaque relais, lorsque la bobine est alimentée, consomme environ 75mA. Si plusieurs relais peuvent être commandés simultanément, cette consommation sera doublée, triplée, etc. Prévoir une alimentation conséquente.

Également, alimenter un module à 1 ou 2 relais à l'aide de la broche 5V d'un ARDUINO alimenté par sa broche VIN est acceptable (tout va dépendre de la tension sur VIN). Au delà de 2 relais, le courant disponible est trop faible. Prévoir une alimentation 5V et alimenter l'ARDUINO (broche 5V) et les relais en parallèle est pratiquement une obligation.

Lorsque l'on manipule du 230V on doit prendre des précautions, en particulier ne pas toucher les parties haute tension (contacts, bornier de sortie), ni le dessous du module.

Il n'est pas obligatoire d'utiliser un module comme ci-dessus, bien qu'il présente pas mal d'avantages. On peut parfaitement utiliser un relais nu :

Relais nu

Attention : ce genre de relais ne se commande pas avec une sortie ARDUINO, car son courant de bobine est d'environ 70mA. Il faut lui ajouter une électronique de commande.

Plus de détails ici :

Piloter un relais. Transistor unipolaire ou bipolaire ?

Piloter des Relais ou des MOSFEts à l'aide d'un Module MCP23008 ou MCP23017  

1.5.5.2. Dimmer

Dimmer

Ces modules permettent d'alimenter une charge 230V (ampoule, moteur, etc.) avec une tension variable.

Ces modules se commandent en PWM et sont équipés d'optocoupleurs, ce qui isole l'ARDUINO du 230V.

Comme avec un relais, lorsque l'on manipule du 230V on doit prendre des précautions, en particulier ne pas toucher les parties haute tension (triac, optocoupleurs, bornier de sortie), ni le dessous du module.

Attention : toutes les ampoules 230V ne sont pas utilisables avec un dimmer, spécialement celles équipées de LEDs. Également, seuls les moteurs universels à balais peuvent être commandés de cette manière.

1.5.5.3. MOSFET

MOSFET AOD4184

Un transistor MOSFET ne pourra commuter que des charges en courant continu et basse tension (5V, 12V, 24V, etc.). Ce type de dispositif est intéressant par exemple pour commander des LEDs de puissance, ou un moteur à courant continu.

Attention, la plupart des modules du commerce (IRF520 par exemple) acceptent difficilement une commande en 5V. Il faut faire attention à ce que l'on appelle la tension VGSth, et choisir des modèles "Logic Level". Éviter les séries IRFxxx.

Voir ici pour plus d'explications :

MOSFETS de puissance

Lorsque l'on a besoin de plusieurs canaux, il n'est pas inintéressant de fabriquer son propre module :

Un Module à 16 Mosfets 

Piloter des Relais ou des MOSFEts à l'aide d'un Module MCP23008 ou MCP23017

1.5.5.4. Commande de moteur

Lorsque l'on a besoin d'un moteur il est impensable d'acheter un modèle dont le vendeur ne précise pas certaines caractéristiques :

  • tension de fonctionnement
  • courant maximal consommé
  • vitesse en tours / minute
  • couple

Je veux bien accorder que le courant consommé dépendra en grande partie de la charge mécanique, mais ce n'est pas une raison pour faire l'impasse sur l'essentiel.

Eviter d'acheter un produit pour lequel le vendeur n'a aucun renseignement à fournir à part "moteur 6V" ou "moteur 12V", et que la datasheet est introuvable.

Module L293D

On utilisera un driver de moteur de ce type pour piloter un moteur à courant continu, lorsque l'on désire le faire tourner dans les deux sens, et faire varier sa vitesse. Le L293D peut débiter 600mA. Il n'est pas le seul du marché :

  • L9110S : 1 moteur, 800mA
  • L298 : 2 moteurs, 4A
  • TB6612FNG : 2 moteurs, 1A
  • VNH7070BASTR : 1 moteur, 15A
Le TB6612FNG et le VNH7070BASTR possèdent une commande de standby, permettant de le placer dans un mode basse consommation.

Il existe des moteurs continus avec réducteur mécanique de vitesse (engrenages), appelés moto-réducteurs. La réduction de vitesse permet également d'augmenter le couple.

Les tutoriels sur le pilotage de moteurs sont nombreux :

https://arduino103.blogspot.com/2011/06/controle-moteur-dc-via-l293d-h-bridge.html

http://gilles.thebault.free.fr/spip.php?article41

1.5.5.5. Servomoteur

Servomoteur

Un servomoteur permet d'effectuer une rotation de 0° à 180°, avec positionnement angulaire. Il ne nécessite aucune électronique de puissance, et se pilote simplement avec une sortie digitale PWM.

Il faut bien connaître les caractéristiques de la charge mécanique afin de faire un choix judicieux (le couple en particulier).

Attention, certains modèles vendus sous la dénomination servomoteur 360° ne permettent pas le positionnement angulaire. Ce sont simplement des moteurs continus avec réducteur de vitesse, dont la vitesse est réglable.

Le pilotage d'un servomoteur est on ne peut plus simple. La librairie Servo est faite pour cela. Et il existe de nombreux tutoriels :

https://www.carnetdumaker.net/articles/controler-un-servomoteur-avec-une-carte-arduino-genuino/

1.5.5.6. Moteur pas à pas

Module A4988

Ce type de module permet de piloter un moteur pas à pas. Je vous conseille de fortement vous documenter avant d'attaquer pareil sujet. Il existe différents types de moteurs (unipolaire, bipolaire) et il ne s'agit pas de se lancer tête baissée, et d'acheter n'importe quel moteur en croyant qu'on pourra facilement le piloter avec le module driver X ou Y.

Il existe beaucoup de tutoriels. Ne pas hésiter à y consacrer du temps :

http://dansetrad.fr/Orgue_de_barbarie/Elec/Action/PasAPas/

1.5.6. L'affichage

1.5.6.1. La LED


La LED est une diode. Elle comporte une borne + (anode) et une borne - (cathode). L'anode est souvent la broche la plus longue.

Trop de débutants relient une LED directement à une sortie de l'ARDUINO. Une résistance de limitation de courant est indispensable, car une sortie ARDUINO est limitée théoriquement à 20mA (40mA grand maxi, ce qui pourrait être fatal pour certaines LEDs) :

En utilisant le montage de gauche, la LED s'allumera en envoyant un niveau haut sur la sortie D4. En utilisant le montage de droite, la LED s'allumera en envoyant un niveau bas sur la sortie D5

Suivant la luminosité désirée la résistance pourra avoir une valeur de 330Ω, 1KΩ, ou même 10KΩ si la LED est performante.

Ne pas oublier que la carte ARDUINO possède déjà une LED, sur la sortie D13 (ou LED_BUILTIN).

Quand on a besoin de plusieurs LEDs accolées, il existe des blocs (bargraph) :

1.5.6.2. La LED de puissance

Il existe un grand nombre de LEDs de puissance. Elles sont disponibles sous forme de composants unitaires ou de rubans, en 5V ou 12V :

 

Ruban de LEDs 12V 5630

Attention : ces LEDs ne se pilotent pas avec une sortie de l'ARDUINO. Comme pour un relais, il faut leur associer une électronique de puissance, un MOSFET par exemple, comme sur ce projet :

Un éclairage d'escalier à LEDs

1.5.6.3. La LED intelligente

Une LED de puissance intelligente peut être commandée par une sortie de l'ARDUINO, la WS2812 :

Module WS2812

Module 8 LEDs WS2812

Ruban WS2812

Matrice WS2812

Ces LEDs RGB (couleur) possèdent 3 fils (5V, GND, DIN), on peut les chaîner, et elles sont disponibles sous forme de modules à une LED, de blocs de 8 LEDs, de rubans, de matrices ou d'anneaux. La commande réclame peu de courant. L'intensité et la couleur sont réglables.

Elles sont adressables, c'est à dire que l'on pourra allumer une LED parmi N sur un ruban.

Attention : le courant consommé par chaque LED peut aller jusqu'à 60mA (luminosité maximale). Si le nombre de LEDs est important une alimentation conséquente est à prévoir.

LEDs APA102

Les LEDs APA102 ou SK9822 sont encore plus intelligentes que les WS2812. D'une part elles fonctionnent sur bus SPI, plus rapide, d'autre part elles possèdent un registre de luminosité.

En d'autres termes à partir du moment où la demande d'allumage des LEDs a été envoyée (couleurs, luminosité), le microcontrôleur n'a pas besoin de renouveler cette demande en permanence comme avec une WS2812. Le circuit de contrôle de chaque LED mémorise les données de couleur et luminosité et gère la LED de manière autonome. L'ARDUINO pourra sans problème être occupé à d'autres tâches.

Si le nombre de LEDs est important les APA102 ou SK9822 sont préférables.

1.5.6.4. Afficheurs matriciels

Afficheur 4 blocs MAX7219

Ces afficheurs sont équipés de 64 LEDs par bloc, adressables une à une.

J'en parle ici :

MAX7219 : affichage matriciel

1.5.6.5. Les écrans

On voit trop de projets équipés d'un écran LCD comme celui-ci :

Ils ne sont pas très simples à utiliser (beaucoup de débutants s'y cassent les dents), ils fonctionnent mal sous 3.3V, et ils sont gros et moches. Je conseille plutôt d'utiliser de petits écrans OLED :

Ils sont beaucoup plus simples à utiliser, et permettent l'affichage de logos, de caractères de tailles différentes, et même de caractères accentués (encore faut-il choisir la bonne police).

J'en parle ici :

Afficheur OLED SSD1306 : comparons 

Certaines cartes ESP32 sont équipées d'écrans OLED :

HELTEC-WIFI-KIT-32

https://riton-duino.blogspot.com/2021/06/esp32-heltec-wifi-kit-32.html

Pour les besoins d'affichage de grande taille, penser aux écrans TFT :

Plus d'informations ici :

LED, LCD, TFT et ARDUINO (et autres VFD, NIXIE)

Cet article parle aussi d'afficheurs à 7 segments à LED ou LCD, et de bien d'autres sujets.

1.5.7. Le son

Lorsque l'on désire produire des sons avec un ARDUINO, on a le choix entre différentes solutions.

S'il s'agit de produire un signal d'avertissement, un buzzer peut suffire :


S'il s'agit de sons plus élaborés (musique ou messages vocaux), un petit lecteur MP3 est la solution la plus abordable :

Cet article peut aider :

Faire du bruit avec un ARDUINO  

1.5.8. Les connecteurs

Les interconnexions entre cartes peuvent être réalisées à l'aide de fils soudés, mais également à l'aide de connecteurs :


Je vous renvoie à cet article :

Les connecteurs pour cartes électroniques

1.6. Le prototypage

Le développement d'un projet passe par une phase importante : le prototype.

Cet article donne beaucoup d'informations utiles pour réaliser un prototype réussi :

ARDUINO : prototypage amélioré sur breadboard

L'essentiel est de s'équiper d'une breadboard et de fils DUPONT pour les raccordements :

Une carte NANO ou PRO MINI peut être directement  enfichée sur la breadboard, ainsi que pas mal de modules équipés de broches mâles. Une UNO ou MEGA sera posée à côté de la breadboard.

1.7. La réalisation finale

La réalisation finale d'un projet peut difficilement être faite sur une breadboard avec des fils DUPONT. La fiabilité serait douteuse. Dans la majorité des cas il sera nécessaire de passer par une étape de finalisation.

On peut utiliser une plaquette à pastilles et souder dessus les composants et modules :

On imagine mal une carte UNO ou MEGA sur une plaquette à pastilles. On privilégiera pour cet usage la NANO ou la PRO MINI, ou d'autres cartes équipées de broches mâles.

Plus de détails ici :

Développement électronique ARDUINO (voir le paragraphe 8.7. Le montage sur plaquette à pastilles)

Ce projet a été entièrement réalisé sur plaquette à pastilles :

Alimentation 3.7V Numérique

Alimentation 3.7V (dessus)

Alimentation 3.7V (dessous)

Pour les puristes, un PCB peut être envisagé :

Concevoir un PCB 

JLCPCB : mode d'emploi

Mais cela commence à faire beaucoup pour un débutant.

2. Le logiciel

2.1. L'IDE ARDUINO

L'IDE ARDUINO est le logiciel qui va permettre d'écrire le programme, appelé sketch. Il est installable à partir de cette page :

https://www.arduino.cc/en/software

Je déconseille totalement l'installation à partir d'autres sources, en particulier Microsoft Store !

L'IDE s'installe sur PC, Windows, LINUX et même MAC.

Il existe deux versions Windows :

  • une version installable par un logiciel d'installation .EXE, qui installera l'IDE dans le répertoire "Program Files".
  • une version sous forme de fichier .ZIP, que l'on décompressera dans son espace personnel. Il sera possible par ce moyen d'installer plusieurs versions.

2.2. Les drivers USB

Pour programmer la carte, nous allons avoir besoin de communiquer avec elle à travers un cordon USB.

Tout d'abord, les utilisateurs de LINUX seront exemptés de cette tâche, car les drivers nécessaires font déjà partie de l'OS.

Pour les autres, si vous avez opté pour l'installation à partir d'un fichier ZIP, il va falloir installer un driver adapté. Pour cela il est impératif de savoir de quel type de convertisseur USB est équipée la carte ARDUINO :

  • ATMEGA16U2
  • FT232RL
  • CH340
  • etc.

Concernant les deux premiers, on peut se référer à cette documentation :

https://www.locoduino.org/spip.php?article15

Si la carte est équipée d'un convertisseur CH340, ce qui est le cas de la majorité des modèles clone chinois, le driver se trouve ici :

http://www.wch.cn/download/ch341ser_exe.html

Si l'on possède un convertisseur USB séparé pour une utilisation avec une PRO MINI, on peut lire cet article :

Les convertisseurs USB / série (voir le paragraphe : 2. Les drivers)

A partir du moment où les drivers sont installés, dans le menu Outils / Port de l'IDE, on verra apparaître un port série pour chaque carte connectée à l'ordinateur.

Si aucun port n'est visible au branchement de la carte, ce n'est pas la peine d'aller plus loin. Trop d'amateurs réinstallent l'IDE, ce qui n'apporte rien.

Cet article peut être utile :

ARDUINO : problèmes de téléversement 

Classiquement la première manipulation que l'on effectue est lancer l'IDE et de charger l'exemple Blink à l'aide du menu "Fichiers / Exemples / 01.Basics / Blink".

  // the setup function runs once when you press reset or power the board
  void setup() {
    // initialize digital pin LED_BUILTIN as an output.
    pinMode(LED_BUILTIN, OUTPUT);
  }

  // the loop function runs over and over again forever
  void loop() {
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);                       // wait for a second
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    delay(1000);                       // wait for a second
  }

Ensuite il faut cliquer sur l'icône Téléverser ou taper CTRL-U pour charger l'exemple dans la carte.

Les cartes sont en général vendues avec cet exemple pré-chargé. Si vous voulez être certains que toute la chaîne fonctionne, remplacez les délais dans le code :

  delay(1000);

Par :

  delay(200);

Si la LED clignote plus rapidement, c'est que tout fonctionne.

2.3. Le terminal ou moniteur série

Le moniteur série est un outil indispensable au développement d'un logiciel. C'est pour cette raison que je le présente en premier.

un article sur le sujet :

ARDUINO : le moniteur série 

2.4. Le code

Tout d'abord, tordons le cou à une idée très répandue. Il n'y a pas de langage ARDUINO à proprement parler. Le code s'écrit en C ou en C++, rien de plus. La syntaxe est rigoureusement identique.

La seule différence entre un programme C classique et un programme C ARDUINO est la suivante :

  // programme C classique :
  int main(int argc, char *argv[])
  {
    // instructions
    return 0;
  }

En programmation C classique la fonction main() s'exécute une seule fois. Elle contient les instructions nécessaires à son exécution (conditions, boucles, etc.). Après que ces instructions aient été exécutées, le programme s'arrête.

  // programme C ARDUINO :
  void setup() {
    // instructions
  }

  void loop() {
    // instructions
  }

En programmation C ARDUINO, la fonction setup() est exécutée au démarrage, une seule fois, et permet d'initialiser des entrées / sorties, des librairies. La fonction loop() est exécutée en boucle, répétitivement, à l'infini. Il n'y a donc jamais d'arrêt.

En réalité, la fonction main() existe, mais elle se trouve dans la librairie ARDUINO, ce n'est pas à vous de l'écrire :

int main(void)
{
    init();
    initVariant();
    setup();
    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
    }
    return 0;
}

Ecrire du code C ou C++ ne se fait pas sans connaissances. La formation est une étape indispensable, sans laquelle on ne pourra en aucun cas développer un projet, ou modifier un projet existant pour l'adapter à ses besoins.

On voit beaucoup trop de demandes sur les forums comme celle-ci :

J'ai trouvé un code sur le WEB, mais il ne fait pas vraiment ce que je veux, pourriez-vous m'aider à le modifier pour qu'il fasse ceci ou cela ?

Non, il y peu de chances que les intervenants d'un forum fassent le travail à votre place.

Il existe de nombreux tutoriels ARDUINO. En général, il faut être prêt a y consacrer une semaine, en écrivant des exemples.

https://zestedesavoir.com/tutoriels/686/arduino-premiers-pas-en-informatique-embarquee/742_decouverte-de-larduino/3418_le-langage-arduino-12/

D'autres tutoriels sont plus généralistes, et présentent seulement le langage C :

https://openclassrooms.com/fr/courses/19980-apprenez-a-programmer-en-c

Sachez toutefois que le code standard C utilise des fonctions pas forcément présentes sur ARDUINO. Un tutoriel spécifique ARDUINO est préférable, surtout si l'on est débutant.

Nous allons tout de même parler de quelques détails.

2.4.1. Les commentaires

Certaines portions de code peuvent être ignorées par le compilateur si on les commente. Elles peuvent être utilisées pour fournir des explications ou de l'aide :

// ceci est un commentaire  (nouvelle notation C++).

/*
 * ceci est aussi un commentaire (ancienne notation C).
 */

2.4.2. Les accolades et parenthèses

Source de nombreux problèmes chez les débutants, elles vont toujours par paires.

void setup()                               // 2 parenthères
{                                                // accolade ouvrante
  Serial.begin(115200);    
        // 2 parenthères
  Serial.println("Serial test");    // 2 parenthèses
}
                                                // accolade fermante

void loop()                                  // deux parenthèses
{                                                 // accolade ouvrante
  if (Serial.available()) {             // 4 parenthèses + accolade ouvrante
    char c = Serial.read();           // 2 parenthèses
    Serial.print("char reçu: ");     // 2 parenthèses
    Serial.println(c);                    // 2 parenthèses
  }                                               // accolade fermante
}                                                 // accolade fermante

Si par exemple la dernière parenthèse est oubliée le compilateur affichera :

expected '}' at end of input

Dans l'IDE, la combinaison CTRL-T permet d'indenter le code, c'est à dire d'ajouter des espaces en début de ligne, en fonction du niveau d'imbrication des instructions.

A utiliser sans modération !

2.4.3. Le point-virgule

Le point-virgule termine une ligne de code.

Erreur souvent commise :

  if (condition == true);
    var = 1;

Ou :

  if (condition == true); {
   
var = 1;
  }

La première ligne est traduite par : si la condition est vraie, ne rien faire, à cause du point-virgule. Donc la seconde ligne sera toujours exécutée.

La syntaxe correcte est :

  if (condition == true)
   
var = 1;

Ou :

  if (condition == true) {
   
var = 1;
  }

Comme on le voit ici, une condition n'est pas forcément suivie d'une instruction ou d'une suite d'instructions entre accolades. S'il n'y a qu'une instruction à exécuter, les accolades peuvent être omises. Par contre je recommande vivement d'utiliser systématiquement les accolades, pour la bonne raison que si l'on ajoute une instruction à exécuter après la condition, on peut oublier d'ajouter les accolades. Si elles sont déjà présentes, cela facilite les choses.

2.4.4. Les variables

En C les variables sont typées, contrairement à d'autres langages. Elles peuvent être numériques (int, long, float) ou textuelles (char, char[]).

Piège courant : une variable flottante utilise le point comme séparateur entre partie entière et décimales, et non pas la virgule :

float f = 12.34;        // correct
float f = 12,34;        // incorrect

2.4.4.1. Portée

En C une variable peut avoir une portée locale ou globale.

int i;        // variable globale

int func(void)
{
  for (i = 0 ; i < 10 ; i++) {
    print("i=");
    println(i);
  }
  return i;

}

Dans ce premier cas, i est une variable globale. Elle est visible de partout dans le sketch. Elle occupe en permanence 2 octets en mémoire RAM.

int func(void)
{
  int i;        // variable locale
  for (i = 0 ; i < 10 ; i++) {
    print("i=");
    println(i);
  }
  return i;

}

Dans ce deuxième cas, i est une variable locale. Elle n'est visible que dans la fonction func(). Elle occupe 2 octets en mémoire RAM, mais sera détruite en sortie de fonction, donc la mémoire qu'elle occupe est temporaire, ce qui est plus économique.

Il est possible de déclarer une variable et de lui affecter une valeur :

int i = 100;

Par contre, pour une variable global ou statique il est inutile de l'initialiser à ZÉRO :

int i = 0;   // inutile
int i;         // OK

Au démarrage, toutes les variables globales et statiques sont initialisées à ZERO, sauf bien sûr celles auxquelles on a affecté une valeur.

2.4.4.2. Variables en FLASH

Un autre moyen de ne pas gaspiller la mémoire RAM est de placer des variables, surtout des messages, en mémoire FLASH :

println("ceci est un message en RAM");

println(F("ceci est un message en FLASH"));

https://www.arduino.cc/reference/en/language/variables/utilities/progmem/ 

2.4.4.3. Constantes

Une variable peut être constante, c'est à dire que son contenu ne pourra pas être modifié par le code :

const int var = 22;

La valeur de cette variable sera conservée indéfiniment. Si l'on essaie de la modifier par programmation, le compilateur affichera un message d'erreur.

2.4.5. Les macros et directives

Il existe une catégorie particulière de constantes, en général peu décrites dans les tutoriels :

#define YELLOW      0xFFE0

Ceci n'est pas une variable. Elle n'occupe aucune place en mémoire. Simplement, le compilateur (ou plutôt son préprocesseur) remplacera YELLOW par 0xFFE0 à chaque fois qu'il rencontrera ce mot.

On peut faire une foule de choses avec des macros :

#define log       Serial.print
#define logln    Serial.println

A chaque fois que l'on appellera la fonction log() ou logln() le préprocesseur remplacera par Serial.print() ou Serial.println().

#define DEBUG

#ifdef DEBUG
#define log       Serial.print
#define logln    Serial.println
#else
#define log
#define logln
#endif

On voit ici qu'une constante (ici DEBUG) n'a pas forcément de valeur associée. Mais on peut la tester avec une directive #ifdef (ou #if defined) qui vérifie si elle est définie ou non.

Comme précédemment, si DEBUG est définie, à chaque fois que l'on appellera la fonction log() ou logln() le préprocesseur remplacera par Serial.print() ou Serial.println().

Si l'on met la ligne en commentaires :

// #define DEBUG

A chaque fois que l'on appellera la fonction log() ou logln() le préprocesseur remplacera par rien du tout, donc aucune fonction ne sera appelée.

Il est possible également d'écrire du code tenant compte de la plateforme :

#ifdef ESP32
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#endif

Les constantes ESP32 et ESP8266 sont définies dans les librairies de l'IDE. On peut donc écrire un code comportant des parties spécifiques à chaque plateforme. Ensuite il suffira de choisir la bonne carte dans le menu "Outils / Type de Carte" pour que la compilation se fasse avec le code correspondant.

D'autres constantes existent :

__AVR__ : définie pour un microcontrôleur AVR
__AVR_ATmega2560__ : définie pour un microcontrôleur ATMEGA2560
__AVR_ATmega32U4__ : définie pour un microcontrôleur ATMEGA32U4 

Il existe des constantes prédéfinies à usage général :

__LINE__: représente le numéro de la ligne de code actuelle.
__FILE__: représente le nom du fichier source.
__DATE__: représente la date de la compilation.
__TIME__: représente l'heure de la compilation.

Je conseille la lecture de cet article :

https://openclassrooms.com/fr/courses/19980-apprenez-a-programmer-en-c/15954-le-preprocesseur

2.4.6. Les chaînes de caractères (C strings)

Le langage C fait la distinction entre char (caractère) et chaîne de caractères.
'a' et "a" ne sont pas équivalents. 'a' est un caractère, tandis que "a" est une chaîne de caractères comportant un seul char. 

  char s1[] = "abcdef";

Une chaîne de caractères C est terminée par un caractère NUL, donc la chaîne ci-dessus occupe 7 octets et non 6. Ce caractère NUL est utilisé par pas mal de fonctions comme terminateur (caractère de fin de chaîne).

Il existe différents moyens de déclarer une chaîne de caractères (tableau de char) :

  char s1[] = "abcdef";
  char *s2 = "abcdef";

Ces deux syntaxes sont équivalentes, à la différence près que la deuxième utilise un pointeur, qui pourra être réaffecté :

  char s1[] = "abcdef";
  char *s2 = "abcdef";
  s1 = "ghijk";  // invalide
  s2 = "ghijk";  // correct

La variable s2 est un pointeur, qui contient l'adresse de la chaîne "abcdef".

La première expression est invalide. On ne peut pas simplement changer la valeur d'une chaîne de caractères comme cela. La deuxième est valide, car elle ne met en jeu qu'une affectation de pointeur.

La première expression doit s'écrire comme ceci :

  char s1[] = "abcdef";
  strcpy(s1, "ghijk");

Quand on a affaire à une chaîne de caractères, on utilise la fonction strcpy() pour faire une recopie. Attention, la longueur de la nouvelle chaîne ne doit pas être supérieure à l'ancienne, sinon on risque des problèmes de débordement.

On peut pré-dimensionner une chaîne, afin d'éviter cela :

  char s1[20] = "abcdef";  // la chaîne de 6 caractères est contenue dans un espace de 20 octets

Il existe également une fonction qui permet de copier une chaîne en limitant la longueur : strncpy().

On peut changer la valeur d'un seul caractère dans la chaîne dans les deux cas, et de la même façon : 

  char s1[] = "abcdef";
  char *s2 = "abcdef";
  s1[0] = 'g';
  s2[0] = 'g'

Enfin, dans les deux cas, l'appel d'une fonction acceptant un pointeur de char en paramètre se fera de la même façon :

  char s1[] = "abcdef";
  char *s2 = "abcdef";
  Serial.println(s1);
 
Serial.println(s2);

Ces deux appels produiront le même résultat.

Comment compare t-on deux chaînes de caractères ?

On voit beaucoup de comparaisons de ce type :

  char s[] = "chaîne";
  if (s == "blabla") {        // NON OK
    // instructions
  }

Dans ce cas, s est une C string. Cette manière de procéder est incorrecte car elle ne fera que comparer l'adresse des deux chaînes. Il faut utiliser strcmp() :

  char s[] = "chaîne";
  if (strcmp(s, "blabla") == 0) {
    // instructions
  }

strcmp() retourne 0 si les chaînes sont identiques. Il existe également une autre fonction, strncmp(), qui compare uniquement les n premiers caractères.

  String s = "chaîne";
  if (s == "blabla") {       // OK
    // instructions
  }

Dans ce cas, s est une String C++. L'opérateur == ou != sont redéfinis par la classe String pour faire une comparaison de contenu.
Attention, la classe String est plutôt déconseillée sur ARDUINO (voir plus bas).

Toutes ces notions sont importantes. Il vaut mieux s'exercer.

2.4.7. Les tableaux

Nous avons vu qu'une C string est un tableau de variables de type char. Un tableau peut stocker d'autres types de variables.

Il est important de connaître les tableaux. Les débutants ignorent souvent jusqu'à leur existence.

https://zestedesavoir.com/tutoriels/686/arduino-premiers-pas-en-informatique-embarquee/742_decouverte-de-larduino/3419_le-langage-arduino-22/#3-10793_les-tableaux

Erreur souvent commise : les tableaux en C / C++ démarrent à l'index ZÉRO, et non pas UN comme dans certains langages :

int array[10];

int i;
i = array[0];     // premier élément du tableau
i = array[9];     // dernier élément du tableau
i = array[10];     // emplacement invalide

Pour parcourir le tableau précédent on procédera comme ceci :

  for (int n = 0 ; n < 10 ; n++) {
    Serial.println(array[n]);
  }

Exemple : ce petit sketch récupère 20 échantillons de température et en fait la moyenne :

#define NSAMPLE     20

float samples[NSAMPLE];

float readTemp(void)
{
  // température : un nombre aléatoire
  return (float)random(2000) / 100.0;
}

void setup() {
  Serial.begin(115200);
}

void loop() {
  static unsigned long previousMillis = 0;
  static int i;

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= 1000) {
    // toutes les secondes
    previousMillis = currentMillis;
    // lire la température
    samples[i] = readTemp();
    Serial.print("temperature: ");
    Serial.println(samples[i]);
    // incrémenter l'index
    i++;
    if (i == NSAMPLE) {
      // le tableau est plein
      float sum = 0;
      // faire la somme
      for (int n = 0 ; n < NSAMPLE ; n++) {
        sum += samples[n];
      }
      // afficher la moyenne
      Serial.print("moyenne: ");
      Serial.println(sum / NSAMPLE);
      // remettre l'index à ZERO (impératif)
      i = 0;
    }
  }
}

A noter :

NSAMPLE définit le nombre d'échantillons. Cette constante est utilisée partout où cela est nécessaire :

  • dimensionnement du tableau
  • vérification que l'on a atteint la fin du tableau
  • parcours du tableau pour faire la moyenne

Cette méthode est bien plus rationnelle que l'utilisation directe d'une valeur (20). Si l'on veut changer le nombre d'échantillons, il suffit de changer la valeur de NSAMPLES.

L'utilisation de millis() sera détaillée au paragraphe 2.6.4.2 millis().

2.4.8. String

On voit beaucoup trop de tutoriels montrant des exemples utilisant des objets C++ String. Je déconseille totalement, sauf sur ESP8266 ou ESP32.

Plus de détails ici :

ARDUINO : la fragmentation mémoire

J'explique également dans ce document comment utiliser les tableaux de char (ou C strings).

2.4.9. OU et ET logique

Si l'on doit tester plusieurs conditions on utilise && ou ||, et non pas & et | comme on le voit parfois.

  if (condition1 == true && condition2 == true) {    // ET logique
    // instructions
  }

  if (condition1 == true || condition2 == true) {    // OU logique
    // instructions
  }

Les premières choses à apprendre en C sont les déclarations de variables et les opérateurs. C'est indispensable.

2.4.10. condition ou boucle

On entend trop de débutants parler de boucle quand le mot condition serait plus approprié.

Les conditions : if, else, else if, switch

Les boucles : while, do while, for

Encore une fois, formez-vous, cela évitera les confusions et vous serez plus à même de vous faire comprendre sur les forums.

https://zestedesavoir.com/tutoriels/686/arduino-premiers-pas-en-informatique-embarquee/742_decouverte-de-larduino/3418_le-langage-arduino-12/#3-10790_les-conditions

https://zestedesavoir.com/tutoriels/686/arduino-premiers-pas-en-informatique-embarquee/742_decouverte-de-larduino/3419_le-langage-arduino-22/#1-10791_les-boucles

2.4.11. switch()

L'instruction switch() est un bon moyen d'éviter une suite de conditions if + else.

  if (valeur == 1) {
    // instructions
  }
  else if (valeur == 2) {
    // instructions
  }
  else if (valeur == 3) {
    // instructions
  }
  else {
    // instructions
  }

Ces lignes peuvent être remplacées par :

  switch (valeur) {
  case 1:
    // instructions
    break;
  case 2:
    // instructions
    break;
  case 3:
    // instructions
    break;
  default:
    // instructions
    break;
  }

Cette écriture est autrement plus claire et élégante ! Mais elle ne fonctionne qu'avec une valeur entière.

Lorsque l'on cherche à évaluer une chaîne de caractères, il faut précéder comme suit :

  char s[] = "chaîne";
  if (strcmp(s, "blabla") == 0) {
    // instructions
  }
  else if (strcmp(s, "coucou") == 0) {
    // instructions
  }
  else if (strcmp(s, "bonjour") == 0) {
    // instructions
  }
  else {
    // instructions
  }

2.4.12. Les fonctions

Beaucoup de débutants appellent une fonction une "void". Appelons un chat un chat, et utilisons la bonne terminologie. Une fonction est une fonction, elle peut être void (ne pas retourner de valeur) ou non  (retourner une valeur) :

void func1(void)
{
  // ne retourne pas de valeur
}

int func2(void)
{
  // retourne une valeur
  return 0;

}

Une fonction void s'appelle comme ceci :

  func1();

Une fonction retournant une valeur s'appelle comme ceci :

  int x = func2();

Bien entendu la variable recevant la valeur retournée doit être du même type que la valeur retournée par la fonction.

Un piège commun :

char *func(void)
{
  char val[] = "abcdef";
  return val;
}

La variable locale val est détruite en sortie de fonction. Le code appelant risque de récupérer une valeur totalement erratique, et le programme peut planter.

On doit utiliser une variable dite statique dans ce cas.

char *func(void)
{
  static char val[] = "abcdef";
  return val;
}

2.4.13. Les structures

On peut regrouper des variables de différents types dans une structure. Ici par exemple nous regroupons les caractéristiques de différentes personnes dans un tableau de structures :

struct person {
  char *name;
  int size;
  int age;
};

Il est possible, comme pour toute variable, de déclarer une structure tout en lui affectant des valeurs :

struct person persons[] =
{
  "Albert", 180, 18,
  "Marie-Claire", 175, 17,
  "Kevin", 170, 15,
};

Nous pouvons accéder aux différents éléments ainsi :

persons[0].name : le nom de la première personne
persons[0].size : la taille de la première personne
persons[0].age : l'âge de la première personne

Nous voyons donc que l'accès aux différents éléments de la structure se fait à l'aide du point.

Parcourons la liste :

const unsigned npersons = sizeof(persons) / sizeof(struct person);

void setup() {
  Serial.begin(115200);
  for (int n = 0 ; n < npersons ; n++) {
    Serial.print(n);
    Serial.print(": ");
    Serial.print(persons[n].name);
    Serial.print("; size=");
    Serial.print(persons[n].size);
    Serial.print("; age=");
    Serial.println(persons[n].age);
  }
}

void loop() {
}

A noter :

Le nom de la personne est présent dans la structure sous forme de pointeur. Cela évite de pré-dimensionner la variable name, car les noms peuvent être courts ou très longs. On évite ainsi un gaspillage de mémoire.

La variable npersons contient le nombre de lignes du tableau. Sa valeur est calculée lors de la compilation en divisant la taille totale du tableau par la taille de la structure. On obtient donc le nombre de structures dans le tableau.

2.4.14. Les pointeurs

Nous allons terminer par un point obscur pour la plupart : les pointeurs.

Dans l'exemple précédent nous avons utilisé un pointeur (name). Ce pointeur contient l'adresse d'une chaîne de caractère (le nom d'une personne).

Mais on peut aussi utiliser un pointeur sur n'importe quel type de variable :

  int i = 100;
  int *p = &i;
  Serial.println(i);
  Serial.println(*p);

Que veulent dire ces quelques lignes ?

i est un entier.

p est un pointeur sur un entier (int *). On lui affecte l'adresse de i grâce à l'opérateur &.

On affiche i.

On affiche ensuite le contenu de la variable pointée par p grâce à l'opérateur *.

Les deux lignes affichent strictement la même valeur.

On peut aller plus loin avec les pointeurs sur les structures. En reprenant l'exemple du paragraphe précédent :

void setup() {
  Serial.begin(115200);
  for (int n = 0 ; n < npersons ; n++) {
    Serial.print(n);
    printPerson(&persons[n]);
  }
}
void printPerson(struct person *p)
{
  Serial.print(": ");
  Serial.print(p->name);
  Serial.print("; size=");
  Serial.print(p->size);
  Serial.print("; age=");
  Serial.println(p->age);
}
void loop() {
}

Ici nous avons créé une fonction printPerson() qui permet l'affichage des caractéristiques d'une personne. Elle accepte un paramètre du type struct person *, c'est à dire un pointeur sur une structure du type person.

La fonction accède cette fois-ci aux différents éléments de la structure à l'aide de la flèche : ->

La fonction setup() quand à elle, appelle la fonction printPerson() en lui passant en paramètre l'adresse de la nième structure du tableau : &persons[n].

2.5. Les librairies ou bibliothèques

ARDUINO a un avantage indéniable par rapport à d'autres plateformes (PIC, MSP430, etc.) : la disponibilité de nombreuses librairies écrites par des développeurs chevronnés, qui évitent d'avoir à écrire soi-même le code de gestion de pas mal de modules et composants. Il serait dommage de se priver du travail accompli.

On peut dans un premier temps explorer le gestionnaire de bibliothèques, à l'aide du menu "Outils / Gérer les bibliothèques" :

Utiliser la zone "Filtrez votre recherche" pour rechercher un module, ou un composant particulier, puis installer la librairie adéquate.

Généralement, chaque librairie est accompagnée d'exemples, et je conseille vivement d'en essayer au moins un, ne serait ce que pour vérifier que les branchements sont corrects, et que la librairie correspond au besoin.

Il est nécessaire de redémarrer l'IDE après installation d'une librairie pour voir apparaître les exemples dans le menu "Fichier / exemples".

Dans certains cas, la recherche dans le gestionnaire de bibliothèques ne donne rien et on devra faire une recherche sur le WEB et récupérer un fichier ZIP que l'on installera à la main dans le répertoire libraries de l'IDE.

2.6. Trucs et astuces

J'ai regroupé ici quelques notions de base que trop de débutants ignorent, et également quelques pièges à éviter.

2.6.1. Entrées / sorties numériques

Pour manipuler les entrées / sorties numériques, il convient de se documenter sur les fonctions pinMode(), digitalRead(), digitalWrite().

2.6.1.1. Tension

Attention : une carte ARDUINO ne supporte pas plus de 5V sur une entrée digitale, et 3.3V pour certains modèles (DUE, PRO MINI 3.3V 8MHz). Si l'on désire appliquer une tension supérieure il conviendra de la réduire en utilisant un pont diviseur :

2.6.1.2. Isolation galvanique

Si l'on désire une isolation galvanique entre la source de tension et l'entrée de l'ARDUINO on peut utiliser un optocoupleur. En effet, la source de tension peut être dangereuse, et l'on n'a pas forcément envie de soumettre l'ARDUINO, et surtout les personnes qui manipulent l'ARDUINO, à cette tension :

Par exemple ce montage permet de détecter la présence du secteur grâce à un optocoupleur. Lorsque le secteur est présent la LED de l'optocoupleur est allumée et le transistor conduit. La sortie OUT est à ZÉRO. Lorsque le secteur est absent la LED de l'optocoupleur est éteinte et le transistor est bloqué. La sortie OUT est à UN.

Le condensateur C1 provoque une chute de tension dite capacitive, sans dégagement de chaleur.

La résistance R2 peut être remplacée par une résistance de PULLUP de l'ARDUINO. Voir paragraphe 2.6.1.5. PULLUP & PULLDOWN.

2.6.1.3. Courant

Attention également au courant disponible. Une sortie ARDUINO ne peut fournir que 20mA (grand maxi 40mA), et la somme des courants de sortie ne doit pas excéder 200mA au total (y compris pour la carte ARDUINO MEGA), sous peine de destruction du microcontrôleur.

Cela veut dire que si l'on envisage d'allumer un certain nombre de LEDs avec 20mA dans chacune, on ne pourra en allumer au grand maximum 10 simultanément. Si les besoins sont supérieurs, beaucoup de solutions existent, l'ULN2803 par exemple.

2.6.1.4. Nombre d'entrées / sorties

Un ATMEGA328P possède 14 entrées / sorties numériques. Les 2 premières D0 et D1 sont réservées au contrôleur USB, et il est assez déconseillé de les utiliser comme entrée / sortie, ou pour communiquer avec un module (BlueTooth par exemple) car cela pourrait poser des problèmes lors du chargement du sketch sur la carte, et le moniteur série deviendrait inutilisable.

Si l'on a besoin d'une ligne série supplémentaire il existe une possibilité de transformer 2 entrées / sorties quelconques d'un ARDUINO en liaison série : SoftwareSerial.

A savoir : les 6 entrées analogiques A0 à A5 sont également disponibles en tant qu'entrées / Sorties numériques. Il suffit de le demander :

  pinMode(A0, OUTPUT);
  pinMode(A1, INPUT);

Ensuite on pourra lire et écrire comme s'il s'agissait d'une vraie entrée / sortie numérique :

  digitalWrite(A0, HIGH);
  int val = digitalRead(A1);

2.6.1.5. PULLUP & PULLDOWN

Le poussoir SW1 est relié à l'entrée D2. Une résistance de PULLUP R1 fixe l'entrée à 5V (HIGH) lorsque le poussoir est relâché.

Le poussoir SW2 est relié à l'entrée D3. Une résistance de PULLDOWN R2 fixe l'entrée à GND (LOW) lorsque le poussoir est relâché. 

Ces résistances permettent d'éviter que l'entrée soit électromagnétiquement parasitée et bascule sans cesse de HIGH à LOW au repos. On dit souvent : une entrée en l'air se comporte comme une antenne.

L'ARDUINO possède des résistances de PULLUP internes qui permettent de se passer de résistance physique. Elles sont activables par logiciel :

  pinMode(2, INPUT_PULLUP);

On pourra ainsi relier directement le bouton entre entrée et GND, et l'entrée passera à LOW quand le bouton sera appuyé :

  if (digitalRead(2) == LOW) {
    // traitement de l'appui
  }

L'ARDUINO ne possède pas de résistances de PULLDOWN internes, mais certains microcontrôleurs le proposent.

2.6.1.6. Anti-rebond

Un bouton, lorsqu'il est appuyé ou relâché, provoque plusieurs rebonds, et l'entrée de l'ARDUINO passera plusieurs fois à HIGH et LOW.

Pour éviter cela il suffit d'installer un petit condensateur de 100nF en parallèle sur le bouton :

La valeur du condensateur dépendra de la qualité du bouton. Plus le nombre de rebonds est important plus la valeur sera élevée.

Une solution logicielle existe :

https://github.com/thomasfredericks/Bounce2

2.6.1.7. Le PWM

La technique PWM est souvent employée pour régler la luminosité d'une LED, ou la vitesse d'un moteur continu :


En faisant varier le rapport cyclique, la tension moyenne varie, ainsi que la luminosité de la LED, la vitesse du moteur, ou le positionnement d'un servomoteur.

Pour activer le PWM sur une sortie numérique, il convient de se documenter sur la fonction analogWrite().

Cette documentation précise quelles sont les sorties utilisables en PWM en fonction de la carte.

6 sorties sont disponibles sur une UNO, NANO, PRO MINI : 3, 5, 6, 9, 10, 11

Un article plus complet :

https://www.locoduino.org/spip.php?article47

2.6.2. Entrées analogiques

2.6.2.1. Mesure de tension

Une entrée analogique permet de mesurer une tension. Pour lire une entrée analogique, il convient de se documenter sur la fonction analogRead(), et éventuellement analogReference().

Sur un ARDUINO équipé d'un ATMEGA328P, la tension de référence peut être choisie, comme l'indique la documentation de analogReference() :

  • DEFAULT : 5V (ou 3.3V pour une PRO MINI). La mesure sera fortement dépendante de la valeur exacte de la tension d'alimentation
  • INTERNAL : 1.1V. La mesure sera indépendante de la tension d'alimentation mais cela nécessite de connaître la valeur exacte de cette tension de 1.1V, qui est précise à 10% près. Elle peut être mesurée sur la broche AREF
  • EXTERNAL : une référence externe (TL431 (1%), ou mieux LM4040 : 0.1%) peut être utilisée pour plus de précision et de stabilité en température. Il existe de nombreux tutoriels

Pour mesurer une tension on procède comme ceci :

#define VREF                  4.95   // valeur exacte sur AREF

  // code de mesure (dans la fonction loop() par exemple)
  unsigned int adc = analogRead(0);    // mesure sur A0
  float voltage = adc * VREF / 1023;    // 1023 car l'ADC est un 10 bits (1024 points)

On obtiendra ainsi une mesure en volts.

Attention : sur ses entrées analogiques, une carte ARDUINO ne supportera pas une tension supérieure à sa tension d'alimentation. Deuxièmement elle devra également être inférieure à la référence de tension choisie, sinon, l'ADC mesurera une valeur de 1023.

Si l'on désire mesurer une tension supérieure il conviendra de la réduire en utilisant un pont diviseur :

Dans le cas d'une entrée analogique, on utilisera de préférence des résistances de précision 1%.

Attention : si l'on désire lire plusieurs entrées, il y a certaines précautions à prendre :

ARDUINO : les performances de l'ADC

Il n'y a pas que l'ARDUINO qui soit capable de mesurer une tension. D'autre microcontrôleurs sont plus performants dans ce domaine :

STM32 : l'ADC

2.6.2.2. Mesure de courant continu

Une entrée ADC peut également servir à mesurer un courant, à l'aide d'une résistance shunt :


Sur ce montage de pilotage de moteur, la résistance shunt R1 de 1Ω provoque une chute de tension de 1V pour un courant moteur de 1A . Il suffit de mesurer la tension aux bornes de la résistance pour savoir quel est le courant consommé par le moteur.

Il sera nécessaire de se documenter sur la loi d'Ohm :

U = RI

I = U / R

Le courant passant dans une résistance de 1Ω est donc de 1A si la tension mesurée à ses bornes est de 1V. 

2.6.2.3. Mesure de courant continu avec un INA226

Pour mesurer un courant continu avec précision l'INA226 est particulièrement intéressant :

Sa précision de 16 bits est largement supérieure à celle de l'ADC ARDUINO. Généralement le shunt soudé sur la carte est un 10mΩ (marquage R010). Le courant maximal mesurable sera de 8A. Avec un shunt de 100mΩ (marquage R100) il sera de 800mA.

L'INA226 est capable également de mesurer une tension : 36V maximum.

J'en parle ici :

https://riton-duino.blogspot.com/search?q=ina226

Il y a d'autres solutions :

INA138 et MAX4372 : Current Monitoring

2.6.2.4. Mesure de courant alternatif

La mesure de courant alternatif ne peut pas être effectuée directement avec un ARDUINO. Il existe deux moyens principaux :

Capteur de courant ACS712

Ce capteur fonctionne par effet HALL. On trouve beaucoup de tutoriels :

https://wiki.mchobby.be/index.php?title=SENSEUR-COURANT-ACS712

Transformateur de courant SCT013

Ce capteur est un transformateur de courant. Il fonctionne comme une pince ampèremétrique. Voici un tutoriel sur le sujet :

https://learn.openenergymonitor.org/electricity-monitoring/ct-sensors/interface-with-arduino?redirected=true

Sur ce schéma on voit une résistance de charge du transformateur (33Ω), dite "Burden". Attention, certains transformateurs du commerce sont déjà équipés de cette résistance, d'autres non.

2.6.3. Communication série

Le port USB de l'ARDUINO ne sert pas seulement au chargement du code sur la carte et afficher des informations sur le moniteur série. On peut l'utiliser également pour communiquer avec un logiciel tournant sur le PC.

Chargeons ce petit exemple :

void setup()
{
  Serial.begin(115200);
  Serial.println("Serial test");
}

void loop() {
  if (Serial.available()) {
    char c = Serial.read();
    Serial.print("char reçu: ");
    Serial.println(c);
  }
}

Ce sketch affiche les caractères envoyés par le moniteur série (entrer les caractères dans la zone de texte du haut, puis cliquer sur "Envoyer" ou la touche RETURN du clavier :

Serial test
char reçu: e
char reçu: f
char reçu: d
char reçu: f
char reçu: s
char reçu: d
char reçu:   
char reçu:
 
char reçu: q
char reçu: s
char reçu: d
char reçu:   
char reçu:

Ici j'ai tapé efdfsd RETURN, puis qsd RETURN. On voit deux lignes vides à la fin de chaque mot entré. Il s'agit des caractères RETURN (\r) et NELINE (\n), car dans la liste de choix "Fin de ligne" du moniteur série (en bas à droite), j'ai choisi "Les deux, NL et CR".

Bien entendu on peut envoyer ces caractères à l'ARDUINO avec un programme tournant sur le PC, ou une carte RASPBERRY PI, écrit en PYTHON et PYSERIAL, par exemple.

Si l'on préfère éviter de monopoliser le port USB de l'ARDUINO, il existe une possibilité de transformer 2 entrées / sorties quelconques d'un ARDUINO en liaison série : SoftwareSerial.

Lorsque l'on a besoin d'une solution fiable de transfert de données, une solution logicielle appelée protocole est nécessaire :

ARDUINO : un protocole série

2.6.4. Le temps

Il existe différents moyens d'introduire une notion de temps dans un programme :

2.6.4.1 delay(), delayMicroseconds()

Ces deux fonctions laissent s'écouler un temps en millisecondes ou microsecondes.

L'exemple basique Blink :

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

La LED de la carte clignote, une seconde allumée, une seconde éteinte.

2.6.4.2 millis()

Cette fonction retourne le temps écoulé en millisecondes depuis le démarrage.

Cet exemple BlinkWithoutDelay fait la même chose que le précédent :

int ledState = LOW;
unsigned long previousMillis = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= 1000) {
    previousMillis = currentMillis;
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    digitalWrite(LED_BUILTIN, ledState);
  }
  // autres instructions
}

Quelle est la différence ? La fonction loop(), dans le deuxième cas, ne sera pas bloquée pendant l'exécution de delay(), et pourra exécuter d'autres instructions. On parle d'exécution non bloquante.

2.6.4.3 RTC

Jusqu'ici nous avons vu le moyen d'utiliser le temps relatif. Dans certains cas on aura besoin de beaucoup plus : l'heure et la date.

Pour cela, il sera nécessaire d'utiliser un circuit dit RTC (Real Time Clock). Je déconseille les circuits du type DS1307, peu précis. Le DS3231 apportera une précision bien supérieure et une compensation en température :

Je conseille l'utilisation de la librairie RTCLIB, installable depuis l'IDE.

Dans l'exemple DS3231 de la librairie, on voit ces lignes dans la fonction setup() :

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

Ces lignes permettent d'ajuster la date et l'heure à celles de la compilation du sketch. La méthode lostPower() permet de savoir si le circuit vient d'être démarré pour la première fois. Si la pile est en place, l'heure ne sera ajustée qu'une seule fois.

Si l'on veut ajuster la date et l'heure à la demande, il faudra imaginer une solution plus souple (boutons, encodeur rotatif, télécommande, écran tactile, etc.).

2.6.4.4 NTP

Le Protocole NTP (Network Time Protocol) permet de récupérer l'heure par le réseau. Il est réservé aux cartes possédant une interface WIFI ou celles équipées d'une carte Ethernet.

ARDUINO : NTP sur Ethernet

2.6.5. La communication en réseau

Il existe différents moyens de communiquer en réseau avec un ARDUINO :

  • carte Ethernet (filaire) 
  • NRF24L01 (radio 2.4GHz)
  • ESP8266 ou ESP32 (WIFI)

Souvent il y a confusion entre NRF24L01 et WIFI, qui utilisent tous deux la bande de fréquence 2.4GHz. Le WIFI permet de communiquer à l'aide des protocoles standards utilisés sur Internet (TCP, UDP, etc.), ce que ne permet pas le NRF24L01.

Voir la table des matières : Communication

Fuyez les architectures du type ARDUINO + ESP8266, ou les cartes dites "UNO WIFI". Un ESP8266 seul, ou mieux, un ESP32 vous fera gagner beaucoup de temps de codage, grâce à leurs librairies très élaborées et leur système de fichier FLASH (spiffs, littlefs), dans lequel on pourra stocker des fichiers HTML, des images, etc.

Penser également au BlueTooth, qui permet une communication point à point (un ARDUINO avec module BlueTooth, ou un ESP32, avec un smartphone).

2.6.6. Pas assez d'entrées / sorties numériques ?

Parfois, un ARDUINO ne possède pas assez d'entrées / sorties pour le projet en vue. Et l'on a pas forcément envie de s'encombrer d'une carte MEGA.

Cet article vous dira comment en ajouter :

https://riton-duino.blogspot.com/2019/02/les-extensions-de-gpios-de-larduino.html

Ceci aussi valable pour les ESP8266 et ESP32, à condition de choisir un module capable d'être alimenté sous 3.3V (c'est majoritairement le cas des expanders).

2.6.7. Pas assez d'entrées / sorties analogiques ?

Si l'on a besoin d'entrées analogiques supplémentaires, un ADC sera nécessaire, un module ADS1115 (4 canaux sur bus I2C) par exemple, que l'on peut trouver facilement :

ADS1115 (16 bits)

L'ADS1115 est un ADC 16bits, il offre une plus grande précision que l'ADC de l'ARDUINO (10bits).

2.6.8. Pas assez de mémoire ?

Tout d'abord il ne faut pas espérer augmenter la quantité de mémoire réservée au code. Dans ce cas, seul un processeur possédant plus de mémoire FLASH pourra nous venir en aide.

On est souvent tenté d'ajouter une carte mémoire SD ou microSD à un ARDUINO lorsque l'on a besoin de stocker de grandes quantité de données. La fiabilité n'est pas toujours au rendez-vous. Tout dépend de la SD. Il convient d'en essayer plusieurs.

Pour un de mes projets, un serveur WEB, une Kingston SDHC 4Gb produisait des défauts aléatoires : fichier non trouvé, présence de caractères étranges lors de lectures. Avec une Sandisk Extreme SDHC le résultat s'est très nettement amélioré.

La SD n'est pas la seule solution. Souvent, une mémoire FLASH SPI de quelques Mo sera plus que suffisante.

Et il existe des modules, tout aussi faciles à raccorder qu'un module SD :

Module W25Q128 (16 Mo)

J'en parle ici :

ARDUINO : Stockage en Flash SPI

2.6.9. Besoin d'une vraie sortie analogique ?

Comme on l'a vu plus haut, la technique PWM permet de faire varier la luminosité d'une LED par exemple. Mais le signal qu'elle reçoit n'est pas une tension continue :

La luminosité est perçue comme étant continue car la rétine a une inertie importante.

Si l'on a besoin d'une vraie tension continue on peut utiliser trois solutions :

  • un signal PWM + un filtrage RC (résistance + condensateur)
  • un potentiomètre numérique (un AD8400 par exemple)
  • un DAC

Un filtrage par réseau RC aura une inertie importante et sera peu réactif. Un potentiomètre numérique revient assez cher. Un DAC est très bon marché.

Sur un ARDUINO, il est possible d'en ajouter un sur le bus I2C :

Module DAC 12 bits MCP4725

Voir ce projet : Alimentation 3.7V Numérique

A savoir : l'ESP32 possède un DAC.

2.6.10. Interruptions et notions avancées

Ces sujets débordent un peu du cadre "comment bien débuter". Il est difficilement accessible aux débutants.

J'ai préféré les regrouper dans un article à part :

Lorsque l'on désire aller un peu plus loin que le simple assemblage de cartes et de modules, il est nécessaire de posséder un peu de matériel.

3.1. Alimentation

Si l'on désire réaliser des montages avec de la puissance, une alimentation réglable peut être utile pendant la phase de prototypage :

Alimentation 5-30V 5A

Ensuite, la réalisation finale pourra adopter une alimentation fixe :

Bloc secteur 5V 1A USB

Alimentation secteur 5V 3W pour PCB

Alimentation secteur 12V 25W

Si l'on envisage de laisser une alimentation secteur branchée 24H/24, un modèle de qualité s'impose :

3.2. Multimètre

Ne pas hésiter à faire l'acquisition d'un multimètre, qui permettra de mesurer tensions, courants, résistances, condensateurs, etc.

Cet article en parle :

Acheter un multimètre

3.3. Ampèremètre USB

Un ampèremètre USB permet de mesurer la tension USB et surtout le courant consommé par une carte ARDUINO.

3.4. Testeur

Ce petit testeur de composants permet beaucoup de choses pour un prix ridicule :

Plus de détails ici :

Testeur de composants : le GM328

3.5. Fer à souder

Pour ceux qui désirent réaliser des projets plus aboutis et finalisés, un fer à souder sera indispensable :

Je vous renvoie à cet article :

Fer à Souder : recommandations

4. Liens utiles

https://zestedesavoir.com/tutoriels/686/arduino-premiers-pas-en-informatique-embarquee/ 

https://www.locoduino.org/ 

Le forum ARDUINO en français, où l'on pourra poser des questions :

https://forum.arduino.cc/c/international/francais/49

5. Conclusion

J'espère avoir aidé certains débutants à se mettre en selle, ou les avoir motivés pour tenter un premier contact.

Ce blog comporte une table des matières. Il ne faut pas hésiter à la parcourir.


Cordialement

Henri


6. Mises à jour


23/05/2021 : ajout des LEDs APA102
20/06/2021 : 2.6.2.2. Mesure de courant continu
                      2.6.2.3. Mesure de courant continu avec un INA226
                      2.6.2.4. Mesure de courant alternatif
                      1.5.3.3. Clavier matriciel
                      1.5.3.4. Ecran TFT tactile
                      1.5.3.5. Interface WEB
22/06/2021 : 2.4.1. Les commentaires
                      2.4.4.3. Constantes
                      2.4.5. Les macros et directives
                      2.4.7. Les tableaux
                      2.4.13. Les structures
                      2.4.14. Les pointeurs