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.

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++ : Le fichier Station_meteo.cpp

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


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 589 (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;