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.
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.
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.
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).
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…
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 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 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.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.
- Click here to download .zip folder with the User_Setup.h config file (view raw file)
In your Arduino IDE, go to File and open the Preferences menu.
Copy the Arduino IDE “Sketchbook location” path. In my case, it’s:
C:\Users\rui_s\Documents\Arduino
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).
Open the libraries folder:
You should see the TFT_eSPI library folder there. Open it.
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.
Then, download the lv_conf.h configuration file.
- Click here to download .zip folder with the lv_conf.h config file (view raw file)
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).
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.
- Click here to download .zip folder with the User_Setup.h config file (view raw file)
In your Arduino IDE, open the Settings menu.
Copy the Arduino IDE “Sketchbook location” path. In my case, it’s:
/Users/rui/Documents/Arduino
In Finder, type /Users/rui/Documents/Arduino and open that directory.
Open the libraries folder.
You should see the TFT_eSPI library folder there. Open it.
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.
You should now have the User_Setup.h file provided on that path.
Then, download the lv_conf.h configuration file.
- Click here to download .zip folder with the lv_conf.h config file (view raw file)
Open a similar folder path for the libraries 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).
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.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>
// 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 240
#define SCREEN_HEIGHT 320
// 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 0: touchscreen.setRotation(0);
touchscreen.setRotation(2);
// 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);
// 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
}
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 240
#define SCREEN_HEIGHT 320
#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(2);
Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 0: touchscreen.setRotation(0);
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));
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
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.
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″…
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).
Drag the slider with your finger, you’ll see the slider moving and the slider label changing when you move your finger.
All the events will be printed in your Arduino IDE Serial Monitor for debugging purposes:
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:
- Getting Started with ESP32 Cheap Yellow Display Board – CYD (ESP32-2432S028R)
- ESP32 Touchscreen On/Off Button – Cheap Yellow Display (ESP32-2432S028R)
- ESP32 Cheap Yellow Display (CYD) Pinout (ESP32-2432S028R)
To learn more about the ESP32, make sure to take a look at our resources:
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
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
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
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
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
Then i have this
sketch_may29a:174:3: error: ‘tft’ was not declared in this scope
tft.setRotation(1);
^~~
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.
Hi Ben, I had the same issue just with only teh tft_eSPI-library and found somewhere that you need to add a delay after setting te rotation to give the chip some time to act. 50ms will do.
tft.init();
tft.setRotation(2);
delay(50); // needed for display to react
Did the job in my case.
Are you successful in this sketch to rotate the screen?
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.
Can I use I2C with the CYD?
What is the best way to power the CYD with batteries?
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
Thanks for this userfull procedure.
Do you have a piece of code for a button onclick event with an http request ?
Merci,
marc,
Hi.
Not at the moment.
But, we’re working on an eBook that will include similar projects.
Regards,
Sara
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?
Hi.
We’re currently working on an eBook about this subject.
It will include many practical examples.
Regards,
Sara
Great guide, sharing a fantastic community on CYD over here on Brian Lough’s Discord channel discord.gg/998cVD53
I recently discovered that Discord channel, great source for information about the CYD, and the many varieties of CYD.
Some very helpful and knowledgeable people too, who will often advise or help with so many aspects of CYD and other ESP stuff too.
Even a great Display Library with a very simple way to define the board your are using in the library(no complicated User_setup file req), called bb_spi_lcd in the Arduino library
Awesome, I bought two of them, but I’m using micropython. Do you have the same tutorial with micropython?
Hi.
No.
Unfortunately, at the moment, we don’t have any examples for MicroPython with the display.
Regards,
Sara
Here is a working MicroPython with examples:
github.com/jtobinart/MicroPython_CYD_ESP32-2432S028R
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
Add the following
In Definition
TFT_eSPI tft = TFT_eSPI();
In Setup
tft.invertDisplay(1);// this changes the color assignment
Hi.
For more information about color assignment see this: https://rntlab.com/question/colors-inverted-on-cyb-esp32-display/
Regards,
Sara
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,
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
Hi,
when pressing button2, the toggle button, it changes color, but cannot find where this change of color is done . Any input ?
Hello, thank you very much for the tutorial. How do I include the code of other lvgl elements?
it’s possible?
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.
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.
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
Yes, I had the lv_conf.h incorrectly in the lvgl folder, moving it to the libraries folder fixed it.
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
Hello,
Did you manage to find a solution for this. I am facing the same issue
Hi.
Please follow the libraries’ installation carefully.
Make sure you’re using the provided files and placing them on the right folders.
Regards,
Sara
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.
Hi.
Yes. We’re working on an eBook that includes an example with a keyboard.
Regards,
Sara
Hello,
Which board do I have to select in the board manager?
Hi.
It is mentioned in the tutorial.
It’s the ESP32 Dev Module.
Regards,
Sara
Thank you, it’s done
An upgrade on screen rotation and the right colors.
I have an ESP32-2432S028 (without the final R) and to have the correct rotation you need to use the TFT_eSPI library command at the beginning of the sketch immediately after the declarations:
TFT_eSPI tft = TFT_eSPI();
then these lines must be added to the setup
touchscreen.setRotation(1);
lv_display_t * disp;
disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT,draw_buf,sizeof(draw_buf));
tft.setRotation(1);
tft.invertDisplay(1);// this changes the color assignment
Hi.
Thanks for the info.
Regards,
Sara
I have the same variant without R, my Rotation was ok but colors were off (Yellow buttons on Black Background)
I just added these lines:
In Definition
TFT_eSPI tft = TFT_eSPI();
In Setup
tft.invertDisplay(1);// this changes the color assignment
Touch mapping seems a bit off ill try the suggestions above
@Gabryx
Thank you very much! We must have the same CYD. I’ve added a note about this to my library folder so it’ll always be there to remind me.
@Sara Santos
These tutorials are very useful/straight forward, and I really appreciate it! I do wish that all the preliminary hand holding could be put into a separate tutorial though. I’m referring to the sections for Windows and Mac users on how to copy and paste a file to a directory. Wow! How many pictures does it take?! Just kidding, yeah, but no I’m serious. I know, beginners gotta begin somewhere. So that’s cool. But yeah…
When i use the LVLG library, the code takes so long to compile (Over 10 min), anything i could do to improve the compilation speed?
Hi.
I’m not sure.
At the moment, it also takes approximately that time for me also.
Regards,
Sara
You should leave the Arduino IDE and start using VS Code and PlatformIO, the compiling is very fast, only the first compile takes around 250 seconds, after that if you only make small changes the compiler is smart and takes only 20-25 seconds
1 min max for me if they are large sketches
AMD Ryzen 5 4500 6-Core (Renoir) 3.60GHz 16GB Ram
To speed up compilation you must exclude all Arduino directories from antivirus, including those in C:User/name\AppData\Local\Arduino15\
For Much better colors for the CYD2USB Variant, add all this to the setup
tft.invertDisplay(1); // if your colors are inverted
tft.writecommand(ILI9341_GAMMASET); //Gamma curve selected
tft.writedata(2);
delay(120);
tft.writecommand(ILI9341_GAMMASET); //Gamma curve selected
tft.writedata(1);
I want to be able to use larger fonts (90+). I have looked at a number of github sites but am not getting anywhere. Do you know of a site that is easy to understand and fairly simple ( like your tutorials) to implement.
Thanks
Hi Chris, for larger Fonts pop over to the above discord channel for some advice, there are some good examples of how to use larger fonts within various libraries.
Brian Lough’s Discord channel discord.gg/998cVD53
Hi, is it possible to set the screen in portrait?
Thanks
Hi.
Here are some suggestions on how to achieve that: https://rntlab.com/question/cyd-with-lvgl-how-to-set-portrait-mode/
Regards,
Sara
Hi, how can I reduce the compiling time? It takes about half an hour to do any code using “lvgl”-library. I followed all instructions on this website. My Board is “ESP32-2432S028”. Laptop is very strong.
Pin 21 not realy usable. It also manage the backlignht display.
Ciao
Potete aiutarmi nella compilazione mi da questo errore: Grazie
c:\Users\ivovi\AppData\Local\Programs\Arduino IDE\libraries\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.
Hi.
Check Rui’s answer here: https://rntlab.com/question/lvgl-book-1-3-compile-problem/
Regards,
Sara
Ciao, ho aggiornato librerie con i files di configurazione ultima versione dell’ebook v1.3 LVGL ma ritorna sempre errore compilazione. Grazie
c:\Users\ivovi\AppData\Local\Programs\Arduino IDE\libraries\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.
Alternative per ../../src/themes/lv_theme_private.h: []
ResolveLibrary(../../src/themes/lv_theme_private.h)
-> candidati: []
exit status 1
Compilation error: exit status 1
Hi.
https://rntlab.com/question/lvgl-book-1-3-compile-problem/
Regards.
Sara
Hi,
Followed the different steps, and compiled…..but get errors :
166:3: error: ‘lv_log_register_print_cb’ was not declared in this scope
166 | lv_log_register_print_cb(log_print);
178:10: error: ‘lv_tft_espi_create’ was not declared in this scope; did you mean ‘lv_timer_create’?
178 | disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
Any suggestions ?
thank you in advancd
best regards
Ludo
Hi.
Are you using VS Code or Arduino IDE?
Did you copy the whole code’ It seems it cannot find some of the functions defined in the code??
Regards,
Sara
Hi Sara,
I am using arduino ide.
In the mean time, I moved back to lvgl 9.0, and then I did not get the errors anymore. Strange that 9.2 works for you, not for me…. Anyway, it works now.
Thank you
Regards
Hi,
The toggle button is changing color when clicked, but cannot find the place where these colors are defined.
Please confirm.
Thank you.
Regards
Ludo
Hi.
Those are the default colors for that kind of widget in the LVGL library.
Regards,
Sara
Hi,
I sent in a question, but it does not show up… Why?
Hi.
I already answered your question.
Most questions need to be approved manually.
Regards.
Sara
Hi,
in this example, the toggle button changes color .
Where in the program is this done ? I cannot find any red/blue color change
regards
Ludo
Hi.
Those are the default colors for a toggle button set by the LVGL library for that kind of widget.
But it is possible to change the colors using styles.
Regards,
Sara
#include “../../lv_examples.h”
#if LV_USE_SCALE && LV_BUILD_EXAMPLES
LV_IMAGE_DECLARE(img_hand);
lv_obj_t * needle_line;
lv_obj_t * needle_img;
static void set_needle_line_value(void * obj, int32_t v)
{
lv_scale_set_line_needle_value(obj, needle_line, 60, v);
}
static void set_needle_img_value(void * obj, int32_t v)
{
lv_scale_set_image_needle_value(obj, needle_img, v);
}
/**
* A simple round scale
*/
void lv_example_scale_3(void)
{
lv_obj_t * scale_line = lv_scale_create(lv_screen_active());
lv_obj_set_size(scale_line, 150, 150);
lv_scale_set_mode(scale_line, LV_SCALE_MODE_ROUND_INNER);
lv_obj_set_style_bg_opa(scale_line, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(scale_line, lv_palette_lighten(LV_PALETTE_RED, 5), 0);
lv_obj_set_style_radius(scale_line, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_clip_corner(scale_line, true, 0);
lv_obj_align(scale_line, LV_ALIGN_LEFT_MID, LV_PCT(2), 0);
lv_scale_set_label_show(scale_line, true);
lv_scale_set_total_tick_count(scale_line, 31);
lv_scale_set_major_tick_every(scale_line, 5);
lv_obj_set_style_length(scale_line, 5, LV_PART_ITEMS);
lv_obj_set_style_length(scale_line, 10, LV_PART_INDICATOR);
lv_scale_set_range(scale_line, 10, 40);
lv_scale_set_angle_range(scale_line, 270);
lv_scale_set_rotation(scale_line, 135);
needle_line = lv_line_create(scale_line);
lv_obj_set_style_line_width(needle_line, 6, LV_PART_MAIN);
lv_obj_set_style_line_rounded(needle_line, true, LV_PART_MAIN);
lv_anim_t anim_scale_line;
lv_anim_init(&anim_scale_line);
lv_anim_set_var(&anim_scale_line, scale_line);
lv_anim_set_exec_cb(&anim_scale_line, set_needle_line_value);
lv_anim_set_duration(&anim_scale_line, 1000);
lv_anim_set_repeat_count(&anim_scale_line, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_playback_duration(&anim_scale_line, 1000);
lv_anim_set_values(&anim_scale_line, 10, 40);
lv_anim_start(&anim_scale_line);
lv_obj_t * scale_img = lv_scale_create(lv_screen_active());
lv_obj_set_size(scale_img, 150, 150);
lv_scale_set_mode(scale_img, LV_SCALE_MODE_ROUND_INNER);
lv_obj_set_style_bg_opa(scale_img, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(scale_img, lv_palette_lighten(LV_PALETTE_RED, 5), 0);
lv_obj_set_style_radius(scale_img, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_clip_corner(scale_img, true, 0);
lv_obj_align(scale_img, LV_ALIGN_RIGHT_MID, LV_PCT(-2), 0);
lv_scale_set_label_show(scale_img, true);
lv_scale_set_total_tick_count(scale_img, 31);
lv_scale_set_major_tick_every(scale_img, 5);
lv_obj_set_style_length(scale_img, 5, LV_PART_ITEMS);
lv_obj_set_style_length(scale_img, 10, LV_PART_INDICATOR);
lv_scale_set_range(scale_img, 10, 40);
lv_scale_set_angle_range(scale_img, 270);
lv_scale_set_rotation(scale_img, 135);
/* image must point to the right. E.g. -O------>*/
needle_img = lv_image_create(scale_img);
lv_image_set_src(needle_img, &img_hand);
lv_obj_align(needle_img, LV_ALIGN_CENTER, 47, -2);
lv_image_set_pivot(needle_img, 3, 4);
lv_anim_t anim_scale_img;
lv_anim_init(&anim_scale_img);
lv_anim_set_var(&anim_scale_img, scale_img);
lv_anim_set_exec_cb(&anim_scale_img, set_needle_img_value);
lv_anim_set_duration(&anim_scale_img, 1000);
lv_anim_set_repeat_count(&anim_scale_img, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_playback_duration(&anim_scale_img, 1000);
lv_anim_set_values(&anim_scale_img, 10, 40);
lv_anim_start(&anim_scale_img);
}
#endif
The Arduino IDE does not like this
get
src/main.cpp: In function ‘void set_needle_line_value(void*, int32_t)’:
src/main.cpp:94:36: error: invalid conversion from ‘void‘ to ‘lv_obj_t‘ [-fpermissive]
lv_scale_set_line_needle_value(obj, needle_line, 60, v);
static void set_needle_line_value(void * obj, int32_t v)
{
lv_scale_set_line_needle_value(obj, needle_line, 60, v);
}
Hi,
Does enyone know why I can’t get this to work on my ESP32-2432S032? I ordered an ESP32-2432S028 from Amazon, but they shipped a ..32.
When I’ve compared images to the ESP32-2432S028, it seems mostly the cards are mirrored and the USB-port is USB-C, instead if micro-USB, but could they have switched IO-pins for the TFT or something? I haven’t found many tutorials with the ESP32-2432S032.
Main goal was to set up a Weather station, which you also have a tutorial for here, but when I compiled that, it said that it’s too big 137% for my card.
I’m fairly new to ESP-boards, so it might be that I’m doing some trivial mistake also.
Out of the box the card had this demo with the girl and the shop ect. so it’s not completely faulty. I then tried a template from the Ardruino Cloud, the Sparkfun Weather Station, and I could even get it online, but didn’t find any way to modify the code.
After this I did the steps here with Arduino IDE 2.3.3 and uploaded the code, but only the led is red and display is not on/responding at all.
I could return this card and ask for the ESP32-2432S028, but I don’t think this one is broken, just need some help to get the display setting correct I asume? Any help would be great!
Thanks in advance!
Hi.
If you see an Error like this: “Sketch too big” during the uploading process, in Arduino IDE go to Tools > Partition scheme > choose anything that has more than 1.4MB APP, for example: “Huge APP (3MB No OTA/1MB SPIFFS“.
What chip does your display use? Is it the ILI9341?
If it’s a different one, you need to specify that in the User_Setup file.
Regards,
Sara
Hi Sara,
I now changed the Partitition scheme as you suggested and had inserted my Wi-Fi credentials to the Weather station code. I now see the ESP32 on my routers device list, but still the dislpay is dead and now the led is off, so there’s nothing to indicate the board is even on.
I also looked in to the User_Setup file, but I’m kind of more leaning to this display actually being a ILI9341, as I also have a 2,4″ ILI9341 display. I could try that together with another ESP32 learning kit I have and see if I could figure out what’s the problem.
Br,
Andreas
Hello! Thanks for all the excellent tutorials.
This code doesn’t compile in PIO/VS Code. It seems that touchscreen.begin() does not take an argument (touchscreenSPI). If I change to touchscreen.begin(); the code compiles and runs on my CYD, but the touchscreen doesn’t work (even without any touch, it constantly returns warnings about indev_pointer_proc is smaller than zero). I notice all the RNT CYD pages configure the touchscreen this way. Am I doing something wrong or has there been a change in the library?
Well this is a weird one…there’s no issue with the code. The library should take SPI as an argument. However, when I install the library using PIO library manager, it doesn’t work. If I refer to the library directly it works.
In other words, this platformio.ini does NOT work:
lib_deps =
lvgl/lvgl@^9.2.0
bodmer/TFT_eSPI@^2.5.43
paulstoffregen/XPT2046_Touchscreen
And this platformio.ini DOES work:
lib_deps =
lvgl/lvgl@^9.2.0
bodmer/TFT_eSPI@^2.5.43
https://github.com/PaulStoffregen/XPT2046_Touchscreen
Hi,
Everything works fine, but the slider action is reversed.
If i slide left the value increases instead of decreasing, and vice versa.
How can I fix this ?
Any solution?
my screen was worked but the color was not right
background was black not white color like you
Hi.
Take a look at this discussion: https://rntlab.com/question/colors-inverted-on-cyb-esp32-display/
I hope this helps.
Regards,
Sara
Is there any existing firmware for an esp32 lvgl display that would allow me to send graphics drawing commands to it from a Linux program on another computer, either over wifi or a serial connection? I.e. an interpreter for some graphics protocol. I’m not too fussy what the graphics package driving it on the Linux side is (as long as it is in C or at least callable from C). Basically I want to use an LVGL display as a graphics terminal, sort of like how we used to use Tektronix terminals back in the 80’s over RS232 serial lines… I don’t particularly want to have to create it from scratch myself and I definitely don’t want to port my actual applications to the ESP32, I just want to use it as a smart display or terminal which I can ask to draw lines and curves etc from a C program on a linux computer. If it could handle creating and stamping sprites that would be a bonus, but minimum requirement for me is just line drawing.
After posting that, it occurred to me that a solution which would be more generally useful, would be to have a version of the LVGL library hosted on the Linux system, which passed the call and parameters to the ESP32 to be executed – i.e. a similar model to how X-windows works. I don’t mind coding to the LVGL library interface, I just can’t run Linux-specific applications on the ESP32. Does a remote driver like that exist for LVGL anywhere?
By the way, I did find something approaching the sort of library I was looking for: https://github.com/feilipu/ReGIS although it may be a bit more complex to use than I was hoping to find.
Sorry I forgot to point out: although ReGIS is the sort of remote graphics protocol I’m looking for, it’s 1) serial only, no wifi support; and 2) no implementation for the ESP32/LVGL boards – so I would have to add both of those. Which I’m willing to do at some point (got a lot of projects on the go to finish first) but I was really hoping to find something that already existed and was ready to use.
Hi,
Is there a system available which would allow me to build up a GUI without directly looking at the code for IT, for example QT has the capability to add different types of things onto a page, such as text boxes, radio buttons etc.
Without changing the settings in the IDE the screen did not work anymore. I did the setup again but now I get this message.
No such file or directory
#include <User_Setups/User_Custom_Fonts.h>
Help me please