Semaine 5 : Bibliothèques et sorties

Arduino est modulaire, et c’est tant mieux !

Nous avons choisi la plateforme Arduino pour sa facilité d’utilisation, son ouverture mais aussi parce que cette plateforme est incroyablement modulaire !

Arduino est le cerveau de projets issus de domaines variés : domotique, vêtements intelligents, drones, robotique, objets connectés, installations artistiques… Cette modularité est rendue possible grâce à l’extension des fonctions de base d’Arduino par des modules d’extensions. Transition parfaite puisque c’est le sujet de la semaine !

Après avoir abordé des composants électroniques simples dans les précédentes semaines, nous nous lançons dans la découverte d’un composant plus complexe : le servomoteur. C’est un moteur équipé d’un asservissement électronique qui nous permettra de contrôler sa position angulaire. Cette semaine, nous verrons comment brancher ce composant sur notre Arduino et comment utiliser une bibliothèque logicielle (un ensemble de fonctions) pour faciliter notre travail (en anglais library).

Nous verrons également comment améliorer notre code grâce à l’utilisation de boucles.

La semaine s’annonce chargée !

Corrigé du TP 3

Corrigé du TP Thérémine Lumineux

Voici la correction du TP de la semaine dernière qui reprend des éléments du cours sur les capteurs analogiques.

Code

Voici une des solutions possibles pour répondre au problème dans l’état actuel de nos connaissances.

/*
    Thérémine lumineux
  TP de la semaine 4 du MOOC "La Fabrication Numérique" 
 
 Le montage :
 * Un piezo branché sur la broche 8
 * Une photorésistance branchée sur la broche A0 depuis +5V
 * Une résistance de 10kΩ branchée sur la broche A0 depuis GND
 
 créé le 9 Avril 2014
 par Baptiste Gaultier
 
 Ce code est en CC0 1.0 Universal
 
 https://www.france-universite-numerique-mooc.fr/courses/MinesTelecom/04002/Trimestre_1_2014/about
*/

// variable pour stocker la valeur reçue sur A0
int sensorValue;

void setup() {
}

void loop() {
    // lire la valeur de la photorésistance
    // et stocker ça dans une variable
    sensorValue = analogRead(A0);

    // re étalonne la variable vers un grand intervalle de fréquences audibles
    int pitch = map(sensorValue, 0, 1023, 50, 3000);

    // jouer la fréquence sur le piezo branché sur la broche 8
    tone(8, pitch);

    // attendre 10 ms
    delay(10);
}
Schéma électronique

Comme on nous l’a demandé sur le forum, voici le schéma électronique créé avec Fritzing :

Theremine lumineux - schéma
Theremine lumineux - schéma
Montage
Theremine lumineux - montage
Theremine lumineux - montage

Pour réaliser ce montage, vous avez besoin de :

  • Un Arduino ;
  • Une platine de prototypage ;
  • Un câble USB ;
  • Une résistance de 10kΩ ;
  • Des fils de prototypage ;
  • Une photorésistance ;
  • Un piézo ;
  • Du temps :)

Notre correction s’arrête là. Pour des éclaircissements, nous vous invitons à poser vos questions sur le forum et à reparcourir le cours.

On vous laisse avec un montage réalisé par ProPhil qui nous a éclaté avec ce montage photo sur le forum :

Exemple de montage
Exemple de montage

Bibliothèques et sorties

Le Sweep

Cette semaine, nous tentons une nouvelle approche dans le cours avec le code directement expliqué dans le texte de la vidéo que vous venez de voir :

Salut à toutes et tous et bienvenue dans ce 5ème épisode où l’on va aborder les bibliothèques de code.

Bibliothèques, ça vient de l’anglais Libraries, qui signifie Bibliothèques. Peu importe ce que vous trouverez comme terme sur internet, l’idée c’est d’avoir sous la main un rassemblement de morceaux de code, classés par thématique, que vous invoquez à la demande. Un peu comme des supers pouvoirs qui vous évitent d’avoir à réinventer la roue à chaque projet !

