/************************************************************
  * FGFS_MCDU_ILI9341
  * 
  * Pour afficheur ILI9341 2.8" tactile
  * Le board reliant l'ESP32 à l'afficheur ILI9341 est en conséquence totalement différent de ceux utilisés pour les autre modules
  * Les fichiers Kicad sont fournis dans le fichier "documents.zip" au bas de l'article PFD sur mon site 'silicium628.fr'
  *
  ***********************************************************/

#include <Arduino.h>


//==================================
	 String version = "2026-09-08";
//==================================
 


#include "Constantes_partagees.h"
#include "MCDU_main.h"
#include "voyant.h"
#include "bouton.h"
#include "etiquette.h"

#include "TFT_eSPI.h"

#include "FG_data.h"
#include "Fonctions_MCDU.h"


#include <WiFi.h>
#include <HTTPClient.h> // Remarque: les fonctions WiFi consomment 50% de la mémoire flash (de programme)

const char* ssid = "PFD_srv"; // connection en tant que client au même serveur (le PFD) que le ND.
const char* password = "72r4TsJ28"; // doit être identique à celui du serveur du PFD

const char* srvName_flags = "http://192.168.4.1/flags";
const char* srvName_num_airport = "http://192.168.4.1/num_airport";

uint16_t options_nav = 0; // mot dont chaque bit correspond à un bouton
uint16_t compteur1=0;
uint16_t compteur2=0;
uint16_t etat=0;

uint8_t SDcardOk=0;
uint8_t num_capture = 0;
uint16_t color565px;

uint16_t flags=0;
String recp_flags;  // qui sont émis par le serveur (PFD) en réponse à la requette

String recp_msg;
String memo_msg;

String recp_num_airport="---";
String memo_num_airport;

String recp_reponse;
String memo_recp_reponse;

int16_t num_airport=0;
uint16_t flags_i;
uint16_t memo_flags_i;

uint32_t memo_micros=0;
uint32_t temps_ecoule;

#define SPI_READ_FREQUENCY 16000000

#define XPT2046_IRQ 36
#define XPT2046_MOSI 32 //T_DI 32
#define XPT2046_MISO 39 //T_DO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33

//attention: ces valeurs ne sont pas celles par défaut pour SDA et SCL
const int GPIO_SDA = 21; 
const int GPIO_SCL = 22; 

//const int analogPin = 35;

const int _DX = 320;
const int _DY = 240;

SPIClass mySpi = SPIClass(VSPI);
XPT2046_Touchscreen ts(XPT2046_CS, XPT2046_IRQ);

SPIClass mySpi2 = SPIClass(HSPI);

TFT_eSPI TFT = TFT_eSPI(); // Configurer le fichier User_Setup.h de la bibliothèque TFT_eSPI au préalable


struct POINT
{
	uint16_t x;
	uint16_t y;
};

// coordonnées des points de calibrage de l'écran tactile 
POINT A, B;

uint16_t x_touch, y_touch;
uint16_t memo_x_touch, memo_y_touch;

//uint8_t SDcardOk=0;

boolean do_capt_screen = false;
boolean MA = false; // Master Warning

float raddeg = M_PI/180.0;
float deg_to_rad = 2.0 * M_PI /360.0;

boolean test_touch_screen = false; // mettre true pour activer le test

VOYANT voyant_MA; // Master Warning
VOYANT voyant_AP; // auto-pilot

ETIQUETTE etiquette_destination;
ETIQUETTE etiquette_info;

TOUCH_BOUTON bt_CE; // copie d'écran (vers SDcard)
TOUCH_BOUTON bt_RST;
TOUCH_BOUTON bt_HELP;
TOUCH_BOUTON bt_OK;

//boutons exclusifs :
//TOUCH_BOUTON bt_P[6]; // 6 boutons route

PANEL_BOUTONS panel_bt_decollage;
PANEL_BOUTONS panel_bt_Route;
PANEL_BOUTONS panel_bt_landing;
PANEL_BOUTONS panel_bt_roulage;

//boutons exclusifs :
//TOUCH_BOUTON bt_landing_normal;
//TOUCH_BOUTON bt_landing_court;

//boutons exclusifs :
//TOUCH_BOUTON bt_Roulage;
//TOUCH_BOUTON bt_frein; // stop roulage

uint16_t compte=0;
uint16_t couleur_fond_ecran = 229; // outil : https://rgbcolorpicker.com/565
 

