diff --git a/docs/Settings.md b/docs/Settings.md index 2df6b7ebf4f..6434a85b271 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -2583,6 +2583,26 @@ Used to prevent Iterm accumulation on during maneuvers. Iterm will be dampened w --- +### ledstrip_rainbow_delta_deg + +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 | Min | Max | +| --- | --- | --- | +| 30 | 0 | 359 | + +--- + +### ledstrip_rainbow_sweep_rate + +Rainbow overlay sweep rate. Higher values sweep faster. 0 freezes the rainbow. + +| Default | Min | Max | +| --- | --- | --- | +| 100 | 0 | 255 | + +--- + ### limit_attn_filter_cutoff Throttle attenuation PI control output filter cutoff frequency diff --git a/src/main/fc/cli.c b/src/main/fc/cli.c index 3f85979a788..cfb89c18ee7 100644 --- a/src/main/fc/cli.c +++ b/src/main/fc/cli.c @@ -2043,8 +2043,8 @@ static void cliWaypoints(char *cmdline) static void printLed(uint8_t dumpMask, const ledConfig_t *ledConfigs, const ledConfig_t *defaultLedConfigs) { const char *format = "led %u %s"; - char ledConfigBuffer[20]; - char ledConfigDefaultBuffer[20]; + char ledConfigBuffer[LED_CONFIG_STRING_LENGTH]; + char ledConfigDefaultBuffer[LED_CONFIG_STRING_LENGTH]; for (uint32_t i = 0; i < LED_MAX_STRIP_LENGTH; i++) { ledConfig_t ledConfig = ledConfigs[i]; generateLedConfig(&ledConfig, ledConfigBuffer, sizeof(ledConfigBuffer)); diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 8849ccdaf28..5eb2043f7d4 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -4626,6 +4626,23 @@ 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_sweep_rate + description: "Rainbow overlay sweep rate. Higher values sweep faster. 0 freezes the rainbow." + default_value: 100 + field: ledstrip_rainbow_sweep_rate + 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." + default_value: 30 + field: ledstrip_rainbow_delta_deg + min: 0 + max: 359 - name: PG_DRONECAN_CONFIG type: dronecanConfig_t headers: ["drivers/dronecan/dronecan.h"] diff --git a/src/main/io/ledstrip.c b/src/main/io/ledstrip.c index 8e8e5771450..8001bdff5d6 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_sweep_rate = 100; + instance->ledstrip_rainbow_delta_deg = 30; } static int scaledThrottle; @@ -224,7 +226,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 @@ -340,8 +342,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_snprintf(ledConfigBuffer, bufferSize, "%u,%u:%s:%s:%u", ledGetX(ledConfig), ledGetY(ledConfig), directions, baseFunctionOverlays, ledGetColor(ledConfig)); } typedef enum { @@ -841,6 +842,45 @@ static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer) } } +static void applyLedRainbowLayer(bool updateNow, timeUs_t *timer) +{ + static uint16_t accumulator = 0; + static uint8_t stepCount = 0; + + if (updateNow) { + *timer += LED_STRIP_HZ(100); + 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; + + uint8_t rainbowIndex = 0; + + for (unsigned i = 0; i < ledCounts.count; i++) { + const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; + + if (ledGetOverlayBit(ledConfig, LED_OVERLAY_RAINBOW)) { + const hsvColor_t ledColor = { + .h = (rainbowHue + (rainbowIndex * rainbowDelta)) % 360, + .s = 0, // full saturation (INAV stores saturation inverted) + .v = 255, // full brightness + }; + setLedHsv(i, &ledColor); + rainbowIndex++; + } + } +} + // blink twice, then wait ; either always or just when landing static void applyLedBlinkLayer(bool updateNow, timeUs_t *timer) { @@ -905,7 +945,8 @@ static void applyLedAnimationLayer(bool updateNow, timeUs_t *timer) #endif typedef enum { - timBlink = 0, + timRainbow = 0, + timBlink, timLarson, timBattery, timRssi, @@ -931,6 +972,7 @@ static timeUs_t timerVal[timTimerCount]; typedef void applyLayerFn_timed(bool updateNow, timeUs_t *timer); static applyLayerFn_timed* layerTable[timTimerCount] = { + [timRainbow] = &applyLedRainbowLayer, [timBlink] = &applyLedBlinkLayer, [timLarson] = &applyLarsonScannerLayer, [timBattery] = &applyLedBatteryLayer, diff --git a/src/main/io/ledstrip.h b/src/main/io/ledstrip.h index 2aea06cde73..aa426e5865d 100644 --- a/src/main/io/ledstrip.h +++ b/src/main/io/ledstrip.h @@ -26,7 +26,10 @@ #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 + +// Longest CLI representation of one LED, e.g. "15,15:NESWUD:CTOBNIWEV:15" (25 characters) plus the terminator +#define LED_CONFIG_STRING_LENGTH 32 #define LED_SPECIAL_COLOR_COUNT 9 #define LED_FUNCTION_OFFSET 8 @@ -122,7 +125,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 { @@ -154,6 +158,8 @@ 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, 0-255, 0=stopped, 255=fastest + uint16_t ledstrip_rainbow_delta_deg; // hue offset between adjacent rainbow-overlay LEDs, 0-359 degrees } ledStripConfig_t; PG_DECLARE(ledStripConfig_t, ledStripConfig);