aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-12-07 00:48:44 -0700
committerJosh Rahm <joshuarahm@gmail.com>2025-12-07 00:48:44 -0700
commit3f8b6e6f34275bdba501e3b1cddab43331b6f569 (patch)
tree240abbebd3ed62565325626ad47887a9d78ee96d
parent8c6fd7fd39a17216b1a12601a1ec943d557040c2 (diff)
downloadesp32-ws2812b-3f8b6e6f34275bdba501e3b1cddab43331b6f569.tar.gz
esp32-ws2812b-3f8b6e6f34275bdba501e3b1cddab43331b6f569.tar.bz2
esp32-ws2812b-3f8b6e6f34275bdba501e3b1cddab43331b6f569.zip
Add persistence to keep the state params the same across reboots.
-rw-r--r--include/ws2812b_writer.h3
-rw-r--r--main/CMakeLists.txt12
-rw-r--r--main/http_server.c1
-rw-r--r--main/main.c11
-rw-r--r--main/state_store.c59
-rw-r--r--main/state_version.h.in3
-rw-r--r--main/tcp_server.c2
-rw-r--r--main/ws2812b_writer.c1
8 files changed, 90 insertions, 2 deletions
diff --git a/include/ws2812b_writer.h b/include/ws2812b_writer.h
index ac4eb05..b750da6 100644
--- a/include/ws2812b_writer.h
+++ b/include/ws2812b_writer.h
@@ -3,6 +3,7 @@
#define WS2812B_WRITER_H_
#include "drv/ws2812b.h"
+#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
@@ -24,6 +25,8 @@ typedef struct {
} ws_params_t;
void reset_parameters(ws_params_t* params);
+esp_err_t ws_state_save(const ws_state_t* state);
+esp_err_t ws_state_load(ws_state_t* state);
portTASK_FUNCTION_PROTO(ws2812b_write_task, params);
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index c595f30..4f7b302 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -3,7 +3,17 @@ file(GLOB_RECURSE MAIN_SRCS "${CMAKE_CURRENT_LIST_DIR}/*.c")
# CMakeLists and registration.
list(FILTER MAIN_SRCS EXCLUDE REGEX ".*/drv/.*")
+# Derive a schema hash from the parameter definition so persisted state
+# automatically invalidates when the schema changes.
+set(STATE_PARAMS_FILE "${CMAKE_CURRENT_LIST_DIR}/../include/state_params.i")
+file(SHA1 "${STATE_PARAMS_FILE}" STATE_SCHEMA_SHA1)
+string(SUBSTRING "${STATE_SCHEMA_SHA1}" 0 8 STATE_SCHEMA_HASH)
+configure_file(
+ "${CMAKE_CURRENT_LIST_DIR}/state_version.h.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/state_version.h"
+ @ONLY)
+
idf_component_register(
SRCS ${MAIN_SRCS}
- INCLUDE_DIRS "../include"
+ INCLUDE_DIRS "../include" "${CMAKE_CURRENT_BINARY_DIR}"
REQUIRES esp_driver_spi spi_flash esp_http_server esp_wifi nvs_flash)
diff --git a/main/http_server.c b/main/http_server.c
index aabd9fb..ec8a8aa 100644
--- a/main/http_server.c
+++ b/main/http_server.c
@@ -145,6 +145,7 @@ static void handle_set_color_int_t(httpd_req_t* req, color_int_t* val)
return ESP_FAIL; \
} \
handle_set_##typ(req, &params->state.attr); \
+ ws_state_save(&params->state); \
xSemaphoreGive(params->state_lock); \
httpd_resp_send(req, NULL, 0); \
return ESP_OK; \
diff --git a/main/main.c b/main/main.c
index 88f6994..c57c414 100644
--- a/main/main.c
+++ b/main/main.c
@@ -108,6 +108,7 @@ void app_main(void)
ws_params.drv = ws2812b_init(spi);
ws_params.state_lock = xSemaphoreCreateMutex();
configASSERT(ws_params.state_lock != NULL);
+ reset_parameters(&ws_params);
printf("Configuration complete!!\n");
@@ -115,6 +116,16 @@ void app_main(void)
wifi_init_station("Wort", "JoshIsBau5");
ESP_LOGI("main", "Complete!");
+ if (xSemaphoreTake(ws_params.state_lock, portMAX_DELAY) == pdTRUE) {
+ esp_err_t load_err = ws_state_load(&ws_params.state);
+ if (load_err == ESP_OK) {
+ ESP_LOGI("main", "Restored state from NVS");
+ } else {
+ ESP_LOGI("main", "No saved state (%d); using defaults", load_err);
+ }
+ xSemaphoreGive(ws_params.state_lock);
+ }
+
xTaskCreate(
time_keeper,
"time_keeper",
diff --git a/main/state_store.c b/main/state_store.c
new file mode 100644
index 0000000..c764e19
--- /dev/null
+++ b/main/state_store.c
@@ -0,0 +1,59 @@
+#include "ws2812b_writer.h"
+
+#include "esp_log.h"
+#include "nvs_flash.h"
+#include "state_version.h"
+
+static const char* TAG = "state_store";
+static const char* NAMESPACE = "ws";
+static const char* KEY = "state";
+
+typedef struct {
+ uint32_t version;
+ ws_state_t state;
+} ws_state_blob_t;
+
+esp_err_t ws_state_save(const ws_state_t* state)
+{
+ nvs_handle_t h;
+ esp_err_t err = nvs_open(NAMESPACE, NVS_READWRITE, &h);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "nvs_open failed: %d", err);
+ return err;
+ }
+
+ ws_state_blob_t blob = {.version = WS_STATE_SCHEMA_HASH, .state = *state};
+ err = nvs_set_blob(h, KEY, &blob, sizeof(blob));
+ if (err == ESP_OK) {
+ err = nvs_commit(h);
+ }
+ nvs_close(h);
+
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "nvs_set_blob/commit failed: %d", err);
+ }
+ return err;
+}
+
+esp_err_t ws_state_load(ws_state_t* out_state)
+{
+ nvs_handle_t h;
+ esp_err_t err = nvs_open(NAMESPACE, NVS_READONLY, &h);
+ if (err != ESP_OK) {
+ return err;
+ }
+
+ ws_state_blob_t blob;
+ size_t len = sizeof(blob);
+ err = nvs_get_blob(h, KEY, &blob, &len);
+ nvs_close(h);
+ if (err != ESP_OK) {
+ return err;
+ }
+ if (len != sizeof(blob) || blob.version != WS_STATE_SCHEMA_HASH) {
+ return ESP_ERR_INVALID_VERSION;
+ }
+
+ *out_state = blob.state;
+ return ESP_OK;
+}
diff --git a/main/state_version.h.in b/main/state_version.h.in
new file mode 100644
index 0000000..42e0874
--- /dev/null
+++ b/main/state_version.h.in
@@ -0,0 +1,3 @@
+#pragma once
+// Generated at build time from include/state_params.i
+#define WS_STATE_SCHEMA_HASH 0x@STATE_SCHEMA_HASH@u
diff --git a/main/tcp_server.c b/main/tcp_server.c
index 237ed97..a8c4db9 100644
--- a/main/tcp_server.c
+++ b/main/tcp_server.c
@@ -164,6 +164,8 @@ static void handle_set_cmd(
sockbuf_write(sockbuf, "Unknown attribute: '");
sockbuf_write(sockbuf, key);
sockbuf_write(sockbuf, "'\n");
+ } else {
+ ws_state_save(&ws_params->state);
}
xSemaphoreGive(ws_params->state_lock);
diff --git a/main/ws2812b_writer.c b/main/ws2812b_writer.c
index 8bf0d91..50d89b5 100644
--- a/main/ws2812b_writer.c
+++ b/main/ws2812b_writer.c
@@ -122,7 +122,6 @@ void calculate_colors(const ws_state_t* state, ws2812b_buffer_t* buffer)
portTASK_FUNCTION(ws2812b_write_task, params)
{
ws_params_t* ws_params = (ws_params_t*)params;
- reset_parameters(ws_params);
ws2812b_buffer_t* buffer = ws2812b_new_buffer(SIZE);