Buttons displayed using the lvgl library do not work (ili9488 + ESP32)

Description

I placed a large button in the center of the screen and asked chat GPT to create a simple program to see if touch operations would work properly.I was able to display the button, but there was no response to touch operations at all.

The string “Simple Button Pressed!” that should be output on the monitor when the button is touched

// シンプルなボタンイベントハンドラ
static void simple_button_event_handler(lv_event_t *e) {
    lv_event_code_t code = lv_event_get_code(e);
    if(code == LV_EVENT_CLICKED) {
        Serial.println("Simple Button Pressed!");
    }
}

But this string “Simple Button Pressed!” is not output at all when the button is pressed.
In that case, I think the interrupt function simple_button_event_handler is not working at all, and I have no idea why it is not working.

If you have any suggestions for improvement, please let us know.

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

Board is ili9488 + ESP32.
I use platformio.

What do you want to achieve?

I want to call simple_button_event_handler when the button is pressed.

What have you tried so far?

Code to reproduce

Add the relevant code snippets here.

The code block(s) should be between ```c and ``` tags:

//main.cファイル内容

#include <Arduino.h>
#include <lvgl.h>
#include <TFT_eSPI.h> // ILI9488ドライバを含むライブラリ

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p); // ILI9488用のflush関数

TFT_eSPI tft = TFT_eSPI(); // TFTインスタンスを作成

static lv_style_t style_pressed;

// ボタンイベントハンドラ
static void button_event_handler(lv_event_t *e) {
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *button = lv_event_get_target(e);
    if(code == LV_EVENT_CLICKED) {
        Serial.println("Button Pressed!");  // デバッグメッセージを追加

        // ボタンの背景色を変更するスタイルを作成

        lv_style_init(&style_pressed);
        lv_style_set_bg_color(&style_pressed, lv_color_make(0xFF, 0xA5, 0x00)); // オレンジ色に設定

        // スタイルをボタンに適用
        lv_obj_add_style(button, &style_pressed, 0);

        const char *btn_text = lv_label_get_text(lv_obj_get_child(button, 0));
        lv_obj_t *label = (lv_obj_t *)lv_event_get_user_data(e); // 修正:ユーザーデータを正しく取得
        lv_label_set_text(label, btn_text);
    }
}

// シンプルなボタンイベントハンドラ
static void simple_button_event_handler(lv_event_t *e) {
    lv_event_code_t code = lv_event_get_code(e);
    if(code == LV_EVENT_CLICKED) {
        Serial.println("Simple Button Pressed!");
    }
}


static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[320 * 10]; // 描画バッファを定義


static lv_disp_drv_t disp_drv;

void setup() {
    Serial.begin(115200);  // シリアル通信を開始
    lv_init(); // LVGLを初期化
    tft.begin(); // TFTを初期化
    tft.setRotation(1); // 必要に応じて画面の向きを設定

    //uint16_t calData[5] = { 275, 3620, 264, 3532, 1 };
    //tft.setTouch(calData);


    lv_disp_draw_buf_init(&draw_buf, buf, NULL, 320 * 10);

    // ディスプレイドライバを設定

    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 480;
    disp_drv.ver_res = 320;
    disp_drv.flush_cb = my_disp_flush; // ここでILI9488用のflush関数を設定
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    // シンプルなボタンを作成
    lv_obj_t *btn = lv_btn_create(lv_scr_act());
    lv_obj_set_size(btn, 200, 100);
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
    lv_obj_add_event_cb(btn, simple_button_event_handler, LV_EVENT_ALL, NULL);
}

void loop() {
    lv_timer_handler(); // LVGLタイマーを処理
    delay(5);
}

// ILI9488用のflush関数(ディスプレイドライバの設定に必要)
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((uint16_t *)&color_p->full, w * h, true);
    tft.endWrite();
    lv_disp_flush_ready(disp);
}

Supplement

Furthermore, we have confirmed that the screen program below created using only the TFT_eSPI library can also be used with touch operations correctly.
So, aren’t there any problems with the physical wiring between the ESP32 board and ili9488 and the various settings of the TFT_eSPI library? I think···
Is there anything else I should check?

TFT_eSPI only code (circuit test)

#include <Arduino.h>
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <TFT_eSPI.h>

