Table des matières
Arduino
Arduino ! kézako ?
Arduino est une plateforme de prototypage open-source qui permet aux utilisateurs de créer des objets électroniques interactifs à partir de cartes électroniques matériellement libres sur lesquelles se trouve un microcontrôleur (d'architecture Atmel AVR comme l'Atmega328p, et d'architecture ARM comme le Cortex-M3 pour l'Arduino Due).
C'est une plateforme basée sur une interface entrée/sortie simple. Il était destiné à l'origine principalement mais pas exclusivement à la programmation multimédia interactive en vue de spectacles ou d'animations artistiques, ce qui explique en partie la descendance de son environnement de développement de Processing.
Arduino peut être utilisé pour construire des objets interactifs indépendants (prototypage rapide), ou bien peut être connecté à un ordinateur pour communiquer avec ses logiciels (ex. : Max/MSP, Usine Hollyhock, Pure Data, SuperCollider). En 2011, les versions vendues sont pré-assemblées. Des informations sont fournies pour ceux qui souhaitent assembler ou construire une carte Arduino eux-mêmes.
Le projet Arduino a reçu un titre honorifique à l'Ars Electronica 20062, dans la catégorie Digital Communities.
Le langage
Si le logiciel Arduino est une application écrite en Java, le langage dans lequel nous devons écrire nos sketchs (nom des programmes Arduino) est quant à lui du C++ (C plus plus). Vous allez donc coder en C++ quelle chance !! ;)
Noté quand même que l'IDE Arduino est basé sur l'IDE de Processing, mais qu'en Processing nous codons sur une surcouche de Java et non de C++! Quel mic-Mac :D
Téléchargement de l'IDE Arduino
Comme vous le savez parfaitement maintenant, l'IDE est l'environnement de développement, nécessaire a la réalisation de projets Arduino.
Si vous ne l'avez pas encore, il vous faut donc télécharger le programme (IDE) sur le site d'arduino, rubrique Software → Downloads.
Une fois le logiciel téléchargé (en fonction de votre OS) il ne vous restes qu'à l'installer !
Quelques notions de bases
Le point virgule, L.E P.O.I.N.T V.I.R.G.U.L.E :D
Nous avons vue plus haute que dans le langage Python, le “;” n'est jamais utilisé pour terminer une ligne, la tabulation faisant office. En revanche, en Arduino (héhé oui en C++ vous avez raison) le point virgule est absolument INDISPENSABLE pour indiquer à l'ordinateur qu'il est à la fin d'une ligne et qu'il doit donc aller à la prochaine.
Si vous oubliez le “;” une erreur sera détectée et vous ne pourrez pas Téléverser votre sketch sur la carte Arduino !
Petit conseil
Commencez TOUJOURS par écrire la syntaxe général d'une variable, d'une fonction, d'une class, d'une condition ou encore d'une boucle avant d'y entrer les paramètres !
Exemple pour une condition et une variable :
if(){ println(); }
int = ;
De cette manière le code que vous écrirez sera claire pour vous, en plus d'être certain de ne pas faire d'erreurs !
Une fois fait, il ne vous restera qu'à entrer les paramètres.
if( x < y ){ println("x est plus petit que y"); }
int valeur_capteur = 10 ;
Quand vous commencez à coder, 80% des erreurs viennent d'une faute de syntaxe, donc d'un oublis de point virgule, par exemple !
Alors que votre cerveau pensera “Mince j'ai mal calculer une fonction à dérivée fractionnel”, il ne s'agira que d'un oublie de “;” ou d'une accolade “}” mal fermée.
Serial.print VS Serial.prinln
Faire fonctionner un programme et un prototype, c'est bien ! Mais pouvoir récupérer et afficher des valeurs pour optimiser notre proto., c'est mieux !
Et bien les fonctions “print” et “println” sont là pour ça !
Alors que tous deux ont pour unique but d'afficher une valeur ou un texte dans la console de l'IDE, l'un les écrieras sur une ligne, alors que l'autre effectuera un retour à la ligne à chaque nouvelle valeur.
Ainsi, avec
Serial.print( valeur_capteur );
Le terminal affichera
102030405060708090100
Alors qu'avec un
Serial.println( valeur_capteur );
Le terminal affichera
10 20 30 40 50 60 70 80 90 100
Nettement plus claire non ?
Et pour définitivement être le roi de la console, nous combinons les deux
Serial.print( "La valeur du capteur est de : "); Serial.println( valeur_capteur );
Pour obtenir
La valeur du capteur est de : 10 La valeur du capteur est de : 20 La valeur du capteur est de : 30 La valeur du capteur est de : 40 La valeur du capteur est de : 50 La valeur du capteur est de : 60 La valeur du capteur est de : 70 La valeur du capteur est de : 80 La valeur du capteur est de : 90 La valeur du capteur est de : 100
Les delay();
Arduino est une sacré bestiole et une sacrée bestiole qui va vite !
Si nous gardons sa fréquence de rafraîchissement initial, nous somme bien incapable de voir défiler les chiffres dans la console.
De plus, bon nombre de composants, comme les servo moteurs dépendent d'un délais entre chaque impulsion électrique pour pouvoir être paramétré à notre guise.
Dans cette exemple, vous comprendrez rapidement l’inestimable importance de ce fameux “delay()” ;).
#include <Servo.h> // On importe la bibliothèque "Servo.h" Servo myservo; // On renomme Servo en myservo int pos = 0; // On initialise la position du servo moteur sur l'angle "0" void setup(){ myservo.attach(9); // On indique que le servo est sur la pin "9" } void loop(){ // On crée une boucle "for" dont le nom est "pos" et qui commence à "0", // pour que la "pos" soit plus petite que 180 on ajoute "1". // Au dessus de 179, la condition n'est plus respectée donc Arduino sort de la boucle. for(pos = 0; pos < 180; pos++){ myservo.write(pos); // On écris l'incrémentation du compteur sur le servo moteur delay(15); // On laisse un délais de 15 milliseconde chaque incrémentation de boucle. } for(pos = 180; pos >= 1; pos--){ myservo.write(pos); delay(150); // Cette fois on laisse 10X plus de temps entre chaque incrémentation, // ce qui à pour effet de diminuer la vitesse de rotation du servo moteur ! } }
Si vous essayez ce code, vous verrez que le servo moteur accélère 10X plus à chaque retour et c'est une très bonne chose !
Un delay() est donc le délais qui s'écoule entre deux instructions. Nous l’utilisons autant pour contrôler une servo moteur que pour allumer et éteindre une led, et bien plus encore…
i++ VS ++i
Nous avons aussi la possibilité d'écrire
i++
Mais la différence est infime et pas simple a comprendre.
Mais dans les grandes lignes cela signifie que :
i++ = i i=i+1
alors que
++i
se traduit par :
i = i+1 x = i
Analogique VS Digital
Voici sans doute LA notion la plus importante à comprendre pour développer un projet en Arduino.
Alors, une idée ?
Imaginer que vous vous voulez interagir avec un interrupteur (WHOOO ! gros programme :D) vous avez deux possibilités.
Soit il est allumé (1 en prog.) soit il est éteint (0), donc soit il est ON (1) soit il est OFF(0).
Et bien c'est CA le Digital (ou numérique), soit il est à 1, soit il est à 0 ! mais il ne peut être entre les deux (Pour simplifier largement bien sur !)
“Oui, mais si je mets un variateur bah ton truc là, ça va pas ! Et TOK !”
Exacte ! Si nous plaçons un variateur entre l’alimentation et l’ampoule, à ce moment là entre le 0 et le 1 il y a bon nombres de possibilités et nous entrons alors dans la matrice analogique :D. Je m'explique.
Si la lumière est éteinte à 100%, nous considérons que son état est 0 et à l'extrême, si elle est allumé à 100% son état est à 1.
Maintenant, si on lui donne comme état 50% allumé, donc 50% éteint, quel sera sont équivalent Analogique ?
BRAVO ! 0.5
Nous pouvons passer comme argument à notre lampe les valeurs ANALOGIQUE suivantes :
0.0 → Allumée à 0%
0.1 → Allumée à 10%
0.2 → Allumée à 20%
0.3 → Allumée à 30%
0.4 → Allumée à 40%
0.5 → Allumée à 50%
0.6 → Allumée à 60%
0.7 → Allumée à 70%
0.8 → Allumée à 80%
0.9 → Allumée à 90%
1.0 → Allumée à 100%
Vous voilà donc dans les deux matrices “Analogique” et “Digital / Numérique”.
Valeur max et valeur min
Nous avons vue plus haut qu'il était possible d'allumer un lumière, disons une LED, en lui donnant une valeur autre que “0” ou “1”.
En réalité, les valeurs min. et max. que l'Arduino accepte ne sont pas comprise entre 0.0 et 1.0, mais entre 0 et 255 !
255 ! vous exclamerez vous !
Et bien oui.Et le temps de bien comprendre, nous allons replonger dans la matrice ;)
Comme vous le savez, un ordinateur ne manipule QUE des 0 et des 1, c'est son truc à lui cà le binaire !
Ainsi, si vous regroupez 8 zéros (00000000) vous obtenez un Bit
Un code simple pour mieux comprendre :
void setup() { Serial.begin( 9600 ); pinMode(Pin_Capteur, INPUT); } void loop() { int Valeur_Capteur = analogRead(A0); // lecture du port analogique A0 Serial.print( "La valeur du capteur est de :" ); // On écris dans la console "La valeur du capteur est de :" Serial.println( Valeur_Capteur ); // On écris la valeur du capteur // En sortie de console, cela donne "La valeur du capteur est de : 120" par exemple. }
Explications :
Ici nous voulons récupérer la valeur d'un capteur Photoélectrique, en gros l'intensité lumineuse d'un endroit. Vous imaginez donc bien que la valeur n'est pas à 100% → 1, ni à 100% → 0, mais se situe quelque part entre les deux et en plus, varie. Il ne sera donc pas possible d'analyser la lumière avec une fonction Digital ! D'où la ligne :
int Valeur_Capteur = analogRead(A0);
Littéralement on peu traduire cette ligne par :
On LIT de manière ANALOGIQUE la valeur ENTIÈRE A0 nommée “Valeur_Capteur”
Lisez la ligne de code à l'envers et tout vous paraîtra plus simple !
Le map() :
Allez, un petit dernier pour la route, et non des moindres !
La fonction map(); (ou ré-étalonnage) permet de convertire une fourchette de nombres, dans une autre fourchette de nombres.
Bon, dit comme çà, ça fait peur, mais pas de panic on s'explique.
Imaginez un entonnoir posé sur sa pointe.
En haut, nous avons l'ouverture la plus grande. Disons maintenant que le côté haut gauche = 0 et que le côté haut droit = 1023.
Nous avons donc une fourchette comprise entre 0 et 1023, c'est beaucoup ! Nous, nous souhaitons avoir une fourchette comprise entre 0 et 255.
Comment faire?
Et bien imaginez maintenant que le coin en bas à gauche de notre entonnoir est = 0 et que le coin en bas à droite = 255.
Nous avons donc une fourchette comprise entre 0 et 255.
Voilà, vous avez fait l'expérience mental d'une fonction map(); et contraint la fourchette 0 → 1023 à être reconvertie en 0 → 255.
la syntaxe
map(i, A, B, Y, Z); Où map(capteur, valeur effective MIN, valeur effective MAX, valeur voulue MIN, valeur voulue MAX );
Et pour être tout a fait complet dans la syntaxe, il nous faut bien sur stocker les valeurs dans une variable, disons ici “val_map”, ce qui nous donne
int val_map = map(A0, 0, 1023, 0, 255);
Si vous voulez tester un code complet, avec un potentiomètre :
void setup() { Serial.begin(9600); pinMode(9, OUTPUT); // sets the pin as output } void loop() { int val_pot = analogRead(0); val_map = map(val_pot, 0, 1023, 0, 255); // On ré-étalonne la valeur entre 0 et 1023 sur une fourchette entre 0 et 255 Serial.print( val_map ); Serial.println( "La valeur Du potentiomètre est de: " ); }
Bien sur, le map(); fonctionne aussi à l'inverse, la valeur nominal peut être plus grande que la valeur de destination.
Le p'tit +
Pour les férus de Math, voici la fonction entière et le calcule effectué par map();
long map(long x, long in_min, long in_max, long out_min, long out_max){ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; }
Bon appétit ! ;)
Réalisation d'une voiture autonome en Arduino
L'algorithme et le logigramme
Avant de s'attaquer à un programme, il est important d'en avoir une perception dans son ensemble.
Pour ça, rien de plus simple, il suffit d'en faire un logigramme !
Pour plus d'explications, voici un lien très intéressant.
Si vous cherchez une logiciel qui fait le taf, c'est par ICI, mais il est en version d'essais si on ne l'achète pas !
Sinon, voici un autre logiciel, disponible ICI. Ce logiciel est en ligne, donc pas d'installation nécessaire !
Et pour les Linuxiens, vous le trouverez dans votre logithèque.
Voici un exemple illustrant l’algorithme de la voiture
Le schéma du capteur à ultrason HC-SR04:
Le capteur à ultrason HC-SR04
const int TRIG_Pin= 10; const int ECHO_Pin= 9; long duree, distance_cm; void setup() { Serial.begin(9600); pinMode(TRIG_Pin, OUTPUT); pinMode(ECHO_Pin, INPUT); } void loop() { digitalWrite(TRIG_Pin, HIGH); // On allume le TRIG pendant 10 microsecondes delayMicroseconds(10); digitalWrite(TRIG_Pin, LOW); // On éteint le TRIG duree = pulseIn(ECHO_Pin, HIGH); // On récupère le l'onde et on stock sa valeur dans une variable 'duree' distance_cm = microsecondsToCentimeters(duree); // On appel la fonction 'microsecondsToCentimeters'et on lui donne la durée 'duree' // et on stock sa valeur dans une variable 'distance_cm' Serial.print(distance_cm); // On affiche le résultat Serial.println(" cm"); // On précise l'unité de mesure (en centimètres) delay(500); // On laisse une délais de 0,5 secondes } long microsecondsToCentimeters( long microseconds ) { /* * Le son se déplace à 343 mètres par seconde, ce qui signifie qu'il a besoin de 29,155 microsecondes par centimètre. * Il faut donc diviser la durée par 29 puis par 2, car le son doit parcourir la distance deux fois. * Il se rend à l'objet et revient ensuite au capteur. */ return microseconds / 29 / 2; }
Les servo moteurs
Un servo moteur (souvent abrégé en « servo », provenant du latin servus qui signifie « esclave »), tout un programme !
Un servomoteur est donc un système motorisé capable d'atteindre des positions prédéterminées (angles), puis de les maintenir.
La position est :
- dans le cas d’un moteur rotatif, une valeur d'angle (servo à 180deg)
- dans le cas d’un moteur linéaire, une distance (servo à rotation continue).
Le démarrage et la conservation de la position prédéterminée sont commandés par un programme.
Pour un ajustement précis de la position, le moteur et son réglage sont équipés d'un système de mesure qui détermine la position courante.
Par exemple, l'angle de rotation parcouru, est relatif à la position de départ du moteur.
Les servomoteurs sont commandés par l'intermédiaire d'un câble électrique à trois fils qui permet d’alimenter le moteur et de lui transmettre des consignes de position sous forme d’un signal codé en largeur d'impulsion, plus communément appelé PWM.
Cela signifie que c'est la durée des impulsions qui détermine l'angle absolu de l'axe de sortie et donc la position du bras de commande du servomoteur.
Le signal est répété périodiquement, en général toutes les 20 millisecondes, ce qui permet à l'électronique de contrôler et de corriger continuellement la position angulaire de l'axe de sortie, cette dernière étant mesurée par le potentiomètre.
Lorsque le moteur tourne, l'axe du servomoteur change de position, ce qui modifie la résistance du potentiomètre.
Le rôle de l'électronique est de commander le moteur pour que la position de l'axe de sortie soit conforme à la consigne reçue : c'est un asservissement.
Le montage
Le code
// Dans cette exemple, nous faisons tourner le Servo moteur de gauche à droite sur 180 degrés /* On inclut la lib Servo pour manipuler le servomoteur */ #include <Servo.h> /* On créer un objet Servo pour contrôler le servomoteur */ Servo monServomoteur; void setup() { // On attache le servomoteur à la broche 9 monServomoteur.attach(9); } void loop() { // On fait bouger le bras de 0° à 180° for (int position = 0; position <= 180; position++) { monServomoteur.write(position); delay(15); } // On fait bouger le bras de 180° à 0° for (int position = 180; position >= 0; position--) { monServomoteur.write(position); delay(15); } }
Compilation des deux
Si vous avez bien compris le fonctionnement du HC-SR04 et des servos moteur, ce sera un jeux d'enfant pour vous de combiner et rectifier le code afin qu'il soit fonctionnel.
Trouver l'erreur
Ou devrais-je dire, LES erreurs !
Nous voici avec un code complet, mais des bugs se sont glissés dans la matrice ! Pour vous aider, il y en a trois (c'est un chiffre raisonnable ;) ) et inutile de pousser le code dans votre IDE en mode débug, les erreurs ne sont pas syntaxiques, uniquement logiques :D.
Bien sur, toutes les lignes ne sont pas commentées, ce serait trop facile ! D'ailleurs ne vous y filez pas trop, on ne sais jamais ;)
#include <Servo.h> // On importe la librairie Servo.h #define AVANCE 0 // On définit les deux états possibles du robot : marche avant ou #define TOURNE_A_GAUCHE 1 // virage à gauche /************************************************************************************************************** ** ATTENTION QU'EN C++ QUAND ON DECLARE AVEC #include IL NE FAUT PAS DE POINT VIRGULE, allez savoir pourquoi ** **************************************************************************************************************/ Servo MOTEUR_GAUCHE; // On crée le servo gauche Servo MOTEUR_DROIT; // On crée le servo droit const int TRIG_PIN = 10; // On assigne les fonctions TRIG const int ECHO_PIN = 9; // On assigne les fonctions ECHO const int boutton = 8; unsigned long SerialDelais = 0; unsigned long LoopDelais = 0; const int loopPeriod = 20; // On fixe la fréquence de lecture du détecteur à 20 ms, // ce qui correspond à une fréquence de 50 Hz long distance_cm, duree; int ETAT = AVANCE; void setup() { Serial.begin(9600); // On ouvre un canal entre Arduino et l'ordinateur sur 9600 baud pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); // On assigne les Servo aux pin de l’Arduino MOTEUR_GAUCHE.attach(6); MOTEUR_DROIT.attach(5); } void loop() { // On détecter le bouton sur la Pin 2 de l'Arduino if (digitalRead(2) == HIGH) { // SI le bouton EST STRICTEMENT EGALE à HIGH (donc 1) while (1) { // TANT QUE 1 = 1 MOTEUR_GAUCHE.write(90); // On fait tourner le servo gauche MOTEUR_DROIT.write(90); // On fait tourner le servo droit } } // On demande au détecteur de lire les données et de stocker les distances mesurées if (millis() - LoopDelais >= loopPeriod) { lecture_du_capteur(); etatMachine(); LoopDelais = millis(); } } // On crée une function nommée "lecture_du_capteur" elle est appelée juste au dessus, dans la condition void lecture_du_capteur() { digitalWrite(TRIG_PIN , HIGH); // On met le TRIG en HIGH pour l'allumer delayMicroseconds( 10 ); // On garde la fiche TRIG en position HIGH pendant au moins 10 microsecondes digitalWrite(TRIG_PIN , LOW); // On met le TRIG en LOW pour l'éteindre // On convertis le signal temps en distance duree = pulseIn(ECHO_PIN , HIGH); distance_cm = microsecondes_en_centimetres( duree ); // On affiche la distance dans le moniteur Série Serial.print( distance_cm ); Serial.print(" cm"); Serial.println(); // On laisse une espace pour plus de lisibilité } // On crée une function nomée "ETATMachine" elle est aussi appelée juste au dessus, dans la condition void etatMachine() { // si aucun obstacle détecté if (ETAT > AVANCE) { if (distance_cm > 15 || distance_cm < 0) { // SI distance_cm EST PLUS GRAND QUE 15 OU // On avance // distance_cm PLUS PETIT QUE 0 MOTEUR_DROIT.write(180); MOTEUR_GAUCHE.write(0); } //si un obstacle est détecté else { ETAT = TOURNE_A_GAUCHE; } } // si l’obstacle est détecté, tourne à droite else if (ETAT == TOURNE_A_GAUCHE) { unsigned long temps_pour_tourner = 500; // il faut 0,5 secondes pour tourner de 90 degrés. unsigned long temps_enregistre = millis(); // enregistre le début de rotation des roues // On reste dans la boucle jusqu’à ce que temps_pour_tourner soit terminé while ( ( millis() - temps_enregistre ) > temps_pour_tourner ) { MOTEUR_DROIT.write(180); MOTEUR_GAUCHE.write(180); } ETAT = AVANCE; } } long microsecondes_en_centimetres( long microseconds ) { /* * Le son se déplace à 343 mètres par seconde, ce qui signifie qu'il a besoin de 29,155 microsecondes par centimètre. * Il faut donc diviser la durée par 29 puis par 2, car le son doit parcourir la distance deux fois. * Il se rend à l'objet et revient ensuite au capteur. */ return microseconds / 29 / 2; }
Les moteurs à courent continue
Pour faire simple, un moteur à courant continu est constitué de deux parties : une partie fixe qui génère un champ magnétique (le stator) et une partie tournante (le rotor).
Un moteur à courant continu est constitué de deux parties électriques : le stator et le rotor.
Lorsqu'on alimente le moteur, il se crée une interaction magnétique qui met le moteur en mouvement.
Lorsqu'on inverse le sens de la tension qui alimente le moteur, il tourne en sens inverse.
Le schéma de montage
Maintenant que nous avons le code et toutes les clés pour bien le comprendre, il vous reste à construire votre voiture.
Pour ca, il vous faudra quelques composants :
1 Batterie 9V
1 Batterie 4.8V
1 Bouton poussoir
1 Capteur HC-SR04
2 Résistances 10KΩ (brun, noir, orange, or)
2 Régulateurs de tension
2 Moteurs CC
Au passage, arrêtons nous un instant sur le régulateur de tension.
Rien de bien méchant, tout est dans le nom ;). Un régulateur de tension,… régule la tension !
Ce composant maintient à sa sortie, une tension constante et indépendamment de la charge et de la tension d'entrée.
imaginez deux bassins d'eau reliés par une canalisation, le bassin “1” déverse son contenu dans le bassin “2”.
Vous, Oh passionné de pataugeoire, nagez avec énergie dans le bassin “1”. Vous faites des vagues, des remous, de l'eau déborde, bref c'est le carnage, mais votre ami dans le bassin “2” détestes les remous, pour lui c'est spa et détente !
Problème ? Non en aucun cas, car la canalisation régule les remous du bassin “1”. L'eau est canalisée, les fluctuation sont donc annulées et un débit constant est déversé dans le bassin “2” pour le plus grand plaisir de votre ami.
Côté branchement, vous pouvez voir qu'il y a trois broches sur ce composant.
• La première broche, à gauche, est reliée à une sortie de l'Arduino, par exemple “6”.
• La seconde, au milieu, c'est la broche V- OUT autrement dit la sortie négative du courant qui est régulé.
• La troisième, à droite, c'est la broche V- IN autrement dit l'entrée négative du courant non régulé.