Host-side tooling that generates a panel descriptor by observing a real GxEPD2 driver run, instead of hand-writing one. See ../../CONTRIBUTING.md for the full model; this is the how-to.
The vendored GxEPD2 source it compiles against lives in third_party/GxEPD2/.
Everything here runs on a dev machine with plain g++ — no ESP toolchain.
One command (from the code/ root) resolves the driver, builds+runs the
harness, and prints a candidate descriptor:
tools/capture.py --list # capturable driver classes
tools/capture.py GxEPD2_154_Z90c # candidate -> stdout
tools/capture.py GxEPD2_154_Z90c -o /tmp/panel_154_z90c.h
tools/capture.py GxEPD2_154_Z90c --debug # also dump the capture structure
It finds the driver in catalog.json (or by globbing third_party/GxEPD2/ for
one the catalog doesn't list), so there's no build line to hand-write. Under
the hood it does exactly:
VSRC=third_party/GxEPD2/src
g++ -std=c++17 -I tools/gxepd2_harness/spy -I $VSRC -I tools/gxepd2_harness \
<generated emit main>.cpp \
$VSRC/GxEPD2_EPD.cpp $VSRC/<subdir>/GxEPD2_<Class>.cpp \
-o /tmp/emit_<panel>
emit_descriptor.h phase-segments the captured command stream into the
init / window / refresh sequences + plane-write command(s), reads
resolution / busy-polarity / timeout from the driver, and derives
plane_invert and 1bpp-vs-packed from a fill probe. Review the result, add any
packed-format fields the diag passes own (bits_per_pixel / palette for
packed panels; plane_width / row_interleave for dual-region), then save it
as components/gxepd2/panel_<panel>.h and wire it into display.py's MODELS.
EMIT_DEBUG=1 /tmp/emit_<panel> dumps the image-stage command/burst structure.
tools/check_descriptors.py re-emits every shipped panel from its GxEPD2
driver and structurally diffs the result against the committed panel_*.h
(command sequences + plane layout only, not the hand/diag-filled fields). It
exits non-zero on any unexpected mismatch, so wire it into CI as a plain step:
tools/check_descriptors.py # all panels; -v shows the diff on a mismatch
A DIFF means either an emitter regression or GxEPD2 upstream drift (a
vendor bump changed that driver — review the panel). Dual-region panels the
emitter doesn't fully model yet are listed in its KNOWN_DIFF and reported as
XFAIL; panels with no GxEPD2 driver source (the synthetic MASTER_ONLY
variant) are skipped. Run it after any third_party/GxEPD2/ update.
run_<panel>.cpp + run_harness.h — bare-driver capture plus the
window-encoder sweep (window_sweep.h), for deriving the coordinate →
window-byte mapping.run_<panel>_probe.cpp + probe_plane_invert.h — print the plane_invert
probe result on its own.run_*diag*.cpp — the diagnostic-fill passes that derive pixel packing,
palette codes, and row order for packed / multi-region panels.spy/ — the host stand-ins for the Arduino/SPI APIs a GxEPD2 driver calls;
trace.h records what the driver does.