Strange crash happening with Arduino Giga

I am trying to code an Arduino Giga with a Giga Display Shield using LVGL, and I am encountering some really weird bugs.

The program is part of my PhD research, and it's several thousand lines of code, so I am not really able to share the full code (not to mention, this is a long enough post without adding all of the code). I'm trying to track down the issue to try make it easier to find the problem so that I can choose the relevant sections to share, but I am confused because there should be no connection between the two functions that are connected to the crashes. The most that I have seen for memory fragmentation is 2% and that's only briefly, and most times it is 0% before the crash happens (I don't use Strings either, but I just leared about using Serial.print(F() instead of Serial.print(), so I have to change that; and I just noticed that some of my arrays are not null terminated, so I am going to try that as well). I'm going to test to see if it has anything to do with using a mix of GFX and LVGL, but the only time that I use GFX is right at the start, and only because I was having trouble finding code to turn the screen black with white text for the startup sequence using LVGL. I have also tried using the LVGL logging, but that only seems to produce any output when I choose trace, and nothing shows up with the other options.

The main bug that I encounter is I am using the touchscreen to allow the user to make selections using radio buttons and to enter numbers using a number pad. Afterwards, it shows a summary on the screen and you have the option to press some buttons. The function that creates and displays the buttons is identical no matter what the user has chosen as their program, but some options result in the Arduino crashing and others work fine when the user presses one of the buttons.

Oddly enough, I found out what part of the problem is, and it has to do with the USB-A, but it has me confused. I want to use the USB to log data (I built it off of the data logger example provided by Arduino). However, if I remove the USB mass storage device setup code, the issues with crashing disappear, even though the code in that function has nothing to do with the USB. If I include even one msd.connect(); though, the code would have crashing problems on the summary screen. I will share these two sections below that I pulled out (sorry, I might have missed some of the variables that are global or local that get reused).

I was also having some weird things happen with the USB as well. The USB would power on and run, but then disconnect from the flash drive for some reason. I tried six different flash drives and only two of them would stay connected (both are over 10 years old, and the newer ones were disconnecting after about 20–30 seconds, no matter what I tried). I would also see if there were any error codes using this code that is part of the Arduino example (err is stored as an int, though it is also used to output text to the file as per the example):

  err = usb.mount(&msd);
  if (err) {
    Serial.print("Error mounting USB device ");
    Serial.println(err);
    while (1)
      ;
  }

And I would get error code -22, though it would still be able to open the file and write to it if I took out the while(1); line (I did see -5 one time as well, but that never repeated).

I am able to get past that part of the menu once the code for the USB is removed, but then it will either crash going to the next screen or not, again depending on what options the user has chosen, even though it should have no effect. The odd thing is, these crashes happen with the simplest set of options chosen by the user, but the most complex set of options works just fine.

On top of that, I want the screen to show a running update. I copied the code from the display screen in how it outputs the text on the screen, but for some reason, it won't space them out anymore like it does for the summary screen (it doesn't appear to be an error with the math, as I get it to output the math for the spacing to the serial monitor to double check). I get to this screen with the more complex set options provided by the user, but it crashes going to this screen if the user chooses the simplest options, even without the USB drive setup. I will share these two sections below after the USB and button sections (sorry, I might have missed copying some of the variables that are global or local that get initialized then later reused).

On top of all of this, for some reason, the Arduino IDE has made me add two extra } at the bottom of the code, even though they don't pair up with a { anywhere else in the code. It kept saying in the very last line of code that it needs a } after the ;, even though there was one that closed the function and there was only one { in that function (I have my code organized to easily collapse the functions and I can quickly scan to make sure that all { are paired with a }, but I also went through line by line to double check). It started with adding one }, then later I had to add a second one for some reason when it did it again.

I have asked a couple friends, but no one is really sure what is going on. I'm going to try to meet up with a friend later who is a developer, as he has more experience with Arduino than I do, but I figured that I would also ask here if anyone has any ideas about what might be going on or suggestions on how to troubleshoot this? Thanks.

Code for USB set up and button display
Code to initialize the USB (I use serialPrint as a bool to turn serial prints off and on, depending on what I want to do):

// Libraries
  #include <Arduino_GigaDisplay.h>
  #include <lvgl.h>
  #include <Arduino_GigaDisplayTouch.h>
  #include <ArduinoGraphics.h>
  #include <Arduino_GigaDisplay_GFX.h>
  #include <Arduino_USBHostMbed5.h>
  #include <DigitalOut.h>
  #include <FATFileSystem.h>

//Global variables
  GigaDisplay_GFX DisplayGFX;
  USBHostMSD msd;
  mbed::FATFileSystem usb("usb");
  int err;

