[RESOLU] Utilisation de classe dans une classe "undefined reference to"

Bonsoir !

Je m’excuse d’avance de vous prendre du temps pour mon problème de codage, qui doit être tout simple… je ne sais pas si c’est l’heure ou autre chose, mais ça fait plusieurs heures que je cherche en vain… je suis aveugle ce soir. :sleeping:
J’espère qu’un regard neuf là dessus pourra me sortir de là :~
Je vous laisse voir la screen:

les fichiers correspondant sont en pièces jointes.
Merci !

QUADRICOPTER.h (965 Bytes)

QUADRICOPTER.cpp (740 Bytes)

motor.h (510 Bytes)

motor.cpp (765 Bytes)

Bonjour Plutot qu'une copie d'écran, il est préférable de copier le texte (ctrl-c / crrl-v) Il manque aussi le ino

Mais surtout on ne voit pas le debut des erreurs de compil

Je parie qu'il ne trouve pas motor.h

Bonjour.
Merci pour la réponse !

C’est tout ce que j’ai en erreur de compilation !

QUADRICOPTER\QUADRICOPTER.cpp.o: In function QUADRICOPTER': C:\Users\HéliX\Documents\Arduino\libraries\QUADRICOPTER/QUADRICOPTER.cpp:8: undefined reference to MOTOR::MOTOR(unsigned char, unsigned int, unsigned int)’
C:\Users\HéliX\Documents\Arduino\libraries\QUADRICOPTER/QUADRICOPTER.cpp:9: undefined reference to MOTOR::MOTOR(unsigned char, unsigned int, unsigned int)' C:\Users\HéliX\Documents\Arduino\libraries\QUADRICOPTER/QUADRICOPTER.cpp:10: undefined reference to MOTOR::MOTOR(unsigned char, unsigned int, unsigned int)’
C:\Users\HéliX\Documents\Arduino\libraries\QUADRICOPTER/QUADRICOPTER.cpp:11: undefined reference to MOTOR::MOTOR(unsigned char, unsigned int, unsigned int)' C:\Users\HéliX\Documents\Arduino\libraries\QUADRICOPTER/QUADRICOPTER.cpp:12: undefined reference to GYROSCOPE::GYROSCOPE()’

Maintenant le ino:

#include <Servo.h>
#include <inttypes.h>
#include "configuration.h"
#include "def.h"
#include <RADIOCOMMAND.h>
#include <QUADRICOPTER.h>

RADIOCOMMAND Radio(THROTTLE_PIN, YAW_PIN, PITCH_PIN, ROLL_PIN);
QUADRICOPTER Quadri(MOT_A_PIN, MOT_B_PIN, MOT_C_PIN, MOT_D_PIN, PULSE_MOTOR_WIDTH_MIN, PULSE_MOTOR_WIDTH_MAX);

void setup()
{
}

void loop()
{
  // Petit Check batterie
  if (Quadri.etatBatterie() == Q_BAT_HS)
    Quadri.LED_etatBatterie(Q_BLINK);
  
  // Condition de mise route
  while (Radio.isRTF() != Q_READY_TO_FLY);
  
  /***************************/
  /* PHASE DE CONTROL DE VOL */
  /***************************/
  Quadri.setConsignesDeVol(/*quelque chose*/);
  Quadri.CorrectionsDeVol(/* éventuellement quelques chose *unknown 4 the moment* */);
}

S’il ne trouvait pas le motor.h il devrait me le dire étend donné que je l’inclus dans QUADRICOPTER.h ?
Dans tous mes essaies j’ai essayé d’inclure directement motor.h dans le QUADRICOPTER.cpp et ça ne change strictement rien :~

Pas facile d'essayer de reproduire ton problème, il manque encore plein de fichiers. Quoi qu'il en soit j'arrive a compiler QUADRICOPTER et MOTOR Je ne tombe pas sur ton erreur mais j'ai du bricoler les chemins d'accès aux fichier include pour arriver a compiler quelque chose. Donc je pense que tu as un problème de ce coté là. Peut être ne va t'il pas chercher le MOTOR.h que tu crois .....

Essaye de créer une erreur volontaire dans ton MOTOR.h pour voir s'il la trouve :

#error Est-qu'il passe par ici ?

Par contre je voit d'autres erreurs plutot graves a corriger :

motor.cpp: In member function 'void MOTOR::writeVitesse(uint8_t)':
motor.cpp:19: warning: division by zero

QUADRICOPTER.cpp: In member function 'boolean QUADRICOPTER::etatBatterie()':
QUADRICOPTER.cpp:25: warning: no return statement in function returning non-void
QUADRICOPTER.cpp:25: warning: control reaches end of non-void function

J’ai rajouté la ptite ligne au milieu de la classe et la compilateur passe bien par là.

