T-Watch. Afficher des images XBM (TFT_eSPI) et C++ (LVGL). Compatible ESP32, Arduino • Domotique et objets connectés à faire soi-même

Les librairies TFT_eSPI et LVGL permettent d’afficher des images très simplement. La librairie TFT_eSPI affiche facilement des images au format XBM, qui n’est en fait qu’un tableau d’entiers. La librairie LVGL est plus complexe à intégrer mais elle est aussi plus puissante. Dans les deux cas, il faudra au préalable convertir les images nécessaires au projet. 

Dans ce tutoriel, nous ne traiterons pas l’affichage d’image stockés sur une carte SD. Il est possible – et assez facile – d’utiliser dans un même projet les librairies TFT_eSPI et LVGL, lisez cet article pour en savoir plus.

Préparer les images

La librairies TFT_eSPI ne permet pas de redimensionner (ou du moins sans effort) les images par programmation. Il faudra donc prévoir une image pour chaque taille (icône, fond d’écran…), ce qui demande un peu de temps de préparation et d’actualisation.

La librairie LVGL dispose de quelques fonctions de transformation :

  • lv_img_set_zoom(img,factor) ajuste la taille de l’image. 128 correspond à 50% de la taille. 512 (max) pour obtenir le double de la taille initiale.
  • lv_img_set_angle(img,angle) fait une rotation de l’image avec une précision de 0.1°
  • lv_img_set_antialias(img, true/false) pour diminuer les effets d’escalier (attention à la baisse de performance !)

Convertisseur d’image en ligne gratuit au format XBM

Le format XBM est un format qui décrit les images en langage C, ce qui permet d’intégrer très facilement des images XBM à un logiciel écrit en C. C’un format d’image monochrome originellement conçu pour le système X Window, notamment pour les images de pointeur et d’icône.

Plus d’informations sur le format XBM sur cette page Wikipedia.

On trouve assez facilement des convertisseurs en ligne. En voici deux gratuits :

  • Free File Converter permet de convertir les images par lot de 5 à chaque fois. Il faudra télécharger manuellement chaque image, mais c’est déjà un gain de temps énorme.
  • Online Utility ne propose pas la conversion de lot d’image. Il faudra reproduire l’opération pour la conversion de chaque image.

L’interface de Free File Converter est ultra simple. Ajouter manuellement les fichiers à convertir puis sélection le format XBM dans la boite de sélection

Cliquer sur Convert puis télécharger chaque fichier XBM

kepdq3nvg86w9bvadgd5-8880601

Convertir en XBM avec GIMP

L’autre solution consiste à installer GIMP sur votre ordinateur. GIMP (pour GNU Image Manipulation Program) est un dinosaure du logiciel libre. En plus d’être totalement Open Source et gratuit, il est à bien des niveaux comparable à Photoshop.

GIMP est très pratique car il va nous permettre de faire plusieurs opérations sur les images très utiles :

  • Rogner / redimensionner l’image (c’est vrai que n’importe quel éditeur d’image peut le faire)
  • Ajuster les couleurs
  • Inverser les couleurs (pratique pour des icônes monochromes), une fonction généralement absente des éditeurs intégrés à l’OS (Windows, macOS, Linux)

Enfin, GIMP peut faire la conversion au format XBM. Pour cela, il suffit d’aller dans le menu Fichier puis Exporter sous.

Saisissez le nom du fichier en lui donnant directement l’extension xbm puis enregistrer.

Une fenêtre s’ouvre pour ajuster les paramètres d’exportation. Indiquer le préfixe qui servira de variable dans le projet Arduino / ESP32.

Visualiser un fichier XBM avec GIMP !

On trouve de nombreux fichiers XBM sur internet. Plutôt que d’écrire un projet Arduino / ESP32 pour avoir un rendu, sachez que GIMP est capable d’ouvrir un fichier XBM et de le modifier comme n’importe quelle autre format d’image !