Les Bibliothèques pour Arduino sont nombreuses, et abordent la plupart des besoins courants. On trouve ainsi les bibliothèques standard, pour par exemple gérer le Wifi, les écrans à cristaux liquide, utiliser simplement une carte SD, ou encore des moteurs. Pour l’occasion, nous allons nous intéresser à des moteurs un peu particuliers que l’on retrouve dans le monde du modélisme : les servomoteurs.

Au boulot ! Je vous laisse ouvrir l’exemple « sweep » que l’on trouve dans les exemples du dossier Servo : Fichier→Exemples→Servo→Sweep.

Détaillons maintenant les nouvelles instructions présentes dans ce programme.

Après quelques lignes de commentaires, nous trouvons une instruction particulière :

#include<Servo.h>

Voilà, vous venez de charger la bibliothèque et obtenez du même coup la boîte à outils correspondante. À partir de là, vous pouvez créer des objets en partant du « moule » Servo, un peu comme l’on fait de nombreuse gaufres à partir d’un seul moule, à gaufre forcement ! C’est ce qu’on va tout de suite faire. En créant une gaufre chantilly…

Non, pardon ! Un servomoteur, que nous appelons ici myservo.

Servo myservo;

On déclare ensuite une variable pos, pour stocker une position au cours du programme :

int pos = 0;

Il est temps de passer au bloc setup(). C’est vite réglé, puisqu’il suffit d’attacher notre servo fraîchement créé à la broche 9 : 

myservo.attach(9);

La méthode attach()  est disponible pour les objets de type Servo. La bibliothèque de code gère le reste pour vous. Sympa, non ?

Comme indiqué sur la page de Mon Club Elec, les méthodes disponibles sur les objets de type Servo sont assez explicites : attach()write()detach().

Fin du setup, il est temps de looper à présent ! :)

La boucle principale commence par… faire une boucle !

for(pos = 0; pos < 180; pos +=1) {
    myservo.write(pos);
    delay(15);
}

Cette “bouclette” à pour but de faire varier la position cible demandée au moteur. Autrement dit, elle sert uniquement à faire varier la valeur de pos du minimum au maximum. En français, ça donne quelque chose comme « Pour une valeur position allant de 0 à 179 et une marche à la fois, demande au servo myservo d’aller en position pos » Un pos += 4 vous aurait fait monter l’escalier 4 à 4 :)

On laisse quelques millisecondes au moteur afin qu’il ait le temps d’aller une marche plus loin, concrètement de tourner de 1°.

C’est reparti pour un tour, cette fois-ci dans l’autre sens. Au lieu d’incrémenter la valeur de +1 à chaque passage de bouclette, on la décrémente ici de -1 :

for(pos = 180; pos >= 1; pos -= 1) {
    myservo.write(pos);
    delay(15);
}

Puisque nous arrivons en fin de boucle, notre servo va bientôt repartir. Certains d’entre vous doivent se dire : « Pourquoi ne pas être aller directement au but avec par exemple ? » :

myservo.write(0);
myservo.write(180);

La réponse tient en un mot : la douceur !

Utiliser un délai dans une boucle secondaire permet de ralentir le processus. Plus la valeur du délai sera importante dans votre programme, plus le déplacement du moteur sera doux dans la vrai vie… À vous d’adapter la valeur du délai à vos besoins de tendresse.

Nous pouvons maintenant téléverser (ou upload en anglais) notre programme sur l’Arduino.

Montage
Montage d'un servo-moteur
Montage d’un servo-moteur

Pour le réaliser, vous aurez besoin de :

  • Un Arduino ;
  • Un câble USB ;
  • Des fils de prototypage ;
  • Une platine de prototypage ;
  • Un servomoteur.
Schéma
Branchement d'un servomoteur
Branchement d’un servomoteur
Code
#include <Servo.h> 

Servo myservo;
// créer un objet appelé myservo à partir
// du moule Servo

int pos = 0; // variable pour stocker la position courante du servo

