Custom fonts crashing the board (platformIO/ESP32)

What do you want to achieve?

Using custom fonts with LVGL without crashing.

What have you tried so far?

I have used the online font converter to generate a custom font with only printable ASCII char for testing. I have declared the font in lv_conf.h and used in my main.cpp, the build & upload have no problem, but the board seems entered a crash loop once I started to use the font to display something. When I switch back to built-in font, the text shown with no problem.

The font, named lv_font_test_16.c, is placed in \.pio\libdeps\my_env\lvgl\src\font
(the docs just said “copy into your LGVL project”, which is pretty confusing to me.)

P.S., I am very new to LVGL

Code to reproduce

Code of declaring the font in lv_conf.h (line 629):

#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(lv_font_test_16)

Code of using the font in style, main.cpp:

 lv_style_init(&CJK_font);
 lv_style_set_text_font(&CJK_font, &lv_font_test_16);

Note that it was working well when using built-in font, so I don’t think the problem is in main.cpp

Parameter in the font converter:

name: lv_font_test_16
size: 16
Bpp: 2bit/px
fallback: lv_font_test_16
output format: C file

Enabled Font compression
Font range: 0x20-0x7F (printable ASCII char)

Size: 33KB

Environment

PlatformIO / VScode

  • MCU/MPU/Board: ESP32S3 with ST7789 2.8inch LCD TFT display
  • LVGL version:: 9.3.0

Hi

I would not put the font in that folder, the font file you can put it in the src folder, where all your c files code is. The folder you used is “internal” to PlatformIo and it will typically not be placed under version control (to share code with other persons / computer) and i think is “cleared” when you performa a “clean build”.

You can keep the #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(lv_font_test_16) I think is OK, I normally just add in one of my files:

LV_FONT_DECLARE(myfont_18)

typically, on main.c or in the gui.c file… in order to use it in the rest of the code.

Eventually attach the generated font file, to check for issues.

Nope, it’s still crashing
Ima attach the font here:
lv_font_test_16.c (32.0 KB)

and here is the main.cpp as i can’t upload C++ files:

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

#define BUF_SIZE (320 * 240 / 10) // Reduced buffer size

TFT_eSPI tft = TFT_eSPI();

static lv_style_t CJK_font;

lv_obj_t *player_lable;
lv_obj_t *title_lable;
lv_obj_t *artist_lable;

static uint8_t buf1[BUF_SIZE];
static uint8_t buf2[BUF_SIZE];

String receivedMessage[6] = {"", "", ""};
String values[6] = {"", "", ""};

int counter = 0;

/* Tick source, tell LVGL how much time (milliseconds) has passed */
static uint32_t get_time(void)
{
    return millis(); // Use millis() for Arduino compatibility
}

/* Display flushing */
void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors((uint16_t *)&color_p[0], w * h, true);
    tft.endWrite();

    lv_display_flush_ready(disp);
}

void setup() {
  Serial.begin(115200); // Initialize hardware serial for USB communication
  pinMode(2, OUTPUT);
  analogWrite(2, 100);             // Backlight PWM

  lv_init();

  // TFT init
  tft.init();
  tft.setRotation(1); // Adjust rotation as needed
  tft.fillScreen(TFT_BLACK);

  lv_tick_set_cb(get_time);

  lv_display_t * display1 = lv_display_create(320, 240);

  lv_display_set_buffers(display1, buf1, buf2, BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL);

  lv_display_set_flush_cb(display1, my_disp_flush);

  LV_FONT_DECLARE(lv_font_test_16);
  lv_style_init(&CJK_font);
  lv_style_set_text_font(&CJK_font, &lv_font_test_16);

  //[Player_Name]
  player_lable = lv_label_create(lv_scr_act());
  lv_label_set_text(player_lable, "Stopped");
  lv_obj_align(player_lable, LV_ALIGN_TOP_LEFT, 10, 10);

  //[Title]
  title_lable = lv_label_create(lv_scr_act());
  lv_label_set_text(title_lable, "Fetching");
  lv_obj_align(title_lable, LV_ALIGN_TOP_LEFT, 10, 30);
  lv_obj_add_style(title_lable, &CJK_font, 0);

  //[Artist]
  artist_lable = lv_label_create(lv_scr_act());
  lv_label_set_text(artist_lable, "Fetching");
  lv_obj_align(artist_lable, LV_ALIGN_TOP_LEFT, 10, 50);
  lv_obj_add_style(artist_lable, &CJK_font, 0);


  while (Serial.available()){
    Serial.read();
  }
}

void loop() {
  while (Serial.available()) { // Check if data is available in the Serial buffer

    char incomingChar = Serial.read(); // Read each character

    if (incomingChar == '\n') { // If newline character (Enter) is received
      receivedMessage[counter] += '\n';
      counter++;

      if (counter >= 6){
        counter = 0;
        for (int i = 0; i < 6; i++){
          values[i] = receivedMessage[i];
          receivedMessage[i] = "";
        }
      }

    } else {
      receivedMessage[counter] += incomingChar; // Append characters to the message
    }
  }
  lv_label_set_text(player_lable, values[0].c_str());
  lv_label_set_text(title_lable, values[1].c_str());
  lv_label_set_text(artist_lable, values[2].c_str());
  lv_timer_handler(); // Update the UI
  delay(5);
}

Tho im planning to give up on custom font, as I noticed I need to make multiple font files for different font size, and i wanna use CJK fonts, which means most likely i will be short on memory.

Sorry, don’t see any obvious issue, it seems to work on the simulator.

One thing i would do, is to pass the font decaration:

LV_FONT_DECLARE(lv_font_test_16);

to the start of the c file, like, next to the

static lv_style_t CJK_font;

Although on the simulator, there was no “issue” with doing that, but since that macro extends to extern const lv_font_t font_name; is better to have it outside functions or in a h file…

And when the ESP Crash… what is it complaining? what does it print on the serial console? It typically tells the function/line where the crash was created…

Too cleaner is place lv_timer_handler(); // Update the UI on loop start.