/* ************************************************************************************
	Radio TEF6686  par Silicium628
	pour ma carte (fab : JLCPCB) ESP32 Wroom + afficheur 2.8" TFT 240x320  + SDcard
	
	Cette version utilise la SDcard

************************************************************************************ */

// REMARQUES: 
// 1) lorsque la carte est connectée sur un bus USB de l'ordinateur,
// un terminal série tel que CuteCom affichera beaucoup de choses en temps réel.
// 2) Pour s'y retrouver dans le code, le plus simple est de partir de la fonction 'loop()' qui appelle les autres 
// 3) Le fichier User_Setup dans le dossier 'lib/TFT_eSPI' est spécifique à cette configuration
// pour ce qui concerne les connexions mais aussi les limitations de fréquence du bus SPI

#include <Arduino.h>
#include "main.h"
//#include "Tuner_Patch_Lithio_V102_p224.h" // voir la fonction 'Tuner_Patch()' dans la fonction 'setup()'

String version = "1.14.0"; // (avec enregistrement sur SD card)

#include "FS.h"
#include "SD.h"
#include "Wire.h"
#include <stdint.h>

#include <Free_Fonts.h>
#include "TFT_eSPI.h" // Hardware-specific library
#include "SPI.h"
#include "Digit_Font.h"
#include <XPT2046_Touchscreen.h>


#include "constantes_628.h"
#include "Couleurs_AEC.h"
#include "DSP_INIT_628.h"
#include "driverTEF6686_628.h"

#define SPI_READ_FREQUENCY 16000000

#define bande_SW (frequence > 1500) && (frequence < 28000)
#define bande_interdite1 (frequence > 28000) && (frequence < 88000)
#define bande_interdite2 (frequence > 108000) && (frequence < 118000)
#define bande_FM (frequence >= 88000) && (frequence < 108000)
#define bande_AIR (frequence >= 118000) && (frequence < 138000)

#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

//sur le connecteur CN1 de la carte CYD sérigraphiée 'ESP32-2432S028'; 
//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 GPIO_BL = 4; //   pour LED backlight ILI9341

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

#define High_16bto8b(a) ((uint8_t)((a) >> 8))
#define Low_16bto8b(a) ((uint8_t)(a ))
#define Convert8bto16b(a) ((uint16_t)(((uint16_t)(*(a))) << 8 |((uint16_t)(*(a+1)))))

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


TFT_eSprite sprite_frq = TFT_eSprite(&TFT);
TFT_eSprite sprite_ligne1 = TFT_eSprite(&TFT);


struct ETALON_TS // etalon touch screen
{
    int16_t x0;
    int16_t dx;
	int16_t y0;
    int16_t dy;
};

ETALON_TS eTS;


struct POINT
{
	uint16_t x;
	uint16_t y;
};

// coordonnées des points de calibrage de l'écran tactile 
// (calibration ? étalonnage ?? ajustage ??? boaf, m'enfin !!)
POINT A, B;

uint16_t x_touch, y_touch;
uint16_t memo_x_touch, memo_y_touch;
uint16_t memo_tiret_H[20];
uint16_t memo_tiret_V[20];
uint16_t valeur_bargraph=0;

uint8_t SDcardOk=0;
uint16_t color565px;
boolean stop = false;
boolean do_capt_screen = false;
boolean affi_AB = false;

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

uint16_t compteur1 = 0;
uint16_t compteur2 = 0;
uint16_t compteur3 = 0;
uint8_t num_capture = 0;

uint32_t frequence=10000; 
uint32_t saut_freq;
uint16_t seuil = 50;
uint16_t memo_seuil = 50;

GROUPE_FREQUENCES groupe_SW;
GROUPE_FREQUENCES groupe_FM;
GROUPE_FREQUENCES groupe_AIR;
GROUPE_FREQUENCES groupe_SCAN;

uint32_t frq_preset_SW[8]; // 8 fréquences attribuées aux boutons [preset1] à [preset8] soit 8x4=32 octets
uint32_t frq_preset_FM[8]; // 8 fréquences attribuées aux boutons [preset1] à [preset8] soit 8x4=32 octets
uint32_t frq_preset_AIR[8];// 8 fréquences attribuées aux boutons [preset1] à [preset8] soit 8x4=32 octets total 32*3 = 96 octets

uint32_t memo_freq;
uint16_t frq_preset_adr_0;
String frequence_txt = "";

TOUCH_BOUTON bt_info;
TOUCH_BOUTON bt_sleep;
TOUCH_BOUTON bt_quiet; // calme (anti-parasites)
TOUCH_BOUTON bt_TEST;

TOUCH_BOUTON bt_mode_FRQ, bt_mode_MEM;
TOUCH_BOUTON bt_plus, bt_moins;

TOUCH_BOUTON bt_SD_write, bt_erase_1F, bt_LST; //, bt_SD_LST, bt_SD_RAZ;
TOUCH_BOUTON bt_1, bt_2, bt_3, bt_4, bt_5, bt_6; // 6 boutons au dessus des chiffres de la fréquence
TOUCH_BOUTON bt_coul;
TOUCH_BOUTON bt_coul_to_SD;

TOUCH_BOUTON Bt_SCAN, bt_scan_air;
TOUCH_BOUTON bt_scan_SW, bt_scan_FM;

TOUCH_BOUTON bt_RST_affi; // "ok"

TOUCH_BOUTON bt_LEV, bt_SNR, bt_re_scan, bt_scan_suivant;
TOUCH_BOUTON bt_seuil_plus, bt_seuil_moins;


// ---------- gridPad ------------
GRID_PAD gridPad1;

// ---------- numPad ------------
NUM_PAD numPad1;

// ---------- presetPad1 ------------
PRESET_PAD presetPad1;

// ---------- swPad1 ------------
SW_PAD swPad1;

// -------------------------------
TOUCH_BOUTON bt_SW, bt_FM, bt_AIR, bt_SCN;
TOUCH_BOUTON bt_LED;
TOUCH_BOUTON bt_mute;
TOUCH_BOUTON bt_reset;
TOUCH_BOUTON bt_cal;
TOUCH_BOUTON bt_stop_scan;

TOUCH_BOUTON bt_set; // attribtion d'une fréquence à un des 8 boutons preset
TOUCH_BOUTON bt_annuler; // "x"
TOUCH_BOUTON bt_close; // "x"

//les variables globales sont le Mal, et il faut éviter à tous prix de s'en servir, m'enfin!
boolean mute;
boolean vu_metre_actif;

enum MODE_AFFI {COUL, NORMAL, SCAN_F, SCAN_M, SET_F_PRESET}; //[couleur], [normal], [scan], [set 1F pour 1Bt]
MODE_AFFI mode_affi;

enum MODE_SELECT {_FRQ=0, _MEM=1} mode_s; // mode de mofif fréquence, en tapant les chiffres /ou en mémoire

enum MODUL {AM, WFM};
MODUL modulation_active;

enum BANDE {SW, FM, AIR, SCN}; //fréquences;  
BANDE bande_active;

enum GRP_ACT {gSW, gFM, gAIR, gSCN}; // groupes mémoire SW, FM, AIR, SCN; le grp SCN mémorise le résultat d'un scan FREQUENCE
GRP_ACT groupe_actif;

enum MODE_SCAN {FREQUENCE, MEMOIRE}; 
MODE_SCAN mode_scan;

enum MODE_SEUIL {LEV, SNR};  // level ou signal/bruit
MODE_SEUIL mode_seuil;

boolean quiet = false;

uint16_t FRQ_x0;
uint16_t FRQ_y0;

uint16_t x0_box_SD;
uint16_t y0_box_SD;

uint16_t x0_box_PRESET;
uint16_t y0_box_PRESET;

uint16_t x0_box_GROUPE;  // SW - FM - AIR
uint16_t  y0_box_GROUPE;

uint16_t x0_box_boutons_scan;
uint16_t y0_box_boutons_scan;

uint16_t x0_box_SCAN; // grande surface d'affichage
uint16_t y0_box_SCAN;
uint16_t dx_box_SCAN;
uint16_t dy_box_SCAN; 

uint16_t x0_numPad;
uint16_t y0_numPad;

uint16_t x0_vu_metre;
uint16_t y0_vu_metre;

uint16_t x0_box_info1; // en bas à gauche pour afficher les infos RDS
uint16_t y0_box_info1;

uint16_t x0_box_info2; // à la place du vu-metre
uint16_t y0_box_info2;
uint16_t dx_box_info2;
uint16_t dy_box_info2;

uint16_t x0_gridPad;
uint16_t y0_gridPad;
uint16_t dx_gridPad;
uint16_t dy_gridPad;

uint16_t x0_saisie;
uint16_t y0_saisie;

uint16_t x0_choix_couleur;
uint16_t y0_choix_couleur;

uint8_t n_appui; // incrémenté à chaque appui sur une touche du numPad numérique
uint32_t total_saisi;


uint16_t status;
int16_t level;
uint16_t usn;
uint16_t wam; 
int16_t offset; 
uint16_t bandwidth; 
uint16_t mod;
int8_t snr;

uint16_t A_block;
uint16_t B_block;
uint16_t C_block;
uint16_t D_block;
uint16_t dec_error;

float position_aiguille; // vu-metre
float valeur_affi;
float memo_valeur_affi;
float ltx;    // aiguille
uint16_t osx;
uint16_t osy;

uint16_t couleur_traits = GRIS_5;
uint16_t JAUNE_chiffres = 65504; 
uint16_t VERT_chiffres = 2016;

uint8_t cR = 0;
uint8_t cG = 0;
uint8_t cB = 0;
uint16_t couleur_fond_ecran = VERT_FONCE; 

String s_recue;
String memo_s_recue="";



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_variables_globales()
// je ne les initialise pas lors de leur déclaration sinon lors d'un reset logiciel (appel de la fonction 'setup()'
// par le boouton 'RST') elles ne seraient pas réinitialisées
{
	// étalonnage touch screen
	eTS.x0 = -30;	eTS.y0 = -30;
	eTS.dx =  11;	eTS.dy =  14;

	mode_affi = NORMAL;
	bande_active = FM;
	modulation_active = WFM;
	mode_s = _MEM;
	mode_scan = FREQUENCE;
	mode_seuil = LEV;
	mute = false;
	vu_metre_actif = true;

	FRQ_x0 = 50;				FRQ_y0 = 30;
	x0_box_SD = 2;			y0_box_SD = 150;
	x0_box_PRESET =2;			y0_box_PRESET =87;
	x0_box_GROUPE = 205;		y0_box_GROUPE = 115;
	x0_box_boutons_scan = 2;	y0_box_boutons_scan = 105;

 // grande surface d'affichage	
	x0_box_SCAN = 2;			y0_box_SCAN = 90;
	dx_box_SCAN = 315;			dy_box_SCAN = 148; 

	x0_numPad = 70;				y0_numPad = 115;
	x0_vu_metre = 170;			y0_vu_metre = 140;
	x0_box_info1 = 2;			y0_box_info1 = 220;

	x0_box_info2 = x0_vu_metre;	y0_box_info2 = y0_vu_metre;
	dx_box_info2 = 140;			dy_box_info2 = 70;

	x0_gridPad = x0_vu_metre;
	y0_gridPad = y0_vu_metre;
	dx_gridPad = 140;			dy_gridPad = 70;

	x0_saisie = 2;				y0_saisie = 2;
	x0_choix_couleur = 170;	y0_choix_couleur = 145;

	n_appui = 0; // incrémenté à chaque appui sur une touche du numPad numérique
	total_saisi = 0;

	valeur_bargraph=0;

	position_aiguille = 0; // vu-metre
	valeur_affi = 0;
	memo_valeur_affi = 0;
	ltx = 0;    // aiguille
	osx = x0_vu_metre;
	osy = y0_vu_metre;
}


//uint16_t bmp_offset = 0;
uint16_t bmp_width;
uint16_t bmp_heigh;




void draw_bmp565(uint16_t x0, uint16_t y0, uint8_t sens, File* fp)
{
		uint16_t i,j;
		uint16_t y1;
		uint8_t bmp_data[2]={0};
		uint16_t bmp_color[2];
		
		fp->seek(0);
		for(i=0; i<bmp_heigh; i++)
		{
			for(j=0; j<(bmp_width); j++)
			{
				fp->read(bmp_data, 2); // lit les 2 octets constituant la valeur rgb565 et les place dans bmp_data[]
				if (sens == 0) {y1 = y0+bmp_heigh-i;} 
				else {y1 = y0+i;}
				TFT.drawPixel(x0+j, y1, bmp_data[0] + (bmp_data[1] << 8) );
			}
		}
}



// 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 ++;

		// TFT.fillScreen(NOIR);
		// TFT.setTextColor(BLANC, NOIR);
		// TFT.setFreeFont(FF1);
		// TFT.drawString("Capture ok", 20, 100);
		// delay(1000);
		// init_affichages();
	}
}


void affiche_index_frq() // 6 petits boutons juste au dessus de chaque chiffre pour indiquer celui à modifier
{
	int x = FRQ_x0 +15;
	int y = FRQ_y0 - 5;
	uint16_t c1 = NOIR;
	uint16_t c2 = VERT;
	 
	bt_1.init(x, y, 20, 5, 0, GRIS_5); bt_1.affiche(c2, 1);	x+=31;
	bt_2.init(x, y, 20, 5, 0, GRIS_5); bt_2.affiche(c2, 1);	x+=31;
	bt_3.init(x, y, 20, 5, 0, GRIS_5); bt_3.affiche(c2, 1);	x+=46;
	bt_4.init(x, y, 20, 5, 0, GRIS_5); bt_4.affiche(c2, 1);	x+=31;
	bt_5.init(x, y, 20, 5, 0, GRIS_5); bt_5.affiche(c2, 1);	x+=31;
	bt_6.init(x, y, 20, 5, 0, GRIS_5); bt_6.affiche(c2, 1);
}


void efface_index_frq() // les 6 petits boutons
{
	int x = FRQ_x0 +15;
	int y = FRQ_y0 - 5;

	TFT.fillRect(x, y, 190, 5, couleur_fond_ecran);
}



void init_boutons_Plus_Moins()// boutons '<' et '>'
{
	int x0 = 240;
	int y0 = 90;

	String s1, s2;

	uint16_t c1 = GRIS_6;
	uint16_t c2 = JAUNE;

	if(mode_s == _FRQ)	{ s1 = " -"; s2 = " +"; }
	if(mode_s == _MEM)	{ s1 = " <"; s2 = " >"; }

	bt_moins.init(x0, y0, 30, 15, 3, GRIS_5);
	bt_moins.cliked = false;
	bt_moins.label= s1;
	
	bt_plus.init(x0+35, y0, 30, 15, 3, GRIS_5);
	bt_plus.cliked = false;
	bt_plus.label= s2;
}


void init_boutons_GROUPE() // groupe de fréquence (SW - FM - AIR - SCAN)
{
	uint16_t x = x0_box_GROUPE+4;
	uint16_t y = y0_box_GROUPE+5;

	TFT.setFreeFont(FF0);

	bt_SW.init(x, y, 20, 15, 3, GRIS_5);
	bt_SW.cliked = false;
	bt_SW.selected = false;
	bt_SW.label="SW";
	x+=25;

	bt_FM.init(x, y, 20, 15, 3, GRIS_5);
	bt_FM.cliked = false;
	bt_FM.selected = false;
	bt_FM.label="FM";
	x+=25;

	bt_AIR.init(x, y, 22, 15, 3, GRIS_5);
	bt_AIR.cliked = false;
	bt_AIR.selected = false;
	bt_AIR.label="AIR";
	x+=27;

	bt_SCN.init(x, y, 20, 15, 3, GRIS_5);
	bt_SCN.cliked = false;
	bt_SCN.selected = false;
	bt_SCN.label="SC";
	
}


void init_1_bouton(uint8_t n_font, uint16_t xi, uint16_t yi, uint8_t dx, uint8_t dy, String si, TOUCH_BOUTON *bouton_i)
{
	//uint16_t c1 = GRIS_5;
	uint16_t c2 = BLANC;

	bouton_i->init(xi, yi, dx, dy, 3, GRIS_5);
	bouton_i->cliked = false;
	bouton_i->selected = false;
	bouton_i->label = si;
	bouton_i->affiche(c2, n_font);
}


void init_1_bouton_rouge(uint8_t n_font, uint16_t xi, uint16_t yi, uint8_t dx, uint8_t dy, String si, TOUCH_BOUTON *bouton_i)
{
	uint16_t c2 = BLANC;

	bouton_i->init(xi, yi, dx, dy, 3, ROUGE);
	bouton_i->cliked = false;
	bouton_i->selected = false;
	bouton_i->label = si;
	bouton_i->affiche(c2, n_font);
}


void init_boutons_MODE() // en haut à gauche
{
	uint16_t c1 = NOIR;
	uint16_t c2 = VERT;

	TFT.setFreeFont(FF0);
	TFT.setTextColor(GRIS_3, NOIR);
	TFT.drawString("mode", 6, 24);

	TFT.drawFastVLine(45, 15, 72, couleur_traits);

	bt_mode_FRQ.init(5, 35, 37, 20, 2, GRIS_5);
	bt_mode_FRQ.selected = false;
	bt_mode_FRQ.label="FRQ";
	bt_mode_FRQ.affiche(c2, 2);

	bt_mode_MEM.init(5, 60, 37, 20, 2, GRIS_5);
	bt_mode_MEM.selected = true;
	bt_mode_MEM.label="MEM";
	bt_mode_MEM.affiche(c2, 2);
		
	affiche_index_frq();
}


void affiche_1_bt_RGB(TOUCH_BOUTON *bouton_i, uint16_t x, uint16_t y, uint8_t dx, uint16_t couleur, String s_i)
{
	uint16_t c1 = couleur;
	uint16_t c2 = JAUNE;

	bouton_i->init(x, y, dx, 14, 3, GRIS_5); 
	bouton_i->cliked = false; 
	bouton_i->selected = false;
	bouton_i->label = s_i; 	
	bouton_i->affiche(c2, 1);
}


void init_sprites()
{
	sprite_frq.createSprite(220, 55);
	sprite_frq.loadFont(digitfont1);
	sprite_frq.setTextColor(JAUNE_2, NOIR);
	sprite_frq.setTextDatum(MR_DATUM); // alignement du texte

}


void Tuner_Reset(void) 
{
	Wire.beginTransmission(0x64);
	Wire.write(0x1e);
	Wire.write(0x5a);
	Wire.write(0x01);
	Wire.write(0x5a);
	Wire.write(0x5a);
	Wire.endTransmission();
  }

/* 
static void Tuner_Patch_Load(const unsigned char *pLutBytes, uint16_t size) 
{
  unsigned char buf[24 + 1];
  uint16_t i, len;
  uint16_t r;
  buf[0] = 0x1b;
  while (size) 
  {
    len = (size > 24) ? 24 : size;
    size -= len;
    for (i = 0; i < len; i++) buf[1 + i] = pgm_read_byte(&pLutBytes[i]);
    pLutBytes += len;
    if (1 != (r = Tuner_WriteBuffer(buf, len + 1))) break;
  }
}

void Tuner_Patch() 
{
	Tuner_Reset();
	delay(100);
	Wire.beginTransmission(0x64);
	Wire.write(0x1c);
	Wire.write(0x00);
	Wire.write(0x00);
	Wire.endTransmission();
	delay(100);
	Wire.beginTransmission(0x64);
	Wire.write(0x1c);
	Wire.write(0x00);
	Wire.write(0x74);
	Wire.endTransmission();
	Tuner_Patch_Load(pPatchBytes102, PatchSize102);
	Wire.beginTransmission(0x64);
	Wire.write(0x1c);
	Wire.write(0x00);
	Wire.write(0x00);
	Wire.endTransmission();
	delay(100);
	Wire.beginTransmission(0x64);
	Wire.write(0x1c);
	Wire.write(0x00);
	Wire.write(0x75);
	Wire.endTransmission();
    Tuner_Patch_Load(pLutBytes102, LutSize102);
	Wire.beginTransmission(0x64);
	Wire.write(0x1c);
	Wire.write(0x00);
	Wire.write(0x00);
	Wire.endTransmission();
}
*/


bool Tuner_Table_Write(const unsigned char *tab) 
{
	if (tab[1] == 0xff) 
	{
		delay(tab[2]);
		return 1;
	} 
	else { return Tuner_WriteBuffer((unsigned char *)&tab[1], tab[0]); }
}


