From df50aaeddadaaa02263c7fd71008212416cd14de Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Wed, 21 Jan 2026 21:53:03 +0100 Subject: [PATCH] get/post led segments Signed-off-by: Peter Siegmund --- .../components/api-server/src/api_handlers.c | 115 +++++++++++++++--- .../led-manager/include/led_segment.h | 16 +++ .../include/persistence_manager.h | 27 ++++ .../src/persistence_manager.c | 29 ++++- 4 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 firmware/components/led-manager/include/led_segment.h diff --git a/firmware/components/api-server/src/api_handlers.c b/firmware/components/api-server/src/api_handlers.c index 19707e0..d45cf1a 100644 --- a/firmware/components/api-server/src/api_handlers.c +++ b/firmware/components/api-server/src/api_handlers.c @@ -2,14 +2,17 @@ #include "common.h" #include "message_manager.h" +#include "led_segment.h" +#include "persistence_manager.h" #include #include #include #include -#include #include #include +#define MAX_BODY_SIZE 4096 + static const char *TAG = "api_handlers"; // Helper function to set CORS headers @@ -400,31 +403,113 @@ esp_err_t api_wled_config_get_handler(httpd_req_t *req) { ESP_LOGI(TAG, "GET /api/wled/config"); - // TODO: Implement actual LED config retrieval - const char *response = "{" - "\"segments\":[" - "{\"name\":\"Main Light\",\"start\":0,\"leds\":60}," - "{\"name\":\"Accent Light\",\"start\":60,\"leds\":30}" - "]" - "}"; - return send_json_response(req, response); + extern led_segment_t segments[LED_SEGMENT_MAX_LEN]; + extern size_t segment_count; + size_t required_size = sizeof(segments) * segment_count; + + cJSON *json = cJSON_CreateObject(); + + persistence_manager_t pm; + if (persistence_manager_init(&pm, "led_config") == ESP_OK) + { + persistence_manager_get_blob(&pm, "segments", segments, required_size, NULL); + uint8_t segment_count = persistence_manager_get_int(&pm, "segment_count", 0); + persistence_manager_deinit(&pm); + + cJSON *segments_arr = cJSON_CreateArray(); + for (uint8_t i = 0; i < segment_count; ++i) + { + cJSON *seg = cJSON_CreateObject(); + cJSON_AddStringToObject(seg, "name", segments[i].name); + cJSON_AddNumberToObject(seg, "start", segments[i].start); + cJSON_AddNumberToObject(seg, "leds", segments[i].leds); + cJSON_AddItemToArray(segments_arr, seg); + } + cJSON_AddItemToObject(json, "segments", segments_arr); + } + else + { + cJSON_AddItemToObject(json, "segments", cJSON_CreateArray()); + } + + char *response = cJSON_PrintUnformatted(json); + cJSON_Delete(json); + esp_err_t res = send_json_response(req, response); + free(response); + return res; } esp_err_t api_wled_config_post_handler(httpd_req_t *req) { ESP_LOGI(TAG, "POST /api/wled/config"); - char buf[512]; - int ret = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (ret <= 0) + char *buf = malloc(MAX_BODY_SIZE); + if (!buf) + return send_error_response(req, 500, "Memory allocation failed"); + int total = 0, ret; + while (total < MAX_BODY_SIZE - 1) { - return send_error_response(req, 400, "Failed to receive request body"); + ret = httpd_req_recv(req, buf + total, MAX_BODY_SIZE - 1 - total); + if (ret <= 0) + break; + total += ret; } - buf[ret] = '\0'; + buf[total] = '\0'; ESP_LOGI(TAG, "Received WLED config: %s", buf); - // TODO: Parse JSON and save LED configuration + cJSON *json = cJSON_Parse(buf); + free(buf); + + if (!json) + { + return send_error_response(req, 400, "Invalid JSON"); + } + + cJSON *segments_arr = cJSON_GetObjectItem(json, "segments"); + if (!cJSON_IsArray(segments_arr)) + { + cJSON_Delete(json); + return send_error_response(req, 400, "Missing segments array"); + } + + extern led_segment_t segments[LED_SEGMENT_MAX_LEN]; + extern size_t segment_count; + size_t count = cJSON_GetArraySize(segments_arr); + if (count > LED_SEGMENT_MAX_LEN) + count = LED_SEGMENT_MAX_LEN; + segment_count = count; + for (size_t i = 0; i < LED_SEGMENT_MAX_LEN; ++i) + { + cJSON *seg = cJSON_GetArrayItem(segments_arr, i); + cJSON *name = cJSON_GetObjectItem(seg, "name"); + cJSON *start = cJSON_GetObjectItem(seg, "start"); + cJSON *leds = cJSON_GetObjectItem(seg, "leds"); + if (cJSON_IsString(name) && cJSON_IsNumber(start) && cJSON_IsNumber(leds) && i < count) + { + strncpy(segments[i].name, name->valuestring, sizeof(segments[i].name) - 1); + segments[i].name[sizeof(segments[i].name) - 1] = '\0'; + segments[i].start = (uint16_t)start->valuedouble; + segments[i].leds = (uint16_t)leds->valuedouble; + } + else + { + // Invalid entry, skip or set defaults + segments[i].name[0] = '\0'; + segments[i].start = 0; + segments[i].leds = 0; + } + } + cJSON_Delete(json); + + persistence_manager_t pm; + if (persistence_manager_init(&pm, "led_config") == ESP_OK) + { + persistence_manager_set_blob(&pm, "segments", segments, sizeof(led_segment_t) * segment_count); + persistence_manager_set_int(&pm, "segment_count", (int32_t)segment_count); + persistence_manager_deinit(&pm); + } + set_cors_headers(req); return httpd_resp_sendstr(req, "{\"status\":\"ok\"}"); } diff --git a/firmware/components/led-manager/include/led_segment.h b/firmware/components/led-manager/include/led_segment.h new file mode 100644 index 0000000..8a1af7c --- /dev/null +++ b/firmware/components/led-manager/include/led_segment.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#define LED_SEGMENT_MAX_LEN 15 + +typedef struct +{ + char name[32]; + uint16_t start; + uint16_t leds; +} led_segment_t; + +led_segment_t segments[LED_SEGMENT_MAX_LEN]; +size_t segment_count; diff --git a/firmware/components/persistence-manager/include/persistence_manager.h b/firmware/components/persistence-manager/include/persistence_manager.h index 4ddf322..140952f 100644 --- a/firmware/components/persistence-manager/include/persistence_manager.h +++ b/firmware/components/persistence-manager/include/persistence_manager.h @@ -205,6 +205,33 @@ extern "C" void persistence_manager_get_string(const persistence_manager_t *pm, const char *key, char *out_value, size_t max_len, const char *default_value); + /** + * @brief Set a blob (binary data) value for a key in NVS storage. + * + * This function stores arbitrary binary data under the given key. + * + * @param pm Pointer to the persistence manager structure. + * @param key Key to set. + * @param value Pointer to the data to store. + * @param length Length of the data in bytes. + */ + void persistence_manager_set_blob(persistence_manager_t *pm, const char *key, const void *value, size_t length); + + /** + * @brief Get a blob (binary data) value for a key from NVS storage. + * + * This function retrieves binary data previously stored under the given key. + * + * @param pm Pointer to the persistence manager structure. + * @param key Key to retrieve. + * @param out_value Buffer to store the retrieved data. + * @param max_length Maximum length of the output buffer in bytes. + * @param out_length Pointer to variable to receive the actual data length. + * @return true if the blob was found and read successfully, false otherwise. + */ + bool persistence_manager_get_blob(const persistence_manager_t *pm, const char *key, void *out_value, + size_t max_length, size_t *out_length); + #ifdef __cplusplus } #endif diff --git a/firmware/components/persistence-manager/src/persistence_manager.c b/firmware/components/persistence-manager/src/persistence_manager.c index 41260a7..51b8b14 100644 --- a/firmware/components/persistence-manager/src/persistence_manager.c +++ b/firmware/components/persistence-manager/src/persistence_manager.c @@ -1,4 +1,3 @@ - #include "persistence_manager.h" #include #include @@ -174,6 +173,17 @@ void persistence_manager_set_string(persistence_manager_t *pm, const char *key, } } +void persistence_manager_set_blob(persistence_manager_t *pm, const char *key, const void *value, size_t length) +{ + if (!persistence_manager_is_initialized(pm) || !value || length == 0) + return; + esp_err_t err = nvs_set_blob(pm->nvs_handle, key, value, length); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Failed to set blob key '%s': %s", key, esp_err_to_name(err)); + } +} + bool persistence_manager_get_bool(const persistence_manager_t *pm, const char *key, bool default_value) { if (!persistence_manager_is_initialized(pm)) @@ -245,3 +255,20 @@ void persistence_manager_get_string(const persistence_manager_t *pm, const char return; } } + +bool persistence_manager_get_blob(const persistence_manager_t *pm, const char *key, void *out_value, size_t max_length, + size_t *out_length) +{ + if (!persistence_manager_is_initialized(pm) || !out_value || max_length == 0) + return false; + size_t required_size = 0; + esp_err_t err = nvs_get_blob(pm->nvs_handle, key, NULL, &required_size); + if (err != ESP_OK || required_size == 0 || required_size > max_length) + return false; + err = nvs_get_blob(pm->nvs_handle, key, out_value, &required_size); + if (err != ESP_OK) + return false; + if (out_length) + *out_length = required_size; + return true; +}