Amélioration de mon code

Bonjour,

Dans mon programme, j'ai une fonction reglages() qui me permet de paramétrer des acquisitions. Comme je souhaite contrôler la saisie, je me retrouve avec mon "simple" niveau de programmation, à avoir une fonction qui s'alourdit très vite. En effet, je rajoute au contrôle la possibilité de revenir vers la question précédente.
J'imagine (pour ne pas dire, je suis sûr) qu'il y a possibilité d'améliorer mon code, afin de le rendre plus lisible et efficace, mais mon niveau de programmation obtenu par recherche à droite à gauche me limite actuellement pour améliorer ma fonction.
Aussi, je viens demander un peu d'aide pour me donner des pistes d'amélioration.

Mon code actuel (sachant que je n'ai pas encore traité la dernière saisie, avec la possibilité de controler la saisie et le retour au menu précédent) :

void reglages()
{
  while (topDepart == 0)
  {
    envoi = Serial.readString();

    affiche_texte_lcd(0, 0, "Suivre indications ");
    affiche_texte_lcd(0, 1, "sur moniteur serie ");
    affiche_texte_lcd(0, 2, "pour parametrage de");
    affiche_texte_lcd(0, 3, "l'acquisition      ");

    Serial.println(F("Quelle gamme de mesure du conductimètre :"));
    Serial.println(F("calibre 0-2 mS/cm --> tapez 0"));
    Serial.println(F("calibre 2-20 mS/cm --> tapez 1"));
    Serial.println(F("calibre automatique --> tapez 2"));
    Serial.println();
    while (Serial.available() == 0 );
    if (Serial.available() != 0)
    {
      char lecture = Serial.read();
      while ((lecture != '0') && (lecture != '1') && (lecture != '2')) //on teste la saisie
      {
        Serial.println(F("Erreur : il faut saisir 0, 1 ou 2"));
        Serial.println();
        Serial.println(F("Quelle gamme de mesure du conductimètre :"));
        Serial.println(F("calibre 0-2 mS/cm --> tapez 0"));
        Serial.println(F("calibre 2-20 mS/cm --> tapez 1"));
        Serial.println(F("calibre automatique --> tapez 2"));
        Serial.println();
        while (Serial.available() == 0 );
        if (Serial.available() != 0)
        {
          lecture = Serial.read();
        }
      }
      calibre = lecture - '0'; //on convertit le code ASCII issu de lecture en chiffre
      //Serial.parseInt(); //on enregistre la valeur saisie dans timeRes
      Serial.print(F("Vous avez choisi la gamme "));

      if (calibre == 0)
      {
        Serial.println(F("0-2 mS/cm"));
        Serial.println();
      }
      else if (calibre == 1)
      {
        Serial.println(F("2-20 mS/cm"));
        Serial.println();
      }
      else
      {
        Serial.println(F("automatique"));
        Serial.println();
      }


      delay(1000); //délai de temporisation
    }

    Serial.println(F("Saisir la période d'échantillonnage (en s) : "));
    Serial.println(F("-1 pour revenir au menu précédent (choix calibre)"));
    Serial.println();
    while (Serial.available() == 0 );
    if (Serial.available() != 0)
    {

      timeRes = Serial.parseInt(); //on enregistre la valeur saisie dans timeRes
      if (timeRes == -1)
      {
        Serial.println(F("Quelle gamme de mesure du conductimètre :"));
        Serial.println(F("calibre 0-2 mS/cm --> tapez 0"));
        Serial.println(F("calibre 2-20 mS/cm --> tapez 1"));
        Serial.println(F("calibre automatique --> tapez 2"));
        Serial.println();
        while (Serial.available() == 0 );
        if (Serial.available() != 0)
        {
          char lecture = Serial.read();
          while ((lecture != '0') && (lecture != '1') && (lecture != '2')) //on teste la saisie
          {
            Serial.println(F("Erreur : il faut saisir 0, 1 ou 2"));
            Serial.println();
            Serial.println(F("Quelle gamme de mesure du conductimètre :"));
            Serial.println(F("calibre 0-2 mS/cm --> tapez 0"));
            Serial.println(F("calibre 2-20 mS/cm --> tapez 1"));
            Serial.println(F("calibre automatique --> tapez 2"));
            Serial.println();
            while (Serial.available() == 0 );
            if (Serial.available() != 0)
            {
              lecture = Serial.read();
            }
          }
          calibre = lecture - '0'; //on convertit le code ASCII issu de lecture en chiffre
          //Serial.parseInt(); //on enregistre la valeur saisie dans timeRes
          Serial.print(F("Vous avez choisi la gamme "));

          if (calibre == 0)
          {
            Serial.println(F("0-2 mS/cm"));
            Serial.println();
          }
          else if (calibre == 1)
          {
            Serial.println(F("2-20 mS/cm"));
            Serial.println();
          }
          else
          {
            Serial.println(F("automatique"));
            Serial.println();
          }


          delay(1000); //délai de temporisation
        }
      }
      else
      {
        Serial.print(F("Période d'échantillonnage saisie : "));
        Serial.print(timeRes);
        Serial.println(F(" s"));
        Serial.println();
        timeRes *= 1000;
      }


      delay(1000); //délai de temporisation
    }

    Serial.println("Saisir la durée totale d'acquisition (en s) : ");
    Serial.println(F("-1 pour revenir au menu précédent (choix calibre)"));
    while (Serial.available() == 0 );
    if (Serial.available() != 0)
    {
      dureeAcquisition = Serial.parseInt(); //on enregistre la valeur saisie dans dureeAcquisition
      Serial.print(F("Durée d'acquisition saisie : "));
      Serial.print(dureeAcquisition);
      Serial.println(F(" s"));
      Serial.println();
      dureeAcquisition *= 1000;
      delay(1000); //délai de temporisation
    }

    Serial.println(F("Saisir S pour démarrer l'acquisition : "));
    while (Serial.available() == 0 );
    if (Serial.available() != 0)
    {
      topDepart = 1;
      tempsPrec = 0;
      tempsZero = millis();
    }
  }
  lcd.clear();
}

Merci par avance pour vos précieux conseils

Effectivement, ça tourne à la spaghetti-party :slight_smile:
Essaye de découper en fonctions plus élémentaires :

  • afficher une message (plusieurs lignes) sur le moniteur série
  • lire un caractère sur la ligne série, vérifier qu'il est correct (dans la bonne plage)
  • ...
    Tu peux aussi penser à une machine à états:
    ETAT 0 = rien de saisi
    ETAT1 = 1er paramètre saisi
    ETAT2 = 2ème paramètre saisi
    Ainsi quand tu es par exemple dans l'ETAT1, selon ce que tu reçois tu passes à l'ETAT2 ou bien tu reviens à l'ETAT0.
    L'ami J-M-L a écrit un excellent tuto sur les machines à états, j'ai pas le lien mais je suis sûr que quelqu'un va te le donner.

Tuto sur la machine à états


Pour le code en lui même, sans chercher à changer la structure:

Le caractère retour chariot existe, c’est \n
Par exemple

Serial.println(F("calibre automatique --> tapez 2"));
Serial.println();

Peut s’écrire:

Serial.println(F("calibre automatique --> tapez 2\n"));

On gagne une ligne en moins.

Dans le même ordre d’idée, plutôt que:

   Serial.println(F("Quelle gamme de mesure du conductimètre :"));
   Serial.println(F("calibre 0-2 mS/cm --> tapez 0"));
   Serial.println(F("calibre 2-20 mS/cm --> tapez 1"));
   Serial.println(F("calibre automatique --> tapez 2\n"));

j’écris

   Serial.println(F("Quelle gamme de mesure du conductimètre :\n"
                        "calibre 0-2 mS/cm --> tapez 0\n"
                        "calibre 2-20 mS/cm --> tapez 1\n"
                        "calibre automatique --> tapez 2\n"));

Je trouve que cela allège aussi la lecture. On pourrait écrire sur une seule ligne:
  Serial.println(F("Quelle gamme de mesure du conductimètre :\ncalibre 0-2 mS/cm --> tapez 0\ncalibre 2-20 mS/cm --> tapez 1\ncalibre automatique --> tapez 2\n")); mais on y perd en lisibilité

Le code

void setup()
{
  Serial.println(F("A"));
  Serial.println(F("B"));
}

void loop(){}

fait 1428 octets avec une Uno tandis que:
void setup()

{
  Serial.println(F("A\nB"));
}

void loop(){}

fait 1382 octets.


    while (Serial.available() == 0 );
    if (Serial.available() != 0)
    {
       <lignes>
    }

La condition derrière le if est toujours vraie car à la ligne précédente on a attendu qu’il y ait un caractère. On peut écrire

    while (Serial.available() == 0 );
    <lignes>

        if (Serial.available() != 0)
        {
          lecture = Serial.read();
        }

derrière if on attend une seule instruction. Si il y en a plusieurs, on est obligé de mettre des accolades. Si on en a qu’une, ce n’est pas utile. On peut donc écrire au choix:

        if (Serial.available() != 0)
          lecture = Serial.read();

ou

        if (Serial.available() != 0) lecture = Serial.read();

Moi, j’écrirais dailleurs

        if (!Serial.available()) lecture = Serial.read();

A voir quelle est le plus lisble (ce n’est pas toujours la même solution).


?: est sympa dans une fonction:
condition ? <valeur 1> : <valeur 2> retourne valeur 1 ou valeur 2 en fonction de la condition.
Par exemple:

      Serial.print(F("Vous avez choisi la gamme "));
      if (calibre == 0)
      {
        Serial.println(F("0-2 mS/cm"));
      }
      else
      {
        Serial.println(F("2-20 mS/cm"));
      }

Peut s’écrire:

      Serial.print(F("Vous avez choisi la gamme "));
      Serial.println( calibre == 0? F("0-2 mS/cm") : F("2-20 mS/cm") );

Un peu plus compliqué si il y a 3 choix:

      if (calibre == 0)
      {
        Serial.println(F("0-2 mS/cm"));
        Serial.println();
      }
      else if (calibre == 1)
      {
        Serial.println(F("2-20 mS/cm"));
        Serial.println();
      }
      else
      {
        Serial.println(F("automatique"));
        Serial.println();
      }

Peut donner:

      Serial.println(calibre == 0? calibre == 1? F("2-20 mS/cm\n"): F("automatique\n") : F("0-2 mS/cm"));

Bonjour,

Merci pour vos retours. En cherchant, je suis effectivement tombé sur le tutoriel que j'ai lu.
Si j'ai bien compris, dans mon cas :

  • je définis les états différents : pour moi ce serait donc si je suis à la phase de choix de la gamme, choix de l’échantillonnage, choix de la durée --> à mettre dans un enum
  • je créé une fonction qui prend en entrée la variable (mettons phase) qui indique à quelle phase je suis : dans la fonction, j'insère un switch/case avec les 3 actions à faire selon la valeur de la variable
  • dans mon programme, lors de chaque étape, j'assigne une valeur à la variable phase selon ce que je souhaite faire

Est-ce ça dans l'idée ?
Merci

Tu es sur la bonne voie.
Tu prends un papier et un crayon (ça existe encore !) et tu traces autant de patates que tu as d'états (ce que tu appelles "phase", le mot consacré est "état"). Dans chaque patate, tu écris le nom de l'état.

Ensuite il y a des événements, écris la liste complète:

  • saisie correcte du paramètre 1
  • saisie incorrecte du paramètre 1
  • idem autres paramètres
  • time-out pour répondre (optionnel)
  • ...

Pour chaque événement, tu te demandes ce qu'il doit se passer si l'événement survient, et cela pour chaque état.
Parfois ça n'a pas de sens : tel événement ne peut pas se produire si on est dans tel état. Dans ce cas tu ne fais rien.
Si l'événement peut se produire (pour un état donné), de 2 choses l'une :

  • ou bien il provoque un changement d'état, avec une action associée, quelque chose à faire. Dans ce cas tu traces une flèche qui part de l'état de départ, aboutit à l'état nouveau. Sur la flèche, tu écris le nom de l'événement ainsi que l'action à faire.
  • l'événement ne provoque pas de changement d'état. Comme ci-avant tu fais une flèche, qui part de l'état considéré mais qui revient vers lui. Ecris le nom et l'action (s'il y en a une ).
    Exactement comme dans le tuto de J-M-L.

Un fois que cela est fait, c'est facile à coder. Quand un événement se produit, tu rentres dans un switch/case qui trie sur l'état courant. Tu as un "case" pour chaque état. Tu sais ce que tu dois y faire : regarde les flèches qui partent de l'état, tu vois si tu as une action associée et si tu dois changer d'état.

Attention tu as plusieurs états et plusieurs événements. Chaque couple (événement [se produisant dans l'] état), il y a un code spécifique.
Tu peux donc en arriver à faire des switch/case impriqués, ou procéder autrement, mais il y a de la gestion à faire.

Vous passez d'un état à l'autre avec la validation d'entrée utilisateur (quelque chose qui se termine par un marqueur de fin comme '\n')


Suivant la valeur (attendue correcte sinon on ne fait rien) lue, vous vous déplacez à la case suivante ou précédente en effectuant les actions notées en rouge

Bon point de @biggil, je n'ai pas représenté de timeout mais ça pourrait aussi être un événement pris en compte

Pour écouter le port série aussi sous forme de machine à état et attendre la validation utilisateur sans rien bloquer (ou gérer un flux asynchrone genre keypad) vous pouvez jeter un oeil à mon petit tuto sur le sujet

un truc du genre

// GESTION DU PORT SERIE

const byte tailleCode = 10;
char message[tailleCode + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'

boolean messageDisponible()
{
  static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
  boolean messageRecu = false;

  while (Serial.available() && !messageRecu) {
    int c = Serial.read();
    if (c != -1) {
      switch (c) {
        case '\r': // on ignore le '\r'
          break;
        case '\n':
          message[indexMessage] = '\0'; // on termine la c-string
          indexMessage = 0; // on se remet au début pour la prochaine fois
          messageRecu = true;
          break;
        default:
          if (indexMessage <= tailleCode - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
          break;
      }
    }
  }
  return messageRecu;
}

// GESTION DES ETATS
enum t_etat : byte {CALIBRE, PERIODE, DUREE, GO} etat = CALIBRE;

void menuCalibre()
{
  Serial.println(F("Choix calibre 1/2/3"));
  etat = CALIBRE;
}

void menuPeriode()
{
  Serial.println(F("Choix Periode en secondes ou 0 pour retour"));
  etat = PERIODE;
}

void menuDuree()
{
  Serial.println(F("Choix Durée en secondes ou 0 pour retour"));
  etat = DUREE;
}

void menuGo()
{
  Serial.println(F("*** ACQUISITION ***"));
  etat = GO;
}

bool parametrage()
{
  bool gestionTerminee = false;
  switch (etat) {
    case CALIBRE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (strlen(message) == 1) { // et qu'on a un seul caractère
          switch (message[0]) {
            case '1': Serial.println(F("calibre 0-2 mS/cm"));   menuPeriode(); break; // à mémoriser bien sûr
            case '2': Serial.println(F("calibre 2-20 mS/cm"));  menuPeriode(); break; // à mémoriser bien sûr
            case '3': Serial.println(F("calibre automatique")); menuPeriode(); break; // à mémoriser bien sûr
            default: Serial.print(F("code: [")); Serial.print(message); Serial.println(F("] non reconnu")); break;
          }
        } else {
          Serial.print(F("Erreur: [")); Serial.print(message); Serial.println(F("] non reconnu"));
          menuCalibre();
        }
      }
      break;

    case PERIODE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (!strcmp(message, "0")) { // a-t-il tapé 0 ?
          menuCalibre();
        } else {
          //  A FAIRE: vérifier si message ne contient que des chiffres
          Serial.print(F("Periode choisie = ")); Serial.println(message); // à mémoriser bien sûr utiliser atoi() ou atol() pour convertir en nombre
          menuDuree();
        }
      }
      break;

    case DUREE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (!strcmp(message, "0")) { // a-t-il tapé 0 ?
          menuPeriode();
        } else {
          //  A FAIRE: vérifier si message ne contient que des chiffres
          Serial.print(F("Durée choisie = ")); Serial.println(message); // à mémoriser bien sûr
          menuGo();
          gestionTerminee = true;
        }
      }
      break;

    case GO:
      Serial.println(F("ACQUISITION TERMINEE"));
      menuCalibre(); break;
  }
  return gestionTerminee;
}

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

void loop()
{
  if (parametrage()) {
    // effectuer l'acquisition ici
  }

  // ici on peut faire autre chose en même temps que le menu est géré

}

(tapé ici donc sans garantie - il vous reste du boulot)

Bonjour,

J'ai encore un peu du mal à comprendre tous les mécanismes de la machine à états : je vais laisser ça de côté pendant un temps et le reprendrais à tête reposée quand j'aurais plus de temps.

Merci pour votre aide, je conserve les éléments bien au chaud !

pas de souci, ça reste sur le forum :slight_smile:

Bonsoir,

Je me suis mis un petit coup de pied au derrière : à partir du code fourni, j’ai complété avec quelques informations. Dans un premier temps, je n’ai pas codé le test des valeurs chiffrées lors de la saisie de la période d’échantillonnage ni de la durée.
Le seul hic, c’est que lorsque je lance mon acquisition, je n’ai pas de décompte et n’obtiens qu’une seule mesure.
Pourtant si j’ai bien compris, quand je rentre dans le loop, je rentre dans la fonction booléenne parametrage qui ne devient vrai que lorsque tous les paramètres sont ok → une fois que j’ai validé une saisie lors de l’appel de l’état GO, alors parametrage = true et donc je devrais lancer ma procédure d’acquisition avec calcul du temps courant et llecture de mon capteur
Ou alors j’ai loupé une étape dans le fonctionnement ?
Merci pour votre aide

void setup()
{
  //initialisation de la liaison série, du module pH et du lcd et du capteur de température
  Serial.begin(flux);
  ec.begin();
  lcd.init(); // initialisation de l'afficheur
  temp.begin();
 
  menuCalibre();
  
  lcd.clear();
  tempsZero = millis();
  tempsPrec = 0;
}


void loop()
{
  if (parametrage())
  {
    tempsCourant = millis() - tempsZero;
    affiche_texte_lcd(0, 0, "Chrono : ");
    lcd.setCursor(9, 0);
    int longueur = lcd.print(tempsCourant / 1000);
    affiche_texte_lcd(10 + longueur, 0, "s");
    if ((tempsCourant >= tempsPrec) && (cpt <= nbrPoints + 1))
    {
      lecture();
      cpt++;
    }

    if (cpt == (nbrPoints + 2))
    {
      testFinAcquisition = 1; //l'acquisition est finie
      lecture(); //on affiche que sur l'écran les valeurs
    }
  }

}






boolean messageDisponible()
{
  static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
  boolean messageRecu = false;

  while (Serial.available() && !messageRecu) {
    int c = Serial.read();
    if (c != -1) {
      switch (c) {
        case '\r': // on ignore le '\r'
          break;
        case '\n':
          message[indexMessage] = '\0'; // on termine la c-string
          indexMessage = 0; // on se remet au début pour la prochaine fois
          messageRecu = true;
          break;
        default:
          if (indexMessage <= tailleCode - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
          break;
      }
    }
  }
  return messageRecu;
}

void menuCalibre()
{
  Serial.println(F("Quelle gamme de mesure du conductimètre :\n"
                   "calibre 0-2 mS/cm --> tapez 1\n"
                   "calibre 2-20 mS/cm --> tapez 2\n"
                   "calibre automatique --> tapez 3\n"));
  etat = CALIBRE;
}

void menuPeriode()
{
  Serial.println(F("Saisir la période d'échantillonnage (en s) : "));
  Serial.println(F("Saisir 0 pour revenir au menu précédent (choix calibre)\n"));
  etat = PERIODE;
}

void menuDuree()
{
  Serial.println("Saisir la durée totale d'acquisition (en s) : ");
  Serial.println(F("Saisir 0 pour revenir au menu précédent (choix période d'échantillonnage)\n"));
  etat = DUREE;
}

void menuGo()
{
  Serial.println(F("Saisir S pour démarrer l'acquisition : "));
  etat = GO;
}

bool parametrage()
{
  bool gestionTerminee = false;
  switch (etat) {
    case CALIBRE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (strlen(message) == 1) { // et qu'on a un seul caractère
          switch (message[0]) {
            case '1': Serial.println(F("Vous avez choisi le calibre 0-2 mS/cm\n"));   menuPeriode(); break;
            case '2': Serial.println(F("Vous avez choisi le calibre 2-20 mS/cm\n"));  menuPeriode(); break;
            case '3': Serial.println(F("Vous avez choisi le calibre automatique\n")); menuPeriode(); break;
            default: Serial.print(F("Erreur, vous avez saisi ")); Serial.print(message); Serial.println(F(" qui n'est pas un choix possible. Veuillez sélectionner le choix 1, 2 ou 3")); break;
          }
        } else {
          Serial.print(F("Erreur, vous avez saisi ")); Serial.print(message); Serial.println(F(" qui n'est pas un choix possible. Veuillez sélectionner le choix 1, 2 ou 3"));
          menuCalibre();
        }
      }
      break;

    case PERIODE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (!strcmp(message, "0")) { // a-t-il tapé 0 ?
          menuCalibre();
        } else {
          //  A FAIRE: vérifier si message ne contient que des chiffres
          Serial.print(F("Période d'échantillonnage saisie : ")); Serial.println(message); Serial.println();
          timeRes = 1000 * atoi(message);
          menuDuree();
        }
      }
      break;

    case DUREE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (!strcmp(message, "0")) { // a-t-il tapé 0 ?
          menuPeriode();
        } else {
          //  A FAIRE: vérifier si message ne contient que des chiffres
          Serial.print(F("Durée d'acquisition saisie : ")); Serial.println(message); Serial.println();
          dureeAcquisition = 1000 * atoi(message);
          nbrPoints = dureeAcquisition / timeRes;
          menuGo();
          
        }
      }
      break;

    case GO:
      if (messageDisponible()) { // si l'utilisteur a validé
        Serial.print(F("t"));  // donne la date en seconde
        Serial.print('\t');
        Serial.println(F("Sigma")); // donne la conductivité
        Serial.print(F("s"));  // donne la date en seconde
        Serial.print('\t');
        Serial.println(F("mS/cm")); // donne la valeur recalculée de la tension
        gestionTerminee = true;
        //menuCalibre();
      }
      break;
  }
  return gestionTerminee;
}


void lecture()
{
  float resultatec = 0.0;
  for (int i = 0; i < cycles; i++) { //on boucle pour prendre plusieurs valeurs et améliorer la précision
    voltage = analogRead(EC_PIN) / 1024.0 * 5000; // lecture de la tension sur la borne de la cellule conductimétrique
    //temperature = readTemperature();  // mesure de la température via la fonction readTemperature dédiée
    ecValue = ec.readEC(voltage, temperature, calibre); // conversion de la tension en conductivité avec prise en compte de la température
    //Serial.print("temperature:");
    //Serial.print(temperature,1);
    //Serial.print("^C  pH:");
    resultatec += ecValue;  //on cumule les valeurs pour le calcul de la moyenne
  }

  resultatec /= cycles;  //on divise la somme des conductivités par le nombre de cycles pour obtenir la valeur moyenne


  if (testFinAcquisition == 0)
  {
    // Envoie les données sur moniteur série si pas la fin d'acquisition
    Serial.print(tempsCourant / 1000); // donne la date en seconde
    Serial.print('\t');
    Serial.println(resultatec, 3); // donne la conductivité avec 3 chiffres après la virgule
  }

  tempsPrec += timeRes;

  affiche_texte_lcd(0, 1, "Derniere mesure :");
  affiche_texte_lcd(0, 2, "t = ");
  lcd.print(tempsCourant / 1000);
  affiche_texte_lcd(8, 2, "s");
  affiche_texte_lcd(0, 3, "sigma = ");
  lcd.setCursor(8, 3);  //on se place sur la 3ème ligne, 8ème caractère de l'afficheur lcd
  if (resultatec < 3) //si la valeur de conductivité est inférieure à 3 mS/cm
  {
    lcd.print(resultatec, 3); //on affiche la moyenne avec 3 chiffre après la virgule
  }
  else
  {
    lcd.print(resultatec, 2); //on affiche la moyenne avec 2 chiffre après la virgule
  }
  affiche_texte_lcd(14, 3, "mS/cm");
}

void affiche_texte_lcd(byte caractere, byte ligne, String texte)
{
  lcd.setCursor(caractere, ligne);
  lcd.print(texte);
}

manque du code dans ce que vous avez posté. Où sont définis tempsCourant, tempsZero etc ?

Quand vous faites cela vous débordez peut êtretimeRes = 1000 * atoi(message);écrivez timeRes = 1000UL * atoi(message);pour forcer le calcul en unsigned long

PS: pas un gros problème si le montage ne tourne pas longtemps, mais pour tester l'expiration d'un délai il vaut mieux faire toujours des soustractionsif (millis() - tempsPrecedent >= duree) {...}

Dans mon essai, j'ai mis 1 seconde donc timeRes=1000, je ne pense pas que cela déborde ; d'autant que dans mon code "initial" (avant l'essai de la machine à états), il fonctionnait bien avec une formule similaire.

Je note pour la soustraction !

Ok - il faudrait mettre des traces.

Normalement comme l’état reste sur GO vous devriez boucler mais sans voir tous le code difficile de dire

Idéalement il vaudrait mieux faire toutes les acquisition et ne plus repasser par la machine à état puisque elle n a plus lieu d’être une fois le paramétrage effectué

Oui, je ne vois aucun Serial.print() de débogage dans le code.
Quand on ne comprends plus ce qui se passe dans le programme, il faut mettre des Serial.print(...) pour envoyer des infos sur le moniteur.
C'est une technique basique de débogage.
Bien sûr, tu enlèves ces écritures quand le pb est résolu (perso je les mets simplement en commentaires ... au cas où un auter pb surviendrait plus tard).

J-M-L:
Ok - il faudrait mettre des traces.

Normalement comme l’état reste sur GO vous devriez boucler mais sans voir tous le code difficile de dire

Idéalement il vaudrait mieux faire toutes les acquisition et ne plus repasser par la machine à état puisque elle n a plus lieu d’être une fois le paramétrage effectué

Si je comprends bien, je place la fonction parametrage dans le setup (= initialisation de mon acquisition) pour ensuite laisser faire les mesures dans le loop ?
Je vais mettre des serial.print pour voir ce qui se passe

Soit dans le setup si vous n’avez plus jamais à demander à l’utilisateur les informations ni rien d’autre à faire en même temps ( avec un while() bien sûr autour car il faut l’appeler en boucle), soit dans la fonction Associée a GO mais dans ce cas elle est bloquante ce qui peut être un souci, soit dans la loop et vous testez l’état pour savoir où vous en êtes.

L’idée du test en début de loop serait un peu comme cela

void loop()
{
  if (etat != GO) (
    ...    // gestion des paramètres 
  } else {
    ....    // acquisition 
  }
  ...  // ici autre chose fait en permanence 
}

Les paramètres d’acquisition sont faites une fois pour toutes : une fois ceci, l’acquisition est lancée (saisie S mais en pratique n’importe quelle saisie) et lance la partie dans le loop à savoir appel à la fonction de lecture tant qu’on n’atteint pas le nombre de points déterminés grâce aux paramètres d’acquisition.
Je n’ai rien à faire en parallèle : il s’agit ici d’un programme pour commander un capteur et en faire un “appareil de mesure” sans rien d’autres.
Je ne pense donc pas que mettre le paramétrage dans le loop soit, in fine, pertinent : je peux donc mettre simplement un code du type

void setup()
{
  //initialisation de la liaison série, du module pH et du lcd et du capteur de température
  Serial.begin(flux);
  ec.begin();
  lcd.init(); // initialisation de l'afficheur
  temp.begin();
 
  menuCalibre();
  
  lcd.clear();
  tempsZero = millis();
  tempsPrec = 0;
if (parametrage())
{
Serial.print(F("t")); 
...
}
}


void loop()
{
    tempsCourant = millis() - tempsZero;
    affiche_texte_lcd(0, 0, "Chrono : ");
    lcd.setCursor(9, 0);
    int longueur = lcd.print(tempsCourant / 1000);
    affiche_texte_lcd(10 + longueur, 0, "s");
    if ((tempsCourant >= tempsPrec) && (cpt <= nbrPoints + 1))
    {
      lecture();
      cpt++;
    }

    if (cpt == (nbrPoints + 2))
    {
      testFinAcquisition = 1; //l'acquisition est finie
      lecture(); //on affiche que sur l'écran les valeurs
    }
  }

}






boolean messageDisponible()
{
  static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
  boolean messageRecu = false;

  while (Serial.available() && !messageRecu) {
    int c = Serial.read();
    if (c != -1) {
      switch (c) {
        case '\r': // on ignore le '\r'
          break;
        case '\n':
          message[indexMessage] = '\0'; // on termine la c-string
          indexMessage = 0; // on se remet au début pour la prochaine fois
          messageRecu = true;
          break;
        default:
          if (indexMessage <= tailleCode - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
          break;
      }
    }
  }
  return messageRecu;
}

void menuCalibre()
{
  Serial.println(F("Quelle gamme de mesure du conductimètre :\n"
                   "calibre 0-2 mS/cm --> tapez 1\n"
                   "calibre 2-20 mS/cm --> tapez 2\n"
                   "calibre automatique --> tapez 3\n"));
  etat = CALIBRE;
}

void menuPeriode()
{
  Serial.println(F("Saisir la période d'échantillonnage (en s) : "));
  Serial.println(F("Saisir 0 pour revenir au menu précédent (choix calibre)\n"));
  etat = PERIODE;
}

void menuDuree()
{
  Serial.println("Saisir la durée totale d'acquisition (en s) : ");
  Serial.println(F("Saisir 0 pour revenir au menu précédent (choix période d'échantillonnage)\n"));
  etat = DUREE;
}

void menuGo()
{
  Serial.println(F("Saisir S pour démarrer l'acquisition : "));
  etat = GO;
}

bool parametrage()
{
  bool gestionTerminee = false;
  switch (etat) {
    case CALIBRE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (strlen(message) == 1) { // et qu'on a un seul caractère
          switch (message[0]) {
            case '1': Serial.println(F("Vous avez choisi le calibre 0-2 mS/cm\n"));   menuPeriode(); break;
            case '2': Serial.println(F("Vous avez choisi le calibre 2-20 mS/cm\n"));  menuPeriode(); break;
            case '3': Serial.println(F("Vous avez choisi le calibre automatique\n")); menuPeriode(); break;
            default: Serial.print(F("Erreur, vous avez saisi ")); Serial.print(message); Serial.println(F(" qui n'est pas un choix possible. Veuillez sélectionner le choix 1, 2 ou 3")); break;
          }
        } else {
          Serial.print(F("Erreur, vous avez saisi ")); Serial.print(message); Serial.println(F(" qui n'est pas un choix possible. Veuillez sélectionner le choix 1, 2 ou 3"));
          menuCalibre();
        }
      }
      break;

    case PERIODE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (!strcmp(message, "0")) { // a-t-il tapé 0 ?
          menuCalibre();
        } else {
          //  A FAIRE: vérifier si message ne contient que des chiffres
          Serial.print(F("Période d'échantillonnage saisie : ")); Serial.println(message); Serial.println();
          timeRes = 1000 * atoi(message);
          menuDuree();
        }
      }
      break;

    case DUREE:
      if (messageDisponible()) { // si l'utilisteur a validé
        if (!strcmp(message, "0")) { // a-t-il tapé 0 ?
          menuPeriode();
        } else {
          //  A FAIRE: vérifier si message ne contient que des chiffres
          Serial.print(F("Durée d'acquisition saisie : ")); Serial.println(message); Serial.println();
          dureeAcquisition = 1000 * atoi(message);
          nbrPoints = dureeAcquisition / timeRes;
          menuGo();
          
        }
      }
      break;

    case GO:
      if (messageDisponible()) { // si l'utilisteur a validé

Comme dit précédemment, dans le setup() il faudra mettre un while car paramtrage() est prévu non bloquant. il faut l’appeler sans cesse. la loop() nous rendait ce service avant.

while(true) {
  if (parametrage()) {
    Serial.print(F("t")); 
     ...
    break; // <<=== ON QUITTE LE WHILE
  }
}

Bonjour,

Avec vos retours, tout fonctionne.

Merci beaucoup à tous pour le temps partagé !

Bravo