Erreur lors de la compilation d'une librairie

Bonjour à tous :slight_smile: ,

j'ai réalisé un petit couple "logiciel PC / Sketch" pour gérer les sorties d'une carte UNO , standard et PWM, par l'intermédiaire de la liaison série. Ce couple fonctionne parfaitement (pas de séparation en vue) et pour en simplifier l'usage, si ça peut intéresser quelqu’un, j'ai modifié le sketch pour en faire une librairie.

C'est là que ça coince. J'ai des erreurs de compilation du style:

C:\arduino\projets\libraries\Toto/Toto.cpp:11: multiple definition of Toto::Toto()' Toto.cpp.o:C:\Users\naute\AppData\Local\Temp\build4305738701655055254.tmp/Toto.cpp:9: first defined here Toto\Toto.cpp.o: In function Toto':

Je ne comprend pas ce que "multiple definition of ... " veut dire, dans la mesure ou chaque déclaration est unique et que le code rigoureusement identique du sketch (excepté pour les différences dues à la librairie) fonctionne parfaitement.

J'ai consulté plusieurs tutos qui, heureusement donnent les mêmes indications, et que j'ai respectés à la lettre.

En gros, je nage. J'ai tenté différentes approches sans résultat. Cela dit, je suis nouveau dans le monde Arduino et j'ai du passer à coté de quelque chose.

Si quelqu'un a, sinon une solution, ne serait-ce qu'une piste à suivre, j'en serai ravi.

Quoiqu'il en soit, merci d'avance,

naute.

Pas de code, pas de chocolat !

Bonjour :slight_smile: ,

mais je l'ai mon code! J'ai même la conduite. En plus, j'aime pas le chocolat... :smiley: .

Je pensais que le message d'erreur suffisait pour avoir une idée de la panne. Enfin, c'est comme ça dans les Rolls-Royce. En même temps, j'ai pas de Rolls-Royce.

Alors voilà le code en question:

Entête:

#ifndef DOMINO_H
#define DOMINO_H

#include "Arduino.h"

class Domino
{
  private:
    void pinStatus();
  public:
    Domino();
    ~Domino();
    void begin(int Bauds);
    void connect();
    void pwm(byte Pin);
    void onOff(byte Pin);  
};

#endif

Code proprement dit:

#include "Domino.h"



Domino::Domino()	//Constructeur
 {
 }


int Tableau[20];     //Tableau d'état des sorties: -1 pour l'état HIGH, -2 
                    //pour l'état LOW, entre 0 et 255 pour la valeur PWM
byte Reception;  	//Stockage de l'octet entrant
String Message = "";    //Messages entrants et sortants
boolean FlagReception = false;  //


//Méthode privée

void pinStatus() /*Renvoie au programme appelant, sur sa demande, l'état des
broches de la carte, inactif (-3) haut (-1), bas (-2) ou valeur PWM ([0..255]),
ce qui lui permet de savoir si la broche est utilisée ou non, et si oui, si
elle est utilisée en sortie standard ou PWM et de connaître son état.
*/
{            //
  if (FlagReception == true)
  {
    String Status = "";
    if (Message == "status")  //Si demande du status des pins
    {
      for (byte i=0; i<=19; i++)
      {
        Status += Tableau[i];
        Status += ";";      
      }
      Serial.print(Status);
      delay(1);
      FlagReception = false;
    }
  }
} 

//Méthodes publiques

void Domino::begin(int Bauds) 	//<<constructeur>>
{
  for (byte i=0; i<=19; i++)	//Initialisation du tableau
  {
    Tableau[i] = -3; //Pin non utilisée en sortie, Onoff ou PWM         
  }
  Serial.begin(Bauds);	//Initialise le port série à la vitesse "Bauds"
} 

void Domino::connect()
{
  Message = "";
  while (Serial.available() > 0) //Tant qu'il reste un octet dans le buffer
  {
    FlagReception = true;        //On positionne l'indicateur de réception
    Reception = Serial.read();   //On l'extrait vers "Reception"
    Message += char(Reception);  //On construit la chaine "Message"   
    delay(10);
  }
  if (FlagReception == true) Serial.flush();  //On vide le tampon par précaution
  pinStatus();
}

