External buttons on ESP32 using lv_arduino

Hello all.
My hardware setup is an ESP32 with a 320x240 serial TFT that has a ILI9341 chip. I use the TFT_eSPI library to drive the TFT display.

The IDE I am using is Platformio with the LV_arduino v3.0.1 library.

I am initializing and reading the GPIO pins associated with the hardware buttons using the Arduino pinmode and digitalread functions. And this is in the lv_port_indev.c file.

The problem is that the Arduino functions don’t seem to work. The GPIO pins are not being initialized. So a button clicked event is never invoked.

I can initialize the read the GPIO pins fine if the code is in the main.c file. The GUI is a simple a screen with two buttons. I want to associate the hardware buttons with the GUI screen buttons, so that when I press a hardware button, the GUI button will depress.

Here is the main.c file:

#include <lvgl.h>
#include <Ticker.h>
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI(); /* TFT instance */

static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];
lv_disp_drv_t disp_drv;
lv_indev_drv_t indev_drv;


lv_obj_t *btn1;
lv_obj_t *btn2;
lv_obj_t *screenMain;
lv_obj_t *label;

static void event_handler_btn(lv_obj_t * obj, lv_event_t event) {
  if (event == LV_EVENT_PRESSED) {
    if (obj == btn1) {
      lv_label_set_text(label, "Button1");
      printf("Button1 clicked\n");
    }
    else if (obj == btn2) {
      lv_label_set_text(label, "Button2");
    }
 
  }
}

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);
  uint32_t wh = w * h;

  tft.startWrite();
  tft.setAddrWindow(area->x1, area->y1, w, h);
  while (wh--) tft.pushColor(color_p++->full);
  tft.endWrite();

  lv_disp_flush_ready(disp);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  tft.begin();
  tft.setRotation(0);
  analogReadResolution(10);
   
  lv_init();
  lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);

  /*Initialize the display*/

  lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res = 240;
  disp_drv.ver_res = 320;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.buffer = &disp_buf;
  lv_disp_drv_register(&disp_drv);

  /*Create screen objects*/

  screenMain = lv_obj_create(NULL, NULL);

  label = lv_label_create(screenMain, NULL);
  lv_label_set_long_mode(label, LV_LABEL_LONG_BREAK);
  lv_label_set_text(label, "Press a button");
  lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
  lv_obj_set_size(label, 240, 40);
  lv_obj_set_pos(label, 0, 15);

  btn1 = lv_btn_create(screenMain, NULL);
  lv_obj_set_event_cb(btn1, event_handler_btn);
  lv_obj_set_width(btn1, 90);
  lv_obj_set_height(btn1, 32);
  //lv_obj_set_pos(btn1, 32, 100);
  lv_obj_set_pos(btn1, 15, 100);

  lv_obj_t * label1 = lv_label_create(btn1, NULL);
  lv_label_set_text(label1, "Hello");

  btn2 = lv_btn_create(screenMain, NULL);
  lv_obj_set_event_cb(btn2, event_handler_btn);
  lv_obj_set_width(btn2, 90);
  lv_obj_set_height(btn2, 32);
  //lv_obj_set_pos(btn2, 142, 100);
  lv_obj_set_pos(btn2, 125, 100);

  lv_obj_t * label2 = lv_label_create(btn2, NULL);
  lv_label_set_text(label2, "Goodbye");

  lv_scr_load(screenMain);

}

void loop() {
  // put your main code here, to run repeatedly:
  
  lv_task_handler(); /* let the GUI do its work */
}

Here is the lv_port_indev.c file:

/**
 * @file lv_port_indev.c
 *
 */

 /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1

/*********************
 *      INCLUDES
 *********************/
#include "lv_port_indev.h"

/*********************
 *      DEFINES
 *********************/
#define HIGH 1
#define LOW 0
#define INPUT 1
/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/

static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);

static void mouse_init(void);
static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool mouse_is_pressed(void);
static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y);

static void keypad_init(void);
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);

static void encoder_init(void);
//static bool encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static void encoder_handler(void);

static void button_init(void);
static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static int8_t button_get_pressed_id(void);
static bool button_is_pressed(uint8_t id);

/**********************
 *  STATIC VARIABLES
 **********************/
lv_indev_t * indev_touchpad;
lv_indev_t * indev_mouse;
lv_indev_t * indev_keypad;
lv_indev_t * indev_encoder;
lv_indev_t * indev_button;

lv_obj_t *label3; 

const int button1 = 36;     // the number of the pushbutton pin
int buttonState = 0;        // variable for reading the pushbutton status
const int button2 = 34;		// the number of the second pushbutton pin
static int32_t encoder_diff;
static lv_indev_state_t encoder_state;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/


