Skip to content

Getting Started

This guide builds the default embedded-bundle path: an ESP-IDF application owns the display, ESP-GSP compiles one JSON scene during the build, and the application updates the UI through the generated C API.

If you want to evaluate ESP-GSP before integrating it, start with the maintained example:

Terminal window
idf.py create-project-from-example "espressif/esp-gsp=1.6.0:hello_world"
cd hello_world
python -m pip install -U esp-gsp-tools

Then follow the example’s README.md for a simulator preview or a matching board profile. The rest of this guide explains how to integrate ESP-GSP into an existing application.

  • ESP-IDF 6.0 or later is exported in the current shell.
  • Either esp-gsp-tools is installed for automatic resolution, or a compatible GSPC release is available for explicit GSPC_EXECUTABLE selection.
  • A BSP or application layer can initialize an esp_lcd panel and fill an esp_display_present_target_config_t.
  • The logical scene size and pixel format are known.

Touch is optional. When present, the BSP supplies an esp_lcd_touch_handle_t whose coordinates match the logical display orientation.

Before first startup: stack and integrity checks

Section titled “Before first startup: stack and integrity checks”

Bundle creation and resource checks run on the task that starts ESP-GSP. Begin with the hello_world setting CONFIG_ESP_MAIN_TASK_STACK_SIZE=20480, or give your custom startup task the equivalent headroom. This is a starting point, not a minimum: measure uxTaskGetStackHighWaterMark() with the fonts, decoders and logging used by the product before reducing it. Render-task stack settings do not increase the caller’s stack.

Keep Bundle CRC checks enabled. Do not disable the task watchdog to mask a startup stall. Use disable_bundle_crc only when another trusted mechanism has already verified the static assets.

From the ESP-IDF project root:

Terminal window
idf.py add-dependency "espressif/esp-gsp^1.6.0"

The equivalent component manifest entry is:

dependencies:
espressif/esp-gsp: "^1.6.0"

For a local ESP-GSP component directory, use Component Manager override_path or add it to EXTRA_COMPONENT_DIRS. Keep only one selected component copy in a build so the compiler, public headers, and runtime come from the same version; applications do not need their implementation sources.

2. Install GSPC; add the simulator when needed

Section titled “2. Install GSPC; add the simulator when needed”

Install the toolchain manager. CMake reads the component’s .gspc_version and invokes esp-gsp-tools; the first invocation downloads and verifies that GSPC release into the local cache automatically:

Terminal window
python -m pip install -U esp-gsp-tools

The default .gspc_version in the component package is the latest GSPC version compatible with the current GSP release at publish time. To override the GSPC version for an IDF project, create .gspc_version in the project root; the project marker takes precedence over the component marker:

Terminal window
echo '0.7.0' > .gspc_version
idf.py build

[!TIP]

If the manager cannot be installed or automatic downloads are unsuitable, see the Compatibility contract, unpack the archive into a stable tool directory, and override via an environment variable or CMake argument:

Terminal window
export GSPC_EXECUTABLE=/absolute/path/to/gspc
# You can also override via CMake:
idf.py -D GSPC_EXECUTABLE=/absolute/path/to/gspc build

gsp_add_bundle() validates the selected compiler during CMake configuration. Do not copy a compiler from an unrelated source checkout merely because gspc --version runs.

The simulator is optional; firmware builds do not require it. Select the sim version using the version field from idf_component.yml (the ESP-GSP component version), not the GSPC version in .gspc_version:

Terminal window
python -m gsp.execute --version '<ESP-GSP version>' sim --bundle product.gspb

Alternatively, download the matching simulator archive from the ESP-GSP Releases page, set GSP_SIM_EXECUTABLE, and invoke that executable directly.

See the Simulator reference for capabilities, CLI options, and evidence limits.

A small application can use this layout:

my_app/
├── CMakeLists.txt
├── sdkconfig.defaults
├── main/
│ ├── CMakeLists.txt
│ ├── app_main.c
│ └── idf_component.yml
└── scenes/
├── main.json
└── assets/

Create scenes/main.json:

{
"screen": "main",
"w": 320,
"h": 240,
"screen_bg": "#101820",
"objects": [
{
"type": "progress",
"parent": -1,
"name": "load",
"x": 40,
"y": 96,
"w": 240,
"h": 24,
"value": 30,
"fg_color": "#4CC9F0"
},
{
"type": "toggle",
"parent": -1,
"name": "power",
"x": 128,
"y": 164,
"w": 64,
"h": 32,
"fg_color": "#4361EE",
"callback": "power_changed"
}
]
}

