Horloge GPS client-serveur ESP32

Une horloge ESP32 serveur WiFi, pilotée par GPS, + horloge ESP32 client.
Cette réalisation nous permettra donc de nous familiariser avec :
- la programmation des microcontrôleurs ESP32.
- la connexion d'un module récepteur GPS NEO-6M à un ESP32.
- la création d'un réseau local WiFi autonome (ne nécessitant pas de box routeur internet).
- l'échange de données directement entre deux ESP32 par WiFi (requête et retour au format html).
- l'utilisation d'une mini carte ESP32 TTGO T-display (avec affichage intégré OLED couleur).

1 Le serveur de temps GPS

Le serveur dans son boîtier imprimé en 3D sur une Ender3 Pro.

- à droite le cordon d'alim 5V avec son adaptateur USB-C
- en haut le connecteur SMA pour la liaison à l'antenne GPS.

2 Le serveur vu de dos :

La carte ESP32 configurée en serveur WiFi 2.4GHz + le module GPS absorbent (et restituent en chaleur) environ 5V x 50mA = 0.25W, ce qui suffit à élever significativement la température dans cette très petite boite. D'où la grille pour l'aération. Il est extrêmement simple de dessiner une telle structure avec le logiciel libre FreeCad. Vous trouverez les sources au bas de la page.

3 Dans la boite :

Ici on voit surtout le module GPS.

La petite vis près du connecteur SMA sert à le fixer en rotation (avec une petite équerre en laiton, soudée).

4 La carte ESP32 TTGO t-display

L"écran couleur LCD de 1.14" a une définition de 135 x 240 pixels. Çà peut paraître peu, mais compte tenu de la taille minuscule de l'afficheur, c'est tout à fait suffisant : on ne discerne pas les pixels à l’œil nu, et les images sont parfaitement nettes.

A noter toutefois une particularité étonnante : le connecteur USB est un modèle USB-C (qui se branche donc indifféremment dans les deux sens, mais qui nécessite un adaptateur pour pouvoir être connecté à un câble USB classique).

5 Schéma du serveur

C'est sans doute le schéma le plus simple que j'ai eu à dessiner !

Les pins GPIO37 et GPIO38 choisis pour la liaison série (RX-TX) sont définis dans le logiciel.

static const int RxPin = 38; // UART pin_in relié à GPS_Tx static const int TxPin = 37; // UART pin_out relié à GPS_Rx

On peut choisir autre chose, en fonction de la présence d'éventuels périphériques supplémentaires... Ce qui compte c'est que la sortie de l'un soit reliée à l'entrée de l'autre, et vice-versa.

6 Le code source en C++ du serveur :

