Skip to content

Fix ESP32 crash with custom fonts in LVGL 9.x#2

Open
youkorr wants to merge 25 commits intomainfrom
claude/fix-esp32-font-crash-UdBfx
Open

Fix ESP32 crash with custom fonts in LVGL 9.x#2
youkorr wants to merge 25 commits intomainfrom
claude/fix-esp32-font-crash-UdBfx

Conversation

@youkorr
Copy link
Copy Markdown
Owner

@youkorr youkorr commented Jan 27, 2026

Add null pointer safety checks and explicit initialization to prevent
crashes when using custom fonts (Google Fonts, web fonts, local TTF) on
ESP32 with LVGL 9.4.

Changes:

  • Add null pointer checks in get_glyph_bitmap callback
  • Add null pointer checks in get_glyph_dsc_cb callback
  • Add empty vector check in find_glyph to prevent out-of-bounds access
  • Explicitly initialize lv_font_t.fallback to nullptr
  • Explicitly initialize lv_font_t.kerning to 0
  • Update README with troubleshooting section

This fixes the issue where custom fonts crash ESP32 while built-in
montserrat fonts work correctly.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir

claude and others added 25 commits January 27, 2026 19:04
Add null pointer safety checks and explicit initialization to prevent
crashes when using custom fonts (Google Fonts, web fonts, local TTF) on
ESP32 with LVGL 9.4.

Changes:
- Add null pointer checks in get_glyph_bitmap callback
- Add null pointer checks in get_glyph_dsc_cb callback
- Add empty vector check in find_glyph to prevent out-of-bounds access
- Explicitly initialize lv_font_t.fallback to nullptr
- Explicitly initialize lv_font_t.kerning to 0
- Update README with troubleshooting section

This fixes the issue where custom fonts crash ESP32 while built-in
montserrat fonts work correctly.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
- Initialize lv_font_glyph_dsc_t with memset to zero all fields
- Add detailed logging for glyph bitmap retrieval errors
- Add sanity check for null glyph data with non-zero dimensions
- Include <cstring> for memset

This helps debug font crashes and ensures all LVGL 9.x struct
fields are properly initialized.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
CRITICAL FIX: In LVGL 9.x, the lv_font_t structure has a static_bitmap
flag that must be set to 1 for fonts with bitmap data in ROM/flash.
If this is 0 (default), LVGL may try to free the bitmap data after
rendering, causing a crash.

Changes:
- Set lv_font_.static_bitmap = 1 (bitmap data is in PROGMEM)
- Set lv_font_.release_glyph = nullptr (no cleanup needed)

This should fix the silent crash when using custom fonts (Roboto, MDI
icons, etc.) while built-in montserrat fonts work correctly.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
Added logging to help diagnose font initialization issues:
- Log when Font is created with glyph count, height, baseline, bpp
- Log LVGL font init parameters when USE_LVGL_FONT is defined
- Warn if USE_LVGL_FONT is NOT defined (callbacks won't work)

This will help identify if the font is being created and if
USE_LVGL_FONT is properly enabled during compilation.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
Added DEBUG level logs in get_glyph_dsc_cb to trace:
- Unicode codepoint being requested
- Font bpp and glyph count
- Whether glyph was found or not
- Glyph dimensions if found

This will help diagnose:
- MDI icons showing as squares (glyph not found?)
- Roboto causing reboot (crash in callback?)

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
- Add extensive debug logging to get_glyph_bitmap() to trace crash location
- Remove memset(dsc, 0, ...) which may destroy LVGL pre-initialized fields
- Log format, stride, and box dimensions after setting glyph descriptor

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
The crash at address 0x1c86c371 was caused by LVGL 9.x reading
uninitialized fields in lv_font_glyph_dsc_t after get_glyph_bitmap
returned. LVGL's renderer expects internal fields (like 'entry' cache
pointer) to be NULL/0.

The memset is required to zero out ALL fields before we populate
the ones we know about.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
- Add memset(&lv_font_, 0, sizeof(lv_font_t)) to zero ALL font fields
- Explicitly set user_data = nullptr for LVGL 9.x compatibility
- Add detailed logging of g_dsc fields in get_glyph_bitmap
- Log draw_buf parameter to check if LVGL expects it to be used

