Programmation Automate fini / Machine à état

si on peut faire ce que l’on veut lors de l’appui d’un bouton ou de la détection de l’état du bouton. cet appel de fonction peut changer ou pas l’état courant

const byte pinBouton = 2;

void fonctionSiLOW() {
  // votre code
}

void fonctionSiHigh() {
  // votre code
}

void setup() {
  Serial.begin(115200);
  pinMode(pinBouton, INPUT_PULLUP);
}

void loop() {
  if (digitalRead(pinBouton) == LOW) fonctionSiLOW();
  else fonctionSiHigh();
}

dans ce code j’appelle la fonction fonctionSiLOW quand la pin est LOW sinon fonctionSiHigh

je ne suis pas sûr de voir ce qui vous chagrine…

ce qui me chagrine, c’est de ne pas pouvoir rentrer dans une fonction sur l’appui d’un bouton, alors que l’appui d’un bouton déclenche en temps normal des actions. En temps normal, quand on fait un digitalRead, on lit l’état, et si High ou Low, on déclenche l’allumage d’une led par exemple. le changement d’état du bouton est bien lu, et ça sans que ce soit un rising ou falling, mais bien seulement un passage à LOW par exemple, qui est détecté sur l’entrée déclarée. L’exemple arduino dans l’IDE “bouton”, est bien comme celà, et il ne faut pas pour autant faire une machine à état. c’est ça qui me brouille la tête. j’espère être clair. merci :grinning:

mince j’avais pas vu votre code.

avec votre code, je comprends encore moins ce qui ne va pas dans le mien… J’ai l’impression d’avoir écrit la même chose. c’est malheureux.

La machine a état va vous permettre de ne pas répéter les appels aux fonctions s’il a déjà été fait

Bon, maintenant que j’ai branché mon bouton sur la bonne des 4 pattes… ça fonctionne, même si j’ai un rebond dégueulasse qui perturbe le fonctionnement. Merci pour tout.

l’anti-rebond “du pauvre” c’est de mettre delay(15);juste après la détection du front… c’est généralement suffisant pour absorber les rebonds

j’ai fait un anti rebond du pauvre en effet , juste pour le test. par contre pas moyen de relacher assez vite, j’ai donc mis 150 de delay.
merci pour tout.

const byte LeboutonPin4 = 4; // notre bouton est sur la pin 4
const byte LeboutonPin3 = 3; // un autre bouton est sur la pin 3
byte etatBoutonPin4 = 0; // pour enregistrer l'état du bouton 4
byte etatBoutonPin3 = 0; // pour enregistrer l'état du bouton 3

// les pins utilisées pour les LEDs
const byte pinLedRouge = 8;
const byte pinLedOrange = 9;
const byte pinLedJaune = 10;
const byte pinLedVerte = 11;

// On introduit le temps comme évènement supplémentaire
unsigned long chrono; // attention, type unsigned long comme millis()
const unsigned long TimeOut = 15000ul; // 15 secondes (le ul à la fin pour unsigned long)

// 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;

// ------------------------------------------------------
// On initialise notre système dans le setup
// ------------------------------------------------------
void setup() {
  pinMode (pinLedRouge,  OUTPUT);
  pinMode (pinLedOrange, OUTPUT);
  pinMode (pinLedJaune,  OUTPUT);
  pinMode (pinLedVerte,  OUTPUT);
  pinMode (LeboutonPin4,  INPUT_PULLUP);
  pinMode (LeboutonPin3,  INPUT_PULLUP);
  //conditions Initiales
  mettreAuRepos();
}

// ------------------------------------------------------
// Cette fonction installe l'état initial
// ------------------------------------------------------
void mettreAuRepos()
{
  digitalWrite(pinLedVerte,  LOW);
  digitalWrite(pinLedJaune,  LOW);
  digitalWrite(pinLedOrange, LOW);
  digitalWrite(pinLedRouge,  LOW);
  etatCourant = REPOS;
}

// ------------------------------------------------------
// La fonction appelée automatiquement quand on clique
// ------------------------------------------------------
void boutonPin4()
{
  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;
  }
  chrono = millis(); // on vient d'avoir une action donc on réarme notre chronomètre
}

// ------------------------------------------------------
// La fonction appelée automatiquement quand on clique
// ------------------------------------------------------
void boutonPin3()
{
  switch (etatCourant) {
    case REPOS:    // dans tous les états
    case ETAT_V:   // sauf celui où tout était allumé
    case ETAT_VJ:  // en cas de double click on veut
    case ETAT_VJO: // allumer toutes les LEDs
      digitalWrite(pinLedVerte, HIGH);
      digitalWrite(pinLedJaune, HIGH);
      digitalWrite(pinLedOrange, HIGH);
      digitalWrite(pinLedRouge, HIGH);
      etatCourant = ETAT_VJOR;  // et on déclare notre nouvel état courant
      break;

    case ETAT_VJOR: // on a tout qui est allumé et on reçoit le double click
      mettreAuRepos(); // donc on retourne à l'état de repos
      break;
  }
  chrono = millis(); // on vient d'avoir une action donc on ré-arme notre chronomètre
}

