LVGL on Raspberry Pi Pico 2 (Arduino framework/arduino-pico) with 320x480 TFT SPI ST7796 + FT6336U Capacitive Touch

LVGL (Light and Versatile Graphics Library) is a popular free and open-source embedded graphics library to create beautiful UIs for any MCU, MPU and display type, supports working with the TFT_eSPI library.

Following previous exercise "Raspberry Pi Pico 2 drive 4.0" 320x480 TFT SPI ST7796 with FT6336U Capacitive Touch + SD, in Arduino framework", this exercise show steps to add LVGL on top of TFT_eSPI, on Raspberry Pi Pico 2.



Install lvgl library in Arduino IDE's Library Manager.

Using LVGL with Arduino requires some extra steps:
Be sure to read the docs here: https://docs.lvgl.io/master/integration/framework/arduino.html

Follow these configuration steps:
- Go to the directory of the installed Arduino libraries
- Go to lvgl and copy lv_conf_template.h as lv_conf.h into the Arduino Libraries directory next to the lvgl library folder.

- Open lv_conf.h and change:

  To enable the content of the file:
  change the first #if 0 to #if 1

  To use TFT_eSPI:
  change LV_USE_TFT_ESPI 0 to 1

- Test with example:
  > File > Examples > lvgl > arduino > LVGL_Arduino

My exercise code:

pico2_LVGL_Arduino.ino
/*
lvgl exercise run on Raspberry Pi Pico 2 in Arduino framework,
with 4.0" 320x480 TFT SPI ST7796 with FT6336U Capacitive Touch.

Use TFT_eSPI to interface with ST7796.

To setup TFT_eSPI and connection, read:
https://coxxect.blogspot.com/2025/04/raspberry-pi-pico-2-drive-40-480x320.html

*/

/*Using LVGL with Arduino requires some extra steps:
 *Be sure to read the docs here: https://docs.lvgl.io/master/integration/framework/arduino.html  */

#include <lvgl.h>
#include "lv_conf.h"

#include <TFT_eSPI.h>

// for FT6336U cap touch
#include <Wire.h>
#define CTP_RST 14  // option, can connect to 3V3.
#define FT6336U_ADDR 0x38

/*Set to your screen resolution and rotation*/
#define TFT_HOR_RES   320 //320
#define TFT_VER_RES   480 //240
#define TFT_ROTATION  LV_DISPLAY_ROTATION_0

/*LVGL draw into this buffer, 1/10 screen size usually works well. The size is in bytes*/
#define DRAW_BUF_SIZE (TFT_HOR_RES * TFT_VER_RES / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];

/* LVGL calls it when a rendered image needs to copied to the display*/
void my_disp_flush( lv_display_t *disp, const lv_area_t *area, uint8_t * px_map)
{
    Serial.println("my_disp_flush()");
    /*Copy `px map` to the `area`*/

    /*Call it to tell LVGL you are ready*/
    lv_display_flush_ready(disp);
}

/*Read the touchpad*/
void my_touchpad_read( lv_indev_t * indev, lv_indev_data_t * data )
{
    if (get_touch_count()>0){
        data->state = LV_INDEV_STATE_PRESSED;

        int x, y;
        
        Wire.beginTransmission(FT6336U_ADDR);
        Wire.write(0x03);
        Wire.endTransmission();
        Wire.requestFrom(FT6336U_ADDR, 2);
        x = (Wire.read() & 0x0f) << 8 | Wire.read();
        
        Wire.beginTransmission(FT6336U_ADDR);
        Wire.write(0x05);
        Wire.endTransmission();
        Wire.requestFrom(FT6336U_ADDR, 2);
        y = (Wire.read() & 0x0f) << 8 | Wire.read();

        data->point.x = x;
        data->point.y = y;

    }else{
        data->state = LV_INDEV_STATE_RELEASED;
    }

}

