Temporisation de l'info d'un capteur

Ouh-là ! je vois que j'ai réveillé les cervaux musclés :slight_smile:

C'est pas sympa de ressortir tous les trucs que je suis sensé savoir parce que je les ai
appris il y a ...pfiouuuu....

Herriot disait "la culture c'est ce qui reste quand on a tout oublié".
Ben, j'ai tout oublié :frowning:

Bref, je n'ai plus qu'à commander mon petit capteur chinois et à voir ce que ça raconte quand il cause dans une "Analog In".

Délai, entre 2 et 3 semaines habituellement, je reviendrai vous en parler.

En attendant, juste pour ma culture personelle, si quelqu'un pouvait m'aiguiller sur la question initiale de "comment ne prendre en compte une info que si elle dure plus de x secondes" ça me permettrait d'avancer sur le reste.

Merci et bon w-e !

comment ne prendre en compte une info que si elle dure plus de x secondes

Vous lisez l'info de temps en temps (assez souvent). quand elle s'active vous notez l'heure (millis) et marquez que vous attendez maintenant une désactivation (phase de mesure de la durée)

Quand elle se désactive vous notez la nouvelle heure et marquez que vous êtes sorti de l'état "attente de désactivation" et passez à un état prise d'Action

la différence entre les deux temps vous donne le temps "activé" et vous utilisez cette information pour déclencher ou pas votre action

C'est donc une petite machine à états.

par exemple essayez ce code

const byte pinDuBouton = 2;
unsigned long debut, fin, duree;
enum {attente, mesure, action} etat;

void setup() {
  Serial.begin(115200);
  pinMode(pinDuBouton, INPUT_PULLUP);
  etat = attente;
}

void loop() {
  switch (etat) {
    case attente:
      if (digitalRead(pinDuBouton) == LOW) {
        // bouton appuyé
        debut = millis();
        Serial.print(F("ON  @")); Serial.println(debut);
        delay(15); // petit hack pour ne pas avoir de rebond, OK si vous êtes intéressé par des appuis humains
        etat = mesure;
      }
      break;
    case mesure:
      if (digitalRead(pinDuBouton) == HIGH) {
        // bouton relâché
        fin = millis();
        Serial.print(F("OFF @")); Serial.println(fin);
        duree = fin - debut;
        etat = action; // ou on pourrait faire le test sur la durée ici ou au prochain tour de loop
      }
      break;
    case action:
      Serial.print(F("La duree d'appui sur le bouton est de "));
      Serial.print(duree);
      Serial.println(F(" ms"));
      if (duree > 3000) {
        Serial.print(F("ACTION !!! -> Plus de 3 secondes, wow !!"));
      }
      etat = attente;
      break;
  }
}

vous branchez un petit fil dans la pin 2 de votre Arduino et mettez l'autre bout à GND pour simuler l'appui sur le bouton et l'enlevez pour simuler le relâchement du bouton. ça vous donnera des petits messages sur la console série (réglée à 115200 bauds) expliquant ce qu'il se passe.

Bonjour J-M-L,

Je me suis mal exprimé : il faut que l'info dure AU-MOINS x secondes, avant d'agir, et qu'elle ne soit pas relâchée (expliqué plus en détail dans le premier message de cette enfilade).

J'ai déjà utilisé millis() dans ce code pour le démarrage de ma pompe (voir code dans mon post du 16/08 7:51 pm) mais je n'arrive pas à réutiliser cette fonction une deuxième fois sans tout perturber.

Merci de votre aide

Lofanauty:
... il faut que l'info dure AU-MOINS x secondes, avant d'agir, et qu'elle ne soit pas relâchée (expliqué plus en détail dans le premier message de cette enfilade)....
.

C'est pourtant ce que j'ai cru comprendre dans la description de la machines à états ci dessus (appui, je mesure l'heure H1, relâchement je mesure la nouvelle heure H2, si H2-H1 > x seconde, alors je passe à la suite.

68tjs, il ne faut pas se braquer et conclure hâtivement sur des informations incomplêtes, le rms comme le true rms sont de nos jours très fréquemment (voir couramment) mesurés sur nos appareils de mesures, même standards.

Si si je te l'assure, c'est rentré dans la culture électronicienne contemporaine du BAC à BAC++++++ et +.

Et pourtant, je suis un vieux moi aussi, j'ai connu 68 sur les bancs de l'école, mais comme j'ais encore quelques années à "supporter" les jeunes hightechniciens avant la retraite, je me tiens informés pour ne pas être dépassé par ceux-ci.

Bon, donc je m'exprime mal :
C'est :

  • j'appuie
  • si c'est relâché avant x secondes, je ne tiens pas compte de l'info
  • si l'appui est maintenu au moins X secondes je le prends en compte à t=x sans me préoccuper si le relâchement intervient à x+1, x+50, ou jamais.

la finalité étant :

  • la pompe avale de l'air, le "capteur" est actionné (quel qu'il soit)
  • si elle se réamorce en moins de x secondes ("capteur" relâché) je ne fais rien.
  • si le désamorçage persiste plus de x secondes j'arrête la pompe (et donc le "capteur" ne sera pas relâché puisque la pompe sera arrêtée)

