[ Arduino 303] [TP] Baignade interdite !

Afin d’appliquer vos connaissances acquises durant la lecture de ce tutoriel, nous allons maintenant faire un gros TP. Il regroupera tout ce que vous êtes censé savoir en terme de matériel (LED, boutons, voie série et bien entendu Arduino) et je vous fais aussi confiance pour utiliser au mieux vos connaissances en terme de « savoir coder » (variables, fonctions, tableaux…). Bon courage et, le plus important : Amusez-vous bien !

Sujet du TP sur la voie série

Contexte

Imaginez-vous au bord de la plage. Le ciel est bleu, la mer aussi… Ahhh le rêve. Puis, tout un coup le drapeau rouge se lève ! « Requiiiinn » crie un nageur… L’application que je vous propose de développer ici correspond à ce genre de situation. Vous êtes au QG de la zPlage, le nouvel endroit branché pour les vacances. Votre mission si vous l’acceptez est d’afficher en temps réel un indicateur de qualité de la plage et de ses flots. Pour cela, vous devez informer les zTouristes par l’affichage d’un code de 3 couleurs. Des zSurveillants sont là pour vous prévenir que tout est rentré dans l’ordre si un incident survient.

Objectif

Comme expliqué ci-dessus, l’affichage de qualité se fera au travers de 3 couleurs qui seront représentées par des LEDs :

  • Rouge : Danger, ne pas se baigner
  • Orange : Baignade risquée pour les novices
  • Vert : Tout baigne !

La zPlage est équipée de deux boutons. L’un servira à déclencher un SOS (si quelqu’un voit un nageur en difficulté par exemple). La lumière passe alors au rouge clignotant jusqu’à ce qu’un sauveteur ait appuyé sur l’autre bouton signalant « Problème réglé, tout revient à la situation précédente« . Enfin, dernier point mais pas des moindres, le QG (vous) reçoit des informations météorologiques et provenant des marins au large. Ces messages sont retransmis sous forme de textos (symbolisés par la voie série) aux sauveteurs sur la plage pour qu’ils changent les couleurs en temps réel. Voici les mots-clés et leurs impacts :

  • meduse, tempete, requin : Des animaux dangereux ou la météo rendent la zPlage dangereuse. Baignade interdite
  • vague : La natation est réservée aux bons nageurs
  • surveillant, calme : Tout baigne, les zSauveteurs sont là et la mer est cool

Conseil

Voici quelques conseils pour mener à bien votre objectif.

Réalisation

– Une fois n’est pas coutume, nommez bien vos variables ! Vous verrez que dès qu’une application prend du volume il est agréable de ne pas avoir à chercher qui sert à quoi. – N’hésitez pas à décomposer votre code en fonction. Par exemple les fonctions changerDeCouleur() peuvent-être les bienvenues. 🙄

Précision sur les chaines de caractères

Lorsque l’on écrit une phrase, on a l’habitude de la finir par un point. En informatique c’est pareil mais à l’échelle du mot ! Je m’explique. Une chaîne de caractères (un mot) est, comme l’indique son nom, une suite de caractères. Généralement on la déclare de la façon suivante :

Lorsque vous faites ça, vous ne le voyez pas, l’ordinateur rajoute juste après le dernier caractère (ici ‘u’) un caractère invisible qui s’écrit ‘\0‘ (antislash-zéro). Ce caractère signifie « fin de la chaîne ». En mémoire, on a donc :

mot[0] ‘c’
mot[1] ‘o’
mot[2] ‘u’
mot[3] ‘c’
mot[4] ‘o’
mot[5] ‘u’
mot[6] ‘\0’

Ce caractère est très important pour la suite car je vais vous donner un petit coup de pouce pour le traitement des mots reçus.

Une bibliothèque, nommée « string » (chaîne en anglais) et présente nativement dans votre logiciel Arduino, permet de traiter des chaînes de caractères. Vous pourrez ainsi plus facilement comparer deux chaînes avec la fonction strcmp(chaine1, chaine2). Cette fonction vous renverra 0 si les deux chaînes sont identiques. Vous pouvez par exemple l’utiliser de la manière suivante :