void Tuner_Init(const unsigned char *table)
{
	uint16_t r;
	const unsigned char *p = table;

	for (uint16_t i = 0; i < sizeof(tuner_init_tab9216); i += (pgm_read_byte(p + i) + 1)) 
	{
		if (1 != (r = Tuner_Table_Write(p + i))) break;
	}
}

 
void Tune_Frequence(uint32_t Fi)
{
	TFT.setFreeFont(FF0);
	//TFT.setTextColor(BLEU, NOIR);
	String s1 = String(Fi);
	//TFT.drawString(s1, 80, 2);

	quiet = false; // stop l'anti-parasites

	if((mode_affi != SCAN_F) && (mode_affi != SCAN_M))
	{
		bt_quiet.selected = false;
		bt_quiet.affiche(VERT, 1);
		bt_mute.selected = false;
		bt_mute.affiche(VERT, 1);
	}
	mute = false;

	frequence = Fi;

	if (Fi == 1500)
	{
		TFT.setTextColor(ROUGE, NOIR);
		TFT.drawString("MINIMUM   ", 130, 2); 
	}	

	if (bande_SW)
	{
		modulation_active = AM;
		Tune_Frequence_AM(Fi);
		TFT.setTextColor(VERT, NOIR);
		efface_box_entete2();
		TFT.drawString("SW      ", 130, 2);
	}
	
	if (bande_interdite1)
	{
		efface_box_entete2();
		TFT.setTextColor(ROUGE, NOIR);
		TFT.drawString("FRQ non disponible", 80, 2);
	}
	
	if (bande_FM)
	{
		modulation_active = WFM;
		Tune_Frequence_FM(Fi/10); // envoi les data au TEF6686
		efface_box_entete2();
		TFT.setTextColor(VERT, NOIR);
		TFT.drawString("bande FM", 130, 2);
	}
	
	if (bande_interdite2)
	{
		efface_box_entete2();
		TFT.setTextColor(ROUGE, NOIR);
		TFT.drawString("FRQ NON DISPONIBLE", 80, 2);
	}

	if (bande_AIR)
	{
		modulation_active = AM;
		Tune_Frequence_AM(Fi-110000); // nécessite un convertisseur de fréquence 110MHz en entrée antenne

		if(! affi_AB)
		{
			TFT.setTextColor(BLEU_CLAIR, NOIR);
			efface_box_entete2();
			TFT.drawString("AIR BAND", 130, 2); 
			affi_AB = true;
		}
	}

	if (Fi == 138000)
	{
		TFT.setTextColor(ROUGE, NOIR);
		efface_box_entete2();
		TFT.drawString("F MAX   ", 130, 2); 
	}	
}


void load_GRP_FREQ_SD() // SD --> to RAM
{
Serial.println("--- Frequences lues sur la SDcard ---------------");
Serial.println(" ");

	Serial.println("GROUPE SW");
	groupe_SW.RAZ(); // important, sinon les fréquences se trouvent dédoublées lors d'un soft-reset
	groupe_SW.load_bloc(); // SD --> RAM
	groupe_SW.tri_bloc();  // en RAM
	groupe_SW.bloc_to_serial(); 

Serial.println("---------------------------------------------");	

	Serial.println("GROUPE FM");
	groupe_FM.RAZ();
	groupe_FM.load_bloc();
	groupe_FM.tri_bloc();
	groupe_FM.bloc_to_serial(); 

Serial.println("---------------------------------------------");	

	Serial.println("GROUPE AIR");
	groupe_AIR.RAZ();
	groupe_AIR.load_bloc(); 
	groupe_AIR.tri_bloc();
	groupe_AIR.bloc_to_serial(); 

Serial.println("---------------------------------------------");

// remarque :  le groupe SCAN n'est jamais enregistré
}


uint16_t brightness(uint16_t couleur)
{
	uint8_t r, g, b;

	r = 0xFF & (couleur >> 16);
    g = 0xFF & (couleur >> 8);
    b = 0xFF & couleur;

	return ( r + g + b );
}


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
}


void init_affichages()
{
Serial.println("init_affichages()"); 	
	TFT.fillRect(0, 0, 319, 239, couleur_fond_ecran);
	efface_box_entete1();
	efface_box_entete2();
	efface_box_entete3();
	if (brightness(couleur_fond_ecran) > 500)  {couleur_traits = NOIR;} else {couleur_traits = BLANC;}

	TFT.setTextColor(JAUNE, NOIR);

	TFT.drawRect(0, 0, 319, 240, couleur_traits); // cadre principal pourtour de l'écran
	TFT.setFreeFont(FF0);
	TFT.setTextColor(BLANC, BLEU);
	String s1 = "v:" + version;
	TFT.drawString(s1, 45, 15);

	
	while (!Serial && (millis() <= 1000));
	init_boutons_MODE();

	affiche_box_SD();
	init_1_bouton(1, x0_box_SD+4, y0_box_SD+16, 40, 15, " LST", &bt_LST);
	init_1_bouton(1, x0_box_SD+4, y0_box_SD+33, 40, 15, "Write", &bt_SD_write);

	TFT.setFreeFont(FF0);

	init_boutons_Plus_Moins();
	init_boutons_GROUPE();  // (SW - FM - AIR - SC)
	
	init_1_bouton(1, 305, 15, 15, 15, "?", &bt_info);
	
	uint16_t xi, yi, dx, dy;

//1ere ligne, celle du dessus
	xi = 167;	yi = 212;	dx = 35;	dy = 11;
	init_1_bouton(1, xi, yi, dx, dy, " LED", &bt_LED);  xi+=3*dx+3*2; // LED  backlight
	init_1_bouton(1, xi, yi, dx, dy, " CAL", &bt_cal);  xi+=dx+2;


//2eme ligne, en dessous	
	xi = 130;	yi = 224;	dx = 35;	dy = 11;
	init_1_bouton(1, xi, yi, dx, dy, "Mute", &bt_mute);  xi+=dx+2;
	init_1_bouton(1, xi, yi, dx, dy, "sleep", &bt_sleep); xi+=dx+2;
	init_1_bouton(1, xi, yi-dy-2, dx, 2*dy+2, "QUIET", &bt_quiet); xi+=dx+2;
	init_1_bouton(1, xi, yi, dx, dy, " RST",  &bt_reset); xi+=dx+2;
	init_1_bouton(1, xi, yi, dx, dy, "Color", &bt_coul); xi+=dx+2;

	init_1_bouton(1, 160, 120, 40, 14, "TEST", &bt_TEST);
	
	TFT.fillRect(x0_box_boutons_scan, y0_box_boutons_scan + 10, 60, 25, NOIR);
	init_1_bouton(2, 5, 118, 55, 20, "SCAN", &Bt_SCAN);
	//init_1_bouton(1, 5, 135, 55, 15, "scan AIR", &bt_scan_air);

	init_1_bouton(1, 168, 97, 30, 15, "set", &bt_set);
		
	init_1_bouton(1, x0_box_SD+4, y0_box_SD+33, 40, 15, "Write", &bt_SD_write);
	init_1_bouton(1, x0_box_SD+4, y0_box_SD+50, 40, 15, "raz 1F", &bt_erase_1F);

	numPad1.init(x0_numPad, y0_numPad, true);
	
	affiche_box_presets(); // conteneur des 8 petits boutons
	presetPad1.init(x0_box_PRESET +5, y0_box_PRESET +5);

	efface_box_entete2();
	efface_box_entete3();

	
	affiche_box_FRQ(GRIS_3); // autour de la fréquence (gros chiffres JAUNE ou VERT)
	
	if (mode_affi == NORMAL) 
	{
		init_box_info();
		affiche_box_GROUPE();
		bt_moins.affiche(VERT ,1);	
		bt_plus.affiche(VERT ,1);
		bt_SW.affiche(VERT, 1);
		bt_FM.affiche(VERT, 1);
		bt_AIR.affiche(VERT, 1);
		bt_SCN.affiche(VERT, 1);
		bt_quiet.affiche(VERT, 1);
		bt_mute.selected = false;
		bt_mute.cliked = false;
		bt_mute.affiche(ROUGE, 1); 

		bt_4.selected = true; 
		bt_4.cliked = true; 
		bt_4.affiche(JAUNE,1);

		bt_sleep.affiche(VERT, 1); 
		bt_LED.affiche(VERT, 1); 
		bt_cal.affiche(VERT, 1); 
		bt_TEST.affiche(VERT, 1); 
		bt_coul.affiche(VERT, 1);
	
	}

	bt_reset.affiche(VERT, 1); 
	
	affiche_frequence(frequence);

	valeur_bargraph=0;
	if (vu_metre_actif) 
	{
		dessine_VuMetre();
	}
	else
	{
		//affi_image_from_SD("/bmp565/montagne170x140.bmp", x0_vu_metre, y0_vu_metre);
	}
	

	TFT.drawRect(0, 0, 319, 240, couleur_traits); // cadre principal pourtour de l'écran
}


void read_FRQ_File(FS &fs, String filename, String cible) // en mémoire SD
{
	Serial.print("Reading file: "); Serial.println(filename);
	File file = fs.open(filename);
	if (!file ) { Serial.println("failed to open file for reading"); return;	}
	String s; 
	uint8_t n =0;

	while (file.available()) 
	{
		char c;
		c = char(file.read());
		if ((c !='<') && (c !='>')) {s += c;}
		if(c=='>')
		{
			uint32_t frq;
			frq = s.toInt();
			Serial.println(frq);
			s="";
			if(cible == "SW") {presetPad1.bt_preset[n].frequence_SW = frq;}
			if(cible == "FM") {presetPad1.bt_preset[n].frequence_FM = frq;}
			if(cible == "AIR") {presetPad1.bt_preset[n].frequence_AIR = frq;}
			n++;
		}
	}
  file.close();
}


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 read_params()
{
	Serial.println("---------------------------------------------");
// lecture du fichier '/params.txt' en SD	
	Serial.println("lecture du fichier '/params.txt' en SD");
	String s1;
	int valeur;
	//Serial.println("read params() ");

	//for(uint8_t n=1; n<=6; n++) // lit toutes(6) lignes du fichier
	s1 = "Z";
	uint8_t n=1;
	while (s1 !="") 
	{
		s1 =  read_line_params(n); // retourne(par exemple): [couleur_fond]<267> 
Serial.print(n); Serial.println("  " + s1); 		

		valeur = extract_params(s1, "couleur_fond"); // extrait la valeur correspondant au label
		if(valeur != -1) {couleur_fond_ecran = valeur;}

//couleur_fond_ecran = VERT_FONCE; // pour TEST

		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++;
	}
	Serial.print("---------------------------------------------");
}


void draw_AEC(uint16_t x0, uint16_t y0, uint16_t L, uint8_t sens)
{
// ligne arc-en-ciel	
// affiche une ligne de pixels colorés à partir de la variable 'couleurs_aec' mémorisée en PROGMEM (voir fichier Couleurs_AEC.h)
// L = longueur de la ligne

//Serial.println("draw_draw_AEC()");
	uint16_t x, i, j;
	uint16_t y1;
	uint16_t couleur_i;

	for (int16_t i=0; i<L; i++)
	{
		float f = 470.0/L * i; // pour balayer toute l'échelle des couleurs disponibles
		j=uint16_t(f);
		
		couleur_i = couleurs_aec[2*j] | couleurs_aec[2*j+1]<<8;

		if (sens==0){x=i;} else {x=L-i;}
		TFT.drawPixel(x0+x, y0, couleur_i);
	}
}



void set_frq_default()
{
	presetPad1.bt_preset[0].frequence_FM = 88000;
	presetPad1.bt_preset[1].frequence_FM = 90000;
	presetPad1.bt_preset[2].frequence_FM = 92000;
	presetPad1.bt_preset[3].frequence_FM = 94000;
	presetPad1.bt_preset[4].frequence_FM = 96000;
	presetPad1.bt_preset[5].frequence_FM = 100000;
	presetPad1.bt_preset[6].frequence_FM = 104000;
	presetPad1.bt_preset[7].frequence_FM = 108000;

}

void INT26() // appelé par la ligne 'attachInterrupt(26, INT26, FALLING);', voir dans le setup ci-dessous
{
	do_capt_screen = true; // La capture se fera en dehors de l'interruption (depuis la boucle principale ou secondaire)
	//write_TFT_on_SDcard(); // non, interdit ici !! 
}


void setup()
{
// étalonnage touch screen;  ces valeurs peuvent varier d'un afficheur à l'autre
// pour l'instant il faut les fixer ici à la main...
	eTS.x0 = -30;	eTS.y0 = -30;
	eTS.dx =  11;	eTS.dy =  14;

// La capture d'écran nécessite la présence d'un switch temporaire (bouton pussoir) entre le PIO26 de l'ESP32 et GND
// Je conseille aussi l'ajout d'une R=4k7 de pullup externe au +3V3, celle de pullup interne étant de trop grande valeur
// ***** décommenter les deux lignes suivante pour permettre ces captures d'écran *******
	//pinMode(26, INPUT_PULLUP); //26
    //attachInterrupt(26, INT26, FALLING); // GPIO26 -> pin P15 de ma carte
//***************************************************************************************

	Serial.begin(115200);
	delay(20);

	pinMode(GPIO_BL, OUTPUT);
	digitalWrite(GPIO_BL, HIGH); 

	init_variables_globales();

	Wire.begin(GPIO_SDA, GPIO_SCL, 2000000);
	Tuner_Init(tuner_init_tab9216);
	//affiche_frequence(frequence);

	// 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);

// mySpi2 -> pour le lecteur de SDcard
// mySpi2 -> partage du bus SPI - mêmes valeurs de GPIO (sck=14, miso=12)	
// que pour l'afficheur ILI9341 , sauf CS=5. Voir sur le schéma ainsi que le fichier User_Setup.h 
	mySpi2.begin(14, 12, 13, 5); 

	init_SDcard();
	delay(20);

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

	affi_page_info();
	delay(2000);

	affi_image_from_SD("/bmp565/7.bmp", 0, 0); //   IMAGE D'ACCEUIL
	delay(2000);


	if (SDcardOk == false) 
	{	
		affi_message
		(
			"SDcard absente", 
			"Des parametres et fonctions", 
			"vont manquer: freq preset,", 
			"couleurs, capture ecran,",
			"calibrage Touch Screen..."
		);
	}


	init_sprites();


	if (SDcardOk == true) 
	{ 
		read_params(); // sur la SDcard
	}
	else
	{
		frequence = 88000;
	}	

// 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();
	}
	
	groupe_SW.filename  = "/FRQ_SW.txt"; // nom du fichier sur la SDcard
	groupe_FM.filename  = "/FRQ_FM.txt";
	groupe_AIR.filename = "/FRQ_AIR.txt";
	
	if (SDcardOk == true) 
	{ 
		load_GRP_FREQ_SD(); 
		groupe_SCAN.RAZ();
		presetPad1.set_frequences_PRST();
		presetPad1.set_couleurs();	
	} // -> to RAM
	else
	{ 
		set_frq_default(); 
	}

	init_affichages();


	saut_freq = 100;
	
	//Tuner_Patch(); // envoi une tartine (6000 bytes) de code, non documentée; Je ne constate aucun effet bénéfique...
/* 
Le PDF de NXP précise : "Use of these I2C transmissions is required for proper and full function and performance
as described in this user manual". (une sorte de vaccin anti-Covid donc...)
Toutefois si quelqu'un a réussi à déchiffrer le truc et peut me donner la liste des fonctions améliorées
je serais très heureux de le publier ici. Vous avez mon e-mail sur mon site www.silicium628.fr
*/
    //delay(50);

	Tuner_Init(tuner_init_tab9216);
	delay(50);

	// Set_no_AM_gain_reduction();

	bt_mode_FRQ.selected = false; 
	bt_mode_MEM.selected = true;

	uint16_t c1 = GRIS_6;
	uint16_t c2 = VERT;
	uint16_t c3 = JAUNE;
	bt_mode_FRQ.affiche(c2, 2); // bt en haut à gauche
	bt_mode_MEM.affiche(c3, 2);	// bt en haut à gauche

	groupe_actif = gFM;
	bande_active = FM;
	modulation_active = WFM;
	bt_FM.selected = true;
	bt_SW.selected = false;
	bt_AIR.selected = false;
	bt_SCN.selected = false;

	bt_SW.affiche(c2, 1);
	bt_FM.affiche(c3, 1);	

	//clic_logiciel_bouton(&presetPad1.bt_preset[0]);
	//traite_boutons_presetPad(0); 

	uint8_t nb_F = groupe_FM.nb_freq;
	affiche_numero_frq(String(1), String(nb_F));

	//couleur_fond_ecran = VERT_FONCE;

Serial.print("frequence="); Serial.println(frequence);	
	affiche_frequence(frequence);
	Tune_Frequence(frequence);

	Set_Volume(+70); // +60
	delay(20);

	//clic_logiciel_bouton(&bt_mode_MEM);
	mode_s = _MEM;
	efface_index_frq();

	if (test_touch_screen == true) {printTouchToDisplay();}

	// TS_calibrate(); pour forcer le calibrage 

Serial.print("- FIN DU SETUP -----------------");
// FIN DU SETUP
}


void get_XY_touch()
{
	TS_Point p = ts.getPoint();
	float dx = B.x - A.x;
	float ech_x = 300.0 / dx;
	float x0 = A.x;

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

	memo_x_touch = x_touch;
	memo_y_touch = y_touch;

	x_touch = 10 + uint16_t( (p.x - x0) * ech_x); 
	y_touch = 10 + uint16_t( (p.y - y0) * ech_y); 

/*
	TFT.pushRect(memo_x_touch, memo_y_touch-10, 1, 20, memo_tiret_V); // efface avec l'image enregistrée
	TFT.pushRect(memo_x_touch-10, memo_y_touch, 20, 1, memo_tiret_H); // efface avec l'image enregistrée

	TFT.readRect(x_touch, y_touch-10, 1, 20, memo_tiret_V); // mémorise le segment avant de tracer
	TFT.readRect(x_touch-10, y_touch, 20, 1, memo_tiret_H); // mémorise le segment avant de tracerfloat sy = sin((i - 90) * deg_to_rad);

	TFT.drawFastVLine(x_touch, y_touch-10, 20, NOIR);  // tiret vertical
	TFT.drawFastHLine(x_touch-10, y_touch, 20, NOIR);  // tiret horizontal
*/

}


void printTouchToDisplay() // pour TEST
{
	TFT.fillScreen(NOIR);

	TFT.setFreeFont(FM9);
	TFT.setTextColor(BLEU_CLAIR, NOIR);
	TFT.drawString("TEST TOUCH screen", 80, 120);

	while(1)
	{
		if (ts.tirqTouched() && ts.touched()) 
		{
			get_XY_touch();
			TFT.drawRect(x_touch, y_touch, 1, 1, JAUNE);
		}
	} 
} 


