Add back-photo button; consolidate reset/manage onto one hold-duration button
Build and push server image / build-and-push (push) Successful in 32s
Build and push server image / build-and-push (push) Successful in 32s
Back button (new GPIO0, POST /frame/back): the server now tracks a bounded history of previously-current photos (photo_queue.py), pushed to on every advance (auto or forced) and popped by back_forced() -- symmetric with advance, so pressing next afterwards returns to right where you were. frame_client.c's force_advance bool becomes a 3-way fetch_action_t (NORMAL/ADVANCE/BACK) threaded through the whole fetch path. Also folds the separate reset and manage buttons onto one pin (combo_button.c, replacing reset_button.c/manage_button.c entirely), disambiguated by hold duration: quick press shows the management menu (unchanged), ~3s hold-then-release soft-resets (esp_restart(), config kept -- new), ~15s hold factory-resets (today's old reset behavior, extended from 10s for clearer tier separation). Driven by a production board (Seeed XIAO ESP32-C6) exposing only 3 of the ESP32-C6's 8 deep-sleep-wakeup-capable GPIOs -- next/back keep their own dedicated pins where instant response matters most, everything else shares the third pin via timing instead of needing its own. Same three-pin layout now works on both the dev board and the production board. Fixed a fast-tap bug in combo_button_check() before shipping: it only did a live gpio_get_level() read to decide whether the button was pressed at all, so a press fast enough to already be released by the time boot reached that check was missed entirely (treated as "never pressed" rather than "quick press"). Added the same latched esp_sleep_get_gpio_wakeup_status() check the other buttons already use for exactly this reason.
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
#include "epd7in3e.h"
|
||||
#include "status_screen.h"
|
||||
#include "manage_qr_overlay.h"
|
||||
#include "manage_button.h"
|
||||
#include "combo_button.h"
|
||||
|
||||
#include "frame_client.h"
|
||||
|
||||
@@ -487,22 +487,31 @@ static size_t http_read_fn(uint8_t *chunk, size_t chunk_size, void *ctx_)
|
||||
return (size_t)n;
|
||||
}
|
||||
|
||||
/* GETs /frame/image (or, if force_advance, POSTs /frame/advance to skip
|
||||
* ahead immediately) and streams the response directly into the panel,
|
||||
* splicing in overlay's pixels (if non-NULL) as it streams. 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, bool force_advance,
|
||||
/* GETs /frame/image (FETCH_NORMAL), or POSTs /frame/advance or
|
||||
* /frame/back to force a move in either direction (FETCH_ADVANCE /
|
||||
* FETCH_BACK -- the next-photo / back-photo buttons), and streams the
|
||||
* response directly into the panel, splicing in overlay's pixels (if
|
||||
* non-NULL) as it streams. 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, fetch_action_t action,
|
||||
const manage_overlay_set_t *overlay)
|
||||
{
|
||||
const char *path = "frame/image";
|
||||
if (action == FETCH_ADVANCE) {
|
||||
path = "frame/advance";
|
||||
} else if (action == FETCH_BACK) {
|
||||
path = "frame/back";
|
||||
}
|
||||
|
||||
char url[256];
|
||||
build_url(url, sizeof(url), cfg, force_advance ? "frame/advance" : "frame/image");
|
||||
build_url(url, sizeof(url), cfg, path);
|
||||
|
||||
esp_http_client_config_t config = {
|
||||
.url = url,
|
||||
.method = force_advance ? HTTP_METHOD_POST : HTTP_METHOD_GET,
|
||||
.method = action == FETCH_NORMAL ? HTTP_METHOD_GET : HTTP_METHOD_POST,
|
||||
.timeout_ms = CONFIG_FRAME_FETCH_TIMEOUT_MS,
|
||||
.cert_pem = tools_server_ca_pem_start,
|
||||
};
|
||||
@@ -571,14 +580,14 @@ static bool wait_for_button_press(uint32_t timeout_ms)
|
||||
vTaskDelay(pdMS_TO_TICKS(MANAGE_MENU_POLL_MS));
|
||||
elapsed_ms += MANAGE_MENU_POLL_MS;
|
||||
|
||||
if (!manage_button_is_pressed()) {
|
||||
if (!combo_button_is_pressed()) {
|
||||
continue;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(MANAGE_MENU_DEBOUNCE_MS));
|
||||
if (!manage_button_is_pressed()) {
|
||||
if (!combo_button_is_pressed()) {
|
||||
continue; /* noise, not a real press */
|
||||
}
|
||||
while (manage_button_is_pressed()) {
|
||||
while (combo_button_is_pressed()) {
|
||||
vTaskDelay(pdMS_TO_TICKS(MANAGE_MENU_POLL_MS));
|
||||
}
|
||||
return true;
|
||||
@@ -588,10 +597,10 @@ static bool wait_for_button_press(uint32_t timeout_ms)
|
||||
|
||||
/* Builds and shows one level of the manage menu: level 1 is the base
|
||||
* overlay (management QR + location/date/share-QR wherever the server
|
||||
* had that data); level 2 adds named-face labels on top. force_advance
|
||||
* only applies at level 1 -- escalating to level 2 redisplays the same
|
||||
* photo, so it never re-advances. */
|
||||
static esp_err_t show_menu_level(const frame_config_t *cfg, bool force_advance, int level)
|
||||
* had that data); level 2 adds named-face labels on top. action only
|
||||
* applies at level 1 -- escalating to level 2 redisplays the same
|
||||
* photo, so it never re-advances/-backs. */
|
||||
static esp_err_t show_menu_level(const frame_config_t *cfg, fetch_action_t action, int level)
|
||||
{
|
||||
char management_url[256];
|
||||
build_url(management_url, sizeof(management_url), cfg, "");
|
||||
@@ -626,7 +635,7 @@ static esp_err_t show_menu_level(const frame_config_t *cfg, bool force_advance,
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fetch_and_display(cfg, force_advance, &overlay);
|
||||
err = fetch_and_display(cfg, action, &overlay);
|
||||
manage_overlay_free(&overlay);
|
||||
return err;
|
||||
}
|
||||
@@ -641,13 +650,13 @@ static esp_err_t show_menu_level(const frame_config_t *cfg, bool force_advance,
|
||||
* that (escalating, or the final revert) are logged but don't count as
|
||||
* an overall failure -- something was already shown successfully, which
|
||||
* was the point of the button. */
|
||||
static esp_err_t run_management_menu(const frame_config_t *cfg, bool force_advance)
|
||||
static esp_err_t run_management_menu(const frame_config_t *cfg, fetch_action_t action)
|
||||
{
|
||||
int level = 1;
|
||||
esp_err_t err = show_menu_level(cfg, force_advance, level);
|
||||
esp_err_t err = show_menu_level(cfg, action, level);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Could not render management overlay (%s), showing photo normally", esp_err_to_name(err));
|
||||
return fetch_and_display(cfg, force_advance, NULL);
|
||||
return fetch_and_display(cfg, action, NULL);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
@@ -657,14 +666,14 @@ static esp_err_t run_management_menu(const frame_config_t *cfg, bool force_advan
|
||||
break; /* timeout at any level, or a press while already maxed out -- exit */
|
||||
}
|
||||
level++;
|
||||
esp_err_t level_err = show_menu_level(cfg, false, level);
|
||||
esp_err_t level_err = show_menu_level(cfg, FETCH_NORMAL, level);
|
||||
if (level_err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Could not render menu level %d (%s), reverting", level, esp_err_to_name(level_err));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t revert_err = fetch_and_display(cfg, false, NULL);
|
||||
esp_err_t revert_err = fetch_and_display(cfg, FETCH_NORMAL, NULL);
|
||||
if (revert_err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Failed to revert management overlay (%s)", esp_err_to_name(revert_err));
|
||||
}
|
||||
@@ -674,15 +683,15 @@ static esp_err_t run_management_menu(const frame_config_t *cfg, bool force_advan
|
||||
/* Runs the appropriate fetch for this cycle: a plain fetch, or -- if
|
||||
* show_management_qr -- the escalating manage menu (see
|
||||
* run_management_menu()). */
|
||||
static esp_err_t run_fetch_cycle(const frame_config_t *cfg, bool force_advance, bool show_management_qr)
|
||||
static esp_err_t run_fetch_cycle(const frame_config_t *cfg, fetch_action_t action, bool show_management_qr)
|
||||
{
|
||||
if (!show_management_qr) {
|
||||
return fetch_and_display(cfg, force_advance, NULL);
|
||||
return fetch_and_display(cfg, action, NULL);
|
||||
}
|
||||
return run_management_menu(cfg, force_advance);
|
||||
return run_management_menu(cfg, action);
|
||||
}
|
||||
|
||||
void frame_client_run(const frame_config_t *cfg, bool force_advance, bool show_management_qr)
|
||||
void frame_client_run(const frame_config_t *cfg, fetch_action_t action, bool show_management_qr)
|
||||
{
|
||||
esp_err_t epd_err = epd_init();
|
||||
bool have_display = (epd_err == ESP_OK);
|
||||
@@ -716,7 +725,7 @@ void frame_client_run(const frame_config_t *cfg, bool force_advance, bool show_m
|
||||
* worth it to stop false-failing on the common case. */
|
||||
bool image_ok = true;
|
||||
if (have_display) {
|
||||
esp_err_t fetch_err = run_fetch_cycle(cfg, force_advance, show_management_qr);
|
||||
esp_err_t fetch_err = run_fetch_cycle(cfg, action, show_management_qr);
|
||||
image_ok = (fetch_err == ESP_OK);
|
||||
if (!image_ok) {
|
||||
/* epd_display_stream() never triggers a physical refresh on a
|
||||
|
||||
Reference in New Issue
Block a user