LVGL UI dissapear after callback event

Good day, I'm new to programming and created this sketch through "copy and paste".
I'm using e ESP32 Dev board with TFT 3.2inch display ( ILI9341)
i created a UI with squareline studio and exported it into my sketch. The UI loads and everything is fine but as soon as i toggle the switch the screen goes white. The button still toggles and sends hex code but i cant see anything. Im using VSPI for display and HSPI for touch. Cant get it to work on the same bus. Please have a look at my code (.ino)
i tested the code without the lv_obj_add_event_cb() the the button still toggles but the display doesn't disappear. So it looks like "lv_obj_add_event_cb()" is causing the UI to disappear


#include <Arduino.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>

#include "ui_events.h"
#include "ui.h"        // SquareLine exported UI
#include "rs485_modbus.h"
#include "rs485_commands.h"


// ================== TFT SPI pins ==================
//#define TFT_MOSI 13
//#define TFT_MISO 12
//#define TFT_SCLK 14
//#define TFT_CS   15
//#define TFT_DC    2
//#define TFT_RST  -1   // EN pin reset
//#define TFT_LED  21

TFT_eSPI tft = TFT_eSPI();  // TFT instance

// ================== Touch ==================
SPIClass SPI_TOUCH(HSPI);
#define TOUCH_IRQ 36
#define TOUCH_CS 33
XPT2046_Touchscreen ts(TOUCH_CS, TOUCH_IRQ);


// ================== RS485 ==================
#define RXD2 16
#define TXD2 17
#define RS485_DE_RE 4
#define LV_TICK_CUSTOM 0
#define SCREEN_WIDTH  320
#define SCREEN_HEIGHT 240

volatile bool ui_ready = false;

volatile bool rs485_send_on  = false;
volatile bool rs485_send_off = false;


static uint32_t last_lvgl_tick = 0;


// ================== LVGL ==================
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[320 * 40];

// 

// Touch read callback///////////////////////////////////////////////////
void touch_read(lv_indev_drv_t * indev, lv_indev_data_t * data)
{
   
    if (!ui_ready) {
        data->state = LV_INDEV_STATE_RELEASED;
        return;
    }

    TS_Point p = ts.getPoint();

    if (p.z < 300 || p.x == 0 || p.y == 0) {
        data->state = LV_INDEV_STATE_RELEASED;
        return;
    }

    // Map raw values to screen coordinates
    int16_t x = map(p.x, 351, 3536, 0, 319);
    int16_t y = map(p.y, 403, 3815, 0, 239); // inverted Y

    // Clamp
    x = constrain(x, 0, 319);
    y = constrain(y, 0, 239);

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

    // Debug
    Serial.printf("TOUCH β†’ X=%d Y=%d Z=%d\n", x, y, p.z);
}

//================= LVGL display flush=======================================

// ----------------------------------------------------------------------------------
    void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1,
                      area->x2 - area->x1 + 1,
                      area->y2 - area->y1 + 1);
    tft.pushColors((uint16_t*)color_p, 
                   (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1),
                   true);
    tft.endWrite();
    lv_disp_flush_ready(disp);
}

// =================Send RS485 frame====================
void sendRS485(uint8_t* frame, uint8_t len) {
  digitalWrite(RS485_DE_RE, HIGH); // TX mode
  delay(2);
  RS485.write(frame, len);
  RS485.flush();
  delay(2);
  digitalWrite(RS485_DE_RE, LOW);  // RX mode
}

    hw_timer_t * lvgl_timer = NULL;


// ================== Setup ==================
void setup() {
    Serial.begin(115200);
    delay(100);
    Serial.println("πŸš€ Setup start");

    // ================= RS485 =================
    rs485_init();
    Serial.println("βœ… RS485 initialized");

     // ================= LVGL =================
    Serial.println("Initializing LVGL...");
    lv_init();

    lv_disp_draw_buf_init(&draw_buf, buf1, NULL, 320 * 40);

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 320;
    disp_drv.ver_res = 240;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    // ================= INPUT =================
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touch_read;
    indev_drv.disp = lv_disp_get_default();
    lv_indev_drv_register(&indev_drv);

    // ================= TFT FIRST =================
    Serial.println("Initializing TFT...");
    SPI.begin(14, 12, 13);   // VSPI for TFT
    tft.begin();
    tft.setRotation(1);
    tft.fillScreen(TFT_BLACK);
    Serial.println("βœ… TFT initialized");

    // ================= TOUCH (HSPI) =================
    Serial.println("Initializing touch...");
    SPI_TOUCH.begin(25, 39, 32, 33);   // HSPI for touch
    ts.begin(SPI_TOUCH);
    ts.setRotation(3);
    Serial.println("βœ… Touch initialized");

    // ================= UI LAST =================
    Serial.println("Initializing UI...");
    ui_init();
    ui_ready = true;

    lv_obj_add_event_cb(
        ui_OnSwitch,
        OnSwitch_event_cb,
        LV_EVENT_CLICKED,
        NULL
    );

   // lv_refr_now(NULL);

    Serial.println("βœ… UI initialized");
    Serial.println("🏁 Setup complete");
}


// ================== Loop ==================
void loop()
{
    uint32_t now = millis();
    uint32_t diff = now - last_lvgl_tick;

    if (diff > 0) {
        lv_tick_inc(diff);
        last_lvgl_tick = now;
    }

    lv_timer_handler();

    if (rs485_send_on) {
        rs485_send_on = false;
        sendRS485(relayON, relayON_len);
    }

    if (rs485_send_off) {
        rs485_send_off = false;
        sendRS485(relayOFF, relayOFF_len);
    }

    delay(2);
}

Does that code even compile? I don't see ui_OnSwitch or OnSwitch_event_cb defined anywhere.

Hi @gj_moller ,

welcome to the forum..

don’t know LVGL all that much..

what happens if you move these declarations to above setup, making them global and live beyond setup..

static lv_disp_drv_t disp_drv;
static lv_indev_drv_t indev_drv;

good luck.. ~q

void OnSwitch_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * obj = lv_event_get_target(e);

    if(code == LV_EVENT_VALUE_CHANGED) {  
        bool state = lv_obj_has_state(obj, LV_STATE_CHECKED);

        if(state) {

            // Switch is ON
            rs485_send_on = true;
            Serial.println("Switch ON");

        } else {

            // Switch is OFF
            rs485_send_off = true;
            Serial.println("Switch OFF");
        }

    }

}

The cause may be that the program is looping somewhere and execution permission has not been passed to lv_timer_handler().

It looks like LVGL8.x functions are being used, but which version of LVGL are you using?

If you are using LVGL9.x, try using lv_event_get_target_obj() instead of lv_event_get_target().

You're probably using the lv_switch widget, but in LVGL9.x lv_event_get_target() and lv_event_get_target_obj() work a little differently.

So you didn't post your complete code initially. What other code is still unposted? It's unclear how you expect to get help without providing full information.

Im using LVGL 8.3.11

I have about 7 files each with code. Where should i post all of these files ?

OK, then first, let's confirm whether the problem is with the LVGL programming or with RS485 communication.

In this case, turning the toggle ON doesn't result in RS485 transmission.

Did RS485 transmission succeed before combining with LVGL? If transmission is blocked, it could be the reason the LVGL screen isn't updating. (sorry but I don't have any experience of RS485.)

Max 485 module (TTL - RS485) was not properly grounded fixed the ground problem and moved #define RS485_DE_RE 4 to pin 27.
Thanks