Des capteurs plus évolués

Comme nous avons pu le voir plus tôt, certains capteurs transmettent l’information sous forme d’une donnée électrique qui varie : la résistance ou la tension. Cependant, certains capteurs envoient l’information de manière "codée", afin qu’elle soit plus résistante au bruit (perturbations) et garantir le signal transmis. Parmi ces méthodes de transmission, on retrouve l’utilisation d’une PWM, de la fréquence ou d’un protocole de communication.

Capteur à sortie en modulation de largeur d’impulsion (PWM)

Principe

Vous vous souvenez de la PWM ? Nous l’avons utilisée dans le chapitre sur les sorties analogiques. Dans ce type de signal, l’information est présente dans la durée de l’état haut par rapport à l’état bas. Ici, notre capteur va donc de la même façon coder l’information via une durée d’état (mais elle ne sera pas forcément relative à son état antagoniste). Il est donc nécessaire de connaitre les caractéristiques du capteur pour pouvoir interpréter le signal correctement.

En effet, si on prend le signal sans rien savoir du capteur, comment déterminer ce que 20ms d’état haut signifie par exemple ? Pour cela, ce type de composant doit toujours être utilisé avec sa documentation technique, afin de déterminer des paramètres comme ses bornes inférieure et supérieure de mesure. Mais c’est aussi vrai pour les autres (si vous tombez sur une résistance variable sans rien en connaître, vous devrez vous farcir une belle séance d’étalonnage pour l’identifier !).

Utilisation

Prenons un cas simple. Imaginons que nous avons un capteur de température qui nous renvoie l’information suivante : "La température en °C Celsius est proportionnelle à la durée d’état haut. La plage de mesure va de 0°C à 75°C pour une durée d’état haut de 0ms à 20ms".

Nous avons donc une relation proportionnelle entre une température et une durée. Nous pouvons alors déduire une règle mathématique pour faire la conversion degrésdurée.

En effet, on a les équivalences suivantes :

  • 0C0ms0%0^{\circ}C \Leftrightarrow 0ms \Leftrightarrow 0\%
  • 75C20ms100%75^{\circ}C \Leftrightarrow 20ms \Leftrightarrow 100\%

Une simple règle de trois nous donne : x=7520=3.75C/msx = \frac{75}{20} = 3.75^{\circ}C/ms ce qui signifie que pour chaque milliseconde, on a 3.75°C .

Voyons maintenant comment l’utiliser…

Dans la pratique avec Arduino

Bon, c’est pas mal on a le côté théorique de la chose, mais ça ne nous dit toujours pas comment on va l’exploiter avec notre Arduino. En effet, générer une PWM on sait faire, mais mesurer une durée d’état haut ça on ne sait pas ! Et bien rassurez-vous, comme d’habitude c’est assez simple. En effet, il existe une fonction dans le framework Arduino qui sert exactement à cela, mesurer une durée d’état haut ou bas.

Cette fonction s’appelle pulseIn(). Elle prend simplement en paramètres la broche sur laquelle vous voulez faire la mesure et l’état que vous voulez mesurer (HIGH ou LOW). En option, un troisième paramètre permettra de spécifier un "timeout", un temps maximal à attendre avant de décider que la mesure n’est pas possible. Si le timeout est de 2 secondes et que l’état à mesurer n’a pas commencé 2 secondes après l’appel de la fonction, alors cette dernière retournera 0. Dernier détail, l’intervalle de mesure de la fonction est de 10µs à 3 minutes et renvoie un unsigned long représentant la durée de l’état en microsecondes.

Voilà, vous savez tout pour utiliser cette fonction ! Il ne faut pas oublier cependant que la broche sur laquelle nous allons faire la mesure doit être placée en INPUT lors du setup() ;) .

Reprenons maintenant l’exemple commencé ci-dessus.

Pour mesurer la température, nous allons mesurer l’état haut en sachant que celui-ci sera proportionnel à la température. Un code simple serait donc :

const char capteur = 2; // en admettant que le capteur de température soit sur la broche 2

void setup()
{
   pinMode(capteur, INPUT);
   Serial.begin(9600); // pour afficher la température
}

void loop()
{
   unsigned long duree = pulseIn(capteur, HIGH, 25000);
   // dans notre exemple la valeur est dans l'intervalle [0, 20000]
   float temperature = duree*0.00375; // 3.75 °C par ms donc 0.00375 °C par µs

   /* Dans notre cas, on n'utilise pas "map()" car la fonction fait des arrondis,
   ce qui risquerait de nous faire perdre de l'informations */

   Serial.print("Duree lue : ");
   Serial.println(duree, DEC);
   Serial.print("Temperature : ");
   Serial.println(temperature);

   delay(200); // pour ne pas spammer la voie série
}
Mise en pratique de pulseIn
Simulation de l’exemple