void setup(){
  // Enable the USB-A port
  pinMode(PA_15, OUTPUT);
  digitalWrite(PA_15, HIGH);  

  // Monitor USB stability
  msd.attach_detected_callback(onDeviceDetected);
  msd.attach_removed_callback(onDeviceRemoved); 

  int yPosition = 10; // Initial Y-coordinate for the first line of text for startup screen
  const int lineSpacing = 20; // Space between lines of text

// Initialize the USB for logging data
  msd.connect();

  DisplayGFX.setCursor(10, yPosition); // Set the cursor to the current position
  DisplayGFX.print("Initializing memory");
  yPosition += lineSpacing;
  if (serialPrint)
    Serial.print("Initializing memory");

  retryTimeout = 0;

  while (!msd.connected() && retryTimeout <= 3) {

    msd.connect();

    connectIterations = 0;

    while (connectIterations <= 6 && !msd.connected()){
      delay(1000);
      DisplayGFX.print(".");
      if (serialPrint)
        Serial.print(".");
      connectIterations++;
      msd.connect();
    }
    if (serialPrint)
      Serial.println("");

    if(!msd.connected() && retryTimeout < 3) {
      DisplayGFX.setCursor(10, yPosition); // Set the cursor to the current position
      DisplayGFX.print("MSD not found. Retrying.");
      yPosition += lineSpacing; // Move Y position down for the next line
      if (serialPrint)
        Serial.println("MSD not found. Retrying.");
    }

    retryTimeout++;
  }

  if(!msd.connected()) {
    DisplayGFX.setCursor(10, yPosition); // Set the cursor to the current position
    DisplayGFX.print("MSD not found. Failed.");
    yPosition += lineSpacing; // Move Y position down for the next line
    if (serialPrint)
      Serial.println("MSD not found. Failed.");
  }

  DisplayGFX.setCursor(10, yPosition); // Set the cursor to the current position
  DisplayGFX.print("Mounting USB device.");
  yPosition += lineSpacing; // Move Y position down for the next line
  if (serialPrint) 
    Serial.println("\n\nMounting USB device.");

  err = usb.mount(&msd);

  if (err) {
    DisplayGFX.setCursor(10, yPosition); // Set the cursor to the current position
    DisplayGFX.print("Error mounting USB device. Error code: ");
    DisplayGFX.print(err);
    yPosition += lineSpacing; // Move Y position down for the next line
    if (serialPrint) 
    {
      Serial.print("Error mounting USB device. Code: ");
      Serial.println(err);
    }
    while (1); // Stop the startup
  }

  DisplayGFX.setCursor(10, yPosition); // Set the cursor to the current position
  DisplayGFX.print("MSD read done.");
  yPosition += lineSpacing; // Move Y position down for the next line
  if (serialPrint) 
    Serial.println("MSD read done.");

  if (!msd.connected()) {
    DisplayGFX.setCursor(10, yPosition); // Set the cursor to the current position
    DisplayGFX.print("USB device not detected. Stopping start up.");
    yPosition += lineSpacing; // Move Y position down for the next line
    if(serialPrint)
      Serial.println("USB device not detected. Stopping start up.");
    while(1);
  }
  if(serialPrint)
    Serial.println("USB device connected.");

}

Code to display the buttons that are crashing sometimes:

// Global variables
  lv_obj_t * button_grey_start = NULL;
  lv_obj_t * label_grey_start = NULL;
  lv_obj_t * button_red_pause = NULL;
  lv_obj_t * label_button_red_pause = NULL;
  lv_obj_t * button_red_countdown = NULL;
  lv_obj_t * label_countdown = NULL;
  static lv_style_t style_grey_button;
  static lv_style_t style_red_button;
  
  static int timerIteration = 0; 
  static int repeatPrime = 0;

