A Step-by-Step Guide to Using LVGL with ESP32

LVGL (Light and Versatile Graphics Library) is an open-source embedded graphics user interface (GUI) library designed to provide appealing and highly interactive user interfaces for embedded systems. With the LVGL GUI library, developers can easily create impressive graphical interfaces on ESP32, enhancing the user experience. This tutorial on using the LVGL GUI library on ESP32 is based on “Using ESP32 with Arduino IDE to Control an LCD Display“. It is recommended to ensure that the TFT_eSPI library can correctly drive the 3.5-inch LCD screen before using the LCD screen.

Installing LVGL and lv_examples Libraries

  1. Open the Arduino IDE.
  2. Go to the “Tools” menu and select “Manage Libraries…”.
  3. In the Library Manager, enter “lvgl” in the search box and press Enter.
  4. The Library Manager will display the available LVGL library. Find the “lvgl” library and click the “Install” button on the right. Wait for the installation to complete.
  5. Repeat the same process to search for “lv_examples” and install it.

Configuring LVGL

  1. Open your project in the Arduino IDE.
  2. Go to the “Sketch” menu -> “Show Sketch Folder” and open the folder containing your project.
  3. Navigate to the parent directory of your project folder, locate the “libraries” folder, and enter it.
  4. Enter the “lvgl” folder, find the file named “lv_conf_template.h”, and make a copy of it.
  5. Go back to the parent directory, i.e., the “libraries” folder.
  6. In the “libraries” folder, paste the copied “lv_conf_template.h” file and rename it to “lv_conf.h”.

Make the following modifications to the lv_conf.h file:

Set line 10 to 1.

				
					#if 1 /*Set it to "1" to enable content*/
				
			

Enter your monitor size on line 23.

				
					#define LV_HOR_RES_MAX          (480)
#define LV_VER_RES_MAX          (320)
				
			

Set LV_TICK_CUSTOM to 1 on line 303.

				
					#define LV_TICK_CUSTOM     1
				
			

Set all listed fonts to 1 on line 384.

				
					#define LV_FONT_MONTSERRAT_8     1
#define LV_FONT_MONTSERRAT_10    1
#define LV_FONT_MONTSERRAT_12    1
#define LV_FONT_MONTSERRAT_14    1
#define LV_FONT_MONTSERRAT_16    1
#define LV_FONT_MONTSERRAT_18    1
#define LV_FONT_MONTSERRAT_20    1
#define LV_FONT_MONTSERRAT_22    1
#define LV_FONT_MONTSERRAT_24    1
#define LV_FONT_MONTSERRAT_26    1
#define LV_FONT_MONTSERRAT_28    1
#define LV_FONT_MONTSERRAT_30    1
#define LV_FONT_MONTSERRAT_32    1
#define LV_FONT_MONTSERRAT_34    1
#define LV_FONT_MONTSERRAT_36    1
#define LV_FONT_MONTSERRAT_38    1
#define LV_FONT_MONTSERRAT_40    1
#define LV_FONT_MONTSERRAT_42    1
#define LV_FONT_MONTSERRAT_44    1
#define LV_FONT_MONTSERRAT_46    1
#define LV_FONT_MONTSERRAT_48    1
				
			

Configuring lv_examples

Similar to configuring LVGL, locate the “lv_examples” folder, copy it, paste it into the “libraries” folder, and rename it to “lv_ex_conf.h”.

Make the following modifications to the lv_ex_conf.h file:

Set line 10 to 1.

				
					#if 1 /*Set it to "1" to enable the content*/
				
			

Here, we enable benchmark and music , the former is used to test performance and the latter is a music player, by setting the following to 1.

				
					#define LV_USE_DEMO_BENCHMARK   1
				
			
				
					#define LV_USE_DEMO_MUSIC      1
				
			

Writing the Main File

Open the main.ino file, which contains the following content:

				
					#include <Arduino.h>
