[ Résolut ] Comment faire un Sketch scindé en plusieur blocs

Bonsoir a tous,

Après avoir vu le sketch de Lacuzon sur sa serre,

Serre de Lacuzon

j’ai été tres intéressé par son organisation de fichiers scindé en tronçons
Il m’a expliqué comme il a fait, mais je n’y arrive pas… comme je ne veux pas polluer inutilement son post j’en ouvre un nouveau ici

Pouvez-vous m’expliquer comment on fait? J’ai pris un sketch qui fonctionne, coupé en plusieurs tronçons
Enregistré dans le même dossier avec un nom du genre A_ initialise.ino puis B_setup.ino puis C_fonction.ino et enfin D_loop.ino.
Déjà pour créer ces fichiers j’ai dû truander car ça plantait a l’enregistrement , et lorsque j’essaye de compiler évidemment ça plante car il ne trouve rien.

Merci

Arzou: Déjà pour créer ces fichiers jai dû truander car ca plantait a l'enregistrement

Explique parce que l'IDE propose de créer des nouveaux fichiers dans le projet donc je ne vois pas pourquoi tu as dû truander

Je crois comprendre la question.
Rappel : fonctionnement de l’IDE :
Quand tu écris un fichier ino ce n’est pas ce fichier qui sera transmis au compilateur.
L’IDE ajoute ce qui manque au fichier ino pour le transformer en véritable fichier cpp et c’est ce fichier cpp qui sera transmis au compilateur.

Si tu parles d’organiser ton projet en plusieurs fichiers séparés il y a deux façons différentes de procéder avec l’IDE. C’est vrai que travailler avec des fichiers de plusieurs centaines de lignes est pénible.

Méthode IDE Arduino
Tu peux créer autant de fichier ino que tu veux dans le répertoire du fichier ino principal.
Dans ce cas l’IDE va simplement concaténer tous les différents fichiers *.ino dans le premier fichier ino qui a été créé : c’est peut être à ce niveau que tu as mal géré la création des différents fichiers.
Cet unique fichier ino, qui pourra faire des centaines de lignes, sera traité classiquement comme tout fichier ino.

Méthode classique fichiers C/Cpp et fichier h:
Cela revient à appliquer les règles du C/Cpp de la programmation en fichiers séparés.
Tu places dans le même répertoire que le fichier ino des fichiers *.h et *.cpp. Il faut relancer l’IDE pour qu’elle les prenne en charge.
Attention l’IDE n’interviendra pas sur ces fichiers qui seront livrés tels quels au compilateur. Il faudra déclarer les variables globales avec le préfixe “extern” et faire la déclaration de toutes les fonctions.
Sans oublier d’ajouter un #include<Arduino.h> si tu utilises les fonctions arduino dans le fichier cpp.
Note : cette inclusion d’Arduino.h est ajoutée systématiquement au fichier ino par l’IDE.
Au besoin suivre un tuto de C/Cpp de programmation en fichiers séparés.

La première méthode apporte “seulement” un confort d’organisation.
C’est déjà pas si mal.

La deuxième méthode demande un peu plus de travail mais en échange elle permet de simplifier la réutilisation de code dans différents projets et il n’y a qu’un tout petit pas à franchir pour transformer ces fichiers h et cpp séparés en véritable bibliothèque.

Avec la deuxième méthode la compilation devrait aussi être plus rapide :
Au lancement de l’IDE à la première compilation tous les fichiers sont systématiquement compilés, y compris ceux qui contiennent les fonctions arduino comme digitalWrite() et les différentes bibliothèques utilisées.
C’est ce qui explique qu’avec les micro Espressif la première compilation est très longue.
Aux compilations suivantes seuls les fichiers dont le code a été modifié sont recompilés.

C’est exactement ca 68tjs,

Pour l’instant je vais voir la méthode ide.

Comment doit on procéder pour creer ce genre de fichiers?

Je part d’un sketchs comme celui ci par exemple

#include <Servo.h>  // On inclut les bibliothèques du servo-moteur, du capteur ultrason
#include <HCSR04.h>
// define fait une sorte de copier coller
// Attribuer  les Pins du servo-moteur HCSR04.h

#define TRIG_PIN A0 // le Pin A0, sortie analogique
#define ECHO_PIN A1 // le Pin A1, sortie analogique

// Définissez toutes les cartes de connexion au L298N

#define enA 13
#define in1 12
#define in2 11
#define in3 7
#define in4 6
#define enB 5
#define servoPin 2
 
