ESP32 crashes when calling `lv_obj_clean()`

Description

When I call the function lv_obj_clean(ui_Search_Result);, the ESP32 crashes

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

ESP32 with Arduino IDE

What LVGL version are you using?

8.3.11

What do you want to achieve?

I want to clear everything inside the container ui_Search_Result while being on core 0 (LVGL runs on core 1)

What have you tried so far?

Using a semaphore, running the function in the same core as LVGL (Both which did not solve the problem) and changing screen before clearing the container (the crash is less frequent but it is anoying to have to change screen and it still crashes with the Chnage Page Buttons). I have checked all other issues about it and tried all what was suggested but they all didn’t work.

Code to reproduce

The functions which I am using are below. The SearchComponent function is the one which makes the ESP32 crash.

void createChangePage(lv_obj_t* parent, int totalPages, int currentPage) {
    lv_obj_t* ui_Change_Page_Menu = ui_ChangePage_create(parent);

    // Dynamically retrieve and assign child objects
    lv_obj_t* Previous_Page_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PREVIOUS_PAGE);
    lv_obj_t* Page_Number_1_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_1_BUTTON);
    lv_obj_t* Page_Number_1_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_1_BUTTON_PAGE_NUMBER_1_LABEL);
    lv_obj_t* Page_Number_2_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_2_BUTTON);
    lv_obj_t* Page_Number_2_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_2_BUTTON_PAGE_NUMBER_2_LABEL);
    lv_obj_t* Page_Number_3_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_3_BUTTON);
    lv_obj_t* Page_Number_3_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_3_BUTTON_PAGE_NUMBER_3_LABEL);
    lv_obj_t* Page_Number_4_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_4_BUTTON);
    lv_obj_t* Page_Number_4_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_4_BUTTON_PAGE_NUMBER_4_LABEL);
    lv_obj_t* Page_Number_5_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_5_BUTTON);
    lv_obj_t* Page_Number_5_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_5_BUTTON_PAGE_NUMBER_5_LABEL);
    lv_obj_t* Next_Page_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_NEXT_PAGE);

    // Calculate the starting page number
    int startPage = (currentPage < 3) ? 1 : currentPage - 2;

    // Set the text for the page number labels
    lv_label_set_text_fmt(Page_Number_1_Label, "%d", startPage);
    lv_label_set_text_fmt(Page_Number_2_Label, "%d", startPage + 1);
    lv_label_set_text_fmt(Page_Number_3_Label, "%d", startPage + 2);
    lv_label_set_text_fmt(Page_Number_4_Label, "%d", startPage + 3);
    lv_label_set_text_fmt(Page_Number_5_Label, "%d", startPage + 4);

    // Add event callbacks with integer parameters
    lv_obj_add_event_cb(Previous_Page_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(currentPage - 1));
    lv_obj_add_event_cb(Page_Number_1_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)startPage);
    lv_obj_add_event_cb(Page_Number_2_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(startPage + 1));
    lv_obj_add_event_cb(Page_Number_3_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(startPage + 2));
    lv_obj_add_event_cb(Page_Number_4_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(startPage + 3));
    lv_obj_add_event_cb(Page_Number_5_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(startPage + 4));
    lv_obj_add_event_cb(Next_Page_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(currentPage + 1));

    // Disable buttons if they are out of range
    if (startPage > totalPages) lv_obj_add_flag(Page_Number_1_Button, LV_OBJ_FLAG_HIDDEN);
    if (startPage + 1 > totalPages) lv_obj_add_flag(Page_Number_2_Button, LV_OBJ_FLAG_HIDDEN);
    if (startPage + 2 > totalPages) lv_obj_add_flag(Page_Number_3_Button, LV_OBJ_FLAG_HIDDEN);
    if (startPage + 3 > totalPages) lv_obj_add_flag(Page_Number_4_Button, LV_OBJ_FLAG_HIDDEN);
    if (startPage + 4 > totalPages) lv_obj_add_flag(Page_Number_5_Button, LV_OBJ_FLAG_HIDDEN);
}
void SearchComponent(void* parameters) {
  // static int page = 1;
  int page = (int)parameters;  // Cast back to int
  if (page <= 0) {
    page = 1;
  }
  const char* SearchText = lv_textarea_get_text(ui_TextArea1);
  String SearchTextString = String(SearchText);
  SearchTextString.replace(" ", "+");
  http.begin("https://theyoungmaker.ddns.net/PartSense/api/SearchComponents.php?keyword=" + SearchTextString + "&resultperpage=" + MAX_COMPONENTS + "&page=" + page);

  http.addHeader("Content-Type", "application/json");
  lv_obj_add_flag(ui_No_Result_Container, LV_OBJ_FLAG_HIDDEN);

  // size_t freeHeap = ESP.getFreeHeap();
  // Serial.println("Free heap before JSON parse: " + String(freeHeap));

  // Send HTTP GET request
  int httpResponseCode = http.GET();

  if (httpResponseCode > 0) {
    Serial.println("HTTP Response code: " + String(httpResponseCode));

    // Get the JSON response as a string and print it
    String responseString = http.getString();
    Serial.println("JSON Response: " + responseString);

    // Parse JSON response
    DynamicJsonDocument jsonResponse(2048);  // Adjust size as needed
    DeserializationError error = deserializeJson(jsonResponse, responseString);

    if (!error) {
      // Check if "SearchResults" and "Parts" keys exist
      if (jsonResponse.containsKey("SearchResults") && jsonResponse["SearchResults"].containsKey("NumberOfResult") && jsonResponse["SearchResults"].containsKey("Parts")) {
        int numberOfResult = jsonResponse["SearchResults"]["NumberOfResult"].as<int>();
        JsonArray parts = jsonResponse["SearchResults"]["Parts"].as<JsonArray>();

        // Clear previous component data
        clearComponentData();

        // Create a new screen and switch to it
        lv_obj_t* temp_screen = lv_obj_create(NULL);
        lv_scr_load(temp_screen);

        // Clean old search results safely
        lv_event_send(ui_Search_Result, LV_EVENT_DELETE, NULL);  // Safely remove any pending events
        lv_obj_invalidate(ui_Search_Result);                     // Prevent any drawing operations during clean
        lv_obj_clean(ui_Search_Result);                          // Still causes crashes (This is the line where it crashes)

        if (numberOfResult > 0) {
          createProductCards(ui_Search_Result, numberOfResult);
          createChangePage(ui_Search_Result, numberOfResult, page);
          int i = 0;
          for (JsonObject part : parts) {
            if (i >= MAX_COMPONENTS) break;  // Ensure we don't exceed the array size

            descriptions[i] = strdup(part["description"] | "");
            if (!descriptions[i]) {
              Serial.println("Memory allocation failed for descriptions[" + String(i) + "]");
              continue;
            }

            manufacturers[i] = strdup(part["manufacturer"] | "");
            if (!manufacturers[i]) {
              Serial.println("Memory allocation failed for manufacturers[" + String(i) + "]");
              free((void*)descriptions[i]);
              descriptions[i] = nullptr;
              continue;
            }
            partNumbers[i] = strdup(part["part_number"] | "");
            if (!partNumbers[i]) {
              Serial.println("Memory allocation failed for partNumbers[" + String(i) + "]");
              free((void*)descriptions[i]);
              descriptions[i] = nullptr;
              free((void*)manufacturers[i]);
              manufacturers[i] = nullptr;
              continue;
            }

            manufacturerPartNumbers[i] = strdup(part["mpn"] | "");
            if (!manufacturerPartNumbers[i]) {
              Serial.println("Memory allocation failed for manufacturerPartNumbers[" + String(i) + "]");
              free((void*)descriptions[i]);
              descriptions[i] = nullptr;
              free((void*)manufacturers[i]);
              manufacturers[i] = nullptr;
              free((void*)partNumbers[i]);
              partNumbers[i] = nullptr;
              continue;
            }

            // Print extracted data
            Serial.println("Component " + String(i + 1) + ":");
            Serial.println("Description: " + String(descriptions[i]));
            Serial.println("Manufacturer: " + String(manufacturers[i]));
            Serial.println("Manufacturer Part Number: " + String(manufacturerPartNumbers[i]));

            lv_label_set_text(productCardComponents[i].Mfr_Part, manufacturerPartNumbers[i]);
            lv_label_set_text(productCardComponents[i].Description, descriptions[i]);
            lv_label_set_text(productCardComponents[i].Manufacturer, manufacturers[i]);
            lv_label_set_text(productCardComponents[i].Part_Number, partNumbers[i]);

            const char* partNumberText = lv_label_get_text(productCardComponents[i].Part_Number);
            String response = "https://theyoungmaker.ddns.net/PartSense/images/" + String(partNumberText) + "/96x96.bmp";

            if (getImageFromURL(response, i)) {
              Serial.println("Image downloaded and processed successfully");
              lv_img_set_src(productCardComponents[i].Image, &productImages[i]);
            } else {
              Serial.println("Failed to download or process image");
            }

            i++;
          }
        } else {
          lv_obj_clear_flag(ui_No_Result_Container, LV_OBJ_FLAG_HIDDEN);
        }
      } else {
        Serial.println("JSON response does not contain expected keys");
      }

      lv_obj_add_flag(ui_Spinner1, LV_OBJ_FLAG_HIDDEN);
      lv_obj_clear_flag(ui_Search_Result, LV_OBJ_FLAG_HIDDEN);

      // Switch back to the original product screen
      lv_scr_load(ui_Component_Search);
    } else {
      Serial.println("Failed to parse JSON: " + String(error.c_str()));
    }
  } else {
    Serial.println("Error on HTTP request: " + http.errorToString(httpResponseCode));
  }
  http.end();
  vTaskDelete(searchComponentTaskHandle);
}
void startSearchComponentTask(lv_event_t* event) {
  int page = (int)lv_event_get_user_data(event);
  xTaskCreatePinnedToCore(
    SearchComponent,             // Task function
    "SearchComponentTask",       // Task name
    10000,                       // Stack size (bytes)
    (void*)page,                 // Task parameters
    1,                           // Priority (0 = lowest, 1 = default, 2 = highest)
    &searchComponentTaskHandle,  // Task handle
    0                            // Core to run the task on (0 or 1)
  );
}

