exécution de fonctions selon nombre appui sur bouton poussoir

bonjour,

j’ai un petit projet pour mettre un peu de fun dans le chateau en bois de mon fils. Je souhaite faire un jeu de lumière a l’intérieur du château.

J’ai trois modes à configurer (j’ai une lumière jaune, rouge et blanche), un mode1 feu, un mode2 alerte, un mode3 lumière.

je souhaite changer de mode via un seul bouton poussoir, donc avec un appui je met le mode1, 2 appuis mode2, 3 appuis mode 3, 4 je reviens a 0.
Mon problème c’est que je débute en programmation et que j’ai du mal a gérer les boucles.

Ce que j’ai déjà écris : j’ai déjà fait un petit prog mais j’ai constaté que cela ne fonctionnait pas car lorsque mon compteur d’appui est sur 1 par exemple, je souhaiterais qu’il fasse tourner ma fonction ModeFeu en boucle jusqu’à ce que j’appui pour augmenter le compteur. Mais la je rame lol.

voila mon super programe de débutant désolé pour les yeux …

int led2 = 10; // LED 2 = pin 10
int led1 = 9;  // LED 1 = pin 9
int fadeValue1; //PWM intensité led1
int fadeValue2;  //PWM intensité led2
int bouton = 8;  //en PullDown
int compteur;
//int appuiBouton;

void ModeFeu(){  // éclairage mode feu alternance du rouge et du jaune en mode fondu
   
 if (fadeValue1 <= fadeValue2){ //si la varaition de l'intensité de la led1 est < a celle de la led 2 alors
  // on augmente de 10 jusqu'a 255 pour la led1
  for (fadeValue1 = 0 ; fadeValue1 <= 255; fadeValue1 += 10) {    
    analogWrite(led1, fadeValue1);    
    delay(15);
  }
   // on augmente de 10 jusqu'a 255 pour la led1
  for (fadeValue2 = 255 ; fadeValue2 >= 0; fadeValue2 -= 10) { 
    analogWrite(led2, fadeValue2);
    delay(15);
  }
 }
  
 else{  //sinon on fait l'inverse

  for (fadeValue1 = 255 ; fadeValue1 >= 0; fadeValue1 -= 10) {
    analogWrite(led1, fadeValue1);
    delay(15);
  }
  
  for (fadeValue2 = 0 ; fadeValue2 <= 255; fadeValue2 += 10) {
    analogWrite(led2, fadeValue2);
    delay(15);
  }
 }  
}

void setup() {
  Serial.begin(9600);
  fadeValue1 = 0;  //initialisation variable a 0
  fadeValue2 = 0;  //initialisation variable a 0
  compteur = 0;
  digitalWrite(led1, LOW);  //LED 1 éteinte
  digitalWrite(led2, HIGH);  //LED 2 allumée
  pinMode(bouton, INPUT);
  
}

void loop(){
  //compteur appui bouton
  boolean appuiBouton = digitalRead(bouton);
  if (appuiBouton == HIGH){
    compteur = compteur + 1;
    delay(100);

   if (compteur == 1){
     Serial.println("test 1 ok");
     ModeFeu();  
   }
  
   else if (compteur == 2){
     digitalWrite(led1, LOW);
     digitalWrite(led1, LOW);
     Serial.println("test 2 ok");
   }
  
   else if (compteur == 3){
     Serial.println("test 3 ok");
   }

  else if (compteur == 4){
     Serial.println("test 4 ok");
   compteur = 0;
   }
           

   //pour TEST 
    /*while (compteur == 1){
     Serial.println("test 1 ok");
     ModeFeu();  
    }
  
    while (compteur == 2){
     digitalWrite(led1, LOW);
     digitalWrite(led1, LOW);
     Serial.println("test 2 ok");
    }
  
    while (compteur == 3){
     Serial.println("test 3 ok");
    }
                          
    while (compteur == 3){
     Serial.println("test 4 ok");
      compteur = 0;
    }*/             
  }
}

merci pour votre aide

Après lecture rapide, je pense que ton problème vient de l'imbrication de tes tests dans la loop. Je mettrais une } après

  //compteur appui bouton
  boolean appuiBouton = digitalRead(bouton);
  if (appuiBouton == HIGH){
    compteur = compteur + 1;
    delay(100);

et l'enlèverais à la fin de la loop.

Au lieu de tester ensuite la valeur de compteur, tu devrais utiliser les switch / case qui rendent le code plus lisible. Là, dans le case '1', tu appelles ta fonction ModeFeu.

merci pour ta réponse rapide. Oui tu as raison j'ai changé comme tu as dit et j'arrive a un résultat satisfaisant sauf pour la réactivité de bouton poussoir qui fait un peu comme bon li chante ... une idée ?

void loop(){
  //compteur appui bouton
  boolean appuiBouton = digitalRead(bouton);
  if (appuiBouton == HIGH){
    compteur = compteur + 1;
    delay(100);
  }

  switch (compteur){
    case 1 :
     Serial.println("test 1 ok");
     ModeFeu();
     //delay(60);
     break;
    case 2 :
     digitalWrite(led1, LOW);
     digitalWrite(led1, LOW);
     Serial.println("test 2 ok");
     //delay(60);
     break;
    case 3 :
     Serial.println("test 3 ok");
     //delay(60);
    break;
    case 4 :
     Serial.println("test 4 ok");
      compteur = 0;
     //delay(60);
    break;
  }
  //delay(60);  
}

alexbi:
merci pour ta réponse rapide. Oui tu as raison j'ai changé comme tu as dit et j'arrive a un résultat satisfaisant sauf pour la réactivité de bouton poussoir qui fait un peu comme bon li chante ... une idée ?

Bonjour,

Au lieu de tester l'état du bouton, il faut tester l'enfoncement, c'est à dire le changement d'état en comparant l'état courant à l'état précédent (qu'il faut mémoriser).

bon j'ai tenté pas mal de chose mais meme avec quelque chose de ce type : (je pense que tu parlais de ça @kamill)

void loop(){
  //compteur appui bouton
  boutonState = digitalRead(bouton);
  if (boutonState != lastBoutonState){
    if (boutonState == HIGH) {
      compteur++;
      Serial.println("APPUI"); 
      Serial.print("nombre d'appuis:  ");
      Serial.println(compteur, DEC);
    //delay(100);
    }
    else{
      Serial.println("PAS D'APPUI");
    }
    lastBoutonState = boutonState;
  }

cela ne fonctionne pas mon appui sur le bouton n'est pas validé correctement et ne passe pas mon compteur a une valeur supérieur ou il passe mais c'est lorsque j’insiste sur l'appui du bouton.

j'en arrive a la conclusion, que mon problème vient de ma fonction ModeFeu(), qui fait varier mes 2 LEDS avec le PWM et qui prend donc du temps a s’exécuter et bloque le reste du programme pendant son exécution.

ce qu'il me faudrait c'est pouvoir vérifier si j'appui sur le bouton pendant que je fais le fading avec mes LEDS, mais je n'y suis pas arrivé ...

C'est clair que le fading prend beaucoup de temps. 255 * 15ms ça fait un paquet ...
Il te faudrait passer par les interruptions.
Cherche autour de attachInterrupt
Attention de ne pas passer trop de temps dans une routine d'interruption, changer la valeur d'une variable devrait suffire.

@+

J’ai déterré un vieux code que j’ai adapté à ton cas.

Je verrais quelque chose comme ça :

#define LED2    10  // LED 2 = pin 10
#define LED1    9   // LED 1 = pin 9
#define BUTTON  2   // en PullDown
int fadeValue1;     // PWM intensité led1
int fadeValue2;     // PWM intensité led2
int compteur;

void ModeFeu(){  // éclairage mode feu alternance du rouge et du jaune en mode fondu
   
 if (fadeValue1 <= fadeValue2){ //si la varaition de l'intensité de la led1 est < a celle de la led 2 alors
  // on augmente de 10 jusqu'a 255 pour la led1
  for (fadeValue1 = 0 ; fadeValue1 <= 255; fadeValue1 += 10) {   
    analogWrite(LED1, fadeValue1);   
    delay(15);
  }
   // on augmente de 10 jusqu'a 255 pour la led1
  for (fadeValue2 = 255 ; fadeValue2 >= 0; fadeValue2 -= 10) {
    analogWrite(LED2, fadeValue2);
    delay(15);
  }
 }
 
 else{  //sinon on fait l'inverse

  for (fadeValue1 = 255 ; fadeValue1 >= 0; fadeValue1 -= 10) {
    analogWrite(LED1, fadeValue1);
    delay(15);
  }
 
  for (fadeValue2 = 0 ; fadeValue2 <= 255; fadeValue2 += 10) {
    analogWrite(LED2, fadeValue2);
    delay(15);
  }
 } 
}

void ButtonIsr(void)
{
  static long lastDebounceTime = 0;

  if (lastDebounceTime == 0 || (millis() - lastDebounceTime) > 200) {
    lastDebounceTime = millis();
    compteur++;
  }
}

void setup() {
  attachInterrupt(digitalPinToInterrupt(BUTTON), ButtonIsr, RISING);
  Serial.begin(115200);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  digitalWrite(LED1, LOW);  //LED 1 éteinte
  digitalWrite(LED2, HIGH);  //LED 2 allumée
  pinMode(BUTTON, INPUT);
 
}

void loop(){
  switch (compteur) {
  case 1:
    Serial.println("test 1 ok");
    ModeFeu(); 
    break;
  case 2:
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
    Serial.println("test 2 ok");
    break;
  case 3:
    Serial.println("test 3 ok");
    break;
  case 4:
    Serial.println("test 4 ok");
    compteur = 0;
  }
}

Le bouton est déplacé en GPIO 2 pour bénéficier de l’interruption. On peut utiliser la 3 aussi.
Tout dépend de la carte utilisée. Liste des interruptions ICI.
J’ai utilisé 200ms d’anti-rebond. Cela peut paraître énorme, mais mon poussoir est certainement très nul.

Le terminal en en 115000 baud.

@+

SLT
en utilisant la fonction millis() tu fais une pause de 500ms après chaques appui avant de valider la commande puis tu mémorise le compteur.
dans ton exemple j’ai mis ton entrée à 1 en pullup ce qui permet de ne pas utiliser de résistance supplémentaire avec le bouton.

code à tester…

unsigned long t0,t1; // t0 mémorise le temps et t1 tempos d'appuis
boolean m_appuiBouton; // pour mémorisé l'appui du bouton
int p_compteur; // précomptage 

int led2 = 10; // LED 2 = pin 10
int led1 = 9;  // LED 1 = pin 9
int fadeValue1; //PWM intensité led1
int fadeValue2;  //PWM intensité led2
int bouton = 8;  //en PullDown
int compteur;
//int appuiBouton;

void ModeFeu(){  // éclairage mode feu alternance du rouge et du jaune en mode fondu
   
 if (fadeValue1 <= fadeValue2){ //si la varaition de l'intensité de la led1 est < a celle de la led 2 alors
  // on augmente de 10 jusqu'a 255 pour la led1
  for (fadeValue1 = 0 ; fadeValue1 <= 255; fadeValue1 += 10) {   
    analogWrite(led1, fadeValue1);   
    delay(15);
  }
   // on augmente de 10 jusqu'a 255 pour la led1
  for (fadeValue2 = 255 ; fadeValue2 >= 0; fadeValue2 -= 10) {
    analogWrite(led2, fadeValue2);
    delay(15);
  }
 }
 
 else{  //sinon on fait l'inverse

  for (fadeValue1 = 255 ; fadeValue1 >= 0; fadeValue1 -= 10) {
    analogWrite(led1, fadeValue1);
    delay(15);
  }
 
  for (fadeValue2 = 0 ; fadeValue2 <= 255; fadeValue2 += 10) {
    analogWrite(led2, fadeValue2);
    delay(15);
  }
 } 
}

void setup() {
  Serial.begin(9600);
  fadeValue1 = 0;  //initialisation variable a 0
  fadeValue2 = 0;  //initialisation variable a 0
  compteur = 0;
  digitalWrite(led1, LOW);  //LED 1 éteinte
  digitalWrite(led2, HIGH);  //LED 2 allumée
  pinMode(bouton, INPUT);
  digitalWrite(bouton, HIGH);  // active la pullup
}

void loop(){
  t0=millis(); // mémorise le temps
  
  //compteur appui bouton
  boolean appuiBouton = !digitalRead(bouton);
  if (appuiBouton){ // si appuiBouton=1 
    delay(20);  // permet de filtrer les parasites dû au changement d'état du bouton
    if(!m_appuiBouton){ // anti rebond si m_appuiBouton=0
      m_appuiBouton=true;
      p_compteur ++; // incrémente p_compteur de 1
      t1=t0+500; // active la tempo pour attendre un éventuel autre appui
    }
  } else m_appuiBouton=false;

  if (t0>t1 and p_compteur){  // si pas d'autre appui donc tempo fini et si p_compteur>0
    compteur=p_compteur;  // le nombre d'appui est validé
    p_compteur=0; // initialise pour un prochain appui
  

   if (compteur == 1){
     Serial.println("test 1 ok");
     ModeFeu(); 
   }
 
   else if (compteur == 2){
     digitalWrite(led1, LOW);
     digitalWrite(led1, LOW);
     Serial.println("test 2 ok");
   }
 
   else if (compteur == 3){
     Serial.println("test 3 ok");
   }

  else if (compteur == 4){
     Serial.println("test 4 ok");
   compteur = 0;
   }
           

   //pour TEST
    /*while (compteur == 1){
     Serial.println("test 1 ok");
     ModeFeu(); 
    }
 
    while (compteur == 2){
     digitalWrite(led1, LOW);
     digitalWrite(led1, LOW);
     Serial.println("test 2 ok");
    }
 
    while (compteur == 3){
     Serial.println("test 3 ok");
    }
                         
    while (compteur == 3){
     Serial.println("test 4 ok");
      compteur = 0;
    }*/             
  }
}

@hbachetti:
merci a toi pour avoir déterré un de tes vieux code je te confirme qu'il fonctionne à merveille !
je viens juste de le tester et j'avoue que je n'ai pas encore bien compris certaine chose comme le "void ButtonIsr(void)", un void dans le void ? lol. Je suis la pour apprendre alors je vais étudier tout ça !

@fifi82:
merci a toi aussi, j'ai essayé ton code. D'abord petite question "dans ton exemple j'ai mis ton entrée à 1 en pullup ce qui permet de ne pas utiliser de résistance supplémentaire avec le bouton", la tu parles juste du changement que tu as fais ici ?

 //compteur appui bouton
  boolean appuiBouton = !digitalRead(bouton);
  if (appuiBouton){ // si appuiBouton=1

Il semble y avoir plusieurs problèmes, le premier c'est que mon compteur commence directement a se mettre a 1, le deuxième c'est que arrivé a 4 il ne repart pas a 0 (j'ai résolu ca en changeant compteur en m_compteur a la fin du code) et le troisième c'est que le bouton n'ai pas réactif (appui long) et qu'un fois que j'ai fais un tour de compteur plus rien ne se passe...

je viens juste de le tester et j'avoue que je n'ai pas encore bien compris certaine chose comme le "void ButtonIsr(void)", un void dans le void ? lol. Je suis la pour apprendre alors je vais étudier tout ça !

C'est une routine d'interruption. Elle ne retourne rien, donc elle est void, et elle ne prend aucun paramètre, donc void aussi.
Elle est installée par la fonction attachInterrupt :

attachInterrupt(digitalPinToInterrupt(BUTTON), ButtonIsr, RISING);

A chaque fois que tu appuies sur le bouton, la fonction ButtonIsr est appelée, sur le front montant uniquement (RISING).
Cela fonctionne mieux qu'une scrutation dans la loop car l'exécution est instantanée (le programme principal est interrompu pendant l'exécution de la routine ISR et reprend ensuite). Tu as dû le remarquer, c'est très réactif.

Dans le code que je t'ai fourni, le mode feu est permanent, je ne sais pas si c'est ce que tu voulais.

@+

merci a toi hbachetti pour l'explication, maintenant je comprend mieux.

Je me disais que si j'écrivais void fonction(), c'était la même chose que void fonction(void), mais si tu l'écris c'est qu'il doit y avoir une différence, peut être au niveau de la place en mémoire que prend la fonction ?

j'avais du mal a comprendre la fonction attachInterrupt() car elle se trouve dans le void setup() qui pour moi ne s’exécute qu'une fois et après c'est fini.
Après investigation, cela semble être une fonction très spéciale, qui finalement tourne en boucle afin de détecter l'appuie sur le bouton dans mon cas.
Le seul problème c'est qu'on est limité au nombre de 2 pin dispo sur la carte ...

j'étais parti sur un esp8266 pour mon projet mais la je crois qu'il ne va pas y avoir assez de pin ...

Sinon ton code fait PARFAITEMENT, ce que je voulais, le Feu ne peut être éteint que par le pompier qui viendront sauver le roi du château, donc le fonction ModeFeu() doit tourner en boucle !

Je me disais que si j'écrivais void fonction(), c'était la même chose que void fonction(void), mais si tu l'écris c'est qu'il doit y avoir une différence, peut être au niveau de la place en mémoire que prend la fonction ?

Non c'est exactement pareil.

Après investigation, cela semble être une fonction très spéciale, qui finalement tourne en boucle afin de détecter l'appuie sur le bouton dans mon cas.

Non, elle indique simplement au processeur que quand l'interruption arrive, il doit exécuter la routine ISR. L'adresse de la routine ISR est simplement stockée dans un registre du processeur.
Il y a beaucoup de littérature sur le WEB à ce sujet.

Continue à t'amuser avec tout ça.
@+

merci a toi pour les explications !

j'ai fini par tomber sur ça c'est plutôt complet :

http://gammon.com.au/interrupts

Oui, j'ai pas fini de m'amuser :

  • motorisation de l'ouverture du pont levis via un shield motor wemos mini
  • contrôle de l'ouverture des fenêtres avec un servo via potentiomètre,
  • buzzer pour l'alerte ...
  • contrôle de tout ça via wifi? avec une scene sur mon telephone via tasker ? mdr

mes idées vont m'occuper très longtemps je crois lol

Gammon Forum : Electronics : Microprocessors : Interrupts

Le bien connu Nick Gammon. Bon choix.

@+

hbachetti:
Le bien connu Nick Gammon. Bon choix.

C'est l'un des modos du forum