2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
26 #include <build_config.h>
30 #include <common/color.h>
32 #include "drivers/light_ws2811strip.h"
33 #include "drivers/system.h"
34 #include "drivers/serial.h"
36 #include <common/maths.h>
37 #include <common/printf.h>
38 #include <common/typeconversion.h>
40 #include "sensors/battery.h"
42 #include "config/runtime_config.h"
43 #include "config/config.h"
45 #include "io/rc_controls.h"
46 #include "flight/failsafe.h"
48 #include "io/ledstrip.h"
50 static bool ledStripInitialised
= false;
51 static bool ledStripEnabled
= true;
53 static failsafe_t
* failsafe
;
55 static void ledStripDisable(void);
57 //#define USE_LED_ANIMATION
58 //#define USE_LED_RING_DEFAULT_CONFIG
61 #ifdef USE_LED_ANIMATION
62 static uint32_t nextAnimationUpdateAt
= 0;
65 static uint32_t nextIndicatorFlashAt
= 0;
66 static uint32_t nextWarningFlashAt
= 0;
67 static uint32_t nextRotationUpdateAt
= 0;
69 #define LED_STRIP_20HZ ((1000 * 1000) / 20)
70 #define LED_STRIP_10HZ ((1000 * 1000) / 10)
71 #define LED_STRIP_5HZ ((1000 * 1000) / 5)
73 #if MAX_LED_STRIP_LENGTH > WS2811_LED_STRIP_LENGTH
74 #error "Led strip length must match driver"
80 #define LED_BLACK { 0, 0, 0}
81 #define LED_WHITE { 0, 255, 255}
82 #define LED_RED { 0, 0, 255}
83 #define LED_ORANGE { 30, 0, 255}
84 #define LED_YELLOW { 60, 0, 255}
85 #define LED_LIME_GREEN { 90, 0, 255}
86 #define LED_GREEN {120, 0, 255}
87 #define LED_MINT_GREEN {150, 0, 255}
88 #define LED_CYAN {180, 0, 255}
89 #define LED_LIGHT_BLUE {210, 0, 255}
90 #define LED_BLUE {240, 0, 255}
91 #define LED_DARK_VIOLET {270, 0, 255}
92 #define LED_MAGENTA {300, 0, 255}
93 #define LED_DEEP_PINK {330, 0, 255}
95 const hsvColor_t hsv_black
= LED_BLACK
;
96 const hsvColor_t hsv_white
= LED_WHITE
;
97 const hsvColor_t hsv_red
= LED_RED
;
98 const hsvColor_t hsv_orange
= LED_ORANGE
;
99 const hsvColor_t hsv_yellow
= LED_YELLOW
;
100 const hsvColor_t hsv_limeGreen
= LED_LIME_GREEN
;
101 const hsvColor_t hsv_green
= LED_GREEN
;
102 const hsvColor_t hsv_mintGreen
= LED_MINT_GREEN
;
103 const hsvColor_t hsv_cyan
= LED_CYAN
;
104 const hsvColor_t hsv_lightBlue
= LED_LIGHT_BLUE
;
105 const hsvColor_t hsv_blue
= LED_BLUE
;
106 const hsvColor_t hsv_darkViolet
= LED_DARK_VIOLET
;
107 const hsvColor_t hsv_magenta
= LED_MAGENTA
;
108 const hsvColor_t hsv_deepPink
= LED_DEEP_PINK
;
110 #define LED_DIRECTION_COUNT 6
112 const hsvColor_t
* const defaultColors
[] = {
155 typedef struct modeColorIndexes_s
{
162 } modeColorIndexes_t
;
165 // Note, the color index used for the mode colors below refer to the default colors.
166 // if the colors are reconfigured the index is still valid but the displayed color might
168 // See colors[] and defaultColors[] and applyDefaultColors[]
170 static const modeColorIndexes_t orientationModeColors
= {
179 static const modeColorIndexes_t headfreeModeColors
= {
188 static const modeColorIndexes_t horizonModeColors
= {
197 static const modeColorIndexes_t angleModeColors
= {
207 static const modeColorIndexes_t magModeColors
= {
217 static const modeColorIndexes_t baroModeColors
= {
227 uint8_t ledGridWidth
;
228 uint8_t ledGridHeight
;
230 uint8_t ledsInRingCount
;
232 ledConfig_t
*ledConfigs
;
236 #ifdef USE_LED_RING_DEFAULT_CONFIG
237 const ledConfig_t defaultLedStripConfig
[] = {
238 { CALCULATE_LED_XY( 2, 2), 3, LED_FUNCTION_THRUST_RING
},
239 { CALCULATE_LED_XY( 2, 1), 3, LED_FUNCTION_THRUST_RING
},
240 { CALCULATE_LED_XY( 2, 0), 3, LED_FUNCTION_THRUST_RING
},
241 { CALCULATE_LED_XY( 1, 0), 3, LED_FUNCTION_THRUST_RING
},
242 { CALCULATE_LED_XY( 0, 0), 3, LED_FUNCTION_THRUST_RING
},
243 { CALCULATE_LED_XY( 0, 1), 3, LED_FUNCTION_THRUST_RING
},
244 { CALCULATE_LED_XY( 0, 2), 3, LED_FUNCTION_THRUST_RING
},
245 { CALCULATE_LED_XY( 1, 2), 3, LED_FUNCTION_THRUST_RING
},
246 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING
},
247 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING
},
248 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING
},
249 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING
},
252 const ledConfig_t defaultLedStripConfig
[] = {
253 { CALCULATE_LED_XY( 2, 2), 0, LED_DIRECTION_SOUTH
| LED_DIRECTION_EAST
| LED_FUNCTION_INDICATOR
| LED_FUNCTION_ARM_STATE
},
254 { CALCULATE_LED_XY( 2, 1), 0, LED_DIRECTION_EAST
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
255 { CALCULATE_LED_XY( 2, 0), 0, LED_DIRECTION_NORTH
| LED_DIRECTION_EAST
| LED_FUNCTION_INDICATOR
| LED_FUNCTION_ARM_STATE
},
256 { CALCULATE_LED_XY( 1, 0), 0, LED_DIRECTION_NORTH
| LED_FUNCTION_FLIGHT_MODE
},
257 { CALCULATE_LED_XY( 0, 0), 0, LED_DIRECTION_NORTH
| LED_DIRECTION_WEST
| LED_FUNCTION_INDICATOR
| LED_FUNCTION_ARM_STATE
},
258 { CALCULATE_LED_XY( 0, 1), 0, LED_DIRECTION_WEST
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
259 { CALCULATE_LED_XY( 0, 2), 0, LED_DIRECTION_SOUTH
| LED_DIRECTION_WEST
| LED_FUNCTION_INDICATOR
| LED_FUNCTION_ARM_STATE
},
260 { CALCULATE_LED_XY( 1, 2), 0, LED_DIRECTION_SOUTH
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
261 { CALCULATE_LED_XY( 1, 1), 0, LED_DIRECTION_UP
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
262 { CALCULATE_LED_XY( 1, 1), 0, LED_DIRECTION_UP
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
263 { CALCULATE_LED_XY( 1, 1), 0, LED_DIRECTION_DOWN
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
264 { CALCULATE_LED_XY( 1, 1), 0, LED_DIRECTION_DOWN
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
275 * 16 * 32 leds = 512 bytes storage needed worst case.
276 * = not efficient to store led configs as strings in flash.
277 * = becomes a problem to send all the data via cli due to serial/cli buffers
288 #define PARSE_STATE_COUNT 5
290 static const char chunkSeparators
[PARSE_STATE_COUNT
] = {',', ':', ':',':', '\0' };
292 static const char directionCodes
[] = { 'N', 'E', 'S', 'W', 'U', 'D' };
293 #define DIRECTION_COUNT (sizeof(directionCodes) / sizeof(directionCodes[0]))
294 static const uint8_t directionMappings
[DIRECTION_COUNT
] = {
303 static const char functionCodes
[] = { 'I', 'W', 'F', 'A', 'T', 'R' };
304 #define FUNCTION_COUNT (sizeof(functionCodes) / sizeof(functionCodes[0]))
305 static const uint16_t functionMappings
[FUNCTION_COUNT
] = {
306 LED_FUNCTION_INDICATOR
,
307 LED_FUNCTION_WARNING
,
308 LED_FUNCTION_FLIGHT_MODE
,
309 LED_FUNCTION_ARM_STATE
,
310 LED_FUNCTION_THROTTLE
,
311 LED_FUNCTION_THRUST_RING
315 uint8_t highestYValueForNorth
;
316 uint8_t lowestYValueForSouth
;
317 uint8_t highestXValueForWest
;
318 uint8_t lowestXValueForEast
;
320 void determineLedStripDimensions(void)
326 const ledConfig_t
*ledConfig
;
328 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
329 ledConfig
= &ledConfigs
[ledIndex
];
331 if (GET_LED_X(ledConfig
) >= ledGridWidth
) {
332 ledGridWidth
= GET_LED_X(ledConfig
) + 1;
334 if (GET_LED_Y(ledConfig
) >= ledGridHeight
) {
335 ledGridHeight
= GET_LED_Y(ledConfig
) + 1;
340 void determineOrientationLimits(void)
342 bool isOddHeight
= (ledGridHeight
& 1);
343 bool isOddWidth
= (ledGridWidth
& 1);
344 uint8_t heightModifier
= isOddHeight
? 1 : 0;
345 uint8_t widthModifier
= isOddWidth
? 1 : 0;
347 highestYValueForNorth
= (ledGridHeight
/ 2) - 1;
348 lowestYValueForSouth
= (ledGridHeight
/ 2) + heightModifier
;
349 highestXValueForWest
= (ledGridWidth
/ 2) - 1;
350 lowestXValueForEast
= (ledGridWidth
/ 2) + widthModifier
;
353 void updateLedCount(void)
355 const ledConfig_t
*ledConfig
;
360 for (ledIndex
= 0; ledIndex
< MAX_LED_STRIP_LENGTH
; ledIndex
++) {
362 ledConfig
= &ledConfigs
[ledIndex
];
364 if (ledConfig
->flags
== 0 && ledConfig
->xy
== 0) {
370 if ((ledConfig
->flags
& LED_FUNCTION_THRUST_RING
)) {
376 void reevalulateLedConfig(void)
379 determineLedStripDimensions();
380 determineOrientationLimits();
383 #define CHUNK_BUFFER_SIZE 10
385 #define NEXT_PARSE_STATE(parseState) ((parseState + 1) % PARSE_STATE_COUNT)
388 bool parseLedStripConfig(uint8_t ledIndex
, const char *config
)
390 char chunk
[CHUNK_BUFFER_SIZE
];
394 uint8_t parseState
= X_COORDINATE
;
397 if (ledIndex
>= MAX_LED_STRIP_LENGTH
) {
401 ledConfig_t
*ledConfig
= &ledConfigs
[ledIndex
];
402 memset(ledConfig
, 0, sizeof(ledConfig_t
));
406 char chunkSeparator
= chunkSeparators
[parseState
];
408 memset(&chunk
, 0, sizeof(chunk
));
411 while (*config
&& chunkIndex
< CHUNK_BUFFER_SIZE
&& *config
!= chunkSeparator
) {
412 chunk
[chunkIndex
++] = *config
++;
415 if (*config
++ != chunkSeparator
) {
420 switch((parseState_e
)parseState
) {
423 ledConfig
->xy
|= CALCULATE_LED_X(val
);
427 ledConfig
->xy
|= CALCULATE_LED_Y(val
);
430 for (chunkIndex
= 0; chunk
[chunkIndex
] && chunkIndex
< CHUNK_BUFFER_SIZE
; chunkIndex
++) {
431 for (uint8_t mappingIndex
= 0; mappingIndex
< DIRECTION_COUNT
; mappingIndex
++) {
432 if (directionCodes
[mappingIndex
] == chunk
[chunkIndex
]) {
433 ledConfig
->flags
|= directionMappings
[mappingIndex
];
440 for (chunkIndex
= 0; chunk
[chunkIndex
] && chunkIndex
< CHUNK_BUFFER_SIZE
; chunkIndex
++) {
441 for (uint8_t mappingIndex
= 0; mappingIndex
< FUNCTION_COUNT
; mappingIndex
++) {
442 if (functionCodes
[mappingIndex
] == chunk
[chunkIndex
]) {
443 ledConfig
->flags
|= functionMappings
[mappingIndex
];
450 if (atoi(chunk
) < CONFIGURABLE_COLOR_COUNT
) {
451 ledConfig
->color
= atoi(chunk
);
453 ledConfig
->color
= 0;
461 if (parseState
>= PARSE_STATE_COUNT
) {
467 memset(ledConfig
, 0, sizeof(ledConfig_t
));
470 reevalulateLedConfig();
475 void generateLedConfig(uint8_t ledIndex
, char *ledConfigBuffer
, size_t bufferSize
)
477 char functions
[FUNCTION_COUNT
];
478 char directions
[DIRECTION_COUNT
];
480 uint8_t mappingIndex
;
482 ledConfig_t
*ledConfig
= &ledConfigs
[ledIndex
];
484 memset(ledConfigBuffer
, 0, bufferSize
);
485 memset(&functions
, 0, sizeof(functions
));
486 memset(&directions
, 0, sizeof(directions
));
488 for (mappingIndex
= 0, index
= 0; mappingIndex
< FUNCTION_COUNT
; mappingIndex
++) {
489 if (ledConfig
->flags
& functionMappings
[mappingIndex
]) {
490 functions
[index
++] = functionCodes
[mappingIndex
];
494 for (mappingIndex
= 0, index
= 0; mappingIndex
< DIRECTION_COUNT
; mappingIndex
++) {
495 if (ledConfig
->flags
& directionMappings
[mappingIndex
]) {
496 directions
[index
++] = directionCodes
[mappingIndex
];
500 sprintf(ledConfigBuffer
, "%u,%u:%s:%s:%u", GET_LED_X(ledConfig
), GET_LED_Y(ledConfig
), directions
, functions
, ledConfig
->color
);
503 void applyDirectionalModeColor(const uint8_t ledIndex
, const ledConfig_t
*ledConfig
, const modeColorIndexes_t
*modeColors
)
505 // apply up/down colors regardless of quadrant.
506 if ((ledConfig
->flags
& LED_DIRECTION_UP
)) {
507 setLedHsv(ledIndex
, &colors
[modeColors
->up
]);
510 if ((ledConfig
->flags
& LED_DIRECTION_DOWN
)) {
511 setLedHsv(ledIndex
, &colors
[modeColors
->down
]);
514 // override with n/e/s/w colors to each n/s e/w half - bail at first match.
515 if ((ledConfig
->flags
& LED_DIRECTION_WEST
) && GET_LED_X(ledConfig
) <= highestXValueForWest
) {
516 setLedHsv(ledIndex
, &colors
[modeColors
->west
]);
519 if ((ledConfig
->flags
& LED_DIRECTION_EAST
) && GET_LED_X(ledConfig
) >= lowestXValueForEast
) {
520 setLedHsv(ledIndex
, &colors
[modeColors
->east
]);
523 if ((ledConfig
->flags
& LED_DIRECTION_NORTH
) && GET_LED_Y(ledConfig
) <= highestYValueForNorth
) {
524 setLedHsv(ledIndex
, &colors
[modeColors
->north
]);
527 if ((ledConfig
->flags
& LED_DIRECTION_SOUTH
) && GET_LED_Y(ledConfig
) >= lowestYValueForSouth
) {
528 setLedHsv(ledIndex
, &colors
[modeColors
->south
]);
534 QUADRANT_NORTH_EAST
= 1,
540 void applyQuadrantColor(const uint8_t ledIndex
, const ledConfig_t
*ledConfig
, const quadrant_e quadrant
, const hsvColor_t
*color
)
543 case QUADRANT_NORTH_EAST
:
544 if (GET_LED_Y(ledConfig
) <= highestYValueForNorth
&& GET_LED_X(ledConfig
) >= lowestXValueForEast
) {
545 setLedHsv(ledIndex
, color
);
549 case QUADRANT_SOUTH_EAST
:
550 if (GET_LED_Y(ledConfig
) >= lowestYValueForSouth
&& GET_LED_X(ledConfig
) >= lowestXValueForEast
) {
551 setLedHsv(ledIndex
, color
);
555 case QUADRANT_SOUTH_WEST
:
556 if (GET_LED_Y(ledConfig
) >= lowestYValueForSouth
&& GET_LED_X(ledConfig
) <= highestXValueForWest
) {
557 setLedHsv(ledIndex
, color
);
561 case QUADRANT_NORTH_WEST
:
562 if (GET_LED_Y(ledConfig
) <= highestYValueForNorth
&& GET_LED_X(ledConfig
) <= highestXValueForWest
) {
563 setLedHsv(ledIndex
, color
);
569 void applyLedModeLayer(void)
571 const ledConfig_t
*ledConfig
;
574 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
576 ledConfig
= &ledConfigs
[ledIndex
];
578 if (!(ledConfig
->flags
& LED_FUNCTION_THRUST_RING
)) {
579 setLedHsv(ledIndex
, &hsv_black
);
582 if (!(ledConfig
->flags
& LED_FUNCTION_FLIGHT_MODE
)) {
583 if (ledConfig
->flags
& LED_FUNCTION_ARM_STATE
) {
584 if (!ARMING_FLAG(ARMED
)) {
585 setLedHsv(ledIndex
, &hsv_green
);
587 setLedHsv(ledIndex
, &hsv_blue
);
593 applyDirectionalModeColor(ledIndex
, ledConfig
, &orientationModeColors
);
595 if (FLIGHT_MODE(HEADFREE_MODE
)) {
596 applyDirectionalModeColor(ledIndex
, ledConfig
, &headfreeModeColors
);
598 } else if (FLIGHT_MODE(MAG_MODE
)) {
599 applyDirectionalModeColor(ledIndex
, ledConfig
, &magModeColors
);
602 } else if (FLIGHT_MODE(BARO_MODE
)) {
603 applyDirectionalModeColor(ledIndex
, ledConfig
, &baroModeColors
);
605 } else if (FLIGHT_MODE(HORIZON_MODE
)) {
606 applyDirectionalModeColor(ledIndex
, ledConfig
, &horizonModeColors
);
607 } else if (FLIGHT_MODE(ANGLE_MODE
)) {
608 applyDirectionalModeColor(ledIndex
, ledConfig
, &angleModeColors
);
614 WARNING_FLAG_NONE
= 0,
615 WARNING_FLAG_LOW_BATTERY
= (1 << 0),
616 WARNING_FLAG_FAILSAFE
= (1 << 1),
617 WARNING_FLAG_ARMING_DISABLED
= (1 << 2)
620 static uint8_t warningFlags
= WARNING_FLAG_NONE
;
622 void applyLedWarningLayer(uint8_t updateNow
)
624 const ledConfig_t
*ledConfig
;
626 static uint8_t warningFlashCounter
= 0;
628 if (updateNow
&& warningFlashCounter
== 0) {
629 warningFlags
= WARNING_FLAG_NONE
;
630 if (feature(FEATURE_VBAT
) && calculateBatteryState() != BATTERY_OK
) {
631 warningFlags
|= WARNING_FLAG_LOW_BATTERY
;
633 if (feature(FEATURE_FAILSAFE
) && failsafe
->vTable
->hasTimerElapsed()) {
634 warningFlags
|= WARNING_FLAG_FAILSAFE
;
636 if (!ARMING_FLAG(ARMED
) && !ARMING_FLAG(OK_TO_ARM
)) {
637 warningFlags
|= WARNING_FLAG_ARMING_DISABLED
;
641 if (warningFlags
|| warningFlashCounter
> 0) {
642 const hsvColor_t
*warningColor
= &hsv_black
;
644 if ((warningFlashCounter
& 1) == 0) {
645 if (warningFlashCounter
< 4 && (warningFlags
& WARNING_FLAG_ARMING_DISABLED
)) {
646 warningColor
= &hsv_green
;
648 if (warningFlashCounter
>= 4 && warningFlashCounter
< 12 && (warningFlags
& WARNING_FLAG_LOW_BATTERY
)) {
649 warningColor
= &hsv_red
;
651 if (warningFlashCounter
>= 12 && warningFlashCounter
< 16 && (warningFlags
& WARNING_FLAG_FAILSAFE
)) {
652 warningColor
= &hsv_yellow
;
655 if (warningFlashCounter
>= 12 && warningFlashCounter
< 16 && (warningFlags
& WARNING_FLAG_FAILSAFE
)) {
656 warningColor
= &hsv_blue
;
660 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
662 ledConfig
= &ledConfigs
[ledIndex
];
664 if (!(ledConfig
->flags
& LED_FUNCTION_WARNING
)) {
667 setLedHsv(ledIndex
, warningColor
);
671 if (updateNow
&& (warningFlags
|| warningFlashCounter
)) {
672 warningFlashCounter
++;
673 if (warningFlashCounter
== 20) {
674 warningFlashCounter
= 0;
679 #define INDICATOR_DEADBAND 25
681 void applyLedIndicatorLayer(uint8_t indicatorFlashState
)
683 const ledConfig_t
*ledConfig
;
684 static const hsvColor_t
*flashColor
;
687 if (indicatorFlashState
== 0) {
688 flashColor
= &hsv_orange
;
690 flashColor
= &hsv_black
;
695 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
697 ledConfig
= &ledConfigs
[ledIndex
];
699 if (!(ledConfig
->flags
& LED_FUNCTION_INDICATOR
)) {
703 if (rcCommand
[ROLL
] > INDICATOR_DEADBAND
) {
704 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_NORTH_EAST
, flashColor
);
705 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_SOUTH_EAST
, flashColor
);
708 if (rcCommand
[ROLL
] < -INDICATOR_DEADBAND
) {
709 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_NORTH_WEST
, flashColor
);
710 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_SOUTH_WEST
, flashColor
);
713 if (rcCommand
[PITCH
] > INDICATOR_DEADBAND
) {
714 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_NORTH_EAST
, flashColor
);
715 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_NORTH_WEST
, flashColor
);
718 if (rcCommand
[PITCH
] < -INDICATOR_DEADBAND
) {
719 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_SOUTH_EAST
, flashColor
);
720 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_SOUTH_WEST
, flashColor
);
725 void applyLedThrottleLayer()
727 const ledConfig_t
*ledConfig
;
731 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
732 ledConfig
= &ledConfigs
[ledIndex
];
733 if (!(ledConfig
->flags
& LED_FUNCTION_THROTTLE
)) {
737 getLedHsv(ledIndex
, &color
);
739 int scaled
= scaleRange(rcData
[THROTTLE
], PWM_RANGE_MIN
, PWM_RANGE_MAX
, -60, +60);
740 scaled
+= HSV_HUE_MAX
;
741 color
.h
= scaled
% HSV_HUE_MAX
;
742 setLedHsv(ledIndex
, &color
);
746 #define ROTATION_SEQUENCE_LED_COUNT 6 // 2 on, 4 off
747 #define ROTATION_SEQUENCE_LED_WIDTH 2
749 void applyLedThrustRingLayer(void)
752 static uint8_t rotationPhase
= ROTATION_SEQUENCE_LED_COUNT
;
753 bool nextLedOn
= false;
754 hsvColor_t ringColor
;
755 const ledConfig_t
*ledConfig
;
757 uint8_t ledRingIndex
= 0;
758 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
760 ledConfig
= &ledConfigs
[ledIndex
];
762 if ((ledConfig
->flags
& LED_FUNCTION_THRUST_RING
) == 0) {
766 bool applyColor
= false;
767 if (ARMING_FLAG(ARMED
)) {
768 if ((ledRingIndex
+ rotationPhase
) % ROTATION_SEQUENCE_LED_COUNT
< ROTATION_SEQUENCE_LED_WIDTH
) {
772 if (nextLedOn
== false) {
775 nextLedOn
= !nextLedOn
;
779 ringColor
= colors
[ledConfig
->color
];
781 ringColor
= hsv_black
;
784 setLedHsv(ledIndex
, &ringColor
);
790 if (rotationPhase
== 0) {
791 rotationPhase
= ROTATION_SEQUENCE_LED_COUNT
;
795 #ifdef USE_LED_ANIMATION
796 void updateLedAnimationState(void)
798 static uint8_t frameCounter
= 0;
800 static uint8_t previousRow
;
801 static uint8_t currentRow
;
802 static uint8_t nextRow
;
803 uint8_t animationFrames
= ledGridHeight
;
805 previousRow
= (frameCounter
+ animationFrames
- 1) % animationFrames
;
806 currentRow
= frameCounter
;
807 nextRow
= (frameCounter
+ 1) % animationFrames
;
809 frameCounter
= (frameCounter
+ 1) % animationFrames
;
812 static void applyLedAnimationLayer(void)
814 const ledConfig_t
*ledConfig
;
816 if (ARMING_FLAG(ARMED
)) {
821 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
823 ledConfig
= &ledConfigs
[ledIndex
];
825 if (GET_LED_Y(ledConfig
) == previousRow
) {
826 setLedHsv(ledIndex
, &white
);
827 setLedBrightness(ledIndex
, 50);
829 } else if (GET_LED_Y(ledConfig
) == currentRow
) {
830 setLedHsv(ledIndex
, &white
);
831 } else if (GET_LED_Y(ledConfig
) == nextRow
) {
832 setLedBrightness(ledIndex
, 50);
838 void updateLedStrip(void)
841 if (!(ledStripInitialised
&& isWS2811LedStripReady())) {
845 if (IS_RC_MODE_ACTIVE(BOXLEDLOW
)) {
846 if (ledStripEnabled
) {
848 ledStripEnabled
= false;
851 ledStripEnabled
= true;
854 if (!ledStripEnabled
){
859 uint32_t now
= micros();
861 bool indicatorFlashNow
= indicatorFlashNow
= (int32_t)(now
- nextIndicatorFlashAt
) >= 0L;
862 bool warningFlashNow
= warningFlashNow
= (int32_t)(now
- nextWarningFlashAt
) >= 0L;
863 bool rotationUpdateNow
= (int32_t)(now
- nextRotationUpdateAt
) >= 0L;
864 #ifdef USE_LED_ANIMATION
865 bool animationUpdateNow
= animationUpdateNow
= (int32_t)(now
- nextAnimationUpdateAt
) >= 0L;
871 #ifdef USE_LED_ANIMATION
872 || animationUpdateNow
878 static uint8_t indicatorFlashState
= 0;
882 applyLedThrottleLayer();
886 if (warningFlashNow
) {
887 nextWarningFlashAt
= now
+ LED_STRIP_10HZ
;
889 applyLedWarningLayer(warningFlashNow
);
893 if (indicatorFlashNow
) {
895 uint8_t rollScale
= abs(rcCommand
[ROLL
]) / 50;
896 uint8_t pitchScale
= abs(rcCommand
[PITCH
]) / 50;
897 uint8_t scale
= max(rollScale
, pitchScale
);
898 nextIndicatorFlashAt
= now
+ (LED_STRIP_5HZ
/ max(1, scale
));
900 if (indicatorFlashState
== 0) {
901 indicatorFlashState
= 1;
903 indicatorFlashState
= 0;
907 applyLedIndicatorLayer(indicatorFlashState
);
909 #ifdef USE_LED_ANIMATION
910 if (animationUpdateNow
) {
911 nextAnimationUpdateAt
= now
+ LED_STRIP_20HZ
;
912 updateLedAnimationState();
914 applyLedAnimationLayer();
917 if (rotationUpdateNow
) {
919 applyLedThrustRingLayer();
921 uint8_t animationSpeedScale
= 1;
923 if (ARMING_FLAG(ARMED
)) {
924 animationSpeedScale
= scaleRange(rcData
[THROTTLE
], PWM_RANGE_MIN
, PWM_RANGE_MAX
, 1, 10);
927 nextRotationUpdateAt
= now
+ LED_STRIP_5HZ
/animationSpeedScale
;
933 bool parseColor(uint8_t index
, const char *colorConfig
)
935 const char *remainingCharacters
= colorConfig
;
937 hsvColor_t
*color
= &colors
[index
];
941 uint8_t componentIndex
;
942 for (componentIndex
= 0; ok
&& componentIndex
< HSV_COLOR_COMPONENT_COUNT
; componentIndex
++) {
943 uint16_t val
= atoi(remainingCharacters
);
944 switch (componentIndex
) {
946 if (val
> HSV_HUE_MAX
) {
951 colors
[index
].h
= val
;
954 if (val
> HSV_SATURATION_MAX
) {
958 colors
[index
].s
= (uint8_t)val
;
961 if (val
> HSV_VALUE_MAX
) {
965 colors
[index
].v
= (uint8_t)val
;
968 remainingCharacters
= strstr(remainingCharacters
, ",");
969 if (remainingCharacters
) {
970 remainingCharacters
++;
972 if (componentIndex
< 2) {
979 memset(color
, 0, sizeof(hsvColor_t
));
985 void applyDefaultColors(hsvColor_t
*colors
, uint8_t colorCount
)
987 memset(colors
, 0, colorCount
* sizeof(colors
));
988 for (uint8_t colorIndex
= 0; colorIndex
< colorCount
&& colorIndex
< (sizeof(defaultColors
) / sizeof(defaultColors
[0])); colorIndex
++) {
989 *colors
++ = *defaultColors
[colorIndex
];
993 void applyDefaultLedStripConfig(ledConfig_t
*ledConfigs
)
995 memset(ledConfigs
, 0, MAX_LED_STRIP_LENGTH
* sizeof(ledConfig_t
));
996 memcpy(ledConfigs
, &defaultLedStripConfig
, sizeof(defaultLedStripConfig
));
998 reevalulateLedConfig();
1001 void ledStripInit(ledConfig_t
*ledConfigsToUse
, hsvColor_t
*colorsToUse
, failsafe_t
* failsafeToUse
)
1003 ledConfigs
= ledConfigsToUse
;
1004 colors
= colorsToUse
;
1005 failsafe
= failsafeToUse
;
1006 ledStripInitialised
= false;
1009 void ledStripEnable(void)
1011 reevalulateLedConfig();
1012 ledStripInitialised
= true;
1014 ws2811LedStripInit();
1017 static void ledStripDisable(void)
1019 setStripColor(&hsv_black
);
1021 ws2811UpdateStrip();