// Wi-Fiの設定
const char *ssid = "my_SSID";    // ここにSSIDを入力
const char *password = "my_password"; // ここにWi-Fiパスワードを入力

// NTPサーバの設定
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

void drawButtons();
void drawButton(int buttonIndex, uint32_t color);

// TFTディスプレイの設定
TFT_eSPI tft = TFT_eSPI(); // TFT_eSPIオブジェクトの作成

void setup() {
  Serial.begin(115200);

  // Wi-Fi接続
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi Connected");

  // TFTディスプレイの初期化
  tft.init();
  tft.setRotation(1); // 画面の向きを調整

  uint16_t calData[5] = { 231, 3567, 344, 3355, 7 };
  tft.setTouch(calData);

  // NTPサーバに接続
  timeClient.begin();
  timeClient.setTimeOffset(3600 * 9); // 日本時間の場合、UTC+9時間

  // ボタンの表示(一度だけ実行)
  drawButtons();

}

void drawButtons() {
    for (int i = 0; i < 10; i++) {
        drawButton(i, TFT_BLACK); // デフォルトの色でボタンを描画
    }
}


void drawButton(int buttonIndex, uint32_t color) {
  int x = (buttonIndex % 5) * 80; // ボタンのX座標を更新
  int y = (buttonIndex / 5) * 60 + 100; // ボタンのY座標を更新
  tft.fillRect(x, y, 80, 60, color); // 新しいサイズでボタンの背景を描画
  tft.drawRect(x, y, 80, 60, TFT_WHITE); // 新しいサイズでボタンの枠を描画
  tft.setCursor(x + 30, y + 25); // テキストの位置を調整
  tft.print(buttonIndex + 1); // ボタンの番号を描画
}


// タッチされたボタンのインデックスと時間を記録する変数
int lastButtonIndex = -1;
unsigned long lastButtonTime = 0;

// グローバル変数として前回の時刻を記録するための変数を追加
String lastTime = "";

void loop() {
  timeClient.update();

  String currentTime = timeClient.getFormattedTime();
  if (currentTime != lastTime) {
    // 時計表示エリアのみを更新
    tft.fillRect(0, 0, 240, 80, TFT_BLACK); // 時計表示エリアをクリア
    tft.setTextColor(TFT_WHITE);
    tft.setTextSize(2);
    tft.setCursor(10, 10);
    tft.print("Time: ");
    tft.setTextSize(3);
    tft.setCursor(10, 40);
    tft.print(currentTime);

    lastTime = currentTime; // 現在の時刻を記録
    // シリアルモニターに時刻を出力
    Serial.println(timeClient.getFormattedTime());
  }

  // タッチ座標の変数を宣言
  uint16_t t_x, t_y;

 // タッチ入力の検出
  if (tft.getTouch(&t_x, &t_y)) {
    int buttonIndex = (t_x / 80) + (t_y - 100) / 60 * 5; // 幅80ピクセル、高さ60ピクセルに基づく計算
    if (buttonIndex >= 0 && buttonIndex < 10) {
      drawButton(buttonIndex, TFT_RED);
      lastButtonIndex = buttonIndex;
    }
  } else {
    // タッチが終了した場合、ボタンの色を戻す
    if (lastButtonIndex != -1) {
      drawButton(lastButtonIndex, TFT_BLACK);
      lastButtonIndex = -1;
    }
  }

  delay(10);

}

Screenshot and/or video

If possible, add screenshots and/or videos about the current state.

You have no touch interface, see: Input device interface — LVGL documentation. The example provided should be quite straightforward to implement, you will need to use tft.getTouch() inside of that callback assigend to the read.cb struct member. Good luck!

The button and its callback seem to be properly configured,
However you do not seem to use lv_tick_inc(). See: Set up a project — LVGL documentation. I am not sure if this is 100% necessary but I would advise implementing this if the touch driver does not work.

1 Like

Thank you for your reply.

After modifying the contents of the setup() function,

・my_touchpad_read
・simple_button_event_handler

Now you can call these two.

I was relieved that the circuit was working properly.