void prime_tubing_handler(lv_event_t * e) { 

  setup_button_styles();

  Serial.println("Start priming");

  if(serialPrint)
    Serial.println("Initialize variables");
  long startTimer = millis();
  static int timerIteration = 0;

  if(serialPrint)
    Serial.println("Creating grey button");
  // Create an inactive grey button layered over the start button
  button_grey_start = lv_btn_create(lv_scr_act());
  lv_obj_set_size(button_grey_start, 200, 75);
  lv_obj_align(button_grey_start, LV_ALIGN_CENTER, 120, 75);
  lv_obj_add_style(button_grey_start, &style_grey_button, 0);

  label_grey_start = lv_label_create(button_grey_start);
  lv_label_set_text(label_grey_start, "Starting");
  lv_obj_set_style_text_font(label_grey_start, &lv_font_montserrat_22, LV_PART_MAIN | LV_STATE_DEFAULT); // Set the label font
  lv_obj_center(label_grey_start);


  while(timerIteration < 3) {
        
    if (button_red_countdown != NULL && lv_obj_is_valid(button_red_countdown)) {
      lv_obj_del(button_red_countdown);
      button_red_countdown = NULL; // Prevent dangling pointer
    } 

    if (label_countdown != NULL && lv_obj_is_valid(label_countdown)) {
        lv_obj_del(label_countdown);
        label_countdown = NULL; // Prevent dangling pointer
    } 
     
    if(serialPrint) {
      Serial.println("Starting while");
      Serial.print("millis() - startTimer: ");
      Serial.println(millis() - startTimer);
      Serial.print("timerIteration: ");
      Serial.println(timerIteration);
    }

    // Create countdown button layered over the prime button
    button_red_countdown = lv_btn_create(lv_scr_act());
    lv_obj_set_size(button_red_countdown, 200, 75);
    lv_obj_align(button_red_countdown, LV_ALIGN_CENTER, 120, -75);
    lv_obj_add_style(button_red_countdown, &style_red_button, 0);

    label_countdown = lv_label_create(button_red_countdown);
    lv_obj_set_style_text_font(label_countdown, &lv_font_montserrat_22, LV_PART_MAIN | LV_STATE_DEFAULT); // Set the label font
    lv_obj_center(label_countdown);
          
    timerIteration++;

    if(timerIteration == 1) {
      lv_label_set_text(label_countdown, "3");
    }
    else if(timerIteration == 2) {
      lv_label_set_text(label_countdown, "2");
      }
    else if(timerIteration == 3) {
      lv_label_set_text(label_countdown, "1");
    }  
        
      lv_refr_now(NULL);

    //Serial.println("lv_obj_clean(button_red_countdown)");
    lv_obj_clean(button_red_countdown);

    delay(1000);

  }
  
  timerIteration = 0;

  //if(serialPrint)
    Serial.println("Stop priming");

  //if(serialPrint)
    Serial.println("Creating pause button");
  button_red_pause = lv_btn_create(lv_scr_act());
  lv_obj_set_size(button_red_pause, 200, 75);
  lv_obj_align(button_red_pause, LV_ALIGN_CENTER, 120, -75);
  lv_obj_add_style(button_red_pause, &style_red_button, 0);

  //if(serialPrint)
    Serial.println("Creating pause button label");
  label_button_red_pause = lv_label_create(button_red_pause);
  lv_obj_set_style_text_font(label_button_red_pause, &lv_font_montserrat_22, LV_PART_MAIN | LV_STATE_DEFAULT); // Set the label font
  lv_label_set_text(label_button_red_pause, "Pause");
  lv_obj_center(label_button_red_pause);
  lv_obj_add_event_cb(button_red_pause, stop_priming, LV_EVENT_CLICKED, NULL);
  lv_task_handler();
    
}

void setup_button_styles() {
  if(repeatPrime == 0) {
    // Initialize grey button style
    lv_style_init(&style_grey_button);
    lv_style_set_bg_opa(&style_grey_button, LV_OPA_COVER);
    lv_style_set_bg_color(&style_grey_button, lv_palette_lighten(LV_PALETTE_GREY, 2));
    lv_style_set_text_color(&style_grey_button, lv_palette_darken(LV_PALETTE_GREY, 1));

    // Initialize red button style
    lv_style_init(&style_red_button);
    lv_style_set_bg_color(&style_red_button, lv_palette_main(LV_PALETTE_RED));
    
    repeatPrime++;
  }
}

Also, for some reason, label_countdown doesn't go to NULL after being set to that in the while loop.

Code for summary screen and update screen
Code to write the summary screen:

  // Global variables for summary display screen
  lv_obj_t * btn_prime = NULL;
  lv_obj_t * label_prime = NULL;
  lv_obj_t * btn_start = NULL;
  lv_obj_t * label_start = NULL;.
  const char* menuOptions[] = {"Option 1", "Option 2", "Option 3", "Option 4"};
  const char* flowType[] = {"Steady", "Increasing", "Pulsatile"};
  const char* summaryLabels[] = {"Menu option:", "Variable 1:", "Variable 2:", "Variable 3:", "Variable 4:", "Time 1:", "Variable 5:", "Variable 6:", "Time 2:"};
  bool rememberSelections[9] = {false};
  int summary_loop = 0;