Le truc, c’est que cette fonction compare caractère par caractère les chaînes, or celle de droite : « requin » possède ce fameux ‘\0’ après le ‘n’. Pour que le résultat soit identique, il faut donc que les deux chaînes soient parfaitement identiques ! Donc, avant d’envoyer la chaine tapée sur la voie série, il faut lui rajouter ce fameux ‘\0’.

Je comprends que ce point soit délicat à comprendre, je ne vous taperais donc pas sur les doigts si vous avez des difficultés lors de la comparaison des chaînes et que vous allez vous balader sur la solution… Mais essayez tout de même, c’est tellement plus sympa de réussir en réfléchissant et en essayant ! 🙂

Résultat

Prenez votre temps, faites-moi quelque chose de beau et amusez-vous bien ! Je vous laisse aussi choisir comment et où brancher les composants sur votre carte Arduino. :ninja: Voici une photo d’illustration du montage ainsi qu’une vidéo du montage en action.

Photo du TP
Bon Courage !

Correction !

On corrige ?

J’espère que vous avez réussi à avoir un bout de solution ou une solution complète et que vous vous êtes amusé. Si vous êtes énervé sans avoir trouvé de solutions mais que vous avez cherché, ce n’est pas grave, regardez la correction et essayez de comprendre où et pourquoi vous avez fait une erreur. 🙂

Le schéma électronique

Commençons par le schéma électronique, voici le mien, entre vous et moi, seules les entrées/sorties ne sont probablement pas les mêmes. En effet, il est difficile de faire autrement que comme ceci :

[TP] Baignade Interdite schema [TP] Baignade Interdite montage

Quelles raisons nous ont poussés à faire ces branchements ? Eh bien :

  • On utilise la voie série, donc il ne faut pas brancher de boutons ou de LED sur les broches 0 ou 1 (broche de transmission/réception)
  • On utilisera les LED à l’état bas, pour éviter que la carte Arduino délivre du courant
  • Les rebonds des boutons sont filtrés par des condensateurs (au passage, les boutons sont actifs à l’état bas)

Les variables globales et la fonction setup()

Poursuivons notre explication avec les variables que nous allons utiliser dans le programme et les paramètres à déclarer dans la fonction setup().

Les variables globales

Afin d’appliquer le cours, on se servira ici d’un tableau pour contenir les numéros des broches des LED. Cela nous évite de mettre trois fois « int leds_xxx » (vert, orange ou rouge). Bien entendu, dans notre cas, l’intérêt est faible, mais ça suffira pour l’exercice.

Et c’est quoi ça « #define » ?

Le « #define » est ce que l’on appelle une directive de préprocesseur. Lorsque le logiciel Arduino va compiler votre programme, il va remplacer le terme défini par la valeur qui le suit. Par exemple, chaque fois que le compilateur verra le terme VERT (en majuscule), il mettra la valeur 0 à la place. Tout simplement ! C’est exactement la même chose que d’écrire : const int btn_SOS = 2;

La fonction setup()

Rien de particulier dans la fonction setup() par rapport à ce que vous avez vu précédemment, on initialise les variables

Dans le code précédent, l’astuce mise en œuvre est celle d’utiliser une boucle for pour initialiser les broches en tant que sorties et les mettre à l’état haut en même temps ! Sans cette astuce, le code d’initialisation (lignes 11 à 15) aurait été comme ceci :

Si vous n’utilisez pas cette astuce dans notre cas, ce n’est pas dramatique. En fait, cela est utilisé lorsque vous avez 20 ou même 100 LED et broches à initialiser ! C’est moins fatigant comme ça… Qui a dit programmeur ? o_O

La fonction principale et les autres

Algorithme

Prenez l’habitude de toujours rédiger un brouillon de type algorithme ou quelque chose qui y ressemble avant de commencer à coder, cela vous permettra de mieux vous repérer dans l’endroit où vous en êtes sur l’avancement de votre programme. Voilà l’organigramme que j’ai fait lorsque j’ai commencé ce TP :

Organigramme baignade interdite

Et voilà en quelques mots la lecture de cet organigramme:

  • On démarre la fonction loop
  • Si on a un appui sur le bouton SOS :
    • On commence par faire clignoter la led rouge pour signaler l’alarme
    • Et on clignote tant que le sauveteur n’a pas appuyé sur le second bouton
  • Sinon (ou si l’évènement est fini) on vérifie la présence d’un mot sur la voie série
    • S’il y a quelque chose à lire on va le récupérer
    • Sinon on continue dans le programme
  • Enfin, on met à jour les drapeaux
  • Puis on repart au début et refaisons le même traitement