J'ai pigé, mais je n'ai pas d'idée à part l'intuition qu'il faut essayer ça
https://www.arduino.cc/en/Reference/Interrupts

Mais je ne sais pas faire.

Comme tu a l'air d'être parmis ceux qui fouillent dans le possible, cherche peut-être dans cette voie (les interruptions).

il faut juste améliorer la machine a états...

pendant la phase de mesure, vous regardez combien de temps s'est écoulé. si vous franchissez le seuil fatidique, vous déclenchez une action spéciale (actionEnCours) sinon si c'est relâché avant le seuil vous pouvez déclencher une autre action et reprendre le cours normal des opérations...

L'avantage de cette approche c'est qu'il n'y a aucun délai dans le code (à part pour éviter le bounce) et donc dans la loop() après le gros switch au départ vous pouvez faire autre chose, clignoter une LED (avec l'approche de l'exemple blink without delay), envoyez des SMS, etc...

const byte pinDuBouton = 2;
unsigned long debut, fin, duree;
enum {attente, mesure, actionEnCours, actionRelache, attenteRelache} etat;

void setup() {
  Serial.begin(115200);
  pinMode(pinDuBouton, INPUT_PULLUP);
  // on décide l'état initial en fonction de l'état du bouton
  if (digitalRead(pinDuBouton) == LOW) {
    // s'il est appuyé on va d'abord attendre qu'il soit relâché
    Serial.println(F("Relachez le bouton"));
    etat = attenteRelache;
  } else { //sinon on est bon, on attend le premier appui
    etat = attente;
  }
}

void loop() {
  switch (etat) {
    case attente:
      if (digitalRead(pinDuBouton) == LOW) {
        // bouton appuyé
        debut = millis();
        Serial.print(F("ON  @")); Serial.println(debut);
        delay(15); // petit hack pour ne pas avoir de rebond, OK si vous êtes intéressé par des appuis humains
        etat = mesure;
      }
      break;

    case mesure:
      if (digitalRead(pinDuBouton) == HIGH) {
        // bouton relâché
        fin = millis();
        Serial.print(F("OFF @")); Serial.println(fin);
        duree = fin - debut;
        etat = actionRelache;
      } else {
        // le bouton est toujours appuyé on teste si ça fait 5 secondes
        if (millis() - debut >= 5000) {
          // délai d'attente OK
          Serial.print(F("Bingo @")); Serial.println(millis());
          etat = actionEnCours;
        }
      }
      break;

    case actionEnCours: // bouton tenu assez longtemps
      Serial.println(F("Bouton tenu plus de 5 secondes"));
      // ici faire ce que vous voulez faire
      // ....
      etat = attenteRelache;
      break;

    case actionRelache: // bouton pas tenu assez longtemps
      Serial.print(F("La duree d'appui sur le bouton est de "));
      Serial.print(duree);
      Serial.println(F(" ms"));
      etat = attente;
      break;

    case attenteRelache: // on attend le relâchement du bouton
      if (digitalRead(pinDuBouton) == HIGH) {
        // bouton relâché
        fin = millis();
        Serial.print(F("OFF @")); Serial.println(fin);
        duree = fin - debut;
        Serial.print(F("La duree d'appui sur le bouton est de "));
        Serial.print(duree);
        Serial.println(F(" ms"));
        etat = attente;
      }
      break;
  }
}

Bonsoir;

Est-ce que le grafcet est une machine d'états?
Pour moi ca y ressemble beaucoup.

Et question suivante, si le grafcet est une machine d'état, alors est ce que ""plclib"
http://www.electronics-micros.com/software-hardware/plclib-arduino/

Ne serait pas aussi un outils ultra simple pour programmer une machine d'états?

plclib, pour l'utiliser très régulièrement, c'est vraiment idéal pour faire un automate quelconque sans trop se soucier des difficultés liées au codage.

Le langage se rattache aux automates à états finis et aux réseaux de Pétri et par exemple permet de gérer du parallélisme avec plusieurs états actifs en même temps ce qui n'est pas le cas dans la théorie des automates finis; il y a donc des différences théoriques et pratiques avec des automates type Moore ou Mealy... et le Grafcet trouve ses limites si vous devez comptabiliser des choses - un réseau de pétri devient alors plus pratique