void Domino::onOff(byte Pin)  //Commuter la sortie "Pin" entre les états haut et bas
/*Au premier passage dans la fonction, c'est à dire à la mise sous tension de la carte
ou à sa réinitialisation, la valeur "Tableau" de toutes les sorties est initialisée à
-3 par la méthode "begin". On teste donc la valeur "Tableau" de la sortie "Pin" donnée
en argument ce qui permet de configurer la broche correspondante en sortie (OUTPUT),
chose que l'utilisateur n'aura donc pas à faire, puis on lui affecte la valeur "Tableau"
-2 qui correspond à l'état "LOW". Cette action n'aura lieu qu'une fois, à la mise en
service, et correspond donc bien à une initialisation dans le "setup()". Elle permet
au programme appelant, par l'intermédiaire de la méthode "Status()", de connaître la
configuration de la broche et sa valeur, pour traitement.
Le code de la méthode proprement dit sera, quant à lui, effectué à chaque appel de la
méthode et aura pour action de commuter la sortie correspondante à l'état haut (-1) ou à
l'état bas (-2) en fonction de la commande reçue.
Cette commande est une chaine de la forme "BtnxOn" ou "BtnxOff" où x représente le
numéro (Pin) de la sortie traitée: Par exemple, la réception de la chaine "Btn4On"
indiquera à la carte qu'elle doit passer la sortie 4 à l'état haut.
*/
{
  if (Tableau[Pin] == -3)  //Initialisation de la sortie "Pin" en sortie standard
  {  //Code exécuté une seule fois, au premier passage dans la fonction
    pinMode(Pin,OUTPUT); 
    Tableau[Pin] = -2;  //Valeur LOW
  }
  if (FlagReception == true)
  {  //Code exécuté à chaque passage lors de la réception d'un message		
    String TempStr = "Btn";
    TempStr += byte(Pin);
    TempStr += "On" ; 
    if (Message == TempStr)
    {
      digitalWrite(Pin,HIGH); //Met à l'état haut la Pin correspondante
      Tableau[Pin] = -1; //Renseigne le tableau d'état des sorties (voir déclaration)
      Serial.print(Message); //Renvoi l'état de la Pin pour maj de la led du bouton
      FlagReception = false;
      delay(1);
    }
    else
    {
      TempStr = "Btn";
      TempStr += byte(Pin);
      TempStr += "Off" ;
      if (Message == TempStr)  
      {
        digitalWrite(Pin,LOW); //Met à l'état haut la Pin correspondante
        Tableau[Pin] = -2;  //Renseigne le tableau d'état des sorties (voir déclaration)
        Serial.print(Message); //Renvoi l'état de la Pin pour maj de la led du bouton
        delay(1);
        FlagReception = false;			
      }   
    }
  }
} 

void Domino::pwm(byte Pin)  //Envoie une valeur comprise entre 0 et 255 sur la sortie "Pin"
/*Voir les explications pour la méthode "onOff(byte Pin)", qui sont indentiques mises à
part les valeurs qui, ici, PWM oblige,  varieront de 0 à 255.
Ici, la chaine de commande est de la forme "PWMx;y" où x représente la sortie traitée et
où y représente la valeur PWM que la carte devra "écrire" sur cette sortie. La sortie
sélectionnée devra, bien sûr, être une sortie PWM valide. Par exempe, la réception du
message "PWM10;123" demandera à la carte d'écrire la valeur 123 sur la sortie 10, c'est
à dire mettre à l'état haut cette sortie selon le rapport cyclique 123/255.
*/
{
  if (Tableau[Pin] == -3)  //Initialisation de la sortie "Pin" en sortie PWM
  {  //Code exécuté une seule fois, au premier passage dans la fonction
    pinMode(Pin,OUTPUT);
    Tableau[Pin] = 0;  //Valeur PWM 
  }
  if (FlagReception == true)
  {  //Code exécuté à chaque passage lors de la réception d'un message
    String Bouton = Message.substring(0, Message.indexOf(";"));  //Récupère l'entête
    String Valeur = Message.substring(Message.indexOf(";") +1 ); //Récupère le valeur
    String TempStr = "PWM";  //Construit la chaine d'entête correspondant à "Pin"
    TempStr += byte(Pin);    //pour comparaison avec l'entête reçue
    if (Bouton == TempStr)   //Si le test est positif, on traite
    {		
      int Longueur = Valeur.length()+1;	    //Manipulation destinée à convertir la chaine 
      char CarVal[Longueur];                //"Valeur" de type "String" en une valeur entière 
      Valeur.toCharArray(CarVal, Longueur); //"Val" de type "int", pour contourner l'absence
      int Val = atoi(CarVal);		    //d'une fonction du genre "String.toInt("123")"
      analogWrite(Pin, Val);
      Tableau[Pin] = Val;  
      FlagReception = false;
    } 
  }
}

Exemple pratique:

#include "Domino.h"

Domino Dom; //Instanciation

