Bouton avec interruption

Bonjour,

Je voudrais avoir un bouton et lorsque j'appuie dessus une première fois mon programme se dirige vers un autre void mais lorsque je rappuie dessus alors que je suis dans ce meme void alors mon programme se redirige vers le void principal (loop). Je voudrais faire comme ca:

int ledvert = 10 ;
int ledrouge = 5 ;

volatile int state = 0 ;

void setup() {
  Serial.begin(9600) ;
  
  pinMode(ledvert, OUTPUT) ;
  pinMode(ledrouge, OUTPUT) ;
  pinMode(2, INPUT) ;
  
  attachInterrupt(0, interrupt, CHANGE) ;
}
  
void loop() {
  state = 0 ;

  Serial.print("state: ") ;
  Serial.println(state) ;

  while(state == 0) {
    digitalWrite(ledvert, LOW) ;
    digitalWrite(ledrouge, HIGH) ;

    delay(500) ;
  }
}

void interrupt() {
  Serial.println("bouton") ;
  Serial.print("state: ") ;
  Serial.println(state) ;

  if(state == 0) {
    joystick() ;
  }
  else if(state == 1) {
    loop() ;
  }
}

void joystick() {  
  state = 1 ;
  
  Serial.print("state: ") ;
  Serial.println(state) ;
  
  while(state == 1) {
    digitalWrite(ledvert, HIGH) ;
    digitalWrite(ledrouge, LOW) ;

    delay(15) ;
  }
}

(les led sont la pour me signifier ou je suis je sais que j'aurais pu faire plus simplement :slight_smile: )

Mais je rencontre un problème et je ne sais pas d'ou: lorsque j'essaie j'arrive a passer a jusqu'au void joystick mais j'ai deja une erreur, au lieu de m'afficher:
bouton
state: 0

Il m'affiche uniquement bo et je ne comprends vraiment pas pourquoi.

Puis après si je rappuie sur mon bouton il ne fait plus rien il est stuck dans mon void joystick(), je viens de penser que peut être qu'on ne peut pas utiliser la fonction interruption dans un autre void que le loop? Ou juste j'ai fais une erreur?

Quelqu'un aurait une réponse ou même une solution?

Merci :slight_smile:

Bonjour,

Le traitement dans l'interruption doit être le plus réduit posssible. En particulier tu ne peux pas appeler de Serial.print() ou a fortiori la loop.
Si tu tiens vraiment à utiliser une interruption, il faut positionner un flag dans l'interruption et le tester dans tes fonctions (et non dans tes void car void veut dire vide/invalide)

Les routines d'interruption devraient être minimalistes (elles monopolisent l'UC, au detriment ... d'autres routines d'inetrruption telles que le timer (sans lequel millis(), voire delay() seraient inopérants) et la jonction série...(même en emission: vous emettez beaucoup de caractères dans cette routine...)

Votre routine d'interruption peut très bien avoir une seule fonction, (c'est simple si votre bouton est sans rebonds..):

commuter une variable globale et volatile (state peut très bien être un booleen) (elle fera très peu, mais bien et très vite)

le loop n'aurait alors qu'à appeler, suivant les cas, deux fonctions ....
De plus, souhaitez vous que l'interruption soit déclenchée sur un changement, un passage de bas à haut ou un passage de haut à bas?

Merci, je crois comprendre un peu mais je ne comprends pas le fait qu'il ne faut pas qu'il y est trop d'appel de fonction dans mon void interrupt() car pour moi l'interruption ne fait qu'envoyer le "déroulement du programme" vers ce void non?

J'ai bien compris après que une interruption c'est pas super... Mais je pense que j'en ai besoin car mon code est bien plus long et il met environs 30sec a s'exécuter, c'est pour ça que je pense avoir besoin d'une interruption, elle me servirait a stopper mon programme (dit automatique) pour passer au mode manuel (void joystick).

Et j'ai bien un bouton avec un rebond (un bouton poussoir). Je voudrais que l'interruption soit déclenchée lorsque j'appuie dessus donc sa peut être les trois je pense vu que c'est un bouton poussoir il passe de bas a haut et de haut a bas en soit(?)

Merci :slight_smile:

Comme je te l'ai dit tu positionne un flag en interruption et tu le teste dans tes programmes.
Comme ceci par exemple:

int ledvert = 10 ;
int ledrouge = 5 ;

volatile int state = 0 ;

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

  pinMode(ledvert, OUTPUT) ;
  pinMode(ledrouge, OUTPUT) ;
  pinMode(2, INPUT) ;

  attachInterrupt(0, interrupt, RISING) ;
}

void loop()
{
  if (state == 0)
    automatique();
  else
    joystick();
}

void interrupt() {
  state = !state;
}

void joystick() {
  Serial.print("state: ") ;
  Serial.println(state) ;

  while (state == 1) {
    digitalWrite(ledvert, HIGH) ;
    digitalWrite(ledrouge, LOW) ;

    delay(15) ;
  }
}

