| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- # Canonical bench visual-check pattern, not a minimal smoke test like the
- # other fixtures in this directory. Scales to any panel via
- # it.get_width()/it.get_height(); each zone targets a specific bug class -
- # see claude/DESIGN.md's "Bench test pattern" section for the full
- # rationale. This is the actual pattern (previously only ever staged
- # on-device, never committed) whose row ladder + diagonal caught the
- # GDEY0579F51 row-interleave bug and confirmed the fix - see the
- # gxepd2-bench-validation-status memory.
- #
- # Wired here against GDEY0579F51 (4-color, dual-CS master+slave,
- # row_interleave: true) since that's the panel this pattern was built to
- # debug, but the lambda itself is panel-agnostic - copy it into any other
- # color-capable fixture's `lambda:` unchanged.
- esphome:
- name: gxepd2-test-pattern
- esp32:
- board: esp32dev
- framework:
- type: esp-idf
- external_components:
- - source:
- type: local
- path: ../components
- components: [gxepd2]
- spi:
- clk_pin: 18
- mosi_pin: 23
- logger:
- display:
- - platform: gxepd2
- model: GxEPD2_579c_GDEY0579F51
- cs_pin: 5
- dc_pin: 17
- reset_pin: 16
- busy_pin: 4
- update_interval: 60s
- lambda: |-
- const int w = it.get_width();
- const int h = it.get_height();
- const int n = 4;
- // Wire-code order (see gxepd2_display.h's packed-2bpp classification):
- // black=0x00, white=0x01, yellow=0x02, red=0x03 - matched here so a
- // wrong bar points straight at the wire code it corresponds to.
- Color colors[n] = {Color::BLACK, Color::WHITE, Color(0xFF, 0xFF, 0x00), Color(0xFF, 0x00, 0x00)};
- it.fill(Color::WHITE);
- // Row ladder: 1px-tall rows cycling the full palette, full width.
- // The row-interleave detector - a dropped/duplicated/reordered row
- // breaks the regular N-row cadence.
- const int ladder_h = h / 3;
- for (int y = 0; y < ladder_h; y++)
- it.horizontal_line(0, y, w, colors[y % n]);
- // Column ladder: same idea rotated 90 degrees, directly below.
- // Catches nibble-order/byte-stride bugs the row ladder can't (packed
- // 2bpp stores 4 pixels/byte, so a column-order bug can leave every
- // row looking individually correct).
- for (int x = 0; x < w; x++)
- it.vertical_line(x, ladder_h, ladder_h, colors[x % n]);
- // Color bars: one solid block per available color, in wire-code
- // order (not perceptual brightness order).
- const int bars_y = ladder_h * 2;
- const int bars_h = h - bars_y;
- const int bar_w = w / n;
- for (int i = 0; i < n; i++) {
- int bw = (i == n - 1) ? (w - i * bar_w) : bar_w;
- it.filled_rectangle(i * bar_w, bars_y, bw, bars_h, colors[i]);
- }
- // Corner markers: three distinct corners, fourth left blank. The
- // bars/ladders above are symmetric enough that a 90/180-degree
- // rotation or mirror could otherwise still "look right" at a glance.
- const int mark = (w < h ? w : h) / 8;
- it.filled_rectangle(0, 0, mark, mark, colors[2]); // top-left: yellow
- it.filled_rectangle(w - mark, 0, mark, mark, colors[3]); // top-right: red
- it.filled_rectangle(0, h - mark, mark, mark, colors[0]); // bottom-left: black
- // bottom-right intentionally left blank - the "no marker" 4th state.
- // Corner-to-corner diagonal, drawn on top of everything: the
- // cheapest possible check for a coordinate-transform bug. A real
- // diagonal stays a clean line; any row/column reordering breaks it
- // into visible steps exactly where the reordering happens.
- it.line(0, 0, w - 1, h - 1, Color::BLACK);
- it.line(w - 1, 0, 0, h - 1, Color::BLACK);
- // Border frame, drawn last so it stays a clean unbroken rectangle
- // touching all four edges even where bars/ladders fill to the edge.
- // Catches a clipped or offset draw origin.
- it.rectangle(0, 0, w, h, Color::BLACK);
|