Skip to content

Troubleshooting

Start with the earliest failing layer: component resolution, scene generation, generated API compilation, target linking, runtime startup, input, or visual output. A later symptom often cannot identify an earlier configuration error.

gsp_add_bundle: standalone GSPC is required

Section titled “gsp_add_bundle: standalone GSPC is required”

The preferred path is to install the toolchain manager and let CMake resolve the version in .gspc_version:

Terminal window
python -m pip install -U esp-gsp-tools
idf.py build

The .gspc_version in the IDF project root overrides the compiler-version marker shipped by the component. To select another compiler version, create the project marker manually. If no usable marker exists, set GSPC_EXECUTABLE explicitly. If esp-gsp-tools is unavailable, download supported host tools from the ESP-GSP Releases page, verify the published checksum, and set GSPC_EXECUTABLE to the executable.

GSPC_EXECUTABLE is independent of the ESP-GSP component version. CMake runs the GSPC compatibility command (gspc compatibility, equivalent to "$GSPC_EXECUTABLE" compatibility) and validates the generated formats, requirements, configuration contract and GMD runtime ABI before adding build rules. A compiler without that command, or with any incompatible value, is rejected during project configuration. The current generated values are listed in the Compatibility contract.

Run python -m gsp.execute --version '<ESP-GSP version>' sim --capabilities from a terminal so loader errors remain visible. Use the component version from idf_component.yml; for a manually installed binary, invoke its actual path. Use the archive for the exact host platform and install only the runtime dependencies listed for that release. If native dependencies are unavailable, use the browser/WASM simulator package. Simulator startup validates the host runtime, not display hardware.

Delete stale project dependency state only through the normal ESP-IDF dependency workflow, then reconfigure. Check build/project_description.json to see the component path selected by the current build. Do not assume that a nearby component directory overrides a managed component automatically.

gspc diagnose scene.json --format json reports the JSON path and source range for common field and widget-type errors. A unique nearby name from the widget registry may include a suggestions entry with a replacement. Apply that text only to the reported range, then diagnose again; do not add quotes or interpret the suggestion title as an edit. Ambiguous names or source locations have no automatic replacement. The compiler never changes the input file.

Unsupported bounded dynamic properties include explanatory suggestions listing applicable fields or their requirements. A replacement: null is guidance, not a text edit. Bounded expressions and runtime-style setters are different capabilities; use the generated API and the widget’s documented dynamic fields.

JSON reports an unknown field or component

Section titled “JSON reports an unknown field or component”

Use Scene authoring for the installed component version to select supported fields. Check the full JSON path in the compiler diagnostic; repeated templates and component configuration can make the leaf field appear far from the root cause.

The build passes selected target capacities to the compiler. Compare the reported required and configured values, then either reduce simultaneous scene demand or increase the matching Kconfig/runtime pool. Size recycled Lists and Grids from visible rows plus overscan, not total dataset size. For runtime-created templates, set max_instances to the maximum number of simultaneously live copies.

Asset paths are relative to the scene JSON, not the project root or build directory. Keep assets beside the scene or use a correct relative path. A scene or referenced asset change should trigger regeneration automatically.

  1. Give the element a stable name.
  2. Confirm that the requested property is dynamic for that element type.
  3. Add callback when an event predicate is required.
  4. Rebuild the bundle.
  5. Inspect <symbol>_gsp.h and the included per-scene header.

Use the declarations and editor completion from the generated header.

Confirm that the edited JSON is one of the SCENES passed to the active gsp_add_bundle() call and that the application includes the header for the same SYMBOL. Reconfigure when component or CMake ownership changed.

Check these values together:

  • scene w and h;
  • logical panel width and height after rotation;
  • gsp_add_bundle(PIXEL_FORMAT ...);
  • display target pixel width and byte order;
  • BSP physical rotation and framebuffer configuration.

For 90° or 270° physical rotation on a non-square display, the logical scene dimensions are transposed. Application buffers remain in the native ESP-GSP pixel layout; configure panel byte swapping in the BSP instead of pre-swapping pixels.

ESP-GSP uses little-endian RGB565 or packed B, G, R bytes for RGB888 runtime surfaces. Verify the panel’s wire byte order separately. A panel that needs big-endian RGB565 should set display-target byte swapping.

Updates tear or only part of the screen changes

Section titled “Updates tear or only part of the screen changes”

Verify that the BSP exposes the correct panel class, framebuffer count, TE signal, draw-buffer memory, and completion callbacks. Keep presentation mode on ESP_DISPLAY_PRESENT_MODE_AUTO unless a measured integration requires an explicit mode. See Display presentation.

A left-aligned bound Label with room for at least two font lines uses multiline layout from its first frame, including when the initial text is a single line. This keeps initial text and later set_text() updates aligned. For a single-line caption centered inside a larger control, give the Label one line of height and position that text box within the control.

A setter succeeds but the display has not changed yet

Section titled “A setter succeeds but the display has not changed yet”

Setters are asynchronous on ESP-IDF. Success means the update entered the framework command path. Use esp_gsp_flush() only when a test, screenshot, or other deterministic boundary must observe the committed result.

Getters observe committed state. A setter issued by another task may still be queued. Use one application state owner or an explicit low-frequency flush when a deterministic read-after-write boundary is required.

Shutdown times out or a buffer is still owned

Section titled “Shutdown times out or a buffer is still owned”

Call esp_gsp_stop() from an application task, never from a framework callback. Canvas stop is asynchronous; flush before freeing Canvas callback context or accepted borrowed frames. For runtime images, wait for the release callback defined by the selected COPY/BORROW/TAKE ownership model.

Confirm that:

  • lcd.touch is assigned before startup;
  • the touch controller reports coordinates in the configured logical orientation;
  • the target is visible and not covered by a later object;
  • a child control or viewport has not legitimately captured the gesture;
  • explicit interrupt mode has a usable, unclaimed interrupt source.

AUTO input mode falls back to polling when interrupt setup is unavailable. Explicit interrupt mode remains strict.

Pinch is application-owned. Register esp_gsp_on_pinch(), return true from ESP_GSP_PINCH_BEGIN to capture the gesture, and apply the reported relative scale through the generated image scale helper. ESP-GSP reports the gesture; it does not automatically zoom or pan a control. MAX_TOUCH_POINTS=1 disables the second contact and pinch handling.

A cyclic Wheel stops at an end or shows no rows

Section titled “A cyclic Wheel stops at an end or shows no rows”

Set cyclic: true on the Wheel; a finite items array is the logical data set, not a limit on how many cycles can be displayed. A cyclic Wheel has a generated bind helper; call it once with a null callback, for example gsp_widget_wheel_city_bind(ui, NULL, NULL). The runtime then supplies the compiled item text and reuses the row template without application slot IDs. Use a callback, set_total() and refresh() only when the application owns dynamic row data.

For item sets that will change at runtime, declare dynamic_items: true. The generated bind, set_total() and refresh() APIs then remain available whether the initial array is empty, short or longer than the viewport. Bind once and retain the handle. For compiled item text, pass NULL as the callback; provide a callback when the application owns the row data and declare its possible characters with font_charset.

When debugging a precompiled runtime, first reproduce with the matching component, GSPC and simulator versions. Include gspc compatibility and the firmware startup summary when reporting a problem. The simulator log panel exposes host and runtime diagnostics, and its API bridge supports the same generated Wheel helper. On device, esp_gsp_set_pointer_observer() confirms mapped press and release samples before gesture routing; esp_gsp_render_error_stats() and esp_gsp_service_stats() expose render failures and queued service work. These public diagnostics do not require runtime source code.

List binding eventually returns ESP_GSP_LIST_NONE

Section titled “List binding eventually returns ESP_GSP_LIST_NONE”

Bind each logical List or Wheel once and retain the handle for the UI lifetime. Do not bind again on every scene-ready event. Update item count with the generated set_total() helper and notify visible-data changes with the generated refresh() helper.

A recycled row shows stale asynchronous content

Section titled “A recycled row shows stale asynchronous content”

Do not retain a row handle after the binder returns. Associate asynchronous results with the row token and publish only if it still identifies the same list instance and item. Use stable image cache_key values only for immutable encoded content.

A runtime image stays blank under memory pressure

Section titled “A runtime image stays blank under memory pressure”

Check the decoded cache budget, largest allocatable block, active dynamic image slots, and whether image caching was disabled. A no-PSRAM build may select a bounded regional decode path that does not support runtime PNG/JPEG replacement. See Configuration and Media and application data.

When reporting a problem, include:

  • ESP-GSP component version and selected component path;
  • ESP-IDF version and IDF_TARGET;
  • scene file, logical resolution, and pixel format;
  • panel interface, rotation, framebuffer count, and TE configuration;
  • the first compiler/runtime error, not only later cascading messages;
  • whether the result was reproduced in host preview, target build, real board, or visual inspection.