[ Arduino Annexe F ] Organisez votre code 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 !

Séparer en fichiers

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é 😀 .
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 :
Ajout de fichiers

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

Comme vous pouvez le voir, avec ces définitions on peut savoir ce qu’est supposé 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

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 :

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

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 aller 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 :
Emplacement des fichiers
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/librairies 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 !

Une expérience (bonne ou mauvaise) à partager ? Des questions à poser ? Des conseils à donner ? Ne soyez pas timide !

30 commentaires

  1. Je découvre votre travail avec enthousiasme.
    Vieux débutant j’ai su programmer en basic et assembleur il y a des siècles.
    Arduino ouvre un champ de possibilités étonnant étonnant.
    Donc, merci pour votre clarté qui rend simple et de débuter et donne envie d’aller plus loin…..
    Bon..! D’abord, je débute.

  2. Bonjour,
    J’ai fait les manip expliquées plus haut, mais j’ai rajouté deux choses dans le fichier.h :
    -#pragma once ( mon prof de C++ , m’avait expliqué que cela évitaient des bugs du compilateur si jamais il y avait des #include redondants)

    – #if defined(ARDUINO) && ARDUINO >= 100
    #include « Arduino.h » //compilateur supérieur et égal a 1.0
    #else
    #include « WProgram.h » //compilateur inférieur et égal a 1.0
    #endif
    -> j’ai vu cette inscription dans un forum , elle semblait régler quelques problèmes liées à la séparation de fichiers

    Qu’en pensez-vous?

    Quoiqu’il en soit, avec ou sans ces 2 modifications, le compilateur rechigne : il ne reconnait pas les types ou expressions habituelles à l’Arduino du type OUTPUT, pinMode…. pour le citer ; ‘OUTPUT’ was not declared in this scope

    Avez-vous une idée de l’origine du problème, et comment le résoudre?

    Merci pour tout ces tutoriels sur Arduino ! =)

    • J’utilise habituellement un bloc

      en lieu et place de ton #pragma once donc je ne saurais pas trop me prononcer sur le bon fonctionnement de ce dernier avec Arduino ou pas…

      Pour ce qui est de ton souci, c’est du a un problème d’include que pourtant tu fais bien via ton blog de code…
      Si tu utilises un environnement Arduino recent (j’espere !) essaie de faire juste la ligne #include “Arduino.h” qui est censé résoudre cela…

      • Effectivment en mettant juste #include « Arduino.h » ca marche ! =)

        Seulement j’ai une petite interrogation ; dans mon cours de C++, il suffisait de mettre le #include dans le header « monSD.h » et de mettre #include « monSD.h » dans le fichier principal pour que le compilateur marche, sans redondance pour l’appel à #include .
        Ici le compilateur ne reconnait pas les types liés à la bibli SD.h si je fais ça, en revanche ça marche si je mets deux fois #include . dans le .ino et dans le .h
        Est-ce normal? Est-ce qu’il ne va pas y avoir un bug vu que je l’ai mis deux fois? Ca ne va pas prendre deux fois plus de mémoire?

        Merci d’avoir répondu si rapidement à mon dernier post =)

        • Si tu parles de faire deux fois #include "Arduino.h", pas vraiment d’inquiétudes a avoir, cette lib doit être bien fait et avoir les protections de multi-inclusions comme vu dans mon message précédent 🙂 Du coup ça ne pose pas de problèmes, elle ne sera bien incluse qu’une seule fois 🙂

          • Encore une queston pour la route :
            Connaissez-vous la différence entre SD.begin() et card.init()?

            Généralement SD.begin() permet l’initialisation de la carte dans la fonction setup, tandis que card.init() est utilisée pour initialiser la carte dans la fonction loop… Il y a-t-il une différence entre ces deux initialisations? Sinon pourquoi initialiser 2 fois, avec 2 fonctions différentes, et surtout, pourquoi initialiser la carte en boucle avec card.init?

          • Je n’ai à ce jour pas encore utilisé les cartes SD, du coup je n’ais pas vraiment de réponse… Il faudrait que j’aille lire la doc (ou google) pour cela mais je n’aurais pas le temps aujourd’hui. Désolé 🙁

          • Ok tant pis ; j’ai déjà recherché sur google , mais je n’ai pas trouvé grand chose. Il semble que ce soit lié à l’existence de deux librairies SD ( SD et SDFatlib) , l’une utilisant une partie des fonctions de l’autre d’après ce que j’ai compris…

  3. Bonjour,

    J’ai suivi cet article afin de découper mon programme mais cela ne fonctionne pas.
    Dans ma recherche de solution je suis arrivé a faire un programme très simplifié dont voici le détail :
    programme principal.ino :
    #include « Arduino.h »
    #include « fonction.h »

    void setup(){
    pinMode(led, OUTPUT);
    digitalWrite(led, LOW);
    }

    void loop(){
    init();
    }

    fichier fonction.h :

    #ifndef fonction_H
    #define fonction_H

    int led=4;

    void init();

    #endif;

    fichier fonction.cpp
    #include « Arduino.h »
    #include « fonction.h »

    void init(){
    digitalWrite(led, HIGH);
    }

    J’obtient avec ceci une « Erreur de compilation… » Si vous pouvez m’aider ?

    D’avance merci, et encore merci pour tous vos tutos !

    julien

    • Dur de répondre, il faudrait plus d’informations sur ce qui pose problème pendant la compilation (dans les préférences, cocher la case concernant les détails de la compilation).

      Les fichiers sont-ils aux bons endroits ?

  4. Bonjour,
    j’ai bien compris ces points qui sont une ouverture à la bibliothèque que je commence un peu à cerner et à coder mais pour des choses très simples pour le moment. Ma question relate ce genre de séparation de code qui concerne dans le cadre de l’article les fonctions dirons nous perso. Peut-on aussi initialiser les variables dans ce genre de fichiers si oui comment ?

    Merci pour votre réponse

  5. Bonjour,
    Merci pour cet article très clair.

    J’ai un petit soucis avec un projet, je n’arrive pas à utiliser de bibliothèque. Pour illustrer le problème, j’ai réutilisé votre exemple ai j’ai inclus dans le .cpp:

    /* fichier horloge.cpp */
    #include « Arduino.h »
    #include
    #include « horloge.h »

    etc..

    (Remarque: sans l’#include du Servo, j’ai quand même eu besoin de l’include Arduino.h pour pouvoir compiler)

    Voici le message de la console (j’ai coché les détails lors de la compilation dans les préferences) :

    C:\Program Files (x86)\Arduino/hardware/tools/avr/bin/avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10600 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega C:\Users\fabien\AppData\Local\Temp\build4577536529068928811.tmp\horloge.cpp -o C:\Users\fabien\AppData\Local\Temp\build4577536529068928811.tmp\horloge.cpp.o
    C:\Users\fabien\AppData\Local\Temp\build4577536529068928811.tmp\horloge.cpp:3:19: fatal error: Servo.h: No such file or directory
    #include
    ^
    compilation terminated.
    Erreur lors de la compilation.

    • Après 2 minutes de recherches sur votre exemple (plutôt que 3h sur mon programme) j’ai trouvé, comme quoi ça aide de faire des exemples simple.

      Pour que ça compile, il faut:
      //fichier main:
      #include « horloge.h »
      #include Servo.h //entre crochets: <

      /* fichier horloge.cpp */
      #include "Arduino.h"
      #include Servo.h //entre crochets: <
      #include "horloge.h"

      Voilà, merci bien

  6. Bonjour Messieurs,

    Quelqu’un pourra-t-il me dire où se trouve l’erreur dans mon code ? j’ai 3 fichiers : Main.ion, Carte.cpp et Carte.h

    #ifndef DEF_Carte
    #define DEF_Carte
    #include « Arduino.h »
    Carte.h
    ——–
    {
    public:
    Carte();
    void configurationBroche();
    void lectureEntreeNum();
    void interruption();
    void declenchementRelais();

    protected:
    int temps1 , temps2 , temps3 , temps4 , temps5 , temps6 , temps7 , temps8 ;
    };
    #endif

    Carte.cpp
    ———–
    #include « Arduino.h »
    #include « Carte.h »
    #include
    {

    Carte::Carte()
    {
    temps1=0 ; temps2=0 ; temps3=0 ; temps4=0 ; temps5 ; temps6 ; temps7 ; temps8 ;
    }

    void Carte::configurationBroche()
    {
    FlexiTimer2::set(1,interruption);
    FlexiTimer2::start();
    }

    void Carte::interruption()
    {
    temps1++; temps2++; temps3++; temps4++; temps5++; temps6++; temps7++; temps8++;
    }

    void Carte::declenchementRelais()
    {
    Serial.print(temps1); Serial.print(temps2); Serial.print(temps3); Serial.print(temps4); // etc…
    }

    };

    Main.ino
    —–
    #include « Arduino.h »
    #include « Carte.h »

    Carte carte;

    void setup ()
    {
    carte.configurationBroche();
    }

    void loop ()
    {
    carte.declenchementRelais();
    }

    Voici l’erreur :

    Carte.cpp: In member function ‘void Carte::configurationBroche()’:
    Carte.cpp:89:34: error: no matching function for call to ‘set(int, )’
    FlexiTimer2::set(1,interruption);
    ^
    Carte.cpp:89:34: note: candidates are:
    In file included from Carte.cpp:2:0:
    D:\Users\melanie.vivies\Documents\Arduino\libraries\FlexiTimer2/FlexiTimer2.h:18:7: note: void FlexiTimer2::set(long unsigned int, void (*)())
    void set(unsigned long ms, void (*f)());
    ^
    D:\Users\melanie.vivies\Documents\Arduino\libraries\FlexiTimer2/FlexiTimer2.h:18:7: note: no known conversion for argument 2 from  » to ‘void (*)()’
    D:\Users\melanie.vivies\Documents\Arduino\libraries\FlexiTimer2/FlexiTimer2.h:19:7: note: void FlexiTimer2::set(long unsigned int, double, void (*)())
    void set(unsigned long units, double resolution, void (*f)());
    ^
    D:\Users\melanie.vivies\Documents\Arduino\libraries\FlexiTimer2/FlexiTimer2.h:19:7: note: candidate expects 3 arguments, 2 provided
    Erreur lors de la compilation.

    D’avance je vous remercie

    • Hello !

      La zone de commentaire a tendance a manger quelques morceau de code (include par exemple). Je te propose que l’on se retrouve sur les forums de Zeste de Savoir (rubrique « Systèmes et Matériels ») pour en parler plus en détails (et aussi afin de garder cette section pour les commentaires sur le tutoriel lui-même).

  7. Salut,

    Je voulais savoir si le fait d’organiser mon code en le séparant en fichier .h et .Cpp , c’est justement ce qu’on appelle « creer son propre protocole de communication »

    Si c’est pas le cas, j’aimerai bien etre orienté vers  » comment creer son/ses propre(s) protocoles de communication » car je bosse sur le sujet dans le cadre d’un mémoire et je ne trouve pas jusqu’ici un document qui me montre comment creer son propre protocole.

    Merci d’avance

    • Hello !

      Non un protocole de communication ce n’est pas ca. Séparer les code en fichiers .h et .cpp c’est juste « bien organiser son code ». Un protocole de communication définit plutot comment une transmission d’informations va se faire. Par exemple se dire que « j’envoie les bits un par un, avec 5V pour le niveau 1 et 0V pour le niveau 0. J’envoi d’abord 2 bits à 1 pour signaler le départ de ma trame, puis 16 bits de données avec poids faible en premier, puis 2 bits à 1 pour signaler la fin de la trame ». Tu vois mieux ?

      • Oui…mais j’aimerai bien savoir comment faire ses protocoles, bref, comment “j’envoie les bits un par un, avec 5V pour le niveau 1 et 0V pour le niveau 0. J’envoi d’abord 2 bits à 1 pour signaler le départ de ma trame, puis 16 bits de données avec poids faible en premier, puis 2 bits à 1 pour signaler la fin de la trame”. ???
        Liens, documents…toutes aides me seront tres utiles

        Merci…

        • Si le protocole est le fruit de ton esprit alors aucune doc. ne pourrons te répondre. Sinon il faut se renseigner au cas par cas à grand coup de moteur de recherche pour avoir des infos sur le protocole ciblé (I2C, série, SPI, etc)

  8. Bonjour, bon article, comme d’hab….
    Par contre, j’ai un souci. Bah oui, sinon ce n’est pas marrant. J’ai un projet en cours, qui commence à prendre de l’ampleur: 3 sondes de températures, un afficheur 2×16, un touchepad pour naviguer dans l’affichage, une boucle d’évenements pour gérer tout ça. J’ai tenté de séparer mes fonctions et un fichiers pour les variables globales, mais déjà celui la, je n’arrive pas à lui faire comprendre qu’il existe:
    LCD16022.ino:61:22: fatal error: globales.h: Aucun fichier ou dossier de ce type
    compilation terminated.
    Erreur lors de la compilation.
    bien evidemment, la compilation passait avant d’avoir saucissonné le code.
    Une idée à me soumettre, svp?
    Merci.

    • Bon, il ne m’a pas fallut, en fait, beaucoup de temps pur trouver après avoir posé la question, et pourtant, cela fait deux heure que je cherchais:
      1/ quand on fait un include d’un fichier personnel il faut faire
      #include « nomdufichier.h » et non #include
      je ne suis pas un débutant en programmation, mais là… ça l’a totalement échappé!!!!

      2/ je pense qu’il faut ajouter à votre article une petite remarque: l’IDE arduino, sur un fichier ‘simple’ se place sur la ligne ou il trouve une erreur, c’est pas mal parfois. Mais quand il y a des fichiers multiples, ce n’est plus le cas. C’est bête, mais il vaut mieux le savoir.

      Merci pour votre aide 🙂

  9. Bonjour,
    Je suis en train de lire ce tuto (très bien expliquer d’ailleurs) mais je suis devant un soucis, je dois réunir 5 programmes en 1 seul pour ma carte.
    Est-ce que je dois faire pareil .h et .cpp pour chacun de mes petits programmes et faire un code bien propre dans le premier onglet ? Mais surtout je vois pas comment faire pour demander au programme principale de faire ? je dois juste faire
    Serial.print « ma variable sortie des enfers » et c’est bon ? (par exemple) merci

  10. @Eskimon webmaster
    Tuto clair et très utile! Merci

    Et mention complémentaire: merci de demander gentiment de désactiver le bloqueur de publicité, ça donne envie de faire un geste pour aider à financer le truc! J’aime bien ne pas être forcé 😉

  11. Pingback: DIY - Technologies | Pearltrees

  12. Bonjour,
    je souhaite dupliquer une librairie (trop volumineuse) tout en conservant celle d’origine qui est attachée à des sketches existant.
    la nouvelle libraire allégée pourrait se trouver dans le même répertoire que le nouveau sketche qui l’utilise.
    Pouvez vous m’aiguiller sur ce qu’il faut faire pour déclarer une biblio uniquement visible du sketch dans son repertoire?
    Merci

Laisser un commentaire