LVGL with ESP32 Cheap Yellow Display Board (ESP32-2432S028R)

In this guide, you’ll get started with the LVGL (Light and Versatile Graphics Library) with the ESP32 CYD (Cheap Yellow Display ESP32-2432S028R). The LVGL is a popular free and open-source embedded graphics library to create awesome UIs for many microcontrollers and displays. We’ll use an ESP32 development board with a built-in TFT Touchscreen display.

Getting Started with LVGL Graphics Library with ESP32 Cheap Yellow Display CYD Board ESP32-2432S028R

If you have a standalone TFT Touchscreen Display 2.8 inch with ILI9341 driver, you can read our LVGL guide for the ESP32.

Introducing the ESP32 Cheap Yellow Display – CYD (ESP32-2432S028R)

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

This is a very versatile board to build GUIs for your IoT projects and is much more convenient and practical than using a separate ESP32 board with a TFT screen.

ESP32 Cheap Yellow Display CYD Board ESP32-2432S028R back labeled

Related content: Getting Started with ESP32 Cheap Yellow Display Board – CYD (ESP32-2432S028R)

Here’s a list of more detailed specifications of this development board:

  • Dual-core MCU, integrated WI-FI and Bluetooth functions
  • Frequency can reach 240MHz
  • 520KB SRAM, 448KB ROM, Flash size is 4MB
  • Module size 50.0×86.0mm
  • Operating Voltage: 5V
  • Power consumption: approximately 115mA
  • Product weight: approximately 50g
  • The module includes:
    • 2.8-inch color TFT display screen with ILI9341 driver chip
    • Display resolution: 240x320px with resistive touchscreen
    • Backlight control circuit
    • TF card interface for external storage
    • Serial interface
    • Temperature and humidity sensor interface (DHT11 interface) and reserved IO port interface
    • It can be programmed with: Arduino IDE, MicroPython, ESP-IDF

In the Extended GPIO connectors, there are at least 4 GPIOs available: GPIO 35, GPIO 22, GPIO 21, and GPIO 27. It also has the TX/RX pins available (see previous image).

More information about the CYD board GPIOs: ESP32 Cheap Yellow Display (CYD) Pinout (ESP32-2432S028R).

Where to buy?

You can click the link below to check where to buy the ESP32 Cheap Yellow display and its price in different stores.

Introducing LVGL (Light and Versatile Graphics Library)

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).

Logo LVGL Light and Versatile Graphics Library

Here are some of its key features:

  • Blocks: buttons, charts, lists, sliders, images, etc…
  • Advanced graphics with animations, anti-aliasing, opacity, smooth scrolling;
  • Various input devices such as touchpad, mouse, keyboard, encoder, etc…
  • Multi-language support with UTF-8 encoding;
  • Multi-display support, i.e. use multiple TFT, monochrome displays simultaneously;
  • Fully customizable graphic elements with CSS-like styles;
  • Hardware independent: use with any microcontroller or display;
  • Scalable: able to operate with little memory (64 kB Flash, 16 kB RAM);
  • Written in C for maximal compatibility (C++ compatible) and binding to MicroPython.

It also has a wide range of code examples in their documentation that you can use: text, buttons, sliders, input fields, keyboard, custom styling, images, arcs, lines, animations, menus, tabs, layouts, tables, and much more…

LVGL Library Examples

Installing Arduino Libraries

The ESP32 communicates with the TFT Display and Touchscreen using SPI communication protocol. We’ll be using the TFT_eSPI, XPT2046_Touchscreen, and LVGL 9 libraries.

Installing the TFT_eSPI Library

Open your Arduino IDE and go to Sketch Include Library > Manage Libraries. The Library Manager should open. Search for TFT_eSPI. Select the TFT_eSPI library by Bodmer and install it.

Installing TFT_eSPI library Bodmer Arduino IDE 2

Installing the XPT2046_Touchscreen Library

Open your Arduino IDE and go to Sketch Include Library > Manage Libraries. The Library Manager should open. Search for XPT2046_Touchscreen. Select the XPT2046_Touchscreen library by Paul Stoffregen and install it.

