Skip to content

esp32s3_8048s070c_bsp

Board Support Package (BSP) for the ESP32-S3 8048S070C — a 7-inch, 800 × 480 capacitive-touch display board driven over an RGB parallel bus. This module owns every hardware-specific detail: display initialisation, RGB VSYNC tear-prevention, GT911 touch input, backlight control, filesystem-access pixel-clock patching, and custom LVGL event registration. The rest of the firmware is entirely board-agnostic and communicates with this module through the board_init.h / control_event.h interfaces.


  1. Hardware Profile
  2. Module Structure
  3. Architecture Overview
  4. Component Reference
  5. Initialization Sequence
  6. Display Pipeline & VSYNC Synchronisation
  7. Touch Input Pipeline
  8. Filesystem Access Patching
  9. Snapshot Feature
  10. Activity Manager Integration
  11. Custom LVGL Events
  12. Build System
  13. Dependencies
  14. Related Board BSPs

AttributeValue
MCUESP32-S3
Display7 inch, 800 × 480 px
Display interfaceRGB parallel (16-bit)
Display driver ICEK9716
Touch controllerGT911 (I²C, capacitive)
BacklightPWM / GPIO (configurable)
FilesystemFlash + SD (SPI shared bus — requires pixel-clock throttling)
PSRAMRequired (LVGL draw buffers allocated via MALLOC_CAP_SPIRAM)

boards/esp32s3_8048s070c/
├── components/bsp/
│ ├── board_init.c ← hardware initialisation, LVGL integration
│ ├── board_init.h ← public API (board_init, get_lvgl_*, bsp_accessFs…)
│ ├── board_config.h ← pin map, timing constants, feature flags
│ ├── control_event.h ← control_event_t structure
│ ├── control_types.h ← extern custom LVGL event codes
│ └── control_types.c ← LVGL custom event registration
├── Factory/ ← standalone factory-test application
│ └── main/ → see esp32s3_8048s070c_factory_app.md
└── build_scripts/ ← Python variant build helpers
├── build_one.py
├── common.py
└── variants.py

Sync Primitives

LVGL Runtime

Hardware Drivers

BSP - esp32s3_8048s070c

Application Layer

board_init

get_lvgl_display / get_lvgl_lock

bsp_accessFs / bsp_releaseFs

VSYNC ISR

VSYNC ISR

lvgl_flush_cb

lvgl_flush_cb

draw_bitmap

touch_read_cb

touch_read_cb

ESP3DX / UIManager

ESP3DFlash / ESP3DSd

board_init

control_events_init

bsp_accessFs / bsp_releaseFs

disp_ek9716

RGB panel driver

disp_backlight

PWM/GPIO

touch_gt911

I2C capacitive

bus_i2c

activity_manager

LVGL core

lv_display_t

lv_indev_t

pointer

esp_timer

lvgl_tick

sem_vsync_end

binary semaphore

sem_gui_ready

binary semaphore


The single source file that implements the entire BSP public API. All symbols are either static (internal) or exported through board_init.h.

