Voie respiratoire Automaticien en detresse

Bonjour,
J’ai un projet d’amélioration d’un secteur voie respiratoire dans des Thermes.
Il existe plusieurs types de soins ( humage aérosol, etc… ) très efficaces !

J’aimerais moderniser l’ensemble avec pour chaque poste (40) un écran LCD 16X2 et un bouton.

Avec ce bouton le curiste fait défiler les types de soins, dès qu’il tombe sur celui qu’il veut il attend x secondes et le soin est sélectionné. En fonction du type de soins, des électrovannes s’ouvrent. L’écran affiche un compte à rebours, un message d’instruction et après quelques minutes on lance un module de « cohérence cardiaque » (inspirer expirer en alternance).

Je maitrise assez bien les automates ( Ladder / Grafcet, etc…) et l’électricité. Mais , ici avec 40 postes les automates ne sont pas économiquement viables.

Du coup la solution arduino me semble cohérente. Problème, je n’ai jamais fait de programmation. La commu Arduino est bien organisée alors j’ai pu apprendre quelques trucs sur le tas.

Cependant je bloque sur quelques points.

J’ai découpé mon programme pour l’instant pour tester les fonctions séparément.

Premier programme :

  • Compte à rebours
  • Au bout de x secs « cohérence cardiaque »
  • Défilement inspirer expirer

J’ai un souci dés qu’il lance le défilement, le chrono ne marche plus je sais que c’est à cause du delay mais je ne sais pas par quoi le remplacer. La difficulté pour moi venant des automates c’est de pouvoir faire 2 taches en même temps.

Deuxième programme :

  • En appuyant sur un bouton, je fais défiler les soins et après x secondes sans rien faire le soin est sélectionné
  • Je rentre alors dans un chemin qui réalisera plus tard le premier programme et la fermeture de relais.

Problème, je n’arrive pas à faire la première étape. Pour l’instant j’ai les programmes qui défilent et quand j’ai le bon j’appuie sur le bouton.

Autres questions :

  • En utilisant millis(), j’ai lu qu’au bout de 50 jours il peut y avoir des problèmes comment y remédier
  • Existe-t-il un moyen de tester pas à pas un programme à l’aide d’un débogueur pour se retrouver ?

Merci d’avance à la commu. :slight_smile: :slight_smile:

La première étape si je comprends bien c'est : à chaque appui sur le bouton on incrémente le compteur de soin et on lance un chrono. Si aucun appui n'a été détecté au bout d'un timeout, le soin est considéré comme choisi, sinon, on repart à l'étape précédente. Il faut juste s'assurer que le compteur boucle, c'est à dire repart à 0 s'il dépasse le nombre total de soins disponibles.

Est-ce ce que tu cherches ?

En pseudo code, ça donnerait un truc comme

  • initialiser le compteur à 0
  • initialiser le chrono à 0
  • vérifier le bouton
  • si un appui bouton est détecté : incrémenter le compteur (modulo le nombre de soins) et réinitialiser le chrono, revenir à l'étape 3
  • si le chrono est inférieur au timeout, revenir à l'étape 3
  • sinon stop

Premier programme

#include <LiquidCrystal.h>

void Affichage();


unsigned long time;
unsigned long nextTime;
int minutes,secondes,disec;
char message[16] = "";


LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

void setup()
{
lcd.begin(16, 2);
lcd.print("Bienvenue");
delay(2000);
lcd.clear();
time = millis();
nextTime = time +100;

minutes = 10; // temps à partir du décompte
secondes = 0;
disec = 0;
}

