lvgl-circuitpython integrates PyDevices' generated LVGL bindings into a
CircuitPython firmware build. It supplies the out-of-tree patch and build glue,
a GC-aware LVGL allocator, synced Python helpers, and a CircuitPython-specific
JPEG decoder.
It is not a pip package and does not generate the bindings. Work with sibling
clones of lvgl-bindings and
CircuitPython; this repository
consumes the exact bindings revision recorded in LVGL_BINDINGS_COMMIT.
After building firmware that includes this integration, you import
display_driver before lvgl and then run app.run() (or your own asyncio
loop) to pump LVGL, because CircuitPython has no machine.Timer for this role.
The README's App Usage & Timer Model has
the example.
display_driver imports pydevices' appdev, events, and keys (and
multimer when it is present), plus a board_config unless your code has
already created an appdev.App. Those are not part of this firmware, so they
must be on the device too.
lvgl-bindings at the pinned generated revision
|
v
lvgl-circuitpython patch + build glue
|
+--> CircuitPython shared-bindings/shared-module/lvgl
+--> generated C source/header + allocator
+--> manifest.py (freezes display_driver, fs_driver when
| the build passes it via FROZEN_MANIFEST)
|
v
CircuitPython firmware: import display_driver, then import lvgl
The generated source, header, LVGL pin, and configuration must all match the
recorded bindings commit. Change generator-owned code and the synced Python
helpers in lvgl-bindings, then regenerate and synchronize; do not edit their
copied forms here.
The README's Files table says what each path is for. The
two you meet first are apply_cp_patches.sh, which patches a CircuitPython
clone, and circuitpython.mk, the port Makefile fragment. The synced helpers
are lib/display_driver.py and lib/fs_driver.py; manifest.py freezes them
when the build names it in FROZEN_MANIFEST.
This repository patches a local, uncommitted CircuitPython tree because CircuitPython does not provide a separate out-of-tree C-module mechanism. The usual loop is:
- Regenerate the CircuitPython target in the pinned
lvgl-bindingscheckout when the binding shape changes. - Preview or apply the patch, for example
./apply_cp_patches.sh --dry-run --port unix --variant coverage. - Build with CircuitPython's own
make, passingFROZEN_MANIFESTif you want the helpers frozen. - Run the pinned binding smoke script against the resulting interpreter.
The root README has the exact setup, toolchain, Unix and
Espressif commands. Build and flash notes cover the Qualia
S3 workflow. Keep the generated-binding smoke coverage in lvgl-bindings; this
repository checks that its consumer integration is wired correctly.
LVGL's JPEG support here uses CircuitPython's jpegio implementation, not
LVGL's bundled TJpgDec. When CIRCUITPY_JPEGIO is absent, the firmware still
builds but does not register an LVGL JPEG decoder. PNG and LVGL BIN images do
not need that optional piece.
To build this together with other PyDevices extensions, see the README's Build with other extensions.
Start with a focused change to the patch templates, allocator, or integration
documentation. Run the patch script in --dry-run mode before applying it, keep
the bindings pin aligned, and use the repository's source assertions plus the
matching lvgl-bindings smoke suite for the affected target.