Add two physical buttons: factory-reset and next-photo
Build and push server image / build-and-push (push) Successful in 35s

Factory-reset (GPIO3, hold 10s): clears stored WiFi/server config and
restarts into provisioning -- the deliberate, USB-free replacement for
the earlier reverted RST-based auto-reprovisioning idea.

Next-photo (GPIO2, tap): wakes the device and forces the server to
advance immediately via a new POST /frame/advance, instead of waiting
for the refresh interval. Both buttons arm themselves as deep-sleep GPIO
wakeup sources so a press is noticed promptly even while asleep.

Also makes GET /frame/image side-effect-free: it now only advances once
refresh_interval_s has elapsed since the current photo was set (tracked
server-side), so a device reboot for any reason just redisplays the
current photo instead of silently skipping ahead. The server maintains a
small reorderable upcoming-photos queue, viewable and rearrangeable from
the web UI.
This commit is contained in:
2026-07-18 23:28:36 -04:00
parent 7013311249
commit d395cf3bb9
19 changed files with 668 additions and 47 deletions
+7 -6
View File
@@ -212,19 +212,20 @@ static size_t http_read_fn(uint8_t *chunk, size_t chunk_size, void *ctx_)
return n > 0 ? (size_t)n : 0;
}
/* GETs /frame/image and streams the response directly into the panel.
/* GETs /frame/image (or, if force_advance, POSTs /frame/advance to skip
* ahead immediately) and streams the response directly into the panel.
* 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)
static esp_err_t fetch_and_display(const frame_config_t *cfg, bool force_advance)
{
char url[160];
snprintf(url, sizeof(url), "http://%s/frame/image", cfg->toolsserver);
snprintf(url, sizeof(url), "http://%s/%s", cfg->toolsserver, force_advance ? "frame/advance" : "frame/image");
esp_http_client_config_t config = {
.url = url,
.method = HTTP_METHOD_GET,
.method = force_advance ? HTTP_METHOD_POST : HTTP_METHOD_GET,
.timeout_ms = CONFIG_FRAME_FETCH_TIMEOUT_MS,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
@@ -255,7 +256,7 @@ static esp_err_t fetch_and_display(const frame_config_t *cfg)
return err;
}
void frame_client_run(const frame_config_t *cfg)
void frame_client_run(const frame_config_t *cfg, bool force_advance)
{
esp_err_t epd_err = epd_init();
bool have_display = (epd_err == ESP_OK);
@@ -292,7 +293,7 @@ void frame_client_run(const frame_config_t *cfg)
status_screen_show(cfg->sta_ssid, STATUS_OK, cfg->toolsserver, STATUS_OK);
}
if (have_display) {
esp_err_t fetch_err = fetch_and_display(cfg);
esp_err_t fetch_err = fetch_and_display(cfg, force_advance);
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