Arduino : Comment Gérer Le Temps

Comme nous l'avons dit précédemment, lorsque vous utilisez la fonction delay() ou delayMicroseconds() tout le programme s’arrête. Ce n'est pas toujours un souci, si le programme n'a rien d'autre à effectuer, mais dans certains cas c'est gênant par exemple si vous souhaitez continuer à surveiller une entrée et répondre rapidement.

Par exemple, nous voulons faire clignoter une LED à une certaine fréquence, sauf si un bouton est appuyé. On pourrait modifier le programme précédent de clignotement ainsi :

Image non disponible
Sélectionnezconst byte brocheLed = 3; // le N° de broche de la LED const byte brocheBouton = 2; // le N° de broche du bouton void setup() { pinMode(brocheLed, OUTPUT); // on met la broche en sortie pinMode(brocheBouton, INPUT_PULLUP); // broche en entrée, activation du pullup (pin HIGH par défaut) } void loop() { if (digitalRead(brocheBouton) == HIGH) { // si le bouton n'est pas appuyé digitalWrite(brocheLed, HIGH); // allume la LED delay(1000); // bloquer pendant 1000 millisecondes (1s) digitalWrite(brocheLed, LOW); // éteint la LED delay(1000); // bloquer pendant 1000 millisecondes (1s) } }

Le souci cependant c'est que si on appuie sur le bouton juste après avoir allumé la LED, rien ne va se passer et si on ne le tient pas appuyé plus de 2 secondes, le code ne verra pas la broche à LOW lors du prochain tour de boucle : notre programme n'est pas réactif, l'expérience utilisateur est mauvaise.

Pour corriger cela, il faut écrire un programme qui ne va pas bloquer le microcontrôleur.

On va donc commencer par écrire un clignotement qui ne bloque pas, sans delay(). Il suffit de mémoriser le moment de dernier changement d'état de la LED (debut) et laisser la LED dans cet état jusqu'à ce qu'une certaine durée se soit écoulée. À ce moment, on inverse à nouveau l'état de la LED, on note ce nouveau moment et on recommence.

Image non disponible
Sélectionnezconst byte brocheLed = 3; // le N° de broche de la LED unsigned long debut; void inverserLed() { // on inverse l'état de la broche if (digitalRead(brocheLed) == HIGH) { digitalWrite(brocheLed, LOW); // éteint la LED } else { digitalWrite(brocheLed, HIGH); // allume la LED } // on mémorise le moment de changement d'état debut = millis(); } void setup() { pinMode(brocheLed, OUTPUT); // on met la broche en sortie inverserLed(); // on active la LED } void loop() { unsigned long maintenant = millis(); if (maintenant - debut >= 1000) { // si plus d'une seconde s'est écoulée depuis le début du changement d'état inverserLed(); // alors on inverse l'état } }

Tout le secret est donc dans le test :

Sélectionnezif (maintenant - debut >= 1000)

que l'on répète dans la boucle. Le calcul dit : « si la durée écoulée entre le début et maintenant dépasse 1000 millisecondes alors… ».

Vous remarquez qu'il n'y a pas de else. Donc si le temps n'est pas écoulé, on ne fait rien et la boucle va juste reboucler et on va continuer à tester si c'est le bon moment. Ça ne change donc pas trop du code précédent, on clignote, mais la grande différence, c'est que l'on peut donc effectuer d'autres traitements si le temps n'est pas écoulé.

Par exemple, on pourrait dire « si le bouton est appuyé, éteindre la LED et ne plus clignoter ».

Image non disponible
Sélectionnezconst byte brocheLed = 13; // le N° de broche de la LED const byte brocheBouton = 2; // le N° de broche du bouton unsigned long debut; void inverserLed() { // on inverse l'état de la broche if (digitalRead(brocheLed) == HIGH) { digitalWrite(brocheLed, LOW); // éteint la LED } else { digitalWrite(brocheLed, HIGH); // allume la LED } // on mémorise le moment de changement d'état debut = millis(); } void setup() { pinMode(brocheLed, OUTPUT); // on met la broche en sortie pinMode(brocheBouton, INPUT_PULLUP); // broche en entrée, activation du pullup (pin HIGH par défaut) inverserLed(); // on active la LED } void loop() { if (digitalRead(brocheBouton) == HIGH) { // si le bouton n'est pas appuyé on regarde s'il faut clignoter unsigned long maintenant = millis(); if (maintenant - debut >= 1000) { // si plus d'une seconde s'est écoulée depuis le début du changement d'état inverserLed(); // alors on inverse l'état } } else { // le bouton est appuyé digitalWrite(brocheLed, LOW); // on s'assure que la LED est éteinte } }

Dans ce code, on va regarder si c'est le moment de clignoter seulement si le bouton n'est pas appuyé, sinon on éteint la LED. Comme nous n'avons plus de delay(), le code ne bloque jamais et donc notre programme sera maintenant très réactif : dès que vous pressez le bouton, la LED s'éteint et dès que vous le relâchez, elle se remet à clignoter.

Une erreur fréquente est de prendre un int ou unsigned int comme type de variable pour représenter le temps (debut, maintenant). Sur un UNO ou MEGA un int ne peut pas aller au-delà de 32 767 et un unsigned int au-delà de 65 535 – soit environ 33 ou 66 secondes. Donc si vous testez votre code pendant quelques secondes, vous n'allez rien déceler, mais en production, très vite votre code aura un comportement anormal. Il est donc important de toujours prendre un unsigned long (que l'on note aussi uint32_t pour unsigned int sur 32 bits) pour représenter le temps.

Vous remarquerez que le test sur la durée est écrit sous la forme maintenant - debut >= 1000. On voit souvent des codes avec maintenant >= debut + 1000 et c'est mathématiquement similaire dans le cas général. Cependant, il faut retenir la première formulation et bannir la seconde si votre code doit tourner longtemps. En effet, quand vous approcherez des ~50 jours fatidiques, le unsigned long qui permet de stocker debut va s'approcher de sa valeur maximale. Rajouter 1000 à cette valeur va donc faire déborder ce qui est représentable et on se retrouvera avec une valeur proche de zéro.

La valeur de millis() (maintenant) sera elle très grande (proche des 50 jours) et donc le test maintenant >= debut + 1000 sera tout de suite vrai, sans avoir attendu les 1000 ms. En prenant l'approche par soustraction, même lorsque maintenant a débordé et retourné à 0 et que debut est proche de 50 jours, maintenant - debut représentera bien la bonne durée, et le test sera valable.

Tag » Arduino Compter Le Temps