/*use Arduinos millis() as tick source*/
static uint32_t my_tick(void)
{
    return millis();
}


int get_touch_count(){
    // Register 0x02;
    // Touch count, max 2
    Wire.beginTransmission(FT6336U_ADDR);
    Wire.write(0x02);
    Wire.endTransmission();
    Wire.requestFrom(FT6336U_ADDR, 1);
    return(Wire.read());
}

// Read x, y
// May be have to exchange/adjust x, y according to screen rotation.
void get_touch_point(int *x, int *y) {
    Wire.beginTransmission(FT6336U_ADDR);
    Wire.write(0x03);
    Wire.endTransmission();
    Wire.requestFrom(FT6336U_ADDR, 2);
    *x = (Wire.read() & 0x0f) << 8 | Wire.read();

    Wire.beginTransmission(FT6336U_ADDR);
    Wire.write(0x05);
    Wire.endTransmission();
    Wire.requestFrom(FT6336U_ADDR, 2);
    *y = (Wire.read() & 0x0f) << 8 | Wire.read();
}
void setup()
{
    String LVGL_Arduino = "Hello LVGL! ";
    LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();

    delay(2000);
    Serial.begin( 115200 );
    delay(1000);
    Serial.println( LVGL_Arduino );

    // default SDA = 4
    // default SCL = 5
    Serial.println("SDA " + String(SDA));
    Serial.println("SCL " + String(SCL));
    Wire.begin();   // I2C
    delay(100);
    // Optional CTS_RST
    // In my test, CTS_RST can be connect to 3V3.
    Serial.println("Hardware Reset on CTP_RST: ");
    pinMode(CTP_RST, OUTPUT);
    digitalWrite(CTP_RST, HIGH);
    delay(100);
    digitalWrite(CTP_RST, LOW);
    delay(100);
    digitalWrite(CTP_RST, HIGH);
    delay(100);

    lv_init();

    /*Set a tick source so that LVGL will know how much time elapsed. */
    lv_tick_set_cb(my_tick);

    lv_display_t * disp;
    
    //Use TFT_eSPI interface
    /*TFT_eSPI can be enabled lv_conf.h to initialize the display in a simple way*/
    disp = lv_tft_espi_create(TFT_HOR_RES, TFT_VER_RES, draw_buf, sizeof(draw_buf));
    lv_display_set_rotation(disp, TFT_ROTATION);

    /*Initialize the (dummy) input device driver*/
    lv_indev_t * indev = lv_indev_create();
    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*Touchpad should have POINTER type*/
    lv_indev_set_read_cb(indev, my_touchpad_read);

    lv_my_exercise_buttons();

    Serial.println( "Setup done" );

}

void loop()
{
    lv_timer_handler(); /* let the GUI do its work */
    delay(100); /* let this time pass */

}

/* Callback function */
static void btn_event_cb(lv_event_t * e) {
    /* Get the button object */
    lv_obj_t * obj = (lv_obj_t *)lv_event_get_target(e);

    /* Get user data (color) */
    lv_color_t color = *(lv_color_t *)lv_event_get_user_data(e);

    /* Change screen background color */
    lv_obj_set_style_bg_color(lv_scr_act(), color, LV_PART_MAIN);
}

/* Function to create buttons */
lv_obj_t * create_button(lv_obj_t * parent, const char * text, lv_color_t * color) {
    lv_obj_t * btn = lv_button_create(parent);
    lv_obj_set_size(btn, LV_PCT(100), LV_SIZE_CONTENT);

    /* Set button background color */
    lv_obj_set_style_bg_color(btn, *color, LV_PART_MAIN);

    lv_obj_t * label = lv_label_create(btn);
    lv_label_set_text(label, text);
    lv_obj_center(label);

    /* Attach the common callback and pass the color as user data */
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, color);

    return btn;
}

/**
 * A simple row and a column layout with flexbox
 */