Si comme moi vous n’avez pas de capteur retournant une PWM, voici un petit montage tout simple permettant de tester ce concept.

Pour cela, nous allons utiliser une PWM de l’Arduino ! En effet, on sait depuis le chapitre [Sorties analogiques](https:// zestedesavoir.com/tutoriels/537/arduino-premiers-pas-en-informatique-embarquee/745/les-grandeurs-analogiques/3432/et-les-sorties-analogiques-enfin-presque/) faire varier un rapport cyclique dans une PWM, on va donc l’appliquer ici pour tester pulseIn().

Pour cela, reliez une broche PWM à la broche qui vous sert de capteur puis essayez de réaliser ce que l’on vient de voir.

La fréquence de la PWM via Arduino est d’environ 490Hz, ce qui signifie que la durée d’état haut pourra varier entre 0ms et 2,04ms

const char capteur = 2; // broche capteur
const char emetteur = 3; // broche PWM

void setup()
{
   pinMode(capteur, INPUT);
   pinMode(emetteur, OUTPUT);

   Serial.begin(9600);
}

void loop()
{
   analogWrite(emetteur, 127); // test avec une valeur moyenne : environ 1ms
   unsigned long duree = pulseIn(capteur, HIGH);

   Serial.print("Duree : ");
   Serial.println(duree, DEC); // vérifie qu'on a bien la durée attendue

   delay(250);
}
Simulation d’un capteur PWM et exploitation de pulseIn
Étude de cas : le capteur de distance SRF05

Un tutoriel plus complet sur ce capteur peut être trouvé ici : https://zestedesavoir.com/tutoriels/539/realiser-un-telemetre-a-ultrasons/

Prenons un exemple, le télémètre ultrason SRF05 dont la doc. technique a été retranscrite ici.

Ce composant est l’exemple classique du capteur renvoyant un créneau codant l’information. En effet, ce dernier mesure ce que l’on appelle un temps de vol. Explications ! Le SRF05 est un télémètre ultra-son. Pour mesurer une distance, il compte le temps que met une onde pour faire un aller-retour. Un chronomètre est déclenché lors du départ de l’onde et est arrêté lorsque l’on détecte le retour de l’onde (une fois que celle-ci a "rebondi" sur un obstacle). Puisque l’on connait la vitesse de propagation (V) de l’onde dans l’air, on peut déterminer la distance (d) nous séparant de l’objet. On a donc la formule : v=dtv = \frac{d}{t} soit d=t×vd = t \times v .

Le temps mesuré correspond à l’aller ET au retour de l’onde, on a donc deux fois la distance. Il ne faudra pas oublier de diviser le résultat par deux pour obtenir la distance réelle qui nous sépare de l’objet.

Comme expliqué dans la documentation, pour utiliser le sonar il suffit de générer un état haut pendant 10 µs puis ensuite mesurer l’état haut généré par le sonar.

Ce dernier représente le temps que met l’onde à faire son aller-retour. Si l’onde met plus de 30ms à faire son voyage, elle est alors considérée comme perdue et la ligne repasse à LOW.

Et voilà, vous avez maintenant toutes les informations pour faire un petit programme d’essai pour utiliser ce sonar.

Ah, une dernière information… La vitesse d’une onde sonore dans l’air à 15°C est de 340 mètres par seconde. Ça pourrait être utile !

#define VITESSE 340 // vitesse du son 340 m/s

const int declencheur = 2; // la broche servant à déclencher la mesure
const int capteur = 3; // la broche qui va lire la mesure

void setup()
{
   pinMode(declencheur, OUTPUT);
   pinMode(capteur, INPUT);

   digitalWrite(declencheur, LOW);
   Serial.begin(9600);
}

void loop()
{
   digitalWrite(declencheur, HIGH);
   delayMicroseconds(10); // on attend 10 µs
   digitalWrite(declencheur, LOW);

   // puis on récupère la mesure
   unsigned long duree = pulseIn(capteur, HIGH);

   if(duree > 30000)
   {
      // si la durée est supérieure à 30ms, l'onde est perdue
      Serial.println("Onde perdue, mesure echouee !");
   }
   else
   {
      // l'onde est revenue ! on peut faire le calcul
      // on divise par 2 pour n'avoir qu'un trajet (plutôt que l'aller-retour)
      duree = duree/2;
      float temps = duree/1000000.0; // on met en secondes
      float distance = temps*VITESSE; // on multiplie par la vitesse, d=t*v

      Serial.print("Duree = ");
      Serial.println(temps); // affiche le temps de vol d'un trajet en secondes
      Serial.print("Distance = ");
      Serial.println(distance); // affiche la distance mesurée
   }

   delay(250);
}
Utilisation d’un capteur à ultrasons
https://www.youtube.com/watch?v=vB4oMEFHZSo

Capteur à signal de sortie de fréquence variable

Voyons maintenant un autre type de sortie très similaire à celui vu ci-dessus, la fréquence. Les capteurs de ce type vont donc vous délivrer une fréquence variable en fonction de la valeur mesurée. Je ne vais pas vous mentir, je n’ai pas d’exemple en tête ! Cependant, il est facile d’imaginer comment les utiliser en prenant en compte ce que l’on vient de voir pour le capteur renvoyant une PWM.

En effet, considérons un capteur nous envoyant un signal de type "créneau" à une fréquence f. Un créneau possède en théorie une durée d’état haut égale à la durée de l’état bas. Si on fait donc une mesure de cette durée d’état haut via pulseIn() vue précédemment, on peut aisément déduire la période (T) du signal (qui sera égale à deux fois la valeur lue) et ainsi la fréquence puisque f=1/Tf = 1/T .

De manière programmatoire, on obtiendra donc le code suivant :

const char capteur = 2; // broche sur laquelle est branchée le capteur

void setup()
{
   pinMode(capteur, INPUT);

   Serial.begin(9600);
}

void loop()
{
   unsigned long duree = pulseIn(capteur, HIGH); // ou LOW, ce serait pareil !
   duree = duree*2; // pour avoir la période complète

   // hop! on calcule la fréquence !
   float frequence = 1.0/duree;
   // passe la fréquence en Hz (car la période était mesurée en µs)
   frequence = frequence*1000000;
   Serial.print("Frequence = ");
   Serial.println(frequence);

   delay(250);
}
Mesurer une fréquence
Exemple / Exercice

Afin de mettre tout cela en pratique, je vous propose un petit exercice pour mettre en œuvre ce dernier point. Peu de matériel est à prévoir.

Principe

Pour cet exercice, je vous propose d’émuler le comportement d’un capteur générant une fréquence variable.

Nous allons utiliser un potentiomètre qui va nous servir de "variateur". Ensuite nous allons utiliser une fonction propre à Arduino pour générer une fréquence particulière qui sera l’image multipliée par 10 de la valeur mesurée du potentiomètre. Enfin, nous allons "reboucler" la sortie "fréquence" sur une entrée quelconque sur laquelle nous mesurerons cette fréquence.

C’est clair ? J’espère !

La fonction tone()

Pour faire cette exercice vous connaissez déjà tout à une chose près : Comment générer une fréquence. Pour cela, je vous propose de partir à la découverte de la fonction tone(). Cette dernière génère une fréquence sur une broche, n’importe laquelle. Elle prend en paramètre la broche sur laquelle le signal doit être émis ainsi que la fréquence à émettre. Par exemple, pour faire une fréquence de 100Hz sur la broche 3 on fera simplement : tone(3, 100);.

Un troisième argument peut-être utilisé. Ce dernier sert à indiquer la durée pendant laquelle le signal doit être émis. Si on omet cet argument, la fréquence sera toujours générée jusqu’à l’appel de la fonction antagoniste noTone() à laquelle on passe en paramètre la broche sur laquelle le signal doit être arrêté.

L’utilisation de la fonction tone interfère avec le module PWM des broches 3 et 11. Gardez-le en mémoire ;) . Cette fonction ne peut pas non plus descendre en dessous de 31Hz.

