Add post-connect status screen with WiFi/server checklist

After a successful home WiFi connect, frame_client_run() now redraws the
panel as a two-row checklist (WiFi row with a checkmark, server row) so
the connection sequence is visible on-device rather than only in serial
logs. Refreshes once with the server row pending, probes the tools server
with a plain HTTP HEAD (any response, even 404, confirms the socket-level
connection works -- there's no real server yet), then refreshes again with
the final result. Two refreshes rather than one to actually show staged
progress, at the cost of the extra refresh time inherent to this panel.

Also fixes a second hardware-verified bug in the same area: on a failed
STA connect falling back to provisioning, wifi_init_softap()'s
esp_wifi_init() call was aborting with ESP_ERR_INVALID_STATE, because
frame_wifi_connect_sta() only stopped the WiFi driver on failure rather
than fully deinitializing it (and destroying the STA netif) before
handing back control.
This commit is contained in:
2026-07-18 14:08:19 -04:00
parent ecc42f23c0
commit a518cbdbaf
5 changed files with 138 additions and 3 deletions
+2 -2
View File
@@ -1,3 +1,3 @@
idf_component_register(SRCS main.c wifi_provisioning.c frame_client.c qr_onboarding.c
PRIV_REQUIRES esp_event nvs_flash esp_wifi esp_netif esp_http_server dns_server epd7in3e qrcode epaper_fonts
idf_component_register(SRCS main.c wifi_provisioning.c frame_client.c qr_onboarding.c status_screen.c epd_draw.c
PRIV_REQUIRES esp_event nvs_flash esp_wifi esp_netif esp_http_server esp_http_client dns_server epd7in3e qrcode epaper_fonts
EMBED_FILES root.html)
+7
View File
@@ -44,4 +44,11 @@ menu "ESPresso Frame Configuration"
How long to wait for an IP address on each connection attempt to the
stored home WiFi network before counting it as a failed retry.
config FRAME_SERVER_CHECK_TIMEOUT_MS
int "Tools server reachability check timeout (ms)"
default 3000
help
How long to wait for an HTTP response from the tools server
when checking reachability on the post-connect status screen.
endmenu
+47 -1
View File
@@ -3,12 +3,17 @@
#include "esp_event.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_wifi_default.h"
#include "esp_netif.h"
#include "esp_http_client.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "epd7in3e.h"
#include "status_screen.h"
#include "frame_client.h"
static const char *TAG = "frame_client";
@@ -50,7 +55,7 @@ esp_err_t frame_wifi_connect_sta(const frame_config_t *cfg)
{
s_sta_event_group = xEventGroupCreate();
esp_netif_create_default_wifi_sta();
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
wifi_init_config_t init_cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&init_cfg));
@@ -93,14 +98,55 @@ esp_err_t frame_wifi_connect_sta(const frame_config_t *cfg)
s_sta_event_group = NULL;
if (result != ESP_OK) {
/* Fully tear the WiFi driver back down on failure -- the caller
* falls back to provisioning, which calls esp_wifi_init() again
* for AP mode. Leaving the driver merely stopped (rather than
* deinitialized) made that second esp_wifi_init() call fail with
* ESP_ERR_INVALID_STATE and abort, confirmed on hardware. */
esp_wifi_stop();
esp_wifi_deinit();
esp_netif_destroy_default_wifi(sta_netif);
}
return result;
}
/* Probes cfg->toolsserver with a plain HTTP HEAD -- any HTTP response
* (even a 404, since there's no server yet to define real routes) means
* the socket-level connection succeeded, which is all this is checking. */
static bool check_server_reachable(const char *toolsserver)
{
char url[160];
snprintf(url, sizeof(url), "http://%s/", toolsserver);
esp_http_client_config_t config = {
.url = url,
.method = HTTP_METHOD_HEAD,
.timeout_ms = CONFIG_FRAME_SERVER_CHECK_TIMEOUT_MS,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
esp_http_client_cleanup(client);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Server '%s' not reachable: %s", toolsserver, esp_err_to_name(err));
}
return err == ESP_OK;
}
void frame_client_run(const frame_config_t *cfg)
{
esp_err_t epd_err = epd_init();
if (epd_err != ESP_OK) {
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. */
+62
View File
@@ -0,0 +1,62 @@
#include <stdlib.h>
#include <string.h>
#include "esp_check.h"
#include "epd7in3e.h"
#include "epd_draw.h"
#include "fonts.h"
#include "status_screen.h"
static const char *TAG = "status_screen";
#define TITLE_Y 40
#define ROW1_Y 160
#define ROW2_Y 240
#define LABEL_X 80
#define MARKER_X 640
#define CHECKMARK_SIZE 40
static void draw_status_marker(uint8_t *frame, int x, int y, status_state_t state)
{
switch (state) {
case STATUS_OK:
epd_draw_checkmark(frame, x, y - 8, CHECKMARK_SIZE);
break;
case STATUS_FAILED:
epd_draw_text(frame, &Font24, "FAILED", x, y);
break;
case STATUS_PENDING:
default:
epd_draw_text(frame, &Font24, "...", x, y);
break;
}
}
esp_err_t status_screen_show(const char *wifi_ssid, status_state_t wifi_state, const char *server_addr,
status_state_t server_state)
{
/* Allocated on demand rather than statically reserved -- see
* qr_onboarding.c for why that's fine memory-budget-wise here. */
uint8_t *frame = malloc(EPD_FRAME_BYTES);
ESP_RETURN_ON_FALSE(frame != NULL, ESP_ERR_NO_MEM, TAG, "Failed to allocate frame buffer");
memset(frame, (EPD_COLOR_WHITE << 4) | EPD_COLOR_WHITE, EPD_FRAME_BYTES);
epd_draw_text_centered(frame, &Font24, "CONNECTING", EPD_WIDTH / 2, TITLE_Y);
char wifi_line[64];
snprintf(wifi_line, sizeof(wifi_line), "WIFI: %s", wifi_ssid);
epd_draw_text(frame, &Font24, wifi_line, LABEL_X, ROW1_Y);
draw_status_marker(frame, MARKER_X, ROW1_Y, wifi_state);
char server_line[96];
snprintf(server_line, sizeof(server_line), "SERVER: %s", server_addr);
epd_draw_text(frame, &Font24, server_line, LABEL_X, ROW2_Y);
draw_status_marker(frame, MARKER_X, ROW2_Y, server_state);
esp_err_t err = epd_display_buffer(frame, EPD_FRAME_BYTES);
free(frame);
return err;
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "esp_err.h"
typedef enum {
STATUS_PENDING,
STATUS_OK,
STATUS_FAILED,
} status_state_t;
/**
* Renders a two-row connection checklist onto the e-ink panel: a WiFi row
* (SSID + a checkmark once connected) and a server row (toolsserver
* address + a checkmark once reachable), each showing a checkmark on
* STATUS_OK, "FAILED" on STATUS_FAILED, or "..." while STATUS_PENDING.
* Call once per stage as connection progress is confirmed, so the panel
* visibly updates as each step completes.
*/
esp_err_t status_screen_show(const char *wifi_ssid, status_state_t wifi_state, const char *server_addr,
status_state_t server_state);