Using ESP Exception Decoder, it gives me the following with the backtrace :

0x4200fb67: _lv_event_mark_deleted at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_event.c:155
0x420125a5: lv_obj_destructor at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj.c:454
0x420d453f: _lv_obj_destruct at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_class.c:136
0x42016e51: obj_del_core at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_tree.c:385
0x42016e87: obj_del_core at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_tree.c:362
0x42016e87: obj_del_core at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_tree.c:362
0x42016fa1: lv_obj_clean at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_tree.c:89
0x42003186: SearchComponent(void*) at C:\Users\Loic\Downloads\LVGL_Arduino\LVGL_Arduino.ino:463

I mean your code idea isnt ok with lvgl engine. Call lv_scr_load isnt executed and result to previous sr unloaded and prepared for del…

Can you explan what you mean? The lv_scr_load is used temporarily to prevent the esp32 from crashing due to the lv_obj_clean.

LVGL is not thread safe. You cannot do anything to any LVGL objects from multiple threads/cores without adding locks. So if you happen to be deleting something that is being accessed in another thread/core that will cause it to crash.

1 Like

And how could I add locks?

a thread lock is a mutex or a semaphore those are available as part of FreeRTOS. You can also use the Notify functions in FreeRTOS to pass messages between tasks to instruct them what to do. It is not advisable to create tasks over and over again if possible. You can use the notify functions to stall a task until it is told to do some work.