class Motor{
 
int enablePin;
int directionPin1;
int directionPin2;
 
public:
 
//définir les broches des moteurs  

Motor(int ENPin,int dPin1,int dPin2){
enablePin = ENPin;
directionPin1 = dPin1;
directionPin2 = dPin2;
};
 
// Méthode pour faire avancer le moteur de 0 à 255. -1 ~ -255 pour reculer

void Drive(int speed){
if(speed>=0){
digitalWrite(directionPin1, LOW);
digitalWrite(directionPin2, HIGH);
}
else{
digitalWrite(directionPin1, HIGH);
digitalWrite(directionPin2, LOW);
speed = - speed;
}
analogWrite(enablePin, speed);
}
};
 
Motor leftMotor = Motor(enA, in1, in2);
Motor rightMotor = Motor(enB, in3, in4);
Servo myservo; // create servo object to control a servo
 
void motorInitiation(){
pinMode(enA, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);

// Définir la direction et la vitesse initiale


digitalWrite(enA, LOW);
digitalWrite(enB, LOW);
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);
}
 
//Variables--------------------------------------------------------------------------
// Anything over 400 cm (23200 us pulse) is "out of range"
// Tout ce qui dépasse 400 cm (impulsion 23200 us) est "hors de portée

const unsigned int MAX_DIST = 23200;
bool ObsticalAhead = false;
int servoPos = 90;
 
enum Directions { Forward, TurnLeft, TurnRight, TurnAround,Brake};
 
Directions nextStep = Forward;
 
unsigned long t1;
unsigned long t2;
unsigned long pulse_width;
float cm;
float inches;
 
//SETUP--------------------------------------------------------------------------
void setup() {
 
// The Trigger pin will tell the sensor to range find
pinMode(TRIG_PIN, OUTPUT);
digitalWrite(TRIG_PIN, LOW);
 
// Nous utiliserons le moniteur série pour visualiser la sortie du capteur
Serial.begin(9600);
myservo.attach(servoPin);
motorInitiation();
Directions nextStep = Forward;
}
 
void loop() {
 
checkDistance();
checkDirection();
drive();
}
 
void checkDistance(){
 
// Hold the trigger pin high for at least 10 us
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
 
// Attendre l'impulsion sur le Pin d'écho

while ( digitalRead(ECHO_PIN) == 0 );
 
// Mesurer combien de temps le Pin d'écho a été maintenue haute (largeur d'impulsion)
// Remarque: le compteur micros () débordera après ~ 70 min

t1 = micros();
while ( digitalRead(ECHO_PIN) == 1);
t2 = micros();
pulse_width = t2 - t1;
 
// Calculate distance in centimeters and inches. The constants
// are found in the datasheet, and calculated from the assumed speed
//of sound in air at sea level (~340 m/s).
cm = pulse_width / 58.0;
inches = pulse_width / 148.0;
 
// Imprimer les résultats
if ( pulse_width > MAX_DIST ) {
//Serial.println("Out of range");
} else {
//Serial.print(cm);
//Serial.print(" cm \t");
//Serial.print(inches);
//Serial.println(" in");
}
// Attendez au moins 60 ms avant la prochaine mesure
delay(60);
 
if(cm<= 20){
ObsticalAhead = true;
Serial.println("Problem Ahead");
 
}
else{ ObsticalAhead = false;}
 
}
void checkDirection(){
Serial.println("checking direction");
if(ObsticalAhead ==true){
nextStep = Brake;
drive();
myservo.write(180); // dire au servo- moteur d'aller à la position dans la variable 'pos'

delay(400); // attend 15 ms pour que le servo –moteur atteigne la position

checkDistance();
if(ObsticalAhead ==false){  // si le côté gauche est ouvert

nextStep = TurnLeft;
Serial.println("Next step is TurnLeft");
myservo.write(90); // réinitialiser la position du servo

delay(400);
}
else{ // la gauche est bloquée, il faut maintenant regarder à droite

myservo.write(0); // dire au servo-moteur d'aller à la position dans la variable 'pos

delay(800); // attend 15 ms pour que le servo-moteur atteigne la position

checkDistance();
if(ObsticalAhead ==false){ // si le côté droit est ouvert

nextStep = TurnRight;
Serial.println("Next step is TurnRight");
myservo.write(90); // réinitialiser la position du servo-moteur

delay(400);
}
else{ // la droite est également bloquée, il faut faire demi-tour

nextStep = TurnAround;
myservo.write(90);//reset servo position
delay(300);
Serial.println("Next step is TurnAround");
}
}
 
}
else{nextStep = Forward;} // Pas d'obstacle à venir

}
 