//-----------------------------------------------------------------------------------


void set_bit(uint16_t *mot_i, uint16_t val_bit) // val_bit étant la valeur du bit (le poids) c.a.d =1 ou 2 ou 4 ou 8...
{*mot_i |= val_bit;}

void raz_bit(uint16_t *mot_i, uint16_t val_bit) // val_bit étant la valeur du bit (le poids) c.a.d =1 ou 2 ou 4 ou 8...
{*mot_i &= ~val_bit;}

void inv_bit(uint16_t *mot_i, uint16_t val_bit) // val_bit étant la valeur du bit (le poids) c.a.d =1 ou 2 ou 4 ou 8...
{*mot_i ^= val_bit;}

uint8_t read_bit(uint16_t mot_i, uint16_t val_bit) // val_bit étant la valeur du bit (le poids) c.a.d =1 ou 2 ou 4 ou 8...
{ if ((mot_i & val_bit) == 0 ) {return 0;} else {return 1;} }


uint8_t is_in(float valeur_i, float min, float max)
{ if ((valeur_i >= min) && (valeur_i <= max)) {return 1;} else {return 0;}}

void borne_in(float *valeur_i, float min, float max)
{ if (*valeur_i < min) {*valeur_i = min;} if (*valeur_i > max) {*valeur_i = max;}}


float degTOrad(float angle) {return (angle * M_PI / 180.0);}


uint8_t decToBcd(int val) {return (uint8_t) ((val / 10 * 16) + (val % 10));}
uint16_t Color_To_565(uint8_t r, uint8_t g, uint8_t b) {return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);}


void RGB565_to_888(uint16_t color565, uint8_t *R, uint8_t *G, uint8_t *B)
{
	*R=(color565 & 0xF800) >> 8;
	*G=(color565 & 0x7E0) >> 3;
	*B=(color565 & 0x1F) << 3 ;
}


void init_WiFi()
{
	WiFi.persistent(false);
	WiFi.begin(ssid, password);
	delay(2000);
}


void httpGetParams() // paramètres divers depuis le PFD & envoi état des états des boutons en argument de la requette
{
	String s1;
	String s2;
//affi_txt(210, 95, "1a",  BLANC, &TFT);

	HTTPClient http;

	s1= "?btMCDU=";  // sw1 (nom arbitraire..) pour désigner l'argument n°1 envoyé dans l'URL de la requête

	s2= String(options_nav); // 'options_nav' est configuré par les boutons virtuels de ce module MCDU
	s1 += s2;
//affi_txt(210, 95, "1b",  BLANC, &TFT);
	http.begin(srvName_flags+s1);
	int httpResponseCode = http.GET();
	if (httpResponseCode>0)	{ recp_flags = http.getString();}
	http.end();
//affi_txt(210, 95, "1c",  BLANC, &TFT);	
}



void httpGet_num_airport() // envoie requete au serveur
{
	HTTPClient http;
	
	http.begin(srvName_num_airport);
	int httpResponseCode = http.GET();
	if (httpResponseCode>0)	{ recp_num_airport = http.getString();}
	http.end();

}


void interroge_WiFi()
{
// voir les "server.on()" dans la fonction "setup()" du code du PFD pour la source des données

	if(WiFi.status()== WL_CONNECTED )
	{
		httpGetParams();
		//delay(20);
		flags_i = recp_flags.toInt();
	}
}


void requete_WiFi_3()
{
// voir les "server.on()" dans la fonction "setup()" du code du PFD pour la source des données
	recp_num_airport= "";
	if(WiFi.status() == WL_CONNECTED )
	{
		httpGet_num_airport(); // envoie requete au serveur, avec position des switch en argument.
	}
}


void init_SDcard()
{
	Serial.println("---------------------");
	Serial.println("init_SDcard()");	
	String s1;
	
	TFT.fillRect(0, 0, 480, 320, NOIR); // efface
	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF1);

 	if(!SD.begin(5, mySpi2)) 
	{ 
		Serial.println("Card Mount Failed");
		SDcardOk=0;
	} 
	else 
	{ 
		Serial.println("SDcard OK");	
		SDcardOk=1;
		TFT.fillRect(0, 0, 480, 320, VERT);
		delay(100);
	}
  
    uint8_t cardType = SD.cardType();

    if(cardType == CARD_NONE)
    {
		Serial.println("NO SDcard"); 
		SDcardOk=0;
        return;
    }
    
	Serial.print("SDcard Type: ");
	if(cardType == CARD_SD) {Serial.print("SDSC");}
    else if(cardType == CARD_SDHC) {Serial.println("SDHC");}
	
	uint32_t cardSize = SD.cardSize() / (1024 * 1024);
	s1=(String)cardSize + " GB";
	Serial.println(s1); Serial.println();
	
	delay (100);
	TFT.fillRect(0, 0, 480, 320, NOIR); // efface
}


