ESP32 LVGL Tutorial | Build a Beautiful and Professional UI
ESP32 LVGL in modern embedded development is a powerful combination. The ESP32 is a high-cost-performance, multifunctional Wi-Fi + Bluetooth SoC. However, to create a “professional-looking, intuitive, and well-performing” UI, basic display drivers alone are not enough. This is where LVGL (Light and Versatile Graphics Library) comes into play.
LVGL is a lightweight UI library specifically designed for embedded systems. It supports features like animations, layouts, custom styles, and touch input, with excellent performance and low resource requirements, making it ideal for the ESP32. This article will guide you from scratch on how to install LVGL in ESP-IDF, set up a clean project structure, and successfully display your first LVGL widget.

Contents
What is LVGL?
LVGL (Light and Versatile Graphics Library) is an open-source, cross-platform graphics library designed for low-resource devices.
Key features include:
- Active community and comprehensive documentation.
- Support for multiple resolutions and touch inputs.
- A wide range of UI components (buttons, text, lists, sliders, etc.).
- Support for animations, layouts, and theme switching.
Introduction to ESP32 LVGL
Before running LVGL on the ESP32, understanding its basic architecture and operation principles will help you better integrate display and touch devices.
LVGL is a “pure UI engine“; it does not directly communicate with your LCD or touch IC. Instead, it interacts with the display and input devices through driver callbacks that you provide.
+------------------+
| Your App | ← Your logic and UI flow
+------------------+
↓
+------------------+
| LVGL | ← UI components, layouts, events, etc.
+------------------+
↓
+------------------+
| Display Driver | ← Function to write pixels to the screen
| Input Driver | ← Function to handle touch/buttons input
+------------------+
↓
+------------------+
| ESP32 + LCD | ← Physical screen or touch device
+------------------+
Core Operation Flow of ESP32 LVGL
- Initialization: Call
lv_init()
to initialize LVGL. - Register Display Driver: Provide a screen buffer (memory) and
flush_cb()
function to write pixels to the screen. - Register Input Driver: Provide callback functions for touch or button inputs.
- UI Update Cycle: Periodically call
lv_timer_handler()
to process animations, events, and redrawing. It’s typically called every 5–10 ms.
Understanding these principles will make it easier for you to integrate LCDs, touch panels, and even add icon animations and page transitions in the future!
Development Environment
Before starting your programming, make sure to complete the following preparations:
- Install ESP-IDF (version 4.4 or higher): ESP-IDF is the official development framework for programming the ESP32, and it supports multiple operating systems such as Windows, macOS, and Linux.
- ESP32 Development Board: An ESP32 board is required.
ESP32 LVGL Project Structure
To create a clean ESP-IDF LVGL project, the structure should look like this:
esp32_lvgl_project/
├── CMakeLists.txt
├── components/
│ └── lvgl/ ← LVGL
├── main/
│ ├── CMakeLists.txt
│ └── main.c ← Main
├── sdkconfig
Integrate LVGL into Your ESP-IDF Project
LVGL is not a built-in ESP-IDF component, but it can be easily integrated as a component.
mkdir -p components
cd components
git clone https://github.com/lvgl/lvgl.git
ESP-IDF will automatically detect components/lvgl/
and compile it.
Copy lv_conf_template.h
to lv_conf.h
LVGL provides a default lv_conf_template.h
file. You need to copy it as lv_conf.h
:
cp components/lvgl/lv_conf_template.h components/lvgl/lv_conf.h
Open lv_conf.h
, and you’ll see the following at the top:
#if 0 /* Set this to "1" to enable content */
Change it to:
#if 1 /* Set this to "1" to enable content */
This will enable the entire configuration file; otherwise, LVGL will use default settings that might not be suitable for ESP32.
Code
Here’s the basic LVGL initialization code (in main/main.c
):
#include "lvgl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void app_main(void)
{
lv_init(); // Initialize LVGL
// Screen driver initialization is skipped here for simplicity.
// In practice, you should register the display driver and set up buffers.
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello ESP32 + LVGL!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
while (1) {
lv_timer_handler(); // Handle LVGL tasks (UI updates)
vTaskDelay(pdMS_TO_TICKS(10)); // Call every 10 ms
}
}
Code Explanation
lv_init()
: Initializes the LVGL core.lv_label_create()
: Creates a text label on the main screen.lv_label_set_text()
: Sets the label text.lv_obj_align()
: Aligns the widget to the center.lv_timer_handler()
: Core “screen updater” for LVGL, must be called periodically.
Compile and Flash
After writing the code, you can use the ESP-IDF tools to build, flash, and monitor:
In the VS Code lower-left ESP-IDF toolbar:
- Click Build project
- Click Flash device
- Click Monitor device
After a successful run, you should see logs indicating that LVGL initialization was successful. Although the screen won’t show anything yet (since the LCD driver isn’t connected), the system will compile and run LVGL logic correctly.
Conclusion
We’ve covered the process of integrating ESP32 LVGL into your ESP32 project, from installation to setting up a clean ESP-IDF structure. This guide helps you create a professional and interactive user interface for embedded systems using ESP32 LVGL, providing a solid foundation for further integration of touch screens, animations, and event handling. With ESP32 LVGL, embedded development goes beyond text output and GPIO control, offering a richer user experience for your hardware products. We hope this guide sparks your UI design journey and encourages you to explore more of ESP32 LVGL’s potential in your projects!