// -------------------------------------------------------------------------
// Le vu-metre est une variante perso du code : "/Arduino/libraries/TFT_eSPI/examples/480 x 320/TFT_Meters"
// voir le fichier licence.txt dans le dossier "/Arduino/libraries/TFT_eSPI/examples/"
void dessine_VuMetre()
{
	if (vu_metre_actif == false) {return;}

	uint16_t x0 = x0_vu_metre;
	uint16_t y0 = y0_vu_metre;

	uint16_t dx=140;
	uint16_t dy=70;

	uint8_t AA = 65; // 65
	uint8_t BB = x0 +dx/2;// 120
	uint8_t CC = y0 + dy+20; // 140	

	TFT.setFreeFont(FF0);
  // cadre rectangulaire
	TFT.fillRect(x0, y0, dx, dy, GRIS_3);
	TFT.fillRect(x0+3, y0+3, dx-6, dy-6, BLANC);

	TFT.setTextColor(NOIR);

  // graduation chaque 5 deg entre -50 et +50 deg
	for (int i = -50; i < 51; i += 10)
	{
		int tl = 5; // tiret plus long
		// Coordonnées du tiret à dessiner
		float sx = cos((i - 90) * deg_to_rad);
		float sy = sin((i - 90) * deg_to_rad);
		uint16_t tx0 = sx * (AA + tl) + BB;
		uint16_t ty0 = sy * (AA + tl) + CC;
		uint16_t tx1 = sx * AA + BB;
		uint16_t ty1 = sy * AA + CC;

		float sx2 = cos((i + 5 - 90) * deg_to_rad);
		float sy2 = sin((i + 5 - 90) * deg_to_rad);
		int tx2 = sx2 * (AA + tl) + BB;
		int ty2 = sy2 * (AA + tl) + CC;
		int tx3 = sx2 * AA + BB;
		int ty3 = sy2 * AA + CC;

		// zone verte
		if (i >= 0 && i < 25)
		{
			TFT.fillTriangle(tx0, ty0, tx1, ty1, tx2, ty2, VERT);
			TFT.fillTriangle(tx1, ty1, tx2, ty2, tx3, ty3, VERT);
		}

		// zone orange
		if (i >= 25 && i < 50)
		{
			TFT.fillTriangle(tx0, ty0, tx1, ty1, tx2, ty2, ORANGE);
			TFT.fillTriangle(tx1, ty1, tx2, ty2, tx3, ty3, ORANGE);
		}
    
		if (i % 25 != 0) tl = 8;

		tx0 = sx * (AA + tl) + BB;
		ty0 = sy * (AA + tl) + CC;
		tx1 = sx * AA + BB;
		ty1 = sy * AA + CC;

		TFT.drawLine(tx0, ty0, tx1, ty1, NOIR);

		if (i % 20 == 0)
		{
			tx0 = sx * (AA + tl + 10) + BB;
			ty0 = sy * (AA + tl + 10) + CC;
			switch (i / 20)
			{
				case -2: TFT.drawCentreString("0", tx0, ty0 - 6, 1); break;
				case -1: TFT.drawCentreString("25", tx0, ty0 - 4, 1); break;
				case  0: TFT.drawCentreString("50", tx0, ty0 - 6, 1); break;
				case  1: TFT.drawCentreString("75", tx0, ty0 - 4, 1); break;
				case  2: TFT.drawCentreString("100", tx0, ty0 - 6, 1); break;
			}
		}
		sx = cos((i + 5 - 90) * deg_to_rad);
		sy = sin((i + 5 - 90) * deg_to_rad);
		tx0 = sx * AA + BB;
		ty0 = sy * AA + CC;
		if (i < 50) {TFT.drawLine(tx0, ty0, tx1, ty1, NOIR);}
 	}
}



void plotAiguille(float value)
{
	if (vu_metre_actif == false) {return;}

	uint16_t x0 = x0_vu_metre;
	uint16_t y0 = y0_vu_metre;
	uint16_t dx=140;
	uint16_t dy=50;

	uint8_t AA = dx/2; // 100
	uint8_t BB = x0 +dx/2;// 120
	uint8_t CC = y0 + dy+25; // 140	

	TFT.setTextColor(TFT_BLACK, BLANC);
	char buf[8]; dtostrf(value, 4, 0, buf);

	if (value < -10) value = -10;
	if (value > 110) value = 110;

	float sdeg = map(value, -10, 110, -150, -30);
	float sx = cos(sdeg * deg_to_rad);
	float sy = sin(sdeg * deg_to_rad);

	float tx = tan((sdeg + 90) * deg_to_rad);

	TFT.drawLine(BB + 20*ltx - 1, CC - 20, osx - 1, osy, BLANC); //efface
	TFT.drawLine(BB + 20*ltx, CC - 20, osx, osy, BLANC);
	TFT.drawLine(BB + 20*ltx + 1, CC - 20, osx + 1, osy, BLANC);

	ltx = tx;
	osx = sx*50 + BB;
	osy = sy*50 + CC;

	TFT.drawLine(BB + 20*ltx - 1, CC - 20, osx - 1, osy, ROUGE);
	TFT.drawLine(BB + 20*ltx, CC - 20, osx, osy, VIOLET);
	TFT.drawLine(BB + 20*ltx + 1, CC - 20, osx + 1, osy, ROUGE);

	TFT.fillRect(x0, y0+dy+5, dx, 15, GRIS_2);
}



void init_box_info() // en bas à gauche
{
	TFT.fillRect(x0_box_info1+1, y0_box_info1+1, 118, 16, NOIR); // efface
	TFT.setFreeFont(FF0);
	TFT.setTextColor(JAUNE, couleur_fond_ecran);
	//TFT.drawString("RDS", x0_box_info1 -20, y0_box_info1 + 4);
}	


void affiche_unit(String s)
{
	TFT.setTextColor(JAUNE, NOIR);
	TFT.setFreeFont(FM9); //FM9 FMB9 FSS9... voir le fichier FrSD_Fonts.h
	TFT.drawString(s, FRQ_x0 + 225, FRQ_y0 + 35);
}


void efface_numero_frq()
{
	TFT.fillRect(FRQ_x0 + 225, FRQ_y0 + 20, 40, 12, couleur_fond_ecran);
}


void affiche_numero_frq(String s1, String s2)
{
	//TFT.fillRect(FRQ_x0 + 225, FRQ_y0 + 5, 10, 10, BLEU);
	TFT.setTextColor(BLANC, couleur_fond_ecran);
	TFT.setFreeFont(FF0); 
	TFT.drawString(s1 + "/" + s2 + "  ", FRQ_x0 + 225, FRQ_y0 + 20);
	affiche_box_FRQ(GRIS_3); // pour retracer le côté droit du rectangle
}


void affiche_band(String s) // à droite des gros chiffres
{
	//TFT.fillRect(FRQ_x0 + 225, FRQ_y0 + 5, 10, 10, BLEU);
	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FM9); 
	String blancs;
	if (s == "AIR") {blancs = " ";} else {blancs = "  ";}

	TFT.drawString(s + blancs, FRQ_x0 + 225, FRQ_y0);
	affiche_box_FRQ(GRIS_3); // pour retracer le côté droit du rectangle
}


void efface_box_entete1() // tout en haut à gauche
{
	TFT.fillRect(1, 1, 50, 12, NOIR);
	TFT.drawFastVLine(50, 0, 12, BLANC);
}


void efface_box_entete2() // tout en haut au centre
{
	TFT.fillRect(50, 1, 180, 12, NOIR);
	TFT.drawFastVLine(50, 0, 12, BLANC);
	TFT.drawFastVLine(230, 0, 12, BLANC);
}


void efface_box_entete3()  // tout en haut à droite
{
	TFT.fillRect(230, 1, 89, 12, BLEU);
	TFT.drawFastVLine(230, 0, 12, BLANC);
	memo_valeur_affi--; // pour réafficher tension batterie
}


void affiche_box_FRQ(uint16_t couleur) // autour de la fréquence (en gros chiffres JAUNE
{
	TFT.drawRect(0, FRQ_y0 -17, 319, 74, couleur);
}


void affiche_box_presets() // boutons 1 2 3 4 5 6 7 8
{	
	TFT.fillRect(x0_box_PRESET, y0_box_PRESET, 164, 24, NOIR);
	TFT.setTextColor(GRIS_3, NOIR);

}


void affiche_box_GROUPE() // groupes de fréquences; contient 4 boutons  SW, FM, AIR, SC
{
	TFT.fillRect(x0_box_GROUPE, y0_box_GROUPE, 105, 23, NOIR);
	//TFT.drawRect(x0_box_GROUPE, y0_box_GROUPE, 105, 25, couleur_traits);
	TFT.setFreeFont(FF0);
	TFT.setTextColor(GRIS_3, NOIR);
	TFT.drawString("groupes Freq", x0_box_GROUPE+4, y0_box_GROUPE-6);
}


void efface_box_GROUPE()
{
	//TFT.fillRect(x0_box_GROUPE, y0_box_GROUPE-7, 110, 33, couleur_fond_ecran);
	//TFT.fillRect(x0_box_SCAN, y0_box_SCAN, dx_box_SCAN, dy_box_SCAN, NOIR);
}


void affiche_box_SD() // Raz, Write, Raz 1F, LST
{
	TFT.fillRect(x0_box_SD, y0_box_SD + 10, 46, 58, NOIR);
	TFT.setFreeFont(FF0);
	TFT.setTextColor(GRIS_3, NOIR);
	TFT.drawString("SD", x0_box_SD+6, y0_box_SD+6);
}


void affiche_box_scan(uint16_t dy)
{
	init_1_bouton_rouge(1, 295, y0_box_SCAN+1, 20, 20, "x", &bt_stop_scan);
	if (bande_active != AIR)
	{
		init_1_bouton(1, 275, y0_box_SCAN+40, 40, 15, "rescan", &bt_re_scan);
		init_1_bouton(1, 275, y0_box_SCAN+58, 40, 14, "  >>", &bt_scan_suivant);
	}
	init_1_bouton(1, 255, y0_box_SCAN+1, 25, 15, "+", &bt_seuil_plus);
	init_1_bouton(1, 255, y0_box_SCAN+20, 25, 15, "-", &bt_seuil_moins);
	init_1_bouton(1, 4, y0_box_SCAN+3, 15, 15, "L",  &bt_LEV);
	init_1_bouton(1, 4, y0_box_SCAN+20, 15, 15, "N", &bt_SNR);
}


void affi_boutons_SW_FM_AIR_SCN()
{
	if((mode_affi == SCAN_F ) || (mode_affi == SCAN_M )) {return;}

	uint16_t c1 = GRIS_5;
	uint16_t c2 = VERT;
	bt_SW.affiche(c2, 1); 
	bt_FM.affiche(c2, 1); 
	bt_AIR.affiche(c2, 1);
	bt_SCN.affiche(BLEU_CLAIR, 1);
	bt_mute.affiche(ROUGE, 1);

}


void affiche_frequence(uint32_t frq) 
{	
Serial.println("affiche_frequence()"); 

	if((mode_affi != SCAN_F) && (mode_affi != SCAN_M))
	{
		TFT.fillRect(x0_box_info1+2, y0_box_info1, 118, 16, NOIR); // efface (en bas à gauche)
	}
	
	uint16_t couleur_chiffres;

	if(mode_s == _FRQ) {couleur_chiffres = VERT_chiffres;} 
	if(mode_s == _MEM) {couleur_chiffres = JAUNE_chiffres;} 

	if(bande_SW) // d'après la fréquence; SW - 28 MHz = limite haute du module
	{
		

		modulation_active = AM;
		if (groupe_actif != gSCN)  
		{
			affiche_band("SW");
			bande_active = SW; groupe_actif == gSW;
			bt_SW.selected = true;
			bt_SCN.selected = false;
		}
		
		bt_FM.selected = false; // les boutons sont exclusifs
		bt_AIR.selected = false;
		
		affi_boutons_SW_FM_AIR_SCN();

		String s1, sM, sK;
		uint8_t L; 

		s1 = String(frq);
		s1 = "000000" + s1;
		L= s1.length();

		sK = s1.substring(L-3); 	 // kHz
		sM = s1.substring(L-6, L-3); // Mhz

		sprite_frq.fillRect(0, 0, 220, 60, NOIR); // efface

		sprite_frq.setTextColor(couleur_chiffres, NOIR);
		sprite_frq.drawString(sM + "." + sK + " ", 220+10, 32); 
		// remarque: le fait d'ajouter " " à la fin évite aux chiffres de se balader horizontalement !
		sprite_frq.pushSprite(FRQ_x0, FRQ_y0);
		affiche_unit("MHz");
		Tune_Frequence(frq);
	}

	else if(bande_FM) // bande FM, on efface les deux '0' de droite (si == 0 sinon on affiche quand même)
	{
		affiche_band("FM");
		modulation_active = WFM;
		if (groupe_actif != gSCN) {bande_active = FM; groupe_actif == gFM;}
		
		affi_boutons_SW_FM_AIR_SCN();

		String s1, sM, sK1, sK2, sK3;
		String decimales;
		uint8_t x0 = 0;
		uint8_t L; 

		s1 = String(frq);
		s1 = "000000" + s1;
		L= s1.length();

		sK1 = s1.substring(L-3, L-2); // 1ere décimale
		sK2 = s1.substring(L-2, L-1); // 2eme décimale
		sK3 = s1.substring(L-1, L);   // 3eme décimale

		if ((sK2 == "0") && (sK3 == "0")) 
		{
			decimales = sK1;
			x0=170;
		}
		else
		{
			decimales = sK1 + sK2 + sK3;
			x0=232;
		}

		if  (frq < 100000) {sM = s1.substring(L-5, L-3); }// on ne retient que deux chiffres à gauche du point
		else { sM = s1.substring(L-6, L-3); } // on garde 3 chiffres à gauche du point décimal

		sprite_frq.fillRect(0, 0, 220, 60, NOIR); // efface
		sprite_frq.setTextColor(couleur_chiffres, NOIR);
		sprite_frq.drawString(sM + "." + decimales + " ", x0, 32); 
		// remarque: le fait d'ajouter " " à la fin évite aux chiffres de se balader horizontalement !
		sprite_frq.pushSprite(FRQ_x0, FRQ_y0);
		affiche_unit("MHz");

		Tune_Frequence(frq);
		bt_mute.selected = false;
		bt_mute.cliked = false;
		
	}

	else if(bande_AIR) // bande AIR - 138000-110000 = 28MHz (limite haute de réception AM du module)
	{
		affiche_band("AIR");
		modulation_active = AM;	
		if (groupe_actif != gSCN) {bande_active = AIR; groupe_actif == gAIR;}
		
		bt_AIR.selected = true;
		bt_SW.selected = false; // les boutons sont exclusifs
		bt_FM.selected = false;
		bt_SCN.selected = false;
		affi_boutons_SW_FM_AIR_SCN();

		String s1, sM, sK;
		uint8_t L; 

		s1 = String(frq);
		s1 = "000000" + s1;
		L= s1.length();

		sK = s1.substring(L-3); 	 // kHz
		sM = s1.substring(L-6, L-3); // Mhz

		sprite_frq.fillRect(0, 0, 220, 60, NOIR); // efface
		sprite_frq.setTextColor(couleur_chiffres, NOIR);
		sprite_frq.drawString(sM + "." + sK + " ", 220+10, 32); 
		sprite_frq.pushSprite(FRQ_x0, FRQ_y0);
		affiche_unit("MHz");
		Tune_Frequence(frq);
	}

	else if(bande_interdite1 || bande_interdite2) // bandes interdites par le module TEF6686
	{
		affiche_band("---");
		TFT.setTextColor(ROUGE, NOIR);
		TFT.setFreeFont(FF0);
		TFT.drawString("FRQ non disponible", 80, 2);
		//delay(2000);

		String s1, sM, sK;
		uint8_t L; 

		s1 = String(frq);
		s1 = "000000" + s1;
		L= s1.length();

		sK = s1.substring(L-3); 	 // kHz
		sM = s1.substring(L-6, L-3); // Mhz

		sprite_frq.fillRect(0, 0, 220, 60, NOIR); // efface
		sprite_frq.setTextColor(GRIS_1, NOIR);
		sprite_frq.drawString(sM + "." + sK + " ", 220+10, 32); 
		// remarque: le fait d'ajouter " " à la fin évite aux chiffres de se balader horizontalement !
		sprite_frq.pushSprite(FRQ_x0, FRQ_y0);
		affiche_unit("MHz");

		bt_mute.selected = true;
		bt_mute.affiche(ROUGE, 1);
		bt_mute.selected = false;
		mute = true;
		Set_Mute(mute); // fonction située dans le fichier 'driverTEF6686_628.h'

	}
	else
	{
		affiche_band("---");
		TFT.setTextColor(ROUGE, NOIR);
		TFT.setFreeFont(FF0);
		TFT.drawString("FRQ non disponible", 80, 2);
		//delay(2000);

		String s1, sM, sK;
		uint8_t L; 

		s1 = String(frq);
		s1 = "000000" + s1;
		L= s1.length();

		sK = s1.substring(L-3); 	 // kHz
		sM = s1.substring(L-6, L-3); // Mhz

		sprite_frq.fillRect(0, 0, 220, 60, NOIR); // efface
		sprite_frq.setTextColor(GRIS_1, NOIR);
		sprite_frq.drawString(sM + "." + sK + " ", 220+10, 32); 
		// remarque: le fait d'ajouter " " à la fin évite aux chiffres de se balader horizontalement !
		sprite_frq.pushSprite(FRQ_x0, FRQ_y0);
		affiche_unit("MHz");

		bt_mute.selected = true;
		bt_mute.affiche(ROUGE, 1);
		bt_mute.selected = false;
		mute = true;
		Set_Mute(mute); // fonction située dans le fichier 'driverTEF6686_628.h
	} 

	if(frq==138000) { affiche_band("max"); }
	if(frq>138000) { affiche_band("---"); }
}


void clic_logiciel_bouton(TOUCH_BOUTON *bouton_i)
{
	uint16_t c1 = NOIR;
	uint16_t c2 = JAUNE;

	bouton_i->cliked = true;
	bouton_i->selected = true;
	bouton_i->affiche(c2, 1);
}



void test_clic_boutons(TOUCH_BOUTON *bouton_i)
{
	uint16_t c1 = NOIR;
	uint16_t c2 = GRIS_2;

 	if ((x_touch > bouton_i->x0) && (x_touch < (bouton_i->x0 )+ bouton_i->read_dx()) 
	&& ( y_touch > ((bouton_i->y0) -5) ) && (y_touch < ((bouton_i->y0) + (bouton_i->read_dy())+5) ) ) 
	{
		bouton_i->cliked = true;
		bouton_i->selected = true;
		bouton_i->affiche(c2, 1);
		delay(100);
	} 
}



void test_clic_6_boutons_frq() // 6 rectangles situés au dessus des gros chiffres de la fréquence
{
	uint16_t c1 = NOIR;
	uint16_t c2 = VERT;

	if (( y_touch > (bt_1.y0-20)) && (y_touch < (bt_1.y0 + bt_1.read_dy()+20)) ) // zone des 6 boutons au dessus de la fréquence
	{
		bt_1.cliked = false; bt_1.selected = false; test_clic_boutons(&bt_1 ); bt_1.affiche(c2,1);
		bt_2.cliked = false; bt_2.selected = false; test_clic_boutons(&bt_2 ); bt_2.affiche(c2,1);
		bt_3.cliked = false; bt_3.selected = false; test_clic_boutons(&bt_3 ); bt_3.affiche(c2,1);
		bt_4.cliked = false; bt_4.selected = false; test_clic_boutons(&bt_4 ); bt_4.affiche(c2,1);
		bt_5.cliked = false; bt_5.selected = false; test_clic_boutons(&bt_5 ); bt_5.affiche(c2,1);
		bt_6.cliked = false; bt_6.selected = false; test_clic_boutons(&bt_6 ); bt_6.affiche(c2,1);

		if (bt_6.cliked) {saut_freq = 1;}
		if (bt_5.cliked) {saut_freq = 10;}
		if (bt_4.cliked) {saut_freq = 100;}
		if (bt_3.cliked) {saut_freq = 1000;}
		if (bt_2.cliked) {saut_freq = 10000;}
		if (bt_1.cliked) {saut_freq = 100000;}

		delay(10);
	}
}


void affiche_saisie(String s1)
{
	efface_box_entete1();
	TFT.setFreeFont(FF0); TFT.setTextColor(VERT, NOIR);
	TFT.drawString(s1, x0_saisie+2, y0_saisie);
}


void traite_touches_pad(uint8_t num_touche)  // pavé numérique
{
	if(num_touche == 253) {return;}

	uint16_t c1 = GRIS_6;
	uint16_t c2 = JAUNE;
	uint16_t c3 = VERT;
	 
	int p1;

	if (num_touche == 254) // bouton "."
	{
		frequence_txt += ".";
		affiche_saisie(frequence_txt);
	}

	if (num_touche < 10)
	{
		frequence_txt += String(num_touche);
		affiche_saisie(frequence_txt);
		delay(300);
	}
	
	x_touch =0;
	y_touch =0;

	if (num_touche == 255) // bouton "ok"
	{
Serial.println("touche ok");

		efface_box_entete1();
		efface_box_entete2();
		//affiche_saisie(""); // efface

		mode_s = _FRQ;
		bt_mode_MEM.selected = false;
		bt_mode_FRQ.selected = true;
		
		bt_mode_MEM.affiche(c2, 2);
		bt_mode_FRQ.affiche(c3, 2);
		
		double F = frequence_txt.toDouble();
		p1 = frequence_txt.indexOf(".");
		if (p1 != -1) {F *=1000;} // si présence du point décimal

		frequence = F;
		affiche_frequence(frequence); // 'Tune_Frequence(frq)' est appelée dans cette fonction 'affiche_frequence()'

		n_appui = 0;
		frequence_txt = "";
		n_appui = 0;

	}
	num_touche = 0;
}