SymbolSignatureDescription
board_initesp_err_t board_init(void)Master initialisation entry point. Called once by the application.
board_get_nameconst char *board_get_name(void)Returns BOARD_NAME_STR.
board_get_versionconst char *board_get_version(void)Returns BOARD_VERSION_STR.
get_lvgl_displaylv_display_t *get_lvgl_display(void)Returns the LVGL display handle (NULL when display disabled).
get_lvgl_lock_lock_t *get_lvgl_lock(void)Returns the LVGL API mutex used by ESP3DXUi.
get_touch_indevlv_indev_t *get_touch_indev(void)Returns the LVGL pointer input device (guarded by ESP3D_TOUCH_FEATURE).
bsp_accessFsesp_err_t bsp_accessFs(void)Throttles the RGB pixel clock before SPI flash/SD access (guarded by ESP3D_PATCH_FS_ACCESS_RELEASE).
bsp_releaseFsesp_err_t bsp_releaseFs(void)Restores the RGB pixel clock after SPI flash/SD access.
FunctionDescription
init_lvglCreates LVGL display, allocates PSRAM buffers, registers flush callback, starts tick timer, creates touch indev.
init_touch_controllerInitialises the I²C bus and configures the GT911 driver.
lvgl_flush_cbLVGL flush callback: optionally writes a snapshot chunk, colour-swaps if needed, synchronises with VSYNC, then calls esp_lcd_panel_draw_bitmap.
disp_on_vsync_eventRGB panel ISR: handshakes with lvgl_flush_cb via two binary semaphores to prevent display tearing.
touch_read_cbLVGL indev read callback: maps GT911 native coordinates to display pixels, integrates with activity_manager for wakeup-touch suppression.
increase_lvgl_tickesp_timer periodic callback that calls lv_tick_inc(LVGL_TICK_PERIOD_MS).
static _lock_t lvgl_api_lock; // LVGL thread-safety mutex
static lv_display_t *lvgl_display; // LVGL display handle
static lv_color16_t *lvgl_buf1; // Primary draw buffer (PSRAM)
static lv_color16_t *lvgl_buf2; // Secondary draw buffer (PSRAM, optional)
static esp_timer_handle_t lvgl_tick_timer; // Periodic LVGL tick
static lv_indev_t *touch_indev; // LVGL pointer input device
static esp_lcd_panel_handle_t disp_panel; // EK9716 RGB panel handle
static SemaphoreHandle_t sem_vsync_end; // ISR → flush synchronisation
static SemaphoreHandle_t sem_gui_ready; // flush → ISR synchronisation

Defines the unified event payload passed between BSP input callbacks and LVGL widget event handlers.

typedef struct {
lv_indev_t *indev; // Originating LVGL input device
uint32_t btn_id; // Button/key identifier
lv_indev_type_t type; // POINTER / KEYPAD / ENCODER / BUTTON
control_family_t family_id; // TOUCH / ENCODER / BUTTON / SWITCH / POT
int32_t steps; // Encoder step count (signed)
uint32_t press_duration; // Hold time in milliseconds
} control_event_t;

This board’s BSP only populates the TOUCH family — steps and press_duration are zero for pointer events. Other boards (e.g. pibot_pendant_v1_0) extend the same struct for encoders, buttons, and potentiometers.


Registers board-specific custom LVGL event codes after lv_init() has been called. On this board three codes are registered:

Extern symbolRegistered viaIntended use
LV_EVENT_SWITCH_PRESSEDlv_event_register_id()Physical switch pressed
LV_EVENT_SWITCH_RELEASEDlv_event_register_id()Physical switch released
LV_EVENT_POTENTIOMETER_CHANGEDlv_event_register_id()Analog potentiometer value changed

control_events_init() must be called from board_init() strictly after lv_init() returns. The extern declarations in control_types.h are initialised to LV_EVENT_ALL until that point — using them before registration silently matches every LVGL event.


no

yes

yes

no

yes

no

board_init

activity_manager_init

ESP3D_DISPLAY_FEATURE?

return ESP_OK

backlight_configure

backlight_set 0%

disp_ek9716_configure

obtain disp_panel handle

ESP3D_TOUCH_FEATURE?

bus_i2c_init

I2C_PORT / SDA / SCL / freq

touch_gt911_configure

init_lvgl

lv_init

Create sem_vsync_end

Create sem_gui_ready

Register VSYNC callback

disp_on_vsync_event

lv_display_create

800 x 480

heap_caps_malloc PSRAM

alloc lvgl_buf1

optional lvgl_buf2

lv_display_set_buffers

PARTIAL render mode

RGB565 colour format

esp_timer_create + start

period = LVGL_TICK_PERIOD_MS

ESP3D_TOUCH_FEATURE?

lv_indev_create POINTER

read_cb = touch_read_cb

period = 10 ms

backlight_set

DEFAULT_LEVEL_PCT

control_events_init

Anti-flash pattern: Backlight is explicitly set to 0 % before display initialisation and raised to the configured default only after LVGL is fully ready. This prevents a white-flash artifact on cold boot.


