Interfacing Raspberry Pi GPIO with LVGL


I’m trying to use Raspberry Pi GPIO to simulate button press in LVGL GUI that is running on the same Raspberry Pi using PC simulator

What MCU/Processor/Board and compiler are you using?

Raspberry Pi 3B+

What LVGL version are you using?

Latest 8.0

What do you want to achieve?

I want to using some push buttons to simulate button press in LVGL. Later will try to control LEDs for example when pressing a button in LVGL GUI.

What have you tried so far?

I’ve tried using WiringPi library to get access to the GPIO. I’ve already got LVGL PC simulator working on my RPi. Also, I managed to add WiringPi library to the CMakeLists file that is used to cerate the make file which compiles the LVGL PC simulator code.


//I tested this function to control an LED when a button is pressed while running LVGL example. This function is added in the main.c file of the PC simulator then calling it in the while(1) inside the main function.(Just for testing)
void blink_test(void)
	if(digitalRead(1) == 1)
	// Toggle the LED
	//digitalWrite(0, !digitalRead(0));
  digitalWrite(0, 1);

  if(digitalRead(1) == 0)
    digitalWrite(0, 0);


I’ve made some progress. I followed the instructions in LVGL academy to create an external button that is clicked virtually by the windows random function.
I copied the same code into the PC simulator that I’m running on my RPi 3B+ and it compiled without issues. I modified the code to initiate the WiringPi library and instead of using the random generate number to click the button, I’m reading the state of the physical button.

The code compiles without issues but the button doesn’t get clicked when I press the physical button.
Below is my complete code - I’ve put all the code\functions in the main.c file for testing.

 * @file main

 *      INCLUDES
#define _DEFAULT_SOURCE /* needed for usleep() */
#include <stdlib.h>
#include <unistd.h>
#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/
#include <SDL2/SDL.h>
#include "lvgl/lvgl.h"
#include "lvgl/examples/lv_examples.h"
#include "lv_demos/lv_demo.h"
#include "lv_drivers/display/monitor.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/mouse.h"
#include "lv_drivers/indev/keyboard.h"
#include "lv_drivers/indev/mousewheel.h"

#include <wiringPi.h>//to control raspberry pi GPIO

static void hal_init(void);
static int tick_thread(void *data);

static lv_obj_t * scr1;
static void btn_event_cb(lv_event_t* e);
static lv_led_t* _led;
static lv_btn_t* btn;
int flag;

static void lv_gpio_init(void);
static bool button_read(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data);

void external_button_on_screen()
    lv_gpio_init();//call the GPIO initializing function

    //Creating the input device driver and all necessary declarations and functions
    static lv_indev_drv_t indev_drv;
    indev_drv.type = LV_INDEV_TYPE_BUTTON;
    indev_drv.read_cb = button_read;
    lv_indev_t* my_indev = lv_indev_drv_register(&indev_drv);

    //connecting the actual physical button press to a specific place on the screen
    const lv_point_t points[] =  {{400,220}};
    lv_indev_set_button_points(my_indev, points);

    //dot to check the coordinates of the button on screen only to be used in the above points array
    lv_obj_t* dot = lv_obj_create(lv_scr_act());
    lv_obj_set_size(dot, 10, 10);
    lv_obj_set_pos(dot, 400,220);

    //Creating a button on the screen for testing
    btn = lv_btn_create(lv_scr_act());
    lv_label_t* label = lv_label_create(btn);
    lv_label_set_text(label, "click me!");
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);

    //creating an LED on the screen for testing
    _led = lv_led_create(lv_scr_act());
    lv_obj_align(_led, LV_ALIGN_CENTER, 0, -50);


static void lv_gpio_init(void)

  wiringPiSetup () ;
  pinMode (0, OUTPUT) ;
  pinMode (1, INPUT) ;


static bool button_read(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data)

    //reading GPIO

    if(digitalRead(1) == 1)
        data->state = LV_INDEV_STATE_PR;//Either this
        digitalWrite(0, 1);//This will toggle the physical LED ON - works fine
        //lv_led_on(_led);//this will toggle the on-screen LED ON - works fine

    else if(digitalRead(1) == 0)
        data->state = LV_INDEV_STATE_REL;//OR this
        digitalWrite(0, 0);//This will toggle the physical LED OFF - works fine
        //lv_led_off(_led);//this will toggle the on-screen LED OFF - works fine
    return false;



