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


7 Autre version du serveur : ESP32 +antenne, sans afficheur

J'ai créé cette seconde version afin de bénéficier d'une portée WiFi plus grande ce qui me permet de recevoir le signal horaire jusque dans la cave !
Je n'ai pas trouvé utile cette fois d'y adjoindre un afficheur TFT, mais cela est tout à fait possible.

Vous trouverez le code source au paragraphe "Documents" au bas de cet article.

8 Photos de la version à antenne WiFi

Le tout tient dans un petit boîtier en PLA (la carte ESP dans la boite, le module récepteur GPS NEO-6M dans le couvercle) fabriqué avec une imprimante 3D.

9 Le schéma du serveur version ESP + antenne WiFi

-

Attention :
  • le pin TX du récepteur GPS est relié au pin 33 (GPIO33) de la carte ESP32 et non au "pin RX" de la-dite carte.
  • le pin RX du récepteur GPS est relié au pin 32 (GPIO32) de la carte ESP32 et non au "pin TX" de la-dite carte.
Dans une liaison série, il est correct que le RX de l'un soit relié au TX de l'autre, et vice-versa. Ceci dit, les pins sérigraphié 'RX et "TX" sur la carte ESP32 ne conviennent pas, pour la raison qu'ils sont déjà utilisés en interne pour sa liaison USB. Ce qui m'a d’ailleurs fait perdre quelques heures de travail ! Bah, je suis moins bête qu'avant !

L'alimentation du module GPS est relié à la sortie +5V de la carte ESP32 (elle même alimentée par son connecteur USB avec un adaptateur secteur 5V) et non à sa sortie +3V3. Certes Le bus série doit, pour les deux cartes fonctionner en 3V3 et pas en 5V. MAIS les deux cartes contiennent chacune un régulateur 5V. Ce qui fait que tout est correct, 3V3 sur le bus, tout en alimentant le régulateur 3V3 du module GPS avec une tension supérieure à 3V3 (puisque = 5V) ce qui convient pour un régulateur de tension.

Attention : Le code source de cette version pour 'esp32_devkitc_v4' est différent de celui pour 'TTGO TDisplay'. Il se trouve dans le fichier documents.zip, au bas de cet article.

10 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.

11 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.

12 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).

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


14 Documents :

Includes : fichiers boitier 3D : :

15 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).

16 L'horloge 3,5

17 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.

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


19 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.

20 -

Liens...

6345