Comment stocker les images sur la mémoire Flash de l’ESP32?

Pour limiter l’utilisation de la RAM du micro-contrôleur, il est préférable de stocker les images du projet dans la mémoire Flash. Pour cela on utilise la macro PROGMEM.

PROGMEM fait partie de la bibliothèque pgmspace.h.

En fonction de l’éditeur utilisé (IDE Arduino, PlatformIO), vous serez peut être obligé de déclarer manuellement la librairie pgmspace.h avant de pouvoir utiliser PROGMEM. Il suffit d’ajouter cette ligne de code au début du fichier en cas d’erreur de compilattion

#include 

Adapter le fichier XBM pour un projet Arduino

Ouvrez le fichier au format XBM que vous venez de télécharger à l’aide de PlatformIO (conseillé).

Il contient trois variables :

  • width la largeur de l’image en pixels
  • height la haute de l’image en pixels
  • static char xxxx_bits[] la valeur hexadécimale de chaque point de l’image.
#define 24767_d7cefe673dc19c3b1994da17f988b26a6931654f33b5f0fd41ceec7d35015fa7_width 200
#define 24767_d7cefe673dc19c3b1994da17f988b26a6931654f33b5f0fd41ceec7d35015fa7_height 200
static char 24767_d7cefe673dc19c3b1994da17f988b26a6931654f33b5f0fd41ceec7d35015fa7_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  ....
};

Pour pouvoir utiliser ce fichier dans votre projet Arduino, vous devez faire les adaptations suivantes :

  • Renommez les variables en remplacer l’identifiant pour un nom de variable cohérent avec l’image
  • Ajouter #include au début du fichier
  • Ajouter PROGMEM avant le tableau ce qui permettra de stocker l’image dans la mémoire Flash pour ne pas encombrer la RAM de l’ESP32 inutilement. C’est vrai aussi sur Arduino ou ESP8266.
  • Renommer le fichier .xbm en .h

Ce qui donne par exemple

#include 

#define clock_width 200
#define clock_height 200

PROGMEM const unsigned char clockxbm[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 ...
};

Exemple d’affichage d’image XBM sur une T-Watch avec TFT_eSPI

Voici maintenant un exemple de code Arduino qui permet d’afficher au centre de l’écran l’image après conversion au format XBM.

L’affichage d’une image au format XBM se fait directement à l’aide de la fonctiondrawXBitmap() qui prend les paramètres suivants :

  • x, y origine de l’image
  • w, h dimension de l’image en pixels
  • color couleur de l’image
  • bkcolor couleur de fond

Pour inverser les couleurs (noir / blanc) de l’image, il suffit d’inverser les variables color et bkcolor.

md6ndufycxjd77lkbxdn-8777017

Vous pouvez utiliser n’importe quelle couleur ou une constante pré-définie détaillée ici.

/* Arduino IDE - dé-commenter votre T-Wach */
//#define LILYGO_WATCH_2019_WITH_TOUCH
//#define  LILYGO_WATCH_2019_NO_TOUCH
//#define LILYGO_WATCH_BLOCK
//#define LILYGO_WATCH_2020_V1

/* PlatformIO -> Select votre montre dans le fichier platformio.ini */
#include 
#include 
#include "clock.h"

TTGOClass *ttgo;

void setup() {
  Serial.begin(115200);
  ttgo = TTGOClass::getWatch();
  ttgo->begin();    
  ttgo->openBL();

  ttgo->tft->fillScreen(TFT_BLACK);
  ttgo->tft->setTextSize(2);
  ttgo->tft->fillScreen(TFT_BLACK);

  ttgo->tft->drawString("XBM image", 0,0);
  //drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bgcolor)
  ttgo->tft->drawXBitmap(20,20, clockxbm, clock_width, clock_height, TFT_WHITE, TFT_BLACK);
}

void loop() {}

