Voici un tuto qui montre une méthode simple pour supprimer les delay() d'un programme sans utiliser la fonction millis(), grâce à la bibliothèque easyRun.h
Prenons l'exemple d'un bête feu tricolore
const int pinVert = 4;
const int pinOrange = 5;
const int pinRouge = 6;
void setup()
{
Serial.begin(115200);
pinMode(pinVert, OUTPUT);
pinMode(pinOrange, OUTPUT);
pinMode(pinRouge, OUTPUT);
}
void loop()
{
Serial.println("Feu vert");
digitalWrite(pinVert, HIGH);
digitalWrite(pinOrange, LOW);
digitalWrite(pinRouge, LOW);
delay(4000);
Serial.println("Feu orange");
digitalWrite(pinVert, LOW);
digitalWrite(pinOrange, HIGH);
digitalWrite(pinRouge, LOW);
delay(1000);
Serial.println("Feu rouge");
digitalWrite(pinVert, LOW);
digitalWrite(pinOrange, LOW);
digitalWrite(pinRouge, HIGH);
delay(5000);
}
Ce programme a un vilain défaut : il utilise la fonction delay()
Chaque exécution de la fonction loop() dure 10 secondes.
L'arduino passe l'essentiel de son temps dans la fonction delay().
Il est donc impossible de lui demander de faire autre chose en parallèle de la gestion du feu tricolore.
Dans un premier temps, il convient de restructurer le programme, sans changer son principe de fonctionnement.
Les lignes de code qui doivent être exécutées immédiatement les unes à la suite des autres, sont isolées dans des fonctions dédiées.
Rien de bien méchant, cela donne ceci :
const int pinVert = 4;
const int pinOrange = 5;
const int pinRouge = 6;
void feuVert()
{
Serial.println("Feu vert");
digitalWrite(pinVert, HIGH);
digitalWrite(pinOrange, LOW);
digitalWrite(pinRouge, LOW);
delay(4000);
}
void feuOrange()
{
Serial.println("Feu orange");
digitalWrite(pinVert, LOW);
digitalWrite(pinOrange, HIGH);
digitalWrite(pinRouge, LOW);
delay(1000);
}
void feuRouge()
{
Serial.println("Feu rouge");
digitalWrite(pinVert, LOW);
digitalWrite(pinOrange, LOW);
digitalWrite(pinRouge, HIGH);
delay(5000);
}
void setup()
{
Serial.begin(115200);
pinMode(pinVert, OUTPUT);
pinMode(pinOrange, OUTPUT);
pinMode(pinRouge, OUTPUT);
}
void loop()
{
feuVert();
feuOrange();
feuRouge();
}
La fonction loop() a été éclatée en trois fonctions distinctes, chacune se terminant par un delay().
A ce stade, le programme a toujours le même fonctionnement, et le même défaut.
Il est important de tester cette version intermédiaire, pour s'assurer de n'avoir rien cassé.
Si besoin, certaines variables locales à la fonction loop() peuvent être remontées en variables globales, afin d'être connues de partout.
Ouf le plus dur est fait ![]()
A présent, pour supprimer les delay(), une méthode simple consiste à ajouter une nouvelle variable globale, de type "tâche asynchrone".
Cette bestiole est définie par la bibliothèque easyRun.h
Elle permet d'appeler une fonction "pas tout de suite mais dans un certain temps"
Le code définitif est alors :
#include "easyRun.h"
const int pinVert = 4;
const int pinOrange = 5;
const int pinRouge = 6;
asyncTask lanceur; //lanceur est un objet de type "tâche asynchrone"
void feuVert()
{
Serial.println("Feu vert");
digitalWrite(pinVert, HIGH);
digitalWrite(pinOrange, LOW);
digitalWrite(pinRouge, LOW);
lanceur.set(feuOrange, 4000); //lanceur devra appeler la fonction feuOrange dans 4000 ms
}
void feuOrange()
{
Serial.println("Feu orange");
digitalWrite(pinVert, LOW);
digitalWrite(pinOrange, HIGH);
digitalWrite(pinRouge, LOW);
lanceur.set(feuRouge, 1000);
}
void feuRouge()
{
Serial.println("Feu rouge");
digitalWrite(pinVert, LOW);
digitalWrite(pinOrange, LOW);
digitalWrite(pinRouge, HIGH);
lanceur.set(feuVert, 5000);
}
void setup()
{
Serial.begin(115200);
pinMode(pinVert, OUTPUT);
pinMode(pinOrange, OUTPUT);
pinMode(pinRouge, OUTPUT);
lanceur.set(feuVert, 0); //Pour appeler la fonction feuVert dès que possible, et ainsi amorcer le cycle
}
void loop()
{
easyRun(); //actualisation des objets easyRun, dont lanceur.
}
Et voilà !
La fonction loop() ne contient plus que l'appel à la fonction easyRun(), qui se charge d'actualiser l'objet lanceur.
A présent, le programme exécute la fonction loop() à haute fréquence, des milliers de fois par secondes.
Et une fois de temps en temps, cela déclenche l'appel à une des trois fonctions de commutation des feux.
L'arduino est donc disponible à 99,99% de son temps pour faire autre chose.
Complément :
Lors de la phase de mise au point, je recommande d'ajouter systématiquement une variable globale de type loopMeter.
C'est un autre type de bestiole définie par la bibliothèque easyRun.h
En tout début de programme :
#include "easyRun.h"
loopMeter lm;
...
Juste une ligne à ajouter, rien d'autre à faire.
Littéralement, cela ajoute un loop-mètre (pardon pour ce mot-valise) au programme.
Le loop-mètre génère des stats sur la durée de la fonction loop(), affichées dans le terminal série toutes les 10 secondes.
Ainsi, avec le programme ci-dessus sur une arduino uno, on obtient
loopMeter: min=0ms avg=0ms max=2ms => 42062 loops per sec (2 easyRun items)
On voit que la fonction loop() est exécutée 42.000 fois par seconde.
Quel que soit votre programme, si vous voulez que l'arduino fasse "plusieurs choses à la fois", vous devez conserver une fonction loop() très courte, afin qu'elle puisse être exécutée un grand nombre de fois par seconde.
Le loop-mètre vous y aidera.