void lv_port_indev_init(void)
{
    /**
     * Here you will find example implementation of input devices supported by LittelvGL:
     *  - Touchpad
     *  - Mouse (with cursor support)
     *  - Keypad (supports GUI usage only with key)
     *  - Encoder (supports GUI usage only with: left, right, push)
     *  - Button (external buttons to press points on the screen)
     *
     *  The `..._read()` function are only examples.
     *  You should shape them according to your hardware
     */

    static lv_indev_drv_t indev_drv;

    /*------------------
     * Touchpad
     * -----------------*/

    /*Initialize your touchpad if you have*/
    touchpad_init();

    /*Register a touchpad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touchpad_read;
    indev_touchpad = lv_indev_drv_register(&indev_drv);

    /*------------------
     * Mouse
     * -----------------*/

    /*Initialize your touchpad if you have*/
    mouse_init();

    /*Register a mouse input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = mouse_read;
    indev_mouse = lv_indev_drv_register(&indev_drv);

    /*Set cursor. For simplicity set a HOME symbol now.*/
    lv_obj_t * mouse_cursor = lv_img_create(lv_disp_get_scr_act(NULL), NULL);
    lv_img_set_src(mouse_cursor, LV_SYMBOL_HOME);
    lv_indev_set_cursor(indev_mouse, mouse_cursor);

    /*------------------
     * Keypad
     * -----------------*/

    /*Initialize your keypad or keyboard if you have*/
    keypad_init();

    /*Register a keypad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_KEYPAD;
    indev_drv.read_cb = keypad_read;
    indev_keypad = lv_indev_drv_register(&indev_drv);

    /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
     *add objects to the group with `lv_group_add_obj(group, obj)`
     *and assign this input device to group to navigate in it:
     *`lv_indev_set_group(indev_keypad, group);`*/

	// my stuff
	label3 = lv_label_create(lv_scr_act(), NULL);
    /*------------------
     * Encoder
     * -----------------*/

    /*Initialize your encoder if you have*/
    encoder_init();

    /*Register a encoder input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_ENCODER;
    //indev_drv.read_cb = encoder_read;
    indev_encoder = lv_indev_drv_register(&indev_drv);

    /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
     *add objects to the group with `lv_group_add_obj(group, obj)`
     *and assign this input device to group to navigate in it:
     *`lv_indev_set_group(indev_encoder, group);`*/

    /*------------------
     * Button
     * -----------------*/

    /*Initialize your button if you have*/
    button_init();

    /*Register a button input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_BUTTON;
    indev_drv.read_cb = button_read;
    indev_button = lv_indev_drv_register(&indev_drv);

    /*Assign buttons to points on the screen*/
    static const lv_point_t btn_points[2] = {
            {15, 100},   	/*Button 0 -> x:10; y:10*/
            {125, 100},  	/*Button 1 -> x:40; y:100*/
    };
    lv_indev_set_button_points(indev_button, btn_points);
	
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/*------------------
 * Touchpad
 * -----------------*/

/*Initialize your touchpad*/
static void touchpad_init(void)
{
    /*Your code comes here*/
}

/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    /*Save the pressed coordinates and the state*/
    if(touchpad_is_pressed()) {
        touchpad_get_xy(&last_x, &last_y);
        data->state = LV_INDEV_STATE_PR;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }

    /*Set the last pressed coordinates*/
    data->point.x = last_x;
    data->point.y = last_y;
}

/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
    /*Your code comes here*/

    return false;
}

/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    /*Your code comes here*/

    (*x) = 0;
    (*y) = 0;
}

/*------------------
 * Mouse
 * -----------------*/

/*Initialize your mouse*/
static void mouse_init(void)
{
    /*Your code comes here*/
}

/*Will be called by the library to read the mouse*/
static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    /*Get the current x and y coordinates*/
    mouse_get_xy(&data->point.x, &data->point.y);

    /*Get whether the mouse button is pressed or released*/
    if(mouse_is_pressed()) {
        data->state = LV_INDEV_STATE_PR;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }
}

/*Return true is the mouse button is pressed*/
static bool mouse_is_pressed(void)
{
    /*Your code comes here*/

    return false;
}

/*Get the x and y coordinates if the mouse is pressed*/
static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    /*Your code comes here*/

    (*x) = 0;
    (*y) = 0;
}

/*------------------
 * Keypad
 * -----------------*/

/*Initialize your keypad*/
static void keypad_init(void)
{
    /*Your code comes here*/
}

