Nous allons vu dans les activités précédentes qu'on pouvait fournir des informations à notre robot soit via la télécommande infrarouge, soit via la liaison série.
Nous allons voir aujourd'hui comment réaliser une commande en deux temps :
Commençons par réaliser une liaison série simple entre l'arduino et le poste de programmation.
#include "MeOrion.h"
#include <SoftwareSerial.h>
void setup()
{
Serial.begin(9600);
Serial.println("Ceci est un message automatique pour dire qu'on est dans la fonction setup.");
delay(1000);
}
void loop()
{
Serial.println("Ceci est un message automatique pour dire qu'on est dans la fonction loop.");
Serial.println("Tapez une lettre puis ENTREE");
char a = '';
if (Serial.available())
{
a = Serial.read();
switch(a)
{
case 'A': Serial.println("Touche A"); break;
case 'B': Serial.println("Touche B"); break;
default: break;
}
}
Serial.println(a);
delay(1000);
}
01° Utiliser ce programme sur votre robot. Ouvrir le moniteur série de façon à pouvoir lire l'affichage. Envoyer des caractères.
On voit que cela fonctionne mais c'est assez pénible cet affichage qui change régulièrement à chaque démarrage de la fonction loop.
Pour effacer ce problème, nous allons créer un booléen (qui ne peut prendre que true ou false) de nom affichage. Si on n'a pas rentré de caractères, affichage doit être à false. On ne doit alors pas réactiver le println qui affiche le message demandant de rentrer une lettre.
#include "MeOrion.h"
#include <SoftwareSerial.h>
bool affichage = true;
void changeAffichage()
{
// Inversion de la valeur de affichage
affichage = not(affichage);
}
void setup()
{
Serial.begin(9600);
Serial.println("Ceci est un message automatique pour dire qu'on est dans la fonction setup.");
delay(1000);
}
void loop()
{
if (affichage)
{
Serial.println("Ceci est un message automatique pour dire qu'on est dans la fonction loop.");
Serial.println("Tapez une lettre puis ENTREE");
changeAffichage();
}
char a = ' ';
if (Serial.available())
{
a = Serial.read();
switch(a)
{
case 'A': Serial.println("Touche A"); changeAffichage(); break;
case 'B': Serial.println("Touche B"); changeAffichage(); break;
default: break;
}
}
if (affichage)
{
Serial.println(a);
}
}
02° Que vaut initialement affichage ? Le premier IF de la fonction loop permet-il alors d'activer les println. Que fait la fonction changeAffichage ?
...CORRECTION...
affichage contient true.
La condition du premier if est donc vrai : on va donc afficher les println puis on lance la fonction changeAffichage.
Cette fonction inverse la valeur de affichage en utilisant not et en affectant la nouvelle valeur dans la variable.
03° Que se passe-t-il ensuite tant qu'on n'envoie pas de caractères ?
...CORRECTION...
On affecte espace dans la variable a. On rentre ensuite dans le second if si la liaision série est établie et disponible. Si on ne tape rien, on ne fera donc plus que cela en boucle puisque la variable affichage contient pour l'instant false.
Maintenant que nous avons trouvé le moyen de demander en boucle des caractères, il serait bon de les mémoriser, non ?
Mais comment parvenir à mémoriser 10 actions ? Créer des 10 variables et un compteur ? Et faire un switch sur le compteur pour savoir quelles variables affectées ?
Non. Il y a mieux : on peut enregistrer les données dans les cases numérotés d'un tableau.
Les tableaux sont un ensemble de variables regroupées dans une même entité.
Ainsi une chaîne de caractères "AbcD" n’est qu’une liste de caractères : le premier vaut "a", le second vaut "b".
Mais attention, la première case d’une liste est numérotée n°0 et non n°1. Du coup, il y a un décalage de un par rapport à la logique humaine de base où on nomme la première case la case n°1.
Mais, on peut également créer une liste contenant les nombres 12, 45, 41 et –12.
Pour le cas "AbcD", on peut donc stocker cela dans un tableau de char :
Case d'index | [ 0 ] | [ 1 ] | [ 2 ] | [ 3 ] |
---|---|---|---|---|
pointe | A | b | c | D |
Pour le cas 12, 45, 41 et 23, on peut donc stocker cela dans un tableau de byte, un nombre entier compris de 0 à 255 et donc stocker sur 1 octet. Tout comme uint8_t :
Case d'index | [ 0 ] | [ 1 ] | [ 2 ] | [ 3 ] |
---|---|---|---|---|
pointe | 12 | 45 | 41 | 23 |
Attention : les cases sont numérotées de 0 à 3, pas de 1 à 4. Mais il y en a quatre.
Bon alors, comment faire pour déclarer des tableaux en C et en Arduino ?
short nombresPremiers[5];
Un : on fournit le type des variables à stocker : short
Deux : on fournit le nom du tableau (Array en anglais) : nombresPremiers
Trois : on place des crochets [] après le nom.
Quatre : on fournit le nombre de cases entre les crochets : 5
04° Tester le programme ci-dessous. Comment parvient-on à remplir l'une des cases du tableau ? Comment parvient-on à accéder à l'un des éléments du tableau ?
#include "MeOrion.h"
#include <SoftwareSerial.h>
short leTableau[5];
void setup()
{
// On démarre la liaison série
Serial.begin(9600);
// On initialise les cases du tableau
leTableau[0] = 50;
leTableau[1] = -50;
leTableau[2] = 12;
leTableau[2] = -1123;
// On affiche peu à peu les contenus
Serial.println(leTableau[0]);
delay(1000);
Serial.println(leTableau[1]);
delay(1000);
Serial.println(leTableau[2]);
delay(1000);
Serial.println(leTableau[3]);
delay(1000);
}
void loop()
{
}
...CORRECTION...
On peut affecter le contenu d'une case d'index 0 avec l'instruction : leTableau[0] = 50;
On peut récupérer le contenu d'une case d'index 0 avec leTableau[0]
05° Transformer le code pour lire le contenu du tableau à l'aide d'une boucle for.
...CORRECTION...
#include "MeOrion.h"
#include <SoftwareSerial.h>
short leTableau[4];
void setup()
{
// On démarre la liaison série
Serial.begin(9600);
// On initialise les cases du tableau
leTableau[0] = 50;
leTableau[1] = -50;
leTableau[2] = 12;
leTableau[3] = -1123;
// On affiche peu à peu les contenus
for (byte compteur =0; compteur < 4 ; compteur++) {
Serial.println(leTableau[compteur]);
delay(1000);
}
}
void loop()
{
}
Remarque : la taille du tableau est à imposer dès le début. On ne peut pas paramétrer cette taille. Si on se rend compte qu’en fin de compte, on aura voulu 300 cases plutôt que 5, il faut changer les dimensions de tous les tableaux du programme. En espérant ne pas en avoir oublier un seul ! Bon, de toutes manières, la taille mémoire est plutôt limitée dans les Arduinos.
On peut heureusement faire un peu mieux : on peut définir une constante (c’est une variable dont on ne peut pas changer la valeur une fois la première affectation effectuée) et utiliser cette constante pour définir la taille de tous les tableaux dont vous voulez garantir une taille fixe.
Remarque : les constantes sont habituellement notées uniquement en majuscules. C'est une convention d'écriture, pas une obligation. Pour vous le montrer, j'alternerai entre les deux façons de les noter. Dans les chapitres suivants, nous passerons au tout majuscule.
Elle se déclare comme une variable normale mais on utilise le mot-clé const :
byte tailleVoulue = 4 ; # déclare une variable de type integer qui contient 5 pour l’instant
byte const TAILLE_VOULUE = 4 ; # déclare une constante de type integer qui contiendra toujours 5
06° Changer le programme pour pouvoir déclarer la taille du tableau par une constante et afficher le contenu de cases.
...CORRECTION...
#include "MeOrion.h"
#include <SoftwareSerial.h>
byte const TAILLE_VOULUE = 4;
short leTableau[TAILLE_VOULUE];
void setup()
{
// On démarre la liaison série
Serial.begin(9600);
// On initialise les cases du tableau
leTableau[0] = 50;
leTableau[1] = -50;
leTableau[2] = 12;
leTableau[3] = -1123;
// On affiche peu à peu les contenus
for (byte compteur =0; compteur < TAILLE_VOULUE ; compteur++) {
Serial.println(leTableau[compteur]);
delay(1000);
}
}
void loop()
{
}
07° Modifier le programme pour que la constante contiennent 30. Lancer le code pour vérifier le contenu des 30 cases.
Reprenons le code initial :
#include "MeOrion.h"
#include <SoftwareSerial.h>
MeDCMotor motGauche(M2);
MeDCMotor motDroite(M1);
uint8_t motorSpeed = 100;
void avanceEtStop()
{
motGauche.run(motorSpeed);
motDroite.run(motorSpeed);
delay(500);
motGauche.stop();
motDroite.stop();
}
void reculeEtStop()
{
motGauche.run(-motorSpeed);
motDroite.run(-motorSpeed);
delay(500);
motGauche.stop();
motDroite.stop();
}
void gaucheEtStop()
{
motGauche.run(motorSpeed);
motDroite.run(-motorSpeed);
delay(500);
motGauche.stop();
motDroite.stop();
}
void droiteEtStop()
{
motGauche.run(-motorSpeed);
motDroite.run(motorSpeed);
delay(500);
motGauche.stop();
motDroite.stop();
}
void setup()
{
Serial.begin(9600);
Serial.println("Ceci est un message automatique pour dire qu'on est dans la fonction setup.");
delay(1000);
}
void loop()
{
Serial.println("Ceci est un message automatique pour dire qu'on est dans la fonction loop.");
Serial.println("Donner votre instruction : A pour avance, R pour recule, G pour tourne à gauche et D pour tourne à droite");
if (Serial.available())
{
char a = Serial.read();
switch(a)
{
case 'A': avanceEtStop(); break;
case 'R': reculeEtStop(); break;
default: break;
}
}
delay(1000);
}
08° Compléter le code pour qu'il gère également les rotations.
09° Stocker les actions effectuées dans un tableau contenant des char. On créera un tableau de 10 éléments.
10° Tâche finale : lorsqu'on tape X dans la console, on veut que le robot refasse les actions déjà enregistrées.