I already tried with mutex/semaphore and it didn’t work. Also, would there be a better method to run certain task on an other core?

You can run whatever you want on either core. If it has to do with LVGL directly then it MUST run on the same core and in the same thread. The easiest way to handle this is to create a task that pins the GUI related stuff to a core. You have the startup GUI code in that task and then you have that task loop to update the GUI and to carry out anything that needs to be done to the GUI. You send Notifications to that task from tasks running on the other core.

Ok, I will know this for next time. However, that does not solve my problem since the crash happens even when running the function on the same core as lvgl

OK so I know what you are doing wrong. It didn’t jump out at me the first time I looked at it.

void createChangePage(lv_obj_t* parent, int totalPages, int currentPage) {
    lv_obj_t* ui_Change_Page_Menu = ui_ChangePage_create(parent);

    // Dynamically retrieve and assign child objects
    lv_obj_t* Previous_Page_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PREVIOUS_PAGE);
    lv_obj_t* Page_Number_1_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_1_BUTTON);
    lv_obj_t* Page_Number_1_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_1_BUTTON_PAGE_NUMBER_1_LABEL);
    lv_obj_t* Page_Number_2_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_2_BUTTON);
    lv_obj_t* Page_Number_2_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_2_BUTTON_PAGE_NUMBER_2_LABEL);
    lv_obj_t* Page_Number_3_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_3_BUTTON);
    lv_obj_t* Page_Number_3_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_3_BUTTON_PAGE_NUMBER_3_LABEL);
    lv_obj_t* Page_Number_4_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_4_BUTTON);
    lv_obj_t* Page_Number_4_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_4_BUTTON_PAGE_NUMBER_4_LABEL);
    lv_obj_t* Page_Number_5_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_5_BUTTON);
    lv_obj_t* Page_Number_5_Label = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_PAGE_NUMBERS_PAGE_NUMBER_5_BUTTON_PAGE_NUMBER_5_LABEL);
    lv_obj_t* Next_Page_Button = ui_comp_get_child(ui_Change_Page_Menu, UI_COMP_CHANGEPAGE_NEXT_PAGE);

    // Calculate the starting page number
    int startPage = (currentPage < 3) ? 1 : currentPage - 2;

    // Set the text for the page number labels
    lv_label_set_text_fmt(Page_Number_1_Label, "%d", startPage);
    lv_label_set_text_fmt(Page_Number_2_Label, "%d", startPage + 1);
    lv_label_set_text_fmt(Page_Number_3_Label, "%d", startPage + 2);
    lv_label_set_text_fmt(Page_Number_4_Label, "%d", startPage + 3);
    lv_label_set_text_fmt(Page_Number_5_Label, "%d", startPage + 4);

    // Add event callbacks with integer parameters
    lv_obj_add_event_cb(Previous_Page_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(currentPage - 1));
    lv_obj_add_event_cb(Page_Number_1_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)startPage);
    lv_obj_add_event_cb(Page_Number_2_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(startPage + 1));
    lv_obj_add_event_cb(Page_Number_3_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(startPage + 2));
    lv_obj_add_event_cb(Page_Number_4_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(startPage + 3));
    lv_obj_add_event_cb(Page_Number_5_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(startPage + 4));
    lv_obj_add_event_cb(Next_Page_Button, startSearchComponentTask, LV_EVENT_CLICKED, (void*)(currentPage + 1));

    // Disable buttons if they are out of range
    if (startPage > totalPages) lv_obj_add_flag(Page_Number_1_Button, LV_OBJ_FLAG_HIDDEN);
    if (startPage + 1 > totalPages) lv_obj_add_flag(Page_Number_2_Button, LV_OBJ_FLAG_HIDDEN);
    if (startPage + 2 > totalPages) lv_obj_add_flag(Page_Number_3_Button, LV_OBJ_FLAG_HIDDEN);
    if (startPage + 3 > totalPages) lv_obj_add_flag(Page_Number_4_Button, LV_OBJ_FLAG_HIDDEN);
    if (startPage + 4 > totalPages) lv_obj_add_flag(Page_Number_5_Button, LV_OBJ_FLAG_HIDDEN);
}
void SearchComponent(void* parameters) {
  // static int page = 1;
  int page = (int)parameters;  // Cast back to int
  if (page <= 0) {
    page = 1;
  }
  const char* SearchText = lv_textarea_get_text(ui_TextArea1);
  String SearchTextString = String(SearchText);
  SearchTextString.replace(" ", "+");
  http.begin("https://theyoungmaker.ddns.net/PartSense/api/SearchComponents.php?keyword=" + SearchTextString + "&resultperpage=" + MAX_COMPONENTS + "&page=" + page);

  http.addHeader("Content-Type", "application/json");
  lv_obj_add_flag(ui_No_Result_Container, LV_OBJ_FLAG_HIDDEN);

  // size_t freeHeap = ESP.getFreeHeap();
  // Serial.println("Free heap before JSON parse: " + String(freeHeap));

  // Send HTTP GET request
  int httpResponseCode = http.GET();

  if (httpResponseCode > 0) {
    Serial.println("HTTP Response code: " + String(httpResponseCode));

    // Get the JSON response as a string and print it
    String responseString = http.getString();
    Serial.println("JSON Response: " + responseString);

    // Parse JSON response
    DynamicJsonDocument jsonResponse(2048);  // Adjust size as needed
    DeserializationError error = deserializeJson(jsonResponse, responseString);

    if (!error) {
      // Check if "SearchResults" and "Parts" keys exist
      if (jsonResponse.containsKey("SearchResults") && jsonResponse["SearchResults"].containsKey("NumberOfResult") && jsonResponse["SearchResults"].containsKey("Parts")) {
        int numberOfResult = jsonResponse["SearchResults"]["NumberOfResult"].as<int>();
        JsonArray parts = jsonResponse["SearchResults"]["Parts"].as<JsonArray>();

        // Clear previous component data
        clearComponentData();

        // Create a new screen and switch to it
        lv_obj_t* temp_screen = lv_obj_create(NULL);
        lv_scr_load(temp_screen);

        // Clean old search results safely

// THIS CODE BLOCK IS WRONG
        lv_event_send(ui_Search_Result, LV_EVENT_DELETE, NULL);  // Safely remove any pending events
        lv_obj_invalidate(ui_Search_Result);                     // Prevent any drawing operations during clean
        lv_obj_clean(ui_Search_Result);                          // Still causes crashes (This is the line where it crashes)
// END WRONG CODE BLOCK

        if (numberOfResult > 0) {
          createProductCards(ui_Search_Result, numberOfResult);
          createChangePage(ui_Search_Result, numberOfResult, page);
          int i = 0;
          for (JsonObject part : parts) {
            if (i >= MAX_COMPONENTS) break;  // Ensure we don't exceed the array size

            descriptions[i] = strdup(part["description"] | "");
            if (!descriptions[i]) {
              Serial.println("Memory allocation failed for descriptions[" + String(i) + "]");
              continue;
            }

            manufacturers[i] = strdup(part["manufacturer"] | "");
            if (!manufacturers[i]) {
              Serial.println("Memory allocation failed for manufacturers[" + String(i) + "]");
              free((void*)descriptions[i]);
              descriptions[i] = nullptr;
              continue;
            }
            partNumbers[i] = strdup(part["part_number"] | "");
            if (!partNumbers[i]) {
              Serial.println("Memory allocation failed for partNumbers[" + String(i) + "]");
              free((void*)descriptions[i]);
              descriptions[i] = nullptr;
              free((void*)manufacturers[i]);
              manufacturers[i] = nullptr;
              continue;
            }

            manufacturerPartNumbers[i] = strdup(part["mpn"] | "");
            if (!manufacturerPartNumbers[i]) {
              Serial.println("Memory allocation failed for manufacturerPartNumbers[" + String(i) + "]");
              free((void*)descriptions[i]);
              descriptions[i] = nullptr;
              free((void*)manufacturers[i]);
              manufacturers[i] = nullptr;
              free((void*)partNumbers[i]);
              partNumbers[i] = nullptr;
              continue;
            }

            // Print extracted data
            Serial.println("Component " + String(i + 1) + ":");
            Serial.println("Description: " + String(descriptions[i]));
            Serial.println("Manufacturer: " + String(manufacturers[i]));
            Serial.println("Manufacturer Part Number: " + String(manufacturerPartNumbers[i]));

            lv_label_set_text(productCardComponents[i].Mfr_Part, manufacturerPartNumbers[i]);
            lv_label_set_text(productCardComponents[i].Description, descriptions[i]);
            lv_label_set_text(productCardComponents[i].Manufacturer, manufacturers[i]);
            lv_label_set_text(productCardComponents[i].Part_Number, partNumbers[i]);

            const char* partNumberText = lv_label_get_text(productCardComponents[i].Part_Number);
            String response = "https://theyoungmaker.ddns.net/PartSense/images/" + String(partNumberText) + "/96x96.bmp";

            if (getImageFromURL(response, i)) {
              Serial.println("Image downloaded and processed successfully");
              lv_img_set_src(productCardComponents[i].Image, &productImages[i]);
            } else {
              Serial.println("Failed to download or process image");
            }

            i++;
          }
        } else {
          lv_obj_clear_flag(ui_No_Result_Container, LV_OBJ_FLAG_HIDDEN);
        }
      } else {
        Serial.println("JSON response does not contain expected keys");
      }

      lv_obj_add_flag(ui_Spinner1, LV_OBJ_FLAG_HIDDEN);
      lv_obj_clear_flag(ui_Search_Result, LV_OBJ_FLAG_HIDDEN);

      // Switch back to the original product screen
      lv_scr_load(ui_Component_Search);
    } else {
      Serial.println("Failed to parse JSON: " + String(error.c_str()));
    }
  } else {
    Serial.println("Error on HTTP request: " + http.errorToString(httpResponseCode));
  }
  http.end();
  vTaskDelete(searchComponentTaskHandle);
}
void startSearchComponentTask(lv_event_t* event) {
  int page = (int)lv_event_get_user_data(event);
  xTaskCreatePinnedToCore(
    SearchComponent,             // Task function
    "SearchComponentTask",       // Task name
    10000,                       // Stack size (bytes)
    (void*)page,                 // Task parameters
    1,                           // Priority (0 = lowest, 1 = default, 2 = highest)
    &searchComponentTaskHandle,  // Task handle
    0                            // Core to run the task on (0 or 1)
  );
}