void setup() 
{ 
    myservo.attach(9);
    // attacher notre objet myservo au servomoteur branché sur la broche 9 void loop() 
{ 
    for(pos = 0; pos < 180; pos += 1)  // aller de 0° à 180°
    {                                  
        // une étape à la fois
        // aller à la position stocké dans 'pos'
        myservo.write(pos);
        // attendre 15ms que le servomoteur se rende à 'pos'
        delay(15);
    } 
    for(pos = 180; pos>=1; pos-=1) // aller de 180° à 0°
    {
        // aller à la position stocké dans 'pos'
        myservo.write(pos);              
        // attendre 15ms que le servomoteur se rende à 'pos'
        delay(15);
    } 
}
Un balayage en douceur
Références

Complément de cours

Puisque vous n’êtes pas aussi fainéant(e)s qu’il n’y paraît, nous allons appliquer le savoir nouvellement acquis, en réalisant un exemple d’application concrète.

Seulement voilà, un seul moteur qui bat la mesure, c’est vite barbant. Que diriez-vous d’une machine qui servirait votre boisson favorite, sans broncher ? Que diriez-vous de faire votre premier pas en robotique sans avoir l’air d’y toucher ?

… Voici venir le BoissonMatic' !

Vous qui rêvez d’une invention géniale et susceptible de révolutionner l’univers, j’ai ce qu’il vous faut !

Le BoissonMatic' est une réalisation totalement absurde et c’est bien ce qui la rend indispensable. Elle nous servira ici d’illustration de principe pour un montage fort utile: le Pan Tilt, ou Monture azimutale ("Fiche wikipédia sur la monture azimutale") dans notre bonne vieille langue de Molière.

Le principe est simple et consiste à monter un servo sur l’autre, perpendiculairement. Pendant que le premier moteur tourne à l’horizontal (panoramique), le second tourne à la vertical (azimut).

Assemblage sur un servomoteur
Assemblage sur un servomoteur

Avec des servomoteurs basiques, les déplacements de chacun des axes seront limités à 180°. Ceci étant, vous pouvez facilement trouver un servo tournant à 360° dans n’importe quelle boutique de modèle réduit, ou encore modifier un servo pour une rotation continue.

Code again
// BoissonMatic' 2014// john@labfab.fr sous licence WTFPL v2.0
#include <Servo.h> 
 
Servo servoBas;  //  création d'un premier objet servo, pour le Pan
Servo servoHaut;  //  création du second, pour le tilt
 
int pos = 0;    // variable pour stocker une position

// valeur mini du Pan, en degré.
int  const MINPAN = 0;
// Un quart de tour panoramique
int const MAXPAN = MINPAN + 90;
// valeur mini du Tilt (en cas de servo monté "à l'envers" ! :] )
int  const MINTILT = 180;
// avec un max au quart de tour
int const MAXTILT = MINTILT - 90;

void setup() 
{ 
    servoBas.attach(9);   // attache le servo du bas à la broche 9
    servoHaut.attach(10); // celui du haut à la broche 10
  
    // Mise en place de la machine en position de départ
    servoBas.write(MINPAN);  
    servoHaut.write(MINTILT);
  
    delay(1000); // admettant qu'il faille une seconde pour faire demi-tour
                 // à un servo bien nourri.
}
 
 
void loop() 
// on fait faire un quart de tour au servo panoramique
    servoBas.write(MAXPAN);
  
    // La boucle "for" permet d'adoucir le déplacement du moteur
    for(pos = 180; pos>=70; pos-=1) // On dépasse le quart de tour, 
                                    // pour que le versement du liquide soit plus rapide 
    {
        servoHaut.write(pos); 
        // le servo tourne de la valeur de l'angle indiqué par la variable 'pos' 
        delay(20);
        // on laisse un peu de temp au servo pour arriver à la postion demandéedelay(3000); // encore du temps pour qu'assez de liquide coule du récipient.
  
    for(pos = 70; pos<=180; pos+=1) // la même dans l'autre sens 
    {
        servoHaut.write(pos);
        delay(25);
    }

    servoBas.write(MINPAN ); // retour en position initiale
    delay(400);

    servoHaut.detach(); // on détache les moteurs un par un. 
    servoBas.detach();  
    // la séquence n'aura lieu donc physiquement lieu qu'une seule fois, 
    // même si la boucle principale loop() continue à tourner.

    // devrait retourner quelque chose  // comme "false" sur votre moniteur serie.  
    servoHaut.attached();
}

Vous l’aurez compris, la boucle for est fort fort utile. C’est même la base du fonctionnement d’Arduino, vous ne pouvez pas looper ça… :)