void display_summary() {
  // Clear the screen  
  lv_obj_clean(lv_scr_act());
  lv_refr_now(NULL); // Force immediate screen refresh

  // Display summary of selections
  label_summary = lv_label_create(lv_scr_act());
  lv_label_set_text(label_summary, "Summary of entries");
  lv_obj_set_style_text_font(label_summary, &lv_font_montserrat_26, LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_align(label_summary, LV_ALIGN_TOP_LEFT, 10, 10);

  for (int i; i <= 8; i++){
    if (rememberSelections[i] == true)
      write_summary_screen(i);
  }
  summary_loop = 0;

  // Prime Tubing Button
  btn_prime = lv_btn_create(lv_scr_act());
  lv_obj_set_size(btn_prime, 200, 75);
  lv_obj_align(btn_prime, LV_ALIGN_CENTER, 120, -75);
  label_prime = lv_label_create(btn_prime);
  lv_label_set_text(label_prime, "Prime");
  lv_obj_set_style_text_font(label_prime, &lv_font_montserrat_22, LV_PART_MAIN | LV_STATE_DEFAULT); // Set the label font
  lv_obj_center(label_prime);
  lv_obj_add_event_cb(btn_prime, prime_tubing_handler, LV_EVENT_CLICKED, NULL);

  // Start Perfusion Button
  btn_start = lv_btn_create(lv_scr_act());
  lv_obj_set_size(btn_start, 200, 75);
  lv_obj_align(btn_start, LV_ALIGN_CENTER, 120, 75);
  label_start = lv_label_create(btn_start);
  lv_label_set_text(label_start, "Start");
  lv_obj_set_style_text_font(label_start, &lv_font_montserrat_22, LV_PART_MAIN | LV_STATE_DEFAULT); // Set the label font
  lv_obj_center(label_start);
  lv_obj_add_event_cb(btn_start, start_perfusion, LV_EVENT_CLICKED, NULL);
}

void write_summary_screen(int i){

  label_entered_option = lv_label_create(lv_scr_act());
  lv_obj_set_style_text_font(label_entered_option, &lv_font_montserrat_18, LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_align(label_entered_option, LV_ALIGN_TOP_LEFT, 10, 50 * (summary_loop + 1)); // Align top left with a y-offset 

  label_entered_datum = lv_label_create(lv_scr_act());
  lv_obj_set_style_text_font(label_entered_datum, &lv_font_montserrat_18, LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_align(label_entered_datum, LV_ALIGN_TOP_LEFT, 20, 20 + (50 * (summary_loop + 1))); // Align top left with a y-offset 

  switch (i) {
  case 0:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text(label_entered_datum, menuOptions[menuSelection]);
    lv_refr_now(NULL);
    break;
  case 1:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text(label_entered_datum, variable1[selection]);
    lv_refr_now(NULL);
    break;
  case 2:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text_fmt(label_entered_datum, "%d", variable2);
    lv_refr_now(NULL);
    break;
  case 3:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text_fmt(label_entered_datum, "%d", variable3);
    lv_refr_now(NULL);
    break;
  case 4:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text_fmt(label_entered_datum, "%d", variable4);
    lv_refr_now(NULL);
    break;
  case 5:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text_fmt(label_entered_datum, "%d:%02d", (time1/1000/60), (time1/1000%60)); // need to convert back to time
    lv_refr_now(NULL);
    break;
  case 6:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text_fmt(label_entered_datum, "%d", variable5);
    lv_refr_now(NULL);
    break;
  case 7:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text_fmt(label_entered_datum, "%d", varaible6);
    lv_refr_now(NULL);
    break;
  case 8:
    lv_label_set_text(label_entered_option, summaryLabels[i]);
    lv_label_set_text_fmt(label_entered_datum, "%d:%02d", (time2/1000/60), time2/1000%60); // need to convert back to time
    lv_refr_now(NULL);
    break;
  }

  summary_loop++;

}

Compare this with the code for writing the update screen.

Code to write the update screen:

// Global variables for update screen
  lv_obj_t * label_pause = NULL;
  lv_obj_t * button_resume = NULL;
  lv_obj_t * label_update_screen_option = NULL;
  lv_obj_t * label_update_screen_datum = NULL;
  const char* updateScreenLabels[] = {"Menu option:", "Variable 1:", "Update variable 1:", "Update variable 2:", "Update variable 3:", "Current runtime"};
  bool rememberUpdateScreenSelections[6] = {false, false, false, false, false, true};
  int updateScreenLoop = 0;

void update_screen() {

  lv_obj_clean(lv_scr_act()); // Wipe the screen
  lv_refr_now(NULL); // Force immediate screen refresh

  for (int i = 0; i <= 5; i++){
    if (rememberUpdateScreenSelections[i] == true)
      write_update_screen(i);
  }
  updateScreenLoop = 0;

  // Pause button
  lv_obj_t * pauseButton = lv_btn_create(lv_scr_act()); // Create a button on the active screen
  lv_obj_set_size(pauseButton, 150, 75); // Set button size (width, height)
  lv_obj_align(pauseButton, LV_ALIGN_BOTTOM_MID, 0, -10); // Align button to the bottom middle

  lv_obj_t * label_pauseButton = lv_label_create(pauseButton); // Create a label on the button
  lv_label_set_text(label_pauseButton, "Pause"); // Set label text

  // Set the font for the label
  lv_obj_set_style_text_font(label_pauseButton, &lv_font_montserrat_22, LV_PART_MAIN | LV_STATE_DEFAULT); // Set the label font
  lv_obj_center(label_pauseButton); // Center the label on the button  

  lv_obj_add_event_cb(pauseButton, pause_perfusion_handler, LV_EVENT_CLICKED, NULL); // Assign the event handler to the button

  updateScreenTime = millis();
}

void write_update_screen(int i){

  label_update_screen_option = lv_label_create(lv_scr_act());
  lv_obj_set_style_text_font(label_entered_option, &lv_font_montserrat_30, LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_align(label_entered_option, LV_ALIGN_TOP_LEFT, 10, 70 * (updateScreenLoop + 1)); // Align top left with a y-offset 

  label_update_screen_datum = lv_label_create(lv_scr_act());
  lv_obj_set_style_text_font(label_entered_datum, &lv_font_montserrat_30, LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_align(label_entered_datum, LV_ALIGN_TOP_LEFT, 20, 30 + (70 * (updateScreenLoop + 1))); // Align top left with a y-offset 

  switch (i) {
  case 0:
    lv_label_set_text(label_update_screen_option, updateScreenLabels[i]);
    lv_label_set_text(label_update_screen_datum, menuOptions[menuSelection]);
    lv_refr_now(NULL);
    break;
  case 1:
    lv_label_set_text(label_update_screen_option, updateScreenLabels[i]);
    lv_label_set_text(label_update_screen_datum, variable1[selection]);
    lv_refr_now(NULL);
    break;
  case 2:
    lv_label_set_text(label_update_screen_option, updateScreenLabels[i]);
    lv_label_set_text_fmt(label_update_screen_datum, "%d", updateVariable1);
    lv_refr_now(NULL);
    break;
  case 3:
    lv_label_set_text(label_update_screen_option, updateScreenLabels[i]);
    lv_label_set_text_fmt(label_update_screen_datum, "%d", updateVariable2);
    lv_refr_now(NULL);
    break;
  case 4:
    lv_label_set_text(label_update_screen_option, updateScreenLabels[i]);
    lv_label_set_text_fmt(label_update_screen_datum, "%s", updateVariable3[number - 1]);
    lv_refr_now(NULL);
    break;
  case 5:
    run_time();
    lv_label_set_text(label_update_screen_option, updateScreenLabels[i]);
    lv_label_set_text_fmt(label_update_screen_datum, "%2d:%02d:%02d", currentHour, currentMinute, currentSecond);
    lv_refr_now(NULL);
    break;
  }

  updateScreenLoop++;  

}

Code requiring }} at the end
And in case you are interested, here is the code at the end that requires to extra unpaired }} (it's copied from RTC / UDP / NTP Example (Timezone) example from Arduino with only minor changes related to calculating the time zone):

void onDeviceRemoved() {
  Serial.print("USB device removed. ");
  Serial.println(millis());

  // Clear the screen  
  lv_obj_clean(lv_scr_act());
  lv_refr_now(NULL); // Force immediate screen refresh
  delay(200);

  // Display summary of selections
  lv_obj_t * label_USB_error = lv_label_create(lv_scr_act());
  lv_label_set_text(label_USB_error, "USB drive disconnected.\nThis USB drive might not be compatible.\nPlease try a different USB drive if this persists.");
  lv_obj_set_style_text_font(label_USB_error, &lv_font_montserrat_30, LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_align(label_USB_error, LV_ALIGN_CENTER, 0, 0); 
  lv_refr_now(NULL);
  while(1);
}}}

As a quick follow-up, I have found that the crashing is actually happening due to the combination of using the WiFi and USB-A for mass storage. If I use either one alone, it works fine and the crashes stop. But use them together, and the crashes happen.

I have also found that when I use the WiFi and USB together, I can progress further in the menus if I initialize the USB before the WiFi than the other way around, but either way, it still crashes.

I have also tried disconnecting from the WiFi after using it to set the internal time, but that didn't make any difference.

I also added initializing the USB before using and deinitializing after using it so that it's not left open to disconnecting from the WiFi, but this didn't help either.

I am having trouble with the screen turning white immediately on startup and not showing anything. I'm using an Arduino Giga with an Arduino Giga Display Shield, LVGL v9.2.2, and Arduino_H7_Video.

To test the display buffer, I replaced the code in the display buffer to colour the screen red (i.e., replaced the code in the second for loop with Display.set(x, y, 255, 0, 0);). I saw the red streak through the display in blocks, and then the blocks be trailed almost immediately by black as the red continued down the screen (whether or not I had the code for lv_obj_t * screen_black = lv_obj_create(NULL); there or not), then the screen turned white. I also put a serial print in the flush callback that was outputting to the monitor. So I know that the flush callback is being called and is working.

If I replace lv_obj_t * screen_black = lv_obj_create(NULL); with lv_obj_t * screen_black = lv_obj_create(lv_screen_active());, leave the rest of the code for the screen obj, and remove the code for creating the display and buffers, the screen does appropriately turn black (with a small white border).

I thought about trying TFT_eSPI instead of Arduino_H7_Video that I am currently using, but though it looks like the processor might be supported, I couldn't figure out the right driver for the display (and I haven't found an example from anyone who has got it working with the Giga Display).

