From 6e84d57b771985880763fe778fc7b8233c0dd7a2 Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Sat, 23 Aug 2025 16:02:21 +0200 Subject: [PATCH] starting status WLEDs behavior Signed-off-by: Peter Siegmund --- firmware/.gitignore | 2 +- firmware/.vscode/launch.json | 15 ++ firmware/Makefile | 2 + firmware/components/insa/CMakeLists.txt | 2 +- .../components/led-manager/CMakeLists.txt | 1 + .../led-manager/include/led_status.h | 50 +++++- .../led-manager/src/hal_esp32/led_manager.cpp | 42 ++--- .../led-manager/src/hal_esp32/led_status.cpp | 144 +++++++++++++++++- firmware/main/main.cpp | 18 +++ 9 files changed, 252 insertions(+), 24 deletions(-) create mode 100644 firmware/.vscode/launch.json create mode 100644 firmware/Makefile diff --git a/firmware/.gitignore b/firmware/.gitignore index d6c6d81..c6be05b 100644 --- a/firmware/.gitignore +++ b/firmware/.gitignore @@ -1,5 +1,6 @@ # esp-idf build/ +build-release/ managed_components/ sdkconfig sdkconfig.old @@ -16,7 +17,6 @@ cmake_install.cmake install_manifest.txt CMakeFiles/ CTestTestfile.cmake -Makefile CMakeScripts compile_commands.json diff --git a/firmware/.vscode/launch.json b/firmware/.vscode/launch.json new file mode 100644 index 0000000..2511a38 --- /dev/null +++ b/firmware/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "gdbtarget", + "request": "attach", + "name": "Eclipse CDT GDB Adapter" + }, + { + "type": "espidf", + "name": "Launch", + "request": "launch" + } + ] +} \ No newline at end of file diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 0000000..26d1f9a --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,2 @@ +release: + idf.py -B build-release -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release" fullclean build diff --git a/firmware/components/insa/CMakeLists.txt b/firmware/components/insa/CMakeLists.txt index 162e1c6..3e41cba 100644 --- a/firmware/components/insa/CMakeLists.txt +++ b/firmware/components/insa/CMakeLists.txt @@ -1,4 +1,4 @@ -# Definiere die Quelldateien in einer Variable +# Define the source files in a variable set(SOURCE_FILES src/common/InactivityTracker.cpp src/common/Menu.cpp diff --git a/firmware/components/led-manager/CMakeLists.txt b/firmware/components/led-manager/CMakeLists.txt index 1a35a67..209fe2f 100644 --- a/firmware/components/led-manager/CMakeLists.txt +++ b/firmware/components/led-manager/CMakeLists.txt @@ -6,6 +6,7 @@ if (DEFINED ENV{IDF_PATH}) PRIV_REQUIRES u8g2 esp_event + esp_timer persistence-manager ) return() diff --git a/firmware/components/led-manager/include/led_status.h b/firmware/components/led-manager/include/led_status.h index 17b6712..4d8061e 100644 --- a/firmware/components/led-manager/include/led_status.h +++ b/firmware/components/led-manager/include/led_status.h @@ -1,3 +1,51 @@ #pragma once -void led_status_init(); \ No newline at end of file +#include "driver/rmt_tx.h" +#include + +// Number of LEDs to be controlled +#define STATUS_LED_COUNT 3 + +// Possible lighting modes +typedef enum +{ + LED_MODE_OFF, + LED_MODE_SOLID, + LED_MODE_BLINK +} led_mode_t; + +// Structure for an RGB color +typedef struct +{ + uint8_t r; + uint8_t g; + uint8_t b; +} rgb_t; + +// This is the structure you pass from the outside to define a behavior +typedef struct +{ + led_mode_t mode; + rgb_t color; + uint32_t on_time_ms = 0; // Only relevant for BLINK + uint32_t off_time_ms = 0; // Only relevant for BLINK +} led_behavior_t; + +/** + * @brief Initializes the status manager and the LED strip. + * + * @param gpio_num GPIO where the data line of the LEDs is connected. + * @return esp_err_t ESP_OK on success. + */ +esp_err_t led_status_init(int gpio_num); + +/** + * @brief Sets the lighting behavior for a single LED. + * + * This function is thread-safe. + * + * @param index Index of the LED (0 to STATUS_LED_COUNT - 1). + * @param behavior The structure with the desired behavior. + * @return esp_err_t ESP_OK on success, ESP_ERR_INVALID_ARG on invalid index. + */ +esp_err_t led_status_set_behavior(uint8_t index, led_behavior_t behavior); diff --git a/firmware/components/led-manager/src/hal_esp32/led_manager.cpp b/firmware/components/led-manager/src/hal_esp32/led_manager.cpp index a684eef..13eb6be 100644 --- a/firmware/components/led-manager/src/hal_esp32/led_manager.cpp +++ b/firmware/components/led-manager/src/hal_esp32/led_manager.cpp @@ -18,20 +18,26 @@ const char *TAG = "LED"; uint64_t wled_init(void) { - led_strip_config_t strip_config = {.strip_gpio_num = CONFIG_WLED_DIN_PIN, - .max_leds = 64, - .led_model = LED_MODEL_WS2812, - .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, - .flags = { - .invert_out = false, - }}; + led_strip_config_t strip_config = { + .strip_gpio_num = CONFIG_WLED_DIN_PIN, + .max_leds = 64, + .led_model = LED_MODEL_WS2812, + .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, + .flags = + { + .invert_out = false, + }, + }; - led_strip_rmt_config_t rmt_config = {.clk_src = RMT_CLK_SRC_DEFAULT, - .resolution_hz = 0, - .mem_block_symbols = 0, - .flags = { - .with_dma = true, - }}; + led_strip_rmt_config_t rmt_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .resolution_hz = 0, + .mem_block_symbols = 0, + .flags = + { + .with_dma = true, + }, + }; ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); @@ -74,12 +80,12 @@ uint64_t send_event(uint32_t event, led_event_data_t *payload) return ESP_ERR_INVALID_ARG; } - esp_err_t err = esp_event_post_to(loop_handle, // Event-Loop Handle - LED_EVENTS_BASE, // Event Base + esp_err_t err = esp_event_post_to(loop_handle, // Event loop handle + LED_EVENTS_BASE, // Event base event, // Event ID (EVENT_LED_ON, EVENT_LED_OFF, etc.) - payload, // Daten-Pointer - sizeof(led_event_data_t), // Datengröße - portMAX_DELAY // Wartezeit + payload, // Data pointer + sizeof(led_event_data_t), // Data size + portMAX_DELAY // Wait time ); if (err != ESP_OK) diff --git a/firmware/components/led-manager/src/hal_esp32/led_status.cpp b/firmware/components/led-manager/src/hal_esp32/led_status.cpp index 9edcbbb..8e0ea29 100644 --- a/firmware/components/led-manager/src/hal_esp32/led_status.cpp +++ b/firmware/components/led-manager/src/hal_esp32/led_status.cpp @@ -1,7 +1,145 @@ #include "led_status.h" -void led_status_init() +#include "esp_log.h" +#include "esp_timer.h" // For high-resolution timestamps +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#include "led_strip.h" + +static const char *TAG = "LED_STATUS"; + +// Internal control structure for each LED +typedef struct { - // This function is intentionally left empty for the native implementation. - // It serves as a placeholder to maintain compatibility with the HAL interface. + led_behavior_t behavior; // The desired behavior (target state) + uint64_t last_toggle_time_us; // Timestamp of the last toggle (in microseconds) + bool is_on_in_blink; // Current state in blink mode (actual state) +} led_control_t; + +// --- Module variables --- +static led_strip_handle_t led_strip; +static led_control_t led_controls[STATUS_LED_COUNT]; +static SemaphoreHandle_t mutex; // To protect the led_controls array + +// The core: The task that controls the LEDs +static void led_status_task(void *pvParameters) +{ + ESP_LOGI(TAG, "Led Status Task started."); + + while (1) + { + uint64_t now_us = esp_timer_get_time(); + + // Take the mutex to safely access the control data + if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE) + { + + for (int i = 0; i < STATUS_LED_COUNT; i++) + { + led_control_t *control = &led_controls[i]; + + switch (control->behavior.mode) + { + case LED_MODE_OFF: + led_strip_set_pixel(led_strip, i, 0, 0, 0); + break; + + case LED_MODE_SOLID: + led_strip_set_pixel(led_strip, i, control->behavior.color.r, control->behavior.color.g, + control->behavior.color.b); + break; + + case LED_MODE_BLINK: { + uint32_t duration_ms = + control->is_on_in_blink ? control->behavior.on_time_ms : control->behavior.off_time_ms; + if ((now_us - control->last_toggle_time_us) / 1000 >= duration_ms) + { + control->is_on_in_blink = !control->is_on_in_blink; // Toggle state + control->last_toggle_time_us = now_us; // Update timestamp + } + + if (control->is_on_in_blink) + { + led_strip_set_pixel(led_strip, i, control->behavior.color.r, control->behavior.color.g, + control->behavior.color.b); + } + else + { + led_strip_set_pixel(led_strip, i, 0, 0, 0); + } + } + break; + } + } + // Release the mutex + xSemaphoreGive(mutex); + } + + // Update the physical LED strip with the new values + led_strip_refresh(led_strip); + + // Delay task for 20ms before the next iteration + vTaskDelay(pdMS_TO_TICKS(20)); + } +} + +// Initialization function +esp_err_t led_status_init(int gpio_num) +{ + // LED strip configuration (e.g., for WS2812) + led_strip_config_t strip_config = { + .strip_gpio_num = gpio_num, + .max_leds = STATUS_LED_COUNT, + .led_model = LED_MODEL_WS2812, + .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRBW, + .flags = + { + .invert_out = false, + }, + }; + led_strip_rmt_config_t rmt_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .resolution_hz = 10 * 1000 * 1000, // 10MHz + .mem_block_symbols = 0, + .flags = + { + .with_dma = false, + }, + }; + ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); + ESP_LOGI(TAG, "LED strip initialized."); + + // Create mutex + mutex = xSemaphoreCreateMutex(); + if (mutex == NULL) + { + ESP_LOGE(TAG, "Could not create mutex."); + return ESP_FAIL; + } + + // Start task + xTaskCreate(led_status_task, "led_status_task", 2048, NULL, 5, NULL); + + return ESP_OK; +} + +// Function to set the behavior +esp_err_t led_status_set_behavior(uint8_t index, led_behavior_t behavior) +{ + if (index >= STATUS_LED_COUNT) + { + return ESP_ERR_INVALID_ARG; + } + + if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE) + { + led_controls[index].behavior = behavior; + // Reset internal state variables to start the new pattern cleanly + led_controls[index].is_on_in_blink = false; + led_controls[index].last_toggle_time_us = esp_timer_get_time(); + xSemaphoreGive(mutex); + } + + return ESP_OK; } diff --git a/firmware/main/main.cpp b/firmware/main/main.cpp index 71b47e2..a16e5d4 100644 --- a/firmware/main/main.cpp +++ b/firmware/main/main.cpp @@ -1,7 +1,9 @@ #include "app_task.h" #include "esp_event.h" +#include "esp_log.h" #include "freertos/FreeRTOS.h" #include "led_manager.h" +#include "led_status.h" #include "sdkconfig.h" #ifdef __cplusplus @@ -10,6 +12,22 @@ extern "C" #endif void app_main(void) { + led_status_init(CONFIG_STATUS_WLED_PIN); + + // LED 0: solid red + led_behavior_t led0_solid_red = {.mode = LED_MODE_SOLID, .color = {.r = 50, .g = 0, .b = 0}}; + led_status_set_behavior(0, led0_solid_red); + + // LED 1: fast blinking green (200ms an, 200ms aus) + led_behavior_t led1_blink_green = { + .mode = LED_MODE_BLINK, .color = {.r = 0, .g = 50, .b = 0}, .on_time_ms = 200, .off_time_ms = 200}; + led_status_set_behavior(1, led1_blink_green); + + // LED 2: slow blinking blue (1000ms an, 500ms aus) + led_behavior_t led2_blink_blue = { + .mode = LED_MODE_BLINK, .color = {.r = 0, .g = 0, .b = 50}, .on_time_ms = 1000, .off_time_ms = 500}; + led_status_set_behavior(2, led2_blink_blue); + xTaskCreatePinnedToCore(app_task, "main_loop", 4096, NULL, tskIDLE_PRIORITY + 1, NULL, portNUM_PROCESSORS - 1); wled_init();