T-Watch. Débuter avec la librairie ESP32 TFT_eSPI. Afficher texte, formes, détection tactile • Domotique et objets connectés à faire soi-même

La librairie LilyGoWatch intègre une version pré-configurée de la librairie TFT_eSPI de Badmer qui permet d’afficher du texte, des formes géométriques de base (rectangle, cercle, ellipse, triangle, ligne, point…), des images et plein d’autre choses.

Dans cet article nous allons uniquement utiliser l’écran TFT couleur tactile présent dans la T-Watch Touch et la T-Watch 2020. Cependant, vous ne devriez rencontrer aucune difficulté à l’utiliser sur les modèles équipés d’un écran eInk (T-Block et T-Bot).

Si vous débutez le développement d’application pour les boitiers et montres ESP32 TTGO T-Watch du fabricant LilyGo, vous pouvez commencer par lire cet article d’introduction.

Comment utiliser la librairie TFT_eSPI dans un projet Arduino T-Watch ?

La librairie LilyGoWatch intègre une version pré-configurée (code source) de la librairie TFT_eSPI de Bodmer. Celle-ci est est chargée au démarrage avec les paramètres qui correspondent à l’écran embarqué par le T-Watch utilisée.

La libraire TFT_eSPI prend en charge les écrans eInk et TFT des différents modèles de T-Watch.

Palette de couleur

On dispose d’une palette de couleur pré-définie accessible à l’aide des constantes suivantes