// Fonction optimisée pour l'afficheur ILI9341 320x240 avec la  library 'TFT_eSPI'
// ne convient PAS pour les ESP32 Wroom + afficheur 3.5" TFT 480x320
void write_TFT_on_SDcard() // enregistre image bmp 320x240 RGB565 (5+6+5 = 16bits/px)
{
	Serial.println("write_TFT_on_SDcard()");

	if (SDcardOk==0) 
	{
		//affi_message ( "SDcard absente", "Capture ecran impossible", "", "", "");
		return;
	}
	int32_t x, y;
	uint16_t color565;

	uint8_t  lineBuffer8[(320*2)];
	uint16_t lineBuffer16[320];
	uint8_t octet_A, octet_B;
	String s1, s2;	

	s1 +="/bmp565/capture";
	s1 += String(num_capture);
	s1 += ".bmp" ;

	File file1 = SD.open(s1, FILE_WRITE); // crée le fichier si pas présent
	if (file1)
	{
		// création entête bmp565 - 138 octets; voir bmp565_header[] dans le fichier main.h
		for(uint8_t i=0; i<138; i++) { file1.write(bmp565_header[i]); }

		TFT.setTextColor(VERT, NOIR);
		for (int16_t y=239; y>=0; y--)
		{
			TFT.readRect(0, y, 320, 1, lineBuffer16); // lit une ligne
			s2=String(y/24);
			TFT.drawString(s2, 2, 2); // pour terminer par une écriture

			uint16_t i=0;
			for (int16_t x=0; x<320; x++) //320
			{
				color565px = lineBuffer16[x];  // BBBBBrrr rrrVVVVV 
				octet_A = (color565px & 0b1111111100000000) >> 8;
				octet_B = (color565px & 0b0000000011111111);

				lineBuffer8[i]   = octet_A;
				lineBuffer8[i+1] = octet_B;
				i+=2;
			}	
			file1.write(lineBuffer8, sizeof(lineBuffer8)); 
		} 
		num_capture ++;
	}
}



void affi_train(uint16_t couleur_i) // "Les trois vertes !"
{
	uint16_t x = 50;
	uint16_t y = 8;

	TFT.drawRect(x, y, 30, 22, GRIS_FONCE);
	TFT.fillCircle(x+5, y+15, 3, couleur_i);
	TFT.fillCircle(x+25, y+15, 3, couleur_i);
	TFT.fillCircle(x+15, y+5, 3, couleur_i);
}


void affi_flags() // en haut à droite
{
	TFT.setTextFont(1);
	TFT.setTextColor(JAUNE);

	affi_txt(200, 35, "from PFD: " + String(flags_i), JAUNE, &TFT);
	affi_binaire(300, 45, flags_i, &TFT);

}


void write_fichier_params() // sur la SDcard
{


	File file1 = SD.open("/params.txt", FILE_WRITE);
	String s1;


// enregistrement de la calibration de l'écran tactile :

	s1 ="[Ax]";	s1 += "<";	s1 += String(A.x); 	s1 +=">";
	file1.println(s1); 

	s1 ="[Ay]";	s1 += "<";	s1 += String(A.y); 	s1 +=">";
	file1.println(s1); 

	s1 ="[Bx]";	s1 += "<";	s1 += String(B.x); 	s1 +=">";
	file1.println(s1); 

	s1 ="[By]";	s1 += "<";	s1 += String(B.y); 	s1 +=">";
	file1.println(s1); 

	file1.close();
	delay(100);
}