void loop(){

if (millis() > nextTime) { // Toutes les 100 millis lancer affichage() et ajout 100
   nextTime += 100;
   
   Affichage();
   
   }
   
if (minutes == 8) // au bout de x min lancer la coherence cardiaque
      {
      Affichage();    
      Exp();
      }
}



                                                              void Affichage() // programme affichage compte à rebourd
                                                              {
                                                              disec++;
                                                              
                                                              if(disec == 10){
                                                                disec = 0;
                                                                secondes--;
                                                                }
                                                              if(secondes == 0)
                                                                {
                                                                secondes = 60;
                                                                minutes--;
                                                                }
                                                              
                                                              sprintf(message,"%dmin %dsec",minutes,secondes);
                                                              
                                                              lcd.home();
                                                              lcd.write(message);
                                                              }




                  void Exp(){ // programme defilement >><<
                    
                  lcd.setCursor(0 , 1);  
                  lcd.print("        ");  // Supr les <<
                  lcd.setCursor(8 , 1);
                  lcd.print("Inspirer");
                  
                  for(int i = 0; i < 8; i += 1) // defilement >> sur 8 cases
                  {lcd.setCursor(0+i , 1);
                  lcd.print(">") ;
                  delay(1000); // temps d'attente entre les >
                  }
                  
                  lcd.setCursor(0 , 1);  
                  lcd.print("        "); // Supr les >>
                  lcd.setCursor(15 , 1);
                  lcd.print(" "); // Supr le r
                  lcd.setCursor(8 , 1);
                  lcd.print("Expirer"); 
                  
                  
                  for(int i = 0; i < 8; i += 1) // defilement << sur 8 cases
                  {lcd.setCursor(7-i , 1);
                  lcd.print("<") ;
                  delay(1000); // temps d'attente entre les < 
                  }

}

Deuxième programme

#include <LiquidCrystal.h>


LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
const int btn_sel = 4;
int compt=0; 
int oldcompt=0;
int etape=0;





void setup() {
lcd.begin(16, 2);
pinMode(btn_sel, INPUT_PULLUP); 
lcd.setCursor(0 , 0);
lcd.print("appui");
 etape=0;




}

void loop(){
if (etape==0){ // si etape  0 alors defilement des soins
                         if ( digitalRead(btn_sel) == HIGH ) // tant que le boutton est sur 0
                        {
                           compt=compt+1; // +1 pour defilement des soins 
                          }
                         
                         else // dés que le boutton est sur 1
                        {
                          
                         etape=1; // passage à l'etape 1
                        }
                        
                         
                        if (compt != oldcompt){
                        if (compt<0) compt=2;
                        if (compt>2) compt=0;
                        lcd.setCursor(0 , 0);
                         lcd.print("Grands Thermes:)");;
                        switch (compt){
                          case 0:
                        lcd.setCursor(0 , 0);
                         lcd.print("Humage             ");
                        break;
                        case 1:
                        lcd.setCursor(0 , 0);
                         lcd.print("Aerosol          ");
                        break;
                        case 2:
                        lcd.setCursor(0 , 0);
                         lcd.print("Sonique          ");
                        break;
                        
                        }
                        oldcompt=compt;
                        delay(4000);
                        }
                        
        
}
                                else // si etape dif de 0 alors mise en route du programme choisi
                                {
                                                        switch (compt){
                                                          case 0:
                                                        lcd.setCursor(0 , 0);
                                                        lcd.print("Humage Cycle en cours");
                                                        {
      
  
                                                            // insertion du programme tempo + coherence + relais


                                                        }
                                                         
                                                        break;
                                                        case 1:
                                                        lcd.setCursor(0 , 0);
                                                         lcd.print("Aerosol  Cycle en cours ");
                                                        break;
                                                        case 2:
                                                        lcd.setCursor(0 , 0);
                                                         lcd.print("Sonique  Cycle en cours ");
                                                        break;
                                                                }
                                                                delay (5000);
                                                                etape=0; // retour au debut
                                   }
}

Bonsoir

Des réponses sur deux points particuliers :

Je maitrise assez bien les automates ( Ladder / Grafcet)

Si le Grafcet est maîtrisé faitre un tour du côté de ce tutoriel (Programmation Automate fini/Machine à Etats) qui présente une organisation du code voisine du Grafcet.

  • Existe-t-il un moyen de tester pas à pas un programme à l'aide d'un débogueur pour se retrouver ?

L'IDE Arduino ne permet pas de faire du déboggage pas à pas , la pose de points d'arrêts......
La mise au point se fait en posant des sorties println() aux endroits stratégiques