Démo avec TFT_eSPI sur une T-Watch Touch

Voici ce que cela donne sur une T-Watch Touch.

Tous les modèles de T-Watch, cartes d’extension et accessoires.

Convertir une image en fichier C True Color pour LVGL

LVGL met à disposition un convertisseur en ligne gratuit disponible ici.

Même s’il est possible d’ajuster la taille de l’image avec la fonction lv_img_set_zoom(), il est préférable de fournir une image proche de la dimension souhaitée à l’écran. On réservera la zoom pour ajuster la taille des icônes par exemple.

La conversion d’image en objet C ce fait individuellement :

  • Image File choisissez le fichier à convertir
  • Name Le nom de la variable souhaitée de la code (en majuscule de préférence)
  • Color Format choisir True Color with Alpha
  • Output format choisir C array

Convertir

Téléchargez et ouvrez le fichier c généré. Il n’y a rien à faire, le convertisseur d’image LVGL a déjà tout préparé pour vous ! Il est préférable de conserver un fichier par image. Si nécessaire, un fichier par dimension.

Voici la structure du fichier C généré par le convertisseur en ligne LVGL

#include "lvgl/lvgl.h"

#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif

#ifndef LV_ATTRIBUTE_IMG_R2D2
#define LV_ATTRIBUTE_IMG_R2D2
#endif

const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_COLORCLOCK uint8_t R2D2_map[] = {
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
  /*Pixel format: Blue: 2 bit, Green: 3 bit, Red: 3 bit, Alpha 8 bit */
 ...
#endif
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0
  /*Pixel format: Blue: 5 bit, Green: 6 bit, Red: 5 bit, Alpha 8 bit*/
...
#endif
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0
  /*Pixel format:  Blue: 5 bit Green: 6 bit, Red: 5 bit, Alpha 8 bit  BUT the 2  color bytes are swapped*/
...
#endif
#if LV_COLOR_DEPTH == 32
  /*Pixel format:  Blue: 8 bit, Green: 8 bit, Red: 8 bit, Alpha: 8 bit*/
...
#endif
};

const lv_img_dsc_t R2D2 = {
  .header.always_zero = 0,
  .header.w = 240,
  .header.h = 240,
  .data_size = 57600 * LV_IMG_PX_SIZE_ALPHA_BYTE,
  .header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA,
  .data = R2D2_map,
};

Exemple de code pour afficher une image avec LVGL sur une T-Watch

La librairie LilyGoWatch est livrée avec 3 images de fond d’écran directement accessible à l’aide des constantes WALLPAPER_1_IMG, WALLPAPER_2_IMG et WALLPAPER_3_IMG.

Les fichiers C sont dans le dossier imgs du code source.

md6ndufycxjd77lkbxdn-8777017

L’affichage d’une image avec LVGL demande un peu plus de travail.

/* Arduino IDE - dé-commenter votre T-Watch */
//#define LILYGO_WATCH_2019_WITH_TOUCH
//#define  LILYGO_WATCH_2019_NO_TOUCH
//#define LILYGO_WATCH_BLOCK
//#define LILYGO_WATCH_2020_V1
#define LILYGO_WATCH_LVGL

/* PlatformIO -> Selectionner la T-Watch dans le fichier platformio.ini */
#include 
#include 

TTGOClass *ttgo;

// Déclaration des images
LV_IMG_DECLARE(WALLPAPER_1_IMG);
LV_IMG_DECLARE(WALLPAPER_2_IMG);
LV_IMG_DECLARE(WALLPAPER_3_IMG);
LV_IMG_DECLARE(R2D2);