Any ideas of what might be going on here?

#include <Arduino_H7_Video.h>
#include <Arduino_GigaDisplay.h> // v1.0.2
#include <Arduino_GigaDisplayTouch.h> // v1.0.1
#include <lvgl.h> // LVGL v9.2.2
#include <ArduinoGraphics.h>

Arduino_H7_Video Display(800, 480, GigaDisplayShield);
Arduino_GigaDisplayTouch TouchDetector;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  
  Display.begin();

  lv_init();

  lv_display_t * display = lv_display_create(800, 480); 

  static uint8_t buf1[800 * 480 / 10];
  lv_display_set_buffers(display, buf1, NULL, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);
  lv_display_set_flush_cb(display, my_disp_flush); 
  lv_disp_set_default(display);

  lv_obj_t * screen_black = lv_obj_create(NULL);
  lv_obj_set_size(screen_black, 800, 480);
  lv_obj_set_style_bg_color(screen_black, lv_color_black(), LV_PART_MAIN);
  lv_obj_set_style_bg_opa(screen_black, LV_OPA_COVER, LV_PART_MAIN);

  lv_screen_load(screen_black);

}

void loop() {
  // put your main code here, to run repeatedly:
  lv_timer_handler();
}

void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *color_p) {
  
  // Cast the buffer to lv_color_t for proper interpretation
  lv_color_t *color_map = (lv_color_t *)color_p;

  // Start drawing to the framebuffer
  Display.beginDraw();

  // Loop through the area and set pixels
  for (int32_t y = area->y1; y <= area->y2; y++) {
    for (int32_t x = area->x1; x <= area->x2; x++) {
      // Extract RGB565 from lv_color_t
      uint16_t color = *((uint16_t *)color_map);

      // Convert RGB565 to R, G, B components
      uint8_t r = (color >> 11) & 0x1F;  // Extract 5 bits for red
      uint8_t g = (color >> 5) & 0x3F;   // Extract 6 bits for green
      uint8_t b = color & 0x1F;          // Extract 5 bits for blue

      // Scale to 8-bit values (0-255)
      r = (r * 255) / 31;
      g = (g * 255) / 63;
      b = (b * 255) / 31;

      // Set the pixel color
      Display.set(x, y, r, g, b);

      // Advance to the next color in the buffer
      color_map++;
    }
  }

  // End drawing operation
  Display.endDraw();

  lv_display_flush_ready(disp);
}