//#include "./includes/oled.h"
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <lv_examples.h>
// extern Adafruit_SH1106G display;
TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];
#if USE_LV_LOG != 0
/* Serial debugging */
void my_print(lv_log_level_t level, const char *file, uint32_t line, const char *dsc)
{
  Serial.printf("%s@%d->%s\r\n", file, line, dsc);
  Serial.flush();
}
#endif
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
  uint32_t w = (area->x2 - area->x1 + 1);
  uint32_t h = (area->y2 - area->y1 + 1);
  tft.startWrite();
  tft.setAddrWindow(area->x1, area->y1, w, h);
  tft.pushColors(&color_p->full, w * h, true);
  tft.endWrite();
  lv_disp_flush_ready(disp);
}
/*Read the touchpad*/
bool my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
  uint16_t touchX, touchY;
  bool touched = tft.getTouch(&touchX, &touchY, 600);
  if (!touched)
  {
    data->state = LV_INDEV_STATE_REL;
  }
  else
  {
    data->state = LV_INDEV_STATE_PR;
    /*Set the coordinates*/
    data->point.x = touchX;
    data->point.y = touchY;
    Serial.print("Data x");
    Serial.println(touchX);
    Serial.print("Data y");
    Serial.println(touchY);
  }
  return false; /*Return `false` because we are not buffering and no more data to read*/
}
void setup()
{
  // put your setup code here, to run once:
  // Serial.begin(9600);
  // //testdrawcircle();
  // display.begin(i2c_Address,true);
  // testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH);
  Serial.begin(115200); /* prepare for possible serial debug */
  lv_init();
#if USE_LV_LOG != 0
  lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif
  tft.begin();        /* TFT init */
  tft.setRotation(1); /* Landscape orientation */
  uint16_t calData[5] = {275, 3620, 264, 3532, 1};
  tft.setTouch(calData);
  lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);
  /*Initialize the display*/
  lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res = 480;
  disp_drv.ver_res = 320;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.buffer = &disp_buf;
  lv_disp_drv_register(&disp_drv);
  /*Initialize the (dummy) input device driver*/
  lv_indev_drv_t indev_drv;
  lv_indev_drv_init(&indev_drv);
  indev_drv.type = LV_INDEV_TYPE_POINTER;
  indev_drv.read_cb = my_touchpad_read;
  lv_indev_drv_register(&indev_drv);
  // lv_demo_benchmark();
  lv_demo_music();
}
void loop()
{
  // put your main code here, to run repeatedly:
  // testdrawcircle();
  // Serial.println("hello");
  // delay(1000);
  // Serial.println(getCpuFrequencyMhz());
  // delay(1000);
  lv_task_handler();
  delay(5);
}
				
			

Lines 105 and 106 in the above code are the test codes for benchmark and music respectively. The final effect is shown in the picture at the beginning of this article. The benchmark test is as follows:

Performance test results of ESP32 chip

You Might Be Interested

esp32 connect to wifi
Connecting ESP32 to WiFi in 4 Steps

Unlock the potential of your ESP32 with these 4 simple steps to WiFi connectivity! Dive into the world of IoT and remote control effortlessly, as

Getting Started with ESP-IDF in VSCode
Getting Started with ESP-IDF in VSCode

This guide introduces an efficient method, “ESP-IDF + VSCode,” for ESP32 development, simplifying environment setup and allowing seamless switching between ESP-IDF versions. It covers downloading

How to Enable Multi-Core on ESP32 Microcontroller
How to Enable Multi-Core on ESP32 Microcontroller

This comprehensive guide elucidates the process of enabling multi-core functionality on the ESP32 microcontroller, leveraging its dual-core architecture for enhanced parallel processing capabilities. Through detailed

esp32 pinout
ESP32 Pinout Reference Guide

The ESP32 is a versatile microcontroller with extensive functionalities. Its pinout includes GPIO pins for digital, analog, and capacitive touch, PWM pins for precise control,

Scroll to Top