C'est typiquement un cas où il est extrêmement pratique de programmer avec des machines à états.

En complément du très bon tuto sur la question cité au message précédent, je te conseille je jeter un oeil à ma librairie de création de machines à états, que j'ai justement faite pour créer des automates. Elle incorpore du coup un certain nombre de fonctions de temporisation qui vont bien.

Alors, voilà un premier jet qui combine tes deux programmes avec la librairie YASM. Il n'y a que le compte à rebours que je n'ai pas fait car je n'ai pas compris ce qu'il devait faire

Là en gros tu as : au démarrage, choix du soin

appui sur le bouton = défiler les soins, si plus d'appui pdt 5s --> le soin affiché démarre

pour le moment le soin se limite à afficher >>>>    Inspirer puis    <<<<Expirer

Il faut voir chaque machine à états comme une tâche distincte, qui s'execute en // avec les autres

Ici j'execute d'abord "choix", qui va lancer "soin" qui lui même lance "insexp"

"choix" se stoppe elle-même au lancement de "soin", qui va s'executer en // avec "insexp"

dans loop() il y a pourtant l'execution des trois machines, mais seule "choix" a un état initial défini dans setup(). Tant qu'aucun état n'est défini, l'execution de la machine ne fait juste rien, donc au départ seule "choix" est réellement executée, et c'est dans celle-ci que l'état initial de "soin" est défini (quand il y a expiration de la temporisation de validation du choix)

c'est également chaque état initial de "soin" qui lance "insexp" à sa première exécution.

reste à ajouter des états dans les divers soins pour faire ce que tu veux faire dedans (activation des relais, etc etc)

il y a aussi "btn" qui est aussi une machine YASM (de type BTN) un peu particulière puisqu'elle sert uniquement à gérer un bouton, et qui permet d'avoir les actions suivantes sur le bouton : clic, clic long, doubleclic ce qui peut permettre avec un seul bouton une navigation dans un menu. Ici seul le clic simple est employé.

#include <LiquidCrystal.h>
#include <yasm.h>
#include <btn.h>

YASM choix, soin, insexp;
BTN btn;

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

const int PIN_BTN = 4;


#define TEMPO_VALIDE 5E3 //temporisation pour valider choix = 5s (5E3=5000ms)
#define TEMPO_INSEXP 600 //tempo d'affichage de chaque > ou <


void setup() 
{
	lcd.begin(16, 2);
	pinMode(PIN_BTN, INPUT_PULLUP);
	
	choix.next(ch_depart); //on définit l'état initial de la machine à états "choix"
	
}

void loop()
{
	//surveillance de l'état du bouton
	btn.update(!digitalRead(PIN_BTN)); // ! car le bouton fait contact au GND (actif bas)
	
	//execution des machines à états (tâches)
	choix.run();
	soin.run();
	insexp.run();
} 

//////////////////// choix du soin ////////////////////////////////

void ch_depart()
{
	if(choix.isFirstRun())
	{
		//cette partie ne s'execute qu'une fois
		lcd.clear();
		lcd.print(F(" Choisir soin :")); // F() est une macro pour utiliser facilement progmem pour les chaines et économiser de la ram
		lcd.setCursor(0,1);
		lcd.print(F("Appuyer pr choix"));
	}
	
	if(btn.state(BTN_CLICK)) //clic sur le bouton = on défile dans les choix
		choix.next(ch_choix1);
}

void ch_choix1()
{
	if(choix.isFirstRun()) {
		lcd.setCursor(0,1);
		lcd.print(F("Soin numero 1   "));
	}
	
	if(choix.elapsed(TEMPO_VALIDE)) {
		soin.next(sn_soin1);
		choix.next(ch_depart);
		choix.stop();
	}
	
	if(btn.state(BTN_CLICK)) 
		choix.next(ch_choix2);	
}

