ESP32 CYD with LVGL: Display Image on the Screen

In this guide, you’ll learn how to process and load an image to be displayed on the ESP32 Cheap Yellow Display (CYD) board using LVGL (Light Versatile Graphics Library). The ESP32 will be programmed using Arduino IDE.

Select ESP32 CYD with LVGL: Display Image on the Screen

New to the ESP32 Cheap Yellow Display? Start here: Getting Started with ESP32 Cheap Yellow Display Board – CYD (ESP32-2432S028R).

Project Overview

In this project, you’ll learn how to display images on the CYD using LVGL. We’ll create an example to display the image of a cat as shown in the picture below, but you can modify it to load any other image.

ESP32 CYD with LVGL Display Image on the screen demonstration

Prerequisites

Before proceeding, make sure you follow the next prerequisites. You must follow all steps, otherwise, your project will not work.

1) Parts Required

For this project, you need the following parts

2) Install ESP32 Boards in Arduino IDE

Arduino IDE 2 Logo

We’ll program the ESP32 using Arduino IDE. Make sure you have the ESP32 boards installed. Follow the next tutorial:

3) Get familiar with the ESP32 Cheap Yellow Display

The ESP32-2432S028R development board has become known in the maker community as the “Cheap Yellow Display” or CYD for short. This development board, whose main chip is an ESP32-WROOM-32 module, comes with a 2.8-inch TFT touchscreen LCD, a microSD card interface, an RGB LED, and all the required circuitry to program and apply power to the board.

ESP32 Cheap Yellow Display CYD Board ESP32-2432S028R front

If this is your first time using the ESP32 Cheap Yellow Display, make sure to follow our getting started guide:

4) Install TFT and LVGL Libraries

LVGL (Light and Versatile Graphics Library) is a free and open-source graphics library that provides a wide range of easy-to-use graphical elements for your microcontroller projects that require a graphical user interface (GUI).

LVGL new logo

Follow the next tutorial to install and configure the required libraries to use LVGL for the ESP32 Cheap Yellow Display using Arduino IDE.

Preparing the Image File – image.h

To load a custom image using LVGL, you need to create and configure a file called image.h that must be placed inside the sketch folder. We already prepared that file for you. If you want to load a custom image, you just need to replace the image uint8_t map and the image size.

You can use our sample image or follow the next steps to prepare a custom image:

1. Go to the LVGL Image Converter Website: lvgl.io/tools/imageconverter

2. Select the following options highlighted in the image below:

  • Version: LVGL v9
  • Select an image from your computer
  • Color format: ARGB8888
  • Then, click the Convert button
LVGL Image Converter website LVGL.io v9 ARGB8888

3. It will download a file with the name of your picture but with the .c extension. Open that file and copy the code between brackets { } with many hexadecimal characters.

4. Create a new file called image.h.

5. Copy the code in this repository and paste it into the new image.h file that you’ve just created.