/*Will be called by the library to read the mouse*/
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static uint32_t last_key = 0;

    /*Get the current x and y coordinates*/
    mouse_get_xy(&data->point.x, &data->point.y);

    /*Get whether the a key is pressed and save the pressed key*/
    uint32_t act_key = keypad_get_key();
    if(act_key != 0) {
        data->state = LV_INDEV_STATE_PR;

        /*Translate the keys to LVGL control characters according to your key definitions*/
        switch(act_key) {
        case 1:
            act_key = LV_KEY_NEXT;
            break;
        case 2:
            act_key = LV_KEY_PREV;
            break;
        case 3:
            act_key = LV_KEY_LEFT;
            break;
        case 4:
            act_key = LV_KEY_RIGHT;
            break;
        case 5:
            act_key = LV_KEY_ENTER;
            break;
        }

        last_key = act_key;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }

    data->key = last_key;
}

/*Get the currently being pressed key.  0 if no key is pressed*/
static uint32_t keypad_get_key(void)
{
    /*Your code comes here*/

    return 0;
}

/*------------------
 * Encoder
 * -----------------*/

/*Initialize your keypad*/
static void encoder_init(void)
{
    /*Your code comes here*/
}

/*Will be called by the library to read the encoder*/
static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{

    data->enc_diff = encoder_diff;
    data->state = encoder_state;
}

/*Call this function in an interrupt to process encoder events (turn, press)*/
static void encoder_handler(void)
{
    /*Your code comes here*/

    encoder_diff += 0;
    encoder_state = LV_INDEV_STATE_REL;
}

/*------------------
 * Button
 * -----------------*/

/*Initialize your buttons*/
static void button_init(void)
{
    /*Your code comes here*/
  // initialize the btn1 pin as an input:
  pinMode(button1, INPUT);
  // initialize the btn2 pin as an input:
  pinMode(button2, INPUT);

}

/*Will be called by the library to read the button*/
static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{

    static uint8_t last_btn = 0;

    /*Get the pressed button's ID*/
    int8_t btn_act = button_get_pressed_id();

    if(btn_act >= 0) {
        data->state = LV_INDEV_STATE_PR;
        last_btn = btn_act;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }
}

/*Get ID  (0, 1, 2 ..) of the pressed button*/
static int8_t button_get_pressed_id(void)
{
    uint8_t i;

    /*Check to buttons see which is being pressed (assume there are 2 buttons)*/
    for(i = 0; i < 2; i++) {
        /*Return the pressed button's ID*/
        if(button_is_pressed(i)) {
            return i;
        }
    }

    /*No button pressed*/
    return -1;
}

/*Test if `id` button is pressed or not*/
static bool button_is_pressed(uint8_t id)
{

    /*Your code comes here*/
  // read the state of the pushbutton value:
  if(id == 0)
	buttonState = digitalRead(button1);
  if(id == 1)
	buttonState = digitalRead(button2);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == LOW) {
    // turn LED on:
    return true;
  } else {
    return false;
  }
  
}

#else /*Enable this file at the top*/

/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif

 

Here is the trace output. The button event never happens when I press the hardware button.

— Miniterm on COM3 9600,8,N,1 —
— Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H —
Trace: lv_init started (lv_obj.c #125 lv_init())
Info: lv_init ready (lv_obj.c #163 lv_init())
Trace: Screen create started (lv_obj.c #205 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Trace: Screen create started (lv_obj.c #205 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Trace: Screen create started (lv_obj.c #205 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Trace: Screen create started (lv_obj.c #205 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Trace: label create started (lv_label.c #79 lv_label_create())
Trace: Object create started (lv_obj.c #237 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Info: label created (lv_label.c #167 lv_label_create())
Trace: button create started (lv_btn.c #61 lv_btn_create())
Trace: container create started (lv_cont.c #70 lv_cont_create())
Trace: Object create started (lv_obj.c #237 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Info: container created (lv_cont.c #118 lv_cont_create())
Info: button created (lv_btn.c #106 lv_btn_create())
Trace: label create started (lv_label.c #79 lv_label_create())
Trace: Object create started (lv_obj.c #237 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Info: label created (lv_label.c #167 lv_label_create())
Trace: button create started (lv_btn.c #61 lv_btn_create())
Trace: container create started (lv_cont.c #70 lv_cont_create())
Trace: Object create started (lv_obj.c #237 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Info: container created (lv_cont.c #118 lv_cont_create())
Info: button created (lv_btn.c #106 lv_btn_create())
Trace: label create started (lv_label.c #79 lv_label_create())
Trace: Object create started (lv_obj.c #237 lv_obj_create())
Info: Object create ready (lv_obj.c #392 lv_obj_create())
Info: label created (lv_label.c #167 lv_label_create())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_refr_task: started (lv_refr.c #177 _lv_disp_refr_task())
Trace: lv_refr_task: ready (lv_refr.c #309 _lv_disp_refr_task())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())
Trace: lv_task_handler started (lv_task.c #74 lv_task_handler())
Trace: lv_task_handler ready (lv_task.c #192 lv_task_handler())

What am I doing wrong.

The GUI is created just fine.

Bump. Anyone?