Station Météo - ESP32

Utilisant un ESP32. Affichage TFT 480 x 320px, enregistrement sur TFcard.

1 Présentation

Vous connaissez sans doute ma précédente réalisation basée sur un Arduino Mega2560.
Cette fois, la puissance de l'ESP32 et sa mémoire RAM interne beaucoup plus grande permettent un affichage bien plus réactif ainsi qu'une connectique plus simple.
  • Réalisation autour d'un ESP32
  • Affichage sur écran TFT 3.5" 480x320 px
  • Heure ultra précise (par GPS, et transmise par WiFi)
  • Température extérieure
  • Taux d'humidité extérieure
  • Pression atmosphérique (une variable est à ajuster dans le programme en fonction de l'altitude d'utilisation afin d'obtenir une grande précision)
  • Tracé des courbes en temps réel
  • Enregistrement des mesures sur carte mémoire micro SDcard pour visualisation sur l'ordi avec un soft en Qt5
  • Envoi d'infos en temps réel par la liaison USB (à capter avec un terminal série, par exempe "CuteCom" sous Linux)
  • Fonction de capture d'écran et enregistrement de l'image obtenue sur la SDcard (utilisable pour d'autres projets le cas échéant...)
Un switch câblé directement sur l'afficheur (après coupure d'une piste) permet d'éteindre totalement le rétroéclairage de l'afficheur TFT. (C'est utile si l'appareil est placé dans une chambre à coucher, par exemple, évitant la gêne occasionnée la nuit par la lumière de l'afficheur).

2 en vidéo...


3 L'afficheur 480x320px de 3.5

C'est un TFT LCD 480 x 320 px que l'on pilote ici par son interface parallèle, d'où la très grande rapidité. Cet afficheur existe aussi avec une carte rouge, qui fonctionne très bien avec la library TFT_eSPI. Toutefois il faut l'utiliser avec le driver ILI9488 et non pas le ILI9486 (sinon affiche des couleurs bizarres).

Donc je récapitule : voici les paramètres à inscrire dans le fichier de configuration "User_Setup.h" situé dans le dossier "/home/login/Arduino/libraries/TFT_eSPI/" :

#define ESP32_PARALLEL
#define ILI9486_DRIVER //pour les afficheurs à CI rouge -> ILI9488_DRIVER

4 Le schéma

Le montage se compose d'un minimum de composants :
  • Une carte "ESP32 dev module" 30 pins
  • Un afficheur TFT 480x320 px
  • Un module BMP280 (capteur de pression atmosphérique)
  • Un récepteur 433MHz dédié à la sonde externe de température + humidité externe récupérée
  • Les résistances qui figurent sur le schéma ne servent en fait à rien (tirage à VDD sur certaines entrées de l'ESP32 qui en sont dépourvues)
Attention : les pastilles P1..P7 ne sont pas disposées dans le même ordre sur le shéma et sur le board (ce n'est pas une erreur, et pour connecter les périphériques il faut se fier à la sérigraphie sur le board).

Tiens, le schéma ne comporte pas de module horaire... et pourtant l'heure est précise à la seconde près !
En fait l'heure est reçue par transmission WiFi (les cartes ESP32 savent utiliser la WiFi !!!). Cette réalisation est un client WiFi. Et le serveur ? Le serveur est un autre montage perso décrit sur ce site, basé lui aussi sur un ESP32 couplé à un module récepteur GPS. Plus de dérive horaire !


5 Le circuit imprimé

Le schéma et le circuit imprimé ont été tracés avec le logiciel libre open source Kicad sous Linux. Vous trouverez au bas de cet article au paragraphe "Documents" l’ensemble de tous les fichiers Kicad concernant ce projet.

Ce circuit imprimé qui permet de relier un ESP32 à un affichage LCD 480x320 m'a actuellement servi à l'identique pour plusieurs réalisations (le PFD et ses écrans annexes, l'horizon artificiel, l'horloge LCD cliente WiFi...) J'en ai d'ailleurs fait réaliser plusieurs exemplaires par JLPCB (5 cartes pour 6€, mais c'était avant que l'on finance la troisième guerre mondiale).

6 La sonde extérieure

Il s'agit d'une sonde récupérée.

Le protocole pour l'envoi des info par radio sur la fréquence 433MHz m'étant au départ inconnu,
j'ai du le décrypter en premier par visualisation à l'oscilloscope puis par étude des signaux logiques.

Il s'avère que ce n'est pas un signal "standard"
(la durée des impulsion positives est constante,
ce n'est pas un codage Manchester comme je l'ai un moment pensé).

Je vous le décrirai en détail.

7 Le récepteur radio 433MHz


La puce est un PT4303 en boitier cms 14 pins SOP dont le datasheet est facile à trouver. Il est dit :
The PT4303 is a low power superheterodyne OOK/ASK receiver for the 315/433.92 MHz frequency bands.

FEATURES :
- Ultra-low power consumption: 2.7 mA for full operation (315 MHz)
- Few external components
- Excellent sensitivity on the order of –110 dBm
(peak ASK signal level at 315 MHz)
- 2.4 V to 5.5 V supply voltage range
- 250 MHz to 500 MHz frequency range
- Data rate up to 10 Kb/s

On peut donc l'alimenter en 3.3V
Ce point est important : Le signal de sortie est ainsi compatible avec la tension max applicable sur les entrées de l'ESP32.

8 Le capteur de pression atmosphérique

C'est un classique BMP280 d'une grande précision concernant la pression (il peut servir d'altimètre connaissant le QNH...)


Le BMP280 dispose de deux types de transmissions : I2C et SCI.
Ici nous utilisons le bus I2C.

9 Signaux envoyés par la sonde externe :

De quoi rester perplexe n'est-ce pas ?

10 Zoom sur le signal

Seules les durées à l'état bas varient, codant pour "0" ou "1"
Voir la fonction :
"void analyse_signal_primaire()"
dans le code source.

J'ai procédé de la manière suivante : Dans un premier temps j'ai observé le signal brut à l'oscillo. il est vite apparu que les tops ont tous la même durée. Puis j'ai mesuré par la fonction "analyse_signal_primaire()" que quatre durées différentes sont présentes dans l'ensemble d'une longue salve : Voici ces 4 durées caractéristiques 490us ; 95us ; 1940us; 3700us
- 490us c'est la durée des tops HIGHT (tous identiques)
- 950us ; 1940us ; 3700us sont les différentes durées à l'état LOW qui séparent les tops ()

appelons ces durées par une lettre :
A -> 490us
B -> 950us
C -> 1940us
D -> 3700us

J'ai alors étudié le séquencement de ces lettres :

résultat :
AB AC AB AB AC AB AC AC AC AB AB AB AB AB AB AB AC AC AC

On voit que les motifs qui apparaissent sont AB et AC et rien d'autre (pas de AA ni de BB par exemple).
J'en ai déduit que le motif AB devait coder pour "0" logique, et le motif AC pour "1" logique.

ABABACACACACABACACABABABABABABABACACACACACACACACACACACACABABACABACABABACA
001111011000000011111111111100101001



Je vous invite à suivre la suite du raisonnement en lisant le commentaire qui figure dans la fonction :
void decodeBuffer()

ainsi que les fonctions :
void lit_temperature()
et
void lit_humidite()

11 ET si vous n'avez pas le même capteur ?

Dans ce cas plusieurs possibilités:
  • soit trouver la doc de votre capteur et son protocole sur Internet
  • soit suivre une procédure similaire à la mienne afin de piger le truc...
  • ou encore fabriquer un capteur basé sur un ESP32, avec serveur WiFi. Faudra voir l'autonomie batterie et le mode sleep. Pb: serveur WiFi désactivé en mode veille...
Après quoi il vous faudra bien évidemment modifier le programme en conséquence.

12 Saisie d'écran de l'afficheur LCD TFT 480x320

Affichages :
  • Date et heure
  • Température extérieure
  • Humidité extérieure
  • Pression atmosphérique. (une variable est à ajuster dans le programme en fonction de l'altitude d'utilisation afin d'obtenir une grande précision. Voir la fonction "void acqui_Pression()"
  • "Scope" traçant les courbes de température, de pression et d’humidité pour les 36 dernières heures
  • Phase de la Lune (image) et "age" de la Lune
  • Nombre de salves reçues de la sonde externe
  • réception des signaux horaires GPS transmis par WiFi

13 Programme source en C++

CODE SOURCE en C++
  1. /*
  2. Station_meteo2
  3.  
  4. pour ESP32 Wroom + afficheur 3.5" TFT 480x320
  5.  
  6. par Silicium628
  7.  
  8. Ceci est un client WiFi qui se connecte à tour de rôle à deux serveurs distincts hébergé sur deux autres ESP32
  9. - un serveur de temps réel (lui-même sy,chronisé par GPS)
  10. - un serveur Girouette
  11.  
  12. */
  13.  
  14.  
  15.  
  16.  
  17. /*=====================================================================================================
  18. CONCERNANT L'AFFICHAGE TFT: connexion:
  19.  
  20. (Pensez à configurer le fichier User_Setup.h de la bibliothèque ~/Arduino/libraries/TFT_eSPI/ )
  21.  
  22. les lignes qui suivent ne sont qu'un commentaire pour vous indiquer la config à utiliser
  23. placées ici, elle ne sont pas fonctionnelles
  24. Il FAUT modifier le fichier User_Setup.h installé par le système Arduino dans ~/Arduino/libraries/TFT_eSPI/
  25.  
  26. // ESP32 pins used for the parallel interface TFT
  27. #define TFT_CS 27
  28. #define TFT_DC 14
  29. #define TFT_RST 26
  30.  
  31. #define TFT_WR 12
  32. #define TFT_RD 13
  33.  
  34. #define TFT_D0 16
  35. #define TFT_D1 4
  36. #define TFT_D2 2
  37. #define TFT_D3 22
  38. #define TFT_D4 21
  39. #define TFT_D5 15
  40. #define TFT_D6 25
  41. #define TFT_D7 17
  42. =====================================================================================================*/
  43.  
  44.  
  45. String version="2.0";
  46.  
  47. // commenter la ligne suivante si le module Girouette (avec serveur WiFi) est présent et actif
  48. // #define NO_girouette
  49.  
  50.  
  51. #include <stdint.h>
  52. #include "Station_meteo2.h"
  53. #include "Couleurs_AEC.h"
  54. #include <TFT_eSPI.h> // Hardware-specific library
  55. #include "Free_Fonts.h"
  56.  
  57. #include "FS.h"
  58. #include "SD.h"
  59. //#include "time.h"
  60.  
  61.  
  62. TFT_eSPI TFT480 = TFT_eSPI(); // Configurer le fichier User_Setup.h de la bibliothèque TFT480_eSPI au préalable
  63.  
  64. #include <WiFi.h>
  65. #include <HTTPClient.h>
  66.  
  67. const char* ssid1 = "TPGPS_34";
  68. const char* password1 = "94r6tkJ31";
  69.  
  70. const char* ssid2 = "WIND_srv";
  71. const char* password2 = "62z4exW58";
  72.  
  73. //IP address with URL path
  74. const char* srvName_HR = "http://192.168.4.1/HR";
  75. const char* srvName_ANGLE = "http://192.168.4.1/AGL"; //.4.2 ?
  76.  
  77. #define NOIR 0x0000
  78. #define MARRON 0x9240
  79. #define ROUGE 0xF800
  80. #define ROSE 0xFBDD
  81. #define ORANGE 0xFBC0
  82. #define JAUNE 0xFFE0
  83. #define JAUNE_PALE 0xF7F4
  84. #define VERT 0x07E0
  85. #define VERT_FONCE 0x02E2
  86. #define OLIVE 0x05A3
  87. #define CYAN 0x07FF
  88. #define BLEU_CLAIR 0x455F
  89. #define AZUR 0x1BF9
  90. #define BLEU 0x001F
  91. #define MAGENTA 0xF81F
  92. #define VIOLET1 0x781A
  93. #define VIOLET2 0xECBE
  94. #define GRIS_TRES_CLAIR 0xDEFB
  95. #define GRIS_CLAIR 0xA534
  96. #define GRIS 0x8410
  97. #define GRIS_FONCE 0x5ACB
  98. #define GRIS_TRES_FONCE 0x2124
  99. #define BLANC 0xFFFF
  100.  
  101. uint8_t WiFi_status=0;
  102.  
  103. uint8_t mode = 0; // 0 ou 1
  104.  
  105. String recp_HR = "{}";
  106. String recp_ANGLE = "{}";
  107.  
  108. int angle=8;
  109. int angl_1 =0;
  110. float vent_du=0;
  111. float vent_vers=180;
  112.  
  113. uint32_t bmp_offset = 0;
  114. uint32_t bmp_width;
  115. uint32_t bmp_heigh;
  116.  
  117. uint8_t annee; // attention: =23 pour 2023
  118. uint8_t mois;
  119. uint8_t jour;
  120.  
  121. uint8_t annee_in=0;
  122. uint8_t mois_in=0;
  123. uint8_t jour_in=0;
  124.  
  125. uint8_t heures=0;
  126. uint8_t memo_heures;
  127. uint8_t minutes=0;
  128. uint8_t memo_minutes=0;
  129. uint8_t secondes=0;
  130. uint8_t jour_de_la_semaine;
  131.  
  132. uint8_t heures_in=0; // GPS reçue par WiFi
  133. uint8_t minutes_in=0; // GPS reçue par WiFi
  134. uint8_t secondes_in=0; // GPS reçue par WiFi
  135.  
  136. float age_Lune;
  137.  
  138. String annee_txt;
  139. String mois_txt;
  140. String jour_txt;
  141. String date_txt;
  142. String memo_date_txt="---";
  143.  
  144. uint8_t SDcardOk=0;
  145.  
  146. Etiquette Etiq_1;
  147. Etiquette Etiq_2;
  148. Etiquette Etiq_3;
  149. //Etiquette Etiq_4;
  150.  
  151. LED Led2;
  152. LED Led3;
  153.  
  154. Scope Scope_1;
  155.  
  156. record_meteo lst_records[460]; // voir la définition de la struct 'record_meteo' dans le .h
  157. uint16_t n_record=0;
  158. uint16_t n_record_max=450;
  159.  
  160. uint16_t aff_y;
  161. int16_t gradu_minT, gradu_maxT;
  162. int16_t gradu_minP, gradu_maxP;
  163. uint8_t num_acquisition=4;
  164. uint8_t acqui_valide =1;
  165. uint8_t scope_dat_ok =0;
  166. int16_t T_EXT_lue[10]={0,0,0,0,0,0,0,0,0,0}; //temperature extérieure (la sonde envoie des salves de plusieurs messages identiques)
  167. uint8_t confiance[10]; //indice de confiance = nb d'occurences identiques pour chaque valeur reçue
  168. uint8_t nb_de_salves=6;
  169. int16_t T_EXT_retenue=0; // après comparaison des 5 valeurs reçues
  170. int16_t Tmin, Tmax;
  171. int16_t Tmoy=0; // pour 1 enregistrement, moyenne entre le jour et la nuit
  172. int32_t moyenne_T_sur_toutes; // température mohenne sur l'ensemble des enregistrments affichés
  173. float echelle_T=1; // pour l'affichage des températures sur le Scope
  174. float echelle_P=1; // pour l'affichage des pressions sur le Scope
  175.  
  176. uint16_t Pression_lue=1013;
  177. uint16_t Pmin, Pmax=1013;
  178. uint16_t Pmoy=1013;
  179.  
  180. uint8_t Humid_lue=0; // humidite
  181. uint8_t memo_Humid_lue=0;
  182.  
  183.  
  184. uint8_t GPIO_SDA = 33;
  185. uint8_t GPIO_SCL = 32;
  186.  
  187. const int GPIO_SIGNAL = 34; // (34 = point P5 sur la carte universelle; ATTENTION ne PAS câbler la 10K de pullup externe ici)
  188. bool SIGNAL_etat;
  189. bool memo_SIGNAL_etat;
  190.  
  191. const int GPIO_bouton1 = 36; // attention: le GPIO 36 n'a pas de R de pullup interne, il faut en câbler une (10k) au +3V3
  192. bool bouton1_etat;
  193. bool memo_bouton1_etat;
  194.  
  195.  
  196. uint32_t memo_micros1 = 0;
  197. uint32_t memo_micros2 = 0;
  198. uint32_t temps_ecoule;
  199. uint16_t compteur1s =0;
  200. uint32_t pulseWidth;
  201. uint16_t i_buff;
  202.  
  203. int alerte1=0;
  204.  
  205. uint16_t nb_acqui433=0;
  206. uint16_t nb_wr_SD=0;
  207. uint8_t todo_init_record=0;
  208. uint8_t todo_wr_scope_on_sd=0;
  209. uint8_t todo_wr_ecran_on_sd=0;
  210.  
  211.  
  212. uint16_t couleur_txt = BLANC;
  213. uint16_t couleur_fond = GRIS_TRES_FONCE;
  214. uint16_t couleur_fond_txt = NOIR;
  215.  
  216. const int MAX_BUFFER = 120;
  217. String message;
  218.  
  219. char buffer[MAX_BUFFER];
  220.  
  221. /**
  222. Rappels :
  223. Scope_1.dx = 450
  224. lst_records[500]
  225. **/
  226.  
  227.  
  228. static void smartdelay(unsigned long ms)
  229. {
  230. unsigned long start = millis();
  231. while (millis() - start < ms) {;}
  232. }
  233.  
  234.  
  235. void init_SDcard()
  236. {
  237.  
  238. uint16_t y=10;
  239.  
  240. TFT480.setTextColor(VERT, NOIR);
  241. TFT480.drawString("Init SDcard", 10, y);
  242. y+=20;
  243.  
  244. if(!SD.begin())
  245. {
  246. TFT480.drawString("Card Mount Failed", 0, y);
  247. delay (1000);
  248. TFT480.fillRect(10, 10, 480, 320, NOIR); // efface
  249. return;
  250. }
  251.  
  252.  
  253. uint8_t cardType = SD.cardType();
  254.  
  255. if(cardType == CARD_NONE)
  256. {
  257. TFT480.drawString("No SDcard", 10, y);
  258. delay (1000);
  259. TFT480.fillRect(10, 10, 480, 320, NOIR); // efface
  260. return;
  261. }
  262.  
  263. SDcardOk=1;
  264.  
  265. TFT480.drawString("SDcard Type: ", 10, y);
  266. if(cardType == CARD_SD) {TFT480.drawString("SDSC", 150, y);}
  267. else if(cardType == CARD_SDHC) {TFT480.drawString("SDHC", 150, y);}
  268.  
  269. y+=20;
  270.  
  271. uint32_t cardSize = SD.cardSize() / (1024 * 1024);
  272. String s1=(String)cardSize + " GB";
  273. TFT480.drawString("SDcard size: ", 10, y);
  274. TFT480.drawString(s1, 150, y);
  275.  
  276. //Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  277. //Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
  278.  
  279. delay (1000);
  280. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  281. }
  282.  
  283.  
  284. void connexion_serveur_HR()
  285. {
  286. int compte1=0;
  287. WiFi.persistent(false);
  288. WiFi.begin(ssid1, password1);
  289.  
  290. delay(100);
  291.  
  292. while((WiFi.status() != WL_CONNECTED) && compte1 < 20)
  293. {
  294. delay(100);
  295. compte1++;
  296. //TFT480.print(".");
  297. }
  298. }
  299.  
  300.  
  301. void connexion_serveur_ANGLE()
  302. {
  303. int compte1=0;
  304. WiFi.persistent(false);
  305. WiFi.begin(ssid2, password2);
  306.  
  307. delay(100);
  308.  
  309. while((WiFi.status() != WL_CONNECTED) && compte1 < 20)
  310. {
  311. delay(100);
  312. compte1++;
  313. //TFT480.print(".");
  314. }
  315. if (compte1 >=20)
  316. {
  317. //TFT480.setFreeFont(FF5);
  318. //TFT480.setTextSize(1);
  319. //TFT480.setTextColor(NOIR, BLANC);
  320. //TFT480.drawString("Serveur Girouette HS", 150, 185);
  321. alerte1 =1;
  322. }
  323. else if (alerte1 ==1)
  324. {
  325. //TFT480.fillRect(150, 185, 220, 20, NOIR);
  326. //trace_sur_Scope();
  327. alerte1 =0;
  328. }
  329. }
  330.  
  331.  
  332.  
  333. void httpGetAngle()
  334. {
  335. // Serial.println("envoi req HR");
  336. TFT480.fillCircle(470, 20, 5,BLEU );
  337. delay(200);
  338.  
  339.  
  340. HTTPClient http2;
  341. http2.begin(srvName_ANGLE);
  342.  
  343. int httpResponseCode = http2.GET();
  344.  
  345. if (httpResponseCode>0)
  346. {
  347. recp_ANGLE = http2.getString();
  348. TFT480.fillCircle(470, 20, 5,VERT );
  349. if(recp_ANGLE.substring(0, 6)=="angle=")
  350. {
  351. String s2=recp_ANGLE.substring(6, 8);
  352. angl_1 = s2.toInt();
  353. }
  354. vent_du = 22.5*angl_1;// La direction depuis laquelle souffle le vent, celle de la tête du "coq"" est celle reçue par WiFi
  355. // C'est celle utilisée dans le langage courant ("vent du Nord..." ) et en aéronautique (vent au 359, dix noeuds...)
  356. // mais sur les cartes météo, les petites flèches (vecteurs) pointent dans la direction dans laquelle souffle le vent
  357. // c'est cette dernière que nous affichons ici, par une flèche bleue.
  358. vent_vers = 180 + vent_du;// direction dans laquelle souffle le vent (c.a.d direction de la QUEUE du coq !!)
  359. if (vent_vers > 360) {vent_vers -= 360;}
  360. }
  361. http2.end();
  362.  
  363. }
  364.  
  365.  
  366. void httpGetHeureDate()
  367. {
  368. //Serial.println("httpGetHeureDate()");
  369. //TFT480.fillCircle(465, 122, 5,BLEU );
  370. Led3.setCouleur(BLEU);
  371. delay(200);
  372.  
  373. HTTPClient http2;
  374.  
  375. http2.begin(srvName_HR);
  376.  
  377. int httpResponseCode = http2.GET();
  378.  
  379. if (httpResponseCode>0)
  380. {
  381. recp_HR = http2.getString();
  382. }
  383. http2.end();
  384.  
  385. //TFT480.fillCircle(465, 122, 5,VERT );
  386. Led3.setCouleur(VERT);
  387.  
  388. }
  389.  
  390.  
  391.  
  392. void ajuste_HR()
  393. {
  394. //Serial.println("ajuste_HR()");
  395.  
  396. if(recp_HR.length() == 18)
  397. {
  398.  
  399. WiFi_status =1;
  400. heures_in =(recp_HR.substring(0,2)).toInt();
  401.  
  402.  
  403. minutes_in =(recp_HR.substring(3,5)).toInt();
  404.  
  405. secondes_in =(recp_HR.substring(6,8)).toInt();
  406. //secondes_in++; // pour compenser le temps de traitement
  407.  
  408. if (heures != heures_in) {heures = heures_in;}
  409. if (minutes != minutes_in) {minutes = minutes_in;}
  410. if (secondes != secondes_in) {secondes = secondes_in;}
  411.  
  412. jour_in =(recp_HR.substring(9,11)).toInt();
  413. mois_in =(recp_HR.substring(12,14)).toInt();
  414. annee_in =(recp_HR.substring(15,17)).toInt();
  415.  
  416. if (jour != jour_in) {jour = jour_in;}
  417. if (mois != mois_in) {mois = mois_in;}
  418. if (annee != annee_in) {annee = annee_in;}
  419.  
  420. }
  421. else {WiFi_status=0;}
  422.  
  423. }
  424.  
  425.  
  426.  
  427.  
  428. void calcul_jour_de_la_semaine()
  429. {
  430. // d'après l'Algorithme de Mike Keith
  431. uint16_t d, m, y, z, jds;
  432.  
  433. d=jour;
  434. m=mois;
  435. y=annee;
  436.  
  437. if (m>=3)
  438. {
  439. jds = ( ((23*m)/9) + d + 4 + y + (y/4) - (y/100) + (y/400) - 2 ) % 7;
  440. }
  441. else
  442. {
  443. z = y-1;
  444. jds = ( ((23*m)/9) + d + 4 + y + (z/4) - (z/100) + (z/400) ) % 7;
  445. }
  446. jour_de_la_semaine = jds;
  447. }
  448.  
  449.  
  450. String conv_time(uint8_t t)
  451. {
  452. String r;
  453. r=String(t);
  454. if (t<10) {r="0"+r;}
  455. return r;
  456. }
  457.  
  458.  
  459. void affiche_date()
  460. {
  461. //Serial.println("affiche_date()");
  462. date_txt="";
  463.  
  464. calcul_jour_de_la_semaine();
  465.  
  466. switch (jour_de_la_semaine)
  467. {
  468. case 0: { date_txt+="Dim ";} break;
  469. case 1: { date_txt+="Lun ";} break;
  470. case 2: { date_txt+="Mar ";} break;
  471. case 3: { date_txt+="Mer ";} break;
  472. case 4: { date_txt+="Jeu ";} break;;
  473. case 5: { date_txt+="Ven ";} break;
  474. case 6: { date_txt+="Sam ";} break;
  475. }
  476.  
  477. date_txt += String(conv_time(jour))+" ";
  478.  
  479. switch (mois)
  480. {
  481. case 1: {date_txt+="Janv "; } break;
  482. case 2: {date_txt+="Fev "; } break;
  483. case 3: {date_txt+="Mars "; } break;
  484. case 4: {date_txt+="Avr "; } break;
  485. case 5: {date_txt+="Mai "; } break;
  486. case 6: {date_txt+="Juin "; } break;
  487. case 7: {date_txt+="Juil "; } break;
  488. case 8: {date_txt+="Aout "; } break;
  489. case 9: {date_txt+="Sept "; } break;
  490. case 10: {date_txt+="Oct "; } break;
  491. case 11: {date_txt+="Nov "; } break;
  492. case 12: {date_txt+="Dec "; } break;
  493. }
  494.  
  495. if (annee_in >0) // pour éviter d'afficher une date fantaisiste au départ
  496. {
  497. uint16_t annee_in2 = annee_in + 2000;
  498. annee_txt = (String)annee_in2;
  499.  
  500. date_txt += annee_txt;
  501.  
  502. //memo_date_txt = date_txt;
  503. TFT480.setTextColor(BLANC, NOIR);
  504.  
  505. TFT480.setFreeFont(FF6);
  506. TFT480.setTextSize(1);
  507. TFT480.drawString(date_txt,200,118);
  508. }
  509. }
  510.  
  511.  
  512.  
  513. void affiche_heure()
  514. {
  515. //Serial.println("affiche_heure()");
  516.  
  517. String s1;
  518.  
  519. if (memo_minutes != minutes)
  520. {
  521. memo_minutes = minutes;
  522.  
  523. TFT480.fillRect(200, 85, 105, 26, BLEU); // efface le fond du rectangle dans lequel s'affiche l'heure
  524. s1="";
  525. if(heures<10){s1+="0";}
  526. s1 += String(heures);
  527. s1 += ":";
  528. if(minutes<10){s1+="0";}
  529. s1 += String(minutes);
  530.  
  531. //TFT480.setTextColor(couleur_txt, couleur_fond_txt);
  532.  
  533. TFT480.setFreeFont(FF7);
  534. TFT480.setTextSize(1);
  535.  
  536. TFT480.setTextColor(BLANC);
  537. TFT480.drawString(s1, 200, 85); // Affiche les chiffres de l'heure par ex: 12:34
  538.  
  539. affiche_date();
  540. }
  541.  
  542. TFT480.setTextColor(GRIS, NOIR);
  543.  
  544. TFT480.setFreeFont(FF7);
  545. TFT480.setTextSize(1);
  546. s1="";
  547. if(secondes<10){s1="0";}
  548. s1 += String(secondes);
  549.  
  550. TFT480.fillRect(320, 85, 43, 30, NOIR); // efface
  551. TFT480.drawString(s1, 320, 85); // Affiche les secondes
  552.  
  553. if(WiFi_status == 1) { Led3.setCouleur(VERT);} else { Led3.setCouleur(ROUGE);}
  554.  
  555. if (secondes == 0){ tt_les_1mn(); }
  556. if (((minutes %5) == 0) && (secondes == 10)) { tt_les_5mn(); }
  557. if (((minutes %30) == 0) && (secondes == 20)) { tt_les_30mn(); }
  558. }
  559.  
  560.  
  561. void affi_rayon2(uint16_t x0, uint16_t y0, float r1, float r2, float angle_i, uint16_t couleur_i, bool gras)
  562. {
  563. // trace une portion de rayon de cercle entre les distances r1 et r2 du centre
  564. // angle_i en degrés décimaux - sens trigo
  565.  
  566. float angle =angle_i/57.3; // (57.3 ~ 180/pi)
  567. int16_t x1, x2;
  568. int16_t y1, y2;
  569.  
  570. x1=x0+int16_t(r1* cos(angle));
  571. y1=y0-int16_t(r1* sin(angle));
  572.  
  573. x2=x0+int16_t(r2* cos(angle));
  574. y2=y0-int16_t(r2* sin(angle));
  575.  
  576. if ((x1>0) && (x2>0) && (y1>0) && (y2>0) && (x1<480) && (x2<480) && (y1<320) && (y2<320) )
  577. {
  578. TFT480.drawLine(x1, y1, x2, y2, couleur_i);
  579.  
  580. if (gras)
  581. {
  582. TFT480.drawLine(x1, y1-1, x2, y2-1, couleur_i);
  583. TFT480.drawLine(x1, y1+1, x2, y2+1, couleur_i);
  584. }
  585. }
  586. //TFT480.fillCircle(x2, y2, 2, ROUGE);
  587. }
  588.  
  589.  
  590.  
  591. void affi_pointe(uint16_t x0, uint16_t y0, uint16_t r, double angle_i, float taille, uint16_t couleur_i)
  592. {
  593. // trace une pointe de flèche sur un cercle de rayon r
  594. // angle_i en degrés décimaux - sens trigo
  595.  
  596. float angle =angle_i/57.3; // (57.3 ~ 180/pi)
  597. int16_t x1, x2, x3;
  598. int16_t y1, y2, y3;
  599.  
  600. x1=x0+r* cos(angle); // pointe
  601. y1=y0-r* sin(angle); // pointe
  602.  
  603. x2=x0+(r-7)* cos(angle-taille); // base A
  604. y2=y0-(r-7)* sin(angle-taille); // base A
  605.  
  606. x3=x0+(r-7)* cos(angle+taille); // base B
  607. y3=y0-(r-7)* sin(angle+taille); // base B
  608.  
  609. TFT480.fillTriangle(x1, y1, x2, y2, x3, y3, couleur_i);
  610. }
  611.  
  612.  
  613. void affi_dir_vent(int x0, int y0, int dx, int dy, float angle_i) // en chiffres
  614. {
  615. int a1;
  616.  
  617. TFT480.setFreeFont(FF0);
  618. TFT480.setTextSize(1);
  619. TFT480.setTextColor(BLANC);
  620.  
  621. a1=(int)angle_i;
  622. String s1 =String(a1);
  623. TFT480.fillRect(x0+60, y0+5, 25, 8, NOIR); // efface
  624. TFT480.drawString(s1, x0+65, y0+5);
  625. }
  626.  
  627.  
  628.  
  629.  
  630. void affi_boussole(int x0, int y0, int dx, int dy, float angle_i) // angle_i définit la position de la pointe de la flèche
  631. {
  632. TFT480.drawRect(x0, y0, dx, dy, BLANC);
  633.  
  634. TFT480.fillRect(x0+1, y0+1, dx-2, dy-2, NOIR); // efface
  635.  
  636. TFT480.setFreeFont(FF0);
  637. TFT480.setTextSize(1);
  638. TFT480.drawString("N", x0+35, y0+5);
  639.  
  640. TFT480.setTextColor(BLANC);
  641.  
  642. TFT480.setFreeFont(FF5);
  643. TFT480.setTextSize(1);
  644.  
  645. TFT480.fillRect(x0+15, y0+1, 50, 53, NOIR); // efface
  646.  
  647. //affi_pointe(x0+dx/2, y0+dy/2, (dy/2)-5, 90-angle_i, 2.5, CYAN);
  648. affi_rayon2(x0+dx/2, y0+dy/2, 0, 25, 90-angle_i, JAUNE, 0); // tige de la flèche
  649. affi_pointe(x0+dx/2, y0+dy/2, 25, 90-angle_i, 0.3, JAUNE); // pointe triangulaire en bout de flèche
  650.  
  651. TFT480.drawCircle(x0+dx/2, y0+dy/2, (dy/2)-2, BLANC);
  652.  
  653. TFT480.setTextColor(ROUGE);
  654. TFT480.drawString("N", -6+x0+dx/2, y0+5);
  655. TFT480.setTextColor(JAUNE);
  656. TFT480.drawString("W", x0+6, -8+y0+dy/2);
  657. TFT480.setTextColor(JAUNE);
  658. TFT480.drawString("E", x0+dx-14, -8+y0+dy/2);
  659. TFT480.setTextColor(BLEU);
  660. TFT480.drawString("S", -6+x0+dx/2, y0+dy-20);
  661.  
  662. TFT480.setFreeFont(FF0);
  663. TFT480.setTextSize(1);
  664. TFT480.setTextColor(BLANC);
  665.  
  666. TFT480.setTextColor(BLANC, NOIR);
  667. TFT480.drawString("VENT", x0, y0-3);
  668. }
  669.  
  670.  
  671.  
  672.  
  673. void draw_AEC(uint16_t x0, uint16_t y0, uint16_t L, uint8_t sens)
  674. {
  675. // ligne arc-en-ciel
  676. // affiche une ligne de pixels colorés à partir de la variable 'couleurs_aec' mémorisée en PROGMEM (voir fichier Couleurs_AEC.h)
  677. // L = longueur de la ligne
  678.  
  679. //Serial.println("draw_draw_AEC()");
  680. uint16_t x, i, j;
  681. uint16_t y1;
  682. uint16_t couleur_i;
  683.  
  684. for (int16_t i=0; i<L; i++)
  685. {
  686. float f = 470.0/L * i; // pour balayer toute l'échelle des couleurs disponibles
  687. j=uint16_t(f);
  688.  
  689. couleur_i = couleurs_aec[2*j] | couleurs_aec[2*j+1]<<8;
  690.  
  691. if (sens==0){x=i;} else {x=L-i;}
  692. TFT480.drawPixel(x0+x, y0, couleur_i);
  693. }
  694. }
  695.  
  696.  
  697.  
  698. void draw_bmp(uint16_t x0, uint16_t y0, uint8_t inv_x, File* fp)
  699. {
  700. //Serial.println("draw_bmp()");
  701. uint16_t i, j, j2, k, p, m=0;
  702. uint16_t y1;
  703. uint8_t bmp_data[2*3]={0};
  704. uint16_t bmp_color[2];
  705. uint8_t rot =1;
  706.  
  707. fp->seek(bmp_offset);
  708.  
  709. for(i=0; i<bmp_heigh; i++)
  710. {
  711. for(j=0; j<(bmp_width/2); j++)
  712. {
  713. m=0;
  714. fp->read(bmp_data,2*3);
  715. for(k=0; k<2; k++)
  716. {
  717. bmp_color[k]= Color_To_565(bmp_data[m+2], bmp_data[m+1], bmp_data[m+0]);
  718. m+=3;
  719. }
  720. for(p=0; p<2; p++)
  721. {
  722. if (rot==0)
  723. {
  724. y1=y0;
  725. TFT480.drawPixel(x0+i, y0+j*2+p, bmp_color[p]);
  726. }
  727. if (rot==1)
  728. {
  729. if (inv_x == 0) {j2=j;} else { j2=(bmp_width/2)-j; }
  730. y1=y0;
  731. TFT480.drawPixel(x0+j2*2+p,320-(y1+i), bmp_color[p]);
  732. }
  733. }
  734. }
  735. }
  736. }
  737.  
  738.  
  739. bool teste_bmp_header(File fp)
  740. {
  741. if(read_16(fp) != 0x4D42) { return false; } // (2 bytes) The header field used to identify the BMP
  742. read_32(fp); // (4 bytes) get bmp size (nombre total d'octets)
  743. read_32(fp); // (4 bytes) get creator information
  744. bmp_offset = read_32(fp); // (4 bytes) get offset information
  745. read_32(fp);//get DIB information
  746.  
  747. // ici on a lu 16 octets
  748. bmp_width = read_32(fp); //(4 bytes) get width and heigh information
  749. bmp_heigh = read_32(fp); //(4 bytes)
  750.  
  751. // ici on a lu 24 octets
  752. //if(read_16(fp)!= 1) {return false;}
  753. read_16(fp);
  754. //if(read_32(fp)!= 0) {return false;}
  755. return true;
  756. }
  757.  
  758.  
  759. void affi_img(uint16_t x0, uint16_t y0, uint8_t inv_x, const char* filename1)
  760. {
  761. //Serial.println("affi_img()");
  762. File bmp_file;
  763.  
  764. bmp_file = SD.open(filename1);
  765. if(!bmp_file)
  766. {
  767. //Serial.println("didnt find bmp");
  768. TFT480.setFreeFont(FF1);
  769. TFT480.setTextSize(1);
  770. TFT480.drawString("didnt find bmp",0,10);
  771. delay(2000);
  772. return;
  773. }
  774. if(!teste_bmp_header(bmp_file))
  775. {
  776. //Serial.println("bad bmp !");
  777. TFT480.setFreeFont(FF1);
  778. TFT480.setTextSize(1);
  779. TFT480.drawString("bad bmp !",0,0);
  780. delay(2000);
  781. return;
  782. }
  783. draw_bmp(x0, y0, inv_x, &bmp_file);
  784.  
  785. bmp_file.close();
  786. // delay(1000);
  787.  
  788. }
  789.  
  790.  
  791.  
  792. void affi_graduation_temporelle()
  793. {
  794.  
  795. //Serial.println("affi_graduation_temporelle()");
  796. /*
  797. RAPPEL:
  798. void Scope::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi)
  799. Scope_1.init(18,145, n_record_max,160); // 1px toutes les 5mn; 12px = 1h
  800. uint16_t n_record_max = 450
  801.  
  802. donc:
  803. Scope => (18, 145, 450, 160)
  804.  
  805. */
  806. int16_t h1, mn1;
  807. int16_t j;
  808. int16_t x0, x1;
  809.  
  810. TFT480.fillRect(Scope_1.x0, Scope_1.y0-18, Scope_1.dx, 17, couleur_fond); // // efface
  811.  
  812. j=3; // on va tracer 3 jours = 36h = 432x5mn (correspondant à 432 px)
  813. h1=heures;
  814. mn1=minutes;
  815. x0=18+450-12; // px le plus à droite (on va tracer de droite à gauche en remontant dans le passé)
  816.  
  817. x1=x0;
  818. while (j>0)
  819. {
  820. mn1--;
  821. if (mn1<0)
  822. {
  823. mn1=0;
  824. h1--;
  825. if (h1<0)
  826. {
  827. mn1=59;
  828. h1=23;
  829. j--;
  830. }
  831. }
  832.  
  833. Scope_1.setCouleurTrace(GRIS_FONCE);
  834. if ((h1 == 6) || (h1==18)) {Scope_1.setCouleurTrace(GRIS);}
  835. if (h1 == 0) {Scope_1.setCouleurTrace(GRIS_CLAIR);}
  836. if (h1 == 12) {Scope_1.setCouleurTrace(ROUGE);}
  837.  
  838. if (mn1==0) // tracé de lignes temporelles verticales toutes les heures 'pile'
  839. {
  840. x1-=12;
  841. // mode normal
  842. if (x1 > 30) // pour ne pas abimer l'affichage de la graduation de la pression
  843. {
  844. Scope_1.Tiret(x1, 5, x1, Scope_1.dy-2);
  845. }
  846.  
  847.  
  848. if (h1 == 0)
  849. {
  850. Scope_1.Tiret(x1+1, 5, x1+1, Scope_1.dy-2); // pour épaissir
  851.  
  852. for (int n=2; n<10; n++)
  853. {
  854. draw_AEC(x1+8, Scope_1.y0+n, 142, 0); // lignes aux couleurs de l'arc-en-ciel
  855. }
  856.  
  857. affi_img(x1+8, Scope_1.y0-20, 0, "/bmp/lune3b.bmp");
  858.  
  859. TFT480.setFreeFont(FF5);
  860. TFT480.setTextColor(BLANC, BLEU);
  861. TFT480.drawString("0", x1+14, Scope_1.y0-18);
  862. }
  863.  
  864. if (h1 == 6)
  865. {
  866. TFT480.setFreeFont(FF5);
  867. TFT480.setTextColor(BLANC, BLEU);
  868. TFT480.drawString("6", x1+12, Scope_1.y0-18);
  869. }
  870.  
  871. affiche_degrade(0,0);
  872.  
  873. if (h1 == 12)
  874. {
  875. Scope_1.Tiret(x1+1, 5, x1+1, Scope_1.dy-2); // pour épaissir
  876.  
  877. for (int n=2; n<10; n++)
  878. {
  879. draw_AEC(x1+8, Scope_1.y0+n, 142, 1); // lignes aux couleurs de l'arc-en-ciel
  880. }
  881.  
  882. TFT480.setFreeFont(FF5);
  883. TFT480.setTextColor(BLANC, BLEU);
  884. TFT480.drawString("12", x1+8, Scope_1.y0-18);
  885.  
  886. affi_img(x1+7, Scope_1.y0-20, 0, "/bmp/soleil.bmp"); //icone soleil:
  887. }
  888.  
  889. if (h1 == 18)
  890. {
  891. TFT480.setFreeFont(FF5);
  892. TFT480.setTextColor(BLANC, BLEU);
  893. TFT480.drawString("18", x1+7, Scope_1.y0-18);
  894. }
  895. }
  896.  
  897. }
  898. TFT480.setFreeFont(FF0);
  899. TFT480.setTextColor(BLANC, BLEU);
  900. TFT480.drawString("NOW", Scope_1.x0+Scope_1.dx-10, Scope_1.y0-18);
  901.  
  902. uint16_t xx1 =Scope_1.x0+Scope_1.dx-6;
  903. uint16_t yy1 =Scope_1.y0-10;
  904. uint16_t xx2 =Scope_1.x0+Scope_1.dx-2;
  905. uint16_t yy2 =Scope_1.y0-2;
  906. uint16_t xx3 =Scope_1.x0+Scope_1.dx+2;
  907. uint16_t yy3 =Scope_1.y0-10;
  908.  
  909.  
  910.  
  911. TFT480.fillTriangle(xx1, yy1, xx2, yy2, xx3, yy3, BLANC);
  912.  
  913. TFT480.fillRect(468, Scope_1.y0-2, 12, 20, couleur_fond);
  914. }
  915.  
  916.  
  917.  
  918.  
  919. void init_SCOPE() // vide
  920. {
  921. //Serial.println("init_SCOPE()");
  922.  
  923. Scope_1.init(18,160, n_record_max,160); // 1px toutes les 5mn; 12px = 1h
  924. Scope_1.setCouleurCadre(BLANC);
  925. Scope_1.traceCadre();
  926.  
  927. affi_graduation_temporelle();
  928. }
  929.  
  930.  
  931.  
  932. uint8_t decToBcd( int val )
  933. {
  934. return (uint8_t) ((val / 10 * 16) + (val % 10));
  935. }
  936.  
  937.  
  938.  
  939. uint16_t Color_To_565(uint8_t r, uint8_t g, uint8_t b)
  940. {
  941. return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
  942. }
  943.  
  944.  
  945. void RGB565_to_888(uint16_t color565, uint8_t *R, uint8_t *G, uint8_t *B)
  946. {
  947. *R=(color565 & 0xFFFFF800) >> 8;
  948. *G=(color565 & 0x7E0) >> 3;
  949. *B=(color565 & 0x1F) << 3 ;
  950. }
  951.  
  952.  
  953.  
  954.  
  955. uint16_t read_16(File fp)
  956. {
  957. uint8_t low;
  958. uint16_t high;
  959. low = fp.read();
  960. high = fp.read();
  961. return (high<<8)|low;
  962. }
  963.  
  964.  
  965.  
  966. uint32_t read_32(File fp)
  967. {
  968. uint16_t low;
  969. uint32_t high;
  970. low = read_16(fp);
  971. high = read_16(fp);
  972. return (high<<8)|low;
  973. }
  974.  
  975.  
  976.  
  977. void write_16(uint16_t v16, File fp)
  978. {
  979. uint8_t low, high;
  980.  
  981. low = v16 & 0xFF;
  982. high= v16 >>8;
  983.  
  984. fp.write(low);
  985. fp.write(high);
  986. }
  987.  
  988.  
  989. void write_32(uint32_t v32, File fp)
  990. {
  991. uint16_t low, high;
  992.  
  993. low = v32 & 0xFFFF;
  994. high= v32 >>16;
  995.  
  996. write_16(low, fp);
  997. write_16(high, fp);
  998. }
  999.  
  1000.  
  1001.  
  1002.  
  1003. void setup()
  1004. {
  1005. pinMode(GPIO_SIGNAL, INPUT);
  1006. SIGNAL_etat = digitalRead(GPIO_SIGNAL);
  1007. memo_SIGNAL_etat = SIGNAL_etat;
  1008.  
  1009. pinMode(GPIO_bouton1, INPUT);
  1010. bouton1_etat = digitalRead(GPIO_bouton1);
  1011. memo_bouton1_etat = bouton1_etat;
  1012.  
  1013.  
  1014.  
  1015. Wire.begin(GPIO_SDA, GPIO_SCL, 100000); // OK (source: https://randomnerdtutorials.com/esp32-i2c-communication-arduino-ide/ )
  1016. // en conséquece câbler le capteur BMP280 en i2C sur les GPIO 32 et GPIO 33 de l'ESP32 (à la place de 21, 22 par défaut)
  1017.  
  1018. Serial.begin(19200);
  1019.  
  1020. TFT480.init();
  1021. TFT480.setRotation(1); // 0..3 à voir, suivant le modèle de l'afficheur et sa disposition
  1022.  
  1023. TFT480.fillScreen(NOIR);
  1024.  
  1025. TFT480.setTextColor(BLANC, NOIR);
  1026. TFT480.drawRect(0, 0, 480, 320, BLANC); // ok
  1027.  
  1028. delay(300);
  1029.  
  1030. init_SDcard();
  1031.  
  1032. TFT480.setFreeFont(FF1);
  1033. uint16_t y=5;
  1034. y+=20;
  1035. String s1="version " + version;
  1036. TFT480.drawString(s1, 10, y);
  1037. y+=20;
  1038. TFT480.setTextColor(BLEU, NOIR);
  1039. TFT480.drawString("Heure GPS", 10, y);
  1040. y+=20;
  1041. TFT480.setTextColor(JAUNE, NOIR);
  1042. TFT480.drawString("Client WiFi", 10, y);
  1043. y+=40;
  1044.  
  1045.  
  1046. //affi_img(0, 0, 0, "/bmp/capture1.bmp");
  1047. //affi_img(0, 50, 1, "/bmp/capture1.bmp");
  1048. //affi_img(0, 110, 1, "/bmp/ligne_aec1.bmp");
  1049.  
  1050. draw_AEC(0, 110, 470, 0);
  1051. draw_AEC(100, 130, 250, 1);
  1052.  
  1053. TFT480.setFreeFont(FF1);
  1054.  
  1055. /* **************************************************************************** */
  1056.  
  1057. //Serial.println("ici3");
  1058. TFT480.fillScreen(couleur_fond);
  1059. /*
  1060. TFT480.setTextColor(CYAN, couleur_fond);
  1061. TFT480.setFreeFont(FF5);
  1062. TFT480.setTextSize(1);
  1063. TFT480.drawString("GPS Time",0,0);
  1064. */
  1065.  
  1066. init_BMP280();
  1067.  
  1068. aff_y=0;
  1069.  
  1070. num_acquisition=6;
  1071. for(int i=0; i<=5; i++) {T_EXT_lue[i]=0;}
  1072. T_EXT_retenue=0;
  1073.  
  1074. Pression_lue=1013; //hPa
  1075. Humid_lue = 50; // 50%
  1076.  
  1077. affiche_meteo();
  1078. affi_pannel();
  1079.  
  1080.  
  1081.  
  1082. /* ***************************** A DECOMMENTER **********************
  1083. */
  1084. affiche_heure(); //et initialise les variables : annee, mois, jour, heures, minutes
  1085. affiche_date(); // attention : executer la ligne précedente au moins 1 fois au préalable pour initialiser les variables
  1086.  
  1087. /* ****************************************************************** */
  1088.  
  1089. affi_cadres();
  1090. affiche_LEDS();
  1091. init_SCOPE();
  1092.  
  1093.  
  1094. // --------------------------------------------
  1095. //charge_enregistrements(); // pour afficher rapidement (sans attendre les acquisitions)
  1096. if (0) // =1 à la 1ere utilisation
  1097. {
  1098. TFT480.setTextColor(JAUNE, NOIR); TFT480.setFreeFont(FF5);
  1099. String s1="init_lst_records()"; TFT480.drawString(s1, 50, 200);
  1100. init_lst_records();
  1101.  
  1102. serial_out_datas();
  1103.  
  1104. //trace_sur_Scope();
  1105. write_scope_on_SDcard();
  1106. }
  1107. // --------------------------------------------
  1108.  
  1109. read_scope_on_SDcard();
  1110. trace_sur_Scope();
  1111.  
  1112. affiche_Lune();
  1113.  
  1114. Led2.setCouleur(ROUGE);
  1115.  
  1116. WiFi.disconnect();
  1117. connexion_serveur_HR();
  1118. recp_HR = "{}";
  1119. if(WiFi.status()== WL_CONNECTED )
  1120. {
  1121. httpGetHeureDate();
  1122. ajuste_HR();
  1123. }
  1124. affiche_heure();
  1125.  
  1126. // animation de la boussole, pour test
  1127. int x0=100; int y0=80; int dx=86; int dy =55;
  1128. for(int v1=0; v1<360; v1++)
  1129. {
  1130. affi_boussole(x0, y0, dx, dy, v1);
  1131. delay(10);
  1132. }
  1133. }
  1134.  
  1135.  
  1136.  
  1137. void Draw_arc_elliptique(uint16_t x0, uint16_t y0, int16_t dx, int16_t dy, float alpha1, float alpha2, uint16_t couleur_i) // alpha1 et alpha2 en radians
  1138. {
  1139. /*
  1140. REMARQUES :
  1141. -cette fonction permet également de dessiner un arc de cercle (si dx=dy), voire le cercle complet
  1142. - dx et dy sont du type int (et pas uint) et peuvent êtres négafifs, ou nuls.
  1143. -alpha1 et alpha2 sont les angles (en radians) des azimuts des extrémités de l'arc
  1144. */
  1145. uint16_t n;
  1146. float i;
  1147. float x,y;
  1148.  
  1149. //TFT480.Set_Draw_color(couleur);
  1150. i=alpha1;
  1151. while(i<alpha2)
  1152. {
  1153. x=x0+dx*cos(i);
  1154. y=y0+dy*cos(i+M_PI/2.0);
  1155. TFT480.drawPixel(x,y, couleur_i);
  1156. i+=0.01; // radians
  1157. }
  1158. }
  1159.  
  1160.  
  1161. void Fill_croissant_elliptique(uint16_t x0, uint16_t y0, int16_t dx1, int16_t dx2, int16_t dy, float alpha1, float alpha2, uint16_t couleur)
  1162. {
  1163. // dx1 et dx2 sont les extremums (en pixels) horizontaux d'un croissant vertical
  1164.  
  1165. if (dx2<dx1) {return;}
  1166. int16_t dx;
  1167. dx = dx1;
  1168. while(dx<dx2)
  1169. {
  1170. Draw_arc_elliptique(x0, y0, dx, dy, alpha1, alpha2, couleur);
  1171. dx++;
  1172. }
  1173. }
  1174.  
  1175.  
  1176.  
  1177. void Dessine_Lune2(uint16_t x0, uint16_t y0, float age)
  1178. {
  1179. //Remarque : l'age admet des nombres réels (avec décimales)
  1180.  
  1181. affi_img(x0-24, 258, 0, "/bmp/lune5.bmp"); // image 48x48px
  1182.  
  1183. uint8_t r=22;
  1184. int16_t T1, T2; //Terminators gauche et droite, limites entre partie éclairée et partie dans l'ombre
  1185.  
  1186. if (age<1)
  1187. {
  1188. TFT480.fillRect(x0-r, y0-r, 2*r, 2*r, NOIR); // efface l'image
  1189. TFT480.drawCircle(x0, y0, r, GRIS_FONCE);// Nouvelle Lune
  1190. }
  1191.  
  1192. // on procède par masquage (sur l'image bmp affichée) de la partie dans l'ombre
  1193.  
  1194. if (age <= 15) // premier croissant... pleine
  1195. {
  1196. T1=-r;
  1197. T2=r-2*r*age/15;
  1198. }
  1199.  
  1200. if ((age > 15) && (age <=30)) // pleine... dernier croissant
  1201. {
  1202. T1=r-2*r*(age-15)/15;
  1203. T2=r+1;
  1204. }
  1205.  
  1206. if(((uint8_t)age)==0)
  1207. {
  1208. TFT480.drawCircle(x0, y0, r, GRIS);
  1209. }
  1210. else
  1211. {
  1212. TFT480.drawCircle(x0, y0, r, GRIS_FONCE);
  1213. Fill_croissant_elliptique(x0, y0, T1, T2, r, -M_PI/2, M_PI/2, NOIR);
  1214. }
  1215. }
  1216.  
  1217.  
  1218. void affiche_Lune()
  1219. {
  1220. //Serial.println("affiche_Lune()");
  1221. //Serial.print("annee="); //Serial.println(annee);
  1222. //Serial.print("mois="); //Serial.println(mois);
  1223. //Serial.print("jour="); //Serial.println(jour);
  1224.  
  1225. age_Lune = GetPhase(2000+annee, mois, jour);
  1226.  
  1227. // Affichage de 'l'age' de la Lune [0..29 j]
  1228. //TFT480.Set_Text_Size(1);
  1229.  
  1230. uint16_t x0=432;
  1231. uint16_t y0=15;
  1232.  
  1233. TFT480.fillRect(x0, y0+1, 32, 50, NOIR); // efface
  1234.  
  1235. TFT480.setFreeFont(FF0);
  1236. TFT480.setTextSize(1);
  1237. TFT480.setTextColor(GRIS_CLAIR, NOIR);
  1238. TFT480.drawString("AGE:", x0, y0) ;
  1239.  
  1240. TFT480.setTextColor(BLANC, NOIR);
  1241. String s1=String((uint8_t)age_Lune); TFT480.drawString(s1, x0, y0+10);
  1242.  
  1243. TFT480.setTextColor(BLANC);
  1244. TFT480.setFreeFont(FF1);
  1245.  
  1246. switch (((int8_t)age_Lune))
  1247. {
  1248. case 0: { TFT480.drawString("NL", x0+4, y0+27); } break;
  1249. case 8: { TFT480.drawString("PQ", x0+4, y0+27); } break;
  1250. case 15: { TFT480.drawString("PL", x0+4, y0+27); } break;
  1251. case 22: {TFT480.drawString("DQ", x0+4, y0+27); } break;
  1252. default: { TFT480.drawString("--", x0+4, y0+27); } break;
  1253. }
  1254. Dessine_Lune2(407, 38, age_Lune);
  1255. }
  1256.  
  1257.  
  1258.  
  1259. void incremente_heure(uint8_t nb_s)
  1260. {
  1261. for (uint8_t n=0; n<nb_s; n++)
  1262. {
  1263. if (secondes < 59) {secondes+=1;}
  1264. else
  1265. {
  1266. secondes=0;
  1267. if (minutes < 59) {minutes+=1;}
  1268. else
  1269. {
  1270. minutes=0;
  1271. if (heures < 23) {heures+=1;}
  1272. else
  1273. heures=0;
  1274. }
  1275. }
  1276. }
  1277. }
  1278.  
  1279.  
  1280.  
  1281. void tt_les_30mn()
  1282. {
  1283. //Serial.println("----------------------------") ;
  1284. //Serial.print("tt_les_30mn() ");
  1285. //Serial.print(heures); //Serial.print(":"); //Serial.println(minutes);
  1286.  
  1287. print_meteo_on_SDcard(); // en ASCII dans le fichier "data.txt" (ajout à la fin du fichier, en conservant la totalité des acquis)
  1288. delay(500);
  1289. write_scope_on_SDcard(); // en binaire dans le fichier "scope.dat" (remplace le fichier - ne mémorise que 36h au total)
  1290. delay(500);
  1291. affiche_Lune();
  1292.  
  1293. }
  1294.  
  1295.  
  1296.  
  1297. void tt_les_5mn()
  1298. {
  1299. uint16_t x2;
  1300. uint16_t y2;
  1301.  
  1302. //Serial.println("----------------------------") ;
  1303. //Serial.print("tt_les_5mn() ");
  1304. //Serial.print(heures); //Serial.print(":"); //Serial.println(minutes);
  1305.  
  1306. //Led4.setCouleur(BLANC);
  1307. record_enr_actuel();
  1308. decale_enregistrements();
  1309. trace_sur_Scope();
  1310.  
  1311. affiche_date();
  1312. /*
  1313. TFT480.Set_Text_Back_colour(NOIR);
  1314. TFT480.setTextColor(BLANC);
  1315. TFT480.Set_Text_Size(1);
  1316.  
  1317. x2 = 300;
  1318. y2 = 70;
  1319. TFT480.drawString("WR SDcard", x2, y2);
  1320. TFT480.Print_Number_Int(10-TT3,x2+55,y2,3,' ',10); //affiche le temps restant avant enregistrement sur la SDcard
  1321. TFT480.drawString("mn", x2+70, y2);
  1322. Led4.setCouleur(NOIR);
  1323. */
  1324. }
  1325.  
  1326.  
  1327. void tt_les_1mn()
  1328. {
  1329. //Serial.println("----------------------------") ;
  1330. //Serial.print("tt_les_1mn() ") ;
  1331. //Serial.print(heures); //Serial.print(":"); //Serial.println(minutes);
  1332. //Serial.print("acqui_valide=");//Serial.println(acqui_valide);// TT2=0;
  1333.  
  1334. // TT3+=1;
  1335. // TFT480.Set_Text_Size(1);
  1336. // TFT480.Print_Number_Int(TT3,390,15,3,' ',10);
  1337.  
  1338. #ifndef NO_girouette // voir la direcive au tout début du programme
  1339. if(mode == 0)
  1340. {
  1341. WiFi.disconnect();
  1342. connexion_serveur_HR();
  1343. recp_HR = "{}";
  1344. if(WiFi.status()== WL_CONNECTED )
  1345. {
  1346. httpGetHeureDate();
  1347. ajuste_HR();
  1348. }
  1349. mode=1;
  1350. }
  1351. else
  1352. {
  1353. WiFi.disconnect();
  1354. connexion_serveur_ANGLE();
  1355. recp_ANGLE = "{}";
  1356. if(WiFi.status()== WL_CONNECTED )
  1357. {
  1358. httpGetAngle();
  1359. }
  1360. mode=0;
  1361.  
  1362. int x0=100; int y0=80; int dx=86; int dy =55;
  1363. if (alerte1 ==0)
  1364. {
  1365. affi_boussole(x0, y0, dx, dy, vent_vers);
  1366. // un "vent du Nord" affichera une flèche dirigée dans le sens du vent, donc vers le SUD comme sur les cartes météo
  1367. affi_dir_vent(x0, y0, dx, dy, vent_du); //en chiffres, un "vent du Nord" écrira "0", pas "180"
  1368. }
  1369. else
  1370. {
  1371. // trace une croix rouge dans le rectangle si pas de signal reçu
  1372. TFT480.drawRect(x0, y0, dx, dy, BLANC);
  1373. TFT480.drawLine(x0, y0, x0+dx, y0+dy, ROUGE);
  1374. TFT480.drawLine(x0+1, y0, x0+dx+1, y0+dy, ROUGE); // pour épaissir le trait
  1375. TFT480.drawLine(x0+dx, y0, x0, y0+dy, ROUGE);
  1376. TFT480.drawLine(x0+dx+1, y0, x0+1, y0+dy, ROUGE);
  1377. }
  1378. }
  1379. #endif
  1380.  
  1381. #ifdef NO_girouette
  1382. WiFi.disconnect();
  1383. connexion_serveur_HR();
  1384. recp_HR = "{}";
  1385. if(WiFi.status()== WL_CONNECTED )
  1386. {
  1387. httpGetHeureDate();
  1388. ajuste_HR();
  1389. }
  1390. #endif
  1391.  
  1392. num_acquisition=0;
  1393. calcul_confiance();
  1394. T_EXT_retenue = meilleure_valeur();
  1395. nb_acqui433=0; // RAZ
  1396.  
  1397. calcul_Tmin_Tmax();
  1398. //fixe_Tmin_tmax();
  1399.  
  1400. calcul_Gradu_ech_T();
  1401. calcul_Gradu_ech_P();
  1402. affiche_meteo(); // en haut, dans les cadres du pannel, mais PAS dans le "scope"
  1403.  
  1404.  
  1405. if ((todo_init_record==1)&&(acqui_valide)) {RAZ_data();}
  1406. if (todo_wr_scope_on_sd==1) {write_scope_on_SDcard();}
  1407.  
  1408.  
  1409.  
  1410. //write_TFT_on_SDcard(); // POUR TEST
  1411. //trace_sur_Scope(); // POUR TEST
  1412. //print_meteo_on_SDcard(); // POUR TEST
  1413.  
  1414. }
  1415.  
  1416.  
  1417. void toutes_les_20s()
  1418. {
  1419.  
  1420.  
  1421. }
  1422.  
  1423.  
  1424. void toutes_les_5s()
  1425. {
  1426. if (todo_wr_ecran_on_sd==1) {write_TFT_on_SDcard();}
  1427.  
  1428. // POURT TEST
  1429. //record_enr_actuel();
  1430. //decale_enregistrements();
  1431. //trace_sur_Scope();
  1432. // FIN TEST
  1433.  
  1434. }
  1435.  
  1436.  
  1437. void toutes_les_1s()
  1438. {
  1439. compteur1s++;
  1440.  
  1441. if((compteur1s%5)==0)
  1442. {
  1443. toutes_les_5s();
  1444. }
  1445.  
  1446. if(compteur1s>20)
  1447. {
  1448. compteur1s=0;
  1449. toutes_les_20s();
  1450. }
  1451.  
  1452. incremente_heure(1); // +1s
  1453.  
  1454. affiche_heure();
  1455. }
  1456.  
  1457.  
  1458.  
  1459. void loop()
  1460. {
  1461. temps_ecoule = micros() - memo_micros1;
  1462. if (temps_ecoule >= 1E6) // (>=) et pas strictement égal (==) parce qu'alors on rate l'instant en question
  1463. {
  1464. memo_micros1 = micros();
  1465. toutes_les_1s();
  1466. }
  1467.  
  1468. memo_SIGNAL_etat = SIGNAL_etat;
  1469. SIGNAL_etat = digitalRead(GPIO_SIGNAL);
  1470. if (SIGNAL_etat != memo_SIGNAL_etat) //hors timing local (les signaux horaires sont émis par la sonde sans requete préalable)
  1471. {
  1472. analyse_signal_primaire();
  1473. }
  1474.  
  1475. memo_bouton1_etat = bouton1_etat;
  1476. bouton1_etat = digitalRead(GPIO_bouton1);
  1477. if (bouton1_etat != memo_bouton1_etat) //hors timing local (les signaux horaires sont émis par la sonde sans requete préalable)
  1478. {
  1479. if (bouton1_etat == 0)
  1480. {
  1481. todo_wr_ecran_on_sd =1;
  1482. }
  1483. }
  1484.  
  1485. }
  1486.  
  1487.  
  1488.  
  1489. void affiche_meteo() //(en nombres dans la partie haute de l'afficheur)
  1490. {
  1491. //Serial.println("affiche_meteo()");
  1492.  
  1493. /**
  1494. // utile lors de la phase de mise au point...
  1495.  
  1496. // Affichage de 5 valeurs reçues, constituant une salve, et de leur indice de confiance
  1497.  
  1498. TFT480.Set_Draw_color(NOIR);
  1499. TFT480.Fill_Rectangle(434, 4, 476, 54); // efface tout pour crééer une "animation" de l'affichage même si valeurs inchangées
  1500.  
  1501. TFT480.Set_Text_Back_colour(NOIR);
  1502.   TFT480.setTextColor(BLANC);
  1503.  
  1504. TFT480.Set_Draw_color(GRIS);
  1505. TFT480.Draw_Fast_VLine(430, 4, 55);
  1506.  
  1507.  
  1508. // Affichage des détails de la salve reçue en petits chiffres, à droite
  1509. TFT480.Set_Text_Size(1);
  1510. uint16_t x0=434;
  1511. uint16_t y0=0;
  1512. y0+= 5; TFT480.Print_Number_Int(T_EXT_lue[0],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[0],x0+30,y0,1,' ',10);
  1513. y0+=10; TFT480.Print_Number_Int(T_EXT_lue[1],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[1],x0+30,y0,1,' ',10);
  1514. y0+=10; TFT480.Print_Number_Int(T_EXT_lue[2],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[2],x0+30,y0,1,' ',10);
  1515. y0+=10; TFT480.Print_Number_Int(T_EXT_lue[3],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[3],x0+30,y0,1,' ',10);
  1516. y0+=10; TFT480.Print_Number_Int(T_EXT_lue[4],x0,y0,3,' ',10); TFT480.Print_Number_Int(confiance[4],x0+30,y0,1,' ',10);
  1517.  
  1518. // Affichage en VERT de la valeur retenue
  1519. TFT480.Set_Text_Back_colour(NOIR);
  1520. TFT480.setTextColor(GRIS);
  1521. TFT480.drawString("ok->", 410,57);
  1522. TFT480.setTextColor(VERT);
  1523. y0+=12; TFT480.Print_Number_Int(meilleure_valeur(),x0,y0,3,' ',10);
  1524.  
  1525. **/
  1526.  
  1527. float T_out_f = T_EXT_retenue / 10.0; // float
  1528.  
  1529. Etiq_1.affiche_float(T_out_f, 4, 1, " C", Tmin/10, Tmax/10);
  1530. TFT480.drawCircle(Etiq_1.x0+132, Etiq_1.y0+Etiq_1.dy-20, 3, Etiq_1.couleur_txt); // affiche le signe "°"
  1531.  
  1532. Etiq_3.affiche_int(Pression_lue, 4, "hPa", Pmin, Pmax);
  1533.  
  1534. // if (stable==1) {Led3.setCouleur(VERT);} else {Led3.setCouleur(ORANGE);}
  1535.  
  1536. Etiq_2.affiche_int(Humid_lue, 2, "", 0, 0);
  1537. //TFT480.Set_Text_Size(2);
  1538. TFT480.drawString("%", Etiq_2.x0+70,Etiq_2.y0+35);
  1539.  
  1540. //Serial.println("fin affiche_meteo()");
  1541. }
  1542.  
  1543.  
  1544.  
  1545. void affi_pannel() // init vide
  1546. {
  1547. //Serial.println("affi_pannel()");
  1548.  
  1549. uint16_t hauteur = 0;
  1550.  
  1551. hauteur += 6;
  1552.  
  1553. Etiq_1.setCouleurTxt(JAUNE);
  1554. Etiq_1.setCouleurCadre(BLEU_CLAIR);
  1555. Etiq_1.setCouleurNom(BLANC);
  1556. Etiq_1.init(5, hauteur, 180, 60, "TEMPERATURE");
  1557. Etiq_1.affiche_string ("---");
  1558.  
  1559. Etiq_3.setCouleurTxt(VERT);
  1560. Etiq_3.setCouleurCadre(VERT);
  1561. Etiq_3.setCouleurNom(BLANC);
  1562. Etiq_3.init(200, hauteur, 170, 60, "PRESSION");
  1563. TFT480.setTextColor(Etiq_3.couleur_txt, NOIR);
  1564. Etiq_3.affiche_int(0, 4, "hPa",0,0);
  1565.  
  1566. hauteur += 70;
  1567.  
  1568. Etiq_2.setCouleurTxt(BLEU_CLAIR);
  1569. Etiq_2.setCouleurCadre(BLEU_CLAIR);
  1570. Etiq_2.setCouleurNom(BLANC);
  1571. Etiq_2.init(5, hauteur, 90, 55, "HUMIDITE");
  1572. Etiq_2.affiche_string ("---");
  1573.  
  1574. //Etiq_4.setCouleurTxt(BLANC);
  1575. //Etiq_4.setCouleurCadre(BLANC);
  1576. //Etiq_4.setCouleurNom(BLANC);
  1577. //Etiq_4.init(100, hauteur, 86, 55, "VENT");
  1578. //Etiq_4.affiche_string ("");
  1579.  
  1580.  
  1581. }
  1582.  
  1583.  
  1584. void affi_cadres()
  1585. {
  1586. TFT480.drawRect(380, 12, 90, 59, BLANC); // cadre 1 (PHASE LUNE)
  1587.  
  1588. TFT480.drawRect(380, 78, 90, 35, GRIS); // cadre 2 (affi nb acquisitions)
  1589.  
  1590. TFT480.setFreeFont(FF0);
  1591. TFT480.setTextColor(GRIS, NOIR);
  1592. String s1="433 MHz";
  1593. TFT480.drawString(s1, 425, 82); // dans le cadre 2
  1594.  
  1595. s1="GPS (WiFi)";
  1596. TFT480.drawString(s1, 400, 97);
  1597. }
  1598.  
  1599.  
  1600.  
  1601.  
  1602. void affiche_LEDS()
  1603. {
  1604. //TFT480.Set_Text_Size(1);
  1605. //TFT480.setTextColor(BLANC);
  1606.  
  1607.  
  1608. Led2.init(385,82, 10,10);
  1609. //TFT480.drawString("RX", Led2.x0-10, Led2.y0, BLANC);
  1610. Led2.setCouleur(NOIR);
  1611.  
  1612. Led3.init(385,97, 10,10);
  1613. //TFT480.drawString("RX", Led2.x0-10, Led2.y0, BLANC);
  1614. Led3.setCouleur(NOIR);
  1615. }
  1616.  
  1617.  
  1618. void affiche_degrade(uint16_t x, uint16_t y)
  1619. {
  1620. // todo : charger une image
  1621.  
  1622. }
  1623.  
  1624.  
  1625.  
  1626. void calcul_Gradu_ech_T()
  1627. {
  1628. //Serial.println("calcul_Gradu_ech_T()");
  1629. String s1;
  1630.  
  1631.  
  1632. //calcul de la gradu min & gradu max
  1633. gradu_minT = -10+(Tmin/10)*10;
  1634. gradu_maxT = 10+(Tmax/10)*10;
  1635.  
  1636. echelle_T =(float) (gradu_maxT - gradu_minT)/(Scope_1.dy);
  1637. if (echelle_T==0) {echelle_T=1;} // cas où la température est totalement constante - évite des /0 plus tard
  1638.  
  1639. }
  1640.  
  1641.  
  1642. void calcul_Gradu_ech_P()
  1643. {
  1644. //Serial.println("calcul_Gradu_ech_P()");
  1645. String s1;
  1646.  
  1647.  
  1648.  
  1649. //calcul de la gradu min & gradu max
  1650. gradu_minP = -2+(Pmin/10)*10;
  1651. gradu_maxP = 2+(Pmax/10)*10;
  1652.  
  1653. //Serial.print("gradu_minP="); //Serial.println(gradu_minP);
  1654. //Serial.print("gradu_maxP="); //Serial.println(gradu_maxP);
  1655.  
  1656. echelle_P =(float) (gradu_maxP - gradu_minP)/(Scope_1.dy);
  1657. if (echelle_P==0) {echelle_P=1;} // cas où la Pression est totalement constante - évite des /0 plus tard
  1658.  
  1659. //Serial.print("echelle_P="); //Serial.println(echelle_P);
  1660.  
  1661. }
  1662.  
  1663.  
  1664.  
  1665. void calcul_Tmin_Tmax()
  1666. {
  1667. //Serial.println(" ");
  1668. //Serial.println("calcul_Tmin_Tmax()");
  1669. uint16_t i;
  1670. int16_t T_i, memo_T_i;
  1671. Tmin=500;
  1672. Tmax=-500;
  1673.  
  1674. for (i=0; i< n_record_max-1; i++)
  1675. {
  1676. memo_T_i = T_i;
  1677. T_i=lst_records[i].T;
  1678.  
  1679. if (abs(T_i - memo_T_i) < 100) // anti-glitch (si delta T > 10°C en 5mn... -> sans doute un enregistrement corrompu)
  1680. {
  1681. if ( (T_i != 0) && (T_i > -250) && (T_i < 480)) // -25°C à +48°C
  1682. {
  1683. if (T_i < Tmin) {Tmin= T_i;}
  1684. if (T_i > Tmax) {Tmax= T_i;}
  1685. }
  1686. }
  1687. else {i+=2;}
  1688.  
  1689. }
  1690.  
  1691. if ((Tmax - Tmin) < 40) // soit 4°C
  1692. {
  1693. // dans ce cas on impose des valeurs permettant de calculer une échelle fonctionnelle
  1694. Tmin-=10; // c.à.d -=1°C
  1695. Tmax+=10; // c.à.d +=1°C
  1696. }
  1697.  
  1698. Tmoy=(Tmin+Tmax)/2;
  1699.  
  1700. //Serial.print("Tmin = "); //Serial.println(Tmin);
  1701. //Serial.print("Tmax = "); //Serial.println(Tmax);
  1702. //Serial.println(" ");
  1703. }
  1704.  
  1705.  
  1706. void fixe_Tmin_tmax()
  1707. {
  1708. // pour l'echelle verticale
  1709. // en remplacement éventuel de la fonction précédente
  1710.  
  1711. Tmin=60; // 6°C
  1712. Tmax=220; // 22°C
  1713.  
  1714. Tmoy=(Tmin+Tmax)/2;
  1715. echelle_T =(float) (gradu_maxT - gradu_minT)/(Scope_1.dy);
  1716. }
  1717.  
  1718.  
  1719.  
  1720. void fixe_Pmin_Pmax()
  1721. {
  1722. // pour l'echelle verticale
  1723. // en remplacement éventuel de la fonction suivante
  1724.  
  1725. Pmin=980;
  1726. Pmax=1050;
  1727.  
  1728. Pmoy=(Pmin+Pmax)/2;
  1729. }
  1730.  
  1731.  
  1732.  
  1733. void calcul_Pmin_Pmax()
  1734. {
  1735. //Serial.println("calcul_Pmin_Pmax()");
  1736.  
  1737. uint16_t i, p1;
  1738. Pmin=2000;
  1739. Pmax=0;
  1740. for (i=2; i< n_record_max-1 ; i++) // attention départ=2, pas 0 pour éviter de prendre en compte l'acquisition en cours
  1741. {
  1742.  
  1743. p1=lst_records[i].P;
  1744. if ((p1 !=0) && (p1 != 0xFFFF))
  1745. {
  1746. if ((p1 >800) && (p1 < Pmin)) {Pmin= p1;}
  1747. if (p1 > Pmax) {Pmax= p1;}
  1748. }
  1749.  
  1750. }
  1751.  
  1752. if ((Pmax - Pmin) < 20)
  1753. {
  1754. // dans ce cas on impose des valeurs permettant de calculer une échelle fonctionnelle
  1755. Pmin-=2; // hPa
  1756. Pmax+=2;
  1757. }
  1758.  
  1759. Pmoy=(Pmin+Pmax)/2;
  1760. /*
  1761. //Serial.print("Pmin = "); //Serial.println(Pmin);
  1762. //Serial.print("Pmax = "); //Serial.println(Pmax);
  1763. //Serial.print("Pmoy = "); //Serial.println(Pmoy);
  1764. //Serial.println(" ");
  1765. */
  1766. }
  1767.  
  1768.  
  1769. void calcul_ech_P()
  1770. {
  1771. //Serial.println("calcul_ech_P()");
  1772. echelle_P =(float) (Pmax -Pmin)/(Scope_1.dy-20);
  1773. if (echelle_P==0) {echelle_P=1;}
  1774. }
  1775.  
  1776.  
  1777.  
  1778. void verifie_data_scope()
  1779. {
  1780. //Serial.println(" ");
  1781. //Serial.println("verifie_data_scope() ");
  1782.  
  1783. if(! SDcardOk){return;}
  1784.  
  1785. int16_t T,P;
  1786. uint8_t H;
  1787. uint16_t heure_i;
  1788. uint16_t i;
  1789. uint8_t h, mn;
  1790.  
  1791. for (i=1; i<n_record_max-1; i++)
  1792. {
  1793. T=lst_records[i].T;
  1794. P=lst_records[i].P;
  1795. H=lst_records[i].H;
  1796. //heure_i=lst_records[i].heure;
  1797. //heure2=lst_records[i+1].heure;
  1798.  
  1799. h= heure_i / 60;
  1800. mn = heure_i % 60;
  1801. /*
  1802. //Serial.print(i);
  1803. //Serial.print(" Heure=");
  1804. //Serial.print(h);
  1805. //Serial.print(":");
  1806. //Serial.print(mn);
  1807. //Serial.print(" T=");
  1808. //Serial.print(T);
  1809. //Serial.print(" P=");
  1810. //Serial.print(P);
  1811. //Serial.print(" Hum=");
  1812. //Serial.print(H);
  1813. //Serial.println(" ");
  1814. */
  1815. }
  1816. }
  1817.  
  1818.  
  1819.  
  1820. void efface_glitch_P()
  1821. {
  1822. int16_t p0,p1,p2;
  1823. uint16_t i,n;
  1824.  
  1825. for(n=0; n<2; n++)
  1826. {
  1827. for (i=n_record_max-2; i>2 ; i--)
  1828. {
  1829. p0=lst_records[i].P;
  1830. p1=lst_records[i-1].P;
  1831. p2=lst_records[i-2].P;
  1832.  
  1833. if ((abs(p1-p2)) > 5)
  1834. {
  1835. lst_records[i-1].P=p0;
  1836. lst_records[i-2].P=p0;
  1837. i-=2;
  1838. }
  1839. }
  1840. }
  1841. }
  1842.  
  1843.  
  1844. void efface_glitch_T()
  1845. {
  1846. //Serial.println("efface_glitch_T()");
  1847. int16_t T0,T1,T2;
  1848. uint16_t i,n;
  1849.  
  1850. for(n=0; n<5; n++)
  1851. {
  1852. for (i=2; i<n_record_max-3 ; i++)
  1853. {
  1854. T0=lst_records[i].T;
  1855. T1=lst_records[i+1].T;
  1856. T2=lst_records[i+2].T;
  1857.  
  1858. if ((abs(T1-T2)) > 30)
  1859. {
  1860. //Serial.print("ici i="); //Serial.println(i);
  1861. lst_records[i+1].T=T0;
  1862. lst_records[i+2].T=T0;
  1863. i-=2;
  1864. }
  1865. }
  1866. }
  1867. }
  1868.  
  1869.  
  1870. float Decimale(float valeur)
  1871. {
  1872. float d;
  1873. d=valeur-floor(valeur);
  1874. return d;
  1875. }
  1876.  
  1877.  
  1878. float GetPhase(int Y, int M, int D)
  1879. {
  1880. // return "age de le Lune" (nombre allant de 0.00 à 29.xx)
  1881. float AG, IP;
  1882. uint32_t Y2, M2, K1, K2, K3, JD;
  1883. Y2=Y-floor((12-M)/10);
  1884. M2=M+9;
  1885. if (M2>=12) {M2-=12;}
  1886. K1= floor(365.25 * (Y2 + 4712));
  1887. K2= floor(30.6 * M2+0.5);
  1888. K3= floor(floor((Y2/100) + 49)*0.75)-38;
  1889. JD= K1+K2+D+59;
  1890. if (JD > 2299160) {JD=JD-K3;}
  1891. IP= Decimale((JD-2451550.1)/29.53058);
  1892. AG= IP*29.53;
  1893.  
  1894. return AG;
  1895. }
  1896.  
  1897.  
  1898.  
  1899. void calcul_moyenne_T_sur_toutes()
  1900. {
  1901. // calcul sur l'ensemble des temperatures affichées
  1902. uint16_t i;
  1903. int32_t somme_T=0;
  1904.  
  1905. for (i=0; i< n_record_max-1 ; i++)
  1906. {
  1907. somme_T += lst_records[i].T;
  1908. }
  1909. moyenne_T_sur_toutes = somme_T / n_record_max;
  1910.  
  1911. //Serial.print(" moyenne_T_sur_toutes = "); //Serial.print(moyenne_T_sur_toutes);
  1912. //Serial.print("; c.a.d: "); //Serial.print(moyenne_T_sur_toutes/10.0); //Serial.println("°C");
  1913. }
  1914.  
  1915.  
  1916.  
  1917. void trace_sur_Scope() // (courbes )
  1918. {
  1919. ///Serial.println(" ");
  1920. ///Serial.println("trace_sur_Scope()");
  1921.  
  1922. // verifie_data_scope(); // pour visu sur le port série (USB)
  1923. //efface_glitch_T();
  1924.  
  1925. calcul_moyenne_T_sur_toutes();
  1926.  
  1927. calcul_Tmin_Tmax();
  1928. //fixe_Tmin_tmax(); // pour test
  1929.  
  1930. calcul_Gradu_ech_T();
  1931.  
  1932. //efface_glitch_P();
  1933.  
  1934. //calcul_Pmin_Pmax();
  1935. fixe_Pmin_Pmax();
  1936.  
  1937. calcul_ech_P();
  1938. calcul_Gradu_ech_P();
  1939.  
  1940. //if ((acqui_valide ==1) || (scope_dat_ok==1))
  1941. {
  1942. //Serial.println(" ");
  1943. //Serial.println("Affiche courbes");
  1944.  
  1945. Scope_1.affi_gradu_horizontale(); // lignes H. efface tout au préalable
  1946. affi_graduation_temporelle();
  1947.  
  1948. affiche_courbeH();
  1949. affiche_courbeP();
  1950. affiche_courbeT();
  1951.  
  1952. }
  1953. }
  1954.  
  1955. /*
  1956. uint16_t nb_acq_heures()
  1957. {
  1958. uint16_t v1=0;
  1959. uint16_t i;
  1960.  
  1961. for (i=0; i<n_record_max-1; i++)
  1962. {
  1963. if (lst_records[i].heure != 0xFFFF) {v1++;}
  1964. }
  1965. return v1;
  1966. }
  1967. */
  1968.  
  1969.  
  1970.  
  1971. void affiche_courbeT() // Température extérieure & la graduation temporelle -> sur le scope
  1972. {
  1973. //Serial.println(" "); //Serial.println("affiche_courbeT()");
  1974.  
  1975. //TFT480.setTextColor(JAUNE, NOIR); TFT480.setFreeFont(FF5);
  1976. //String s1="affiche_courbeT()"; TFT480.drawString(s1, 50, 200);
  1977.  
  1978. //sram = freeRam(); //Serial.print("5-freeRam="); //Serial.println(sram);
  1979. // Ymax du scope = 142
  1980. //142 * 3 = 426 -> 42.6°C max
  1981. uint16_t i,j, k1;
  1982. uint16_t x0, x1, memo_x1, xt;
  1983. uint8_t nb_ech=5; // pour le lissage de la courbe
  1984. uint8_t nb1;
  1985.  
  1986. float Temperature1, memo_Temperature1; // =316 pour 31.6°C
  1987. int16_t y1, memo_y1;
  1988.  
  1989. x0=Scope_1.x0 + Scope_1.dx;
  1990.  
  1991.  
  1992. /*
  1993. 1px toutes les 5mn;
  1994. 12px = 1h
  1995. 6px = 30mn
  1996. x= x=Scope_1.dx LORSQUE heure_i = heures*60 + minutes
  1997.  
  1998. RAPPEL du format des enregistrements :
  1999. void record_enr_actuel()
  2000. // enregistre les data actuels en fin de liste avant décalage des enregistrements (par la fonction suivante)
  2001. lst_records[n_record_max].T= T_EXT_retenue; // format 325 -> 32.5°C
  2002. lst_records[n_record_max].P= Pression_lue;
  2003. lst_records[n_record_max].H= Humid_lue;
  2004. lst_records[n_record_max].heure= heures*60 + minutes;
  2005. */
  2006.  
  2007. x1=Scope_1.dx;
  2008.  
  2009. Scope_1.setCouleurTrace(JAUNE);
  2010.  
  2011. //for (i=0; i<n_record_max; i++)
  2012. for (i=n_record_max-1; i>nb_ech+1; i--)
  2013. {
  2014. memo_x1 =x1;
  2015. x1--;
  2016.  
  2017. memo_Temperature1 = Temperature1;
  2018.  
  2019. // lissage de la courbe (filtre passe bas par moyenne glissante)
  2020. Temperature1=0;
  2021. nb1=0;
  2022. for (j=0; j<nb_ech; j++)
  2023. {
  2024. k1=i-j;
  2025. if ((k1>1) && (k1 < n_record_max-1))
  2026. {
  2027. if (lst_records[k1].T !=0)
  2028. {
  2029. Temperature1 += lst_records[k1].T;
  2030. nb1++;
  2031. }
  2032. }
  2033. }
  2034. if (nb1 != 0)
  2035. {
  2036. Temperature1 /= nb1;
  2037. }
  2038.  
  2039. memo_y1=y1;
  2040. float yf;
  2041. if (echelle_T != 0) {yf = Scope_1.y0/2.0 + (Temperature1 - Tmoy) / echelle_T;}
  2042.  
  2043. y1= (int16_t) yf;
  2044.  
  2045. Scope_1.Tiret(x1, y1, memo_x1, memo_y1); // tracé de la courbe
  2046. Scope_1.Tiret(x1+1, y1, memo_x1+1, memo_y1); // pour épaissir le trait
  2047.  
  2048. //Scope_1.Plot(x1, y1); // ponctuel
  2049.  
  2050. }
  2051. }
  2052.  
  2053.  
  2054.  
  2055.  
  2056. void affiche_courbeP() // pression - sur le scope
  2057. {
  2058. //Serial.println(" "); //Serial.println("affiche_courbeP()");
  2059. /**PNM minimum : 870 hPa, au large des Philippines, près du centre du typhon Tip, le 12 octobre 1979...
  2060. Ouragan2 de classe 5 : pression au centre inférieure à 920 hPa
  2061. **/
  2062. uint8_t lissage;
  2063. uint16_t i, j, k1;
  2064. uint16_t x;
  2065. uint8_t nb_ech=15; // pour le lissage de la courbe
  2066. uint8_t nb1;
  2067. float Pression1;
  2068. //float Pi1;
  2069. int16_t y1, memo_y1;
  2070. /*
  2071. //Serial.println("affiche_courbeP()");
  2072. //Serial.print("Pmoy=");
  2073. //Serial.println(Pmoy);
  2074. //Serial.print("echelle_P=");
  2075. //Serial.println(echelle_P);
  2076. */
  2077.  
  2078. Scope_1.setCouleurTrace(VERT);
  2079.  
  2080. x=Scope_1.dx;
  2081.  
  2082.  
  2083. for (i=n_record_max-1; i>nb_ech ; i--)
  2084. {
  2085. Pression1=0;
  2086. nb1=0;
  2087.  
  2088. // lissage de la courbe (filtre passe bas par moyenne glissante)
  2089. for (j=0; j<nb_ech; j++)
  2090. {
  2091. k1=i-j;
  2092. if ((k1>1) && (k1 < n_record_max-1))
  2093. {
  2094. if (lst_records[k1].P !=0)
  2095. {
  2096. Pression1 += lst_records[k1].P;
  2097. nb1++;
  2098. }
  2099. }
  2100. }
  2101. if (nb1 != 0)
  2102. {
  2103. Pression1 /= nb1;
  2104. }
  2105.  
  2106.  
  2107. /*
  2108. //Serial.print("Pression1=");
  2109. //Serial.println(Pression1);
  2110. */
  2111.  
  2112. if (Pression1>800)
  2113. {
  2114. memo_y1 = y1;
  2115. y1 = (int16_t) (Scope_1.dy/2.0 + (Pression1-Pmoy) / echelle_P);
  2116. }
  2117.  
  2118. Scope_1.Tiret(x-1, y1, x, memo_y1);
  2119.  
  2120. x--;
  2121. }
  2122.  
  2123. }
  2124.  
  2125.  
  2126.  
  2127. void affiche_courbeH() // Humidité - sur le scope
  2128. {
  2129. //Serial.println(" "); //Serial.println("affiche_courbeH()");
  2130. uint16_t i,j, k1;
  2131. uint8_t nb_ech=20;
  2132. float Hi1, Hi2;
  2133. float h1, h2;
  2134. float ech_Hum;
  2135.  
  2136. ech_Hum = (Scope_1.dy) / 100.0;
  2137.  
  2138. Scope_1.setCouleurTrace(BLEU);
  2139.  
  2140.  
  2141. for (i=n_record_max-1; i>nb_ech ; i--)
  2142. {
  2143. // lissage de la courbe : filtre passe bas par moyenne glissante
  2144. h1=0; h2=0;
  2145. for (j=0; j<nb_ech; j++)
  2146. {
  2147. k1=i-j;
  2148. if ((k1>1) && (k1 < n_record_max-1))
  2149. {
  2150. h1 += lst_records[k1-1].H;
  2151. h2 += lst_records[k1].H;
  2152. }
  2153. }
  2154. h1/=nb_ech; h2/=nb_ech;
  2155.  
  2156. //h1=80; h2=80; // POUR TEST affichage
  2157.  
  2158. Hi1=h1 * ech_Hum;
  2159. if (Hi1>=Scope_1.dy){Hi1=Scope_1.dy-1;}
  2160. if (Hi1<1) {Hi1=1;}
  2161.  
  2162. Hi2=h2 * ech_Hum;
  2163. if (Hi2>=Scope_1.dy){Hi2=Scope_1.dy-1;}
  2164. if (Hi2<1) {Hi2=1;}
  2165.  
  2166. uint16_t H1_int, H2_int;
  2167. H1_int = (uint16_t)Hi1;
  2168. H2_int = (uint16_t)Hi2;
  2169.  
  2170. if ((H1_int > 1) && (H2_int > 1) && (abs((H1_int-H2_int)) < 20))
  2171. {
  2172. Scope_1.Tiret(i-1, H1_int, i, H2_int);
  2173. }
  2174.  
  2175. }
  2176. }
  2177.  
  2178.  
  2179. /**--------------------------------------------------------
  2180. fonctions RECEPTION & DECODAGE SONDE EXTERNE 433MHz
  2181. --------------------------------------------------------**/
  2182.  
  2183. void serial_out_datas()
  2184. {
  2185. //Serial.println("serial_out_datas()");
  2186. for (int i=0; i<n_record_max; i++)
  2187. {
  2188. //Serial.print(i);
  2189. //Serial.print(" "); //Serial.print(" T°="); //Serial.print(lst_records[i].T);
  2190. //Serial.print(" c.a.d: "); //Serial.print(lst_records[i].T / 10.0); //Serial.println("°C");
  2191. }
  2192. }
  2193.  
  2194.  
  2195.  
  2196. void init_lst_records()
  2197. {
  2198. //Serial.println("init_lst_records()");
  2199.  
  2200. uint16_t i;
  2201. //uint8_t h;
  2202. //uint8_t mn;
  2203. //uint8_t mn5;
  2204. float Tn,Pn,Hn;
  2205.  
  2206. //h= heures;
  2207. //mn=5*(minutes/5);
  2208. //mn=5*mn5;
  2209.  
  2210. for (i=0; i<n_record_max-1; i++)
  2211. {
  2212. //Serial.print(i); //Serial.print(" "); //Serial.print(h); //Serial.print(":");//Serial.print(mn);
  2213.  
  2214. // sinusoïdes :
  2215.  
  2216. // température
  2217. //Tn = 100.0 + 50.0*sin(i/20.0); // sinusoïde (pour affichage echelle consistante pour TEST) ; format 325 -> 32.5°C
  2218. //lst_records[i].T= (int16_t)Tn;
  2219. lst_records[i].T = 180; // 18°C
  2220.  
  2221. // pression
  2222. //Pn = Pression_lue + (i/20)*sin(i/27.0); // sinusoïde (pour affichage echelle consistante pour TEST)
  2223. //lst_records[i].P= (int16_t)Pn;
  2224. lst_records[i].P = 1013;
  2225.  
  2226. // humidité
  2227. //Hn = Humid_lue + (i/10)*sin(i/43.0); // sinusoïde (pour affichage echelle consistante pour TEST)
  2228. //lst_records[i].H= (uint16_t)Hn;
  2229. Hn = 50;
  2230.  
  2231. /*
  2232. if (mn>=5){mn-=5;}
  2233. else
  2234. {
  2235. mn=55;
  2236. if (h>0){h--;} else {h=23;}
  2237. }
  2238. */
  2239. }
  2240. }
  2241.  
  2242.  
  2243. void efface_buffer()
  2244. {
  2245. for(int i=0; i<120; i++)
  2246. {
  2247. buffer[i]=' ';
  2248. }
  2249.  
  2250. }
  2251.  
  2252.  
  2253. void record_enr_actuel()
  2254. {
  2255. //Serial.println("record_enr_actuel()");
  2256. // cette action est effectuée toutes les 5mn
  2257.  
  2258. //Serial.println( "record_enr_actuel()");
  2259. // enregistre les data actuels en fin de liste avant décalage des enregistrements (par la fonction suivante)
  2260. lst_records[n_record_max].T = T_EXT_retenue; // format 325 -> 32.5°C
  2261. lst_records[n_record_max].P = Pression_lue;
  2262. lst_records[n_record_max].H = Humid_lue;
  2263. //lst_records[n_record_max].heure = heures*60 + minutes;
  2264. }
  2265.  
  2266.  
  2267.  
  2268. void decale_enregistrements()
  2269. {
  2270. //Serial.println( "decale_enregistrements");
  2271. //décalage vers la gauche
  2272. uint16_t i;
  2273.  
  2274. //TFT480.Set_Text_Back_colour(BLANC);
  2275. //TFT480.Set_Text_colour(NOIR);
  2276. //TFT480.Set_Text_Size(2);
  2277. //TFT480.Print_String("<", 440, 170);
  2278.  
  2279. for (i=0; i<n_record_max; i++)
  2280. {
  2281. // decalage
  2282. lst_records[i].T=lst_records[i+1].T; // ->remarque : l'enr 'n_record_max' est pris par le 'i+1' et est dupliqué vers la gauche
  2283. lst_records[i].P=lst_records[i+1].P;
  2284. lst_records[i].H=lst_records[i+1].H;
  2285. }
  2286. }
  2287.  
  2288.  
  2289. void write_scope_on_SDcard() // enregistre en binaire la courbe de T°C affichée sur le scope
  2290. {
  2291. //Serial.println( "write_scope_on_SDcard()");
  2292. todo_wr_scope_on_sd=0;
  2293. if (SDcardOk==0) {return;}
  2294. //Serial.println("write_scope_on_SDcard()");
  2295.  
  2296. // enregistre le contenu du "scope" afin de le réafficher après une coupure de l'alimentation
  2297. // Led1.setCouleur(BLEU_CLAIR);
  2298.  
  2299. if(SD.exists("/data/scope.dat")) {SD.remove("/data/scope.dat");} // efface le fichier précédent s'il existe
  2300. File binFile1 = SD.open("/data/scope.dat", FILE_WRITE); //re-création et ouverture du fichier binaire (vierge) en écriture
  2301. if (binFile1)
  2302. {
  2303. binFile1.write((const uint8_t *)&lst_records, sizeof(lst_records));
  2304. binFile1.close(); // referme le fichier
  2305. }
  2306. // Led1.setCouleur(NOIR);
  2307.  
  2308. }
  2309.  
  2310.  
  2311. void RAZ_data()
  2312. {
  2313. //Serial.println(" "); //Serial.println("RAZ_data()");
  2314. todo_init_record =0;
  2315.  
  2316. // TFT480.Set_Draw_color(NOIR);
  2317. // TFT480.Fill_Rectangle(19,146, 467,316);
  2318. Scope_1.efface_surface();
  2319.  
  2320. // Led3.setCouleur(NOIR);
  2321. //TFT480.Set_Text_Back_colour(NOIR);
  2322. //TFT480.Set_Text_colour(VIOLET);
  2323. //TFT480.Set_Text_Size(3);
  2324. //TFT480.Print_String("RAZ SCOPE DATA", 120, 250);
  2325. //delay(1000);
  2326.  
  2327. Scope_1.efface_surface();
  2328.  
  2329. init_lst_records();
  2330. write_scope_on_SDcard();
  2331. read_scope_on_SDcard();
  2332. trace_sur_Scope();
  2333. }
  2334.  
  2335.  
  2336.  
  2337. void read_scope_on_SDcard()
  2338. {
  2339. if (SDcardOk==0) {return;}
  2340. //Serial.println(" ");
  2341. //Serial.println("lecture du fichier /data/scope.dat sur SDcard");
  2342. //Serial.println(" ");
  2343.  
  2344.  
  2345. //const char* filename1;
  2346. //filename1="SCOPE.DAT";
  2347. //filename1+='\0';
  2348.  
  2349. File file_1;
  2350. file_1 = SD.open("/data/scope.dat"); //ouverture du fichier en lecture
  2351. if(!file_1)
  2352. {
  2353. file_1.close();
  2354. //Serial.println("file nof found");
  2355. return;
  2356. }
  2357. else
  2358. {
  2359. //Serial.println("file /data/scope.dat ok");
  2360. file_1.read((uint8_t *)&lst_records, sizeof(lst_records));
  2361. file_1.close(); // referme le fichier
  2362. }
  2363. }
  2364.  
  2365.  
  2366.  
  2367. void print_meteo_on_SDcard()
  2368. {
  2369. //Serial.println("print_meteo_on_SDcard()");
  2370.  
  2371. String date;
  2372. String heure;
  2373. String Str1;
  2374. // Led1.setCouleur(VERT);
  2375. // pour éviter les problèmes avec les températures en °C négatives (< 0°C l'hiver), on passe en kelvin
  2376. uint16_t T_EXT_kelvin = T_EXT_retenue +2730; // en prenant T0abs= -273°C (et pas -273,15 °C...)
  2377.  
  2378. date = String(annee)+"-"+conv_time(mois)+"-"+conv_time(jour);
  2379. heure = conv_time(heures)+":"+conv_time(minutes);
  2380. Str1 = date +" [ "+ heure + " ]";
  2381.  
  2382. Str1 += " ; T(ext)=" + String(T_EXT_kelvin);
  2383. Str1 +=" ; H=" +String(Humid_lue)+ " ; P=" + String(Pression_lue);
  2384.  
  2385. //Serial.println(" ");
  2386. //Serial.println("ecriture data sur SDcard :");
  2387. //Serial.print(Str1);
  2388. //Serial.println(" ");
  2389.  
  2390. File dataFile = SD.open("/data/data.txt", FILE_APPEND); // ouverture du fichier en écriture à la fin du fichier
  2391. if (dataFile)
  2392. {
  2393. dataFile.println(Str1); // écriture 1 ligne à la fin du fichier "data.txt" sur la micro SDcard
  2394. dataFile.close(); // referme le fichier
  2395.  
  2396. // résultat sur la SDcard : ajout d'une ligne comme celle-ci :
  2397. // 2020-09-19 [ 15:17 ] ; T(ext)=3004 ; T(int)=3018 ; H=62 ; P=1003
  2398.  
  2399. //Note : pour lire les températures il faut retrancher 2730 puis /10
  2400. }
  2401. else { Serial.println("error opening /data/data.txt");} // pour info
  2402. delay(100);
  2403. // Led1.setCouleur(NOIR);
  2404.  
  2405. nb_wr_SD++;
  2406.  
  2407. /*
  2408. TFT480.Set_Text_Size(1);
  2409. TFT480.Set_Text_Back_colour(NOIR);
  2410. TFT480.setTextColor(BLANC);
  2411. TFT480.Print_Number_Int(nb_wr_SD,440,70,6,' ',10);
  2412. */
  2413. }
  2414.  
  2415.  
  2416. void analyse_signal_primaire()
  2417. {
  2418.  
  2419. char lettre_i;
  2420. pulseWidth = micros() - memo_micros2; // VOIR la doc micros() -> nb de microsecondes depuis le démarrage de la carte
  2421. memo_micros2 = micros();
  2422.  
  2423. //Serial.print('\n');
  2424. //Serial.print(pulseWidth);
  2425.  
  2426. /**
  2427. Une analyse des signaux fait apparaitre ceci:
  2428. 4 durées caractéristiques 490us ; 95us ; 1940us; 3700us
  2429. - 490us c'est la durée des tops HIGHT (tous identiques)
  2430. - 950ums ; 1940ms ; 3700ms sont les différentes durées à l'état LOW qui séparent les tops ()
  2431.  
  2432. appelons ces durées par une lettre :
  2433. A -> 490us
  2434. B -> 950us
  2435. C -> 1940us
  2436. D -> 3700us
  2437. **/
  2438.  
  2439. if ( (pulseWidth < 460) )
  2440. {
  2441. Led2.setCouleur(VERT);
  2442. return;
  2443. }
  2444. if ( (pulseWidth >= 460) && (pulseWidth < 530) )
  2445. {
  2446. //Serial.print('A');
  2447. lettre_i = 'A';
  2448. buffer[i_buff]= lettre_i; //memorisation de la lettre
  2449. i_buff++;
  2450. if (i_buff>MAX_BUFFER-1) {i_buff=0;}
  2451. }
  2452. if ( (pulseWidth >= 910) && (pulseWidth < 1100) )
  2453. {
  2454. Led2.setCouleur(JAUNE);
  2455. //Serial.print('B');
  2456. lettre_i = 'B';
  2457. buffer[i_buff]= lettre_i;
  2458. i_buff++;
  2459. if (i_buff>MAX_BUFFER-1) {i_buff=0;}
  2460. }
  2461. if ( (pulseWidth >= 1900) && (pulseWidth < 1970) )
  2462. {
  2463. //Serial.print('C');
  2464. lettre_i = 'C';
  2465. buffer[i_buff]= lettre_i;
  2466. i_buff++;
  2467. if (i_buff>MAX_BUFFER-1) {i_buff=0;}
  2468. }
  2469. if (pulseWidth >= 2000) // sonne la fin de la séquence...
  2470. {
  2471. if (i_buff>=72)
  2472. {
  2473. Led2.setCouleur(ROUGE);
  2474. //Serial.println(' ');
  2475. decodeBuffer();
  2476. //Serial.print(" ");
  2477. lit_temperature(); // de la sonde ext 433MHz
  2478. lit_humidite(); // de la sonde ext 433MHz
  2479.  
  2480. message="";
  2481. //memo_micros2 = micros();
  2482. efface_buffer();
  2483.  
  2484. // premiere_passe==0;
  2485.  
  2486. num_acquisition++;
  2487. if (num_acquisition >=6)
  2488. {
  2489. //Serial.println(" "); //Serial.println("num_acquisition >=6");
  2490. num_acquisition=0;
  2491. acqui_valide =1;
  2492.  
  2493. calcul_confiance();
  2494. T_EXT_retenue = meilleure_valeur();
  2495.  
  2496. calcul_Tmin_Tmax();
  2497. //fixe_Tmin_tmax();
  2498.  
  2499. calcul_Gradu_ech_T();
  2500. acqui_Pression();
  2501. affiche_meteo();
  2502. }
  2503. }
  2504. i_buff=0;
  2505. }
  2506. /**
  2507. résultat :
  2508. AB AC AB AB AC AB AC AC AC AB AB AB AB AB AB AB AC AC AC A
  2509. 0 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 1 1 1
  2510.  
  2511. Les motifs qui apparaissent sont AB et AC
  2512. Il est vraissemblable que AB code pour 0 et AC pour 1 (ou l'inverse))
  2513. **/
  2514. }
  2515.  
  2516.  
  2517.  
  2518. int16_t strbinToInt(String str1, uint8_t nb_bits)
  2519. {
  2520. uint8_t i;
  2521. int16_t result=0;
  2522. bool negatif = false;
  2523. int tableau1[10];
  2524.  
  2525. for (i=0; i<nb_bits; i++) { if(str1[i] == '0') {tableau1[i]=0;} else {tableau1[i]=1;} }
  2526. if (tableau1[0] == 1) {negatif=true;}
  2527. if (negatif) { for (i=0; i<nb_bits; i++) {tableau1[i] = 1-tableau1[i]; } }
  2528. for (i=0; i<nb_bits; i++) { if (tableau1[(nb_bits-1)-i]==1) {result+= 1<<i;} }
  2529. if (negatif) { result+=1; result = -result; }
  2530. return result;
  2531. }
  2532.  
  2533.  
  2534. void calcul_confiance()
  2535. {
  2536. // les 6 valeurs reçues à l'issue d'une salve ne sont pas toujours toutes identiques, dû à des aléas de transmission
  2537. // ici on affecte à chaque valeur le nombre d'occurences (qui va de 1 si valeur "exotique" à 5 si toutes identiques)
  2538. // ces nombres d'occurences sont inscrites dans un tableau semblable à celui des valeurs lues (mêmes indices).
  2539. uint8_t i,j;
  2540. uint16_t Ti;
  2541.  
  2542.  
  2543. for(i=0; i<nb_de_salves; i++)
  2544. {
  2545. confiance[i]=0; //RAZ
  2546. }
  2547.  
  2548. for(i=0; i<nb_de_salves; i++)
  2549. {
  2550. Ti=T_EXT_lue[i];
  2551. for(j=0; j<nb_de_salves; j++)
  2552. {
  2553. if (T_EXT_lue[j] == Ti) {confiance[i]++;}
  2554. }
  2555. }
  2556. }
  2557.  
  2558.  
  2559.  
  2560. uint16_t meilleure_valeur()
  2561. {
  2562. // trouver la valeur ayant obtenu le meilleur score de présence dans la salve (= le plus grand nombre d'occurences)
  2563. // à l'issue de la fonction "calcul_confiance()"
  2564.  
  2565. uint16_t meilleure;
  2566. uint16_t i;
  2567.  
  2568. meilleure = T_EXT_lue[0];
  2569. for(i=0; i<nb_de_salves; i++)
  2570. {
  2571. if (confiance[i+1] > confiance[i]) {meilleure = T_EXT_lue[i+1];}
  2572. }
  2573. return (meilleure);
  2574. }
  2575.  
  2576.  
  2577.  
  2578. void lit_temperature() // de la sonde ext 433MHz
  2579. // source = message
  2580. //(message a été constitué par la fonction "decodeBuffer()")
  2581. // destination -> tableau "T_EXT_lue[5]" des 5 valeurs lues (correspondant à une salve)
  2582. {
  2583.  
  2584. uint8_t p; // position de la séquence de référence fixe
  2585. uint8_t decal;
  2586. uint8_t nbBits;
  2587. int16_t valeur_lue;
  2588. String s1;
  2589.  
  2590. p = message.indexOf("100000",6); // séquence apparemment fixe > à la 6eme position... (
  2591. //le début du message change à chaque changement des piles !)
  2592.  
  2593. nbBits=10; // nb de bits à lire
  2594. decal=6; // décalage depuis la position p (ici les 6 bits de la séquence fixe)
  2595.  
  2596.  
  2597. if (p>0)
  2598. {
  2599. s1=message.substring(p+decal, p+decal+nbBits); // TFT480.drawString(s1, 100, 100); // en binaire
  2600.  
  2601. //ici, par exemple s1 = "0011111011" -> 251 décimal -> 25.1 °C
  2602. // ou bien s1 = "1111100101" -> TEMPERATURE NEGATIVE "1111100101" -> -27 decimal -> -2.7°C
  2603.  
  2604. //s1 = "1100010110"; //-234 decimal -> -23.4°C POUR TEST valeurs négatives
  2605.  
  2606. valeur_lue=strbinToInt(s1, nbBits);// =316 pour 31.6°C //TFT480.Print_Number_Int(T1,220,100,3,' ',10);
  2607.  
  2608. //Serial.print(heures); //Serial.print(":"); //Serial.println(minutes);
  2609. //Serial.print("Temperature EXT= ");
  2610. float temp_f = valeur_lue /10.0;
  2611. //Serial.println(temp_f);
  2612.  
  2613. // todo // expérimenter avec des températures négatives (sonde au frigo !!)
  2614.  
  2615. if ((valeur_lue != 0) )
  2616. {
  2617. T_EXT_lue[num_acquisition] = valeur_lue; // on enregistre la nouvelle valeur (parmi 5 qui constituent une salve)
  2618. }
  2619.  
  2620. }
  2621. }
  2622.  
  2623.  
  2624.  
  2625. void lit_humidite()
  2626. {
  2627. // de la sonde ext 433MHz
  2628. uint8_t p; // position de la séquence de référence fixe
  2629. uint8_t decal;
  2630. uint8_t nbBits;
  2631. uint16_t valeur_precedente, valeur_lue;
  2632. String s1;
  2633.  
  2634. p = message.indexOf("100000",6); // séquence apparemment fixe
  2635. nbBits=7; // nb de bits à lire
  2636. decal=6+10+5; // décalage depuis la position p
  2637.  
  2638. if (p>0)
  2639. {
  2640. s1=message.substring(p+decal, p+decal+nbBits);
  2641. memo_Humid_lue = Humid_lue;
  2642. valeur_lue = strbinToInt(s1,nbBits); //TFT480.Print_Number_Int(T1,220,100,3,' ',10);
  2643. valeur_precedente = memo_Humid_lue;
  2644.  
  2645. /*
  2646. if ((valeur_lue != 0) && (valeur_lue < 100) ) // 100% max !
  2647. {
  2648. if ( abs(valeur_lue - valeur_precedente) < 20 ) // delta de 20% max
  2649. {
  2650. Humid_lue = valeur_lue; // on renregistre la nouvelle valeur
  2651. }
  2652. else // au delà -> anti-glich
  2653. {
  2654. if ( valeur_lue > valeur_precedente) {Humid_lue = valeur_precedente+5;}
  2655. if ( valeur_lue < valeur_precedente) {Humid_lue = valeur_precedente-5;}
  2656. }
  2657. }
  2658. */
  2659. if ((valeur_lue != 0) && (valeur_lue < 100) ) // 100% max !
  2660. {
  2661. Humid_lue = valeur_lue; // on renregistre la nouvelle valeur
  2662. }
  2663. }
  2664. }
  2665.  
  2666.  
  2667.  
  2668. void decodeBuffer()
  2669. // transcode les 'A' et 'B' et 'C' qui représentent des durées en '0' et '1' -> destination = message
  2670. // pour la suite du traitement voir fonction "lit_temperature()"
  2671. {
  2672.  
  2673. //Serial.println(" "); //Serial.println("reception signal 433MHz sonde externe :");
  2674. int k;
  2675. char car1;
  2676. char car2;
  2677. k=0;
  2678. while (k<=73)
  2679. {
  2680. car1=buffer[k];
  2681. car2=buffer[k+1];
  2682. if ((car1=='A') &&(car2=='B'))
  2683. {
  2684. message += "0";
  2685. //TFT480.drawString("0", 4*k, 10*aff_y);
  2686. //Serial.print(0);
  2687. }
  2688. else if ((car1=='A') &&(car2=='C'))
  2689. {
  2690. message += "1";
  2691. //TFT480.drawString("1", 4*k, 10*aff_y);
  2692. //Serial.print(1);
  2693. }
  2694. k++;
  2695. }
  2696. //Serial.println(' ');
  2697. efface_buffer();
  2698. aff_y++;
  2699. if (aff_y>50) {aff_y = 1;}
  2700.  
  2701. nb_acqui433++;
  2702.  
  2703. TFT480.setFreeFont(FF1);
  2704. TFT480.setTextColor(BLANC, NOIR);
  2705.  
  2706. if (nb_acqui433>2)
  2707. {
  2708. String s1=String (nb_acqui433);
  2709. TFT480.drawString(s1, Led2.x0+15, Led2.y0-1); // dans le cadre 2
  2710. }
  2711. else {TFT480.fillRect(Led2.x0+14, Led2.y0-1, 25, 14, NOIR);} // efface
  2712.  
  2713.  
  2714. //Serial.println("-----------------------------------");
  2715. }
  2716.  
  2717. /**
  2718.  
  2719. RESULTS
  2720.  
  2721. 2020-09-07 PM
  2722. ABABACACACACABACACABABABABABABABACACACACACACACACACACACACABABACABACABABACA-␍␊
  2723. 001111011000000011111111111100101001-␍␊
  2724.  
  2725. 2020-09-08 AM
  2726. ABACABACABABABACACABABABABABABABACACACABACACABACACACACACABABACABACACACABA-␍␊
  2727. 010100011000000011101101111100101110-␍␊
  2728.  
  2729. ABACABABABACACABABABABABABABACACACACACACACABACACACACABABACACABABACACA-␍␊
  2730. 0100011000000011111110111100110011-␍␊
  2731.  
  2732. ABACABABABABABABACABABABABABABABACACACACACACABABACACACACABABACABACACABABA-␍␊
  2733. 010000001000000011111100111100101100-␍␊
  2734.  
  2735. ABACABABABACABACACABABABABABABABACACACACABACACACACACACACABABACABACACABACA-␍␊
  2736. 010001011000000011110111111100101101-␍␊
  2737.  
  2738. ABABABACABACACABABABABABABABACACACACABACACABACACACACABABACABACACABACA-␍␊
  2739. 010001011000000011110110111100101101-␍␊
  2740.  
  2741. ABACABABABACABACACABABABABABABABACACACACABACABABACACACACABABACABACACABACA-␍␊
  2742. 010001011000000011110100111100101101-␍␊
  2743.  
  2744.  
  2745.  
  2746. 00111000 100000 0100000111 111100 110011-␍␊
  2747. 01000000 100000 0011111111 111100 110010
  2748. 01000001 100000 0011111110 111100 110001
  2749. 01000001 100000 0100000000 111100 101111-␍␊
  2750. 01000001 100000 0100000000 111100 10111-␍␊
  2751.  
  2752. AVEC temperature variable (R ajustable en // sur la thermistance) :
  2753. 00111101 100000 0011111111 111100 101001-␍␊
  2754.  
  2755. 01001000 100000 0011111011 11110 0101010-␍␊ 251 (sans la R ajustable en //) -> 25,1 °C ?
  2756. 01001000 100000 0100011001 11110 0100110-␍␊ 281 (sans la R mais chauffée avec le doigt) -> 28,1 °C ?
  2757. 01100110 100000 0110101100 11110 0011111-␍␊ 428
  2758. 10101110 100000 0111001100 11110 0100010-␍␊ 460
  2759. 10100101 100000 0111011100 11110 0100001-␍␊ 476
  2760. 10011100 100000 0111101110 11110 0100000-␍␊ 494
  2761. 10001101 100000 1000001110 11110 0011101-␍␊ 526
  2762. 10000100 100000 1000100001 11110 0011100-␍␊ 545
  2763. 01110100 100000 1001001011 11110 0011011-␍␊ 587
  2764. 01100101 100000 1001110111 11110 0011001-␍␊ 631
  2765. 01011000 100000 1010100010 11110 0011011-␍␊ 674
  2766. 01000110 100000 1011000010 11110 0011011-␍␊ 706
  2767. 00011111 100000 1011011010 11110 0011010-␍␊ 730
  2768.  
  2769. 10000001 100000 0010000111 11110 0111101
  2770.  
  2771. 11000101 100000 0000000001 11110 0111100 ␍␊
  2772. 11000101 100000 0000000010 11110 0111100
  2773. 11000101 100011 1111111011 11110 0111100 ␍␊ <- TEMPERATURE NEGATIVE
  2774. 11000101 100011 1111100101 11110 0111100 ␍␊ <- TEMPERATURE NEGATIVE "1111100101" -> -27 decimal -> -2.7°C
  2775. 11000101 100011 1111100011 11110 1000000
  2776. 11000101 100000 0000010101 11110 0111011 ␍␊
  2777. 11000101 100000 0000010101 11110 0111100
  2778.  
  2779.  
  2780.  
  2781. ANALYSE "colonne" 3 (= température):
  2782.  
  2783. 0110101100 = 428
  2784. 0111001100 = 460
  2785. 0111011100 = 476
  2786. 0111101110 = 494
  2787. 1000001110 = 526
  2788. 1000100001 = 545
  2789. 1001001011 = 587
  2790. 1001110111 = 631
  2791. 1010100010 = 674
  2792. 1011000010 = 706
  2793. 1011011010 = 730
  2794.  
  2795.  
  2796.  
  2797. Humidité (colonne 5):
  2798.  
  2799.  
  2800.  
  2801. **/
  2802.  
  2803.  
  2804.  
  2805. /** -------------------------------------------------------
  2806.   Sensor local BMP280 (Pression & température)
  2807. --------------------------------------------------------**/
  2808.  
  2809.  
  2810. Adafruit_BMP280 bmp; // use I2C interface
  2811. Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor();
  2812. Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor();
  2813.  
  2814. void init_BMP280()
  2815. {
  2816. Serial.println(("BMP280 Sensor event test"));
  2817.  
  2818. if (!bmp.begin())
  2819. {
  2820. Serial.println(F("Could not find BMP280 sensor !"));
  2821. while (1) delay(10);
  2822. }
  2823. else {Serial.println(F("BMP280 sensor OK!")); }
  2824.  
  2825. /* Default settings from datasheet. */
  2826. bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
  2827. Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
  2828. Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
  2829. Adafruit_BMP280::FILTER_X16, /* Filtering. */
  2830. Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
  2831.  
  2832. bmp_temp->printSensorDetails();
  2833. }
  2834.  
  2835.  
  2836.  
  2837. void acqui_Pression()
  2838. {
  2839. //Serial.println("acqui_Pression()");
  2840. /**
  2841. pression athmosphérique normale au niveau de la mer = 1013 hPa
  2842. 0m ->1013 hPa
  2843. 1000m-> 899 hPa
  2844. -soit 1016-899 = 114 hPa / 1000 m (attention la variation n'est pas linéaire, au dessus de 1000 m, ce n'est plus exact)
  2845. -soit 11.4 hPa / 100 m
  2846. vu que mon capteur est situé à 100 m d'altitude, je vais rajouter 11.4 hPa à la mesure
  2847. afin d'obtenir des valeurs cohérentes par rapport aux information météorologiques
  2848. */
  2849.  
  2850. sensors_event_t temp_event, pressure_event;
  2851. bmp_pressure->getEvent(&pressure_event);
  2852.  
  2853. float pression_acq = pressure_event.pressure;
  2854.  
  2855. //Serial.print("pression_acq = ");
  2856. //Serial.print(pression_acq);
  2857. //Serial.println(" hPa");
  2858.  
  2859. if ( isnan (pression_acq) || (pression_acq < 600) || (pression_acq > 1100)) // cas par exemple où le capteur n'est pas connecté...
  2860. {
  2861. //Serial.println("ERREUR !");
  2862. //Serial.println();
  2863. Pression_lue = 1013;
  2864. return;
  2865. }
  2866.  
  2867. pression_acq += 11.4; // mon capteur est situé à 100 m d'altitude, voir commentaire ci-dessus
  2868. Pression_lue = (int16_t)pression_acq;
  2869.  
  2870. //Serial.print(Pression_lue);
  2871. //Serial.println(" hPa");
  2872. //Serial.println();
  2873. }
  2874.  
  2875.  
  2876. /** ***********************************************************************************
  2877. CAPTURE D'ECRAN vers SDcard
  2878. ***************************************************************************************/
  2879.  
  2880.  
  2881.  
  2882. void write_TFT_on_SDcard() // enregistre
  2883. {
  2884. todo_wr_ecran_on_sd =0;
  2885. if (SDcardOk==0) {return;}
  2886. uint16_t x, y;
  2887. uint16_t color565;
  2888. uint16_t bmp_color;
  2889. uint8_t R, G, B;
  2890.  
  2891. if( ! SD.exists("/bmp/capture2.bmp")) {return;}
  2892. File File1 = SD.open("/bmp/capture2.bmp", FILE_WRITE); // ouverture du fichier binaire (vierge) en écriture
  2893. if (File1)
  2894. {
  2895. /*
  2896. Les images en couleurs réelles BMP888 utilisent 24 bits par pixel:
  2897. Il faut 3 octets pour coder chaque pixel, en respectant l'ordre de l'alternance bleu, vert et rouge.
  2898. */
  2899. uint16_t bmp_offset = 138;
  2900. File1.seek(bmp_offset);
  2901.  
  2902. TFT480.setFreeFont(FF0);
  2903. TFT480.setTextSize(1);
  2904. TFT480.setTextColor(JAUNE, NOIR);
  2905.  
  2906. for (y=320; y>0; y--)
  2907. {
  2908. for (x=0; x<480; x++)
  2909. {
  2910. color565=TFT480.readPixel(x, y);
  2911. RGB565_to_888(color565, &R, &G, &B);
  2912. File1.write(B); //G
  2913. File1.write(G); //R
  2914. File1.write(R); //B
  2915. }
  2916. String s1=String(y/10); TFT480.drawString(s1, 450, 118); // affiche compte à rebour
  2917. }
  2918.  
  2919. File1.close(); // referme le fichier
  2920.  
  2921.  
  2922. //TFT480.Fill_Rectangle(50, 300, 65, 310); // efface le compte à rebour
  2923. }
  2924.  
  2925. }
  2926.  
  2927.  
  2928.  
  2929. /** ***************************************************************************************
  2930. CLASS Etiquette // affiche un nombre ou un petit texte dans un rectangle
  2931. ainsi que (en plus petit) deux valeurs supplémentaires, par ex: les valeurs mini et maxi
  2932. ********************************************************************************************/
  2933.  
  2934. // Constructeur
  2935. Etiquette::Etiquette()
  2936. {
  2937.  
  2938. }
  2939.  
  2940.  
  2941. void Etiquette::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi, char nom_i[12])
  2942. {
  2943. x0 = xi;
  2944. y0 = yi;
  2945. dx = dxi;
  2946. dy = dyi;
  2947.  
  2948. for (int i=0; i<12; i++) {nom[i]=nom_i[i];}
  2949. nom[12]='\0'; // zero terminal
  2950.  
  2951. //TFT480.Set_Text_Back_colour(0, 0, 0);
  2952. ///TFT480.setTextColor(10, 10, 5);
  2953.  
  2954. traceCadre();
  2955. efface();
  2956. }
  2957.  
  2958.  
  2959. void Etiquette::traceCadre()
  2960. {
  2961. TFT480.drawRect(x0, y0+5, dx, dy, couleur_cadre);
  2962. }
  2963.  
  2964.  
  2965. void Etiquette::efface()
  2966. {
  2967. TFT480.fillRect(x0+1, y0+6, dx-2, dy-2, NOIR);
  2968. affi_nom();
  2969. }
  2970.  
  2971.  
  2972. void Etiquette::affi_nom()
  2973. {
  2974. // sur le coin en haut à gauche
  2975. TFT480.setFreeFont(FF0);
  2976. TFT480.setTextSize(1);
  2977. TFT480.setTextColor(BLANC, NOIR);
  2978. TFT480.drawString(nom, x0, y0);
  2979. }
  2980.  
  2981.  
  2982. void Etiquette::setCouleurTxt(uint16_t couleur_i)
  2983. {
  2984. couleur_txt = couleur_i;
  2985. }
  2986.  
  2987.  
  2988. void Etiquette::setCouleurCadre(uint16_t couleur_i)
  2989. {
  2990. couleur_cadre = couleur_i;
  2991. }
  2992.  
  2993.  
  2994. void Etiquette::setCouleurFond(uint16_t couleur_i)
  2995. {
  2996. couleur_fond = couleur_i;
  2997. }
  2998.  
  2999.  
  3000. void Etiquette::setCouleurNom(uint16_t couleur_i)
  3001. {
  3002. couleur_nom = couleur_i;
  3003. }
  3004.  
  3005.  
  3006.  
  3007. void Etiquette::flashFond(uint16_t couleur_i)
  3008. {
  3009. couleur_fond = couleur_i;
  3010. //TFT480.setTextColor(couleur_fond);
  3011. TFT480.fillRect(x0, y0, x0+dx, y0+dy, couleur_fond);
  3012. delay(10);
  3013. //TFT480.setTextColor(NOIR);
  3014. TFT480.fillRect(x0, y0, x0+dx, y0+dy, NOIR);
  3015. traceCadre();
  3016. affi_nom();
  3017. }
  3018.  
  3019.  
  3020. void Etiquette::affiche_int(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3], uint32_t v_min, uint32_t v_max)
  3021. {
  3022. String s1;
  3023. // 'valeur' sera afficée en gros, 'v_min' et 'v_max' en plus petit dans le coin du cadre (facultativement)
  3024. // pour ne pas afficher 'v_min' et 'v_max', il faut que toutes les deux soient ==0
  3025.  
  3026. uint16_t z;
  3027.  
  3028. for (int i=0; i<3; i++) {txt_unite[i]=txt_unite_i[i];}
  3029. txt_unite[3]='\0'; // zero terminal
  3030.  
  3031. efface(); // efface le contenu précédent
  3032.  
  3033. TFT480.setFreeFont(FF7);
  3034. TFT480.setTextSize(1);
  3035. TFT480.setTextColor(couleur_txt, NOIR);
  3036.  
  3037. z=0; if ((valeur<1000)&&(valeur>100)) {z=20;}
  3038. s1=String(valeur); TFT480.drawString(s1, x0+20+z, y0+25);
  3039.  
  3040. TFT480.setFreeFont(FF2);
  3041. TFT480.setTextColor(couleur_txt, NOIR);
  3042. TFT480.drawString(txt_unite, x0+120, y0+35); // ex : mm, kHz, etc...
  3043.  
  3044.  
  3045. if((v_min !=0 )&&(v_max !=0 ))
  3046. {
  3047. // affiche valeurs min et max en haut à droite
  3048. TFT480.setFreeFont(FF0);
  3049. z=0; if (v_max<1000) {z=5;}
  3050. s1=String(v_max); TFT480.drawString(s1, x0+140+z,y0+9,3);
  3051.  
  3052. z=0; if (v_min<1000) {z=5;}
  3053. s1=String(v_min); TFT480.drawString(s1, x0+140+z, y0+17,3);
  3054. }
  3055. }
  3056.  
  3057.  
  3058.  
  3059. void Etiquette::affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3], float v_min, float v_max)
  3060. {
  3061. String s1;
  3062. for (int i=0; i<3; i++) {txt_unite[i]=txt_unite_i[i];}
  3063. txt_unite[3]='\0'; // zero terminal
  3064.  
  3065. efface(); // efface le contenu précédent
  3066.  
  3067. //TFT480.Print_Number_Float(valeur, nb_dec, x0+20, y0+18, '.', nb_chiffres, ' ');
  3068.  
  3069. TFT480.setFreeFont(FF7);
  3070. TFT480.setTextSize(1);
  3071. TFT480.setTextColor(couleur_txt, NOIR);
  3072. s1=String(valeur, 1); TFT480.drawString(s1, x0+20, y0+25);
  3073.  
  3074. TFT480.setFreeFont(FF2);
  3075. TFT480.setTextColor(couleur_txt, NOIR);
  3076. TFT480.drawString(txt_unite, x0+110,y0+35); // ex : deg, hPa, mm, kHz ...
  3077.  
  3078.  
  3079. if((v_min !=0 )||(v_max !=0 ))
  3080. {
  3081. uint16_t z;
  3082.  
  3083. // affiche valeurs min et max en haut à droite
  3084. //TFT480.Set_Text_Back_colour(NOIR);
  3085. //TFT480.setTextColor(GRIS_CLAIR);
  3086. //TFT480.Set_Text_Size(1);
  3087.  
  3088. TFT480.setFreeFont(FF0);
  3089. TFT480.setTextColor(BLANC, NOIR);
  3090.  
  3091. z=0; if (v_max<10) {z=6;}
  3092. //TFT480.Print_Number_Float(v_max, 1, x0+140+z, y0+9, '.', 3, ' ');
  3093. s1=String (v_max, 3); TFT480.drawString(s1, x0+140+z, y0+9);
  3094.  
  3095. z=0; if (v_min<10) {z=6;}
  3096. //TFT480.Print_Number_Float(v_min, 1, x0+140+z, y0+17, '.', 3, ' ');
  3097. s1=String (v_min, 3); TFT480.drawString(s1, x0+140+z, y0+17);
  3098. }
  3099. }
  3100.  
  3101.  
  3102.  
  3103.  
  3104. void Etiquette::affiche_HEXA(uint32_t valeur)
  3105. {
  3106. // affiche un nombre en representation hexadécimale
  3107. // 16 nb_signes hexa max
  3108.  
  3109. uint8_t r;
  3110. uint8_t i;
  3111.  
  3112. char tbl[9];
  3113. char signes[17] = "0123456789ABCDEF";
  3114.  
  3115. for (i=0; i<8; i++)
  3116. {
  3117. r= valeur % 16; // modulo (reste de la division)
  3118. valeur /= 16; // quotient
  3119. tbl[7-i]=signes[r];
  3120. };
  3121. tbl[8]='\0';
  3122.  
  3123. //TFT480.Set_Text_Back_colour(NOIR);
  3124. //TFT480.setTextColor(CYAN);
  3125. // TFT480.setFont(SmallFont);
  3126. TFT480.drawString(tbl, x0+10,y0+15);
  3127. }
  3128.  
  3129.  
  3130.  
  3131.  
  3132. void Etiquette::affiche_texte(char txt_i[10])
  3133. {
  3134. for (int i=0; i<10; i++) {texte[i]=txt_i[i];}
  3135. texte[10]='\0'; // zero terminal
  3136.  
  3137. TFT480.setFreeFont(FF7);
  3138. TFT480.setTextSize(1);
  3139. TFT480.setTextColor(couleur_txt, NOIR);
  3140. TFT480.drawString(texte, x0+5,y0+18);
  3141. }
  3142.  
  3143.  
  3144. void Etiquette::affiche_string(String txt_i)
  3145. {
  3146. TFT480.setFreeFont(FF7);
  3147. TFT480.setTextSize(1);
  3148. TFT480.setTextColor(couleur_txt, NOIR);
  3149. TFT480.drawString(txt_i, x0+8,y0+18);
  3150. }
  3151.  
  3152.  
  3153.  
  3154. /** ***********************************************************************************
  3155. CLASS Scope // affiche des courbes dans une surface rectangulaire
  3156. ***************************************************************************************/
  3157.  
  3158. // Constructeur
  3159. Scope::Scope()
  3160. {
  3161.  
  3162. }
  3163.  
  3164.  
  3165. void Scope::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi)
  3166. {
  3167. x0 = xi;
  3168. y0 = yi;
  3169. dx = dxi;
  3170. dy = dyi;
  3171.  
  3172. //TFT480.setTextColor(couleur_cadre);
  3173. //TFT480.drawRect(x0, y0, dx, dy, couleur_cadre);
  3174. traceCadre();
  3175. }
  3176.  
  3177.  
  3178. void Scope::efface_surface()
  3179. {
  3180. //TFT480.setTextColor(NOIR);
  3181. TFT480.fillRect(x0+1, y0+1, dx-2, dy-2, NOIR);
  3182.  
  3183. // Rappel : Scope_1.init(18,145, 450,172);
  3184. // TFT480.fillRect(19,146, 467,316); // ok
  3185.  
  3186. }
  3187.  
  3188.  
  3189. void Scope::setCouleurCadre(uint16_t couleur_i)
  3190. {
  3191. couleur_cadre = couleur_i;
  3192. }
  3193.  
  3194.  
  3195. void Scope::setCouleurTrace(uint16_t couleur_i)
  3196. {
  3197. couleur_trace = couleur_i;
  3198. }
  3199.  
  3200.  
  3201. void Scope::traceCadre()
  3202. {
  3203.  
  3204. TFT480.drawRect(x0, y0, dx, dy, couleur_cadre);
  3205. TFT480.fillRect(x0+1, y0+1,dx-2, dy-2, NOIR);
  3206. }
  3207.  
  3208.  
  3209. void Scope::efface_libel_gradu()
  3210. {
  3211. //TFT480.setTextColor(NOIR);
  3212. TFT480.fillRect(x0-20, y0+1, x0-2, dy-2, NOIR);
  3213. }
  3214.  
  3215.  
  3216. void Scope::affi_gradu_horizontale()
  3217. {
  3218. int16_t n, nombre;
  3219. float y1, Ti, Pi;
  3220. int16_t y2;
  3221. uint16_t xi;
  3222. uint16_t i;
  3223.  
  3224. efface_surface();
  3225. efface_libel_gradu();
  3226. setCouleurTrace(GRIS_FONCE);
  3227.  
  3228. //TFT480.Set_Text_Back_colour(NOIR);
  3229. //TFT480.setTextColor(JAUNE);
  3230.  
  3231. for (n=gradu_minT; n<=gradu_maxT; n+=20) // 500->50°C
  3232. {
  3233. Ti = (float)n;
  3234. //Serial.print("n=");
  3235. //Serial.println(n);
  3236.  
  3237. //lignes horizontales Temperature:
  3238.  
  3239. y1 = Scope_1.dy/2.0 + (Ti-Tmoy) / echelle_T;
  3240.  
  3241. Tiret(1, (uint16_t)y1, dx-1, (uint16_t)y1); // trace la ligne horizontale
  3242. nombre = n/10;
  3243.  
  3244. y2 = y0+dy-3 -y1;
  3245. if (y2>(y0-20)&& // pour ne pas afficher plus haut que le haut du scope
  3246. (y2 <= (y0+dy-5)))// pour ne pas afficher trop bas (du cadre du scope et par conséquent du TFT))
  3247. {
  3248. String s1=String (int16_t(nombre));
  3249. TFT480.setFreeFont(FF0);
  3250. TFT480.setTextColor(JAUNE, NOIR);
  3251. TFT480.drawString(s1, x0-15, y2); // graduation : petits chiffres à gauche
  3252. }
  3253. }
  3254.  
  3255.  
  3256.  
  3257. //Graduation Pression:
  3258.  
  3259. int pas1 = (gradu_maxP - gradu_minP) / 5;
  3260.  
  3261. if (pas1 <= 2) {pas1 =2;} // en effet si pas1=0 la boucle suivante devient infinie...
  3262. // ce qui provoquait un bug lorsque la pression restait constante...
  3263. // et dire qu'il y a des volontaires pour s'embarquer pour la planète Mars !!!
  3264.  
  3265.  
  3266. for (n=gradu_minP; n<=gradu_maxP+5; n+=pas1)
  3267. {
  3268. Pi = (float)n;
  3269.  
  3270. y1 = dy/2.0 + (Pi-Pmoy) / echelle_P;
  3271.  
  3272. y2 = y0+dy-3 -y1;
  3273. //if (y2>(y0)&& // pour ne pas afficher plus haut que le haut du scope
  3274. //(y2 <= (y0+dy-5)))// pour ne pas afficher trop bas (du cadre du scope et par conséquent du TFT))
  3275. {
  3276. String s1=String (int16_t(n));
  3277. TFT480.setFreeFont(FF0);
  3278. TFT480.setTextColor(VERT);
  3279. TFT480.drawString(s1, x0+8, y2); // graduation : petits chiffres à gauche
  3280. }
  3281. }
  3282.  
  3283. //Graduation Humidité:
  3284.  
  3285. for (n=0; n<10; n+=2)
  3286. {
  3287. y1 = -12 + y0 + dy - 15*n;
  3288. String s1=String (10*n);
  3289. TFT480.setFreeFont(FF0);
  3290. TFT480.setTextColor(BLEU);
  3291. TFT480.drawString(s1, x0+40, y1); // graduation : petits chiffres à gauche
  3292. }
  3293. }
  3294.  
  3295.  
  3296.  
  3297. void Scope::Plot(uint16_t x, uint16_t y)
  3298. {
  3299. TFT480.drawPixel(x0+x, y0+y, couleur_trace);
  3300. }
  3301.  
  3302.  
  3303. void Scope::Cercle(uint16_t x1, uint16_t y1, uint16_t r1, uint16_t couleur_i)
  3304. {
  3305. if (x1>(dx-r1)) {return;} // pour ne pas déborder du cadre
  3306. TFT480.drawCircle(x0+x1, y0+dy-y1, r1, couleur_i);
  3307. }
  3308.  
  3309.  
  3310.  
  3311. void Scope::Tiret(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
  3312. {
  3313. if (x1==0) {x1=1;}
  3314. if (x2==0) {x2=1;}
  3315.  
  3316. if (x1>=dx-1) {x1=dx-2;}
  3317. if (x2>=dx-1) {x2=dx-2;}
  3318.  
  3319. if (y1>y0-5) {y1=y0-5;}
  3320. if (y2>y0-5) {y2=y0-5;}
  3321.  
  3322. if (y1>(y0+dy)) {y1=y0+dy;}
  3323. if (y2>(y0+dy)) {y2=y0+dy;}
  3324.  
  3325. TFT480.drawLine(x0+x1, y0+dy-y1, x0+x2, y0+dy-y2, couleur_trace);
  3326. }
  3327.  
  3328. /** ***********************************************************************************
  3329. CLASS LED // affiche une petite LED
  3330. ***************************************************************************************/
  3331.  
  3332. // Constructeur
  3333. LED::LED()
  3334. {
  3335.  
  3336. }
  3337.  
  3338.  
  3339. void LED::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi)
  3340. {
  3341. x0 = xi;
  3342. y0 = yi;
  3343. dx = dxi;
  3344. dy = dyi;
  3345.  
  3346. TFT480.drawRect(x0, y0, dx, dy, BLANC);
  3347. TFT480.fillRect(x0+1, y0+1, dx-2, dy-2, NOIR);
  3348. }
  3349.  
  3350.  
  3351. void LED::setCouleur(uint16_t couleur_i)
  3352. {
  3353. TFT480.fillRect(x0+1, y0+1, dx-2, dy-2, couleur_i);
  3354. }
  3355.  
  3356.  

14 Le fichier Station_meteo2 .h

CODE SOURCE en C++
  1.  
  2. #ifndef STATION_METEO_H
  3. #define STATION_METEO_H
  4.  
  5.  
  6. String version_h="2.0";
  7.  
  8. #include <stdint.h>
  9. #include <Adafruit_BMP280.h>
  10.  
  11.  
  12. // #include <Wire.h>
  13. // #include <TimeLib.h>
  14.  
  15.  
  16. /** ***********************************************************************************
  17. CLASS Etiq3 // affiche un nombre ou un petit texte dans un rectangle
  18. ***************************************************************************************/
  19.  
  20.  
  21.  
  22. class Etiquette
  23. {
  24. public:
  25.  
  26. uint16_t x0; //position
  27. uint16_t y0;
  28. uint16_t dx; //dimension
  29. uint16_t dy;
  30.  
  31. //couleurs
  32. uint16_t couleur_cadre;
  33. uint16_t couleur_fond;
  34. uint16_t couleur_nom;
  35. uint16_t couleur_txt;
  36.  
  37.  
  38. Etiquette();
  39.  
  40. void init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy,char nom[10]);
  41. void setCouleurCadre(uint16_t couleur_i);
  42. void setCouleurNom(uint16_t couleur_i);
  43. void setCouleurTxt(uint16_t couleur_i);
  44. void setCouleurFond(uint16_t couleur_i);
  45. void efface();
  46. void flashFond(uint16_t couleur_i);
  47. void affiche_int(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3], uint32_t v_min, uint32_t v_max);
  48. void affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3], float v_min, float v_max);
  49. void affiche_HEXA(uint32_t valeur);
  50. void affiche_texte(char txt_i[5]);
  51. void affiche_string(String txt_i);
  52.  
  53.  
  54. private:
  55. char nom[13];
  56. char txt_unite[4];
  57. char texte[11];
  58.  
  59. void traceCadre();
  60. void affi_nom();
  61.  
  62. };
  63.  
  64.  
  65. /** ***********************************************************************************
  66. CLASS Scope // affiche des courbes dans une surface rectangulaire
  67. ************************************************************************************* */
  68.  
  69. class Scope
  70. {
  71. public:
  72.  
  73. uint16_t x0; //position
  74. uint16_t y0;
  75. uint16_t dx; //dimension
  76. uint16_t dy;
  77.  
  78. //couleurs
  79. uint16_t couleur_trace;
  80. uint16_t couleur_cadre;
  81.  
  82. Scope();
  83.  
  84. void init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy);
  85. void setCouleurCadre(uint16_t couleur_i);
  86. void setCouleurTrace(uint16_t couleur_i);
  87. void traceCadre();
  88. void efface_surface();
  89. void efface_libel_gradu();
  90. void affi_gradu_horizontale();
  91. void Plot(uint16_t x, uint16_t y);
  92. void Cercle(uint16_t x1, uint16_t y1, uint16_t r1, uint16_t couleur_i);
  93. void Tiret(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
  94.  
  95.  
  96. private:
  97.  
  98.  
  99. };
  100.  
  101.  
  102. class LED
  103. {
  104. public:
  105.  
  106. uint16_t x0; //position
  107. uint16_t y0;
  108. uint16_t dx; //dimension
  109. uint16_t dy;
  110.  
  111.  
  112. LED();
  113.  
  114. void init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi);
  115. void setCouleur(uint16_t couleur_i);
  116.  
  117.  
  118. private:
  119.  
  120.  
  121. };
  122.  
  123. /************************************************************************************** */
  124. // AUTRES DECLARATIONS
  125.  
  126.  
  127. struct record_meteo
  128. {
  129. int16_t T; // température ; format 325 -> 32.5°C
  130. int16_t P; // pression
  131. uint8_t H; // humidité
  132. };
  133.  
  134.  
  135. int freeRam();
  136.  
  137. void Init_SDcard();
  138. void init_ports();
  139. void init_variables();
  140. void init_BMP280();
  141. void trace_ecran();
  142. void trace_entete();
  143. void clear_screen();
  144. void dessine_triangles();
  145. void dessine_arc_en_ciel();
  146. void affiche_courbeT();
  147. void affiche_courbeP();
  148. void affiche_courbeH();
  149. void affiche_ligne_ref();
  150. void affiche_pannel();
  151. void affiche_LEDS();
  152.  
  153.  
  154.  
  155. void RAZ_data();
  156. void acqui_PressionTemperature();
  157. void init_lst_records();
  158. void efface_buffer();
  159. void record_enr_actuel();
  160. void decale_enregistrements(uint16_t p);
  161. int16_t strbinToInt(String str1, uint8_t nb_bits);
  162. void lit_temperature();
  163. void calcul_confiance();
  164. uint16_t meilleure_valeur();
  165. void lit_humidite();
  166. void decodeBuffer();
  167. void setup();
  168.  
  169. void verifie_data_scope();
  170. void efface_glitch_P();
  171.  
  172. void acqui_PressionTemperature();
  173.  
  174. void calcul_Tmin_Tmax();
  175. void calcul_Pmin_Pmax();
  176.  
  177. void calcul_Gradu_ech_T();
  178. void calcul_ech_P();
  179.  
  180. void trace_sur_Scope();
  181. void affiche_meteo();
  182.  
  183. void tt_les_1mn();
  184. void tt_les_5mn();
  185. void tt_les_30mn();
  186.  
  187. void loop();
  188. void analyse_signal_primaire();
  189. void affiche_date();
  190. void affiche_heure();
  191. void setDateTime();
  192. void print_meteo_on_SDcard(); // en ASCII
  193. void write_scope_on_SDcard(); // en binaire
  194. void read_scope_on_SDcard();
  195. void write_TFT_on_SDcard();
  196.  
  197. #endif
  198.  