void lv_my_exercise_buttons(void)
{
    /*Create a container with COLUMN flex direction*/
    lv_obj_t * cont_main = lv_obj_create(lv_screen_active());
    lv_obj_set_size(cont_main, TFT_HOR_RES-50, TFT_VER_RES-50);
    lv_obj_align(cont_main, LV_ALIGN_CENTER, 0, 0); //lv_obj_align_to(cont_col, cont_row, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
    lv_obj_set_flex_flow(cont_main, LV_FLEX_FLOW_COLUMN);

    lv_obj_t *label_top = lv_label_create(lv_screen_active());
    lv_label_set_text(label_top, "Arduino LVGL Exercise on Pico 2");
    lv_obj_align(label_top, LV_ALIGN_TOP_MID, 0, 0);

    lv_obj_t *label_bottom = lv_label_create(lv_screen_active());
    lv_label_set_text(label_bottom, "coXXect.blogspot.com");
    lv_obj_align(label_bottom, LV_ALIGN_BOTTOM_MID, 0, 0);

    /* Define static colors */
    static lv_color_t red_color = lv_palette_main(LV_PALETTE_RED);
    static lv_color_t green_color = lv_palette_main(LV_PALETTE_GREEN);
    static lv_color_t blue_color = lv_palette_main(LV_PALETTE_BLUE);

    /* Create buttons */
    create_button(cont_main, "RED", &red_color);
    create_button(cont_main, "GREEN", &green_color);
    create_button(cont_main, "BLUE", &blue_color);

}



More Exercise:

Create LVGL Arc(lv_arc) to control NeoPixel ring.




Connection between 8X RGB NeoPixel Ring and Raspberry Pi Pico 2:

VCC - VBUS
GND - GND
DI  - GP0

pico2_LVGL_Arduino_arc_NeoPixel.ino
/*
LVGL exercise run on Raspberry Pi Pico 2 in Arduino framework,
with 4.0" 320x480 TFT SPI ST7796 with FT6336U Capacitive Touch.

Create LVGL Arc(lv_arc) to control NeoPixel ring.

Raspberry Pi Pico 2 drive 4.0" 320x480 TFT SPI ST7796 with FT6336U Capacitive Touch + SD, 
in Arduino framework.
- Setup TFT_eSPI to display on ST7796 SPI TFT.
- I2C interface FT6336U cap. touch.
https://coxxect.blogspot.com/2025/04/raspberry-pi-pico-2-drive-40-480x320.html

LVGL on Raspberry Pi Pico 2 (Arduino framework/arduino-pico)
with 320x480 TFT SPI ST7796 + FT6336U Capacitive Touch.
https://coxxect.blogspot.com/2025/05/lvgl-on-raspberry-pi-pico-2-arduino.html

*/

#include <lvgl.h>
#include "lv_conf.h"

#include <TFT_eSPI.h>
#include <Adafruit_NeoPixel.h>

// for FT6336U cap touch
#include <Wire.h>
#define CTP_RST 14  // option, can connect to 3V3.
#define FT6336U_ADDR 0x38

#define PIN_NeoPixel    0
#define NUMPIXELS 8

/*Set to your screen resolution and rotation*/
#define TFT_HOR_RES   320 //320
#define TFT_VER_RES   480 //240
#define TFT_ROTATION  LV_DISPLAY_ROTATION_0

/*LVGL draw into this buffer, 1/10 screen size usually works well. The size is in bytes*/
#define DRAW_BUF_SIZE (TFT_HOR_RES * TFT_VER_RES / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];

String LVGL_Arduino = "LVGL! ";

Adafruit_NeoPixel pixels(NUMPIXELS, PIN_NeoPixel, NEO_GRB + NEO_KHZ800);