void drive(){
switch (nextStep){
case Forward:
leftMotor.Drive(255);
rightMotor.Drive(255);
Serial.println("Forward");
 
break;
case TurnLeft:
leftMotor.Drive(-255);
rightMotor.Drive(255);
Serial.println(" TurnLeft");
delay(400);
 
break;
case TurnRight:
leftMotor.Drive(255);
rightMotor.Drive(-255);
Serial.println(" TurnRight");
delay(400);
break;
case TurnAround:
leftMotor.Drive(255);
rightMotor.Drive(-255);
Serial.println(" TurnAround");
delay(600);
break;
 
case Brake:
leftMotor.Drive(0);
rightMotor.Drive(0);
Serial.println(" stopped");
}
 
}

Comment je doit faire?

Merci

Méthode ide tout en fichier ino : c'est de la découpe de tranches de saussison. Tu créé un nouveau programme Tu fais enregistrer sous : "bidule" L'IDE va créer un répertoire "bidule" dans lequel est enregistré le fichier "bidule.ino" C'est ce fichier "bidule.ino" qui pour ce qui va suivre sera le fichier principal.

Jusque là rien de nouveau, c'est maintenant que cela change. A partir de l'IDE tu sais comment ajouter un ou plusieurs fichers supplémentaires dans le répertoire "bidule". Autre méthode, si tu es aussi patient que moi et que tu ne trouves pas dans la seconde le sous-menu qui va bien, tu créé directement des fichiers texte vides dans le répertoire "bidule" que tu nomme truc.ino et machin.ino Tu relance l'IDE et toute seule comme une grande elle trouvera les fichiers truc.ino et machin.ino.

Au moment de la phase de compilation l'IDE commencera par concaténer (ajouter) les 2 fichiers truc et machin à la fin du fichier principal du projet bidule. Comme au final le code contenu dans truc et dans machin sera ajouté à la fin de bidule tu fait comme tu veux.

Perso j'utiliserai bidule.ino comme une table des matières du programme. J'y mettrai les "servitudes" : déclarations de variables, includes , etc J'y laisserai aussi setup() et loop() Par contre je décomposerai loop (et setup s'il est volumineux) en sous fonctions dont le code serait déplacé dans truc et machin. Loop ne contiendrait quasiment que des appels à des fonctions => toujours l'effet table des matières

Le code des fonctions appelées dans loop (ou dans setup) serait répartis dans les fichiers annexes truc et machin.

Bonjour,

J'ai publié ici recette compilation séparée comment découper proprement un programme en modules .cpp et .h. C'est de loin la meilleure méthode, mais sa mise en oeuvre demande rigueur et minutie.

Il y a aussi (au moins) une troisième méthode, pas académique du tout et un peu (beaucoup ?) cracra, mais qui peut rendre des services : - vous découpez votre programme .ino en blocs, par ex. premier, deuxieme, troisieme, - vous copiez le code contenu dans ces blocs dans autant de .h, ce qui vous donne : premier.h, deuxieme.h, troisieme.h, - dans le .ino, vous remplacez les blocs par des #include "premier.h", #include "deuxieme.h", etc. (avec les guillemets !), - le .ino et les .h doivent être dans le même répertoire. Pas d'ordre alphabétique à respecter.

Je n'ai pas essayé la méthode avec plusieurs .ino.

Bonne bidouille,

MicroQuettas

Comment je doit faire?

Découpe ton projet thématiquement : partie principale, moteur, distance, etc.

Bonjour,

Je vous rejoins, c'est ainsi que j'ai procédé, j'ai fait un fichier loop qui contient très peu de choses si ce n'est l'appel à plusieurs fonctions (idem pour le setup). Puis comme dit hbachetti, j'ai introduit les divers éléments, les moteurs, les capteurs, etc. C'est vraiment très confortable par rapport à trimballer un long fichier car au-delà de la pénibilité de la manipulation de centaines de ligne de code, on court toujours le risque de modifier par inadvertance un coin perdu du fichier sur lequel on n'était pas en train de travailler. Pour retrouver les erreurs c'est la galère.

C'est simple sans doute aussi un peu primaire mais ça marche.

J'ai fait une tentative hier avec SublimeText et platformio. Il me semblait que ça marchait aussi quand j'ai créé mon programme. Hier ma découpe n'a pas fonctionné. Peut-être que cette facilité est propre à l'IDE arduino.

Dans ce cas la méthode "propre" de décomposition en *.h et *.cpp est plus universelle car elle, elle ne dépendra pas de l'IDE mise en œuvre.

Mais bon, pour les petits (tout petits) autodidacte du C, Cpp, c'est quand même un peu plus compliqué à utiliser.

68tjs: A partir de l'IDE tu sais comment ajouter un ou plusieurs fichers supplémentaires dans le répertoire "bidule". Autre méthode, si tu es aussi patient que moi et que tu ne trouves pas dans la seconde le sous-menu qui va bien

Complètement à droite, dans la barre d'onglet au dessus de la fenêtre d'édition un onglet avec un triangle pointe en bas. Tu cliques et tu choisis "Nouvel onglet"

