Wire the real fetch -> display -> deep-sleep cycle

frame_client_run() now does what it was always meant to: probe the tools
server, GET /frame/image and stream the response straight into the panel
via epd_display_stream() (esp_http_client's manual open/fetch_headers/read
API pulls in exactly the shape epd_display_stream()'s read_fn expects, so
the ~192KB frame never sits in RAM at once), then epd_sleep() and
esp_deep_sleep_start() for an hour.

Skips the WiFi/server status checklist screen on the happy path now that
there's a real photo to show instead -- three full refreshes every single
hour (status-pending, status-final, photo) wasn't worth it once bring-up
was actually working. Still shows it (status FAILED) when the server
isn't reachable, since nothing's been drawn yet that cycle and it's the
cheapest useful diagnostic. A mid-fetch failure after the panel's already
started refreshing just logs and retries sooner, rather than compounding
with a second refresh.

New Kconfig knobs: FRAME_FETCH_TIMEOUT_MS, FRAME_SLEEP_INTERVAL_S
(default 3600s), FRAME_RETRY_INTERVAL_S (default 300s on failure).
This commit is contained in:
2026-07-18 14:48:21 -04:00
parent 1c1d9c6767
commit 1ff91c8a71
2 changed files with 108 additions and 16 deletions
+25
View File
@@ -51,4 +51,29 @@ menu "ESPresso Frame Configuration"
How long to wait for an HTTP response from the tools server
when checking reachability on the post-connect status screen.
config FRAME_FETCH_TIMEOUT_MS
int "Image fetch HTTP timeout (ms)"
default 15000
help
How long to wait on the GET /frame/image request before giving
up. The server does real work per request (Immich download +
resize + dither), so this is longer than the reachability
check's timeout.
config FRAME_SLEEP_INTERVAL_S
int "Deep sleep interval between refreshes (seconds)"
default 3600
help
How long the device deep-sleeps between successful
fetch-and-display cycles. Lower this for bench testing so you
don't have to wait an hour per iteration.
config FRAME_RETRY_INTERVAL_S
int "Deep sleep interval after a failed cycle (seconds)"
default 300
help
How long the device deep-sleeps before retrying after the
server was unreachable or the fetch/display failed, instead of
waiting the full FRAME_SLEEP_INTERVAL_S.
endmenu
+83 -16
View File
@@ -6,9 +6,9 @@
#include "esp_wifi_default.h"
#include "esp_netif.h"
#include "esp_http_client.h"
#include "esp_sleep.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "epd7in3e.h"
@@ -134,27 +134,94 @@ static bool check_server_reachable(const char *toolsserver)
return err == ESP_OK;
}
typedef struct {
esp_http_client_handle_t client;
} http_read_ctx_t;
/* Pulls the next chunk straight out of the in-progress HTTP response --
* epd_display_stream() calls this to feed the panel without ever holding
* the full ~192KB frame in RAM. */
static size_t http_read_fn(uint8_t *chunk, size_t chunk_size, void *ctx_)
{
http_read_ctx_t *ctx = (http_read_ctx_t *)ctx_;
int n = esp_http_client_read(ctx->client, (char *)chunk, (int)chunk_size);
return n > 0 ? (size_t)n : 0;
}
/* GETs /frame/image and streams the response directly into the panel. */
static esp_err_t fetch_and_display(const frame_config_t *cfg)
{
char url[160];
snprintf(url, sizeof(url), "http://%s/frame/image", cfg->toolsserver);
esp_http_client_config_t config = {
.url = url,
.method = HTTP_METHOD_GET,
.timeout_ms = CONFIG_FRAME_FETCH_TIMEOUT_MS,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_open(client, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open '%s': %s", url, esp_err_to_name(err));
esp_http_client_cleanup(client);
return err;
}
int content_length = esp_http_client_fetch_headers(client);
int status = esp_http_client_get_status_code(client);
if (status != 200) {
ESP_LOGE(TAG, "'%s' returned HTTP %d", url, status);
esp_http_client_close(client);
esp_http_client_cleanup(client);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Fetching frame (%d bytes) from '%s'", content_length, url);
http_read_ctx_t ctx = { .client = client };
err = epd_display_stream(http_read_fn, &ctx);
esp_http_client_close(client);
esp_http_client_cleanup(client);
return err;
}
void frame_client_run(const frame_config_t *cfg)
{
esp_err_t epd_err = epd_init();
if (epd_err != ESP_OK) {
bool have_display = (epd_err == ESP_OK);
if (!have_display) {
ESP_LOGW(TAG, "EPD init failed (%s), continuing without display", esp_err_to_name(epd_err));
} else {
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_PENDING);
vTaskDelay(pdMS_TO_TICKS(1000));
bool reachable = check_server_reachable(cfg->toolsserver);
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, reachable ? STATUS_OK : STATUS_FAILED);
}
/* TODO(step 6): HTTP fetch from cfg->toolsserver -> epd_write_stream ->
* epd_refresh -> esp_deep_sleep. Idling here for now so the device
* stays observable over serial while STA connectivity is verified. */
ESP_LOGI(TAG, "Connected to home WiFi. toolsserver='%s' (fetch/display cycle not yet implemented)",
cfg->toolsserver);
uint32_t sleep_seconds = CONFIG_FRAME_SLEEP_INTERVAL_S;
bool server_reachable = check_server_reachable(cfg->toolsserver);
while (1) {
ESP_LOGI(TAG, "heartbeat: still connected");
vTaskDelay(pdMS_TO_TICKS(60000));
if (!server_reachable) {
/* Nothing's been drawn yet this cycle, so this is the only
* refresh -- cheap diagnostics without compounding flashing. */
ESP_LOGW(TAG, "Tools server not reachable, retrying sooner");
sleep_seconds = CONFIG_FRAME_RETRY_INTERVAL_S;
if (have_display) {
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_FAILED);
}
} else if (have_display) {
esp_err_t fetch_err = fetch_and_display(cfg);
if (fetch_err != ESP_OK) {
/* The panel may have already partially refreshed with a
* truncated frame by this point -- log and retry sooner
* rather than drawing a second status screen on top of it. */
ESP_LOGW(TAG, "Fetch/display failed (%s), retrying sooner", esp_err_to_name(fetch_err));
sleep_seconds = CONFIG_FRAME_RETRY_INTERVAL_S;
}
}
if (have_display) {
epd_sleep();
}
ESP_LOGI(TAG, "Deep sleeping for %u seconds", (unsigned)sleep_seconds);
esp_sleep_enable_timer_wakeup((uint64_t)sleep_seconds * 1000000ULL);
esp_deep_sleep_start();
}