Have a look at Bill's article on the GIGA Display at https://dronebotworkshop.com/giga-display/

Thanks for the suggestion. I did find it to be a helpful article when I got started, such as getting images to show, but he doesn't create displays (i.e., lv_display_t * display) in his example. Instead, he creates objects on the active screen.

As a bit of background on why I am trying to get a display, I was having some really odd crashing behaviour when I was just creating objects on the active screen. I want to use the USB-A to log data. I have the user make selections with radio buttons and enter numbers using a button matrix as they progress through some menu screens. The last screen is a summary screen, and it has two buttons. Depending on the selections the user made, the buttons would either work fine with one set of selections or the Arduino would crash with a different set of selections (this is a consistent pattern), even though the buttons were placed over the active screen and their code had nothing to do with the selections made. I was able to track it down to initializing the USB (using Arduino_USBHostMbed5.h). As long as I didn't initialize the USB, the buttons stopped crashing the Arduino, even though that code had nothing to do with the USB. My hope is that I can avoid the crashing by creating a display and using different screens to better organize the memory usage.

I ended up uninstalling and reinstalling the Arduino IDE as well as the libraries, and that got the display and screen showing up. However, the text is completely illegible, appearing as pixelated, cut, and and smeared content the size of the area that the text normally is. Then, when I create a new screen and delete the old one, nothing shows up (though I can see the buffer go through).

Taking out the code for creating a display and assuming that there is a default display, things look fine for the text and the LVGL widgets, and the screen updates immediately (i.e., I don’t see the flush going through). I’m still getting some other crashes switching between menu screens, but it’s progress.

In order to make all relevant information available to any who are interested in this subject, I'll share a link to @littlejohn657's cross-post here:

1 Like

I have an Arduino Giga and I am using a Giga Display Shield, and I am coding with LVGL v9.2.2. I have been trying to troubleshoot a strange issue with the Arduino crashing now for several days now, but I can't seem to figure out the cause.

I have written some code that allows the user to make selections with radio buttons and enter numbers. However, I have been getting unexpected crashes with one specific menu option, but the other ones are fine (though the crashing isn't always consistent, it's maybe 90% of the time). However, the these menu options have nothing to do with either the WiFi or USB-A.

I have tried using the lv_log_register_print_cb(my_log_cb); with different levels of importance. When the Arduino crashes, there are no warnings or anything that indicates that something concerning is happening. However, the crashes seem to happen at the exact same point in the process when I user TRACE as the level of importance; the last four lines are always (with different time stamps):