6. Replace the content of the my_image_map variable with the content you’ve just copied in step 3. (Don’t change the name of the my_image_map variable, only copy and replace what is between brackets.

#ifdef __has_include
    #if __has_include("lvgl.h")
        #ifndef LV_LVGL_H_INCLUDE_SIMPLE
            #define LV_LVGL_H_INCLUDE_SIMPLE
        #endif
    #endif
#endif

#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
    #include "lvgl.h"
#else
    #include "lvgl/lvgl.h"
#endif

#define LV_BIG_ENDIAN_SYSTEM

#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif

#ifndef LV_ATTRIBUTE_IMG_MY_IMAGE
#define LV_ATTRIBUTE_IMG_MY_IMAGE
#endif

const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_MY_IMAGE uint8_t my_image_map[] = {REPLACE_WITH_YOUR__IMAGE_ATTRIBUTE};

const lv_image_dsc_t my_image = {
    .header = {
        .magic = LV_IMAGE_HEADER_MAGIC,
        .cf = LV_COLOR_FORMAT_ARGB8888,
        .flags = 0,          
        .w = 128,
        .h = 128,
        //.stride = 120,
        .reserved_2 = 0
    },
    .data_size = sizeof(my_image_map),
    .data = my_image_map,
    .reserved = NULL
};

7. Scroll down on that file and change the following lines with your image width .w and height .h:

const lv_image_dsc_t my_image = {
    .header = {
        .magic = LV_IMAGE_HEADER_MAGIC,
        .cf = LV_COLOR_FORMAT_ARGB8888,
        .flags = 0,          
        .w = 128,
        .h = 128,
        //.stride = 120,
        .reserved_2 = 0
    },
    .data_size = sizeof(my_image_map),
    .data = my_image_map,
    .reserved = NULL
};

8. Finally, save your image.h file.

Important: the image.h file should be placed next to the .ino file in the sketch folder of your project.

LVGL Load Image Arduino Code example folder

Your Arduino IDE should have two tabs:

Arduino IDE tabs LVGL Load Image Arduino Code example folder

ESP32 CYD: Load and Display Image – Arduino Code

The following code will display an image on the screen. You must have prepared the image.h previously with the image of your choice. Alternatively, you can use our default image.h file. This file should be placed in the sketch folder next to the .ino file.

/*  Rui Santos & Sara Santos - Random Nerd Tutorials - https://RandomNerdTutorials.com/esp32-cyd-lvgl-display-image/
    THIS EXAMPLE WAS TESTED WITH THE FOLLOWING HARDWARE:
    1) ESP32-2432S028R 2.8 inch 240Ă—320 also known as the Cheap Yellow Display (CYD): https://makeradvisor.com/tools/cyd-cheap-yellow-display-esp32-2432s028r/
      SET UP INSTRUCTIONS: https://RandomNerdTutorials.com/cyd-lvgl/
    2) REGULAR ESP32 Dev Board + 2.8 inch 240x320 TFT Display: https://makeradvisor.com/tools/2-8-inch-ili9341-tft-240x320/ and https://makeradvisor.com/tools/esp32-dev-board-wi-fi-bluetooth/
      SET UP INSTRUCTIONS: https://RandomNerdTutorials.com/esp32-tft-lvgl/
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

/*  Install the "lvgl" library version 9.2 by kisvegabor to interface with the TFT Display - https://lvgl.io/
    *** IMPORTANT: lv_conf.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials ***
    *** YOU MUST USE THE lv_conf.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***
    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https://RandomNerdTutorials.com/cyd-lvgl/ or https://RandomNerdTutorials.com/esp32-tft-lvgl/   */
#include <lvgl.h>

/*  Install the "TFT_eSPI" library by Bodmer to interface with the TFT Display - https://github.com/Bodmer/TFT_eSPI
    *** IMPORTANT: User_Setup.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials ***
    *** YOU MUST USE THE User_Setup.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***
    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https://RandomNerdTutorials.com/cyd-lvgl/ or https://RandomNerdTutorials.com/esp32-tft-lvgl/   */
#include <TFT_eSPI.h>

#include <image.h>

#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320

#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];

// If logging is enabled, it will inform the user about what is happening in the library
void log_print(lv_log_level_t level, const char * buf) {
  LV_UNUSED(level);
  Serial.println(buf);
  Serial.flush();
}

void draw_image(void) {
  LV_IMAGE_DECLARE(my_image);
  lv_obj_t * img1 = lv_image_create(lv_screen_active());
  lv_image_set_src(img1, &my_image);
  lv_obj_align(img1, LV_ALIGN_CENTER, 0, 0);
}

void setup() {
  String LVGL_Arduino = String("LVGL Library Version: ") + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
  Serial.begin(115200);
  Serial.println(LVGL_Arduino);
  
  // Start LVGL
  lv_init();
  // Register print function for debugging
  lv_log_register_print_cb(log_print);

  // Create a display object
  lv_display_t * disp;
  // Initialize the TFT display using the TFT_eSPI library
  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
    
  // Function to draw the image
  draw_image();
}

void loop() {
  lv_task_handler();  // let the GUI do its work
  lv_tick_inc(5);     // tell LVGL how much time has passed
  delay(5);           // let this time pass
}

View raw code

How Does the Code Work?

Let’s take a look at how to display an image on the CYD screen. Alternatively, you can skip to the Demonstration section.

Including Libraries

You need to include the lvgl.h and the TFT_eSPI.h libraries to communicate and display text on the screen.

#include <lvgl.h>
#include <TFT_eSPI.h>

Include the image.h file that contains all the information to draw the image.

#include <image.h>

setup()

In the setup(), include the following lines for debugging. These will print the version of LVGL that you’re using. You must be using version 9.

String LVGL_Arduino = String("LVGL Library Version: ") + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.begin(115200);
Serial.println(LVGL_Arduino);

Initialize the LVGL Library

Initialize the LVGL Library by calling the lv_init() function in the setup().

// Start LVGL
lv_init();

Register Debugging Function

Register your log_print() function declared previously as a function associated with debugging LVGL.

// Register print function for debugging
lv_log_register_print_cb(log_print);

Create a Display Object

To write to the display, you must create a display object first. You need to do this in all your LVGL sketches. The following lines will create an LVGL display object called disp with the screen width, screen height, and drawing buffer defined earlier.

// Create a display object
lv_display_t * disp;
// Initialize the TFT display using the TFT_eSPI library
disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);