Vous avez toutes les informations, maintenant à vous de jouer !

Correction

Voici ma correction commentée. Comme il n’y a rien de réellement compliqué, je ne vais pas faire des lignes d’explications et vous laisser simplement avec le code et ses commentaires :diable: !

const char potar = 0; // potentiomètre sur la broche A0;
const char emetteur = 8; // fréquence émise sur la broche 8
const char recepteur = 2; // fréquence mesurée sur la broche 2

void setup()
{
   pinMode(emetteur, OUTPUT);
   pinMode(recepteur, INPUT);

   Serial.begin(9600);
}

void loop()
{
   // fait la lecture analogique (intervalle [0;1023] )
   unsigned int mesure = 100;

   // applique la mesure comme fréquence (intervalle [0;10230] )
   tone(emetteur, mesure*10);

   // mesure la demi-période
   unsigned long periode = pulseIn(recepteur, HIGH);
   // pour avoir une période complète on multiplie par 2
   periode = periode*2;
   // transforme en fréquence
   float frequence = 1.0/periode;
   // passe la fréquence en Hz (car la période était mesurée en µs)
   frequence = frequence*1000000;

   Serial.print("Mesure : ");
   Serial.println(mesure, DEC);
   Serial.print("Periode : ");
   Serial.println(periode, DEC);
   Serial.print("Frequence : ");
   Serial.println(frequence);

   delay(250);
}
Générateur et mesure de fréquence, correction