6. Display Pipeline & VSYNC Synchronisation

Section titled “6. Display Pipeline & VSYNC Synchronisation”

The EK9716 is driven over a 16-bit RGB parallel bus. Unlike SPI or I80 panels, RGB panels stream pixel data continuously from a shared frame buffer. Without coordination, an LVGL partial-buffer copy landing mid-scan produces visible horizontal tearing. Two binary semaphores implement a producer–consumer handshake between lvgl_flush_cb and the VSYNC ISR.

esp_lcd_panel_draw_bitmapdisp_on_vsync_event (ISR)lvgl_flush_cbLVGL core (Core 1)esp_lcd_panel_draw_bitmapdisp_on_vsync_event (ISR)lvgl_flush_cbLVGL core (Core 1)optional snapshot writeoptional RGB565 byte-swapblocks on sem_vsync_end...flush area ready (px_map)xSemaphoreGive(sem_gui_ready)VSYNC rising edge firesTakeFromISR(sem_gui_ready) == pdTRUEGiveFromISR(sem_vsync_end)draw_bitmap(area, px_map)lv_display_flush_ready(disp)
  • sem_gui_ready — given by lvgl_flush_cb (“pixel data is ready”); taken by the ISR.
  • sem_vsync_end — given by the ISR on the next VSYNC edge (“safe to write”); taken by lvgl_flush_cb.
  • The bitmap is pushed to hardware only after VSYNC, ensuring the scan begins on a clean frame boundary.
Build flagBehaviour
DISPLAY_USE_DOUBLE_BUFFER_FLAG = 1Two PSRAM buffers; LVGL prepares the next area while the previous one transfers.
DISPLAY_USE_DOUBLE_BUFFER_FLAG = 0Single PSRAM buffer; simpler, slightly lower throughput.
DISPLAY_SWAP_COLOR_FLAG = 1lv_draw_sw_rgb565_swap() is applied before bitmap push (byte-order correction for some panel wiring).

All buffers are allocated with MALLOC_CAP_SPIRAM. The board requires PSRAM.


yes

first press

yes

no / wakeup

held

no

yes

no

yes

yes

no

no

LVGL indev timer

every 10 ms

touch_read_cb

touch_gt911_read

is_pressed?

last_pressed_state

== false?

Map coords to display px

x = raw_x x 800 / x_max

y = raw_y x 480 / y_max

activity_process_event

activity allowed

touch through?

data state = PRESSED

report mapped xy

touch_consumed_for_wakeup = true

data state = RELEASED

touch_consumed

_for_wakeup?

data state = PRESSED

update last xy

data state = RELEASED

last_pressed_state

== true?

was wakeup cycle?

Reset touch_consumed flag

activity_process_event

log release

return

data state = RELEASED

GT911 coordinate mapping: The GT911’s native resolution may differ from the panel pixel dimensions (e.g., 1024 × 600 vs 800 × 480). touch_read_cb performs a proportional remap:

display_x = touch_raw_x * DISPLAY_WIDTH_PX / touch_gt911_get_x_max();
display_y = touch_raw_y * DISPLAY_HEIGHT_PX / touch_gt911_get_y_max();

This guarantees correct LVGL hit-testing regardless of the hardware difference between the GT911 internal grid and the pixel grid.

Wakeup-touch suppression: When the display is in the activity manager’s idle/sleep state, the first touch fires activity_process_event() which returns false. The BSP suppresses that touch (reports LV_INDEV_STATE_RELEASED to LVGL) and sets touch_consumed_for_wakeup = true. The flag is cleared on the matching release event. The next distinct touch cycle is delivered normally to the UI.


On ESP32-S3 boards with an RGB parallel display and a shared SPI bus (flash and/or SD card), simultaneous high-frequency SPI transactions and a fast pixel clock can cause data corruption. The BSP exposes two hooks to throttle the pixel clock around filesystem operations.

