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)

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. #include "Arduino.h"
  20. #include <stdint.h>
  21. #include "Station_meteo.h"
  22.  
  23.  
  24. /** STATION METEO **/
  25.  
  26. String version="7.2";
  27.  
  28.  
  29. uint32_t bmp_offset = 0;
  30. uint16_t s_width = TFT480.Get_Display_Width();
  31. uint16_t s_heigh = TFT480.Get_Display_Height();
  32.  
  33. uint8_t couleur[3];
  34.  
  35. uint8_t rouge[3]={255, 0, 0};
  36. uint8_t orange[3]={245, 121, 0};
  37. uint8_t jaune[3]={255, 255, 0};
  38. uint8_t jaune_sombre[3]={220, 220, 0};
  39. uint8_t jaune_tres_sombre[3]={100, 100, 0};
  40. uint8_t vert[3]={0, 255, 0};
  41. uint8_t cyan[3]={0, 255, 255};
  42. uint8_t cyan_sombre[3]={0, 200, 200};
  43. uint8_t bleu_pale[3]={160, 240, 255};
  44. uint8_t bleu_clair[3]={0, 100, 255};
  45. uint8_t bleu[3]={0, 0, 255};
  46. uint8_t violet[3]={200, 0, 255};
  47. uint8_t noir[3]={0, 0, 0};
  48. uint8_t gris[3]={150, 150, 150};
  49. uint8_t gris_fonce[3]={50, 50, 50};
  50. uint8_t blanc[3]={255, 255, 255};
  51.  
  52.  
  53. Etiquette Etiq_1;
  54. Etiquette Etiq_2;
  55. //Etiquette Etiq_3;
  56. Etiquette Etiq_4;
  57. Etiquette Etiq_5;
  58. //Etiquette Etiq_6;
  59.  
  60. LED Led1;
  61. LED Led2;
  62. //LED Led3;
  63.  
  64. Scope Scope_1;
  65.  
  66.  
  67. uint16_t aff_y;
  68.  
  69. /**--------------------------------------------------------
  70. déclarations RECEPTION & DECODAGE SONDE EXTERNE
  71. --------------------------------------------------------**/
  72.  
  73. enregistrement enr[1000];
  74. uint16_t n_enr=0;
  75. uint16_t n_enr_max;
  76.  
  77.  
  78. const int INPUT_PIN = 22;
  79. byte etat = 0;
  80. byte memo_etat;
  81. uint32_t memo_micros1 = 0;
  82. uint32_t memo_micros2 = 0;
  83. uint16_t temps_ecoule;
  84. uint16_t memo_temps_ecoule;
  85. uint32_t pulseWidth;
  86. uint16_t i;
  87. uint16_t TT1;
  88. uint16_t TT2;
  89. uint16_t TT3;
  90. uint16_t TT4;
  91. uint16_t nb_acqui433=0;
  92. uint16_t nb_wr_SD=0;
  93.  
  94. //uint8_t premiere_passe;
  95. //uint8_t stable;
  96. float echelle=1; // pour l'affichage des températures sur le Scope
  97. int16_t offset=100;
  98. int16_t grad_min, grad_max;
  99.  
  100. uint8_t num_acquisition;
  101. int16_t T_EXT_lue[5]={200,200,200,200,200}; // temperature extérieure (la sonde envoie des salves de 5 messages identiques)
  102. uint8_t confiance[5]; //indice de confiance = nb d'occurences identiques pour chaque valeur reçue
  103. uint16_t T_EXT_retenue; // après comparaison des 5 valeurs reçues
  104. int16_t Tmin, Tmax;
  105.  
  106. uint16_t T_INTE_lue; // temperature intérieure
  107.  
  108. uint16_t Pression_lue;
  109.  
  110. uint8_t H_in_lue; // humidite
  111. uint8_t memo_H_in;
  112.  
  113. uint8_t H_out_lue; // humidite
  114. uint8_t memo_H_out;
  115.  
  116. String dataString = "";
  117. String enrgString = "";
  118.  
  119. tmElements_t tm;
  120.  
  121.  
  122. const int MAX_BUFFER = 1024;
  123. String message;
  124.  
  125. char buffer[MAX_BUFFER];
  126. void changed() { }
  127.  
  128.  
  129. /** -------------------------------------------------------
  130.   SD card
  131. -----------------------------------------------------------
  132. ADAPT SD -> mega2560 pins
  133.  
  134. CS -> 53
  135. MISO -> 50
  136. MOSI -> 51
  137. SCK -> 52
  138. **/
  139.  
  140. #include <SD.h>
  141.  
  142. void test_SD_2()
  143. {
  144. Serial.print("Init SDcard...");
  145.  
  146. pinMode(53, OUTPUT);
  147. if (!SD.begin(53)) //ici l'appel (et test) de la fonction 'begin' effectue l'initialisation (si possible)
  148. {
  149. Serial.println("init failed! (pas de SDcard ?)");
  150. return;
  151. }
  152. Serial.println("init Ok.");
  153. if (SD.exists("data.txt")) {Serial.println("data.txt existe.");} else { Serial.println("pas de fichier data.txt");}
  154. }
  155.  
  156.  
  157. /** -------------------------------------------------------
  158.   Sensor local BMP280 (Pression & température)
  159. --------------------------------------------------------**/
  160. #include <SPI.h>
  161. #include <Adafruit_Sensor.h>
  162. #include <Adafruit_BMP280.h>
  163.  
  164. Adafruit_BMP280 bmp; // use I2C interface
  165. Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor();
  166. Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor();
  167.  
  168. void init_BMP280()
  169. {
  170. // Serial.begin(9600);
  171. Serial.println(F("BMP280 Sensor event test"));
  172.  
  173. if (!bmp.begin())
  174. {
  175. Serial.println(F("Could not find BMP280 sensor !"));
  176. while (1) delay(10);
  177. }
  178. else {Serial.println(F("BMP280 sensor OK!")); }
  179.  
  180. /* Default settings from datasheet. */
  181. bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
  182. Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
  183. Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
  184. Adafruit_BMP280::FILTER_X16, /* Filtering. */
  185. Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
  186.  
  187. bmp_temp->printSensorDetails();
  188. }
  189.  
  190.  
  191. void acqui_PressionTemperature()
  192. {
  193. /**
  194. pression athmosphérique normale au niveau de la mer = 1013 hPa
  195. 0m ->1013 hPa
  196. 1000m-> 899 hPa
  197. -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)
  198. -soit 11.4 hPa / 100 m
  199. vu que mon capteur est situé à 100 m d'altitude, je vais rajouter 11.4 hPa à la mesure
  200. afin d'obtenir des valeurs cohérentes par rapport aux information météorologiques
  201. */
  202.  
  203. sensors_event_t temp_event, pressure_event;
  204. bmp_temp->getEvent(&temp_event);
  205. bmp_pressure->getEvent(&pressure_event);
  206.  
  207. Serial.println();
  208.  
  209. Serial.print(F("Temperature IN = "));
  210. T_INTE_lue =(int16_t)(10*temp_event.temperature);
  211. Serial.print(T_INTE_lue);
  212. Serial.println(" *C");
  213.  
  214. Serial.print(F("Pression = "));
  215. float pression_acq = pressure_event.pressure;
  216. pression_acq += 11.4;
  217. Pression_lue = (int16_t)pression_acq;
  218. Serial.print(Pression_lue);
  219. Serial.println(" hPa");
  220.  
  221. Serial.println();
  222. }
  223.  
  224. /**--------------------------------------------------------
  225. fonctions AFFICHAGE TFT
  226. --------------------------------------------------------**/
  227.  
  228.  
  229. void clear_screen()
  230. {
  231. TFT480.Fill_Screen(40,40,40);
  232. }
  233.  
  234.  
  235. void dessine_triangles(void)
  236. {
  237. int i = 0;
  238. uint16_t L, H;
  239. L=TFT480.Get_Display_Width();
  240. H=TFT480.Get_Display_Height();
  241.  
  242. for(i=0; i<H/2; i+=5)
  243. {
  244. TFT480.Set_Draw_color(0,i+64,i+64);
  245. 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);
  246. }
  247. }
  248.  
  249.  
  250. void determine_Tmin_Tmax()
  251. {
  252. Tmin=500;
  253. Tmax=0;
  254. for (i=1; i< n_enr_max ; i++)
  255. {
  256. if ((enr[i].T !=0) && (enr[i].T < Tmin)) {Tmin= enr[i].T;}
  257. if (enr[i].T > Tmax) {Tmax= enr[i].T;}
  258. }
  259.  
  260. TFT480.Set_Text_colour(jaune[0],jaune[1],jaune[2]);
  261. TFT480.Set_Text_Size(1);
  262. TFT480.Print_Number_Int(Tmin,400,5,3,' ',10);
  263. TFT480.Print_Number_Int(Tmax,400,15,3,' ',10);
  264. }
  265.  
  266.  
  267. void calcul_Gradu_ech()
  268. {
  269. //graduations de 5°C en 5°C
  270.  
  271. //calcul de la grd min & grad max
  272. grad_min = (Tmin/50)*50; // division entière ce qui donne une valeur arrondie
  273. grad_min -=50;
  274. grad_max = (Tmax/50)*50;
  275. grad_max+=50;
  276.  
  277. offset=-grad_min+20;
  278.  
  279. echelle =(float) (grad_max - grad_min)/(Scope_1.dy-20.0);
  280. if (echelle==0) {echelle=3;} // cas où la température est totalement constante - évite des /0 plus loin
  281.  
  282. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  283. TFT480.Set_Text_Size(1);
  284. TFT480.Print_Number_Int(grad_min,400,30,3,' ',10);
  285. TFT480.Print_Number_Int(grad_max,400,40,3,' ',10);
  286.  
  287. TFT480.Set_Text_colour(rouge[0],rouge[1],rouge[2]);
  288. TFT480.Print_Number_Float(echelle, 1, 400, 50, '.', 3, ' ');
  289.  
  290. }
  291.  
  292.  
  293. void affiche_courbeT() // sur le scope
  294. {
  295. // Ymax du scope = 142
  296. //142 * 3 = 426 -> 42.6°C max
  297.  
  298.  
  299. uint16_t i, k;
  300. float Ti1, Ti2; // =316 pour 31.6°C
  301.  
  302. Scope_1.setCouleurTrace(vert);
  303.  
  304. for (i=1; i< n_enr_max ; i++)
  305. {
  306. Ti1 = (offset + enr[i-1].T )/echelle;
  307. if (Ti1>=Scope_1.dy){Ti1=Scope_1.dy-1;}
  308.  
  309. Ti2 = (offset + enr[i].T ) /echelle;
  310. if (Ti2>=Scope_1.dy){Ti2=Scope_1.dy-1;}
  311.  
  312. if ((Ti1 != 0) && (Ti2 != 0))
  313. {
  314. k=1*i;
  315. Scope_1.Tiret(k-1, Ti1, k, Ti2);
  316. }
  317. }
  318. }
  319.  
  320.  
  321.  
  322. void affiche_courbeH() // sur le scope
  323. {
  324. uint16_t i, k;
  325. uint16_t Hi1, Hi2;
  326.  
  327. Scope_1.setCouleurTrace(bleu);
  328. for (i=1; i<n_enr_max; i++)
  329. {
  330. Hi1 = enr[i-1].H;
  331. Hi2 = enr[i].H;
  332. if ((Hi1 != 0) && (Hi2 != 0))
  333. {
  334. k=1*i;
  335. Scope_1.Tiret(k-1, Hi1, k, Hi2);
  336. }
  337. }
  338. }
  339.  
  340.  
  341. void affiche_ligne_ref()
  342. {
  343. uint8_t R=300/echelle; // 300 -> 30°C
  344. Scope_1.setCouleurTrace(rouge);
  345. Scope_1.Tiret(0, R, Scope_1.dx, R);
  346. }
  347.  
  348.  
  349.  
  350. void affiche_SCOPE() // vide
  351. {
  352. Scope_1.init(18,145, 450,172); // 1px toutes les 5mn; 12px = 1h
  353. Scope_1.setCouleurCadre(blanc);
  354. Scope_1.traceCadre();
  355. // Scope_1.traceReticule(); // et efface_surface au préalable
  356. // affiche_ligne_ref();
  357. }
  358.  
  359.  
  360.  
  361. void affiche_pannel()
  362. {
  363.  
  364. // dessine_triangles();
  365.  
  366. TFT480.Set_Text_Back_colour(0,0,0);
  367. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  368. TFT480.Set_Text_Size(1);
  369. TFT480.Print_String("STATION METEO v:" + version, 250, 0);
  370.  
  371. uint16_t hauteur = 0;
  372.  
  373. // hauteur += 5;
  374.  
  375. //TFT480.Set_Text_colour(orange[0],orange[1],orange[2]);
  376. //TFT480.Set_Text_Back_colour(0,0,0);
  377. //TFT480.Set_Text_Size(2);
  378. //TFT480.Print_String("TEMPERATURES", 120, hauteur);
  379.  
  380. hauteur += 6;
  381.  
  382. Etiq_1.init(5, hauteur, 180, 60, "IN");
  383. Etiq_1.setCouleurTxt(jaune);
  384. Etiq_1.setCouleurCadre(bleu_clair);
  385. //RAPPEL : void affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3]);
  386. Etiq_1.affiche_float(0, 4, 1, "deg");
  387.  
  388.  
  389. Etiq_2.init(200, hauteur, 180, 60, "OUT");
  390. Etiq_2.setCouleurTxt(vert);
  391. Etiq_2.setCouleurCadre(bleu_clair);
  392. Etiq_2.affiche_float(0, 4, 1, "deg");
  393.  
  394. hauteur += 65;
  395.  
  396. TFT480.Set_Text_colour(bleu_pale[0],bleu_pale[1],bleu_pale[2]);
  397. TFT480.Set_Text_Back_colour(0,0,0);
  398. TFT480.Set_Text_Size(1);
  399. TFT480.Print_String("PRESSION", 60, hauteur);
  400.  
  401. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  402. TFT480.Set_Text_Back_colour(0,0,0);
  403. TFT480.Set_Text_Size(1);
  404. TFT480.Print_String("HUMIDITE (%)", 200, hauteur);
  405.  
  406. hauteur += 6;
  407.  
  408. Etiq_5.init(5, hauteur, 180, 60, "");
  409. Etiq_5.setCouleurTxt(gris);
  410. Etiq_5.setCouleurCadre(bleu_clair);
  411. //rappel affiche_valeur(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3])
  412. Etiq_5.affiche_valeur(0, 4, "hPa");
  413.  
  414.  
  415. Etiq_4.init(200, hauteur, 80, 60, "");
  416. Etiq_4.setCouleurTxt(bleu);
  417. Etiq_4.setCouleurCadre(bleu_clair);
  418. Etiq_4.affiche_valeur(0, 2, "");
  419.  
  420. TFT480.Set_Text_Size(1);
  421. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  422.  
  423. TFT480.Print_Number_Int(nb_acqui433,440,84,6,' ',10);
  424. TFT480.Print_Number_Int(nb_wr_SD,440,70,6,' ',10);
  425.  
  426. }
  427.  
  428.  
  429. void affiche_LEDS()
  430. {
  431. TFT480.Set_Text_Size(1);
  432. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  433.  
  434. TFT480.Print_String("WR SDcard", 300, 70);
  435. Led1.init(390,68, 10,10);
  436. Led1.setCouleur(noir);
  437.  
  438. TFT480.Print_String("RX 433 MHz", 300, 84);
  439. Led2.init(390,82, 10,10);
  440. Led2.setCouleur(noir);
  441.  
  442.  
  443. // Led3.init(390,20, 10,10);
  444. // Led3.setCouleur(orange);
  445. }
  446.  
  447.  
  448.  
  449.  
  450. /**--------------------------------------------------------
  451. fonctions RECEPTION & DECODAGE SONDE EXTERNE 433MHz
  452. --------------------------------------------------------**/
  453.  
  454.  
  455. // template températures (heure par heure, d'après Meteociel.fr) pour tests affichage
  456.  
  457. int8_t liste_T[44] = {12,12,12,12,12,11,13,15,17,19,20,22,28,35,33,25,22,19,17,16,15,14,14,13,12,10,7,3,-1,-5,-3,6,15,19,20,21,22,22,22,20,19,17,17,18};
  458. //uint8_t liste_T[44] = {12,12,12,12,12,11,13,15,17,19,20,21,21,22,22,22,21,19,17,16,15,14,14,13,12,12,12,11,10,10,12,15,18,19,20,21,22,22,22,20,19,17,17,18};
  459.  
  460. void charge_enregistrements()
  461. {
  462. // pour test affichage avec des données réalistes sans devoir attendre des heures !
  463. uint8_t i1,i2;
  464. int16_t v1, v2, v3;
  465. for (i1=0; i1<44; i1++)
  466. {
  467. v1=10*liste_T[i1];
  468. v2=10*liste_T[i1-1];
  469. for (i2=0; i2<12; i2++)
  470. {
  471. decale_enregistrements();
  472. v3 = (v1*i2+v2*(12-i2))/12; //lissage pour eviter les escaliers vu que les données sont h par h et l'affichage 5mn par 5mn
  473. enr[n_enr_max].T= v3;
  474. }
  475. T_EXT_retenue = v3;
  476. }
  477. trace_sur_Scope();
  478. }
  479.  
  480.  
  481.  
  482.  
  483. void efface_buffer()
  484. {
  485. for(int i=0; i<100; i++)
  486. {
  487. buffer[i]=' ';
  488. }
  489.  
  490. }
  491.  
  492.  
  493. void decale_enregistrements()
  494. {
  495. uint16_t i;
  496. for (i=0; i<=n_enr_max; i++)
  497. {
  498. enr[i].T=enr[i+1].T;
  499. enr[i].H=enr[i+1].H;
  500. }
  501. enr[n_enr_max].T=0;
  502. enr[n_enr_max].H=0;
  503.  
  504. }
  505.  
  506.  
  507.  
  508. uint16_t strbinToInt(String str1, uint8_t nb_bits)
  509. {
  510.  
  511. uint16_t result=0;
  512. for (uint8_t i=0; i<nb_bits; i++)
  513. {
  514. if (str1[(nb_bits-1)-i]=='1') {result+= 1<<i;}
  515.  
  516. }
  517. return result;
  518. }
  519.  
  520.  
  521.  
  522.  
  523. void calcul_confiance()
  524. {
  525. // les 5 valeurs reçues à l'issue d'une salve ne sont pas toujours toutes identiques, dû à des aléas de transmission
  526. // ici on affecte à chaque valeur le nombre d'occurences (qui va de 1 si valeur "exotique" à 5 si toutes identiques)
  527. // ces nombres d'occurences sont inscrites dans un tableau semblable à celui des valeurs lues (mêmes indices).
  528. uint8_t i,j;
  529. uint16_t Ti;
  530.  
  531.  
  532. for(i=0; i<5; i++) {confiance[i]=0;} //RAZ
  533.  
  534. for(i=0; i<5; i++)
  535. {
  536. Ti=T_EXT_lue[i];
  537. for(j=0; j<5; j++)
  538. {
  539. if (T_EXT_lue[j] == Ti) {confiance[i]++;}
  540. }
  541. }
  542. }
  543.  
  544.  
  545.  
  546. uint16_t meilleure_valeur()
  547. {
  548. // trouver la valeur ayant obtenu le meilleur score de présence dans la salve (= le plus grand nombre d'occurences)
  549. // à l'issue de la fonction "calcul_confiance()"
  550. uint16_t meilleure;
  551.  
  552. meilleure = T_EXT_lue[0];
  553. for(i=0; i<4; i++)
  554. {
  555. if (confiance[i+1] > confiance[i]) {meilleure = T_EXT_lue[i+1];}
  556. }
  557. return (meilleure);
  558. }
  559.  
  560.  
  561.  
  562. void lit_temperature() // de la sonde ext 433MHz
  563. // source = message
  564. //(message a été constitué par la fonction "decodeBuffer()")
  565. // destination -> tableau "T_EXT_lue[5]" des 5 valeurs lues (correspondant à une salve)
  566. {
  567.  
  568. uint8_t p; // position de la séquence de référence fixe
  569. uint8_t decal;
  570. uint8_t nbBits;
  571. uint16_t valeur_lue;
  572. String s1;
  573.  
  574. p = message.indexOf("100000",6); // séquence apparemment fixe > à la 6eme position... (
  575. //le début du message change à chaque changement des piles !)
  576.  
  577. nbBits=10; // nb de bits à lire
  578. decal=6; // décalage depuis la position p (ici les 6 bits de la séquence fixe)
  579.  
  580.  
  581. if (p>0)
  582. {
  583. s1=message.substring(p+decal, p+decal+nbBits); // TFT480.Print_String(s1, 100, 100); // en binaire
  584.  
  585. valeur_lue=strbinToInt(s1, nbBits);// =316 pour 31.6°C //TFT480.Print_Number_Int(T1,220,100,3,' ',10);
  586.  
  587.  
  588. Serial.print("Temperature EXT= ");
  589. float temp_f = valeur_lue /10.0;
  590. Serial.println(temp_f);
  591.  
  592. // todo // expérimenter avec des températures négatives (sonde au frigo !!)
  593.  
  594. if ((valeur_lue != 0) && (valeur_lue < 500) ) // 50°C max
  595. {
  596. T_EXT_lue[num_acquisition] = valeur_lue; // on renregistre la nouvelle valeur (parmi 5 qui constituent une salve)
  597. }
  598. }
  599. }
  600.  
  601.  
  602.  
  603. void lit_humidite()
  604. {
  605. // de la sonde ext 433MHz
  606. uint8_t p; // position de la séquence de référence fixe
  607. uint8_t decal;
  608. uint8_t nbBits;
  609. uint16_t valeur_precedente, valeur_lue;
  610. float Hf;
  611. String s1;
  612.  
  613. p = message.indexOf("100000",6); // séquence apparemment fixe
  614. nbBits=7; // nb de bits à lire
  615. decal=6+10+5; // décalage depuis la position p
  616.  
  617. if (p>0)
  618. {
  619. s1=message.substring(p+decal, p+decal+nbBits);
  620. memo_H_out = H_out_lue;
  621. valeur_lue = strbinToInt(s1,nbBits); //TFT480.Print_Number_Int(T1,220,100,3,' ',10);
  622. valeur_precedente = memo_H_out;
  623.  
  624. if ((valeur_lue != 0) && (valeur_lue < 100) ) // 100% max !
  625. {
  626. if ( abs(valeur_lue - valeur_precedente) < 10 ) // delta de 10% max
  627. {
  628. H_out_lue = valeur_lue; // on renregistre la nouvelle valeur
  629. }
  630. else // au delà -> anti-glich
  631. {
  632. if ( valeur_lue > valeur_precedente) {H_out_lue = valeur_precedente+5;}
  633. if ( valeur_lue < valeur_precedente) {H_out_lue = valeur_precedente-5;}
  634. }
  635. }
  636. }
  637.  
  638. }
  639.  
  640.  
  641. void decodeBuffer()
  642. // transcode les 'A' et 'B' et 'C' qui représentent des durées en '0' et '1' -> destination = message
  643. // pour la suite du traitement voir fonction "lit_temperature()"
  644. {
  645.  
  646. Serial.println("reception signal 433MHz sonde externe :");
  647. int k;
  648. char car1;
  649. char car2;
  650. k=0;
  651. while (k<=73)
  652. {
  653. car1=buffer[k];
  654. car2=buffer[k+1];
  655. if ((car1=='A') &&(car2=='B'))
  656. {
  657. message += "0";
  658. //TFT480.Print_String("0", 4*k, 10*aff_y);
  659. Serial.print(0);
  660. }
  661. else if ((car1=='A') &&(car2=='C'))
  662. {
  663. message += "1";
  664. //TFT480.Print_String("1", 4*k, 10*aff_y);
  665. Serial.print(1);
  666. }
  667. k++;
  668. }
  669. Serial.println(' ');
  670. efface_buffer();
  671. aff_y++;
  672. if (aff_y>50) {aff_y = 1;}
  673.  
  674. nb_acqui433++;
  675.  
  676. TFT480.Set_Text_Size(1);
  677. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  678. TFT480.Print_Number_Int(nb_acqui433,440,84,6,' ',10);
  679.  
  680.  
  681. Serial.println("-----------------------------------");
  682. }
  683.  
  684. /**
  685.  
  686.  
  687. RESULTS
  688.  
  689. 2020-09-07 PM
  690. ABABACACACACABACACABABABABABABABACACACACACACACACACACACACABABACABACABABACA-␍␊
  691. 001111011000000011111111111100101001-␍␊
  692.  
  693. 2020-09-08 AM
  694. ABACABACABABABACACABABABABABABABACACACABACACABACACACACACABABACABACACACABA-␍␊
  695. 010100011000000011101101111100101110-␍␊
  696.  
  697. ABACABABABACACABABABABABABABACACACACACACACABACACACACABABACACABABACACA-␍␊
  698. 0100011000000011111110111100110011-␍␊
  699.  
  700. ABACABABABABABABACABABABABABABABACACACACACACABABACACACACABABACABACACABABA-␍␊
  701. 010000001000000011111100111100101100-␍␊
  702.  
  703. ABACABABABACABACACABABABABABABABACACACACABACACACACACACACABABACABACACABACA-␍␊
  704. 010001011000000011110111111100101101-␍␊
  705.  
  706. ABABABACABACACABABABABABABABACACACACABACACABACACACACABABACABACACABACA-␍␊
  707. 010001011000000011110110111100101101-␍␊
  708.  
  709. ABACABABABACABACACABABABABABABABACACACACABACABABACACACACABABACABACACABACA-␍␊
  710. 010001011000000011110100111100101101-␍␊
  711.  
  712.  
  713. 00111000 100000 0100000111 111100 110011-␍␊
  714. 01000000 100000 0011111111 111100 110010
  715. 01000001 100000 0011111110 111100 110001
  716. 01000001 100000 0100000000 111100 101111-␍␊
  717. 01000001 100000 0100000000 111100 10111-␍␊
  718.  
  719. AVEC temperature variable (R ajustable en // sur la thermistance) :
  720. 00111101 100000 0011111111 111100 101001-␍␊
  721.  
  722. 01001000 100000 0011111011 11110 0101010-␍␊ 251 (sans la R ajustable en //) -> 25,1 °C ?
  723. 01001000 100000 0100011001 11110 0100110-␍␊ 281 (sans la R mais chauffée avec le doigt) -> 28,1 °C ?
  724. 01100110 100000 0110101100 11110 0011111-␍␊ 428
  725. 10101110 100000 0111001100 11110 0100010-␍␊ 460
  726. 10100101 100000 0111011100 11110 0100001-␍␊ 476
  727. 10011100 100000 0111101110 11110 0100000-␍␊ 494
  728. 10001101 100000 1000001110 11110 0011101-␍␊ 526
  729. 10000100 100000 1000100001 11110 0011100-␍␊ 545
  730. 01110100 100000 1001001011 11110 0011011-␍␊ 587
  731. 01100101 100000 1001110111 11110 0011001-␍␊ 631
  732. 01011000 100000 1010100010 11110 0011011-␍␊ 674
  733. 01000110 100000 1011000010 11110 0011011-␍␊ 706
  734. 00011111 100000 1011011010 11110 0011010-␍␊ 730
  735.  
  736. 10000001 100000 0010000111 11110 0111101
  737.  
  738. ANALYSE "colonne" 3 (= température):
  739.  
  740. 0110101100 = 428
  741. 0111001100 = 460
  742. 0111011100 = 476
  743. 0111101110 = 494
  744. 1000001110 = 526
  745. 1000100001 = 545
  746. 1001001011 = 587
  747. 1001110111 = 631
  748. 1010100010 = 674
  749. 1011000010 = 706
  750. 1011011010 = 730
  751.  
  752. Humidité (colonne 5):
  753.  
  754.  
  755.  
  756. **/
  757.  
  758.  
  759.  
  760.  
  761. /**--------------------------------------------------------
  762. partie COMMUNE
  763. --------------------------------------------------------**/
  764.  
  765. uint8_t decToBcd( int val )
  766. {
  767. return (uint8_t) ((val / 10 * 16) + (val % 10));
  768. }
  769.  
  770.  
  771. void setDateTime()
  772. {
  773. // mise à l'heure exacte
  774.  
  775. /*
  776. #define DS1307_ADDRESS 0x68
  777. byte zero = 0x00;
  778.  
  779.  
  780. byte second = 20; //0-59
  781. byte minute = 33; //0-59
  782. byte hour = 19; //0-23
  783. byte weekDay = 4; //1-7
  784. byte monthDay = 16; //1-31
  785. byte month = 9; //1-12
  786. byte year = 20; //0-99
  787.  
  788. Wire.beginTransmission(DS1307_ADDRESS);
  789. Wire.write(zero);
  790.  
  791. Wire.write(decToBcd(second));
  792. Wire.write(decToBcd(minute));
  793. Wire.write(decToBcd(hour));
  794. Wire.write(decToBcd(weekDay));
  795. Wire.write(decToBcd(monthDay));
  796. Wire.write(decToBcd(month));
  797. Wire.write(decToBcd(year));
  798.  
  799. Wire.write(zero); //start
  800.  
  801. Wire.endTransmission();
  802. */
  803. }
  804.  
  805.  
  806.  
  807.  
  808. void setup()
  809. {
  810. // premiere_passe=1;
  811. Serial.begin(115200);
  812. pinMode(INPUT_PIN, INPUT);
  813. attachInterrupt((INPUT_PIN), changed, CHANGE);
  814. Serial.println("Bonjour");
  815. i=0;
  816. efface_buffer();
  817.  
  818.  
  819. TFT480.Init_LCD();
  820. clear_screen();
  821. delay(100);
  822.  
  823. TFT480.Set_Rotation(1);
  824.  
  825. affiche_pannel();
  826. affiche_LEDS();
  827. affiche_SCOPE();
  828.  
  829. n_enr_max = Scope_1.dx-2;
  830.  
  831. TFT480.Set_Text_Back_colour(0,0,0);
  832. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  833. TFT480.Set_Text_Size(1);
  834. // TFT480.Print_String("Bonjour", 0, 0);
  835.  
  836. aff_y=0;
  837. T_INTE_lue=200; // initialisée à 20°C
  838. Pression_lue=1013; //hPa
  839. H_in_lue = 50; // 50%
  840. H_out_lue = 50; // 50%
  841. affiche_meteo();
  842. affiche_meteo();
  843. num_acquisition=0;
  844.  
  845. init_BMP280();
  846.  
  847. // setDateTime(); // à décommenter (une fois, à recommenter ensuite) pour mise à l'heure exacte
  848.  
  849. // test_SDcard();
  850. test_SD_2();
  851. TT1=0;
  852. TT2=50;
  853. TT3=0;
  854. TT4=0;
  855.  
  856. // charge_enregistrements(); // pour aficher rapidement (sans attendre les acquisitions) une courbe de T°C, lors de la mise au point
  857. //(à commenter ^)
  858.  
  859. // write_scope_on_SDcard(); // à commenter
  860.  
  861. read_scope_on_SDcard();
  862.  
  863. trace_sur_Scope();
  864.  
  865. // TFT480.Set_Text_Back_colour(0,0,0);
  866. // TFT480.Set_Text_colour(orange[0],orange[1],orange[2]);
  867. // TFT480.Set_Text_Size(1);
  868. // TFT480.Print_String("en attente de donnees reçues", 150, 260);
  869. }
  870.  
  871.  
  872.  
  873.  
  874. void trace_sur_Scope() //memorise en ram et affiche les courbes
  875. {
  876. enr[n_enr_max].T= T_EXT_retenue; // format 325 -> 32.5°C
  877. enr[n_enr_max].H= H_out_lue;
  878.  
  879. decale_enregistrements();
  880.  
  881. determine_Tmin_Tmax();
  882. calcul_Gradu_ech();
  883.  
  884. Scope_1.traceGraduation(); // ce qui efface tout au préalable
  885. // affiche_ligne_ref();
  886.  
  887. affiche_courbeT();
  888. affiche_courbeH();
  889. }
  890.  
  891.  
  892. void affiche_meteo() // sur l'écran TFT
  893. {
  894. float T_in_f = T_INTE_lue / 10.0; // float
  895. Etiq_1.affiche_float(T_in_f, 4, 1, "deg");
  896.  
  897. // Affichage de 5 valeurs reçues, constituant une salve, et de leur indice de confiance
  898.  
  899. TFT480.Set_Draw_color(0,0,0);
  900. TFT480.Fill_Rectangle(434, 4, 476, 54); // efface tout pour crééer une "animation" de l'affichage même si valeurs inchangées
  901.  
  902. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  903.  
  904. TFT480.Set_Draw_color(gris[0],gris[1],gris[2]);
  905. TFT480.Draw_Fast_VLine(430, 4, 55);
  906.  
  907. TFT480.Set_Text_Size(1);
  908.  
  909. uint16_t x0=434;
  910. uint16_t y0=0;
  911. 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);
  912. 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);
  913. 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);
  914. 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);
  915. 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);
  916.  
  917. // Affichage en vert de la valeur retenue
  918. TFT480.Set_Text_colour(gris[0],gris[1],gris[2]);
  919. TFT480.Print_String("ok->", 410,57);
  920. TFT480.Set_Text_colour(vert[0],vert[1],vert[2]);
  921. y0+=12; TFT480.Print_Number_Int(meilleure_valeur(),x0,y0,3,' ',10);
  922.  
  923.  
  924. float T_out_f = T_EXT_retenue / 10.0; // float
  925. Etiq_2.affiche_float(T_out_f, 4, 1, "deg");
  926.  
  927.  
  928. Etiq_5.affiche_valeur(Pression_lue, 4, "hPa");
  929.  
  930. // if (stable==1) {Led3.setCouleur(vert);} else {Led3.setCouleur(orange);}
  931.  
  932. Etiq_4.affiche_valeur(H_out_lue, 2, "");
  933. TFT480.Set_Text_Size(2);
  934. TFT480.Print_String("%", Etiq_4.x0+60,Etiq_4.y0+35);
  935.  
  936. }
  937.  
  938.  
  939.  
  940. void analyse_signal_primaire()
  941. {
  942.  
  943. char lettre_i;
  944. pulseWidth = micros() - memo_micros1; // VOIR la doc micros() -> nb de microsecondes depuis le démarrage de la carte
  945. memo_micros1 = micros();
  946.  
  947. //Serial.print('\n');
  948. //Serial.print(pulseWidth);
  949.  
  950. /**
  951. Une analyse des signaux fait apparaitre ceci:
  952. 4 durées caractéristiques 490us ; 95us ; 1940us; 3700us
  953. - 490us c'est la durée des tops HIGHT (tous identiques)
  954. - 950ums ; 1940ms ; 3700ms sont les différentes durées à l'état LOW qui séparent les tops ()
  955.  
  956. appelons ces durées par une lettre :
  957. A -> 490us
  958. B -> 950us
  959. C -> 1940us
  960. D -> 3700us
  961. **/
  962.  
  963. if ( (pulseWidth < 460) )
  964. {
  965. return;
  966. }
  967. if ( (pulseWidth >= 460) && (pulseWidth < 530) )
  968. {
  969. //Serial.print('A');
  970. lettre_i = 'A';
  971. buffer[i]= lettre_i; //memorisation de la lettre
  972. i++;
  973. }
  974. if ( (pulseWidth >= 910) && (pulseWidth < 1100) )
  975. {
  976. //Serial.print('B');
  977. lettre_i = 'B';
  978. buffer[i]= lettre_i;
  979. i++;
  980. }
  981. if ( (pulseWidth >= 1900) && (pulseWidth < 1970) )
  982. {
  983. //Serial.print('C');
  984. lettre_i = 'C';
  985. buffer[i]= lettre_i;
  986. i++;
  987. }
  988. if (pulseWidth >= 3000) // sonne la fin de la séquence...
  989. {
  990. if (i>=72)
  991. {
  992. Led2.setCouleur(jaune);
  993. Serial.println(' ');
  994. decodeBuffer();
  995. Serial.print(" ");
  996. lit_temperature(); // de la sonde ext 433MHz
  997. lit_humidite(); // de la sonde ext 433MHz
  998.  
  999. message="";
  1000. memo_micros2 = micros();
  1001. efface_buffer();
  1002.  
  1003. TT2=55; // pour synchro le timing local avec la sonde, afin d'affi + vite le résultat
  1004. // premiere_passe==0;
  1005.  
  1006. num_acquisition++;
  1007. if (num_acquisition >=5)
  1008. {
  1009. num_acquisition=0;
  1010. }
  1011. }
  1012. i=0;
  1013. }
  1014. /**
  1015. résultat :
  1016. AB AC AB AB AC AB AC AC AC AB AB AB AB AB AB AB AC AC AC A
  1017. 0 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 1 1 1
  1018.  
  1019. Les motifs qui apparaissent sont AB et AC
  1020. Il est vraissemblable que AB code pour 0 et AC pour 1 (ou l'inverse))
  1021. **/
  1022. }
  1023.  
  1024.  
  1025. String conv_time(uint8_t t)
  1026. {
  1027. String r;
  1028. r=String(t);
  1029. if (t<10) {r="0"+r;}
  1030. return r;
  1031. }
  1032.  
  1033.  
  1034. void affiche_heure()
  1035. {
  1036. String date;
  1037. String heure;
  1038. String heure_sec;
  1039.  
  1040.  
  1041. if (RTC.read(tm))
  1042. {
  1043. /*
  1044. tm.Day // uint8_t
  1045. tm.Month
  1046. tmYearToCalendar(tm.Year)
  1047. tm.Hour)
  1048. tm.Minute)
  1049. tm.Second)
  1050. */
  1051.  
  1052.  
  1053. date = String(tmYearToCalendar(tm.Year))+"-"+conv_time(tm.Month)+"-"+conv_time(tm.Day);
  1054. heure = conv_time(tm.Hour)+":"+conv_time(tm.Minute);
  1055. dataString = date +" [ "+ heure + " ]";
  1056. /** 2020-09-18 [ 16:56 ] */
  1057.  
  1058. heure_sec = heure+":"+conv_time(tm.Second)+" ";
  1059.  
  1060. TFT480.Set_Text_Size(3);
  1061. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  1062. TFT480.Print_String(heure_sec, 300, 110);
  1063.  
  1064. // Etiq_6.affiche_string(heure_sec);
  1065.  
  1066.  
  1067. }
  1068. }
  1069.  
  1070.  
  1071. void print_meteo_on_SDcard()
  1072. {
  1073. Led1.setCouleur(vert);
  1074. // pour éviter les problèmes avec les températures en °C négatives (< 0°C l'hiver), on passe en kelvin
  1075. uint16_t T_EXT_kelvin = T_EXT_retenue +2730; // en prenant T0abs= -273°C (et pas -273,15 °C...)
  1076. uint16_t T_INTE_kelvin = T_INTE_lue +2730;
  1077.  
  1078. // ici dataString comprend déjà la date et l'heure
  1079. dataString += " ; T(ext)=" + String(T_EXT_kelvin)+ " ; T(int)=" + String(T_INTE_kelvin);
  1080. dataString +=" ; H=" +String(H_out_lue)+ " ; P=" +String(Pression_lue);
  1081. Serial.println(" ");
  1082. Serial.println("ecriture data sur SDcard :");
  1083. Serial.print(dataString); // sur le port USB, pour info, à lire avec un terminal série (CuteCom...)
  1084. Serial.println(" ");
  1085.  
  1086. File dataFile = SD.open("data.txt", FILE_WRITE); // ouverture du fichier en écriture
  1087. if (dataFile)
  1088. {
  1089. dataFile.println(dataString); // écriture 1 ligne à la fin du fichier "data.txt" sur la micro SDcard
  1090. dataFile.close(); // referme le fichier
  1091. dataString="";
  1092. /** résultat sur la SDcard : ajout d'une ligne comme cell-ci :
  1093. 2020-09-19 [ 15:17 ] ; T(ext)=3004 ; T(int)=3018 ; H=62 ; P=1003
  1094. **/
  1095. //Note: pour les températures il faut retrancher 2730 puis /10
  1096. }
  1097. else { Serial.println("error opening data.txt");} // pour info
  1098. delay(100);
  1099. Led1.setCouleur(noir);
  1100.  
  1101. nb_wr_SD++;
  1102.  
  1103. TFT480.Set_Text_Size(1);
  1104. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  1105. TFT480.Print_Number_Int(nb_wr_SD,440,70,6,' ',10);
  1106. }
  1107.  
  1108.  
  1109. void write_scope_on_SDcard() // enregistre en binaire la courbe de T°C affichée sur le scope
  1110. {
  1111. // enregistre le contenu du "scope" afin de le réafficher après une coupure de l'alimantation
  1112. Led1.setCouleur(vert);
  1113. // pour éviter les problèmes avec les températures en °C négatives (< 0°C l'hiver), on passe en kelvin
  1114. uint16_t n;
  1115. uint16_t data_scope[n_enr_max];
  1116.  
  1117. Serial.println(" ");
  1118. Serial.println("ecriture du fichier scope.dat sur SDcard");
  1119. Serial.println(" ");
  1120.  
  1121. for(n=0 ; n<n_enr_max ; n++) // la valeur 'enr[n_enr_max].T' ne doit pas être inclue dans l'enregistrement, donc pas '<='
  1122. {
  1123. data_scope[n]=enr[n].T;
  1124. }
  1125.  
  1126. if(SD.exists("scope.dat")) {SD.remove("scope.dat");} // efface le fichier précédent s'il existe
  1127. File binFile1 = SD.open("scope.dat", FILE_WRITE); //re-creation et ouverture du fichier binaire (vierge) en écriture
  1128. if (binFile1)
  1129. {
  1130. binFile1.write((const uint8_t *)&data_scope, sizeof(data_scope)); // ce qui donne 2 x sizeof(data_scope) octets
  1131. binFile1.close(); // referme le fichier
  1132. }
  1133.  
  1134. }
  1135.  
  1136.  
  1137.  
  1138. void read_scope_on_SDcard()
  1139. {
  1140. // enregistre le contenu du "scope" afin de le réafficher après une coupure de l'alimantation
  1141. Led1.setCouleur(vert);
  1142. // pour éviter les problèmes avec les températures en °C négatives (< 0°C l'hiver), on passe en kelvin
  1143. uint16_t n;
  1144. uint16_t data_scope[n_enr_max];
  1145.  
  1146. Serial.println(" ");
  1147. Serial.println("lecture du fichier enrg.txt sur SDcard");
  1148. Serial.println(" ");
  1149.  
  1150. File binFile1 = SD.open("scope.dat", FILE_READ); //creation et ouverture du fichier en écriture
  1151. if (binFile1)
  1152. {
  1153. binFile1.read((uint8_t *)&data_scope, sizeof(data_scope));
  1154. binFile1.close(); // referme le fichier
  1155. }
  1156.  
  1157. for(n=0 ; n<n_enr_max ; n++)
  1158. {
  1159. enr[n].T=data_scope[n];
  1160. }
  1161. T_EXT_retenue=enr[n-1].T;
  1162.  
  1163. }
  1164.  
  1165.  
  1166. void loop()
  1167. {
  1168. uint16_t x2;
  1169. uint16_t y2;
  1170. memo_etat = etat;
  1171. etat = digitalRead(INPUT_PIN);
  1172. if (etat != memo_etat)
  1173. {
  1174. analyse_signal_primaire();
  1175. }
  1176.  
  1177.  
  1178. TT1+=1;
  1179. if (TT1 >= 1000) // tous les 1000 passages dans la boucle pour ne pas trop charger le processeur
  1180. {
  1181. TT1=0;
  1182. memo_temps_ecoule = temps_ecoule;
  1183. temps_ecoule = (micros() - memo_micros2) / 1E6;
  1184. if (temps_ecoule != memo_temps_ecoule)
  1185. {
  1186. // ici toutes les 1s
  1187.  
  1188. TT2+=1;
  1189. TFT480.Set_Text_Back_colour(0,0,0);
  1190. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  1191. TFT480.Set_Text_Size(1);
  1192.  
  1193. x2 = Etiq_2.x0+Etiq_2.dx;
  1194. y2 = Etiq_2.y0;
  1195. TFT480.Print_Number_Int(60-TT2,x2-16,y2+8,3,' ',10); // petits chiffres dans le coin du cadre température EXT
  1196.  
  1197. affiche_heure();
  1198. Led2.setCouleur(noir);
  1199.  
  1200. }
  1201.  
  1202. /**
  1203. if ( (TT2%2)== 0) // toutes les 2 secondes
  1204. {
  1205. trace_sur_Scope(); // pour test --- à commenter
  1206. }
  1207. **/
  1208.  
  1209. if (TT2 >= 60) // 60 pour toutes les (1) minutes
  1210. {
  1211. TT2=0;
  1212. TT3+=1;
  1213. num_acquisition=0; // pour recevoir la séquence suivante bien calée en mémoire
  1214. calcul_confiance();
  1215. T_EXT_retenue = meilleure_valeur();
  1216. acqui_PressionTemperature();
  1217. affiche_meteo();
  1218.  
  1219. // trace_sur_Scope(); // **************** pour test; A COMMENTER *************************
  1220. }
  1221.  
  1222.  
  1223. if (TT3 >= 5) // toutes les 5 minutes
  1224. {
  1225. TT3=0;
  1226. TT4+=1;
  1227.  
  1228. trace_sur_Scope();
  1229.  
  1230. TFT480.Set_Text_Back_colour(0,0,0);
  1231. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  1232. TFT480.Set_Text_Size(1);
  1233. x2 = 300;
  1234. y2 = 70;
  1235. TFT480.Print_String("WR SDcard", x2, y2);
  1236. TFT480.Print_Number_Int(10-TT3,x2+55,y2,3,' ',10); //affiche le temps restant avant enregistrement sur la SDcard
  1237. TFT480.Print_String("mn", x2+70, y2);
  1238. }
  1239.  
  1240. if (TT4 >= 6) // toutes les 30 minutes
  1241. {
  1242. TT4=0;
  1243.  
  1244. print_meteo_on_SDcard(); // en ASCII dans le fichier "data.txt"
  1245. delay(500);
  1246. write_scope_on_SDcard(); // en binaire dans le fichier "scope.dat"
  1247. delay(500);
  1248. }
  1249.  
  1250.  
  1251. }
  1252.  
  1253. }
  1254.  
  1255. /** ***********************************************************************************
  1256. CLASS Etiquette // affiche un nombre ou un petit texte dans un rectangle
  1257. ***************************************************************************************/
  1258.  
  1259. // Constructeur
  1260. Etiquette::Etiquette()
  1261. {
  1262.  
  1263. }
  1264.  
  1265.  
  1266. void Etiquette::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi, char txt_etiquette_i[12])
  1267. {
  1268. x0 = xi;
  1269. y0 = yi;
  1270. dx = dxi;
  1271. dy = dyi;
  1272.  
  1273. for (int i=0; i<12; i++) {txt_etiquette[i]=txt_etiquette_i[i];}
  1274. txt_etiquette[12]='\0'; // zero terminal
  1275.  
  1276. TFT480.Set_Text_Back_colour(0, 0, 0);
  1277. TFT480.Set_Text_colour(10, 10, 5);
  1278.  
  1279. TFT480.Set_Text_colour(Rcadre, Vcadre, Bcadre);
  1280. TFT480.Draw_Rectangle(x0, y0+5, x0+dx, y0+dy);
  1281.  
  1282. traceCadre();
  1283. affi_etiquette();
  1284.  
  1285. }
  1286.  
  1287. void Etiquette::traceCadre()
  1288. {
  1289. TFT480.Set_Text_colour(Rcadre, Vcadre, Bcadre);
  1290. // TFT480.Draw_Rectangle(x0, y0+5, x0+dx0, y0+dy0);
  1291. TFT480.Set_Draw_color(0,0,0);
  1292. TFT480.Fill_Rectangle(x0, y0+5, x0+dx, y0+dy);
  1293.  
  1294. }
  1295.  
  1296.  
  1297. void Etiquette::affi_etiquette()
  1298. {
  1299. TFT480.Set_Text_Back_colour(0, 0, 0);
  1300. // TFT480.setFont(BigFont);
  1301.  
  1302. TFT480.Set_Text_colour(180,180,180); // couleur de l'étiquette
  1303. TFT480.Set_Text_Size(2);
  1304. TFT480.Print_String(txt_etiquette, x0, y0); // Etiquette
  1305. }
  1306.  
  1307.  
  1308. void Etiquette::setCouleurTxt(uint8_t *couleur_i)
  1309. {
  1310. R=couleur_i[0];
  1311. G=couleur_i[1];
  1312. B=couleur_i[2];
  1313. }
  1314.  
  1315.  
  1316. void Etiquette::setCouleurCadre(uint8_t *couleur_i)
  1317. {
  1318. Rcadre = couleur_i[0];
  1319. Vcadre = couleur_i[1];
  1320. Bcadre = couleur_i[2];
  1321.  
  1322. TFT480.Set_Draw_color(Rcadre, Vcadre, Bcadre);
  1323. TFT480.Draw_Rectangle(x0, y0+5, x0+dx, y0+dy);
  1324. TFT480.Set_Text_Back_colour(0, 0, 0);
  1325. // TFT480.setFont(BigFont);
  1326. TFT480.Set_Text_colour(180,180,180); // couleur de l'étiquette
  1327. TFT480.Print_String(txt_etiquette, x0, y0); // Etiquette (pour écrire volaontairement par dessus le cadre))
  1328. }
  1329.  
  1330.  
  1331. void Etiquette::setCouleurFond(uint8_t *couleur_i)
  1332. {
  1333. Rfond = couleur_i[0];
  1334. Vfond = couleur_i[1];
  1335. Bfond = couleur_i[2];
  1336. }
  1337.  
  1338.  
  1339. void Etiquette::flashFond(uint8_t *couleur_i)
  1340. {
  1341. Rfond = couleur_i[0];
  1342. Vfond = couleur_i[1];
  1343. Bfond = couleur_i[2];
  1344. TFT480.Set_Text_colour(Rfond, Vfond, Bfond);
  1345. TFT480.Fill_Rectangle(x0, y0, x0+dx, y0+dy);
  1346. delay(10);
  1347. TFT480.Set_Text_colour(10, 10, 5);
  1348. TFT480.Fill_Rectangle(x0, y0, x0+dx, y0+dy);
  1349. traceCadre();
  1350. affi_etiquette();
  1351. }
  1352.  
  1353.  
  1354.  
  1355.  
  1356. void Etiquette::affiche_valeur(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3])
  1357. {
  1358. for (int i=0; i<3; i++) {txt_unite[i]=txt_unite_i[i];}
  1359. txt_unite[3]='\0'; // zero terminal
  1360.  
  1361. //traceCadre(); // pour effacer le contenu précédent
  1362.  
  1363. TFT480.Set_Text_Back_colour(0, 0, 0);
  1364. TFT480.Set_Text_colour(R,G,B);
  1365. TFT480.Set_Text_Size(4);
  1366.  
  1367. //RAPPEL Print_Number_Int(long num, int16_t x, int16_t y, int16_t length, uint8_t filler, int16_t system);
  1368. TFT480.Print_Number_Int(valeur, x0+10,y0+18, nb_chiffres, ' ', 10);
  1369.  
  1370. TFT480.Set_Text_Size(2);
  1371.  
  1372. TFT480.Print_String(txt_unite, x0+130,y0+35); // ex : mm, kHz, etc...
  1373. }
  1374.  
  1375.  
  1376.  
  1377. void Etiquette::affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3])
  1378. {
  1379. for (int i=0; i<3; i++) {txt_unite[i]=txt_unite_i[i];}
  1380. txt_unite[3]='\0'; // zero terminal
  1381.  
  1382. TFT480.Set_Text_Back_colour(0, 0, 0);
  1383. TFT480.Set_Text_colour(R,G,B);
  1384. TFT480.Set_Text_Size(4);
  1385.  
  1386. //RAPPEL: Print_Number_Float(double num, uint8_t dec, int16_t x, int16_t y, uint8_t divider, int16_t length, uint8_t filler);
  1387. TFT480.Print_Number_Float(valeur, nb_dec, x0+20, y0+18, '.', nb_chiffres, ' ');
  1388.  
  1389. TFT480.Set_Text_Size(2);
  1390. TFT480.Print_String(txt_unite, x0+130,y0+35); // ex : mm, kHz, etc...
  1391. }
  1392.  
  1393.  
  1394.  
  1395.  
  1396. void Etiquette::affiche_HEXA(uint32_t valeur)
  1397. {
  1398. // affiche un nombre en representation hexadécimale
  1399. // 16 nb_signes hexa max
  1400.  
  1401. uint8_t r;
  1402. uint8_t i;
  1403.  
  1404. char tbl[9];
  1405. char signes[17] = "0123456789ABCDEF";
  1406.  
  1407. for (i=0; i<8; i++)
  1408. {
  1409. r= valeur % 16; // modulo (reste de la division)
  1410. valeur /= 16; // quotient
  1411. tbl[7-i]=signes[r];
  1412. };
  1413. tbl[8]='\0';
  1414.  
  1415. TFT480.Set_Text_Back_colour(0, 0, 0);
  1416. TFT480.Set_Text_colour(0,255,255);
  1417. // TFT480.setFont(SmallFont);
  1418. TFT480.Print_String(tbl, x0+10,y0+15);
  1419. }
  1420.  
  1421.  
  1422.  
  1423.  
  1424. void Etiquette::affiche_texte(char txt_i[10])
  1425. {
  1426. for (int i=0; i<10; i++) {texte[i]=txt_i[i];}
  1427. texte[10]='\0'; // zero terminal
  1428.  
  1429. TFT480.Set_Text_Back_colour(0, 0, 0);
  1430. TFT480.Set_Text_colour(R, G, B);
  1431. // TFT480.setFont(BigFont);
  1432. TFT480.Set_Text_Size(3);
  1433. TFT480.Print_String(texte, x0+5,y0+18);
  1434. }
  1435.  
  1436.  
  1437. void Etiquette::affiche_string(String txt_i)
  1438. {
  1439.  
  1440. TFT480.Set_Text_Back_colour(0, 0, 0);
  1441. TFT480.Set_Text_colour(R, G, B);
  1442. // TFT480.setFont(BigFont);
  1443. TFT480.Set_Text_Size(3);
  1444. TFT480.Print_String(txt_i, x0+8,y0+18);
  1445. }
  1446.  
  1447.  
  1448.  
  1449. /** ***********************************************************************************
  1450. CLASS Scope // affiche des courbes dans une surface rectangulaire
  1451. ***************************************************************************************/
  1452.  
  1453. // Constructeur
  1454. Scope::Scope()
  1455. {
  1456.  
  1457. }
  1458.  
  1459.  
  1460. void Scope::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi)
  1461. {
  1462. x0 = xi;
  1463. y0 = yi;
  1464. dx = dxi;
  1465. dy = dyi;
  1466.  
  1467. TFT480.Set_Draw_color(Rcadre, Vcadre, Bcadre);
  1468. TFT480.Draw_Rectangle(x0, y0, x0+dx, y0+dy);
  1469.  
  1470. }
  1471.  
  1472.  
  1473. void Scope::efface_surface()
  1474. {
  1475. TFT480.Set_Draw_color(0,0,0);
  1476. TFT480.Fill_Rectangle(x0+1, y0+1, x0+dx-2, y0+dy-2);
  1477. }
  1478.  
  1479.  
  1480. void Scope::setCouleurCadre(uint8_t *couleur_i)
  1481. {
  1482. Rcadre = couleur_i[0];
  1483. Vcadre = couleur_i[1];
  1484. Bcadre = couleur_i[2];
  1485. }
  1486.  
  1487. void Scope::setCouleurTrace(uint8_t *couleur_i)
  1488. {
  1489. Rtrace = couleur_i[0];
  1490. Vtrace = couleur_i[1];
  1491. Btrace = couleur_i[2];
  1492. }
  1493.  
  1494.  
  1495. void Scope::traceCadre()
  1496. {
  1497. TFT480.Set_Draw_color(Rcadre, Vcadre, Bcadre);
  1498. TFT480.Draw_Rectangle(x0, y0, x0+dx, y0+dy);
  1499. TFT480.Set_Draw_color(0,0,0);
  1500. TFT480.Fill_Rectangle(x0+1, y0+1, x0+dx-2, y0+dy-2);
  1501.  
  1502. }
  1503.  
  1504.  
  1505. void Scope::efface_libel_gradu()
  1506. {
  1507. TFT480.Set_Draw_color(0,0,0);
  1508. TFT480.Fill_Rectangle(x0-20, y0+1, x0-2, y0+dy-2);
  1509. }
  1510.  
  1511.  
  1512. void Scope::traceGraduation()
  1513. {
  1514. efface_surface();
  1515. efface_libel_gradu();
  1516. setCouleurTrace(gris);
  1517. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  1518. TFT480.Set_Text_Size(1);
  1519. int16_t Yi, Yi2, Y2;
  1520. uint16_t xi;
  1521. uint8_t i, s;
  1522.  
  1523.  
  1524.  
  1525. for (Yi=grad_min; Yi<=grad_max; Yi+=50) // 500->50°C
  1526. {
  1527. //lignes horizontales :
  1528.  
  1529. Tiret(1, (Yi+offset)/echelle, dx-1, (Yi+offset)/echelle);
  1530. Y2 = Yi/10;
  1531. TFT480.Print_Number_Int(Y2 ,x0-15, y0+dy-3-(Yi+offset)/echelle, 3, ' ', 10); // petits chiffres à gauche
  1532. }
  1533.  
  1534. //lignes verticales :
  1535. i=0;
  1536. for (xi=450; xi>12; xi-=12) // 1 trait de graduation tous les 12 px
  1537. {
  1538. s=0;
  1539. if ( (i%12)==0) {s=10;} //pour un tiret de graduation +grand toutes les 12h
  1540. if ( (i%24)==0) {s=40;}
  1541.  
  1542.  
  1543. /**
  1544. Echelle horizontale (temporelle) :
  1545. 1 mesure(=1px)toutes les 5mn -> 450px*5 = 2250mn ;
  1546. 2250mn/60 =37.5h
  1547. 450px / 37.5h = 12px/h
  1548. */
  1549. setCouleurTrace(gris_fonce); if ((i%12) == 0) {setCouleurTrace(jaune);}
  1550. Tiret(xi-1, 20, xi-1, dy-20);
  1551.  
  1552. i++;
  1553. }
  1554.  
  1555. TFT480.Set_Text_Size(1);
  1556. TFT480.Set_Text_colour(blanc[0],blanc[1],blanc[2]);
  1557. TFT480.Print_String("-12h", x0+300, y0+dy-10);
  1558. TFT480.Print_String("-24h", x0+156, y0+dy-10);
  1559. TFT480.Print_String("-36h", x0+12, y0+dy-10);
  1560. }
  1561.  
  1562.  
  1563. void Scope::Plot(uint16_t x, uint16_t y)
  1564. {
  1565. TFT480.Set_Draw_color(Rtrace, Vtrace, Btrace);
  1566. TFT480.Draw_Pixel(x0+x, y0+y);
  1567. }
  1568.  
  1569.  
  1570. void Scope::Tiret(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
  1571. {
  1572. if (x1==0) {x1=1;}
  1573. if (x2==0) {x2=1;}
  1574.  
  1575. if (x1>=dx-1) {x1=dx-2;}
  1576. if (x2>=dx-1) {x2=dx-2;}
  1577.  
  1578. TFT480.Set_Draw_color(Rtrace, Vtrace, Btrace);
  1579. TFT480.Draw_Line(x0+x1, y0+dy-y1, x0+x2, y0+dy-y2);
  1580. }
  1581.  
  1582. /** ***********************************************************************************
  1583. CLASS LED // affiche une petite LED
  1584. ***************************************************************************************/
  1585.  
  1586. // Constructeur
  1587. LED::LED()
  1588. {
  1589.  
  1590. }
  1591.  
  1592.  
  1593. void LED::init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi)
  1594. {
  1595. x0 = xi;
  1596. y0 = yi;
  1597. dx = dxi;
  1598. dy = dyi;
  1599.  
  1600. TFT480.Set_Draw_color(blanc[0], blanc[1], blanc[2]);
  1601. TFT480.Draw_Rectangle(x0, y0, x0+dx, y0+dy);
  1602. TFT480.Set_Draw_color(0, 0, 0);
  1603. TFT480.Fill_Rectangle(x0+1, y0+1, x0+dx-1, y0+dy-1);
  1604. }
  1605.  
  1606.  
  1607. void LED::setCouleur(uint8_t *couleur_i)
  1608. {
  1609. uint8_t R = couleur_i[0];
  1610. uint8_t V = couleur_i[1];
  1611. uint8_t B = couleur_i[2];
  1612.  
  1613. TFT480.Set_Draw_color(R, V, B);
  1614. TFT480.Fill_Rectangle(x0+1, y0+1, x0+dx-1, y0+dy-1);
  1615. }
  1616.  
  1617.  
  1618.  
  1619.  
  1620.  
  1621.  


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.  
  16. //if the IC model is known or the modules is unreadable,you can use this constructed function
  17. LCDWIKI_KBV TFT480(ILI9486,A3,A2,A1,A0,A4); //model,cs,cd,wr,rd,reset
  18.  
  19. //if the IC model is not known and the modules is readable,you can use this constructed function
  20. //LCDWIKI_KBV TFT480(320,480,A3,A2,A1,A0,A4);//width,height,cs,cd,wr,rd,reset
  21.  
  22. /** ***********************************************************************************
  23. CLASS Etiquette // affiche un nombre ou un petit texte dans un rectangle
  24. ***************************************************************************************/
  25.  
  26.  
  27.  
  28. class Etiquette
  29. {
  30. public:
  31.  
  32. uint16_t x0; //position
  33. uint16_t y0;
  34. uint16_t dx; //dimension
  35. uint16_t dy;
  36.  
  37. //couleurs
  38. uint8_t Rfond, Vfond, Bfond;
  39. uint8_t Rcadre, Vcadre, Bcadre;
  40. uint8_t R,G,B;
  41.  
  42. Etiquette();
  43.  
  44. void init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy,char txt_etiquette[10]);
  45. void setCouleurCadre(uint8_t *couleur_i);
  46. void setCouleurTxt(uint8_t *couleur_i);
  47. void setCouleurFond(uint8_t *couleur_i);
  48. void flashFond(uint8_t *couleur_i);
  49. void affiche_valeur(uint32_t valeur, uint8_t nb_chiffres, char txt_unite_i[3]);
  50. void affiche_float(float valeur, uint8_t nb_chiffres, uint8_t nb_dec, char txt_unite_i[3]);
  51. void affiche_HEXA(uint32_t valeur);
  52. void affiche_texte(char txt_i[5]);
  53. void affiche_string(String txt_i);
  54.  
  55.  
  56. private:
  57. char txt_etiquette[13];
  58. char txt_unite[4];
  59. char texte[11];
  60.  
  61. void traceCadre();
  62. void affi_etiquette();
  63.  
  64. };
  65.  
  66.  
  67. /** ***********************************************************************************
  68. CLASS Scope // affiche des courbes dans une surface rectangulaire
  69. ************************************************************************************* */
  70.  
  71. class Scope
  72. {
  73. public:
  74.  
  75. uint16_t x0; //position
  76. uint16_t y0;
  77. uint16_t dx; //dimension
  78. uint16_t dy;
  79.  
  80. //couleurs
  81. uint8_t Rtrace, Vtrace, Btrace;
  82. uint8_t Rcadre, Vcadre, Bcadre;
  83. uint8_t R,G,B;
  84.  
  85. Scope();
  86.  
  87. void init(uint16_t x, uint16_t y, uint16_t dx, uint16_t dy);
  88. void setCouleurCadre(uint8_t *couleur_i);
  89. void setCouleurTrace(uint8_t *couleur_i);
  90. void traceCadre();
  91. void efface_surface();
  92. void efface_libel_gradu();
  93. void traceGraduation();
  94. void Plot(uint16_t x, uint16_t y);
  95. void Tiret(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
  96.  
  97.  
  98.  
  99. private:
  100.  
  101.  
  102. };
  103.  
  104.  
  105. class LED
  106. {
  107. public:
  108.  
  109. uint16_t x0; //position
  110. uint16_t y0;
  111. uint16_t dx; //dimension
  112. uint16_t dy;
  113.  
  114.  
  115. LED();
  116.  
  117. void init(uint16_t xi, uint16_t yi, uint16_t dxi, uint16_t dyi);
  118. void setCouleur(uint8_t *couleur_i);
  119.  
  120.  
  121. private:
  122.  
  123.  
  124. };
  125.  
  126. /************************************************************************************** */
  127. // AUTRES DECLARATIONS
  128.  
  129.  
  130. struct enregistrement
  131. {
  132. int16_t T; // température
  133. uint8_t H; // humidité
  134.  
  135. };
  136.  
  137. void init_ports (void);
  138. void init_variables(void);
  139. void trace_ecran();
  140. void trace_entete();
  141. void clear_screen();
  142. void dessine_triangles(void);
  143. void affiche_courbeT();
  144. void affiche_courbeH();
  145. void affiche_ligne_ref();
  146. void affiche_pannel();
  147. void affiche_LEDS();
  148.  
  149. void efface_buffer();
  150. void decale_enregistrements();
  151. uint16_t strbinToInt(String str1, uint8_t nb_bits);
  152. void lit_temperature();
  153. void calcul_confiance();
  154. uint16_t meilleure_valeur();
  155. void lit_humidite();
  156. void decodeBuffer();
  157. void setup();
  158. void determine_Tmin_Tmax();
  159. void calcul_Gradu_ech();
  160. void trace_sur_Scope();
  161. void affiche_meteo();
  162. void loop();
  163. void analyse_signal_primaire();
  164. void affiche_heure();
  165. void setDateTime();
  166. void print_meteo_on_SDcard(); // en ASCII
  167. void write_scope_on_SDcard(); // en binaire
  168. void read_scope_on_SDcard();
  169.  
  170. #endif
  171.  

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 : I2S et SCI.
Ici nous utilisons le bus I2C.

9 Le module micro SDcard

Classique lui aussi, bien connu dans le monde de l'Arduino.

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

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 le 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

24 oct 2020 :
Les principales amélioration que je viens d'apporter 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 de la courbe de température sur la SDcard
  • chargement à la mise en route de la courbe précédemment sauvegardée

18 -

Liens...



Me contacter à propos de cet article :

Pseudo : Les messages envoyés font l'objet d'une modération à priori.
Question mathématique :
Click to reload image
=
cliquez sur l'image pour un faire autre calcul.
Réponses à certaines de vos questions...


246