T-Watch. Display XBM (TFT_eSPI) and C++ (LVGL) images. ESP32, Arduino compatible

display image xbm cpluplus esp32 ttgo t-watch arduino lvgl tft_espi
Share on facebook
Share on twitter
Share on linkedin
Share on pinterest
Share on email
Share on whatsapp
Table of Contents

The TFT_eSPI and LVGL libraries allow you to display images very simply. The TFT_eSPI library easily displays images in XBM format, which is in fact only an array of integers. The LVGL library is more complex to integrate but it is also more powerful. In both cases, you must first convert the images required for the project. 

 

In this tutorial, we will not deal with displaying images stored on an SD card. It is possible – and quite easy – to use the TFT_eSPI and LVGL libraries in the same project, read this article to find out more.

Read Also
T-Watch. Mix the LVGL and TFT_eSPI libraries in the same ESP32 project

Prepare the images

The TFT_eSPI libraries do not allow you to resize (or at least without effort) the images by programming. It will therefore be necessary to provide an image for each size (icon, wallpaper, etc.), which requires a little preparation and updating time.

The LVGL library has some transformation functions :

  • lv_img_set_zoom(img, factor) adjusts the size of the image. 128 is 50% of the size. 512 (max) to get double the initial size.
  • lv_img_set_angle(img, angle) rotates the image with a precision of 0.1°
  • lv_img_set_antialias(img, true / false) to reduce escalation effects (beware of the drop in performance!)

Free Online Image Converter to XBM Format

The XBM format is a format that describes images in C language, which makes it very easy to integrate XBM images into software written in C. It is a monochrome image format originally designed for the  X Window System, especially for the pointer and icon images.

More information about the XBM format on this Wikipedia page.

Converters are quite easy to find online. Here are two free ones:

  • Free File Converter can convert images in batch of 5 each time. You will have to manually download each image, but it is already a huge time saver.
  • Online Utility does not offer image batch conversion. It will be necessary to reproduce the operation for the conversion of each image.

The interface of Free File Converter is very simple. Manually add the files to convert then select the XBM format in the selection box

freefileconverter convert bmp xbm tft_espi esp32 arduino esp8266

Click on Convert then download each XBM file

freefileconverter convert bmp xbm tft_espi esp32 ttgo t-wacth

Convert to XBM with GIMP

The other solution is to install GIMP on your computer. GIMP (for GNU Image Manipulation Program) is a free software dinosaur. In addition to being completely open source and free, it is in many ways comparable to Photoshop.

GIMP is very practical because it will allow us to do several operations on the very useful images:

  • Crop / resize image (it’s true that any image editor can do this)
  • Adjust colors
  • Invert the colors (useful for monochrome icons), a function generally absent from the editors integrated into the OS (Windows, macOS, Linux)

Finally, GIMP can do the conversion to XBM format. To do this, simply go to the File menu then Export as.

Enter the name of the file by directly giving it the xbm extension then save.

A window opens to adjust the export settings. Indicate the prefix that will be used as a variable in the Arduino / ESP32 project.

export xbm arduino image esp32 tft_espi gimp

View an XBM file with GIMP!

There are many XBM files on the internet. Rather than writing an Arduino / ESP32 project to have it rendered, know that GIMP is able to open an XBM file and modify it like any other image format!

Warning, so that GIMP can open the XBM file, you must not delete the file header.

view xbm file gimp arduino esp32 tft_espi

How do I store the images on the ESP32’s Flash memory?

To limit the use of the micro-controller RAM, it is preferable to store the project images in the Flash memory. For that one uses the macro PROGMEM.

PROGMEM is part of the  pgmspace.h library.

Depending on the editor used (Arduino IDE, PlatformIO), you may have to manually declare the pgmspace.h library before you can use PROGMEM. Just add this line of code at the start of the file in case of a compilation error

#include <pgmspace.h>

Adapt the XBM file for an Arduino project

Open the XBM format file you just downloaded using PlatformIO (recommended).

It contains three variables:

  • width the width of the image in pixels
  • height the height of the image in pixels
  • static char xxxx_bits[] the hexadecimal value of each point in the 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, 
  ....
};

To be able to use this file in your Arduino project, you must make the following adaptations:

  • Rename the variables by replacing the identifier for a variable name consistent with the image
  • Add #include <pgmspace.h> at the beginning of the file
  • Add PROGMEM before the table which will allow the image to be stored in the Flash memory so as not to encumber the RAM of the ESP32 unnecessarily. This is also true on Arduino or ESP8266.
  • Rename the .xbm file to .h