The crash at 0x1c86c371 suggests uninitialized fields somewhere
in LVGL's internal structures. This commit ensures both lv_font_t
and lv_font_glyph_dsc_t are fully zeroed before use.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
The crash at 0x1c86c371 occurred because LVGL 9.x passes a non-null
draw_buf parameter expecting the bitmap data to be copied into it.
Even with static_bitmap=1, LVGL may use draw_buf internally for
its render pipeline.

When draw_buf is provided with valid data pointer, we now copy
the glyph bitmap data into it and return the draw_buf->data pointer.
This ensures LVGL has the data in the expected location.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
- Log draw_buf->data value before using it
- Add log after memcpy to confirm copy completed
- This will help identify if crash is before/during/after our code

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
LVGL 9.x has rendering issues with A1 (1bpp) format that causes
crash at address 0x1c86c371. The A4 format (4bpp) works correctly.

Solution: Convert 1bpp fonts to 8bpp (A8) format on the fly:
- In get_glyph_dsc_cb: Report A8 format and stride=width for bpp=1
- In get_glyph_bitmap: Convert each 1-bit pixel to 0x00 or 0xFF

This ensures all fonts render using formats that LVGL 9.x handles
correctly while maintaining the original font data in flash.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
When LVGL calls get_glyph_bitmap without a draw_buf (draw_buf=0x0),
we couldn't convert A1 to A8 format, causing corrupted text display.

Solution: Use a 4KB static buffer for the conversion when draw_buf
is not provided. This covers glyphs up to 64x64 pixels in A8 format.

The text now displays correctly because the A1 bitmap data is properly
converted to A8 format that LVGL 9.x can render.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
The A1->A8 conversion with static buffer caused issues because
multiple glyphs shared the same buffer, overwriting each other.

Reverting to native A1 format - the original crash at 0x1c86c371
was likely caused by uninitialized fields which are now fixed:
- memset on lv_font_glyph_dsc_t before populating fields
- memset on lv_font_t in constructor
- All LVGL 9.x fields explicitly set including user_data

This simpler approach should work now that memory is properly initialized.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
When static_bitmap=1, LVGL doesn't provide draw_buf for A1 format fonts
and expects the callback to return static data pointers. However, this
approach doesn't work correctly on ESP32-P4 (RISC-V architecture).

By setting static_bitmap=0, LVGL will provide a draw_buf for all glyph
formats (A1, A4, etc.), allowing us to copy data to the buffer. This is
the same mechanism that already works for A4 format (MDI icons).

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
The A1 renderer in LVGL has issues on ESP32-P4 (RISC-V architecture),
but A8 format works correctly. This change:

1. In get_glyph_dsc_cb: Report A8 format and stride for A1/A2 fonts
2. In get_glyph_bitmap: Convert A1/A2 data to A8 when copying to draw_buf
   - A1: bit 0 -> 0x00, bit 1 -> 0xFF
   - A2: values 0-3 -> 0x00, 0x55, 0xAA, 0xFF

A4 format (used by MDI icons) continues to work as before with direct copy.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
On ESP32-P4 (RISC-V architecture), font data is stored in flash memory
(PROGMEM) and must be read using progmem_read_byte() instead of direct
pointer access.

This fix applies to all format conversions:
- A1 to A8 conversion
- A2 to A8 conversion
- A4 direct copy
- A8 direct copy

Without this, the code reads garbage data instead of actual glyph bitmaps,
resulting in squares/corrupted characters on display.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
ESPHome font data uses a sequential bit-stream format (MSB first) without
row padding, as seen in the Font::print() function. The previous code
incorrectly assumed row-based stride with padding.

Changes:
- A1->A8: Read bits sequentially using bitmask, matching print() logic
- A2->A8: Read 2 bits per pixel sequentially
- A4: Read 4 bits per pixel and repack into LVGL A4 format
- A8: Read 8 bits per pixel sequentially

This matches exactly how Font::print() reads glyph data from PROGMEM.

https://claude.ai/code/session_01LFnxwtjd8jHjFeEgHkvVir
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants