LVGL Asset Paths: Resolve Drive Letters and Packaging Paths Deterministically
Make every asset path resolve through an explicit chain: logical identifier, LVGL driver selection, callback path, backend mapping and packaged file identity. When simulator and target disagree, compare those stages before changing the image decoder. A filename that opens successfully can still identify the wrong asset copy.
Applicability: this procedure pins LVGL v9.3.0 and illustrates the standard stdio driver’s prefix mapping. A target using another adapter needs its own recorded mapping. The paths and package trees below are hypothetical, and no simulator or target open test has been run. Package-building recipes, filesystem error translation and decoder format support are separate topics.

1. Separate the LVGL identifier from the native path
The filesystem guide explains that the leading identifier selects a registered driver. It is not a Windows volume letter or proof that a storage device has been mounted. Assign each registered driver a unique identifier and keep its registration storage alive for the required lifetime.
Adopt a project convention such as S:icons/power.bin for packaged UI assets. Here S selects the asset adapter, and icons/power.bin is the adapter-relative name. Keep that logical string unchanged between simulator and target; change the documented backend root instead.
For a hypothetical stdio simulator, use a prefix of /opt/ui-sim/assets/. For a hypothetical stdio target, use /usr/share/panel-ui/. The same callback path then selects /opt/ui-sim/assets/icons/power.bin or /usr/share/panel-ui/icons/power.bin. These are illustrative deployment choices, not LVGL defaults.
2. Pin the default-driver behavior
The configuration template exposes LV_FS_DEFAULT_DRIVER_LETTER. The v9.3.0 resolver behaves differently depending on whether that setting is zero. With a default configured, an explicit prefix requires an uppercase A–Z followed by a colon; otherwise the entire string goes to the default driver. Without a default, the legacy path takes the first character as the driver and consumes an optional colon.
| Input | Default unset | Default S |
|---|---|---|
| S:icons/power.bin | Driver S; icons/power.bin | Driver S; icons/power.bin |
| icons/power.bin | Driver i; cons/power.bin | Driver S; icons/power.bin |
| Sicons/power.bin | Driver S; icons/power.bin | Driver S; Sicons/power.bin |
| s:icons/power.bin | Driver s; icons/power.bin | Driver S; s:icons/power.bin |
The table describes resolution, not successful opens. An unregistered identifier fails lookup, and a literal colon in the callback path still faces the backend’s rules. Avoid relying on the permissive legacy form. A project linter can require the same explicit uppercase prefix and colon in both builds.
3. Inspect the exact backend join
The tagged stdio open callback formats LV_FS_STDIO_PATH followed directly by the callback path. It does not insert a missing separator or perform a general path join. Therefore, the proposed convention uses a trailing separator in the prefix and no leading separator in the callback-relative name.
For example, /usr/share/panel-ui/ plus icons/power.bin produces the intended pathname. Omitting the prefix’s trailing slash produces /usr/share/panel-uiicons/power.bin. Adding a leading slash to the callback name produces a doubled separator inside the constructed string; do not assume the second part replaces the prefix as it might in some path libraries.
The tagged callback uses a fixed buffer and does not inspect the formatter’s truncation result. Keep a checked project limit for prefix plus relative name plus terminator, using the effective adapter buffer capacity. A long path that is silently shortened must fail validation rather than accidentally open another file.
Prefix concatenation is also not a security confinement mechanism. Keep application asset names in a controlled manifest. If external input can select files, traversal, symbolic links and access control require a separately designed backend policy.
4. Define one manifest for both environments
Give each asset a stable logical ID, exact logical path, exact case-sensitive package-relative name, byte length and content hash. Record the backend mapping as environment configuration. Do not include a developer’s home directory or temporary build location in application strings.
Require identical spelling between the manifest and packaged directory entries. Whether a particular filesystem folds case is a platform property to test, not an LVGL guarantee. Two files whose names differ only by case should be rejected by the project’s portable asset policy even if one development environment tolerates them.
Include a deliberate duplicate-copy scenario: place different diagnostic content under the same relative name in an old and a new asset root. Confirm that the trace and hash identify the intended root. This catches successful opens from stale simulator data, which missing-file tests cannot detect.
5. Build a path-only acceptance fixture
- List effective registered identifiers, default-driver setting and backend root for each build. Assert that the chosen asset driver exists before any widget requests a file.
- Open every manifest entry through lv_fs_open in read mode. Record the logical path, resolved callback path and final backend pathname in a development trace.
- Read the complete file with checked status and byte counts, then compare length and hash with the manifest. Close only handles that opened successfully.
- Run from a working directory different from the simulator project directory. Any dependency on that directory should be explicit in the chosen mapping.
- Repeat against the actual deployed asset tree after its storage is ready. A host-directory pass does not establish target availability.
Keep this fixture below image rendering. A successful raw open and matching file hash establish path identity, while a later decoder failure is another diagnostic stage. Conversely, a blank widget alone cannot tell whether the driver was absent, the path was wrong or the file format was unsupported.
6. Include deliberate resolution failures
Test an unknown uppercase driver, omitted colon, lowercase prefix, wrong filename case, missing directory, overlength name and unavailable backend. Declare whether the project validator rejects each before LVGL or whether the adapter is expected to report the failure. Do not let a default driver silently turn malformed explicit paths into unexpected native filenames.
Retain the resolution table, manifest and environment mappings with the release. Acceptance means that each logical asset selects the intended packaged bytes in both environments, and malformed names fail predictably. That evidence is more durable than fixing one simulator path until the splash screen happens to appear.