Current firmware: LVGL, Python and the Rust camera
The current bench image is lvgl-dev.cb6170a37a5a for Luckfox Pico Mini
microSD boot. Bench testing confirmed boot, usable LVGL screens and camera QR
scanning through direct user observation. Automated public-fixture tests cover transaction review, change verification,
signing and QR export. End-to-end acceptance with a real wallet remains pending.
This is a development checkpoint, not a production release.
The tested setup uses the standard 240×240 display/button board and the
base-fork pin profile. The website's eight-pin 2.8-inch pill carrier is a
separate hardware build. Its LCD wiring, buttons and UR2 reader require matching
configuration and physical bring-up; the bench result does not qualify that PCB.
Smartcards and additional languages were outside this LVGL test scope.
Do not use this bench image unchanged on the carrier. Its LCD DC/reset outputs overlap carrier button nets. The PCB/software compatibility audit gives the exact carrier GPIO, 320×240/10 MHz LCD and UART/kernel requirements.
What runs where
| Layer | Responsibility |
|---|---|
| Linux / Buildroot | Boot, memory management, sensor/ISP drivers, SPI, GPIO and process isolation |
| Python application | Original SeedSigner/ShieldSigner navigation, wallet state, transaction review/signing logic and QR-format assembly |
| Native LVGL renderer | C/C++ screen layout, fonts, display rendering and live preview, driven by the Python application |
| Rust camera process | Controls the SC3336 sensor and ISP through existing Linux interfaces, including exposure and white balance |
| Native ZXing-C++ worker | Decodes grayscale camera pixels and returns original QR bytes, including binary payloads |
LVGL replaces the presentation path; it does not replace the Python wallet application. The camera and decoder are separate processes. The normal direct QR path avoids JPEG encoding/decoding, keeps binary payloads intact, and leaves animated QR assembly to the application. This is ZXing-C++, not a Rust QR decoder.
The Rust camera path does not link or call the vendor RKAIQ userspace library. It still uses Linux drivers and the Luckfox/Rockchip SDK build. This is not a Linux-free image or a claim to reproduce every RKAIQ feature, sensor or ISP mode. Pillow remains for still-image/entropy work and other retained consumers; moving screen drawing to LVGL does not remove all Python image dependencies.
Evidence and its limits
| Check | Result and scope |
|---|---|
| Packed image | Hashes, readback and ARM userspace smoke checks passed; emulation alone does not test a display or camera |
| Real device | User reported boot, LVGL usability and camera scanning on the bench setup |
| Signing logic | Public-fixture transaction review, change verification, signature and export checks passed |
| Settings and entropy | Packaged-runtime hardware checks passed, including settings save/reload and camera/RNG seed creation/wiping |
| Host renderer after repository cleanup | Nine native protocol groups passed using canonical source/work paths |
| Personal wallet acceptance | Pending; public fixtures do not replace this end-to-end check |
| Pill carrier, 2.8-inch LCD and smartcards | Not qualified by the bench firmware results above |
This separates a rendered screen, an automated fixture and a user-observed hardware result. The remaining acceptance work includes a complete wallet handoff and physical testing of each intended hardware profile.
Memory: what was actually measured
The Mini has 64 MiB physical RAM. In the measured Linux configuration, about
45 MiB is reported as MemTotal, with 12 MiB reserved for camera DMA and roughly
7 MiB in other platform/kernel reservations that remain incompletely attributed.
Compressed RAM swap uses RAM too;
its logical swapped-byte count is not additional physical memory.
Recent isolated hardware observations used the packaged LVGL runtime on the 240×240 bench setup. The cache comparison simulated first-boot source compilation with isolated cache prefixes; it was not a measured physical-reboot comparison. These are workload measurements, not maximum allocations or an all-process total:
| Measurement | Observed value | Meaning |
|---|---|---|
| Python at Home, shipped bytecode cache | 11,012 KiB | Private anonymous RAM plus logical swap |
| Python during camera preview, shipped cache | 13,576 KiB | Same metric; 57 preview frames processed and clean stop |
| Source-compilation control, Home / preview | 12,308 / 14,084 KiB | Same workload without the ordinary shipped cache: 1,296 / 508 KiB higher |
| Native LVGL private RAM, Home | 92 KiB | Real-camera comparison after preview-buffer lifecycle cleanup |
| Native LVGL private RAM, active preview | 356 KiB | Unchanged in that comparison; active preview still needs its buffer |
These rows come from separate scoped comparisons. Do not add them together or call them total device RAM. RSS includes shared pages; PSS apportions shared pages; private anonymous RAM plus swap also includes logically swapped data. The memory and threads guide explains those distinctions and labels older Python-renderer snapshots separately.
The wins came from delaying optional imports, releasing disposable source/input buffers, avoiding needless preview-buffer writes and shipping checked-hash bytecode for unchanged sources. One earlier QR executable-hash fix lowered an isolated startup peak by 7.82 MiB through chunked reads; that is a transient peak saving, not a permanent reduction during scanning. Import-chain reductions likewise are not interchangeable with running-app memory savings.
Find and verify the local image
From the repository root:
mise run firmware:where
mise run firmware:verify
where reports the selected image paths; verify hashes its files and checks the
recorded packed evidence. The canonical home is dist/firmware/images/, selected
through images/current. The image is a local build artifact; this website
does not publish a firmware binary download.
| Artifact | SHA-256 |
|---|---|
shieldsigner-lvgl-dev-cb6170a37a5a.img.xz |
834b2ca4d24055dbc533997e43e92bb54932e75c49dff27698316a51a6859853 |
Decompressed .img |
e605e47ab21dc4785a24ee356966b8cf649fe1716248de126cfce9605601e862 |
Hashes identify these exact files, not another build. An image-aware flasher can
accept .img.xz; a raw block-device writer requires the decompressed .img.
Use the flash checklist to verify the profile before writing.
Iterate without another full OS build
The desktop renderer can be built and tested without touching a device:
mise run lvgl:app:build:host
mise run lvgl:app:test:host
mise run firmware:test
For a named ARM runtime/overlay staging area, use the current camera base or pass an explicitly reviewed camera bundle. This step builds application inputs; it does not build or flash an OS image:
mise run firmware:stage -- --name my-lvgl-iteration
Outputs go to dist/firmware/staging/<name>/; compiler work stays in
dist/firmware/work/. Frozen baseline inputs and images are preserved. Use a new
name for each attempt. Keep SDK/download/compiler caches and inspect actual
launcher/container state plus terminal exit status for remote image jobs.
See compile for the next boundary, and the
artifact lifecycle
for paths and preservation rules.
Source and evidence
Implementation and detailed evidence live in the public repository:
- LVGL architecture and validation record
- Rust camera source and supported scope
- Packaged-cache memory comparison
- Native preview memory comparison
- Memory-cleanup acceptance and limitations
Historical receipts retain their original dates, paths and claims. They should not be read as a new release qualification merely because documentation moved.
Source: docs/FIRMWARE-STATUS.md ↗. Documentation describes reviewed evidence; it does not replace physical testing.