Station météo avec un arduino mega2560

Avec affichage TFT 320x480 et enregistrement sur SDcard

1 Présentation

J'avais récupéré un capteur de température & humidité externe (transmettant par radio sur 433MHz) ainsi que son petit récepteur associé provenant d'un modèle du commerce. J'ai alors décidé de créer cette station météo, dont voici les principales caractéristiques :
  • réalisation autour d'une carte Arduino mega2560
  • Affichage sur écran TFT 320x480
  • heure
  • température intérieure
  • température extérieure
  • taux d'humidité
  • pression atmosphérique
  • Tracé des courbes en temps réel
  • Enregistrement des mesures sur carte mémoire micro SDcard
  • Envoie des infos en temps réel par la liaison USB (à capter avec le terminal série tel que 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âble directement sur l'afficheur après coupure d'une piste permet d'éteindre l'écran TFT durant la nuit.

Cela existe déjà tout fait ? Sans doute mais cette réalisation est totalement décrite, y compris le code source du programme. Le matériel est modulable. Et puis le tout est compris dans un très petit volume, facile à caser n'importe où dans la maison ou sur le bureau.

Remarque : Cette réalisation date de 2020. J'ai fait mieux depuis : une station météo basée sur un ESP32.

2 Le schema

3 Vue de dessous

Sous la carte Arduino mega2560 se trouve une fine plaque en plastique sur laquelle sont fixés les différents capteurs. Tous ces capteurs sont reliés électriquement par des connecteurs, ce qui rend possible le démontage très simplement. On remarquera que j'ai utilisé du fil de câblage trop gros : ça fait moche ! Je vais tout recâbler avec du fil à wrapper !

4 Programme source en C++

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


5 Le fichier Station_meteo .h

CODE SOURCE en C++
  1.  
  2. #ifndef STATION_METEO_H
  3. #define STATION_METEO_H
  4.  
  5. #include <stdint.h>
  6. #include "Arduino.h"
  7.  
  8. #include <LCDWIKI_GUI.h> //Core graphics library
  9. #include <LCDWIKI_KBV.h> //Hardware-specific library
  10.  
  11. #include <Wire.h>
  12. #include <TimeLib.h>
  13. #include <DS1307RTC.h>
  14.  
  15. #include <SPI.h>
  16. #include <Adafruit_Sensor.h>
  17. #include <Adafruit_BMP280.h>
  18.  
  19. #include <SD.h>
  20.  
  21.  
  22.  
  23. //if the IC model is known or the modules is unreadable,you can use this constructed function
  24. LCDWIKI_KBV TFT480(ILI9486,A3,A2,A1,A0,A4); //model,cs,cd,wr,rd,reset
  25.  
  26. //if the IC model is not known and the modules is readable,you can use this constructed function
  27. //LCDWIKI_KBV TFT480(320,480,A3,A2,A1,A0,A4);//width,height,cs,cd,wr,rd,reset
  28.  
  29. /** ***********************************************************************************
  30. CLASS Etiq3 // affiche un nombre ou un petit texte dans un rectangle
  31. ***************************************************************************************/
  32.  
  33.  
  34.  
  35. class Etiq3
  36. {
  37. public:
  38.  
  39. uint16_t x0; //position
  40. uint16_t y0;
  41. uint16_t dx; //dimension
  42. uint16_t dy;
  43.  
  44. //couleurs
  45. uint16_t couleur_cadre;
  46. uint16_t couleur_fond;
  47. uint16_t couleur_nom;
  48. uint16_t couleur_txt;
  49.  
  50.  
  51. Etiq3();
  52.  
  53. void init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy,char nom[10]);
  54. void setCouleurCadre(uint16_t couleur_i);
  55. void setCouleurNom(uint16_t couleur_i);
  56. void setCouleurTxt(uint16_t couleur_i);
  57. void setCouleurFond(uint16_t couleur_i);
  58. void efface();
  59. void flashFond(uint16_t couleur_i);
  60. void affiche_int(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3], uint32_t v_min, uint32_t v_max);
  61. void affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3], float v_min, float v_max);
  62. void affiche_HEXA(uint32_t valeur);
  63. void affiche_texte(char txt_i[5]);
  64. void affiche_string(String txt_i);
  65.  
  66.  
  67. private:
  68. char nom[13];
  69. char txt_unite[4];
  70. char texte[11];
  71.  
  72. void traceCadre();
  73. void affi_nom();
  74.  
  75. };
  76.  
  77.  
  78. /** ***********************************************************************************
  79. CLASS Scope // affiche des courbes dans une surface rectangulaire
  80. ************************************************************************************* */
  81.  
  82. class Scope
  83. {
  84. public:
  85.  
  86. uint16_t x0; //position
  87. uint16_t y0;
  88. uint16_t dx; //dimension
  89. uint16_t dy;
  90.  
  91. //couleurs
  92. uint16_t couleur_trace;
  93. uint16_t couleur_cadre;
  94.  
  95. Scope();
  96.  
  97. void init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy);
  98. void setCouleurCadre(uint16_t couleur_i);
  99. void setCouleurTrace(uint16_t couleur_i);
  100. void traceCadre();
  101. void efface_surface();
  102. void efface_libel_gradu();
  103. void traceGraduation();
  104. void Plot(uint16_t x, uint16_t y);
  105. void Cercle(uint16_t x1, uint16_t y1, uint16_t r1, uint16_t couleur_i);
  106. void Tiret(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
  107.  
  108.  
  109. private:
  110.  
  111.  
  112. };
  113.  
  114.  
  115. class LED
  116. {
  117. public:
  118.  
  119. uint16_t x0; //position
  120. uint16_t y0;
  121. uint16_t dx; //dimension
  122. uint16_t dy;
  123.  
  124.  
  125. LED();
  126.  
  127. void init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi);
  128. void setCouleur(uint16_t couleur_i);
  129.  
  130.  
  131. private:
  132.  
  133.  
  134. };
  135.  
  136. /************************************************************************************** */
  137. // AUTRES DECLARATIONS
  138.  
  139.  
  140. struct record_meteo
  141. {
  142. int16_t T; // température
  143. int16_t P; // pression
  144. uint8_t H; // humidité
  145. // ligne ci-dessous : 2 infos multiplexées pour économiser de la SRAM (sinon on passe pas !)
  146. uint16_t heure; // au format h*60+mn par exemple pour 23h59 --> 23*60+59=1439 + data invalide si = 0xFFFF
  147. };
  148.  
  149. int freeRam();
  150.  
  151. void Init_SDcard();
  152. void init_ports();
  153. void init_variables();
  154. void trace_ecran();
  155. void trace_entete();
  156. void clear_screen();
  157. void dessine_triangles();
  158. void dessine_arc_en_ciel();
  159. void affiche_courbeT();
  160. void affiche_courbeP();
  161. void affiche_courbeH();
  162. void affiche_ligne_ref();
  163. void affiche_pannel();
  164. void affiche_LEDS();
  165.  
  166. void RGB565_to_888(uint16_t color565, uint8_t *R, uint8_t *G, uint8_t *B);
  167. void draw_bmp(uint16_t x0, uint16_t y0, File fp);
  168. void affi_img(uint16_t x0, uint16_t y0, const char* filename1);
  169.  
  170. void RAZ_data();
  171. void acqui_PressionTemperature();
  172. void init_lst_records();
  173. void efface_buffer();
  174. void record_enr_actuel();
  175. void decale_enregistrements(uint16_t p);
  176. int16_t strbinToInt(String str1, uint8_t nb_bits);
  177. void lit_temperature();
  178. void calcul_confiance();
  179. uint16_t meilleure_valeur();
  180. void lit_humidite();
  181. void decodeBuffer();
  182. void setup();
  183.  
  184. void verifie_data_scope();
  185. void efface_glitch_P();
  186.  
  187. void determine_Tmin_Tmax();
  188. void determine_Pmin_Pmax();
  189.  
  190. void calcul_Gradu_ech_T();
  191. void calcul_ech_P();
  192.  
  193. void trace_sur_Scope();
  194. void affiche_meteo();
  195.  
  196. void tt_les_1mn();
  197. void tt_les_5mn();
  198. void tt_les_30mn();
  199.  
  200. void loop();
  201. void analyse_signal_primaire();
  202. void affiche_date();
  203. void affiche_heure();
  204. void setDateTime();
  205. void print_meteo_on_SDcard(); // en ASCII
  206. void write_scope_on_SDcard(); // en binaire
  207. void read_scope_on_SDcard();
  208. void write_TFT_on_SDcard();
  209.  
  210. #endif
  211.  

6 La sonde extérieure

Comme je vous l'ai dit plus haut, 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

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 5V

8 Le capteur de pression atmosphérique & température

C'est un classique BMP280 d'une grande précision concernant la pression (il peut servir d'altimètre connaissant le QNH...)
Attention : doit être alimenté en 3.3V. Donc étage convertisseur 5V <-> 3.3V sur les signaux obligatoire, voir le schéma.

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

9 Le module micro SDcard

Classique lui aussi, bien connu dans le monde de l'Arduino.
Pourquoi ajouter ce module sachant que le module d'affichage TFT ILI9486 comprend déjà une interface pour TFcard ? Pour la raison suivante :
-Le lecteur de micro-SDcard du module d'affichage ne fonctionne que lorsque ce dernier est utilisé avec une carte Arduino UNO, mais pas avec une carte Mega2560 comme c'est le cas ici. Cela à cause d'un problème de conflit dans l'exploitation du bus SPI. Je cite le ReadMe donné dans l'exemple :

LCDWIKI_KBV/Example/Example_07_show_bmp_picture/show_bmp_picture/ReadMe.txt

Il est dit :

This Example only support the Arduino UNO board, not support the Mega2560, because they use différent SPI bus.

Evidemment j'ai tenté de contourner le problème, mais je n'ai pas réussi. En revanche le module présenté ici fonctionne parfaitement avec la carte Mega2560, même connectée à l'afficheur en question. D'ailleurs j'ai programmé l'affichage d'une image en tout-écran lors de l'allumage de cette station météo. Pour l'instant c'est juste pour faire joli, toutefois cela ouvre des perspectives formidables... (utilisation d’icônes par exemple, petites animation), et pas seulement pour CETTE réalisation. Vous me direz, il y a le Raspberry Pi 4 pour cela. Oui mais c'est (en proportion) beaucoup plus cher, et surtout infiniment plus gourmand en énergie (électrique): Plusieurs watts !

10 Le module horloge temps réel (RTC)

Afin de connaître l'heure actuelle même lorsque l'alimentation principale est arrêtée, le module dispose d'une pile CR2032 classique. Remarque au passage pour les passionnés de physique : connaître l'heure, c'est à dire distinguer et compter et mettre dans le bon ordre des instants distincts dans le temps demande de l'énergie. Que ce soit pour alimenter un oscillateur suivi d'un compteur, d'une horloge comtoise, d'une clepsydre, ou des satellites de Jupiter, il faut une source d'énergie. (Dans le cas des satellites de Jupiter, sans doute l'énergie gravitationnelle lors de l'accrétion du nuage de poussière à l'origine du système solaire, elle même fille de phénomènes antérieurs bien plus tumultueux encore). Oui mais si on se contente de compter les tours effectués par un électron sur sa trajectoire autour du noyau d'un atome ? Remarquons que la description que fait la physique quantique d'un tel système exclut d'entrée la notion de position de l'électron. La fréquence émise lors de transitions ? pour en tirer quelque information, il faut dépenser beaucoup d'énergie justement. Et ces informations proviennent d'un rayonnement, donc d'un phénomène énergétique.
J'allais oublier : la bestiole dont nous parlons ici est un DS130RTC.
Au fait, quel est l'utilité de cette horloge temps réel ? Ce n'est pas seulement pour afficher l'heure sur le TFT, elle sert surtout à dater les enregistrements sur la SDcard.

11 Signaux envoyés par la sonde externe :

De quoi rester perplexe n'est-ce pas ?

12 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

Nous allons donc utiliser le Clustered regularly interspaced short palindromic repeats (CRISPR) associé au CAs9 pour décortiquer tout ça. Je vais donc appeler de suite notre charmante prix Nobel, Emmanuelle Charpentier pour nous aider à y voir plus clair.

Je vous invite à suivre la suite du raisonnement en lisant le commentaire qui figure dans la fonction "void decodeBuffer()" à la ligne 1462 (environ).

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

Dans ce cas deux 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...
Après quoi il vous faudra bien évidemment modifier le programme en conséquence.

Note; la vitesse de la liaison série est fixée à 115200 bauds dans la fonction "setup". Cette liaison série n'est utilisée que pour transmettre des (de nombreuses) infos pour la mise au point et le débogage.

14 Exemple de dialogue par la liaison série

Quelques tests puis un aperçu des signaux reçus depuis la sonde externe par le 433.92MHz ainsi que des capteurs locaux, et de leur décodage... en temps réel.

15 Enregistrements sur micro SDcard

Voici une copie d'écran d'une partie du contenu de la SDcard. Ces enregistrements sont effectué automatiquement toutes les 30mn. Je vais prochainement écrire un petit soft en Qt5 qui permettra de les exploiter (affichage graphique de courbes zoomables).

Remarque : non, non, je n'habite pas dans un pays (très très) chaud ! Les températures sont exprimées en Kelvins * 10., ce qui évite tout problème avec des températures négatives et écritures décimales (l'ajout d'un point et d'un signe - par ci par là...) Et puis c'est quoi une température négative au yeux d'une micro sdCARD ?

16 todo...

20 oct 2020 :
L'appareil fonctionne correctement (à un détail près) toutefois je vais encore l'améliorer.
Le détail en question tient à la mesure de la température intérieure : ça affiche 5 degrés de plus que la température dans la pièce. L'erreur ne provient pas de l'électronique ni de la programmation, mais tout simplement de la température de la sonde interne (qui fait partie du capteur de pression BMP280), de par sa disposition trop près de la carte Arduino et de l'afficheur TFT, qui chauffent ! oh ils ne chauffent pas exagérément (ce ne sont pas des Raspberry pi 4, que j'apprécie beaucoup soit dit en passant), mais suffisamment pour élever la température de l'ensemble de l'appareil de 5°C (environ).