Drawing the image

The LVGL library works asynchronously. You must call the function draw_image() to draw on the display in the setup(). Then, everything works with events and callbacks. The code will always be listening for events in the background. When something happens, it will run the callback function associated with the event. You don’t need to check for any events in the loop().

draw_image();

draw_image()

We create a function called draw_image() that is called on the setup().

void draw_image(void) {
  LV_IMAGE_DECLARE(my_image);
  lv_obj_t * img1 = lv_image_create(lv_screen_active());
  lv_image_set_src(img1, &my_image);
  lv_obj_align(img1, LV_ALIGN_CENTER, 0, 0);
}

Start by declaring your image using the following line (my_image is declared on the image.h file).

LV_IMAGE_DECLARE(my_image);

Then, create an image LVGL object on the current screen called img1 using the lv_image_create() function.

lv_obj_t * img1 = lv_image_create(lv_screen_active());

Set the image source using the lv_image_set_src() function. Pass as argument the LVGL image object and the image source.

lv_image_set_src(img1, &my_image);

Align the image at the center of the screen.

lv_obj_align(img1, LV_ALIGN_CENTER, 0, 0);

loop()

In the loop(), you can add any other tasks that you need your ESP32 to do like in any regular Arduino sketch. In our case, we won’t add any tasks to the loop(), but to keep LVGL running and detecting events, you always need to add the following lines to your loop().

void loop() {
  lv_task_handler();   // let the GUI do its work
  lv_tick_inc(5);         // tell LVGL how much time has passed
  delay(5);                 // let this time pass
}

Demonstration

Your image.h file should be next to your .ino file. The image.h file will automatically be uploaded to the board when you upload the code.

After having both files prepared, upload the code to your board. Go to Tools > Board and select ESP32 > ESP32 Dev Module. Then, select the right COM port in Tools > Port. Finally, click the upload button.

Arduino IDE 2 Upload Button

After a few seconds, the screen will display the image as shown in the picture below.

ESP32 CYD board with LVGL Display Image on the screen

Wrapping Up

In this tutorial, you learned to display images on the Cheap Yellow Display (CYD) board using the LVGL library.

We hope you found this tutorial useful. We’re preparing more guides about this board, so stay tuned. If you would like to learn more about creating graphical user interfaces using the LVGL library with the ESP32, check out our latest eBook:

Other guides you might like reading:

To learn more about the ESP32, make sure to take a look at our resources:



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

