Multi tâches arduino

@biggil

Il ne faut pas oublier que la plupart des personnes qui viennent demander de l'aide sur ce site n'ont pas l'intention de faire carrière dans la programmation.

Ils veulent soit développer un petit projet par besoin (car pas disponible dans le commerce ou pas assez personnalisé) , soit en faire un loisir par curiosité ou passion.

La programmation en C++ ouvre évidemment d'énorme possibilité en programmation, mais la plupart se contenteront des multitudes de librairies déjà à disposition et du C pour faire la même chose.

Ils n'ont pas forcement le temps où la patience, ou tout simplement l'utilité d'apprendre à créer de objets en C++ pour approcher un niveau que tu as mis 45 ans à atteindre parce que tu en as fait ton métier.

On est pas sur la même approche de l'apprentissage de la programmation...

Tu vas les démotiver ou les effrayer si tu leur expliques qu'ils n'arriveront pas à faire quelque chose uniquement avec le langage simple du C.

L'important n'est pas que le code soit parfait et optimisé. Il faut juste qu'il le comprennes, et le reste viendra avec le temps.

@Zlika

ce que tu dis est bien vrai, d'ailleurs j'ai précisé à notre ami qu'il n'était pas obligé de suive mes conseils.

cependant :
Donne un poisson à un homme et il mangera un jour.
Apprends lui à pêcher et il mangera toute sa vie.

Oui, je suis d'accord avec toi. Je ne donne pas de code, mais uniquement une approche pour le créer :wink:

Merci à vous deux pour vos précieux conseils et pour ce "débat" intéressant.
J'ai cependant passé plusieurs heures notamment sur les sites conseillés par Zlika pour continuer en ce sens et la notion de "switch / Case". J'ai lu plusieurs exemple que j'ai du mal à réussir à transposer pour mon cas qui pourrait être :

  1. Etat capteur bas / Cas 1
  2. Etat capteur haut / Cas 2
  3. Etat du temps / Cas 3
  4. Etat des autres capteurs / Cas 4

Pour l'approche de Biggil, ça remet en cause mon programme et j'avais commencé par faire ceci à la base de mon code pour tenter de suivre ses conseils :

// Define stepper motor connections and steps per revolution:
#define dirPin_out 7
#define stepPin_out 8 //Moteur de droite sur la carte Arduino soit l'évacuation en eau
#define ENPin_out 9

#define dirPin 4
#define stepPin 5 // Moteur de gauche sur la carte arduino soit l'alimentation en eau
#define ENPin 6 

const int Led_in = 10;
//#define Led_out 11 


// Define time for start procedure
const int OnHour = 14;
const int OnMin = 31;
const int OffHour = 00;
const int OffMin = 9;

#define Gate 13 //Pin for the MOFSET which control the fan

//Librairies needed
#include <Wire.h>
#include <DS3231.h> //Bibliotheque pour le RTC qui conserve la date et heure même en cas de coupure
#include <LiquidCrystal_I2C.h> //Bibliothéque pour l'écran LCD


// indiquer (adresse i2c, nombre de colonnes, nombre de lignes)
LiquidCrystal_I2C lcd(0x27, 16, 2); // 0x27 define the type of LCD and 16, 2 the position on the screen 

// Init the DS3231 using the hardware interface
DS3231 rtc(SDA, SCL); //SDA & SCL define the pin needed for the RTC module on the arduino

// Init a Time-data structure
Time t;

// Numéro du pin pour les capteurs de niveau
const byte PIN_CAPTEUR_IN = 2; //niveau eau réserve eau propre

// Numéro du pin pour les capteurs de niveau
const byte PIN_CAPTEUR_OUT = 3;// Niveau réserve eau sale

// Numéro du pin pour les capteurs de niveau
const byte PIN_CAPTEUR_AQUA = 11; //Niveau eau aquarium à définir sur la carte; 

// Numéro du pin pour les capteurs de niveau
const byte RELAY = 12; 

// Nombre de millisecondes entre deux changements d'état des void
const unsigned long INTERVAL_1 = 1000;
const unsigned long INTERVAL_2 = 500;
const unsigned long INTERVAL_3 = 2000;

