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>
31 #include <common/maths.h>
32 #include <common/typeconversion.h>
34 #include "drivers/light_ws2811strip.h"
35 #include "drivers/system.h"
36 #include "drivers/serial.h"
38 #include <common/printf.h>
40 #include "sensors/battery.h"
42 #include "io/rc_controls.h"
43 #include "io/ledstrip.h"
47 #include "flight/failsafe.h"
49 #include "config/runtime_config.h"
50 #include "config/config.h"
52 static bool ledStripInitialised
= false;
53 static bool ledStripEnabled
= true;
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"
78 #define LED_BLACK { 0, 0, 0}
79 #define LED_WHITE { 0, 255, 255}
80 #define LED_RED { 0, 0, 255}
81 #define LED_ORANGE { 30, 0, 255}
82 #define LED_YELLOW { 60, 0, 255}
83 #define LED_LIME_GREEN { 90, 0, 255}
84 #define LED_GREEN {120, 0, 255}
85 #define LED_MINT_GREEN {150, 0, 255}
86 #define LED_CYAN {180, 0, 255}
87 #define LED_LIGHT_BLUE {210, 0, 255}
88 #define LED_BLUE {240, 0, 255}
89 #define LED_DARK_VIOLET {270, 0, 255}
90 #define LED_MAGENTA {300, 0, 255}
91 #define LED_DEEP_PINK {330, 0, 255}
93 const hsvColor_t hsv_black
= LED_BLACK
;
94 const hsvColor_t hsv_white
= LED_WHITE
;
95 const hsvColor_t hsv_red
= LED_RED
;
96 const hsvColor_t hsv_orange
= LED_ORANGE
;
97 const hsvColor_t hsv_yellow
= LED_YELLOW
;
98 const hsvColor_t hsv_limeGreen
= LED_LIME_GREEN
;
99 const hsvColor_t hsv_green
= LED_GREEN
;
100 const hsvColor_t hsv_mintGreen
= LED_MINT_GREEN
;
101 const hsvColor_t hsv_cyan
= LED_CYAN
;
102 const hsvColor_t hsv_lightBlue
= LED_LIGHT_BLUE
;
103 const hsvColor_t hsv_blue
= LED_BLUE
;
104 const hsvColor_t hsv_darkViolet
= LED_DARK_VIOLET
;
105 const hsvColor_t hsv_magenta
= LED_MAGENTA
;
106 const hsvColor_t hsv_deepPink
= LED_DEEP_PINK
;
108 #define LED_DIRECTION_COUNT 6
110 const hsvColor_t
* const defaultColors
[] = {
153 typedef struct modeColorIndexes_s
{
160 } modeColorIndexes_t
;
163 // Note, the color index used for the mode colors below refer to the default colors.
164 // if the colors are reconfigured the index is still valid but the displayed color might
166 // See colors[] and defaultColors[] and applyDefaultColors[]
168 static const modeColorIndexes_t orientationModeColors
= {
177 static const modeColorIndexes_t headfreeModeColors
= {
186 static const modeColorIndexes_t horizonModeColors
= {
195 static const modeColorIndexes_t angleModeColors
= {
205 static const modeColorIndexes_t magModeColors
= {
215 static const modeColorIndexes_t navigationModeColors
= {
225 uint8_t ledGridWidth
;
226 uint8_t ledGridHeight
;
228 uint8_t ledsInRingCount
;
230 ledConfig_t
*ledConfigs
;
234 #ifdef USE_LED_RING_DEFAULT_CONFIG
235 const ledConfig_t defaultLedStripConfig
[] = {
236 { CALCULATE_LED_XY( 2, 2), 3, LED_FUNCTION_THRUST_RING
},
237 { CALCULATE_LED_XY( 2, 1), 3, LED_FUNCTION_THRUST_RING
},
238 { CALCULATE_LED_XY( 2, 0), 3, LED_FUNCTION_THRUST_RING
},
239 { CALCULATE_LED_XY( 1, 0), 3, LED_FUNCTION_THRUST_RING
},
240 { CALCULATE_LED_XY( 0, 0), 3, LED_FUNCTION_THRUST_RING
},
241 { CALCULATE_LED_XY( 0, 1), 3, LED_FUNCTION_THRUST_RING
},
242 { CALCULATE_LED_XY( 0, 2), 3, LED_FUNCTION_THRUST_RING
},
243 { CALCULATE_LED_XY( 1, 2), 3, LED_FUNCTION_THRUST_RING
},
244 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING
},
245 { CALCULATE_LED_XY( 1, 1), 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
},
250 const ledConfig_t defaultLedStripConfig
[] = {
251 { CALCULATE_LED_XY(15, 15), 0, LED_DIRECTION_SOUTH
| LED_DIRECTION_EAST
| LED_FUNCTION_INDICATOR
| LED_FUNCTION_ARM_STATE
},
253 { CALCULATE_LED_XY(15, 8), 0, LED_DIRECTION_EAST
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
254 { CALCULATE_LED_XY(15, 7), 0, LED_DIRECTION_EAST
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
256 { CALCULATE_LED_XY(15, 0), 0, LED_DIRECTION_NORTH
| LED_DIRECTION_EAST
| LED_FUNCTION_INDICATOR
| LED_FUNCTION_ARM_STATE
},
258 { CALCULATE_LED_XY( 8, 0), 0, LED_DIRECTION_NORTH
| LED_FUNCTION_FLIGHT_MODE
},
259 { CALCULATE_LED_XY( 7, 0), 0, LED_DIRECTION_NORTH
| LED_FUNCTION_FLIGHT_MODE
},
261 { CALCULATE_LED_XY( 0, 0), 0, LED_DIRECTION_NORTH
| LED_DIRECTION_WEST
| LED_FUNCTION_INDICATOR
| LED_FUNCTION_ARM_STATE
},
263 { CALCULATE_LED_XY( 0, 7), 0, LED_DIRECTION_WEST
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
264 { CALCULATE_LED_XY( 0, 8), 0, LED_DIRECTION_WEST
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
266 { CALCULATE_LED_XY( 0, 15), 0, LED_DIRECTION_SOUTH
| LED_DIRECTION_WEST
| LED_FUNCTION_INDICATOR
| LED_FUNCTION_ARM_STATE
},
268 { CALCULATE_LED_XY( 7, 15), 0, LED_DIRECTION_SOUTH
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
269 { CALCULATE_LED_XY( 8, 15), 0, LED_DIRECTION_SOUTH
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
271 { CALCULATE_LED_XY( 7, 7), 0, LED_DIRECTION_UP
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
272 { CALCULATE_LED_XY( 8, 7), 0, LED_DIRECTION_UP
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
273 { CALCULATE_LED_XY( 7, 8), 0, LED_DIRECTION_DOWN
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
274 { CALCULATE_LED_XY( 8, 8), 0, LED_DIRECTION_DOWN
| LED_FUNCTION_FLIGHT_MODE
| LED_FUNCTION_WARNING
},
276 { CALCULATE_LED_XY( 8, 9), 3, LED_FUNCTION_THRUST_RING
},
277 { CALCULATE_LED_XY( 9, 10), 3, LED_FUNCTION_THRUST_RING
},
278 { CALCULATE_LED_XY(10, 11), 3, LED_FUNCTION_THRUST_RING
},
279 { CALCULATE_LED_XY(10, 12), 3, LED_FUNCTION_THRUST_RING
},
280 { CALCULATE_LED_XY( 9, 13), 3, LED_FUNCTION_THRUST_RING
},
281 { CALCULATE_LED_XY( 8, 14), 3, LED_FUNCTION_THRUST_RING
},
282 { CALCULATE_LED_XY( 7, 14), 3, LED_FUNCTION_THRUST_RING
},
283 { CALCULATE_LED_XY( 6, 13), 3, LED_FUNCTION_THRUST_RING
},
284 { CALCULATE_LED_XY( 5, 12), 3, LED_FUNCTION_THRUST_RING
},
285 { CALCULATE_LED_XY( 5, 11), 3, LED_FUNCTION_THRUST_RING
},
286 { CALCULATE_LED_XY( 6, 10), 3, LED_FUNCTION_THRUST_RING
},
287 { CALCULATE_LED_XY( 7, 9), 3, LED_FUNCTION_THRUST_RING
},
299 * 16 * 32 leds = 512 bytes storage needed worst case.
300 * = not efficient to store led configs as strings in flash.
301 * = becomes a problem to send all the data via cli due to serial/cli buffers
312 #define PARSE_STATE_COUNT 5
314 static const char chunkSeparators
[PARSE_STATE_COUNT
] = {',', ':', ':',':', '\0' };
316 static const char directionCodes
[] = { 'N', 'E', 'S', 'W', 'U', 'D' };
317 #define DIRECTION_COUNT (sizeof(directionCodes) / sizeof(directionCodes[0]))
318 static const uint8_t directionMappings
[DIRECTION_COUNT
] = {
327 static const char functionCodes
[] = { 'I', 'W', 'F', 'A', 'T', 'R', 'C' };
328 #define FUNCTION_COUNT (sizeof(functionCodes) / sizeof(functionCodes[0]))
329 static const uint16_t functionMappings
[FUNCTION_COUNT
] = {
330 LED_FUNCTION_INDICATOR
,
331 LED_FUNCTION_WARNING
,
332 LED_FUNCTION_FLIGHT_MODE
,
333 LED_FUNCTION_ARM_STATE
,
334 LED_FUNCTION_THROTTLE
,
335 LED_FUNCTION_THRUST_RING
,
340 uint8_t highestYValueForNorth
;
341 uint8_t lowestYValueForSouth
;
342 uint8_t highestXValueForWest
;
343 uint8_t lowestXValueForEast
;
345 void determineLedStripDimensions(void)
351 const ledConfig_t
*ledConfig
;
353 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
354 ledConfig
= &ledConfigs
[ledIndex
];
356 if (GET_LED_X(ledConfig
) >= ledGridWidth
) {
357 ledGridWidth
= GET_LED_X(ledConfig
) + 1;
359 if (GET_LED_Y(ledConfig
) >= ledGridHeight
) {
360 ledGridHeight
= GET_LED_Y(ledConfig
) + 1;
365 void determineOrientationLimits(void)
367 bool isOddHeight
= (ledGridHeight
& 1);
368 bool isOddWidth
= (ledGridWidth
& 1);
369 uint8_t heightModifier
= isOddHeight
? 1 : 0;
370 uint8_t widthModifier
= isOddWidth
? 1 : 0;
372 highestYValueForNorth
= (ledGridHeight
/ 2) - 1;
373 lowestYValueForSouth
= (ledGridHeight
/ 2) + heightModifier
;
374 highestXValueForWest
= (ledGridWidth
/ 2) - 1;
375 lowestXValueForEast
= (ledGridWidth
/ 2) + widthModifier
;
378 void updateLedCount(void)
380 const ledConfig_t
*ledConfig
;
385 if( ledConfigs
== 0 ){
389 for (ledIndex
= 0; ledIndex
< MAX_LED_STRIP_LENGTH
; ledIndex
++) {
391 ledConfig
= &ledConfigs
[ledIndex
];
393 if (ledConfig
->flags
== 0 && ledConfig
->xy
== 0) {
399 if ((ledConfig
->flags
& LED_FUNCTION_THRUST_RING
)) {
405 void reevalulateLedConfig(void)
408 determineLedStripDimensions();
409 determineOrientationLimits();
412 #define CHUNK_BUFFER_SIZE 10
414 #define NEXT_PARSE_STATE(parseState) ((parseState + 1) % PARSE_STATE_COUNT)
417 bool parseLedStripConfig(uint8_t ledIndex
, const char *config
)
419 char chunk
[CHUNK_BUFFER_SIZE
];
423 uint8_t parseState
= X_COORDINATE
;
426 if (ledIndex
>= MAX_LED_STRIP_LENGTH
) {
430 ledConfig_t
*ledConfig
= &ledConfigs
[ledIndex
];
431 memset(ledConfig
, 0, sizeof(ledConfig_t
));
435 char chunkSeparator
= chunkSeparators
[parseState
];
437 memset(&chunk
, 0, sizeof(chunk
));
440 while (*config
&& chunkIndex
< CHUNK_BUFFER_SIZE
&& *config
!= chunkSeparator
) {
441 chunk
[chunkIndex
++] = *config
++;
444 if (*config
++ != chunkSeparator
) {
449 switch((parseState_e
)parseState
) {
452 ledConfig
->xy
|= CALCULATE_LED_X(val
);
456 ledConfig
->xy
|= CALCULATE_LED_Y(val
);
459 for (chunkIndex
= 0; chunk
[chunkIndex
] && chunkIndex
< CHUNK_BUFFER_SIZE
; chunkIndex
++) {
460 for (uint8_t mappingIndex
= 0; mappingIndex
< DIRECTION_COUNT
; mappingIndex
++) {
461 if (directionCodes
[mappingIndex
] == chunk
[chunkIndex
]) {
462 ledConfig
->flags
|= directionMappings
[mappingIndex
];
469 for (chunkIndex
= 0; chunk
[chunkIndex
] && chunkIndex
< CHUNK_BUFFER_SIZE
; chunkIndex
++) {
470 for (uint8_t mappingIndex
= 0; mappingIndex
< FUNCTION_COUNT
; mappingIndex
++) {
471 if (functionCodes
[mappingIndex
] == chunk
[chunkIndex
]) {
472 ledConfig
->flags
|= functionMappings
[mappingIndex
];
479 if (atoi(chunk
) < CONFIGURABLE_COLOR_COUNT
) {
480 ledConfig
->color
= atoi(chunk
);
482 ledConfig
->color
= 0;
490 if (parseState
>= PARSE_STATE_COUNT
) {
496 memset(ledConfig
, 0, sizeof(ledConfig_t
));
499 reevalulateLedConfig();
504 void generateLedConfig(uint8_t ledIndex
, char *ledConfigBuffer
, size_t bufferSize
)
506 char functions
[FUNCTION_COUNT
];
507 char directions
[DIRECTION_COUNT
];
509 uint8_t mappingIndex
;
511 ledConfig_t
*ledConfig
= &ledConfigs
[ledIndex
];
513 memset(ledConfigBuffer
, 0, bufferSize
);
514 memset(&functions
, 0, sizeof(functions
));
515 memset(&directions
, 0, sizeof(directions
));
517 for (mappingIndex
= 0, index
= 0; mappingIndex
< FUNCTION_COUNT
; mappingIndex
++) {
518 if (ledConfig
->flags
& functionMappings
[mappingIndex
]) {
519 functions
[index
++] = functionCodes
[mappingIndex
];
523 for (mappingIndex
= 0, index
= 0; mappingIndex
< DIRECTION_COUNT
; mappingIndex
++) {
524 if (ledConfig
->flags
& directionMappings
[mappingIndex
]) {
525 directions
[index
++] = directionCodes
[mappingIndex
];
529 sprintf(ledConfigBuffer
, "%u,%u:%s:%s:%u", GET_LED_X(ledConfig
), GET_LED_Y(ledConfig
), directions
, functions
, ledConfig
->color
);
532 void applyDirectionalModeColor(const uint8_t ledIndex
, const ledConfig_t
*ledConfig
, const modeColorIndexes_t
*modeColors
)
534 // apply up/down colors regardless of quadrant.
535 if ((ledConfig
->flags
& LED_DIRECTION_UP
)) {
536 setLedHsv(ledIndex
, &colors
[modeColors
->up
]);
539 if ((ledConfig
->flags
& LED_DIRECTION_DOWN
)) {
540 setLedHsv(ledIndex
, &colors
[modeColors
->down
]);
543 // override with n/e/s/w colors to each n/s e/w half - bail at first match.
544 if ((ledConfig
->flags
& LED_DIRECTION_WEST
) && GET_LED_X(ledConfig
) <= highestXValueForWest
) {
545 setLedHsv(ledIndex
, &colors
[modeColors
->west
]);
548 if ((ledConfig
->flags
& LED_DIRECTION_EAST
) && GET_LED_X(ledConfig
) >= lowestXValueForEast
) {
549 setLedHsv(ledIndex
, &colors
[modeColors
->east
]);
552 if ((ledConfig
->flags
& LED_DIRECTION_NORTH
) && GET_LED_Y(ledConfig
) <= highestYValueForNorth
) {
553 setLedHsv(ledIndex
, &colors
[modeColors
->north
]);
556 if ((ledConfig
->flags
& LED_DIRECTION_SOUTH
) && GET_LED_Y(ledConfig
) >= lowestYValueForSouth
) {
557 setLedHsv(ledIndex
, &colors
[modeColors
->south
]);
563 QUADRANT_NORTH_EAST
= 1,
569 void applyQuadrantColor(const uint8_t ledIndex
, const ledConfig_t
*ledConfig
, const quadrant_e quadrant
, const hsvColor_t
*color
)
572 case QUADRANT_NORTH_EAST
:
573 if (GET_LED_Y(ledConfig
) <= highestYValueForNorth
&& GET_LED_X(ledConfig
) >= lowestXValueForEast
) {
574 setLedHsv(ledIndex
, color
);
578 case QUADRANT_SOUTH_EAST
:
579 if (GET_LED_Y(ledConfig
) >= lowestYValueForSouth
&& GET_LED_X(ledConfig
) >= lowestXValueForEast
) {
580 setLedHsv(ledIndex
, color
);
584 case QUADRANT_SOUTH_WEST
:
585 if (GET_LED_Y(ledConfig
) >= lowestYValueForSouth
&& GET_LED_X(ledConfig
) <= highestXValueForWest
) {
586 setLedHsv(ledIndex
, color
);
590 case QUADRANT_NORTH_WEST
:
591 if (GET_LED_Y(ledConfig
) <= highestYValueForNorth
&& GET_LED_X(ledConfig
) <= highestXValueForWest
) {
592 setLedHsv(ledIndex
, color
);
598 void applyLedModeLayer(void)
600 const ledConfig_t
*ledConfig
;
603 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
605 ledConfig
= &ledConfigs
[ledIndex
];
607 if (!(ledConfig
->flags
& LED_FUNCTION_THRUST_RING
)) {
608 if (ledConfig
->flags
& LED_FUNCTION_COLOR
) {
609 setLedHsv(ledIndex
, &colors
[ledConfig
->color
]);
611 setLedHsv(ledIndex
, &hsv_black
);
615 if (!(ledConfig
->flags
& LED_FUNCTION_FLIGHT_MODE
)) {
616 if (ledConfig
->flags
& LED_FUNCTION_ARM_STATE
) {
617 if (!ARMING_FLAG(ARMED
)) {
618 setLedHsv(ledIndex
, &hsv_green
);
620 setLedHsv(ledIndex
, &hsv_blue
);
626 applyDirectionalModeColor(ledIndex
, ledConfig
, &orientationModeColors
);
628 if (FLIGHT_MODE(HEADFREE_MODE
)) {
629 applyDirectionalModeColor(ledIndex
, ledConfig
, &headfreeModeColors
);
631 } else if (FLIGHT_MODE(MAG_MODE
)) {
632 applyDirectionalModeColor(ledIndex
, ledConfig
, &magModeColors
);
635 } else if (FLIGHT_MODE(NAV_ALTHOLD_MODE
| NAV_POSHOLD_MODE
| NAV_RTH_MODE
| NAV_WP_MODE
)) {
636 applyDirectionalModeColor(ledIndex
, ledConfig
, &navigationModeColors
);
638 } else if (FLIGHT_MODE(HORIZON_MODE
)) {
639 applyDirectionalModeColor(ledIndex
, ledConfig
, &horizonModeColors
);
640 } else if (FLIGHT_MODE(ANGLE_MODE
)) {
641 applyDirectionalModeColor(ledIndex
, ledConfig
, &angleModeColors
);
647 WARNING_FLAG_NONE
= 0,
648 WARNING_FLAG_LOW_BATTERY
= (1 << 0),
649 WARNING_FLAG_FAILSAFE
= (1 << 1),
650 WARNING_FLAG_ARMING_DISABLED
= (1 << 2)
653 static uint8_t warningFlags
= WARNING_FLAG_NONE
;
655 void applyLedWarningLayer(uint8_t updateNow
)
657 const ledConfig_t
*ledConfig
;
659 static uint8_t warningFlashCounter
= 0;
661 if (updateNow
&& warningFlashCounter
== 0) {
662 warningFlags
= WARNING_FLAG_NONE
;
663 if (feature(FEATURE_VBAT
) && getBatteryState() != BATTERY_OK
) {
664 warningFlags
|= WARNING_FLAG_LOW_BATTERY
;
666 if (feature(FEATURE_FAILSAFE
) && failsafeIsActive()) {
667 warningFlags
|= WARNING_FLAG_FAILSAFE
;
669 if (!ARMING_FLAG(ARMED
) && !ARMING_FLAG(OK_TO_ARM
)) {
670 warningFlags
|= WARNING_FLAG_ARMING_DISABLED
;
674 if (warningFlags
|| warningFlashCounter
> 0) {
675 const hsvColor_t
*warningColor
= &hsv_black
;
677 if ((warningFlashCounter
& 1) == 0) {
678 if (warningFlashCounter
< 4 && (warningFlags
& WARNING_FLAG_ARMING_DISABLED
)) {
679 warningColor
= &hsv_green
;
681 if (warningFlashCounter
>= 4 && warningFlashCounter
< 12 && (warningFlags
& WARNING_FLAG_LOW_BATTERY
)) {
682 warningColor
= &hsv_red
;
684 if (warningFlashCounter
>= 12 && warningFlashCounter
< 16 && (warningFlags
& WARNING_FLAG_FAILSAFE
)) {
685 warningColor
= &hsv_yellow
;
688 if (warningFlashCounter
>= 12 && warningFlashCounter
< 16 && (warningFlags
& WARNING_FLAG_FAILSAFE
)) {
689 warningColor
= &hsv_blue
;
693 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
695 ledConfig
= &ledConfigs
[ledIndex
];
697 if (!(ledConfig
->flags
& LED_FUNCTION_WARNING
)) {
700 setLedHsv(ledIndex
, warningColor
);
704 if (updateNow
&& (warningFlags
|| warningFlashCounter
)) {
705 warningFlashCounter
++;
706 if (warningFlashCounter
== 20) {
707 warningFlashCounter
= 0;
712 #define INDICATOR_DEADBAND 25
714 void applyLedIndicatorLayer(uint8_t indicatorFlashState
)
716 const ledConfig_t
*ledConfig
;
717 static const hsvColor_t
*flashColor
;
719 if (!rxIsReceivingSignal()) {
723 if (indicatorFlashState
== 0) {
724 flashColor
= &hsv_orange
;
726 flashColor
= &hsv_black
;
731 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
733 ledConfig
= &ledConfigs
[ledIndex
];
735 if (!(ledConfig
->flags
& LED_FUNCTION_INDICATOR
)) {
739 if (rcCommand
[ROLL
] > INDICATOR_DEADBAND
) {
740 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_NORTH_EAST
, flashColor
);
741 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_SOUTH_EAST
, flashColor
);
744 if (rcCommand
[ROLL
] < -INDICATOR_DEADBAND
) {
745 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_NORTH_WEST
, flashColor
);
746 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_SOUTH_WEST
, flashColor
);
749 if (rcCommand
[PITCH
] > INDICATOR_DEADBAND
) {
750 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_NORTH_EAST
, flashColor
);
751 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_NORTH_WEST
, flashColor
);
754 if (rcCommand
[PITCH
] < -INDICATOR_DEADBAND
) {
755 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_SOUTH_EAST
, flashColor
);
756 applyQuadrantColor(ledIndex
, ledConfig
, QUADRANT_SOUTH_WEST
, flashColor
);
761 void applyLedThrottleLayer()
763 const ledConfig_t
*ledConfig
;
767 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
768 ledConfig
= &ledConfigs
[ledIndex
];
769 if (!(ledConfig
->flags
& LED_FUNCTION_THROTTLE
)) {
773 getLedHsv(ledIndex
, &color
);
775 int scaled
= scaleRange(rcData
[THROTTLE
], PWM_RANGE_MIN
, PWM_RANGE_MAX
, -60, +60);
776 scaled
+= HSV_HUE_MAX
;
777 color
.h
= (color
.h
+ scaled
) % HSV_HUE_MAX
;
778 setLedHsv(ledIndex
, &color
);
782 #define ROTATION_SEQUENCE_LED_COUNT 6 // 2 on, 4 off
783 #define ROTATION_SEQUENCE_LED_WIDTH 2
785 void applyLedThrustRingLayer(void)
788 static uint8_t rotationPhase
= ROTATION_SEQUENCE_LED_COUNT
;
789 bool nextLedOn
= false;
790 hsvColor_t ringColor
;
791 const ledConfig_t
*ledConfig
;
793 uint8_t ledRingIndex
= 0;
794 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
796 ledConfig
= &ledConfigs
[ledIndex
];
798 if ((ledConfig
->flags
& LED_FUNCTION_THRUST_RING
) == 0) {
802 bool applyColor
= false;
803 if (ARMING_FLAG(ARMED
)) {
804 if ((ledRingIndex
+ rotationPhase
) % ROTATION_SEQUENCE_LED_COUNT
< ROTATION_SEQUENCE_LED_WIDTH
) {
808 if (nextLedOn
== false) {
811 nextLedOn
= !nextLedOn
;
815 ringColor
= colors
[ledConfig
->color
];
817 ringColor
= hsv_black
;
820 setLedHsv(ledIndex
, &ringColor
);
826 if (rotationPhase
== 0) {
827 rotationPhase
= ROTATION_SEQUENCE_LED_COUNT
;
831 #ifdef USE_LED_ANIMATION
832 static uint8_t previousRow
;
833 static uint8_t currentRow
;
834 static uint8_t nextRow
;
836 void updateLedAnimationState(void)
838 static uint8_t frameCounter
= 0;
840 uint8_t animationFrames
= ledGridHeight
;
842 previousRow
= (frameCounter
+ animationFrames
- 1) % animationFrames
;
843 currentRow
= frameCounter
;
844 nextRow
= (frameCounter
+ 1) % animationFrames
;
846 frameCounter
= (frameCounter
+ 1) % animationFrames
;
849 static void applyLedAnimationLayer(void)
851 const ledConfig_t
*ledConfig
;
853 if (ARMING_FLAG(ARMED
)) {
858 for (ledIndex
= 0; ledIndex
< ledCount
; ledIndex
++) {
860 ledConfig
= &ledConfigs
[ledIndex
];
862 if (GET_LED_Y(ledConfig
) == previousRow
) {
863 setLedHsv(ledIndex
, &hsv_white
);
864 scaleLedValue(ledIndex
, 50);
866 } else if (GET_LED_Y(ledConfig
) == currentRow
) {
867 setLedHsv(ledIndex
, &hsv_white
);
868 } else if (GET_LED_Y(ledConfig
) == nextRow
) {
869 scaleLedValue(ledIndex
, 50);
875 void updateLedStrip(void)
878 if (!(ledStripInitialised
&& isWS2811LedStripReady())) {
882 if (IS_RC_MODE_ACTIVE(BOXLEDLOW
)) {
883 if (ledStripEnabled
) {
885 ledStripEnabled
= false;
888 ledStripEnabled
= true;
891 if (!ledStripEnabled
){
896 uint32_t now
= micros();
898 bool indicatorFlashNow
= (int32_t)(now
- nextIndicatorFlashAt
) >= 0L;
899 bool warningFlashNow
= (int32_t)(now
- nextWarningFlashAt
) >= 0L;
900 bool rotationUpdateNow
= (int32_t)(now
- nextRotationUpdateAt
) >= 0L;
901 #ifdef USE_LED_ANIMATION
902 bool animationUpdateNow
= (int32_t)(now
- nextAnimationUpdateAt
) >= 0L;
908 #ifdef USE_LED_ANIMATION
909 || animationUpdateNow
915 static uint8_t indicatorFlashState
= 0;
919 applyLedThrottleLayer();
923 if (warningFlashNow
) {
924 nextWarningFlashAt
= now
+ LED_STRIP_10HZ
;
926 applyLedWarningLayer(warningFlashNow
);
930 if (indicatorFlashNow
) {
932 uint8_t rollScale
= ABS(rcCommand
[ROLL
]) / 50;
933 uint8_t pitchScale
= ABS(rcCommand
[PITCH
]) / 50;
934 uint8_t scale
= MAX(rollScale
, pitchScale
);
935 nextIndicatorFlashAt
= now
+ (LED_STRIP_5HZ
/ MAX(1, scale
));
937 if (indicatorFlashState
== 0) {
938 indicatorFlashState
= 1;
940 indicatorFlashState
= 0;
944 applyLedIndicatorLayer(indicatorFlashState
);
946 #ifdef USE_LED_ANIMATION
947 if (animationUpdateNow
) {
948 nextAnimationUpdateAt
= now
+ LED_STRIP_20HZ
;
949 updateLedAnimationState();
951 applyLedAnimationLayer();
954 if (rotationUpdateNow
) {
956 applyLedThrustRingLayer();
958 uint8_t animationSpeedScale
= 1;
960 if (ARMING_FLAG(ARMED
)) {
961 animationSpeedScale
= scaleRange(rcData
[THROTTLE
], PWM_RANGE_MIN
, PWM_RANGE_MAX
, 1, 10);
964 nextRotationUpdateAt
= now
+ LED_STRIP_5HZ
/animationSpeedScale
;
970 bool parseColor(uint8_t index
, const char *colorConfig
)
972 const char *remainingCharacters
= colorConfig
;
974 hsvColor_t
*color
= &colors
[index
];
978 uint8_t componentIndex
;
979 for (componentIndex
= 0; ok
&& componentIndex
< HSV_COLOR_COMPONENT_COUNT
; componentIndex
++) {
980 uint16_t val
= atoi(remainingCharacters
);
981 switch (componentIndex
) {
983 if (val
> HSV_HUE_MAX
) {
988 colors
[index
].h
= val
;
991 if (val
> HSV_SATURATION_MAX
) {
995 colors
[index
].s
= (uint8_t)val
;
998 if (val
> HSV_VALUE_MAX
) {
1002 colors
[index
].v
= (uint8_t)val
;
1005 remainingCharacters
= strstr(remainingCharacters
, ",");
1006 if (remainingCharacters
) {
1007 remainingCharacters
++;
1009 if (componentIndex
< 2) {
1016 memset(color
, 0, sizeof(hsvColor_t
));
1022 void applyDefaultColors(hsvColor_t
*colors
, uint8_t colorCount
)
1024 memset(colors
, 0, colorCount
* sizeof(hsvColor_t
));
1025 for (uint8_t colorIndex
= 0; colorIndex
< colorCount
&& colorIndex
< (sizeof(defaultColors
) / sizeof(defaultColors
[0])); colorIndex
++) {
1026 *colors
++ = *defaultColors
[colorIndex
];
1030 void applyDefaultLedStripConfig(ledConfig_t
*ledConfigs
)
1032 memset(ledConfigs
, 0, MAX_LED_STRIP_LENGTH
* sizeof(ledConfig_t
));
1033 memcpy(ledConfigs
, &defaultLedStripConfig
, sizeof(defaultLedStripConfig
));
1035 reevalulateLedConfig();
1038 void ledStripInit(ledConfig_t
*ledConfigsToUse
, hsvColor_t
*colorsToUse
)
1040 ledConfigs
= ledConfigsToUse
;
1041 colors
= colorsToUse
;
1042 ledStripInitialised
= false;
1045 void ledStripEnable(void)
1047 reevalulateLedConfig();
1048 ledStripInitialised
= true;
1050 ws2811LedStripInit();
1053 static void ledStripDisable(void)
1055 setStripColor(&hsv_black
);
1057 ws2811UpdateStrip();