In file included from C:\Users\HéliX\Documents\Arduino\libraries\QUADRICOPTER/QUADRICOPTER.h:7,
                 from Helixcopter.ino:6:
C:\Users\HéliX\Documents\Arduino\libraries\QUADRICOPTER/includes\motor.h:17:4: error: #error Est-qu'il passe par ici ?

Au fur et à mesure des tests j’avais des fichiers qui se baladait par tout. J’ai donc tout supprimé et gardé les indispensables. J’ai tout recompilé et j’ai toujours l’erreur.
Pour simplifier ce topic je joins donc tout mon dossier arduino (le projet entier avec les librairies) pour que vous puissiez reproduire ma compilation.

En ce qui concerne la division par zéro je ne sais pas trop quoi dire… 255/1000 ne donne pas zéro pour moi ^^ les autres warning c’est parce que les fonctions ne sont pas encore implémentées.

Encore merci pour votre aide !

Arduino.zip (8.06 KB)

Reynosa: En ce qui concerne la division par zéro je ne sais pas trop quoi dire... 255/1000 ne donne pas zéro pour moi ^^ les autres warning c'est parce que les fonctions ne sont pas encore implémentées.

Si, en entier. 255/1000 = 0 255.0 / 1000.0 = 0.255 Note que passer en flottant n'est pas forcément une solution car les calculs flottants sur un micro 8 bits coûtent extrêmement chers en CPU. De plus ce code présente d'autres problèmes :

        uint8_t vitesseMicros;
        vitesseMicros = (vitesseMicros + 255) / (255/1000); // conversion num?rique

Car vitesseMicros étant un uint8_t, vitesseMicros+255 aboutit quasiment tout le temps à un débordement. Ce code pourrait être ré-écrit ainsi :

        vitesseMicros = (1000*((unsigned long)vitesseMicros + 255)) / 255; // conversion num?rique

Ou alors réfléchir plus profondément à tes échelles de valeurs. Ce genre de conversion prend du temps et fait perdre en précision.

Pour ce qui est de regarder ton code, de mon coté je n'aurais maintenant plus le temps avant la fin de semaine.

Bon réveillon.

Ok je vais revoir tout ce coté calcul ! Pour la compile il ne me reste plus qu'à attendre je ne trouve pas...

Bonnes fêtes !

Je viens de jeter un coup d’oeil.
L’erreur est une erreur de l’édition de lien, pas de la compilation. C’est le linker qui ne trouve pas le code de Motor.cpp parce que motor.cpp n’est pas dans la lib. Le problème viens de ton arborescence.
Tu met des CPP dans le répertoire “Includes” ce qui semble empecher leur inclusion dans le projet.
Les CPP devraient être dans le répertoire racine de la lib. Tu peux laisser les includes dans “Includes” ou bien tout mettre dans le répertoire racine.
Perso je choisirait de tout mettre dans le répertoire racine de la lib.

Je constate bien que certaines libs standard comme Ethernet utilise bien des sous répertoires mais je ne vois pas la différence avec ton cas. En tout cas si je met tout dans le répertoire racine de la lib, ton code compil beaucoup mieux.

Ensuite, il faut que chaque lib utilisée (même par un sous module d’une autre lib) soit déclarée par un #include dans le INO.
L’environnement Arduino n’ira chercher les includes que dans les répertoires des libs qui ont été déclarées dans le INO.
Par exemple, si tu ne met pas #include <Wire.h> dans le INO, il ne trouvera pas Wire.h lors de la compilation de gyroscope.cpp

Par ailleurs il est préférable d’utiliser <Arduino.h> plutot que <inttypes.h>

A chaque fois que je corrige un problème, je tombe sur un autre.
Par exemple fonctions déclarées comme request_Xout() mais appellée comme requestXout().
J’arrête là, je vais pas chercher a tout corriger.

Je pense qu’il faut que tu repartes d’un squelette propre qui compile et que tu ajoutes les éléments petit a petit au lieu de coder au kilomètre et de te retrouver avec un gros machin qui ne compile pas et qui comporte des problèmes en cascade.

A+

Merci beaucoup d'avoir pris un temps précieux à l'approche du réveillon pour me répondre !

Tout est rentré dans l'ordre ! tout fonctionne à merveille maintenant. À un détail près. Comment doit-on s'y prendre pour passer un nom de méthode d'une classe à "attachInterrupt()" ? J'ai essayé avec et sans parenthèses, j'ai aussi essayé par le setup avec "Radio.ISR_UpdateInfoCommand" et dans le constructeur de la classe avec "ISR_UpdateInfoCommand" il n'en veut pas dans tous les cas.