// ------------------------------------------------------
// La fonction de appellée en cas de dépassement du délai
// (on pourrait aussi faire un if (etatCourant != REPOS) mettreAuRepos(); )
// ------------------------------------------------------
void timeOut()
{
  /* // version longue si on veut le switch case
    switch (etatCourant) {
    case ETAT_V:    // pour tous les états
    case ETAT_VO:   // sauf celui au repos
    case ETAT_VOJ:  // on doit tout éteindre
    case ETAT_VOJR: // et revenir à l'état initial
      mettreAuRepos(); // ce que fait cette fonction
      break;
    }
  */
  // version courte, si on n'est pas au repos, alors passer au repos
  if (etatCourant != REPOS) mettreAuRepos();

  // à noter que le timeOut continuera de se déclencher toutes les 15 secondes
  // mais ne fera rien puisqu'on sera au repos
  // ça peut être utile pour continuer à faire autre chose
  // sinon il faut tester avant de l’appeler qu’on n’est pas au repos
}

void loop() {
  // On vérifie l'état des boutons, ce qui déclenche l'appel d'une des fonctions nécessaire
  etatBoutonPin4 = digitalRead(LeboutonPin4);
  if (etatBoutonPin4 == LOW) {
    boutonPin4();
    delay (150);
  }

  etatBoutonPin3 = digitalRead(LeboutonPin3);
  if (etatBoutonPin3 == LOW) {
    boutonPin3();
    delay (150);
  }

  // On vérifie le timer et on déclenche l'évènement si nécéssaire
  // rajouter dans la condition “&& (etatCourant != REPOS)” si vous ne souhaitez pas
  // appeler la fonction au repos
  if (millis() - chrono >= TimeOut) {
    timeOut();
    chrono = millis(); // on ré-arme notre chronomètre
  }

  // ici on peut faire autre chose du moment que ça ne prend pas trop longtemps

}

ça c’est pas bien… ce n’est pas du rebond, c’est de l’interface utilisateur… et ça bloque le micro pendant super longtemps… et Si vous tenez le bouton appuyé trop longtemps ça ne va pas aller…

→ c’est pour cela qu’il faut

  • soit coder soi même proprement la gestion d’un bouton (front appui, rebonds, appui stable, front de relâche, rebonds, relâché stable) et ces états sont gérés dans une… machine à état,
  • soit utiliser une des bibliothèques qui fait cela très bien. Si vous n’aimez pas les callbacks, prenez la bibliothèque de @bricoleau, elle est vraiment super simple à utiliser.
#include <simpleBouton.h>
simpleBouton boutonTest(2);   //Cablage : pin2---BP---GND

void setup() {
  Serial.begin(115200);
}

void loop() {
  if (boutonTest) Serial.println("appui bouton !!");
}
1 Like

Bonjour,
Je supprime, mauvaise idée, fonctionnement aléatoire.

Oui il y a de nombreuses façons de se passer de bibliothèques pour les boutons. C’en est une valide bien sûr.

Si on fait le job correctement pour la loop, il est assez rare cependant qu’on ait besoin des interruptions pour une action humaine qui prend souvent des centaines de ms et de toutes façons le traitement du drapeau se fait ensuite dans la loop donc avec la même latence.

Personnellement donc je préfère réserver les interruptions aux ports de communication, timers etc

1 Like

Bonjour J-M-L,

Je viens de redécouvrir cet excellent tuto.
Je l'avais déjà parcouru lors de mes débuts sur arduino et j'avoue que je n'y avais pas compris grand chose. Maintenant avec un peu d'expérience j'ai tout compris.

Voilà je tenais aussi à vous dire que vous êtes une référence pour moi.
Vous inspirez naturellement le respect avec vos compétences exceptionnelles tant au niveau programmation qu'au niveau matériel !

Bonne journée.

1 Like

Merci :slight_smile:
Côté matériel il y a de très bonnes pointures bien meilleures que moi sur ce forum !

Bonjour,

Oui, de plus le code que j'avais présenté n'était pas fiable.

En passant, ce qui n'enlève rien à la qualité de ce tuto bien utile pour tout les débutants, je voulais apporter une petite remarque, pour ceux qui comme moi, n'avaient pas bien compris la librairie GitHub - mathertel/OneButton: Une bibliothèque Arduino pour utiliser un seul bouton pour la saisie à plusieurs fins.

Ce qui me gênait c'est le temps de latence entre le moment du click et l'allumage de la led d'environ 500 ms.
En remplaçant doubleclick par longclick et dans setup() :

button.attachLongPressStart(longclick); // maintien de l'appui 1 seconde

cela devient plus fluide et rapide. C'est probablement ce qui génait aussi rikousky19.
Voilà, si ça peut aider :wink:

La bibliothèque en question réagit lorsqu’on relâche le bouton si je me souviens bien donc ça peut créer cette sensation

Je préfère celle de @bricoleau au final qui a depuis ce tuto enrichi sa bibliothèque

Même délais de latence sur simpleClic() et pas de fonction appui long dans la class boutonAction.
On arrive à contourner mais ça ne fonctionne pas aussi bien.

Oui si vous utilisez les callbacks mais pas en utilisation directe, vous pouvez tester l’appui

Il fallait ajouter un drapeau qui évite une double action de longClic()
dans loop() :

  bouton.actualiser();
  static bool acquit = false;
  if (bouton.estEnfonceDepuisAuMoins(1000) && !acquit) {
    longClic();
    acquit = true;
  }
  if (bouton.vientDEtreRelache()){
    if (acquit)acquit = false;// acquite l'appui long
    else simpleClic();
  }