From a982789b3c8fbf8d5c2826d6968671a30bcf1dd0 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Mon, 17 Aug 2026 19:07:49 -0700 Subject: [PATCH 01/11] Update LED strip logic - RGB Sweep --- src/main/io/ledstrip.c | 75 ++++++++++++++++++++++++++++++++++++++---- src/main/io/ledstrip.h | 5 +-- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/main/io/ledstrip.c b/src/main/io/ledstrip.c index 8e8e5771450..329b9e7b7e2 100644 --- a/src/main/io/ledstrip.c +++ b/src/main/io/ledstrip.c @@ -224,7 +224,7 @@ static const hsvColor_t* getSC(ledSpecialColorIds_e index) static const char directionCodes[LED_DIRECTION_COUNT] = { 'N', 'E', 'S', 'W', 'U', 'D' }; static const char baseFunctionCodes[LED_BASEFUNCTION_COUNT] = { 'C', 'F', 'A', 'L', 'S', 'G', 'R', 'H' }; -static const char overlayCodes[LED_OVERLAY_COUNT] = { 'T', 'O', 'B', 'N', 'I', 'W', 'E' }; +static const char overlayCodes[LED_OVERLAY_COUNT] = { 'T', 'O', 'B', 'N', 'I', 'W', 'E', 'V' }; #define CHUNK_BUFFER_SIZE 11 @@ -239,9 +239,10 @@ bool parseLedStripConfig(int ledIndex, const char *config) DIRECTIONS, FUNCTIONS, RING_COLORS, + PARAMS, PARSE_STATE_COUNT }; - static const char chunkSeparators[PARSE_STATE_COUNT] = {',', ':', ':',':', '\0'}; + static const char chunkSeparators[PARSE_STATE_COUNT] = {',', ':', ':',':', ':', '\0'}; ledConfig_t *ledConfig = &ledStripConfigMutable()->ledConfigs[ledIndex]; memset(ledConfig, 0, sizeof(ledConfig_t)); @@ -250,6 +251,7 @@ bool parseLedStripConfig(int ledIndex, const char *config) int baseFunction = 0; int overlay_flags = 0; int direction_flags = 0; + int params = 0; for (enum parseState_e parseState = 0; parseState < PARSE_STATE_COUNT; parseState++) { char chunk[CHUNK_BUFFER_SIZE]; @@ -261,9 +263,15 @@ bool parseLedStripConfig(int ledIndex, const char *config) } chunk[chunkIndex++] = 0; // zero-terminate chunk if (*config != chunkSeparator) { - return false; + // tolerate config strings saved before the PARAMS field existed - they end + // right after RING_COLORS with no trailing ':params'. Anything else is a + // genuine parse error. + if (!(parseState == RING_COLORS && *config == '\0')) { + return false; + } + } else { + config++; // skip separator } - config++; // skip separator } switch (parseState) { case X_COORDINATE: @@ -304,11 +312,16 @@ bool parseLedStripConfig(int ledIndex, const char *config) if (color >= LED_CONFIGURABLE_COLOR_COUNT) color = 0; break; + case PARAMS: + params = fastA2I(chunk); + if (params > ((1 << LED_PARAMS_BITCNT) - 1)) + params = (1 << LED_PARAMS_BITCNT) - 1; + break; case PARSE_STATE_COUNT:; // prevent warning } } - DEFINE_LED(ledConfig, x, y, color, direction_flags, baseFunction, overlay_flags, 0); + DEFINE_LED(ledConfig, x, y, color, direction_flags, baseFunction, overlay_flags, params); reevaluateLedConfig(); @@ -341,7 +354,7 @@ void generateLedConfig(ledConfig_t *ledConfig, char *ledConfigBuffer, size_t buf *fptr = 0; // TODO - check buffer length - tfp_sprintf(ledConfigBuffer, "%u,%u:%s:%s:%u", ledGetX(ledConfig), ledGetY(ledConfig), directions, baseFunctionOverlays, ledGetColor(ledConfig)); + tfp_sprintf(ledConfigBuffer, "%u,%u:%s:%s:%u:%u", ledGetX(ledConfig), ledGetY(ledConfig), directions, baseFunctionOverlays, ledGetColor(ledConfig), ledGetParams(ledConfig)); } typedef enum { @@ -841,6 +854,54 @@ static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer) } } +// bit layout of led_params (6 bits) when the LED carries LED_OVERLAY_RAINBOW: +// bits [2:0] - spacing index 0-7 -> (index * 10) degrees of hue offset between adjacent LEDs +// bits [5:3] - speed index 0-7 -> (index + 1) degrees of hue shift per update tick +// only the first LED (lowest index) carrying the overlay is consulted; the sweep uses one +// shared clock for the whole strip (mirroring how the Larson scanner keeps a single shared +// larsonParameters state rather than per-LED state), so params on any later rainbow LED are +// ignored. +#define LED_RAINBOW_SPACING_BITS 3 +#define LED_RAINBOW_SPACING_MASK ((1 << LED_RAINBOW_SPACING_BITS) - 1) +#define LED_RAINBOW_SPEED_OFFSET LED_RAINBOW_SPACING_BITS +#define LED_RAINBOW_SPEED_MASK ((1 << LED_RAINBOW_SPACING_BITS) - 1) + +// sweep hue across all LEDs carrying the rainbow overlay bit; saturation/value +// are left as configured for the LED so brightness-based effects (e.g. thrust +// ring, battery) still compose correctly with this overlay. +static void applyLedRainbowLayer(bool updateNow, timeUs_t *timer) +{ + static uint16_t rainbowHue = 0; + static uint16_t rainbowSpacing = 20; // degrees offset between adjacent LEDs + static uint16_t rainbowSpeed = 2; // degrees of hue shift per update tick + + if (updateNow) { + for (unsigned i = 0; i < ledCounts.count; i++) { + const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; + if (ledGetOverlayBit(ledConfig, LED_OVERLAY_RAINBOW)) { + uint8_t params = ledGetParams(ledConfig); + rainbowSpacing = (params & LED_RAINBOW_SPACING_MASK) * 10; + rainbowSpeed = ((params >> LED_RAINBOW_SPEED_OFFSET) & LED_RAINBOW_SPEED_MASK) + 1; + break; + } + } + + rainbowHue = (rainbowHue + rainbowSpeed) % 360; + *timer += LED_STRIP_HZ(50); + } + + for (unsigned i = 0; i < ledCounts.count; i++) { + const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; + + if (ledGetOverlayBit(ledConfig, LED_OVERLAY_RAINBOW)) { + hsvColor_t ledColor; + getLedHsv(i, &ledColor); + ledColor.h = (rainbowHue + (i * rainbowSpacing)) % 360; + setLedHsv(i, &ledColor); + } + } +} + // blink twice, then wait ; either always or just when landing static void applyLedBlinkLayer(bool updateNow, timeUs_t *timer) { @@ -907,6 +968,7 @@ static void applyLedAnimationLayer(bool updateNow, timeUs_t *timer) typedef enum { timBlink = 0, timLarson, + timRainbow, timBattery, timRssi, #ifdef USE_GPS @@ -933,6 +995,7 @@ typedef void applyLayerFn_timed(bool updateNow, timeUs_t *timer); static applyLayerFn_timed* layerTable[timTimerCount] = { [timBlink] = &applyLedBlinkLayer, [timLarson] = &applyLarsonScannerLayer, + [timRainbow] = &applyLedRainbowLayer, [timBattery] = &applyLedBatteryLayer, [timRssi] = &applyLedRssiLayer, #ifdef USE_GPS diff --git a/src/main/io/ledstrip.h b/src/main/io/ledstrip.h index 2aea06cde73..88fd0e81ce3 100644 --- a/src/main/io/ledstrip.h +++ b/src/main/io/ledstrip.h @@ -26,7 +26,7 @@ #define LED_MODE_COUNT 7 #define LED_DIRECTION_COUNT 6 #define LED_BASEFUNCTION_COUNT 8 -#define LED_OVERLAY_COUNT 7 +#define LED_OVERLAY_COUNT 8 #define LED_SPECIAL_COLOR_COUNT 9 #define LED_FUNCTION_OFFSET 8 @@ -122,7 +122,8 @@ typedef enum { LED_OVERLAY_LANDING_FLASH, LED_OVERLAY_INDICATOR, LED_OVERLAY_WARNING, - LED_OVERLAY_STROBE + LED_OVERLAY_STROBE, + LED_OVERLAY_RAINBOW } ledOverlayId_e; typedef struct modeColorIndexes_s { From 3826861fb4066ee294091f98b422eb0f430b0b65 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Mon, 17 Aug 2026 22:06:59 -0700 Subject: [PATCH 02/11] Updated LED strip logic - RGB Sweep V0.0.1 --- src/main/io/ledstrip.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/io/ledstrip.c b/src/main/io/ledstrip.c index 329b9e7b7e2..288069923f3 100644 --- a/src/main/io/ledstrip.c +++ b/src/main/io/ledstrip.c @@ -894,10 +894,11 @@ static void applyLedRainbowLayer(bool updateNow, timeUs_t *timer) const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; if (ledGetOverlayBit(ledConfig, LED_OVERLAY_RAINBOW)) { - hsvColor_t ledColor; - getLedHsv(i, &ledColor); - ledColor.h = (rainbowHue + (i * rainbowSpacing)) % 360; - setLedHsv(i, &ledColor); + hsvColor_t ledColor; + ledColor.h = (rainbowHue + (i * rainbowSpacing)) % 360; + ledColor.s = 0; // 0 = fully saturated in this codebase's convention + ledColor.v = 255; // full brightness + setLedHsv(i, &ledColor); } } } From a40032c03400821fcfb7f3568104a5f4cb106039 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Mon, 17 Aug 2026 23:34:52 -0700 Subject: [PATCH 03/11] Update Rainbow Overlay Settings --- src/main/fc/settings.yaml | 17 +++++++++ src/main/io/ledstrip.c | 75 +++++++++++++-------------------------- src/main/io/ledstrip.h | 2 ++ 3 files changed, 43 insertions(+), 51 deletions(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 4e8affb0221..f8fd87c7633 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -4502,3 +4502,20 @@ groups: field: noWayHomeAction table: geozone_rth_no_way_home type: uint8_t + - name: PG_LED_STRIP_CONFIG + type: ledStripConfig_t + headers: ["io/ledstrip.h"] + condition: USE_LED_STRIP + members: + - name: ledstrip_rainbow_freq_hz + description: "How often the rainbow overlay's hue advances by one degree, in Hz. Higher values sweep faster." + default_value: 60 + field: ledstrip_rainbow_freq_hz + min: 1 + max: 2000 + - name: ledstrip_rainbow_delta_deg + description: "Hue offset in degrees between adjacent LEDs carrying the rainbow overlay. 0 makes every rainbow LED the same color; larger values spread more of the spectrum across the strip." + default_value: 30 + field: ledstrip_rainbow_delta_deg + min: 0 + max: 359 \ No newline at end of file diff --git a/src/main/io/ledstrip.c b/src/main/io/ledstrip.c index 288069923f3..9bee90b985f 100644 --- a/src/main/io/ledstrip.c +++ b/src/main/io/ledstrip.c @@ -73,7 +73,7 @@ #include "telemetry/telemetry.h" -PG_REGISTER_WITH_RESET_FN(ledStripConfig_t, ledStripConfig, PG_LED_STRIP_CONFIG, 2); +PG_REGISTER_WITH_RESET_FN(ledStripConfig_t, ledStripConfig, PG_LED_STRIP_CONFIG, 3); static bool ledStripInitialised = false; static bool ledStripEnabled = true; @@ -154,6 +154,8 @@ void pgResetFn_ledStripConfig(ledStripConfig_t *instance) } memcpy_fn(&instance->modeColors, &defaultModeColors, sizeof(defaultModeColors)); memcpy_fn(&instance->specialColors, &defaultSpecialColors, sizeof(defaultSpecialColors)); + instance->ledstrip_rainbow_freq_hz = 60; + instance->ledstrip_rainbow_delta_deg = 30; } static int scaledThrottle; @@ -239,10 +241,9 @@ bool parseLedStripConfig(int ledIndex, const char *config) DIRECTIONS, FUNCTIONS, RING_COLORS, - PARAMS, PARSE_STATE_COUNT }; - static const char chunkSeparators[PARSE_STATE_COUNT] = {',', ':', ':',':', ':', '\0'}; + static const char chunkSeparators[PARSE_STATE_COUNT] = {',', ':', ':',':', '\0'}; ledConfig_t *ledConfig = &ledStripConfigMutable()->ledConfigs[ledIndex]; memset(ledConfig, 0, sizeof(ledConfig_t)); @@ -251,7 +252,6 @@ bool parseLedStripConfig(int ledIndex, const char *config) int baseFunction = 0; int overlay_flags = 0; int direction_flags = 0; - int params = 0; for (enum parseState_e parseState = 0; parseState < PARSE_STATE_COUNT; parseState++) { char chunk[CHUNK_BUFFER_SIZE]; @@ -263,15 +263,9 @@ bool parseLedStripConfig(int ledIndex, const char *config) } chunk[chunkIndex++] = 0; // zero-terminate chunk if (*config != chunkSeparator) { - // tolerate config strings saved before the PARAMS field existed - they end - // right after RING_COLORS with no trailing ':params'. Anything else is a - // genuine parse error. - if (!(parseState == RING_COLORS && *config == '\0')) { - return false; - } - } else { - config++; // skip separator + return false; } + config++; // skip separator } switch (parseState) { case X_COORDINATE: @@ -312,16 +306,11 @@ bool parseLedStripConfig(int ledIndex, const char *config) if (color >= LED_CONFIGURABLE_COLOR_COUNT) color = 0; break; - case PARAMS: - params = fastA2I(chunk); - if (params > ((1 << LED_PARAMS_BITCNT) - 1)) - params = (1 << LED_PARAMS_BITCNT) - 1; - break; case PARSE_STATE_COUNT:; // prevent warning } } - DEFINE_LED(ledConfig, x, y, color, direction_flags, baseFunction, overlay_flags, params); + DEFINE_LED(ledConfig, x, y, color, direction_flags, baseFunction, overlay_flags, 0); reevaluateLedConfig(); @@ -354,7 +343,7 @@ void generateLedConfig(ledConfig_t *ledConfig, char *ledConfigBuffer, size_t buf *fptr = 0; // TODO - check buffer length - tfp_sprintf(ledConfigBuffer, "%u,%u:%s:%s:%u:%u", ledGetX(ledConfig), ledGetY(ledConfig), directions, baseFunctionOverlays, ledGetColor(ledConfig), ledGetParams(ledConfig)); + tfp_sprintf(ledConfigBuffer, "%u,%u:%s:%s:%u", ledGetX(ledConfig), ledGetY(ledConfig), directions, baseFunctionOverlays, ledGetColor(ledConfig)); } typedef enum { @@ -854,51 +843,35 @@ static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer) } } -// bit layout of led_params (6 bits) when the LED carries LED_OVERLAY_RAINBOW: -// bits [2:0] - spacing index 0-7 -> (index * 10) degrees of hue offset between adjacent LEDs -// bits [5:3] - speed index 0-7 -> (index + 1) degrees of hue shift per update tick -// only the first LED (lowest index) carrying the overlay is consulted; the sweep uses one -// shared clock for the whole strip (mirroring how the Larson scanner keeps a single shared -// larsonParameters state rather than per-LED state), so params on any later rainbow LED are -// ignored. -#define LED_RAINBOW_SPACING_BITS 3 -#define LED_RAINBOW_SPACING_MASK ((1 << LED_RAINBOW_SPACING_BITS) - 1) -#define LED_RAINBOW_SPEED_OFFSET LED_RAINBOW_SPACING_BITS -#define LED_RAINBOW_SPEED_MASK ((1 << LED_RAINBOW_SPACING_BITS) - 1) - -// sweep hue across all LEDs carrying the rainbow overlay bit; saturation/value -// are left as configured for the LED so brightness-based effects (e.g. thrust -// ring, battery) still compose correctly with this overlay. +// rainbow overlay sweep, tuned by two global settings (ledstrip_rainbow_freq_hz, +// ledstrip_rainbow_delta_deg) +// ledstrip_rainbow_freq_hz - how often hue advances by 1 degree (1-2000Hz) +// ledstrip_rainbow_delta_deg - hue offset between adjacent rainbow LEDs (0-359) static void applyLedRainbowLayer(bool updateNow, timeUs_t *timer) { static uint16_t rainbowHue = 0; - static uint16_t rainbowSpacing = 20; // degrees offset between adjacent LEDs - static uint16_t rainbowSpeed = 2; // degrees of hue shift per update tick if (updateNow) { - for (unsigned i = 0; i < ledCounts.count; i++) { - const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; - if (ledGetOverlayBit(ledConfig, LED_OVERLAY_RAINBOW)) { - uint8_t params = ledGetParams(ledConfig); - rainbowSpacing = (params & LED_RAINBOW_SPACING_MASK) * 10; - rainbowSpeed = ((params >> LED_RAINBOW_SPEED_OFFSET) & LED_RAINBOW_SPEED_MASK) + 1; - break; - } + uint16_t freqHz = ledStripConfig()->ledstrip_rainbow_freq_hz; + if (freqHz == 0) { + freqHz = 1; // guard against divide-by-zero in LED_STRIP_HZ() } - rainbowHue = (rainbowHue + rainbowSpeed) % 360; - *timer += LED_STRIP_HZ(50); + rainbowHue = (rainbowHue + 1) % 360; + *timer += LED_STRIP_HZ(freqHz); } + const uint16_t rainbowDelta = ledStripConfig()->ledstrip_rainbow_delta_deg % 360; + for (unsigned i = 0; i < ledCounts.count; i++) { const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; if (ledGetOverlayBit(ledConfig, LED_OVERLAY_RAINBOW)) { - hsvColor_t ledColor; - ledColor.h = (rainbowHue + (i * rainbowSpacing)) % 360; - ledColor.s = 0; // 0 = fully saturated in this codebase's convention - ledColor.v = 255; // full brightness - setLedHsv(i, &ledColor); + hsvColor_t ledColor; + ledColor.h = (rainbowHue + (i * rainbowDelta)) % 360; + ledColor.s = 0; // 0 = fully saturated in this codebase's convention + ledColor.v = 255; // full brightness + setLedHsv(i, &ledColor); } } } diff --git a/src/main/io/ledstrip.h b/src/main/io/ledstrip.h index 88fd0e81ce3..f7ba99a6089 100644 --- a/src/main/io/ledstrip.h +++ b/src/main/io/ledstrip.h @@ -155,6 +155,8 @@ typedef struct ledStripConfig_s { hsvColor_t colors[LED_CONFIGURABLE_COLOR_COUNT]; modeColorIndexes_t modeColors[LED_MODE_COUNT]; specialColorIndexes_t specialColors; + uint16_t ledstrip_rainbow_freq_hz; // update rate driving the rainbow overlay's hue sweep, 1-2000Hz + uint16_t ledstrip_rainbow_delta_deg; // hue offset between adjacent rainbow-overlay LEDs, 0-359 degrees } ledStripConfig_t; PG_DECLARE(ledStripConfig_t, ledStripConfig); From 35ebad4380537f7ba086d4ce6813602be3dacd3d Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Thu, 20 Aug 2026 22:55:54 -0700 Subject: [PATCH 04/11] LED strip: add RGB sweep/rainbow overlay --- src/main/io/ledstrip.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/io/ledstrip.c b/src/main/io/ledstrip.c index 9bee90b985f..f0a57c5c295 100644 --- a/src/main/io/ledstrip.c +++ b/src/main/io/ledstrip.c @@ -843,10 +843,6 @@ static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer) } } -// rainbow overlay sweep, tuned by two global settings (ledstrip_rainbow_freq_hz, -// ledstrip_rainbow_delta_deg) -// ledstrip_rainbow_freq_hz - how often hue advances by 1 degree (1-2000Hz) -// ledstrip_rainbow_delta_deg - hue offset between adjacent rainbow LEDs (0-359) static void applyLedRainbowLayer(bool updateNow, timeUs_t *timer) { static uint16_t rainbowHue = 0; From 31be653ebb7077014913a8b7bc497daff0c96d39 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Fri, 21 Aug 2026 00:15:57 -0700 Subject: [PATCH 05/11] LED strip: refactor rainbow sweep rate to use task counter --- src/main/fc/settings.yaml | 10 +++++----- src/main/io/ledstrip.c | 17 ++++++++--------- src/main/io/ledstrip.h | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index f8fd87c7633..b8748e8c4d9 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -4507,12 +4507,12 @@ groups: headers: ["io/ledstrip.h"] condition: USE_LED_STRIP members: - - name: ledstrip_rainbow_freq_hz - description: "How often the rainbow overlay's hue advances by one degree, in Hz. Higher values sweep faster." - default_value: 60 - field: ledstrip_rainbow_freq_hz + - name: ledstrip_rainbow_sweep_rate + description: "Rainbow overlay sweep rate. Hue advances 2 degrees every N task ticks (100 Hz). Lower values sweep faster." + default_value: 1 + field: ledstrip_rainbow_sweep_rate min: 1 - max: 2000 + max: 255 - name: ledstrip_rainbow_delta_deg description: "Hue offset in degrees between adjacent LEDs carrying the rainbow overlay. 0 makes every rainbow LED the same color; larger values spread more of the spectrum across the strip." default_value: 30 diff --git a/src/main/io/ledstrip.c b/src/main/io/ledstrip.c index f0a57c5c295..fe16fcba727 100644 --- a/src/main/io/ledstrip.c +++ b/src/main/io/ledstrip.c @@ -154,7 +154,7 @@ void pgResetFn_ledStripConfig(ledStripConfig_t *instance) } memcpy_fn(&instance->modeColors, &defaultModeColors, sizeof(defaultModeColors)); memcpy_fn(&instance->specialColors, &defaultSpecialColors, sizeof(defaultSpecialColors)); - instance->ledstrip_rainbow_freq_hz = 60; + instance->ledstrip_rainbow_sweep_rate = 1; instance->ledstrip_rainbow_delta_deg = 30; } @@ -846,15 +846,14 @@ static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer) static void applyLedRainbowLayer(bool updateNow, timeUs_t *timer) { static uint16_t rainbowHue = 0; + static uint8_t rainbowCounter = 0; if (updateNow) { - uint16_t freqHz = ledStripConfig()->ledstrip_rainbow_freq_hz; - if (freqHz == 0) { - freqHz = 1; // guard against divide-by-zero in LED_STRIP_HZ() + *timer += LED_STRIP_HZ(100); + if (++rainbowCounter >= ledStripConfig()->ledstrip_rainbow_sweep_rate) { + rainbowCounter = 0; + rainbowHue = (rainbowHue + 2) % 360; } - - rainbowHue = (rainbowHue + 1) % 360; - *timer += LED_STRIP_HZ(freqHz); } const uint16_t rainbowDelta = ledStripConfig()->ledstrip_rainbow_delta_deg % 360; @@ -865,8 +864,8 @@ static void applyLedRainbowLayer(bool updateNow, timeUs_t *timer) if (ledGetOverlayBit(ledConfig, LED_OVERLAY_RAINBOW)) { hsvColor_t ledColor; ledColor.h = (rainbowHue + (i * rainbowDelta)) % 360; - ledColor.s = 0; // 0 = fully saturated in this codebase's convention - ledColor.v = 255; // full brightness + ledColor.s = 0; + ledColor.v = 255; setLedHsv(i, &ledColor); } } diff --git a/src/main/io/ledstrip.h b/src/main/io/ledstrip.h index f7ba99a6089..2d4cf44a07f 100644 --- a/src/main/io/ledstrip.h +++ b/src/main/io/ledstrip.h @@ -155,7 +155,7 @@ typedef struct ledStripConfig_s { hsvColor_t colors[LED_CONFIGURABLE_COLOR_COUNT]; modeColorIndexes_t modeColors[LED_MODE_COUNT]; specialColorIndexes_t specialColors; - uint16_t ledstrip_rainbow_freq_hz; // update rate driving the rainbow overlay's hue sweep, 1-2000Hz + uint8_t ledstrip_rainbow_sweep_rate; // hue sweep rate for rainbow overlay, 1-255, 1=fastest, 255=slowest uint16_t ledstrip_rainbow_delta_deg; // hue offset between adjacent rainbow-overlay LEDs, 0-359 degrees } ledStripConfig_t; From 94d4cd49778313f3017b1f2c5eb544f6a5d3d5ac Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Sun, 23 Aug 2026 17:19:23 -0700 Subject: [PATCH 06/11] LED strip: implement fixed-point accumulator for rainbow sweep rate --- src/main/fc/settings.yaml | 5 +++-- src/main/io/ledstrip.c | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index b8748e8c4d9..d0a7e92389c 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -4508,8 +4508,9 @@ groups: condition: USE_LED_STRIP members: - name: ledstrip_rainbow_sweep_rate - description: "Rainbow overlay sweep rate. Hue advances 2 degrees every N task ticks (100 Hz). Lower values sweep faster." - default_value: 1 + description: "Rainbow overlay sweep rate. Higher values sweep faster. 0 freezes the rainbow." + # Skittles Tastes the Rainbow. Prompt Writer Reads the Code + default_value: 10 field: ledstrip_rainbow_sweep_rate min: 1 max: 255 diff --git a/src/main/io/ledstrip.c b/src/main/io/ledstrip.c index fe16fcba727..c63d722098c 100644 --- a/src/main/io/ledstrip.c +++ b/src/main/io/ledstrip.c @@ -154,7 +154,7 @@ void pgResetFn_ledStripConfig(ledStripConfig_t *instance) } memcpy_fn(&instance->modeColors, &defaultModeColors, sizeof(defaultModeColors)); memcpy_fn(&instance->specialColors, &defaultSpecialColors, sizeof(defaultSpecialColors)); - instance->ledstrip_rainbow_sweep_rate = 1; + instance->ledstrip_rainbow_sweep_rate = 10; instance->ledstrip_rainbow_delta_deg = 30; } @@ -845,17 +845,24 @@ static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer) static void applyLedRainbowLayer(bool updateNow, timeUs_t *timer) { - static uint16_t rainbowHue = 0; - static uint8_t rainbowCounter = 0; + static uint16_t accumulator = 0; + static uint8_t stepCount = 0; if (updateNow) { *timer += LED_STRIP_HZ(100); - if (++rainbowCounter >= ledStripConfig()->ledstrip_rainbow_sweep_rate) { - rainbowCounter = 0; - rainbowHue = (rainbowHue + 2) % 360; + const uint8_t speed = ledStripConfig()->ledstrip_rainbow_sweep_rate; + if (speed > 0) { + accumulator += speed; + while (accumulator >= 256) { + accumulator -= 256; + if (++stepCount >= 180) { + stepCount = 0; + } + } } } + const uint16_t rainbowHue = (uint16_t)stepCount << 1; const uint16_t rainbowDelta = ledStripConfig()->ledstrip_rainbow_delta_deg % 360; for (unsigned i = 0; i < ledCounts.count; i++) { From e4044f592d34e6a5c77d6bc31e9107e4aa1907f9 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Sun, 23 Aug 2026 20:23:05 -0700 Subject: [PATCH 07/11] LED strip: implement fixed-point accumulator for rainbow sweep rate --- src/main/fc/settings.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index d0a7e92389c..0e9526052f9 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -4509,7 +4509,6 @@ groups: members: - name: ledstrip_rainbow_sweep_rate description: "Rainbow overlay sweep rate. Higher values sweep faster. 0 freezes the rainbow." - # Skittles Tastes the Rainbow. Prompt Writer Reads the Code default_value: 10 field: ledstrip_rainbow_sweep_rate min: 1 From 31832165112246325badf6dab6c674862c6e4949 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Sun, 23 Aug 2026 23:29:51 -0700 Subject: [PATCH 08/11] Rainbow Overlay- Fix Minimum Sweep Rate Bug --- src/main/fc/settings.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 0e9526052f9..0b8c72e760b 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -4511,7 +4511,7 @@ groups: description: "Rainbow overlay sweep rate. Higher values sweep faster. 0 freezes the rainbow." default_value: 10 field: ledstrip_rainbow_sweep_rate - min: 1 + min: 0 max: 255 - name: ledstrip_rainbow_delta_deg description: "Hue offset in degrees between adjacent LEDs carrying the rainbow overlay. 0 makes every rainbow LED the same color; larger values spread more of the spectrum across the strip." From 8407c8c054589e190e410a303a369a72986349a4 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Sun, 23 Aug 2026 23:33:34 -0700 Subject: [PATCH 09/11] Rainbow Overlay- Fix outdated comment --- src/main/io/ledstrip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/io/ledstrip.h b/src/main/io/ledstrip.h index 2d4cf44a07f..ff3c2fcec44 100644 --- a/src/main/io/ledstrip.h +++ b/src/main/io/ledstrip.h @@ -155,7 +155,7 @@ typedef struct ledStripConfig_s { hsvColor_t colors[LED_CONFIGURABLE_COLOR_COUNT]; modeColorIndexes_t modeColors[LED_MODE_COUNT]; specialColorIndexes_t specialColors; - uint8_t ledstrip_rainbow_sweep_rate; // hue sweep rate for rainbow overlay, 1-255, 1=fastest, 255=slowest + uint8_t ledstrip_rainbow_sweep_rate; // hue sweep rate for rainbow overlay, 0-255, 0=stopped, 255=fastest uint16_t ledstrip_rainbow_delta_deg; // hue offset between adjacent rainbow-overlay LEDs, 0-359 degrees } ledStripConfig_t; From 0b6a7bc95e6277b6a49bfd4cf99a1edd7ed13383 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Sun, 23 Aug 2026 23:38:38 -0700 Subject: [PATCH 10/11] Rainbow Overlay- Correct Uncessary Version Bump --- src/main/io/ledstrip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/io/ledstrip.c b/src/main/io/ledstrip.c index c63d722098c..16d75288962 100644 --- a/src/main/io/ledstrip.c +++ b/src/main/io/ledstrip.c @@ -73,7 +73,7 @@ #include "telemetry/telemetry.h" -PG_REGISTER_WITH_RESET_FN(ledStripConfig_t, ledStripConfig, PG_LED_STRIP_CONFIG, 3); +PG_REGISTER_WITH_RESET_FN(ledStripConfig_t, ledStripConfig, PG_LED_STRIP_CONFIG, 2); static bool ledStripInitialised = false; static bool ledStripEnabled = true; From 40ae72e6007efbfca3fc69b0b511906bc32bbb30 Mon Sep 17 00:00:00 2001 From: HereComesWhitey Date: Sun, 23 Aug 2026 23:40:45 -0700 Subject: [PATCH 11/11] Rainbow Overlay- Increase Sweep Rate Default --- src/main/fc/settings.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 0b8c72e760b..898c5fa2b4c 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -4509,7 +4509,7 @@ groups: members: - name: ledstrip_rainbow_sweep_rate description: "Rainbow overlay sweep rate. Higher values sweep faster. 0 freezes the rainbow." - default_value: 10 + default_value: 100 field: ledstrip_rainbow_sweep_rate min: 0 max: 255