void setup() {
  Serial.begin(115200);
  ttgo = TTGOClass::getWatch();
  ttgo->begin();    

  // initialise LVGL
  ttgo->lvgl_begin(); 
  ttgo->openBL();
  
  // Dans le coin supérieur gauche zoom 50%
  lv_obj_t *img1 = lv_img_create(lv_scr_act(), NULL);
  lv_img_set_src(img1, &WALLPAPER_1_IMG);
  lv_img_set_zoom(img1, 128);
  lv_obj_align(img1, NULL, LV_ALIGN_CENTER, -60, -60);
  // Dans le coin supérieur droit
  lv_obj_t *img2 = lv_img_create(lv_scr_act(), NULL);
  lv_img_set_src(img2, &WALLPAPER_2_IMG);
  lv_img_set_zoom(img2, 128);
  lv_obj_align(img2, NULL, LV_ALIGN_CENTER, 60, -60);
  
  // En bas et à gauche
  lv_obj_t *img3 = lv_img_create(lv_scr_act(), NULL);
  lv_img_set_src(img3, &WALLPAPER_3_IMG);
  lv_img_set_zoom(img3, 128);
  lv_obj_align(img3, NULL, LV_ALIGN_CENTER, -60, 60);
  
  // En bas à droite
  lv_obj_t *img4 = lv_img_create(lv_scr_act(), NULL);
  lv_img_set_src(img4, &R2D2);
  lv_img_set_zoom(img4, 128);
  lv_obj_align(img4, NULL, LV_ALIGN_CENTER, 60, 60);
}

void loop() {
  lv_task_handler();
}

Explication du code C++

LVGL est intégrée dans la librairie LilyGoWatch. Inutile donc de l’installer manuellement dans votre projet. Pour l’activer, il suffit d’ajouter la constante LILYGO_WATCH_LVGL en début de programme Arduino.

#define LILYGO_WATCH_LVGL

On démarre la librairie LVGL après la création du pointeur vers l’objet T-Watch.

ttgo = TTGOClass::getWatch();
ttgo->begin();    
ttgo->lvgl_begin();

Avant de pouvoir utiliser une image, il faut déclarer le fichier C à l’aide de la fonction LV_IMG_DECLARE().

LV_IMG_DECLARE(WALLPAPER_1_IMG);

On peut maintenant créer un objet lv_obj_t qui contiendra l’image dans la scene actuelle lv_scr_act()

lv_obj_t *img1 = lv_img_create(lv_scr_act(), NULL);

On spécifie le fichier image de cet objet

lv_img_set_src(img1, &WALLPAPER_1_IMG);

On peut éventuellement appliquer des transformations à l’image. Ici on réduit la taille de 50%

lv_img_set_zoom(img1, 128);

Et enfin il ne reste plus qu’à l’afficher à l’endroit désiré sur l’écran. Ici dans le coin supérieur gauche en appelant la fonction

lv_obj_align(img1, NULL, LV_ALIGN_CENTER, -60, -60);

Pour que l’écran soit généré, il faut appeler la méthode lv_task_handler(). Pour pouvoir récupérer les actions de l’utilisateur sur l’écran tactile, il est préférable de placer le supervision dans la loop() pour qu’il appelé régulièrement.

void loop() {
  lv_task_handler();
}

Démo avec LVGL sur une T-Watch 2020

Le rendu de la mosaïque d’images sur la T-Watch 2020 avec la librairie LVGL.

Mosaïque d’image avec LVGL sur T-Watch 2020

Tous les modèles de T-Watch, cartes d’extension et accessoires

Conclusion

La librairie TFT_eSPI est super rapide à mettre en oeuvre. Si vous débutez et que la couleur n’est pas essentielle à votre application, c’est la librairie qu’il vous faut.

Pour afficher des images en couleur, il est préférable d’utiliser la librairie LVGL. Elle est beaucoup plus puissante. En l’absence d’éditeur d’interface, il faudra plus de temps pour monter en compétence et mettre au point l’interface de votre application.

Mises à jour

24/11/2020 Comment visualiser et exporter des images au format XBM avec GIMP

20/11/2020 Publication de l’article

English Version

Avez-vous aimé cet article ?