Primary flight display pour FlightGear avec un ESP32

Horizon artificiel +compas +Altitude +Vitesse + Vt ascensionnelle + réglages de l'autopilot. Avec la puissance de l'ESP32.
Cliquez sur une image ci-dessous pour visualiser l'album. Certaines sont commentées

1 Un aperçu en vidéo


Ceci est un test de la rapidité d'affichage avec des données calculées par des fonctions sinus... La vidéo quant-à-elle est prise en vitesse normale (!)

En réalité l'ESP32 se connecte au programme de simulation de vol FlightGear par liaison série USB. L'affichage devient alors plus calme et surtout réaliste.

2 Vue d'ensemble de cette réalisation :

L'intégration dans un boîtier maison (imprimé en 3D) est prévu, avec peut être d'autres instruments de bord.

3 Le schéma

A noter que les ports GPIO 34 et 35 ne sont pas pourvus de résistances de rappel à VCC (Pull-up) internes dans l'ESP32, d'où l'ajout des deux résistances externes de 10k.
La saisie des paramètres pour l'autopilot se fait avec deux classiques encodeurs rotatifs. Il ne reste plus beaucoup de broches libres sur le module ESP32 !

4 Le programme pour l'ESP32 à compiler avec l'IDE Arduino

CODE SOURCE en C++
  1.  
  2. /*
  3. FG_panel.ino - pour ESP32 - version Afficheur TFT 480x320
  4.  
  5. avec ligne de séparation inclinée
  6.  
  7. par Silicium628
  8.  
  9.  
  10. */
  11.  
  12.  
  13. /**
  14. --------------------------------------------------------
  15. CONCERNANT L'AFFICHAGE TFT : Connections :
  16.  
  17. ( Pensez à configurer le fichier User_Setup.h de la bibliothèque ~/Arduino/libraries/TFT_eSPI/ )
  18.  
  19.  
  20. TFT_CS <-> GPIO 27 // Chip select control pin
  21. TFT_DC <-> GPIO 14 // Data Command control pin - must use a pin in the range 0-31
  22. TFT_RST <-> GPIO 26 // Reset pin
  23.  
  24. TFT_WR <-> GPIO 12 // Write strobe control pin - must use a pin in the range 0-31
  25. TFT_RD <-> GPIO 13
  26.  
  27. TFT_D0 <-> GPIO 16 // Must use pins in the range 0-31 for the data bus
  28. TFT_D1 <-> GPIO 4 // so a single register write sets/clears all bits
  29. TFT_D2 <-> GPIO 23
  30. TFT_D3 <-> GPIO 22
  31. TFT_D4 <-> GPIO 21
  32. TFT_D5 <-> GPIO 19
  33. TFT_D6 <-> GPIO 18
  34. TFT_D7 <-> GPIO 17
  35.  
  36. **/
  37.  
  38.  
  39. String version="4.2";
  40.  
  41.  
  42. #include <stdint.h>
  43. #include <TFT_eSPI.h> // Hardware-specific library
  44. #include "Free_Fonts.h"
  45.  
  46. TFT_eSPI TFT = TFT_eSPI(); // Configurer le fichier User_Setup.h de la bibliothèque TFT_eSPI au préalable
  47.  
  48. TFT_eSprite SPR_E = TFT_eSprite(&TFT); // Declare Sprite object "SPR_11" with pointer to "TFT" object
  49. TFT_eSprite SPR_N = TFT_eSprite(&TFT);
  50. TFT_eSprite SPR_O = TFT_eSprite(&TFT);
  51. TFT_eSprite SPR_S = TFT_eSprite(&TFT);
  52.  
  53. //uint16_t s_width = TFT.Get_Display_Width();
  54. //uint16_t s_heigh = TFT.Get_Display_Height();
  55.  
  56. int16_t Ax_actu, Ay_actu;
  57. int16_t Bx_actu, By_actu;
  58.  
  59.  
  60. // COULEURS RGB565
  61. // Outil de sélection -> http://www.barth-dev.de/online/rgb565-color-picker/
  62.  
  63. #define NOIR 0x0000
  64. #define ROUGE 0xF800
  65. #define ROSE 0xFBDD
  66. #define ORANGE 0xFBC0
  67. #define JAUNE 0xFFE0
  68. #define JAUNE_PALE 0xF7F4
  69. #define VERT 0x07E0
  70. #define CYAN 0x07FF
  71. #define BLEU_CLAIR 0x455F
  72. #define BLEU 0x001F
  73. #define MAGENTA 0xF81F
  74. #define VIOLET1 0x781A
  75. #define VIOLET2 0xECBE
  76. #define GRIS_TRES_CLAIR 0xDEFB
  77. #define GRIS_CLAIR 0xA534
  78. #define GRIS 0x8410
  79. #define GRIS_FONCE 0x5ACB
  80. #define BLANC 0xFFFF
  81.  
  82.  
  83. // AVIONIQUE : Horizon Artificiel (HA):
  84.  
  85. #define HA_CIEL 0x33FE
  86. #define HA_SOL 0xDB60
  87.  
  88. //position et dimensions de l'horizon artificiel
  89.  
  90. #define HA_x_offset 230
  91. #define HA_y_offset 120
  92. #define HA_w 160 // demi largeur
  93. #define HA_h 100 // demi hauteur
  94.  
  95.  
  96. // Width and height of sprite
  97. #define SPR_W 20
  98. #define SPR_H 20
  99.  
  100. int32_t altitude;
  101. int32_t vitesse;
  102. int16_t cap;
  103. int16_t vspeed; // vitesse verticale
  104.  
  105. float roulis;
  106. float tangage;
  107.  
  108. int16_t hdg1 = 150; // consigne cap
  109. int16_t asel1 = 30; // consigne altitude
  110.  
  111. uint8_t data_ok;
  112. uint8_t attente_data=1;
  113.  
  114. uint8_t premier_passage =1;
  115.  
  116. //const int bouton1 = 25; // GPIO25
  117. //bool bouton1_etat;
  118. //bool memo_bouton1_etat;
  119.  
  120.  
  121. // deux encodeurs rotatifs pas à pas
  122. const int rot1a = 32; // GPIO32
  123. const int rot1b = 33; // GPIO33
  124.  
  125. const int rot2a = 35; // ATTENTION : cabler une résistance extérieure de pullup au +3V3 pour ce port
  126. const int rot2b = 34; // ATTENTION : cabler une résistance extérieure de pullup au +3V3 pour ce port
  127.  
  128.  
  129. #define TEMPO 50 // tempo anti-rebond pour l'acquisition des encodeurs rotatifs
  130. volatile uint32_t timer1 = 0;
  131. volatile uint32_t timer2 = 0;
  132.  
  133. uint16_t compteur1;
  134.  
  135. /**--------------------------------------------------------
  136. fonctions AFFICHAGE TFT
  137. --------------------------------------------------------**/
  138.  
  139.  
  140. /*
  141.  
  142. void dessine_triangles()
  143. {
  144.   int i = 0;
  145.   uint16_t L, H;
  146.   L=TFT.Get_Display_Width();
  147.   H=TFT.Get_Display_Height();
  148.  
  149.   for(i=0; i<H/2; i+=5)
  150.   {
  151.   TFT.Set_Draw_color(0,i+64,i+64);
  152.   TFT.Draw_Triangle(L/2-1,H/2-1-i,L/2-1-i,H/2-1+i,L/2-1+i,H/2-1+i);
  153.   }
  154. }
  155.  
  156. */
  157.  
  158.  
  159. // TFT.setTextColor(TFT_BLACK, TFT_WHITE);
  160. // TFT.drawLine(x0, y0, x1, y1, TFT_BLACK);
  161. // TFT.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN);
  162. // TFT.drawRect(5, 3, 230, 119, TFT_BLACK);
  163. // TFT.fillRect(5, 3, 230, 119, TFT_WHITE);
  164.  
  165.  
  166.  
  167. void Draw_arc_elliptique(uint16_t x0, uint16_t y0, int16_t dx, int16_t dy, float alpha1, float alpha2, uint16_t couleur) // alpha1 et alpha2 en radians
  168. {
  169. /*
  170. REMARQUES :
  171. -cette fonction permet également de dessiner un arc de cercle (si dx=dy), voire le cercle complet
  172. - dx et dy sont du type int (et pas uint) et peuvent êtres négafifs, ou nuls.
  173. -alpha1 et alpha2 sont les angles (en radians) des caps des extrémités de l'arc
  174. */
  175. uint16_t n;
  176. float i;
  177. float x,y;
  178.  
  179. i=alpha1;
  180. while(i<alpha2)
  181. {
  182. x=x0+dx*cos(i);
  183. y=y0+dy*cos(i+M_PI/2.0);
  184. TFT.drawPixel(x,y, couleur);
  185. i+=0.01; // radians
  186. }
  187. }
  188.  
  189.  
  190.  
  191.  
  192.  
  193. void init_affi_HA()
  194. {
  195. TFT.fillRect(HA_x_offset-HA_w, HA_y_offset-HA_h-1, 2*HA_w, HA_h+1, HA_CIEL);
  196. TFT.fillRect(HA_x_offset-HA_w, HA_y_offset-HA_h + HA_h, 2*HA_w, HA_h, HA_SOL);
  197. }
  198.  
  199.  
  200.  
  201. void truc(uint16_t Ax, uint16_t Ay, uint16_t Bx, uint16_t By)
  202. {
  203. TFT.drawLine(Ax, Ay, Bx, By-1, HA_CIEL);
  204. TFT.drawLine(Ax, Ay+1, Bx, By+1, HA_SOL);
  205.  
  206. }
  207.  
  208.  
  209.  
  210. void trace_HA()
  211. {
  212. ////String s1=(String) roulis;
  213. ////TFT.drawString(s1, 400, 20, 4);
  214.  
  215.  
  216. // pivot
  217. int16_t x0=0;
  218. int16_t y0=0;
  219.  
  220. //points d'intersection avec le bord du carré
  221. int16_t Ax, Ay; // sur le bord gauche
  222. int16_t Bx, By; // sur le bord droit
  223. // Le dessin consistera à tracer des segments colorés entre les points A et B
  224.  
  225.  
  226. // roulis -> [-90..+90]
  227.  
  228. // normalisation de la valeur R2 -> toujours >0
  229. float R2 = roulis;
  230. if (R2<0) {R2+=360;} // ce qui est un angle identique, de valeur positive (sens trigo)
  231.  
  232. // le pivot reste centré horizontalement mais se déplace verticalement en fonction du tangage
  233. y0 += tangage;
  234.  
  235. //calcul & memorisation de ces deux facteurs, ce qui évitera 2 calculs de tangente à chaque passage dan la fonction
  236. float tgt_moins = tan(degTOrad(90-R2));
  237. float tgt_plus = tan(degTOrad(90+R2));
  238.  
  239. //-----------------------------------------------------------------------------
  240. // CALCUL COTE DROIT (point B)
  241.  
  242. // calcul du point B d'intersection
  243. Bx=HA_w;
  244. By=y0 + HA_w*tan(degTOrad(R2));
  245.  
  246. //test si le point d'intersection se trouve plus haut que le haut du carré :
  247.  
  248. if(By>HA_h)
  249. {
  250. By=HA_h;
  251. Bx = x0 + (HA_h-y0)*tgt_moins;
  252. }
  253.  
  254. if(By< -HA_h)
  255. {
  256. By= -HA_h;
  257. Bx = x0 + (HA_h+y0)*tgt_plus;
  258. }
  259. //-----------------------------------------------------------------------------
  260. // CALCUL COTE GAUCHE (point A)
  261.  
  262. Ax=-HA_w;
  263. Ay=y0 - HA_w*tan(degTOrad(R2));
  264.  
  265. if(Ay> HA_h)
  266. {
  267. Ay= HA_h;
  268. Ax = x0 + (HA_h-y0)*tgt_moins;
  269. }
  270.  
  271. if(Ay< -HA_h)
  272. {
  273. Ay= -HA_h;
  274. Ax = x0 + (HA_h+y0)*tgt_plus;
  275. }
  276.  
  277.  
  278. //-----------------------------------------------------------------------------
  279. // positionnement de l'ensemble sur l'écran
  280.  
  281. Ax += HA_x_offset;
  282. Ay += HA_y_offset;
  283.  
  284. Bx += HA_x_offset;
  285. By += HA_y_offset;
  286.  
  287. // pour éciter un tracé hors cadre au premier passage :
  288. if (premier_passage == 1)
  289. {
  290. Ax_actu = Ax;
  291. Ay_actu = Ay;
  292.  
  293. Bx_actu = Bx;
  294. By_actu = By;
  295. premier_passage=0;
  296. }
  297.  
  298. //while ((Bx_actu != Bx) || (x2_actu != x2) || (By_actu != By) || (y2_actu != y2) )
  299. while ((Bx_actu != Bx) || (By_actu != By) || (Ax_actu != Ax) || (Ay_actu != Ay))
  300. {
  301. // déplacements successifs de 1 pixel de chaque extrémités de la ligne
  302.  
  303. //TFT.drawLine(Bx, By, x2, y2, BLANC);
  304.  
  305. if (Bx_actu < Bx) { Bx_actu++; truc(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  306. if (Bx_actu > Bx) { Bx_actu--; truc(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  307.  
  308. if (Ax_actu < Ax) { Ax_actu++; truc(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  309. if (Ax_actu > Ax) { Ax_actu--; truc(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  310.  
  311. if (By_actu < By) { By_actu++; truc(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  312. if (By_actu > By) { By_actu--; truc(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  313.  
  314. if (Ay_actu < Ay) { Ay_actu++; truc(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  315. if (Ay_actu > Ay) { Ay_actu--; truc(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  316.  
  317.  
  318. }
  319. dessine_avion(BLANC);
  320. }
  321.  
  322.  
  323.  
  324.  
  325. void affi_vitesse()
  326. {
  327.  
  328. uint16_t x1;
  329. String s1;
  330.  
  331. TFT.setTextColor(BLANC); // Background is not defined so it is transparent
  332.  
  333. //---------------------------------------------------------------------------------------
  334. //bande verticale multicolore
  335.  
  336. #define vitesse_sol 40
  337. int16_t vitesse_mini1 =90;
  338. int16_t vitesse_mini2 =130;
  339. int16_t vitesse_maxi =280;
  340.  
  341. //calcul de la position des limites entre les différentes couleurs verticales
  342. float d1=HA_y_offset+HA_w/2 + 3.2*((vitesse - vitesse_sol)); if (d1<0) {d1=0;} if (d1>319) {d1=319;}
  343. float d2=HA_y_offset+HA_w/2 + 3.2*((vitesse - vitesse_mini1)); if (d2<0) {d2=0;} if (d2>319) {d2=319;}
  344. float d3=HA_y_offset+HA_w/2 + 3.2*((vitesse - vitesse_mini2)); if (d3<0) {d3=0;} if (d3>319) {d3=319;}
  345. float d4=HA_y_offset+HA_w/2 + 3.2*((vitesse - vitesse_maxi)); if (d4<0) {d4=0;} if (d4>319) {d4=319;}
  346.  
  347.  
  348. TFT.fillRect(50, 0, 6, (int16_t)d4, ORANGE);
  349. TFT.fillRect(50, (int16_t)d4, 6, d3, VERT);
  350. TFT.fillRect(50, (int16_t)d3, 6, d2, ORANGE);
  351. TFT.fillRect(50, (int16_t)d2, 6, d1, ROUGE);
  352. TFT.fillRect(50, (int16_t)d1, 6, 239, GRIS);
  353.  
  354. //---------------------------------------------------------------------------------------
  355. //échelle verticale graduée glissante
  356.  
  357. uint16_t y0;
  358. int16_t vit1;
  359. float d5;
  360.  
  361. TFT.setFreeFont(FF1);
  362.  
  363. y0=3.2*(vitesse%10);
  364.  
  365. TFT.fillRect(0, 0, 50, 319, GRIS_FONCE); // bande verticale à gauche
  366. for(int n=0; n<10; n++)
  367. {
  368. d5 =5+y0+32.0*n; // 24 pixels verticalement entre chaque trait -> 10*24 = 240px (hauteur de l'afficheur)
  369. {
  370. TFT.fillRect(45, (int16_t)d5+5, 10, 2, BLANC); // petits tirets horizontaux
  371.  
  372. vit1 = vitesse -10*(n-5);
  373. vit1 /= 10;
  374. vit1 *= 10;
  375. s1=(String) vit1;
  376.  
  377. if ( /*(n%2==0) &&*/ (vit1>=0) && (vit1<1000) )
  378. {
  379. TFT.setTextColor(BLANC, GRIS_FONCE);
  380. //TFT.drawString(" ", 9, d5, 1);
  381. x1=0;
  382. if(vit1<100){x1=7;} // pour affichage centré
  383. if(vit1<10){x1=14;}
  384. TFT.drawString(s1, x1, (uint16_t)d5, 1); // Graduation (tous les 20kts)
  385. }
  386. }
  387. }
  388.  
  389. //---------------------------------------------------------------------------------------
  390. // affichage de la valeur principale
  391.  
  392. TFT.setTextColor(BLANC, NOIR);
  393. TFT.setFreeFont(FF19);
  394. s1=(String)vitesse;
  395.  
  396. TFT.fillRect(3, 160, 42, 26, NOIR); // efface le nombre précédemment affiché (pour le cas où on passe de trois à deux chiffres)
  397.  
  398. if ((vitesse>=0) && (vitesse <1000))
  399. {
  400. x1=3;
  401. if(vitesse<100){x1=10;} // pour affichage centré
  402. if(vitesse<10){x1=20;}
  403. TFT.drawString(s1, x1, 160, 4); // affiche le nombre
  404. } // affiche en gros à mi-hauteur de l'écran
  405. else
  406. { TFT.fillRect(3, 160, 42, 26, GRIS); }
  407.  
  408. TFT.drawRoundRect(1, 159, 45, 28, 5, BLANC); // encadrement de la valeur centrale affichée
  409.  
  410. TFT.fillTriangle(45, 167, 45, 177, 55, 172, NOIR); // petit triangle (curseur) noir
  411. }
  412.  
  413.  
  414.  
  415. void affiche_asel()
  416. {
  417. String s2 =(String)(asel1*100); // consigne ALTITUDE de l'autopilot
  418. TFT.setTextColor(ROSE, NOIR); //
  419. TFT.setFreeFont(FF5);
  420. uint16_t x1;
  421. x1=405;
  422. if(asel1<10000){x1+=7;}
  423. if(asel1<1000){x1+=7;}
  424. if(asel1<100){x1+=7;} // pour affichage centré
  425. if(asel1<10){x1+=7;}
  426. TFT.fillRect(x1, 0, 70, 20, NOIR); // efface
  427. TFT.drawString(s2, x1, 5, 1);
  428. }
  429.  
  430.  
  431.  
  432. void affi_vt_verticale()
  433. {
  434. // = vitesse ascensionnelle, sous forme de barres verticales vertes, à droite, près de l'echelle d'altitude
  435. uint16_t x0=405;
  436. uint16_t y0=40;
  437.  
  438. int16_t dy=0;
  439.  
  440. //fleche haute
  441. TFT.fillRect(x0, 0, 10, 140, GRIS_FONCE); // efface haut
  442. if (vspeed > 1)
  443. {
  444. dy= vspeed;
  445.  
  446. TFT.fillRect(x0, y0+100-dy, 10, dy, VERT); // fleche
  447. }
  448.  
  449.  
  450.  
  451.  
  452. //fleche basse
  453. TFT.fillRect(x0, y0+150, 10, 135, GRIS_FONCE); // efface bas
  454. if (vspeed < -1)
  455. {
  456. dy= -vspeed;
  457.  
  458. TFT.fillRect(x0, y0+150, 10, dy, VERT); // fleche
  459. }
  460.  
  461.  
  462. }
  463.  
  464.  
  465.  
  466. void affi_rayon(uint16_t x0, uint16_t y0, uint16_t rayon, double angle)
  467. {
  468. // angle en radians
  469. float x1, x2;
  470. float y1, y2;
  471.  
  472. x1=x0+rayon* cos(angle);
  473. y1=y0-rayon* sin(angle);
  474.  
  475. x2=x0+0.9*rayon* cos(angle);
  476. y2=y0-0.9*rayon* sin(angle);
  477.  
  478. TFT.drawLine(x1, y1, x2, y2, BLANC);
  479. }
  480.  
  481.  
  482. float degTOrad(float angle)
  483. {
  484. return (angle * M_PI / 180.0);
  485. }
  486.  
  487.  
  488.  
  489. void affi_cap()
  490. {
  491.  
  492. // cercle de CAP gradué
  493.  
  494. uint16_t x02 = 230;
  495. uint16_t y02 = 350;
  496. float angle; // en radians
  497. //float cap_RD; // en radians (le cap fourni par FG estant en degrés d'angle)
  498. uint16_t x_spr;
  499. uint16_t y_spr;
  500.  
  501. uint16_t x_hdg;
  502. uint16_t y_hdg;
  503. /**
  504. 360° =2 pi rad
  505. 1° = 2 pi/360 rad = pi/180 rad
  506. **/
  507. //cap_RD = cap * 2*M_PI / 360;
  508.  
  509. TFT.fillRect(150, 250, 200, 70, NOIR); // efface
  510.  
  511. TFT.drawCircle(x02, y02, 85, BLANC);
  512.  
  513.  
  514. for(uint8_t n=0; n<24; n++ )
  515. {
  516. angle = cap+15 + n*15; // 1 tiret tous les 15 degrés
  517. affi_rayon(x02, y02, 80, degTOrad(angle)); // tirets de graduation
  518. }
  519. x_hdg = x02 + 80*cos(degTOrad(hdg1-90-cap));
  520. y_hdg = y02 + 80*sin(degTOrad(hdg1-90-cap));
  521.  
  522. TFT.drawLine(x02, y02, x_hdg, y_hdg, GRIS_CLAIR);
  523. TFT.drawCircle(x_hdg, y_hdg, 5, BLEU_CLAIR); // rond vert sur le cercle = consigne de cap de l'autopilot
  524.  
  525. x_spr = x02+80 * cos(degTOrad(angle));
  526. y_spr = y02-80 * sin(degTOrad(angle));
  527. TFT.setPivot(x_spr, y_spr);
  528. SPR_E.pushRotated(-cap+90, TFT_BLACK); // Plot rotated Sprite, black = transparent
  529.  
  530. x_spr = x02+80 * cos(degTOrad(angle+90));
  531. y_spr = y02-80 * sin(degTOrad(angle+90));
  532. TFT.setPivot(x_spr, y_spr);
  533. SPR_N.pushRotated(-cap, TFT_BLACK);
  534.  
  535. x_spr = x02+80 * cos(degTOrad(angle+180));
  536. y_spr = y02-80 * sin(degTOrad(angle+180));
  537. TFT.setPivot(x_spr, y_spr);
  538. SPR_O.pushRotated(-cap-90, TFT_BLACK);
  539.  
  540. x_spr = x02+80 * cos(degTOrad(angle-90));
  541. y_spr = y02-80 * sin(degTOrad(angle-90));
  542. TFT.setPivot(x_spr, y_spr);
  543. SPR_S.pushRotated(-cap, TFT_BLACK);
  544.  
  545.  
  546. // petite "maison" en bas (valeur du cap)
  547. #define a 200
  548. #define b a+30
  549. #define c b+30
  550. #define d 280
  551. #define e d+10
  552. #define f e+26
  553.  
  554. TFT.drawLine(a, f, c, f, BLANC); // sol
  555. TFT.drawLine(a, f, a, e, BLANC); // mur de gzuche
  556. TFT.drawLine(c, f, c, e, BLANC); // mur de droite
  557. TFT.drawLine(a, e, b, d, BLANC); // toit pente gauche
  558. TFT.drawLine(c, e, b, d, BLANC); // toit pente droite
  559.  
  560.  
  561. // affiche la valeur
  562. String s1;
  563. uint16_t x0 = a+10;
  564. uint16_t y0 = e;
  565.  
  566. uint16_t x1= x0;
  567. if(cap<100){x1+=5;} // pour affichage centré
  568. if(cap<10){x1+=5;}
  569.  
  570. s1=(String) cap;
  571.  
  572. TFT.fillRect(x0, y0, 42, 20, NOIR); // efface le nombre précédemment affiché
  573. TFT.setTextColor(BLANC, NOIR);
  574. TFT.setFreeFont(FF19);
  575. TFT.drawString(s1, x1, y0, 4);
  576.  
  577.  
  578. }
  579.  
  580.  
  581.  
  582. void affi_altitude()
  583. {
  584. String s1;
  585. uint16_t x0 =420;
  586. //---------------------------------------------------------------------------------------
  587. //échelle verticale graduée glissante
  588.  
  589. uint16_t x1;
  590. uint16_t y0;
  591. int16_t alt1;
  592. float d5;
  593.  
  594. TFT.setFreeFont(FF1);
  595.  
  596. y0=3.2*(altitude%10);
  597.  
  598. TFT.fillRect(x0, 20, 60, 319, GRIS_FONCE); //efface bande verticale à droite
  599.  
  600.  
  601. for(int n=0; n<10; n++)
  602. {
  603. d5 =0+y0+32.0*n; // pixels verticalement entre chaque trait -> 10*24 = 240px (hauteur de l'afficheur)
  604. {
  605. if (d5>=20) // marge en haut
  606. {
  607. TFT.fillRect(x0, (int16_t)d5+5, 5, 2, BLANC); // petits tirets horizontaux
  608.  
  609. alt1 = altitude -10*(n-5);
  610. alt1 /= 10;
  611. alt1 *= 10;
  612. s1=(String) alt1;
  613.  
  614. TFT.setTextColor(BLANC, GRIS_FONCE);
  615. //TFT.drawString(" ", 9, d5, 1);
  616. x1=x0;
  617. if(alt1<10000){x1+=7;} // pour affichage centré
  618. if(alt1<1000){x1+=7;}
  619. if(alt1<100){x1+=7;}
  620. if(alt1<10){x1+=7;}
  621. TFT.drawString(s1, x1, (uint16_t)d5, 1); // Graduation (tous les 20kts)
  622. }
  623. }
  624. }
  625.  
  626.  
  627. //---------------------------------------------------------------------------------------
  628. // affichage de la valeur principale
  629.  
  630.  
  631. uint16_t x2;
  632. uint16_t y0b = 155;
  633. TFT.fillRect(x0-20, y0b, 80, 25, NOIR); // efface le nombre précédemment affiché
  634. TFT.setTextColor(BLANC, NOIR);
  635. TFT.setFreeFont(FF19);
  636.  
  637. if ((altitude >=0) && (altitude < 60000))
  638. {
  639. s1=(String) altitude;
  640. }
  641. else {s1="----";}
  642. x2=x0-20;
  643. if(altitude<10000){x2+=10;} // pour affichage centré
  644. if(altitude<1000){x2+=10;}
  645. if(altitude<100){x2+=10;}
  646. if(altitude<10){x2+=10;}
  647. TFT.drawString(s1, x2, y0b, 4);
  648. TFT.drawRoundRect(x0-20, y0b-3, 75, 28, 5, BLANC); // encadrement de la valeur centrale affichée
  649.  
  650.  
  651. }
  652.  
  653.  
  654.  
  655. void dessine_avion(uint16_t couleur_i)
  656. {
  657. TFT.fillRect(HA_x_offset-20, 120, 40, 5, couleur_i);
  658. TFT.fillCircle(HA_x_offset, 120, 5, couleur_i);
  659. }
  660.  
  661.  
  662.  
  663.  
  664. void trace_arc_gradu()
  665. {
  666. Draw_arc_elliptique(HA_x_offset, 120, 60, 60, 0.6, 2.6, BLANC);
  667. }
  668.  
  669.  
  670.  
  671. void rotation1()
  672. {
  673. // consigne de cap
  674. if ( millis() - TEMPO >= timer1 )
  675. {
  676. timer1 = millis();
  677. bool etat = digitalRead(rot1b);
  678. if(etat == 0) { hdg1+=5;} else { hdg1-=5;}
  679. if (hdg1<0){hdg1=355;}
  680.  
  681. if (hdg1>359){hdg1=0;}
  682. affi_autopilot();
  683. }
  684. }
  685.  
  686.  
  687. void rotation2()
  688. {
  689. // consigne d'altitude
  690. if ( millis() - TEMPO >= timer2 )
  691. {
  692. timer2 = millis();
  693. bool etat = digitalRead(rot2b);
  694. if(etat == 0)
  695. {
  696. asel1+=5;
  697. }
  698. else
  699. {
  700. asel1-=5;
  701. }
  702. if (asel1<5){asel1=5;}
  703. if (asel1>600){asel1=600;}
  704. affi_autopilot();
  705. }
  706.  
  707. }
  708.  
  709.  
  710. void setup()
  711. {
  712. Serial.begin(9600);
  713.  
  714. //pinMode(bouton1, INPUT_PULLUP);
  715.  
  716. pinMode(rot1a, INPUT_PULLUP);
  717. pinMode(rot1b, INPUT_PULLUP);
  718. pinMode(rot2a, INPUT_PULLUP);
  719. pinMode(rot2b, INPUT_PULLUP);
  720.  
  721. attachInterrupt(rot1a, rotation1, RISING);
  722. attachInterrupt(rot2a, rotation2, RISING);
  723.  
  724.  
  725. TFT.init();
  726. TFT.setRotation(1); // 0..3 à voir, suivant disposition de l'afficheur
  727.  
  728. TFT.fillScreen(TFT_BLACK);
  729.  
  730. SPR_E.createSprite(SPR_W, SPR_H);
  731. SPR_E.setPivot(SPR_W/2, SPR_H/2); // Set pivot relative to top left corner of Sprite
  732. SPR_E.fillSprite(GRIS_FONCE);
  733. SPR_E.setTextColor(JAUNE);
  734. SPR_E.setFreeFont(FF19);
  735. SPR_E.drawString("E", 0, 0, 4);
  736.  
  737. SPR_N.createSprite(SPR_W, SPR_H);
  738. SPR_N.setPivot(SPR_W/2, SPR_H/2);
  739. SPR_N.fillSprite(GRIS_FONCE);
  740. SPR_N.setTextColor(ROUGE);
  741. SPR_N.setFreeFont(FF19);
  742. SPR_N.drawString("N", 0, 0, 4);
  743.  
  744. SPR_O.createSprite(SPR_W, SPR_H);
  745. SPR_O.setPivot(SPR_W/2, SPR_H/2);
  746. SPR_O.fillSprite(GRIS_FONCE);
  747. SPR_O.setTextColor(JAUNE);
  748. SPR_O.setFreeFont(FF19);
  749. SPR_O.drawString("W", 0, 0, 4);
  750.  
  751. SPR_S.createSprite(SPR_W, SPR_H);
  752. SPR_S.setPivot(SPR_W/2, SPR_H/2);
  753. SPR_S.fillSprite(GRIS_FONCE);
  754. SPR_S.setTextColor(VERT);
  755. SPR_S.setFreeFont(FF19);
  756. SPR_S.drawString("S", 0, 0, 4);
  757.  
  758. delay(100);
  759.  
  760.  
  761.  
  762. TFT.setTextColor(NOIR, BLANC);
  763.  
  764. //TFT.setFreeFont(FF19);
  765. //TFT.drawString("FlightGear motor", 0, 0, 4);
  766.  
  767. init_affi_HA();
  768.  
  769. delay(100);
  770.  
  771. //TFT.fillRect(48, 0, 6, 100, 0xFFE0);
  772.  
  773. // TFT.fillRect(0, 0, 479, 30, NOIR);
  774. TFT.setTextColor(BLANC, NOIR);
  775. TFT.setFreeFont(FF19);
  776. String s1 = "PFD v";
  777. s1+= version;
  778. // TFT.drawString(s1, 70, 3, 2);
  779.  
  780.  
  781. Ay_actu=120;
  782. By_actu=120;
  783.  
  784. altitude =0;
  785. vitesse =0;
  786. roulis =0;
  787. tangage =0;
  788. cap=0;
  789. vspeed=0; // vitesse verticale
  790.  
  791.  
  792. // affichages();
  793.  
  794. //bouton1_etat = digitalRead(bouton1);
  795. //memo_bouton1_etat = bouton1_etat;
  796.  
  797. init_affi_autopilot();
  798.  
  799. }
  800.  
  801.  
  802.  
  803.  
  804. //uint8_t buffer1[200];
  805.  
  806. uint16_t L=0;
  807. uint16_t C=0;
  808. uint16_t x, y;
  809. uint16_t i1 = 0;
  810.  
  811. uint8_t p1;
  812.  
  813. int32_t number = 0;
  814.  
  815. String parametre;
  816. String s1;
  817. String s2;
  818.  
  819.  
  820. void acquisitions()
  821. {
  822.  
  823. if(Serial.available() > 14)
  824. {
  825. parametre="";
  826. s1="";
  827. char octet_in;
  828.  
  829. while(octet_in != '=')
  830. {
  831. octet_in = Serial.read();
  832. if(octet_in != '=') {parametre += octet_in; }
  833. }
  834. while(octet_in != '\n')
  835. {
  836. octet_in = Serial.read();
  837. if(octet_in != '\n') {s1 += octet_in; }
  838. }
  839.  
  840. if(parametre == "alti" )
  841. {
  842. char buf[50];
  843. s1.toCharArray(buf, 50);
  844. altitude = atol(buf);
  845. data_ok |= 1; // positionne bit0
  846.  
  847.  
  848. ////TFT.drawString("Altitude", 0, 40, 4);
  849. ////s2= (String) altitude;
  850. ////TFT.fillRect(120, 40, 100, 30, TFT_BLACK);
  851. ////TFT.drawString(s2, 120, 40, 4);
  852. }
  853.  
  854. if(parametre == "speed" )
  855. {
  856. char buf[50];
  857. s1.toCharArray(buf, 50);
  858. vitesse = atol(buf);
  859. data_ok |= 2; // positionne bit1
  860.  
  861.  
  862. ////TFT.drawString("Vitesse", 0, 70, 4);
  863. ////s2= (String) vitesse;
  864. ////TFT.fillRect(120, 70, 100, 30, TFT_BLACK);
  865. ////TFT.drawString(s2, 120, 70, 4);
  866. }
  867.  
  868. if(parametre == "pitch" )
  869. {
  870. char buf[50];
  871. s1.toCharArray(buf, 50);
  872. tangage = atol(buf);
  873. data_ok |= 4; // positionne bit2
  874.  
  875. ////TFT.drawString("Tangage", 0, 100, 4);
  876. ////s2= (String) tangage;
  877. ////TFT.fillRect(120, 100, 100, 30, TFT_BLACK);
  878. ////TFT.drawString(s2, 120, 100, 4);
  879. }
  880.  
  881. if(parametre == "roll" )
  882. {
  883. char buf[50];
  884. s1.toCharArray(buf, 50);
  885. roulis = atol(buf);
  886. data_ok |= 8; // positionne bit3
  887.  
  888. ////TFT.drawString("Roulis", 0, 130, 4);
  889. ////s2= (String) roulis;
  890. ////TFT.fillRect(120, 130, 100, 30, TFT_BLACK);
  891. ////TFT.drawString(s2, 120, 130, 4);
  892. }
  893.  
  894.  
  895.  
  896. if(parametre == "heading" )
  897. {
  898. char buf[50];
  899. s1.toCharArray(buf, 50);
  900. cap = atol(buf);
  901. data_ok |= 16; // positionne bit4
  902.  
  903. ////TFT.drawString("Cap", 0, 160, 4);
  904. ////s2= (String) roulis;
  905. ////TFT.fillRect(120, 160, 100, 30, TFT_BLACK);
  906. ////TFT.drawString(s2, 120, 160, 4);
  907. }
  908.  
  909.  
  910. if(parametre == "vspeed" )
  911. {
  912. char buf[50];
  913. s1.toCharArray(buf, 50);
  914. vspeed = atol(buf);
  915. data_ok |= 32; // positionne bit5
  916.  
  917. ////TFT.drawString("vspeed", 0, 170, 4);
  918. ////s2= (String) vspeed;
  919. ////TFT.fillRect(120, 170, 100, 30, TFT_BLACK);
  920. ////TFT.drawString(s2, 120, 170, 4);
  921. }
  922.  
  923. }
  924.  
  925. delay(1); // important sinon ne recevra pas la totalité des données (qui se fait en plusieurs passes)
  926.  
  927. // pour test
  928. ////TFT.drawString("data= ", 90, 40, 4);
  929. ////s2= (String) data_ok;
  930. ////TFT.fillRect(140, 40, 50, 30, TFT_BLACK);
  931. ////TFT.drawString(s2, 150, 40, 4);
  932.  
  933. }
  934.  
  935.  
  936.  
  937. void data_out()
  938. {
  939. // destination FlightGear par la liaison série USB
  940. Serial.print(hdg1); // consigne de Cap -> autopilot
  941. Serial.print(',');
  942. Serial.print(asel1); // consigne d'alititude -> autopilot
  943. Serial.print('\n');
  944.  
  945. }
  946.  
  947.  
  948.  
  949. void affichages()
  950. {
  951. trace_HA();
  952. affi_vitesse();
  953. affi_altitude();
  954. trace_arc_gradu();
  955. affi_cap();
  956. affi_vt_verticale();
  957. affi_autopilot();
  958. affiche_asel();
  959.  
  960. }
  961.  
  962.  
  963. void affi_nop()
  964. {
  965. TFT.drawLine(HA_x_offset-HA_w, HA_y_offset-HA_h, HA_x_offset +HA_w, HA_y_offset +HA_h, JAUNE);
  966. TFT.drawLine(HA_x_offset-HA_w, HA_y_offset+HA_h, HA_x_offset +HA_w, HA_y_offset -HA_h, JAUNE);
  967.  
  968.  
  969. //TFT.fillRect(0, 0, 239, 30, NOIR);
  970. TFT.setTextColor(JAUNE, BLEU);
  971. TFT.setFreeFont(FF19);
  972. TFT.drawString("No Data", 180, 140, 4);
  973. }
  974.  
  975.  
  976.  
  977.  
  978. void init_affi_autopilot()
  979. {
  980. uint16_t x0;
  981. TFT.setFreeFont(FF1);
  982. TFT.setTextColor(JAUNE, GRIS_FONCE);
  983.  
  984.  
  985. // ALT
  986. x0=355;
  987. TFT.drawString("ALT", x0, 260, 1);
  988. TFT.drawRoundRect(x0-4, 255, 45, 42, 5, BLANC);
  989.  
  990. }
  991.  
  992.  
  993.  
  994. void affi_autopilot()
  995. {
  996. // HDG (cap)
  997.  
  998. uint16_t x0=70; // 70
  999. uint16_t y0=245; // 255
  1000.  
  1001. TFT.setFreeFont(FF1);
  1002.  
  1003. TFT.fillRect(x0, y0, 70, 80, NOIR); // efface
  1004. //TFT.drawLine(105, 255, 105, 319, BLEU);
  1005. //TFT.drawLine(75, 289, 135, 289, BLEU);
  1006.  
  1007. TFT.setTextColor(JAUNE, NOIR);
  1008. TFT.drawString("HDG", x0+20, y0+15, 1);
  1009. //TFT.drawRoundRect(x0-4, 255, 45, 42, 5, BLANC);
  1010.  
  1011. TFT.drawCircle(x0+35, y0+34, 30, BLANC);
  1012.  
  1013. String s1 =(String)hdg1;
  1014. TFT.setTextColor(BLANC, NOIR);
  1015.  
  1016. TFT.drawString(s1, x0+18, y0+35, 1);
  1017.  
  1018. TFT.setTextColor(BLANC, NOIR);
  1019. TFT.drawString("N", x0+30, y0-5, 1);
  1020.  
  1021. TFT.setTextColor(BLANC, NOIR);
  1022. TFT.drawString("S", x0+30, y0+60, 1);
  1023.  
  1024. TFT.setTextColor(BLANC, NOIR);
  1025. TFT.drawString("E", x0+60, y0+27, 1);
  1026. TFT.drawString("W", x0, y0+27, 1);
  1027.  
  1028. uint16_t x1,y1;
  1029. x1= x0+35 + 30*cos((hdg1-90)/57.3); // =360/2pi
  1030. y1= y0+34 + 30*sin((hdg1-90)/57.3);
  1031. TFT.fillCircle(x1, y1, 3, VERT); // rond vert sur le cercle = cap
  1032. TFT.drawLine(x0+35, y0+34, x1, y1, VERT);
  1033.  
  1034.  
  1035. // ALTITUDE
  1036. uint16_t x2=355;
  1037.  
  1038. // ce paramétrage permet de régler la valeur de 0 à 200 (soit 0 à 20000 pieds soit 6000m environ)
  1039. //pour aller plus haut on peut toujours utiliser l'interface de FG (touche F11...)
  1040. String s2 =(String)asel1;
  1041. TFT.setTextColor(ROSE, NOIR);
  1042. TFT.fillRect(x2, 280, 40, 15, NOIR); // efface
  1043. TFT.drawString(s2, x2, 278, 1);
  1044.  
  1045. affiche_asel();
  1046.  
  1047. data_out();
  1048.  
  1049. }
  1050.  
  1051.  
  1052.  
  1053. void loop()
  1054. {
  1055.  
  1056. /*
  1057. bouton1_etat = digitalRead(bouton1);
  1058.  
  1059. if (bouton1_etat != memo_bouton1_etat)
  1060. {
  1061. memo_bouton1_etat = bouton1_etat;
  1062.  
  1063. if (bouton1_etat == 0)
  1064. {
  1065. TFT.fillCircle(70, 20, 10, VERT);
  1066. data_out();
  1067. delay(300);
  1068. TFT.fillCircle(70, 20, 10, NOIR);
  1069.  
  1070. }
  1071. }
  1072. */
  1073.  
  1074.  
  1075. // #define TEST_AFFI // commenter cette ligne pour un fonctionnement normal; décommenter pour tester les affichages et la fluidité
  1076.  
  1077.  
  1078. //---------------------------------------------------------------------------------------
  1079. #ifdef TEST_AFFI // ne pas toucher cette ligne
  1080.  
  1081. // pour test des affichages:
  1082.  
  1083. uint8_t octet_i;
  1084. uint16_t t=0; // temps -> rebouclera si dépassement
  1085.  
  1086.  
  1087. while(1) // boucle infinie
  1088. {
  1089.  
  1090. // les valeurs de ces paramètres peuvent être modifiées afin de tester tel ou tel point particulier
  1091. vitesse = 100 - 100*cos(t/50.0);
  1092. altitude = 1000 - 10*cos(t/80.0);
  1093.  
  1094. vspeed = 100*sin(t/30.0);
  1095.  
  1096. tangage =80.0*sin(t/82.0);
  1097. roulis = 65.0*sin(t/35.0);
  1098.  
  1099. cap =180.0+180.0*sin(t/20.0);
  1100. data_ok=63;
  1101.  
  1102.  
  1103. affichages();
  1104.  
  1105. t++;
  1106.  
  1107. TFT.setTextColor(JAUNE, BLEU);
  1108. TFT.setFreeFont(FF19);
  1109. TFT.drawString("TEST AFFI", 150, 180, 4);
  1110.  
  1111. delay(20);
  1112.  
  1113.  
  1114. }
  1115.  
  1116. //---------------------------------------------------------------------------------------
  1117. #else // ne pas toucher cette ligne
  1118.  
  1119. compteur1+=1;
  1120. if (compteur1 >= 100) // tous les 1000 passages dans la boucle
  1121. {
  1122. compteur1=0;
  1123. affi_autopilot();
  1124. }
  1125.  
  1126.  
  1127. acquisitions();
  1128.  
  1129. if (data_ok == 63) // 63 cete valeur = 1+2+4+8+16+32 - voir fonction "acquisitions()"
  1130. {
  1131. TFT.fillCircle(390, 310, 5, VERT);
  1132. if (attente_data == 1)
  1133. {
  1134. attente_data=0;
  1135. TFT.fillScreen(TFT_BLACK);
  1136. init_affi_HA();
  1137. init_affi_autopilot();
  1138. }
  1139. affichages();
  1140. data_ok=0;
  1141. }
  1142. else
  1143. {
  1144. TFT.fillCircle(390, 310, 5, ROUGE);
  1145. if(attente_data==1)
  1146. {
  1147. affi_nop();
  1148. affichages();
  1149. delay(300);
  1150. }
  1151. }
  1152.  
  1153. //---------------------------------------------------------------------------------------
  1154. #endif // ne pas toucher cette ligne
  1155.  
  1156.  
  1157.  
  1158.  
  1159. }
  1160.  
  1161.  
  1162.  
  1163. /**
  1164. -----------------------------------------------------------------------------------------------------------
  1165.   contenu du fichier (sous Linux) "/usr/share/games/protocol/hardware4.xml" qui va bien pour ce programme
  1166.  
  1167.  
  1168.   * FG doit en conséquence être lancé avec les paramètres *
  1169.  
  1170. --generic=serial,in,2,/dev/ttyUSB0,9600,hardware4
  1171. --generic=serial,out,2,/dev/ttyUSB0,9600,hardware4
  1172.  
  1173. IMPORTANT :
  1174. Il faut ajouter les DEUX lignes dans un lanceur (tel que "FGo!") pour obtenir un fonctionnement bidirectionnel
  1175.  
  1176. Remarques :
  1177.   - le nom du fichier "hardware4.xml" peut être autre chose pourvu que le paramètre de lancement corresponde exactement au nom choisi
  1178.   - le fichier en question n'existe pas par défaut dans ce répertoire, il faut le créer
  1179.  
  1180. -----------------------------------------------------------------------------------------------------------
  1181.  
  1182. <?xml version="1.0"?>
  1183.  
  1184. <PropertyList>
  1185.  
  1186. <generic>
  1187.   <output>
  1188.   <binary_mode>false</binary_mode>
  1189.   <line_separator>\n</line_separator>
  1190.   <var_separator>\n</var_separator>
  1191.   <preamble></preamble>
  1192.   <postamble></postamble>
  1193.  
  1194.   <chunk>
  1195.   <name>Altitude</name>
  1196.   <node>/position/altitude-ft</node>
  1197.   <type>integer</type>
  1198.   <format>alti=%i</format>
  1199.   </chunk>
  1200.  
  1201.   <chunk>
  1202.   <name>Speed</name>
  1203.   <node>/velocities/airspeed-kt</node>
  1204.   <type>integer</type>
  1205.   <format>speed=%i</format>
  1206.   </chunk>
  1207.  
  1208.   <chunk>
  1209.   <name>Tangage</name>
  1210.   <node>/orientation/pitch-deg</node>
  1211.   <type>integer</type>
  1212.   <format>pitch=%i</format>
  1213.   </chunk>
  1214.  
  1215.   <chunk>
  1216.   <name>Roulis</name>
  1217.   <node>/orientation/roll-deg</node>
  1218.   <type>integer</type>
  1219.   <format>roll=%i</format>
  1220.   </chunk>
  1221.  
  1222.   <chunk>
  1223.   <name>Cap</name>
  1224.   <node>/orientation/heading-deg</node>
  1225.   <type>integer</type>
  1226.   <format>heading=%i</format>
  1227.   </chunk>
  1228.  
  1229.   <chunk>
  1230.   <name>Vertical_speed</name>
  1231.   <node>/velocities/vertical-speed-fps</node>
  1232.   <type>integer</type>
  1233.   <format>vspeed=%i</format>
  1234.   </chunk>
  1235.  
  1236.  
  1237.  
  1238.   </output>
  1239.  
  1240.  
  1241.   <input>
  1242. <line_separator>\n</line_separator>
  1243.   <var_separator>,</var_separator>
  1244.  
  1245.   <chunk>
  1246.   <name>heading_bug</name>
  1247.   <node>/autopilot/settings/heading-bug-deg</node>
  1248.   <type>integer</type>
  1249.   <relative>false</relative>
  1250.   </chunk>
  1251.  
  1252.   <chunk>
  1253.   <name>asel</name>
  1254.   <node>/autopilot/settings/asel</node>
  1255.   <type>integer</type>
  1256.   <relative>false</relative>
  1257.   </chunk>
  1258.  
  1259.   </input>
  1260.  
  1261.  
  1262.  
  1263. </generic>
  1264.  
  1265. </PropertyList>
  1266.  
  1267.  
  1268.  
  1269. **/
  1270.  

J'ai fait un effort pour commenter en détail le code source.

A noter : dans la boucle principale (loop) figure cette ligne que l'on doit gérer absolument. (C'est une directive de compilation conditionnelle) :

#define TEST_AFFI // commenter cette ligne pour un fonctionnement normal; dé-commenter pour tester les affichages et la fluidité

Pour la commenter, il suffit d'ajouter // en début de ligne afin d'obtenir un fonctionnement normal à la place d'un affichage (agité !) de test

5 Le principe

Le simulateur FlightGear Open-source, gratuit, (pour Linux) comprend outre un serveur html, des serveurs de protocoles série (telnet, mais aussi "générique" qui nous intéresse ici). Encore faut-il définir ce protocole générique précisément. Il faut pour cela créer un fichier xml et le placer dans le répertoire de "protocol" de FlightGear.

Sous linux ça donne ça :

/usr/share/games/protocol/hardware4.xml


J'ai recopié le contenu de ce fichier au bas du code source. Il comprend deux parties, "output" et "input".

Ce n'est pas tout : pour que le programme FG tienne compte de ce fichier, il faut le lancer avec les deux options suivantes (les 2 options sont indispensables afin d'obtenir un fonctionnement bidirectionnel):

--generic=serial,in,2,/dev/ttyUSB0,9600,hardware4
--generic=serial,out,2,/dev/ttyUSB0,9600,hardware4


La partie "output" permet de publier, à destination de l'ESP :
  • l'altitude
  • la vitesse
  • le tangage
  • le roulis
  • le cap
  • la vitesse verticale
La partie "input" permet de régler à l'aide de deux encodeurs rotatifs pas à pas reliés à l'ESP, les consignes d'altitude et de cap du pilote automatique de FG. (avant de décoller, on doit aller dans la fenêtre de paramétrage de l'autopilot avec la touche de fonction F11, et sélectionner HDG et ALT. ensuite après prise d'altitude minimale, on peut lancer l'autopilot avec par exemple un bouton de la manette de jeux...)

6 L'afficheur utilisé :

C'est un TFT LCD 480 x 320 que l'on pilote ici par son interface parallèle, d'où la très grande rapidité.

7 Vidéo - Décollage LFMT


8 Le Lac du Salagou


J'ai plaqué une texture image sur le relief de la presqu’île ce qui donne un rendu bien plus réaliste, avec la couleur rouge caractéristique des roches, qu'on appelle dans l'Hérault la "ruffe".

9 Utilité du panel ESP32

Cela permet de piloter l'avion calmement, en pilotage automatique, sans afficher le tableau de bord sur la moité de l'écran de l'ordinateur, ou même en affichage extérieur sans le tableau de bord, par exemple, et profiter de la vue. Parce que oui, FlightGear bien configuré et très beau, comme on peut le constater avec les copies d'écran que j'ai rajoutées au début de cet article. J'ai blenderisé en 3D pas mal de choses pour FGFS.

10 Evolution

Cette réalisation est en pleine phase d'évolution, par exemple j'améliore l'affichage chaque jour...

11 Circuit imprimé

9 mai 2021 :
Je prépare en ce moment, sous Kicad, un circuit imprimé (en jaune sur cette vue) qui assemblera facilement le module ESP32 et l'afficheur TFT. Je publierai ici le tout dès que ce sera fait, et que le fonctionnement sera correct (j'ai créé les composants, il est assez facile de se gourer dans la numérotation des pins voire de dessiner en image miroir... surtout avec ces écrans TFT pour lesquels on hésite parfois à déterminer quelle est la face composants et quelle est la face cuivre, je vous laisse méditer la chose...)

12 Documents :

13 -

Liens...

Me contacter à propos de cet article :

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


120