From 3d7417e83789539a3cafcf9c0f1ea4a35794fa59 Mon Sep 17 00:00:00 2001 From: Clawdia Date: Wed, 9 Sep 2026 17:17:28 +0200 Subject: [PATCH] Migrate to Arduino: NeoPixel 7-phase relaxation (10/8/5/2/1/4/7 Hz) --- led-laser-frequenzen.ino | 224 --------------------------------------- led_entspannung.ino | 86 +++++++++++++++ platformio.ini | 7 +- src/CMakeLists.txt | 6 -- src/led_strip_encoder.c | 126 ---------------------- src/led_strip_encoder.h | 36 ------- src/main.c | 123 --------------------- 7 files changed, 91 insertions(+), 517 deletions(-) delete mode 100644 led-laser-frequenzen.ino create mode 100644 led_entspannung.ino delete mode 100644 src/CMakeLists.txt delete mode 100644 src/led_strip_encoder.c delete mode 100644 src/led_strip_encoder.h delete mode 100644 src/main.c diff --git a/led-laser-frequenzen.ino b/led-laser-frequenzen.ino deleted file mode 100644 index 7623142..0000000 --- a/led-laser-frequenzen.ino +++ /dev/null @@ -1,224 +0,0 @@ -/* - * LED-Laser-Frequenzsteuerung für Rotlichttherapie - * Ziel-Hardware: ESP32-C6 mit WS2812B LED-Streifen (30 LEDs) - * - * Funktion: Steuert 30 WS2812B-LEDs über verschiedene Frequenz-Presets - * die Rotlichttherapie nachbilden. Jedes Preset definiert Farbe, Helligkeit - * und Pulsfrequenz für photobiostimulation. - */ - -#include - -// ===== KONFIGURATION ===== -#define LED_PIN GPIO6 // Datenleitung zum WS2812B-Streifen -#define NUM_LEDS 30 // Anzahl LEDs im Streifen -#define DATA_RESISTOR 330 // Vorwiderstand in Ohm an der Datenleitung - -// ===== FREQUENZ-PRESETS ===== -// Jeder Preset definiert: Name, Grundfrequenz (Hz), Farbe (R,G,B), Helligkeit (0-255) -struct LaserPreset { - const char* name; - float frequencyHz; // Puls-Frequenz in Hz - uint8_t colorR; - uint8_t colorG; - uint8_t colorB; - uint8_t brightness; // 0-255, 255 = maximal - uint32_t durationMs; // Wie lange das Preset läuft (0 = unbegrenzt) -}; - -// Therapeutische Frequenzen für Rotlichttherapie (Peptide / Laser Therapy) -// Diese Frequenzen sind typisch für die Behandlung von Gewebe/Regeneration -const LaserPreset presets[] = { - // [0] Niedrigfrequenz - Entspannung & Regeneration - {"Regeneration", 40.0, 255, 15, 0, 200, 60000}, - - // [1] Mittelfrequenz - Durchblutung - {"Durchblutung", 60.0, 255, 30, 0, 255, 60000}, - - // [2] Hochfrequenz - Tiefenwirksam - {"Tiefenwirkung", 80.0, 255, 60, 0, 230, 45000}, - - // [3] Gepulst Rot - Geweberegeneration - {"Gepulst Rot", 50.0, 255, 0, 0, 180, 60000}, - - // [4] Warmes Rot-Orange - Oberflächliche Heilung - {"Oberflächenlicht", 30.0, 255, 80, 10, 210, 45000}, - - // [5] Infrarot-Nachbildung (tiefes Rot) - Muskeln & Gelenke - {"Muskeln/Gelenke", 65.0, 200, 10, 0, 240, 60000}, - - // [6] Wechselnd - Ganzkörperbehandlung - {"Ganzkörper", 45.0, 255, 0, 0, 190, 90000}, -}; - -#define NUM_PRESETS (sizeof(presets) / sizeof(presets[0])) - -// ===== GLOBAL STATE ===== -Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); - -uint8_t currentR = 0, currentG = 0, currentB = 0; -float currentBrightness = 0.0; -uint32_t pulsePhase = 0; -unsigned long lastPulseUpdate = 0; -unsigned long presetStartTime = 0; -int currentPresetIndex = -1; -bool running = false; - -// ===== SETUP ===== -void setup() { - Serial.begin(115200); - while (!Serial && millis() < 3000) {} - - strip.begin(); - strip.clear(); - strip.show(); - - // Starte mit erstem Preset - selectPreset(0); - - Serial.println("========================================"); - Serial.println(" LED-Laser-Frequenzsteuerung (ESP32-C6)"); - Serial.println(" WS2812B x 30 LEDs"); - Serial.println("========================================"); - printPresets(); - running = true; - - presetStartTime = millis(); -} - -// ===== LOOP ===== -void loop() { - if (!running) { - // Warte auf seriellen Befehl zum Neustart - if (Serial.available()) { - String cmd = Serial.readStringUntil('\n'); - handleCommand(cmd); - } - yield(); - return; - } - - const LaserPreset& preset = presets[currentPresetIndex]; - unsigned long now = millis(); - - // Prüfe ob Preset-Zeit abgelaufen (wenn durationMs > 0) - if (preset.durationMs > 0 && (now - presetStartTime) >= preset.durationMs) { - nextPreset(); - } - - // Berechne pulsende Helligkeit basierend auf Frequenz - calculatePulse(preset, now); - - // Update alle LEDs - uint32_t color = strip.Color( - (uint8_t)(currentR * currentBrightness / 255.0), - (uint8_t)(currentG * currentBrightness / 255.0), - (uint8_t)(currentB * currentBrightness / 255.0) - ); - - for (int i = 0; i < NUM_LEDS; i++) { - strip.setPixelColor(i, color); - } - strip.show(); - - // Seriellen Input prüfen für Befehle - if (Serial.available()) { - String cmd = Serial.readStringUntil('\n'); - handleCommand(cmd.trim()); - } - - yield(); -} - -// ===== PULSBERECHNUNG ===== -void calculatePulse(const LaserPreset& preset, unsigned long now) { - float dutyCycle = 0.5; // Standard: 50% Tastgrad - - // Sinusbasiertes Pulsieren für sanfte Übergänge - float periodMs = 1000.0 / preset.frequencyHz; - float phase = fmodf((float)(now % (uint32_t)periodMs), periodMs) / periodMs; - - // Sanfter An- und Abstieg - currentBrightness = (uint8_t)(preset.brightness * sinf(phase * 3.14159f)); -} - -// ===== PRESET SELEKTION ===== -void selectPreset(int index) { - if (index < 0 || index >= NUM_PRESETS) return; - - currentPresetIndex = index; - presetStartTime = millis(); - currentBrightness = 0.0; - - Serial.printf("\n>>> Preset: %s\n", presets[index].name); - Serial.printf(" Frequenz: %.1f Hz\n", presets[index].frequencyHz); - Serial.printf(" Farbe: R=%d, G=%d, B=%d\n", - presets[index].colorR, - presets[index].colorG, - presets[index].colorB); - Serial.printf(" Helligkeit: %d/255\n", presets[index].brightness); - if (presets[index].durationMs > 0) { - Serial.printf(" Dauer: %lu ms (%.1f min)\n", - presets[index].durationMs, - presets[index].durationMs / 60000.0); - } else { - Serial.println(" Dauer: unbegrenzt (⏹ zum Stoppen)"); - } -} - -void nextPreset() { - int next = (currentPresetIndex + 1) % NUM_PRESETS; - selectPreset(next); -} - -// ===== PRESETS DRUCKEN ===== -void printPresets() { - Serial.println("\nVerfügbare Presets:"); - for (int i = 0; i < NUM_PRESETS; i++) { - const char* durStr; - if (presets[i].durationMs > 0) { - durStr = ""; - } else { - durStr = " (⏹ stopp)"; - } - Serial.printf(" [%d] %s %.1fHz R:%d G:%d B:%d hell:%d - Zeit: %s\n", - i, - presets[i].name, - presets[i].frequencyHz, - presets[i].colorR, - presets[i].colorG, - presets[i].colorB, - presets[i].brightness, - durStr); - } -} - -// ===== SERIELLE BEFEHLE ===== -void handleCommand(String cmd) { - if (cmd.equalsIgnoreCase("stop")) { - running = false; - strip.clear(); - strip.show(); - Serial.println("LEDs ausgeschaltet. Serieller Befehl zum Neustart."); - return; - } - - if (cmd.equalsIgnoreCase("next")) { - nextPreset(); - return; - } - - if (cmd.equalsIgnoreCase("list") || cmd.equalsIgnoreCase("presets")) { - printPresets(); - return; - } - - // Number parsing: "0", "1", etc. → Preset-Index - int idx = cmd.toInt(); - if (idx >= 0 && idx < NUM_PRESETS) { - selectPreset(idx); - } else if (cmd.length() > 0 && !cmd[0].isdigit()) { - Serial.printf("Unbekannter Befehl: '%s'\n", cmd.c_str()); - Serial.println("Verfügbare Befehle: 0-6, stop, next, list"); - } -} diff --git a/led_entspannung.ino b/led_entspannung.ino new file mode 100644 index 0000000..8a5db96 --- /dev/null +++ b/led_entspannung.ino @@ -0,0 +1,86 @@ +/* + * led_entspannung.ino + * LED-Entspannungsablauf für XIAO ESP32-C6 + 30x WS2812B + * + * Pin: D10 (GPIO18) – wie im "Standard-Code" getestet und bestätigt + * Ablauf (auto-Start beim Einschalten): + * 1) Ankommen / Beta→Alpha 10 Hz 60 s (255, 80, 0) + * 2) Alpha-Entspannung 8 Hz 90 s (255, 40, 0) + * 3) Theta-Eintauchen 5 Hz 90 s (200, 0, 0) + * 4) Delta-Regeneration 2 Hz 120 s (180, 0, 0) + * 5) Delta-Tiefschlaf 1 Hz 120 s (150, 0, 0) + * 6) Theta-Aufstieg 4 Hz 90 s (220, 30, 0) + * 7) Alpha-Weckphase 7 Hz 90 s (255, 60, 0) + * Danach: alle LEDs aus, 3 s Pause, dann von vorn. + * + * Arduino-IDE: + * - Board: "Seeed XIAO ESP32C6" + * - Port: COMx (bzw. /dev/ttyACM0) + * - Bibliothek "Adafruit NeoPixel" muss installiert sein + * - Upload → fertig, es startet automatisch + */ + +#include + +#define LED_PIN 18 // D10 +#define NUM_LEDS 30 + +Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); + +struct Phase { + uint8_t r, g, b; + uint32_t freq_hz; + uint32_t duration_s; + const char *name; +}; + +static const Phase phases[] = { + { 255, 80, 0, 10, 60, "Ankommen / Beta-Alpha" }, + { 255, 40, 0, 8, 90, "Alpha-Entspannung" }, + { 200, 0, 0, 5, 90, "Theta-Eintauchen" }, + { 180, 0, 0, 2, 120, "Delta-Regeneration" }, + { 150, 0, 0, 1, 120, "Delta-Tiefschlaf" }, + { 220, 30, 0, 4, 90, "Theta-Aufstieg" }, + { 255, 60, 0, 7, 90, "Alpha-Weckphase" }, +}; +#define NUM_PHASES (sizeof(phases) / sizeof(phases[0])) + +void setup() { + Serial.begin(115200); + delay(500); + Serial.println("==== LED-Entspannungsablauf (7 Phasen) ===="); + Serial.printf("XIAO ESP32-C6 | D10 (GPIO%d) | %d LEDs\n", LED_PIN, NUM_LEDS); + + strip.begin(); + strip.setBrightness(255); + strip.show(); // alles aus + Serial.println("Strip initialisiert, Start in 1 s ..."); + delay(1000); +} + +void loop() { + for (int p = 0; p < NUM_PHASES; p++) { + const Phase &ph = phases[p]; + Serial.printf("Phase %d/%d: %s %d Hz %d s (%d,%d,%d)\n", + p + 1, NUM_PHASES, ph.name, ph.freq_hz, ph.duration_s, ph.r, ph.g, ph.b); + + uint32_t start = millis(); + while (millis() - start < ph.duration_s * 1000UL) { + uint32_t half_ms = (1000UL / ph.freq_hz) / 2; // 50 % ein / 50 % aus + + for (int i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, ph.r, ph.g, ph.b); + strip.show(); + delay(half_ms); + + strip.clear(); + strip.show(); + delay(half_ms); + } + } + + // Kurze Pause zwischen Durchläufen + Serial.println("Durchlauf beendet - 3 s Pause, dann neu"); + strip.clear(); + strip.show(); + delay(3000); +} diff --git a/platformio.ini b/platformio.ini index 3c38120..e68677e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,7 +1,10 @@ [env:xiao-esp32c6] platform = espressif32 board = seeed_xiao_esp32c6 -framework = espidf +framework = arduino monitor_speed = 115200 -board_build.flash_size = 2MB +board_build.flash_size = 4MB build_type = release + +lib_deps = + adafruit/Adafruit NeoPixel@^1.15.0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 483bc0c..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -# This file was automatically generated for projects -# without default 'CMakeLists.txt' file. - -FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) - -idf_component_register(SRCS ${app_sources}) diff --git a/src/led_strip_encoder.c b/src/led_strip_encoder.c deleted file mode 100644 index 243c427..0000000 --- a/src/led_strip_encoder.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "esp_check.h" -#include "led_strip_encoder.h" - -static const char *TAG = "led_encoder"; - -typedef struct { - rmt_encoder_t base; - rmt_encoder_t *bytes_encoder; - rmt_encoder_t *copy_encoder; - int state; - rmt_symbol_word_t reset_code; -} rmt_led_strip_encoder_t; - -RMT_ENCODER_FUNC_ATTR -static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) -{ - rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); - rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; - rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; - rmt_encode_state_t session_state = RMT_ENCODING_RESET; - rmt_encode_state_t state = RMT_ENCODING_RESET; - size_t encoded_symbols = 0; - switch (led_encoder->state) { - case 0: // send RGB data - encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state); - if (session_state & RMT_ENCODING_COMPLETE) { - led_encoder->state = 1; // switch to next state when current encoding session finished - } - if (session_state & RMT_ENCODING_MEM_FULL) { - state |= RMT_ENCODING_MEM_FULL; - goto out; // yield if there's no free space for encoding artifacts - } - // fall-through - case 1: // send reset code - encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code, - sizeof(led_encoder->reset_code), &session_state); - if (session_state & RMT_ENCODING_COMPLETE) { - led_encoder->state = RMT_ENCODING_RESET; // back to the initial encoding session - state |= RMT_ENCODING_COMPLETE; - } - if (session_state & RMT_ENCODING_MEM_FULL) { - state |= RMT_ENCODING_MEM_FULL; - goto out; // yield if there's no free space for encoding artifacts - } - } -out: - *ret_state = state; - return encoded_symbols; -} - -static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) -{ - rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); - rmt_del_encoder(led_encoder->bytes_encoder); - rmt_del_encoder(led_encoder->copy_encoder); - free(led_encoder); - return ESP_OK; -} - -RMT_ENCODER_FUNC_ATTR -static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) -{ - rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); - rmt_encoder_reset(led_encoder->bytes_encoder); - rmt_encoder_reset(led_encoder->copy_encoder); - led_encoder->state = RMT_ENCODING_RESET; - return ESP_OK; -} - -esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder) -{ - esp_err_t ret = ESP_OK; - rmt_led_strip_encoder_t *led_encoder = NULL; - ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - led_encoder = rmt_alloc_encoder_mem(sizeof(rmt_led_strip_encoder_t)); - ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder"); - led_encoder->base.encode = rmt_encode_led_strip; - led_encoder->base.del = rmt_del_led_strip_encoder; - led_encoder->base.reset = rmt_led_strip_encoder_reset; - // different led strip might have its own timing requirements, following parameter is for WS2812 - rmt_bytes_encoder_config_t bytes_encoder_config = { - .bit0 = { - .level0 = 1, - .duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us - .level1 = 0, - .duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us - }, - .bit1 = { - .level0 = 1, - .duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us - .level1 = 0, - .duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us - }, - .flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0 - }; - ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed"); - rmt_copy_encoder_config_t copy_encoder_config = {}; - ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed"); - - uint32_t reset_ticks = config->resolution / 1000000 * 50 / 2; // reset code duration defaults to 50us - led_encoder->reset_code = (rmt_symbol_word_t) { - .level0 = 0, - .duration0 = reset_ticks, - .level1 = 0, - .duration1 = reset_ticks, - }; - *ret_encoder = &led_encoder->base; - return ESP_OK; -err: - if (led_encoder) { - if (led_encoder->bytes_encoder) { - rmt_del_encoder(led_encoder->bytes_encoder); - } - if (led_encoder->copy_encoder) { - rmt_del_encoder(led_encoder->copy_encoder); - } - free(led_encoder); - } - return ret; -} diff --git a/src/led_strip_encoder.h b/src/led_strip_encoder.h deleted file mode 100644 index db5ef07..0000000 --- a/src/led_strip_encoder.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#pragma once - -#include -#include "driver/rmt_encoder.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Type of led strip encoder configuration - */ -typedef struct { - uint32_t resolution; /*!< Encoder resolution, in Hz */ -} led_strip_encoder_config_t; - -/** - * @brief Create RMT encoder for encoding LED strip pixels into RMT symbols - * - * @param[in] config Encoder configuration - * @param[out] ret_encoder Returned encoder handle - * @return - * - ESP_ERR_INVALID_ARG for any invalid arguments - * - ESP_ERR_NO_MEM out of memory when creating led strip encoder - * - ESP_OK if creating encoder successfully - */ -esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder); - -#ifdef __cplusplus -} -#endif diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 2c82164..0000000 --- a/src/main.c +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "esp_timer.h" -#include "driver/rmt_tx.h" -#include "led_strip_encoder.h" - -#define RMT_LED_STRIP_RESOLUTION_HZ 10000000 -#define LED_GPIO_NUM 18 -#define LED_NUMBERS 30 - -static const char *TAG = "led-laser"; - -static uint8_t led_pixels[LED_NUMBERS * 3]; -static rmt_channel_handle_t led_chan = NULL; -static rmt_encoder_handle_t led_encoder = NULL; - -typedef struct { - const char *name; - int freq_hz; - int duration_sec; - uint8_t r, g, b; -} preset_t; - -static const preset_t presets[] = { - {"Ankommen / Beta→Alpha", 10, 60, 255, 80, 0}, - {"Alpha-Entspannung", 8, 90, 255, 40, 0}, - {"Theta-Eintauchen", 5, 90, 200, 0, 0}, - {"Delta-Regeneration", 2,120, 180, 0, 0}, - {"Delta-Tiefschlaf", 1,120, 150, 0, 0}, - {"Theta-Aufstieg", 4, 90, 220, 30, 0}, - {"Alpha-Weckphase", 7, 90, 255, 60, 0} -}; -#define PRESET_COUNT (sizeof(presets)/sizeof(presets[0])) - -static int current_preset = -1; -static bool led_on = false; -static int64_t last_toggle_us = 0; -static int64_t phase_start_us = 0; - -static void set_all_pixels(uint8_t r, uint8_t g, uint8_t b){ - for(int i=0;i=PRESET_COUNT) return; - current_preset = idx; - const preset_t *p = &presets[idx]; - set_all_pixels(p->r,p->g,p->b); - led_on = true; - last_toggle_us = esp_timer_get_time(); - phase_start_us = esp_timer_get_time(); - ESP_LOGI(TAG,"Preset %d %s %dHz %ds", idx, p->name, p->freq_hz, p->duration_sec); -} - -static void stop_leds(void){ - memset(led_pixels,0,sizeof(led_pixels)); - flush_leds(); - current_preset = -1; -} - -void app_main(void){ - ESP_LOGI(TAG,"LED Laser Frequenz start"); - - rmt_tx_channel_config_t tx_cfg = { - .clk_src=RMT_CLK_SRC_DEFAULT, - .gpio_num=LED_GPIO_NUM, - .mem_block_symbols=64, - .resolution_hz=RMT_LED_STRIP_RESOLUTION_HZ, - .trans_queue_depth=4 - }; - ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_cfg,&led_chan)); - led_strip_encoder_config_t enc_cfg={.resolution=RMT_LED_STRIP_RESOLUTION_HZ}; - ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&enc_cfg,&led_encoder)); - ESP_ERROR_CHECK(rmt_enable(led_chan)); - - memset(led_pixels,0,sizeof(led_pixels)); - flush_leds(); - - apply_preset(0); - ESP_LOGI(TAG,"Auto-Start Pin D10/GPIO18"); - - while(1){ - if(current_preset>=0){ - const preset_t *p=&presets[current_preset]; - int64_t now = esp_timer_get_time(); - int64_t period_us = p->freq_hz>0 ? 1000000LL / p->freq_hz : 1000000LL; - int64_t half_us = period_us/2; - if(half_us<1000) half_us=1000; - if(now - last_toggle_us >= half_us){ - led_on = !led_on; - last_toggle_us = now; - if(led_on){ - set_all_pixels(p->r,p->g,p->b); - }else{ - memset(led_pixels,0,sizeof(led_pixels)); - } - flush_leds(); - } - if(now - phase_start_us >= (int64_t)p->duration_sec * 1000000LL){ - if(current_preset+1 < PRESET_COUNT){ - apply_preset(current_preset+1); - }else{ - stop_leds(); - vTaskDelay(pdMS_TO_TICKS(2000)); - apply_preset(0); - } - } - } - vTaskDelay(pdMS_TO_TICKS(2)); - } -}