void ch_choix2()
{
	if(choix.isFirstRun()) {
		lcd.setCursor(0,1);
		lcd.print(F("Soin numero 2   "));
	}
	
	if(choix.elapsed(TEMPO_VALIDE)) {
		soin.next(sn_soin2);
		choix.next(ch_depart);
		choix.stop();
	}
	
	if(btn.state(BTN_CLICK)) 
		choix.next(ch_choix3);	
}

void ch_choix3()
{
	if(choix.isFirstRun()) {
		lcd.setCursor(0,1);
		lcd.print(F("Soin numero 3   "));
	}
	
	if(choix.elapsed(TEMPO_VALIDE)) {
		soin.next(sn_soin3);
		choix.next(ch_depart);
		choix.stop();
	}
	
	if(btn.state(BTN_CLICK)) 
		choix.next(ch_choix4);	
}

void ch_choix4()
{
	if(choix.isFirstRun()) {
		lcd.setCursor(0,1);
		lcd.print(F("Soin numero 4   "));
	}
	
	if(choix.elapsed(TEMPO_VALIDE)) {
		soin.next(sn_soin4);
		choix.next(ch_depart);
		choix.stop();
	}
	
	if(btn.state(BTN_CLICK)) 
		choix.next(ch_choix1);	
}

///////////////// machine insexp : affichage des > et < ////////////////
byte position;

void ie_inspirer()
{
	if(insexp.isFirstRun()) {
		lcd.setCursor(0,1);
		lcd.print(F("        Inspirer"));
		position=0;
	}
	
	if(insexp.periodic(TEMPO_INSEXP)) {
		lcd.setCursor(position,0);
		lcd.print(F(">"));
		position++;
		if (position == 8)
			insexp.next(ie_expirer);
	}
}

void ie_expirer()
{
	if(insexp.isFirstRun()) {
		lcd.setCursor(0,1);
		lcd.print(F("        Expirer "));
		position=7;
	}
	
	if(insexp.periodic(TEMPO_INSEXP)) {
		lcd.setCursor(position,0);
		lcd.print(F("<"));
		position--;
		if (position == 0)
			insexp.next(ie_expirer);
	}
}

///////////////// programmes de soin //////////////////
void sn_soin1()
{
	if (soin.isFirstRun()) {
		lcd.clear();
		lcd.print(F(" *** SOIN 1 *** "));
		insexp.next(ie_inspirer); //lancement de insexp
	}
	
}

void sn_soin2()
{
	if (soin.isFirstRun()) {
		lcd.clear();
		lcd.print(F(" *** SOIN 2 *** "));
		insexp.next(ie_inspirer);
	}
		
	
}

void sn_soin3()
{
	if (soin.isFirstRun()) {
		lcd.clear();
		lcd.print(F(" *** SOIN 3 *** "));
		insexp.next(ie_inspirer);
	}	
	
}

void sn_soin4()
{
	if (soin.isFirstRun()) {
		lcd.clear();
		lcd.print(F(" *** SOIN 4 *** "));
		insexp.next(ie_inspirer);
	}
	
}

Le programme compile, en revanche je ne l'ai pas testé, la flemme de sortir un afficheur et un bouton. Mais ça devrait fonctionner, je suis assez confiant :slight_smile:

Là c'est la version "sale" qui prends plein de lignes de code, il est assez facile de simplifier tout ça car énormément de fonctions sont redondantes : choix du soin, etc. En rangeant les soins dans un tableau, il est assez simple de remplacer autant de fonctions que de choix par une seule, mais pour la clarté de l'exemple ça me semblait bien plus compréhensible ainsi.

Je peux faire la version "propre" demain si besoin.

Attention ensuite à l’usage grandeur nature pour de vrais soins, tout cela est sans doute normé/besoin d’agrément comme dispositif à usage médical

On entend par dispositif médical « tout instrument, appareil, équipement, matière, produit, à l’exception des produits d’origine humaine, ou autre article utilisé seul ou en association, y compris les accessoires et logiciels nécessaires au bon fonctionnement de celui-ci, destiné par le fabricant à être utilisé chez l’homme à des fins médicales et dont l’action principale voulue n’est pas obtenue par des moyens pharmacologiques ou immunologiques ni par métabolisme, mais dont la fonction peut être assistée par de tels moyens. Constitue également un dispositif médical le logiciel destiné par le fabricant à être utilisé spécifiquement à des fins diagnostiques ou thérapeutiques.

....

Conditions de mise sur le marché d’un dispositif médical

Pour être mis sur le marché dans l’UE, un DM doit respecter les exigences de sécurité et de santé définies par la directive DM.

La mise sur le marché d’un DM est conditionnée à l’obtention, préalablement à sa commercialisation, du marquage CE. Ce dernier traduit la conformité du dispositif médical aux exigences de sécurité et de santé énoncées dans la législation européenne.

Le fabricant doit constituer un dossier permettant de prouver les moyens mis en œuvre pour atteindre les objectifs de sécurité et de santé fixés par la législation.

Ainsi, les dispositifs doivent être conçus de façon à ce que leur utilisation ne compromette ni l’état clinique des patients, ni la sécurité et la santé des patients et des utilisateurs. De plus, les dispositifs doivent atteindre les performances qui sont revendiquées par le fabricant et leurs risques éventuels doivent être acceptables au regard des bénéfices apportés au patient.

Sauf pour les DM de classe 1 (non stérile et sans fonction de mesurage), le marquage CE est obtenu via un organisme notifié qui va étudier le dossier de marquage CE présenté par le fabricant et évaluer la conformité du produit aux exigences essentielles définies par la directive européenne.

A l’issue d’une évaluation qui conclut à la conformité du DM, l’organisme notifié délivre un certificat de conformité permettant au fabricant de marquer CE son dispositif et de le mettre sur le marché européen.

Pour les DM de classe 1, il s’agit d’une auto-certification par le fabricant qui lui permet d’apposer le marquage CE sur son dispositif.

Si un litige survient entre l’organisme notifié et le fabricant sur la classification du DM, il revient à l’autorité compétente de trancher. Dans le cas des DM de classe I, l’autorité compétente peut prendre une décision de police sanitaire pour non-conformité du DM à la réglementation.

J-M-L:
Attention ensuite à l’usage grandeur nature pour de vrais soins, tout cela est sans doute normé/besoin d’agrément comme dispositif à usage médical

bonjour
oui , "le bricolage mal géré" dans des thermes peut vite avoir des conséquences sanitaires et économiques importantes ,
voir par exemple ce cas actuel d'une catastrophe économique locale

Merci pour vos réponses super intéressantes !
je m'attendais pas à autant.
J'ai pas accès à mon PC ce week-end mais je me mets à fond lundi.
pour la notion médicale elle est maîtrisé actuellement pas de modification dessus !
À lundi

J-M-L:
Attention ensuite à l’usage grandeur nature pour de vrais soins, tout cela est sans doute normé/besoin d’agrément comme dispositif à usage médical

Quand même!

Voila une version plus "propre" avec des tableaux

c'est pas optimisé niveau mémoire, il faudrait utiliser progmem pour les tableaux, au moins pour les chaines de caractères

pour le tableau pSoin[], il n'est pas obligatoire il aurait tout aussi bien été possible de faire un switch/case dans sn_depart() pour le choix du soin, mais je trouvais ça plus élégant (même si ça bouffe probablement de la ram pour pas grand chose)

#include <LiquidCrystal.h>
#include <yasm.h>
#include <btn.h>

YASM choix, soin, insexp;
BTN btn;

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

const byte PIN_BTN = 4;

const byte nbrSoin=4;

byte numero=0; //variable pour garder en memoire la position dans le menu

//tableau de chaines de caractère contenant le nom des soins
const char *nomSoin[nbrSoin]={
    "Soin numero 1",
    "Soin numero 2",
    "Soin numero 3",
    "Soin numero 4"
};

//prototypes des fonctions de soin, car l'IDE ne fait pas toujours bien son boulot et il faut alors écrire du vrai C++
//sinon il ne trouve pas les fonctions pour remplir le tableau fnSoin
void sn_soin1();
void sn_soin2();
void sn_soin3();
void sn_soin4();

//tableau de pointeurs de fonctions vers les différents soins
const void (*pSoin[nbrSoin])()={
    sn_soin1,
    sn_soin2,
    sn_soin3,
    sn_soin4
};

#define TEMPO_VALIDE 5E3 //temporisation pour valider choix = 5s (5E3=5000ms)
#define TEMPO_INSEXP 600 //tempo d'affichage de chaque > ou <


void setup() 
{
    lcd.begin(16, 2);
    pinMode(PIN_BTN, INPUT_PULLUP);
    
    choix.next(ch_depart); //on définit l'état initial de la machine à états "choix"
    
}

void loop()
{
    //surveillance de l'état du bouton
    btn.update(!digitalRead(PIN_BTN)); // ! car le bouton fait contact au GND (actif bas)
    
    //execution des machines à états (tâches)
    choix.run();
    soin.run();
    insexp.run();
} 

//////////////////// choix du soin ////////////////////////////////

void ch_depart()
{
    if(choix.isFirstRun())
    {
        //cette partie ne s'execute qu'une fois
        lcd.clear();
        lcd.print(F(" Choisir soin :")); // F() est une macro pour utiliser facilement progmem pour les chaines et économiser de la ram
        lcd.setCursor(0,1);
        lcd.print(F("Appuyer pr choix"));
    }
    
    if(btn.state(BTN_CLICK)) //clic sur le bouton = on défile dans les choix
        choix.next(ch_choix);
}

void ch_choix()
{
    //affichage du soin actuel
    //l'affichage est executé seulement au premier passage pour ne pas faire clignoter l'écran inutilement
    if(choix.isFirstRun()) {
        lcd.setCursor(0,1);
        lcd.print(nomSoin[numero]);
    }
    
    //défilement dans la liste des soins à chaque appui sur le bouton
    if(btn.state(BTN_CLICK)) {		
        choix.next(ch_choix, true); //ici on reste dans le même état mais en y re-entrant (valeur "true" dans l'appel de next() ) de manière à provoquer à nouveau au prochain passage l'affichage du soin et la RAZ des chronos
        numero++; //et bien sûr on incrémente le numero du soin
        if (numero==nbrSoin) //on vérifie si on doit revenir au premier élément
            numero=0;
    }
    
    //validation du choix du soin par timeout
    if(choix.elapsed(TEMPO_VALIDE)) {
        soin.next(sn_depart);	//lancement du soin
        choix.next(ch_depart);	//raz du menu de choix
        choix.stop();			//arret du menu, il pourra etre relancé à la fin du soin
    }
    
}



///////////////// machine insexp : affichage des > et < ////////////////
byte position;

void ie_inspirer()
{
    if(insexp.isFirstRun()) {
        lcd.setCursor(0,1);
        lcd.print(F("        Inspirer"));
        position=0;
    }
    
    if(insexp.periodic(TEMPO_INSEXP)) {
        lcd.setCursor(position,0);
        lcd.print(F(">"));
        position++;
        if (position == 8)
            insexp.next(ie_expirer);
    }
}

void ie_expirer()
{
    if(insexp.isFirstRun()) {
        lcd.setCursor(0,1);
        lcd.print(F("        Expirer "));
        position=7;
    }
    
    if(insexp.periodic(TEMPO_INSEXP)) {
        lcd.setCursor(position,0);
        lcd.print(F("<"));
        position--;
        if (position == 0)
            insexp.next(ie_expirer);
    }
}

///////////////// programmes de soin //////////////////
void sn_depart()
{
    lcd.clear();
    lcd.print(nomSoin[numero]);
    insexp.next(ie_inspirer); //lancement de insexp
    soin.next(pSoin[numero]); //lancement du soin choisi
    
}

void sn_soin1()
{
    
}

void sn_soin2()
{
    
}

void sn_soin3()
{
    
}

void sn_soin4()
{
    
}

&bricofoy — J’ai pas de quoi tester sous la main mais ce n’est pas comme celà qu’on déclare un tableau de pointeur sur fonctions ou qu’on les appelle mais c’est peut être comme cela avec votre YASM ??

J-M-L:
&bricofoy — J’ai pas de quoi tester sous la main mais ce n’est pas comme celà qu’on déclare un tableau de pointeur sur fonctions ou qu’on les appelle mais c’est peut être comme cela avec votre YASM ??

:-[ :-[
On va dire qu'on avait rien vu....

pour l'appel en revanche, je ne vois pas ce qui cloche ?

bricofoy:
:-[ :-[
On va dire qu'on avait rien vu....

:smiley: :smiling_imp: :grin:

bricofoy:
pour l'appel en revanche, je ne vois pas ce qui cloche ?

Pour l'appel c'est peut être le commentaire qui m'enduit d'erreur :slight_smile: (ne connaissant pas bien YASM), quand vous faites

soin.next(pSoin[numero]); //lancement du soin choisi

je suppose qu'en fait vous ne lancez pas à cet endroit le soin et c'est YASM et la méthode next() qui prend en paramètre le pointeur sur fonction et qui ensuite déclenche l'appel (ie vous passez la pointeur, vous ne passez pas le résultat de l'exécution de la fonction pointé à la fonction next()) ??)

oui on ne passe que le pointeur, et la fonction pointée est executée lors de l'appel à run() (dans la loop dans le cas présent)

très exactement dans yasm.cpp il y a ça :

void YASM::next(void (*pnextstate)(), bool reset/*=false*/)
{
	if ((!reset) && (pnextstate == _pState)) return; //we do nothing if we stay in the same state

	//we change the state so we set the associated values as needed
	_isFirstRun = true; 
	_lastTime = _enterTime = millis();
	_runCount = 0;
	
	//and enventually change exec pointer to the next state
	_pState = pnextstate; 
}

bool YASM::run()
{
	if(_pState==YASM::nop) return false; //check if the machine is currently stopped
										//and just return false if this is the case
	//now machine is running
	_pLastState=_pState; //store the current state
	_pState(); //run the current state
		
	//if pState is the same now than before running it means we did not got state 
	//change request during _pState() execution so next time we again will run the 
	//same state, so we clear the flag and increment runs counter
	if (_pState==_pLastState) {
		_isFirstRun = false; 
		_runCount++; 
	}
	return true; //and eventually report machine was running this time
}

Ok

J'ai rentré ton programme, j'ai fais quelques modifs c'est nickel pour la sélection et le démarrage de chaque soins. Par contre j'ai un soucis, impossible de faire tourner le compte à rebours en même temps que le défilement <<<>>>.
Je me demande si Arduino arrive à gérer 2 tempo en même temps ?

Avez vous retiré tous les delay(s) (et remplacé par des scrutations de l'horloge)?

Foun26:
J'ai rentré ton programme, j'ai fais quelques modifs c'est nickel pour la sélection et le démarrage de chaque soins. Par contre j'ai un soucis, impossible de faire tourner le compte à rebours en même temps que le défilement <<<>>>.
Je me demande si Arduino arrive à gérer 2 tempo en même temps ?

quel programme ? un des miens ?

oui il est parfaitement possible d'ajouter le compte à rebours, et d'une manière générale de gérer plusieurs tempos en même temps, c'est justement pour ce genre de cas que l'utilisation de YASM simplifie la tâche.

simplement le compte à rebours je ne l'avais pas fait car je n'ai pas compris ce qu'il devait faire exactement

poste ton code actuel

Alors je précise que le second programme ne fonctionne pas exactement comme il devrait car il y a un bug dans YASM. J'y ai passé jusqu'a 4h cette nuit sans arriver à le résoudre.

l'appel à next(etat_en_cours, true) depuis l'état en cours pour y re-rentrer et ainsi déclencher à nouveau les actions de premier passage dans l'état ne fonctionne pas. On rerentre bien dans l'état, mais le flag de premier passage est re-désactivé à la fin de l'execution en cours (donc avant l'execution suivante de l'état qui n'est plus alors vue comme une première mais une seconde execution... fail. @J-M-L si vous passez par là je veux bien avoir votre avis sur la question)