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.

ESP32 LVGL

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

  1. Initialization: Call lv_init() to initialize LVGL.
  2. Register Display Driver: Provide a screen buffer (memory) and flush_cb() function to write pixels to the screen.
  3. Register Input Driver: Provide callback functions for touch or button inputs.
  4. 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:

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!