ZiB cd1730f91c Dedupe generalized engine facts out of generated panel headers 2 هفته پیش
..
spy f502ba3b63 Add GDEY075T7 (7.5" 800x480 mono, UC8179); define PROGMEM in the spy shim 3 هفته پیش
README.md cd1730f91c Dedupe generalized engine facts out of generated panel headers 2 هفته پیش
emit_descriptor.h 187fa75490 Capture writeImageForFullRefresh's extra RAM-bank write for partial refresh 2 هفته پیش
emit_palette_4c.h 65918c7bdb emitter: auto-fill 4-colour fields, class-aware busy timeout, paste-ready PANELS line 3 هفته پیش
emit_palette_7c.h f6874087be emitter: auto-fill 7-colour fields via emit_palette_7c.h 3 هفته پیش
probe_plane_invert.h db1cd5b6ec harness: fold plane_invert + 1bpp/packed detection into the emitter 3 هفته پیش
run_154_z90c.cpp 42c87bb88d Add a dynamic-tracing harness for extracting GxEPD2 per-panel protocol data 1 ماه پیش
run_154_z90c_emit.cpp 5acc8432c4 Add GDEH0154Z90 3-color panel descriptor (emitter-derived) 3 هفته پیش
run_213_z98c.cpp 42c87bb88d Add a dynamic-tracing harness for extracting GxEPD2 per-panel protocol data 1 ماه پیش
run_213_z98c_diag.cpp d6e65922fb harness: diagnostic-fill Pattern A (row/byte ordering) 3 هفته پیش
run_213_z98c_probe.cpp 54022b2ea1 harness: derive plane_invert by capture (probe) 3 هفته پیش
run_213c_gdey0213f52_emit.cpp 65918c7bdb emitter: auto-fill 4-colour fields, class-aware busy timeout, paste-ready PANELS line 3 هفته پیش
run_290_t94.cpp 42c87bb88d Add a dynamic-tracing harness for extracting GxEPD2 per-panel protocol data 1 ماه پیش
run_290_t94_diag.cpp d6e65922fb harness: diagnostic-fill Pattern A (row/byte ordering) 3 هفته پیش
run_290_t94_partial.cpp 560f5e9fbe harness: add fast-partial capture program (GxEPD2_290_T94) 3 هفته پیش
run_290_t94_wrapper.cpp 42c87bb88d Add a dynamic-tracing harness for extracting GxEPD2 per-panel protocol data 1 ماه پیش
run_290c_gdey029f51.cpp a4ac4b79e4 Add harness runners for the four panels on the bench today 1 ماه پیش
run_290c_gdey029f51_diagB.cpp 15cc4d5a3d harness: diagnostic-fill Pattern B (pixel packing + codes) 3 هفته پیش
run_290c_gdey029f51_diagN.cpp ca79e6cea3 harness: native-path row ordering for packed panels 3 هفته پیش
run_290c_gdey029f51_emit.cpp 65918c7bdb emitter: auto-fill 4-colour fields, class-aware busy timeout, paste-ready PANELS line 3 هفته پیش
run_426_gdeq0426t82.cpp a4ac4b79e4 Add harness runners for the four panels on the bench today 1 ماه پیش
run_426_gdeq0426t82_diag.cpp d6e65922fb harness: diagnostic-fill Pattern A (row/byte ordering) 3 هفته پیش
run_565c_diagB.cpp 9491ec9d41 harness: extend Pattern B to 7-colour ACeP 3 هفته پیش
run_579c_gdey0579f51.cpp a4ac4b79e4 Add harness runners for the four panels on the bench today 1 ماه پیش
run_579c_gdey0579f51_diagN.cpp ca79e6cea3 harness: native-path row ordering for packed panels 3 هفته پیش
run_583_gdeq0583t31.cpp a4ac4b79e4 Add harness runners for the four panels on the bench today 1 ماه پیش
run_583_gdeq0583t31_diag.cpp d6e65922fb harness: diagnostic-fill Pattern A (row/byte ordering) 3 هفته پیش
run_730c_gdep073e01.cpp d647f286b6 Add GDEP073E01 (7.3in Spectra6 800x480) panel descriptor 3 هفته پیش
run_diagB_harness.h 9491ec9d41 harness: extend Pattern B to 7-colour ACeP 3 هفته پیش
run_diagN_harness.h ca79e6cea3 harness: native-path row ordering for packed panels 3 هفته پیش
run_diag_harness.h 00bd4e3be7 docs: refresh panel table for the 750/579 status; finish row_interleave->line_order 3 هفته پیش
run_harness.h c0c21c11cd harness: make the window-encoder sweep a standard capture step 3 هفته پیش
run_partial_harness.h c0c21c11cd harness: make the window-encoder sweep a standard capture step 3 هفته پیش
run_wrapper_harness.h 42c87bb88d Add a dynamic-tracing harness for extracting GxEPD2 per-panel protocol data 1 ماه پیش
trace.h b3d1b32e22 harness: prototype descriptor emitter (single-plane panels) 3 هفته پیش
window_sweep.h c0c21c11cd harness: make the window-encoder sweep a standard capture step 3 هفته پیش

README.md

gxepd2 capture harness

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.

Emit a descriptor

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. For a 4-colour (epd4c/) panel it goes further: emit_palette_4c.h drives the driver's own GxEPD2_4C wrapper, reads the wire code for each named colour, validates it against the engine's GXEPD2_PALETTE_4C, and fills bits_per_pixel / palette / blank_code automatically (an unexpected code map stays a TODO carrying the observed table). A 7-colour (epd7c/) panel works the same way via emit_palette_7c.h / the driver's GxEPD2_7C wrapper, validated against GXEPD2_PALETTE_7C (black/white/green/blue/red/yellow/orange, no code collisions); a panel with a non-standard 7-colour map (e.g. GDEP073E01's rotated codes) stays an explicit TODO with the observed table rather than being mislabelled. For a panel with no colour wrapper at all (still GxEPD2_BW/binary) that nonetheless packs >1 bit/pixel on the wire - e.g. GxEPD2_583/UC8159c's _send8pixel, which expands each 1bpp buffer byte into a wider wire format using only two of its codes - emit_descriptor.h's make_resolver_bw_packed is the fallback resolver (used whenever no colour-wrapper resolver applies): it drives the plain driver's own writeImage with a uniform all-white/all-black 1bpp buffer directly (no wrapper to instantiate), and if both wire bursts are internally uniform, the byte-count ratio against the pixel count gives bits_per_pixel and each fill's byte gives that colour's code - no source reading needed, and no "two 1bpp planes" primitive required from the engine. A driver whose packing depends on more than one bit at a time (dithering, error diffusion) fails the uniformity check and stays an honest TODO. A dual-region panel (per-region select + window, e.g. GDEY0579F51 / HX8717) is segmented too: its per-region init / window / refresh, plane_width (decoded from the region window-set) and a candidate line_order (PAIRED_EDGES when a region's burst is 2x its pixel bytes) all come out, with only the exact paired-edges row order left for the diag native-path pass to confirm. Review the result, add any packed-format fields still left as TODO, then save it as components/gxepd2/panel_<panel>.h. capture.py also prints the exact PANELS line to paste into display.py (key / descriptor macro / format filled from the capture; part / controller scraped from the driver header to sanity-check, status starting at structural).