Installing XPT2046_Touchscreen Library by Paul Stoffregen Arduino IDE 2

Installing the LVGL 9 Library

Open your Arduino IDE and go to Sketch Include Library > Manage Libraries. The Library Manager should open. Search for LVGL. Select the LVGL library by kiskegabor and install version 9.

Installing LVGL Library 9 by kiskegabor Arduino IDE 2

Prepare Config Files for TFT_eSPI and LVGL Library

To properly use the TFT_eSPI library, you need a configuration file called User_Setup.h with the right definitions. You also need to prepare the lv_conf.h file for the LVGL library. We’ve already prepared these two files so that you don’t have any configuration issues following our examples. You just need to download them and move them to the correct folders. Follow the next instructions to learn how to do it.

a) Preparing the Config Files – Windows PC

b) Preparing the Config Files – Mac OS

a) Preparing the Config Files – Windows PC

Having all the libraries installed (TFT_eSPI, XPT2046_Touchscreen, and LVGL), start by downloading the User_Setup.h configuration file.

User_Setup.h file for TFT_eSPI library Bodmer Arduino IDE 2

In your Arduino IDE, go to File and open the Preferences menu.

Open Arduino IDE 2 Preferences Menu Windows PC

Copy the Arduino IDE “Sketchbook location” path. In my case, it’s:

C:\Users\rui_s\Documents\Arduino
Open Sketchbook Location Arduino IDE 2 Browse

Then, in your Windows PC File Explorer tab enter the sketchbook location path to open the Arduino folder (it’s usually under the Documents folder).

Browse to Arduino libraries folder to copy lv_conf.h file Arduino IDE 2

Open the libraries folder:

Open the Arduino IDE 2 Libraries Folder Windows PC

You should see the TFT_eSPI library folder there. Open it.

Open TFT_eSPI folder libraries Arduino IDE 2

You should be in a similar folder path as shown below:

C:\Users\rui_s\Documents\Arduino\libraries\TFT_eSPI

Copy the User_Setup.h file provided earlier and replace the existing file.

Move copy User_Setup.h file to TFT_eSPI library folder Arduino IDE 2

Then, download the lv_conf.h configuration file.

lv_config.h file for LVGL library by Kisvegabor Arduino IDE 2

Open a similar folder path in your computer as shown below:

C:\Users\rui_s\Documents\Arduino\libraries

Move the lv_conf.h to the libraries folder (do NOT move it inside the lvgl folder).

Move copy lv_conf.h configuration file to Arduino libraries folder Arduino IDE 2

Then, open the lvgl folder. Move the demos and examples folder to the src folder as illustrated in the image below:

move examples demos to source LVGL library Arduino IDE 2

The demos and examples folder should be inside the src folder:

moved examples demos folders to src folder LVGL library Arduino IDE 2

IMPORTANT: other User_Setup.h and lv_conf.h files available on the internet will probably NOT work with the examples available at Random Nerd Tutorials. You must use the exact files provided in this article.

Note: if you update your libraries, you’ll need to do this procedure again and place the right configuration files in the right places.

b) Preparing the Config Files – Mac OS

Having both libraries installed (TFT_eSPI and XPT2046_Touchscreen), download the User_Setup.h configuration file.

Download User_Setup.h file for TFT_eSPI library Bodmer Arduino IDE 2

In your Arduino IDE, open the Settings menu.

Open Arduino IDE 2 Preferences Menu Mac OS

Copy the Arduino IDE “Sketchbook location” path. In my case, it’s:

/Users/rui/Documents/Arduino
Open Sketchbook Location Arduino IDE 2 Browse Mac OS

In Finder, type ~/.arduinoIDE/ and open that directory.

Open the Arduino IDE 2 Folder Mac OS

Open the libraries folder.

Open the Arduino IDE 2 Libraries Folder Mac OS

You should see the TFT_eSPI library folder there. Open it.

Open TFT_eSPI folder libraries Arduino IDE 2 Mac OS

You should be in a similar folder path as shown below:

/Users/rui/Documents/Arduino/libraries/TFT_eSPI