▉ TFT_BLACK
▉ TFT_NAVY (#000080)
▉ TFT_DARKGREEN (#008000)
▉ TFT_DARKCYAN (#008080)
▉ TFT_MAROON (#80000)
▉ TFT_PURPLE #800080)
▉ TFT_OLIVE (#808000)
▉ TFT_LIGHTGREY (#D3D3D3)
▉ TFT_DARKGREY (#808080)
▉ TFT_BLUE (#0000FF)
▉ TFT_GREEN (#00FF00)
▉ TFT_CYAN (#00FFFF)
▉ TFT_RED (#FF0000)
▉ TFT_MAGENTA (#FF00FF)
▉ TFT_YELLOW (#FFFF00) ▢ TFT_WHITE (#FFFFFF)

▉ TFT_ORANGE (#FFB400)

▉ TFT_GREENYELLOW (#B4FF00)
▉ TFT_PINK (#FFC0CB)
▉ TFT_BROWN (#964B00)
▉ TFT_GOLD (#FFD700)
▉ TFT_SILVER (#C0C0C0)
▉ TFT_SKYBLUE (#87CEEB)
▉ TFT_VIOLET (#B42EE2)

Fonctions de la librairie TFT_eSPI (API)

Voici une liste des fonctions les plus courantes proposées par la librairie TFT_eSPI. En l’absence de documentation officielle, les fonctions ont été extraites directement du code source de la librairie LilyGoWatch sur GitHub.

Liste des variables utilisées

  • m entier
  • color au format hexadécimal
  • x0, y0, x1, y1 coordonnées (en pixel)
  • r, rx, ty rayon (en pixel)
  • w (width) largeur en pixel
  • h (height) hauteur en pixel

Ecran et curseur

  • width() largeur de l’écran en pixels
  • height() hauteur de l’écran en pixels
  • setRotation(uint8_t m) tourne le contenu de l’écran. L’orientation est un nombre compris entre 0 et 3 et 4 à 7 pour un bitmap
    • getRotation() renvoie l’orientation de l’écran
  • invertDisplay(bool i) inverser les couleurs d’affichage i = 1 inverser, i = 0 normal
  • fillScreen(uint32_t color) met en couleur le fond de l’écran. En fait la fonction dessine un rectangle plein prenant toute la surface de l’écran
  • setCursor(int16_t x, int16_t y) positionne le curseur.
    • getCursorX renvoie la coordonnée X du curseur
    • getCursorY renvoie la coordonnée Y du curseur
  • setPivot(int16_t x, int16_t y) point pivot de l’écran
    • getPivotX() coordonnée X du point pivot
    • getPivotY() coordonnée X du point pivot
  • readPixel(int32_t x, int32_t y) Lit la couleur d’un pixel au format 565

Primitives géométriques

  • drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) dessine le contour d’un cercle
  • fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) dessine un cercle plein
  • drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) dessine le contour d’une ellipse
  • fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) dessine une ellipse pleine
  • drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) dessine le contour d’un rectangle
  • fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) dessine un rectangle plein
  • drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color) contour d’un rectangle au bord arrondi. Tous les angles ont le même rayon
  • fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color) idem mais plein
  • drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) contour d’un triangle. Il faut passer les coordonnées des trois sommets
  • fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) idem mais avec un triangle plein.
  • drawPixel(int32_t x, int32_t y, uint32_t color) dessine un pixel de colour
  • drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) dessine une ligne entre deux points arbitraires
  • drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) ligne verticale de longueur
  • drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) ligne horizontale de longueur l

Images

  • drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) affiche une image stockée dans un tableau
  • drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_tw, int16_th, uint16_tcolor) dessine une image monochrome stockée dans un tableau. On peut par exemple convertir une image JPG ou PNG à l’aide de GIMP au format XBM et récupérer le tableau de point.

  • setBitmapColor(uint16_t c, uint16_t b) attribue la couleur de fond d’une image bitmap

Polices

  • fontsLoaded() renvoie une valeur codée de 16 bits indiquant les polices chargées
  • fontHeight(int16_t font) hauteur de la police
  • setFreeFont(const GFXfont *f) Définit la police GFX à utiliser

Texte, caractère, chaîne

  • setTextSize(uint8_t s) attribue la taille du texte. Elle doit être comprise entre 1 et 7 (max.)
  • setTextColor(uint16_t c) attribue la couleur de texte. Couleur en hexadécimal
  • setTextColor(uint16_t c, uint16_t b) idem mais définit également la couleur du fond. A privilégier pour du texte qui change souvent (horloge, coordonnées, position…), cela évite de redessiner la totalité de l’écran à chaque actualisation
  • setTextWrap(bool wrapX, bool wrapY) ne fonctionne pas…
  • textWidth(const String &string, uint8_t font) renvoie la largeur en pixels d’une chaîne dans une police donnée
  • drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) affiche (dessine) un caractère à la position indiquée.

Affichage de chaine justifiée (Datum)

Positions disponibles

  • TL_DATUM 0 // En haut à gauche (par défaut)
  • TC_DATUM 1 // Centre supérieur
  • TR_DATUM 2 // En haut à droite
  • ML_DATUM 3 // Milieu gauche
  • CL_DATUM 3 // Centre gauche, comme ci-dessus
  • MC_DATUM 4 // Centre central
  • CC_DATUM 4 // Centre au centre, comme ci-dessus
  • MR_DATUM 5 // Milieu droit
  • CR_DATUM 5 // Centre droit, comme ci-dessus
  • BL_DATUM 6 // En bas à gauche
  • BC_DATUM 7 // En bas au centre
  • BR_DATUM 8 // En bas à droite
  • L_BASELINE 9 // Ligne de base du caractère gauche (ligne sur laquelle le caractère ‘A’ serait assis)
  • C_BASELINE 10 // Ligne de base du caractère central
  • R_BASELINE 11 // Ligne de base du caractère droit

Méthodes

  • setTextDatum(uint8_t d) référence du texte. Utiliser l’une des constantes ci-dessus
  • setTextPadding(uint16_t x_width) marge interne (équivalent au style CSS)
    • getTextPadding() renvoie le padding actuel
  • drawString(const String &string, int32_t poX, int32_t poY) dessine une chaîne (type String)
  • drawCentreString(const String &string, int32_t dX, int32_t poY, uint8_t font) dessine une chaîne (type String) centrée sur la position X
  • drawRightString(const String &string, int32_t dX, int32_t poY, uint8_t font)

Conversions

Quelques méthodes de conversion utiles

  • Couleurs
    • color8to16(uint8_t color) convertir une couleur 8 bits en une valeur de couleur 16 bits 565
    • color16to24(uint16_t color565) convertir une couleur 16 bits en une valeur de couleur 24 bits 888
    • color24to16(uint32_t color888) convertir une couleur 24 bits en une valeur de couleur 16 bits 565
  • Chaînes
    • decodeUTF8(uint8_t c) Convertit un caractère ASCII au format UTF-8
    • decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) Décode un buffer de caractères au format ASCII étendu en UTF-8

La librairie n’est pas encore documentée, il faut directement chercher directement dans le code source.

Comment accéder aux fonctions de la librairie TFT_eSPI ?

En therme informatique, la Librairie LilyGoWatch est un wrapper, c’est à dire qu’elle unifie l’accès à un ensemble de librairies.

Les méthodes proposées par les différentes librairies sont accessibles depuis l’objet C++ TTGOClass.

Pour accéder à un périphérique puis aux fonctions (méthodes C++), on utilise l’opérateur flèche ->. C’est l’équivalent du point . en Javascript par exemple.

Voici un exemple de programme minimal qui allume l’écran et colorie le fond en rouge

#include 

TTGOClass *ttgo;

void setup() {
    ttgo = TTGOClass::getWatch();
    ttgo->begin();     
    ttgo->openBL();
        
    ttgo->tft->fillScreen(TFT_RED);
}

void loop(){}

On doit commencer par initialiser l’objet watch en appelant la méthode getWatch(). Cet objet contiendra toutes les méthodes pour accéder aux périphériques de la montre (accéléromètre, GPS, horloge RTC, écran TFT ou eInk…).

watch = TTGOClass::getWatch();

Ensuite on doit allumer l’écran TFT de la montre en appelant la méthode begin(), puis le rétro-éclairage à l’aide de la méthode openBL().

watch->begin();  // Initialise l'écran couleur TFT
watch->openBL(); // Allume le rétro-éclairage de l'écran

Maintenant on accède aux fonctions comme ceci

objetmontre->driver->fonction()

Ce qui donne pour mettre le fond de l’écran en rouge

ttgo->tft->fillScreen(TFT_RED);

Créer un objet TFT_eSPI contenant le driver de l’écran

Il est également possible de créer un objet de type TTGOClass qui contiendra le drivers de l’écran pour réduire le code

TFT_eSPI * tft = ttgo->tft;
tft->fillScreen(TFT_RED);

Connaitre la résolution de l’écran

Il est souvent nécessaire de connaitre la résolution de l’écran pour positionner les éléments. Pour cela, on dispose des méthodes width() et height() pour connaitre respectivement la largeur et la hauteur de l’écran en pixels.

  w = ttgo->tft->width();
  h = ttgo->tft->height();
  Serial.printf("Screen size %u * %u", w, h);

Comment positionner le curseur sur l’écran ?

La librairie TFT_eSPI utilise un curseur pour positionner les éléments à l’écran. Les coordonnées X et Y du curseur sont déterminée à partir du coin supérieur gauche de l’écran. L’origine change en fonction de l’orientation de l’écran.

Pour changer manuellement l’orientation de l’écran, rendez-vous à ce paragraphe pour apprendre comment faire.

Le curseur permet d’afficher du texte à l’aide des fonctions standards print, println ou printf par exemple.

Les fonctions getCursorX et getCursorY permettent de connaître la position du curseur et setCursor pour positionner ce dernier au pixel prêt.

Par contre, les autres méthodes de dessin (y compris pour dessiner du texte) utilisent simplement les coordonnées.

Détecter lorsque l’utilisateur touche l’écran et récupérer la position

La méthode getTouch(x, y) permet de détecter lorsque l’utilisateur touche l’écran. La méthode retourne un booléen et affecte les coordonnées X et Y du point touché sur l’écran.

La méthode getTouch() est directement accessible depuis la classe ttgo de la librarie LiLyGoWatch

La librairie est livrée avec l’exemple Touch Pad dans le dossier BasicUnit

TTGOClass *ttgo;

char buf[128];

void setup()
{
    ttgo = TTGOClass::getWatch();
    ttgo->begin();
    ttgo->openBL();
    ttgo->tft->fillScreen(TFT_BLACK);
    ttgo->tft->setTextFont(2);
    ttgo->tft->setTextColor(TFT_WHITE, TFT_BLACK);
    ttgo->tft->drawString("T-Watch Touch Test", 62, 90);
}

void loop()
{
    int16_t x, y;
    if (ttgo->getTouch(x, y)) {
        sprintf(buf, "x:%03d  y:%03d", x, y);
        ttgo->tft->drawString(buf, 80, 118);
    }
    delay(5);
}

Comment la librairie TFT_eSPI construit l’écran ?

Avant d’aller plus loin, il est important de comprendre comment la librairie construit l’écran. Il faut considérer l’écran comme un tableau de points. Lorsqu’on dessine une forme (ou du texte ou une image), on ne fait “qu’allumer” des points du tableau avec une certaine couleur.

C’est à dire que l’ordre d’execution des fonctions va avoir un impact direct sur la construction de l’affichage. Ici par exemple le cercle bleu masque une partie du texte blanc posé lui même sur le rectangle vert.

Même si la librairie TFT_eSPI est très puissante, elle ne gère pas les éléments graphiques.

Lorsqu’on actualise l’affichage, on a deux solutions

Reconstruire la totalité de l’écran à chaque modification Ne reconstruire que le strict nécessaire.

Par exemple effacer (donner la couleur du fond) de l’aiguille d’une horloge puis dessiner la nouvelle position.

Avantage Facile Plus complexe
Inconvénient Risque élevé de clignotement.

Le clignotement augmente avec le nombre d’éléments à afficher et la fréquence de rafraîchissement.

Performant, peu de clignotement

Afficher du texte avec les fonctions C++ print(), println() ou printf()

Pour afficher du texte, on peut utiliser les mêmes méthodes C++ qui permettent d’écrire sur le port série print(), println() ou printf().

La macro F() qui permet de placer la chaîne dans la mémoire Flash est supportée mais cela n’a toutefois aucun intérêt.

Le texte est affiché à la position du curseur. Ici par exemple, on fait varier la taille de la police entre 1 et 7 (il n’est pas possible d’aller au delà).

  TFT_eSPI * tft = ttgo->tft;
  tft->fillScreen(TFT_BLACK);
  tft->setTextColor(TFT_WHITE);
    for (size_t i = 1; i setTextFont(1);     // Uniquement 1
      tft->setTextSize(i);     // change la taille        
      tft->setCursor(0, 0);
      txt = "Text with size "; txt += i;
      Serial.print("Display"); Serial.println(txt);
      tft->println(txt);
      delay(1000);
      tft->fillScreen(TFT_BLACK);
    }

Afficher une liste défilante

Dans l’exemple précédent le curseur reste à l’origine (0,0) et l’écran est effacé (repeint en noir) à chaque itération de la boucle for. La méthode println() permet de déplacer le curseur à la ligne suivante. Toutefois, la librairie TFT_eSPI ne gère pas le scrolling vertical de la fonction print.

Pour afficher une liste qui dépasse la hauteur de l’écran, l’astuce consiste à tester la position du curseur et renvoyer ce dernier en haut de l’écran dès que la position courante dépasse la hauteur de l’écran.

On a vu précédemment comment récupérer la résolution de l’écran.

Voici un exemple de code qui affiche une liste de 20 éléments à l’écran

  TFT_eSPI * tft = ttgo->tft;
  tft->fillScreen(TFT_BLACK);
  tft->setTextColor(TFT_WHITE, TFT_BLACK);
  tft->setTextSize(2);
  tft->setCursor(0,0);
  
  for (size_t i = 0; i width();     // largeur
  h = tft->height();    // hauteur

  // Orientation initiale de l'écran
  orientation = tft->getRotation();
  drawReference();
}

void loop() {
  int16_t x, y;
  if (ttgo->getTouch(x, y)) {
    if ( orientation == 3 ) {
      orientation = 0;
    } else {
      orientation += 1;
    }
    tft->setRotation(orientation);
    Serial.printf("Change screen orientation %u \n", orientation);
    drawReference();
    delay(100);
  }
}

void drawReference(){
  // Efface l'écran
  tft->fillScreen(TFT_BLACK);  // Colorie le fond de l'écran en noir
  tft->setTextFont(1);
  tft->setTextSize(2);
  tft->setTextColor(TFT_WHITE, TFT_BLACK);
  tft->setCursor(0, 0);

  // Dessine le référentiel X/Y
  tft->drawFastHLine(p,p, w - 2*p, TFT_WHITE);
  tft->drawFastVLine(p,p, h - 2*p, TFT_WHITE);

  // Affiche les lettres X et Y (en utilisant le code ascii)
  // Use http://www.asciitable.com/ (for example) to find ascii char code
  // drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size)
  tft->drawChar(w - 3*p, 3*p, 88, TFT_WHITE, TFT_BLACK, 2); // X
  tft->fillTriangle(w, p, w - 2*p, 0, w - 2*p, 2*p, TFT_RED ); // Triangle pour la flèche X
  tft->drawChar(3*p, w - 3*p, 89, TFT_WHITE, TFT_BLACK, 2);  // Y
  tft->fillTriangle(p, h, 0, h - 2*p, 2*p, h - 2*p, TFT_RED ); // Triangle pour la flèche Y
  tft->fillCircle(p,p,p,TFT_RED); // Point d'origine

  // Message au centre de l'écran 
  tft->setTextDatum(MC_DATUM); 
  tft->drawString("Touch Screen", w / 2, h / 2 - 10, 1);
  tft->drawString("to rotate", w / 2, h / 2 + 10, 1);
}

Code complet des exemples

Voici le code complet de tous les exemples présentés précédemment.

Vous pouvez également récupérer directement sur le dépôt GitHub le code source

Débuter avec la librairie TFT_eSPI et la T-Watch

Orientation de l’écran

Le code est compatible avec l’IDE Arduino ou PlatfomIO.

/* Arduino IDE - uncomment your watch */
//#define LILYGO_WATCH_2019_WITH_TOUCH
//#define  LILYGO_WATCH_2019_NO_TOUCH
//#define LILYGO_WATCH_BLOCK=1
//#define LILYGO_WATCH_2020_V1

/* PlatformIO -> Select your watch in platformio.ini file */
#include 
#include 

// Objet C++ qui permettra d'accéder aux fonctions du boitier
TTGOClass *ttgo;
// Objet C++ TFT_eSPI permettant un accès rapide aux fonctions
TFT_eSPI *tft;

String txt; 
int w = 240;    // Largeur de l'écran
int h = 240;    // Hauteur de l'écran
int16_t x, y;
char buf[128];

/* Prototypes */
void fillScreenBackground();
void displayFontSize();
void drawBasic();
void drawStringDemo();
void screenOrientationDemo();
void printAListToScreen();

void setup() {
    Serial.begin(115200);
    // Récupère l'objet Watch et allume l'écran
    ttgo = TTGOClass::getWatch();
    ttgo->begin();     
    ttgo->openBL();
    tft = ttgo->tft;
    // Dé-commenter les fonctions que vous voulez tester
    //fillScreenBackground();
    //displayFontSize();
    drawBasic();
    //drawStringDemo();
    //screenOrientationDemo();
    //printAListToScreen();
    
    // Efface l'écran 
    tft->fillScreen(TFT_BLACK);
    tft->setTextColor(TFT_WHITE, TFT_BLACK);
    tft->setTextDatum(MC_DATUM);
}

void loop(){
  // Affiche les coordonnées du curseur chaque fois que l'écran est touché
  if (ttgo->getTouch(x, y)) {
    sprintf(buf, "  x:%u  y:%u   ", x, y);
    tft->drawString(buf, w / 2, h / 2);
    Serial.println(buf);
  }
}

// Constantes des couleurs disponibles
/* 
 * TFT_BLACK         0,   0,   0 
 * TFT_NAVY          0,   0, 128 
 * TFT_DARKGREEN     0, 128,   0 
 * TFT_DARKCYAN      0, 128, 128 
 * TFT_MAROON      128,   0,   0 
 * TFT_PURPLE      128,   0, 128 
 * TFT_OLIVE       128, 128,   0 
 * TFT_LIGHTGREY   211, 211, 211 
 * TFT_DARKGREY    128, 128, 128 
 * TFT_BLUE          0,   0, 255 
 * TFT_GREEN         0, 255,   0 
 * TFT_CYAN          0, 255, 255 
 * TFT_RED         255,   0,   0 
 * TFT_MAGENTA     255,   0, 255 
 * TFT_YELLOW      255, 255,   0 
 * TFT_WHITE       255, 255, 255 
 * TFT_ORANGE      255, 180,   0 
 * TFT_GREENYELLOW 180, 255,   0 
 * TFT_PINK        255, 192, 203     
 * TFT_BROWN       150,  75,   0 
 * TFT_GOLD        255, 215,   0 
 * TFT_SILVER      192, 192, 192 
 * TFT_SKYBLUE     135, 206, 235 
 * TFT_VIOLET      180,  46, 226 
*/
void fillScreenBackground(){
    Serial.println("Red");
    tft->fillScreen(TFT_RED);
    delay(1000);
    Serial.println("Green");
    tft->fillScreen(TFT_GREEN);
    delay(1000);
    Serial.println("Black");
    tft->fillScreen(TFT_BLACK);
    delay(1000);
}

void getScreenSize(){
  w = tft->width();
  h = tft->height();
  Serial.printf("Screen size %u * %u", w, h);
}

// Dessine les primitives geometriques
void drawBasic(){
  int margin = 20;
  int _delay = 500;
  getScreenSize();
  
  Serial.println("Draw a rectangle");
  tft->fillScreen(TFT_BLACK);
  tft->drawRect(margin,margin, w - ( 2* margin ), h - ( 2 * margin),TFT_RED);
  delay(_delay);

  Serial.println("Draw a filled rectangle");
  // fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
  tft->fillRect(2 * margin, 2 * margin, w - ( 4 * margin), h - (4 * margin), TFT_VIOLET);
  delay(_delay);

  Serial.println("Draw a rounded corner rectangle outline");
  //drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t r, uint32_t color)
  tft->drawRoundRect(3 * margin, 3 * margin, w - ( 6 * margin), h - (6 * margin), 8, TFT_YELLOW);
  delay(_delay);

  Serial.println("Draw a line");
  // drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color)
  tft->drawLine(0, 0, w, h, TFT_DARKGREY);
  delay(_delay);  

  Serial.println("Draw a triangle outline using 3 arbitrary points");
  // drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color)
  tft->drawTriangle(w,h - 1, w - 40 , h, w - 20, h - 30, TFT_PURPLE);
  delay(_delay); 

  Serial.println("Draw a circle outline");
  //drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color)
  tft->drawCircle(w / 2, h / 2, 80, TFT_SKYBLUE);
  delay(_delay);

  Serial.println("Draw a ellipse outline");
  //drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color)
  tft->drawEllipse(w, h, 80, 60, TFT_SKYBLUE);
  delay(_delay);

  Serial.println("Draw horizontal line");
  // drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color)
  tft->drawFastHLine(0, 0, w, TFT_DARKGREY);
  delay(_delay);  
  
  Serial.println("Draw Vertical line");
  // drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color)
  tft->drawFastVLine(0, 0, h, TFT_DARKGREY);
  delay(_delay);
  
  Serial.println("Draw A char (ascii 65)");
  //drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size)
  // Use this to find char code http://www.asciitable.com/
  tft->drawChar(margin, margin, 65, TFT_BLUE, TFT_WHITE, 2);
  delay(_delay);

  Serial.println("Draw a string");
  //drawString(const char *string, int32_t poX, int32_t poY)
  tft->drawString("T-Watch",margin, margin * 2);
  delay(_delay);

  Serial.println("Draw a pixel");
  //drawPixel(int32_t x, int32_t y, uint32_t color)
  tft->drawPixel(w / 2 , h / 2, TFT_RED);
  delay(_delay);
  /* Other usefull functions 
      invertDisplay(bool i)
      decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining)
  */
}

// Affiche une police en augmentant la taille progressivement de 1 à 7 (max)
void displayFontSize(){
  tft->fillScreen(TFT_BLACK);
  tft->setTextColor(TFT_WHITE);
    for (size_t i = 1; i setTextFont(1);     // Only font 1 is available | Uniquement 1
      tft->setTextSize(i);     // Change size | change la taille        
      tft->setCursor(0, 0);
      txt = "Text with size "; txt += i;
      Serial.print("Display"); Serial.println(txt);
      tft->println(txt);
      delay(1000);
      tft->fillScreen(TFT_BLACK);
    }
}

void drawStringDemo(){
  tft->fillScreen(TFT_BLACK);
  tft->setTextColor(TFT_WHITE);
  tft->setTextSize(2); 
  for (size_t i = 0; i < 12; i++)
  {
    tft->setTextDatum(i);
    Serial.printf("Display string with justification %u \n", tft->getTextDatum());
    tft->drawString("TFT_eSPI Demo", w / 2, h / 2, 2);
    delay(1000);
    tft->fillScreen(TFT_BLACK);
  }
}

void screenOrientationDemo(){
  Serial.printf("Current screen orientation %u \n", tft->getRotation());
  tft->fillScreen(TFT_BLACK);
  tft->setTextColor(TFT_WHITE);
  tft->setTextDatum(MC_DATUM);
  tft->setTextSize(2); 
  tft->drawString("Rotate Screen", w / 2, h / 2, 2);
  for (size_t i = 1; i < 4; i++)
  {
    // rotate the screen orientation m = 0-3 or 4-7 for BMP drawing
    //setRotation(uint8_t m)
    Serial.printf("Rotate the screen to %u \n", i);
    tft->setRotation(i);
    tft->fillScreen(TFT_BLACK);
    tft->drawString("Rotate Screen", w / 2, h / 2, 2);
    delay(1000);
  }
  
  tft->setRotation(2);
  tft->fillScreen(TFT_BLACK);
  tft->drawString("Rotate Screen", w / 2, h / 2, 2);
}

// Use printf or println to display a list to screen | utilise la fonction println or printf pour afficher une liste à l'écran
void printAListToScreen(){
  //TFT_eSPI * tft = tft;
  tft->fillScreen(TFT_BLACK);
  tft->setTextColor(TFT_WHITE, TFT_BLACK);
  tft->setTextSize(2);
  tft->setCursor(0,0);
  
  for (size_t i = 0; i