19 thoughts on “ESP32 CYD with LVGL: Display Image on the Screen”

  1. As far as I can see I did everything right. Still I get this message ” image.h: No such file or directory ” while the file is there. Can this be explained?

    Reply
  2. Huh, I am by no means a LVGL expert but I just defined a static image like below. I’m wondering if you could just stick it in the wew folder on the HA server and point to the internal http address to the image.

    image:
    – file: esphome.io/_static/favicon-512×512.png
    id: boot_logo
    resize: 100×100
    type: RGB565
    use_transparency: true

    Reply
  3. Hello
    I downloaded user_setup.h and lv_conf.h form your site.
    Replaced user_setup.h in TFT_eSPI and cpoied lv_conf.h in Libraries.

    I keep getting this error code (line 53):
    Compilation error: ‘lv_log_register_print_cb’ was not declared in this scope
    What do I do wrong?

    Regards,
    Walter

    Reply
    • Are you using Arduino IDE or VS Code?
      It seems the code cannot find the lv_log_register_print_cb() function. Try moving that function definition to the beginning of the code.
      Regards,
      Sara

      Reply
      • Hello
        Thank you for your response.

        I use Arduino IDE
        Tried what you advised but no result.
        Complete erro messag is:’

        C:\Users\walte\OneDrive\Documenten\Arduino\SantosCYDImage\RuiTestCYDImage\RuiTestCYDImage.ino: In function ‘void setup()’:
        C:\Users\walte\OneDrive\Documenten\Arduino\SantosCYDImage\RuiTestCYDImage\RuiTestCYDImage.ino:56:3: error: ‘lv_log_register_print_cb’ was not declared in this scope
        56 | lv_log_register_print_cb(log_print);
        | ^~~~~~~~~~~~~~~~~~~~~~~~
        C:\Users\walte\OneDrive\Documenten\Arduino\SantosCYDImage\RuiTestCYDImage\RuiTestCYDImage.ino:61:10: error: ‘lv_tft_espi_create’ was not declared in this scope; did you mean ‘lv_tlsf_create’?
        61 | disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
        | ^~~~~~~~~~~~~~~~~~
        | lv_tlsf_create

        exit status 1

        Compilation error: ‘lv_log_register_print_cb’ was not declared in this scope

        regards,
        Walter

        Reply
        • Hi.
          I think there’s something wrong with the set up of your LVGL library.
          Please double-check the instructions.
          Regards,
          Sara

          Reply
          • Hello
            Thank you for your response.
            You where right.
            I found 2 old files lv_conf.h in wrong maps.
            Cat is showing now.

            regards,
            Walter

  4. Hello
    I got an update to LVGL 9.2.0
    Now uploading goes wrong.

    c:\Users\walte\OneDrive\Documenten\Arduino\libraries\lvgl\src\examples\styles\lv_example_style_14.c:1:10: fatal error: ../../src/themes/lv_theme_private.h: No such file or directory
    1 | #include “../../src/themes/lv_theme_private.h”
    | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    compilation terminated.
    exit status 1

    LVGL 9.1.0 still works fine.

    Regards,
    Walter

    Reply
    • Hello
      Correction:
      LVGL 9.2.0 compiles but the graphics are distorted.
      Knowing that I’m only a hobby programmer I carefully read the instructions and
      tried it on 2 computers and also with your “Buttons and Slider” file.
      Same result.
      Version 9.1.0 works fine for me.

      BTW , for a hobby programmer your site is great.

      Regards

      Reply
  5. Great tutorial, thanks.
    Will you be doing a tutorial to explain how to display an image stored on an SD card as a .h image or even better a standard png?

    Reply
  6. Don’t forget to adjust the picture width and height in the Image File – image.h

    const lv_image_dsc_t my_image = { ////// SET WIDTH .w AND HEIGHT .h
    .header = {
    .magic = LV_IMAGE_HEADER_MAGIC,
    .cf = LV_COLOR_FORMAT_ARGB8888,
    .flags = 0,
    .w = 167,
    .h = 169,
    //.stride = 120,
    .reserved_2 = 0
    },
    .data_size = sizeof(my_image_map),
    .data = my_image_map,
    .reserved = NULL
    };

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.