byte led5 = 5;  //Déclarations
byte led10 = 10;
byte led13 = 13;

void setup()
{
  Dom.begin(9600); //Initialisations
}

void loop()
{
  Dom.pwm(led5);
  Dom.pwm(led10); 
  Dom.onOff(led13);
}

Après téléversement, si ça fonctionne, il suffit d'envoyer, à l'aide du terminal Arduino, un message sous forme de chaine du type "BtnxOn" ou "BtnxOff" avec x représentant le numéro de broche de 2 à 19 (les broches 0 et 1 étant réservées pour la liaison série), pour, d'une part, recevoir l'accusé-réception sur le terminal sous la forme d'une chaine identique à celle envoyée, et d'autre part, d'allumer ou d'éteindre la (les) diode(s) raccordées aux sorties utilisées à titre d'exemple.
Pour les sorties "PWM", le message à envoyer est du type "PWM;x" où x représente un entier compris entre 0 et 255. En fonction de la valeur envoyée, la diode éventuellement raccordée brillera plus ou moins. Il n'y a pas d'accusé-réception sur le terminal.

J'utilise la version 1.0.5-r2 de l'IDE Arduino sous Windows 7.
Je travaille avec une carte Arduino UNO.

Par contre, je ne connais pas l'age du capitaine :confused: .

En espérant que ces précisions suffiront,

cordialement,

naute.

Le code principal contient #include "Domino.h", c'est bon,

Par contre le fichier de librairie "Domino" comporte lui même un #include "Domino.h" (de trop) et on se retrouve avec une double définition.

Bonjour Christian :slight_smile: ,

qu'entends-tu par "Fichier principal" (exemple.ino ?) et par fichier librairie "Domino" (Domino.cpp ?)?

Si je supprime le "#include "Domino.h" du fichier Domino.cpp j'obtiens le message d'erreur suivant:

Domino.cpp:67: error: 'Domino' is not a class or namespace

Si je supprime le "#include "Domino.h" du fichier exemple.ino j'obtiens le message d'erreur suivant:

exemple:11: error: 'Dom' was not declared in this scope

Dans les deux cas, la compilation échoue.

En tout cas, merci pour ta réponse.

Cordialement,

naute.

Bonjour,
Pourrais-tu joindre à un de tes messages les fichiers Domino.h et Domino.cpp ?
@+

Bonjour icare :slight_smile: ,

je ne comprends le pourquoi de ta demande dans la mesure où j'ai posté le code de ces fichiers un peu plus haut, mais tu dois avoir tes raisons.

Voici donc les deux fichiers demandés et je te remercie de t'intéresser à mon cas.

Cordialement,

naute.

Domino.h (257 Bytes)

Domino.cpp (6.03 KB)

Re,

Supprime ton destructor dans le fichier .h ou complète ton .cpp
et ça compile.

#ifndef DOMINO_H
#define DOMINO_H
#include "Arduino.h"
class Domino
{
  private:
    void pinStatus();
  public:
    Domino();
//    ~Domino();  //****************************************
    void begin(int Bauds);
    void connect();
    void pwm(byte Pin);
    void onOff(byte Pin);  
};

#endif

Bonjour icare :wink: ,

avant tout, méa culpa, j'ai effectivement oublié de remettre le destructeur dans le fichier que je t'ai envoyé. Lors de mes différents essais, j'avais, entre autre, supprimé le destructor, déclaration et implémentation, et j'ai oublié, quand j'ai refait sa déclaration dans le fichier .h, de le ré-implémenter dans le .ccp. Grandeur et misère!

Je complète donc mon .ccp (même s'il n'y a rien à détruire, mais ça me semble plus rigoureux que de supprimer sa déclaration dans le .h), et je compile: rebelote. Hors, comme chez toi, ça compile, c'est qu'il doit s'agir d'autre chose que d'un problème de code.

J'ai donc essayé autre chose, puisque maintenant, après tes essais, je sais que le code fonctionne.

J'ai réinstallé (dézippé) un IDE Arduino complet avec lequel j'ai lancé une compilation, et la, bingo: ça compile jusqu'au bout.

Cela vient donc, à priori, de l'autre "installation", mais comme tout le reste y fonctionne parfaitement, je me gratte la tête.

Je te remercie grandement pour ton aide car je sais maintenant que ma librairie fonctionne, mais je ne marque pas tout de suite le sujet comme "Résolu" je voudrais quand même trouver ce qui coince dans un des IDE et pas dans l'autre, alors qu'ils sont, à priori, identiques. Cela pourrait déboucher sur quelque chose d'utile.