Pour rappel et pour ceux qui roupillaient au fond de la salle, sa syntaxe est toujours la même.

for (condition) { 
    // ici du code pour bouclette 
}

Ce qui se trouve entre les {} s’appelle un bloc d’instruction. Les variables y ont un comportement spécifique que nous aborderons une autre fois si vous le voulez bien !

Vous allez devoir apprendre à la boucler, car vous utiliserez cette structure de contrôle pour de nombreux propos. Imaginez par exemple une multitude de servomoteurs que vous voudriez "détacher". En plaçant vos objets dans un tableau, vous appliquerez un bon principe de fainéantise. Il ne vous restera plus qu’à parcourir ce tableau d’un for, et d’appliquer pour chaque tour de boucle secondaire, la méthode detach sur l’objet.

Si vous vous êtes amusé(e)s dans cette modeste découverte d’une bibliothèque standard particulièrement adaptée aux servos, adoptez le bon réflexe et sachez que pour chacun de vos projets, il en existe certainement une qui fera votre affaire.

Vous n’aurez ainsi pas à résoudre des problèmes déjà réglés, ni à recoder ce qui l’a déjà été mille fois. Vous pourrez ainsi vous concentrer sur votre problématique, et pas sur le code qui gère "la tuyauterie" sous-jacente. N’hésitez pas à parcourir les bibliothèques existantes, le soir au coin du feu. Vous apprendrez beaucoup, et gagnerez au final un temps précieux.

Ressources

 

Pour celles et ceux qui n’auraient pas un second servomoteur à disposition, ni un lab dans les parages, vous pouvez toujours suivre le TP général qui n’utilise qu’un seul servo. Vous améliorerez votre feu bicolore en y ajoutant une barrière. Ceinture et bretelles !

Les condensateurs

Composants passifs

Nous avons pas mal discuté des résistances, composant incontournable dans les montages électroniques.  Voici d’autres composants dits "passifs" que nous avons souvent à utiliser :

Les Condensateurs ou capacitances

Sujet un peu plus difficile si on veut tout comprendre des capacitances – mais nous allons rester simple… Voici le symbole électronique :

Symbole du condensateur
Symbole du condensateur

Leur construction et leur forme peut varier beaucoup mais leur principe reste le même : deux matériaux conducteurs (les deux traits verticaux), séparés par une matière isolante, appelée diélectrique (l’espace entre les traits), l’ensemble souvent en forme de "sandwich" ou parfois enroulés.

Un échantillon de condensateurs
Un échantillon de condensateurs
Condensateurs polarisés

Il faut noter que, à partir de la valeur d’environ 1uF, bon nombre de condensateurs sont polarisés - c’est à dire qu’ils ont un "+" et un "-". On les appellent souvent condensateurs chimiques. Il faut les brancher dans le bon sens afin d’éviter les surprises désagréables : inversés ils ont tendance à se gonfler jusqu’à parfois se rompre carrément. Les produits chimiques contenus dedans sont très corrosifs…

D’habitude c’est le fil "-" qui est repéré avec une bande noire sur le coté. Photo : merci Wikipedia :

La bande symbolise le côté '-'
La bande symbolise le côté '-'

Le condensateur est conçu pour stocker les charges électriques.  Leur valeur est mesurée et calculée en Farads (F). Plus la valeur est grande et plus le condensateur peut stocker de charge. Les valeurs rencontrées dans l’électronique classique sont de l’ordre de nanofarads  (10-9 Farads), jusqu’à des centaines de microfarads (10-6 F).  Souvent les valeurs sont écrites de la manière suivante :

  • 47uF = 47 microfarads ou 47 × 10-6 ;
  • 100nF = 100 nanofarads ou 100 × 10-9 ;
  • 14pF = 14 picofarads ou 14 × 10-12 (circuits radio) ;

