Skip to content

feat(p4): integrate esp_lvgl_adapter for unified LVGL management#36

Closed
Ghost64847 wants to merge 2 commits intoHighCodeh:devfrom
Ghost64847:feat/esp-lvgl-adapter-integration
Closed

feat(p4): integrate esp_lvgl_adapter for unified LVGL management#36
Ghost64847 wants to merge 2 commits intoHighCodeh:devfrom
Ghost64847:feat/esp-lvgl-adapter-integration

Conversation

@Ghost64847
Copy link
Copy Markdown

@Ghost64847 Ghost64847 commented Mar 26, 2026

Summary

Integrates esp_lvgl_adapter (v0.4.1) as a local component into the ESP32-P4 firmware, replacing the manual LVGL initialization boilerplate with a unified, thread-safe adapter layer.

Changes

Build System

    • Updated EXTRA_COMPONENT_DIRS to list each component path individually (required due to ESP-IDF CMake behavior: directories with CMakeLists.txt are registered as a single component, not scanned for sub-components)

New Components Added

Component Location Purpose
esp_lvgl_adapter Service/ Unified LVGL task, mutex, tick management
esp_lv_fs Service/ LVGL virtual filesystem driver
esp_lv_decoder Service/ LVGL image decoder (JPEG/PNG)
button Drivers/ Button component (input dependency)
knob Drivers/ Rotary encoder component (input dependency)
esp_lcd_touch Drivers/ Touch input component (input dependency)
freetype Core/ FreeType font rendering library

Kernel & UI Changes (kernel.c, ui_manager.c)

  • Replace manual lv_init() / tick timer / mutex creation with esp_lv_adapter_init()
    • esp_lv_adapter_start() now creates and manages the LVGL worker task
    • ui_acquire() / ui_release() delegate to esp_lv_adapter_lock() / esp_lv_adapter_unlock() for unified thread safety
    • lv_port_disp_init() (BLE screen mirror hook) and lv_port_indev_init() (physical keypad) are retained and remain fully functional

New Capabilities Available (opt-in via menuconfig)

  • esp_lv_adapter_refresh_now() - force immediate display refresh
    • esp_lv_adapter_pause() / resume() - pause LVGL worker for exclusive access
    • esp_lv_adapter_set_dummy_draw() - bypass LVGL for direct framebuffer writes (camera/video)
    • FPS statistics API (CONFIG_ESP_LV_ADAPTER_ENABLE_FPS_STATS)
    • FreeType TrueType/OpenType font rendering (CONFIG_ESP_LV_ADAPTER_ENABLE_FREETYPE)
    • PPA hardware acceleration on ESP32-P4 (auto-enabled)

Testing

  • Verified no double lv_display_create() calls
    • Thread safety confirmed via single mutex delegation through ui_acquire()/ui_release()
    • BLE screen mirror hook preserved in lv_port_disp.c flush callback

Relocate esp_lvgl_adapter, esp_lv_fs, esp_lv_decoder to components/Service/;
button, knob, esp_lcd_touch to components/Drivers/; freetype to components/Core/.
Update EXTRA_COMPONENT_DIRS to list each component path individually since
ESP-IDF registers a directory as a component when it contains CMakeLists.txt
instead of scanning its subdirectories.
- Add esp_lvgl_adapter (v0.4.1) as a local component under components/Service/
- Add companion components: esp_lv_fs, esp_lv_decoder (Service/),
  button, knob, esp_lcd_touch (Drivers/), freetype (Core/)
- Replace manual lv_init/tick/mutex boilerplate in kernel.c with
  esp_lv_adapter_init() and esp_lv_adapter_start()
- Delegate ui_acquire()/ui_release() to esp_lv_adapter_lock/unlock()
  for unified thread-safe LVGL access across all tasks
- Retain lv_port_disp_init (BLE screen mirror hook) and lv_port_indev_init
  (physical keypad) as they don't conflict with the adapter
- Enable PPA hardware acceleration on ESP32-P4 (auto-detected in CMakeLists)
- Update sdkconfig and partitions for ESP32-P4 build requirements
@Ghost64847 Ghost64847 force-pushed the feat/esp-lvgl-adapter-integration branch from 5e339bf to a1cf2d2 Compare March 26, 2026 17:27
@anarchyysm
Copy link
Copy Markdown
Member