Fort de cet outil, nous allons pouvoir coder proprement notre fonction loop() puis tout un tas de fonctions utiles tout autour.

Fonction loop()

Voici dès maintenant la fonction loop(), qui va exécuter l’algorithme présenté ci-dessus. Vous voyez qu’il est assez « léger » car je fais appel à de nombreuses fonctions que j’ai créées. Nous verrons ensuite le rôle de ces différentes fonctions. Cependant, j’ai fait en sorte quelles aient toutes un nom explicite pour que le programme soit facilement compréhensible sans même connaître le code qu’elles contiennent.

Lecture des données sur la voie série

Afin de garder la fonction loop « légère », nous avons rajouté quelques fonctions annexes. La première sera celle de lecture de la voie série. Son job consiste à aller lire les informations contenues dans le buffer de réception du micro-contrôleur. On va lire les caractères en les stockant dans le tableau global « mot[] » déclaré plus tôt. La lecture s’arrête sous deux conditions :

  • Soit on a trop de caractère et donc on risque d’inscrire des caractères dans des variables n’existant pas (ici tableau limité à 20 caractères)
  • Soit on a rencontré le caractère symbolisant la fin de ligne. Ce caractère est ‘\n’.

Voici maintenant le code de cette fonction :

Allumer les drapeaux

Voilà un titre à en rendre fou plus d’un ! Vous pouvez ranger vos briquets, on en aura pas besoin. :mrgreen: Une deuxième fonction est celle permettant d’allumer et d’éteindre les LED. Elle est assez simple et prend un paramètre : le numéro de la LED à allumer. Dans notre cas : 0, 1 ou 2 correspondant respectivement à vert, orange, rouge. En passant le paramètre -1, on éteint toutes les LED.

Vous pouvez voir ici un autre intérêt du tableau utilisé dans la fonction setup() pour initialiser les LED. Une seule ligne permet de faire l’allumage de la LED concernée !

Faire clignoter la LED rouge

Lorsque quelqu’un appui sur le bouton d’alerte, il faut immédiatement avertir les sauveteurs sur la zPlage. Dans le programme principal, on va détecter l’appui sur le bouton SOS. Ensuite, on passera dans la fonction alerte() codée ci-dessous. Cette fonction est assez simple. Elle va tout d’abord relever le temps à laquelle elle est au moment même (nombre de millisecondes écoulées depuis le démarrage). Ensuite, on va éteindre toutes les LED. Enfin, et c’est là le plus important, on va attendre du sauveteur un appui sur le bouton. TANT QUE cet appui n’est pas fait, on change l’état de la LED rouge toute les 250 millisecondes (choix arbitraire modifiable selon votre humeur). Une fois que l’appui du Sauveteur a été réalisé, on va repartir dans la boucle principale et continuer l’exécution du programme.

Comparer les mots

Et voici maintenant le plus dur pour la fin, enfin j’exagère un peu. En effet, il ne vous reste plus qu’à comparer le mot reçu sur la voie série avec la banque de données de mots possible. Nous allons donc effectuer cette vérification dans la fonction comparerMot(). Cette fonction recevra en paramètre la chaîne de caractères représentant le mot qui doit être vérifié et comparé. Elle renverra ensuite « l’état » (vert (0), orange (1) ou rouge (2)) qui en résulte. Si aucun mot n’a été reconnu, on renvoie « ORANGE » car incertitude.

Code complet

Comme vous avez été sage jusqu’à présent, j’ai rassemblé pour vous le code complet de ce TP. Bien entendu, il va de pair avec le bon câblage des LED, placées sur les bonnes broches, ainsi que les boutons et le reste… Je vous fais cependant confiance pour changer les valeurs des variables si les broches utilisées sont différentes.

Je rappel que si vous n’avez pas réussi à faire fonctionner complètement votre programme, aidez vous de celui-ci pour comprendre le pourquoi du comment qui empêche votre programme de fonctionner correctement ! A bons entendeurs. 😉

Améliorations

Je peux vous proposer quelques idées d’améliorations que je n’ai pas mises en oeuvre, mais qui me sont passées par la tête au moment où j’écrivais ces lignes :