À quoi servent des condensateurs ? Je vais vous montrer quelques exemples pratiques de leur utilisation.

Anti-rebond

Nous avons déjà utilisé des boutons afin de commander nos montages Arduino et vous avez sûrement eu à faire avec le problème de rebond des contacts (contact bounce, en anglais).  La construction mécanique des boutons fait que les contacts rebondissent plusieurs fois avant de s’arrêter en position ouverte ou fermée.  Avec un Arduino capable de réagir en un milliardième de seconde, ces rebonds sont un vrai casse-tête.

D’habitude on résout le problème dans le logiciel - on attend un certain nombre de millisecondes et on lit à nouveau l’entrée concernée pour déterminer l’état.  Sachez toutefois qu’il y a aussi une solution électronique qui peut parfois nous simplifier la vie. Voici le branchement typique d’un bouton en entrée d’un microcontrôleur :

Branchement simple d'un bouton
Branchement simple d’un bouton

L’entrée est tenue au niveau 1 par la résistance.  Quand on appuie sur le bouton l’entrée est 'tirée' vers le niveau 0. Regardons déjà à quoi ressemble le 'bounce' :

Un signal pas très propre
Un signal pas très propre

Cette trace d’oscilloscope correspond à une seule pression sur le bouton !  La durée dans le temps de cette trace est de 200 micro-seconds (uS).  Chaque division fait 25uS.  On voit bien que cela peut être interprété par le microcontrôleur comme au moins six pressions !  On peut améliorer la situation on ajoutant un condensateur (et oui, c’est toujours le sujet de ce chapitre…) comme ici :

Branchement d'un bouton avec un condensateur
Branchement d’un bouton avec un condensateur

Un condensateur a besoin de temps pour se charger et se décharger.  L’ajout de ce (petit) condensateur en parallèle avec le bouton va 'ralentir' les changements de tension, supprimant les effets aléatoires dus aux faux contacts. Et avec cette petite modification, nous avons :

Un signal plus propre
Un signal plus propre

Ce n’est pas un carré parfait, mais notre Arduino ne verra qu’une seule pression.  Sans avoir à ajouter du code spécifique anti-rebond.  Utile les petits condensateurs !

Base de temps RC

Voilà un montage simple à construire avec l’Arduino. D’abord le montage sur la platine de prototypage ou "breadboard" (littéralement "planche à pain" en anglais…) :

Expérimentation sur breadboard
Expérimentation sur breadboard

Très simple à câbler, ce petit montage sert pour voir comment les condensateurs peuvent nous fournir des bases de temps.  La sortie 13 de l’Arduino est celle équipée d’une DEL (LED) sur la platine de l’Arduino.  Quand la sortie est au niveau '1' la DEL s’allume.  Nous prenons la même sortie logique pour piloter notre montage. À travers la résistance de 10kΩ le condensateur se charge pendant que la sortie 13 est au niveau '1’, et se décharge pendant le niveau '0’.  Le transistor est utilisé comme une sorte d’interrupteur piloté (nous allons parler des transistors bientôt…) pour allumer la DEL rouge. Voici le schéma :

Schéma de l’expérience
Schéma de l’expérience

Et le programme (ou sketch en anglais pour l’Arduino) : (l’exemple Blink modifié un peu)

/*
    SlowBlink
    Turns on an LED on for 5 seconds, then off for 5 seconds, repeatedly.
    Derived from the 'Blink' example in the Arduino suite.
    This example code is in the public domain.
 */
 
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup() {                
    // initialize the digital pin as an output.
    pinMode(led, OUTPUT);     
}

// the loop routine runs over and over again forever:
void loop() {
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(5000);               // wait for 5 seconds
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    delay(5000);               // wait for 5 seconds
}

À partir des schémas, branchez les composants sur la platine de prototypage puis téléchargez le programme et vérifiez que la LED sur l’Arduino clignote (très) lentement. Regardez la LED rouge sur la platine d’expérimentation : elle clignote aussi, mais avec presque une seconde de retard. Le délai exact dépend de plusieurs paramètres, notamment le type de transistor.

Dans un monde idéal1, le temps de charge/décharge d’un condensateur se calcule facilement : T=R×CT = R×C

  • TT le temps en secondes ;
  • RR la résistance en Ohms ;
  • CC la capacitance en Farads.

Merci à Glenn Smith pour ce cours !


  1. Mais ce temps dépend beaucoup des conditions de branchement, notamment la résistance de sortie du montage (souvent appelée l’impédance). Prenant l’exemple ici, nous avons R = 10000Ω et C = 0,00047F ce qui donne le temps de charge ou décharge de 4,7 secondes.  Pourquoi notre DEL n’avait qu’un retard d’environ 1 seconde, alors ? La réponse au chapitre "transistors" ! Vous pouvez changer le condensateur pour un plus grand ou plus petit, ou en mettre plusieurs en parallèle ou série.  Vous verrez que les valeurs des condensateurs en parallèle s’additionnent, et la valeur des condensateurs en série se calcule comme ceci : Ctot=11C1+1C2++1C2C_{tot} = \frac{1}{\frac{1}{C_1} + \frac{1}{C_2} + \dots + \frac{1}{C_2}} (exactement l’inverse que pour les résistances)

Travaux pratiques

TP à faire pour la semaine 6

Cette semaine, nous restons dans nos montages de feux en compliquant un peu la chose avec une barrière. Le montage à réaliser devra comporter :

  • Un servomoteur qui jouera le rôle de barrière ;
  • Un bouton pour demander l’ouverture de la barrière ;
  • Un feu bicolore qui passera au vert lorsque la barrière sera complètement ouverte.

Le scénario sera le suivant :

Illustration du fonctionnement
Illustration du fonctionnement

Le fonctionnement normal est un feu allumé au rouge et une barrière fermée (0°). Le fonctionnement normal est interrompu par l’appui sur un bouton poussoir.

Si l’appui du bouton est détecté, alors la barrière (actionnée par le servomoteur) se relève doucement. Lorsque la barrière est à la verticale (90°), le feu vert s’allume pendant 5 secondes pendant lesquelles la barrière reste ouverte (90°). Après les 5 secondes, le feu repasse au rouge, la barrière redescend doucement et le fonctionnement normal reprend.

Aussi, nous souhaitons recevoir le message "Bouton appuyé" dans le moniteur série lorsque l’appui a été détecté.

Quelques indices

Vous aurez besoin de mobiliser toutes les compétences vues ces dernières semaines pour réaliser ce TP :

  • L’utilisation de boucles for qui ont été décrites cette semaine ;
  • L’utilisation d’entrée et de sorties numériques ;
  • Importation de bibliothèques et d’un servomoteur ;
  • Utilisation des instructions Serial.
Quelques conseils
  • Avertissez nous sur le fil de discussion ci-dessous si les consignes ne vous semblent pas claires ;
  • N’allez pas regarder la solution sur Internet sinon il n’y a pas de fun ;
  • Prenez toujours les hypothèses qui vous arrangent ; :)
  • Seules les notions abordées dans le cours et sur cette page sont nécessaires pour mener à bien ce TP ;
  • Il n’y a pas une mais plusieurs solutions à chaque problème. La meilleure est celle que vous comprenez !

Bon courage et bonne semaine,

L’équipe du MOOC

Environnements de développement

Annexe

Lors du montage de ce MOOC,  nous avons fait le choix de parler du code : le langage que nous utilisons pour programmer Arduino.

Sachez cependant qu’il existe des alternatives pour développer  des programmes pour Arduino :

  • S4A (Scratch pour Arduino) : est un environnement de programmation libre qui permet d’aborder la programmation de façon ludique et intuitive. Le concept d’écriture du programme repose sur un principe d’assemblage de modules classés par code couleur (comme l’idée des Lego) et chaque module correspond à une instruction (if, delay, for, digitalWrite…). Pour installer ce logiciel et en savoir plus sur son utilisation, nous vous invitons à lire la section consacrée à S4A sur le FlossManual Arduino.
