From 462b558befc48a89b873124269515f527e34e44a Mon Sep 17 00:00:00 2001 From: Thomas Faour Date: Wed, 22 Jul 2026 09:45:59 -0400 Subject: [PATCH] Fix captive portal redirect: visible countdown, keep AP up until it finishes Previously the softAP was torn down (esp_restart) only 1s after sending the success page, while the page's own redirect timer waited 7s -- so the AP (and the phone's captive-portal session with it) was gone long before the redirect could fire. Now the page shows a live 10s countdown before redirecting, and the device holds the AP up for 11s so the countdown always completes. Also added a "Redirect now" button for a phone that's already reconnected to normal WiFi. --- firmware/main/wifi_provisioning.c | 51 ++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/firmware/main/wifi_provisioning.c b/firmware/main/wifi_provisioning.c index d224fda..2c38589 100644 --- a/firmware/main/wifi_provisioning.c +++ b/firmware/main/wifi_provisioning.c @@ -448,11 +448,17 @@ static esp_err_t save_config_post_handler(httpd_req_t *req) /* The success page hands the browser off to the server's claim page, * carrying this device's id -- how a frame gets linked to a user - * account. The ~7s delay covers the phone dropping this softAP (the - * device reboots right after this response) and rejoining its normal - * WiFi before the redirect fires; the visible link is the fallback - * if the phone loses that race. Scheme handling matches - * frame_client.c's build_url(): a bare host gets http://. */ + * account. This page is entirely self-contained (no external + * resources) so it renders fully from what we send now, before the + * softAP goes away -- a phone mid-load of a remote asset would just + * time out once the AP drops. The visible countdown ticks down for + * PROVISIONING_COUNTDOWN_S seconds and then redirects; the AP is kept + * alive for one second longer than that (see the vTaskDelay below) so + * the countdown always finishes, and the phone has that whole window + * to rejoin its normal WiFi and let the redirect land on the real + * server. "Redirect now" covers a phone that's already reconnected. + * Scheme handling matches frame_client.c's build_url(): a bare host + * gets http://. */ char device_id[FRAME_DEVICE_ID_LEN + 1]; frame_device_id_get(device_id, sizeof(device_id)); @@ -463,25 +469,40 @@ static esp_err_t save_config_post_handler(httpd_req_t *req) } snprintf(claim_url, sizeof(claim_url), "%s%s/claim?device_id=%s", scheme, cfg.toolsserver, device_id); - char resp[1024]; +#define PROVISIONING_COUNTDOWN_S 10 + + char resp[1536]; snprintf(resp, sizeof(resp), "" - "" - "" + "" + "" "

Saved — the frame is restarting

" - "

Reconnect to your normal WiFi. You'll be taken to the claim page " - "in a few seconds…

" - "

Continue to claim your frame

" - "" + "

Reconnect to your normal WiFi if it doesn't happen automatically.

" + "

Redirecting you in %d seconds…

" + "

Redirect now

" + "" "", - claim_url, claim_url, '"', claim_url, '"'); + PROVISIONING_COUNTDOWN_S, claim_url, PROVISIONING_COUNTDOWN_S, claim_url, + PROVISIONING_COUNTDOWN_S, claim_url); httpd_resp_set_type(req, "text/html"); httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN); - /* Let the response flush to the client before rebooting into STA mode. */ - vTaskDelay(pdMS_TO_TICKS(1000)); + /* Keep the softAP up for the full visible countdown (plus a 1s margin + * for the response to flush and the JS timer to fire) before tearing + * it down -- see the comment above for why. */ + vTaskDelay(pdMS_TO_TICKS((PROVISIONING_COUNTDOWN_S + 1) * 1000)); esp_restart(); +#undef PROVISIONING_COUNTDOWN_S + return ESP_OK; }