I marked the section of code that is wrong. You had already marked it as causing the crash. That block of code you need to change to lv_obj_del(ui_Search_Result); and that will do it. easy peazy.

Now that being said if you delete the object where the code block is then this code is going to cause problems. You are attempting to use a deleted object.

          createProductCards(ui_Search_Result, numberOfResult);
          createChangePage(ui_Search_Result, numberOfResult, page);

Once you delete the object is becomes invalid and it can no longer be used. If you intention is to reuse the object then you would be better suited to simply hide it using lv_obj_add_flag(ui_Search_Result, LV_OBJ_FLAG_HIDDEN); and to show the object when it needs to be seen again lv_obj_clear_flag(ui_Search_Result, LV_OBJ_FLAG_HIDDEN);

I might have those function names wrong as I am used to the LVGL version 9.x API and not 8.x. so you may have to look in the docs for the exact function names.

Thanks! That already helps me a lot. I will test this tomorrow and let you you know if it works!

no worries m8, glad to help. I am sure that is going to fix your crashing problem.

If you have any other questions ask away, I will do my best to answer them for ya.
If that fixes your problem remember to mark my post as the solution. This lets other users know that the problem is solved but also makes it easy for them to locate the solution.

I tried it and it did the exact same thing as lv_obj_clean(ui_Search_Result); and it crashed. Also lv_obj_clean(ui_Search_Result); only delete all its child and that is what i need, not delete the container itself.

