Skip to main content

Section 08 Porting LVGL

In the previous section we used the ESP_Display_Panel library to drive the screen for display. In this section we also use this library to drive the screen and touch panel, and port the LVGL library to display the official examples.

Hardware Connection

To upload the corresponding code to the ESP32-S3, you need to connect the USB port of the ESP32-S3-Touch-LCD-7 to the computer's USB port using a Type-C to Type-A cable:

ESP32-S3-Touch-LCD-7 I2C demo 2

Demo

Please download the example package from the following address: ESP32-S3-Touch-LCD-7 Demo (if already downloaded, simply open the corresponding folder). After extracting the example package, open the 09_lvgl_Porting folder:

ESP32-S3-Touch-LCD-7 LVGL demo 1

Double-click to open 09_lvgl_Porting.ino:

ESP32-S3-Touch-LCD-7 LVGL demo 2

We have already connected the USB port for downloading the program. After opening the program, select the correct serial port and board model "Waveshare ESP32-S3-Touch-LCD-7" in the upper right corner.

ESP32-S3-Touch-LCD-7 LVGL demo 3

Set the corresponding board parameters:

ESP32-S3-Touch-LCD-7 LVGL demo 4

Then click the Upload button to upload the program, and wait for the upload to complete.

ESP32-S3-Touch-LCD-7 LVGL demo 5

Running Results

Power cycle the screen, and you will see the lvgl example displayed on the screen. For the version with a touch panel, sliding on the screen also allows touch testing.

ESP32-S3-Touch-LCD-7 LVGL demo 6

Code Review

1. Driving the Display and Touch Panel

In this section's example, we will use the esp_panel_board_custom_conf.h file to configure the screen and touch. This file is located in the current directory of the example, and the system will automatically detect it and use it for configuration. For information about configuring this file, please refer to the official documentation.

Below we explain how to configure this file:

ESP32-S3-Touch-LCD-7 LVGL demo 7

Enable custom LCD panel

#define ESP_PANEL_BOARD_DEFAULT_USE_CUSTOM (1)

First, change the macro ESP_PANEL_BOARD_DEFAULT_USE_CUSTOM from 0 to 1 to enable custom configuration. This will enable the configuration parameters under #if ESP_PANEL_BOARD_DEFAULT_USE_CUSTOM.

#define ESP_PANEL_BOARD_NAME "Waveshare:ESP32-S3-Touch-LCD-7"
#define ESP_PANEL_BOARD_WIDTH (800) // Panel width (horizontal, in pixels)
#define ESP_PANEL_BOARD_HEIGHT (480) // Panel height (vertical, in pixels)

Set the custom board name and define the screen resolution.

Set LCD parameters

#define ESP_PANEL_BOARD_USE_LCD (1)

Change the macro ESP_PANEL_BOARD_USE_LCD from 0 to 1 to enable the LCD panel. This will enable the configuration parameters under #if ESP_PANEL_BOARD_USE_LCD.

#define ESP_PANEL_BOARD_LCD_CONTROLLER ST7262
#define ESP_PANEL_BOARD_LCD_BUS_TYPE (ESP_PANEL_BUS_TYPE_RGB)

Define the LCD controller IC and communication type – choose RGB bus communication.

#if ESP_PANEL_BOARD_LCD_BUS_TYPE == ESP_PANEL_BUS_TYPE_RGB
#define ESP_PANEL_BOARD_LCD_RGB_USE_CONTROL_PANEL (0) // 0/1. Typically set to 1

#define ESP_PANEL_BOARD_LCD_RGB_CLK_HZ (16 * 1000 * 1000)
...
#define ESP_PANEL_BOARD_LCD_RGB_IO_DATA15 (40)

#endif

ESP_PANEL_BOARD_LCD_RGB_USE_CONTROL_PANEL is set to 0, indicating that a three‑wire SPI RGB interface is not used. Then, various parameters are configured according to the LCD specifications, similar to the previous RGB bar example.

Set touch panel parameters

#define ESP_OPEN_TOUCH 1

Define the macro to enable touch functionality. A value of 0 disables touch, 1 enables touch – choose according to the purchased model.

#define ESP_PANEL_BOARD_USE_TOUCH (ESP_OPEN_TOUCH)