Copy the User_Setup.h file provided earlier and replace the existing file.

Move copy User_Setup.h file to TFT_eSPI library folder Arduino IDE 2 Mac OS

You should now have the User_Setup.h file provided on that path.

Move User_Setup.h file to TFT_eSPI library folder Arduino IDE 2 Mac OS

Then, download the lv_conf.h configuration file.

Download lv_config.h file for LVGL library by Kisvegabor Arduino IDE 2

Open a similar folder path in your computer as shown below:

/Users/rui/Documents/Arduino/libraries

Move the lv_conf.h to the libraries folder (do NOT move it inside the lvgl folder).

Open the Arduino IDE 2 Libraries Folder Mac OS

Then, open the lvgl folder. Move the demos and examples folder to the src folder as illustrated in the image below:

Move copy lv_conf.h configuration file to Arduino libraries folder Arduino IDE 2 Mac OS

Then, open the lvgl folder. Move the demos and examples folder to the src folder as illustrated in the image below:

move examples demos to the src LVGL library Arduino IDE 2

The demos and examples folder should be inside the src folder:

Moved examples demos folders to src folder LVGL library Arduino IDE 2 Mac OS

IMPORTANT: other User_Setup.h and lv_conf.h files available on the internet will probably NOT work with the examples available at Random Nerd Tutorials. You must use the exact files provided in this article.

Note: if you update your libraries, you’ll need to do this procedure again and place the right configuration files in the right places.

Code – Display Text, Create Buttons and Slider

The following code displays a simple text in your TFT display and allows you to test the touchscreen using a button and slider. When you press the buttons or slider, it should trigger some events.

Copy the following code to the Arduino IDE and upload it to your board.

/*  Rui Santos & Sara Santos - Random Nerd Tutorials
    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.X 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>

// Install the "XPT2046_Touchscreen" library by Paul Stoffregen to use the Touchscreen - https://github.com/PaulStoffregen/XPT2046_Touchscreen - Note: this library doesn't require further configuration
#include <XPT2046_Touchscreen.h>

// Touchscreen pins
#define XPT2046_IRQ 36   // T_IRQ
#define XPT2046_MOSI 32  // T_DIN
#define XPT2046_MISO 39  // T_OUT
#define XPT2046_CLK 25   // T_CLK
#define XPT2046_CS 33    // T_CS

SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

// Touchscreen coordinates: (x, y) and pressure (z)
int x, y, z;

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

// Get the Touchscreen data
void touchscreen_read(lv_indev_t * indev, lv_indev_data_t * data) {
  // Checks if Touchscreen was touched, and prints X, Y and Pressure (Z)
  if(touchscreen.tirqTouched() && touchscreen.touched()) {
    // Get Touchscreen points
    TS_Point p = touchscreen.getPoint();
    // Calibrate Touchscreen points with map function to the correct width and height
    x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
    y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
    z = p.z;

    data->state = LV_INDEV_STATE_PRESSED;

    // Set the coordinates
    data->point.x = x;
    data->point.y = y;

    // Print Touchscreen info about X, Y and Pressure (Z) on the Serial Monitor
    /* Serial.print("X = ");
    Serial.print(x);
    Serial.print(" | Y = ");
    Serial.print(y);
    Serial.print(" | Pressure = ");
    Serial.print(z);
    Serial.println();*/
  }
  else {
    data->state = LV_INDEV_STATE_RELEASED;
  }
}

int btn1_count = 0;
// Callback that is triggered when btn1 is clicked
static void event_handler_btn1(lv_event_t * e) {
  lv_event_code_t code = lv_event_get_code(e);
  if(code == LV_EVENT_CLICKED) {
    btn1_count++;
    LV_LOG_USER("Button clicked %d", (int)btn1_count);
  }
}

// Callback that is triggered when btn2 is clicked/toggled
static void event_handler_btn2(lv_event_t * e) {
  lv_event_code_t code = lv_event_get_code(e);
  lv_obj_t * obj = (lv_obj_t*) lv_event_get_target(e);
  if(code == LV_EVENT_VALUE_CHANGED) {
    LV_UNUSED(obj);
    LV_LOG_USER("Toggled %s", lv_obj_has_state(obj, LV_STATE_CHECKED) ? "on" : "off");
  }
}