why don’t you iterate over the children and delete them

uint16_t child_count = lv_obj_get_child_cnt(object);
lv_obj_t *child;

for (uint16_t i=0; i<child_count;i++) {
    child = lc_obj_get_child(object, i);
    lv_obj_del(child);
}

something along those lines.

also, this is what threw me as you wanting to delete the object.

        lv_event_send(ui_Search_Result, LV_EVENT_DELETE, NULL);  // Safely remove any pending events

That is going to start a process to delete the object. If you are still calling that you shouldn’t be and that is more than likely where the issue is stemming from. If you want to delete the children of an object the way to do it is to iterate over the objects children and delete them.

If you don’t want to do that there is another way. You create an object that has a parent that is the object you don’t want to delete. set the background to have an opacity of 0 and also set the border opacity and outline opacity to 0 as well. Change the border and outline widths to zero and change the padding so it is also all zeros.

Then use that object as the parent for whatever you want to display. so now when you want to delete the children you only have to delete the child at index 0. You will need to make the “container” each and every time you want to add things to display, it removes the need to iterate over the children in your code. The iteration does still take place it just happens in LVGL’s code.

This looks like a dangling pointer issue.

If it crashes during the lv_obj_clean call, then is probably something inside a LV_EVENT_DELETE event callback.

If it crashes soon after that call, then maybe there are a lv_timer_t or lv_event_t that is using a pointer that is no longer valid.