Améliorations logicielles

Avec la nouvelle version d’Arduino, la version 1.0,; il existe une fonction SerialEvent() qui est exécutée dès qu’il y a un évènement sur la voie série du micro-contrôleur. Je vous laisse le soin de chercher à comprendre comment elle fonctionne et s’utilise, sur cette page.

Améliorations matérielles

  • On peut par exemple automatiser le changement d’un drapeau en utilisant un système mécanique avec un ou plusieurs moteurs électriques. Ce serait dans le cas d’utilisation réelle de ce montage, c’est-à-dire sur une plage…
  • Une liaison filaire entre un PC et une carte Arduino, ce n’est pas toujours la joie. Et puis bon, ce n’est pas toujours facile d’avoir un PC sous la main pour commander ce genre de montage. Alors pourquoi ne pas rendre la connexion sans-fil en utilisant par exemple des modules XBee ? Ces petits modules permettent une connexion sans-fil utilisant la voie série pour communiquer. Ainsi, d’un côté vous avez la télécommande (à base d’Arduino et d’un module XBee) de l’autre vous avez le récepteur, toujours avec un module XBee et une Arduino, puis le montage de ce TP avec l’amélioration précédente.

Sérieusement si ce montage venait à être réalité avec les améliorations que je vous ai données, prévenez-moi par MP et faites en une vidéo pour que l’on puisse l’ajouter en lien ici même ! :mrgreen:

Voila une grosse tâche de terminée ! J’espère qu’elle vous a plu même si vous avez pu rencontrer des difficultés. Souvenez-vous, « à vaincre sans difficulté on triomphe sans gloire », donc tant mieux si vous avez passé quelques heures dessus et, surtout, j’espère que vous avez appris des choses et pris du plaisir à faire votre montage, le dompter et le faire fonctionner comme vous le souhaitiez !