Use name only for elements the application must address. Use callback for actions that must cross from the UI into product logic. Asset paths are relative to the scene file.

Read the Scene JSON guide for hierarchy, field selection, runtime naming, and reference-image translation. The exhaustive field and widget reference is available in Scene authoring.

In main/CMakeLists.txt:

idf_component_register(SRCS "app_main.c"
PRIV_REQUIRES esp-gsp)
gsp_add_bundle(${COMPONENT_LIB})

The default call discovers and sorts PROJECT_DIR/scenes/*.json, and uses RGB565. Keep scene JSON and scene-relative assets in the scenes directory.

gsp_add_bundle() performs four jobs during the ESP-IDF build:

  1. validates and compiles the JSON scene;
  2. tracks referenced fonts and images as build dependencies;
  3. packs the generated scene and resources into an embedded bundle;
  4. adds bundle_gsp.h and the per-scene generated headers to the component’s include path.

The default symbol is bundle. For multiple independent bundles, or for JSON that must remain elsewhere, list paths explicitly:

gsp_add_bundle(${COMPONENT_LIB}
SCENES "../product/home.json" "../shared/settings.json"
SYMBOL product
PIXEL_FORMAT rgb888)

With SYMBOL product, include product_gsp.h and initialize with gsp_product_config(). The startup example below uses the default bundle symbol. Set a different SYMBOL for each bundle. See Configuration for all build-time options.

The BSP remains responsible for the panel, framebuffer, byte order, physical rotation, and optional touch controller. Application startup then uses the generated bundle configuration:

#include "esp_gsp_esp_lcd.h"
#include "bundle_gsp.h"
static void on_ui_event(esp_gsp_handle_t ui,
const esp_gsp_event_t *event,
void *user_ctx)
{
(void)ui;
(void)user_ctx;
if (gsp_main_event_is_power_changed(event)) {
/* Notify an application task; do not block this callback. */
}
}
void app_main(void)
{
esp_display_present_target_config_t display;
ESP_ERROR_CHECK(board_display_init(&display));
esp_lcd_touch_handle_t touch = NULL;
(void)board_touch_init(&touch); /* Optional. */
esp_gsp_config_t app = gsp_bundle_config();
esp_gsp_esp_lcd_config_t lcd = ESP_GSP_ESP_LCD_CONFIG_INIT();
lcd.display = display;
lcd.touch = touch;
esp_gsp_handle_t ui;
ESP_ERROR_CHECK(esp_gsp_esp_lcd_start(&app, &lcd, &ui));
ESP_ERROR_CHECK(esp_gsp_on_event(ui, on_ui_event, NULL));
ESP_ERROR_CHECK(gsp_main_load_set_value(ui, 60));
}

board_display_init() and board_touch_init() are placeholders for the product BSP. The repository’s shared examples/common/hw_init component shows complete panel targets used by the examples.

Select the real target and build:

Terminal window
idf.py set-target <target>
idf.py build

If the scene, an asset, a selected Kconfig capacity, or a bundle option is invalid, generation fails as part of this build. After a successful build, use editor completion or inspect bundle_gsp.h. Generated names follow:

gsp_<scene>_<named-element>_<operation>()

The available operations depend on the element type and dynamic properties. Update JSON and rebuild to refresh the available helpers; use the declarations and editor completion from the generated header.

ESP-GSP works with balanced defaults. If the scene compiler reports a capacity shortage, or product measurements justify a feature, memory, task, or input change, open:

Terminal window
idf.py menuconfig

Then navigate to Component config -> ESP-GSP. Use menuconfig for the active build and keep intentional clean-build values in sdkconfig.defaults. The active sdkconfig is the value that actually controls an existing build. See the ESP-IDF Kconfig guide for target-specific defaults, runtime precedence, memory trade-offs, and a clean-profile validation command.

examples/usage/hello_world is the smallest complete repository integration. For the included ESP32-P4 profile:

Terminal window
cd examples/usage/hello_world
idf.py -D SDKCONFIG_DEFAULTS=sdkconfig.defaults \
set-target esp32p4 build
idf.py flash monitor

ESP-IDF automatically applies sdkconfig.defaults.esp32p4 after the base defaults. Other checked-in sdkconfig.defaults.<target> fragments exercise different display paths. Their pins and timings are examples; they must match the actual board before flashing.

edit JSON or assets
|
v
build and fix compiler diagnostics
|
v
inspect generated API
|
v
integrate application state and events
|
v
preview -> target build -> check display and interaction on the board

Continue with Recommended development workflow. For ownership, callback, and shutdown rules, read Application lifecycle before adding runtime media, lists, or background tasks.