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

ComponentSpecificationPurpose
ESP32 Dev BoardPinout ReferenceMain controller
ST7735S Display1.8" TFT 128×160Visual interface
Physical ButtonsPush/capacitive touchUser input
Breadboard & WiresStandard prototypingConnections

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

  1. Official LVGL Hardware Button Guide:

    • Contains deprecated functions for newer LVGL versions
    • Designed for STM32, not ESP32
    • Based on older LVGL v6.x
  2. 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:

ST7735s

$$\text{Capacitive Touch sensor}$$

ST7735s

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

ESP32

$$\text{ESP32 dev Board }$$

System Connections:

Sl No.Capacitive Touch Signal pinESP32Description
1.Button_127Capacitive touch button
2.Button_214Capacitive touch button
3.Button_312Capacitive 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.c as lv_port_indev.c and lv_port_indev_template.h as lv_port_indev.h in the folder name lv_port_indev in your local lib folder in the PlatformIO or in the skechbook folder of the Arduino IDE.
  • Now open the lv_port_indev.h file 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.c file 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:

ESP32

This is some other example running :

ESP32

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:

  1. Wrong declaration problem: issue
  2. ESP DEBUGGER and follow this installation guide
  3. 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.