S4A - Scratch For Arduino
S4A - Scratch For Arduino
  • MATLAB : est un langage de haut niveau et un environnement interactif pour le calcul numérique, la visualisation et la programmation. Une extension de MATLAB appelée Simulink Coder permet de générer et exécuter du code pour Arduino à partir d’un langage graphique basé sur l’assemblage de briques symbolisant des instructions ou fonctions. Dans le cadre de ce MOOC, la société MathWorks a réalisé les vidéos d’initiations suivantes :

    MathWorks nous permet également de télécharger gratuitement une version de leurs outils durant toute la durée du MOOC. Cliquez ici pour en savoir plus et pour récupérer une licence du logiciel.

SIMULINK
SIMULINK
  • ArduBlock : est une extension libre du logiciel Arduino (celui utilisé dans le MOOC) qui permet de programmer en utilisant là encore des blocs symbolisants des tests, des boucles et des fonctions. Pour installer ce logiciel et en savoir plus sur son utilisation, nous vous invitons à lire cette page.
ArduBlock
ArduBlock
Licence
CC-BY-SA
CC-BY-SA

Licence CC-BY-SA : si vous faites des modifications, le contenu doit-être redistribué sous la même licence

Séparer en fichiers

Lorsque vous commencez à faire de gros projets, il devient utile voire indispensable de (très) bien organiser son code. Cela commence par séparer son code en différents fichiers afin d’avoir des entités logiques séparées les unes des autres.

Voyons cela !

Une opération simple à faire et qui permet de gagner beaucoup en organisation de son code est de séparer ce dernier en différents fichiers. Généralement, on fait un fichier par unités “logiques”. Par exemple, imaginons que nous utilisions un composant un peu compliqué qui sert d’horloge. Ce composant peut renvoyer une date en entier, juste le jour, mois, année ou encore juste l’heure, la minute ou la seconde courante. Pour bien faire, il nous faudrait une fonction par unité de temps. On aurait ainsi au moins 6 fonctions pour récupérer heure/minutes/secondes/jour/mois/année et 6 fonctions pour les régler dans le composant. 12 fonctions + la loop() et le setup() et vous voilà avec un fichier original bien encombré ! :D

Pour créer un nouveau fichier dans l’IDE Arduino, il suffit de cliquer sur la petite flèche en haut de l’espace d’édition du code puis ensuite de cliquer sur “Nouvel Onglet” ou “New Tab” comme mis en évidence sur la capture d’écran ci-dessous :

Bouton de nouvel onglet
Bouton de nouvel onglet
Le fichier .h

lorsque l’on veut séparer son code en plusieurs fichiers, il y a certaines choses à respecter. Ainsi, à chaque fois que l’on veut créer un nouveau fichier de code on ne vas pas en créer un mais deux ! Le premier fichier aura l’extension .h signifiant header, c’est ce que nous allons voir maintenant.

Ce fichier va regrouper les prototypes des fonctions ainsi que les définitions de structures ou de classes mais nous verrons cela après.

Le prototype d’une fonction représente un peu un contrat. Il va définir le nom de la fonction, ce qui rentre à l’intérieur (les paramètres) et ce qui en sort (la variable de retour). Ainsi, votre programme principal aura une idée de comment fonctionne extérieurement votre fonction. Un peu comme s’il s’adressait à une boîte noire.

Si l’on devait écrire l’exemple ci-dessus on pourrait avoir le contenu de fichier suivant :

horloge.h

char getHeure();
char getMinute();
char getSeconde();
char getJour();
char getMois();
char getAnnee();

void setHeure(char val);
void setMinute(char val);
void setSeconde(char val);
void setJour(char val);
void setMois(char val);
void setAnnee(char val);

void afficherDate();
void afficherHeure();
void afficherDateHeure();