thanks for the contribution and for taking the time to put this together! I appreciate the effort.
However, after analyzing the PR in detail, I have some concerns and questions before we can move forward.


Why we're hesitant about esp_lvgl_adapter

Our current LVGL setup is intentionally minimal and gives us full control over the rendering pipeline. We manually manage lv_init(), the tick timer, the recursive mutex, and the lv_timer_handler() task - this is roughly ~30 lines of straightforward code in ui_manager.c and kernel.c that works reliably.

More importantly, we need direct access to the display buffers and flush callback. Our lv_port_disp.c hooks into esp_lcd_panel_draw_bitmap() and simultaneously streams partial frames to our mobile app via BLE (ble_screen_server_send_partial()). This screen mirroring feature is core to our product, any abstraction layer sitting between us and the LVGL display pipeline adds risk and complexity to maintaining that. The adapter doesn't provide or replace this functionality; we'd still need our own lv_port_disp_init() regardless.

On the technical side, esp_lcd already handles all the hardware-level display management (panel creation, SPI IO, rotation, backlight). The adapter essentially wraps what we already do without adding meaningful capability for our use case.

Regarding multilingual support - which was mentioned as the main motivation, this is actually a native LVGL 9 feature. We can enable FreeType rendering by simply setting CONFIG_LV_USE_FREETYPE=y in sdkconfig and adding espressif/freetype to our idf_component.yml. No adapter needed.


Questions

  1. Could you clarify what specific advantages esp_lvgl_adapter would bring to our project that we can't achieve with standard LVGL 9 configuration? We want to make sure we're not adding an abstraction layer for features we can enable directly.

  2. The PR adds 967 files with ~385,000 lines of code - the vast majority being vendored third-party source trees copied directly into the repo:

    • Full FreeType library with builds for Amiga, Atari, Symbian, DOS, Windows CE
    • button, knob, esp_lcd_touch components — all disabled in sdkconfig and not relevant to our hardware
    • Python test scripts, example projects, documentation

    Why were these included rather than using ESP-IDF's component manager (idf_component.yml)? And why include components that aren't enabled or relevant to our hardware?


We're open to discussing this further, if there's a concrete benefit we're missing, we'd love to hear it! But as it stands, the adapter seems like an unnecessary abstraction for our specific use case, and the PR scope is way beyond what's needed.
Consider enter in our Discord Server so we can talk about this more easily

@Ghost64847
Copy link
Copy Markdown
Author

Hi @anarchyysm,
Thanks for the patient explanation! It makes perfect sense to me now why the esp_lvgl_adapter is redundant and not suitable for your setup. I will definitely pay more attention to this and make sure to remove all the unnecessary files.
I have also joined your Discord server. I'm really looking forward to learning more about the project and chatting with the community there, thanks again for the guidance!

@Ghost64847 Ghost64847 closed this Mar 26, 2026
@Ghost64847
Copy link
Copy Markdown
Author

I also plan to add arduino-esp32 in firmware_c5 to port Marauder, what do you think?

@Ghost64847 Ghost64847 deleted the feat/esp-lvgl-adapter-integration branch March 26, 2026 23:18
@anarchyysm
Copy link
Copy Markdown
Member

I also plan to add arduino-esp32 in firmware_c5 to port Marauder, what do you think?

depends on how are you planning to make this, you want to add libs for compatibility with arduino libs? Tell me your plan

@Ghost64847
Copy link
Copy Markdown
Author

Ghost64847 commented Mar 27, 2026

I plan to add the arduino-esp32 component to port Marauder and modify Marauder's code for the display parts and SPI communication of the esp32p4 and esp32c5 to adapt it to TentacleOS. I have also reverse-engineered libnet80211.a.zip to send deauth frames. I noticed that Espressif has released the esp32e22. Will TentacleOS run on esp32e22 in the future? What do you think of these ideas of mine?

@anarchyysm
Copy link
Copy Markdown
Member

Maybe its an option for a custom firmware later, made by community. If we put Marauder on our firmware, the device won't pass through the regulations tests, we need to even remove our aggressive functions. Plus we not want arduino libs on our firnware, because of ecessive abstraction. Our original firmware, can do everything that marauder, and better.