Bref Le grafcet est un support parmi bien d'autres pour apprendre le concept d'automate, ce n'est pas en soi un automate juste un langage - mais oui c'est une possibilité pour générer ensuite une machine à états finis équivalente (pas toujours possible strictement) (et c'est assez franchouillard :slight_smile: )

Je n'aime pas trop les librairies qui structurent mon code, je trouve qu'on perd en flexibilité et ajoute bcp de lourdeurs mais si ça vous simplifie la vie pourquoi pas !

(bien sûr la génération automatique à partir de modèle à avantage de pouvoir être démontrable mathématiquement et testable)

Tout dépend du temps que met la fonction loop() pour s'effectuer.

Si elle est plus rapide que la temporisation demandée :

  • à l'apparition de l'info tu mémorise la valeur de millis()
  • a chaque entrée dans loop() tu vérifie que l'info est toujours présente et tu compare la nouvelle valeur de millis avec celle qui a déjà été mémorisée.
    Si la différence est supérieure à la tempo que tu cherche tu exploite l'information.
    Si ce n'est pas le cas tu passe pour ce tour dans loop();

Nota : ce que je viens d'écrire ne diffère en rien de ce qui t'a déjà été dit.

Si le temps d'exécution de loop() est trop lent, perso (mais je ne suis pas une flèche en programmation), je n'ai pas de solution si ce n'est reprendre loop() pour l'accélérer.

La boucle infinie est constituée ainsi ;

while(1)
{
loop();
}
Comme la condition while(1) est toujours vérifiée il est impossible de sortir du while et donc à peine sorti de loop() le programme y re-entre.

Hum... Le tuto vante un schema deja propose ici. Les grands esprits se rencontrent.

Quel tuto?

68tjs:
Si le temps d'exécution de loop() est trop lent, perso (mais je ne suis pas une flèche en programmation), je n'ai pas de solution si ce n'est reprendre loop() pour l'accélérer.

On peut aussi appeler plusieurs fois dans la loop() la fonction critique qui vérifie "l'apparition de l'info" et si nécessaire en dernière mesure (pas pour du temps long) "l'apparition de l'info" pourrait générer une interruption qui pourrait dans certains cas suffire à capturer l'info pertinente (eg une roue codeuse)

68tjs:
La boucle infinie est constituée ainsi ;

while(1)
{
loop();
}
Comme la condition while(1) est toujours vérifiée il est impossible de sortir du while et donc à peine sorti de loop() le programme y re-entre.

pas tout à fait exactement cette tête là mais le concept y est:

voilà le vrai main() qui est utilisé à la compilation

en enlevant la phase d'init

int main(void)
{
   setup();
   // enlevé pour simplifier un peu
   for (;;) {
      loop();
      if (serialEventRun) serialEventRun();
   }     
   return 0;
}

on voit que c'est une boucle for sans aucune expression (c'est pareil que le while(true)) mais qu'après l'appel de la fonction loop() si vous avez défini la fonction [url=https://www.arduino.cc/en/Tutorial/SerialEvent]SerialEvent()[/url] (et qu'il y a un caractère dispo sur le port série) alors votre fonction est appelée.

What :astonished: ?! me direz vous, comment se fait-il que SerialEvent() soit appelée alors qu'on voit le code mentionner serialEventRun ?

Bonne question !

Pour les curieux serialEventRun qui semble bien mystérieux est défini dans HardwareSerial.cpp sous cette forme

void serialEventRun(void)
{
#if defined(HAVE_HWSERIAL0)
  if (Serial0_available && serialEvent && Serial0_available()) serialEvent();
#endif
#if defined(HAVE_HWSERIAL1)
  if (Serial1_available && serialEvent1 && Serial1_available()) serialEvent1();
#endif
#if defined(HAVE_HWSERIAL2)
  if (Serial2_available && serialEvent2 && Serial2_available()) serialEvent2();
#endif
#if defined(HAVE_HWSERIAL3)
  if (Serial3_available && serialEvent3 && Serial3_available()) serialEvent3();
#endif
}

le code de cette fonction est plein de define pour vérifier ce que vous avez en port série matériel. Si vous n'en avez aucun port série dispo la fonction serialEventRun est définie à null (dans d'autres architectures) et sinon cette fonction vérifie chacun des ports série dispo et appelle la fonction serialEvent[color=red]x[/color](); en fonction de la présence où non d'un octet sur le port série

x

Tout cela pour dire qu'à la sortie de la loop() il y a un certain nombre de tests qui sont effectués et éventuellement si vous avez implémenté la fonction serialEvent() et qu'il y a un caractère reçu alors vous n'allez pas tout de suite revenir dans la loop().