Comme vous pouvez le voir, avec ces définitions on peut savoir ce qu’est supposée faire la fonction grâce à son nom et le type de variable qu’elle manipule en entrée et en sortie.

Bien, maintenant passons à la suite pour voir où et comment implémenter ces fonctions.

Le second fichier .cpp {#fichiercpp}

Le second fichier que nous allons créer sera avec une extension .cpp (pour C plus plus ou C++). Il regroupera le code à proprement parler, l’implémentation de vos fonctions. C’est ici que vous allez écrire le contenu de vos fonctions, ce qui est censé se passer à l’intérieur de ces dernières.

Pour faire cela, la première étape sera d’inclure le fichier de prototypes via la commande de préprocesseur #include :

#include "horloge.h" // horloge.h pour notre exemple

Cette ligne doit être la première de votre fichier .cpp et elle ne prend pas de ; à la fin.

Une fois cela fait, il va falloir taper le code de vos fonctions.

Pour le besoin de l’exercice, je vais me contenter d’écrire des instructions bidons. Dans la vraie vie de tous les jours, vous auriez bien sûr fait un joli code pour communiquer avec un module où je ne sais quoi encore bien sûr ! :)

horloge.cpp

/* fichier horloge.cpp */
#include "horloge.h"

char getHeure() {
    Serial.println("getHeure");
    return 0;
}

char getMinute() {
    Serial.println("getHeure");
    return 0;
}

char getSeconde() {
    Serial.println("getHeure");
    return 0;
}

char getJour() {
    Serial.println("getHeure");
    return 0;
}

char getMois() {
    Serial.println("getHeure");
    return 0;
}

char getAnnee() {
    Serial.println("getHeure");
    return 0;
}


void setHeure(char val) {
    Serial.print("setHeure : ");
    Serial.println(val, DEC);
}

void setMinute(char val) {
    Serial.print("setMinute : ");
    Serial.println(val, DEC);
}

void setSeconde(char val) {
    Serial.print("setSeconde : ");
    Serial.println(val, DEC);
}

void setJour(char val) {
    Serial.print("setJour : ");
    Serial.println(val, DEC);
}

void setMois(char val) {
    Serial.print("setMois : ");
    Serial.println(val, DEC);
}

void setAnnee(char val) {
    Serial.print("setAnnee : ");
    Serial.println(val, DEC);
}


void afficherDate() {
    Serial.println("afficherDate");
}

void afficherHeure() {
    Serial.println("afficherHeure");
}

void afficherDateHeure() {
    Serial.println("afficherDateHeure");
}
Lier nos fichiers au programme principal

Vos définitions sont écrites et vos fonctions sont implémentées ? Il ne reste plus qu’à les ajouter à votre programme principal ! C’est en fait très simple vous allez voir.

Tout d’abord, il va falloir s’assurer que vos fichiers .h et .cpp sont dans le même dossier que votre .ino où se trouve votre fichier de programme Arduino.

Comme ceci :

Les fichiers sont dans le même dossier
Les fichiers sont dans le même dossier

C’est bon ?

Bien, il ne reste qu’une seule chose à faire, l’inclure dans le programme. Pour cela c’est tout bête, il suffit d’ajouter la ligne #include "horloge.h" en haut de votre fichier.

Et voilà ! Il ne vous reste plus qu’à faire des appels tout simples à vos fonctions perso dans le programme (setup ou loop ou où vous voulez !).

Maintenant, quand vous allez compiler, le compilateur va aller chercher le fichier pointé par le include, le compiler puis le lier dans votre programme principal.

Il peut arriver que le lien avec les symboles/bibliothèques Arduino ne se fasse pas correctement. Dans ce cas là, rajoutez l’include suivant au début de votre .h ou .cpp : #include "Arduino.h"

Séparer son code en fichiers est important pour facilement s’y retrouver, j’espère que vous l’avez bien compris. Une fois cette étape faite, vous devriez y voir plus clair dans vos gros programmes.

Les plus aguerris d’entre vous qui connaissent le C++ peuvent même coder en C++ pour créer des classes et ainsi pousser l’organisation encore plus loin !