Capteur utilisant un protocole de communication

Certains capteurs ne renvoient pas l’information sous forme "physique" dans le sens où ils ne renvoient pas quelque chose de mesurable directement, comme un temps ou une tension. Non, ces derniers préfèrent envoyer l’information encapsulée bien au chaud dans une trame d’un protocole de communication.

Le gros intérêt de cette méthode est très probablement la résistance au "bruit". Je ne parle bien sûr pas des cris des enfants du voisin ou les klaxons dans la rue mais bien de bruit électronique. Ce dernier est partout et peut avoir des conséquences ennuyeuses sur vos mesures. Transmettre l’information par un protocole de communication est donc un moyen fiable de garantir que la donnée arrivera de manière intègre jusqu’au destinataire. De plus, on peut facilement coupler cette transmission avec un protocole de vérification simple ou compliqué (comme la vérification de parité ou un calcul de CRC).

Cette partie ne va pas vous enseigner comment utiliser chacun des moyens de communication que nous allons voir. En effet, il s’agit plutôt d’une introduction/ouverture sur l’existence de ces derniers. Ils seront traités de manière indépendante dans des chapitres dédiés comme le fût la voie série.

Quelques protocoles de communication
Voie série / UART

Ce protocole vous devez déjà le connaître par cœur puisque nous l’utilisons presque dans tous les chapitres ! Il s’agit en effet de la voie série via Serial. Rien de réellement compliqué donc tellement vous êtes habitués à le voir ! Ceci est une liaison point-à-point, donc seuls deux composants (l’Arduino et le capteur) peuvent être reliés entre eux directement. Elle est généralement bi-directionnelle, ce qui signifie que les deux composants reliés peuvent émettre en même temps.

I2C

Le protocole I²C (Inter-Integrated Circuit) ou TWI (Two Wire Interface) permet d’établir une liaison de type "maître/esclave". L’Arduino sera maître et le capteur l’esclave (l’Arduino peut aussi être un esclave dans certains cas). Ainsi, l’Arduino émettra les ordres pour faire les demandes de données et le capteur, lorsqu’il recevra cet ordre, la renverra.

Ce protocole utilise 3 fils. Un pour la masse et ainsi avoir un référentiel commun, un servant à émettre un signal d’horloge (SCL) et un dernier portant les données synchronisées avec l’horloge (SDA).

Chez Arduino, il existe une librairie pour utiliser l’I2C, elle s’appelle Wire.

On peut placer plusieurs esclaves à la suite. Un code d’adresse est alors utilisé pour décider à quel composant le maître fait une requête.

https://www.youtube.com/watch?v=5_ZEJNjwF1Q
SPI

Le SPI (Serial Peripheral Interface) est une sorte de combo entre la voie série et l’I2C. Elle prend le meilleur des deux mondes.

Comme en voie série, la liaison est bi-directionnelle et point-à-point1. Cela signifie que Arduino et le capteur sont reliés directement entre eux et ne peuvent que parler entre eux. Cela signifie aussi que les deux peuvent s’envoyer des données simultanément.

Comme en I2C, la liaison est de type maître/esclave. L’un fait une demande à l’autre, le maître transmet l’horloge à l’esclave pour transmettre les données.

Cette transmission utilise 4 fils. Une masse pour le référentiel commun, un fil d’horloge (SCLK), un fil nommé MOSI (Master Output, Slave Input, données partant de l’Arduino et allant vers le capteur) et MISO (Master Input, Slave Output, données partant du capteur et allant vers l’Arduino).

Chez Arduino, il existe une librairie pour utiliser le SPI, elle s’appelle … SPI.

Protocole propriétaire

Ici pas de solution miracle, il faudra manger de la documentation technique (très formateur !). En effet, si le constructeur décide d’implémenter un protocole à sa sauce alors ce sera à vous de vous plier et de coder pour réussir à l’implémenter et l’utiliser.


  1. Afin d’améliorer cette voie série, il existe une autre broche nommée SS (Slave Select) permettant de choisir à quel composant le maître parle. Ainsi la liaison n’est plus limitée à un esclave seulement.

Vous savez maintenant tout sur les capteurs, votre Arduino peut maintenant "sentir" le monde qui l’entoure comme promis en introduction de cette partie. Mais ce n’est pas fini, il existe un grand nombre de capteurs, beaucoup trop important pour en faire une liste exhaustive dans un tutoriel comme celui-ci. Maintenant que vous pouvez percevoir le monde, passons à la suite en essayant d’interagir avec ce dernier grâce à l’utilisation des moteurs.