La carte Arduino UNO R3 est une carte de développement avec tout ce qu'il faut pour sa mise en œuvre, basée sur le microcontrôleur ATMega328P d'Atmel. Elle se programme avec l'EDI (environnement de développement intégré) Arduino, dans le langage Arduino proche du C++. Selon le modèle de carte, le microcontrôleur se présente sous deux formes :
Si cette carte se prend en main assez facilement, elle reste toutefois fragile. Le court-circuit le plus bref la détruit irrémédiablement. Il convient donc de ne pas effectuer ou de modifier un câblage lorsqu'elle est sous tension.
Relier la carte au PC via le câble USB. Dans le menu Outils :
La carte Arduino UNO possède une petite DEL (diode électroluminescente ou LED en anglais) associée à la variable LED_BUILTIN
(de valeur 13), permettant d'effectuer des tests.
Entrer le programme (on dit aussi croquis
) :
void setup() { pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUILTIN, HIGH); // la DEL émet de la lumière delay(1000); // on attend une seconde digitalWrite(LED_BUILTIN, LOW); // la DEL cesse d'émettre de la lumière delay(1000); // on attend une seconde }
Dans le menu de l'EDI :
Vérifier, la première en partant de la gauche, permet de compiler le programme, de vérifier qu'il ne comporte pas d'erreur.
Téléverser, la deuxième en suivant, compile le programme et le charge dans la mémoire de la carte.
Observer le Le clignotement de la DEL sur la carte et recommencer en faisant varier le délai.
A l'aide de l'icône Vérifier
, répondre aux questions suivantes :
Tester le programme suivant :
void clignote(int nombre) { while (nombre>0) { digitalWrite(LED_BUILTIN, HIGH); delay(200); digitalWrite(LED_BUILTIN, LOW); delay(400); nombre=nombre-1; } } void setup() { pinMode(LED_BUILTIN, OUTPUT); } void loop() { clignote(3); delay(1000); }
Modifier le programme, avec la fonction random() et une variable locale :
void loop() { int cligne = random(8); clignote(cligne); delay(1000); }
Modifier le programme, avec une variable globale cette fois :
int cligne = random(8); ... void loop() { clignote(cligne); delay(1000); }
Le moniteur série permet d'afficher sur l'ordinateur du texte, des nombres.
On y accède via le menu Outils/Moniteur série
.
Tester ce programme :
unsigned int compteur = 0 // unsigned signifie que l'entier est positif void setup() { Serial.begin(115200); // mise en route du port série } void loop() { Serial.print("Compteur : "); Serial.println(compteur); compteur++; delay(2000); }
Ouvrir le menu Outils/Moniteur série
, régler la vitesse de connexion (115200 bauds), pour obtenir :
Les données envoyées par la carte Arduino UNO sur le port USB sont des octets. Du point de vue du moniteur série, ces huit bits représentent des caractères.
void setup() { Serial.begin(115200); } void loop() { // l'octet de valeur 97 donne le caractère a Serial.write(97); Serial.print("a"); // les octets de valeurs 195 et 169 donnent le caractère é Serial.write(195); Serial.write(169); Serial.println("é"); delay(5000); }
Ce qui est vrai pour les versions récentes de l'EDI ne l'est pas nécessairement pour une version ancienne. Le code ci-dessous permet de tester la prise en charge des caractères accentués.
void setup() { Serial.begin(115200); } void loop() { Serial.println("èéà"); delay(5000); }
Il est possible d'envoyer au moniteur série un seul octet avec le premier bit à 1. Le code ci-dessous permet de constater que cela ne présente aucun intérêt.
void setup() { Serial.begin(115200); } void loop() { for (int i=200; i<250; i++) { Serial.write(i); Serial.println(); } delay(300); }
Le traceur série permet d'afficher sur l'ordinateur une série de nombres sous la forme d'un graphique.
On y accède via le menu Outils/Traceur série
.
Dans l'exemple ci-dessous, les valeurs envoyées par la carte Arduino varient de 0 à 1023.
float v=0.0; void setup() { Serial.begin(115200); } void loop() { Serial.println(round(511.5*sin(v)+511.5)); v=v+0.1; delay(20); }
Les octets envoyés par le PC à la carte Arduino via le câble USB sont stockés dans la mémoire tampon (buffer en anglais). Cette mémoire tampon se vide au fur et à mesure que les octets sont lus par le programme de la carte Arduino. Cette mémoire tampon peut recevoir 63 octets au maximum. Cela se vérifie avec le programme ci-dessous, en envoyant un grand nombre de caractères via le moniteur série.
void setup() { Serial.begin(115200); } void loop() { int nb_octets=Serial.available(); Serial.println(nb_octets); delay(1000); }
Si vous créez votre IHM (interface homme-machine), en Python par exemple, vous pouvez envoyer à la carte Arduino les octets de votre choix. Par contre, si vous utilisez le moniteur série de l'EDI, vous devez comprendre comment il se comporte. Ce comportement, auquel il faut vous adapter, varie selon la version de l'EDI.
Qu'obtient-on en envoyant une suite de caractères via le moniteur série? Quels octets sont envoyés à la carte Arduino UNO après :
Envoyer?
Le code ci-dessous permet de répondre à cette question.
void setup() { Serial.begin(115200); } void loop() { int nb_octets=Serial.available(); Serial.println(nb_octets); for (int i=0; i<nb_octets; i++) { int octet= Serial.read(); Serial.println(octet); } delay(1000); }
Tester le programme ci-dessous.
Pour allumer ou éteindre la DEL présente sur la carte Arduino UNO,
utiliser la première ligne du moniteur série, écrire les caractères a
ou e
.
Ensuite, appuyer sur la touche Entrée
du clavier ou cliquer sur bouton Envoyer
.
void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); } void loop() { if (Serial.available()>0) { // si des caractères sont envoyés char caractere = Serial.read(); // lecture d'un caractère if (caractere == 'a') { digitalWrite(LED_BUILTIN, HIGH); } if (caractere == 'e') { digitalWrite(LED_BUILTIN, LOW); } Serial.print(caractere); } }
void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); } void loop() { while (Serial.available()==0) { } // attente d'un envoi int octet = Serial.read(); // lecture d'un octet if (octet == 97) { digitalWrite(LED_BUILTIN, HIGH); } if (octet == 101) { digitalWrite(LED_BUILTIN, LOW); } Serial.write(octet); }
Tester ce programme, en utilisant maintenant les chaînes de caractères Allumer DEL
ou Eteindre DEL
:
void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); } void loop() { while (Serial.available() > 0) { String commande = Serial.readString(); if (commande == "Allumer DEL\n") { digitalWrite(LED_BUILTIN, HIGH); } if (commande == "Eteindre DEL\n") { digitalWrite(LED_BUILTIN, LOW); } Serial.print(commande); } }
Il se peut que ce programme ne fonctionne pas.
Selon la version de l'EDI, le moniteur série envoie ou pas un retour de chariot \r
,
juste avant le saut de ligne \n
...
La méthode trim()
lève l'incertitude en supprimant les caractères non imprimables en début et fin de chaîne.
void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); } void loop() { while (Serial.available() > 0) { String commande = Serial.readString(); commande.trim(); if (commande == "Allumer DEL") { digitalWrite(LED_BUILTIN, HIGH); } if (commande == "Eteindre DEL") { digitalWrite(LED_BUILTIN, LOW); } Serial.println(commande); } }
Ceux qui veulent se rapprocher de la syntaxe du langage C n'emploient pas d'objets de type String.
En C, il n'y a pas de chaînes de caractères. On utilise à la place un tableau de caractères avec une particularité :
Le dernier caractère est suivi d'un octet de valeur 0, appelé caractère de fin de chaîne
ou terminateur
,
noté '\0'
. Les caractères sont donc lus les uns après les autres pour former le tableau de caractères.
char commande[20]; int i = 0; void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); } void loop() { while (Serial.available() > 0) { char c = Serial.read(); if ((c == '\n') || (c == '\r')) { commande[i] = '\0'; if (!strcmp(commande, "Allumer DEL")) { digitalWrite(LED_BUILTIN, HIGH); } if (!strcmp(commande, "Eteindre DEL")) { digitalWrite(LED_BUILTIN, LOW); } Serial.println(commande); i = 0; } else { commande[i] = c; i++; } } }
Quels sont l'avantage et l'inconvénient du langage C par rapport au C++?
Tester ce programme ci-dessous :
void setup() { Serial.begin(115200); delay(100); while (Serial.available()!=0) { Serial.read(); } } void loop() { Serial.println("Entrer un nombre entier : nb1 = ?"); while (Serial.available()==0) { } String ch_nb1=Serial.readString(); Serial.println("Entrer un autre nombre entier : nb2 = ?"); while (Serial.available()==0) { } String ch_nb2=Serial.readString(); int nb1 = ch_nb1.toInt(); int nb2 = ch_nb2.toInt(); int nb3 = 2*nb1+nb2; Serial.print("2 x nb1 + nb2 = 2 x "); Serial.print(nb1); Serial.print(" + "); Serial.print(nb2); Serial.print(" = "); Serial.println(nb3); }
Il est possible d'écrire directement :
int nombre_entier = Serial.parseInt(); float nombre_decimal = Serial.parseFloat();
Dans les deux cas, la variable prend pour valeur le nombre entré.
Par contre, les caractères '\r'
ou '\n'
sont éventuellement laissés dans la mémoire tampon...
Il faut penser à la vider avant d'entrer une nouvelle valeur!
La commande suivante convertie une chaîne de caractères en nombre décimal :
float nb1=ch_nb1.toFloat();
De nombreuses fonctions mathématiques sont disponibles en important la bibliothèque math.h
. Exemple :
#include <math.h> float nb1 = M_PI/3; float nb2 = cos(nb1); float nb3 = sin(nb1);
L'IMC, en kg/m2 est l'indice de masse corporelle. Il se calcule en fonction de la masse m en kg et de la taille t en m, avec la formule :
IMC =
L'interprétation de l'IMC dépend de l'âge, de l'activité sportive... En première approximation :
IMC < 19 | Maigreur |
19 ≤ IMC ≤ 25 | Normal |
25 < IMC | Surpoids |
Modifier le programme proposé dans l'exemple pour calculer l'indice de masse corporelle et interpréter le résultat.
Le code ci-dessous, en langage C, permet à la DEL associée au connecteur 13 de clignoter.
#include <stdint.h> #include <avr/io.h> #include <avr/interrupt.h> #define Port_led PORTB #define Pin_led PB5 // PB5 = d13 pour arduino #define seconde 0x64 // définit valeur seconde volatile uint8_t F_bdet = 0; // définit et RaZ flag base de temps uint8_t cpt_cycle = seconde; // définit et positionne compteur uint8_t Val_BdeT = 0x64; // 10ms quartz 16MHz // interruption sur timer 0 ISR (TIMER0_OVF_vect) { // interruption sur débordement timer0 TCNT0 = Val_BdeT; F_bdet = 1; } void Init_port() { // sous programme initialisations DDRB = 0x20; // fixer la direction du port (1 = sortie) : 0010 0000 PORTB = 0x00; // RaZ port b } void Init_timer0() { TCNT0 = Val_BdeT; // positionne le compteur TIMSK0 |= (1 << TOIE0); // autorise l'IRQ sur débordement TCCR0B |= (1 << CS02) | (1 << CS00); // prédivision par 1024 } int main() { Init_port(); // appel initialisation port Init_timer0(); // appel initialisation timer 0 sei(); // autorise l'IRQ while(1) { // boucle principale do { // boucle 10 ms while (F_bdet == 0); // attente de fin base de temps (10ms) F_bdet = 0; // RaZ flags BdeT cpt_cycle -= 1; } while (cpt_cycle != 0); // boucle des secondes cpt_cycle = seconde; // recharge compteur Port_led ^= (1 << Pin_led); // inverse sortie led } return 0; }