static lv_obj_t * slider_label;
// Callback that prints the current slider value on the TFT display and Serial Monitor for debugging purposes
static void slider_event_callback(lv_event_t * e) {
  lv_obj_t * slider = (lv_obj_t*) lv_event_get_target(e);
  char buf[8];
  lv_snprintf(buf, sizeof(buf), "%d%%", (int)lv_slider_get_value(slider));
  lv_label_set_text(slider_label, buf);
  lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
  LV_LOG_USER("Slider changed to %d%%", (int)lv_slider_get_value(slider));
}

void lv_create_main_gui(void) {
  // Create a text label aligned center on top ("Hello, world!")
  lv_obj_t * text_label = lv_label_create(lv_screen_active());
  lv_label_set_long_mode(text_label, LV_LABEL_LONG_WRAP);    // Breaks the long lines
  lv_label_set_text(text_label, "Hello, world!");
  lv_obj_set_width(text_label, 150);    // Set smaller width to make the lines wrap
  lv_obj_set_style_text_align(text_label, LV_TEXT_ALIGN_CENTER, 0);
  lv_obj_align(text_label, LV_ALIGN_CENTER, 0, -90);

  lv_obj_t * btn_label;
  // Create a Button (btn1)
  lv_obj_t * btn1 = lv_button_create(lv_screen_active());
  lv_obj_add_event_cb(btn1, event_handler_btn1, LV_EVENT_ALL, NULL);
  lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -50);
  lv_obj_remove_flag(btn1, LV_OBJ_FLAG_PRESS_LOCK);

  btn_label = lv_label_create(btn1);
  lv_label_set_text(btn_label, "Button");
  lv_obj_center(btn_label);

  // Create a Toggle button (btn2)
  lv_obj_t * btn2 = lv_button_create(lv_screen_active());
  lv_obj_add_event_cb(btn2, event_handler_btn2, LV_EVENT_ALL, NULL);
  lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 10);
  lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
  lv_obj_set_height(btn2, LV_SIZE_CONTENT);

  btn_label = lv_label_create(btn2);
  lv_label_set_text(btn_label, "Toggle");
  lv_obj_center(btn_label);
  
  // Create a slider aligned in the center bottom of the TFT display
  lv_obj_t * slider = lv_slider_create(lv_screen_active());
  lv_obj_align(slider, LV_ALIGN_CENTER, 0, 60);
  lv_obj_add_event_cb(slider, slider_event_callback, LV_EVENT_VALUE_CHANGED, NULL);
  lv_slider_set_range(slider, 0, 100);
  lv_obj_set_style_anim_duration(slider, 2000, 0);

  // Create a label below the slider to display the current slider value
  slider_label = lv_label_create(lv_screen_active());
  lv_label_set_text(slider_label, "0%");
  lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
}

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

  // Start the SPI for the touchscreen and init the touchscreen
  touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
  touchscreen.begin(touchscreenSPI);
  // Set the Touchscreen rotation in landscape mode
  // Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 1: touchscreen.setRotation(1);
  touchscreen.setRotation(3);

  // 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));
  
  // Initialize an LVGL input device object (Touchscreen)
  lv_indev_t * indev = lv_indev_create();
  lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
  // Set the callback function to read Touchscreen input
  lv_indev_set_read_cb(indev, touchscreen_read);

  // Function to draw the GUI (text, buttons and sliders)
  lv_create_main_gui();
}

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 the Code Works

Let’s take a quick look at the parts of the code that are relevant to this example.

Libraries

Include the lvgl, TFT_eSPI and XPT2046_Touchscreen libraries.

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

Initialize Touchscreen

The following lines set the touchscreen pinout:

#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33

Create a touchscreenSPI and touchscreen instances:

SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);

Other Variables

Set the screen width and screen height:

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define FONT_SIZE 2

Variables to store the coordinates: (x, y) and pressure (z).

