diff options
| author | Luke Hoersten <[email protected]> | 2026-06-13 22:42:14 -0500 |
|---|---|---|
| committer | Luke Hoersten <[email protected]> | 2026-06-13 22:42:14 -0500 |
| commit | e7feac61e5ea275f303574fedd942ebed8fc8e73 (patch) | |
| tree | 26bbb23c8faf16294325b27c254df6dd6fbdf1e8 /main/nvs_config.c | |
| parent | e91ed43dee0f2715362fed9f52fa684e655de7be (diff) | |
M4: NVS-backed /config with partial updates + validation
nvs_config.{h,c} — persist the runtime config (viewport, scrypted,
idle_timeout_ms, orientation, brightness) under a single NVS namespace.
nvs_config_load() applies persisted values over the in-RAM defaults on
boot and flips state from UNCONFIGURED to ASLEEP once both name and
Scrypted URL are present. nvs_config_save() commits the whole record
atomically.
http_api.c — add GET /config and POST /config:
- GET serializes viewport_state to the spec's JSON shape, with null
for unset string fields and defaults filled in for the rest.
- POST is partial: each field is optional; only present fields are
validated and applied. Validation runs on a staged copy and errors
short-circuit with 400 + reason before any state mutation, so a
rejected request leaves the device untouched.
- Validation rules: viewport non-empty <64 chars; scrypted starts with
http:// and <256 chars; idle_timeout_ms 0 or >=5000; orientation in
{portrait,landscape}; brightness 0..100.
- Side-effects fire after the lock + save: brightness change pushes
PWM to the panel MCU; viewport/orientation change reapplies mDNS
hostname + TXT.
- 204 on success; 400 with a single-line reason on validation error.
app_main calls nvs_config_load() right after viewport_state_init(), so
mdns_service_start() and display_init() see the persisted hostname,
orientation, and brightness from the first packet/PWM.
Build clean against ESP-IDF 5.4 (binary ~620 KB).
TESTING.md M3 now documents the Hosyond jumper wiring (5V/GND/SDA=GPIO7/
SCL=GPIO8 from board to panel header; DSI FPC carries only the high-
speed lanes). M4 entry expands the validation matrix and side-effects
to verify.
Diffstat (limited to 'main/nvs_config.c')
| -rw-r--r-- | main/nvs_config.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/main/nvs_config.c b/main/nvs_config.c new file mode 100644 index 0000000..164db53 --- /dev/null +++ b/main/nvs_config.c @@ -0,0 +1,119 @@ +#include "nvs_config.h" + +#include <string.h> + +#include "esp_log.h" +#include "nvs.h" +#include "nvs_flash.h" + +#include "viewport_state.h" + +static const char *TAG = "nvs_config"; +static const char *NS = "viewport"; + +static const char *K_VIEWPORT = "viewport"; +static const char *K_SCRYPTED = "scrypted"; +static const char *K_IDLE_MS = "idle_ms"; +static const char *K_ORIENT = "orient"; // 0 = portrait, 1 = landscape +static const char *K_BRIGHT = "bright"; + +esp_err_t nvs_config_load(void) +{ + nvs_handle_t h; + esp_err_t err = nvs_open(NS, NVS_READONLY, &h); + if (err == ESP_ERR_NVS_NOT_FOUND) { + ESP_LOGI(TAG, "no saved config — first boot"); + return ESP_OK; + } + if (err != ESP_OK) return err; + + viewport_state_lock(); + viewport_state_t *st = viewport_state_get(); + + size_t len = sizeof(st->viewport_name); + err = nvs_get_str(h, K_VIEWPORT, st->viewport_name, &len); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) goto done; + + len = sizeof(st->scrypted_url); + err = nvs_get_str(h, K_SCRYPTED, st->scrypted_url, &len); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) goto done; + + uint32_t u32 = 0; + err = nvs_get_u32(h, K_IDLE_MS, &u32); + if (err == ESP_OK) st->idle_timeout_ms = u32; + else if (err != ESP_ERR_NVS_NOT_FOUND) goto done; + + uint8_t u8 = 0; + err = nvs_get_u8(h, K_ORIENT, &u8); + if (err == ESP_OK) { + st->orientation = (u8 == 1) ? VIEWPORT_ORIENTATION_LANDSCAPE + : VIEWPORT_ORIENTATION_PORTRAIT; + } else if (err != ESP_ERR_NVS_NOT_FOUND) { + goto done; + } + + err = nvs_get_u8(h, K_BRIGHT, &u8); + if (err == ESP_OK) st->brightness = u8; + else if (err != ESP_ERR_NVS_NOT_FOUND) goto done; + + err = ESP_OK; + + // A device is "configured" only once both name and Scrypted URL are set. + if (st->viewport_name[0] && st->scrypted_url[0]) { + st->configured = true; + st->state = VIEWPORT_STATE_ASLEEP; // configured devices boot asleep + ESP_LOGI(TAG, "loaded config: viewport=%s scrypted=%s " + "idle_ms=%u orient=%s bright=%u", + st->viewport_name, st->scrypted_url, + (unsigned)st->idle_timeout_ms, + st->orientation == VIEWPORT_ORIENTATION_LANDSCAPE + ? "landscape" : "portrait", + st->brightness); + } else { + ESP_LOGI(TAG, "partial config in NVS — staying unconfigured"); + } + +done: + viewport_state_unlock(); + nvs_close(h); + return err; +} + +esp_err_t nvs_config_save(void) +{ + nvs_handle_t h; + esp_err_t err = nvs_open(NS, NVS_READWRITE, &h); + if (err != ESP_OK) return err; + + viewport_state_lock(); + viewport_state_t *st = viewport_state_get(); + + if ((err = nvs_set_str(h, K_VIEWPORT, st->viewport_name)) != ESP_OK) goto done; + if ((err = nvs_set_str(h, K_SCRYPTED, st->scrypted_url)) != ESP_OK) goto done; + if ((err = nvs_set_u32(h, K_IDLE_MS, st->idle_timeout_ms)) != ESP_OK) goto done; + if ((err = nvs_set_u8 (h, K_ORIENT, + (st->orientation == VIEWPORT_ORIENTATION_LANDSCAPE) + ? 1 : 0)) != ESP_OK) goto done; + if ((err = nvs_set_u8 (h, K_BRIGHT, st->brightness)) != ESP_OK) goto done; + + err = nvs_commit(h); + +done: + viewport_state_unlock(); + nvs_close(h); + return err; +} + +esp_err_t nvs_config_reset(void) +{ + nvs_handle_t h; + esp_err_t err = nvs_open(NS, NVS_READWRITE, &h); + if (err == ESP_ERR_NVS_NOT_FOUND) return ESP_OK; + if (err != ESP_OK) return err; + + nvs_erase_all(h); + err = nvs_commit(h); + nvs_close(h); + ESP_LOGI(TAG, "NVS config cleared"); + return err; +} |