esp_lcd_rgb_panel_set_pclkBSP hooksESP3DFlash / ESP3DSdesp_lcd_rgb_panel_set_pclkBSP hooksESP3DFlash / ESP3DSdvTaskDelay(DISPLAY_PATCH_FS_DELAY_MS)vTaskDelay(DISPLAY_PATCH_FS_DELAY_MS)bsp_accessFs()set_pclk(DISPLAY_PATCH_FS_FREQ_HZ)ESP_OKSPI flash / SD read-writebsp_releaseFs()set_pclk(DISPLAY_PCLK_FREQ_HZ)ESP_OK

These hooks are compiled only when ESP3D_PATCH_FS_ACCESS_RELEASE is defined. disp_panel is checked for NULL so calls before display initialisation are silently ignored.

For the full fragmentation and bus-contention analysis, see esp32_memory_constraints.md (section Serial CNC + WiFi remote — fragmentation playbook) and feature_resource_matrix.md §2–§4.


When ESP3D_SNAPSHOT_FEATURE is enabled, lvgl_flush_cb writes each flushed pixel block to an open file before sending it to the display. This enables screen-capture without halting the UI.

no

yes

no

yes

yes

no

yes

no

lvgl_flush_cb

g_snapshot.ongoing?

VSYNC sync + draw_bitmap

xSemaphoreTake

g_snapshot.mutex

file open

and no error?

xSemaphoreGive mutex

fwrite in 120-byte chunks

loop until area exhausted

write error?

g_snapshot.error = true

g_snapshot.ongoing = false

captured_pixels

+= pixel_count

captured >= expected?

g_snapshot.ongoing = false

lv_display_flush_ready

The 120-byte chunk limit avoids large stack or heap allocations in the flush path, complying with the project’s strict memory constraints (see esp32_memory_constraints.md).

The snapshot_state_t type and g_snapshot global are declared in esp3d_snapshot.h (UI layer); the BSP references this header conditionally under the ESP3D_SNAPSHOT_FEATURE guard.


board_init() calls activity_manager_init() as its first action, before any display work. The activity manager tracks user-interaction events and drives screen-timeout / sleep policies.

Two integration points exist in this BSP:

LocationCallPurpose
touch_read_cb — first press edgeactivity_process_event()Signal new user activity; the return value determines whether the touch is delivered to LVGL or consumed as a wakeup.
touch_read_cb — release edge (non-wakeup)activity_process_event()Signal end of interaction so the timeout timer is refreshed correctly.

For the activity manager API, see esp3d_activity_manager.md.


control_types.c registers three runtime event codes with LVGL after lv_init(). They are declared extern in control_types.h and consumed by UI screens and input-handling code in the application layer.

control_types.c

lv_event_register_id

lv_event_register_id

lv_event_register_id

control_events_init

called from board_init

LV_EVENT_SWITCH_PRESSED

LV_EVENT_SWITCH_RELEASED

LV_EVENT_POTENTIOMETER_CHANGED

UI Screens

and Widgets

Timing constraint: control_events_init() is invoked by board_init() strictly after init_lvgl() returns (which itself calls lv_init()). Calling it before lv_init() leaves all codes as LV_EVENT_ALL, causing undefined widget behaviour.


The build system follows the same Python-driven pattern used across all BSPs in the project.

FileEntry pointPurpose
build_scripts/build_one.pymain()CLI for building a single named variant; delegates to common.py.
build_scripts/common.pybuild_variant(), check_variant()Runs idf.py CMake, triggers resource generation, copies factory artifacts, packages the ui_resources kit, prints size report.
build_scripts/variants.pymake_variant_args()Produces the -D FLAG=ON/OFF CMake argument list for each named build variant from a shared DEFAULT_OFF_CMAKE_ARGS base.
Terminal window
# List all available variants
python boards/esp32s3_8048s070c/build_scripts/build_one.py
# Build a specific variant
python boards/esp32s3_8048s070c/build_scripts/build_one.py <variant_name>
# Build with a clean first
python boards/esp32s3_8048s070c/build_scripts/build_one.py <variant_name> --clean
# CMake configuration check only (no compile)
python boards/esp32s3_8048s070c/build_scripts/build_one.py <variant_name> --check