int x, y, z;

setup()

Start a serial communication with the Serial Monitor at a baud rate of 115200 and print the LVGL library version that you are using:

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

Start the LVGL and assign a callback function for debugging purposes.

lv_init();
lv_log_register_print_cb(log_print);

Start the SPI for the touchscreen and initialize the touchscreen.

touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreenSPI);
touchscreen.setRotation(3);

Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 1: touchscreen.setRotation(1);

Create the display object and initialize the TFT display using the TFT_eSPI library.

lv_display_t * disp;
disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));

Initialize an LVGL input device object (touchscreen) and set the callback function that will be triggered when you click the touchscreen.

lv_indev_t * indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, touchscreen_read);

Finally, call the lv_create_main_gui() function to draw the GUI for your touchscreen:

lv_create_main_gui();

loop()

When running an LVGL example, the loop() will usually look like this:

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

lv_create_main_gui()

The lv_create_main_gui() function draws the text label, buttons, and sliders. It’s also where you assign the event handler callback functions that will be triggered when you interact with your display.

void lv_create_main_gui(void) {
  // Create a text label aligned center on top ("Hello, world!")
  lv_obj_t * text_label = lv_label_create(lv_screen_active());
  lv_label_set_long_mode(text_label, LV_LABEL_LONG_WRAP);    // Breaks the long lines
  lv_label_set_text(text_label, "Hello, world!");
  lv_obj_set_width(text_label, 150);    // Set smaller width to make the lines wrap
  lv_obj_set_style_text_align(text_label, LV_TEXT_ALIGN_CENTER, 0);
  lv_obj_align(text_label, LV_ALIGN_CENTER, 0, -90);

  lv_obj_t * btn_label;
  // Create a Button (btn1)
  lv_obj_t * btn1 = lv_button_create(lv_screen_active());
  lv_obj_add_event_cb(btn1, event_handler_btn1, LV_EVENT_ALL, NULL);
  lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -50);
  lv_obj_remove_flag(btn1, LV_OBJ_FLAG_PRESS_LOCK);

  btn_label = lv_label_create(btn1);
  lv_label_set_text(btn_label, "Button");
  lv_obj_center(btn_label);

  // Create a Toggle button (btn2)
  lv_obj_t * btn2 = lv_button_create(lv_screen_active());
  lv_obj_add_event_cb(btn2, event_handler_btn2, LV_EVENT_ALL, NULL);
  lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 10);
  lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
  lv_obj_set_height(btn2, LV_SIZE_CONTENT);

  btn_label = lv_label_create(btn2);
  lv_label_set_text(btn_label, "Toggle");
  lv_obj_center(btn_label);
  
  // Create a slider aligned in the center bottom of the TFT display
  lv_obj_t * slider = lv_slider_create(lv_screen_active());
  lv_obj_align(slider, LV_ALIGN_CENTER, 0, 60);
  lv_obj_add_event_cb(slider, slider_event_callback, LV_EVENT_VALUE_CHANGED, NULL);
  lv_slider_set_range(slider, 0, 100);
  lv_obj_set_style_anim_duration(slider, 2000, 0);

  // Create a label below the slider to display the current slider value
  slider_label = lv_label_create(lv_screen_active());
  lv_label_set_text(slider_label, "0%");
  lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
}

event_handler_btn1()

The event_handler_btn1() function is triggered when you click the “Button” and it will also display in the Arduino IDE Serial Monitor the number of times the button has been clicked.

int btn1_count = 0;
// Callback that is triggered when btn1 is clicked
static void event_handler_btn1(lv_event_t * e) {
  lv_event_code_t code = lv_event_get_code(e);
  if(code == LV_EVENT_CLICKED) {
    btn1_count++;
    LV_LOG_USER("Button clicked %d%", (int)btn1_count);
  }
}

event_handler_btn2()

The event_handler_btn2() is triggered when you click the “Toggle” button, it stores the current button state and prints a message with the current state in the Serial Monitor.