/*
void TS_calibrate()  // calibrage de l'écran tactile
{
	uint8_t L = 20;
	boolean ok; 
	String s1;

	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF1);

// traitement point A
	TFT.fillScreen(NOIR);
	TFT.drawString("Cliquez sur le point A", 20, 50);
	delay(300);
	TFT.drawFastVLine(10, 0, L, JAUNE);
	TFT.drawFastHLine(0, 10, L, JAUNE);  
	ok = false;
	while (! ok)
	{
		if (ts.tirqTouched() && ts.touched()) 
		{
			memo_y_touch = y_touch;
			TS_Point p = ts.getPoint();
			s1 = "Ax=" + String(p.x) + "     "; TFT.drawString(s1, 50, 100);
			s1 = "Ay=" + String(p.y) + "     "; TFT.drawString(s1, 50, 120);
			delay(100);

			if( (p.x<600) && (p.y < 600)) // évite de valider une position irréaliste 
			{
				A.x = p.x; // point A : variable globale
				A.y = p.y;
				ok = true;
			}
		}	
	}	

// traitement point B
	TFT.fillScreen(NOIR);
	delay(300);
	TFT.drawString("Cliquez sur le point B", 20, 70);
	TFT.drawFastVLine(310, 230-L/2, L, JAUNE);
	TFT.drawFastHLine(310-L/2, 230, L, JAUNE);
	ok = false;
	while (! ok)
	{
		if (ts.tirqTouched() && ts.touched()) 
		{
			memo_y_touch = y_touch;
			TS_Point p = ts.getPoint();
			s1 = "Bx=" + String(p.x) + "     "; TFT.drawString(s1, 50, 150);
			s1 = "By=" + String(p.y) + "     "; TFT.drawString(s1, 50, 170);
			delay(100);

			if( (p.x>3600) && (p.y > 3400)) // évite de valider une position irréaliste
			{
				B.x = p.x;
				B.y = p.y;
				ok = true;
			}
		}	
	}

	if(SDcardOk==1)
	{	
		TFT.fillScreen(NOIR);
		TFT.drawString("Ok", 20, 60);
		TFT.setFreeFont(FF0);
		TFT.drawString("Write fichier params sur la SD", 10, 90);
		write_fichier_params();
		delay(1000);
	}
	TFT.fillScreen(NOIR);
}
*/




/* fonction à réécrire pour la memoire EEPROM ou SPDIFF

String read_line_params(uint16_t line_num)
{
	int i = 1;
	char buffer[64];
	String s;	

	File file = SD.open("/params.txt", "r");

	while (file.available())
	{
		int l = file.readBytesUntil('\n', buffer, sizeof(buffer));
		buffer[l] = 0;
		if (line_num == i)
		{
			s = buffer;
			file.close();
			return(s);
		}  
		i++;
	}
	return "";
}
*/

int32_t extract_params(String ligne, String label)
{
	String s2;
	uint32_t valeur = 0;
	int p1, p2;

	p1 = ligne.indexOf('[');	p2 = ligne.indexOf(']');
	s2 = ligne.substring(p1+1, p2);

	if (s2 == label)
	{
		p1 = ligne.indexOf('<');	p2 = ligne.indexOf('>');
		s2 = ligne.substring(p1+1, p2);
		valeur = s2.toInt();
		return valeur;
	}
	return -1;
}


void set_params()
{
	A.x=578;
	A.y=540;
	B.x=3810;
	B.y=3666;
}


// fonction à réécrire pour la memoire EEPROM ou SPDIFF
/*
void read_params() // lecture du fichier '/params.txt' en SD	
{
	Serial.println("lecture du fichier '/params.txt' en SD");
	String s1;
	int valeur;
	
	s1 = "Z";
	uint8_t n=1;
	while (s1 !="") 
	{
		s1 =  read_line_params(n); // retourne(par exemple): [couleur_fond]<267> 

		//valeur = extract_params(s1, "frequence");
		//if(valeur != -1) {frequence = valeur;}

		valeur = extract_params(s1, "Ax");
		if(valeur != -1) {A.x = valeur;}

		valeur = extract_params(s1, "Ay");
		if(valeur != -1) {A.y = valeur;}

		valeur = extract_params(s1, "Bx");
		if(valeur != -1) {B.x = valeur;}

		valeur = extract_params(s1, "By");
		if(valeur != -1) {B.y = valeur;}
		n++;
	}
}
*/

void affi_page_info()
{
// affiche une page d'information:

	TFT.fillScreen(NOIR);
	TFT.setTextColor(JAUNE, NOIR);	TFT.setFreeFont(FF1);
	uint16_t y=0;
	TFT.drawString("FGFS_MCDU ESP32 + ILI9341 ", 0, y);	y+=20;
	String s1="version " + version;
	TFT.drawString(s1, 0, y);	y+=20;

	TFT.setTextColor(CYAN, NOIR);
	TFT.drawString("Silicium628", 0, y);	y+=40;
}


