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);
|
EPD_CHECK(err);
|
||||||
|
|
||||||
if (total != EPD_FRAME_BYTES) {
|
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();
|
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.
|
/* GETs /frame/image and streams the response directly into the panel.
|
||||||
* *out_started_stream tells the caller whether the panel was actually
|
* Returning non-ESP_OK means the panel was never actually refreshed --
|
||||||
* touched -- false means the failure happened before any pixel data was
|
* epd_display_stream() (see epd7in3e.c) refuses to trigger a physical
|
||||||
* sent (bad status, connection error), so nothing was drawn this cycle. */
|
* refresh on a short/wrong-size stream, so a failure here always leaves
|
||||||
static esp_err_t fetch_and_display(const frame_config_t *cfg, bool *out_started_stream)
|
* 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];
|
char url[160];
|
||||||
snprintf(url, sizeof(url), "http://%s/frame/image", cfg->toolsserver);
|
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);
|
ESP_LOGI(TAG, "Fetching frame (%d bytes) from '%s'", content_length, url);
|
||||||
|
|
||||||
*out_started_stream = true;
|
|
||||||
http_read_ctx_t ctx = { .client = client };
|
http_read_ctx_t ctx = { .client = client };
|
||||||
err = epd_display_stream(http_read_fn, &ctx);
|
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);
|
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_OK);
|
||||||
}
|
}
|
||||||
if (have_display) {
|
if (have_display) {
|
||||||
bool stream_started = false;
|
esp_err_t fetch_err = fetch_and_display(cfg);
|
||||||
esp_err_t fetch_err = fetch_and_display(cfg, &stream_started);
|
|
||||||
if (fetch_err != ESP_OK) {
|
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));
|
ESP_LOGW(TAG, "Fetch/display failed (%s), retrying sooner", esp_err_to_name(fetch_err));
|
||||||
sleep_seconds = CONFIG_FRAME_RETRY_INTERVAL_S;
|
sleep_seconds = CONFIG_FRAME_RETRY_INTERVAL_S;
|
||||||
if (!stream_started) {
|
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_FAILED);
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user