Which gives for example

#include <pgmspace.h>

#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, 
 ...
};

Example of XBM image display on a T-Watch with TFT_eSPI

Here is now an example of Arduino code that displays in the center of the screen the image after conversion to XBM format.

xbm clock image arduino

The display of an image in XBM format is done directly using the drawXBitmap() function which takes the following parameters:

  • x, y origin of the image
  • w, h image size in pixels
  • color picture color
  • bkcolor background color

To invert the colors (black / white) of the image, all you have to do is invert the color and bkcolor variables.

logo github

You can use any color or a predefined constant detailed here.

/* Arduino IDE - un-comment your 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 your t-watch infile platformio.ini */
#include <Arduino.h>
#include <LilyGoWatch.h>
#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() {}

Demo with TFT_eSPI on a T-Watch Touch

Here’s what it looks like on a T-Watch Touch.

tft_espi ttgo t-watch display xbm image esp32 project

All models of T-Watch, expansion boards and accessories.

Convert an Image to a True Color C File for LVGL

LVGL provides a free online converter available here .

lvgl on line image converter esp32 t-watch arduino

Although it is possible to adjust the size of the image with the lv_img_set_zoom() function , it is preferable to provide an image close to the desired dimension on the screen. We reserve the zoom to adjust the size of the icons for example.

Converting image to object C does this individually:

  • Image File choose the file to convert
  • Name The name of the desired variable in the code (preferably in uppercase)
  • Color Format choisir True Color with Alpha
  • Output format choose C array

lvgl on line image converter esp32 t-watch arduino c file

Convert

Download and open the generated c file. There is nothing to be done, the LVGL image converter has already prepared everything for you! It is preferable to keep one file per image. If necessary, one file per dimension.

Here is the structure of the C file generated by the LVGL online converter

#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,
};

Sample code to display an image with LVGL on a T-Watch

The LilyGoWatch library comes with 3 wallpaper images directly accessible using the constants WALLPAPER_1_IMG , WALLPAPER_2_IMG and WALLPAPER_3_IMG.

The files are in the folder C imgs the source code.

logo github

Displaying an image with LVGL takes a little more work.

/* 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 <Arduino.h>
#include <LilyGoWatch.h>

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

Explanation of C ++ code

LVGL is integrated into the LilyGoWatch library. No need to install it manually in your project. To activate it, just add the LILYGO_WATCH_LVGL constant at the start of the Arduino program.

#define LILYGO_WATCH_LVGL

We start the LVGL library after creating the pointer to the T-Watch object.

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

Before you can use an image, you must declare the C file using the LV_IMG_DECLARE() function.

LV_IMG_DECLARE(WALLPAPER_1_IMG);

We can now create a lv_obj_t object which will contain the image in the current scenelv_scr_act()

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

We specify the image file of this object

lv_img_set_src(img1, &WALLPAPER_1_IMG);

You can optionally apply transformations to the image. Here we reduce the size by 50%

lv_img_set_zoom(img1, 128);

And finally all that remains is to display it at the desired location on the screen. Here in the upper left corner by calling the function lv_obj_align().

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

For the screen to be generated, you must call the lv_task_handler() method. To be able to retrieve the user’s actions on the touch screen, it is preferable to place the supervision in the loop() so that it is called regularly.

void loop() {
  lv_task_handler();
}

Demo with LVGL on a 2020 T-Watch

Render the image mosaic on the T-Watch 2020 with the LVGL library.

Image mosaic with LVGL on T-Watch 2020

Image mosaic with LVGL on T-Watch 2020

All models of T-Watch, expansion boards and accessories

Conclusion

The TFT_eSPI library is super fast to set up. If you are just starting out and color isn’t essential to your application, this is the library for you.

To display images in color, it is preferable to use the LVGL library. It is much more powerful. In the absence of an interface editor, it will take more time to develop skills and develop the interface of your application.

Updates

2020/11/24 How to view and export images in XBM format with GIMP

23/11/2020 Publication of the article

Version française

 

Click to rate this post!
[Total: 0 Average: 0]

Thanks for your reading

Did you like this project ? Don't miss any more projects by subscribing to our weekly newsletter!

Are you having a problem with this topic?

Maybe someone has already found the solution, visit the forum before asking your question
We will be happy to hear your thoughts

      Leave a Reply

      Ads
      Read more
      Recent posts on the Forum
      DIY Projects
      DIY Projects