void trace_sinusoide()
{
	float y;
	float v;
	for (uint16_t n=0; n<320; n++)
	{
		v = (float)n/20.0;
		y = 180-50*sin(v);
		TFT.drawPixel(n, (int)y, VERT);
	}
}	


void init_panels()
{
	voyant_AP.init(10, 10, 30, 20);
	voyant_AP.libel="AP";
	voyant_AP.affiche(BLANC, ROUGE, &TFT);

	voyant_MA.init(100, 10, 30, 20);
	voyant_MA.libel=" !";
	voyant_MA.affiche(BLANC, ROUGE, &TFT);
	

 	etiquette_destination.init(5, 60, 310, 30, "DEPART / DESTINATION");
	etiquette_destination.affiche("-----", 1, 65216,  &TFT);

 	etiquette_info.init(130, 107, 185, 30, "INFO");
	etiquette_info.affiche("-----", 1, 1055, &TFT);	

	String libels_D[5] = {"TAKE OFF","","","",""};
	panel_bt_decollage.init(10, 112, 120, 1,"", libels_D, &TFT);
	panel_bt_decollage.deselecte_tous(&TFT);
	panel_bt_decollage.affiche(&TFT);

	String libels_R[5] = {" X","PTi","RW"," A"," B"};
	panel_bt_Route.init(10, 160, 210, 5,"ROUTE", libels_R, &TFT);
	panel_bt_Route.affiche(&TFT);

	String libels_L[5] = {"norm","crt","","",""};
	panel_bt_landing.init(10, 205, 110, 2,"LANDING", libels_L, &TFT);
	panel_bt_landing.bt_P[0].selected=true;
	panel_bt_landing.affiche(&TFT);

	String libels_R2[5] = {" X","GO","","",""};
	panel_bt_roulage.init(130, 205, 90, 2,"ROULAGE", libels_R2, &TFT);
	panel_bt_roulage.affiche(&TFT);


	bt_RST.init(290, 220, 25, 15, 2, GRIS);
	bt_RST.label="RST";
	bt_RST.affiche(BLANC, 1, &TFT);

	bt_CE.init(290, 200, 25, 15, 2, GRIS);
	bt_CE.label="CE";  // CAPTURE ÉCRAN
	bt_CE.affiche(BLANC, 1, &TFT);

	bt_HELP.init(290, 90, 25, 15, 2, BLEU);
	bt_HELP.label=" ?";
	bt_HELP.affiche(BLANC, 1, &TFT);

}




void affi_message1()
{
	TFT.fillScreen(330); //  430   40510
	TFT.drawRect(0, 0, 320, 240, BLANC); // cadre principal pourtour de l'écran	
	TFT.setTextColor(BLANC);	
	TFT.setFreeFont(FF0);

	uint16_t y = 10;
	uint16_t dy = 12;

	TFT.drawString("Assurez-vous que l'Airport choisi sur le module ND ", 5, y); 
	y+=dy;  
	TFT.drawString("est bien celui sur lequel se trouve l'avion.", 5, y); 
	y+=dy;
	TFT.drawString("Cela permet au systeme de connaitre l'orientation ", 5, y); 
	y+=dy;
	TFT.drawString("et la longueur de la piste pour decoller.", 5, y); 
	y+=dy; 
	y+=dy;
	TFT.drawString("Apres le decollage il faudra choisir la destination.", 5, y); y+=dy;
	TFT.drawString("", 5, y); y+=dy;

	bt_OK.init( 280, 210, 30, 20, 2, GRIS);
	bt_OK.label="OK";
	bt_OK.affiche(BLANC, 2, &TFT);
	
	bt_OK.cliked=false;
	while (! bt_OK.cliked) 
	{
		if (ts.tirqTouched() && ts.touched()) 
		{
			get_XY_touch();
			bt_OK.test_clic(x_touch, y_touch);
		}
	}
	init_affichages();
}


 void init_affichages()
 {
	TFT.fillScreen(330); 
	TFT.drawRect(0, 0, 320, 240, BLANC); // cadre principal pourtour de l'écran	

	init_panels();
	delay(100);
	panel_bt_decollage.deselecte_tous(&TFT);
	panel_bt_Route.deselecte_tous(&TFT);
	panel_bt_roulage.deselecte_tous(&TFT);


	affi_train(VERT);

 }



void setup() 
{
	Serial.begin(115200);
	delay(20);

	init_WiFi();

	//Wire.begin(GPIO_SDA, GPIO_SCL, 2000000);

	// Start the SPI for the touch screen and init the TS library
	mySpi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS); 
	ts.begin(mySpi);
	ts.setRotation(3);

	set_params(); 

	init_SDcard();
	delay(20);

	TFT.init();
	TFT.setRotation(3); // 0..3 à voir, suivant disposition de l'afficheur
	TFT.fillScreen(NOIR);

	init_FG_AIRPORTS();

	// si les coordonnées des points de calibrage sont aux fraises...
	if ( (A.x <300) || (A.x >700) || (A.y <200) || (A.y >600) || 
	(B.x < 3500) || (B.x > 3900) || (B.y <3400) || (A.y >3800) )
	{
		//TS_calibrate();
	}

	affi_page_info();
	delay(500);
	compteur1=0;
	compteur2=0;

	init_affichages();
}


void get_XY_touch() // pour affi 320 x 240px
{
	TS_Point p = ts.getPoint();
	float dx = B.x - A.x;
	float ech_x = 290.0 / dx;//300
	float x0 = A.x;

	float dy = B.y - A.y;
	float ech_y = 210.0 / dy; //230
	float y0 = A.y;

	memo_x_touch = x_touch;
	memo_y_touch = y_touch;

	x_touch = 12 + uint16_t( (p.x - x0) * ech_x); //x_touch = 10 + uint16_t( (p.x - x0) * ech_x); 
	if(x_touch > 319){x_touch = 0;}
	y_touch = 15 + uint16_t( (p.y - y0) * ech_y);  //y_touch = 10 + uint16_t( (p.y - y0) * ech_y); 
	if(y_touch > 239){y_touch = 0;} 
}

/*
boolean ZT1(uint16_t bit1_i) // détecte le passage de 0 à 1
{
	if((read_bit(memo_flags_i, bit1_i) == 0) && (read_bit(flags_i, bit1_i) == 1)) return 1; 
	return 0;
}
*/


void traite_affi_etiquette_info()
{

/* rappel xor :
0^0 = 0
0^1 = 1
1^0 = 1
1^1 = 0
*/  
	uint16_t diff_flag = flags_i  ^ memo_flags_i; // xor ; si un bit a changé son résultat et =1 
	memo_flags_i = flags_i;
	
// *****  maintenant on va traiter uniquement les bits qui ont changé ******

	if((read_bit(diff_flag, flg_bit_take_off) == 1) && (read_bit(flags_i, flg_bit_take_off) == 1))
	{
		panel_bt_decollage.bt_P[0].selected=true; //le voyant GO passe au vert
		panel_bt_decollage.affiche(&TFT);
	}

	if(read_bit(flags_i, flg_bit_V1) == 1)
	{ 
		etiquette_info.affiche("V1", 1, 1055, &TFT); 
		delay(1000);
	} 
	else { etiquette_info.affiche("-", 1, 1055, &TFT); }

	
	if (read_bit(flags_i, flg_bit_bad_RWY) == 1) 
	{ 
		MA = true;
		etiquette_info.affiche("BAD runway", 1, 0xf800, &TFT); // rouge
	} 
	else 
	{ 
		MA = false;
		etiquette_info.affiche("-", 1, 1055, &TFT); // bleu
	} 


	if(		// si un de ces bits a changé
		(read_bit(diff_flag, flg_bit_route) == 1) ||
		(read_bit(diff_flag, flg_bit_nav_to_point_i) == 1) ||
		(read_bit(diff_flag, flg_bit_nav_to_RW) == 1) ||
		(read_bit(diff_flag, flg_bit_nav_to_point_AA) == 1) ||
		(read_bit(diff_flag, flg_bit_nav_to_point_BB) == 1) 
	) 
	{
		if(read_bit(flags_i, flg_bit_route) == 1) 
		{ 
			String s1 = "ROUTE vers ";
			if (read_bit(flags_i, flg_bit_nav_to_point_i) == 1) {s1+="pt i";}
			if (read_bit(flags_i, flg_bit_nav_to_RW) == 1) {s1+="RW";}
			if (read_bit(flags_i, flg_bit_nav_to_point_AA) == 1) {s1+="pt A";}
			if (read_bit(flags_i, flg_bit_nav_to_point_BB) == 1) {s1+="pt B";}
	 	
			etiquette_info.affiche(s1, 1, 1055, &TFT); 
		}
	}	

}