void write_fichier_params() // sur la SDcard
{
Serial.println("write fichier '/params.txt'");	

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

	s1 ="[couleur_fond]";
	s1 += "<";
	s1 += String(couleur_fond_ecran); 
	s1 +=">";
	file1.println(s1); 

	s1 ="[frequence]";
	s1 += "<";
	s1 += String(frequence); 
	s1 +=">";
	file1.println(s1); 

	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 record_fichier_FRQ_SW_PRST()
{
Serial.println("record_fichier_FRQ_SW_PRST()");	

	File file1 = SD.open("FRQ_FM_PRST.txt", FILE_WRITE);
	String s1;
	
	for(uint8_t n=0; n<8; n++)
	{	
		s1 = "<";
		s1 += String(presetPad1.bt_preset[n].frequence_SW); 
		s1 +=">";
		file1.println(s1); 
	}

	file1.close();
}



void record_fichier_FRQ_FM_PRST()
{
Serial.println("record_fichier_FRQ_FM_PRST()");	

	File file1 = SD.open("FRQ_FM_PRST.txt", FILE_WRITE);
	String s1;
	
	for(uint8_t n=0; n<8; n++)
	{	
		s1 = "<";
		s1 += String(presetPad1.bt_preset[n].frequence_FM); 
		s1 +=">";
		file1.println(s1); 
	}
	file1.close();
}


void record_fichier_FRQ_AIR_PRST()
{
Serial.println("record_fichier_FRQ_AIR_PRST()");	

	File file1 = SD.open("FRQ_AIR_PRST.txt", FILE_WRITE);
	String s1;
	
	for(uint8_t n=0; n<8; n++)
	{	
		s1 = "<";
		s1 += String(presetPad1.bt_preset[n].frequence_AIR); 
		s1 +=">";
		file1.println(s1); 
	}
	file1.close();
}


void traite_boutons_presetPad(uint8_t n_bt)
{
	if (n_bt > 7) {return;}

	uint16_t c1 = GRIS_5;
	uint16_t c2 = JAUNE;
	uint32_t adr0, adr1, adr2, adr;

	if (mode_affi == NORMAL)
	{	
		if(bande_active == SCN) 
		{
			bande_active = FM; // afin de pouvoir traiter les fréquences preset attribuées aux boutons
			groupe_actif = gFM;
			modulation_active = WFM;
			bt_FM.selected = true;
			bt_SW.selected = false;
			bt_AIR.selected = false;
			bt_SCN.selected = false;
			affi_boutons_SW_FM_AIR_SCN();
		}
		efface_index_frq();
		mode_s = _MEM;
		bt_mode_MEM.selected = true;
		bt_mode_FRQ.selected = false;
		bt_mode_MEM.affiche(c2, 2);
		bt_mode_FRQ.affiche(c2, 2);
		affiche_frequence(frequence);

		// Lit la frequence dans le bouton concerné (VOIR: 'class TOUCH_BOUTON_PRESET')
		if(bande_active == SW)	{frequence = presetPad1.bt_preset[n_bt].frequence_SW;}
		if(bande_active == FM)	{frequence = presetPad1.bt_preset[n_bt].frequence_FM;}
		if(bande_active == AIR)	{frequence = presetPad1.bt_preset[n_bt].frequence_AIR;}
	}

	if (mode_affi == SET_F_PRESET)
	{
		if(bande_active == SW) 
		{ 
			presetPad1.bt_preset[n_bt].frequence_SW = frequence; // en RAM
			record_fichier_FRQ_SW_PRST();
			mode_affi = NORMAL;
			delay(100);
		} 
		if(bande_active == FM) 
		{ 
			presetPad1.bt_preset[n_bt].frequence_FM = frequence; 
			record_fichier_FRQ_FM_PRST();
			mode_affi = NORMAL;
			delay(100);
		}
		if(bande_active == AIR) 
		{
			presetPad1.bt_preset[n_bt].frequence_AIR = frequence;
			record_fichier_FRQ_AIR_PRST();
			mode_affi = NORMAL;
			delay(100);
		}

		mode_affi = NORMAL;
		vu_metre_actif = true;
		init_affichages();
	}

	vu_metre_actif = true;
	dessine_VuMetre();
	affiche_frequence(frequence);
	TFT.setTextColor(JAUNE, NOIR);
	TFT.setFreeFont(FF0);
	Tune_Frequence(frequence);
	write_fichier_params();
}



uint32_t inc_Frq_in_groupe(GROUPE_FREQUENCES *groupe_Freq, int8_t di)  // di = +/-1
{
	if ((di<-1)||(di>1)) {return 0;}

	uint16_t n_max = groupe_Freq->nb_freq;

	uint16_t n = groupe_Freq->num_F_actuelle;

	if(n_max > 99) {n_max = 99;}

	if (di == 1)
	{
		if (n<n_max-1) { n++; }
		else if (n >= (n_max-1)) {n=0;}
	}

	if (di == -1)
	{
		if (n>=1) { n--; }
		else if (n==0) {n = n_max-1;}
	}

	if(n > 99) {n = 0;}
	 	
	groupe_Freq->num_F_actuelle = n;
	uint16_t adr = groupe_Freq->adr_1ere_frq + n;

	uint8_t nb_F = groupe_Freq->nb_freq;
	uint8_t num_1ere_F = groupe_Freq->adr_1ere_frq;
	affiche_numero_frq(String(1 + adr - num_1ere_F), String(nb_F));

	uint32_t valeur_lue = groupe_Freq->G_freq[adr];
	return valeur_lue;

}



void test_clic_boutons_plus_moins()   // boutons '<' et '>'
{
	uint16_t c1 = GRIS_6;
	uint16_t c2 = GRIS_3;

	boolean bouton_cliked = false;
		
	//--------------------------------------------------------------------
	if(mode_s == _FRQ) // on va modifier directement la fréquence
	{
		test_clic_boutons(&bt_plus ); 
		bt_plus.affiche(c2, 1);
		if (bt_plus.cliked) 
		{
			efface_box_entete2();
			presetPad1.deselect_boutons();
			frequence += saut_freq;
			bouton_cliked = true;
		}

		delay(100);
		if (bt_plus.cliked && ((frequence + saut_freq) <= 138000 ))
		{ 
			Tune_Frequence(frequence);
		}

		bt_plus.cliked = false;  
		bt_plus.selected = false;
		bt_plus.affiche(c2, 1); // fugitif

		test_clic_boutons(&bt_moins ); 
		bt_moins.affiche(c2, 1);
		delay(100);
		if (bt_moins.cliked)
		{
			efface_box_entete2();
			if (frequence > saut_freq) // évite de se retrouver avec une F négative ! 
			{
				presetPad1.deselect_boutons();
				bouton_cliked = true;
				frequence -= saut_freq;
			}
		}

		if(bt_moins.cliked  && frequence > saut_freq) { Tune_Frequence(frequence); } 
		bt_moins.cliked = false;  
		bt_moins.selected = false; 
		bt_moins.affiche(c2, 1); // fugitif	

		if(bouton_cliked == true)
		{
			bouton_cliked = false;
			affiche_frequence(frequence);
		}
	}

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

	if(mode_s == _MEM) // on va parcourir les fréquences de la liste
	{
		test_clic_boutons(&bt_plus ); 
		bt_plus.affiche(c2, 1);
		if (bt_plus.cliked) 
		{
			bouton_cliked = true;

//Serial.println("bt_plus.cliked");

			if(groupe_actif == gSW) { frequence = inc_Frq_in_groupe(&groupe_SW, 1); }
			if(groupe_actif == gFM) { frequence = inc_Frq_in_groupe(&groupe_FM, 1); }
			if(groupe_actif == gAIR) { frequence = inc_Frq_in_groupe(&groupe_AIR, 1); }
			if(groupe_actif == gSCN) { frequence = inc_Frq_in_groupe(&groupe_SCAN, 1); }

			Tune_Frequence(frequence);
			if(vu_metre_actif == false)
			{
				quiet = false;
				vu_metre_actif = true;
				dessine_VuMetre();
				affiche_force_signal();
				delay(100);
			}

		}

		bt_plus.cliked = false;  
		bt_plus.selected = false;
		bt_plus.affiche(c2, 1); // fugitif

		test_clic_boutons(&bt_moins ); 
		bt_moins.affiche(c2, 1);
		delay(100);
		if (bt_moins.cliked)
		{
			bouton_cliked = true;

//-Serial.println("bt_moins.cliked");			

			if(groupe_actif == gSW) { frequence = inc_Frq_in_groupe(&groupe_SW, -1); }
			if(groupe_actif == gFM) { frequence = inc_Frq_in_groupe(&groupe_FM, -1); }
			if(groupe_actif == gAIR) { frequence = inc_Frq_in_groupe(&groupe_AIR, -1); }
			if(groupe_actif == gSCN) { frequence = inc_Frq_in_groupe(&groupe_SCAN, -1); }

			Tune_Frequence(frequence);
			if(vu_metre_actif == false)
			{
				quiet = false;
				vu_metre_actif = true;
				dessine_VuMetre();
				affiche_force_signal();
				delay(100);
			}
		}

		bt_moins.cliked = false;  
		bt_moins.selected = false; 
		bt_moins.affiche(c2, 1); // fugitif	

		if(bouton_cliked == true)
		{
			bouton_cliked = false;
			affiche_frequence(frequence);
		}
	}	
}


void test_clic_bt_RST_affi() // bouton "ok" de la box saisie couleur de fond ecran
{
	if(mode_affi != COUL) {return;}

	uint16_t c1 = GRIS_6;
	uint16_t c2 = JAUNE;

	test_clic_boutons(&bt_RST_affi ); 

	if (bt_RST_affi.cliked) 
	{
		bt_RST_affi.cliked = false;
		bt_RST_affi.selected = true;
		bt_RST_affi.affiche(VERT, 1);

		init_affichages();
	}
}


void test_clic_bt_coul() // bouton au coin en bas à droite
{
	uint16_t c1 = GRIS_5;
	uint16_t c2 = BLANC;

	uint8_t n;

	test_clic_boutons(&bt_coul ); 

	if (bt_coul.cliked) 
	{
		bt_coul.cliked = false;
		bt_coul.selected = true;
		bt_coul.affiche(VERT, 1);

		vu_metre_actif = false;
			
		//delay(1000);
		TFT.fillRect(x0_gridPad, y0_gridPad, dx_gridPad, dy_gridPad, NOIR); 
		
		gridPad1.init(x0_gridPad, y0_gridPad);
		gridPad1.set_couleurs();
		gridPad1.affiche();

		boolean ok = false;

		while(! ok)
		{
			if (ts.tirqTouched() && ts.touched()) 
			{
				get_XY_touch();
				delay(100);
				n = gridPad1.test_clic();
				if (n != 253)
				{
Serial.println(n);
					if ((n != 32) && (n != 255))
					{	
						couleur_fond_ecran = gridPad1.bt_grid[n].couleur;
						write_fichier_params();
					}
					ok = true;
					mode_affi = NORMAL;
					vu_metre_actif = true;
					init_affichages();
				}
			}
		}	
	}
}



void test_clic_bouton_mute()
{
	test_clic_boutons(&bt_mute ); 
	
	if (bt_mute.cliked) 
	{
		bt_mute.affiche(ROUGE, 1);
		delay(10);

		bt_mute.cliked = false;
		if(mute == false) {mute = true;} else {mute = false;}
		Set_Mute(mute); // fonction située dans le fichier 'driverTEF6686_628.h'
		bt_mute.selected = mute;
		bt_mute.affiche(ROUGE, 1);
		if (mute == false) {Tune_Frequence(frequence);}
		delay(10);
	}
}


void test_clic_bt_quiet()
{
	test_clic_boutons(&bt_quiet ); 
	if (bt_quiet.cliked) 
	{
		bt_quiet.cliked = false;
		bt_quiet.selected = true;
		bt_quiet.affiche(VERT, 1);

		if(quiet==false)
		{
			quiet = true; // anti-parasites
			vu_metre_actif = false;
			affi_image_from_SD("/bmp565/montagne170x140b.bmp", x0_vu_metre, y0_vu_metre);
		}
		else 
		{
			quiet = false;
			vu_metre_actif = true;
			dessine_VuMetre();
		}

		bt_quiet.cliked = false;
		bt_quiet.selected = quiet;
		bt_quiet.affiche(VERT, 1);
		delay(300);
	}
}


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("Radio TEF6686", 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;

	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF0);
	TFT.drawString("SW (AM): 1500kHz -- 28MHz", 0, y);	y+=20;
	TFT.drawString("bande FM (WFM): 88MHz -- 108MHz", 0, y);	y+=20;
	TFT.drawString("bande Aviation Civile: 118MHz -- 137MHz (AM)", 0, y);	y+=20;

}



void test_clic_bouton_info()
{
	test_clic_boutons(&bt_info); 
	if (bt_info.cliked) 
	{
		bt_info.cliked = false;
		bt_info.selected = true;
		bt_info.affiche(VERT, 1);
		delay(10);

		affi_page_info();
		
		//delay(10);

		attente_clic();

	}
}



void affi_message(String L1, String L2, String L3, String L4, String L5)
{
	TFT.fillScreen(NOIR);
	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF1);
	uint16_t y = 50;
	uint16_t dy = 20;
	TFT.drawString(L1, 10, y); y+=dy;
	TFT.drawString(L2, 10, y); y+=dy;
	TFT.drawString(L3, 10, y); y+=dy;
	TFT.drawString(L4, 10, y); y+=dy;
	TFT.drawString(L5, 10, y);

	delay(10);
	attente_clic();

}




void test_clic_bouton_Sleep()
{
	test_clic_boutons(&bt_sleep ); 

	if (bt_sleep.cliked) 
	{
		bt_sleep.cliked = false;
		bt_sleep.selected = true;
		bt_sleep.affiche(ROUGE, 1);
		delay(10);
		
//todo: save en SD
		TFT.fillScreen(NOIR);
		TFT.setTextColor(BLANC, NOIR);
		TFT.setFreeFont(FF1);
		TFT.drawString("Cliquez pour MODE SLEEP", 20, 50);
		TFT.drawString("de l'ESP32", 20, 70);
		TFT.drawString("Eteindre pour quitter", 20, 90);
		TFT.drawString("ce mode SLEEP", 20, 110);

		delay(10);

		attente_clic();

		esp_deep_sleep_start();
	}
}


void test_clic_bt_LED()
{
	test_clic_boutons(&bt_LED ); 

	if (bt_LED.cliked) 
	{
		bt_LED.cliked = false;
		bt_LED.selected = true;
		bt_LED.affiche(ROUGE, 1);
		delay(10);

		TFT.fillScreen(NOIR);
		TFT.setTextColor(BLANC, NOIR);
		TFT.setFreeFont(FF1);
		TFT.drawString("ECRAN NOIR", 20, 50);
		TFT.drawString("Clic pour rallumer", 20, 90);
	
		for(int n=10; n>0; n--)
		{
			TFT.drawString(String(n) + "  ", 20, 110);
			delay(500);
		}

		digitalWrite(GPIO_BL, LOW);
		attente_clic();
		digitalWrite(GPIO_BL, HIGH);
	}
}


void test_clic_bt_CAL() 
{
	test_clic_boutons(&bt_cal ); 

	if (bt_cal.cliked) 
	{
		bt_cal.cliked = false;
		bt_cal.selected = true;
		bt_cal.affiche(BLEU, 1);
		delay(10);

		TFT.fillScreen(NOIR);
		TS_calibrate();
	}
}



uint16_t read_16(File fp)
{
    uint8_t low;
    uint16_t high;
    low = fp.read();
    high = fp.read();
    return (high<<8)|low;
}


uint32_t read_32(File fp)
{
    uint16_t low;
    uint32_t high;
    low = read_16(fp);
    high = read_16(fp);
    return (high<<8)|low;   
}


void affi_image_from_SD(String filename, uint16_t x0, uint16_t y0)
{
Serial.println("----------------------------");
Serial.println("affi_image_from_SD()");

	uint8_t bmp_data[2]={0,0};
	uint8_t  a, b;
	uint16_t x=0;
	uint16_t y=0;
	int16_t xmax;

	uint32_t seekOffset;
  	uint16_t w, h, row, col;


	File fp = SD.open(filename, "r");
	if (!fp) { Serial.println("ERREUR open file on SD"); return; }

	uint16_t etiq1 = read_16(fp);

// test bmp_header	
	if (etiq1 == 0x4D42) 
	{
		read_32(fp);
		read_32(fp);
		seekOffset = read_32(fp);
Serial.print("seekOffset ="); Serial.println(seekOffset); // 138

		Serial.print("seekOffset= "); Serial.println(seekOffset); 
		read_32(fp);
		w = read_32(fp);
		h = read_32(fp);
		Serial.print("w= "); Serial.println(w); // 75
		Serial.print("h= "); Serial.println(h); // 50
		Serial.println(read_16(fp)); // 1
		Serial.println(read_16(fp)); // 16
		Serial.println(read_16(fp)); // 3
		fp.seek(seekOffset);
	}	

	y += h;
	xmax = w;
	if(xmax%2 == 1) {xmax +=1;}

	uint16_t padding = (4 - ((w * 2) & 2)) & 2;
	uint8_t lineBuffer[(w*2) + padding];

	for (row = 0; row < h; row++) 
	{
		fp.read(lineBuffer, sizeof(lineBuffer));
		uint8_t*  bptr = lineBuffer;
		uint16_t* tptr = (uint16_t*)lineBuffer;

		for (uint16_t col = 0; col < w; col++)
		{
			a = *bptr++;
			b = *bptr++;
			*tptr++ = (a <<8 | b);
		}
		TFT.pushImage(x0 + x, y0 + y, w, 1, (uint16_t*)lineBuffer);
		y--;			
	}
	fp.close();
}

/*
void ajout_1_freq()
{
// AJOUT d'une fréquence en fin d'une liste de fréquences. Travaille directement sur la mémoire SD
	File file = SD.open("/FRQ_FM.txt", FILE_APPEND);
	file.print("<88800>"); // ok ;      attention à ne pas oublier les < >
	file.close(); // enregistre CE fichier en mémoire SD, sans toucher aux autres
	delay(100);
}
*/


void affi_nuances_de_gris()
{
	TFT.fillScreen(NOIR);
	uint16_t x, dx, y;
	x=30;
	dx=21;
	y=100;
	TFT.fillRect(x, y, dx, dx, BLANC); x+=dx;
	TFT.fillRect(x, y, dx, dx, GRIS_1); x+=dx;
	TFT.fillRect(x, y, dx, dx, GRIS_2); x+=dx;
	TFT.fillRect(x, y, dx, dx, GRIS_3); x+=dx;
	TFT.fillRect(x, y, dx, dx, GRIS_4); x+=dx;
	TFT.fillRect(x, y, dx, dx, GRIS_5); x+=dx;
	TFT.fillRect(x, y, dx, dx, GRIS_6); x+=dx;

	attente_clic();
	
}


void TS_verif()
{
	TFT.fillScreen(BLANC);
	TFT.setTextColor(NOIR, BLANC);
	TFT.setFreeFont(FF1);
	TFT.drawString("VERIF CALIBRATION", 10, 50);

	init_1_bouton(1, 280, 20, 20, 20, "X", &bt_close);
	boolean stop = false;
	while(! stop)
	{
		get_XY_touch();
		test_clic_boutons(&bt_close ); 
		if (bt_close.cliked)
		{
			bt_close.cliked = false;
			stop = true;
		} 
		delay(10);
	}
	TFT.fillScreen(NOIR);
	init_affichages();
}


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);
	init_affichages();

	//TS_verif();
	//printTouchToDisplay(); // POUR TESTER LA CALIBRATION DE L'ECRAN TACTILE
}


void affi_lst_codes_PI_RDS() // lecture directement sur la SDcard, pas de mise en mémoire RAM
{
	Serial.println("liste_codes_RDS()");
	int i = 1;
	char buffer1[64];

	//uint32_t valeur_lue;
	uint16_t line_num;
	uint16_t nombre =0;
	uint16_t y=0;
	String s1;
	boolean stop = false;

	TFT.fillScreen(NOIR);
	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF0);
	TFT.drawString("Liste codes PI RDS (en memoire SD)", 0, y);	
	y+=30;

	init_1_bouton(1, 280, 20, 50, 15, "stop", &bt_annuler);
	bt_annuler.affiche(BLANC, 1);

	File file1 = SD.open("/RDS_codes_PI.lst", "r");
	while (file1.available() && stop == false )
	{
		int nb_bytes = file1.readBytesUntil('\n', buffer1, sizeof(buffer1));
		buffer1[nb_bytes] = 0; // zéro terminal afin d'obtenir un string
		//if (line_num == i)
		{
			//if(s1 != "") // n'affiche pas les emplacements vides
			{	
				nombre++;
				s1 = String(nombre);
				TFT.setTextColor(BLEU, NOIR);
				TFT.drawString(s1, 5, y);

				s1 = buffer1;
				//s1 += "                      ";
				TFT.setTextColor(BLANC, NOIR);
				TFT.drawString(s1, 30, y);	
				y+=10;

				if(y>230)
				{
					delay(500); //300
					if (ts.tirqTouched() && ts.touched()) 
					{
						stop = true;
						TFT.fillScreen(NOIR);
						TFT.drawString("STOP", 100, 100);
						return;
					}
					else
					{
						delay(300); //200
						TFT.fillScreen(NOIR);
						y=0;
						TFT.drawString("Liste codes PI RDS (en memoire SD)", 0, y);	
						bt_annuler.affiche(BLANC, 1);
						y+=30; //30
					}
				}
			}
		}  
		i++;
				
	}
	file1.close();
	attente_clic2();
}

