Débuter avec Arduino

Partie 4 : les signaux analogiques.

Tutoriels, exercices, documentations et références


SECONDAIRE | DIFFICULTÉ MOYENNE | 6 À 8 HEURES


Déjà la partie 4, bravo ! Si vous êtes ici, c'est que vous savez :
  1. Lire et écrire un signal numérique sur les prises 0 à 13 d'Arduino graâce aux fonctions digitalRead() et digitalWrite().
  2. Vous savez construire un circuit électronique programmable comportant un capteur (entrée, "INPUT") et une sortie (effecteur, OUTPUT).
  3. Vous avez compris le fonctionnement des variables.
  4. Vous avez obtenu au moins 90% au quizz de la partie 3 !

Parfait, vous êtes prêt pour la suite ! Ici vous allez apprendre à lire et écrire des signaux analogiques, ce qui va vous ouvrir vers encore plus de projets possibles avec Arduino !


Lire un signal analogique.


Une nouvelle simulation ! Cette fois-ci nous allons observer le signal brut sortant d'un capteur analogique : un potentiomètre.
Les potentiomètres sont ces boutons rotatifs que l'on retrouve un peu partout, en particulier les boutons de volume dans les systèmes de sons ou les amplificateurs. Parfois ce sont des "dimmers", ou "gradateurs" en français.
En gros un potentiomètre est un robinet : si on le tourne dans un sens on diminue la tension électrique, si on le tourne dans l'autre sens on augmente la tension électrique. C'est une résistance variable.

moniteur série
Figure 1
moniteur série
Figure 2
moniteur série
Figure 3
Pour faire fonctionner cette simulation, cliquez d'abord sur Code (figure 1).
Une fois le code affiché, vous verrez tout en bas à gauche une icône Serial Monitor. Cliquez dessus, c'est le moniteur série qui apparaît (figure 2).
Démarrez ensuite la simulation avec le bouton Start Simulation (figure 3).
Vous pouvez déplacer le circuit de sorte à pouvoir accéder au potentiomètre (comme dans la figure 3).
Faites tourner le potentiomètre dans un sens ou dans l'autre. Est-ce que vous voyez des chiffres défiler dans le moniteur série ? Voyez vous les chiffres varier ? Devenir plus grands ou plus petits ? (figure 3, fenêtre encadrée en rouge)


Voyons maintenant un peu plus en détail ce qu'est ce moniteur série, à quoi il sert et ce qu'il nous affiche.

Le moniteur série.