void setup() {
    Serial.begin(115200);  // シリアル通信を開始
    lv_init(); // LVGLを初期化
    tft.begin(); // TFTを初期化
    tft.setRotation(1); // 必要に応じて画面の向きを設定

    uint16_t calData[5] = { 231, 3567, 344, 3355, 7 };
    tft.setTouch(calData);

    //uint16_t calData[5] = { 275, 3620, 264, 3532, 1 };
    //tft.setTouch(calData);


    lv_disp_draw_buf_init(&draw_buf, buf, NULL, 320 * 10);

    // ディスプレイドライバを設定

    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 480;
    disp_drv.ver_res = 320;
    disp_drv.flush_cb = my_disp_flush; // ここでILI9488用のflush関数を設定
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    /*Register at least one display before you register any input devices*/
    lv_disp_drv_register(&disp_drv);

    // タッチパッド入力デバイスを初期化して登録
    static 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_t *my_indev = lv_indev_drv_register(&indev_drv); // デバイスを登録

    #if 1
    // シンプルなボタンを作成
    lv_obj_t *btn = lv_btn_create(lv_scr_act());
    lv_obj_set_size(btn, 200, 100);
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
    lv_obj_add_event_cb(btn, simple_button_event_handler, LV_EVENT_ALL, NULL);
    #endif

}

Overall code content

#include <Arduino.h>
#include <lvgl.h>
#include <TFT_eSPI.h> // ILI9488ドライバを含むライブラリ

//#define DEBUG_MODE // デバッグモードを有効にする。デバッグが不要な場合はこの行をコメントアウト


void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p); // ILI9488用のflush関数

TFT_eSPI tft = TFT_eSPI(); // TFTインスタンスを作成

static lv_style_t style_pressed;


void my_touchpad_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {


    uint16_t touchX, touchY;

    // タッチイベントの確認
    bool touched = tft.getTouch(&touchX, &touchY);

    

    if (touched) {

        data->point.x = touchX;
        data->point.y = touchY;
        data->state = LV_INDEV_STATE_PR; // プレス状態

        #ifdef DEBUG_MODE
        Serial.println("TFT getTouch!");
        // タッチ座標をシリアル出力
        Serial.print("Touch X: ");
        Serial.print(touchX);
        Serial.print(", Touch Y: ");
        Serial.println(touchY);
        #endif
    } else {
        data->state = LV_INDEV_STATE_REL; // リリース状態
    }

    //return false; // データが常に有効であることを示す

}





// シンプルなボタンイベントハンドラ
static void simple_button_event_handler(lv_event_t *e) {
    lv_event_code_t code = lv_event_get_code(e);
    if(code == LV_EVENT_CLICKED) {
        Serial.println("Simple Button Pressed!");
    }
}


static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[320 * 10]; // 描画バッファを定義


static lv_disp_drv_t disp_drv;

void setup() {
    Serial.begin(115200);  // シリアル通信を開始
    lv_init(); // LVGLを初期化
    tft.begin(); // TFTを初期化
    tft.setRotation(1); // 必要に応じて画面の向きを設定

    uint16_t calData[5] = { 231, 3567, 344, 3355, 7 };
    tft.setTouch(calData);

    //uint16_t calData[5] = { 275, 3620, 264, 3532, 1 };
    //tft.setTouch(calData);


    lv_disp_draw_buf_init(&draw_buf, buf, NULL, 320 * 10);

    // ディスプレイドライバを設定

    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 480;
    disp_drv.ver_res = 320;
    disp_drv.flush_cb = my_disp_flush; // ここでILI9488用のflush関数を設定
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    /*Register at least one display before you register any input devices*/
    lv_disp_drv_register(&disp_drv);

    // タッチパッド入力デバイスを初期化して登録
    static 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_t *my_indev = lv_indev_drv_register(&indev_drv); // デバイスを登録

    #if 1
    // シンプルなボタンを作成
    lv_obj_t *btn = lv_btn_create(lv_scr_act());
    lv_obj_set_size(btn, 200, 100);
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
    lv_obj_add_event_cb(btn, simple_button_event_handler, LV_EVENT_ALL, NULL);
    #endif

}

void loop() {

    lv_timer_handler(); // LVGLタイマーを処理
    delay(5);
}

// ILI9488用のflush関数(ディスプレイドライバの設定に必要)
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((uint16_t *)&color_p->full, w * h, true);
    tft.endWrite();
    lv_disp_flush_ready(disp);
}