Or use the standard ESP-IDF CLI directly:

Terminal window
idf.py -B build build
idf.py -B build flash monitor

Feature flags (socket client, BT, WebUI, etc.) are selected via -D CMake flags assembled by make_variant_args(). See board_build_guidelines.md for the full flag reference and cmake/sanity_check.cmake for mutual-exclusion rules (e.g., SOCKET_CLIENT_SERVICE vs. WEBUI_SERVER).


Optional

ESP-IDF and LVGL

Hardware Driver Layer

BSP

ESP3D_SNAPSHOT_FEATURE

board_init.c

control_event.h

control_types.c

disp_ek9716

RGB panel

disp_backlight

PWM/GPIO

touch_gt911

I2C capacitive

bus_i2c

shared I2C bus

activity_manager

LVGL core

esp_lcd_rgb_panel

esp_timer

esp_heap_caps

SPIRAM

driver/gpio

FreeRTOS

semaphores

esp3d_snapshot.h

esp3d_log

DriverConfig structNotable fields for this board
disp_ek9716disp_ek9716_config_tpanel_config (RGB timing/pins), orientation, hor_res=800, ver_res=480
touch_gt911touch_gt911_config_ti2c_addr[], i2c_port, rst_pin, int_pin, swap_xy, invert_x/y, x_max/y_max
disp_backlightbacklight_config_tpwm_control, gpio_num, duty, freq_hz, resolution_bits
bus_i2cinline paramsI2C_PORT_IDX, I2C_SDA_PIN, I2C_SCL_PIN, I2C_FREQ_HZ (from board_config.h)

All board-specific pin numbers, clock frequencies, buffer sizes, and feature-enable flags live in board_config.h.


The esp32s3_8048s070c_bsp belongs to the RGB parallel display BSP family. All boards in this family share the same bsp_accessFs / bsp_releaseFs pixel-clock throttle pattern and VSYNC-semaphore tear-prevention design. Primary differences are panel size, display driver IC, and touch controller.

BSP modulePanelDriver ICTouch ICKey difference
esp32s3_8048s070c_bsp ← this module7” 800×480EK9716 RGBGT911 I²C—
esp32s3_8048s050c_bsp5” 800×480ST7262 RGBGT911 I²CDifferent panel size / driver IC
esp32s3_8048s043c_bsp4.3” 800×480ST7262 RGBGT911 I²CSmaller panel
esp32s3_4827s043c_bsp4.3” 480×272RGBGT911 I²CLower resolution
esp32s3_8048_touch_lcd_7_bsp7” 800×480RGBGT911 I²CAlternative 7” variant
pibot_pendant_v1_0_bsp2.4” 320×240ILI9341 SPIXPT2046 SPIPendant form factor; adds encoder, buttons, potentiometer, switches
esp32_3248s035c_bsp3.5” 320×480ST7796 SPIGT911 I²CSPI display — no RGB/VSYNC synchronisation needed
esp32s3_hmi43v3_bsp4.3” 480×272RM68120 I80GT911 I²CIntel 8080 parallel bus with i80_flush_ready_cb

For the full hardware and feature compatibility matrix (WiFi / BT / transport exclusions) see feature_resource_matrix.md.

For display driver architecture, RGB vs SPI vs I80 trade-offs, orientation/rotation math, and swap_xy + mirror semantics see display_drivers.md.


components/bsp/lv_conf.h was regenerated from the LVGL 9.6.0 template (Configuration file for v9.6.0), and the sdkconfig files set CONFIG_LV_CONF_SKIP=n so this file is actually used (see Board_Support_Packages.md). The project values are kept (LV_MEM_SIZE 48 KB, monitors positions), LV_USE_COLOR_FILTER is 0 (color filters are not used: pressed feedback is a darker local style, see ui_components.md). LV_COLOR_DEPTH is no longer set in the file: LVGL takes CONFIG_LV_COLOR_DEPTH, or 16 by default.