void affi_deroulant(String titre, String filename)
{
//decale le texte fichier texte vers le haut et le complète en bas comme dans un générique de film

	delay(300);
	uint16_t memo_line_H[1920]; // 1280
	char buffer1[64];
	String s1, s2, s3, s4;
	
	TFT.setFreeFont(FF0);
	TFT.fillScreen(NOIR);
	TFT.drawRect(1, 0, 319, 165, BLANC);

	TFT.setTextColor(JAUNE, NOIR);
	TFT.drawString(titre, 10, 5);
	TFT.drawFastHLine(1, 14, 318, GRIS_5);
	TFT.setTextColor(BLANC, NOIR);


	init_1_bouton(1, 280, 220, 50, 15, "stop", &bt_annuler);
	bt_annuler.affiche(BLANC, 1);

	File file1 = SD.open(filename, "r");
	int n=0;
	boolean stop = false;
	while (file1.available() && stop == false )
	{
		if(do_capt_screen== true) { do_capt_screen = false; write_TFT_on_SDcard(); }

		for(int y=22; y<160; y+=6) // 4
		{
			TFT.readRect(2, y, 317, 6, memo_line_H); // 4  mémorise la ligne
			TFT.pushRect(2, y-4, 317, 6, memo_line_H); // la recopie un ligne au dessus
		}
		if(n%3 == 0) // 2
		{
			int nb_bytes = file1.readBytesUntil('\n', buffer1, sizeof(buffer1));
			buffer1[nb_bytes] = 0; // zéro terminal afin d'obtenir un string

			s1 = String(n/3); // 2
			s2 = buffer1;
			s3 = s2.substring(0, 4);
			s4 = s2.substring(5); 
			//TFT.fillRect(10, 148, 339, 8, NOIR);
			TFT.setTextColor(BLEU, NOIR);
			TFT.drawString(s1, 10, 148);
			TFT.setTextColor(JAUNE, NOIR);
			TFT.drawString(s3, 40, 148);
			TFT.setTextColor(GRIS_3, NOIR);
			TFT.drawString(s4, 70, 148);

		}
		n++;
		if ((ts.tirqTouched() && ts.touched())) 
		{
			get_XY_touch();
			test_clic_boutons(&bt_annuler); 
			if (bt_annuler.cliked) 
			{
				bt_annuler.cliked = false;
				TFT.fillScreen(NOIR);
				x_touch = 0;
				y_touch = 0;
				delay(500);
				stop = true;
			}
		}	
	}
	file1.close();
}



void test_clic_bouton_TEST()
{
	test_clic_boutons(&bt_TEST ); 
	bt_TEST.affiche(ROUGE, 1);
	if (bt_TEST.cliked) 
	{
		delay(10);
		Serial.println("-------------------------------");
		Serial.println("bt_TEST.cliked");
		bt_TEST.cliked = false;
		bt_TEST.selected = true;
		bt_TEST.affiche(ROUGE, 1);

		//TFT.fillscreen(GRIS_3); // effface
		//affi_image_from_SD("/7.bmp");
		//ajout_1_freq();
/*
		uint16_t dx= 40;
		uint16_t dy =30;
		uint16_t memo_line_H[dx*dy];
		TFT.readRect(20, 20 , dx, dy, memo_line_H);
		TFT.pushRect(100, 100, dx, dy, memo_line_H);
*/
/*
		String s1;
		for (int n=0; n<10; n++)
		{
			s1 =  read_line_params(n);
			Serial.println(s1);
		}
*/		
		//TS_calibrate();

		//affi_nuances_de_gris();
		
		//TFT.fillScreen(NOIR); // effface
		//draw_AEC(0, 100, 320, 1);

		//affi_lst_codes_PI_RDS();
		//TFT.fillScreen(GRIS_3); // effface

		//affi_deroulant("Liste codes PI RDS", "/RDS_codes_PI.lst");

		//TFT.drawString("Lecture faite", 10, 90);
		//attente_clic2();

		bt_TEST.cliked = false;
		bt_TEST.selected = false;
		bt_TEST.affiche(ROUGE, 1);

		init_affichages();
		Serial.println("-------------------------------");
	}
}


void test_clic_Bt_reset()
{
	test_clic_boutons(&bt_reset ); 
	bt_reset.affiche(NOIR, 1);
	if (bt_reset.cliked) 
	{
		delay(10);
		bt_reset.cliked = false;
		bt_reset.selected = true;
		bt_reset.affiche(VERT, 1);
		
		delay(10);
		bt_reset.selected = false;
		bt_reset.affiche(VERT, 1);
		
		setup();
	}
}


void test_clic_bt_LEV()
{
	test_clic_boutons(&bt_LEV); 
	if (bt_LEV.cliked) 
	{
		bt_LEV.cliked = false;
		bt_LEV.selected = true;  bt_LEV.affiche(VERT, 1);
		bt_SNR.selected = false; bt_SNR.affiche(VERT, 1);
		mode_seuil = LEV;
	}
}


void test_clic_bt_SNR()
{
	test_clic_boutons(&bt_SNR);
	if (bt_SNR.cliked) 
	{
		bt_SNR.cliked = false;
		bt_SNR.selected = true;  bt_SNR.affiche(VERT, 1);
		bt_LEV.selected = false; bt_LEV.affiche(VERT, 1);
		mode_seuil = SNR;
	}
}



void test_clic_bt_stop_scan()
{
	test_clic_boutons(&bt_stop_scan); 
	if (bt_stop_scan.cliked) 
	{
		Serial.println("STOP scan");
		bt_stop_scan.affiche(BLANC, 1);
		mode_affi = NORMAL;
		bt_stop_scan.cliked = false;
		bt_stop_scan.affiche(BLANC, 1);
	}
}


void test_clic_bt_annuler() 
{
	test_clic_boutons(&bt_annuler); 
	if (bt_annuler.cliked) 
	{
		bt_annuler.cliked = false;
		bt_annuler.affiche(BLANC, 1);

		mode_affi = NORMAL; // ce qui repasse les boutons 'preset' en mode sélection et empêche l'écriture en SD
		vu_metre_actif = true;
		init_affichages();
	}
}


/*
MODES DE SCAN
	1-scan_frq() : tt fréquences d'une bande (ex: FM). Mise en mémoire temporaire (en RAM) des stations actives.
	2-scan_mem_AIR() : cannaux occupés (pour la bande aviation) - pas de mise en mémoire mais stop si message reçu
	puis reprise auto du scan à la fin du message.
*/

//enum MODE_SEUIL {LEV, SNR};  // level ou signal/bruit
//MODE_SEUIL mode_seuil = LEV;




void scan_mem_AIR() 
{
// le scan de la bande aviation civile nécessite un convertisseur de fréquence 110MHz en entrée antenne
// le scan_AIR ne teste pas toutes les fréquences de la gamme mais seulement celles prédéfinies
// dans le fichier 'FRQ_AIR.txt' (sur la SDcard)
// ces fréquences peuvent avoir été écrites par le programme (touche 'Write') ou manuellement 
// avec un  éditeur de texte en respectant la syntaxe, par exemple <123500>

	Serial.println("scan_mem_AIR()");
	bande_active = AIR;
	affi_AB = false;

	efface_box_GROUPE();
	TFT.fillScreen(couleur_fond_ecran); 

	uint16_t couleur_ciel = 10739; 

	TFT.fillRect(x0_box_SCAN, y0_box_SCAN, dx_box_SCAN, dy_box_SCAN, GRIS_6);
	affi_image_from_SD("/bmp565/avion1.bmp", 1, 80);
	
	affiche_box_scan(dy_box_SCAN);

	TFT.setFreeFont(FF0);
	TFT.setTextColor(JAUNE, NOIR);

	TFT.fillRect(2, 2, 42, 80, couleur_ciel); // efface

	affi_image_from_SD("/bmp565/avion3.bmp", 1, 15);
	affi_image_from_SD("/bmp565/PFD42x28.bmp", 1, 40);
		
	String si;
	uint16_t y;
	uint16_t xi, yi;
	int16_t dy, memo_dy;
	
	int16_t attente =0;
	int16_t signal_i;
	uint16_t base = y0_box_SCAN + dy_box_SCAN -5; // bas du graphique
	uint16_t memo_line_H[320];

	mode_seuil = SNR; //2 types de détection possibles: LEV(level) et SNR(signal-to-noise ratio = signal/bruit)
	bt_SNR.selected = true;  bt_SNR.affiche(VERT, 1);
	bt_LEV.selected = false; bt_LEV.affiche(VERT, 1);

	TFT.setFreeFont(FM9);
	TFT.setTextColor(JAUNE, NOIR);

	frequence = 124075;
	Tune_Frequence(frequence);
	affiche_frequence(frequence);
	memo_dy = 0;

	TFT.readRect(0, base - seuil , 319, 1, memo_line_H); // mémorise la ligne avant de tracer
	TFT.drawFastHLine(0, base -seuil, 319, JAUNE); // seuil (trait horizontal sur toute la largeur)

	while(mode_affi == SCAN_M)
	{
		y = y0_box_SCAN + 5;
		uint8_t module;
		if(bande_active == FM) {module = 32;} else {module = 33;}
		Get_Quality( module, &status,  &level, &usn, &wam, &offset, &bandwidth, &mod, &snr );

		si = String(level);
		TFT.drawString("level", x0_box_SCAN +25 , y);
		TFT.drawString(si+"   ", x0_box_SCAN +65 , y);
		y+=15;

		si = String(snr);
		TFT.drawString("snr", x0_box_SCAN +25 , y);
		TFT.drawString(si+"   ", x0_box_SCAN +65 , y);
		y+=20;

		if (mode_seuil == LEV) { signal_i = level; } // attention! level peut être négatif (échelle log)
		if (mode_seuil == SNR) { signal_i= 16 * snr; }

		dy = signal_i/10; 
		if(dy<0) {dy=0;}
		dy+=2;
		
		
	// petites barres verticales
		xi = (frequence - 120000)/50;
	 // xi(120MHz) = 0 px;	
	 // xi(136MHz) = (136000 - 120000) /50 = 320 px

		//TFT.drawFastVLine(xi, base-50, 50, couleur_ciel); //  95 efface
		uint16_t c0 = GRIS_3;
		if(signal_i > seuil*10){c0 = JAUNE;}
		TFT.drawFastVLine(xi, base -dy, dy, c0);
		

		if(signal_i > seuil*10)
		{
			TFT.fillRect(x0_box_SCAN +100, y0_box_SCAN + 20, 20, 20, VERT); // carré coloré
			memo_dy = dy;
			attente = 2;
		}
		else attente --;

		if (attente <=0)
		{
			TFT.drawFastVLine(xi, base -memo_dy, memo_dy, GRIS_3);
			memo_dy = 0;
			TFT.fillRect(x0_box_SCAN +100, y0_box_SCAN + 20, 20, 20, GRIS_5); // carré coloré
			frequence = inc_Frq_in_groupe(&groupe_AIR, 1);
			Tune_Frequence(frequence);
			affiche_frequence(frequence);
			TFT.setFreeFont(FF0);
			TFT.setTextColor(BLANC, NOIR);
			TFT.drawString("120MHz", x0_box_SCAN +5 , base-10);
			TFT.drawString("136", x0_box_SCAN +dx_box_SCAN -22, base-10);

			attente =0;
		}

		delay(50); // ne pas réduire, temps nécessaire pour accrocher la fréquence

		if (ts.tirqTouched() && ts.touched()) 
		{
			get_XY_touch();
			test_clic_bt_stop_scan();

			test_clic_boutons(&bt_seuil_plus); 
			if (bt_seuil_plus.cliked) 
			{
				bt_seuil_plus.cliked = false;
				TFT.pushRect(0, base - memo_seuil, 319, 1, memo_line_H); // efface avec l'image enregistrée
				memo_seuil = seuil;
				seuil += 1;
				if (seuil > 120) {seuil = 120;}
				TFT.readRect(0, base - seuil , 319, 1, memo_line_H); // mémorise la ligne avant de tracer
				TFT.drawFastHLine(0, base -seuil, 319, JAUNE); // seuil (trait horizontal sur toute la largeur)
				TFT.setFreeFont(FF0); TFT.setTextColor(JAUNE, NOIR);
				si = "seuil:" + String(seuil*10);	TFT.drawString(si+" ", x0_box_SCAN +190 , y0_box_SCAN +15);
			}

			test_clic_boutons(&bt_seuil_moins); 
			if (bt_seuil_moins.cliked) 
			{
				bt_seuil_moins.cliked = false;
				TFT.pushRect(0, base - memo_seuil, 319, 1, memo_line_H); // efface avec l'image enregistrée
				memo_seuil = seuil;
				seuil -= 1;
				if (seuil < 5) {seuil = 5;}
				TFT.readRect(0, base - seuil , 319, 1, memo_line_H); // mémorise la ligne avant de tracer
				TFT.drawFastHLine(0, base -seuil, 319, JAUNE); // seuil (trait horizontal sur toute la largeur)
				TFT.setFreeFont(FF0); TFT.setTextColor(JAUNE, NOIR);
				si = "seuil:" + String(seuil*10);	TFT.drawString(si+" ", x0_box_SCAN +190 , y0_box_SCAN +15);
			}

			test_clic_bt_LEV();
			test_clic_bt_SNR();

			test_clic_boutons(&bt_stop_scan); 
		}	
	}

	//delay(1000);

	mode_s = _MEM;
	mode_affi = NORMAL;
	bande_active = FM;
	bt_mode_MEM.selected = true; bt_mode_MEM.affiche(VERT, 2);
	bt_mode_FRQ.selected = false; bt_mode_FRQ.affiche(VERT, 2);
	
	//boutons_preset_set_frequences();
	init_affichages();
	frequence = 89400;
	Tune_Frequence(frequence);
	affiche_frequence(frequence);

	bt_FM.selected = true; bt_FM.affiche(VERT, 1);
	bt_AIR.selected = false; bt_AIR.affiche(VERT, 1);
}


void affi_passes(String band_name, int num_passe, int nb_passes)
{
	TFT.setTextColor(NOIR, BLANC);	TFT.setFreeFont(FMB9);
	String s1 = "SW bande" + band_name;
	s1 += "_";
	TFT.drawString(s1, 2, 14);
	s1 = String(num_passe);
	s1 += "/";
	s1 += String(nb_passes);
	TFT.drawString(s1, 284, 14); // en haut à droite
	TFT.drawString(s1, 10, 200); // en bas à gauche
}


void scan_frq(uint32_t freq_iA, uint32_t freq_iB, String band_name) // toutes les fréquences de la bande, incrémental, saut_freq = constant
{
	Serial.println("scan_frq()");
	Serial.print("freq_iA = "); Serial.println(freq_iA);
	Serial.print("freq_iB = "); Serial.println(freq_iB);

	if(bande_active == SCN) {bande_active = FM;}

	String si;
	String s1;
	String sA, sB;
	float xi=0;
	uint32_t xi32;
	uint16_t y;
	int16_t dy;
	uint16_t yi;
	uint32_t freq;
	uint32_t largeur_bande = freq_iB - freq_iA;
	uint16_t nb_F;
	int n_max;
	int nb_passes = 1;
	int num_passe = 1;

	int16_t signal_i;
	uint16_t base = y0_box_SCAN + dy_box_SCAN -5; // bas du graphique
	uint16_t memo_line_H[320];
	
	uint16_t decal_x;
	uint16_t decal_y;
	

	TFT.fillScreen(couleur_fond_ecran); 
	TFT.fillRect(x0_box_SCAN, y0_box_SCAN, dx_box_SCAN, dy_box_SCAN, NOIR);
	memo_freq = freq_iA;  // variables globales

	mode_seuil = SNR; //2 types de détection possibles: LEV(level) et SNR(signal-to-noise ratio = signal/bruit)
	bt_SNR.selected = true;  bt_SNR.affiche(VERT, 1);
	bt_LEV.selected = false; bt_LEV.affiche(VERT, 1);

	groupe_SCAN.RAZ();
	efface_box_GROUPE();
	
	//TFT.setFreeFont(FM9);
	TFT.setTextColor(JAUNE, NOIR);
	TFT.setFreeFont(FF0);

	freq = freq_iA;

	uint8_t module;
	if(bande_active == FM) 
	{
		saut_freq = 100; // kHz
		module = 32;
		n_max = 190;
		nb_passes = 1;
	} 
	else 
	{
		saut_freq = 5; // kHz
		module = 33;
		n_max = largeur_bande / saut_freq;
		nb_passes = 1 +  n_max / 200;
		if(nb_passes < 1) {nb_passes=1;}
	}

	y = y0_box_SCAN + 5;
	decal_x=0;
	decal_y=0;
	nb_F=0;

	// sA = String(freq);
	// sB = String(freq + n_max * saut_freq);
	// TFT.drawString(sA, x0_box_SCAN +5 , base-5);
	// TFT.drawString(sB, x0_box_SCAN +dx_box_SCAN -40, base-5);

	sA = String(freq_iA + (num_passe-1) * 200 * saut_freq);
	sB = String(freq_iA +  num_passe * 200 * saut_freq);
	TFT.setTextColor(JAUNE, NOIR);
	TFT.setFreeFont(FF0);
	TFT.drawString(sA, x0_box_SCAN +5 , base-5);
	TFT.drawString(sB, x0_box_SCAN +dx_box_SCAN -40, base-5);

	TFT.readRect(0, base - seuil , 319, 1, memo_line_H); // mémorise la ligne avant de tracer
	TFT.drawFastHLine(0, base -seuil, 319, VIOLET); // seuil (trait horizontal sur toute la largeur)

	TFT.setTextColor(VIOLET, NOIR);
	TFT.setFreeFont(FF0);
	si = "seuil:" + String(seuil*10);	TFT.drawString(si+" ", x0_box_SCAN +190 , y0_box_SCAN +15);

	boolean scan_complet;
	boolean rescan = false;

	do 
	{
		groupe_SCAN.RAZ();
		nb_F = 0;
		affiche_box_scan(dy_box_SCAN);
		uint32_t freq_min = freq;

		if(bande_active == SW)  
		{
			affi_passes(band_name, num_passe, nb_passes);
			s1 = "[" + String(freq_iA) + "-" + String(freq_iB) + "]";
			TFT.setTextColor(BLANC, couleur_fond_ecran);	TFT.setFreeFont(FF1);
			TFT.drawString(s1, 145, 14);
		}
//......................................................................
		int n = n_max;

		while((n>0) && !(bande_interdite1 || bande_interdite2))
		{
			if (ts.tirqTouched() && ts.touched()) // écran tactile
			{
				get_XY_touch();
				test_clic_boutons(&bt_stop_scan);
				if (bt_stop_scan.cliked) 
				{
					//bt_stop_scan.cliked = false;
					n=0; // force la sortie de la boucle while()
				}
			}	

			freq += saut_freq;
		
			affiche_frequence(freq);
		
			if( (freq + saut_freq) <= 138000 )
			{ 
				Tune_Frequence(freq);
				delay(15);
			}

			if(bande_active == FM) {module = 32;} else {module = 33;}
			Get_Quality( module, &status,  &level, &usn, &wam, &offset, &bandwidth, &mod, &snr );

			delay(50);
			//if(bande_active == SW) {xi += 1.6;}
			//if(bande_active == FM) {xi += 2;}
			xi += 1.6;

			if (mode_seuil == LEV) { signal_i = level; } // attention! level peut être négatif (échelle log)
			if (mode_seuil == SNR) { signal_i= 16 * snr; }

			dy = signal_i/10; 
			dy-=5;
			if(dy<1) {dy=1;} // 1 et pas 0 afin d'afficher au moins 1 point en cas de signal très faible
			
Serial.print("dy"); Serial.println(dy);			

			//uint16_t c1;
			xi32 = x0_box_SCAN + (uint32_t)xi;

// BARRES VERTICALES ----------------------------	

			if (xi32 < 318) // limite d'affichage à droite
			{
				// on va les tracer les barres verticales point par point afin de pouvoir les coloriser
				int a = base;
				int b = a - dy;

				uint16_t x, y1, j, L;
				uint16_t couleur_i;
				L = dy; // longueur (verticale) de la barre
				for (int16_t i=0; i<L; i++)
				{
					float f = 10.0 * i; // pour balayer toute l'échelle des 470 couleurs disponibles
					j=uint16_t(f);
					if(j>460){j=460;}
					couleur_i = couleurs_aec[2*j] | couleurs_aec[2*j+1]<<8; //couleurs arc-en-ciel, voir fichier Couleurs_AEC.h
					y1=225-i;
					TFT.drawPixel(xi32, y1, couleur_i);
				}
			}
			else // on arrive en bout d'affichage à droite, on passe à la plage suivante si necessaire...
			{
				TFT.fillRect(x0_box_SCAN, y0_box_SCAN, dx_box_SCAN, dy_box_SCAN, NOIR);
				affiche_box_scan(dy_box_SCAN); // efface le tracé et re-dessine les boutons dans la boite
				TFT.readRect(0, base - seuil , 319, 1, memo_line_H); // mémorise la ligne avant de tracer
				TFT.drawFastHLine(0, base -seuil, 319, VIOLET); // seuil (trait horizontal sur toute la largeur)
				si = "seuil:" + String(seuil*10);	TFT.drawString(si+" ", x0_box_SCAN +190 , y0_box_SCAN +15);
				xi = 0;
				num_passe ++;
				if(bande_active == SW)  
				{
					affi_passes(band_name, num_passe, nb_passes);
					sA = String(freq_iA + (num_passe-1) * 200 * saut_freq);
					sB = String(freq_iA +  num_passe * 200 * saut_freq);
					TFT.setTextColor(JAUNE, NOIR);
					TFT.setFreeFont(FF0);
					TFT.drawString(sA, x0_box_SCAN +5 , base-5);
					TFT.drawString(sB, x0_box_SCAN +dx_box_SCAN -40, base-5);
				}
			}


//  --------------------------------------------------	
			if(signal_i > seuil*10) // DETECTION
			{
				if (decal_y > 5) // concerne l'affichage des textes (fréquences trouvées)
				{
					decal_y = 0;
					decal_x ++;
				}

				if((nb_F % 25) == 0) // limite le nb de freq affichées simultanément à 25
				{
					affiche_box_scan(dy_box_SCAN/2);
					decal_x = 0;
					decal_y = 0;
				}
				
				s1 = String(freq);
				TFT.setTextColor(BLANC, NOIR);
				TFT.drawString(s1, x0_box_SCAN + 20 + 45*decal_x, y0_box_SCAN + 9*decal_y); // affiche FREQUENCE
				nb_F++;

				TFT.setFreeFont(FF0);
				TFT.setTextColor(VERT, NOIR);
				s1 = String(nb_F);
				TFT.drawString(s1, x0_box_SCAN +3 , y0_box_SCAN + 45); // nb freq
				TFT.setFreeFont(FF0);

				groupe_SCAN.add_frq(freq); // AJOUTE la fréquence trouvée  au groupe spécial 'groupe_SCAN'
				decal_y ++;

				if(groupe_SCAN.nb_freq >= 100) // groupe plein (en RAM)
				{
					s1 = String(freq);
					TFT.setTextColor(ROUGE, NOIR);
					TFT.drawString(s1+"  groupe SC plein", x0_box_SCAN +80 , y0_box_SCAN + 60);
					TFT.setTextColor(BLANC, NOIR); 
					n=0;
				} 
			}
			n--;
			//if (n<10) {scan_complet = true;}
			delay(10); // ne pas réduire, temps nécessaire pour accrocher la fréquence
			//delay(300); // pour balayage lent

			if (ts.tirqTouched() && ts.touched()) // écran tactile
			{
				get_XY_touch();

				test_clic_boutons(&bt_seuil_plus); 
				if (bt_seuil_plus.cliked) 
				{
					bt_seuil_plus.cliked = false;
					TFT.pushRect(0, base - memo_seuil, 319, 1, memo_line_H); // efface avec l'image enregistrée
					memo_seuil = seuil;
					seuil += 1;
					if (seuil > 120) {seuil = 120;}
					TFT.readRect(0, base - seuil , 319, 1, memo_line_H); // mémorise la ligne avant de tracer
					TFT.drawFastHLine(0, base -seuil, 319, VIOLET); // seuil (trait horizontal sur toute la largeur)
					TFT.setFreeFont(FF0); TFT.setTextColor(VIOLET, NOIR);
					si = "seuil:" + String(seuil*10);	TFT.drawString(si+" ", x0_box_SCAN +190 , y0_box_SCAN +15);
				}

				test_clic_boutons(&bt_seuil_moins); 
				if (bt_seuil_moins.cliked) 
				{
					bt_seuil_moins.cliked = false;
					TFT.pushRect(0, base - memo_seuil, 319, 1, memo_line_H); // efface avec l'image enregistrée
					memo_seuil = seuil;
					seuil -= 1;
					if (seuil < 5) {seuil = 5;}
					TFT.readRect(0, base - seuil , 319, 1, memo_line_H); // mémorise la ligne avant de tracer
					TFT.drawFastHLine(0, base -seuil, 319, VIOLET); // seuil (trait horizontal sur toute la largeur)
					TFT.setFreeFont(FF0); TFT.setTextColor(VIOLET, NOIR);
					si = "seuil:" + String(seuil*10);	TFT.drawString(si+" ", x0_box_SCAN +190 , y0_box_SCAN +15);
				}

				// modes de détection :
				test_clic_bt_LEV(); // mode Level
				test_clic_bt_SNR(); // mode Signal / bruit

				test_clic_boutons(&bt_re_scan);
				if (bt_re_scan.cliked) 
				{
					bt_re_scan.cliked = false;
					TFT.fillRect(x0_box_SCAN, y0_box_SCAN, dx_box_SCAN, dy_box_SCAN, NOIR);
					affiche_box_scan(dy_box_SCAN); // efface le tracé et re-dessine les boutons dans la boite
					freq = memo_freq; // on repart de ma même frequence
					n = n_max;
					xi =0; // départ tracé à gauche
					decal_x=0; // textes fréquences trouvées
					decal_y=0;
					nb_F=0;
					TFT.readRect(0, base - seuil , 319, 1, memo_line_H); // mémorise la ligne avant de tracer
					TFT.drawFastHLine(0, base -seuil, 319, VIOLET); // seuil (trait horizontal sur toute la largeur)
					si = "seuil:" + String(seuil*10);	TFT.drawString(si+" ", x0_box_SCAN +190 , y0_box_SCAN +15);
				}
			}	
		} // fin de la boucle while()

//.......................................................................

		
		if(nb_passes == 1) // because la fonction d'écoute au stylet incorrecte si spectre partiel
		{
			TFT.setTextColor(CYAN, NOIR);
			TFT.setFreeFont(FF0);
			TFT.drawString("On peut ecouter les freq", 10, 145);
			TFT.drawString("en deplacant le stylet sur le spectre", 10, 155);
		}

		boolean stop = false;
		while (! stop) // attend une action en fin de scan (par ex: clic sur bouton 'stop_scan' [x]) ou [re_scan]
		{
			if(do_capt_screen== true) { do_capt_screen = false; write_TFT_on_SDcard(); }
			
			if (ts.tirqTouched() && ts.touched()) 
			{
				get_XY_touch();

				if((x_touch != memo_x_touch) && (y_touch > 170)) // écoute de la fréquence pointée avec le stylet
				{
					TFT.drawFastVLine(memo_x_touch, base, 4, NOIR); // efface
					TFT.drawFastVLine(x_touch, base, 4, BLANC); 

					TFT.drawFastVLine(memo_x_touch, base-50, 4, NOIR); // efface
					TFT.drawFastVLine(x_touch, base-50, 4, BLANC); 

					memo_x_touch = x_touch ;

					float dF = (x_touch - x0_box_SCAN) * saut_freq  / 1.6;
					uint32_t F1 = freq_min + uint32_t(dF);
					freq = F1;
					affiche_frequence(freq);
					Tune_Frequence(freq);
					String sF1 = String(F1);
					TFT.drawString(sF1, 200, 100);
				}

				test_clic_boutons(&bt_re_scan);
				if (bt_re_scan.cliked) 
				{
					bt_re_scan.cliked = false;
					freq = memo_freq; // on repart de ma même frequence
					xi=0;
					xi32 = x0_box_SCAN + (uint32_t)xi;
					stop =true;
					rescan = true; //pas d'appel RECURSIF de la fonction. Trop de pb !!
				}

				test_clic_boutons(&bt_scan_suivant);
				if (bt_scan_suivant.cliked) 
				{
					bt_re_scan.cliked = false;
					TFT.fillRect(x0_box_SCAN, y0_box_SCAN, dx_box_SCAN, dy_box_SCAN, NOIR);
					affiche_box_scan(dy_box_SCAN); // efface le tracé et re-dessine les boutons dans la boite

					n = n_max;
					xi =0; // départ tracé à gauche
					decal_x=0; // textes fréquences trouvées
					decal_y=0;
					nb_F=0;
					memo_freq = freq;
					stop =true; // sortira de la boucle while()
					rescan = true; // bouclera dans la boucle do()
				}			
				
				test_clic_boutons(&bt_stop_scan);
				if (bt_stop_scan.cliked) 
				{
					n=0;
					stop =true; // sortira de la boucle while()
					rescan = false; // ne rebouclera PAS dans la boucle do()
				}
			}
		}
		

	} while (rescan == true); // Fin de la boucle do()

	groupe_SCAN.bloc_to_serial(); 	// pour test avec moniteur série (CuteCom par exemple)
	mode_affi = NORMAL;
	mode_s = _MEM;
	bt_mode_FRQ.selected = false;	bt_mode_FRQ.affiche(VERT, 2);
	bt_mode_MEM.selected = true;	bt_mode_MEM.affiche(VERT, 2);

	write_fichier_params();

	if (bt_stop_scan.cliked == false) 
	{ 
		// si on est sorti de la boucle normalement, (pas par clic sur bouton stop), on passe ici
		uint16_t ya = 20;
		uint8_t dya = 15;
		TFT.fillScreen(NOIR);
		TFT.setTextColor(BLANC, NOIR);
		TFT.setFreeFont(FF0);
		TFT.drawString("Les Freq. detectees sont memo en RAM", 5, ya);	ya += dya;
		TFT.drawString("accessibles par le bouton 'SC'", 5, ya);	ya += dya;
		TFT.drawString("On peut les enregistrer en SD", 5, ya);	ya += dya;
		TFT.drawString("une a une avec le bouton 'Write'", 5, ya);	ya += dya;
		TFT.drawString("sinon -> perdues si stop radio", 5, ya);	ya += dya;
		attente_clic();
		delay(300);
	}
}


void choix_type_scan()
{
	TFT.fillScreen(NOIR);
	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF1);
	TFT.drawString("CHOIX TYPE SCAN", 50, 5);

	init_1_bouton(2, 10, 70, 85, 20, "scan SW", &bt_scan_SW);
	init_1_bouton(2, 120, 70, 85, 20, "scan FM", &bt_scan_FM);
	init_1_bouton(2, 60, 100, 95, 20, "scan AIR", &bt_scan_air);

	boolean ok1 = false;
	while (! ok1)
	{
		if(do_capt_screen== true) { do_capt_screen = false; write_TFT_on_SDcard(); }

		if ((ts.tirqTouched() && ts.touched())) 
		{
			get_XY_touch();
			test_clic_boutons(&bt_scan_SW); 
			if (bt_scan_SW.cliked == true) 
			{
				ok1 = true;
// -------------------------- BANDES SW choisies ----------------	
				bt_scan_SW.cliked = false;
				TFT.fillScreen(NOIR);
				TFT.setTextColor(JAUNE, NOIR);
				TFT.setFreeFont(FF0);
				TFT.drawString("CHOIX BANDE SW", 230, 2);
				swPad1.init(5, 2); // initialise et affiche les 14 boutons des 14 bandes d'ondes courtes (SW)
				swPad1.set_frequences_SW();// voir la fonction 'void SW_PAD::set_frequences_SW()' pour les fréquences SW

				affi_image_from_SD("/bmp565/3r.bmp", 150, 60);

				boolean ok2 = false;	
				while (! ok2)
				{
					if(do_capt_screen== true) { do_capt_screen = false; write_TFT_on_SDcard(); }

					if ((ts.tirqTouched() && ts.touched())) 
					{
						// ---------------- choix de la bande AM SW (impose la fréquence de départ) ---------------
						get_XY_touch();
						uint8_t n = swPad1.test_clic();
						
						uint32_t FA = swPad1.bt_SW[n].freq_A; // fréquence de départ du scan AM
						uint32_t FB = swPad1.bt_SW[n].freq_B; // fréquence de fin du scan AM

						if (FA != 0) // si on n'a pas cliqué en dehors des clous...
						{
							String band_name = " "; // espace à gauche
							uint16_t L = swPad1.bt_SW[n].lambda;
							band_name += String(L)+"m";
							TFT.setTextColor(NOIR, BLANC);
							TFT.setFreeFont(FMB12);
							TFT.drawString(band_name + " ", 150, 10);

							String s2 = String(FA) + ".." + String(FB)+ " kHz";
							TFT.setTextColor(JAUNE, NOIR);
							TFT.setFreeFont(FF1);
							TFT.drawString(s2, 140, 35);

							bande_active = SW; 
							ok2=true;
							mode_affi = SCAN_F;
							mode_s = _FRQ;
							delay(1000);
							scan_frq(FA, FB, band_name); // scanner (AM-SW ou FM )
						}
					}
				}
			}

			if ((ts.tirqTouched() && ts.touched())) 
			{
				get_XY_touch();
				test_clic_boutons(&bt_scan_FM); 
				if (bt_scan_FM.cliked == true) 
				{
// ------------------------------ BANDE FM choisie -----------------					
					bt_scan_FM.cliked = false;
					bande_active = FM;
					ok1=true;
					mode_affi = SCAN_F;
					mode_s = _FRQ;
					scan_frq(88000, 108000, "bande FM");
				}
			}	

			test_clic_boutons(&bt_scan_air); 
			if (bt_scan_air.cliked == true) 
			{
// ------------------------------ BANDE AIR ----------------				
				bt_scan_air.cliked = false;
				mode_affi = SCAN_M;
				bande_active == AIR;
				ok1=true;
				scan_mem_AIR();
				mode_s = _MEM;
			}
		}	
	}
}



void test_clic_Bt_SCAN() 
{
	if ((ts.tirqTouched() && ts.touched())) 
	{
		get_XY_touch();
		test_clic_boutons(&Bt_SCAN); 
		if (Bt_SCAN.cliked == true) 
		{
			Bt_SCAN.cliked = false;
			choix_type_scan();  // choix bande à scanner (SW - FM) et type FRQ ou AIR (puis déclenche le scan)

			init_affichages();

			groupe_actif = gSCN;
			bande_active = SCN;

			//bt_SCN.selected = true; bt_SCN.affiche(CYAN, 1);
			bt_SW.selected = false; bt_SW.affiche(VERT, 1);
			bt_FM.selected = false; bt_FM.affiche(VERT, 1);
			bt_AIR.selected = false;bt_AIR.affiche(VERT, 1);

			//affiche_numero_frq(String(1), String(nb_F));
			affiche_frequence(frequence);
		}
	}
}



void retour_normal()
{
	TFT.fillScreen(NOIR);
	init_affichages();
	//bt_SW.selected = false;
	//bt_FM.selected = true;
	//bande_active = FM;
	//modulation_active = WFM;
	//frequence = presetPad1.bt_preset[0].frequence_FM;
	//bt_SW.affiche(VERT, 1);
	//bt_FM.affiche(VERT, 1);
}


void attente_clic()
{
	delay(300);
	init_1_bouton(1, 300, 5, 20, 20, "X", &bt_close);
	while ( !(ts.tirqTouched() && ts.touched()) ) {;}
	retour_normal();

}



void attente_clic2() // et anti-parasite !!!
{
	delay(300);
	while ( !(ts.tirqTouched() && ts.touched()) ) 
	{
		if(do_capt_screen== true) { do_capt_screen = false; write_TFT_on_SDcard(); }
	}
}



void SD_listFiles() 
{
	uint16_t y=0;
	String s1;

	TFT.fillScreen(NOIR);
	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF0);
	TFT.drawString("Liste des fichiers (en memoire SD)", 0, y);	y+=30;


	File root = SD.open("/");
	File file = root.openNextFile();
	while (file) 
	{
		s1 = String(file.name());
		TFT.setTextColor(VERT, NOIR);
		TFT.drawString(s1, 0, y);

		s1 = "size:";
		TFT.setTextColor(BLANC, NOIR);
		TFT.drawString(s1, 100, y);

		s1 = file.size();
		TFT.setTextColor(JAUNE, NOIR);
		TFT.drawString(s1, 140, y);	
		
		y+=10;
		file = root.openNextFile();
	}
}



void SD_RAZ()
{
	boolean valider = false; // <-- ecrire "= true" pour rendre l'effacement possible
	// (évite tout effacement accidentel et donc perte de centaines de fréquences...)

	if(valider == false)
	{
		TFT.fillScreen(NOIR);
		TFT.setFreeFont(FM9);
		TFT.drawString("FONCTION VOLONTAIREMENT", 50, 120);
		TFT.drawString("INACTIVE", 50, 140);
		TFT.drawString("VOIR LE CODE SOURCE", 50, 160);
		TFT.drawString("void SD_RAZ()", 50, 180);
		delay(3000);
		init_affichages();
		return;
	}

	SD.remove("FRQ_SW.txt");
	SD.remove("FRQ_SW_PRST.txt");
	SD.remove("FRQ_FM.txt");
	SD.remove("FRQ_FM_PRST.txt");
	SD.remove("FRQ_AIR.txt");
	SD.remove("FRQ_AIR_PRST.txt");
	
	Serial.println("fin RAZ SD"); 
}



void test_clic_bouton_LST()
{
	uint16_t c1 = NOIR;
	uint16_t c2 = VERT;

	test_clic_boutons(&bt_LST );
	if(bt_LST.cliked)
	{
		if (SDcardOk == false) { affi_message ( "SDcard absente", "...", "", "", "" ); return; }

		bt_LST.cliked = false; 
		bt_LST.selected = false;
		bt_LST.affiche( c1, 1);

		SD_listFiles();
		init_1_bouton(1, 300, 5, 15, 15, "OK", &bt_annuler);
		attente_clic2();

		TFT.fillScreen(NOIR);
		groupe_SW.affiche_bloc("SW",0); 
		groupe_FM.affiche_bloc("FM",100); 
		groupe_AIR.affiche_bloc("AIR",200); 
		init_1_bouton(1, 300, 5, 15, 15, "OK", &bt_annuler);
		attente_clic2();

		mute = true;
		Set_Mute(true); // fonction située dans le fichier 'driverTEF6686_628.h'
		delay(50);
		bt_mute.selected = true;
		bt_mute.affiche(ROUGE, 1);
		delay(500);
		affi_deroulant("Liste codes PI RDS", "/RDS_codes_PI.lst");
		retour_normal();
	}
}