// Callback that is triggered when btn2 is clicked/toggled
static void event_handler_btn2(lv_event_t * e) {
  lv_event_code_t code = lv_event_get_code(e);
  lv_obj_t * obj = (lv_obj_t*) lv_event_get_target(e);
  if(code == LV_EVENT_VALUE_CHANGED) {
    LV_UNUSED(obj);
    LV_LOG_USER("Toggled %s", lv_obj_has_state(obj, LV_STATE_CHECKED) ? "on" : "off");
  }
}

slider_event_callback()

The slider_event_callback() function is called when you move the slider, it will also display both in the touchscreen and Serial Monitor the latest slider value on a scale from 0 to 100%.

static lv_obj_t * slider_label;
// Callback that prints the current slider value on the TFT display and Serial Monitor for debugging purposes
static void slider_event_callback(lv_event_t * e) {
  lv_obj_t * slider = (lv_obj_t*) lv_event_get_target(e);
  char buf[8];
  lv_snprintf(buf, sizeof(buf), "%d%%", (int)lv_slider_get_value(slider));
  lv_label_set_text(slider_label, buf);
  lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
  LV_LOG_USER("Slider changed to %d%%", (int)lv_slider_get_value(slider));
}

Demonstration

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 uploading the code to your board, it should display the sample “Hello, world!” text centered at the top.

Press with your finger the first “Button”. In the Serial Monitor, it will print the coordinates of your button press, as well as a message with the number of times the button has been clicked: “Button clicked 1”, “Button clicked 2”, “Button clicked 3″…

ESP32 Cheap Yellow Display Board CYD TFT Display Touchscreen LVGL Library Example

Then, click the “Toggle” button, this could be used to control an LED or any other output. This button type has two states (color blue when off and color red when on).

ESP32 Cheap Yellow Display Board CYD TFT Display Touchscreen LVGL Library Example Button Toggle

Drag the slider with your finger, you’ll see the slider moving and the slider label changing when you move your finger.

ESP32 Cheap Yellow Display Board CYD TFT Display Touchscreen LVGL Library Example Move Slider

All the events will be printed in your Arduino IDE Serial Monitor for debugging purposes:

ESP32 TFT Touchscreen Display LVGL Basic Example Text Label Button Slider

Wrapping Up

In this tutorial, you learned to get started with the LVGL library with the ESP32 Cheap Yellow Display board. In the next guides, we’ll explore other features and components of the LVGL library.

We hope you found this tutorial useful. We’re preparing more guides about this board, so stay tuned.

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!

