LVGL Hardware Button Integration with ESP32
LVGL Hardware Button Integration
Physical Meets Digital: Learn how to seamlessly integrate physical buttons with beautiful LVGL interfaces for tactile user interaction.
Extended Hardware Setup
Building upon the basic LVGL setup, this guide adds physical button integration.
Core Components
| Component | Specification | Purpose |
|---|---|---|
| ESP32 Dev Board | Pinout Reference | Main controller |
| ST7735S Display | 1.8" TFT 128×160 | Visual interface |
| Physical Buttons | Push/capacitive touch | User input |
| Breadboard & Wires | Standard prototyping | Connections |
Button Configuration
8 GPIO Button Setup: This tutorial demonstrates configuring 8 independent buttons on different ESP32 GPIO pins, supporting both:
- Digital push buttons
- Capacitive touch sensors
Software Dependencies
Same as basic LVGL setup - refer to previous guide for detailed installation
Project Objectives
This advanced tutorial focuses on:
Core Functionality
- Multi-button GPIO mapping across ESP32 pins
- LVGL button event handling for UI interaction
- Landscape display orientation (portrait also supported)
- Hybrid input support (physical + touch)
Prerequisites
Required Reading: Complete the LVGL ESP32 Display Setup guide first if you haven't already.
Research & Legacy Issues
Previous Attempts
Official LVGL Hardware Button Guide:
- Contains deprecated functions for newer LVGL versions
- Designed for STM32, not ESP32
- Based on older LVGL v6.x
This Updated Guide:
- Uses LVGL v7.x compatible functions
- Optimized for ESP32 architecture
- Modern best practices
putting the Setup
Step 1:Create a new project in platformIO and Do the connection of the esp32 with Capacitive touch button are as follows:

$$\text{Capacitive Touch sensor}$$

$$\text{Capacitive Touch sensor setup}$$