Doesnt need rev Eng the wifi libs too, the comunity already done this, on esp-idf, you just need to put:

int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3) {
    return 0;
}

into deauther file, in our firmware, is located on /firmware_c5/components/Applications/wifi/wifi_deauther.c


and put this on CMakeLists.txt:

target_link_libraries(${COMPONENT_LIB} -Wl,-zmuldefs)

who is located on /firmware_c5/components/Applications/CMakeLists.txt

@Ghost64847
Copy link
Copy Markdown
Author

Thank you for your response, but I am currently a bit confused and unsure about what contributions I should make or what features I should add to this project, as it seems that everything has already been developed.

@anarchyysm
Copy link
Copy Markdown
Member

anarchyysm commented Mar 27, 2026

maybe its better to wait selection of developer program finish, so you are gon to have all the schematics, pins and access to complete architecture. So at these point, we're going to start open issues/task without assign to specifc person in High Boy GitHub Projects so any open task, you can pick, asign to yourself and start to develop. For now we're using this link just for official developers, but in a few weeks we're going open tasks for the community

@Ghost64847
Copy link
Copy Markdown
Author

I have an idea to use wasm (https://components.espressif.com/components/espressif/wasm-micro-runtime/versions/2.4.0~1/readme) instead of Flipper Zero's FAP. What do you think?

@anarchyysm
Copy link
Copy Markdown
Member

this is a idea for apps right? actually thats a really good ideia, but i never used wasm-micro-runtime, so i need to share our actual planned architecture, and ask some questions. We didn't start to develop this yet, so its a good moment to change and decides if we're going to use ELF or WASM.

We already have a fully designed app architecture based on native ELF binaries running on the ESP32-P4. Here's an overview:

App Container Format (.hb)

Our custom container format includes:

  • Header with magic bytes, format version, and offset addresses
  • Metadata — app name, version, author, and raw icon for LVGL
  • Permission Manifest — list of capabilities the app requests (WiFi, SD, GPIO, etc.)
  • Digital Signature — SHA-256 hash signed with the developer's private key
  • Payload — the relocatable ELF binary itself

Execution Engine

  • ELF Loader reads the .hb file, validates the signature, and loads the code into PSRAM (MALLOC_CAP_EXEC) for direct native execution
  • Jump Table — a fixed address table in the firmware that exposes system functions to apps. This is the bridge that allows apps to call firmware APIs without breaking across firmware updates
  • Process Monitor — a dedicated FreeRTOS task per app with an active Watchdog

ABI Versioning

The .hb header contains a required_api_level field. The firmware checks compatibility before loading — if the app requires API level 7 but the firmware only provides level 5, it refuses to run and prompts the user to update.

UI Handoff (LVGL)

  • The firmware creates an lv_obj_t container and passes the pointer directly to the app
  • The app draws its UI as children of that container
  • On app close (or crash), the firmware deletes the container and all children automatically, preventing memory leaks
  • Input events (buttons/touch) are routed exclusively to the foreground app

Permission System & Sandbox

  • The firmware reads the permission manifest before execution
  • The Jump Table enforces access control — if an app calls wifi_start() without the WiFi permission in its manifest, the call returns an error
  • User consent prompt is shown on first run, listing all sensitive capabilities the app requests

IPC & Data

  • A global clipboard buffer allows apps to share data: App A writes via hb_set_handoff_data(), App B reads via hb_get_handoff_data()
  • Each app has isolated persistent storage at /sd/apps/data/[app_id]/

Power Management

  • Apps must explicitly request wake-locks: hb_power_request(WIFI_KEEP_ALIVE)
  • On app close or crash, all wake-locks and peripheral states are force-released by the firmware

Developer SDK

  • Toolchain: riscv-esp-elf-gcc with position-independent code (PIC)
  • Custom linker script for PSRAM execution
  • API headers exposing the authorized function set
  • Python packager script that bundles the ELF + icon + manifest + signature into the .hb file

Code Signing & Distribution

  • Firmware holds a root public key; apps are verified pre-execution
  • Unsigned or invalid apps are not blocked, but display a large warning — protecting the user without removing freedom
  • Central repository at repo.highboy.com with a global catalog (catalog.json) and a version-check API for OTA app updates

Questions About the WASM Proposal

I looked into the WAMR component and I can see it supports the ESP32-P4, has AOT compilation with near-native speed, and provides native API registration for host functions — which would effectively replace our Jump Table. The built-in sandboxing is also a real advantage over our manifest-based permission enforcement.

That said, I have some specific questions before considering a switch:

1. LVGL Binding — This is the big one

Our current architecture passes raw lv_obj_t* pointers directly to apps for UI rendering. WASM modules run in their own linear memory and cannot dereference native pointers. How do you envision exposing the LVGL API to WASM apps?

  • Wrapping the entire LVGL API as host functions is a massive undertaking — there are hundreds of functions, all pointer-heavy with callbacks and hierarchical object trees
  • Would you use a handle/ID translation layer instead of raw pointers?
  • Or do you have a different approach for the UI layer entirely (e.g., a declarative UI protocol instead of direct LVGL calls)?

2. Complex Data Marshalling

Beyond LVGL, our API surface includes functions that pass structs, buffers, and callback function pointers between firmware and app. The WAMR docs don't detail how pointer/struct marshalling works between host and WASM. Have you prototyped any non-trivial host function bindings on ESP32 to see how this works in practice?

3. AOT Performance on P4 With Graphics

WAMR claims near-native speed with AOT, but the benchmarks are for compute workloads (CoreMark, PolyBench). Has anyone tested AOT performance on the P4 for something interactive and graphics-heavy — like an app rendering LVGL widgets and responding to input events at 30+ fps?

4. Trade-off Justification

The ELF-based architecture is already fully designed and maps closely to what Flipper Zero does with FAP (which is battle-tested in production). The main advantage of WASM I see is stronger sandboxing. Is that the primary motivation, or are there other benefits you see that would justify redesigning the loader, the SDK toolchain, the .hb container format, and the entire developer experience?

5. Developer Experience Impact

Currently our SDK is straightforward: GCC + C/C++ + our headers + a packager script. With WASM, developers would need clang --target=wasm32 or Emscripten, plus the wamrc AOT compiler. Is the added toolchain complexity acceptable for our target developer community?


I'm genuinely open to the idea if the answers are solid, especially on the LVGL binding question, since that's the core of the user experience on HighBoy. Would love to hear your thoughts.

@Ghost64847
Copy link
Copy Markdown
Author

First of all, thank you for your response. I apologise for not having tested WASM on the ESP32, and I am also more inclined to use raw pointers. Encapsulating various APIs is indeed a massive undertaking. After reading your description, I believe the ELF Loader is indeed better than WASM, since WASM essentially acts as a sandbox. Moreover, you already have a complete application architecture based on running native ELF binaries on the ESP32-P4. I look forward to the release of your ELF Loader. I have a suggestion to update the ESP-IDF version from 5.5.1 to 5.5.3, and to update driver/i2c.h to the new driver/i2c_master.h. Additionally, I have a question regarding Espressif's release of the ESP32-E22, with specifications as follows: (Chip Series: ESP32-E22 CPU: RISC-V 32-bit dual-core HP Core @ 500 MHz RAM (Expandable PSRAM): 1 MB Wireless: 2.4 / 5 / 6 GHz tri-band Wi-Fi 6E + BLE 6.0 + Classic BT Key Peripherals: PCIe, SDIO, USB 2.0 Typical Use Cases: PC wireless adapters, AI computing & robotics, XR wearables, consumer electronics, industrial gateways Longevity Commitment: None). Will TentacleOS eventually be run on the ESP32-E22?

@anarchyysm
Copy link
Copy Markdown
Member

Currently, TentacleOS is exclusively for the current High Boy dual-mcu architecture, with ESP32-P4 and ESP32-C5. The main idea is not to create a multi-device firmware like Bruce Firmware and others. But you know, our code is open source, the community can create forks that run on other chips.

If you really want to contribute, how about implementing a JS and Lua interpreter? This is in our plan so that the community can create quick scripts without having to compile an entire ELF file, but we haven't worked out the entire workflow for how this will work. We know JavaScript works, but we haven't looked into whether the Lua language can run smoothly on the ESP.

@Ghost64847
Copy link
Copy Markdown
Author

Thank you for your answer. Let me share my thoughts: I will use MQuickJS for the JS interpreter, and for the Lua interpreter, I will use the official Lua version 5.5. What do you think?

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