boolean TourDeRole = true;

void tous_les_500ms() // pour ne pas trop solliciter le serveur
{
	compteur2++;

	TourDeRole = ! TourDeRole;

	if(TourDeRole == true)
	{
		TFT.fillCircle(300, 170, 3, VERT);
		interroge_WiFi(); // et envoie les arguments le cas échéant

	}
	else
	{
		TFT.fillCircle(300, 170, 3, ROUGE);
		requete_WiFi_3();
	}

	//affi_txt(210, 95, String(compteur2),  BLANC, &TFT);
	//delay(100);
	
	
	String s1;

//Serial.print("recp_num_airport "); Serial.println(recp_num_airport);
//Serial.print("num_airport "); Serial.println(num_airport);

	if(recp_num_airport != memo_num_airport)
	{
		memo_num_airport = recp_num_airport;
		num_airport = recp_num_airport.toInt();


		if(num_airport >= nb_elements-1) {num_airport=0;}

		s1 = (String)liste_airports[num_airport].ID_OACI;
		s1 += " ";
		s1 += (String)liste_airports[num_airport].nom;
		s1 = s1.substring(0,23);
		etiquette_destination.affiche(s1, 2, 65216, &TFT);
	}
	affi_flags();



/*
	//si l'avion n'est plus au sol, on éteint le bouton [GO] (décollage)
	if(read_bit(flags_i, flg_bit_au_sol) == 0) // info venant du PFD
	{
		panel_bt_decollage.deselecte_tous(&TFT);
	}
*/
	//si l'avion a est passé en mode autoland, on déselecte le go to... 
	
	if(read_bit(flags_i, flg_bit_autoland) == 1) // info venant du PFD
	{
		panel_bt_Route.deselecte_tous(&TFT);

		raz_bit(&options_nav, opt_bit_nav_to_pti);		// ce qui cessera d'envoyer la consigne d'auto-route au PFD
		raz_bit(&options_nav, opt_bit_nav_to_piste);	//
		raz_bit(&options_nav, opt_bit_nav_to_ptAA);		//
		raz_bit(&options_nav, opt_bit_nav_to_ptBB);		//
	}

	if(read_bit(flags_i, flg_bit_bad_RWY) == 1) // si l'avion ne se trouve pas sur la piste sélectionnée sur la 'module ND'
	{
		//en effet, le pilote auto doit connaitre l'orientation exacte de la piste pour le décollage
		panel_bt_decollage.deselecte_tous(&TFT);
	}

	if(read_bit(flags_i, flg_bit_FG_AP) == 1) // ce qui se prodit en fin de décollage
	{
		panel_bt_decollage.deselecte_tous(&TFT);
		raz_bit(&options_nav, opt_bit_decollage);
	}

	if(read_bit(flags_i, flg_bit_FG_AP) == 1) { voyant_AP.affiche(BLANC, ROUGE, &TFT);	}
	else { voyant_AP.affiche(NOIR, GRIS_FONCE, &TFT);}

	if(MA == true) { voyant_MA.affiche(BLANC, ROUGE, &TFT);	}
	else {voyant_MA.affiche(NOIR, GRIS_FONCE, &TFT);}

	if(read_bit(flags_i, flg_bit_gear_down) == 1) { affi_train(VERT); } else { affi_train(GRIS); }

	
	if(flags_i != memo_flags_i)
	{
		traite_affi_etiquette_info();
		//etiquette_info.affiche(String(flags_i), 1, VERT, &TFT);
	}

	affi_options_nav();

}


void affi_options_nav()
{
	affi_txt(200, 5, "OUT: " + String(options_nav),  JAUNE, &TFT);
	affi_binaire(300, 15, options_nav, &TFT);
}