void automatique() {
  Serial.print("state: ") ;
  Serial.println(state) ;

  while (state == 0) {
    digitalWrite(ledvert, LOW) ;
    digitalWrite(ledrouge, HIGH) ;

    delay(15) ;
  }
}

Si ton bouton a des rebonds (ce qui est le cas en général) il faut ajouter un traitement anti rebond.

LEs rebonds, qui affectent certains boutons poussoirs, ne sont malheureusement pas ce que vous croyez voir: quand on appuie sur ces BP de mauvaise qualité, ils se mettent à rebondir plusieurs centaines de fois par seconde... générant un beau désordre dans les logiciels.

Une interruption bloque dès le départ toutes les interruptions suivantes (et l'envoi de caractères par Serial.print arme une interruption qui fait effectivement la tâche d'impression. Que se passe-t-il si les interruptions sont inhibées?)

Vous auriez interet à décomposer les tâches chronophages en petites étapes assez rapides pour être appelées en séquence (le comportement serait le même qu'avant) , puis une étape après l'autre dans le loop: vous auriez alors un automate assez rapide pour réagir aux sollicitations imprévues du monde externe.

Oui mais je ne peux pas faire ça car mon programme est long et j'ai besoin de pouvoir le passer en mode manuel a n'importe quel moment et de plus mon loop de base fait appel a d'autre void...

Il y a certainement le moyen de l'adapter
Mets ton programme.

Il manque encore des parties que je n'ai pas encore incrémenté.

#include <SharpIR.h>
#include <WiFi.h>

SharpIR sensor( SharpIR::GP2Y0A21YK0F, A0 ) ;

int n = 0 ;

const int ledvert = 4 ;
const int ledorange = 5 ;

const char* ssid = "L'etre parfait" ; // Nom WiFi
const char* password = "oouuaaiiss" ; // Mdp WiFi

// Valeurs pour le serveur Web
const char* host = "api.openweathermap.org" ;
const char* apikey = "1a702a15a2f46e405e61804cf67c0d30" ;
const char* town = "Pamiers,fr" ;

String keyword = String("\"icon\":\"") ; // Chaîne que l'on recherche dans le JSON

const int httpPort = 80 ; // Serveur web attend sur le port 80

bool inBody = false ; // Indique si on est dans l'en-tête HTTP (false) ou dans le contenu de la ressource

// On se place dans le rôle du client en utilisant WifiClient
WiFiClient client ;

void setup() {
  // Initialisation du port série
  Serial.begin(9600) ;

  pinMode(ledvert, OUTPUT) ;
  pinMode(ledorange, OUTPUT) ;

  // On affiche le wifi sur lequel on veut se connecter
  Serial.print("Connexion au WiFi ") ;
  Serial.println(ssid) ;

  WiFi.begin(ssid, password) ; // On se connecte

  while (WiFi.status() != WL_CONNECTED) { // On attend d'être connecté
    delay(500) ;
    Serial.print(".") ; 
  }

  Serial.println("") ; // On affiche les paramètres
  Serial.println("WiFi connecte") ;
  Serial.print("Adresse IP du module ESP: ") ;
  Serial.println(WiFi.localIP()) ;
  Serial.print("Adresse IP ddu point d'accès : ") ;
  Serial.println(WiFi.gatewayIP()) ;

  // On vérifie que la connexion est ok, sinon on passe en mode erreur
  if (!client.connect(host, httpPort)) {
    Serial.println("La connexion avec le serveur a échouée.") ;
  }
}

void loop() {

  digitalWrite(ledvert, LOW) ;
  digitalWrite(ledorange, LOW) ;

  if(int distance = sensor.getDistance() < 20) {
    Serial.print("distance: ") ;
    Serial.println(distance) ;
    IR() ;
  }
  
  digitalWrite(ledvert, HIGH) ;
  digitalWrite(ledorange, LOW) ;
  
  Serial.print("connexion au serveur : ") ;
  Serial.println(host) ;

  // On vérifie que la connexion est ok, sinon on passe en mode erreur
  if (!client.connect(host, httpPort)) {
    Serial.println("La connexion avec le serveur a échouée.") ;
  }

  /* La connexion a réussi on forme le chemin
   URL complexe composée du chemin et de deux
   questions contenant le nom de ville et l'API key */

  String url = String("/data/2.5/weather?q=") + town + "&appid=" + apikey ;

  Serial.print("demande URL: ") ;

  Serial.println(url) ;

  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                                  "Host: " + host + "\r\n" +
                                  "Connection: close\r\n\r\n") ;

  // On attend 1 seconde
  delay(750) ;

  if(int distance = sensor.getDistance() < 20) {
    Serial.print("distance: ") ;
    Serial.println(distance) ;
    IR() ;
  }

  digitalWrite(ledvert, HIGH) ;
  digitalWrite(ledorange, LOW) ;

  inBody = false ; // On est dans l'en-tête

  // On se préparer à recevoir la chaîne de caractères correspondant à l'icone
  String iconString = "" ;

  // Réception de message
  while(client.available()) {
    String line = client.readStringUntil('\r') ;

    if (line.length() == 1) inBody = true ; // Passer l'en-tête jusqu'à une ligne vide

    if (inBody) {
      // Ligne du corps du message, on cherche le mot clé
      int pos = line.indexOf(keyword) ;

      if (pos > 0) {
        // indexOf donne la position du début du mot-clé, en ajoutant sa longueur
        // On se place à la fin.
        pos += keyword.length() ;

        // On ne souhaite garder que les 3 caractères correspondant à l'icône
        line[pos+3] = '\0' ;

        // On stocke le résultat dans iconString
        iconString = &line[pos] ;
      }
    }
  }

  Serial.print("Icone : ") ;
  Serial.println(iconString) ; // On affiche la valeur de l'icône

  if(iconstring == "valeur d'inondation") {
    flood() ; // Il n'y a pas de double vérification mais on pourrait très bien vérifier sur d'autres site si les informations se coordonnes
  }

  if(int distance = sensor.getDistance() < 20) {
    Serial.print("distance: ") ;
    Serial.println(distance) ;
    IR() ;
  }

  digitalWrite(ledvert, HIGH) ;
  digitalWrite(ledorange, LOW) ;

  delay(10000) ; // On attend 10 secondes avant la prochaine requête

  // Il va falloir que je trouve a quoi correspond l'icone qui m'interesse pour l'exploiter   
}

