Exercice #1
Dans cet exercice nous souhaitons démarrer avec tout éteint et que le bouton serve à allumer les LEDs les unes à la suite des autres pour éclairer de plus en plus fort par exemple (ou ici faire des couleurs):
- Premier appui la LED verte s'allume
- deuxième appui la LED verte reste allumée et on allume la jaune
- troisième appui la LED orange s'allume en plus
- quatrième appui la led rouge s'allume en plus
- cinquième appui tout s'éteint.
Cela resemble fortement à une machine à état que l'on pourrait décrire ainsi
plusieurs états:
- tout éteint (REPOS)
- Led Verte allumée (V)
- Led Verte et Jaune allumées (VJ)
- Led Verte, Jaune et Orange allumées (VJO)
- Led Verte, Jaune et Orange et Rouge allumées (VJOR)
état initial = repos
action possible = click sur le bouton
et voici le diagramme des transitions possibles
Comment coder tout cela ?
Bon pour se concentrer sur l'essentiel, je vas utiliser la librairie OneButton.
--> télécharger et installer cette librairie OneButton.
EDIT: j'utilise aussi la librairie de @bricoleau pour gérer des boutons, elles n'ont pas la même API et pour les trucs simples l'encapsulation objet de celle de @bricoleau rend le code plus sympa.
EDIT (2022): @bricoleau a étendu sa la librairie et la classe Button est intégrée dans easyRun. EasyRun offre aussi des fonctions pour développer une machine à état (plus de détails: easyRun : un couteau suisse pour faire plusieurs choses à la fois)
Vous déclarez un objet bouton en précisant sur quelle pin il est connecté et s'il est actif à l'état High ou Low (c'est à dire si son pinMode()
est en INPUT_PULLUP ou pas) et vous attachez une fonction à appeler (on dit que c'est un callBack
en anglais) quand une action est détectée sur le bouton.
Dans le code ça ressemble à cela
#include <OneButton.h> // on inclut la librairie
const byte buttonPin = 4; // on définit un nom pour la pin associée au bouton
OneButton button(buttonPin, true); // true pour dire qu'on est en INPUT_PULLUP, donc actif LOW, connecté à GND
On déclare ensuite une fonction de call back
void simpleclick()
{
... // le code à exécuter quand on fait un click sur le bouton
}
et dans le setUp() on fera
void setup() {
button.attachClick(simpleclick); // on associe le fonction callBack à l'appui sur le bouton
}
enfin dans la loop() la librairie doit être appelée de manière répétitive pour voir si un bouton est appuyé
void loop() {
button.tick(); // On vérifie l'état des boutons, ce qui déclenche les appels aux fonctions
}
Voilà c'est donc simple d'emploi et ça permet de nous concentrer sur notre machine à état (si vous êtes curieux allez voir le source de la librairie et vous verrez que c'est aussi une machine à état )
Revenons à notre code
il va falloir déclarer bien sûr toutes les pins utilisées pour les LEDs, instancier le bouton, et coder la machine à état en utilisant une union pour les différents états et on va déclarer une fonction callback qui est appelée quand on appuie sur le bouton dans la quelle on aura un beau switch/case comme mentionné plus haut
Voici la code commenté
// La librairie de gestion des boutons
#include <OneButton.h>
const byte buttonPin = 4; // notre bouton est sur la pin 4
OneButton button(buttonPin, true); // true pour le mettre en INPUT_PULLUP
// les pins utilisées pour les LEDs
const byte pinLedRouge = 8;
const byte pinLedOrange = 9;
const byte pinLedJaune = 10;
const byte pinLedVerte = 11;
// la liste des états possible de notre système
// ainsi qu'une variable etatCourant prenant une de ces valeurs
enum {REPOS, ETAT_V, ETAT_VJ, ETAT_VJO, ETAT_VJOR} etatCourant;
// ------------------------------------------------------
// Cette fonction installe l'état initial
// ------------------------------------------------------
void mettreAuRepos()
{
digitalWrite(pinLedVerte, LOW);
digitalWrite(pinLedJaune, LOW);
digitalWrite(pinLedOrange, LOW);
digitalWrite(pinLedRouge, LOW);
etatCourant = REPOS;
}
// ------------------------------------------------------
// La fonction de call back, appellée automatiquement quand on clique
// ------------------------------------------------------
void simpleclick()
{
switch (etatCourant) {
case REPOS: // on était au repos et on a un appui, on allume la verte
digitalWrite(pinLedVerte, HIGH); // LED verte alimentée
etatCourant = ETAT_V; // on note le nouvel état de notre système
break;
case ETAT_V: // on était led verte allumée et on a un appui, on allume la jaune
digitalWrite(pinLedJaune, HIGH); // LED jaune alimentée
etatCourant = ETAT_VJ;// on note le nouvel état de notre système
break;
case ETAT_VJ: // vert et jaune allumées, on a un appui, on allume la orange
digitalWrite(pinLedOrange, HIGH); // LED orange alimentée
etatCourant = ETAT_VJO;// on note le nouvel état de notre système
break;
case ETAT_VJO:// vert, orange et jaune allumées, on a un appui, on allume la rouge
digitalWrite(pinLedRouge, HIGH); // LED rouge alimentée
etatCourant = ETAT_VJOR;// on note le nouvel état de notre système
break;
case ETAT_VJOR: // tout était allumé, on a un appui, on retourne au repos
mettreAuRepos(); // on retourne à l'état initial
break;
}
}
// ------------------------------------------------------
// On initialise notre système dans le setup
// ------------------------------------------------------
void setup() {
pinMode (pinLedRouge, OUTPUT);
pinMode (pinLedOrange, OUTPUT);
pinMode (pinLedJaune, OUTPUT);
pinMode (pinLedVerte, OUTPUT);
//conditions Initiales
mettreAuRepos();
// On attache la fonction simpleClick() comme callBack
button.attachClick(simpleclick);
}
void loop() {
// On vérifie l'état des boutons, ce qui déclenche l'appel de la fonction callBack si nécessaire
button.tick();
// ici on peut faire autre chose du moment que ça ne prend pas trop longtemps
}
Toute l'intelligence de la machine est donc dans la fonction callBack simpleclick()
qui est toute simple à lire grace au switch /case
et à l'usage de code d'état simples à lire tels que déclarés dans l'enum
.
Pour faire simple, grâce au switch /case
on regarde quel est notre état courant et comme on sait que cette fonction n'est appelée que lorsqu'on a reçu un click, on sait qu'il faut passer à l'état suivant. en regardant sur le diagramme on sait quelle action il faut faire et quel est l'état suivant, il suffit donc de coder cela. c'est tout simple