EMIT_DEBUG=1 /tmp/emit_<panel> dumps the image-stage command/burst structure.

Regression check (CI)

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). Any panel the emitter can't yet segment is listed in its KNOWN_DIFF and reported as XFAIL (none at present — the dual-region GDEY0579F51 was promoted out once the emitter modelled the region-select layer; an XPASS means a KNOWN_DIFF entry now reproduces and should be removed). Panels with no GxEPD2 driver source of their own (a synthetic model) are skipped. Run it after any third_party/GxEPD2/ update.

Other programs

  • tools/capture.py MODEL --sweep / --diag — runs run_harness.h's window-encoder sweep, or run_diag_harness.h's Pattern-A row/byte-order fill, for any capturable model with no run_<panel>.cpp to hand-write (both headers are already templates over Driver; capture.py builds a one-off generic main() the same way it does for descriptor emission). Older panels still have a committed run_<panel>[_diag].cpp from before this existed - either form works, prefer --sweep/--diag for anything new. Output is the full byte-by-byte trace (large for a big/packed panel - redirect to a file and grep for >>> SWEEP / [row-tag] rather than reading it raw).
  • tools/capture.py MODEL --power — prints just power_off_sequence/ hibernate_sequence (array + EpdCommand table + the two descriptor-field lines to paste), for splicing into an EXISTING panel_*.h that shipped before these fields existed, without regenerating - and losing any hand-written commentary in - the rest of the file. Same noisy-trace caveat as --sweep/--diag: redirect to a file and take the tail (everything after the last digitalRead line is the actual result).
  • 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. Pattern A (run_diag_harness.h) only covers the 1bpp byte-preserving path (see CONTRIBUTING.md); Pattern B (run_diagB_harness.h) drives a driver's own GxEPD2_4C/GxEPD2_7C wrapper and its named colours, so it doesn't apply to a still-monochrome (GxEPD2_BW-wrapped) driver that packs internally (e.g. GxEPD2_583/UC8159c's _send8pixel - a 1bpp buffer expanded to a 2-code wire format). That case is covered by emit_descriptor.h's make_resolver_bw_packed instead - folded into the one-command tools/capture.py capture, same as 4-colour/7-colour - not a separate diag program at all.
  • spy/ — the host stand-ins for the Arduino/SPI APIs a GxEPD2 driver calls; trace.h records what the driver does.