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
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Changelog

## Unreleased

### Features

* partial-region refresh over the PIPE_WRITE sliding window (0x0080 flags bit1
`PIPE_FLAG_PARTIAL` + 12-byte LE geometry; ACK flags bit1 confirms). Small mono
partial updates now get the pipe's throughput/robustness and the e-paper partial
waveform, with the legacy 0x76 path retained as fallback. New START NACK codes
0x05 (etag mismatch) / 0x06 (partial unsupported) / 0x07 (rect invalid) drive the
fallback ladder. No new capability bit — eligibility is inferred from the existing
`supports_pipe_write` + `partial_update_support` signals. Works unchanged under an
encrypted session.
* PIPE_WRITE (full and partial) is now hard-gated on the device config advertising
`transmission_modes` bit 0x10 (`supports_pipe_write`): a device whose config lacks
the bit never receives a 0x0080 probe and stays on the legacy path. When the gate
passes, the 0x0080 negotiation remains authoritative for transfer parameters.
Previously the bit was advisory and the probe alone decided.
* With the config gate in place, the 0x0080 START wait is no longer a 2 s discovery
probe: `TIMEOUT_PIPE_PROBE` is replaced by `TIMEOUT_PIPE_START` (30 s, a normal
command timeout sized for ESP32's response-queue flush landing after slow color
panel bring-up). Silence still falls back to legacy (stale config bit), cached per
connection.
* Hardening: the sliding-window sender and END_ACK waiter now bound pathological
ACK streams that never make progress (previously loops without a progress
guarantee, reachable only with buggy/hostile firmware).

## [7.11.2](https://github.com/OpenDisplay/py-opendisplay/compare/v7.11.1...v7.11.2) (2026-07-06)


Expand Down
15 changes: 15 additions & 0 deletions FINDINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,21 @@ of **8** regardless of bpp (`display_service.cpp:1534`). Python:
Fix: restrict partial to `ColorScheme.MONO`, align to 8 pixels, and treat all pre-refresh
0x76/0x71 NACKs as fallback-to-full.

**Update (pipe-partial):** partial-region refresh now rides the sliding window when the
device advertises `supports_pipe_write` and `max_queue_size > 1`. `_maybe_upload_partial`
negotiates an extended `0x0080` START (flags bit1 `PIPE_FLAG_PARTIAL` + 12-byte LE
`[old_etag][x][y][w][h]` geometry, `total_size = plane_size*2`); the device confirms with
ACK flags bit1. New START NACK codes gate the fallback ladder: `0x05 ETAG_MISMATCH` → skip
0x76, go full (device already cleared its etag); `0x06 PARTIAL_UNSUPPORTED`/`0x07
RECT_INVALID` → go full (0x76 would fail identically; 0x06 caches a per-connection
negative); `0x02` on a partial request (after one uncompressed-still-partial retry) or an
ACK without bit1 → disable pipe-partial for the connection and fall back to legacy 0x76;
silence/garble → 0x76. Partial transfers never auto-complete (firmware waits for the
explicit `0x0082` END which alone carries the refresh selector `2` + new_etag), so the
sender uses the same explicit-END contract compressed transfers use. Encryption parity
holds: the 24-byte plaintext START fits one CCM frame, data frames size to 212 B @ frame
244, and the 0x76 fallback rung still caps at `ENCRYPTED_CHUNK_SIZE`.

### M2. 🟠 [FW-interplay] Etag never committed on uncompressed full uploads — partial mode never engages, and a stale-etag hazard exists

Uncompressed uploads always finish via firmware auto-END at the exact byte count
Expand Down
2 changes: 2 additions & 0 deletions src/opendisplay/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
OTAError,
OTANotSupportedError,
ProtocolError,
RefreshTimeoutError,
TruncatedConfigError,
)
from .landing import LANDING_URL_PREFIX, build_landing_payload, build_landing_url
Expand Down Expand Up @@ -101,6 +102,7 @@
"BLEConnectionError",
"BLETimeoutError",
"ProtocolError",
"RefreshTimeoutError",
"ConfigParseError",
"TruncatedConfigError",
"InvalidResponseError",
Expand Down
Loading