[Trace]	(96.288, +16)	 event_send_core: Sending event 30 to 0x24004a0c with 0x24003db4 param lv_obj_event.c:348
[Trace]	(96.288, +0)	 lv_free: freeing 0x24005544 lv_mem.c:121
[Trace]	(96.288, +0)	 lv_free: freeing 0x240054ec lv_mem.c:121
[Trace]	(96.288, +0)	 call_flush_cb: Calling flush_cb on (0;24)(799;47) area with 0x24023910 image pointer lv_refr.c:1195

By accident, I happened to find that the crashes happen when I am using both the WiFi to update the internal clock (I also use it to create file names later in the code) and initializing the USB-A for mass storage. If I comment out the code to initialize the USB-A but keep the WiFi, the crashes stop happening. If I comment out the code to connect to the WiFi, but keep the USB-A, the crashes stop happening. But put the two of them together, and the crashes return. But again, only with certain menu options.

I built the code that I am using for the WiFi and USB-A from the NTP with timezone example and the datalogger example. This is the code that I have in void setup() for initializing and connecting to the WiFi and the USB-A:

  // Attempt to connect to WiFi network three times:
  do {

    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    connectIterations = 0;

    // Wait up to 10 seconds for connection
    while (connectIterations <= 10 && status != WL_CONNECTED){
      delay(1000);
      connectIterations++;
      status = WiFi.status();
    }
    if (serialPrint)
      Serial.println(F(""));
    
    yPosition += lineSpacing;
    if(status != WL_CONNECTED) {
      status = WiFi.disconnect();
      lv_obj_align(label_startup, LV_ALIGN_TOP_LEFT, 10, yPosition);
      lv_label_set_text_fmt(label_startup, "Attempting to reconnect to SSID: %s", MY_SSID);
      lv_refr_now(NULL);
      Serial.print(F("Attempting to reconnect to SSID: "));
      Serial.println(F(MY_SSID));
    }

  }while (status != WL_CONNECTED && retryTimeout < 3);
  
  lv_obj_align(label_startup, LV_ALIGN_TOP_LEFT, 10, yPosition);
  lv_label_set_text(label_startup, "Connected to WiFi.");
  lv_refr_now(NULL);
  yPosition += lineSpacing;
  Serial.println(F("Connected to WiFi."));

  setNtpTime();
  delay(500);
  lv_obj_align(label_startup, LV_ALIGN_TOP_LEFT, 10, yPosition);
  lv_label_set_text(label_startup, "Setting time.");
  lv_refr_now(NULL);
  yPosition += lineSpacing;
  Serial.println(F("Setting time"));
  
 // Initialize the USB for logging data
 
  msd.connect();

  lv_obj_align(label_startup, LV_ALIGN_TOP_LEFT, 10, yPosition);
  lv_label_set_text(label_startup, "Initializing memory.");
  lv_refr_now(NULL);
  yPosition += lineSpacing;
  Serial.println(F("Initializing memory"));

  retryTimeout = 0;

  while (!msd.connected() && retryTimeout <= 3) {

    msd.connect();

    connectIterations = 0;

    while (connectIterations <= 6 && !msd.connected()){
      delay(1000);
      connectIterations++;
      msd.connect();
    }
    Serial.println(F(""));

    if(!msd.connected() && retryTimeout < 3) {
      lv_obj_align(label_startup, LV_ALIGN_TOP_LEFT, 10, yPosition);
      lv_label_set_text(label_startup, "MSD not found. Retrying.");
      lv_refr_now(NULL);
      yPosition += lineSpacing; // Move Y position down for the next line
      Serial.println(F("MSD not found. Retrying."));
      
    }

    retryTimeout++;
  }

(The other functions from the NTP example for setting the time are not included here, but are part of my longer code.)

You might run out of RAM...
Can you print the heap size and stack size every loop()?
LVGL is pretty heavy on memory
Some LCD libraries make a full copy of the info to be displayed in RAM... (numhorpixels x numverpixels x 2 byte)...

I tried using digitalWrite(PA_15, LOW); to turn the USB off as a way to disable the USB, as well as msd.deinit();, but neither of these, alone or in combination, was able to stop the crashing issue.

I also tried WiFi.disconnect(); in combination with the first two USB ideas, but this didn't solve the crashing issue either.

I also tried to see if there was a way to create a new class of WiFi (i.e., WiFiClass * myWiFi = new WiFiClass();) that I could later delete to clear the WiFi (I use the WiFi for maybe 5 seconds at the start), but WiFi.h doesn't allow this.

Thanks for the suggestion. I looked at both the mbed heap and the LVGL memory, but I am not seeing anything that is really jumping out at me.

Memory statistics at last point that I can visually register that it's working before crashing:

  1. mbed memory logs
  • Current heap size: 172100
  • Max heap size: 268340
  • Total heap size: 384640
  • Free heap size: 212540
  1. LVGL memory logs
  • Total Memory: 63740
  • Free Memory Blocks: 9
  • Memory Blocks Allocated and in Use: 104
  • Free Memory: 57332
  • Largest Free Block: 56400
  • Memory Used (%): 11%
  • Memory Fragmentation (%): 2%
  • Max size of heap memory used: 10252