22 commentaires

    • Aucune raison particulière. Je préfère la structure while pour ce cas ci puisqu’on ne sait pas quand elle est censé se terminer, SAUF si le compteur de caractères arrive au max. On aurait pu faire tout pareil avec une boucle for sur le compteur de caractère et quitter la boucle si il n’y a plus rien a lire. Comme je trouvais le premier cas plus clair et plus fréquent, je l’ai préféré. Mais encore une fois la boucle for est possible aussi ici.

  1. Bonjour Eskimon,
    Est-ce que tu pourrais me donner un petit coup de main?

    J’ai fais le câblage (et puis je suis allé vérifié pour être sur dans la partie solution)
    J’ai commencé à coder la partie d’alerte mais la LED rouge clignote quoi que je fasse. J’y aie passé déjà 2 heures sans résultat donc je te demande un peux d’aide 🙂 .

    Voici mon programme (partie alerte) :

    Merci si tu pouvais y jeté un œil ça serrai SUPER simpas !!!

    Encore merci 🙂

    Maxime

    • Salut !

      D’après ton code, si ca clignote tout le temps c’est que tu restes coincé dans la fonction alarme(). Il semble donc y avoir un problème avec les boutons. Je te conseille de vérifier ton montage 😉
      Si cette vérification ne résout rien, l’idéal serait de poster un sujet d’aide sur les forums d’aides de Zeste de Savoir avec si possible une photo du montage, ca devrait être plus simple ainsi 🙂

  2. Bonjour Eskimon
    tout d’abord merci pour ces Tuto que je suis depuis presque un moi maintenant.
    jusqu’a présent j’essaie toujours de trouver des solutions par moi-même et souvent j’essaie même de modifier tes solutions pour aller plus loin et trouver d’autre façon de faire.
    Mais pour cette exercice « alerte baignade », je n’arrive pas à faire de comparaison de chaine.
    J’ai donc fait un copier/coller de ta solution parce que je ne voyais pas ce que je faisait comme erreur, et cela ne marche pas non plus.

    #define VERT 0
    #define ORANGE 1
    #define ROUGE 2

    int etat = 0; //stock l’état de la situation (vert = 0, orange = 1, rouge = 2)
    char mot[20]; //le mot lu sur la voie série

    //numéro des broches utilisées
    const int btn_SOS = 2;
    const int btn_OK = 3;

    //tableau de 3 éléments contenant les numéros de broches des LED
    const int leds[3] = {11,12,13};

    void setup()
    {
    //On démarre la voie série avec une vitesse de 9600 bits/seconde
    Serial.begin(9600);

    //réglage des entrées/sorties
    //les entrées (2 boutons)
    pinMode(btn_SOS, INPUT);
    pinMode(btn_OK, INPUT);

    //les sorties (3 LED) éteintes
    for(int i=0; i 0 && i lit un mot sur la voie série (lit jusqu’à rencontrer le caractère ‘\n’)
    Fonction allumerDrapeau() :
    > Allume un des trois drapeaux
    > paramètre : le numéro du drapeau à allumer (note : si le paramètre est -1, on éteint toutes les LED)
    */

    void allumerDrapeau(int numLed)
    {
    //On commence par éteindre les trois LED
    for(int j=0; j 250)
    {
    //on change l’état de la LED rouge
    clignotant = !clignotant; //si clignotant était FALSE, il devient TRUE et inversement
    //la LED est allumée au gré de la variable clignotant
    digitalWrite(leds[ROUGE], clignotant);
    //on se rappel de la date de dernier passage
    temps = millis();
    }
    }
    }

    int comparerMot(char mot[])
    {
    //on compare les mots « VERT » (surveillant, calme)
    if(strcmp(mot, « surveillant ») == 0)
    {
    return VERT;
    }
    if(strcmp(mot, « calme ») == 0)
    {
    return VERT;
    }
    //on compare les mots « ORANGE » (vague)
    if(strcmp(mot, « vague ») == 0)
    {
    return ORANGE;
    }
    //on compare les mots « ROUGE » (meduse, tempete, requin)
    if(strcmp(mot, « meduse ») == 0)
    {
    return ROUGE;
    }
    if(strcmp(mot, « tempete ») == 0)
    {
    return ROUGE;
    }
    if(strcmp(mot, « requin ») == 0)
    {
    return ROUGE;
    }

    //si on a rien reconnu on renvoi ORANGE
    return ORANGE;
    }
    enfin la comparaison de chaine ne marche pas la fonction « strcmp » ne retourne jamais 0.
    pour vérifier ce qui se passait, j’ai utilisé la voie série pour afficher les valeurs de mot et les valeur retourné d’abord avec un mot (requin) puis avec d’autre pour provoquer une erreur, et chaque fois la valeur retournée est différente. (ci-dessous capture d’écran avec different test
    /Users/roux/Desktop/Capture d’écran 2015-03-17 à 14.20.08.png
    D’avance merci pour venir à mon secours.
    Patrick
    PS je travail sous Mac Y a fil des problèmes connus avec le logiciel Arduino ?

  3. Je n’arrive pas à envoyer la copie d’écran
    ci- joint les résultat copier dans l’éditeur avec comparaison avec le mot « requin »

    requin
    13
    calme
    -15
    vague
    4
    meduse
    -5
    tempete
    2

  4. Salut Eskimon, merci pour ce tuto et ce tp très créatif 🙂
    j’aimerais partager avec vous ma version du programme. A part la structure de mon programme qui est différente, j’ai convenu à ce que les leds jaune, verte et même rouge continuent à changer d’état sous l’effet des mots reçus par liaison série . si lors du clignotement de la led rouge on a par exemple un message « requin » reçu, alors dés appui sur btn_OK, la led rouge va basculer vers l’état allumée. si mtn lors du clignotement de la led rouge on a reçu par exemple « calme » ou « vague » alors le fait qu’on a pas encore appuyé sur btn_OK ne va pas empêcher de bien recevoir l’information et de la visualiser par une mise sous tension de la led verte (orange si « vague »)

    Secret SelectionnerAfficher
  5. Une autre précision que j’ai oublié de mentionner. Maintenant du côté des marins au large, sur leurs bateaux munis de l’interface pour émission/réception (càd le terminal série :p ). si celui chargé d’envoyer l texto a envoyé un mot faux (qui correspond à aucun des mots clés prédéfinis ) il voit apparaître un message (——-Erreur——– ) et là se dit (Tient, il parait que j’ai rentré un mot bidon, je vais donc ré envoyer à nouveau, mais en faisant plus attention cette fois :p )

  6. bonjours Eskimon
    je n’ arrive pas à comparer les mots avec la fonction strcmp, en cherchant sur le net, je suis arriver en utilisant les String. et la fonction .equals (« mot ») ci-joint le code
    // fonction qui lit le message et retourne la valeur du mot.

    String lire_mot(void)
    {
    while (Serial.available() >0)
    {
    char byte_recu = Serial.read (); //on lit chaque caractere
    delay(10);
    // si c’est vide traiter le message
    if (byte_recu ==13)
    {
    mot =cmd; //on sauvegarde le mot
    return mot ; // on retourne le mot trouve
    }
    else
    { //sinon on ajoute le caractere à la memeoire tampon cmd pour deboggage
    cmd += String(byte_recu);
    }
    }
    }
    // fonction qui compare le mot avec ma bibliotheque
    int comparer_mot(String val)
    {

    if((val.equals(« requin »))||(val.equals(« meduse »)))
    {
    clear_cmd();
    return ROUGE;
    }
    else if (val.equals(« vague »))
    {
    clear_cmd();
    return ORANGE;
    }
    else if (val.equals(« calme »))
    {

    clear_cmd();
    return VERT;
    }

    else // pour toutes autres entree
    {
    clear_cmd();
    Serial.println (« Commande inconnue » );
    return VERT;
    }
    }
    Je sait qu’il y a plusieurs façon d’arriver au résultat mais la je ne comprend pas
    Encore merci pour tout

  7. Salut Eskimon
    idem que ROUX, je n’arrive pas à faire de comparaison de chaine…
    Dans mon prog comme dans la correction le strcmp ne retourne jamais 0.
    Lorsque j’initialise le tableau avec char mot[]= »meduses »; par exemple, ça fonctionne.
    donc je pense que c’est au niveau de la bidouille du …
    Ma version est Arduino 1.6.1
    Est-ce que ça peut jouer?

  8. bonjour eskimoon, cela fait deja quelque jour que je modifie ton programme et j ai remarquer que quand quil avait une erreur de frappe tu return la valeur orange. je trouve qui serais domage de fAIRE SORTIR tout les amateur de l eau du coup en remplacant la valeur orange par (etat) du renvoie la derniere etat si il a une erreure
    merci sinon superbe ton tuto japprend pomal

  9. bonjour, j’ai fait tous le code et le montage mais ça ne fonctionnait pas correctement juste par ce que je ne savais pas qu’il fallait laisser un temps a la mémoire lol, peut être serait il intéressant de le mettre avec les autres conseils du tp?En tous cas cétait un tp très intéressant.
    Je me posais juste une question, lorsque je fait appel au temps avec ma carte arduino, comme par exemple pour faire clignoter la led rouge ici, ca fini toujours par s’arrêter.La led clignote pendant quelques secondes puis elle reste figée et je dois relancer le programme pour quelle clignote de nouveau.Une idée de pourquoi ca bloque comme ca?
    Merci pour ces cours et bonne continuation!

  10. Bonsoir , juste une question au niveau logiciel ,pour l ‘ optimisation , je me posait la question : pourquoi ne pas employer un tableau defini manuellement et creer une fonction comparemot () avec un ordre logique ( exemple du plus soft => au plus terrible ) et du coup n ‘ ecrire que 4 ou 5 lignes ?
    a la place de :

    int comparerMot(char mot[])
    {
    //on compare les mots "VERT" (surveillant, calme)
    if(strcmp(mot, "surveillant") == 0)
    {
    return VERT;
    }
    if(strcmp(mot, "calme") == 0)
    {
    return VERT;
    }
    //on compare les mots "ORANGE" (vague)
    if(strcmp(mot, "vague") == 0)
    {
    return ORANGE;
    }
    //on compare les mots "ROUGE" (meduse, tempete, requin)
    if(strcmp(mot, "meduse") == 0)
    {
    return ROUGE;
    }
    if(strcmp(mot, "tempete") == 0)
    {
    return ROUGE;
    }
    if(strcmp(mot, "requin") == 0)
    {
    return ROUGE;
    }

  11. arf , sorry pour le flood , mais je ne suis pas certain que le parametre de cette fonction soit correct , une erreur de frape peut etre ?

    void lireVoieSerie(void)
    {

    en tout cas superbe tuto d’ aprentissage , agreable , facile a digerer , amusant , et surtout pratique pour les debutants comme moi .

    Bonne soirée .

Laisser un commentaire