// variables will change:
int CapteurState;         // variable for reading the water sensor aqua status
int CapteurState_IN;         // variable for reading the water sensor IN bank status
int CapteurState_OUT;         // variable for reading the water sensor OUT bank status

int etat_capteuraqua_haut = HIGH; //capteur aqua haut
int etat_capteuraqua_bas = LOW; //capteur aqua bas

//////////////////////////////////////////////////////////////////////////////////////////////////////////:

void setup() 
{

// Setup Serial connection
Serial.begin(38400);

// Declare pins as output:
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
pinMode(ENPin, OUTPUT);

digitalWrite(ENPin, LOW);

pinMode(stepPin_out, OUTPUT);
pinMode(dirPin_out, OUTPUT);
pinMode(ENPin_out, OUTPUT);
pinMode(Led_in, OUTPUT);

digitalWrite(ENPin_out, LOW);


pinMode(PIN_CAPTEUR_IN, INPUT_PULLUP); // Affecte le mode Entrée au pin capteur. En l'absence de branchement, la résistance interne Pullup tire vers un état HIGH non aléatoire.
pinMode(PIN_CAPTEUR_OUT, INPUT_PULLUP); // Affecte le mode Entrée au pin capteur. En l'absence de branchement, la résistance interne Pullup tire vers un état HIGH non aléatoire.
pinMode(PIN_CAPTEUR_AQUA, INPUT_PULLUP); // Affecte le mode Entrée au pin capteur. En l'absence de branchement, la résistance interne Pullup tire vers un état HIGH non aléatoire.

pinMode (RELAY, OUTPUT); //Pin for relay
digitalWrite(RELAY, LOW);

pinMode (Gate, OUTPUT);
digitalWrite (Gate, LOW);

// initialise l'afficheur LCD
lcd.begin();

// Initialise le RTC
rtc.begin();

//The following lines can be uncommented to set the date and time
//rtc.setDOW(FRIDAY);     // Set Day-of-Week to SUNDAY
//rtc.setTime(23, 41, 0);     // Set the time to 12:00:00 (24hr format)
//rtc.setDate(22, 5, 2020);   // Set the date to January 1st, 2014


}

//////////////////////////////////////////////////////////////////////////////////////////////////////////:

void loop() 

{

// read the state of the water sensor aqua value:
CapteurState = digitalRead(PIN_CAPTEUR_AQUA);

// read the state of the water sensor water bank in value:
CapteurState_IN = digitalRead(PIN_CAPTEUR_IN);

// read the state of the water sensor water bank out value:
CapteurState_OUT = digitalRead(PIN_CAPTEUR_OUT);

// Read the date
t = rtc.getTime();

void RTC();
void NIVEAU();
void RESERVE();
void ECRAN();

Serial.print(t.hour);
Serial.print(" hour(s), ");
Serial.print(t.min);
Serial.print(" minute(s)");
Serial.println(" ");
}

J'ai donc déclaré des variables pour enregistrer l'état de mes capteurs.
Cependant c'est la notion "d'événements" qui m'échappe un peu plus.

Si je reprends le séquences expliquées :

  1. je déclare des variables qui reprends l'état des capteurs
  2. J'observe l'état des capteurs avec un digitalread
  3. Mais j'ai du mal à comprendre la notion d'évenement et de compteur temps dans la pratique. J'ai d'ailleurs commencé à créer un "int etat_capteuraqua_haut"

De mon point de vue, une fois l'état d'un capteur "enregistré", une action peut en découler.
Pour mon cas, mon capteur est mécanique et binaire (ON/OFF).

De plus pour reprendre les termes de Zlika, une fonction Delay serait à proscrire pour éviter un temps d'attente bloquant mais je ne vois pas comment le remplacer pour un moteur pas à pas à travers un polulu.

digitalWrite(stepPin_out, HIGH);
delayMicroseconds(1000);
digitalWrite(stepPin_out, LOW);

A part avec un "for" mais pour lequel je dois imposer un nombre de tours ce que je souhaite pas puisque je veux que le déclenchement de l'action dépende de l'état d'un capteur.

Bref je tourne un peu en rond et je relis les mêmes tutoriels en boucle qui finissent par m'embrouiller.

Donc si vous avez encore un peu de temps à me consacrer, vos lumières seront plus que les bienvenues.
Merci

Il ne suffit pas de lire l'état des capteurs.
Ce qui est intéressant, ce n'est pas que le bouton soit lu "appuyé". Selon la vitesse à laquelle s'exécute la loop(), tu vas obtenir la valeur "appuyé" un grand nombre de fois.
Par ex. si tu appuies sur le bouton pdt 0,5 secondes, et que ta loop() tourne à 100 fois/seconde, tu auras 50 fois d'affilée la valeur "appuyé". Ce n'est pas intéressant.

La chose interessante, c'est quand le bouton change d'état (donc, 1 fois au moment de l'appui, 1 fois au lâcher). Pour ca, il est nécessaire de mémoriser l'état du bouton. A chaque cycle de loop(), tu dois:

  • comparer l'état présent à l'état mémorisé au tour d'avant,
  • remplacer l'état passé par l'état présent (pout le prochain cycle)
  • si changement, prévenir la partie "supérieure" du prog, celle qui prend des décisions, qu'un événement s'est produit (et qu'il est du type "bouton1 appuyé", ou relâché, ou bouton2...)

Pareil pour la gestion des attentes. Tu as une ou plusieurs attentes en cours, lorsqu'une d'entre elles arrive au bout, c'est un événement.

Voilà pour le concept. Mais comment écrire ça ?

Tu commences par faire la liste des événements attendus, et tu leur donnes un nom.

enum { EVT_RIEN, EVT_BOUTON1_ON, EVT_BOUTON1_OFF, EVT_ATTENT1_TERMINEE };

ici pour 1 seul bouton et 1 seule attente + un evénement "vide" dont on aura besoin.

Un événement sera représenté par un entier.

int Evenement = EVT_ATTENT1_TERMINEE;

le programme gère une liste d'événements. En fait une liste c'est compliqué, alors on va faire un tableau, avec un nombre max d'éléments.

const int MAX_EVENEMENTS  = 8;
int TabEvenements [MAX_EVENEMENTS];

Au début du cycle, tu effaces ce tableau

for ( int i = 0, i< MAX_EVENEMENTS; i++)
  TabEvenements[i] = EVT_RIEN; // valeur signifiant "pas d'evenement"

Tu interroges tes capteurs boutons, tu calcules si l'état a changé, et si oui, tu mets le 1er élément du tableau à la bonne valeur (par ex. EVT_BOUTON1_ON)
Si tu as un second capteur, s'il génère un événement, tu le mets à la suite dans le tableau.
Pareil pour les attentes, si une attente est échue, tu pousses EVT_ATTENT1_TERMINEE dans le tableau (à la suite).

Et voilà. Tu as ta liste des événements. La partie "intelligente" du prog n'a plus qu'à parcourir ce tableau pour décider des actions à faire.

Note bien que si tu as des attentes à faire aussi bien côté mesures (attendre 30s après appui) et côté commandes (pour gérer le moteur), c'est pareil ! Une attente est une attente.

bon, je suis dans un bon jour, je t'offre l'Horloge multi-tâches dont tu as besoin.
Au début de ton prog, tu copie-colles ça :

// ------------------------------------
const int MAX_ATTENTES = 8; // max de taches simultanées
class cHorloge {
public:
   cHorloge ();
   
   int  AjouterAttente   ( int identifiant, unsigned long millisecondes );
   int  SupprimerAttente ( int identifiant );
   int  ListerEvenemnts ( int* tableau, int max_Evenements);

private:
  int  mNbMaxAttentes;
  struct Attente {
    int identifiant,
    unsigned long delai_ms; // 0 veut dire attente terminée.
    unsigned long depart;   // heure de lancement
  };
  Attente mAttentes[MAX_ATTENTES];
};

// ------------------------------------
cHorloge::cHorloge () {
  for ( int i=0; i<MAX_ATTENTES; i++ ) {
    mAttentes[i].delai_ms = 0; // toutes attentes inactives  
}
// ------------------------------------
int cHorloge::AjouterAttente   ( int identifiant, unsigned long duree_ms )
{
  for ( int i=0; i<MAX_ATTENTES; i++ ) {
    if ( mAttentes[i].delai_ms == 0 ) { // si case libre
      mAttentes[i].identifiant == identifiant;
      mAttentes[i].delai_ms = duree_ms;
      mAttentes[i].depart = millis(); // heure de depart = maintenant
      return 0; // ce 0 veut dire OK
    }
  }
  // aucune case de libre ! renvoyer -1 pour signaler l'erreur.
  return -1;
}
// ------------------------------------
int cHorloge::SupprimerAttente ( int identifiant )
{
  for ( int i=0; i<MAX_ATTENTES; i++ ) {
    if ( mAttentes[i].identifiant == identifiant) {
      mAttentes[i].delai_ms = 0; // liberer cette case du tableau
      return 0; // pas besoin de tester plus loin, renvoyer OK
    }
  }
  // identifiant non trouve. Renvoyer erreur.
  return -1; 
}
// ------------------------------------
int cHorloge::ListerEvenemnts ( int* tableau, int max_Evenements)
{
  int nb_evt_generes = 0;
  unsigned long now = millis(); // demander l'heure courante
  for ( int i=0; i<MAX_ATTENTES; i++ ) {
    if ( mAttentes[i].delai_ms == 0 )
      continue; // attente inactive
    if (  now - mAttentes[i].depart >= mAttentes[i].delai_ms ) {
      //cette attente est terminee
      // on la desactive
      mAttentes[i].delais_ms = 0;
      // on a donc un evenement, que l'on pousse dans le tableau passé en paramètre (si y'a la place)
      if ( nb_evt_generes <=  max_Evenements ) {
        tableau[nb_evt_generes] = mAttentes[i].identifiant;
        nb_evt_generes++;
      } else {
        return -1; // problemo, donc on renvoie une erreur.
      }    
    }
  }
  return nb_evt_generes;
}

cHorloge Horloge;

// ------------------------------------

Puis tu donne un nom à tous les types d'attentes que tu vas utiliser. Par exemple :

enum { ATTENTE_BOUTON, ATTENTE_SERVO };

pour lancer une attente (non-bloquante, donc)

  Horloge.AjouterAttente ( ATTENTE_BOUTON, 30000 ); // 30 secondes

A chaque cycle (chaque passage dans loop() )

  int  evenements_horloge [8];
  int nb = Horloge.ListerEvenemnts ( evenements_horloge, 8 );
  if ( nb < 0 ) {
    // il y a eu une erreur ... agir en conséquence
  }
  // ici les événements temporels sont dans le tableau, il y en a nb. A toi de jouer...

@biggil

Heu. .. Je vois pas très bien le côté pédagogique de ta class que tu balances à l'arrache sans rien expliquer.

Je comprend que tu veuilles utiliser les outils que tu maîtrise au quotidien, mais la on parle d'une personne qui débute.

Son projet ne comporte que quelques 'événement de type capteur qu'on peut aisément gérer avec des variables de base et du code simple (à part peut être pour gérer le moteur avec l'utilisation de timer/interruption si on veux fignoler) .

Avec ta class clé en main, tu vas justes le rendre dépendant de ton code et au final il ne saura toujours pas utiliser millis().

Je n'ai rien contre la programmation objet qu'on utilise de toute façon avec les librairies mais la on parle de millis() qu'il me semble intéressant de maîtriser quand on est débutant si on veux faire des programmes plus complexes.

Tu pêche le poisson, tu le cuisines avec ta recette sans lui indiquer les ingrédients que tu utilises et il ne saura même pas avec quel vin l'accompagner

@greglamouche

Utilise une fonction pour y placer la lecture des capteurs. Ta Loop sera plus simple à lire.

Pour que ton programme soit réactif, il faut que le temps d'exécution de la loop soit court.

Il faut donc détecter les fonctions qui n'ont pas forcément besoin d'être appelé à chaque tour de loop().

Prennont par exemple la première instruction qui suit le capteur.

// Read the date
t = rtc.getTime();

La lecture du temps sur la RTC va se faire à chaque loop.

Si tu gères le temps à la minute ou même à la seconde, tu va effectuer de nombreux accès au RTC et de nombreuses comparaisons pour détecter une modification à faire pour indiquer l'heure sur l'afficheur, activer ton relais...

Idem pour la fonction ECRAN.

Inutile dans le meilleur des cas d'effectuer de nombreuses comparaison pour savoir si des caractères changent ou pire encore, de récrire les mêmes caractères aux mêmes endroits plusieurs milliers de fois en attendant que la seconde passe.

Cela pourrait ne pas être gênant dans certain cas, mais en programmation, il est préférable de prévoir au cas où, plutôt que d'avoir à tout modifier, en voulant ajouter une nouvelle fonction.

Pourquoi ne pas utiliser millis() pour cadencer la lecture de la RTC, modifier l'affichage du temps sur l'écran et détecter les moment d'activation de ton relais.

Même une activation de cette fonction toutes les demi-seconde sera largement suffisante pour obtenir un bon résultat.

Si des caractères ne changent pas sur ton écran, il faut les imprimer dans le setup, car la loop le fera plusieurs fois.

Bonsoir,

navré pour ma réponse tardive mais je suis en plein travaux et c'est la course au quotidien.

@Biggil, je dois faire aveux de faiblesse mais à part les explications sur les événements, je suis complètement perdu avec le reste. Mais merci d'essayer de me faire grandir en programmation.

@Zlika
(...) Pourquoi ne pas utiliser millis() pour cadencer la lecture de la RTC, modifier l'affichage du temps sur l'écran et détecter les moment d'activation de ton relais. (...)

Oui justement c'était bien mon objectif pour chaque void mais en vain. A part la fonction première de chronomètre de Millis, je n'arrive pas à assimiler et surtout à utiliser la fonction correctement. Malgré les nombreux exemples que j'ai parcouru, je n'arrive pas à retranscrire la fonction dans mon cas.

Donc si vous avez encore la patience de m'assister, n'hésitez pas :wink:
Greg

bonsoir,
si tu comprends l'espagnol, un excellent tuto sur millis() :
https://forum.arduino.cc/index.php?topic=654615.msg4649920

(dans le pire des cas, tu peux te faire traduire : Traduire en temps réel | Tradukka, mais bonjour la gymnastique du [CTRL+C]/[CTRL+V] pour tout décoder !)

tu peux certainement aussi en trouver sur le forum français, mais j'ai trouvé celui-là très bien fait

je ne donne pas le poisson, je donne l'horloge pour le faire cuire :slight_smile:

Dans l'optique du débutant qui veut réaliser son projet, avant d'appronfondir la programmation, je fournis le bout difficile. Il reste encore pas mal de chose à faire, à comprendre.
Si le but c'est d'apprendre à utiliser millis(), je suis clairement hors sujet, certes.
J'ai passé pas mal de temps à expliquer une façon d'organiser un programme "multitache", dans le cas général. Selon moi, c'est cela qui est interessant, pour tout niveau.

Quant à mon Horloge, et bien c'est bête, hein, mais j'avais juste envie de l'écrire. Ca me démangeait :slight_smile:
Reste qu'un lecteur silencieux pourrait en tirer profit.

Pas de problème :wink: , on a tous une vie en dehors de notre passion.

L'utilisation de millis() n'est pas si compliqué, et une fois qu'on a compris le principe, on peut l'utiliser pour faire beaucoup de chose en parallèle.

Je vais partir de l'exemple d'Eskimon.

temps = millis();

La fonction millis() retourne une valeur qui s'incrémente toutes les millisecondes de façon continue depuis que ta carte est sous tension.

Comme elle utilise un compteur interne sur 32 bits, il faudra attendre environ 50 jours pour que le compteur déborde, et que sa valeur reprenne à 0.

On commence par enregistrer la valeur retournée par millis() dans une variable adaptée (unsigned long).

Cette valeur correspond à l'instant présent comme si tu regardes l'heure sur ta montre.

Si tu veux faire une action dans 1 seconde à partir de ce moment présent, tu devras juste regarder ta montre de temps en temps, donc la nouvelle valeur retournée par millis() pour la comparer à celle que tu as mémorisée précédemment.

if((millis() - temps) > 1000)
        {
            etat_led = !etat_led; // on inverse l'état de la LED
            digitalWrite(led, etat_led); // on allume ou éteint
            temps = millis(); // on stocke la nouvelle heure
        }

Tant que tu vois que la seconde n'est pas passée, tu laisses tomber et tu continue en boucle les fonctions dans ta loop.

Si tu dépasses enfin la seconde, tu exécutes l'action en attente.

Il est important de bien respecter la tournure de la comparaison avec millis().

millis() - valeur mémorisée > temps d'attente

Cette façon de procéder évite un comportement non désiré qui se produit lorsque la valeur retournée par millis() transite entre ses valeurs maximales et ses valeurs minimales après débordement.

Il ne faut pas non plus attendre un égalité parfaite == lors de la comparaison, car rien ne t'assures que tus ne vas pas arriver légèrement en retard lors du test.

Dans ta loop, tu peux avoir évidemment plusieurs fonctions qui testent millis() pour attendre des instants différents.

Dans l'exemple ci-dessus, la valeur mémorisée est rechargée, après l'attente, avec la nouvelle heure pour une nouvelle attente.

Si tu veux pouvoir désactiver ta fonction d'attente car tu n'en a plus besoin, il suffit simplement d'ajouter dans la condition une variable booléenne qui te servira de commande.

Pendant ces temps d'attentes, ta loop reste donc disponible pour scruter tes capteurs, un clavier ou toute autre événement.

Si attente activée et que millis() - valeur mémorisée > temps d'attente

@biggil pas de problème, un débat est toujours intéressant et apporte forcément des réponses aux lecteurs ou au moins une façon d'envisager plusieurs solutions.

Je propose juste une solution pédagogique sur le sujet récurrent qu'est millis(), et donne à certaines personnes l'occasion de comprendre ce principe.

Mais je suis persuadé que ton code séduira sûrement d'autres programmeurs plus avancés :wink:

On est des programmeurs compulsifs et c'est notre drogue, toujours besoin de concrétiser nos pensées :D.

Superbe explication de l'usage de millis() ! A mettre en message épinglé tellement cette question revient souvent ?

Appeler millis() c'est regarder l'heure.

Comment je sais si les pâtes sont cuites ? je note l'heure de départ, et je regarde l'heure de temps en temps. Quand la différence entre la nouvelle heure et celle de départ dépasse le temps désiré, j'arrête la cuisson.
Tout le monde sait faire ça, non ? (ah non zut, y'a les minuteurs sur smartphone, maintenant )
Pourquoi ça semble si compliqué quand au lieu de dire "regarder l'heure", on dit "appeler millis()" ?

Dans la même veine, pourquoi est-ce si compliqué la programmation événementielle, alors que nous fonctionnons tous de cette façon ? Le minuteur sonne, je vais arrêter les pâtes. Personne ne déroule une journée écrite à l'avance dans les moindres détails.

Une analogie est souvent plus simple pour expliquer ceci.

Il y aura toujours quelqu'un sur ce forum qui arrivera avec des bouts de codes fonctionnels séparément, mais aura toutes les peines à les réunir.

C'est comme ça. On commence par leurs apprendre à utiliser delay(), mais il faut un certain temps pour qu'ils comprennent que ce n'est pas la fonction miracle pour gérer le temps.

On est tous passé par là...

Pêcher n'est pas à la portée de tous, il faut aussi apprendre la patience.

Bonjour et merci 5_cylindres, google translate sera mon ami pour l'occasion, je vais regarder cela.
Greg

l'approche en décrivant la machine à état (de mon tuto mentionné dans la conversation au post #17) permet de fusionner simplement à mon sens ce qui est événementiel et ce qui est fonctionnel:

la boucle d'écoute des transitions à effectuer pour un état donné = événementiel
ce que l'on fait lors de la transition = fonctionnel

prendre le temps de dessiner ce graphe des états et des transitions permet bien souvent d'économiser des heures de debug d'un code qui devient spaghetti (long et entremêlé) !

@Biggil, @Zilka, merci pour cette approche de la fonction Millis
Je vais devoir faire encore plusieurs essais sur mon code pour vraiment assimiler cette fonction.
Du coup ce que je ne comprends, mise à part la différence entre les variables locales et globales ainsi que leur priorisation, c'est pourquoi en intégrant Millis comme expliqué dans mon code, cela ne se déroule pas comme prévu.
J'ai intégré ce critère :

unsigned long currentMillis = millis();

      // Si INTERVAL_1 ou plus millisecondes se sont écoulés
      if(currentMillis - previousMillis1 >= INTERVAL_1)

Ainsi que :

// Nombre de millisecondes entre deux changements d'état des void
const unsigned long INTERVAL_1 = 1000;
const unsigned long INTERVAL_2 = 500;
const unsigned long INTERVAL_3 = 2000;

En pensant qu'à chaque intervalle de temps, une portion du programme concerné serait lancé. Mais visiblement ça ne fonctionne pas.
Je comprenais mon code comme ceci : Tu vérifies si le temps actuel moins le temps écoulé jusque là est supérieur ou égal à l'intervalle défini. Si c'est le cas, tu peux rentrer dans la boucle concernée et tant que la condition de la boucle est effective. Une fois que ce n'est plus le cas, tu remets remets le temps précédent à la valeur du compteur temps actuel puis tu passes à la vérification "temporelle" d'aprés.
Qu'es ce qui m'a donc échappé dans le raisonnement ou la mise en forme?
Ou alors serait ce parce que le temps nécessaire à la premiére loop est supérieur au temps de contrôle de la seconde loop, de fait il passe de l'une à l'autre trop rapidement et l'impression que j'aurais d'entendre mon moteur pas à pas "bloquer" serait simplement dû au fait qu'il alterne trop rapidement d'une boucle d'action à une autre?
Encore merci pour votre aide qui, même si elle doit me faire chauffer les neurones, a l'avantage de m'apprendre beaucoup!

@J-M-L, j'ai parcouru avec attention ton post et même si il est très pédagogique, j'ai encore beaucoup de mal à projeter la notion de "switch/ case" à mon cas. Probablement un manque de maturité dans mon apprentissage du code, je suppose que chaque chose en son temps.
J'essai de fait de me concentrer sur des notions plus accessibles pour moi pour l'instant et de faire preuve de réalisme sur mon très faible niveau en programmation.
Je suis au code ce que le bébé est au "papa/maman" dans sa diction, j'écoute et j'essaie maladroitement d'appliquer votre enseignement :wink:
Merci pour ton intervention.
Greg

@greglamouche

As-tu bien pensé à initialiser la variable previousMillis1 au départ de l'attente?

@J-M-L

En effet.

Je trouve que l'utilisation de la machine à état est plus simple à assimiler pour un débutant car elle élimine par principe les informations qui ne sont pas nécessaire à l'état en cours.

De plus, il ne sait pas toujours pas quoi commencer. Avec cette méthode, le point de départ importe peu, les états sont définis selon les besoins et il se concentre sur les événements importants. Puis la boucle des états se ferme naturellement.

La vision plus centralisée des informations de décision de biggil impose un filtrage plus conséquent des informations (en général par des if pour un débutant) pour au final se retrouver dans une structure proche de celle de la machine à état, qui, avec l'utilisation du switch / case, rend le code bien plus lisible qu'une multitude de if imbriqués, et reste facile à modifier.

@biggil

en y réfléchissant , il y une nuance concernant la programmation événementielle sur arduino (language C pour rester sur un niveau débutant) et d'autres langages comme le Javascript ou le Python qui ont nativement un gestionnaire d'événements .

il y a une différence entre utiliser un gestionnaire ou il faut juste attacher les fonctions que l'on veux associer, est créer un début de gestionnaire (il suffirait de passer les variables d'entrée par référence et ajouter les fonctions de sortie en utilisant des pointeurs) pour un arduino.

je ne parle même pas de la notion de code asynchrone ou du multiprocessing comme apportée par asyncio dans Python, ou, même en comprenant le concept, cela nécessite une petite gymnastique intellectuelle (c'est encore plus dur avec l'age et les habitudes) pour remodeler l'organisation d'un programme.

Et maintenant, avec l'arrivée des ESP32 à double coeurs, les débutants auraient de nouveau de quoi se prendre la tête si FreeRTOS n'existait pas.

Malheureusement, la quantité de RAM et de Flash est très limitée sur une ARDUINO UNO, et c'est la carte de base qui est souvent utilisé par les débutants.

Ajouter des librairies pour faciliter l'écriture du programme se fait au détriment de l'espace de stockage.

L'envie de rajouter des fonctions par la suite risque d'obliger à repenser finalement le code car la chasse aux octets sera très compliquée.

Bonjour @Zilka,

oui avant mon setup tel que :

// Précédente valeur de millis() pour les différents void
unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
unsigned long previousMillis3 = 0;

Devrais je le déclarer dans mon Loop?
Merci