Bonjour
Suite à la lecture du tutoriel de @J-M-L sur les machines à état, je me suis dit que j'allais en faire une : voici le code ( très simple, c'est juste pour découvrir ) - le code compile bien, testé sur tinkercad
////////////////////////////////////////////////////////// code machine à états ///////////////////////
////////////////////////////////////////////////////////// Led sur la 11, et Btn sur la 12 ////////////
// tuto de @J-M-L : https://forum.arduino.cc/t/programmation-automate-fini-machine-a-etat/452532 //////
const byte pinBtn = 12 ;
const byte pinLED = 11 ;
bool etatLED = 0 ;
bool valBtn = 0 ;
unsigned long prevMillis = 0 ;
const int LED_delay = 500 ;
void setup()
{
pinMode(pinBtn, INPUT_PULLUP);
pinMode(pinLED, OUTPUT);
digitalWrite(pinLED, HIGH) ;
Serial.begin(115200);
}
void loop()
{
unsigned long MILLIS = millis() ;
///////////////////////////////////////////////// lecture Btn et inversion valeur car pullup
valBtn = digitalRead(pinBtn);
valBtn = ! valBtn ;
///////////////////////////////////////////////// test machine à état //////////////////////
if (valBtn == 1 && etatLED == 0)
{
etatLED = !etatLED ;
digitalWrite(pinLED, etatLED);
Serial.println("Led allume") ;
}
else if (valBtn == 1 && etatLED == 1)
{
etatLED = ! etatLED ;
digitalWrite(pinLED, etatLED);
Serial.println("Led eteinte") ;
}
/////////////////////////////////////////// Normalement on entre pas dans cette boucle... /////////
else
{
if ((MILLIS - prevMillis) > LED_delay)
{
etatLED = ! etatLED ;
digitalWrite(pinLED, etatLED);
prevMillis = MILLIS ;
Serial.print("Led clignote - erreur !") ;
}
}
}
Puis je me suis demandé si ce que j'avais fait était vraiment une machine à état... c'est la raison pour laquelle j'ai crée ce topic.
Pour avoir une machine à état il ne suffit pas d'avoir une variable qui représente l'état de ta LED, même si on simplifie au maximum, c'est quand même valable.
En gros tu as bien un machine à deux états, mais tes états ne sont pas vraiment visible.
Après il y a la théorie pure, mais on peut laisser ça de coté au début, je trouve.
Du coup personnellement, je verrais bien tes IF avec uniquement tes état, sans prendre en compte les actions.
Pour rendre ça plus lisible.
Ok, donc ça veut dire que par exemple, je dois avoir un enum etat[]{etat1, etat2, etat3 }; (avec des noms plus explicites, bien sur)
Avec tous les états possibles, et crée une variable int etat qui dit dans le moniteur série a quel état je suis ? C'est ça ?
Merci beaucoup pour votre aide
Cordialement
Pandaroux007 / Rémi
P.S : j'aurai besoin d'un moyen d'avoir un truc qui a plusieurs d'état, du coup j'ai pensé a utiliser un potar avec map(valPOTAR, 0, 1023, 0, 10);, puis allumer une rangée de LED en fonction de la valeur avec une machine à état...
Les trois codes sont équivalents, mais la lisibilité du code suivant les sensibilités sont différentes.
Après suivant ce que tu veux faire, une machine peut être une perte de temps dans un premier temps, mais devenir intéressante, si la complexité augmente.
Pour ton PS, oui pourquoi pas comme base c'est très bien.
La première question à se poser quand on veut faire une machine à états c’est quel est le système qu’on modélise et quels sont ses états. Ensuite on regarde les événements qui font changer d’état
L'exemple de @fdufnews est excellent pour démontrer une machine à états.
Dans cet exemple il a clairement séparé les 2 étapes d'une telle machine, la gestion des états et l'action liée aux états.
Ces 2 phases peuvent "dispersées" dans le programme, mais rarement une gestion d'état déclenche une action "à la suite"
Ce qui fait que les machines à états c'est super en programmation, est que gestion(s) et action(s) travaillent "chacun dans leur coin".
C’est un moyen d’expliquer ce qu’il se passe mais C’est un peu plus que cela car déclarer un enum revient à déclarer un nouveau type différent de int.
Il y a toujours possibilité de passer d’une valeur de l’enum vers un entier (conversion implicite) mais la norme dit que l’inverse n’est pas vrai.
Donc si on a un enum, il faut toujours utiliser des variables du type déclaré et toujours utiliser les mots clés de l’enum pour les tests ou affectations. Pas de maths pour affecter une certaine valeur dans une variable de type énuméré.
Il faut faire attention à cela surtout sur les plateformes 32 bits ou le compilateur refusera de compiler s’il voit ce cas.
Oui, c'est le coté qui peu être un peu déroutant pour les débutants, qui rajouté à la notion de machine à état, donne un aspect "compliqué".
Il faut arriver à oublié que le nouveau type énuméré est stocké sous forme d'entier, tout en le gardant à l'esprit, surtout si des propriétés de ce type sont utilisées dans des structures envoyées entre deux programmes.
Cette liste d'états peut sembler fastidieuse mais en fait cela limite le nombre de configurations possibles et on peut ainsi maitriser comment est notre système malgré le fait qu'il y a de multiples actionneurs à gérer qui peuvent tous avoir des tas de configurations possibles.
Comme le souligne J-M-L la base est de définir les différents "états", les différents modes de fonctionnement que l'on peut/veut avoir sur le système. Pour chacun de ces modes on défini comment doivent être tous les actionneurs.
Ensuite le switch case permet de déterminer le mode dans lequel on se trouve. Ce sera peut être un mode PAR_DEFAUT ou SITUATION_INCONNUE mais on sera dans tous les cas dans un mode bien défini quelquepart ; les actionneurs ne feront jamais n'importe quoi.
Et si demain on ajoute un actionneur il ne fera pas n'importe quoi dans son coin ; il suffira de définir ce qu'il doit faire dans chaque état.
ce n'est pas une variable c'est juste une liste qui détaille les conditions d'utilisation du morceau de "code" qui suit. J'ai levé l’ambiguïté en ajoutant un tiret devant
Bonjour, j'ai commencé a faire un petit code comme l'exemple de @J-M-L pour allumer une led de plus a chaque appui sur un bouton, mais je ne comprend pas le code a cause de l'utilisation de la librairie OneButton.h... Par exemple la fonction :
// ------------------------------------------------------
// 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;
}
}
J'ai compris une partie du code, mais comment faire pour l'appeler automatiquement quand on clique ?
Merci pour tous vos conseils, j'en prend note
Rémi
P.S : autre question : Une lampe de vélo, c'est aussi une machine à état ? quand on appuie, il y a d'abord un mode, puis un autre qui clignote, etc ?