J'ai fait une tentative hier avec SublimeText et platformio. Il me semblait que ça marchait aussi quand j'ai créé mon programme. Hier ma découpe n'a pas fonctionné. Peut-être que cette facilité est propre à l'IDE arduino.

Oui, PlatformIO permet seulement le découpage .cpp .h, traditionnel, mais aussi plus rigoureux. Un découpage .cpp et .h sera portable sur différents IDE y compris Eclipse. Un découpage .ino multiples marche uniquement sur ARDUINO IDE.

Merci a tous, Merci 68tjs , je viens de tester ta methode et ca marche bien!! , pour l'instant je vais en rester là.

super en tout cas..

fdufnews: Complètement à droite, dans la barre d'onglet au dessus de la fenêtre d'édition un onglet avec un triangle pointe en bas. Tu cliques et tu choisis "Nouvel onglet"

pouarf , ca fait deja 4 versions d' IDE arduino que je met a jour , j ' avais jamais fait gaffe MDR !

ba maintenant , je saurais comment faire des ino a la pelle pour mes futurs sketch , merci .

Bonjour,

confinement oblige et mauvais temps, ça laisse du temps pour bricoler et approfondir nos connaissances :slight_smile:

Donc j’ai tenté de scinder un fichier un peu lourd en *.h et *.cpp.

Deux remarques :

  • d’une part, il n’est pas toujours simple de comprendre ce que l’on doit mettre dans le fichier *.h (pour ceux qui n’ont pas baigné dans le langage C). Pour le fichier *.cpp c’est un peu plus simple. Donc on rame un peu…
  • d’autre part, on se trouve vite avec des erreurs du style “variable untel non définie” alors qu’elle est bien là mais sans doute pas à la bonne place ou pas dans le bon fichier.

Bref, ce n’est pas si immédiat même si comme d’hab, il y a un excellent tuto d’Eskimon.

C’est pourtant simple :

truc.h : macros, structures, classes, externs, prototypes

#ifndef _TRUC_H_        // évite d'inclure plusieurs fois truc.h
#define _TRUC_H_

#define TRUC        1     // macro

extern int bazar;          // variable globale (déclaration externe)

int beginTruc(void);      // prototype fonction

#endif

truc.cpp : variables, fonctions

#include <Arduino.h>      // nécessaire

#include "truc.h"              // chaudement conseillé

int bazar;                         // variable globale
static int state;                 // variable statique

static int staticFunc(void)  // fonction non visible du sketch (absente de truc.h)
{
  bazar = TRUC;
  return 0;
}

int beginTruc(void)           // fonction 
{
  state = 1;
  staticFunc();
}

sketch.ino :

#include "truc.h"            // forcément inclus dans le sketch

void setup()
{
  Serial.begin(115200);
  beginTruc();
}

void loop() {
  if (bazar == TRUC) {
    Serial.println("bazar is OK");
  }
}

Je n’ai pas utilisé de structures ni de classes pour rester simple.
En gros toutes les fonctions du .cpp doivent avoir un prototype dans truc.h
toutes les variables doivent être extern dans truc.h
Les fonctions et variables non utiles au sketch peuvent être absentes de truc.h, comme state ou staticFunc, peuvent être static c’est à dire visibles seulement de truc.cpp.

Un découpage en .h + .cpp peut permettre de changer d’IDE, un découpage en .ino NON.

Attention Hbachetti, c'est toujours simple pour l'enseignant, ce qui compte c'est que ce soit simple pour l'apprenant. Je l'ai vécu maintes fois à mes dépends quand je faisais partie des premiers !

Cela dit je vais creuser la chose avec tes conseils et je vais sans doute finir par comprendre et être convaincu.

Merci pour tes nombreux coups de main

hbachetti: Un découpage en .h + .cpp peut permettre de changer d'IDE, un découpage en .ino NON.

?

Ca veut dire que s'il y a une mise à jour de l'IDE mon sketchs ne marchera plus?

Non, cela veut dire que tu peux utiliser PlatformIO ou Eclipse avec un découpage .cpp .h. Ces environnements ne connaissent pas .ino, en tous cas ils n'effectuent pas les mêmes traitements que l'IDE ARDUINO. Voir les explications de 68tjs #2

La question a été souvent posée avec platformio et la seule réponse est effectivement le découpage en h et cpp.

J'avais eu l'impression qu'avec Sublimetext le découpage en bloc fonctionnait. Après de nouveaux essais, c'est bien sûr faux avec platformio (et le pluggin deviot) mais vrai avec le pluggin arduino qui n'utilise pas platformio. D'ailleurs, je trouve que la compilation est plus rapide avec sublimetext et le pluggin arduino q'avec l'IDE arduino.