Memory statistics as it is starting to create the text for the next screen:

  1. mbed memory logs
  • Current heap size: 172100
  • Max heap size: 268340
  • Total heap size: 384640
  • Free heap size: 212540
  1. LVGL memory logs
  • Total Memory: 63652
  • Free Memory Blocks: 1
  • Memory Blocks Allocated and in Use: 134
  • Free Memory: 56140
  • Largest Free Block: 56140
  • Memory Used (%): 12%
  • Memory Fragmentation (%): 0%
  • Max size of heap memory used: 10252

Memory statistics as it starts creating the button matrix for the next screen:

  1. mbed memory logs
  • Current heap size: 172100
  • Max heap size: 268340
  • Total heap size: 384640
  • Free heap size: 212540
  1. LVGL memory logs
  • Total Memory: 63648
  • Free Memory Blocks: 2
  • Memory Blocks Allocated and in Use: 134
  • Free Memory: 56096
  • Largest Free Block: 56084
  • Memory Used (%): 12%
  • Memory Fragmentation (%): 1%
  • Max size of heap memory used: 10252

Memory statistics as it finishes making the button matrix for the next screen:

  1. mbed memory logs
  • Current heap size: 172100
  • Max heap size: 268340
  • Total heap size: 384640
  • Free heap size: 212540
  1. LVGL memory logs
  • Total Memory: 63632
  • Free Memory Blocks: 2
  • Memory Blocks Allocated and in Use: 138
  • Free Memory: 55684
  • Largest Free Block: 55672
  • Memory Used (%): 13%
  • Memory Fragmentation (%): 1%
  • Max size of heap memory used: 10252

Memory statistics after it finishes creating the next screen (but before the screen appears) and the point where it crashes:

  1. mbed memory logs
  • Current heap size: 172100
  • Max heap size: 268340
  • Total heap size: 384640
  • Free heap size: 212540
  1. LVGL memory logs
  • Total Memory: 63600
  • Free Memory Blocks: 3
  • Memory Blocks Allocated and in Use: 145
  • Free Memory: 55480
  • Largest Free Block: 55412
  • Memory Used (%): 13%
  • Memory Fragmentation (%): 1%
  • Max size of heap memory used: 10252

For reference, if I use one of the menu options where it doesn't crash, at the point where the above option crashes, this is what the memory looks like when it doesn't crash and shows the next screen:

  1. mbed memory logs
  • Current heap size: 170108
  • Max heap size: 268340
  • Total heap size: 384640
  • Free heap size: 214532
  1. LVGL memory logs
  • Total Memory: 63568
  • Free Memory Blocks: 3
  • Memory Blocks Allocated and in Use: 153
  • Free Memory: 55232
  • Largest Free Block: 55164
  • Memory Used (%): 14%
  • Memory Fragmentation (%): 1%
  • Max size of heap memory used: 10436

@littlejohn657 ,

Your two or more topics on the same or similar subject have been merged.

Please do not duplicate your questions as doing so wastes the time and effort of the volunteers trying to help you as they are then answering the same thing in different places.

Please create one topic only for your question and choose the forum category carefully. If you have multiple questions about the same project then please ask your questions in the one topic as the answers to one question provide useful context for the others, and also you won’t have to keep explaining your project repeatedly.

Repeated duplicate posting could result in a temporary or permanent ban from the forum.

Could you take a few moments to Learn How To Use The Forum

It will help you get the best out of the forum in the future.

Thank you.

1 Like

I figured out that Arduino_H7_Video.h has a bunch of code for LVGL (you have to include lvgl.h for it to work), including initializing LVGL, setting up a display and buffer, etc., and you don't need to recreate it in your own code. So the issues that I was having with the display being blank and the weird buffering on the screen was because I was essentially creating a display with a buffer over a display with a buffer, and it was causing issues. This is why deleting my code to create a display and sticking to creating screens for the LVGL screen created by Arduino_H7_Video.h solved that issue.

1 Like

It might be a bit premature of me to say that the crashing has stopped, but it hasn't crashed the last few times that I tested it, and the rest of the functions seem to be working, so I am hopeful.

I ended up writing the rest of the code for the program and figured that I could come back and work on it if need be. In the process, I got rid of essentially every delay() of any significant length (lv_timer_handler() should run every 5–10 ms) and replaced it with a while loop of equivalent length that calls lv_timer_handler every 5 ms (delays can cause problems with blocking the LVGL processes). I can't say for certain what fixed the crashing problem, but it's worth trying if you get stuck.

nonblockingTimer = millis();
while (millis() - nonblockingTimer > 1000){
    lv_timer_handler();
    delay(5);
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.