Une façon simpliste d'y remédier serait d'ajouter un "-=5" (voire "-=4.96") au bon endroit dans le soft, mais alors adieu la précision et la justesse du résultat suivant le comportement thermique du matériel en fonctionnement.

Je pense que la bonne solution sera d'éloigner un peu ce capteur du reste de l'appareil (en conservant sa liaison filaire).


22 oct 2020 :
Je travaille sur le soft, pour l'améliorer et je publie de nouvelle versions ici-même parfois plusieurs fois par jour. Donc à surveiller !
Par exemple les 5 valeurs émises par la sonde externe en 433MHz lors d'une salve sont maintenant toutes prises en compte, et lorsque des différences sont détectées, c'est la valeur ayant le plus d’occurrences dans la salve qui est retenue. Ceci permet de s"affranchir des aléas de transmission radio.

Je vais maintenant mémoriser toutes les valeurs affichées sur le "scope" en EEPROM de façon à ne pas perdre la courbe suite à une coupure de courant (si uniquement alimenté par le secteur) où lors d'un changement de pile ou batterie.

Je compte aussi programmer une mise à l'échelle automatique de la courbe de températures entre [valeur min - 10%] et [valeur max +10%] par exemple, avec re-graduation automatique.

17 Evolution 1

29 oct 2020 :
Les principales améliorations que j'ai apportées sont les suivantes :
  • prise en compte des 5 valeurs émises dans une salve par le capteur externe afin d'éliminer les valeurs erronées suite à des aléas de transmission radio en 433 MHz (si le concepteur de la sonde a prévu ces salves multiples, ce n'est pas pour rien, il y a effectivement des erreurs de transmission, surtout en seconde partie de nuit. Il y a là matière à spéculer sur la cause...)
  • prise en compte des températures négatives (< 0°C)
  • cadrage automatique de la courbe affichée sur le "scope"
  • enregistrement des courbes (température, humidité, pression atmosphérique) sur la SDcard
  • chargement à la mise en route des courbes précédemment sauvegardées
  • lissage (filtre passe bas logiciel) des courbes de pression (verte) et humidité (bleue) pour gommer les irrégularités ponctuelles. je vais faire de même pour la température.
Les "brisures" dans les courbes proviennent des périodes pendant lesquelles je modifie le programme. Donc pas de soucis en fonctionnement normal.
A noter le dessin d'un arc-en-ciel en fond d'écran, pour faire joli. Les couleurs sont bien plus belles en vrai que sur ces photos, les APN et autres smartphones rendent assez mal les couleurs primaires des écrans TFT.



18 L'appareil en fonctionnement

3 nov 2020 :
Sur la photo ci-dessus on peut voir que l'appareil :
  • a effectué 68 écriture sur la SDcard
  • a reçu 9541 trains de signaux sur la fréquence de 433.92 MHz, ce qui fait 1908 salves de 5 trains, soit 1908 minutes, ce qui donne 1908/60=32 heures. Et en effet, les courbes sont affichées d'une façon significative depuis 32 heures
  • affiche actuellement une pression atmosphérique très haute, conforme à ce que donne le site meteociel.fr pour la région de Montpellier.
  • affiche une humidité qui est montée jusqu'à 83% (68% actuellement) et en effet, après un temps de brouillard, il se met à pleuvoir légèrement.
  • affiche les valeurs mini-maxi des mesures
  • affiche le détail de la dernière salve reçue : quatre fois la valeur "140" (pour 14.0°C) et une fois "25" ce qui est une erreur de transmission HF, et en vert que c'est bien la valeur "140" qui a été retenue. (Voir le code source du programme pour constater que cela fonctionne démocratiquement...)
Bon au risque de me répéter, je vous signale qu'il ne fait pas 24.5°C en novembre chez moi mais plutôt 19°C, la trop grande proximité du capteur de température interne avec la carte Arduino et l'afficheur augmente la température de 5°C environ (je dispose d'un thermomètre infra-rouge à visée laser, j'ai vérifié).

19 Dix heures plus tard...

4 nov 2020 :

Les courbes se sont décalées vers la gauche, effaçant les parties brisées non significatives. La station est maintenant en régime de croisière.

On remarquera que les échelles d'affichage ont été automatiquement recalculées.
Il s'avère toutefois que l'échelle jaune est un peut trop étendue, la graduation 20°C n'est la affichée et la courbe jaune des températures ne remplit pas bien l'écran.

La courbe verte de la pression atmosphérique évolue par paliers (ici 5 niveaux) correspondant chacun à 1 hPa. Ben oui, la pression est quelque chose qui varie peu autour d'une grande valeur... (toutefois les transitions sont ici lissées par un filtre passe bas logiciel basé sur le calcul d'une moyenne glissante, voir le programme).

20 Echelles

.

L'échelle des températures n'était pas bien adaptée à la courbe. Cela ne provenait pas d'une erreur de calcul de l'échelle, mais d'un glitch qui faussait la valeur de la température max (en la fixant ponctuellement à 20°C) tout en restant invisible à l'affichage à cause du lissage (efficace) de la courbe. Il me restait deux solutions, soit calculer les extremums de la courbe après lissage, soit programmer une petite fonction anti-glitch. J'ai opté pour cette dernière. Voir le code.

21 Nouvelle graduation temporelle

14 nov 2020 :
Désormais l'échelle temporelle est à date glissante : Les heures indiquées ne sont plus référencées par rapport à l'heure actuelle (-1h , -2h, ...) mais correspondent à l'heure exacte d'acquisition. Des lignes rouges verticales signalent Midi (12h) et Minuit (00). Cela évite de devoir faire un calcul mental à partir de l'heure actuelle pour connaître par exemple l'heure où la température fut la plus élevée dans l'après-midi (entre 15 et 16h sur la photo), ou bien la plus basse dans la nuit. On voit ici que la température remonte en seconde partie de nuit, du fait de la mise en place d'une couverture nuageuse (entrée maritime venant de Méditerranée).

D'autre part les enregistrements de données sur la micro SDcard sont maintenant cadencées par le module horloge RTC, calées sur les minutes, et non plus sur un comptage approximatif (pour cause logicielle) de l'horloge de l'ATmega2560. Cela facilite entre autres choses, l'exploitation des données, et leur affichage sur le "scope".

22 Evolution 2

20 nov 2020 :
Les heures Midi et Minuit sont maintenant repérées par de petites icônes représentant le Soleil et la Lune. Ces icônes sont lues sur la carte SD. Eh oui, on peut désormais afficher des images préalablement enregistrées sur la micro SDcard.

Et alors m'est venue une idée : Afficher (graphiquement) la phase actuelle de la Lune ! Phase calculée d'après la date avec une fonction mathématique comme il se doit. Pratique pour les astronomes amateurs (étourdis) pour savoir si la clarté de la Lune ne risque pas d'être trop gênante pour une observation du ciel profond. Et pour les jardiniers ? Bah, si vous y croyez...
A propos d'astronomie, en ce moment on ne peut pas rater la planète Mars, très lumineuse comme une étoile rouge le soir vers l'est, haut dans le ciel.

23 Affichage des phases de la Lune