Merci et à bientôt,

naute.

Re,

j'ai un semblant de piste. Je me demande si la librairie n'est pas chargée deux fois lors de la compilation, ce qui pourrait éventuellement expliquer le message "Multiple definition" du compilateur.

Je m'explique:
Lors de la mise au point (un bien grand mot pour un si petit code), j'avais, à un moment, créé le dossier "Domino" dans "arduino\libraries" et copié dedans mes fichiers .h et .cpp, puis j'avais importé la librairie correspondante avec la commande "Add library..." de l'IDE. C'était un peu prématuré, et, j'ai voulu la supprimer par la suite. Hors, il n'y a pas d'utilitaire de prévu à cet effet. J'ai donc supposé qu'il suffisait de supprimer le dossier "arduino\libraries\domino" pour supprimer cette librairie.
Que nenni! Elle apparait toujours dans la liste des librairies, et quand je fais "Importer bibliothèque...", j'ai bien la ligne "#include " qui s'affiche dans mon sketch.

Encore plus fort, pour rire, j'ai recréé le dossier "arduino\libraries\domino" et j'ai refais "Add library..." et je me retrouve maintenant avec deux entrées "Domino" dans "Importer bibliothèque...", comme si le logiciel faisait une copie du dossier ou des fichiers quelque part ailleurs que dans le dossier "arduino\libraries". Si c'est le cas, j'aimerai bien savoir où.

La question est donc, pour l'instant, de savoir s'il existe une méthode pour se débarrasser définitivement d'une librairie, une fois qu'elle a été importée.

Quelqu'un aurait-il une idée?

Accessoirement, je ne connais pas la différence entre "#include "toto.h"" et "#include <toto.h>". Est-ce que ces deux expressions sont interchangeables?

A bientôt,

naute.

naute:
Accessoirement, je ne connais pas la différence entre "#include "toto.h"" et "#include <toto.h>". Est-ce que ces deux expressions sont interchangeables?

les include avec les <> sont cherchés dans le PATH
les include avec les "" sont cherchés dans le répertoire courant

Il y a 2 endroits où les librairies peuvent être installées

  1. dans le répertoire d'installation de l'IDE, il y a un sous-répertoire library
  2. dans le répertoire sketchbook, il y a un sous-répertoire library

Dans le premier cas, les librairies sont visibles de tous les utilisateurs de la machine mais elles peuvent être perdues lors d'une réinstallation de l'IDE.
Dan le second cas, elles ne sont visibles que de l'utilisateur courant mais sont pérennes dans le temps.

Bonjour fdufnews :slight_smile: ,

c'est super, j'apprends beaucoup dans ce post, et il y a du boulot :roll_eyes: . Ce sont, bien sûr, des notions de base, mais si on ne les a pas...

J'ai donc supprimé "proprement" ma librairie de l'IDE en la supprimant des deux sous-répertoires "library" ce qui m'a permit de réimporter "proprement" la version définitive.

Encore deux question à propos de cette version:

  • Comment ce fait-il que le "#include "arduino.h"" de mon fichier d'entête "Domino.h" fonctionne, alors que, si j'ai bien compris tes explications, "arduino.h" n'étant pas dans le même dossier, je devrais utiliser la syntaxe "#include <arduino.h>"?
  • Pourquoi ai-je une erreur de compilation quand je déclare "private: void pinStatus()", ce qui me parait logique, cette fonction n'étant pas utilisée par l'utilisateur final, alors que "public void pinStatus()" fonctionne sans soucis?

Errata: Dans le fichier exemple dont j'ai fourni le code plus haut, j'ai commis un oubli (ce n'est pas le premier et ce ne sera probablement pas le dernier) qui l'empêche de fonctionner. J'ai omis l'appel à la méthode "connect()", ce qui est un peu gênant dans la mesure où c'est elle qui assure la réception du message :confused: .

Voici le code qui fonctionne:

#include "Domino.h"

Domino Dom; //Instanciation

byte led5 = 5;  //Déclarations
byte led10 = 10;
byte led13 = 13;

void setup()
{
  Dom.begin(9600); //Initialisations
}

void loop()
{
  Dom.connect();  //Ligne oubliée dans la version précédente
  Dom.pwm(led5);
  Dom.pwm(led10); 
  Dom.onOff(led13);
}

Sinon, tout fonctionne maintenant à merveille.

J'attends encore un peu, avant de clore ce post, qui est techniquement résolu, dans l'éventualité où quelqu'un pourrait répondre à mes deux dernières questions.

Merci et à bientôt,

naute.