Go Down

Topic: Séparer un gros code en plusieurs fichiers (Read 1 time) previous topic - next topic

lesept

Salut les amis !

Pour mon projet actuel, un contrôleur de prises connectées Sonoff S26 via internet, j'ai décidé de faire les choses proprement pour une fois : séparer le fichier ino en plusieurs fichiers cpp avec les fonctions dedans, et ajouter un fichier .h avec les déclarations en extern des variables globales et les prototypes des fonctions de mes fichiers cpp.

Mal m'en a pris, car je galère dessus depuis hier, grave (comme dit ma fille)...

J'ai 2 problèmes principaux à la compilation mais pas mal de questions en plus :

1/ D'abord les prototypes ne font rien : le compilo (ou le linker) me dit qu'il ne trouve pas mes fonctions. Exemple :
Quote
C:\Users\irena\Documents\Arduino\Client_simple_final\Client_simple_final.ino: In function 'void loop()':

Client_simple_final:136: error: 'setTimesBE' was not declared in this scope

       if (timeinfo.tm_hour + timeinfo.tm_min == 0) setTimesBE ();
Alors que j'ai bien dans le fichier .h :
Code: [Select]
void setTimesBE ();
Tous mes fichiers .cpp et le .ino débutent par le include de mon fichier .h
Code: [Select]
#include "client.h"

2/ Ensuite j'utilise une struct que j'ai définie dans le fichier .h et j'en déclare une à la ligne suivante :
Code: [Select]
typedef struct {
  byte device;
  byte days : 7;  // binary 0bSMTWTFS (Sunday, Monday, Tuesday ... Saturday) - use 7 bits
  byte hourBegin;  // 0 ... 23
  byte minBegin;   // 0 ... 59
  byte hourEnd;
  byte minEnd;
  byte randomMin;  // randomly add / decrease begin and end times of xx minutes
} TimeSlot ;
TimeSlot Slot[];

Puis je l'initie dans un fichier cpp (pas dans le ino, car c'est un paramètre que j'aimerais rendre facilement accessible à un utilisateur), mais le compilateur ne la connait pas :
Quote
C:\Users\irena\Documents\Arduino\Client_simple_final\Client_simple_final.ino: In function 'void verifyDevices(int)':

Client_simple_final:149: error: 'Slot' was not declared in this scope

     byte device = Slot.device;
Ce qui est bizarre c'est que maligne 149 n'est pas comme le dit le compilateurn c'est en vérité
Code: [Select]
byte device = Slot[i].device;

Si vous pouvez m'aider à corriger ça, ce sera déjà un grand pas en avant pour moi.

A part ça, j'utilise des #define pour définir des constantes (tailles de tableaux par exemple ou numéro de pin). J'ai remarqué que si je les mets dans le .h elles ne sont pas connues du programme alors que si je les mets dans le ino ça marche. C'est normal ? Peut-on faire autrement ? Mon but est de simplifier le ino au maximum...

Pour moi, l'idéal (mais peut-on atteindre l'idéal dans notre monde ? C'est le sujet de philo du bac 2019, je relève les copies dans 4 heures :) ) serait d'avoir un fichier spécifique (extension .h ou autre) dans lequel je mettrais tous les paramètres utilisateur : ssid, password, nombre de prises, nom des prises, définition du contenu de mon tableau de structure, etc. Est-ce possible ?

Merci de votre aide.
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

supercc

Bonjour lesept,

cela me ferai plaisir de t'aider. Fait peut-être un zip de ton projet et je le compile chez moi.


supercc


Et à ta question la réponse est oui ;-). C'est bien que tu passes à la compilation modulaire, c'est comme cela que le noyau Linux est compilé alors crois moi ce n'est pas quelques malheureux fichiers qui vont poser problèmes ;-)

lesept

A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

J-M-L

#4
May 26, 2019, 10:36 am Last Edit: May 26, 2019, 10:51 am by J-M-L
Salut @lesept, éventuellement jetez un oeil sur un petit exemple simple d'organisation de code que j'avais posté dans une discussion récente

ça devrait vous donner des idées sur la structuration.

il faut aussi bien comprendre la différence entre "déclaration" et "définition".

Déclarer une variable ou une fonction, c'est dire au compilateur "je vais utiliser ce truc, voilà son type ou la forme que ça prend. Tu ne le connais pas encore mais t'en fais pas fais moi confiance ça existe par ailleurs". Le compilateur alors vous fait confiance et remet à plus tard (au link) la mise ensemble des différents bouts. On fait cela en utilisant le mot clé extern pour dire  que ce sera défini ailleurs

Définir une fonction ou une variable, c'est vraiment allouer la mémoire / le code qui qui est nécessaire à son usage. Bien sûr on ne peut avoir qu'une seule définition.