// Fonction 'IR', vérifie que l'eau est bien trop haute et que ce soit pas juste une erreur
void IR() {
  digitalWrite(ledvert, LOW) ; // Eteint la led verte qui signale que le niveau d'eau est bon
  digitalWrite(ledorange, HIGH) ; // Allume la led orange qui signale la vérification du niveau d'eau

  // Vérifie à 4 reprises que le niveau d'eau est trop haut pour annuler les erreurs
  while(sensor.getDistance() < 20 || n != 4) {
    n = n++ ;
    delay(500) ;
  }

  // Réinitialise n pour les prochaines vérifications du niveau d'eau 
  // et renvoie a la fonction 'flood' si le niveau de l'eau et bien trop haut après la seconde vérification
  if(n == 4) {
    n = 0 ; // Réinitialise  n pour les prochaines vérification de niveau d'eau
    flood() ; // Déclanche la fonction 'flood' ci-dessous
  }
  delay(500) ; // on attend 0.5s avant la reprise de la fonction 'loop' pour reposer la petite carte
}


// Fonction 'flood', elle déclanche le système de levage
void flood() {
  digitalWrite(ledorange, millis() / 500 % 2) ; // Fait clignoter la led orange pour signaler aux utilisateurs que le système va protéger la maison d'une inondation via le système de levage

  digitalWrite ;
  digitalWrite ;
  digitalWrite ;
  digitalWrite ;
  delay() ;

  digitalWrite ;
  digitalWrite ;
  digitalWrite ;
  digitalWrite ;
  delay() ;

  if(sensor.getDistance() < 20) {
    IR() ;
  }
}
1 Like

Bon, vous utilisez sharpIR, qui doit être très bien;
que vois je dans Arduino_SharpIR/src/SharpIR.cpp at master · qub1750ul/Arduino_SharpIR · GitHub, ligne 5

if( !avoidBurstRead ) while( millis() <= lastTime + 20 ) {} //wait for sensor's sampling time

si getDistance est appelé sans mode burst depuis une interruption (quand on est dans une interruption, toutes les autres interruptions sont inhibées: en particulier les interruptions du timer, qui gère le comptage des millisecondes....) vous pouvez générer des délais infinis.... Ce n'est pas une structure viable...
Edité: j'ai fait 2 hypothèses, a) que ma mémoire ne flanche pas, b) que vous avez un avr (sinon, je ne sais pas: même sur PC, ils recommandent des interruptions très courtes...)
Edité encore:
vous faites appel à delay() à tour de bras (un millis() bloquant). Comme millis() a de forte chances, une fois rentré dans une interruption, de ne pas évoluer, vous allez générer des délais .... infinis..

Si j'ai bien compris c'est si getDistance est appelé par une interruption hors dans mon cas je n'ai pas ce problème car si interruption il y a alors cette fonction ne sera pas appelé (mode burst?)

D'accord donc les delay il faut en utiliser très peu c'est bien ça? Le problème c'est que j'en ai pas mal besoin surtout dans le loop car c'est une connexion a un wifi et il faut que j'attende les réponses et elles se font parfois... longues.
Si j'ai bien compris une interruption commence lorsqu'elle est appelé (logique) et se finis lorsque le programme appelé est finis, hors dans mon cas jamais car elle fait appel a une autre qui ne se finis jamais a moins que je re fasse appel a une interruption qui est impossible car vous m'avez dit que lorsqu'on est dans une interruption les autres sont inhibées. Voila ce que je crois avoir compris.

Edité: Merci de m'avoir dit que les interruptions sont recommandé d'être courte je ne le savais pas et également le fait qu'ils ne faut pas abuser des delay :slight_smile: