/* -------------------------------------------------------------------------
	RADIO FM - pour ESP32
 
	par Silicium628
 
--------------------------------------------------------
CONCERNANT L'AFFICHAGE TFT :
 
Pensez à configurer le fichier User_Setup.h de la bibliothèque ~/Arduino/libraries/TFT_eSPI/  au préalable
voici les paramètres OK pour cette réalisation :
 
#define ST7789_DRIVER     // Full configuration option, define additional parameters below for this display
#define TFT_RGB_ORDER TFT_BGR  // Colour order Blue-Green-Red
#define TFT_WIDTH  240 // ST7789 240 x 240 and 240 x 320
#define TFT_HEIGHT 240 // ST7789 240 x 240
 
#define TFT_MISO 19
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS   15  // Chip select control pin
#define TFT_DC    2  // Data Command control pin
#define TFT_RST   4  // Reset pin (could connect to RST pin)
 
explications ici : https://www.youtube.com/watch?v=HoZhgNcJjNA
 
 Remerciements à :
 - big12boy 2017 - Licence: GNU GPL
 
--------------------------------------------------------
 CONCERNANT LA TRANSFORMEE DE FOURIER :
 
 	FFT par Silicium628
 
	logiciel libre, OPEN SOURCE
 
 pour l'explication des calculs voir mes pages :
 
http://www.silicium628.fr/article_i.php?id=123 -> La transformée de Fourier Etude analytique  
http://www.silicium628.fr/article_i.php?id=126 -> Transformée de Fourier rapide 
http://www.silicium628.fr/article_i.php?id=31  -> Transformée de Fourier sur ATmega32 et Arduino Mega2560
 
------------------------------------------------------------------------- */
 
	String version="2.0";
 
 
#include <SPI.h>
#include <TEA5767.h>
#include <TFT_eSPI.h> // Hardware-specific library
#include "complexe628.h"
#include "Free_Fonts.h"
 
TEA5767 radio = TEA5767();
TFT_eSPI tft = TFT_eSPI(); // Configurer le fichier User_Setup.h de la bibliothèque TFT_eSPI au préalable
 
typedef int16_t complexe_f[2];  // entier signé pour calculs (rapides) en virgule fixe
 
Complexe ech[480]; // (480 = nb_ech echantillons)
Complexe tab_X[480];   // nb_ech valeurs traitées
Complexe tab_W[240];    // nb_ech/2
 
uint nb_etapes=8;
uint nb_ech = pow (2,nb_etapes); // nombre d'échantillons = 2 puissance(nb_etapes)
 
struct boite
{
	uint16_t x;
	uint16_t y;
	uint16_t w;
	uint16_t h;
};
 
boite boite_FFT;
 
 
#define TFT_GREY 0x5AEB
 
float ltx = 0;    // Saved x coord of bottom of aiguille
uint16_t osx = 120, osy = 120; // Saved x & y coords
 
 
const int bouton1 = 12;  // GPIO12
const int bouton2 = 14;  // GPIO14
const int bouton3 = 27;  // GPIO27
 
//const int led1 = 13; // GPIO13
 
bool bouton1_etat;
bool bouton2_etat;
bool bouton3_etat;
 
bool memo_bouton1_etat;
bool memo_bouton2_etat;
bool memo_bouton3_etat;
 
uint8_t mode_boutons;
uint8_t mode_affi;
uint8_t memo_mode_affi;
 
uint8_t num_station;
uint8_t nb_stations;
 
uint16_t frq_Int;
float frq_out; // ex: 89.4 (MHz)
 
 
uint16_t compteur1=0;
 
 
struct station
{
  uint16_t frq;
  char nom[20+1]; 
};
 
 
struct station liste_stations[20];  // 0..19
 
 
void init_stations()
{
  nb_stations = 12; 
 
  strcpy(liste_stations[0].nom , "France Inter");
  liste_stations[0].frq = 894;
 
  strcpy(liste_stations[1].nom , "France Musique");
  liste_stations[1].frq = 929;
 
  strcpy(liste_stations[2].nom , "RFM");
  liste_stations[2].frq = 956;
 
  strcpy(liste_stations[3].nom , "France Culture");
  liste_stations[3].frq = 978;
 
  strcpy(liste_stations[4].nom , "Radio France Herault");
  liste_stations[4].frq = 1011;
 
  strcpy(liste_stations[5].nom , "Fun Radio");
  liste_stations[5].frq = 918;
 
   strcpy(liste_stations[6].nom , "Radio Clapas");
  liste_stations[6].frq = 935;
 
  strcpy(liste_stations[7].nom , "Cherie FM");
  liste_stations[7].frq = 969;
 
  strcpy(liste_stations[8].nom , "FIP");
  liste_stations[8].frq = 997;
 
  strcpy(liste_stations[9].nom , "Nostalgie");
  liste_stations[9].frq = 1039;
 
  strcpy(liste_stations[10].nom , "France Info");
  liste_stations[10].frq = 1051;
 
  strcpy(liste_stations[11].nom , "Radio Classique");
  liste_stations[11].frq = 1073;
} 
 
 
 
void affiche_Signal_Level()
{
	short level = radio.getSignalLevel(); //Get Signal Level
 
	plotAiguille(100*level/15);
}
 
// ================== FONCTIONS FFT ==============================
 
 
void effacer_trace()
{
	tft.fillRect(boite_FFT.x+1, boite_FFT.y+1, boite_FFT.w-2, boite_FFT.h-2, TFT_BLACK);
}
 
 
 
void RAZ_tableau_echantillons()
{
   uint n;
   for(n=0; n < nb_ech; n++)
   {
       ech[n].a = 0; // partie reelle
       ech[n].b = 0; // partie imaginaire
   }
}
 
 
uint16_t bit_reversal(uint16_t num, uint8_t nb_bits)
{
    uint r = 0, i, s;
    if ( num > (1<< nb_bits)) { return 0; }
 
    for (i=0; i<nb_bits; i++)
    {
        s = (num & (1 << i));
        if(s) { r |= (1 << ((nb_bits - 1) - i)); }
    }
    return r;
}
 
 
void bit_reverse_tableau_X()
{
 
// recopie les échantillons en les classant dans l'ordre 'bit reverse'
    uint n,r;
 
    for(n=0; n < nb_ech; n++) // nb d'échantillons
    {
        r=bit_reversal(n,nb_etapes);
        tab_X[n] = ech[r];
    }
}
 
 
void acquisition()
{
// source = signal analogique en entrée du CAN.
// destination -> tableau des échantillons complexes	
    float x;
 
    for(int n=0; n<nb_ech; n++)
    {
        x=  analogRead(25)/30.0; // GPIO 25;  le /30.0 détermine la sensibilité (atténuateur sur l'amplitude)
        ech[n].a = x; // partie reelle
        ech[n].b = 0; // partie imaginaire
    }
}
 
 
 
/**
 pour l'explication des calculs suivants voir mes pages :
 
http://www.silicium628.fr/article_i.php?id=123 -> La transformée de Fourier Etude analytique  
http://www.silicium628.fr/article_i.php?id=126 -> Transformée de Fourier rapide 
http://www.silicium628.fr/article_i.php?id=31  -> Transformée de Fourier sur ATmega32 et Arduino Mega2560
 
**/
 
 
void calcul_tableau_W()
{
// calcul et memorisation dans un tableau des twiddle factors
 
    uint n;
    float x;
 
    for(n=0; n<(nb_ech/2-1); n++)
    {
        x=2.0*M_PI * n / nb_ech;
 
        tab_W[n].a = cos(x);  // partie reelle
        tab_W[n].b = -sin(x); // partie imaginaire
    }
}
 
 
 
void calcul_fourier()
{
    Complexe produit; // voir la classe "Complexe" : complexe.h et complexe.ccp
 
    uint etape, e1, e2, li, w, ci;
    uint li_max=nb_ech;
 
    e2=1;
 
    for (etape=1; etape<=nb_etapes; etape++)
    {
        e1=e2; //(e1 evite d'effectuer des divisions e2/2 plus bas)
        e2=e2+e2;
 
        for (li=0; li<li_max; li+=1)
        {
            ci=li & (e2-1);  // ET bit à bit
            if (ci>(e1-1))
            {
                w=li_max/e2*(li & (e1 -1));  // ET bit à bit  calcul du numéro du facteur de tripatouillages W
 
                produit = tab_W[w] * tab_X[li]; // le twiddle factor est lu en memoire; le produit est une mutiplication de nb complexes. Voir "complexe.cpp"
 
                tab_X[li]=tab_X[li-e1]-produit;         // concerne la ligne basse du croisillon; soustraction complexe; Voir "complexe.cpp"
                tab_X[li-e1]=tab_X[li-e1] + produit;    // concerne la ligne haute du croisillon; addition complexe; Voir "complexe.cpp"
            }
        }
    }
}
 
 
// couleurs RGB24 :
 
//couleur1= 255; // bleu
//couleur1= 255<<1; // bleu-clair
//couleur1= 255<<2; // bleu très clair
//couleur1= 255<<3; // blanc bleuté
//couleur1= 255<<4; // cyan
//couleur1= 255<<5; // vert-vert-jaune
//couleur1= 255<<6; // vert-jaune
//couleur1= 255<<7; // vert-jaune
//couleur1= 255<<8; // jaune
//couleur1= 255<<9; // jaune orangé
//couleur1= 255<<10; // orange
//couleur1= 255<<11; // rouge
 
 
void affi_FFT()
{
	float TF;
	uint8_t offset_y =   boite_FFT.y + boite_FFT.h -1; 
	float echelle_x = 8.0;
	int n;
	float x, y, memo_x, memo_y;
	float z= 0.5;
 
	uint32_t couleur1;
 
	x=0;
	y=offset_y;
	uint16_t n_max = nb_ech/2;
	for(n=1; n< n_max; n++)
	{
		memo_x = x;
		memo_y = y;
		x = boite_FFT.x  + echelle_x * n;
 
		if (x<(boite_FFT.w-10))
		{
			TF = z * sqrt( tab_X[n].a * tab_X[n].a + tab_X[n].b * tab_X[n].b ); // racine de A²+B²
 
			if(TF>100) {TF=100;}
 
			y =  offset_y - TF;
 
			//tft.drawLine(memo_x ,memo_y, x, y, TFT_YELLOW); // courbe continue
			//tft.drawLine(x+1 ,offset_y, x+1, y, TFT_YELLOW);// barres verticales
			//tft.drawFastVLine(x, y, TF, TFT_YELLOW); // barres verticales, tarcé rapide
 
			int decalage = 12 - (int16_t)x/21.0;
			if (decalage < 0) {decalage = 0;}
 
			couleur1= (255 << decalage);
 
			tft.fillRect(x+1, y, 6, TF, couleur1); // barres verticales larges
 
		}
	}
}
 
// ===============================================================
 
 
void setup(void)
{
	pinMode(bouton1, INPUT_PULLUP);
	pinMode(bouton2, INPUT_PULLUP);
	pinMode(bouton3, INPUT_PULLUP);
 
	//pinMode(led1, OUTPUT);
 
	tft.init();
	tft.setRotation(3); // 0..3 à vous de voir, suivant disposition de l'afficheur
//	Serial.begin(57600); 
	tft.fillScreen(TFT_BLACK);
 
	//Setup I2C
	Wire.begin();
 
	dessine_VuMetre();
	//dessine_gamme_FM();
 
	mode_affi=0;
	mode_boutons=0;
 
	init_stations();
	num_station = 0;
 
	if (mode_boutons==0) {frq_Int = liste_stations[num_station].frq;}
	if (mode_boutons==1) {frq_Int=880;}
 
	frq_out = (float)frq_Int / 10;
 
	MAJ_frq();
	affiche_numero_station(num_station);
 
	delay(20);
	affiche_Signal_Level();
 
	bouton1_etat = digitalRead(bouton1);
	memo_bouton1_etat = bouton1_etat;
 
	bouton2_etat = digitalRead(bouton2);
	memo_bouton2_etat = bouton2_etat;
 
	bouton3_etat = digitalRead(bouton3);
	memo_bouton3_etat = bouton3_etat;
 
 
 
	calcul_tableau_W(); // (twiddle factors)
 
    boite_FFT.x = 0;
    boite_FFT.y = 0;
    boite_FFT.w = 239;
    boite_FFT.h = 120;
 
    tft.drawRect(boite_FFT.x, boite_FFT.y, boite_FFT.w, boite_FFT.h, TFT_CYAN);
 
 
	//updateTime = millis(); // Next update time
}
 
 
void affiche_numero_station(uint8_t n_i)
{
	if (mode_boutons==0)
	{
		tft.fillRect(0, 138, 25, 25, TFT_BLUE);
		String s1= (String) (n_i + 1);
		tft.setTextColor(TFT_WHITE);
		tft.drawString(s1, 5, 145, 2);
	}
	if (mode_boutons==1)
	{
		tft.fillRect(0, 138, 25, 25, TFT_GREY);
	}	
}
 
 
 
void efface_nom_station()
{
	tft.fillRect(0, 180, 239, 30, TFT_BLACK);
}
 
 
void affiche_nom_station(uint8_t n)
{
	tft.setTextColor(TFT_YELLOW);
	String s3= liste_stations[n].nom;
	tft.drawString(s3, 0, 180, 4);
}
 
 
 
void affiche_frq_out()
{
 
	tft.fillRect(25, 135, 239, 30, TFT_BLACK); //TFT_BLACK
 
	String s2= (String) frq_out;
	s2+=" MHz";
 
	if (mode_boutons==0) {tft.setTextColor(TFT_CYAN);}
	if (mode_boutons==1) {tft.setTextColor(TFT_GREEN);}
 
 
	tft.setFreeFont(FF19);
	tft.drawString(s2, 35, 135, GFXFF);
 
	tft.fillRect(0, 180, 239, 30, TFT_BLACK);
	tft.setTextColor(TFT_YELLOW);
 
}
 
 
void MAJ_frq()
{
	radio.setFrequency(frq_out);
	affiche_frq_out();
	affiche_nom_station(num_station);
	dessine_gamme_FM();
	delay(20);
	affiche_Signal_Level();
}
 
 
void inc_frq_int()
{
	if ( frq_Int  < 1080) {frq_Int += 1;}
	if ( frq_Int  > 1080) {frq_Int = 1080; }
}
 
 
 
void dec_frq_int()
{
	if ( frq_Int  > 880) {frq_Int -= 1;}
}
 
 
 
uint8_t detect_num_station(uint16_t frq_Int_i)
{
	for (int i=0; i<nb_stations; i++)
	{
		if (frq_Int_i == liste_stations[i].frq) return i;
	}
	return 255;
}
 
 
void loop()
{
	memo_mode_affi = mode_affi;
	uint16_t compte=0;
	bouton1_etat = digitalRead(bouton1);
	bouton2_etat = digitalRead(bouton2);
 
	if (mode_boutons == 0)
	{
 
		if (bouton1_etat != memo_bouton1_etat)
		{
			memo_bouton1_etat = bouton1_etat;
 
			if (bouton1_etat == 0)
			{
				mode_affi=0;
				if ( num_station  < nb_stations) {num_station += 1;}
				if ( num_station  > (nb_stations-1)) {num_station = nb_stations-1; }
				frq_Int = liste_stations[num_station].frq;
				frq_out = (float)frq_Int / 10;
			}
			affiche_numero_station(num_station);
			MAJ_frq();
		}
 
		if (bouton2_etat != memo_bouton2_etat)
		{
			memo_bouton2_etat = bouton2_etat;
 
			if (bouton2_etat == 0)
			{
				mode_affi=0;
				if ( num_station  > 0) {num_station -= 1;}
				frq_Int = liste_stations[num_station].frq;
				frq_out = (float)frq_Int / 10;
			}
			affiche_numero_station(num_station);
			MAJ_frq();
		}
 
	}
 
 
	if (mode_boutons == 1)
	{
		bouton1_etat = digitalRead(bouton1);
		bouton2_etat = digitalRead(bouton2);
		if (bouton1_etat != memo_bouton1_etat)
		{
			memo_bouton1_etat = bouton1_etat;
 
			if (bouton1_etat == 0) // suite à un appui bref, on avance de 1 pas
			{
				mode_affi=0;
				inc_frq_int();
				frq_out = (float)frq_Int / 10;
				radio.setFrequency(frq_out);
				affiche_frq_out();
				dessine_gamme_FM();
				affiche_Signal_Level();				
			}
			//MAJ_frq();
 
			bouton1_etat = digitalRead(bouton1);
			while(bouton1_etat == 0) // pui on détecte un éventuel appui long (500ms)
			{
				bouton1_etat = digitalRead(bouton1);
				compte++;
				if (compte>=50) // l'appui long est advenu
				{
					compte=0;
					bouton1_etat = digitalRead(bouton1);
					while(bouton1_etat == 0) // tant que le bouton reste appuyé, on incrémente rapidement les pas
					{
						bouton1_etat = digitalRead(bouton1);
						inc_frq_int();
						frq_out = (float)frq_Int / 10;
						radio.setFrequency(frq_out);
						affiche_frq_out();
						dessine_gamme_FM();
						affiche_Signal_Level();
						delay(50);
					}
				}
				delay(10);
			}
			efface_nom_station();
			uint8_t num1= detect_num_station(frq_Int);
			if (num1 !=255)	{affiche_nom_station(num1);}
		}
 
 
 
		if (bouton2_etat != memo_bouton2_etat)
		{
			memo_bouton2_etat = bouton2_etat;
 
			if (bouton2_etat == 0) // suite à un appui bref, on avance de 1 pas
			{
				mode_affi=0;
				dec_frq_int();
				frq_out = (float)frq_Int / 10;
				radio.setFrequency(frq_out);
				affiche_frq_out();
				dessine_gamme_FM();
				affiche_Signal_Level();
			}
			//MAJ_frq();
 
			bouton2_etat = digitalRead(bouton2);
			while(bouton2_etat == 0) // puis on détecte un éventuel appui long (500ms)
			{
				bouton2_etat = digitalRead(bouton2);
				compte++;
				if (compte>=50) // l'appui long est advenu
				{
					compte=0;
					bouton2_etat = digitalRead(bouton2);
					while(bouton2_etat == 0) // tant que le bouton reste appuyé, on incrémente rapidement les pas
					{
						bouton2_etat = digitalRead(bouton2);
						dec_frq_int();
						frq_out = (float)frq_Int / 10;
						radio.setFrequency(frq_out);
						affiche_frq_out();
						dessine_gamme_FM();
						affiche_Signal_Level();
						delay(50);
					}
 
				}
				delay(10);
			}
			efface_nom_station();
			uint8_t num1= detect_num_station(frq_Int);
			if (num1 !=255)	{affiche_nom_station(num1);}	
		}
 
 
	}
 
 
	bouton3_etat = digitalRead(bouton3);
	if (bouton3_etat != memo_bouton3_etat)
	{
		memo_bouton3_etat = bouton3_etat;
		if (bouton3_etat == 0)
		{
			mode_boutons=1-mode_boutons;
			affiche_frq_out();
			delay(20);
		}
		if(mode_boutons==0){tft.fillRect(0, 138, 25, 25, TFT_BLUE);}
		if(mode_boutons==1){tft.fillRect(0, 138, 25, 25, TFT_GREY);}
	}	
 
 
 
	if(memo_mode_affi != mode_affi)
	{
		if(mode_affi==0)
		{
			tft.drawRect(boite_FFT.x, boite_FFT.y, boite_FFT.w, boite_FFT.h, TFT_CYAN);
			dessine_VuMetre();
		}
 
 
	} 
 
	if (mode_affi==0) {compteur1++;}
	if (compteur1>=100)
	{
		compteur1=0;
		mode_affi=1;
		tft.fillRect(0, 0, 239, 126, TFT_GREY);
	}
 
 
	if(mode_affi==1)
	{
		// ----------- FFT ---------------
		effacer_trace();
		RAZ_tableau_echantillons();
 
		acquisition();
 
		//tracer_signal();
		bit_reverse_tableau_X();
		calcul_fourier();
 
		affi_FFT();
	}	
 
	delay(20);
 
}
 
 
void dessine_gamme_FM()
{
	// cadre rectangulaire
	tft.fillRect(0, 210, 239, 28, 0x040004);
	tft.drawRect(0, 212, 239, 28, TFT_WHITE);
 
	tft.drawLine(20, 224, 215, 224, TFT_WHITE); // TFT_DARKGREEN
 
	uint16_t x=20;
	for(int n=0; n<20; n++)
	{
 
		tft.drawLine(3+x, 215, 3+x, 230, TFT_WHITE); // TFT_DARKGREEN
		x+=10;
 
	}
 
	tft.setTextColor(TFT_YELLOW); //TFT_GREEN
 
	tft.setFreeFont(FSS9);
	tft.drawString("88", 4, 218, GFXFF);
 
	tft.setFreeFont(FSSB9);
	tft.drawString("108", 208, 218, GFXFF);
 
	uint16_t frq1 = 15 + (( frq_out) - 88) * 10;
 
	tft.fillCircle(frq1, 224, 5, TFT_GREEN);
 
}
 
 
 
// -------------------------------------------------------------------------
void dessine_VuMetre()
{
  // cadre rectangulaire
  tft.fillRect(0, 0, 239, 126, TFT_GREY);
  tft.fillRect(5, 3, 230, 119, TFT_WHITE);
 
  tft.setTextColor(TFT_BLACK);
 
  // graduation chaque 5 deg entre -50 et +50 deg
  for (int i = -50; i < 51; i += 5)
  {
    int tl = 15; // tiret plus long
 
    // Coordonnées du tiret à dessiner
    float sx = cos((i - 90) * 0.0174532925);
    float sy = sin((i - 90) * 0.0174532925);
    uint16_t x0 = sx * (100 + tl) + 120;
    uint16_t y0 = sy * (100 + tl) + 140;
    uint16_t x1 = sx * 100 + 120;
    uint16_t y1 = sy * 100 + 140;
 
    // Coordonnées of next tick for zone fill
    float sx2 = cos((i + 5 - 90) * 0.0174532925);
    float sy2 = sin((i + 5 - 90) * 0.0174532925);
    int x2 = sx2 * (100 + tl) + 120;
    int y2 = sy2 * (100 + tl) + 140;
    int x3 = sx2 * 100 + 120;
    int y3 = sy2 * 100 + 140;
 
    // zone verte
    if (i >= 0 && i < 25)
    {
      tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN);
      tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN);
    }
 
    // zone orange
    if (i >= 25 && i < 50)
    {
      tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_ORANGE);
      tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_ORANGE);
    }
 
    if (i % 25 != 0) tl = 8; // Short scale tick length
 
    // Recalcule coords in case tick lenght changed
    x0 = sx * (100 + tl) + 120;
    y0 = sy * (100 + tl) + 140;
    x1 = sx * 100 + 120;
    y1 = sy * 100 + 140;
 
    // Draw tick
    tft.drawLine(x0, y0, x1, y1, TFT_BLACK);
 
    // Check if labels should be drawn, with position tweaks
    if (i % 25 == 0)
    {
      // Calculate label positions
      x0 = sx * (100 + tl + 10) + 120;
      y0 = sy * (100 + tl + 10) + 140;
      switch (i / 25)
      {
        case -2: tft.drawCentreString("0", x0, y0 - 12, 2); break;
        case -1: tft.drawCentreString("25", x0, y0 - 9, 2); break;
        case 0: tft.drawCentreString("50", x0, y0 - 6, 2); break;
        case 1: tft.drawCentreString("75", x0, y0 - 9, 2); break;
        case 2: tft.drawCentreString("100", x0, y0 - 12, 2); break;
      }
    }
 
    // draw the arc of the scale
    sx = cos((i + 5 - 90) * 0.0174532925);
    sy = sin((i + 5 - 90) * 0.0174532925);
    x0 = sx * 100 + 120;
    y0 = sy * 100 + 140;
    // Draw scale arc, don't draw the last part
    if (i < 50) {tft.drawLine(x0, y0, x1, y1, TFT_BLACK);}
  }
 
  tft.drawRect(5, 3, 230, 119, TFT_BLACK); // Draw bezel line
 
}
 
 
void plotAiguille(int value)
{
	tft.setTextColor(TFT_BLACK, TFT_WHITE);
	char buf[8]; dtostrf(value, 4, 0, buf);
 
	if (value < -10) value = -10; // Limit value to emulate aiguille end stops
	if (value > 110) value = 110;
 
	float sdeg = map(value, -10, 110, -150, -30); // Map value to angle
	// Calcul tip of aiguille coords
	float sx = cos(sdeg * 0.0174532925);
	float sy = sin(sdeg * 0.0174532925);
 
	// Calcul x delta of aiguille start (does not start at pivot point)
	float tx = tan((sdeg + 90) * 0.0174532925);
 
	// Erase old aiguille image
	tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, TFT_WHITE);
	tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, TFT_WHITE);
	tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, TFT_WHITE);
 
	// Re-plot texte sous l'aiguille
	tft.fillRect(100, 70, 40, 25, TFT_WHITE);
	tft.setTextColor(TFT_BLACK);
	tft.drawCentreString((String)value, 120, 70, 4); 
 
	// Store new aiguille end coords for next erase
	ltx = tx;
	osx = sx * 98 + 120;
	osy = sy * 98 + 140;
 
	// Draw the aiguille in the new postion, magenta makes aiguille a bit bolder
	// draws 3 lines to thicken aiguille
	tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, TFT_RED);
	tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, TFT_MAGENTA);
	tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, TFT_RED);
}