par exemple dans votre code, je vois dans client.h
Code: [Select]
TimeSlot Slot[];On est bien d'accord que ça veut dire que vous demandez de définir la variable de type tableau Slot mais vous ne donnez pas la taille. et ensuite dans parameters.cpp vous mettez à nouveau une définition mais ce coup ci correcte.
Code: [Select]
TimeSlot Slot[] = {
  /* Salon, every days, from 7:10 to 8:00, no random */
  {Salon, 0b1111111, 7, 10, 8, 0, 0},
  /* Salon, saturday & sunday, from 21:45 to 23:15, no random */
  {Salon, 0b1000001, 21, 45, 23, 15, 0},
  /* Salon, saturday & sunday, from 23:45 to 00:15, no random */
  {Salon, 0b1000001, 23, 45, 0, 15, 0},
  /* Salon, week days, from 22:30 to 23:55, no random */
  {Salon, 0b0111110, 22, 30, 23, 55, 0},
  /* Salon, -MTW---, from 00:15 to 01:05, no random */
  {Salon, 0b0111000, 0, 15, 1, 5, 0},
  /*
    Cuisine */
  {Cuisine, 0b1111111, 6, 55, 7, 40, 0},
  {Cuisine, 0b1000001, 19, 55, 20, 15, 0},
  {Cuisine, 0b0111110, 19, 30, 21, 10, 0}
};
Le compilo va en perdre son latin... Dans le client.h il faudrait avoir extern pour que ce ne soit qu'une déclaration, la définition (unique) se faisant dans parameters.cpp (ce qui n'est pas terrible en soi - autant le mettre en début de .ino)
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

supercc

Je t'en prie, et puis j'espère ne pas avoir vendu la peau de l'ours avant de l'avoir tué ;-)

Bon sinon je suis en train de virer le corps de toutes tes fonctions histoire de voir plus clair car il y a du code ;-)

lesept

Merci J-M-L, je me souvenais de cette conversation mais impossible de remettre la souris dessus...
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

J-M-L

Merci J-M-L, je me souvenais de cette conversation mais impossible de remettre la souris dessus...
j'ai rajouté un peu d'explications ci dessus
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

lesept

#8
May 26, 2019, 10:57 am Last Edit: May 26, 2019, 10:58 am by lesept
Oui, merci
En parcourant ton post, j'ai remarqué que j'avais oublié le extern devant la déclaration de Slot : j'essaye donc avec
Code: [Select]
typedef struct {
  byte device;
  byte days : 7;  // binary 0bSMTWTFS (Sunday, Monday, Tuesday ... Saturday) - use 7 bits
  byte hourBegin;  // 0 ... 23
  byte minBegin;   // 0 ... 59
  byte hourEnd;
  byte minEnd;
  byte randomMin;  // randomly add / decrease begin and end times of xx minutes
} TimeSlot ;
extern TimeSlot Slot[];
mais j'ai toujours les mêmes erreurs de compilation...

Fidèle à ma devise, j'ai aussi essayé de mettre extern devant le typedef : pas mieux.

Enfin j'ai ajouté extern devant chaque prototype ... pareil. Je songe à me mettre au tricot...
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

kamill

Bonjour,

client_h semble défini, ce qui empêche d'inclure client.h

Au lieu d'utiliser un #define pour empêcher plusieurs inclusions tu peux utiliser
Code: [Select]
#pragma once

supercc

Je continu a décrire ce que je fais, la démarche peut-être critiquable, et surtout moins efficace que d'autres ;-)

Là je suis un peu embêter avec des points h externes que je ne possède pas, rien de grave, je vire :

#include <WebServer.h>, #include <WebServer.h>, ...

Entre temps, j'ai je pense compris ton découpage et ce qu'on appelle le graphe de dépendance entre tes fichiers (qui à besoin de qui). Ici client .h est central.

lesept

OK, j'avance aussi de mon côté.

@Kamill : merci, le pragma corrige les problèmes des prototypes.

J'ai déplacé la déclaration de l'écran dans le fichier ino avec la bibli u8g2lib.
Il me reste juste une erreur dans le setup :
Quote
Client_simple_final:63: error: invalid application of 'sizeof' to incomplete type 'TimeSlot []'

   numberOfSlots = sizeof(Slot) / sizeof(TimeSlot);
J'ai déclaré ça dans le fichier .h :
Code: [Select]
typedef struct {
  byte device;
  byte days : 7;  // binary 0bSMTWTFS (Sunday, Monday, Tuesday ... Saturday) - use 7 bits
  byte hourBegin;  // 0 ... 23
  byte minBegin;   // 0 ... 59
  byte hourEnd;
  byte minEnd;
  byte randomMin;  // randomly add / decrease begin and end times of xx minutes
} TimeSlot ;
extern TimeSlot Slot[];
et j'ai bien l'include au début du ino :
Code: [Select]
#include "client.h"
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

lesept

@supercc : c'est vrai que je compile pour un ESP32, d'où ces bibliothèques exotiques...
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

J-M-L

Quote
Fidèle à ma devise, j'ai aussi essayé de mettre extern devant le typedef : pas mieux.
Ben non, votre type il faut le définir :)
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

supercc

Je crois que tu as un petit problème avec :
enum state {OFF, ON, UNPLUG, TIMEOUT};
enum state deviceState[deviceNumber];

Le 2nd enum est de trop.

Ensuite j'arrive à un vrai problème de compilation modulaire.

./Client/client.h:31:1: error: 'state' does not name a type
state deviceState[deviceNumber];
^

client.h ne connaît pas l'enum state et c'est normal elle est définie dans le .ino

Pour passer ce cap je déplace ta definition de l'enum dans client.h. Le .ino la verra puisqu'il inclut le .h

Quote
@supercc : c'est vrai que je compile pour un ESP32, d'où ces bibliothèques exotiques...
Aucun soucis, se suis passé sur compilateur esp8266 pour ne pas avoir a virer le wifi mais l'écran je n'ai envie de télécharger les bibliothèques (et surtout a ce stade cela ne sert à rien). standards.

Go Up