void traitement_boutons_graphiques()
{
	uint8_t n_bt_D; // numero des boutons du panel 'DECOLLAGE'
	uint8_t n_bt_R; // numero des boutons du panel 'ROUTE'
	uint8_t n_bt_L; // numero des boutons du panel 'LANDING'
	uint8_t n_bt_R2;// numero des boutons du panel 'ROULAGE'

	uint16_t memo_options_nav = options_nav;

//  DECOLLAGE	
	n_bt_D = panel_bt_decollage.traite_boutons(x_touch, y_touch, 1, &TFT);
	if (n_bt_D != 255) 
	{
		if (n_bt_D==0) {set_bit(&options_nav, opt_bit_decollage);} // bouton unique,0  -  GO
	}
	

// ROUTE
	n_bt_R = panel_bt_Route.traite_boutons(x_touch, y_touch, 7, &TFT);
	if (n_bt_R != 255) 
	{ 
		options_nav &= 0b1111111111100001; // RAZ des bits concernés dans la cible "options_nav"
		switch (n_bt_R) 
		{
			case 0: 
			{ 
				panel_bt_Route.bt_P[0].affiche(ROUGE, 1, &TFT);  // flash rouge
				delay(100);
				panel_bt_Route.deselecte_tous(&TFT); // NO route

			} break;
			case 1: {set_bit(&options_nav, opt_bit_nav_to_pti);}  break;
			case 2: {set_bit(&options_nav, opt_bit_nav_to_piste);} break;
			case 3: {set_bit(&options_nav, opt_bit_nav_to_ptAA);}  break;
			case 4: {set_bit(&options_nav, opt_bit_nav_to_ptBB);} break;
			default: { } break;
		}
		delay(100);
	}


// LANDING
	n_bt_L = panel_bt_landing.traite_boutons(x_touch, y_touch, 2, &TFT);
	if (n_bt_L!=255) 
	{
		if (n_bt_L==0) {raz_bit(&options_nav, opt_bit_att_short);} // landing normal
		if (n_bt_L==1) {set_bit(&options_nav, opt_bit_att_short);} // landing court
	}	


// ROULAGE	
	n_bt_R2 = panel_bt_roulage.traite_boutons(x_touch, y_touch, 2, &TFT);
	if (n_bt_R2!=255) 
	{
		if (n_bt_R2==0) 
		{
			panel_bt_roulage.bt_P[0].affiche(ROUGE, 1, &TFT); // flash rouge
			delay(100);
			raz_bit(&options_nav, opt_bit_roulage);
			panel_bt_roulage.deselecte_tous(&TFT);
		}
		if (n_bt_R2==1) {set_bit(&options_nav, opt_bit_roulage);} 
	}	



	if (options_nav != memo_options_nav) 
	{
		affi_options_nav();
		raz_bit(&options_nav, opt_bit_validation); // pour ne pas l'afficher localement; voir ci-dessous sa RA1
		affi_options_nav();

	}

	set_bit(&options_nav, opt_bit_validation); // remis à 1 en vue de la transmission

	delay(300);
}



void loop() 
{
/*
	temps_ecoule = micros() - memo_micros;
	if (temps_ecoule  >= (1E6 + 100) )  // (>=) et pas strictement égal (==) parce qu'alors on rate l'instant en question
	// + 100 (us) pour se décaler / accès de l'autre module (ND)
	{
		memo_micros = micros();
		toutes_les_1s();
	}
*/
	compteur1++;
	if(compteur1 >= 50)
	{
		compteur1=0;
		tous_les_500ms();
	}


	if (ts.tirqTouched() && ts.touched()) 
	{
		memo_y_touch = y_touch;
		get_XY_touch();
		traitement_boutons_graphiques();

		bt_RST.cliked=false;
		bt_RST.test_clic(x_touch, y_touch);
		if(bt_RST.cliked == true)
		{
			bt_RST.affiche(ROUGE, 1, &TFT);
			delay(100);
			bt_RST.cliked == false;
			TFT.fillScreen(330);
			options_nav = 0;
			init_panels();
			num_airport=0;
			memo_num_airport=1;  // ce qui permettra la mise à jour à la bonne valeur reçue du PFD
		}

		bt_CE.cliked=false;
		bt_CE.test_clic(x_touch, y_touch);
		if(bt_CE.cliked == true)
		{
			bt_CE.affiche(VERT, 1, &TFT);
			delay(100);
			bt_CE.cliked == false;
			write_TFT_on_SDcard();
			bt_CE.selected == false;
			bt_CE.affiche(GRIS, 1, &TFT); // éteint le bouton lorsque la capture d'écran est terminée
		}

		bt_HELP.cliked=false;
		bt_HELP.test_clic(x_touch, y_touch);
		if(bt_HELP.cliked == true)
		{
			bt_HELP.cliked == false;
			bt_HELP.selected == false;
			affi_message1();
		}


	}	


	delay(10);
	
}