CODE SOURCE en C++
  1. /***********
  2.   GPS_Time & SERVEUR WiFi local (ip=192.168.4.1/)
  3.   pour ESP32 (TTGO T-Display, avec petit ecran LCD couleur IPS ST7789V 1.14 Inch 135x240 px)
  4.   + module NEO-6M
  5.  
  6.  
  7. il faut :
  8. - choisir (décommenter) la ligne suivante, dans le fichier ~/Arduino/libraries/TFT_eSPI/User_Setup_Select.h :
  9. ligne 62... (environ):
  10.   #include <User_Setups/Setup25_TTGO_T_Display.h> // Setup file for ESP32 and TTGO T-Display ST7789V SPI bus TFT
  11.  
  12.  
  13. -et bien évidemment commenter tout autre ligne de choix, en particulier celle par défaut:
  14. (ligne 22) :
  15. #include <User_Setup.h> // Default setup is root library folder
  16.  
  17.  
  18.  
  19.   voir aussi l'exemple "Colour_Test"
  20.  
  21. ************/
  22.  
  23. #define Version "2.1"
  24.  
  25. #include <SPI.h>
  26. #include <TFT_eSPI.h>
  27.  
  28. #include <SoftwareSerial.h>
  29. #include <TinyGPS.h>
  30.  
  31. #include <TimeLib.h> // include Arduino time library
  32. #include <WiFi.h>
  33.  
  34. // #include <WebServer.h>
  35. #include "ESPAsyncWebServer.h"
  36.  
  37.  
  38. const char* ssid = "TPGPS_34";
  39. const char* password = "94r6tkJ31";
  40.  
  41.  
  42. // WebServer server(80);
  43. AsyncWebServer server(80); // Create AsyncWebServer object on port 80
  44.  
  45.  
  46. static const int RxPin = 38; // UART pin_in relié à GPS_Tx
  47. static const int TxPin = 37; // UART pin_out relié à GPS_Rx
  48.  
  49. TinyGPS GPS;
  50. TFT_eSPI ECRAN_1 = TFT_eSPI();
  51. SoftwareSerial ss(RxPin, TxPin);
  52.  
  53.  
  54. unsigned long age_GPS;
  55. unsigned long date_GPS;
  56. unsigned long heure_GPS;
  57.  
  58.  
  59. uint16_t annee;
  60. uint8_t mois;
  61. uint8_t jour;
  62. uint8_t jour_local; // tenant compte du décallage horaire
  63. uint8_t heures=0;
  64. uint8_t minutes=0;
  65. uint8_t secondes=0;
  66. uint8_t jour_de_la_semaine;
  67.  
  68. uint16_t compte1=0;
  69.  
  70.  
  71. String annee_txt;
  72. String mois_txt;
  73. String jour_txt;
  74. String date_txt;
  75. String date_txt_compacte;
  76.  
  77. String heures_txt;
  78. String minutes_txt;
  79. String secondes_txt;
  80.  
  81.  
  82. char heure_array[9]; // 23:56:02 + zero terminal
  83. char date_array[17]; // Sam 30 Janv 2021 + zero terminal
  84.  
  85. char heure_date_array[28]; // 23:56:02 (Sam 30 Janv 2021) + zero terminal
  86.  
  87.  
  88. void setup()
  89. {
  90. Serial.begin(115200);
  91. ss.begin(9600);
  92.  
  93. Serial.println("\n");
  94.  
  95.  
  96. ECRAN_1.init();
  97. ECRAN_1.setRotation(1);
  98. ECRAN_1.fillScreen(TFT_BLACK);
  99. ECRAN_1.setCursor(0, 0, 2); // Set "cursor" at top left corner of display (0,0) and select font 4
  100. ECRAN_1.setTextColor(TFT_WHITE, TFT_BLACK);
  101. ECRAN_1.println("Horloge GPS + serveur WiFi");
  102. delay(500);
  103. ECRAN_1.setCursor(0, 20, 4);
  104. ECRAN_1.setTextColor(TFT_YELLOW, TFT_BLACK);
  105. ECRAN_1.println("Connexion...");
  106.  
  107. WiFi.persistent(false);
  108.  
  109. WiFi.softAP(ssid, password); // ok, ça marche, crée un réseau. mode privé
  110.  
  111. IPAddress IP = WiFi.softAPIP();
  112. Serial.print("AP IP address: "); Serial.println(IP);
  113.  
  114. Serial.println(WiFi.status());
  115. // while (WiFi.status() != WL_CONNECTED) // <------ ne marche pas ! toujours = 255
  116. // while (WiFi.localIP().toString() == "0.0.0.0") // <------ ne marche pas non plus !!
  117. {
  118. Serial.print("...");
  119. delay(200);
  120. }
  121.  
  122. /*
  123.  
  124. //= heures_txt +":" + minutes_txt +":" + secondes_txt;
  125. server.on("/heure", HTTP_GET, [](AsyncWebServerRequest *request)
  126. {
  127. request->send_P(200, "text/plain", heure_array);
  128. });
  129.  
  130.  
  131. server.on("/date", HTTP_GET, [](AsyncWebServerRequest *request)
  132. {
  133. request->send_P(200, "text/plain", date_array);
  134. });
  135.  
  136. */
  137. server.on("/HR", HTTP_GET, [](AsyncWebServerRequest *request)
  138. {
  139. request->send_P(200, "text/plain", heure_date_array);
  140. });
  141.  
  142.  
  143. server.begin();
  144.  
  145. Serial.println("Serveur web actif!");
  146.  
  147. ECRAN_1.setTextColor(TFT_GREEN, TFT_BLACK);
  148. ECRAN_1.println("Serveur web actif !");
  149.  
  150. smartdelay(2000);
  151. ECRAN_1.fillScreen(TFT_BLACK);
  152.  
  153. ECRAN_1.setCursor(200, 0, 2); // Set "cursor" at top left corner of display (0,0) and select font 4
  154. ECRAN_1.setTextColor(TFT_BLUE, TFT_BLACK);
  155. ECRAN_1.println("GPS");
  156.  
  157. delay(1000);
  158. GPS.get_datetime(&date_GPS, &heure_GPS, &age_GPS);
  159. decode_time(date_GPS, heure_GPS);
  160.  
  161.  
  162. affiche_date();
  163. }
  164.  
  165.  
  166.  
  167. static void smartdelay(unsigned long ms)
  168. {
  169. unsigned long start = millis();
  170. do
  171. {
  172. while (ss.available()) { GPS.encode(ss.read()); }
  173. }
  174. while (millis() - start < ms);
  175. }
  176.  
  177.  
  178.  
  179. void incremente_time(uint8_t nb_s)
  180. {
  181. for (uint8_t n=0; n<nb_s; n++)
  182. {
  183. if (secondes < 59) {secondes+=1;}
  184. else
  185. {
  186. secondes=0;
  187. if (minutes < 59) {minutes+=1;}
  188. else
  189. {
  190. minutes=0;
  191. if (heures < 23) {heures+=1;}
  192. else
  193. {
  194. heures=0;
  195. }
  196. }
  197. }
  198. }
  199. }
  200.  
  201.  
  202. void inc_heures(uint8_t nb)
  203. {
  204. heures += nb;
  205. if (heures >= 24) {heures -= 24;}
  206. }
  207.  
  208.  
  209. void decode_time(int32_t date_GPS, int32_t time_GPS)
  210. {
  211. // ******************** IL FAUT MODIFIER LA LIGNE SUIVANTE DEUX FOIS PAR AN ************************
  212. // la date des changements entre heure d'éte et heure d'hiver est un truc que seuls les shadocks peuvent prévoir
  213. // avec la formule J = 2 Ga + (2/3 Bu - 4 Zo) * Meu
  214.  
  215. uint8_t decal_Horaire_FR = 2; //décalage Horaire: l'hiver =1 ; l'été =2 (heure allemande !)
  216.  
  217. // **************************************************************************************************
  218. annee = date_GPS % 100;
  219. mois = (date_GPS / 100) % 100;
  220. jour = date_GPS / 10000;
  221. heures = time_GPS / 1000000;
  222.  
  223. /*
  224. // POUR TEST:
  225.  
  226. annee=22;
  227. mois=1;
  228. jour=29;
  229. heures=17;
  230. */
  231. inc_heures(decal_Horaire_FR);
  232. // problème: la date reçue change à 0h GMT ! et pas à 0h de cette cette heure décallée...
  233. // d'où la correction plus bas tenant compte du décallage horaire
  234.  
  235. minutes = (time_GPS / 10000) % 100;
  236. secondes = (time_GPS / 100) % 100;
  237.  
  238. annee_txt="";
  239. if (annee<10) {annee_txt="0";}
  240. annee_txt += (String) annee;
  241.  
  242. mois_txt="";
  243. if (mois<10) {mois_txt="0";}
  244. mois_txt += (String) mois;
  245.  
  246. jour_txt="";
  247. if (jour<10) {jour_txt="0";}
  248.  
  249. if (heures >= decal_Horaire_FR)
  250. {
  251. jour_local = jour;
  252. }
  253. else
  254. {
  255. // on passe iici :
  256. // l'hiver entre minuit et 0h59 du matin heure locale FR)
  257. // l'été entre minuit et 1h59 du matin heure locale FR)
  258. // penser à mettre à jour deux fois par an la variable "decal_Horaire_FR" plus haut
  259. jour_local = jour++;
  260. }
  261.  
  262. jour_txt += (String) jour_local;
  263.  
  264. heures_txt="";
  265. if (heures<10) {heures_txt="0";}
  266. heures_txt += (String) heures;
  267.  
  268. minutes_txt="";
  269. if (minutes<10) {minutes_txt="0";}
  270. minutes_txt += (String) minutes;
  271.  
  272. secondes_txt="";
  273. if (secondes<10) {secondes_txt="0";}
  274. secondes_txt += (String) secondes;
  275. }
  276.  
  277.  
  278. void calcul_jour_de_la_semaine()
  279. {
  280. // d'après l'Algorithme de Mike Keith
  281. uint16_t d, m, y, z, jds;
  282.  
  283. d=jour;
  284. m=mois;
  285. y=annee;
  286.  
  287. if (m>=3)
  288. {
  289. jds = ( ((23*m)/9) + d + 4 + y + (y/4) - (y/100) + (y/400) - 2 ) % 7;
  290. }
  291. else
  292. {
  293. z = y-1;
  294. jds = ( ((23*m)/9) + d + 4 + y + (z/4) - (z/100) + (z/400) ) % 7;
  295. }
  296. jour_de_la_semaine = jds;
  297. }
  298.  
  299.  
  300. String conv_time(uint8_t t)
  301. {
  302. String r;
  303. r=String(t);
  304. if (t<10) {r="0"+r;}
  305. return r;
  306. }
  307.  
  308.  
  309.  
  310. void affiche_date()
  311. {
  312. uint16_t box2_x = 0;
  313. uint16_t box2_y = 121-20;
  314. uint16_t box2_w = 175;
  315. uint16_t box2_h = 20;
  316.  
  317. date_txt="";
  318.  
  319. calcul_jour_de_la_semaine();
  320.  
  321.  
  322. switch (jour_de_la_semaine)
  323. {
  324. case 0: { date_txt+="Dim ";} break;
  325. case 1: { date_txt+="Lun ";} break;
  326. case 2: { date_txt+="Mar ";} break;
  327. case 3: { date_txt+="Mer ";} break;
  328. case 4: { date_txt+="Jeu ";} break;;
  329. case 5: { date_txt+="Ven ";} break;
  330. case 6: { date_txt+="Sam ";} break;
  331. }
  332.  
  333. date_txt += String(conv_time(jour_local))+" ";
  334.  
  335. switch (mois)
  336. {
  337. case 1: {date_txt+="Janv "; } break;
  338. case 2: {date_txt+="Fev "; } break;
  339. case 3: {date_txt+="Mars "; } break;
  340. case 4: {date_txt+="Avr "; } break;
  341. case 5: {date_txt+="Mai "; } break;
  342. case 6: {date_txt+="Juin "; } break;
  343. case 7: {date_txt+="Juil "; } break;
  344. case 8: {date_txt+="Aout "; } break;
  345. case 9: {date_txt+="Sept "; } break;
  346. case 10: {date_txt+="Oct "; } break;
  347. case 11: {date_txt+="Nov "; } break;
  348. case 12: {date_txt+="Dec "; } break;
  349. }
  350.  
  351. date_txt += "20"; // ce sera le bug de l'an 3000 ;)
  352. date_txt += annee_txt;
  353.  
  354. ECRAN_1.setCursor(0, 100, 4);
  355. ECRAN_1.setTextColor(TFT_CYAN, TFT_BLACK);
  356. ECRAN_1.print(date_txt);
  357.  
  358. }
  359.  
  360.  
  361.  
  362. void affiche_heure()
  363. {
  364. ECRAN_1.setCursor(20, 30, 6);
  365. ECRAN_1.setTextColor(TFT_YELLOW, TFT_BLACK);
  366.  
  367. if(heures<10){ECRAN_1.print(0);}
  368. ECRAN_1.print(heures);
  369. ECRAN_1.print(":");
  370. if(minutes<10){ECRAN_1.print(0);}
  371. ECRAN_1.print(minutes);
  372.  
  373. ECRAN_1.print(" ");
  374. ECRAN_1.setTextColor(TFT_DARKGREY, TFT_BLACK);
  375. if(secondes<10){ECRAN_1.print(0);}
  376. ECRAN_1.print(secondes);
  377. }
  378.  
  379.  
  380. void loop()
  381. {
  382.  
  383. Serial.println(compte1);
  384. // date as ddmmyy, time as hhmmsscc, and age in milliseconds
  385. // void get_datetime(unsigned long *date, unsigned long *time, unsigned long *age = 0);
  386.  
  387. GPS.get_datetime(&date_GPS, &heure_GPS, &age_GPS);
  388. decode_time(date_GPS, heure_GPS);
  389.  
  390. Serial.print("date_GPS: "); Serial.println(date_GPS);
  391. Serial.print("heure_GPS: "); Serial.println(heure_GPS);
  392.  
  393. Serial.print("annee= "); Serial.println(annee);
  394. Serial.print("mois= "); Serial.println(mois);
  395. Serial.print("jour= "); Serial.println(jour);
  396.  
  397. Serial.print("heures= "); Serial.println(heures);
  398. Serial.print("minutes= "); Serial.println(minutes);
  399. Serial.print("secondes= "); Serial.println(secondes);
  400.  
  401. Serial.print("heures_txt= "); Serial.println(heures_txt);
  402. Serial.print("minutes_txt= "); Serial.println(minutes_txt);
  403. Serial.print("secondes_txt= "); Serial.println(secondes_txt);
  404.  
  405.  
  406. uint8_t i=0;
  407.  
  408. for(int n=0; n<2; n++) { heure_date_array[i]=heures_txt[n]; i++; }
  409. heure_date_array[i]=':'; i++;
  410.  
  411. for(int n=0; n<2; n++) { heure_date_array[i]=minutes_txt[n]; i++; }
  412. heure_date_array[i]=':'; i++;
  413.  
  414. for(int n=0; n<2; n++) { heure_date_array[i]=secondes_txt[n]; i++; }
  415. heure_date_array[i]='('; i++;
  416.  
  417. for(int n=0; n<2; n++) { heure_date_array[i]=jour_txt[n]; i++; }
  418. heure_date_array[i]=':'; i++;
  419.  
  420. for(int n=0; n<2; n++) { heure_date_array[i]=mois_txt[n]; i++; }
  421. heure_date_array[i]=':'; i++;
  422.  
  423. for(int n=0; n<2; n++) { heure_date_array[i]=annee_txt[n]; i++; }
  424. heure_date_array[i]=')'; i++;
  425.  
  426.  
  427. heure_date_array[i]=0; // zéro terminal -> chaine C
  428.  
  429. /*
  430.  
  431. heure_array[0]=heures_txt[0];
  432. heure_array[1]=heures_txt[1];
  433. heure_array[2]=':';
  434. heure_array[3]=minutes_txt[0];
  435. heure_array[4]=minutes_txt[1];
  436. heure_array[5]=':';
  437. heure_array[6]=secondes_txt[0];
  438. heure_array[7]=secondes_txt[1];
  439. heure_array[8]=0; // zéro terminal -> string
  440. */
  441.  
  442.  
  443.  
  444. // 31:01:21 pour "31 Janv 2021"
  445.  
  446. date_array[0]=jour_txt[0];
  447. date_array[1]=jour_txt[1];
  448. date_array[2]=':';
  449. date_array[3]=mois_txt[0];
  450. date_array[4]=mois_txt[1];
  451. date_array[5]=':';
  452. date_array[6]=annee_txt[0];
  453. date_array[7]=annee_txt[1];
  454. date_array[8]=0; // zéro terminal -> string
  455.  
  456.  
  457.  
  458. Serial.println();
  459.  
  460. Serial.print("GPS.satellites: "); Serial.println(GPS.satellites());
  461. Serial.println(" ");
  462.  
  463. Serial.println();
  464.  
  465. affiche_heure();
  466. affiche_date();
  467.  
  468. smartdelay(1000);
  469. compte1++;
  470.  
  471. }
  472.  