Suspect about your global variables (if any) for lv_obj_t* and lv_timer_t*, or how you use the user_data void pointer in objects, timers and event callbacks. Maybe a timer should have been deleted, or event should have been removed, before the lv_obj_clean gets called.

Reducing the complexity of your code can help you find the error. You said this problem occurs even when not using threads, well, since solving problems with threads can get way more complex, would recommend to solve this issue without using threads first.

Thanks, I’ll check that tommorow and let you know. Also, the code does not always crash, but most of the times, it does. Can this be a hint about the problem?

Yes, a dangling pointer can cause that too. For example, at some point you call lv_obj_del, but the address of the object you deleted is still saved in a global variable of the type lv_obj_t* or inside a user_data in a timer or event, but now that address is no longer valid, at some point you try to do something to this global variable, let’s say lv_obj_add_style, it is possible that your code will not crash here, and actually you are corrupting your memory at some really random location. When you are calling lv_obj_clean it is possible that corruption has become critical.

Try enabling all LV_USE_ASSERT_... in the lv_conf.h file for now, so LVGL can try to identify what’s wrong.

*Enable asserts if an operation is failed or an invalid data is found.
 *If LV_USE_LOG is enabled an error message will be printed on failure*/
#define LV_USE_ASSERT_NULL          1   /*Check if the parameter is NULL. (Very fast, recommended)*/
#define LV_USE_ASSERT_MALLOC        1   /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/
#define LV_USE_ASSERT_STYLE         1   /*Check if the styles are properly initialized. (Very fast, recommended)*/
#define LV_USE_ASSERT_MEM_INTEGRITY 1   /*Check the integrity of `lv_mem` after critical operations. (Slow)*/
#define LV_USE_ASSERT_OBJ           1   /*Check the object's type and existence (e.g. not deleted). (Slow)*/

/*Add a custom handler when assert happens e.g. to restart the MCU*/
#define LV_ASSERT_HANDLER_INCLUDE <esp_system.h>
#define LV_ASSERT_HANDLER esp_restart();

I usually develop all the interface in a simulator on PC, with lots os mocks to the platform, so it easier to develop and debug, only then I compile and test on the prototype. But managing the project so it can be compiled in MSVC and GCC at the same time can be a challenge.

In the serial, when it crashes, LVGL prints nothing, but I get Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled. with a backtrace and the backtrace, with esp exception decoder gives that :

0x42016fcf: _lv_event_mark_deleted at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_event.c:155
0x42019a0d: lv_obj_destructor at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj.c:454
0x420dff2b: _lv_obj_destruct at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_class.c:136
0x4201e2e5: obj_del_core at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_tree.c:385
0x4201e31b: obj_del_core at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_tree.c:362
0x4201e31b: obj_del_core at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_tree.c:362
0x4201e435: lv_obj_clean at c:\Users\Loic\Documents\Arduino\libraries\lvgl\src\core\lv_obj_tree.c:89
0x42003816: SearchComponent(void*) at C:\Users\Loic\Downloads\LVGL_Arduino\LVGL_Arduino.ino:528