15 Capture d'écran

Le programme dispose d'une fonction de capture de l'écran TFT, avec comme résultat l'image ci-contre (qui s'enregistre sur la micro SDcard , fichier capture2.bmp au format 24bits RGB 888). Une image parfaitement nette !

voir la fonction :
void write_TFT_on_SDcard()

IMPORTANT : Un fichier image 480x320px format paysage, 24bits RGB888, nommé capture2.bmp doit être présent sur la SDcard, dans le dossier "bmp" au préalable, afin de fournir l'entête "qui va bien"

Dans un premier temps j'avais codé la création de l'entête, ce qui n'est pas très compliqué, et puis voyant que c'est une trouvaille à la sauce Windows... j'ai laissé tomber. J'en fournis une, vous pouvez aussi la créer (de couleur unie par exemple) avec "Gimp". La fonction de capture ne fait qu'écrire dans ce fichier à partir de la position (offset) 138; c'est à dire après la fin de l'entête.

16 SONDE GIROUETTE EXTERNE

14 février 2024 : Cette version 2.0 se voit ajouter une nouvelle fonction : l'affichage de la direction du vent, captée par une girouette externe basée sur un ESP32 et une roue codeuse à 16 positions à code gray. La girouette comprend un serveur WiFi que notre station météo consulte (envoie de requête) toutes les 2mn.

En conséquence l'ESP32 de cette station météo est programmé en tant que client se connectant (à tour de rôle) à deux serveurs distincts :

17 Programme de visualisations en Qt5

Comme nous l'avons vu, cet appareil enregistre les relevés sur une micro SDcard. Il ne manquait plus qu'à écrire un programme permettant de les visualiser en détail sous forme de graphiques : j'ai donc développé cette application en Qt5 sous Linux. Et comme je vous fournis le code source, il devrait être possible de le recompiler pour une autre plateforme.

18 vue à plus grande échelle

Vous trouverez le code source dans les documents plus bas.

19 Documents

Code source en C++ Contenu de la SDcard (4GB formatée en fat32) :
DOCUMENTS + Soft en Qt5 (avec les .h, .ui etc...) :

20 -

Liens...

2260