static void btn_event_cb(lv_event_t* e)

    lv_event_code_t code = lv_event_get_code(e);

    if(code == LV_EVENT_PRESSED && flag == 0)

          lv_led_on(_led);//turn the on-screen LED ON
          flag = 1;

    else if (code == LV_EVENT_RELEASED && flag == 1)
            lv_led_off(_led);//Turn the on-screen LED OFF
            flag = 0;




int main(int argc, char **argv)

  (void)argc; /*Unused*/
  (void)argv; /*Unused*/

  /*Initialize LVGL*/

  /*Initialize the HAL (display, input devices, tick) for LVGL*/

//  lv_example_switch_1();
//  lv_example_calendar_1();
//  lv_example_btnmatrix_2();
//  lv_example_checkbox_1();
//  lv_example_colorwheel_1();
//  lv_example_chart_6();
//  lv_example_table_2();
//  lv_example_scroll_2();
//  lv_example_textarea_1();
//  lv_example_msgbox_1();
//  lv_example_dropdown_2();
//  lv_example_btn_1();
//  lv_example_scroll_1();
//  lv_example_tabview_1();
//  lv_example_tabview_1();
//  lv_example_flex_3();
//  lv_example_label_1();


  while(1) {
      /* Periodically call the lv_task handler.
       * It could be done in a timer interrupt or an OS task too.*/
      usleep(5 * 1000);

  return 0;


 * Initialize the Hardware Abstraction Layer (HAL) for the LVGL graphics
 * library
static void hal_init(void)
  /* Use the 'monitor' driver which creates window on PC's monitor to simulate a display*/
  /* Tick init.
   * You have to call 'lv_tick_inc()' in periodically to inform LittelvGL about
   * how much time were elapsed Create an SDL thread to do this*/
  SDL_CreateThread(tick_thread, "tick", NULL);

  /*Create a display buffer*/
  static lv_disp_draw_buf_t disp_buf1;
  static lv_color_t buf1_1[MONITOR_HOR_RES * 100];
  static lv_color_t buf1_2[MONITOR_HOR_RES * 100];
  lv_disp_draw_buf_init(&disp_buf1, buf1_1, buf1_2, MONITOR_HOR_RES * 100);

  /*Create a display*/
  static lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv); /*Basic initialization*/
  disp_drv.draw_buf = &disp_buf1;
  disp_drv.flush_cb = monitor_flush;
  disp_drv.hor_res = MONITOR_HOR_RES;
  disp_drv.ver_res = MONITOR_VER_RES;
  disp_drv.antialiasing = 1;

  lv_disp_t * disp = lv_disp_drv_register(&disp_drv);

  lv_theme_t * th = lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK, LV_FONT_DEFAULT);
  lv_disp_set_theme(disp, th);

  lv_group_t * g = lv_group_create();

  /* Add the mouse as input device
   * Use the 'mouse' driver which reads the PC's mouse*/
  static lv_indev_drv_t indev_drv_1;
  lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
  indev_drv_1.type = LV_INDEV_TYPE_POINTER;

  /*This function will be called periodically (by the library) to get the mouse position and state*/
  indev_drv_1.read_cb = mouse_read;
  lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);

  static lv_indev_drv_t indev_drv_2;
  lv_indev_drv_init(&indev_drv_2); /*Basic initialization*/
  indev_drv_2.type = LV_INDEV_TYPE_KEYPAD;
  indev_drv_2.read_cb = keyboard_read;
  lv_indev_t *kb_indev = lv_indev_drv_register(&indev_drv_2);
  lv_indev_set_group(kb_indev, g);
  static lv_indev_drv_t indev_drv_3;
  lv_indev_drv_init(&indev_drv_3); /*Basic initialization*/
  indev_drv_3.type = LV_INDEV_TYPE_ENCODER;
  indev_drv_3.read_cb = mousewheel_read;

  lv_indev_t * enc_indev = lv_indev_drv_register(&indev_drv_3);
  lv_indev_set_group(enc_indev, g);

  /*Set a cursor for the mouse*/
  LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/
  lv_obj_t * cursor_obj = lv_img_create(lv_scr_act()); /*Create an image object for the cursor */
  lv_img_set_src(cursor_obj, &mouse_cursor_icon);           /*Set the image source*/
  lv_indev_set_cursor(mouse_indev, cursor_obj);             /*Connect the image  object to the driver*/

 * A task to measure the elapsed time for LVGL
 * @param data unused
 * @return never return
static int tick_thread(void *data) {

    while(1) { 
        lv_tick_inc(5); /*Tell LittelvGL that 5 milliseconds were elapsed*/

    return 0;

I would be grateful if there are any suggestions ?