void test_clic_bouton_SD_write() // en SD, dans un des fichiers ("/FRQ_SW.txt" ...)
{
	uint16_t c1 = GRIS_6;
	uint16_t c2 = VERT;
	String s1;

	test_clic_boutons(&bt_SD_write );
	if(bt_SD_write.cliked)
	{

		bt_SD_write.cliked=false;
		bt_SD_write.selected=false;
		bt_SD_write.affiche( GRIS_5, 1);

		Serial.println("--------------------------------");
		Serial.println("bouton_SD_write clic");

		//attention à ne pas oublier ces < >
		s1 = "<";
		s1 += String(frequence); // // fréquence en cours
		s1 += ">";

		boolean ok = false;
		if (bande_SW)  
		{ 
			if (! groupe_SW.test_Frq_presente(frequence)) // test effectué sur le groupe en RAM
			{
// AJOUTE la fréquence en fin d'une liste de fréquences. Travaille directement sur la mémoire SD
				File file = SD.open("/FRQ_SW.txt", FILE_APPEND);
				file.print(s1); 
				file.print('\n');
				file.close(); // enregistre CE fichier en mémoire SD, sans toucher aux autres
				delay(300);
				ok = true;
			}
		}

		if (bande_FM)  
		{ 
			if (! groupe_FM.test_Frq_presente(frequence))
			{
				File file = SD.open("/FRQ_FM.txt", FILE_APPEND);
				file.print(s1);
				file.close();
				delay(300);
				ok = true;
			}
		}

		if (bande_AIR)  
		{ 
			if (! groupe_AIR.test_Frq_presente(frequence))
			{			
				File file = SD.open("/FRQ_AIR.txt", FILE_APPEND);
				file.print(s1);
				file.close();
				delay(300);
				ok = true;
			}
		}	

		String s1, s2;
		if (ok) {s1 = "Enregistrement OK";   s2 = ""; }
		else 	{s1 = "Freq. deja presente";  s2 = "NOT SAVE";} 
		Serial.println(s1);

		if (ok) {load_GRP_FREQ_SD();}	// recharge les listes de fréquences depuis la SD pour être à jour

		TFT.fillScreen(NOIR);
		TFT.setTextColor(JAUNE, NOIR);
		TFT.setFreeFont(FF1);
		TFT.drawString(s1, 5, 40);
		TFT.drawString(s2, 5, 60);
		delay(1000);
		retour_normal();
	}
}



void test_clic_bt_erase_1F()  // en SD
{
	uint16_t c1 = GRIS_6;
	uint16_t c2 = VERT;

	test_clic_boutons(&bt_erase_1F );
	if(bt_erase_1F.cliked)
	{
		bt_erase_1F.cliked = false; 
		bt_erase_1F.selected = false;
		bt_erase_1F.affiche(c1, 1);

		delay (100);
		bt_erase_1F.cliked = false;  
		bt_erase_1F.selected = false;
		bt_erase_1F.affiche(c2, 1); // fugitif		

		uint16_t F;

		//todo: en SD

	}
}


void test_clic_boutons_MODE() // FREQ - MEM
{
	uint16_t c1 = NOIR;
	uint16_t c2 = VERT;
	uint16_t c3 = JAUNE;

	bt_mode_FRQ.cliked = false;
	bt_mode_MEM.cliked = false;
	
	test_clic_boutons(&bt_mode_FRQ );
	if (bt_mode_FRQ.cliked) // les boutons sont exclusifs
	{
		presetPad1.deselect_boutons();
		mode_s = _FRQ;
		affiche_index_frq();
		bt_4.selected=true; // 100kHz
		bt_4.affiche(c2, 1);
		bt_mode_FRQ.selected = true;
		bt_mode_MEM.selected = false;
		affiche_frequence(frequence);
		efface_numero_frq();
	} 

	test_clic_boutons(&bt_mode_MEM );
	if (bt_mode_MEM.cliked) 
	{
		efface_index_frq();
		mode_s = _MEM;
		bt_mode_MEM.selected = true;
		bt_mode_FRQ.selected = false;
		affiche_frequence(frequence);
	} 

	bt_mode_FRQ.affiche(c2, 2);
	bt_mode_MEM.affiche(c3, 2);
	init_boutons_Plus_Moins(); //ce qui va changer le symbole affiché en '-' et '+' ou '<' et '>' en fonction du 'mode_s'
	if (mode_affi == NORMAL) 
	{
		bt_moins.affiche(VERT ,1);	
		bt_plus.affiche(VERT ,1);
	}

}


void test_clic_boutons_BANDE() // SW, FM, AIR, SCAN
{
	uint16_t c1 = GRIS_5;
	uint16_t c2 = VERT;

	bt_SW.cliked = false;
	bt_FM.cliked = false;
	bt_AIR.cliked = false;

	test_clic_boutons(&bt_SW );
	if (bt_SW.cliked) 
	{
		TFT.fillRect(x0_box_info1+2, y0_box_info1+1, 118, 12, NOIR); // efface
		efface_box_entete2();
		//Serial.println("bt_SW.cliked"); 
		bt_SW.cliked = false;

		groupe_actif = gSW;
		bande_active = SW;
		modulation_active = AM;
		bt_SW.selected = true;
		bt_FM.selected = false; // les boutons sont exclusifs
		bt_AIR.selected = false;
		bt_SCN.selected = false;

		//clic_logiciel_bouton(&presetPad1.bt_preset[0]);
		//traite_boutons_presetPad(0); 

		frequence = 15000; // 15MHz
		Tune_Frequence(frequence);
		affiche_frequence(frequence);
		
		uint8_t nb_F = groupe_SW.nb_freq;
		affiche_numero_frq(String(1), String(nb_F));
	} 


	test_clic_boutons(&bt_FM );
	if (bt_FM.cliked) 
	{
		//Serial.println("bt_FM.cliked");
		efface_box_entete2(); 
		bt_FM.cliked = false;

		groupe_actif = gFM;
		bande_active = FM;
		modulation_active = WFM;
		bt_FM.selected = true;
		bt_SW.selected = false;
		bt_AIR.selected = false;
		bt_SCN.selected = false;
		affi_boutons_SW_FM_AIR_SCN();
		
		//traite_boutons_presetPad(0); 
		frequence = presetPad1.bt_preset[0].frequence_FM;
		Tune_Frequence(frequence);
		affiche_frequence(frequence);
		clic_logiciel_bouton(&presetPad1.bt_preset[0]);

		uint8_t nb_F = groupe_FM.nb_freq;
		affiche_numero_frq(String(1), String(nb_F));
	} 


	test_clic_boutons(&bt_AIR );
	if (bt_AIR.cliked) 
	{
		TFT.fillRect(x0_box_info1+2, y0_box_info1+1, 118, 12, NOIR); // efface
		efface_box_entete2();
		//Serial.println("bt_AIR.cliked"); 
		bt_AIR.cliked = false;

		groupe_actif = gAIR;
		bande_active = AIR;
		modulation_active = AM;
		bt_AIR.selected = true;
		bt_SW.selected = false;
		bt_FM.selected = false;
		bt_SCN.selected = false;
		affi_boutons_SW_FM_AIR_SCN();

		frequence = 123500;
		Tune_Frequence(frequence);
		affiche_frequence(frequence);

		//clic_logiciel_bouton(&presetPad1.bt_preset[0]);
		//traite_boutons_presetPad(0); 


		uint8_t nb_F = groupe_AIR.nb_freq;
		affiche_numero_frq(String(1), String(nb_F));
	} 


	test_clic_boutons(&bt_SCN );
	if (bt_SCN.cliked == true) 
	{
		efface_box_entete2();
		Serial.println("bt_SCN.cliked"); 
		bt_SCN.cliked = false;

		groupe_actif = gSCN;
		bande_active = SCN;
		bt_SCN.selected = true;
		bt_SW.selected = false;
		bt_FM.selected = false;
		bt_AIR.selected = false;
		affi_boutons_SW_FM_AIR_SCN();

		presetPad1.deselect_boutons(); 

		uint8_t nb_F = groupe_SCAN.nb_freq;
		affiche_numero_frq(String(1), String(nb_F));
	} 

	Serial.print("groupe actif = "); Serial.println(groupe_actif); 

}


void test_clic_bouton_set() // bouton pour passer dans le mode d'écriture de la F en cours en SD
// L'écriture en question sera effectuée par la fonction 'traite_boutons_presetPad()'
{
	test_clic_boutons(&bt_set);
	if (bt_set.cliked) 
	{
		bt_set.cliked = false;
		vu_metre_actif = false;
		TFT.fillRect(x0_box_info2, y0_box_info2, 140, 70, NOIR); 
	
		TFT.setTextColor(VERT, NOIR);
		TFT.setFreeFont(FF0);
		TFT.drawString("Cliquez sur un des 8 bt", x0_box_info2+2, y0_box_info2+2);
		TFT.drawString("'preset' pour lui ", x0_box_info2+2, y0_box_info2+12);
		TFT.drawString("attribuer la F en cours", x0_box_info2+2, y0_box_info2+22);

		init_1_bouton(1, x0_box_info2+82, y0_box_info2+50, 50, 15, "annuler", &bt_annuler);
		bt_annuler.affiche(BLANC, 1);	

		mode_affi = SET_F_PRESET; // ce qui modifie le comportement de la fonction 'traite_boutons_presetPad()'
		Serial.println("mode_affi == SET_F_PRESET");

		delay (100);
		bt_set.selected = false;
		bt_set.affiche(BLANC, 1);	
	}
}


void affi_bargraph(uint16_t x, uint16_t y, uint16_t dx, int16_t vi)
{
	if(! quiet) 
	{
		//TFT.fillRect(x, y, dx, 8, NOIR); // efface
		if (valeur_bargraph < vi) {valeur_bargraph ++;} // pour déplassements progressifs
		if (valeur_bargraph > vi) {valeur_bargraph --;} // pour déplassements progressifs

		if(valeur_bargraph<0) {valeur_bargraph =0;}
		if(valeur_bargraph > dx-1) {valeur_bargraph = dx-1;}
		TFT.drawRect(x, y, dx, 5, couleur_traits);
		//TFT.fillRect(x, y, valeur_bargraph, 5, JAUNE);
		TFT.fillRect(x + valeur_bargraph, y, 1, 5, VERT);	// affiche juste l'extrémité 
		TFT.fillRect(x + valeur_bargraph+1, y, 1, 5, NOIR); // efface juste l'extrémité +1
	}
	else
	{
		TFT.fillRect(x, y, dx, 5, couleur_fond_ecran); // efface le bargraph
	}
}


void affiche_tension_batt(uint16_t x, uint16_t y, uint16_t dx, int8_t valeur) // valeur en %
{
	uint16_t c1;
	c1 = ROUGE;
	if(valeur>30){c1 = ORANGE;}
	if(valeur>50){c1 = JAUNE;}
	if(valeur>70){c1 = VERT;}

	uint32_t v2 = valeur * dx / 100;
	TFT.fillRect(x-20, y, dx, 8, NOIR); // efface
	efface_box_entete3();
	if(v2<0) {valeur =0;}
	if(v2 > dx) {v2 = dx;}
	TFT.drawRect(x, y, dx, 5, couleur_traits);
	TFT.fillRect(x, y, v2, 5, c1);

	String s1 = String(valeur) +"%";
	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF0);
	TFT.drawString(s1, x-20, y); 
	TFT.drawString("bat", x + 10 , y+5);

	TFT.drawRect(0, 0, 319, 240, couleur_traits); // cadre principal pourtour de l'écran
}


/*
void affiche_1_bargraph(uint16_t  x, uint16_t  y, uint16_t dx,  int16_t valeur, uint16_t couleur)
{
	TFT.fillRect(x, y, dx+40, 8, NOIR); // efface
	TFT.drawString(String(valeur), x, y); 

	if(valeur<0) {valeur =0;}
	if(valeur > dx) {valeur = dx;}
	TFT.drawRect(x+40, y, dx, 5, couleur_traits);
	TFT.drawRect(x+40, y, valeur, 5, couleur);
}

void affiche_bars_graph()
{
	affiche_1_bargraph(170, 150, 100, level/10, VERT);
	affiche_1_bargraph(170, 160, 100, usn, JAUNE);
	affiche_1_bargraph(170, 170, 100, wam, BLEU_CLAIR);
	affiche_1_bargraph(170, 180, 100, offset, BLANC);
}
*/

