Export LVGL canvas symbols for side-loaded GameBoy app

This commit is contained in:
Hermes Reyna Bot
2026-07-17 18:10:52 -04:00
parent 94b5cdbfc4
commit 047c7c87d7
3 changed files with 82 additions and 5 deletions
@@ -0,0 +1,73 @@
# GameBoy side-loaded ELF support notes
This branch records the firmware-side changes used to make the Tactility GameBoy emulator prototype load past the generic `missing symbol` failure on an ESP32-S3 ES3C28P board.
## Problem
Side-loaded `.app` packages are relocated by the firmware ELF loader. The app can build successfully against the local SDK but still fail at runtime if the board firmware does not export every dynamic symbol used by the app.
For the GameBoy prototype, USB serial logs showed the emulator app was being found and started, but firmware without the needed LVGL exports showed missing-symbol failures for the canvas path.
## Firmware changes in this branch
`TactilityC/Source/tt_init.cpp` now includes LVGL and exports the core LVGL canvas/display functions the GameBoy app needs:
- `lv_canvas_create`
- `lv_canvas_set_buffer`
- `lv_obj_invalidate`
- `lv_display_get_horizontal_resolution`
- `lv_display_get_vertical_resolution`
These are exported through the existing `main_symbols[]` table with `ESP_ELFSYM_EXPORT(...)`, making them visible to side-loaded ESP32-S3 app ELFs.
The branch also removes the `AlwaysRun` devicetree delete target from `Firmware/CMakeLists.txt`. During iterative firmware builds this target deleted `Generated/devicetree.c` while compile rules still depended on it, causing intermittent build failures. The devicetree generation rule now depends directly on the source YAML.
## Verification performed
On the iMac build host:
```bash
cd ~/Projects/gitea/tactility
. ~/esp/esp-idf/export.sh
python3 device.py es3c28p
idf.py build
idf.py -p /dev/ttyACM0 flash
```
Build result:
```text
Tactility.bin binary size 0x2b82b0 bytes
Smallest app partition is 0x400000 bytes
0x147d50 bytes (32%) free
```
Flash target:
```text
ESP32-S3 ES3C28P
USB serial: /dev/ttyACM0
MAC: 14:c1:9f:d0:5c:5c
```
Runtime serial showed the GameBoy core loop running and reporting approximately 49-50 FPS:
```text
GAMEBOY_FPS 50
GAMEBOY_FPS 49
GAMEBOY_FPS 49
```
## Current caveat
The app loop/FPS is running, but the photographed device showed the GameBoy framebuffer was not visibly rendering yet. That remaining issue is in the app/UI canvas presentation path, not the firmware missing-symbol issue addressed by this branch.
## Related app work
The app-side prototype lives in the separate Gitea repo:
```text
https://git.reynafamily.com/adolforeyna/tactility_apps.git
```
The app branch changes include restoring the LVGL canvas path, using `/data/roms/gb/default.gb` for the flashed data-image test, and printing `GAMEBOY_FPS` markers over USB serial.
+1 -5
View File
@@ -100,17 +100,13 @@ endif ()
#
# Devicetree code generation
#
add_custom_target(AlwaysRun
COMMAND ${CMAKE_COMMAND} -E rm -f "${GENERATED_DIR}/devicetree.c"
)
add_custom_command(
OUTPUT "${GENERATED_DIR}/devicetree.c"
"${GENERATED_DIR}/devicetree.h"
COMMAND python "${CMAKE_SOURCE_DIR}/Buildscripts/DevicetreeCompiler/compile.py"
"${DEVICETREE_LOCATION}" "${GENERATED_DIR}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
DEPENDS AlwaysRun "${DEVICETREE_LOCATION}/devicetree.yaml" # AlwaysRun ensures it always gets built
DEPENDS "${DEVICETREE_LOCATION}/devicetree.yaml"
COMMENT "Generating devicetree source files..."
)
add_custom_target(Generated DEPENDS "${GENERATED_DIR}/devicetree.c")
+8
View File
@@ -43,6 +43,7 @@
#include <esp_netif.h>
#include <esp_heap_caps.h>
#include <esp_timer.h>
#include <lvgl.h>
#include <fcntl.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>
@@ -376,6 +377,13 @@ const esp_elfsym main_symbols[] {
// tt::lvgl
ESP_ELFSYM_EXPORT(tt_lvgl_spinner_create),
// LVGL APIs needed by side-loaded apps that render into canvases.
ESP_ELFSYM_EXPORT(lv_canvas_create),
ESP_ELFSYM_EXPORT(lv_canvas_set_buffer),
ESP_ELFSYM_EXPORT(lv_obj_invalidate),
ESP_ELFSYM_EXPORT(lv_display_get_horizontal_resolution),
ESP_ELFSYM_EXPORT(lv_display_get_vertical_resolution),
// stdio.h
ESP_ELFSYM_EXPORT(rename),
ESP_ELFSYM_EXPORT(rewind),