Since we set ESP_OPEN_TOUCH to 1, the touch configuration under #if ESP_PANEL_BOARD_USE_TOUCH will be enabled. If you purchased the non‑touch version, set ESP_OPEN_TOUCH to 0 to disable the touch panel parameters.

#define ESP_PANEL_BOARD_TOUCH_CONTROLLER GT911
#define ESP_PANEL_BOARD_TOUCH_BUS_TYPE (ESP_PANEL_BUS_TYPE_I2C)

Define the touch controller as GT911 and the communication interface as I2C bus.

#define ESP_PANEL_BOARD_TOUCH_I2C_IO_SCL (9)
#define ESP_PANEL_BOARD_TOUCH_I2C_IO_SDA (8)
#define ESP_PANEL_BOARD_TOUCH_INT_IO (4)

Here we set the SCL, SDA, and interrupt pins for the touch.

Set backlight parameters

#define ESP_PANEL_BOARD_USE_BACKLIGHT (1)

Set ESP_PANEL_BOARD_USE_BACKLIGHT to 1 to enable backlight control, which will enable the configuration under #if ESP_PANEL_BOARD_USE_BACKLIGHT.

#define ESP_PANEL_BOARD_BACKLIGHT_TYPE (ESP_PANEL_BACKLIGHT_TYPE_SWITCH_EXPANDER)

#if (ESP_PANEL_BOARD_BACKLIGHT_TYPE == ESP_PANEL_BACKLIGHT_TYPE_SWITCH_GPIO) || \
(ESP_PANEL_BOARD_BACKLIGHT_TYPE == ESP_PANEL_BACKLIGHT_TYPE_SWITCH_EXPANDER) || \
(ESP_PANEL_BOARD_BACKLIGHT_TYPE == ESP_PANEL_BACKLIGHT_TYPE_PWM_LEDC)

#define ESP_PANEL_BOARD_BACKLIGHT_IO (2) // Output GPIO pin number
#define ESP_PANEL_BOARD_BACKLIGHT_ON_LEVEL (1) // Active level, 0: low, 1: high

#endif

Here the backlight type is set to be controlled by an I/O expander, and the control I/O pin is set to EXIO2.

#define ESP_PANEL_BOARD_BACKLIGHT_IDLE_OFF (0)

Set the backlight to be on after initialization.

Set I/O expander parameters

#define ESP_PANEL_BOARD_USE_EXPANDER (1)

Set the macro ESP_PANEL_BOARD_USE_EXPANDER to 1 to enable the I/O expander, which will enable the configuration under #if ESP_PANEL_BOARD_USE_EXPANDER.

#define ESP_PANEL_BOARD_EXPANDER_CHIP CH422G

Set the I/O expander chip name to CH422G.

#define ESP_PANEL_BOARD_EXPANDER_I2C_CLK_HZ (400 * 1000)
// Typically set to 400K
#define ESP_PANEL_BOARD_EXPANDER_I2C_SCL_PULLUP (1) // 0/1. Typically set to 1
#define ESP_PANEL_BOARD_EXPANDER_I2C_SDA_PULLUP (1) // 0/1. Typically set to 1
#define ESP_PANEL_BOARD_EXPANDER_I2C_IO_SCL (9)
#define ESP_PANEL_BOARD_EXPANDER_I2C_IO_SDA (8)

#define ESP_PANEL_BOARD_EXPANDER_I2C_ADDRESS (0x20)

Set the I2C communication parameters for the I/O expander: clock frequency, SDA and SCL pins, etc.

After configuring the above parameters, the program can initialize the screen and display.

2. Porting and Configuring LVGL

Header files

#include <Arduino.h>
#include <esp_display_panel.hpp>

#include <lvgl.h>
#include "lvgl_v8_port.h"
#include <demos/lv_demos.h>

using namespace esp_panel::drivers;
using namespace esp_panel::board;
  • This includes the various libraries required by the program: the core header file of the ESP Display Panel official library (esp_display_panel.hpp), the LVGL main library (lvgl.h), and the LVGL v8 porting layer for ESP32 (lvgl_v8_port.h), etc.

  • using namespace: simplifies the syntax so we can write Board directly instead of esp_panel::board::Board.

    What is the lvgl_v8_port.h library?

    This library is a porting layer for LVGL v8 in the ESP32 / ESP‑IDF / Arduino environment. It connects LVGL to the ESP32‑S3 hardware, for example:

    • Display driver (RGB / SPI / QSPI)
    • Touch driver (I2C / SPI)
    • LVGL tick (lv_tick_inc)
    • LVGL rendering task (lv_timer_handler)
    • DMA / double‑buffer configuration (frame buffer)
    • malloc memory allocation area adaptation

    Without this file, you cannot directly run LVGL on the ESP32‑S3.

setup() function

Serial.begin(115200);
Serial.println("Initializing board");

Initializes the serial port for debug output.

Board *board = new Board();
board->init();

Initializes the Board, including LCD, Touch, and backlight. We have already configured the esp_panel_board_custom_conf.h file, and calling the init() function performs the initialization according to that configuration.

#if LVGL_PORT_AVOID_TEARING_MODE
auto lcd = board->getLCD();
// When avoid tearing function is enabled, the frame buffer number should be set in the board driver
lcd->configFrameBufferNumber(LVGL_PORT_DISP_BUFFER_NUM);

This code is effective only for RGB screens, which have a large bandwidth and require extra optimization. In lvgl_v8_port.h, LVGL_PORT_DISP_BUFFER_NUM is set to 2, so this configures the use of 2 frame buffers to avoid display tearing.

#if ESP_PANEL_DRIVERS_BUS_ENABLE_RGB && CONFIG_IDF_TARGET_ESP32S3
auto lcd_bus = lcd->getBus();

if (lcd_bus->getBasicAttributes().type == ESP_PANEL_BUS_TYPE_RGB) {
static_cast<BusRGB *>(lcd_bus)->configRGB_BounceBufferSize(lcd->getFrameWidth() * 10);
}
#endif

As in the previous example, this obtains the Bus pointer from the LCD, casts it to BusRGB*, and calls the configuration function to set the "bounce buffer" size. This is an RGB acceleration method specific to the ESP32‑S3.

assert(board->begin());

Starts the screen and touch, initializes the LCD driver chip, Touch chip, and configures the backlight PWM.

Serial.println("Initializing LVGL");
lvgl_port_init(board->getLCD(), board->getTouch());

Here LVGL is configured: initializes the LVGL display driver, configures the touch input device, initializes the LVGL tick, creates an LVGL loop task that continuously runs lv_timer_handler(), etc. No need to manually write flush_cb or touch_read_cb; the library handles it automatically.

Serial.println("Creating UI");
lvgl_port_lock(-1);

LVGL is not thread‑safe. lvgl_port_lock() is a function that locks LVGL to ensure that "only one task manipulates LVGL at a time". Set to -1 means waiting indefinitely until the lock is acquired.

lv_obj_t *label_1 = lv_label_create(lv_scr_act()); // Create label 1 on the whole screen
lv_label_set_text(label_1, "Hello World!"); // Set the label text to "Hello World!”
lv_obj_set_style_text_font(label_1, &lv_font_montserrat_30, 0); // Set the font
lv_obj_align(label_1, LV_ALIGN_CENTER, 0, -20); // Set the label position

This creates a "Hello World" text on the parent object.

lv_obj_t *label_2 = lv_label_create(lv_scr_act());
lv_label_set_text_fmt(
label_2, "ESP32_Display_Panel (%d.%d.%d)",
ESP_PANEL_VERSION_MAJOR, ESP_PANEL_VERSION_MINOR, ESP_PANEL_VERSION_PATCH
);
lv_obj_set_style_text_font(label_2, &lv_font_montserrat_16, 0);
lv_obj_align_to(label_2, label_1, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
lv_obj_t *label_3 = lv_label_create(lv_scr_act());
lv_label_set_text_fmt(label_3, "LVGL (%d.%d.%d)", LVGL_VERSION_MAJOR, LVGL_VERSION_MINOR, LVGL_VERSION_PATCH);
lv_obj_set_style_text_font(label_3, &lv_font_montserrat_16, 0);
lv_obj_align_to(label_3, label_2, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);

These steps are similar to the previous creation, used to display the ESP32_Display_Panel library version and the LVGL version.

lv_demo_widgets();

This demo automatically creates a UI with buttons, sliders, lists, etc.

lvgl_port_unlock();

Unlocks, finishing the UI construction.