/*Read the touchpad*/
void my_touchpad_read( lv_indev_t * indev, lv_indev_data_t * data )
{
    if (get_touch_count()>0){
        data->state = LV_INDEV_STATE_PRESSED;

        int x, y;
        
        Wire.beginTransmission(FT6336U_ADDR);
        Wire.write(0x03);
        Wire.endTransmission();
        Wire.requestFrom(FT6336U_ADDR, 2);
        x = (Wire.read() & 0x0f) << 8 | Wire.read();
        
        Wire.beginTransmission(FT6336U_ADDR);
        Wire.write(0x05);
        Wire.endTransmission();
        Wire.requestFrom(FT6336U_ADDR, 2);
        y = (Wire.read() & 0x0f) << 8 | Wire.read();

        data->point.x = x;
        data->point.y = y;

    }else{
        data->state = LV_INDEV_STATE_RELEASED;
    }

}

/*use Arduinos millis() as tick source*/
static uint32_t my_tick(void)
{
    return millis();
}

int get_touch_count(){
    // Register 0x02;
    // Touch count, max 2
    Wire.beginTransmission(FT6336U_ADDR);
    Wire.write(0x02);
    Wire.endTransmission();
    Wire.requestFrom(FT6336U_ADDR, 1);
    return(Wire.read());
}

// Read x, y
// May be have to exchange/adjust x, y according to screen rotation.
void get_touch_point(int *x, int *y) {
    Wire.beginTransmission(FT6336U_ADDR);
    Wire.write(0x03);
    Wire.endTransmission();
    Wire.requestFrom(FT6336U_ADDR, 2);
    *x = (Wire.read() & 0x0f) << 8 | Wire.read();

    Wire.beginTransmission(FT6336U_ADDR);
    Wire.write(0x05);
    Wire.endTransmission();
    Wire.requestFrom(FT6336U_ADDR, 2);
    *y = (Wire.read() & 0x0f) << 8 | Wire.read();
}
void setup()
{
    
    LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();

    delay(2000);
    Serial.begin( 115200 );
    delay(1000);
    Serial.println( LVGL_Arduino );

    Wire.begin();   // I2C
    delay(100);

    pinMode(CTP_RST, OUTPUT);
    digitalWrite(CTP_RST, HIGH);
    delay(100);
    digitalWrite(CTP_RST, LOW);
    delay(100);
    digitalWrite(CTP_RST, HIGH);
    delay(100);

    pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
    pixels.setBrightness(10);
    pixels.clear();
    pixels.show();

    lv_init();

    /*Set a tick source so that LVGL will know how much time elapsed. */
    lv_tick_set_cb(my_tick);

    lv_display_t * disp;
    
    //Use TFT_eSPI interface
    /*TFT_eSPI can be enabled lv_conf.h to initialize the display in a simple way*/
    disp = lv_tft_espi_create(TFT_HOR_RES, TFT_VER_RES, draw_buf, sizeof(draw_buf));
    lv_display_set_rotation(disp, TFT_ROTATION);

    /*Initialize the (dummy) input device driver*/
    lv_indev_t * indev = lv_indev_create();
    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*Touchpad should have POINTER type*/
    lv_indev_set_read_cb(indev, my_touchpad_read);

    lv_my_exercise_arc();

    Serial.println( "Setup done" );
}

void loop()
{
    lv_timer_handler(); /* let the GUI do its work */
    delay(100); /* let this time pass */
}

static void value_changed_event_cb(lv_event_t * e)
{
    lv_obj_t * arc = lv_event_get_target_obj(e);
    int16_t arc_value = lv_arc_get_value(arc);
    Serial.println(arc_value);

    pixels.clear();
    if (arc_value>0){
        for (int i=0; i < arc_value; i++){
            pixels.setPixelColor(i, pixels.Color(100, 100, 100));
        }
    }
    pixels.show();
}

/**
 * A simple row and a column layout with flexbox
 */