$$\text{ESP32 dev Board }$$
System Connections:
| Sl No. | Capacitive Touch Signal pin | ESP32 | Description |
|---|---|---|---|
| 1. | Button_1 | 27 | Capacitive touch button |
| 2. | Button_2 | 14 | Capacitive touch button |
| 3. | Button_3 | 12 | Capacitive touch button |
Step 2: Now do the following setup in the downloaded library as in my case i used PlatformIO so i will go to :{your platformIO project name}/.pio/libdeps/esp32dev/TFT_eSPI/User_Setup.h
and for the Arduino IDE go to /home/{USERNAME}/Arduino/libraries/TFT_eSPI/User_Setup.h
So either you can comment everything their and put these lines or read the file uncomment these changes in the User_Setup.h.
Step 2: Now go to the {your platformIO project name}/.pio/libdeps/esp32dev/lv_arduino/lv_conf.h and for the Arduino IDE go to /home/{USERNAME}/Arduino/libraries/.pio/libdeps/esp32dev/lv_arduino/lv_conf.h and do the following changes :
1/* Input device read period in milliseconds */
2#define LV_INDEV_DEF_READ_PERIOD 30 // for button till 20ms is recommended
3/* Long press time in milliseconds.
4 * Time to send `LV_EVENT_LONG_PRESSED`) */
5#define LV_INDEV_DEF_LONG_PRESS_TIME 400 // if long press required
6/* Repeated trigger period in long press [ms]
7 * Time between `LV_EVENT_LONG_PRESSED_REPEAT */
8#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100
9
10// enable log settings
11#define LV_USE_LOG 1
12# define LV_LOG_LEVEL LV_LOG_LEVEL_INFO // easy for debugging
13# define LV_LOG_PRINTF 1
Step 3: Now go to the {your platformIO project name}/.pio/libdeps/esp32dev/lvgl/examples/porting folder and for the Arduino IDE go to /home/{USERNAME}/Arduino/libraries/.pio/libdeps/esp32dev/lvgl/examples/porting and do the following changes :
- and copy the file name
lv_port_indev_template.caslv_port_indev.candlv_port_indev_template.haslv_port_indev.hin the folder namelv_port_indevin your local lib folder in the PlatformIO or in the skechbook folder of the Arduino IDE. - Now open the
lv_port_indev.hfile and remove with following lines:1 /** 2 * @file lv_port_indev.h 3 * 4 */ 5 6 /*Copy this file as "lv_port_indev.h" and set this value to "1" to enable content*/ 7 #if 1 8 9 #ifndef LV_PORT_INDEV_H 10 #define LV_PORT_INDEV_H 11 12 #ifdef __cplusplus 13 extern "C" { 14 #endif 15 16 /********************* 17 * INCLUDES 18 *********************/ 19 /********************* 20 * DEFINES 21 *********************/ 22 #define BUTTON_1 27 // GPIO from esp32 dev module 23 #define BUTTON_2 14 // GPIO from esp32 dev module 24 #define BUTTON_3 12 // GPIO from esp32 dev module 25 /********************** 26 * TYPEDEFS 27 **********************/ 28 typedef struct 29 { 30 const char pin_name[15]; 31 int8_t pin_no; 32 int8_t button_no; 33 volatile bool pin_state; 34 int8_t pin_mode; 35 bool pin_last_state; 36 }buttons_c; 37 38 /********************** 39 * GLOBAL PROTOTYPES 40 **********************/ 41 void lv_port_indev_init(void); 42 /********************** 43 * MACROS 44 **********************/ 45 46 #ifdef __cplusplus 47 } /* extern "C" */ 48 #endif 49 50 #endif /*LV_PORT_INDEV_TEMPL_H*/ 51 52 #endif /*Disable/Enable content*/ - Now open the
lv_port_indev.cfile and add the following lines:1 //enable the content 2 #if 1 3 4 /********************* 5 * INCLUDES 6 *********************/ 7 // include the header file 8 #include <Arduino.h> 9 # include <lv_examples.h> 10 #include "lv_port_indev.h" 11 /********************* 12 DEFINES 13 *********************/ 14 buttons_c buttons [] = { 15 {.pin_name = "BUTTON_1 " , .pin_no = BUTTON_1 , .button_no = 1}, 16 {.pin_name = "BUTTON_2 " , .pin_no = BUTTON_2 , .button_no = 2}, 17 {.pin_name = "BUTTON_3 " , .pin_no = BUTTON_3 , .button_no = 3} 18 }; 19 // in the function void lv_port_indev_init(void) 20 /*Assign buttons to points on the screen change according to your preference*/ 21 static const lv_point_t btn_points[(sizeof(buttons) / sizeof(buttons_c))] = { 22 { 80, 25}, /*Button 1 at coordinate on screen -> x:80; y:10*/ 23 { 80, 85}, /*Button 2 at coordinate on screen -> x:80; y:30*/ 24 { 80, 50}, /*Button 3 at coordinate on screen -> x:80; y:50*/ 25 } 26 // in the function static void button_init(void) 27 /* Initialize your buttons */ 28 static void button_init(void) 29 { 30 uint8_t i; 31 for(i = 0; i < sizeof(buttons) / sizeof(buttons_c); i++) 32 { 33 pinMode(buttons[i].pin_no, buttons[i].pin_mode = INPUT); 34 buttons[i].pin_state = digitalRead(buttons[i].pin_no); 35 buttons[i].pin_last_state = buttons[i].pin_state; 36 buttons[i].pin_last_state = -1; 37 } 38 } 39 // in the function static int8_t button_get_pressed_id(void) 40 /*Get ID (1- 3) of the pressed button*/ 41 static int8_t button_get_pressed_id(void) 42 { 43 uint8_t i; 44 for(i = 0; i < sizeof(buttons) / sizeof(buttons_c); i++) 45 { 46 buttons[i].pin_last_state = buttons[i].pin_state; 47 buttons[i].pin_state = digitalRead(buttons[i].pin_no); 48 if(button_is_pressed(buttons[i].pin_state)) 49 return buttons[i].button_no; // return button no. 50 } 51 /*No button pressed*/ 52 return -1; 53 } 54 55 /*Test if `id` button is pressed or not*/ 56 static bool button_is_pressed(uint8_t id) 57 { 58 if(id) 59 return true; 60 return false; 61 }
Step 4: Now open a new file in PlatformIO from the src folder like main.cpp or in the Arduino IDE open a main file which contains the void setup() function and do these changes:
1 // include the following library
2 #include <Arduino.h>
3 #include <lvgl.h>
4 #include <main.h>
5 #include <TFT_eSPI.h>
6 #include <lv_examples.h>
7 #include "../lib/lv_port_indev/lv_port_indev.h"
And in the void setup() function add this after display driver initialization:
1 lv_disp_drv_register(&disp_drv);
2
3 /* Try an example from the lv_examples repository
4 * https://github.com/lvgl/lv_examples*/
5 lv_port_indev_init(); // add this line
6 lv_ex_btn_1(); // calling from lv_example
Step 6: Now upload the program to your ESP32. Here is the content of platformio.ini file:
1 [env:esp32dev]
2 platform = espressif32
3 board = esp32dev
4 framework = arduino
5 monitor_speed = 115200
6 monitor_port = /dev/ttyUSB0
7 lib_deps =
8 lvgl/lv_arduino@2.1.5
9 bodmer/TFT_eSPI@^2.3.59
10 greiman/SdFat@^2.0.4
11 lvgl/lvgl@^7.10.0
12 lvgl/lv_examples@^7.10.0
Conclusion: The whole output should look somthing like this and The above code should generate this result:

This is some other example running :

Note: Below is the example of wrong configuration.e.g, in the step 3
1#define LV_HOR_RES_MAX (100) or //10
2#define LV_VER_RES_MAX (100) or //10
Reference Note:
- Wrong declaration problem: issue
- ESP DEBUGGER and follow this installation guide
- lvgl event handler guide
Help in improving the guide
Thanks Futuristic Labs Pvt. Ltd. for providing me the resources of the project.
Please if you find any useful improvement feel free to contact at : @pranav083 or comment below.