7 La seconde horloge : Client WiFi

Même carte ESP32 TTGO T-display dans un boîtier moins profond puisque cette fois il n'y a pas de module GPS.

8 La carte ESP dans le boitier

Le boîtier mesure 55x30mm pour une profondeur de 16mm. Voir le dessin 3D pour le logiciel FreeCad au bas de l'article.

9 Vue d'ensemble des éléments :

Il faut préciser que le câble USB n'est absolument pas nécessaire, aucune donnée n'est transmise de cette façon, et on peut tout à fait alimenter la carte par un petit connecteur prévu pour cela (au dos de la carte).

10 Le code source en C++ du Client WiFi :

CODE SOURCE en C++
  1. /***********
  2.   GPS_Time & CLIENT WiFi local (ip=192.168.4.1/)
  3.   pour ESP32 (TTGO T-Display, avec petit ecran LCD couleur IPS ST7789V 1.14 Inch 135x240 px)
  4.  
  5.  
  6. il faut choisir (décommenter) la ligne suivante, dans le fichier /libraries/TFT_eSPI/User_Setup_Select.h :
  7.   #include <User_Setups/Setup25_TTGO_T_Display.h> // Setup file for ESP32 and TTGO T-Display ST7789V SPI bus TFT
  8.  
  9.   voir l'exemple "Colour_Test"
  10.  
  11. ************/
  12. #define Version "2.0"
  13.  
  14. #include <SoftwareSerial.h>
  15. #include <TFT_eSPI.h>
  16. #include <TimeLib.h> // include Arduino time library
  17.  
  18. #include <WiFi.h>
  19. #include <HTTPClient.h>
  20.  
  21.  
  22. const char* ssid = "TPGPS_34";
  23. const char* password = "94r6tkJ31";
  24.  
  25. //IP address with URL path
  26. //const char* srvName_heure = "http://192.168.4.1/heure";
  27. //const char* srvName_date = "http://192.168.4.1/date";
  28. const char* srvName_HR = "http://192.168.4.1/HR";
  29.  
  30. TFT_eSPI ECRAN_1 = TFT_eSPI();
  31.  
  32. //String recp_time = "{}";
  33. //String recp_date = "{}";
  34. String recp_HR = "{}";
  35.  
  36. uint8_t WiFi_status=0;
  37.  
  38. uint32_t memoMillis = 0;
  39. uint32_t currentMillis;
  40. const uint32_t tempo = 2000;
  41.  
  42.  
  43. uint16_t annee;
  44. uint8_t mois;
  45. uint8_t jour;
  46.  
  47. uint8_t annee_in=0;
  48. uint8_t mois_in=0;
  49. uint8_t jour_in=0;
  50.  
  51. uint8_t heures=0;
  52. uint8_t minutes=0;
  53. uint8_t secondes=0;
  54. uint8_t jour_de_la_semaine;
  55.  
  56. uint8_t heures_in=0;
  57. uint8_t minutes_in=0;
  58. uint8_t secondes_in=0;
  59.  
  60. uint16_t compte1=0;
  61. uint16_t compte2=0;
  62.  
  63. String annee_txt;
  64. String mois_txt;
  65. String jour_txt;
  66. String date_txt;
  67.  
  68. String heures_txt;
  69. String minutes_txt;
  70. String secondes_txt;
  71.  
  72.  
  73.  
  74. void setup()
  75. {
  76. Serial.begin(115200);
  77. delay(2000);
  78. Serial.println("ESP CLIENT WiFi");
  79. Serial.println("Setup");
  80.  
  81. ECRAN_1.init();
  82. ECRAN_1.setRotation(1);
  83. ECRAN_1.fillScreen(TFT_BLACK);
  84. ECRAN_1.setCursor(0, 0, 2); // Set "cursor" at top left corner of display (0,0) and select font 4
  85. ECRAN_1.setTextColor(TFT_WHITE, TFT_BLACK);
  86. ECRAN_1.println("Horloge Client WiFi");
  87.  
  88. delay(500);
  89.  
  90. ECRAN_1.setCursor(0, 20, 4);
  91. ECRAN_1.setTextColor(TFT_YELLOW, TFT_BLACK);
  92. ECRAN_1.print("Connexion :");
  93.  
  94. WiFi.persistent(false);
  95. WiFi.begin(ssid, password);
  96. delay(2000);
  97.  
  98.  
  99.  
  100. Serial.println("Connecting");
  101.  
  102. ECRAN_1.setTextColor(TFT_BLUE, TFT_BLACK);
  103. //ECRAN_1.setTextFont(1);
  104. ECRAN_1.setCursor(0, 40, 1);
  105. while(WiFi.status() != WL_CONNECTED)
  106. {
  107. delay(500);
  108. ECRAN_1.print(".");
  109. Serial.print(".");
  110. }
  111.  
  112. Serial.println("");
  113. Serial.print("Connected to WiFi - IP Address : ");
  114. Serial.println(WiFi.localIP());
  115. Serial.println("\n");
  116.  
  117. ECRAN_1.setTextColor(TFT_GREEN, TFT_BLACK);
  118. ECRAN_1.println(" ");
  119. ECRAN_1.println("OK");
  120. smartdelay(500);
  121. ECRAN_1.fillScreen(TFT_BLACK);
  122.  
  123. ECRAN_1.setCursor(130, 0, 2); // Set "cursor" at top left corner of display (0,0) and select font 4
  124. ECRAN_1.setTextColor(TFT_BLUE, TFT_BLACK);
  125. ECRAN_1.println("Client WiFi");
  126.  
  127. delay(500);
  128.  
  129. compte1=8;
  130. affiche_date();
  131. }
  132.  
  133.  
  134.  
  135. void httpGetHeureDate()
  136. {
  137. Serial.println("envoi req HR");
  138.  
  139. HTTPClient http2;
  140.  
  141. http2.begin(srvName_HR);
  142.  
  143. int httpResponseCode = http2.GET();
  144.  
  145. if (httpResponseCode>0)
  146. {
  147. recp_HR = http2.getString();
  148. }
  149. http2.end();
  150.  
  151. Serial.println("reponse: ");
  152. Serial.println(recp_HR);
  153.  
  154. }
  155.  
  156.  
  157. static void smartdelay(unsigned long ms)
  158. {
  159. unsigned long start = millis();
  160. while (millis() - start < ms) {;}
  161. }
  162.  
  163.  
  164.  
  165. void ajuste_HR()
  166. {
  167.  
  168. Serial.print("length="); Serial.println(recp_HR.length());
  169. if(recp_HR.length() == 18)
  170. {
  171.  
  172. WiFi_status =1;
  173. Serial.println("ajuste_HR");
  174. Serial.println( recp_HR);
  175.  
  176. // String data_in = "11:40:30"
  177.  
  178.  
  179. heures_in =(recp_HR.substring(0,2)).toInt();
  180. Serial.println(heures_in);
  181.  
  182. minutes_in =(recp_HR.substring(3,5)).toInt();
  183. Serial.println(minutes_in);
  184.  
  185. secondes_in =(recp_HR.substring(6,8)).toInt();
  186. Serial.println(secondes_in);
  187. //secondes_in++; // pour compenser le temps de traitement
  188.  
  189. if (heures != heures_in) {heures = heures_in;}
  190. if (minutes != minutes_in) {minutes = minutes_in;}
  191. if (secondes != secondes_in) {secondes = secondes_in;}
  192.  
  193.  
  194. jour_in =(recp_HR.substring(9,11)).toInt();
  195. Serial.println(jour_in);
  196.  
  197. mois_in =(recp_HR.substring(12,14)).toInt();
  198. Serial.println(mois_in);
  199.  
  200. annee_in =(recp_HR.substring(15,17)).toInt();
  201. Serial.println(annee_in);
  202.  
  203.  
  204. if (jour != jour_in) {jour = jour_in;}
  205. if (mois != mois_in) {mois = mois_in;}
  206. if (annee != annee_in) {annee = annee_in;}
  207.  
  208. }
  209. else {WiFi_status=0;}
  210.  
  211. }
  212.  
  213.  
  214. void calcul_jour_de_la_semaine()
  215. {
  216. // d'après l'Algorithme de Mike Keith
  217. uint16_t d, m, y, z, jds;
  218.  
  219. d=jour;
  220. m=mois;
  221. y=annee;
  222.  
  223. if (m>=3)
  224. {
  225. jds = ( ((23*m)/9) + d + 4 + y + (y/4) - (y/100) + (y/400) - 2 ) % 7;
  226. }
  227. else
  228. {
  229. z = y-1;
  230. jds = ( ((23*m)/9) + d + 4 + y + (z/4) - (z/100) + (z/400) ) % 7;
  231. }
  232. jour_de_la_semaine = jds;
  233. }
  234.  
  235.  
  236. String conv_time(uint8_t t)
  237. {
  238. String r;
  239. r=String(t);
  240. if (t<10) {r="0"+r;}
  241. return r;
  242. }
  243.  
  244.  
  245.  
  246. void affiche_date()
  247. {
  248. Serial.println("affiche_date");
  249. date_txt="";
  250.  
  251. calcul_jour_de_la_semaine();
  252.  
  253. switch (jour_de_la_semaine)
  254. {
  255. case 0: { date_txt+="Dim ";} break;
  256. case 1: { date_txt+="Lun ";} break;
  257. case 2: { date_txt+="Mar ";} break;
  258. case 3: { date_txt+="Mer ";} break;
  259. case 4: { date_txt+="Jeu ";} break;;
  260. case 5: { date_txt+="Ven ";} break;
  261. case 6: { date_txt+="Sam ";} break;
  262. }
  263. date_txt+=" ";
  264. date_txt += String(conv_time(jour));
  265. date_txt+=" ";
  266.  
  267. switch (mois)
  268. {
  269. case 1: {date_txt+="Janv "; } break;
  270. case 2: {date_txt+="Fev "; } break;
  271. case 3: {date_txt+="Mars "; } break;
  272. case 4: {date_txt+="Avr "; } break;
  273. case 5: {date_txt+="Mai "; } break;
  274. case 6: {date_txt+="Juin "; } break;
  275. case 7: {date_txt+="Juil "; } break;
  276. case 8: {date_txt+="Aout "; } break;
  277. case 9: {date_txt+="Sept "; } break;
  278. case 10: {date_txt+="Oct "; } break;
  279. case 11: {date_txt+="Nov "; } break;
  280. case 12: {date_txt+="Dec "; } break;
  281. }
  282.  
  283. date_txt+=" ";
  284. Serial.print("date_txt="); Serial.println(date_txt);
  285. //if (annee_in >0) // pour éviter d'afficher une date fantaisiste au départ
  286. {
  287. uint16_t annee_in2 = annee_in + 2000;
  288. annee_txt = (String)annee_in2;
  289. Serial.print("annee_txt="); Serial.println(annee_txt);
  290.  
  291.  
  292. date_txt += annee_txt;
  293.  
  294. ECRAN_1.setCursor(0, 100, 4);
  295. ECRAN_1.setTextColor(TFT_CYAN, TFT_BLACK);
  296. ECRAN_1.print(date_txt);
  297. }
  298.  
  299. }
  300.  
  301.  
  302.  
  303. void affiche_heure()
  304. {
  305. Serial.println("affiche_heure");
  306. ECRAN_1.setCursor(20, 30, 6);
  307. ECRAN_1.setTextColor(TFT_YELLOW, TFT_BLACK);
  308.  
  309. if(heures<10){ECRAN_1.print(0);}
  310. ECRAN_1.print(heures);
  311. ECRAN_1.print(":");
  312. if(minutes<10){ECRAN_1.print(0);}
  313. ECRAN_1.print(minutes);
  314.  
  315. ECRAN_1.print(" ");
  316. ECRAN_1.setTextColor(TFT_DARKGREY, TFT_BLACK);
  317. if(secondes<10){ECRAN_1.print(0);}
  318. ECRAN_1.print(secondes);
  319.  
  320. if(WiFi_status == 1) {ECRAN_1.fillCircle(233, 5, 5,TFT_GREEN );} else {ECRAN_1.fillCircle(233, 5, 5,TFT_RED );}
  321. }
  322.  
  323.  
  324.  
  325. void incremente_heure(uint8_t nb_s)
  326. {
  327. for (uint8_t n=0; n<nb_s; n++)
  328. {
  329. if (secondes < 59) {secondes+=1;}
  330. else
  331. {
  332. secondes=0;
  333. if (minutes < 59) {minutes+=1;}
  334. else
  335. {
  336. minutes=0;
  337. if (heures < 23) {heures+=1;}
  338. else
  339. heures=0;
  340. }
  341. }
  342. }
  343. }
  344.  
  345.  
  346.  
  347. void loop()
  348. {
  349. compte1++;
  350. Serial.println(compte1);
  351.  
  352. if ((compte1 % 10)==0) // toutes les 10s
  353. {
  354. compte2++;
  355.  
  356. recp_HR = "{}";
  357. if(WiFi.status()== WL_CONNECTED )
  358. {
  359. httpGetHeureDate();
  360. ajuste_HR();
  361. Serial.println("------");
  362. }
  363. else { Serial.println("WiFi Disconnected"); }
  364.  
  365. }
  366.  
  367. incremente_heure(1); // 1s
  368. smartdelay(1000);
  369.  
  370. affiche_heure();
  371.  
  372. affiche_date();
  373.  
  374.  
  375. }
  376.  