void lv_my_exercise_arc(void)
{
    /*Create a container with COLUMN flex direction*/
    lv_obj_t * cont_main = lv_obj_create(lv_screen_active());
    lv_obj_set_size(cont_main, TFT_HOR_RES-50, TFT_VER_RES-50);
    lv_obj_align(cont_main, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_flex_flow(cont_main, LV_FLEX_FLOW_COLUMN);

    lv_obj_t *label_top = lv_label_create(lv_screen_active());
    lv_label_set_text(label_top, "Arduino LVGL Exercise on Pico 2");
    lv_obj_align(label_top, LV_ALIGN_TOP_MID, 0, 0);

    lv_obj_t *label_bottom = lv_label_create(lv_screen_active());
    lv_label_set_text(label_bottom, "coXXect.blogspot.com");
    lv_obj_align(label_bottom, LV_ALIGN_BOTTOM_MID, 0, 0);

    lv_obj_t *label_lvgl = lv_label_create(cont_main);
    lv_label_set_text(label_lvgl, LVGL_Arduino.c_str());

    /* Create arc object */
    lv_obj_t * arc_obj = lv_arc_create(cont_main);
    lv_arc_set_range(arc_obj, 0, 8);
    lv_arc_set_value(arc_obj, 0);
    lv_arc_set_bg_start_angle(arc_obj, 90);
    lv_obj_add_event_cb(arc_obj, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);

}

Create LVGL grid of buttons to control NeoPixel Matrix



pico2_LVGL_Arduino_NeoPixel_matrix.ino
/*
LVGL exercise run on Raspberry Pi Pico 2 in Arduino framework,
with 4.0" 320x480 TFT SPI ST7796 with FT6336U Capacitive Touch.

Create LVGL grid of button to control NeoPixel matrix.

Raspberry Pi Pico 2 drive 4.0" 320x480 TFT SPI ST7796 with FT6336U Capacitive Touch + SD, 
in Arduino framework.
- Setup TFT_eSPI to display on ST7796 SPI TFT.
- I2C interface FT6336U cap. touch.
https://coxxect.blogspot.com/2025/04/raspberry-pi-pico-2-drive-40-480x320.html

LVGL on Raspberry Pi Pico 2 (Arduino framework/arduino-pico)
with 320x480 TFT SPI ST7796 + FT6336U Capacitive Touch.
https://coxxect.blogspot.com/2025/05/lvgl-on-raspberry-pi-pico-2-arduino.html

*/

#include <lvgl.h>
#include "lv_conf.h"

#include <TFT_eSPI.h>
#include <Adafruit_NeoPixel.h>

// for FT6336U cap touch
#include <Wire.h>
#define CTP_RST 14  // option, can connect to 3V3.
#define FT6336U_ADDR 0x38

#define PIN_NeoPixel    0
#define NUMPIXELS 16

/*Set to your screen resolution and rotation*/
#define TFT_HOR_RES   320 //320
#define TFT_VER_RES   480 //240
#define TFT_ROTATION  LV_DISPLAY_ROTATION_0

/*LVGL draw into this buffer, 1/10 screen size usually works well. The size is in bytes*/
#define DRAW_BUF_SIZE (TFT_HOR_RES * TFT_VER_RES / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];

String LVGL_Arduino = "LVGL! ";

Adafruit_NeoPixel pixels(NUMPIXELS, PIN_NeoPixel, NEO_GRB + NEO_KHZ800);

#define FRAME_WIDTH 4
#define FRAME_HEIGHT 4
byte neopixel_frame[FRAME_HEIGHT][FRAME_WIDTH] = {
  { 0, 0, 0, 0 },
  { 0, 0, 0, 0 },
  { 0, 0, 0, 0 },
  { 0, 0, 0, 0 }
};

uint16_t neopixel_map[FRAME_HEIGHT][FRAME_WIDTH] = {
  { 15, 14, 13, 12 },
  { 8, 9, 10, 11 },
  { 7, 6, 5, 4 },
  { 0, 1, 2, 3 }
};

int get_touch_count(){
    // Register 0x02;
    // Touch count, max 2
    Wire.beginTransmission(FT6336U_ADDR);
    Wire.write(0x02);
    Wire.endTransmission();
    Wire.requestFrom(FT6336U_ADDR, 1);
    return(Wire.read());
}

/*Read the touchpad*/
void my_touchpad_read( lv_indev_t * indev, lv_indev_data_t * data )
{
    if (get_touch_count()>0){
        data->state = LV_INDEV_STATE_PRESSED;

        int x, y;
        
        Wire.beginTransmission(FT6336U_ADDR);
        Wire.write(0x03);
        Wire.endTransmission();
        Wire.requestFrom(FT6336U_ADDR, 2);
        x = (Wire.read() & 0x0f) << 8 | Wire.read();
        
        Wire.beginTransmission(FT6336U_ADDR);
        Wire.write(0x05);
        Wire.endTransmission();
        Wire.requestFrom(FT6336U_ADDR, 2);
        y = (Wire.read() & 0x0f) << 8 | Wire.read();

        data->point.x = x;
        data->point.y = y;
    }else{
        data->state = LV_INDEV_STATE_RELEASED;
    }
}

/*use Arduinos millis() as tick source*/
static uint32_t my_tick(void)
{
    return millis();
}

void setup()
{
    
    LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();

    delay(2000);
    Serial.begin( 115200 );
    delay(1000);
    Serial.println( LVGL_Arduino );

    Wire.begin();   // I2C
    delay(100);

    pinMode(CTP_RST, OUTPUT);
    digitalWrite(CTP_RST, HIGH);
    delay(100);
    digitalWrite(CTP_RST, LOW);
    delay(100);
    digitalWrite(CTP_RST, HIGH);
    delay(100);

    pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
    pixels.setBrightness(10);
    pixels.clear();
    pixels.show();

    lv_init();

    /*Set a tick source so that LVGL will know how much time elapsed. */
    lv_tick_set_cb(my_tick);

    lv_display_t * disp;
    
    //Use TFT_eSPI interface
    /*TFT_eSPI can be enabled lv_conf.h to initialize the display in a simple way*/
    disp = lv_tft_espi_create(TFT_HOR_RES, TFT_VER_RES, draw_buf, sizeof(draw_buf));
    lv_display_set_rotation(disp, TFT_ROTATION);

    /*Initialize the (dummy) input device driver*/
    lv_indev_t * indev = lv_indev_create();
    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*Touchpad should have POINTER type*/
    lv_indev_set_read_cb(indev, my_touchpad_read);

    lv_my_exercise_Neopixel_matrix();

    Serial.println( "Setup done" );

    show_Neopixel_frame();
}

void loop()
{
    lv_timer_handler(); /* let the GUI do its work */
    delay(100); /* let this time pass */
}

void show_Neopixel_frame(){
    pixels.clear();
    for (int y=0; y < FRAME_HEIGHT; y++) {
        for (int x=0; x < FRAME_WIDTH; x++) {
            if (neopixel_frame[y][x] == 0) {
                pixels.setPixelColor(neopixel_map[y][x], 
                                    pixels.Color(0, 0, 0));
            }else{
                pixels.setPixelColor(neopixel_map[y][x], 
                                    pixels.Color(0, 0, 100));
            }
        }
    }
    pixels.show();
}

// Define a struct to store the integer parameter
typedef struct {
    int row;
    int col;
} my_callback_data_t;

// Callback function
static void my_callback(lv_event_t * e) {
    lv_obj_t * btn = (lv_obj_t *) lv_event_get_target(e); // Get the button object
    bool is_checked = lv_obj_has_state(btn, LV_STATE_CHECKED); // Check if it's toggled

    my_callback_data_t * data = (my_callback_data_t *) lv_event_get_user_data(e);
    if(data) {
        Serial.printf("Callback received value: %d %d \n", data->row, data->col);

        if (is_checked){
            neopixel_frame[data->row][data->col] = 1;
        }else{
            neopixel_frame[data->row][data->col] = 0;
        }
    }

    show_Neopixel_frame();
}

void lv_my_exercise_Neopixel_matrix(void)
{
    /*Create a container with COLUMN flex direction*/
    lv_obj_t * cont_main = lv_obj_create(lv_screen_active());
    lv_obj_set_size(cont_main, TFT_HOR_RES-50, TFT_VER_RES-50);
    lv_obj_align(cont_main, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_flex_flow(cont_main, LV_FLEX_FLOW_COLUMN);

    lv_obj_t *label_top = lv_label_create(lv_screen_active());
    lv_label_set_text(label_top, "Arduino LVGL Exercise on Pico 2");
    lv_obj_align(label_top, LV_ALIGN_TOP_MID, 0, 0);

    lv_obj_t *label_bottom = lv_label_create(lv_screen_active());
    lv_label_set_text(label_bottom, "coXXect.blogspot.com");
    lv_obj_align(label_bottom, LV_ALIGN_BOTTOM_MID, 0, 0);

    lv_obj_t *label_lvgl = lv_label_create(cont_main);
    lv_label_set_text(label_lvgl, LVGL_Arduino.c_str());

    create_button_grid(cont_main);
}

/**
 * A grid of 4x4 buttons
 */
void create_button_grid(lv_obj_t * cont)
{
    static int32_t col_dsc[] = {50, 50, 50, 50, LV_GRID_TEMPLATE_LAST};
    static int32_t row_dsc[] = {50, 50, 50, 50, LV_GRID_TEMPLATE_LAST};

    /*Create button grid*/
    lv_obj_t *cont_button_grid = lv_obj_create(cont);
    lv_obj_set_style_grid_column_dsc_array(cont_button_grid, col_dsc, 0);
    lv_obj_set_style_grid_row_dsc_array(cont_button_grid, row_dsc, 0);
    lv_obj_set_size(cont_button_grid, 256, 256);

    lv_obj_center(cont_button_grid);
    lv_obj_set_layout(cont_button_grid, LV_LAYOUT_GRID);

    // Create a style for the unchecked state (Gray)
    static lv_style_t style_unchecked;
    lv_style_init(&style_unchecked);
    lv_style_set_bg_color(&style_unchecked, lv_color_make(230, 230, 230)); // Gray color

    // Create a style for the checked state (Blue)
    static lv_style_t style_checked;
    lv_style_init(&style_checked);
    lv_style_set_bg_color(&style_checked, lv_color_make(0, 0, 255)); // Blue color

    lv_obj_t * btn;

    for (int row=0; row<FRAME_HEIGHT; row++){
        for (int col=0; col<FRAME_WIDTH; col++){
            btn = lv_button_create(cont_button_grid);  // Create button
            lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); // Enable toggle mode

            // Apply the styles based on state
            lv_obj_add_style(btn, &style_unchecked, LV_STATE_DEFAULT);
            lv_obj_add_style(btn, &style_checked, LV_STATE_CHECKED);

            lv_obj_set_grid_cell(btn, LV_GRID_ALIGN_STRETCH, col, 1,
                             LV_GRID_ALIGN_STRETCH, row, 1);

            // Allocate memory for user data
            my_callback_data_t * data = (my_callback_data_t *) malloc(sizeof(my_callback_data_t));

            data->row = row;
            data->col = col;

            lv_obj_add_event_cb(btn, my_callback, LV_EVENT_CLICKED, data);

        }
    }

}


Comments

Popular posts from this blog

480x320 TFT/ILI9488 SPI wih EP32C3 (arduino-esp32) using Arduino_GFX Library

Drive 320x240 ILI9341 SPI TFT using ESP32-S3 (NodeMCU ESP-S3-12K-Kit) using TFT_eSPI library, in Arduino Framework.