C:\Users\HéliX\Documents\Arduino\libraries\RADIOCOMAND\RADIOCOMAND.cpp: In constructor 'RADIOCOMMAND::RADIOCOMMAND(uint8_t, uint8_t, uint8_t, uint8_t)':
C:\Users\HéliX\Documents\Arduino\libraries\RADIOCOMAND\RADIOCOMAND.cpp:18: error: argument of type 'void (RADIOCOMMAND::)()' does not match 'void (*)()'
C:\Users\HéliX\Documents\Arduino\libraries\RADIOCOMAND\RADIOCOMAND.cpp:19: error: argument of type 'void (RADIOCOMMAND::)()' does not match 'void (*)()'
C:\Users\HéliX\Documents\Arduino\libraries\RADIOCOMAND\RADIOCOMAND.cpp:20: error: argument of type 'void (RADIOCOMMAND::)()' does not match 'void (*)()'
C:\Users\HéliX\Documents\Arduino\libraries\RADIOCOMAND\RADIOCOMAND.cpp:21: error: argument of type 'void (RADIOCOMMAND::)()' does not match 'void (*)()'
C:\Users\HéliX\Documents\Arduino\libraries\RADIOCOMAND\RADIOCOMAND.cpp:22: error: argument of type 'void (RADIOCOMMAND::)()' does not match 'void (*)()'

Je dois m'y prendre comme une patate, mais j'ai pas trouvé d'exemple ailleurs...

Bon réveillon !

Il n'est pas possible de passer une fonction membre non-statique de classe comme paramètre de attachInterrupt(). Il faut une fonction statique. Cela veut dire que la fonction ne peut pas accéder aux membres d'une instance.

Par exemple le code ci dessous marche

class MaClasse
{
public:
  MaClasse() { a = 0; }  
  
  int a;
  static int s_b;
  
  static void Interrupt();
};

void MaClasse::Interrupt()
{
  s_b++;
}

int MaClasse::s_b = 0; // Initialisation d'une variable statique de classe ~= variable globale de la classe

MaClasse essai;

void setup()
{
  attachInterrupt( INT0, MaClasse::Interrupt, RISING );
}

void loop()
{
}

Mais il ne fait peut être pas ce que tu veux car la fonction MaClasse::Interrupt() est statique (donc "globale" à la classe) et ne peut pas accéder à l'instance "essai".

Pour gérer des fonctions d'interruptions associées à une instance d'objet, il faut gérer la passage du contexte. Par exemple :

#define NB_INTR 2

class MaClasse
{
public:
  MaClasse() { a = 0; }  
  
  int a;
  static int s_b;
  
  static class MaClasse *IntrObject[NB_INTR];
  static void s_Interrupt0();
  static void s_Interrupt1();
  static void setInterrupt( byte intnum, MaClasse *pObject = NULL, byte mode = RISING );
  
  void Interrupt();
};

class MaClasse *MaClasse::IntrObject[NB_INTR] = {0, };

void MaClasse::setInterrupt( byte intnum, class MaClasse *pObject, byte mode )
{
  if ( intnum >= NB_INTR )
    return;
    
  if ( pObject )
  {
    IntrObject[intnum] = pObject;
    switch( intnum )
    {
      case 0:  attachInterrupt( intnum, s_Interrupt0, mode ); break;
      case 1:  attachInterrupt( intnum, s_Interrupt1, mode ); break;
    }
  }
  else
  {
    detachInterrupt( intnum );
    IntrObject[intnum] = NULL;
  }

}

void MaClasse::s_Interrupt0()
{
  if ( IntrObject[0] )
    IntrObject[0]->Interrupt();
}

void MaClasse::s_Interrupt1()
{
  if ( IntrObject[1] )
    IntrObject[1]->Interrupt();
}

void MaClasse::Interrupt()
{
  ++a;
}

int MaClasse::s_b = 0; // Initialisation d'une variable statique de classe ~= variable globale de la classe

MaClasse essai0;
MaClasse essai1;

void setup()
{
  // attachement de l'objet essai0 à INT0
  MaClasse::setInterrupt( 0, &essai0 );
  // attachement de l'objet essai1 à INT1
  MaClasse::setInterrupt( 1, &essai1 );
}

void loop()
{
}

Bonjour ! Bonne année !

J'ai bien compris le premier exemple et c'est vrai qu'étant liée à la classe la fonction ne répond pas à ce que je veux. Quant au deuxième exemple... Je ne comprend pas du tout, il fait appel à des connaissances que je n'ai pas...

Par contre sachant que ma classe RADIOCOMMAND n'est instanciée qu'une fois, est-ce que cela revient au même de mettre ses attributs en 'static' ? je pourrais par conséquent utiliser la première méthode, une méthode statique peut modifier des attributs statiques.

Je ne pense pas que le 2eme exemple soit si compliqué.

Mais sinon, oui si tu met tout en static ça marche. Tu n'as même pas besoin d'instancier dans ce cas et la classe ne sert pas à grand chose.

Ok, merci pour toutes ces réponses ! Problème résolu