CODE SOURCE en C++
  1.  
  2. /***********************************************************************************
  3.  Mega2560
  4.  
  5. use the BREAKOUT BOARD only and use these 8bit data lines to the LCD,
  6. pin usage:
  7.   LCD_CS LCD_CD LCD_WR LCD_RD LCD_RST SD_SS SD_DI SD_DO SD_SCK
  8. Arduino Mega2560 A3 A2 A1 A0 A4 10 11 12 13
  9.  
  10. **********************************************************************************/
  11.  
  12. /**
  13. RAPPEL ATmega 2560 :
  14. – 256KBytes of In-System Self-PrograM2able Flash
  15. – 4Kbytes EEPROM
  16. – 8Kbytes Internal SRAM
  17. **/
  18.  
  19. #include "Arduino.h"
  20. #include <stdint.h>
  21.  
  22. #include <LCDWIKI_GUI.h> //Core graphics library
  23. #include <LCDWIKI_KBV.h> //Hardware-specific library
  24.  
  25.  
  26. #include "Graphiques_TFT.h"
  27.  
  28. /** Graphiques_TFT **/
  29.  
  30. String version="3.0";
  31.  
  32.  
  33. int sram;
  34. uint32_t bmp_offset = 0;
  35. uint16_t s_width = TFT480.Get_Display_Width();
  36. uint16_t s_heigh = TFT480.Get_Display_Height();
  37.  
  38. #define NOIR 0x0000
  39. #define ROUGE 0xF800
  40. #define ORANGE 0xFBC0
  41. #define JAUNE 0xFFE0
  42. #define VERT 0x07E0
  43. #define CYAN 0x07FF
  44. #define BLEU_CLAIR 0x455F
  45. #define BLEU 0x001F
  46. #define MAGENTA 0xF81F
  47. #define VIOLET 0x781A
  48. #define GRIS_CLAIR 0xA534
  49. #define GRIS 0x8410
  50. #define GRIS_FONCE 0x5ACB
  51. #define BLANC 0xFFFF
  52.  
  53.  
  54.  
  55. const int INPUT_PIN = 22;
  56. uint8_t SDcardOk=0;
  57.  
  58.  
  59.  
  60.  
  61. /** -------------------------------------------------------
  62.   SD card
  63. -----------------------------------------------------------
  64. ADAPT SD -> mega2560 pins
  65.  
  66. CS -> 53
  67. MISO -> 50
  68. MOSI -> 51
  69. SCK -> 52
  70. **/
  71.  
  72.  
  73. int freeRam() // pour tests
  74. {
  75. extern int __heap_start, *__brkval;
  76. int v;
  77. return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
  78. }
  79.  
  80.  
  81.  
  82. /**--------------------------------------------------------
  83. fonctions AFFICHAGE TFT
  84. --------------------------------------------------------**/
  85.  
  86.  
  87. void clear_screen()
  88. {
  89. TFT480.Fill_Screen(40,40,40);
  90. }
  91.  
  92.  
  93. void dessine_triangles()
  94. {
  95. int i = 0;
  96. uint16_t L, H;
  97. L=TFT480.Get_Display_Width();
  98. H=TFT480.Get_Display_Height();
  99.  
  100. for(i=0; i<H/2; i+=5)
  101. {
  102. TFT480.Set_Draw_color(0,i+64,i+64);
  103. TFT480.Draw_Triangle(L/2-1,H/2-1-i,L/2-1-i,H/2-1+i,L/2-1+i,H/2-1+i);
  104. }
  105. }
  106.  
  107.  
  108. void dessine_degrade(uint16_t x, uint16_t y, uint8_t longueur, uint8_t RR1, uint8_t RR2, uint8_t GG1, uint8_t GG2, uint8_t BB1, uint8_t BB2)
  109. {
  110. // dessine un dégr&dé horizontal de couleurs (R1,G1,B1) -> (R2,G2,B2)
  111. uint16_t i;
  112. float R,G,B;
  113.  
  114. if(longueur==0) {return;}
  115.  
  116. i=0;
  117.  
  118. R=RR1;
  119. G=GG1;
  120. B=BB1;
  121.  
  122. while(i<longueur)
  123. {
  124. x++;
  125. R+=(RR2-RR1)/longueur;
  126. G+=(GG2-GG1)/longueur;
  127. B+=(BB2-BB1)/longueur;
  128. TFT480.Set_Draw_color((uint8_t)R,(uint8_t)G,(uint8_t)B);
  129. TFT480.Draw_Line(x, y, x, y+10);
  130. i++;
  131. }
  132. }
  133.  
  134.  
  135. void dessine_arc_en_ciel()
  136. {
  137. int16_t i;
  138. float xr,xv,xb;
  139. float r,v,b;
  140. for (i=0; i<63; i++) // 10 * 2 pi radians
  141. {
  142. xr=(i+12)/10.0; r=140.0+120.0*sin(xr)-i; if (r<0) {r=0;} if (r>255) {r=255;}
  143. xv=(i+54)/10.0; v=130.0+150.0*sin(xv); if (v<0) {v=0;} if (v>255) {v=255;};
  144. xb=(i+38)/10.0; b=100.0+150.0*sin(xb); if (b<0) {b=0;} if (b>255) {b=255;};
  145.  
  146. /*
  147. //pour test du déphasage des courbes sinus...
  148. TFT480.Set_Draw_color(255, 255, 255);
  149. TFT480.Draw_Pixel(200+i, 150-(uint8_t)(r/10.0));
  150. TFT480.Draw_Pixel(200+i, 200-(uint8_t)(v/10.0));
  151. TFT480.Draw_Pixel(200+i, 250-(uint8_t)(b/10.0));
  152. */
  153.  
  154. TFT480.Set_Draw_color((uint8_t)r,(uint8_t)v,(uint8_t)b);
  155. TFT480.Draw_Circle_Helper(400, 280, 400-i, 1);
  156. }
  157. }
  158.  
  159.  
  160. void dessine_cercle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t couleur)
  161. {
  162. uint16_t n;
  163. float i;
  164. float x,y;
  165.  
  166. TFT480.Set_Draw_color(couleur);
  167. i=0;
  168. while(i<2*M_PI)
  169. {
  170. x=x0+r*cos(i);
  171. y=y0+r*sin(i);
  172. TFT480.Draw_Pixel(x,y);
  173. i+=0.01; // radians
  174. }
  175. }
  176.  
  177.  
  178. void dessine_ellipse(uint16_t x0, uint16_t y0, int16_t dx, int16_t dy, uint16_t couleur)
  179. {
  180. uint16_t n;
  181. float i;
  182. float x,y;
  183.  
  184. TFT480.Set_Draw_color(couleur);
  185. i=0;
  186. while(i<2*M_PI)
  187. {
  188. x=x0+dx*cos(i);
  189. y=y0+dy*sin(i);
  190. TFT480.Draw_Pixel(x,y);
  191. i+=0.01; // radians
  192. }
  193. }
  194.  
  195.  
  196. void dessine_ellipse_inclinee()
  197. {
  198. uint16_t n;
  199. float i;
  200. float x,y;
  201.  
  202. TFT480.Set_Draw_color(VERT);
  203. i=0;
  204. while(i<2*M_PI)
  205. {
  206. x=280+50.0*cos(i-0.5);
  207. y=150+80.0*cos(i+0.5);
  208. TFT480.Draw_Pixel(x,y);
  209. i+=0.01; // radians
  210. }
  211. }
  212.  
  213.  
  214.  
  215.  
  216. void Draw_arc_elliptique(uint16_t x0, uint16_t y0, int16_t dx, int16_t dy, float alpha1, float alpha2, uint16_t couleur) // alpha1 et alpha2 en radians
  217. {
  218. /*
  219. REMARQUES :
  220. -cette fonction permet également de dessiner un arc de cercle (si dx=dy), voire le cercle complet
  221. - dx et dy sont du type int (et pas uint) et peuvent êtres négafifs, ou nuls.
  222. -alpha1 et alpha2 sont les angles (en radians) des azimuts des extrémités de l'arc
  223. */
  224. uint16_t n;
  225. float i;
  226. float x,y;
  227.  
  228. TFT480.Set_Draw_color(couleur);
  229. i=alpha1;
  230. while(i<alpha2)
  231. {
  232. x=x0+dx*cos(i);
  233. y=y0+dy*cos(i+M_PI/2.0);
  234. TFT480.Draw_Pixel(x,y);
  235. i+=0.01; // radians
  236. }
  237. }
  238.  
  239.  
  240. 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)
  241. {
  242. // dx1 et dx2 sont les extremums (en pixels) horizontaux d'un croissant vertical
  243.  
  244. if (dx2<dx1) {return;}
  245. int16_t dx;
  246. dx = dx1;
  247. while(dx<dx2)
  248. {
  249. Draw_arc_elliptique(x0, y0, dx, dy, alpha1, alpha2, couleur);
  250. dx++;
  251. }
  252. }
  253.  
  254.  
  255.  
  256.  
  257.  
  258. void dessine_phases_Lune() // animation
  259. {
  260. //très bien pour une animation, mais pas pratique pour dessiner une phase donnée en Lune descendante
  261. float i;
  262. uint16_t x,y;
  263. uint16_t n;
  264.  
  265. //Lune montante
  266. for(n=0 ; n< 200; n++)
  267. {
  268. x = random(470);
  269. y = random(300);
  270. if (((x-200)*(x-200) +(y-150)*(y-150)) > 5000)
  271. {
  272. TFT480.Set_Draw_color(BLANC); // étoiles
  273. TFT480.Draw_Pixel(x,y);
  274. }
  275. }
  276.  
  277. Draw_arc_elliptique(200, 150, 60, 60, 0, 2*M_PI, GRIS_FONCE); // cercle complet
  278.  
  279. i= 60;
  280. while(i>(-60))
  281. {
  282. Draw_arc_elliptique(200, 150, i, 60, -M_PI/2, M_PI/2, BLANC);
  283. i-=1;
  284. delay(10);
  285. }
  286.  
  287. Draw_arc_elliptique(200, 165, 30, -20, 0, M_PI, GRIS_FONCE);
  288. dessine_cercle(180,140,5, NOIR);
  289. dessine_cercle(220,140,5, NOIR);
  290.  
  291.  
  292. //Lune descendante
  293. i= 60;
  294. while(i>(-60))
  295. {
  296. Draw_arc_elliptique(200, 150, i, 60, -M_PI/2, M_PI/2, GRIS_FONCE); // dessine en noir par dessus la pleine Lune
  297. i-=1;
  298. delay(10);
  299. }
  300. }
  301.  
  302.  
  303.  
  304. void Dessine_Lune(uint16_t x0, uint16_t y0, uint8_t r, float age)
  305. {
  306. //Remarque : l'age admet des nombres réels (avec décimales)
  307.  
  308. int16_t T1, T2; //Terminators gauche et droite, limites entre partie éclairée et partie dans l'ombre
  309.  
  310. if (age<1) {dessine_cercle(x0, y0, r, BLANC);} // Nouvelle Lune
  311. if (age <= 15) // premier croissant... pleine
  312. {
  313. T1=r-2*r*age/15;
  314. T2=r;
  315. }
  316. if ((age > 15) && (age <=30)) // pleine... dernier croissant
  317. {
  318. T1=-r;
  319. T2=r-2*r*(age-15)/15;
  320. }
  321. Fill_croissant_elliptique(x0, y0, T1, T2, r, -M_PI/2, M_PI/2, BLANC);
  322. }
  323.  
  324.  
  325.  
  326. float Decimale(float valeur)
  327. {
  328. float d;
  329. d=valeur-floor(valeur);
  330. return d;
  331. }
  332.  
  333.  
  334. float GetPhase(int Y, int M, int D)
  335. {
  336. // return "age de le Lune" (nombre allant de 0.00 à 29.xx)
  337. float AG, IP;
  338. uint32_t Y2, M2, K1, K2, K3, JD;
  339. Y2=Y-floor((12-M)/10);
  340. M2=M+9;
  341. if (M2>=12) {M2-=12;}
  342. K1= floor(365.25 * (Y2 + 4712));
  343. K2= floor(30.6 * M2+0.5);
  344. K3= floor(floor((Y2/100) + 49)*0.75)-38;
  345. JD= K1+K2+D+59;
  346. if (JD > 2299160) {JD=JD-K3;}
  347. IP= Decimale((JD-2451550.1)/29.53058);
  348. AG= IP*29.53;
  349. return AG;
  350. }
  351.  
  352.  
  353.  
  354.  
  355.  
  356. void setup()
  357. {
  358. Serial.begin(115200);
  359.  
  360. TFT480.Init_LCD();
  361. clear_screen();
  362. delay(10);
  363.  
  364. TFT480.Set_Rotation(1);
  365.  
  366. TFT480.Set_Text_Back_colour(NOIR);
  367. TFT480.Set_Text_colour(BLANC);
  368. TFT480.Set_Text_Size(2);
  369. TFT480.Print_String("Plein de choses utiles", 0, 10);
  370. TFT480.Print_String("pour une Station Meteo", 0, 30);
  371.  
  372. delay(2000);
  373. clear_screen();
  374.  
  375. // =======================================================================================
  376. // ********* DEGRADES DE COULEUR **************
  377.  
  378. uint8_t RR1, RR2, GG1, GG2, BB1, BB2;
  379.  
  380. //primaires noir -> clair
  381. RR1=0; RR2=255;
  382. GG1=0; GG2=0;
  383. BB1=0; BB2=0;
  384. dessine_degrade(0, 0, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  385.  
  386. RR1=0; RR2=0;
  387. GG1=0; GG2=255;
  388. BB1=0; BB2=0;
  389. dessine_degrade(0, 20, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  390.  
  391. RR1=0; RR2=0;
  392. GG1=0; GG2=0;
  393. BB1=0; BB2=255;
  394. dessine_degrade(0, 40, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  395.  
  396. //primaires clair -> noir
  397. RR1=255; RR2=0;
  398. GG1=0; GG2=0;
  399. BB1=0; BB2=0;
  400. dessine_degrade(0, 60, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  401.  
  402. RR1=0; RR2=0;
  403. GG1=255; GG2=0;
  404. BB1=0; BB2=0;
  405. dessine_degrade(0, 80, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  406.  
  407. RR1=0; RR2=0;
  408. GG1=0; GG2=0;
  409. BB1=255; BB2=0;
  410. dessine_degrade(0, 100, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  411.  
  412. // rouge ->vert
  413. RR1=255; RR2=0;
  414. GG1=0; GG2=255;
  415. BB1=0; BB2=0;
  416. dessine_degrade(0, 120, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  417.  
  418. // rouge ->vert très court
  419. RR1=255; RR2=0;
  420. GG1=0; GG2=255;
  421. BB1=0; BB2=0;
  422. dessine_degrade(300, 120, 37, RR1, RR2, GG1, GG2, BB1, BB2);
  423.  
  424. // jaune -> noir
  425. RR1=255; RR2=0;
  426. GG1=255; GG2=0;
  427. BB1=0; BB2=0;
  428. dessine_degrade(0, 140, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  429.  
  430.  
  431. // jaune -> bleu
  432. RR1=255; RR2=0;
  433. GG1=255; GG2=0;
  434. BB1=0; BB2=255;
  435. dessine_degrade(0, 160, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  436.  
  437. // jaune -> bleu en plus court
  438. RR1=255; RR2=0;
  439. GG1=255; GG2=0;
  440. BB1=0; BB2=255;
  441. dessine_degrade(300, 160, 100, RR1, RR2, GG1, GG2, BB1, BB2);
  442.  
  443. // noir -> blanc
  444. RR1=0; RR2=255;
  445. GG1=0; GG2=255;
  446. BB1=0; BB2=255;
  447. dessine_degrade(0, 180, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  448.  
  449. // noir -> cyan
  450. RR1=0; RR2=0;
  451. GG1=0; GG2=255;
  452. BB1=0; BB2=255;
  453. dessine_degrade(0, 200, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  454.  
  455. // rouge -> cyan
  456. RR1=255; RR2=0;
  457. GG1=0; GG2=255;
  458. BB1=0; BB2=255;
  459. dessine_degrade(0, 220, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  460.  
  461. // orange -> bleu
  462. RR1=255; RR2=0;
  463. GG1=120; GG2=0;
  464. BB1=0; BB2=255;
  465. dessine_degrade(0, 240, 255, RR1, RR2, GG1, GG2, BB1, BB2);
  466.  
  467.  
  468. // orange -> bleu plus court
  469. RR1=255; RR2=0;
  470. GG1=120; GG2=0;
  471. BB1=0; BB2=255;
  472. dessine_degrade(300, 240, 85, RR1, RR2, GG1, GG2, BB1, BB2);
  473.  
  474.  
  475. //longueur variable
  476. for(uint8_t n=10 ; n<255 ; n++)
  477. {
  478. RR1=255; RR2=0;
  479. GG1=0; GG2=255;
  480. BB1=0; BB2=0;
  481. // dessine_degrade(0, 260, n, RR1, RR2, GG1, GG2, BB1, BB2);
  482. }
  483.  
  484. // =======================================================================================
  485. delay(1000);
  486. clear_screen();
  487.  
  488. TFT480.Set_Text_Back_colour(NOIR);
  489. TFT480.Set_Text_colour(BLANC);
  490. TFT480.Set_Text_Size(1);
  491. TFT480.Print_String("Affichage d'ellipses parametriques en cos et sin", 30, 10);
  492.  
  493.  
  494.  
  495. double phase;
  496. int j;
  497.  
  498. for (j=1; j<31; j++)
  499. {
  500. phase = GetPhase(2020,11,j);
  501. Serial.print(j); Serial.print(" Phase="); Serial.println(phase);
  502. }
  503.  
  504. delay(1000);
  505.  
  506. clear_screen();
  507. dessine_arc_en_ciel();
  508.  
  509. dessine_cercle(100,150,80, JAUNE);
  510. dessine_ellipse(100,150,60,120, ORANGE);
  511. dessine_ellipse_inclinee();
  512. Draw_arc_elliptique(410, 150, 60, 100, -M_PI/2.0, M_PI/2.0, JAUNE );
  513. Draw_arc_elliptique(405, 150, 60, 60, 0, 2*M_PI, CYAN); // <- dessin d'un cercle
  514.  
  515. delay(1000);
  516. clear_screen();
  517.  
  518. dessine_phases_Lune();
  519.  
  520. delay(1000);
  521. clear_screen();
  522.  
  523. // =======================================================================================
  524.  
  525. //RAPPEL : Fill_croissant_elliptique(x0, y0, dx1, dx2, dy, alpha1, alpha2, couleur)
  526.  
  527. Fill_croissant_elliptique(10, 150, 50, 60, 60, -M_PI/2, M_PI/2, BLANC); // premier croissant
  528. Fill_croissant_elliptique(100, 150, 0, 60, 60, -M_PI/2, M_PI/2, BLANC); // premier quartier
  529. Fill_croissant_elliptique(230, 150, -60, 60, 60, -M_PI/2, M_PI/2, BLANC); // pleine
  530. Fill_croissant_elliptique(360, 150, -60, 30, 60, -M_PI/2, M_PI/2, BLANC); //Gibi
  531. Fill_croissant_elliptique(460, 150, -60, -40, 60, -M_PI/2, M_PI/2, BLANC);// dernier croissant
  532.  
  533. Fill_croissant_elliptique(250, 240, -20, 10, 20, -M_PI/2, M_PI/2, BLANC); // + petite
  534.  
  535.  
  536. delay(2000);
  537. clear_screen();
  538.  
  539. // =======================================================================================
  540.  
  541.  
  542. // Dessine une suite de Lunes par une formule unique en fonction de l'age de la Lune
  543. // non, non, il ne s'agit pas de son age depuis sa naissance il y a 4.53 milliards d'années !!!
  544. // L'âge de la lune est le nombre de jours écoulés depuis la nouvelle lune : 0 le jour de la nouvelle lune, 1 le lendemain ...
  545.  
  546. TFT480.Set_Text_Back_colour(NOIR);
  547. TFT480.Set_Text_colour(BLANC);
  548. TFT480.Set_Text_Size(2);
  549. TFT480.Print_String(" 'Age' de la Lune (en jours)", 10, 10);
  550.  
  551. uint8_t age =0;
  552. uint8_t r = 10;
  553.  
  554. TFT480.Set_Text_Size(1);
  555. TFT480.Set_Text_colour(VERT);
  556. while(age <= 15)
  557. {
  558. TFT480.Print_Number_Int(age,20+22*age, 58, 2, ' ', 10);
  559. Dessine_Lune(20+22*age, 80, r, age);
  560. age++;
  561. }
  562. while ((age > 15) && (age < 30) )
  563. {
  564. TFT480.Print_Number_Int(age, -300+20*age, 108, 2, ' ', 10);
  565. Dessine_Lune(-300 +8+20*age, 130, r, age); // 2eme ligne + bas
  566. age++;
  567. }
  568. }
  569.  
  570.  
  571. void loop()
  572. {
  573. delay(10);
  574. }
  575.  
  576.  
  577.  
  578.  
  579.  


21 nov 2020 :
Voici ci-dessus un petit programme qui permet de tracer des cercles et des éllipses en équations paramétriques, puis d'afficher la lune sous forme d'une petite animation montrant l'évolution de sa phase en continu, depuis le premier croissant jusqu'au dernier, en passant par la Pleine Lune, pour finir sur la Nouvelle Lune. Tout ça avec deux fonctions sinus en quadrature.

Il me reste à intégrer tout ça (en version statique) dans le programme principal, et de le piloter en fonction de la date et de l'heure.

à suivre...

24 Pleine Lune !

29 nov 2020 :
Voici donc en haut à droite la Lune affichée dans sa phase actuelle (la Pleine Lune aura lieu demain le 30 novembre 2020). Les petits croissants sur le scope ne sont là qu'à titre indicatif pour différentier le jour et la nuit. L'affichage du quadrillage vertical (horaire) est très évanescent dans la partie droite du scope sur cette photo, c'est juste un effet de la prise de vue par mon smartphone qui n'apprécie que très peu les écrans TFT.
J'ai d'ailleurs pour projet d'écrire une fonction permettant la capture d'écran de ces petits afficheurs par écriture directe de l'image sur la SDcard, sans devoir passer par une photo. (ça existe déjà mais je voudrais obtenir un code sans doute moins universel, mais bien plus plus léger).

30 nov 2020 :
J'ai supprimé l'affichage de certaines données techniques (utiles lors de la mise au point), pour y mettre à la place la date et le jour de la semaine.

à suivre...

25 La Lune pour de vrai !

2 dec 2020 :
Dans cette dernière nouvelle version (14.4) la Lune est représentée par une vraie photo perso de ladite Lune, prise en décembre... 2008.
En effet, ce code permet d'afficher des images lues sur la micro SDcard.
La photo d'origine représente la pleine Lune, le programme en efface la partie non éclairée (avec un croissant noir elliptique comme il se doit). Pour un surcroît de réalisme, je sais qu'il faudrait contrôler l'inclinaison de l'image.

26 Capture d'écran

10 dec 2020 :
J'ai ajouté 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). Enfin une image parfaitement nette partout !

La capture est déclenchée par appui sur le petit switch connecté au port PL2 de l'ATmega 2560 (voir schéma).

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'ai 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) à la fonction de capture). J'en fournis une, vous pouvez aussi la créer (de couleur unie par exemple) avec The 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. Pour plus de détails, voir la fonction :

void write_TFT_on_SDcard()
(ligne 2520 environ...)

27 Joyeux Noël

26 dec 2020 :
Par joyeux Noël j'entends souligner le fait qu'il fait froid, comme on peut le voir sur cette courbe jaune descendant inexorablement. Pour cette nouvelle version du soft j'ai affiché l'heure avec des caractères plus gros.

28 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. Voici qui est chose faite : j'ai 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.

29 Affichage en couleur :

Couleurs arc-en-ciel avec une formule fortement empirique !

Remarque : c'est un peu l'inverse des couleurs du corps noir chauffé, mais ça correspond mieux à la représentation classique de la température.
En effet, lorsque la température augmente, la matière rayonne depuis l'infrarouge vers l'ultraviolet en passant donc, dans l'ordre par le rouge, l'orange, le jaune, pas tellement le vert (vu à l'oeil) puisque le rayonnement rouge est toujours en partie présent, et lorsque s'y ajoute du bleu, on glisse du jaune vers... le blanc (d'où l'expression "chauffer à blanc" des métallurgistes).
Toutefois, dans la nature, le blanc est la couleur de la neige, le bleu de l'eau et de la glace, donc des températures froides et le rouge (et le jaune) la couleur du feu de bois dans la chemninée, donc de la chaleur. Tout le contraire donc.

30 Anomalie météo du mois d'octobre 2022

Les derniers relevés (au 26 décembre 2022) font apparaître deux remontées inattendues des températures (octobre 2022 puis plus importante encore en décembre). Nous sommes ici dans le département de l'Hérault, où nous avons connu deux mois de canicule quasiment non-stop, dès le mois de juin qui plus est. Jusqu"au 15 novembre, on rencontrait des papillons, des fourmis en nombre. Au 15 décembre des lézards se chaufant au soleil.

31 Deux ans 1/2 de données pour l'Hérault :

à jour au 22 Aout 2023, ce qui montre que cette appareil fonctionne toujours après deux ans et demi ;)

32 Code source du soft en Qt5 :

CODE SOURCE en C++
  1. /*
  2.   Programme écrit par Silicium628
  3.   ce logiciel est libre et open source
  4.  
  5.   */
  6. #include "mainwindow.h"
  7. #include "ui_mainwindow.h"
  8. #include <QDebug>
  9. #include <QMessageBox>
  10. #include <QDir>
  11. #include <QFileDialog>
  12. #include <QStringList>
  13. #include <QTextStream>
  14.  
  15. #include "math.h"
  16.  
  17. QString version = "1.9";
  18.  
  19. #define MIN 0
  20. #define MAX 1
  21.  
  22. QColor couleur_ecran = QColor::fromRgb(50, 50, 50, 255);
  23. QColor couleur_cadre = QColor::fromRgb(255, 255, 255, 255);
  24. QColor couleur_grille = QColor::fromRgb(150, 150, 150, 150);
  25. QColor couleur_trace = QColor::fromRgb(0, 150, 0, 255);
  26. QColor couleur_texte = QColor::fromRgb(255, 255, 255, 255);
  27. QColor couleur_date = QColor::fromRgb(0, 100, 0, 255);
  28.  
  29. QColor couleur_tiret1 = QColor::fromRgb(0, 100, 100, 255); // cyan
  30. QColor couleur_tiret2 = QColor::fromRgb(100, 100, 100, 255); // gris
  31. QColor couleur_tiret3 = QColor::fromRgb(33, 181, 55, 255); //152, 255, 152, 255
  32. QColor couleur_tiret4 = QColor::fromRgb(255, 0, 0, 255); // rouge
  33.  
  34. QColor couleur_index = QColor::fromRgb(255, 255, 100, 255);
  35.  
  36. QPen pen_cadre(couleur_cadre, 1, Qt::SolidLine);
  37. QPen pen_grille(couleur_grille, 1, Qt::SolidLine);
  38. QPen pen_trace(couleur_trace, 1, Qt::SolidLine);
  39. QPen pen_texte(couleur_texte, 1, Qt::SolidLine);
  40. QPen pen_date(couleur_date, 1, Qt::SolidLine);
  41. QPen pen_tiret1(couleur_tiret1, 1, Qt::SolidLine);
  42. QPen pen_tiret2(couleur_tiret2, 1, Qt::SolidLine);
  43. QPen pen_tiret3(couleur_tiret3, 1, Qt::SolidLine);
  44. QPen pen_tiret4(couleur_tiret4, 1, Qt::SolidLine);
  45. QPen pen_index(couleur_index, 1, Qt::SolidLine);
  46.  
  47.  
  48. QString nom_fichier_in="";
  49. QStringList liste1;
  50. QStringList liste_mois;
  51.  
  52. QList<Enrt> liste_enr;
  53. QList<Enrt_min_max> liste_min_max;
  54.  
  55. Enrt enr_i;
  56. Enrt memo_enr_i;
  57.  
  58.  
  59. //date du début de la prise en compte des mesures
  60. QDate d00;
  61. QTime t00;
  62. QDateTime dt00;
  63.  
  64. //date de la première mesure
  65. QDate d0;
  66. QTime t0;
  67. QDateTime dt0;
  68.  
  69. //date flotante pour balayage de la plage
  70. QDate d1;
  71. QTime t1;
  72. QDateTime dt1;
  73. QDateTime memo_dt1;
  74.  
  75. QDateTime dt_debut_affi;
  76.  
  77. int jour1=1;
  78.  
  79.  
  80. int num_enr0;
  81.  
  82. int num_out=0;
  83.  
  84. int GV1_x0 = 10;
  85. int GV1_y0 = 220;
  86. int GV1_dx = 1570;
  87. int GV1_dy = 650;
  88.  
  89. int nb_enr_affi; // nb d'enregistrements à afficher
  90. char type_i; // = 'j' .. 's' .. 'm' .. '6' .. 'a' .. '2'
  91. int echelle=1;
  92.  
  93. int x_offset = 40; // pour faire coincider la grille pré-calculée avec l'affichage de la courbe
  94.  
  95. int n_offset; // pour décaller la zone à afficher dans l'ensemble des enregistrements
  96.  
  97. int annee_affi=2020;
  98. int mois_affi=1;
  99. int nb_de_jours;;
  100.  
  101.  
  102. int fichier_OK =0;
  103.  
  104.  
  105.  
  106.  
  107. MainWindow::MainWindow(QWidget *parent) :
  108. QMainWindow(parent)
  109.  
  110. {
  111.  
  112. setupUi(this);
  113. setWindowTitle("Traceur Meteo " + version);
  114. setGeometry(0,0,1600,995);
  115.  
  116. // scene 1
  117.  
  118. scene1 = new QGraphicsScene(this);
  119. scene1->setBackgroundBrush(couleur_ecran);
  120.  
  121. graphicsView1->setScene(scene1);
  122. graphicsView1->setGeometry(GV1_x0, GV1_y0, GV1_dx, GV1_dy); // POSITION et dimensions de l'écran
  123.  
  124. groupe_grille = new QGraphicsItemGroup();
  125. groupe_trace = new QGraphicsItemGroup();
  126. groupe_texte = new QGraphicsItemGroup();
  127.  
  128. scene1->addItem(groupe_grille);
  129. scene1->addItem(groupe_trace);
  130. scene1->addItem(groupe_texte);
  131.  
  132. // scene 2
  133.  
  134. scene2 = new QGraphicsScene(this);
  135. scene2->setBackgroundBrush(couleur_ecran);
  136. graphicsView2->setScene(scene2);
  137. graphicsView2->setGeometry(10, 870, 1571, 20); // rectangle mince horizontal en bas
  138.  
  139. groupe_index = new QGraphicsItemGroup();
  140. groupe_data_presentes = new QGraphicsItemGroup();
  141.  
  142. scene2->addItem(groupe_data_presentes);
  143. scene2->addItem(groupe_index);
  144.  
  145. QStringList liste_entetes2;
  146. liste_entetes2 <<"T°C"<<"an"<<"mois"<<"jour"<<"heure"<<"minute"<<"QDate"<<"JulianDay"<< "delta t (s)";
  147. liste_mois << "janvier"<< "février"<< "mars"<< "avril"<< "mai"<< "juin"
  148. << "juillet"<< "août"<< "septembre"<< "octobre"<< "novembre"<< "décembre";
  149. tableWidget_2->setHorizontalHeaderLabels (liste_entetes2);
  150. //on_spinBox_2_valueChanged(1);
  151.  
  152. //date du début de la prise en compte des mesures
  153. etiquette2(5, 0, "01/01/2021");
  154. d00.setDate(2021, 1, 1);
  155. t00.setHMS(0,0,0);
  156. dt00.setDate(d00);
  157. dt00.setTime(t00);
  158.  
  159. }
  160.  
  161.  
  162.  
  163.  
  164. MainWindow::~MainWindow()
  165. {
  166.  
  167. }
  168.  
  169.  
  170. void MainWindow::effacer_trace()
  171. {
  172. foreach( QGraphicsItem *item, scene1->items( groupe_trace->boundingRect() ) )
  173. {
  174. if( item->group() == groupe_trace ) { delete item; }
  175. }
  176. }
  177.  
  178.  
  179.  
  180. void MainWindow::effacer_grille()
  181. {
  182. foreach( QGraphicsItem *item, scene1->items( groupe_grille->boundingRect() ) )
  183. {
  184. if( item->group() == groupe_grille ) { delete item; }
  185. }
  186. }
  187.  
  188.  
  189. void MainWindow::effacer_texte()
  190. {
  191.  
  192. foreach( QGraphicsItem *item, scene1->items( groupe_texte->boundingRect() ) )
  193. {
  194. if( item->group() == groupe_texte ) { delete item; }
  195. }
  196. }
  197.  
  198.  
  199. void MainWindow::effacer_tout()
  200. {
  201. effacer_trace();
  202. effacer_grille();
  203. effacer_texte();
  204. }
  205.  
  206.  
  207. void MainWindow::effacer_index()
  208. {
  209. foreach( QGraphicsItem *item, scene2->items( groupe_index->boundingRect() ) )
  210. {
  211. if( item->group() == groupe_index ) { delete item; }
  212. }
  213. }
  214.  
  215.  
  216. void MainWindow::etiquette1(int x, int y, QString s1) // sur scene1
  217. {
  218. textItem1 = new QGraphicsSimpleTextItem(s1);
  219. QBrush brush1;
  220. brush1.setStyle(Qt::SolidPattern);
  221. brush1.setColor("#FFFFFF");
  222. //if (Ti > 0){brush1.setColor("#FF4400");}
  223. textItem1->setBrush(brush1);
  224. textItem1->setPos(x+5, y-5);
  225. groupe_texte->addToGroup(textItem1);
  226. }
  227.  
  228.  
  229. void MainWindow::etiquette2(int x, int y, QString s1) // sur scene2
  230. {
  231. textItem1 = new QGraphicsSimpleTextItem(s1);
  232. QBrush brush1;
  233. brush1.setStyle(Qt::SolidPattern);
  234. brush1.setColor("#0055FF");
  235. //if (Ti > 0){brush1.setColor("#FF4400");}
  236. textItem1->setBrush(brush1);
  237. textItem1->setPos(x+5, y-5);
  238. scene2->addItem(textItem1);
  239. }
  240.  
  241.  
  242. void MainWindow::determine_nb_de_jours()
  243. {
  244. nb_de_jours =10; // à priori
  245.  
  246. if(type_i=='j') {nb_de_jours = 1;}
  247. if(type_i=='s') {nb_de_jours = 8;}
  248. if(type_i=='m')
  249. {
  250. if (mois_affi==1) {nb_de_jours = 31;}
  251. if (mois_affi==2) {nb_de_jours = 28;}
  252. if (mois_affi==3) {nb_de_jours = 31;}
  253. if (mois_affi==4) {nb_de_jours = 30;}
  254. if (mois_affi==5) {nb_de_jours = 31;}
  255. if (mois_affi==6) {nb_de_jours = 30;}
  256. if (mois_affi==7) {nb_de_jours = 31;}
  257. if (mois_affi==8) {nb_de_jours = 31;}
  258. if (mois_affi==9) {nb_de_jours = 30;}
  259. if (mois_affi==10) {nb_de_jours = 31;}
  260. if (mois_affi==11) {nb_de_jours = 30;}
  261. if (mois_affi==12) {nb_de_jours = 31;}
  262. }
  263. if(type_i=='6') {nb_de_jours = 7;} // à voir
  264. if(type_i=='a') {nb_de_jours = 13;}
  265. if(type_i=='2') {nb_de_jours = 25;}
  266. }
  267.  
  268.  
  269.  
  270. void MainWindow::affiche_grille()
  271. {
  272. int x, y, w, h;
  273. float Ti=-20;
  274.  
  275. x=0;
  276. y=-420;
  277. w= 1550;
  278. h=640;
  279.  
  280. rectangle1 = new QGraphicsRectItem(x, y, w, h);
  281. rectangle1->setPen(pen_cadre);
  282. groupe_grille->addToGroup(rectangle1);
  283.  
  284.  
  285. //traits horizontaux
  286. x=0;
  287. for(int i=0; i<13; i++)
  288. {
  289.  
  290. y = - 10 * Ti;
  291. segment_i = new QGraphicsLineItem(30+x, y, x+1500, y);
  292. segment_i->setPen(pen_grille);
  293. groupe_grille->addToGroup(segment_i);
  294.  
  295. // étiquettes texte
  296. QString s1;
  297. s1.setNum(Ti); // température
  298. etiquette1(x+5, y-5, s1); // graduation de l'axe vertical des température, à gauche
  299.  
  300. Ti += 5.0;
  301.  
  302. }
  303.  
  304. // traits verticaux
  305.  
  306. determine_nb_de_jours();
  307.  
  308. y=-400;
  309. for(int i=0; i<nb_de_jours+1; i++)
  310. {
  311. if(type_i=='j') {x = x_offset + 7*4.5*48 *i; }
  312. if(type_i=='s') {x = x_offset + 4*48 *i; }
  313. if(type_i=='m') {x = x_offset + 48 *i; } // OK pour 1 mois ; 48*30mn = 24h
  314. if(type_i=='6') {x = x_offset + 5*48 *i; } // à voir
  315. if(type_i=='a') {x = x_offset + 2*48 *i; }
  316. if(type_i=='2') {x = 0; }
  317.  
  318. if (type_i == 'm')
  319. {
  320. segment_i = new QGraphicsLineItem(x, y, x, y+600);
  321. segment_i->setPen(pen_grille);
  322. groupe_grille->addToGroup(segment_i);
  323.  
  324. if ( (jour1 + i) <= nb_de_jours)
  325. {
  326. QString s1;
  327. s1.setNum(jour1 + i);
  328. etiquette1(x, 150, s1);
  329. }
  330. }
  331. }
  332. scene1->addItem(groupe_grille);
  333. }
  334.  
  335.  
  336. void engendre_liste_min_max()
  337. {
  338. float T, T_min, T_max;
  339. int n_max = liste_enr.length();
  340. int a, m, j, h;
  341. int fin_de_journee =0;
  342. Enrt enr_i;
  343. Enrt_min_max enr_min_max_i;
  344. T_min = 100;
  345. T_max = -100;
  346.  
  347. for(int n=0; n<n_max; n++)
  348. {
  349. a=liste_enr[n].annee;
  350. m=liste_enr[n].mois;
  351. j=liste_enr[n].jour;
  352. h=liste_enr[n].heure;
  353.  
  354. if ((h>=1) && (h<2) ) {fin_de_journee = 0; T_min = 100; T_max = -100; } // départ pour le jour suivant
  355. // en principe le minimum se situe vers 5h du mat et le maximum vers 16h
  356.  
  357. T=liste_enr[n].temperature;
  358. if (T < T_min) {T_min = T;}
  359. if (T > T_max) {T_max = T;}
  360.  
  361. if (fin_de_journee == 0)
  362. {
  363. if (h>=20) // '>=' et pas '==' parce que l'enr exact '20h00' peut manquer
  364. {
  365. fin_de_journee=1; // pour ne plus repasser ici
  366.  
  367. enr_min_max_i.annee=a;
  368. enr_min_max_i.mois=m;
  369. enr_min_max_i.jour=j;
  370. enr_min_max_i.temperature_min = T_min;
  371. enr_min_max_i.temperature_max = T_max;
  372. liste_min_max << enr_min_max_i; // on enregistre
  373. }
  374. }
  375. }
  376. }
  377.  
  378.  
  379.  
  380.  
  381. int MainWindow::determine_d0()
  382. // premier 1er d'un mois dans des enregistrements (qui démarrent éventuellement en cours de mois)
  383. {
  384. int n=1;
  385. int trouve =0;
  386. int a, m, j, h, mi;
  387.  
  388. while ( (trouve == 0) && (n < (liste_enr.length() ))) //- 1440) ) )
  389. {
  390. a=liste_enr[n].annee;
  391. m=liste_enr[n].mois;
  392. j=liste_enr[n].jour;
  393. h=liste_enr[n].heure;
  394. mi=liste_enr[n].minute;
  395.  
  396. d0.setDate(a, m, j);
  397. t0.setHMS(h,mi,0);
  398.  
  399. dt0.setDate(d0);
  400. dt0.setTime(t0);
  401.  
  402. //if((m==mois_affi) && (j==1) && (h==0) && (mi==0)) {trouve=1;}
  403. if( (a==annee_affi) && (m==mois_affi) && (j==jour1) && (h==0) && (mi==0)) { trouve=1; }
  404. n++;
  405. }
  406. if ( (trouve == 1) && (n>1) )
  407. {
  408. num_enr0 = n-1;
  409. QString s1;
  410. s1.setNum(n);
  411. lineEdit_3->setText(s1);
  412. return 0; // OK
  413. }
  414. else {return 1;} // 1 erreur (pas trouvé)
  415. }
  416.  
  417.  
  418. void MainWindow::affiche_index(QDateTime dt_i)
  419. {
  420. // indique la partie affichée, par un petit rectangle bleu sur la barre horizontale du bas
  421. qint64 delta_t;
  422. int x, dx;
  423. dx=1;
  424.  
  425. if (type_i == 's'){dx=7;}
  426. if (type_i == 'm'){dx=28;}
  427. if (type_i == '6'){dx=28*6;}
  428. if (type_i == 'a'){dx=28*12;}
  429.  
  430. delta_t = (dt00 .msecsTo(dt_i)/1000); // (en s) intervalle de temps entre les deux QDateTime
  431. delta_t /= 1800; //(en demi-heures)
  432.  
  433. x = 0 + delta_t/50;
  434.  
  435. rectangle1 = new QGraphicsRectItem(x, -1, dx, 10);
  436. rectangle1->setPen(pen_index);
  437. groupe_index->addToGroup(rectangle1);
  438. scene2->addItem(groupe_index);
  439. }
  440.  
  441.  
  442.  
  443. void MainWindow::tracer_data_presentes() // dans la barre d'index horizontale en bas
  444. {
  445. qint64 delta_t;
  446. float x=0;
  447.  
  448. int nb_enr_dispo = liste_enr.length()-2;
  449.  
  450. for(int n=1; n<nb_enr_dispo; n+=100) // +100 pour gagner en rapidité !
  451. {
  452. int a, m, j, h, mi;
  453. a=liste_enr[n].annee;
  454. m=liste_enr[n].mois;
  455. j=liste_enr[n].jour;
  456. h=liste_enr[n].heure;
  457. mi=liste_enr[n].minute;
  458.  
  459. //date flotante pour balayage de la plage
  460. d1.setDate(a, m, j);
  461. t1.setHMS(h,mi,0);
  462. dt1.setDate(d1);
  463. dt1.setTime(t1);
  464.  
  465. // calcul intervalle depuis la date du début de la prise en compte des mesures
  466. delta_t = (dt00.msecsTo(dt1)/1000); // (en s) intervalle de temps entre les deux QDateTime
  467. delta_t /= 1800; //(en demi-heures)
  468.  
  469. // transcription [durée] -> [longueur]
  470. x = 0 + delta_t/50;
  471.  
  472. segment_i = new QGraphicsLineItem(x, 0, x, 8);
  473. segment_i->setPen(pen_tiret3);
  474.  
  475. groupe_data_presentes->addToGroup(segment_i);
  476. scene2->addItem(groupe_data_presentes);
  477. }
  478. }
  479.  
  480.  
  481. QColor couleur_de_temperature(float i)
  482. {
  483. float xr,xv,xb;
  484. float r,v,b;
  485. QColor c1;
  486.  
  487. i = 35-i;
  488. i /= 8;
  489. xr=(i+12)/10.0; r=140.0+120.0*sin(xr); if (r<0) {r=0;} if (r>255) {r=255;}
  490. xv=(i+54)/10.0; v=130.0+150.0*sin(xv); if (v<0) {v=0;} if (v>255) {v=255;};
  491. xb=(i+38)/10.0; b=100.0+150.0*sin(xb); if (b<0) {b=0;} if (b>255) {b=255;};
  492.  
  493. c1.setRgb(b,v,r);
  494. return c1;
  495. }
  496.  
  497.  
  498.  
  499. void MainWindow::tracer_courbe()
  500. {
  501. qint64 delta_t;
  502.  
  503. //float temperature_i;
  504. float memo_temperature_i = 0;
  505. int glitch =0;
  506. int n2;
  507.  
  508. // effacer_index();
  509.  
  510. if (fichier_OK == 0) {return;}
  511.  
  512. determine_nb_de_jours();
  513.  
  514. if(type_i=='j') {nb_enr_affi = 48; spinBox_jour->setHidden(0);}
  515. if(type_i=='s') {nb_enr_affi = 336; spinBox_jour->setHidden(0);}
  516. if(type_i=='m') {nb_enr_affi = 48 * (nb_de_jours-1); spinBox_jour->setHidden(0);}
  517. if(type_i=='6') {nb_enr_affi = 8766; spinBox_jour->setValue(1); spinBox_jour->setHidden(1);}
  518. if(type_i=='a') {nb_enr_affi = 17532; spinBox_jour->setValue(1); spinBox_jour->setHidden(1);}
  519. if(type_i=='2') {nb_enr_affi = 35064; spinBox_jour->setValue(1); spinBox_jour->setHidden(1);}
  520.  
  521. float x=0;
  522. float y=0;
  523. float memo_x, memo_y;
  524. int n, p;
  525. int n_max;
  526.  
  527. effacer_tout();
  528.  
  529. int nb_enr_dispo = liste_enr.length()-2;
  530.  
  531. p=0;
  532.  
  533. // on va afficher 'nb_enr_affi' enregistrements
  534. n_max = nb_enr_affi;
  535. if(n_max > (nb_enr_dispo-num_enr0)) {n_max = nb_enr_dispo -num_enr0 -1;}
  536.  
  537.  
  538. for(n=1; n<n_max; n++)
  539. {
  540. //qDebug()<< n;
  541. n2 = num_enr0 + n;
  542.  
  543. // on va donc considérer la liste comme une collections de points pas forcément équidistants dans le temps
  544. // et on va les placer sur l'axe horizontal en fontion de leur position réelle dans le temps
  545. // c'est simple puisque que le type 'QDate' permet de connaitre cette position temporelle
  546.  
  547. int a, m, j, h, mi;
  548.  
  549. // lecture de la date de chaque enregistrement
  550. a=liste_enr[n2].annee;
  551. m=liste_enr[n2].mois;
  552. j=liste_enr[n2].jour;
  553. h=liste_enr[n2].heure;
  554. mi=liste_enr[n2].minute;
  555.  
  556. //constitution de la date flotante pour balayage de la plage
  557. d1.setDate(a, m, j);
  558. t1.setHMS(h,mi,0);
  559. dt1.setDate(d1);
  560. dt1.setTime(t1);
  561.  
  562. // mémorisation pour afficher le petit rectangle en bas
  563. if (n==1) {dt_debut_affi = dt1;} // date du début de l'affichage
  564.  
  565. // calcul intervalle depuis la date de la première mesure
  566. delta_t = (dt0.msecsTo(dt1)/1000); // (en s)
  567. delta_t /= 1800; //(en demi-heures)
  568.  
  569. memo_x = x;
  570. memo_y = y;
  571.  
  572. if(type_i=='j')
  573. {
  574. x = 45 + x_offset + 16* delta_t;
  575. if ( (h==12)&&(mi==0))
  576. {
  577. QString s1, s2, s3, st;
  578. s1.setNum(j); s2.setNum(m); s3.setNum(a);
  579. st = s1 + "/"+ s2 + "/" + s3;
  580. etiquette1(x-30, -200, st); // date affichée en haut
  581. }
  582. }
  583. if(type_i=='s')
  584. {
  585. // transcription [durée] -> [longueur]
  586. x = 45 + 4*delta_t;
  587.  
  588. if((h==12) && (mi==0))
  589. {
  590. QString s1, s2, s3, st;
  591. s1.setNum(j); s2.setNum(m); s3.setNum(a);
  592. st = s1 + "/"+ s2 + "/" + s3;
  593. etiquette1(x-30, 200, st); // dates affichées en bas sur l'axe x
  594. }
  595. }
  596. if(type_i=='m') { x = x_offset + delta_t; }
  597. if(type_i=='6') { x = x_offset + delta_t/6; }
  598. if(type_i=='a')
  599. {
  600. x = x_offset + delta_t/12;
  601. if( (j==1) && (h==0) )
  602. {
  603. QString s1, s2, st;
  604. s1.setNum(m); s2.setNum(a);
  605. st = s1 + "/"+ s2;
  606. etiquette1(x+30, -400, st); // dates affichées en haut
  607. }
  608. }
  609. if(type_i=='2') { x = x_offset + delta_t/24; }
  610.  
  611.  
  612. float temperature_i = liste_enr[n2].temperature;
  613. glitch =0;
  614. if ((abs(temperature_i - memo_temperature_i)) > 10.0 ) { glitch = 1;} // dans ce cas ne pas afficher la valeur foireuse
  615.  
  616. y = - 10 * temperature_i;
  617.  
  618. memo_temperature_i = temperature_i;
  619. p++; // nb de passages
  620.  
  621. if ((p > 1 ) && (glitch ==0)) // pour éviter de tracer le 1er segment dont un seul point est défini
  622. {
  623. if (abs(memo_x - x)<20)
  624. {
  625. // TRACE LA COURBE DE TEMPERATURE
  626. segment_i = new QGraphicsLineItem(memo_x, memo_y, x, y);
  627. QColor coul_1=couleur_de_temperature(y);
  628. pen_trace.setColor(coul_1);
  629. segment_i->setPen(pen_trace);
  630. groupe_trace->addToGroup(segment_i);
  631.  
  632. //segment_i->setLine(memo_x+5, memo_y, x+5, y);
  633. //groupe_trace->addToGroup(segment_i);
  634. }
  635. }
  636.  
  637. // traits verticaux
  638. if ((h==0)&&(mi==0))
  639. {
  640. segment_i = new QGraphicsLineItem( x, -20, x, 20);
  641. segment_i->setPen(pen_tiret1); // indique minuit (séparation entre les jours)
  642. groupe_trace->addToGroup(segment_i);
  643.  
  644. if (j==1)
  645. {
  646. segment_i = new QGraphicsLineItem( x, -400, x, 200);
  647. segment_i->setPen(pen_tiret2); // indique minuit (séparation entre les jours)
  648. groupe_trace->addToGroup(segment_i);
  649. }
  650. }
  651.  
  652. if((type_i=='j') && (mi==0))
  653. {
  654. segment_i = new QGraphicsLineItem( x, -400, x, 200);
  655. if (h==12) {segment_i->setPen(pen_tiret4);} else {segment_i->setPen(pen_tiret2);}
  656. groupe_trace->addToGroup(segment_i); // indique les heures (midi = rouge)
  657.  
  658. QString s1;
  659. s1.setNum(h);
  660. etiquette1(x-10, 205, s1); // dates affichées en bas sur l'axe x
  661. }
  662.  
  663. if((type_i=='6') && (j==1)&& (h==0))
  664. {
  665. segment_i = new QGraphicsLineItem( x, -400, x, 200);
  666. if (h==12) {segment_i->setPen(pen_tiret4);} else {segment_i->setPen(pen_tiret2);}
  667. groupe_trace->addToGroup(segment_i); // indique les heures (midi = rouge)
  668.  
  669. QString s1;
  670. //s1.setNum(m);
  671. s1 = liste_mois[m-1];
  672. etiquette1(x+50, 180, s1); // dates affichées en bas sur l'axe x
  673. }
  674. }
  675.  
  676. scene1->addItem(groupe_trace);
  677.  
  678. affiche_grille();
  679. }
  680.  
  681.  
  682.  
  683. void MainWindow::tracer_min_max(int plein)
  684. {
  685. qint64 delta_t;
  686.  
  687. float x=0;
  688. float y1=0;
  689. float y2=0;
  690. float memo_tpr_min =0;
  691. float memo_tpr_max =0;
  692. float memo_x, memo_y1, memo_y2;
  693.  
  694. int n, n2, p;
  695.  
  696. int num_enr_min0;
  697. int n_max;
  698. int glitch =0;
  699.  
  700. determine_nb_de_jours();
  701.  
  702. // if(type_i=='j') {nb_enr_affi = 48; spinBox_jour->setHidden(0);}
  703. // if(type_i=='s') {nb_enr_affi = 336; spinBox_jour->setHidden(0);}
  704. if(type_i=='m') {nb_enr_affi = nb_de_jours-1; spinBox_jour->setHidden(0);}
  705. // if(type_i=='6') {nb_enr_affi = 8766; spinBox_jour->setValue(1); spinBox_jour->setHidden(1);}
  706. // if(type_i=='a') {nb_enr_affi = 17532; spinBox_jour->setValue(1); spinBox_jour->setHidden(1);}
  707. // if(type_i=='2') {nb_enr_affi = 35064; spinBox_jour->setValue(1); spinBox_jour->setHidden(1);}
  708.  
  709.  
  710. int nb_enr_dispo = liste_min_max.length()-2;
  711. num_enr_min0 = (num_enr0 / 47) -5;
  712.  
  713. // qDebug() << "num_enr_min0" << num_enr_min0;
  714.  
  715. p=0;
  716.  
  717. // on va afficher 'nb_enr_affi' enregistrements
  718. //n_max = nb_enr_affi;
  719. n_max =40;
  720.  
  721. if(n_max > (nb_enr_dispo - num_enr_min0)) {n_max = nb_enr_dispo-num_enr_min0 -1;}
  722.  
  723. for(n=1; n<n_max; n++)
  724. {
  725. n2 =num_enr_min0 + n;
  726. int a, m, j;
  727.  
  728. // lecture de la date de chaque enregistrement
  729. a=liste_min_max[n2].annee;
  730. m=liste_min_max[n2].mois;
  731. j=liste_min_max[n2].jour;
  732.  
  733. //constitution de la date flotante pour balayage de la plage
  734. d1.setDate(a, m, j);
  735. t1.setHMS(14,0,0);
  736. dt1.setDate(d1);
  737. dt1.setTime(t1);
  738.  
  739. delta_t = (dt0.msecsTo(dt1)/1000); // (en s)
  740. delta_t /= 1800; //(en demi-heures)
  741.  
  742. memo_x = x;
  743. memo_y1 = y1;
  744. memo_y2 = y2;
  745.  
  746. if(type_i=='m')
  747. {
  748. x = x_offset + delta_t;
  749. }
  750.  
  751. float tpr_min =0;
  752. float tpr_max =0;
  753.  
  754. tpr_min = liste_min_max[n2].temperature_min;
  755. tpr_max = liste_min_max[n2].temperature_max;
  756.  
  757. // lorsque l'on coupe l'alimentation de la station météo, par exemple pour la modifier (hard ou soft) il s'ensuit
  758. // des manques de données et des 'sauts' de température dans la liste des enregistrements, des 'glitch'
  759. glitch =0; // à priori
  760. // if ((abs(tpr_max - memo_tpr_max)) > 10.0 ) { glitch = 1;}
  761. // if ((abs(tpr_min - memo_tpr_min)) > 10.0 ) { glitch = 1;}
  762.  
  763. y1 = - 10 * tpr_min;
  764. y2 = - 10 * tpr_max;
  765.  
  766. p++; // nb de passages
  767.  
  768. memo_tpr_min = tpr_min;
  769. memo_tpr_max = tpr_max;
  770.  
  771. if ((p > 1 ) && (glitch ==0)) // 'p' pour éviter de tracer le 1er segment dont un seul point est défini
  772. {
  773.  
  774. if(plein == 1)
  775. {
  776. // remplissage entre les 2 courbes mini-maxi, avec couche alpha
  777. QPolygonF poly1;
  778. poly1 << QPointF(memo_x, memo_y1) << QPointF(x, y1) << QPointF(x, y2) << QPointF(memo_x, memo_y2);
  779. polygone1 = new QGraphicsPolygonItem(poly1);
  780.  
  781. QBrush brush1;
  782. QColor couleur_plein = QColor::fromRgb(255, 255, 100, 50);
  783. brush1.setColor(couleur_plein);
  784.  
  785. brush1.setStyle(Qt::SolidPattern);
  786. polygone1->setBrush(brush1);
  787.  
  788. QColor couleur_trait = QColor::fromRgb(0, 0, 0, 0);
  789. QPen pen1;
  790. pen1.setColor(couleur_trait);
  791. polygone1->setPen(pen1);
  792.  
  793. groupe_trace->addToGroup(polygone1);
  794. }
  795.  
  796. // TRACE LA COURBE DE TEMPERATURE MIN
  797. //segment_i = new QGraphicsLineItem(memo_x, memo_y, x, y);
  798. segment_i = new QGraphicsLineItem(memo_x, memo_y1, x, y1);
  799. pen_trace.setColor("#0055FF");
  800. segment_i->setPen(pen_trace);
  801. groupe_trace->addToGroup(segment_i);
  802.  
  803. // TRACE LA COURBE DE TEMPERATURE MAX
  804. segment_i = new QGraphicsLineItem(memo_x, memo_y2, x, y2);
  805. pen_trace.setColor("#FF5500");
  806. segment_i->setPen(pen_trace);
  807. groupe_trace->addToGroup(segment_i);
  808. }
  809. }
  810. }
  811.  
  812.  
  813.  
  814.  
  815. void MainWindow::Extraire_1_Enr(QString ligne_i)
  816. {
  817. int annee, mois, jour, heure, minute;
  818. QString s1, s2;
  819. int p1;
  820.  
  821. int Tdeg;
  822. float T_float;
  823. QDate d1;
  824. QTime t1;
  825. QDateTime dt1;
  826. qint64 JDay;
  827. qint64 delta_t;
  828.  
  829.  
  830. // "2022-08-03 [ 14:30 ] ; T(ext)=3063 ; T(int)=2976 ; H=28 ; P=1015"
  831. //enr_i.dateTime =
  832. //
  833. // //d1.fromString(s1, Qt::ISODate);
  834. // d1.fromString(s1, "yyyy-MM-dd");
  835.  
  836. int R=tableWidget_2->rowCount();
  837. tableWidget_2->setRowCount(R+1); //ajoute une nouvelle ligne au tableau
  838.  
  839. p1 = ligne_i.indexOf("T(ext)=");
  840. s1 = ligne_i.mid(p1+7, 4);
  841. Tdeg = s1.toInt();
  842. T_float = (float)Tdeg - 2730;
  843. T_float /= 10.0;
  844.  
  845. if ((T_float < -30) || (T_float > 50)) {T_float =0;}
  846. enr_i.temperature = T_float;
  847.  
  848. s2.setNum(T_float);
  849. tableWidget_2->setItem(num_out, 0, new QTableWidgetItem (s2) );
  850.  
  851.  
  852. s1 = ligne_i.left(4);
  853. tableWidget_2->setItem(num_out, 1, new QTableWidgetItem (s1) );
  854. annee = s1.toInt();
  855. enr_i.annee = annee;
  856.  
  857. s1 = ligne_i.mid(5, 2);
  858. tableWidget_2->setItem(num_out, 2, new QTableWidgetItem (s1) );
  859. mois = s1.toInt();
  860. enr_i.mois = mois;
  861.  
  862. s1 = ligne_i.mid(8, 2);
  863. tableWidget_2->setItem(num_out, 3, new QTableWidgetItem (s1) );
  864. jour = s1.toInt();
  865. enr_i.jour = jour;
  866.  
  867. // "2022-08-03 [ 14:30 ] ; T(ext)=3063 ; T(int)=2976 ; H=28 ; P=1015"
  868.  
  869. p1 = ligne_i.indexOf("[ ");
  870. s1 = ligne_i.mid(p1+2, 2);
  871. tableWidget_2->setItem(num_out, 4, new QTableWidgetItem (s1) );
  872. heure = s1.toInt();
  873. enr_i.heure = heure;
  874.  
  875. s1 = ligne_i.mid(p1+5, 2);
  876. tableWidget_2->setItem(num_out, 5, new QTableWidgetItem (s1) );
  877. minute = s1.toInt();
  878. enr_i.minute = minute;
  879.  
  880. t1.setHMS(heure, minute, 0); //QTime
  881. dt1.setTime(t1); //QDateTime
  882.  
  883. d1.setDate(annee, mois, jour); //QDate
  884. dt1.setDate(d1); //QDateTime
  885.  
  886. delta_t = memo_dt1.msecsTo(dt1) / 1000; // calcule l'intervalle de temps entre les deux QDateTime
  887.  
  888. s1.setNum(delta_t);
  889. tableWidget_2->setItem(num_out, 8, new QTableWidgetItem (s1) );
  890.  
  891. memo_dt1 = dt1;
  892.  
  893. s1 = d1.toString();
  894. tableWidget_2->setItem(num_out, 6, new QTableWidgetItem (s1) );
  895.  
  896. JDay = d1.toJulianDay();
  897. enr_i.JulianDay = JDay;
  898. s1.setNum(JDay);
  899. tableWidget_2->setItem(num_out, 7, new QTableWidgetItem (s1) );
  900.  
  901.  
  902. memo_enr_i= enr_i;
  903. liste_enr << enr_i;
  904. num_out++;
  905.  
  906. }
  907.  
  908.  
  909. int MainWindow::openFile()
  910. {
  911. QString ligne_i, str1;
  912.  
  913. fichier_OK = 0;
  914. liste1.clear();
  915. liste_enr.clear();
  916. liste_min_max.clear();
  917. //liste_chaines_out.clear();
  918.  
  919. nom_fichier_in = QFileDialog::getOpenFileName(this, tr("Ouvrir Fichier..."),
  920. QString(), tr("Fichiers txt (*.txt);;All Files (*)"));
  921. if (!nom_fichier_in.isEmpty())
  922. {
  923. lineEdit_1->setText(nom_fichier_in);
  924. }
  925.  
  926. QFile file1(nom_fichier_in);
  927. if (!file1.open(QIODevice::ReadOnly | QIODevice::Text)) return 1;
  928. QTextStream TextStream1(&file1);
  929.  
  930. int num_ligne = 0;
  931. while ( !TextStream1.atEnd() )
  932. {
  933. ligne_i = TextStream1.readLine();
  934. liste1 << ligne_i;
  935. Extraire_1_Enr(ligne_i);
  936.  
  937. num_ligne++;
  938. }
  939. file1.close();
  940.  
  941. str1.setNum(num_ligne);
  942. lineEdit_2->setText(str1 + " lignes");
  943.  
  944. tableWidget_2->resizeColumnsToContents();
  945. fichier_OK = 1;
  946.  
  947. return 0;
  948. }
  949.  
  950.  
  951. void MainWindow::LISTER()
  952. {
  953. if (!nom_fichier_in.isEmpty())
  954. {
  955. QString ligne_i1;
  956. QString debut_ligne;
  957.  
  958. int nb_lignes=liste1.count();
  959. int num_ligne =0;
  960.  
  961.  
  962. tableWidget_1->clear();
  963. tableWidget_1->setRowCount(0);
  964.  
  965. while((num_ligne < nb_lignes-1) )
  966. {
  967. ligne_i1 = liste1[num_ligne];
  968.  
  969. int R=tableWidget_1->rowCount();
  970. tableWidget_1->setRowCount(R+1); //ajoute une nouvelle ligne au tableau
  971. tableWidget_1->setItem(num_ligne, 0, new QTableWidgetItem (ligne_i1) );
  972. num_ligne++;
  973. }
  974. tableWidget_1->resizeColumnsToContents();
  975. }
  976. }
  977.  
  978.  
  979. void MainWindow::on_Btn_FICHIER_clicked()
  980. {
  981. setCursor(Qt::WaitCursor);
  982. Btn_FICHIER->setStyleSheet("background-color: #FFF396;"); //jaune
  983. lineEdit_1->clear();
  984. int result = openFile();
  985. if (result == 0)
  986. {
  987.  
  988. Btn_FICHIER->setStyleSheet("background-color: #73FF7A;"); // vert
  989. LISTER();
  990. determine_d0();
  991. engendre_liste_min_max();
  992. type_i='m';
  993. tous_bt_gris();
  994. Bt_1mois->setStyleSheet("background-color: #73FF7A;");
  995. on_spinBox_1_valueChanged(2022);
  996. mois_affi--;
  997. on_Bt_mois_plus_clicked();
  998.  
  999. tracer_data_presentes();
  1000.  
  1001. }
  1002. else
  1003. {
  1004. Btn_FICHIER->setStyleSheet("background-color: #FF9090;"); //rouge
  1005. }
  1006. setCursor(Qt::ArrowCursor);
  1007. }
  1008.  
  1009.  
  1010.  
  1011. void MainWindow::on_pushButton_2_clicked()
  1012. {
  1013. affiche_grille();
  1014. }
  1015.  
  1016.  
  1017. //Rappel : dispo : 1 enregistrement toutes les 30 minutes (2 enr/h)
  1018.  
  1019. void MainWindow::on_Bt_1jour_clicked()
  1020. {
  1021. Bt_1jour->setStyleSheet("background-color: #73FF7A;");
  1022. Bt_1semaine->setStyleSheet("background-color: #F5F5F5;");
  1023. Bt_1mois->setStyleSheet("background-color: #F5F5F5;");
  1024. // 1j = 24h -> 24*2 = 48 enr
  1025. // nb_enr_affi = 48;
  1026. type_i='j';
  1027. effacer_index();
  1028. int R = determine_d0();
  1029. if (R==0) { tracer_courbe(); }
  1030.  
  1031. QDateTime dti;
  1032. QDate di;
  1033. QTime ti;
  1034. di.setDate(annee_affi, mois_affi, 1);
  1035. ti.setHMS(0,0,0);
  1036.  
  1037. dti.setDate(di);
  1038. dti.setTime(ti);
  1039. affiche_index(dti);
  1040. }
  1041.  
  1042.  
  1043. void MainWindow::tous_bt_gris()
  1044. {
  1045. Bt_2ans->setStyleSheet("background-color: #F5F5F5;");
  1046. Bt_1an->setStyleSheet("background-color: #F5F5F5;");
  1047. Bt_6mois->setStyleSheet("background-color: #F5F5F5;");
  1048. Bt_1mois->setStyleSheet("background-color: #F5F5F5;");
  1049. Bt_1jour->setStyleSheet("background-color: #F5F5F5;");
  1050. Bt_1semaine->setStyleSheet("background-color: #F5F5F5;");
  1051. }
  1052.  
  1053.  
  1054. void MainWindow::on_Bt_1semaine_clicked()
  1055. {
  1056. tous_bt_gris();
  1057. Bt_1semaine->setStyleSheet("background-color: #73FF7A;");
  1058.  
  1059. // 1 semaine -> 7* 48 = 336 enr
  1060. // nb_enr_affi = 336;
  1061. type_i='s';
  1062. effacer_index();
  1063. int R = determine_d0();
  1064. if (R==0) { tracer_courbe(); }
  1065.  
  1066. QDateTime dti;
  1067. QDate di;
  1068. QTime ti;
  1069. di.setDate(annee_affi, mois_affi, 1);
  1070. ti.setHMS(0,0,0);
  1071.  
  1072. dti.setDate(di);
  1073. dti.setTime(ti);
  1074. affiche_index(dti);
  1075.  
  1076. }
  1077.  
  1078.  
  1079. void MainWindow::on_Bt_1mois_clicked()
  1080. {
  1081. tous_bt_gris();
  1082. Bt_1mois->setStyleSheet("background-color: #73FF7A;");
  1083. type_i='m';
  1084. effacer_index();
  1085. int R = determine_d0();
  1086. if (R==0) { tracer_courbe(); }
  1087.  
  1088. QDateTime dti;
  1089. QDate di;
  1090. QTime ti;
  1091. di.setDate(annee_affi, mois_affi, 1);
  1092. ti.setHMS(0,0,0);
  1093.  
  1094. dti.setDate(di);
  1095. dti.setTime(ti);
  1096. affiche_index(dti);
  1097.  
  1098. QString s1;
  1099. s1.setNum(annee_affi);
  1100. QString s2;
  1101. s2=liste_mois[mois_affi-1];
  1102. s2 += " ";
  1103. s2+=s1;
  1104. lineEdit_4->setText(s2);
  1105. etiquette1(100, -370, s2);
  1106. }
  1107.  
  1108.  
  1109. void MainWindow::on_Bt_6mois_clicked()
  1110. {
  1111. tous_bt_gris();
  1112. Bt_6mois->setStyleSheet("background-color: #73FF7A;");
  1113.  
  1114. type_i='6';
  1115. effacer_index();
  1116. int R = determine_d0();
  1117. if (R==0) { tracer_courbe(); }
  1118.  
  1119. QDateTime dti;
  1120. QDate di;
  1121. QTime ti;
  1122. di.setDate(annee_affi, mois_affi, 1);
  1123. ti.setHMS(0,0,0);
  1124.  
  1125. dti.setDate(di);
  1126. dti.setTime(ti);
  1127. affiche_index(dti);
  1128. }
  1129.  
  1130.  
  1131. void MainWindow::on_Bt_1an_clicked()
  1132. {
  1133. tous_bt_gris();
  1134. Bt_1an->setStyleSheet("background-color: #73FF7A;");
  1135. // 365.25 * 48 = 17532 enr
  1136. //nb_enr_affi = 17532;
  1137. type_i='a';
  1138. effacer_index();
  1139. int R = determine_d0();
  1140. if (R==0) { tracer_courbe(); }
  1141.  
  1142. QDateTime dti;
  1143. QDate di;
  1144. QTime ti;
  1145. di.setDate(annee_affi, mois_affi, 1);
  1146. ti.setHMS(0,0,0);
  1147.  
  1148. dti.setDate(di);
  1149. dti.setTime(ti);
  1150. affiche_index(dti);
  1151. }
  1152.  
  1153.  
  1154. void MainWindow::on_Bt_2ans_clicked()
  1155. {
  1156. tous_bt_gris();
  1157. Bt_2ans->setStyleSheet("background-color: #73FF7A;");
  1158. // 730.5 * 48 = 35064 enr
  1159. // nb_enr_affi = 35064;
  1160. type_i='2';
  1161.  
  1162. int R = determine_d0();
  1163. if (R==0) { tracer_courbe(); }
  1164.  
  1165. QDateTime dti;
  1166. QDate di;
  1167. QTime ti;
  1168. di.setDate(annee_affi, mois_affi, 1);
  1169. ti.setHMS(0,0,0);
  1170.  
  1171. dti.setDate(di);
  1172. dti.setTime(ti);
  1173. affiche_index(dti);
  1174. }
  1175.  
  1176. void MainWindow::on_Bt_effacer_clicked()
  1177. {
  1178. effacer_tout();
  1179. }
  1180.  
  1181.  
  1182.  
  1183. void MainWindow::on_spinBox_jour_valueChanged(int arg1)
  1184. {
  1185. jour1=arg1;
  1186. effacer_index();
  1187. int R = determine_d0();
  1188. if (R==0) { tracer_courbe(); }
  1189.  
  1190. QDateTime dti;
  1191. QDate di;
  1192. QTime ti;
  1193. di.setDate(annee_affi, mois_affi, jour1);
  1194. ti.setHMS(0,0,0);
  1195.  
  1196. dti.setDate(di);
  1197. dti.setTime(ti);
  1198. affiche_index(dti);
  1199. }
  1200.  
  1201.  
  1202. void MainWindow::on_spinBox_1_valueChanged(int arg1)
  1203. {
  1204. //effacer_index();
  1205. annee_affi = arg1;
  1206. QString s1;
  1207. s1.setNum(annee_affi);
  1208.  
  1209. }
  1210.  
  1211.  
  1212. void MainWindow::on_Bt_mois_plus_clicked()
  1213. {
  1214. effacer_index();
  1215. mois_affi++;
  1216. if (mois_affi>12)
  1217. {
  1218. mois_affi =1;
  1219. annee_affi ++;
  1220. }
  1221.  
  1222. QString s1;
  1223. s1.setNum(annee_affi);
  1224. //lineEdit_5->setText(s1);
  1225. spinBox_1->setValue(annee_affi);
  1226.  
  1227. effacer_tout();
  1228. int R = determine_d0();
  1229. affiche_grille();
  1230.  
  1231. if (R==0) { tracer_courbe(); } else { QString s1 ="Pas de données depuis cette date"; etiquette1(500, -200, s1); }
  1232.  
  1233. QString s2;
  1234. s2=liste_mois[mois_affi-1];
  1235. s2 += " ";
  1236. s2+=s1;
  1237. lineEdit_4->setText(s2);
  1238. if (type_i == 'm') { etiquette1(100, -370, s2);}
  1239. QDateTime dti;
  1240. QDate di;
  1241. QTime ti;
  1242. di.setDate(annee_affi, mois_affi, 1);
  1243. ti.setHMS(0,0,0);
  1244.  
  1245. dti.setDate(di);
  1246. dti.setTime(ti);
  1247. affiche_index(dti);
  1248. }
  1249.  
  1250.  
  1251. void MainWindow::on_Bt_mois_moins_clicked()
  1252. {
  1253. effacer_index();
  1254. mois_affi--;
  1255. if (mois_affi<1)
  1256. {
  1257. mois_affi =12;
  1258. annee_affi --;
  1259. }
  1260. QString s1=liste_mois[mois_affi-1];
  1261. lineEdit_4->setText(s1);
  1262. s1.setNum(annee_affi);
  1263. //lineEdit_5->setText(s1);
  1264. spinBox_1->setValue(annee_affi);
  1265.  
  1266. effacer_tout();
  1267. int R = determine_d0();
  1268. affiche_grille();
  1269. if (R==0) { tracer_courbe(); } else { QString s1 ="Pas de données depuis cette date"; etiquette1(500, -200, s1); }
  1270.  
  1271. QString s2;
  1272. s2=liste_mois[mois_affi-1];
  1273. s2 += " ";
  1274. s2+=s1;
  1275. lineEdit_4->setText(s2);
  1276. if (type_i == 'm') { etiquette1(100, -370, s2);}
  1277.  
  1278. QDateTime dti;
  1279. QDate di;
  1280. QTime ti;
  1281. di.setDate(annee_affi, mois_affi, 1);
  1282. ti.setHMS(0,0,0);
  1283.  
  1284. dti.setDate(di);
  1285. dti.setTime(ti);
  1286. affiche_index(dti);
  1287. }
  1288.  
  1289.  
  1290.  
  1291.  
  1292. void MainWindow::on_pushButton_clicked()
  1293. {
  1294. effacer_trace();
  1295. int R = determine_d0();
  1296. on_Bt_1mois_clicked();
  1297. //affiche_grille();
  1298. tracer_min_max(0);
  1299. }
  1300.  
  1301. void MainWindow::on_pushButton_3_clicked()
  1302. {
  1303. affiche_grille();
  1304. int R = determine_d0();
  1305. tracer_min_max(1);
  1306. }
  1307.  

33 Documents

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

34 -

Liens...

12070