Fetch refresh interval from server instead of hardcoding it
Replaces check_server_reachable() (bare bool, GET /health) with fetch_frame_config(), which GETs the server's new /frame/config endpoint instead -- doubles as the reachability check (any completed HTTP response counts, same as before) and delivers the server-configured refresh_interval_s, used for the success-path deep sleep duration instead of the Kconfig-only default. Parses the tiny JSON response with a hand-rolled scalar extractor (json_extract_uint) rather than pulling in a JSON library -- cJSON isn't bundled in this ESP-IDF install, and a single flat integer field doesn't justify a new dependency. Verified standalone against exactly the JSON shape the server emits, including a missing-field fallback case. FRAME_SLEEP_INTERVAL_S (Kconfig) is now just the fallback used before the device has ever reached a configured server, or if the response is missing/unparseable -- documented as such in its help text.
This commit is contained in:
@@ -45,11 +45,13 @@ menu "ESPresso Frame Configuration"
|
||||
stored home WiFi network before counting it as a failed retry.
|
||||
|
||||
config FRAME_SERVER_CHECK_TIMEOUT_MS
|
||||
int "Tools server reachability check timeout (ms)"
|
||||
int "Tools server /frame/config request 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.
|
||||
How long to wait for a GET /frame/config response from the
|
||||
tools server -- doubles as both the reachability check for the
|
||||
post-connect status screen and the source of the
|
||||
server-configured refresh interval below.
|
||||
|
||||
config FRAME_FETCH_TIMEOUT_MS
|
||||
int "Image fetch HTTP timeout (ms)"
|
||||
@@ -61,12 +63,17 @@ menu "ESPresso Frame Configuration"
|
||||
check's timeout.
|
||||
|
||||
config FRAME_SLEEP_INTERVAL_S
|
||||
int "Deep sleep interval between refreshes (seconds)"
|
||||
int "Fallback 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.
|
||||
The refresh interval is normally set server-side (the "Refresh
|
||||
interval" field in the server's web UI, delivered via GET
|
||||
/frame/config) so it can be changed without reflashing. This
|
||||
value is only a fallback: used before the device has ever
|
||||
successfully reached a configured server, or if the server's
|
||||
response doesn't include a valid interval (e.g. an older
|
||||
server version). 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)"
|
||||
|
||||
@@ -111,12 +111,52 @@ esp_err_t frame_wifi_connect_sta(const frame_config_t *cfg)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Probes the server's /health endpoint -- any completed HTTP response
|
||||
* means the socket-level connection succeeded, which is all this checks. */
|
||||
static bool check_server_reachable(const char *toolsserver)
|
||||
typedef struct {
|
||||
bool reachable;
|
||||
uint32_t refresh_interval_s; /* CONFIG_FRAME_SLEEP_INTERVAL_S if absent/unparseable */
|
||||
} frame_server_config_t;
|
||||
|
||||
/* Finds the first integer value associated with "key" in a small JSON
|
||||
* blob, e.g. 3600 in {"refresh_interval_s": 3600}. Not a general JSON
|
||||
* parser -- just enough for this project's small, flat config response,
|
||||
* to avoid pulling in a JSON library for one scalar field. */
|
||||
static bool json_extract_uint(const char *json, const char *key, uint32_t *out)
|
||||
{
|
||||
char needle[48];
|
||||
snprintf(needle, sizeof(needle), "\"%s\"", key);
|
||||
const char *pos = strstr(json, needle);
|
||||
if (pos == NULL) {
|
||||
return false;
|
||||
}
|
||||
pos = strchr(pos, ':');
|
||||
if (pos == NULL) {
|
||||
return false;
|
||||
}
|
||||
pos++;
|
||||
while (*pos == ' ') {
|
||||
pos++;
|
||||
}
|
||||
char *end;
|
||||
unsigned long value = strtoul(pos, &end, 10);
|
||||
if (end == pos) {
|
||||
return false;
|
||||
}
|
||||
*out = (uint32_t)value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* GETs the server's /frame/config -- doubles as both the reachability
|
||||
* check (any completed HTTP response means the socket-level connection
|
||||
* succeeded) and the source of the server-configurable refresh interval. */
|
||||
static frame_server_config_t fetch_frame_config(const char *toolsserver)
|
||||
{
|
||||
frame_server_config_t result = {
|
||||
.reachable = false,
|
||||
.refresh_interval_s = CONFIG_FRAME_SLEEP_INTERVAL_S,
|
||||
};
|
||||
|
||||
char url[160];
|
||||
snprintf(url, sizeof(url), "http://%s/health", toolsserver);
|
||||
snprintf(url, sizeof(url), "http://%s/frame/config", toolsserver);
|
||||
|
||||
esp_http_client_config_t config = {
|
||||
.url = url,
|
||||
@@ -124,13 +164,38 @@ static bool check_server_reachable(const char *toolsserver)
|
||||
.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);
|
||||
|
||||
esp_err_t err = esp_http_client_open(client, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Server '%s' not reachable: %s", toolsserver, esp_err_to_name(err));
|
||||
esp_http_client_cleanup(client);
|
||||
return result;
|
||||
}
|
||||
return err == ESP_OK;
|
||||
|
||||
esp_http_client_fetch_headers(client);
|
||||
result.reachable = true;
|
||||
|
||||
char body[256];
|
||||
int total = 0;
|
||||
int n;
|
||||
while (total < (int)sizeof(body) - 1 &&
|
||||
(n = esp_http_client_read(client, body + total, sizeof(body) - 1 - total)) > 0) {
|
||||
total += n;
|
||||
}
|
||||
body[total] = '\0';
|
||||
|
||||
esp_http_client_close(client);
|
||||
esp_http_client_cleanup(client);
|
||||
|
||||
uint32_t interval;
|
||||
if (json_extract_uint(body, "refresh_interval_s", &interval)) {
|
||||
result.refresh_interval_s = interval;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' response missing refresh_interval_s, using fallback %ds", url,
|
||||
(int)result.refresh_interval_s);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -212,10 +277,10 @@ void frame_client_run(const frame_config_t *cfg)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sleep_seconds = CONFIG_FRAME_SLEEP_INTERVAL_S;
|
||||
bool server_reachable = check_server_reachable(cfg->toolsserver);
|
||||
frame_server_config_t server_cfg = fetch_frame_config(cfg->toolsserver);
|
||||
uint32_t sleep_seconds = server_cfg.refresh_interval_s;
|
||||
|
||||
if (!server_reachable) {
|
||||
if (!server_cfg.reachable) {
|
||||
/* Nothing's been drawn yet this cycle (aside from the optional
|
||||
* PENDING screen above), so this is cheap diagnostics without
|
||||
* compounding flashing. */
|
||||
|
||||
Reference in New Issue
Block a user