String int_to_hex(uint16_t nb)
{
	char symb[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
	uint8_t A = (nb & 0b1111000000000000) >>12;
	uint8_t B = (nb & 0b0000111100000000) >>8;
	uint8_t C = (nb & 0b0000000011110000) >>4;
	uint8_t D = (nb & 0b0000000000001111);
	String s1 = String(symb[A]) + String(symb[B])  + String(symb[C])  + String(symb[D]) ;
	return s1;
}



void traite_signal_RDS()
{
	Get_RDS_Data(&status, &A_block, &B_block, &C_block, &D_block, &dec_error);
/*	
	A_Block always contains the 16-bit program identifier. 
	The first 11 bits (bits 15–5) of block 2 are also the same in all groups. 

	La Liste des codes RDS autorisés se trouve ici : https://www.csa.fr/maradiofm/radiords_tableau
	
	Pour les autres blocks, voir : 
	https://en.wikipedia.org/wiki/Radio_Data_System
	(c'est sans doute logique mais terriblement indigeste !!!)
*/
	char buffer1[64];
	uint16_t nombre =0;
	String s_recue, s1, s2, s3;

	s_recue = int_to_hex(A_block); // exemple: 'F201'  ; mis à jour par le 'Get_RDS_Data()'
	if (s_recue == memo_s_recue) {return; } // puisque rien de nouveau à traiter
	memo_s_recue = s_recue;

	TFT.fillRect(x0_box_info1+2, y0_box_info1, 118, 16, NOIR); // efface (en bas à gauche)
	TFT.setTextColor(BLEU_CLAIR, GRIS_6);
	TFT.setFreeFont(FF0);

	File file1 = SD.open("/RDS_codes_PI.lst", "r");
	while (file1.available())
	{
		int nb_bytes = file1.readBytesUntil('\n', buffer1, sizeof(buffer1));
		buffer1[nb_bytes] = 0; // zéro terminal afin d'obtenir un string
		s1 = buffer1;

		s2 = s1.substring(0, 4); // exemple: 'F218'
		s3 = s1.substring(4); // la suite

		
		if (s_recue == s2) // la station reçue figure dans la liste
		{
			TFT.setTextColor(JAUNE, GRIS_6);
			TFT.drawString(s2, x0_box_info1+5, y0_box_info1 +4); 
	
			TFT.setTextColor(BLEU_CLAIR, GRIS_6);
			TFT.drawString(s3, x0_box_info1+30, y0_box_info1 +4); 

			return; // on ne continue pas à boucler si trouvé
		}
	}
}



void affiche_force_signal()
{
//----------------------------------------------------------------------------------------------------------
// AFFICHE FORCE SIGNAL HF		
		compteur1 =0;
		
		//float signal_sur_bruit = level;// - 1.5 * usn; // marche à peu près bien pour la FM.

		float diff =  level - position_aiguille;
		if(diff > 60.0) {diff = 60.0;} 
		if(diff < -60.0) {diff = -60.0;} // évite une trop grande réaction au sortir du mode 'mute'
// supprime les tremblements de l'aiguille		
		position_aiguille += diff / 4.0; 
		
		//affi_bargraph(170, 90, 60, mod/2);

		if ((mode_affi == NORMAL) && (! mute)  && (! quiet)) 
		{ 
			plotAiguille(position_aiguille/6.0); // génère des parasites audibles !
		} 
}



void loop()
{
	if((mode_affi != SCAN_F) && (mode_affi != SCAN_M))// invalide les boutons situés derrière le panneau scan en modes SCAN
	{
		if (ts.tirqTouched() && ts.touched()) 
		{
			memo_y_touch = y_touch;
			get_XY_touch();

			test_clic_6_boutons_frq(); // 6 rectangles situés au dessus des gros chiffres de la fréquence
			test_clic_bouton_mute();
			//test_clic_bouton_SD_RAZ();
			test_clic_bouton_SD_write();
			test_clic_bouton_LST();
			test_clic_bt_erase_1F();
			uint8_t n_touch1 = numPad1.test_clic(); // pavé numérique
			if (n_touch1 != 253) { traite_touches_pad(n_touch1); } // pavé numérique	

			test_clic_boutons_plus_moins(); // boutons '<' et '>'
			test_clic_bouton_Sleep();
			test_clic_bt_LED();
			test_clic_bt_CAL(); // calibration de l'écran tactile
			test_clic_bt_quiet();
			test_clic_bouton_TEST();
			test_clic_bouton_info();
			test_clic_Bt_reset();
			test_clic_Bt_SCAN();  // choix bande à scanner (SW - FM) et type FRQ ou AIR (puis déclenche le scan)
			test_clic_boutons_MODE(); 	// FREQ / MEM 		 (tout en haut à gauche des gros chiffres)
			test_clic_boutons_BANDE();  // SW, FM, AIR, SC;  (SC = SCAN)

			uint8_t num_bouton = presetPad1.test_clic(); // 8 petits boutons carrés de présélection de 8 fréquences
			if (num_bouton != 253) { traite_boutons_presetPad(num_bouton); }	 

			test_clic_bouton_set();  // attribution de la fréquence en cours à un des 8 boutons preset.
			test_clic_bt_annuler();

			test_clic_bt_coul();
			test_clic_bt_RST_affi();
		}

	// la limitation SW max = 28MHz est due au module TEF6686 lui-même
		if (frequence >= 138000) {frequence = 138000;} //138 MHz limite à cause SW max = 28MHz et conv AirBand = 110 MHz
		if (frequence < 1500) {frequence = 1500;} // 1.5 MHz

		uint8_t module;
		if(bande_active == FM) {module = 32;} else {module = 33;}
		Get_Quality( module, &status,  &level, &usn, &wam, &offset, &bandwidth, &mod, &snr );
		
		affi_bargraph(170, 90, 60, mod/2);

		if (compteur1 >= 20)
		{
			affiche_force_signal();

//----------------------------------------------------------------------------------------------------------
	// AFFICHE TENSION BATTERIE
//----------------------------------------------------------------------------------------------------------
	// voir la feuille de calcul 'Mesure de la tension de la batterie.ods' établie sur tests avec alim numérique
	// voir aussi, sur le schéma, la valeur des résistances (diviseur analogique) sur l'entrée GPIO35 (analogPin = 35)
	// Note: la valeur de la tension sur le pin  GPIO35 ne doit jamais dépasser 3v3 (ce qui est lu = 4096)
			int valeurLue;
			valeurLue = analogRead(analogPin);
			//Serial.print("valeurLue = "); Serial.println(valeurLue);
			float pourcent  = ((float)valeurLue - 2570.0) / 7.5;
			if (pourcent > 100.0) {pourcent = 100.0;}
			if (pourcent < 0.0) {pourcent = 0.0;}

		//Serial.print("pourcent = "); Serial.println(pourcent);

	// intégration
			float diff2 =  pourcent - valeur_affi;
			if(diff2 > 10.0) {diff2 = 10.0;} 
			if(diff2 < -10.0) {diff2 = -10.0;} // évite une trop grande réaction au sortir du mode 'mute'
			valeur_affi += diff2 / 10.0; 

			if ( abs(valeur_affi - memo_valeur_affi) > 1.5  ) // pour éviter trop d'affichages inutiles (et clignotements à l'écran)
			{
				affiche_tension_batt(270, 4, 46, (int8_t) valeur_affi);
				memo_valeur_affi = valeur_affi;
			}
		}

		if (compteur2 >= 100)
		{
			compteur2 =0;

			if(bande_active == FM) {traite_signal_RDS();}
		}

		if (quiet == true) 
		{
			// quiet (anti-parasites audio, surtout pour les stations faibles)
			// inconvéniants: fige les affichages (vu_metre, bargraph signal audio...)	
			affi_image_from_SD("/bmp565/montagne140x70.bmp", x0_vu_metre, y0_vu_metre);
			attente_clic2(); // quiet (anti-parasites)
		} 

		if(do_capt_screen== true) { do_capt_screen = false; write_TFT_on_SDcard(); }

/*		
		if (compteur3 >= 1000)
		{
			// passe en mode quiet automatiquement, avec ses inconvéniants !
			compteur3 =0;

			quiet = true;

			bt_quiet.cliked = false;
			bt_quiet.selected = true;
			bt_quiet.affiche(VERT, 1); 
		}	
*/

		delay(5); // détermine la réactivité du bargraph
		compteur1++;
		compteur2++;
		compteur3++;
	}
	
}


/** ***************************************************************************************
	CLASS TOUCH_BOUTON  // affiche un nombre ou un petit texte dans un rectangle
	ainsi que (en plus petit) deux valeurs supplémentaires, par ex: les valeurs mini et maxi					
********************************************************************************************/

// Constructeur
TOUCH_BOUTON::TOUCH_BOUTON()
{

}

// Constructeur
TOUCH_BOUTON_PRESET::TOUCH_BOUTON_PRESET()
{

}


// Constructeur
TOUCH_BOUTON_SW::TOUCH_BOUTON_SW()
{

}


void TOUCH_BOUTON::init(uint16_t x_i, uint16_t y_i, uint8_t dx_i, uint8_t dy_i, uint8_t dr_i, uint16_t couleur_i)
{
	x0 = x_i;
	y0 = y_i;
	dx = dx_i;
	dy = dy_i;
	dr = dr_i;
	couleur = couleur_i;

	cliked = false;
	selected = false;
}



void TOUCH_BOUTON::affiche(uint16_t coul_fill_select, uint8_t n_font)
{
	uint16_t couleur_contour = GRIS_5;
	uint16_t couleur_texte = BLANC;

	if(selected) 
	{
		TFT.fillRoundRect(x0, y0, dx, dy, dr, coul_fill_select);
		TFT.setTextColor(NOIR);
	}  
	else 
	{
		TFT.fillRoundRect(x0, y0, dx, dy, dr, couleur); // efface
		TFT.drawRoundRect(x0, y0, dx, dy, dr, couleur_contour); // retrace juste le contour
		TFT.setTextColor(couleur_texte);
	}

	//FM9 FMB9 FSS9... voir le fichier FrSD_Fonts.h
	if (n_font == 1) { TFT.setFreeFont(FF0);}
	if (n_font == 2) { TFT.setFreeFont(FM9);}
	if (n_font == 3) { TFT.setFreeFont(FMB9);}
	if (n_font == 4) { TFT.setFreeFont(FSS9);} 

	TFT.drawString(label, x0+3, y0 - (2*n_font) + (dy-4)/2);
}


uint8_t TOUCH_BOUTON::read_dx()
{
	return dx;
}


uint8_t TOUCH_BOUTON::read_dy()
{
	return dy;
}

/** ***************************************************************************************
	CLASS grid_PAD
********************************************************************************************/
// Constructeur
GRID_PAD::GRID_PAD() 
{

}


void GRID_PAD::init(uint16_t xi, uint16_t yi)
{
	x0 = xi;
	y0 = yi;
	uint16_t x, y;


	x = x0+2;	
	y = y0+1;

	for(uint8_t n =1; n <= nb_t; n++)
	{
		bt_grid[n].init(x, y, dxt, dyt, 3, n * 100);
		bt_grid[n].label="";
		//bt_grid[n].affiche(BLEU, 1);	
		x += dxt+1;
		if (x > (x0 + nb_bt_x * dxt)) {x = x0+2; y += dyt+1; }
	}
}


void GRID_PAD::affiche()
{
	for(uint8_t n =1; n <= nb_t; n++)
	{
		bt_grid[n].affiche(BLEU, 1);
	}
}	


void GRID_PAD::set_couleurs()  // F = facteur d'assombrissement
{
/* 
voir la page: 
https://rgbcolorpicker.com/565
qui permet de reconfigurer les couleurs RGB565 (5+6+5 = 16 bits) très simplement
*/
	uint16_t c565i, c565i2;
	uint8_t R, G, B;
	int16_t j, k;


// les deux premières lignes couleurs arc-en-ciel
	k=-16;
	uint8_t F=1;
	for(uint8_t n=0; n<32; n++)
	{
		j=30*(32-n);
		c565i = couleurs_aec[2*j] | couleurs_aec[2*j+1]<<8;

		RGB565_to_888(c565i,  &R,  &G,  &B);
		R /= F;
		G /= F;
		B /= F;
		c565i2 = Color_To_565(R, G, B);
		if((k>=0) && (k<16)) {bt_grid[k].couleur = c565i2;}
		k++;
	}
	
// les lignes 3 et 4 couleurs plus sombres	
	k=0;
	F=3;
	for(uint8_t n=0; n<32; n++)
	{
		j=30*(32-n);
		c565i = couleurs_aec[2*j] | couleurs_aec[2*j+1]<<8;

		RGB565_to_888(c565i,  &R,  &G,  &B);
		R /= F;
		G /= F;
		B /= F;
		c565i2 = Color_To_565(R, G, B);
		if((k>=16) && (k<32)) {bt_grid[k].couleur = c565i2;}
		k++;
	}

	bt_grid[16].couleur = 49174; // violet

	//bt_grid[25].couleur = 0b1111100000000000; // R
	//bt_grid[26].couleur = 0b0000011111100000; // V
	//bt_grid[27].couleur = 0b0000000000011111; // B
	//bt_grid[28].couleur = 0xFFFF;
	//bt_grid[29].couleur = 0;

	bt_grid[32].couleur = GRIS_4;
	bt_grid[32].label="X";
}



uint8_t GRID_PAD::test_clic()
{
	if ( (( x_touch > x0) && (x_touch < (x0 + dxt*nb_bt_x +20))) && (( y_touch > y0) && (y_touch < (y0 + dyt*nb_bt_y +10))))
	{
		uint8_t num_bouton =255;

		for(uint8_t n = 0; n<nb_t; n++)
		{
			test_clic_boutons(&bt_grid[n] ); 
			if(bt_grid[n].cliked) 
			{
				num_bouton = n; 
			}
		}

		delay(100);
		return num_bouton;
	}
	return 253;
}


/** ***************************************************************************************
	CLASS NUMPAD
********************************************************************************************/
// Constructeur
NUM_PAD::NUM_PAD() 
{

}


void NUM_PAD::init(uint16_t xi, uint16_t yi, boolean fond) // si fond =false, ne resessine que les boutons
{
	x0 = xi;
	y0 = yi;
	uint16_t x, y;
	uint8_t dxt = 25; // taille x d'une touche
	uint8_t dyt = 23; // taille y d'une touche

	if(fond == true) {TFT.fillRect(x0, y0, 3*dxt +6, 4*dyt +7, NOIR);}

	if(fond == true) {TFT.fillRect(x0, y0, 3*dxt +6, 4*dyt +7, NOIR);}

	uint16_t c1 = GRIS_5;
	uint16_t c2 = JAUNE;
	
	x = x0+2;	
	y = y0+2;

	for(uint8_t n =1; n<10; n++)
	{
		bt_pad[n].init(x, y, dxt, dyt, 3, GRIS_5);
		bt_pad[n].label=String(n);
		bt_pad[n].affiche(c2, 2);	
		x += dxt+1;
		if (x > (x0 + 3*dxt)) {x = x0+2; y += dyt+1; }
	}
	bt_pad[0].init(x, y, dxt, dyt, 3, GRIS_5);	bt_pad[0].label="0"; bt_pad[0].affiche(c2, 2);	x += dxt+1;
	bt_point.init(x, y, dxt, dyt, 3, GRIS_5);  bt_point.label=".";	 bt_point.affiche(c2, 2); 
	
	x += dxt+1;
	bt_ok.init(x, y, dxt, dyt, 3, GRIS_5);	bt_ok.label="ok";	
	bt_ok.affiche(c2, 1);
}


uint8_t NUM_PAD::test_clic()
{
	// zone des boutons du clavier
	if ( (( x_touch > x0) && (x_touch < x0 + 100)) && (( y_touch > y0) && (y_touch < y0 + 130)))
	{
		uint8_t num_touche =0;

		for(uint8_t n = 0; n<10; n++)
		{
			test_clic_boutons(&bt_pad[n] ); if(bt_pad[n].cliked) {num_touche=n; n_appui ++;}
		}

		test_clic_boutons(&bt_point ); if(bt_point.cliked) {num_touche=254; } // bouton "."
		test_clic_boutons(&bt_ok ); if(bt_ok.cliked) {num_touche=255; n_appui ++;} // bouton "ok"
		delay(100);
		init(x0, y0, false); // 'false' évite le clignotement lorsqu'on repeint le fond noir
		
		return num_touche;
	}
	return 253;
}

/** ***************************************************************************************
	CLASS PRESET_PAD
********************************************************************************************/

// Constructeur
PRESET_PAD::PRESET_PAD() 
{

}


void PRESET_PAD::init(uint16_t xi, uint16_t yi)
{
	x0 = xi;
	y0 = yi;

	uint16_t c1 = GRIS_5;
	uint16_t c2 = JAUNE;

	uint16_t x2, y2;
	x2 = x0;	
	y2= y0;

	for(uint8_t n=0; n<8; n++)
	{
		bt_preset[n].init(x2, y2, 15, 15, 3, GRIS_5);
		bt_preset[n].label=String(n+1);
		if(mode_affi == COUL) {c1 = bt_preset[n].couleur;}
		bt_preset[n].affiche(c2, 1);	
		x2 += 20;
	}
}


uint8_t PRESET_PAD::read_dx()
{
	return dx;
}


uint8_t PRESET_PAD::read_dy()
{
	return dy;
}



void PRESET_PAD::set_frequences_PRST() // lit les fréquences en SD et les attribue à chaque bouton PRESET
{
	Serial.println("void PRESET_PAD::set_frequences()");	

	uint8_t n=0;

	read_FRQ_File(SD, "/FRQ_SW_PRST.txt", "SW");
	read_FRQ_File(SD, "/FRQ_FM_PRST.txt", "FM");
	read_FRQ_File(SD, "/FRQ_AIR_PRST.txt", "AIR");
}


void PRESET_PAD::set_couleurs()
{
/* 
voir la page: 
https://rgbcolorpicker.com/565
qui permet de reconfigurer les couleurs RGB565 (5+6+5 = 16 bits) très simplement
*/

	bt_preset[0].couleur = 28672;	// rouge sombre 
	bt_preset[1].couleur = 43584;	// orange
	bt_preset[2].couleur = 832;		// vert
	bt_preset[3].couleur = 14407;	// violet
	bt_preset[4].couleur = 58157;	// rose
	bt_preset[5].couleur = 975;		// cyan
	bt_preset[6].couleur = 21819;	// bleu ciel
	bt_preset[7].couleur = 12;		// bleu marine
}



void PRESET_PAD::deselect_boutons()
{
	uint16_t c1 = GRIS_5;
	uint16_t c2 = JAUNE;

	for(int n =0; n<8; n++)
	{
		bt_preset[n].selected=false;
		bt_preset[n].cliked=false;
		bt_preset[n].affiche(c2, 1);
	}
}


uint8_t PRESET_PAD::test_clic()
{
	if ( (( x_touch > x0) && (x_touch < (x0+155))) && (( y_touch > y0) && (y_touch < (y0+15))))
	{
		uint8_t num_bouton =0;
		for(uint8_t n=0; n<8; n++)
		{
			test_clic_boutons(&bt_preset[n]); 
			if(bt_preset[n].cliked) 
			{
				deselect_boutons(); // les autres
				bt_preset[n].selected =true; // pour l'affichage de celui-ci
				bt_preset[n].affiche(JAUNE, 1); 
				num_bouton = n; 
				//Serial.print("BB num_bouton= ");  Serial.println(num_bouton);
			}
		}
		delay(300);
		return num_bouton;
	}
	return 253;
}


	
/** ***************************************************************************************
	CLASS SW_PAD
********************************************************************************************/
// Constructeur
SW_PAD::SW_PAD() 
{

}


void SW_PAD::init(uint16_t xi, uint16_t yi)
{
	x0 = xi;
	y0 = yi;

	uint16_t x2, y2;
	x2 = x0;	
	y2 = y0;

	set_frequences_SW();

	for(uint8_t n=0; n<14; n++)
	{
		bt_SW[n].init(x2, y2, 30, 12, 3, GRIS_5);
		String sA = String(bt_SW[n].freq_A);
		String sB = String(bt_SW[n].freq_B);
		bt_SW[n].affiche(JAUNE, 1);  
		TFT.setTextColor(CYAN, NOIR);
		TFT.setFreeFont(FF0);
		TFT.drawString(sA + "..", x2+35, y2);
		TFT.drawString(sB, x2+80, y2);	
		if (n==0) {TFT.drawString("kHz", x2+110, y0);}	
		y2 += 16;
	}
}


void SW_PAD::set_frequences_SW()
{
// bandes plus larges que les 'bandes officielles de radiodiffusion' afin de couvrir tt les frq sans trous
	bt_SW[0].freq_A=2300;	bt_SW[0].freq_B=3200; bt_SW[0].lambda=120;  
	bt_SW[1].freq_A=3200;	bt_SW[1].freq_B=3900; bt_SW[1].lambda=90; 
	bt_SW[2].freq_A=3900;	bt_SW[2].freq_B=4750; bt_SW[2].lambda=75; 
	bt_SW[3].freq_A=4750;	bt_SW[3].freq_B=5950; bt_SW[3].lambda=60; 
	bt_SW[4].freq_A=5950;	bt_SW[4].freq_B=7100; bt_SW[4].lambda=49; 
	bt_SW[5].freq_A=7100;	bt_SW[5].freq_B=9500; bt_SW[5].lambda=41; 
	bt_SW[6].freq_A=9500;	bt_SW[6].freq_B=11650; bt_SW[6].lambda=31; 
	bt_SW[7].freq_A=11650;	bt_SW[7].freq_B=13600; bt_SW[7].lambda=25; 
	bt_SW[8].freq_A=13600;	bt_SW[8].freq_B=15100; bt_SW[8].lambda=22; 
	bt_SW[9].freq_A=15100;	bt_SW[9].freq_B=17550; bt_SW[9].lambda=19; 
	bt_SW[10].freq_A=17550;	bt_SW[10].freq_B=18900; bt_SW[10].lambda=16; 
	bt_SW[11].freq_A=18900;	bt_SW[11].freq_B=21450; bt_SW[11].lambda=15; 
	bt_SW[12].freq_A=21450;	bt_SW[12].freq_B=25670; bt_SW[12].lambda=13; 
	bt_SW[13].freq_A=25670;	bt_SW[13].freq_B=27900; bt_SW[13].lambda=11; 

	for(int n =0; n<=13; n++)
	{
		bt_SW[n].label = String(bt_SW[n].lambda) + "m";
	}
}


uint8_t SW_PAD::test_clic()
{
	if ( (( x_touch > x0) && (x_touch < (x0+40))) && (( y_touch > y0) && (y_touch < 240)))
	{
		uint8_t num_bouton =0;
		for(uint8_t n=0; n<=13; n++)
		{
			test_clic_boutons(&bt_SW[n]); 
			if(bt_SW[n].cliked) 
			{
				deselect_boutons(); // les autres
				bt_SW[n].selected =true; // pour l'affichage de celui-ci
				bt_SW[n].affiche(JAUNE, 1); 
				num_bouton = n; 
			}
		}
		delay(300);
		return num_bouton;
	}
	return 253;
}


void SW_PAD::deselect_boutons()
{
	uint16_t c1 = GRIS_5;
	uint16_t c2 = JAUNE;

	for(int n =0; n<=13; n++)
	{
		bt_SW[n].selected=false;
		bt_SW[n].cliked=false;
		bt_SW[n].affiche(c2, 1);
	}
}

/** ***************************************************************************************
	CLASS GROUPE_FREQUENCES  // objet image d'un bloc mémoire en SD
	permet diverses manipulations en RAM (tri...) sans toucher à la SD (évite usure mémoire flash)					
********************************************************************************************/

// Constructeur
GROUPE_FREQUENCES::GROUPE_FREQUENCES()
{

}


void GROUPE_FREQUENCES::RAZ() // en RAM uniquement
{
	for(int n=0; n<100; n++)
	{
		G_freq[n]=0;
	}
	nb_freq = 0;
}


void GROUPE_FREQUENCES::load_bloc() // depuis la SDcard --> vers groupe en RAM
{
	Serial.println("--------------------------");
	Serial.println("GROUPE_FREQUENCES::load_bloc()");
	Serial.print("Reading file: "); Serial.println(filename);
	File file = SD.open(filename);
	if (!file ) { Serial.println("failed to open file for reading"); return; }
	String s; 
	uint8_t n =0;

	while (file.available()) 
	{
		char c;
		c = char(file.read());
		if ((c !='<') && (c !='>')) {s += c;}
		if(c=='>')
		{
			uint32_t frq;
			frq = s.toInt();
			Serial.println(frq);
			s="";
			G_freq[n] = frq;
			n++;
		}
	}
	file.close();
	Serial.print(n); Serial.println(" frequences");
}


boolean GROUPE_FREQUENCES::test_Frq_presente(uint32_t F_i) // travaille en RAM
{
	Serial.println("--------------------------");
	Serial.println("test_Frq_presente()");
	uint16_t n =0;
	uint16_t adresse_lue;
	uint32_t valeur_lue;
	boolean ok = false;
	for (n=0; n<100; n++)
	{
		valeur_lue = G_freq[n];
		if (valeur_lue == F_i) {return true;}  // si F est présente
	}
	return false; // si F non présente
}


void GROUPE_FREQUENCES::tri_bloc()
{
	Serial.println("--------------------------");
	Serial.println("tri_block()"); 
	// tri par bulles

    uint32_t F1=0, F2=0;
    uint32_t Fi;
    uint16_t i_max = 100; 
	uint16_t p_max = 100;


    for(uint16_t p=0; p<p_max; p++)
    {
        //for(int n=0; n<i_max-1; n++)
		uint16_t n =0;
		while(n<i_max-1)
        {
            F1=G_freq[n];
            F2=G_freq[n+1];

            if(F1 > F2)
            {
                Fi = G_freq[n];
				G_freq[n] = G_freq[n+1];
                G_freq[n+1] = Fi;
            }
			n++;
        }
    }

// ici les freq sont dédoublées !!!!!!!!!!!!

// compte le nombre de fréquences != 0	
	uint16_t nombre =0;
	for(uint16_t n = 0; n<100; n++ ) 
	{
		F1 = G_freq[n];
		if(F1 != 0) { nombre++;}	 
	}
	nb_freq = nombre;

// recherche première fréquence non nulle	
	uint16_t n2=0;
	F1 =0;
	while ((F1==0) && (n2<100))
	{
		F1=G_freq[n2];
		n2++;
	}
	adr_1ere_frq = n2-1; // 1ere F non nulle

}


void GROUPE_FREQUENCES::bloc_to_serial() 
{
// pour tests, avec CuteCom sous Linux
// travaille en RAM uniquement, sans toucher aux fichiers SD
	Serial.println("--------------------------");
	Serial.println("bloc_to_serial()");
	uint32_t valeur_lue;
	uint16_t nombre =0;
	for(uint16_t n = 0; n<100; n++ ) 
	{
		valeur_lue = G_freq[n];
		if(valeur_lue != 0) // n'affiche pas les emplacements vides
		{	
			nombre++;
			Serial.println(valeur_lue);
		}
	}
	Serial.print(nombre); Serial.println(" frequences");
	//Serial.print("1ere Frq=  ");	Serial.println(G_freq[adr_1ere_frq]);
}


// voir la fonction 'void affi_liste()// des aérodromes' du ND.ino
void GROUPE_FREQUENCES::affiche_bloc(String nom, uint16_t x) 
{
// travaille en RAM uniquement, sans toucher aux fichiers SD
	uint32_t valeur_lue;
	uint16_t nombre =0;
	uint16_t y;
	String s1;

	TFT.setTextColor(BLANC, NOIR);
	TFT.setFreeFont(FF0);
	TFT.drawString(nom, x,  0);
	y=20;
	for(uint16_t n = 0; n<100; n++ ) 
	{
		valeur_lue = G_freq[n];
		if(valeur_lue != 0) // n'affiche pas les emplacements vides
		{	
			nombre++;

			s1 = String(nombre);
			TFT.setTextColor(BLEU, NOIR);
			TFT.drawString(s1, x, y);

			s1 = valeur_lue;
			TFT.setTextColor(BLANC, NOIR);
			TFT.drawString(s1, x+20, y);	
			
			y+=10;
		}
	}
	Serial.print(nombre); Serial.println(" frequences");

}



void GROUPE_FREQUENCES::add_frq(uint32_t Fi) // en RAM uniquement
{
	Serial.println("--------------------------");
	if (nb_freq >= 100) {return;}
	Serial.println("GROUPE_FREQUENCES::add_frq()");

	if (test_Frq_presente(Fi) == true) 
	{
		Serial.println("Frq presente, pas d'ajout");
		return;
	}
	tri_bloc(); // les F nulles se retrouvent en haut
	Serial.print("Ajout frequence: "); Serial.println(Fi);
	G_freq[0] = Fi; // la nouvelle fréquence est placée en haut
	nb_freq++;
	tri_bloc(); // la nouvelle fréquence se retrouve rangée dans l'ordre de F croissantes
}



void GROUPE_FREQUENCES::erase_1_freq(uint32_t Fi) // en RAM
{
	Serial.println("--------------------------");
	Serial.println("GROUPE_FREQUENCES::erase_1_freq()");
	uint32_t valeur_lue;
	for(int n=0; n<100; n++)
	{
		valeur_lue = G_freq[n];
		if(valeur_lue == Fi)
		{
			G_freq[n]=0;
		}
	}
	tri_bloc();
}