32 thoughts on “LVGL with ESP32 Cheap Yellow Display Board (ESP32-2432S028R)”

  1. As Always your Examples are great,
    I have the 2USB version of the CYD which has the st7789 screen, as I left my own User_setup file in TFT_espi library vut used your lv_conf the sketch ran very well.
    The only issue I have is the mapping for touch is alittle off and the buttons click and toggle slighty off to the lh side of button and will not toggle on the rh side for about the last 10% of the button and slider.
    DO I have to adjust the touch mapping and if so which parts do i need to touch or is this contained within LVGL as I am unfamiliar with this library?
    Many thanks as always a great tutorial
    Mark D

    Reply
  2. Suprisingly I went into your lc conf file and changed it to use the st7789 screen and it made no difference to colour brightness or function. all seemed well

    Reply
    • Wow used a bit of intellect and used the serial print inside the sketch and got it to print p.y and px and then adjusted the mapping now buttons and slider in perfect touchscreen harmony.
      if anyone else uses a CYD2USB, and finds mapping alittle off try these.
      x = map(p.x, 330, 3868, 1, SCREEN_WIDTH); //was 200 3700
      y = map(p.y, 280, 3850, 1, SCREEN_HEIGHT); // was 240 3800

      Reply
  3. Hello Rui and Sara.
    I can use the touchscreen.setRotation(3); change to touchscreen.setRotation(1); But the screen then remains upside down. The slider and buttons also no longer work. greetings from old man Bert

    Reply
  4. Hi Bert you have only rotated the touchscreen not the display.
    Try tft.setRotation(3); or (1) to Match your screen rotation and your touch rotation
    Regards
    Mark D

    Reply
      • Hi Bert,
        You need to create a tft object first. Insert this line somewhere at the top of your sketch (after the #include statements):

        TFT_eSPI tft = TFT_eSPI();

        Now your
        tft.setRotation(1);
        will compile.

        The problem is that it does not help!
        Regardless of the value used for tft.setRotation() (I’ve tried all four), the graphics on the screen does not rotate.

        I’ve also tried an LVGL function

        lv_disp_set_rotation(disp,LV_DISP_ROTATION_x); // replace x with 0, 90, 180 or 270

        to no avail.

        Reply
  5. You are awesome!

    I spent the whole weekend a couple of weeks ago and ran into some other problem with LVGL. I got this email today, followed these steps, and it worked in 5 minutes. Can’t thank you enough for this.

    Reply
    • Hi.
      Yes, you can use I2C on GPIOs 22 and 27 using the extended IO socket.
      I haven’t tested powering the display with batteries. The only input is via the microUSB socket.
      Regards,
      Sara

      Reply
  6. Thanks for this userfull procedure.

    Do you have a piece of code for a button onclick event with an http request ?

    Merci,
    marc,

    Reply
  7. Hi you
    Thank you very much for this great tutorial. I will study it more in depth later. RNT is a very valuable maker source for me, even if I search for information later. Your examples are often a good starting point, as they always work.
    Will this tutorial be part of a ebook?

    Reply
    • Hi.
      We’re currently working on an eBook about this subject.
      It will include many practical examples.
      Regards,
      Sara

      Reply
  8. Great guide, sharing a fantastic community on CYD over here on Brian Lough’s Discord channel discord.gg/998cVD53

    Reply
    • Hi.
      No.
      Unfortunately, at the moment, we don’t have any examples for MicroPython with the display.
      Regards,
      Sara

      Reply
  9. Congratulation Rui & Sara !
    It works !
    But colors are inverted on mine (buttons are yellow and the back is black) How to invert video ?
    Thanks
    JMH

    Reply
  10. hello,
    It works but I need a piece of code for the action when we click on the button.
    Do you have a exemple ?
    Thanks,

    Reply
    • Hi.
      You can had the action you want inside the buttons callback functions.
      For example, for button 1, it’s the event_handler_btn1() function. Inside add any actions you want to perform when you click the button.
      Regards,
      Sara

      Reply
  11. Hello, thank you very much for the tutorial. How do I include the code of other lvgl elements?
    it’s possible?

    Reply
  12. I had previously tried fighting my way through confusing instructions from various sources, setting up lvgl.h, the Touch SPI, the various libraries, moving some folders into other folders for unknown reasons, etc. This took days, and worse, it didn’t work, so this is All Good Stuff. Thank you.

    Reply
  13. Hey, First of all thanks for the great tutorials (esp-now really helped me out), Now I am trying to get a LVGL GUI for my project but I get this error all the time.
    line 199:
    lv_display_t * disp;
    disp = lv_tft_espi_create(TFT_HOR_RES, TFT_VER_RES, draw_buf, DRAW_BUF_SIZE);

    screen.cpp:199:10: error: ‘lv_tft_espi_create’ was not declared in this scope; did you mean ‘lv_tlsf_create’?
    199 | disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));

    I have tried several other lvgl projects, made sure I have the same versions but to no help.

    Reply
    • Hi.
      Please double-check the instructions to install the libraries and preparing the configuration files.
      Make sure you’re placing everything in the right places and that you’re using the recommended library versions.
      Regards,
      Sara

      Reply
  14. Hey, sorry if this post comes twice.

    But I am having a problem with this code, the error I get is this:
    error: ‘lv_tft_espi_create’ was not declared in this scope; did you mean ‘lv_tlsf_create’?
    178 | disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));

    And i just copy your code, can you help me?
    ps. thanks for great tutorial, especially the esp-now was super helpful to me

    Reply
  15. Fantastic resource, as always. Thanks again for helping us beginners! Do you intend to create a tutorial on using the keyboard widget too? This really intrigues me and I would like to use it, but alas… my knowledge is not complete enough.

    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.