11 Documents :

Includes : fichiers boitier 3D : :

12 Client avec Ecran TFT 3,5

Afin d'améliorer l’esthétique de l'horloge cliente, j'en ai crée une basée sur un ESP32 classique connecté à un afficheur TFT couleur 3,5". Le schéma, la circuiterie et donc le circuit imprimé sont identiques à ceux utilisé pour le "Primary Flight Display" et autre "Navigation Display" décrits sur ce site (avec les boutons en moins).

13 L'horloge 3,5

14 Schéma de l'horloge cliente GPS 3,5

Comme dit précédemment, il faut juste supprimer les Switches qui sont absents de cette réalisation. Il n'y a donc que deux composants: la platine ESP32 et l'afficheur 3,5" 480x320px.

Cette horloge étant un client Wifi, elle ne fonctionne que si le serveur décrit plus haut est en marche.

15 Le code source de l'horloge 3,5

CODE SOURCE en C++
  1. /*
  2. Horloge_TFT ()
  3.  
  4. pour ESP32 Wroom + afficheur 3.5" TFT 480x320
  5.  
  6. par Silicium628
  7.  
  8. */
  9.  
  10.  
  11. /*=====================================================================================================
  12. CONCERNANT L'AFFICHAGE TFT: connexion:
  13.  
  14. (Pensez à configurer le fichier User_Setup.h de la bibliothèque ~/Arduino/libraries/TFT_eSPI/ )
  15.  
  16. les lignes qui suivent ne sont qu'un commentaire pour vous indiquer la config à utiliser
  17. placée ici, elle ne sont pas fonctionnelles
  18. Il FAUT modifier le fichier User_Setup.h installé par le système Arduino dans ~/Arduino/libraries/TFT_eSPI/
  19.  
  20. // ESP32 pins used for the parallel interface TFT
  21. #define TFT_CS 27 // Chip select control pin
  22. #define TFT_DC 14 // Data Command control pin - must use a pin in the range 0-31
  23. #define TFT_RST 26 // Reset pin
  24.  
  25. #define TFT_WR 12 // Write strobe control pin - must use a pin in the range 0-31
  26. #define TFT_RD 13
  27.  
  28. #define TFT_D0 16 // Must use pins in the range 0-31 for the data bus
  29. #define TFT_D1 4 // so a single register write sets/clears all bits
  30. #define TFT_D2 2 // 23
  31. #define TFT_D3 22
  32. #define TFT_D4 21
  33. #define TFT_D5 15 // 19
  34. #define TFT_D6 25 // 18
  35. #define TFT_D7 17
  36. =====================================================================================================*/
  37.  
  38.  
  39. String version="3.1";
  40.  
  41. uint8_t fond_blanc = 0;
  42.  
  43.  
  44. #include <stdint.h>
  45. #include <TFT_eSPI.h> // Hardware-specific library
  46. #include "Free_Fonts.h"
  47.  
  48. #include "FS.h"
  49. #include "SD.h"
  50.  
  51. TFT_eSPI TFT480 = TFT_eSPI(); // Configurer le fichier User_Setup.h de la bibliothèque TFT480_eSPI au préalable
  52.  
  53. #include <WiFi.h>
  54. #include <HTTPClient.h>
  55.  
  56. const char* ssid = "TPGPS_34";
  57. const char* password = "94r6tkJ31";
  58.  
  59. //IP address with URL path
  60. const char* srvName_HR = "http://192.168.4.1/HR";
  61.  
  62. String recp_HR = "{}";
  63.  
  64. uint8_t WiFi_status=0;
  65.  
  66. uint32_t memoMillis = 0;
  67. uint32_t currentMillis;
  68. const uint32_t tempo = 2000;
  69.  
  70.  
  71. uint16_t annee;
  72. uint8_t mois;
  73. uint8_t jour;
  74.  
  75. uint8_t annee_in=0;
  76. uint8_t mois_in=0;
  77. uint8_t jour_in=0;
  78.  
  79. uint8_t heures=0;
  80. uint8_t memo_heures;
  81. uint8_t minutes=0;
  82. uint8_t memo_minutes=0;
  83. uint8_t secondes=0;
  84. uint8_t jour_de_la_semaine;
  85.  
  86. uint8_t heures_in=0;
  87. uint8_t minutes_in=0;
  88. uint8_t secondes_in=0;
  89.  
  90. uint16_t compte1=0;
  91. //uint16_t compte2=0;
  92.  
  93. String annee_txt;
  94. String mois_txt;
  95. String jour_txt;
  96. String date_txt;
  97. String memo_date_txt="---";
  98.  
  99. uint8_t SDcardOk=0;
  100.  
  101. uint8_t num_image;
  102.  
  103.  
  104. //uRTCLib rtc(0x68); // MODULE HORLOGE TEMPS REEL; 0x68 = adresse sur le bus I2C
  105.  
  106.  
  107. //const int led2 = 12;
  108. //bool led2_etat = LOW;
  109.  
  110. /*
  111. const int GPIO_bouton0 = 13; // Bouton interne
  112. bool bouton0_etat;
  113. bool memo_bouton0_etat;
  114.  
  115. const int GPIO_bouton1 = 14; // bouton externe
  116. bool bouton1_etat;
  117. bool memo_bouton1_etat;
  118.  
  119. const int GPIO_bouton2 = 27; // bouton externe
  120. bool bouton2_etat;
  121. bool memo_bouton2_etat;
  122. */
  123.  
  124. uint32_t memoM1 = 0;
  125. uint32_t memoM2 = 0;
  126. const uint32_t tempo1 = 2000; // 2000 ms = 2s
  127. const uint32_t tempo2 = 300*1000; // 300s = 5mn
  128.  
  129. uint8_t stop_affichage =0;
  130.  
  131.  
  132. // #define TIME_TO_SLEEP 1000 /* Time ESP32 will go to sleep (en ms) */
  133. // uint8_t sleep_enable = 1;
  134.  
  135.  
  136. #define NOIR 0x0000
  137. #define MARRON 0x9240
  138. #define ROUGE 0xF800
  139. #define ROSE 0xFBDD
  140. #define ORANGE 0xFBC0
  141. #define JAUNE 0xFFE0
  142. #define JAUNE_PALE 0xF7F4
  143. #define VERT 0x07E0
  144. #define VERT_FONCE 0x02E2
  145. #define OLIVE 0x05A3
  146. #define CYAN 0x07FF
  147. #define BLEU_CLAIR 0x455F
  148. #define AZUR 0x1BF9
  149. #define BLEU 0x001F
  150. #define MAGENTA 0xF81F
  151. #define VIOLET1 0x781A
  152. #define VIOLET2 0xECBE
  153. #define GRIS_TRES_CLAIR 0xDEFB
  154. #define GRIS_CLAIR 0xA534
  155. #define GRIS 0x8410
  156. #define GRIS_FONCE 0x5ACB
  157. #define GRIS_TRES_FONCE 0x2124
  158. #define BLANC 0xFFFF
  159.  
  160. //int startX = 40, startY = 10;
  161.  
  162.  
  163. uint16_t couleur_txt = BLANC;
  164. uint16_t couleur_fond = GRIS_TRES_FONCE;
  165. uint16_t couleur_fond_txt = VERT_FONCE;
  166.  
  167.  
  168. uint32_t bmp_offset = 0;
  169.  
  170.  
  171. static void smartdelay(unsigned long ms)
  172. {
  173. unsigned long start = millis();
  174. while (millis() - start < ms) {;}
  175. }
  176.  
  177.  
  178. void init_SDcard()
  179. {
  180. String s1;
  181.  
  182. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  183. TFT480.setTextColor(BLANC, NOIR);
  184. TFT480.setFreeFont(FF1);
  185.  
  186. uint16_t y=0;
  187.  
  188. TFT480.drawString("ND - Navigation Display", 0, y);
  189. y+=20;
  190.  
  191. s1="version " + version;
  192. TFT480.drawString(s1, 0, y);
  193.  
  194. y+=40;
  195. TFT480.setTextColor(VERT, NOIR);
  196. TFT480.drawString("Init SDcard", 0, y);
  197. y+=20;
  198.  
  199. if(!SD.begin())
  200. {
  201. TFT480.drawString("Card Mount Failed", 0, y);
  202. delay (2000);
  203. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  204. return;
  205. }
  206.  
  207.  
  208. uint8_t cardType = SD.cardType();
  209.  
  210. if(cardType == CARD_NONE)
  211. {
  212. TFT480.drawString("No SDcard", 0, y);
  213. delay (2000);
  214. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  215. return;
  216. }
  217.  
  218. SDcardOk=1;
  219.  
  220. TFT480.drawString("SDcard Type: ", 0, y);
  221. if(cardType == CARD_SD) {TFT480.drawString("SDSC", 150, y);}
  222. else if(cardType == CARD_SDHC) {TFT480.drawString("SDHC", 150, y);}
  223.  
  224. y+=20;
  225.  
  226. uint32_t cardSize = SD.cardSize() / (1024 * 1024);
  227. s1=(String)cardSize + " GB";
  228. TFT480.drawString("SDcard size: ", 0, y);
  229. TFT480.drawString(s1, 150, y);
  230.  
  231. // listDir(SD, "/", 0);
  232.  
  233. //Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  234. //Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
  235.  
  236. delay (1000);
  237. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  238. }
  239.  
  240.  
  241. void httpGetHeureDate()
  242. {
  243. // Serial.println("envoi req HR");
  244. TFT480.fillCircle(470, 5, 5,BLEU );
  245. delay(200);
  246.  
  247. HTTPClient http2;
  248.  
  249. http2.begin(srvName_HR);
  250.  
  251. int httpResponseCode = http2.GET();
  252.  
  253. if (httpResponseCode>0)
  254. {
  255. recp_HR = http2.getString();
  256. }
  257. http2.end();
  258.  
  259. TFT480.fillCircle(470, 5, 5,VERT );
  260.  
  261. // Serial.println("reponse: ");
  262. // Serial.println(recp_HR);
  263.  
  264. }
  265.  
  266.  
  267.  
  268. void ajuste_HR()
  269. {
  270.  
  271. // Serial.print("length="); Serial.println(recp_HR.length());
  272. if(recp_HR.length() == 18)
  273. {
  274.  
  275. WiFi_status =1;
  276. // Serial.println("ajuste_HR");
  277. // Serial.println( recp_HR);
  278.  
  279. // String data_in = "11:40:30"
  280.  
  281.  
  282. heures_in =(recp_HR.substring(0,2)).toInt();
  283. // Serial.println(heures_in);
  284.  
  285. minutes_in =(recp_HR.substring(3,5)).toInt();
  286. // Serial.println(minutes_in);
  287.  
  288. secondes_in =(recp_HR.substring(6,8)).toInt();
  289. // Serial.println(secondes_in);
  290. //secondes_in++; // pour compenser le temps de traitement
  291.  
  292. if (heures != heures_in) {heures = heures_in;}
  293. if (minutes != minutes_in) {minutes = minutes_in;}
  294. if (secondes != secondes_in) {secondes = secondes_in;}
  295.  
  296.  
  297. jour_in =(recp_HR.substring(9,11)).toInt();
  298. // Serial.println(jour_in);
  299.  
  300. mois_in =(recp_HR.substring(12,14)).toInt();
  301. // Serial.println(mois_in);
  302.  
  303. annee_in =(recp_HR.substring(15,17)).toInt();
  304. // Serial.println(annee_in);
  305.  
  306.  
  307. if (jour != jour_in) {jour = jour_in;}
  308. if (mois != mois_in) {mois = mois_in;}
  309. if (annee != annee_in) {annee = annee_in;}
  310.  
  311. }
  312. else {WiFi_status=0;}
  313.  
  314. }
  315.  
  316.  
  317.  
  318.  
  319. void calcul_jour_de_la_semaine()
  320. {
  321. // d'après l'Algorithme de Mike Keith
  322. uint16_t d, m, y, z, jds;
  323.  
  324. d=jour;
  325. m=mois;
  326. y=annee;
  327.  
  328. if (m>=3)
  329. {
  330. jds = ( ((23*m)/9) + d + 4 + y + (y/4) - (y/100) + (y/400) - 2 ) % 7;
  331. }
  332. else
  333. {
  334. z = y-1;
  335. jds = ( ((23*m)/9) + d + 4 + y + (z/4) - (z/100) + (z/400) ) % 7;
  336. }
  337. jour_de_la_semaine = jds;
  338. }
  339.  
  340. String conv_time(uint8_t t)
  341. {
  342. String r;
  343. r=String(t);
  344. if (t<10) {r="0"+r;}
  345. return r;
  346. }
  347.  
  348.  
  349.  
  350. void affiche_date()
  351. {
  352. date_txt="";
  353.  
  354. calcul_jour_de_la_semaine();
  355.  
  356. switch (jour_de_la_semaine)
  357. {
  358. case 0: { date_txt+="Dim ";} break;
  359. case 1: { date_txt+="Lun ";} break;
  360. case 2: { date_txt+="Mar ";} break;
  361. case 3: { date_txt+="Mer ";} break;
  362. case 4: { date_txt+="Jeu ";} break;;
  363. case 5: { date_txt+="Ven ";} break;
  364. case 6: { date_txt+="Sam ";} break;
  365. }
  366.  
  367. date_txt += String(conv_time(jour))+" ";
  368.  
  369. switch (mois)
  370. {
  371. case 1: {date_txt+="Janv "; } break;
  372. case 2: {date_txt+="Fev "; } break;
  373. case 3: {date_txt+="Mars "; } break;
  374. case 4: {date_txt+="Avr "; } break;
  375. case 5: {date_txt+="Mai "; } break;
  376. case 6: {date_txt+="Juin "; } break;
  377. case 7: {date_txt+="Juil "; } break;
  378. case 8: {date_txt+="Aout "; } break;
  379. case 9: {date_txt+="Sept "; } break;
  380. case 10: {date_txt+="Oct "; } break;
  381. case 11: {date_txt+="Nov "; } break;
  382. case 12: {date_txt+="Dec "; } break;
  383. }
  384.  
  385. if (annee_in >0) // pour éviter d'afficher une date fantaisiste au départ
  386. {
  387. uint16_t annee_in2 = annee_in + 2000;
  388. annee_txt = (String)annee_in2;
  389. // Serial.print("annee_txt="); Serial.println(annee_txt);
  390.  
  391. date_txt += annee_txt;
  392.  
  393. //memo_date_txt = date_txt;
  394. TFT480.setTextColor(JAUNE, NOIR);
  395. TFT480.setFreeFont(FF6);
  396. TFT480.setTextSize(1);
  397.  
  398. TFT480.drawString(date_txt,0,300);
  399.  
  400. }
  401.  
  402. }
  403.  
  404.  
  405.  
  406. void affiche_heure()
  407. {
  408. String s1;
  409.  
  410. if (memo_minutes != minutes)
  411. {
  412. memo_minutes = minutes;
  413.  
  414. //TFT480.fillRect(0, 50, 479, 125, couleur_fond_txt);
  415. num_image = random(1, 23);
  416. affi_img(0, 0, num_image);
  417.  
  418. s1="";
  419. if(heures<10){s1+="0";}
  420. s1 += String(heures);
  421. s1 += ":";
  422. if(minutes<10){s1+="0";}
  423. s1 += String(minutes);
  424.  
  425. TFT480.setTextColor(couleur_txt, couleur_fond_txt);
  426.  
  427.  
  428. //TFT480.setFreeFont(FF24);
  429. TFT480.setFreeFont(FF8);
  430. TFT480.setTextSize(3);
  431.  
  432. TFT480.setTextColor(NOIR);
  433. TFT480.drawString(s1, 20+5, 50+5);
  434. TFT480.setTextColor(couleur_txt);
  435. TFT480.drawString(s1, 20, 50);
  436.  
  437. affiche_date();
  438. }
  439.  
  440. TFT480.setTextColor(couleur_txt, couleur_fond_txt);
  441.  
  442. TFT480.setFreeFont(FF7);
  443. TFT480.setTextSize(1);
  444. s1="";
  445. if(secondes<10){s1+="0";}
  446. s1 += String(secondes);
  447.  
  448. TFT480.fillRect(400, 200, 40, 24, couleur_fond_txt);
  449. TFT480.drawString(s1, 400, 200);
  450.  
  451. if(WiFi_status == 1) { TFT480.fillCircle(470, 5, 5,VERT );} else { TFT480.fillCircle(470, 5, 5,ROUGE );}
  452.  
  453. }
  454.  
  455.  
  456. uint8_t decToBcd( int val )
  457. {
  458. return (uint8_t) ((val / 10 * 16) + (val % 10));
  459. }
  460.  
  461.  
  462. uint16_t bmp_width;
  463. uint16_t bmp_heigh;
  464.  
  465.  
  466. uint16_t Color_To_565(uint8_t r, uint8_t g, uint8_t b)
  467. {
  468. return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
  469. }
  470.  
  471.  
  472. void RGB565_to_888(uint16_t color565, uint8_t *R, uint8_t *G, uint8_t *B)
  473. {
  474. *R=(color565 & 0xFFFFF800) >> 8;
  475. *G=(color565 & 0x7E0) >> 3;
  476. *B=(color565 & 0x1F) << 3 ;
  477. }
  478.  
  479.  
  480.  
  481.  
  482. uint16_t read_16(File fp)
  483. {
  484. uint8_t low;
  485. uint16_t high;
  486. low = fp.read();
  487. high = fp.read();
  488. return (high<<8)|low;
  489. }
  490.  
  491.  
  492.  
  493. uint32_t read_32(File fp)
  494. {
  495. uint16_t low;
  496. uint32_t high;
  497. low = read_16(fp);
  498. high = read_16(fp);
  499. return (high<<8)|low;
  500. }
  501.  
  502.  
  503.  
  504. void write_16(uint16_t v16, File fp)
  505. {
  506. uint8_t low, high;
  507.  
  508. low = v16 & 0xFF;
  509. high= v16 >>8;
  510.  
  511. fp.write(low);
  512. fp.write(high);
  513. }
  514.  
  515.  
  516. void write_32(uint32_t v32, File fp)
  517. {
  518. uint16_t low, high;
  519.  
  520. low = v32 & 0xFFFF;
  521. high= v32 >>16;
  522.  
  523. write_16(low, fp);
  524. write_16(high, fp);
  525. }
  526.  
  527.  
  528.  
  529.  
  530. bool teste_bmp_header(File fp)
  531. {
  532. if(read_16(fp) != 0x4D42) { return false; } // (2 bytes) The header field used to identify the BMP
  533. read_32(fp); // (4 bytes) get bmp size (nombre total d'octets)
  534. read_32(fp); // (4 bytes) get creator information
  535. bmp_offset = read_32(fp); // (4 bytes) get offset information
  536. read_32(fp);//get DIB information
  537.  
  538. // ici on a lu 16 octets
  539. bmp_width = read_32(fp); //(4 bytes) get width and heigh information
  540. bmp_heigh = read_32(fp); //(4 bytes)
  541.  
  542. // ici on a lu 24 octets
  543. //if(read_16(fp)!= 1) {return false;}
  544. read_16(fp);
  545. //if(read_32(fp)!= 0) {return false;}
  546. return true;
  547. }
  548.  
  549.  
  550. uint8_t LumCtr(uint8_t vi, float lum, float ctr)
  551. {
  552. float v2;
  553. uint8_t result;
  554. v2 = ((float)vi - lum) * ctr;
  555. if (v2<0) {v2=0;}
  556. if (v2>255) {v2=255;}
  557. result = (uint8_t) v2;
  558. return result;
  559. }
  560.  
  561.  
  562. void draw_bmp(uint16_t x0, uint16_t y0, File* fp)
  563. {
  564.  
  565. //sram = freeRam(); Serial.print("03-freeRam="); Serial.println(sram);
  566. uint16_t i,j,k,p,m=0;
  567. uint16_t y1;
  568. uint8_t bmp_data[2*3]={0};
  569. uint16_t bmp_color[2];
  570. uint8_t rot =1;
  571.  
  572. fp->seek(bmp_offset);
  573. for(i=0; i<bmp_heigh; i++)
  574. {
  575. for(j=0; j<(bmp_width/2); j++)
  576. {
  577. m=0;
  578. fp->read(bmp_data,2*3);
  579. for(k=0; k<2; k++)
  580. {
  581. bmp_color[k]= Color_To_565(bmp_data[m+2], bmp_data[m+1], bmp_data[m+0]);
  582. m+=3;
  583. }
  584. for(p=0; p<2; p++)
  585. {
  586. if (rot==0)
  587. {
  588. y1=y0;
  589. TFT480.drawPixel(x0+i, y0+j*2+p, bmp_color[p]);
  590. }
  591. if (rot==1)
  592. {
  593. //y1=160-y0;
  594. y1=y0;
  595. TFT480.drawPixel(x0+j*2+p,320-(y1+i), bmp_color[p]);
  596. }
  597. }
  598. }
  599. }
  600. }
  601.  
  602.  
  603. void affi_img(uint16_t x0, uint16_t y0, uint8_t num)
  604. {
  605.  
  606. File bmp_file;
  607. TFT480.setFreeFont(FF1);
  608. TFT480.setTextColor(ORANGE, NOIR);
  609. String numtxt = String(num);
  610. String filename;
  611. filename ="/bmp/";
  612. filename += numtxt;
  613. filename += ".bmp";
  614.  
  615. //bmp_file = SD.open("/bmp/1.bmp");
  616. bmp_file = SD.open(filename);
  617. if(!bmp_file)
  618. {
  619. //efface_carte(x0,y0);
  620. bmp_file.close();
  621. // Serial.println("ici1");
  622. return;
  623. }
  624. if(!teste_bmp_header(bmp_file))
  625. {
  626. bmp_file.close();
  627. // Serial.println("ici2");
  628. return;
  629. }
  630.  
  631. // Serial.println("ici3");
  632.  
  633. draw_bmp(x0, y0, &bmp_file);
  634.  
  635. bmp_file.close();
  636. // delay(1000);
  637.  
  638. }
  639.  
  640.  
  641.  
  642.  
  643.  
  644. void setup()
  645. {
  646. //esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000);
  647. //esp_sleep_enable_ext0_wakeup((gpio_num_t)BUTTON_PIN, LOW);
  648.  
  649. // Serial.begin(19200);
  650.  
  651. num_image = random(1, 23);
  652.  
  653. init_SDcard();
  654.  
  655. // Serial.println("display.init()");
  656.  
  657. TFT480.init();
  658. TFT480.setRotation(3); // 0..3 à voir, suivant disposition de l'afficheur et sa disposition
  659.  
  660. TFT480.fillScreen(NOIR);
  661.  
  662. TFT480.setTextColor(BLANC, NOIR);
  663.  
  664. TFT480.setFreeFont(FF1);
  665. uint16_t y=0;
  666. TFT480.drawString("Horloge TFT", 0, y);
  667. y+=20;
  668. String s1="version " + version;
  669. TFT480.drawString(s1, 0, y);
  670. y+=20;
  671. TFT480.setTextColor(BLEU, NOIR);
  672. TFT480.drawString("Heure GPS", 0, y);
  673. y+=20;
  674. TFT480.setTextColor(JAUNE, NOIR);
  675. TFT480.drawString("Client WiFi", 0, y);
  676. y+=40;
  677.  
  678.  
  679. /*
  680. TFT480.setFreeFont(FF24);
  681. TFT480.setTextSize(4);
  682. uint16_t y=0;
  683. TFT480.drawString("12:34", 0, y);
  684. */
  685. delay (2000);
  686.  
  687. if(fond_blanc == 1) {TFT480.fillScreen(BLANC);} else {TFT480.fillScreen(NOIR);}
  688. TFT480.setCursor(0, 20, 4);
  689. TFT480.setTextColor(JAUNE, NOIR);
  690. TFT480.print("Connexion au serveur GPS WiFi:");
  691.  
  692. WiFi.persistent(false);
  693. WiFi.begin(ssid, password);
  694.  
  695. delay(1000);
  696.  
  697. // Serial.println("Connecting");
  698.  
  699. TFT480.setTextColor(BLEU, NOIR);
  700. //TFT480.setTextFont(1);
  701. TFT480.setCursor(0, 40, 1);
  702. while(WiFi.status() != WL_CONNECTED)
  703. {
  704. delay(500);
  705. TFT480.print(".");
  706. // Serial.print(".");
  707. }
  708.  
  709. // Serial.println("");
  710. // Serial.print("Connected to WiFi - IP Address : ");
  711. // Serial.println(WiFi.localIP());
  712. // Serial.println("\n");
  713.  
  714. TFT480.setTextColor(VERT, NOIR);
  715. TFT480.println(" ");
  716. TFT480.println("OK");
  717. smartdelay(500);
  718. TFT480.fillScreen(NOIR);
  719.  
  720. TFT480.setCursor(130, 0, 2); // Set "cursor" at top left corner of display (0,0) and select font 4
  721. TFT480.setTextColor(TFT_BLUE, TFT_BLACK);
  722. TFT480.println("Client WiFi");
  723.  
  724. delay(500);
  725.  
  726. TFT480.fillScreen(couleur_fond);
  727.  
  728.  
  729. TFT480.setTextColor(CYAN, couleur_fond);
  730. TFT480.setFreeFont(FF5);
  731. TFT480.setTextSize(1);
  732.  
  733. TFT480.drawString("GPS Time",0,0);
  734.  
  735. //affi_img(0, 0, num_image);
  736.  
  737. //affiche_date();
  738.  
  739. compte1=9;
  740.  
  741. httpGetHeureDate();
  742. ajuste_HR();
  743. affiche_heure();
  744. }
  745.  
  746.  
  747.  
  748. void incremente_heure(uint8_t nb_s)
  749. {
  750. for (uint8_t n=0; n<nb_s; n++)
  751. {
  752. if (secondes < 59) {secondes+=1;}
  753. else
  754. {
  755. secondes=0;
  756. if (minutes < 59) {minutes+=1;}
  757. else
  758. {
  759. minutes=0;
  760. if (heures < 23) {heures+=1;}
  761. else
  762. heures=0;
  763. }
  764. }
  765. }
  766. }
  767.  
  768.  
  769. void loop()
  770. {
  771. compte1++;
  772. // Serial.println(compte1);
  773.  
  774. if ((compte1 % 20)==0) // toutes les 20s
  775. {
  776. recp_HR = "{}";
  777. if(WiFi.status()== WL_CONNECTED )
  778. {
  779. httpGetHeureDate();
  780. ajuste_HR();
  781. }
  782.  
  783. }
  784.  
  785. incremente_heure(1); // +1s
  786. smartdelay(1000);
  787.  
  788. affiche_heure();
  789.  
  790. }
  791.  
  792.  
  793.  


16 Documents

Voici le code source complet ainsi que les images de fond d'écran à placer dans un dossier nommé "bmp" lui-même placé à la racine de la SDcard connectée directement au dos de l'afficheur. L'image affichée change aléatoirement toutes les minutes. Vous pouvez utiliser vos propres images, elles doivent être au format bpm 480x320px.

17 -

Liens...

4621