Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion include/opendisplay_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* fully-correct client from THIS FILE ALONE, without reading firmware.
*
* OD_PROTOCOL_VERSION 2.2 (MAJOR.MINOR; see VERSIONING POLICY below)
* LAST CHANGED 2026-07-22
* LAST CHANGED 2026-07-25
*
* --------------------------------------------------------------------------
* VERSIONING POLICY
Expand Down Expand Up @@ -71,6 +71,11 @@
* silently dropped. Breaking only for peers that wrote >253 bytes in a
* single GATT write (none deployed -- HA's GATT client caps at 244 and
* rejects larger writes outright).
* - DOC-ONLY: expand the CMD_FIRMWARE_VERSION (0x0043) @response layout to
* spell out the [shaLen:1][sha:shaLen] and optional trailing [patch:1]
* bytes (shaLen = ASCII SHA byte count 0..40; missing patch => 0), matching
* what shipped firmware already emits. Backported from Firmware's vendored
* copy. No wire change, no version bump.
* - Add each further wire-spec change here as it lands. On the next version
* bump, move these under a new "MAJOR.MINOR (YYYY-MM-DD)" heading.
*
Expand Down
30 changes: 19 additions & 11 deletions include/opendisplay_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
* type mirrors (see docs/shared-types-plan.md); codegen must carry the prose
* through as idiomatic doc comments in each target language.
*
* OD_STRUCTS_VERSION 2.0 (MAJOR.MINOR spec marker for THIS file; see
* OD_STRUCTS_VERSION 2.1 (MAJOR.MINOR spec marker for THIS file; see
* VERSIONING POLICY below -- NOT sent on the wire)
* LAST CHANGED 2026-07-18
* LAST CHANGED 2026-08-30
*
* The two version schemes carried here are DISTINCT (see §6 Q2 of the plan):
* - OD_STRUCTS_VERSION_* : documents this spec file, like protocol.h's
Expand Down Expand Up @@ -66,14 +66,23 @@
* CHANGELOG (newest first; entries accrue under "Unreleased" and roll into a
* new version heading on each bump -- see AGENT INSTRUCTIONS below)
* --------------------------------------------------------------------------
* Unreleased (since 2.0)
* Unreleased (since 2.1)
* - LedFlags bit4: OD_LED_FLAG_BUTTON_PRESS (short LED flash on physical
* button press / button-wake synthetic click; default off).
* - BuzzerFlags bit1: OD_BUZZER_FLAG_BUTTON_PRESS (short chirp on physical
* button press / button-wake synthetic click; default off).
* - MsdStatusBits bit3: OD_MSD_STATUS_ENCRYPTION_ENABLED (1 = app-layer
* encryption active). Firmware sets it when encryption_enabled and master
* key are non-zero; dynamic[11] is all 0xFF when no display is configured.
* (Folded back from the Firmware side, PR #157 — parity restore.)
* - Add each payload-layout change here as it lands. On the next version bump,
* move these under a new "MAJOR.MINOR (YYYY-MM-DD)" heading.
*
* 2.1 (2026-08-30)
* - Add PanelIC value OD_PANEL_IC_M5PAPERS3_960X540 = 3004 (M5Stack PaperS3,
* ED047TC1-class 960x540 parallel panel, 16-gray capable; FastEPD native
* parallel path). Backward-compatible enum addition (@since 1.5); the
* on-wire config-format minor bumps 4 -> 5 (OD_CONFIG_MINOR_VERSION).
* - Doc-only: fixed two comment shapes the codegen parser mis-read and added
* the CODEGEN AUTHORING RULES banner section to prevent recurrence. Split the
* combined BusFlags/PinBitmap @bits comment into one comment per group; folded
Expand All @@ -83,8 +92,6 @@
* placeholder macros (no wire change; these bits stay reserved-must-be-0):
* TransmissionModes bit5/bit6 -> OD_TRANSMISSION_MODE_RESERVED_5/_6;
* MsdStatusBits bit3 -> OD_MSD_STATUS_RESERVED_3. Documentation only.
* - Add each payload-layout change here as it lands. On the next version bump,
* move these under a new "MAJOR.MINOR (YYYY-MM-DD)" heading.
*
* 2.0 (2026-07-18)
* - Initial canonical shared payload contract: the wire-payload counterpart
Expand Down Expand Up @@ -252,8 +259,8 @@
/* Payload-spec revision, MAJOR.MINOR. Documents THIS file; NOT sent on the wire.
* Distinct from the on-wire OD_CONFIG_VERSION pair defined in SECTION 1. */
#define OD_STRUCTS_VERSION_MAJOR 2u
#define OD_STRUCTS_VERSION_MINOR 0u
#define OD_STRUCTS_VERSION_STR "2.0"
#define OD_STRUCTS_VERSION_MINOR 1u
#define OD_STRUCTS_VERSION_STR "2.1"

/* --------------------------------------------------------------------------
* Portable compile-time size check. Defined once here; every packed struct is
Expand Down Expand Up @@ -288,10 +295,10 @@

/* On-wire config-format version. UNLIKE OD_STRUCTS_VERSION / OD_PROTOCOL_VERSION,
* these ARE transmitted / negotiated: OuterPacketHeader.version carries the
* MAJOR byte. Frozen at 1.4 by this header (the app's bundled config.yaml is at
* MAJOR byte. At 1.5 as of this header (the app's bundled config.yaml is at
* minor 3 and must catch up). */
#define OD_CONFIG_VERSION 1u /* @doc "outer-packet major version byte" */
#define OD_CONFIG_MINOR_VERSION 4u /* @doc "config-format minor; backward-compatible additions" */
#define OD_CONFIG_MINOR_VERSION 5u /* @doc "config-format minor; backward-compatible additions" */

/* CRC over the outer packet. CRC16-CCITT, poly 0x1021, init 0xFFFF, computed
* over length+version+packets AS IF the 2-byte length field were 0x0000 (the
Expand Down Expand Up @@ -682,7 +689,8 @@ enum PanelIC {
OD_PANEL_IC_ED103TC2_1872X1404 = 3000, /**< @doc "E Ink ED103TC2 + IT8951 (10.3\", 1872x1404, 1bpp; FastEPD IT8951 path, values 3000+)" */
OD_PANEL_IC_ED103TC2_1872X1404_4GRAY = 3001, /**< @doc "same panel as 3000; 4bpp (16-level gray via FastEPD)" */
OD_PANEL_IC_INKPLATE5V2_1280X720 = 3002, /**< @doc "Soldered Inkplate 5 V2 (ED050WROW, 1280x720, 1bpp; FastEPD native parallel path)" */
OD_PANEL_IC_INKPLATE10_1200X825 = 3003 /**< @doc "Soldered Inkplate 10 (ED097TC2, 1200x825, 1bpp; FastEPD native parallel path)" */
OD_PANEL_IC_INKPLATE10_1200X825 = 3003, /**< @doc "Soldered Inkplate 10 (ED097TC2, 1200x825, 1bpp; FastEPD native parallel path)" */
OD_PANEL_IC_M5PAPERS3_960X540 = 3004 /**< @doc "M5Stack PaperS3 (ED047TC1-class, 960x540, 16-gray capable; FastEPD native parallel path)" @since 1.5 */
};

/* DisplayConfig.transmission_modes @bits TransmissionModes (bits 5-6 reserved --
Expand Down Expand Up @@ -1235,7 +1243,7 @@ OD_STATIC_ASSERT(sizeof(struct AuthProof) == 32, "AuthProof wire size");
#define OD_MSD_STATUS_REBOOT_FLAG (1u << 1) /* @doc "device rebooted since last read" */
#define OD_MSD_STATUS_CONNECTION_REQUESTED (1u << 2) /* @doc "device is requesting a connection" */
#define OD_MSD_STATUS_ENCRYPTION_ENABLED (1u << 3) /* @doc "application-layer encryption active (encryption_enabled and non-zero master key)" */
#define OD_MSD_STATUS_RESERVED_3 OD_MSD_STATUS_ENCRYPTION_ENABLED /* legacy doc name */
#define OD_MSD_STATUS_RESERVED_3 (1u << 3) /* @deprecated @doc "legacy name for OD_MSD_STATUS_ENCRYPTION_ENABLED (same bit)" */
#define OD_MSD_STATUS_MAIN_LOOP_COUNTER_SHIFT 4u /* @doc "bits 4-7: free-running main-loop nibble counter (liveness)" */
#define OD_MSD_STATUS_MAIN_LOOP_COUNTER_MASK 0xF0u /* @doc "mask for the bits 4-7 main-loop counter nibble" */

Expand Down
4 changes: 3 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/
framework = arduino
lib_deps =
${env.lib_deps}
bitbank2/FastEPD@^2.2.0
; Exact pin during PaperS3 bring-up: BB_PANEL_* is a positional enum
; upstream, so a floating 2.x could shift panel behavior.
bitbank2/FastEPD@2.2.0
build_flags =
-DTARGET_ESP32
-DOPENDISPLAY_ENABLE_WIFI ; LAN transport + ROM tinfl. Set ONLY on -DBOARD_HAS_PSRAM envs (src/wifi_service.h)
Expand Down
31 changes: 29 additions & 2 deletions src/display_fastepd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ class OdFastEPD : public FASTEPD {
};

// OpenDisplay panel_ic_type -> FastEPD native parallel panel id (-1 if not a
// parallel panel). FastEPD owns the Inkplate bus/PMIC/IO-expanders internally.
// parallel panel). FastEPD owns the board's bus/PMIC/IO-expanders internally.
static int fastepd_parallel_panel(uint16_t panel_ic_type) {
switch (panel_ic_type) {
case OD_PANEL_IC_INKPLATE5V2_1280X720: return BB_PANEL_INKPLATE5V2;
case OD_PANEL_IC_INKPLATE10_1200X825: return BB_PANEL_INKPLATE10;
case OD_PANEL_IC_M5PAPERS3_960X540: return BB_PANEL_M5PAPERS3;
default: return -1;
}
}
Expand Down Expand Up @@ -112,6 +113,19 @@ static bool fastepd_panel_is_4gray(void) {
return d.color_scheme == OD_COLOR_SCHEME_GRAY16;
}

// The effective pixel mode follows the runtime config (color_scheme), which
// can change between transfers via CONFIG_WRITE without a reboot. FastEPD's
// mode is sticky per hardware init, so warm transfer paths must re-assert it
// or incoming data is interpreted in the previous config's depth.
static void fastepd_apply_mode(void) {
if (!s_hw_initialized || !g_epd.currentBuffer()) return;
int want = fastepd_panel_is_4gray() ? BB_MODE_4BPP : BB_MODE_1BPP;
if (g_epd.getMode() != want) {
g_epd.setMode(want);
g_epd.setPreviousMode((uint8_t)g_epd.getMode());
}
}

static size_t fb_byte_size(void) {
uint32_t w = globalConfig.displays[0].pixel_width;
uint32_t h = globalConfig.displays[0].pixel_height;
Expand Down Expand Up @@ -229,6 +243,8 @@ void fastepd_epaper_begin(void) {
g_epd.setPreviousMode((uint8_t)g_epd.getMode());
} else {
g_epd.einkPower(1);
s_hw_initialized = true; // apply_mode requires it; set before the call
fastepd_apply_mode();
}
s_hw_initialized = true;
return;
Expand Down Expand Up @@ -264,7 +280,16 @@ void fastepd_epaper_begin(void) {
// REFRESH_FULL: parallel panels flash-clear to flush ghosting; IT8951 clears
// internally (ignores the mode) so it stays CLEAR_NONE.
static void fastepd_full_refresh_impl(void) {
int clear = fastepd_is_parallel() ? CLEAR_FAST : CLEAR_NONE;
int clear = CLEAR_NONE;
if (fastepd_is_parallel()) {
// The PaperS3 needs the library-default 10-pass clear: CLEAR_FAST's
// 8 passes leave residual banding on this panel (verified on
// hardware 2026-08-31, library-only sketch clean with CLEAR_SLOW).
// Inkplates keep the faster clear they were tuned with.
clear = (globalConfig.displays[0].panel_ic_type == OD_PANEL_IC_M5PAPERS3_960X540)
? CLEAR_SLOW
: CLEAR_FAST;
}
g_epd.fullUpdate(clear, true, NULL);
}

Expand Down Expand Up @@ -302,6 +327,7 @@ void fastepd_direct_write_reset(void) {
fastepd_epaper_begin();
} else {
g_epd.einkPower(1);
fastepd_apply_mode();
}
s_direct_offset = 0;
uint8_t* p = g_epd.currentBuffer();
Expand Down Expand Up @@ -366,6 +392,7 @@ void fastepd_partial_prepare(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
fastepd_epaper_begin();
} else {
g_epd.einkPower(1);
fastepd_apply_mode();
}
s_partial_x = x;
s_partial_y = y;
Expand Down
9 changes: 5 additions & 4 deletions src/display_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -852,10 +852,11 @@ bool fastepd_driver_used(void) {
// FastEPD IT8951 (SPI) path: E Ink ED103TC2 (Seeed reTerminal).
const bool it8951 = (d.panel_ic_type == OD_PANEL_IC_ED103TC2_1872X1404 ||
d.panel_ic_type == OD_PANEL_IC_ED103TC2_1872X1404_4GRAY);
// FastEPD native parallel path: Soldered Inkplate 5V2 / 10.
const bool inkplate = (d.panel_ic_type == OD_PANEL_IC_INKPLATE5V2_1280X720 ||
d.panel_ic_type == OD_PANEL_IC_INKPLATE10_1200X825);
if (!it8951 && !inkplate) return false;
// FastEPD native parallel path: Soldered Inkplate 5V2 / 10, M5Stack PaperS3.
const bool parallel = (d.panel_ic_type == OD_PANEL_IC_INKPLATE5V2_1280X720 ||
d.panel_ic_type == OD_PANEL_IC_INKPLATE10_1200X825 ||
d.panel_ic_type == OD_PANEL_IC_M5PAPERS3_960X540);
if (!it8951 && !parallel) return false;
if (d.display_technology != 0 && d.display_technology != 1) return false;
return true;
#endif
Expand Down
Loading