oui un zip. j'essayerais de jeter un oeil demain mais j'ai de la famille qui vient déjeuner.
Version imprimable
pour vous situer le problème il s'agit de la conversion de 3 variables ( je vous avez déjà solliciter sur ce problème dans un post précédent en Juillet)
- affichdate
- affichHeure
- affichalarme
pour mémoire ces 3 variables sont créés avec désormais dans 3 fonctions distincts
par ex pour affichdate sous la forme de variable "static" dans le sous-programme "ephemeride.cpp"
ouCode:
1
2
3
4
5
6
7
8
9
10
11 //######################################################################################### void afficherDate(int jour, int mois, int annee) { char texte[20]; static String(affichdate); // <====================== sprintf(texte, "%02d/%02d/%04d", jour, mois, annee); afficherTexte(texte); affichdate = " Date : "; affichdate += texte; }
Code:
1
2
3
4
5
6
7
8
9
10
11 //######################################################################################### void afficherHeure(int heure, int minute, int seconde) { char texte[22]; static String(affichHeure); // <========================= sprintf(texte, "%02d:%02d:%02d", heure, minute, seconde); afficherTexte(texte); affichHeure = " Heure UTC: "; affichHeure += texte; }
ensuite ces variables sont utilisées pour l'affichage dans une autre fonction mais dans un autre sous-programme : "affichage.cpp" par ex :
je précise que le programme .ino mais sans ces sous-programme se compile normalement sans erreur mais dès que je crée les sous-programme , j'obtiens ceci :Code:
1
2
3
4
5
6 //######################################################################################### void Draw_Heading_Section() { u8g2Fonts.setFont(u8g2_font_helvB08_tf); drawString(2, 1, affichdate+affichHeure, LEFT); }
Vous l'aurez compris que mon problème se situe sur la "portance" des variables , je n'arrive à en comprendre le principe , tout en ayant pourtant pris soinCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 n file included from C:\Users\Utilisateur\Documents\Arduino\test_char\ephemeride.cpp:3: C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.h:16:31: error: 'String' has not been declared 16 | void drawString(int x, int y, String text, alignmentType alignment); | ^~~~~~ In file included from C:\Users\Utilisateur\Documents\Arduino\test_char\DS3231.cpp:4: C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.h:16:31: error: 'String' has not been declared 16 | void drawString(int x, int y, String text, alignmentType alignment); | ^~~~~~ C:\Users\Utilisateur\Documents\Arduino\test_char\test_char.ino: In function 'void setup()': C:\Users\Utilisateur\Documents\Arduino\test_char\test_char.ino:27:4: error: 'StartTime' was not declared in this scope; did you mean 'strptime'? 27 | StartTime = millis(); | ^~~~~~~~~ | strptime In file included from C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.cpp:3: C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.h:16:31: error: 'String' has not been declared 16 | void drawString(int x, int y, String text, alignmentType alignment); | ^~~~~~ C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.cpp: In function 'void DisplayAstronomySection(int, int)': C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.cpp:51:24: error: 'affichalarme' was not declared in this scope 51 | display.display(false); // Full screen update mode | ^~~~~~~~~~~~ C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.cpp: In function 'void Draw_Heading_Section()': C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.cpp:58:20: error: 'affichdate' was not declared in this scope; did you mean 'afficherDate'? 58 | } | ^ | afficherDate C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.cpp:58:31: error: 'affichHeure' was not declared in this scope; did you mean 'afficherHeure'? 58 | } | ^ | afficherHeure exit status 1 Compilation error: 'String' has not been declared
de faire apparaitre dans chaque sous-programme les "include" concernés
merci par avance
si je prends ce code par exempleCitation:
pour mémoire ces 3 variables sont créés avec désormais dans 3 fonctions distincts
La syntaxe correcte estCode:
1
2
3
4
5
6
7
8
9
10 void afficherDate(int jour, int mois, int annee) { char texte[20]; static String(affichdate); // <====================== sprintf(texte, "%02d/%02d/%04d", jour, mois, annee); afficherTexte(texte); affichdate = " Date : "; affichdate += texte; }
Les parenthèses dans String(affichdate) sont interprétées comme une tentative d’appel de constructeur, ce qui n’a pas de sens ici pour une déclaration de variableCode:static String affichdate;
donc il faut mettre (idem pour l'heure)
ensuite vient le souci de rendre visible la variable affichdate à l'extérieur de la fonction et du module, et c'est là que ça se gâte : Une variable déclarée avec static à l’intérieur d’une fonction n’est visible que dans cette fonction. Elle conserve sa valeur entre les appels, mais elle reste locale.Code:
1
2
3
4
5
6
7
8
9
10
11
12 void afficherDate(int jour, int mois, int annee) { char texte[20]; static String affichdate; sprintf(texte, "%02d/%02d/%04d", jour, mois, annee); afficherTexte(texte); affichdate = " Date : "; affichdate += texte; }
Si vous souhaitez qu’une autre fonction y accède, il faut la définir en dehors de toute fonction, c’est-à-dire au niveau global de ce fichier, sans le mot-clé static (En C++, le mot clé `static` réduit la portée de la variable à l’unité de compilation (en gros le fichier) dans le cas d’une variable globale, ou à la fonction dans laquelle elle est déclarée pour une variable locale.), puis la déclarer dans le .h (ou ailleurs) avec extern.
Par exemple dans ephemeride.cpp, au début vous définissez
;Code:String affichdate
Et dans ephemeride.h :
Tout fichier qui fera include de ephemeride.h connaîtra alors la variable affichdate. Il en va de même pour affichHeure. et autres variables que vous voulez exporter.Code:extern String affichdate;
Cela dit, ce n'est pas beau ! :)
par exemple dans
Vous utilisez ces deux variables dans cette fonction mais cette fonction en elle même ne garantit pas que vous ayez mis à jour affichdate et affichHeure. ça va afficher ce qu'il y a dedans qui date potentiellement d'un vieil appel aux fonctions. C'est source d'erreurs.Code:
1
2
3
4 void Draw_Heading_Section() { u8g2Fonts.setFont(u8g2_font_helvB08_tf); drawString(2, 1, affichdate+affichHeure, LEFT); }
il faudrait restructurer un peu tout cela pour obtenir de la cohérence. J'ai déjà dû vous le dire par le passé, ce n'est pas une bonne pratique que de dépendre d'une variable stockée quelque par ailleurs et on ne sait pas trop comment elle évolue. On utilise généralement des paramètres pour passer du contenu et on laisse à l'appelant la décision du stockage de cette variable et combien de temps elle va être conservée.
Vous pourriez par exemple avoir la fonction obetnirDate qui retourne la Date bien formatée (faire idem pour obtenirHeure)
La fonction vous retourne une String (même si je n'aime pas trop les Strings).Code:
1
2
3
4
5
6
7
8
9
10 String obetnirDate(int jour, int mois, int annee) { char texte[20]; snprintf(texte, sizeof texte, "%02d/%02d/%04d", jour, mois, annee); String affichdate = " Date : "; affichdate += texte; afficherTexte(texte); return affichdate; }
De même, plutôt que de cacher dans la fonction Draw_Heading_Section() l'accès aux variables, la fonction pourrait prendre en paramètre le texte à afficher
et dans votre setup() vous feriezCode:
1
2
3
4 void Draw_Heading_Section(String & heading) { u8g2Fonts.setFont(u8g2_font_helvB08_tf); drawString(2, 1, heading, LEFT); }
ce serait plus propre.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13void setup() { Serial.begin(115200); InitialiseDisplay(); Init_RTC(); String date = obetnirDate(jour,mois,annee); Serial.println("\n-----------"); String heure obtenirHeure(heure,minute,seconde); Serial.println("\n-----------"); Infos_RTC(); String enTete = date + heure; Draw_Heading_Section(enTete); ...
Merci Jay M
Mille pardons pour le "dérangement dominical" ;)
Je digère tout çà
pascal
Pas de souci - le poulet était au four :)
Re... Jay M
je suis désolé mais je vais devoir renoncer à créer des sous-programmes
je n'arrive pas à compiler suite à des problèmes récurrents de déclaration de variables comme par ex ici :
des erreurs qui n'apparaissaient pas dans le croquis initial mais maintenant du fait de la création des sous-programmes et ce que je ne m'explique pasCode:
1
2
3
4 In file included from C:\Users\Utilisateur\Documents\Arduino\test_char\ephemeride.cpp:3: C:\Users\Utilisateur\Documents\Arduino\test_char\affichage.h:16:31: error: 'String' has not been declared 16 | void drawString(int x, int y, String text, alignmentType alignment);
j'ai pourtant cherché à respecter les déclarations mais là ....
je remets en copie ce que j'ai modifié du croquis précédent en respectant les recommandations mais
Ne renoncez pas, vous allez y arriver
Le compilateur vous dit qu'il ne connait pas le type String. C'est normal, ce n'est pas du C++ standard.
==> il faut faire comme pour les autres bibliothèques dont vous utilisez les fonctions, dans les .h de vos .cpp, pensez à ajouter
qui doit apparaître AVANT que vous n'utilisiez un des éléments spécifiques au monde Arduino comme la classe String (ou Serial, etc).Code:#include <Arduino.h>
Dans votre cas précis, vous avez dans votre affichage.h
il faut donc qu'au moment où le compilateur lit cela il connaisse la classe String mais dans affichage.cpp vous faitesCode:void drawString(int x, int y, String text, alignmentType alignment);
vous avez l'inclusion de Arduino.h après affichage.h et donc au moment de lire affichage.h, il ne sait pas ce qu'est une String.Code:
1
2 #include "affichage.h" #include <Arduino.h>
Sinon dans affichage.h vous avez aussi
==> il manque le type (j'avais proposé de passer le paramètre qui est de type String par référence (d'ou le &) car ça évite de dupliquer ce paramètre)Code:
1
2 void Draw_Heading_Section(enTete);
pour ce prototype de fonction il faut aussi que le compilateur sache ce qu'est une StringCode:
1
2 void Draw_Heading_Section(String& enTete);
==> le plus simple, mettez #include <Arduino.h> dans votre .h
Bonjour Jay M
j'espère que je ne vous ai pas obligé à manger votre poulet froid , encore désolé ! ;)
Bon, çà fonctionne mieux maintenant grâce à vous
pour éviter à l'avenir mes questions redondantes
j'ai voulu faire un petit mémo des différentes configurations de sous-programmes et de leurs fonctions que vous m'avez décrites
j'aurai voulu avoir votre avis ( et d'autres aussi ) pour savoir si j'ai bien tout compris ou si j'ai omis quelques exemples
merci encore
Salut
pas de souci pour le poulet :)
concernant votre petit mémo
le .ino n'a pas besoin d'inclure Arduino.h, l'IDE se charge de le rajouter pour vous
vous avez deux fois fonction_2() - je ne sais pas si vous avez fait exprès. en soi ce n'est pas un souci car il y en a une qui est privée au module Toto et celle qui est exportée provient du module Tata
Pourquoi importez vous Toto.h dans Tata.cpp ? c'est Tata.h qu'il faut importer
vous avez une déclaration extern int variable_2 mais personne ne définit variable_2
Sinon, si un .h contient des trucs spécifiques au monde arduino (byte, String, ...) alors il faut inclure Arduino.h dans le .h aussi par cohérence plutôt que de dépendre du fait que le développeur aura pensé à le mettre dans son cpp.
---
voici un exemple wokwi avec le .ino et Toto et Tata
le .ino appelle une fonction de Toto qui appelle une fonction de Tata
https://wokwi.com/projects/445327592228040705
merci pour les corrections
là, où je n'imprime pas c'est le terme "extern"
par définition ( externe) , j'avais bêtement compris qu'il s'agissait que de variables que l'on importait dans le .cpp
pas celles que l'on traitait dans ce même .cpp
comme par ex :
int toto = 0; => cette variable ne provient pas d'un autre programme puisqu'elle est créée au sein même de Toto.cpp puis ensuite "exportée" vers Tata.h
où là j'aurai compris l'utilisation du terme "extern toto"
alors que je vois qu'elle est déclarée "extern int toto" dans Toto.h et pas dans Tata.h ?
erare humanum est ....Citation:
Pourquoi importez vous Toto.h dans Tata.cpp ? c'est Tata.h qu'il faut importer
---------------------------------------------
Même si le programme se compile désormais il me reste cependant un petit bémol lié au fait de la création de ces sous -programmes
à savoir ce pourquoi j'ai fait ce croquis : la date du crépuscule qui ne se met plus à jour
voici ce que je lis sur la console
dans la fonction : programmerAlarme(int annee, int mois, int jour, double heureJulienne)Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 load:0x40080400,len:3480 entry 0x400805b4 RTC = OK <================== Alarme réglée! Lever : 06:03:32 Meridien : 11:23:38 Coucher : 16:43:02 <================== _PowerOn : 94999 _Update_Full : 2131000 _Update_Full : 2130000 _PowerOff : 140000 20/10/2025 ----------- 13:37:50 ----------- _PowerOn : 95000 _Update_Full : 2131000 _PowerOff : 140000 [ Alarme 1: 20 12:00:00, Mode: Date ] <============== _PowerOn : 95000 _Update_Full : 2131000 _PowerOff : 140000
je mets normalement à jour rtc_setAlarm1Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//######################################################################################### void programmerAlarme(int annee, int mois, int jour, double heureJulienne) { // on calcule l'heure, minute et seconde comme dans le code de bricoleau // dans sa fonction afficherHeure() en modifiant éventuellement le jour int h, m, s; double d = heureJulienne + 0.5; if (d < 0.0) { jour -= 1; d += 1.0; } else if (d >= 1.0) { jour += 1; d -= 1.0; } h = d * 24.0; d -= (double) h / 24.0; m = d * 1440.0; d -= (double) m / 1440.0; s = d * 86400.0 + 0.5; DateTime alarme(annee, mois, jour, h, m, s); // puis on fabrique un DateTime avec ces infos <================ rtc.setAlarm1(alarme, DS3231_A1_Date); // qu'on utilise pour armer l'alarme1 du DS3231 en mode date+heure exacte //rtc.enableAlarm(DS3231_ALARM_1); }
puis dans la fonction : void checkDate()
je viens mettre à jour la variable : alarm1Time mais çà ne semble pas fonctionner correctement puisque l'on peut voir que la date du "coucher" ne se remet pas à jour alors que celle-ci apparait plus haut
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39DateTime alarm1Time = rtc.getAlarm1(); <============================= Ds3231Alarm1Mode alarm1mode = rtc.getAlarm1Mode(); char alarm1Date[12] = "DD hh:mm:ss"; alarm1Time.toString(alarm1Date); Serial.print("[ Alarme 1: "); Serial.print(alarm1Date); Serial.print(", Mode: "); switch (alarm1mode) { case DS3231_A1_PerSecond: Serial.println("PerSecond ]"); affichmode = "Par Seconde"; break; case DS3231_A1_Second: Serial.println("Second ]"); affichmode = "Seconde"; break; case DS3231_A1_Minute: Serial.println("Minute ]"); affichmode = "Minute"; break; case DS3231_A1_Hour: Serial.println("Hour ]"); affichmode = "Heure"; break; case DS3231_A1_Date: Serial.println("Date ]"); affichmode = "Date"; break; case DS3231_A1_Day: Serial.println("Day ]"); affichmode = "Jour"; break; } affichalarme = "Crepuscule le: "; affichalarme += alarm1Date; affichalarme += " - En Mode: "; affichalarme += affichmode; affichalarme += " ";
le alarm1Time utilisé dans programmerAlarme n'est initié qu'au début de DS3231.cpp (c'est ici une variable globale)
Dans ta fonction checkDate, tu créés une variable locale aklarm1Time, qui est donc différente de la variable locale, ce qui explique que ton alarme n'est pas mise à jour. Il suffirait de supprimer le "typage" de la variable dans checkDate, pour que ce soit ta variable globale qui soit utilisée.
Si ça vous aide, Voyez "extern" comme juste un mot clé pour dire au compilateur de ne pas allouer la mémoire et juste l'informer sur le type et le nom d'une variable qui existe quelque part (même dans le même fichier).