Never physically refresh the panel with a short/wrong-size frame
Found on hardware: the Tools Server field was pointed at Immich's own port instead of the frame server's, so /frame/image was actually hitting Immich and getting back a small error response (~10KB) instead of a 192,000-byte frame. epd_display_stream() logged a size-mismatch warning but called epd_turn_on_display() anyway, physically refreshing the panel with a buffer that was ~95% whatever was left over from before -- visible as "garbage" on screen, overwriting a previously-good image. epd_display_stream() now returns ESP_ERR_INVALID_SIZE instead of refreshing when the stream doesn't supply exactly EPD_FRAME_BYTES. Since this check happens before epd_turn_on_display() is ever called, the pixel data that *did* arrive only ever reached the panel's internal RAM over SPI, not the physically visible display, so aborting here leaves the screen exactly as it was. This also means fetch_and_display() failing now always implies the panel was never touched -- simplified frame_client_run() accordingly (dropped the now-always-true/false out-param that used to distinguish "failed before vs. during streaming", and always shows the FAILED status screen on any fetch/display error, since it's now guaranteed safe to do so).
This commit is contained in:
@@ -234,7 +234,17 @@ esp_err_t epd_display_stream(epd_read_fn_t read_fn, void *ctx)
|
||||
EPD_CHECK(err);
|
||||
|
||||
if (total != EPD_FRAME_BYTES) {
|
||||
ESP_LOGW(TAG, "Stream supplied %u bytes, expected %u", (unsigned)total, (unsigned)EPD_FRAME_BYTES);
|
||||
/* Whatever was received has already been clocked into the panel's
|
||||
* internal RAM over SPI, but epd_turn_on_display() (the actual
|
||||
* physical refresh trigger) hasn't been called yet -- returning
|
||||
* here instead leaves the visible screen exactly as it was, rather
|
||||
* than refreshing onto a mostly-garbage buffer. Confirmed on
|
||||
* hardware: a misdirected fetch that returned a ~10KB error page
|
||||
* instead of a 192,000-byte frame still triggered a refresh before
|
||||
* this check existed, painting garbage over a previously-good image. */
|
||||
ESP_LOGE(TAG, "Stream supplied %u bytes, expected %u -- aborting refresh",
|
||||
(unsigned)total, (unsigned)EPD_FRAME_BYTES);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
return epd_turn_on_display();
|
||||
|
||||
@@ -213,13 +213,12 @@ static size_t http_read_fn(uint8_t *chunk, size_t chunk_size, void *ctx_)
|
||||
}
|
||||
|
||||
/* GETs /frame/image and streams the response directly into the panel.
|
||||
* *out_started_stream tells the caller whether the panel was actually
|
||||
* touched -- false means the failure happened before any pixel data was
|
||||
* sent (bad status, connection error), so nothing was drawn this cycle. */
|
||||
static esp_err_t fetch_and_display(const frame_config_t *cfg, bool *out_started_stream)
|
||||
* Returning non-ESP_OK means the panel was never actually refreshed --
|
||||
* epd_display_stream() (see epd7in3e.c) refuses to trigger a physical
|
||||
* refresh on a short/wrong-size stream, so a failure here always leaves
|
||||
* the visible screen exactly as it was. */
|
||||
static esp_err_t fetch_and_display(const frame_config_t *cfg)
|
||||
{
|
||||
*out_started_stream = false;
|
||||
|
||||
char url[160];
|
||||
snprintf(url, sizeof(url), "http://%s/frame/image", cfg->toolsserver);
|
||||
|
||||
@@ -247,7 +246,6 @@ static esp_err_t fetch_and_display(const frame_config_t *cfg, bool *out_started_
|
||||
}
|
||||
ESP_LOGI(TAG, "Fetching frame (%d bytes) from '%s'", content_length, url);
|
||||
|
||||
*out_started_stream = true;
|
||||
http_read_ctx_t ctx = { .client = client };
|
||||
err = epd_display_stream(http_read_fn, &ctx);
|
||||
|
||||
@@ -294,20 +292,16 @@ void frame_client_run(const frame_config_t *cfg)
|
||||
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_OK);
|
||||
}
|
||||
if (have_display) {
|
||||
bool stream_started = false;
|
||||
esp_err_t fetch_err = fetch_and_display(cfg, &stream_started);
|
||||
esp_err_t fetch_err = fetch_and_display(cfg);
|
||||
if (fetch_err != ESP_OK) {
|
||||
/* epd_display_stream() never triggers a physical refresh on
|
||||
* a failed/short/wrong-size stream (see epd7in3e.c), so the
|
||||
* visible screen is guaranteed untouched here -- always
|
||||
* safe to show what went wrong instead of leaving stale
|
||||
* content with no indication anything failed. */
|
||||
ESP_LOGW(TAG, "Fetch/display failed (%s), retrying sooner", esp_err_to_name(fetch_err));
|
||||
sleep_seconds = CONFIG_FRAME_RETRY_INTERVAL_S;
|
||||
if (!stream_started) {
|
||||
/* The panel was never touched this cycle (e.g. a bad
|
||||
* HTTP status before any pixel data was sent) -- show
|
||||
* the failure instead of leaving whatever was on
|
||||
* screen before. A mid-stream failure, by contrast,
|
||||
* already refreshed the panel with something, so
|
||||
* skip compounding it with a second refresh here. */
|
||||
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_FAILED);
|
||||
}
|
||||
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user