Commençons par une définition théorique du moniteur série.
Le moniteur série est une fenêtre distincte qui permet de communiquer avec Arduino en recevant et en envoyant des données "série" ("serial" en anglais).
Les données série sont envoyées sur un seul fil par le câble USB et consistent en une série de 1 et de 0. Les données peuvent être envoyées dans les deux sens (dans notre cas, sur deux fils : d'Arduino vers l'ordinateur et de l'ordinateur vers Arduino).
Un moniteur série permet de communiquer avec Arduino. Il est très utile et il ne faut pas se passer de l'utiliser !
Il permet par exemple :
  • De vérifier en temps réél le signal sortant d'un capteur analogique ou numérique (c'est l'exemple ci-dessus, on vérifie en permanence le signal sortant du potentiomètre - capteur analogique).
    On peut vérifier ce signal pour, entre autre :
  • Commander Arduino à distance (en envoyant des commandes avec son clavier).
  • Faire communiquer Arduino avec des logiciels présents sur votre ordinateur (exemple : produire des effets visuels en fonction de l'environnement à la manière de ce qu'on peut voir sur le pont Jacques Cartier à Montréal).

Pour des performances artistiques visuelles ou musicales, la communication série est indispensable. La plupart des logiciels de musique ou d'édidion vidéo sont capables de comprendre les données transmises par la communication série. Le moniteur série affiche les données transmises par cette communication.
Dans l'exemple de simulation ci-dessus, nous pourrions utiliser les valeurs du potentiomètre transmises par le moniteur série pour contrôler une vidéo (qui serait lue par un logiciel spécifique), modifier le volume ou la tonalité d'une chanson composée dans Garage Band, Ableton ou autre logiciel d'édition musicale.
C'est le côté "sans limites" d'Arduino : nous pourrions contrôler n'importe quel logiciel compatible avec la communication série grâce à des capteurs.

Le programme.


Le voici (vous pouvez aussi le consulter dans la simulation):
int valeur = 0;
int potentiometre = A0;

void setup()
{
  pinMode(potentiometre, INPUT);
  Serial.begin (9600);
}

void loop()
{
  valeur = analogRead (potentiometre);
  Serial.println (valeur);
}

Essayons de le comprendre ensemble.
Tout d'abord, la déclaration des variables.

int valeur = 0;
int potentiometre = A0;

Nous déclarons ici deux variables de types entier ("int").
  • int valeur = 0; : cette variable permettra de stocker la valeur du potentiomètre lue par Arduino et que nous voudrons afficher dans le moniteur série.
  • int potentiometre = A0; : cette variable permet de dire à Arduino que le mot "potentiometre" est égal à "A0". Le potentiomètre est branché sur la prise A0.
Voilà c'est assez simple, passons à la suite.

La partie void setup().

void setup()
{
  pinMode(potentiometre, INPUT);
  Serial.begin (9600);
}

Elle ne comporte elle aussi que deux lignes :
  • pinMode(potentiometre, INPUT); : nous indiquons à Arduino que le potentiomètre est une "entrée". Étant donné que nous avons préalablement déclaré que potentiometre = A0, cette ligne équivaut à dire pinMode (A0, INPUT);.
  • Serial.begin (9600); : voici une nouveauté. Cette ligne sert à démarrer la communication série. La fonction Serial.begin() prend un argument entre ses parenthèses : la vitesse de communication. Cette vitesse s'exprime en "bauds", une unité largement utilisée dans le domaine des communications électroniques (téléphonie, réseaux etc). La définition théorique est un peu complexe mais disons que 9600 bauds correspondent à peu près à 9600 bits/seconde. Nous utilisons cette valeur car c'est la valeur par défaut lorsqu'on ouvre le moniteur série. Ce n'est pas la plus rapide mais elle suffira largement à nos besoins.
Nous venons donc d'initialiser le programme en indiquant le comportement de la pin "potentiometre" (A0) (pinMode (potentiometre, INPUT);)et en démarrant la communication série entre Arduino et l'ordinateur (Seriel.begin (9600);).

La partie void loop().

void loop()
{
  valeur = analogRead (potentiometre);
  Serial.println (valeur); 
}

Elle ne comporte elle aussi que deux lignes relativement simples :
  • valeur = analogRead(potentiometre); : cette commande nous permet de lire la valeur analogique du potentiomètre et de stocker cette valeur dans la vriable "valeur".
  • Serial.println (valeur); : cette ligne permet d'imprimer à l'écran, dans le moniteur série, la variable "valeur".
Remarque : on peut afficher de différentes manières les données dans le moniteur série.
  • Ici nous avons utilisé la commande Serial.println(), le terme "println" signifiant "print line". Les données s'afficheront en colonne avec un retour à la ligne à chaque nouvelle donnée affichée.
  • Nous aurions pu utiliser la commande Serial.print(). Dans ce cas il n'y a pas de retour à la ligne et toutes les données s'affichent les unes après les autres sur une seule ligne. Cela peut vite devenir illisible. Essayez pour voir !
  • Seules les prises A0 à A5 sont capables de lire des signaux analogiques. Ce sont les prises "ANALOG IN".



Écrire un signal analogique.


Arduino permet aussi de produire des signaux analogiques. Mais attention, toutes les prises ne peuvent en produire !
Seules les prises identifiées par un tilde (~) sont capables de produire des signaux analogiques. On les appelle les prises PWM et ce sont les prises numéros 3, 5, 6, 9, 10 et 11.
Afin de mieux comprendre, voici une simulation permettant de moduler l'éclairage d'une DEL avec le potentiomètre.


Le programme.

Pas vraiment de difficultés, et je suis sur que vous avez déja compris en lisant le programme.
  • Premièrement nous déclarons une nouvelle variable : int del = 3; (ligne 3). La DEL est branchée sur la prise 3 et nous associons la valeur "3" au mot "DEL".
  • Dans le void setup(), une nouvelle ligne : pinMode(del, OUTPUT); (ligne 8). On indique à Arduino que la DEL (donc valeur 3 pour la prise #3) est une sortie.
  • Dans le void loop(), une nouvelle ligne : analogWrite (del, valeur); (ligne 16). Voici la grande nouveauté !
Pour écrire un signal analogique, on utilise la fonction analogWrite (# prise, valeur), qui prend 2 arguments :
  • Le numéro de la prise sur laquelle écrire le signal (ici prise #3, également identifée sous le nom "del").
  • La valeur que doit prendre la prise. Cette valeur est comprise entre 0 et 255 :
    • 0 : la prise est éteinte.
    • 255 : la prise est allumée à 100%.
    • 128 : la prise est allumée à 50%.
Amusez-vous à modifier les paramètres : changez "valeur" pour un nombre compris entre 0 et 255. Vous verrez qu'avec 0 la DEL est éteinte, avec 255 la DEL est allumée au maximum.
Essayez des valeurs supérieures à 255. Qu'observez vous ? Est-ce que la DEL est capable de plus éclairer ?

Maintenant que vous savez lire et écrire un signal analogique, il est temps de comprendre c'est quoi, un signal analogique !

La nature des signaux analogiques.


Nous en avons déjà parlé dans la partie 1 : un signal analogique est un signal électrique qui peut prendre n'importe quelle valeur entre 0 et 5 Volts. Il est opposé au signal numérique (ou digital).

Signaux analogiques entrants - analogRead()


Dans la simulation précédente, vous pouvez observer que lorsque vous tournez le potentiomètre, les valeurs affichées dans le moniteur série varient. Avez-vous noté les valeurs minimales et maximales que vous pouvez obtenir ? C'est étrange, le signal varie théoriquement entre 0 et 5 Volts mais ça affiche des valeurs entre 0 et 1023... Regardons ça de plus près !
Afin de pouvoir traiter le signal analogique de façon numérique, Arduino va convertir la tension reçue dans la prise analogique en un signal numérique codé sur 10 bits (c'est à dire une suite de 10 chiffres binaires - 0 ou 1 - soit 210 possibilités = 1024). Arduino va donc découper le signal électrique (0-5 Volts) en 1024 tranches :

SIGNAL ANALOGIQUE ENTRANT
0 Volts → signal 0
5 Volts → signal 1023

Chaque tranche de signal correspond à 5 Volts / 1024 = 0,0049 Volts, soit 4,9 mV par tranche de signal.
C'est une résolution correcte mais sachez que parfois, certains signaux émis par des capteurs sont extrèmement faibles, de l'ordre de quelques mV. Ceci dit on ne fait pas de l'électronique de pointe alors les performances d'Arduino sont suffisantes pour nous !

Signal analogique sortant - analogWrite()


Pour les signaux analogiques sortant c'est encore une autre histoire !
Dans ce cas le signal est codé de manière numérique sur 8 bits. C'est à dire une succession de 8 chiffres binaires - 0 ou 1 - soit 28 possibilités = 256. C'est un signal PWM : pulse with modulation.
En fait pour écrire un signal analogique, Arduino ne produit pas une tension variable, il produit plutôt des "pulsations" électriques dont la fréquence permet de simuler un signal électrique variable.

SIGNAL ANALOGIQUE SORTANT : PWM
0 Volts → signal 0
5 Volts → signal 255

Seules les prises PWM peuvent produire un tel signal : les prises 3, 5, 6, 9, 10 et 11, identifiées avec un tilde (~).

Il n'est pas nécessaire à votre niveau de parfaitement comprendre la nature d'un signal PWM, mais voici tout de même une manière de l'imaginer.

le signal PWM
  • Sur chaque prise PWM, Arduino émet des pulsations électriques, dont la férquence est 490 Hertz sur les prises 3, 9, 10 et 11 (soit 490 pulsations par secondes) et 980 Hertz sur les prises 5 et 6 (980 pulsations par secondes).
  • Prenons le cas des pin 5 et 6 qui ont une fréquence de 980 Hz. Arrondissons à 1000 pour simplifier les calculs. Si la prise "vibre" 1000 fois par seconde, la période est de 1 seconde / 1000 = 1 milliseconde. Dans le scéma ci dessus, le terme "période" correspondrait donc à 1 ms.
  • Cas A : vous avez écrit analogWrite (0). La pulsation électrique émise par Arduino dure 0% de 1 ms : le signal moyen est de 0 Volts.
  • Cas B : vous avez écrit analogWrite (67). La valeur "67" correspond à 25% du signal maximum (signal max = 255, 25% de 255 = 67). La pulsation électrique émise par Arduino dure 25% de 1 ms : le signal moyen est de 1,25 Volts (25% de 5 Volts).
  • Cas C : vous avez écrit analogWrite (255). La valeur "255" correspond à 100% du signal maximum (signal max = 255, 100% de 255 = 255). La pulsation électrique émise par Arduino dure 100% de 1 ms : le signal moyen est de 5 Volts (100% de 5 Volts).
Reprenons la toute dernière simulation, la revoici :


Vous souvenez-vous ? Je vous avez dit que passé la valeur 255 la DEL ne peut pas plus éclairer. Comprenez vous pourquoi maintenant ?
La fonction analogRead() lit un signal et le converti en une valeur numérique comprise entre 0 et 1023.
La fonction analogWrite() produit un signal à partir d'une valeur numérique comprise entre 0 et 255.
Dans notre programme, on prend le signal provenant du potentiomètre grâce à la fonction analogRead()et on le stocke dans la variable "valeur". La variable "valeur" prendra donc une valeur entre 0 et 1023. On utilise ensuite directement "valeur" pour éclairer la DEL avec la fonction analogWrite(). Cependant, la fonction analogWrite() ne permet que d'envoyer une valeur comprise entre 0 et 255. Donc chaque fois que la variable "valeur" sera supérieure à 255, la DEL ne pourra pas plus éclairer.
Comment résoudre ce problème ?
  • De façon mathématique toute simple : pour passer de l'intervalle mathématique [0 1023] à [0 255], il suffit de diviser par 4. Nous pourrions alors modifier le "void loop()" comme ceci :

    void loop()
    {
      valeur = analogRead (potentiometre);
      Serial.println (valeur);
      analogWrite (del, valeur/4);
    }
    

    Essayez pour voir !!
  • Arduino à une fonction toute prête pour convertir des valeurs ! C'est la fonction map(variable, x départ, y départ, x arrivée, y arrivée). Constatez que la fonction map() prend 5 arguments :
    • le nom de la variable à modifier (dans notre cas, "valeur").
    • x départ et y départ : les valeurs minimales et maximales de la variable au départ (dans notre cas, 0 et 1023).
    • x arrivée et y arrivée : les valeurs minimales et maximales de la variable que l'on veut obtenir (dans notre cas, 0 et 255).
    Nous écririons dans notre exemple :
    map (valeur, 0, 1023, 0, 255)
    Ce qui nous donnerai le bout de code suivant dans le "void loop()":

    void loop()
    {
      valeur = analogRead (potentiometre);
      Serial.println (valeur);
      valeur = map (valeur, 0, 1023, 0, 255);
      analogWrite (del, valeur);
    }
    

    Essayez pour voir !
    Notez que dans cet exemple nous réassignons à la variable "valeur" sa nouvelle valeur. C'est comme si j'enlevai les chaussettes vertes de ma valise pour en mettre des rouges. La variable est toujours là, elle s'appelle toujours "valeur" mais elle est recalculée.
Si vous avez compris la nature d'un signal PWM bravo, vous allez devenir de vrais électroniciens ! Si ce n'est pas le cas ce n'est pas grave, mais retenez qu'un signal PWM est une façon, pour une machine numérique programmable, de simuler un signal analogique, soit une tension variable.
Pour ceux qui veulent aller plus loin, consulter la documentation officielle d'Arduino : fonction analogRead() et fonction analogWrite().


Résumé de la partie 4.


Voici ce que vous devriez avoir retenu de ce chapitre :
  • La fonction analogRead() permet de lire un signal analogique.
  • Un signal analogique ne peut être lu que sur les prises A0 à A5 (ANALOG IN).
  • Un signal analogique lu est converti par Arduino en un nombre entier compris entre 0 et 255.
  • La fonction analogWrite() permet de produire un signal analogique.
  • Un signal analogique ne peut être produit que par les prises PWM indiquée par le symbole ~ : prises 3, 5, 6, 9, 10 et 11.
  • Un signal analogique écrit est converti par Arduino en un nombre entier compris entre 0 et 1023.
  • La fonction Serial.begin() permet de démarrer une communication série. Elle se place dans le "setup".
  • La fonction Serial.println() permet d'afficher , dans le moniteur série, la variable indiquée en argument.
  • La fonction map() permet de convertir automatiquement une variable d'un intervalle de départ vers un intervalle d'arrivée.

Le tableau suivant résume les fonctions apprises durant ce chapitre :

Syntaxe Emplacement Utilisation Argument(s)
analogRead (a); loop() Lecture analogique de la prise "a" a = nom ou # de la prise analogique (A0 à A5) lue
analogWrite (a, b); loop() Ecriture d'un signal analogique sur la pin indiquée en argument a = nom ou # de prise
b = valeur du signal à écrire (compris entre 0 et 255)
Serial.begin (9600); setup() Initialisation la communication série a la vitesse indiquée en argument a = vitesse de la communication série : 9600 par défaut
Serial.println (a); loop() IAffiche dans le moniteur série la variable indiquée en argument a = nom de la variable à afficher
map (a, x1, y1, x2, y2); loop() Modifie l'intervalle de valeur que peut prendre la variable indiquée en argument a = variable
[x1 x2] = intervalle dans lequel se situe la variable "a" au départ
[y1 y2] = intervalle dans lequel se situe la variable "a" à l'arrivée


Commentaires ?