- Envoyer et recevoir des données sur la voie série
- [Annexe] Ordinateur et voie série dans un autre langage de programmation
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
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 :
- Rouge : meduse, tempete, requin : Des animaux dangereux ou la météo rendent la zPlage dangereuse. Baignade interdite
- Orange : vague : La natation est réservée aux bons nageurs
- Vert : 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 :
char mot[20] = "coucou"
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 :
case | car. |
---|---|
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 chaîne 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. Voici une photo d’illustration du montage ainsi qu’une vidéo du montage en action.
Bon Courage !
Pour ceux voulant uniquement se plonger dans le code ou ne disposant pas du matériel, un simulateur avec uniquement le montage est disponible dans la correction, à la fin de la partie concernant l’électronique.
Correction !
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 :
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)
Voici le simulateur avec juste l’électronique de prêt, à vous de faire le code !
!(https://www.tinkercad.com/embed/7NX9hi4SkUJ)
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 :
// on définit les broches, où les LED sont connectées, en sortie
pinMode(led_vert, OUTPUT);
pinMode(led_rouge, OUTPUT);
pinMode(led_orange, OUTPUT);
// On éteint les LED
digitalWrite(led_vert, HIGH);
digitalWrite(led_orange, HIGH);
digitalWrite(led_rouge, HIGH);
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 ?
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 :
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. 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.
Voici la correction interactive :
!(https://www.tinkercad.com/embed/9xf4ByxOK1B)
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 !
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 !