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"
37 #include "drivers/sensor.h"
38 #include "drivers/accgyro.h"
39 #include "drivers/gpio.h"
40 #include "drivers/timer.h"
41 #include "drivers/pwm_rx.h"
43 #include <common/printf.h>
44 #include "common/axis.h"
45 #include "common/utils.h"
47 #include "io/rc_controls.h"
49 #include "sensors/battery.h"
50 #include "sensors/sensors.h"
51 #include "sensors/boardalignment.h"
52 #include "sensors/gyro.h"
53 #include "sensors/acceleration.h"
54 #include "sensors/barometer.h"
56 #include "io/ledstrip.h"
57 #include "io/beeper.h"
58 #include "io/escservo.h"
59 #include "io/gimbal.h"
60 #include "io/serial.h"
67 #include "flight/failsafe.h"
68 #include "flight/mixer.h"
69 #include "flight/pid.h"
70 #include "flight/imu.h"
71 #include "flight/navigation.h"
73 #include "telemetry/telemetry.h"
75 #include "config/runtime_config.h"
76 #include "config/config.h"
77 #include "config/config_profile.h"
78 #include "config/config_master.h"
81 PG_REGISTER_ARR_WITH_RESET_FN(ledConfig_t, LED_MAX_STRIP_LENGTH, ledConfigs, PG_LED_STRIP_CONFIG, 0);
82 PG_REGISTER_ARR_WITH_RESET_FN(hsvColor_t, LED_CONFIGURABLE_COLOR_COUNT, colors, PG_COLOR_CONFIG, 0);
83 PG_REGISTER_ARR_WITH_RESET_FN(modeColorIndexes_t, LED_MODE_COUNT, modeColors, PG_MODE_COLOR_CONFIG, 0);
84 PG_REGISTER_WITH_RESET_FN(specialColorIndexes_t, specialColors, PG_SPECIAL_COLOR_CONFIG, 0);
87 static bool ledStripInitialised
= false;
88 static bool ledStripEnabled
= true;
90 static void ledStripDisable(void);
92 //#define USE_LED_ANIMATION
93 //#define USE_LED_RING_DEFAULT_CONFIG
95 #define LED_STRIP_HZ(hz) ((int32_t)((1000 * 1000) / (hz)))
96 #define LED_STRIP_MS(ms) ((int32_t)(1000 * (ms)))
98 #if LED_MAX_STRIP_LENGTH > WS2811_LED_STRIP_LENGTH
99 # error "Led strip length must match driver"
119 const hsvColor_t hsv
[] = {
121 [COLOR_BLACK
] = { 0, 0, 0},
122 [COLOR_WHITE
] = { 0, 255, 255},
123 [COLOR_RED
] = { 0, 0, 255},
124 [COLOR_ORANGE
] = { 30, 0, 255},
125 [COLOR_YELLOW
] = { 60, 0, 255},
126 [COLOR_LIME_GREEN
] = { 90, 0, 255},
127 [COLOR_GREEN
] = {120, 0, 255},
128 [COLOR_MINT_GREEN
] = {150, 0, 255},
129 [COLOR_CYAN
] = {180, 0, 255},
130 [COLOR_LIGHT_BLUE
] = {210, 0, 255},
131 [COLOR_BLUE
] = {240, 0, 255},
132 [COLOR_DARK_VIOLET
] = {270, 0, 255},
133 [COLOR_MAGENTA
] = {300, 0, 255},
134 [COLOR_DEEP_PINK
] = {330, 0, 255},
136 // macro to save typing on default colors
137 #define HSV(color) (hsv[COLOR_ ## color])
139 STATIC_UNIT_TESTED
uint8_t ledGridWidth
;
140 STATIC_UNIT_TESTED
uint8_t ledGridHeight
;
142 STATIC_UNIT_TESTED
uint8_t highestYValueForNorth
;
143 STATIC_UNIT_TESTED
uint8_t lowestYValueForSouth
;
144 STATIC_UNIT_TESTED
uint8_t highestXValueForWest
;
145 STATIC_UNIT_TESTED
uint8_t lowestXValueForEast
;
147 STATIC_UNIT_TESTED ledCounts_t ledCounts
;
149 // macro for initializer
150 #define LF(name) LED_FUNCTION_ ## name
151 #define LO(name) LED_FLAG_OVERLAY(LED_OVERLAY_ ## name)
152 #define LD(name) LED_FLAG_DIRECTION(LED_DIRECTION_ ## name)
154 #ifdef USE_LED_RING_DEFAULT_CONFIG
155 static const ledConfig_t defaultLedStripConfig
[] = {
156 DEFINE_LED( 2, 2, 3, 0, LF(THRUST_RING
), 0, 0),
157 DEFINE_LED( 2, 1, 3, 0, LF(THRUST_RING
), 0, 0),
158 DEFINE_LED( 2, 0, 3, 0, LF(THRUST_RING
), 0, 0),
159 DEFINE_LED( 1, 0, 3, 0, LF(THRUST_RING
), 0, 0),
160 DEFINE_LED( 0, 0, 3, 0, LF(THRUST_RING
), 0, 0),
161 DEFINE_LED( 0, 1, 3, 0, LF(THRUST_RING
), 0, 0),
162 DEFINE_LED( 0, 2, 3, 0, LF(THRUST_RING
), 0, 0),
163 DEFINE_LED( 1, 2, 3, 0, LF(THRUST_RING
), 0, 0),
164 DEFINE_LED( 1, 1, 3, 0, LF(THRUST_RING
), 0, 0),
165 DEFINE_LED( 1, 1, 3, 0, LF(THRUST_RING
), 0, 0),
166 DEFINE_LED( 1, 1, 3, 0, LF(THRUST_RING
), 0, 0),
167 DEFINE_LED( 1, 1, 3, 0, LF(THRUST_RING
), 0, 0),
170 static const ledConfig_t defaultLedStripConfig
[] = {
171 DEFINE_LED(15, 15, 0, LD(SOUTH
) | LD(EAST
), LF(ARM_STATE
), LO(INDICATOR
), 0),
173 DEFINE_LED(15, 8, 0, LD(EAST
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
174 DEFINE_LED(15, 7, 0, LD(EAST
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
176 DEFINE_LED(15, 0, 0, LD(NORTH
) | LD(EAST
), LF(ARM_STATE
) , LO(INDICATOR
), 0),
178 DEFINE_LED( 8, 0, 0, LD(NORTH
) , LF(FLIGHT_MODE
), 0, 0),
179 DEFINE_LED( 7, 0, 0, LD(NORTH
) , LF(FLIGHT_MODE
), 0, 0),
181 DEFINE_LED( 0, 0, 0, LD(NORTH
) | LD(WEST
), LF(ARM_STATE
) , LO(INDICATOR
), 0),
183 DEFINE_LED( 0, 7, 0, LD(WEST
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
184 DEFINE_LED( 0, 8, 0, LD(WEST
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
186 DEFINE_LED( 0, 15, 0, LD(SOUTH
) | LD(WEST
), LF(ARM_STATE
) , LO(INDICATOR
), 0),
188 DEFINE_LED( 7, 15, 0, LD(SOUTH
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
189 DEFINE_LED( 8, 15, 0, LD(SOUTH
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
191 DEFINE_LED( 7, 7, 0, LD(UP
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
192 DEFINE_LED( 8, 7, 0, LD(UP
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
193 DEFINE_LED( 7, 8, 0, LD(DOWN
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
194 DEFINE_LED( 8, 8, 0, LD(DOWN
) , LF(FLIGHT_MODE
), LO(WARNING
), 0),
196 DEFINE_LED( 8, 9, 3, 0, LF(THRUST_RING
), 0, 0),
197 DEFINE_LED( 9, 10, 3, 0, LF(THRUST_RING
), 0, 0),
198 DEFINE_LED(10, 11, 3, 0, LF(THRUST_RING
), 0, 0),
199 DEFINE_LED(10, 12, 3, 0, LF(THRUST_RING
), 0, 0),
200 DEFINE_LED( 9, 13, 3, 0, LF(THRUST_RING
), 0, 0),
201 DEFINE_LED( 8, 14, 3, 0, LF(THRUST_RING
), 0, 0),
202 DEFINE_LED( 7, 14, 3, 0, LF(THRUST_RING
), 0, 0),
203 DEFINE_LED( 6, 13, 3, 0, LF(THRUST_RING
), 0, 0),
204 DEFINE_LED( 5, 12, 3, 0, LF(THRUST_RING
), 0, 0),
205 DEFINE_LED( 5, 11, 3, 0, LF(THRUST_RING
), 0, 0),
206 DEFINE_LED( 6, 10, 3, 0, LF(THRUST_RING
), 0, 0),
207 DEFINE_LED( 7, 9, 3, 0, LF(THRUST_RING
), 0, 0),
216 static const modeColorIndexes_t defaultModeColors
[] = {
217 // NORTH EAST SOUTH WEST UP DOWN
218 [LED_MODE_ORIENTATION
] = {{ COLOR_WHITE
, COLOR_DARK_VIOLET
, COLOR_RED
, COLOR_DEEP_PINK
, COLOR_BLUE
, COLOR_ORANGE
}},
219 [LED_MODE_HEADFREE
] = {{ COLOR_LIME_GREEN
, COLOR_DARK_VIOLET
, COLOR_ORANGE
, COLOR_DEEP_PINK
, COLOR_BLUE
, COLOR_ORANGE
}},
220 [LED_MODE_HORIZON
] = {{ COLOR_BLUE
, COLOR_DARK_VIOLET
, COLOR_YELLOW
, COLOR_DEEP_PINK
, COLOR_BLUE
, COLOR_ORANGE
}},
221 [LED_MODE_ANGLE
] = {{ COLOR_CYAN
, COLOR_DARK_VIOLET
, COLOR_YELLOW
, COLOR_DEEP_PINK
, COLOR_BLUE
, COLOR_ORANGE
}},
222 [LED_MODE_MAG
] = {{ COLOR_MINT_GREEN
, COLOR_DARK_VIOLET
, COLOR_ORANGE
, COLOR_DEEP_PINK
, COLOR_BLUE
, COLOR_ORANGE
}},
223 [LED_MODE_BARO
] = {{ COLOR_LIGHT_BLUE
, COLOR_DARK_VIOLET
, COLOR_RED
, COLOR_DEEP_PINK
, COLOR_BLUE
, COLOR_ORANGE
}},
226 static const specialColorIndexes_t defaultSpecialColors
[] = {
227 {{ [LED_SCOLOR_DISARMED
] = COLOR_GREEN
,
228 [LED_SCOLOR_ARMED
] = COLOR_BLUE
,
229 [LED_SCOLOR_ANIMATION
] = COLOR_WHITE
,
230 [LED_SCOLOR_BACKGROUND
] = COLOR_BLACK
,
231 [LED_SCOLOR_BLINKBACKGROUND
] = COLOR_BLACK
,
232 [LED_SCOLOR_GPSNOSATS
] = COLOR_RED
,
233 [LED_SCOLOR_GPSNOLOCK
] = COLOR_ORANGE
,
234 [LED_SCOLOR_GPSLOCKED
] = COLOR_GREEN
,
239 static int scaledThrottle
;
245 #ifdef USE_LED_RING_DEFAULT_CONFIG
246 const ledConfig_t defaultLedStripConfig[] = {
247 { CALCULATE_LED_XY( 2, 2), 3, LED_FUNCTION_THRUST_RING},
248 { CALCULATE_LED_XY( 2, 1), 3, LED_FUNCTION_THRUST_RING},
249 { CALCULATE_LED_XY( 2, 0), 3, LED_FUNCTION_THRUST_RING},
250 { CALCULATE_LED_XY( 1, 0), 3, LED_FUNCTION_THRUST_RING},
251 { CALCULATE_LED_XY( 0, 0), 3, LED_FUNCTION_THRUST_RING},
252 { CALCULATE_LED_XY( 0, 1), 3, LED_FUNCTION_THRUST_RING},
253 { CALCULATE_LED_XY( 0, 2), 3, LED_FUNCTION_THRUST_RING},
254 { CALCULATE_LED_XY( 1, 2), 3, LED_FUNCTION_THRUST_RING},
255 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING},
256 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING},
257 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING},
258 { CALCULATE_LED_XY( 1, 1), 3, LED_FUNCTION_THRUST_RING},
260 #elif defined(USE_COLIBTI_RACE_LED_DEFAULT_CONFIG)
261 const ledConfig_t defaultLedStripConfig[] = {
262 { CALCULATE_LED_XY( 0, 0), 6, LED_DIRECTION_WEST | LED_FUNCTION_WARNING | LED_FUNCTION_COLOR },
263 { CALCULATE_LED_XY( 0, 1), 6, LED_DIRECTION_WEST | LED_FUNCTION_WARNING | LED_FUNCTION_COLOR },
264 { CALCULATE_LED_XY( 0, 8), 6, LED_DIRECTION_WEST | LED_FUNCTION_WARNING | LED_FUNCTION_COLOR },
265 { CALCULATE_LED_XY( 7, 15), 6, LED_FUNCTION_COLOR },
266 { CALCULATE_LED_XY( 8, 15), 6, LED_FUNCTION_COLOR },
267 { CALCULATE_LED_XY( 7, 14), 6, LED_FUNCTION_COLOR },
268 { CALCULATE_LED_XY( 8, 14), 6, LED_FUNCTION_COLOR },
269 { CALCULATE_LED_XY( 15, 8), 6, LED_DIRECTION_EAST | LED_FUNCTION_WARNING | LED_FUNCTION_COLOR },
270 { CALCULATE_LED_XY( 15, 1), 6, LED_DIRECTION_EAST | LED_FUNCTION_WARNING | LED_FUNCTION_COLOR },
271 { CALCULATE_LED_XY( 15, 0), 6, LED_DIRECTION_EAST | LED_FUNCTION_WARNING | LED_FUNCTION_COLOR },
274 const ledConfig_t defaultLedStripConfig[] = {
275 { CALCULATE_LED_XY(15, 15), 0, LED_DIRECTION_SOUTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
277 { CALCULATE_LED_XY(15, 8), 0, LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
278 { CALCULATE_LED_XY(15, 7), 0, LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
280 { CALCULATE_LED_XY(15, 0), 0, LED_DIRECTION_NORTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
282 { CALCULATE_LED_XY( 8, 0), 0, LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE },
283 { CALCULATE_LED_XY( 7, 0), 0, LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE },
285 { CALCULATE_LED_XY( 0, 0), 0, LED_DIRECTION_NORTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
287 { CALCULATE_LED_XY( 0, 7), 0, LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
288 { CALCULATE_LED_XY( 0, 8), 0, LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
290 { CALCULATE_LED_XY( 0, 15), 0, LED_DIRECTION_SOUTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
292 { CALCULATE_LED_XY( 7, 15), 0, LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
293 { CALCULATE_LED_XY( 8, 15), 0, LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
295 { CALCULATE_LED_XY( 7, 7), 0, LED_DIRECTION_UP | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
296 { CALCULATE_LED_XY( 8, 7), 0, LED_DIRECTION_UP | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
297 { CALCULATE_LED_XY( 7, 8), 0, LED_DIRECTION_DOWN | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
298 { CALCULATE_LED_XY( 8, 8), 0, LED_DIRECTION_DOWN | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
300 { CALCULATE_LED_XY( 8, 9), 3, LED_FUNCTION_THRUST_RING},
301 { CALCULATE_LED_XY( 9, 10), 3, LED_FUNCTION_THRUST_RING},
302 { CALCULATE_LED_XY(10, 11), 3, LED_FUNCTION_THRUST_RING},
303 { CALCULATE_LED_XY(10, 12), 3, LED_FUNCTION_THRUST_RING},
304 { CALCULATE_LED_XY( 9, 13), 3, LED_FUNCTION_THRUST_RING},
305 { CALCULATE_LED_XY( 8, 14), 3, LED_FUNCTION_THRUST_RING},
306 { CALCULATE_LED_XY( 7, 14), 3, LED_FUNCTION_THRUST_RING},
307 { CALCULATE_LED_XY( 6, 13), 3, LED_FUNCTION_THRUST_RING},
308 { CALCULATE_LED_XY( 5, 12), 3, LED_FUNCTION_THRUST_RING},
309 { CALCULATE_LED_XY( 5, 11), 3, LED_FUNCTION_THRUST_RING},
310 { CALCULATE_LED_XY( 6, 10), 3, LED_FUNCTION_THRUST_RING},
311 { CALCULATE_LED_XY( 7, 9), 3, LED_FUNCTION_THRUST_RING},
320 static void updateLedRingCounts(void);
322 STATIC_UNIT_TESTED
void determineLedStripDimensions(void)
327 for (int ledIndex
= 0; ledIndex
< ledCounts
.count
; ledIndex
++) {
328 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
330 maxX
= MAX(ledGetX(ledConfig
), maxX
);
331 maxY
= MAX(ledGetY(ledConfig
), maxY
);
333 ledGridWidth
= maxX
+ 1;
334 ledGridHeight
= maxY
+ 1;
337 STATIC_UNIT_TESTED
void determineOrientationLimits(void)
339 highestYValueForNorth
= (ledGridHeight
/ 2) - 1;
340 lowestYValueForSouth
= ((ledGridHeight
+ 1) / 2);
341 highestXValueForWest
= (ledGridWidth
/ 2) - 1;
342 lowestXValueForEast
= ((ledGridWidth
+ 1) / 2);
345 STATIC_UNIT_TESTED
void updateLedCount(void)
347 int count
= 0, countRing
= 0, countScanner
= 0;
349 for (int ledIndex
= 0; ledIndex
< LED_MAX_STRIP_LENGTH
; ledIndex
++) {
350 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
357 if (ledGetFunction(ledConfig
) == LED_FUNCTION_THRUST_RING
)
360 if (ledGetOverlayBit(ledConfig
, LED_OVERLAY_LARSON_SCANNER
))
364 ledCounts
.count
= count
;
365 ledCounts
.ring
= countRing
;
366 ledCounts
.larson
= countScanner
;
369 void reevaluateLedConfig(void)
372 determineLedStripDimensions();
373 determineOrientationLimits();
374 updateLedRingCounts();
377 // get specialColor by index
378 static hsvColor_t
* getSC(ledSpecialColorIds_e index
)
380 return &masterConfig
.colors
[masterConfig
.specialColors
.color
[index
]];
383 static const char directionCodes
[LED_DIRECTION_COUNT
] = { 'N', 'E', 'S', 'W', 'U', 'D' };
384 static const char baseFunctionCodes
[LED_BASEFUNCTION_COUNT
] = { 'C', 'F', 'A', 'L', 'S', 'G', 'R' };
385 static const char overlayCodes
[LED_OVERLAY_COUNT
] = { 'T', 'O', 'B', 'N', 'I', 'W' };
387 #define CHUNK_BUFFER_SIZE 11
389 bool parseLedStripConfig(int ledIndex
, const char *config
)
391 if (ledIndex
>= LED_MAX_STRIP_LENGTH
)
402 static const char chunkSeparators
[PARSE_STATE_COUNT
] = {',', ':', ':',':', '\0'};
404 ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
405 memset(ledConfig
, 0, sizeof(ledConfig_t
));
407 int x
= 0, y
= 0, color
= 0; // initialize to prevent warnings
408 int baseFunction
= 0;
409 int overlay_flags
= 0;
410 int direction_flags
= 0;
412 for (enum parseState_e parseState
= 0; parseState
< PARSE_STATE_COUNT
; parseState
++) {
413 char chunk
[CHUNK_BUFFER_SIZE
];
415 char chunkSeparator
= chunkSeparators
[parseState
];
417 while (*config
&& *config
!= chunkSeparator
&& chunkIndex
< (CHUNK_BUFFER_SIZE
- 1)) {
418 chunk
[chunkIndex
++] = *config
++;
420 chunk
[chunkIndex
++] = 0; // zero-terminate chunk
421 if (*config
!= chunkSeparator
) {
424 config
++; // skip separator
426 switch (parseState
) {
434 for (char *ch
= chunk
; *ch
; ch
++) {
435 for (ledDirectionId_e dir
= 0; dir
< LED_DIRECTION_COUNT
; dir
++) {
436 if (directionCodes
[dir
] == *ch
) {
437 direction_flags
|= LED_FLAG_DIRECTION(dir
);
444 for (char *ch
= chunk
; *ch
; ch
++) {
445 for (ledBaseFunctionId_e fn
= 0; fn
< LED_BASEFUNCTION_COUNT
; fn
++) {
446 if (baseFunctionCodes
[fn
] == *ch
) {
452 for (ledOverlayId_e ol
= 0; ol
< LED_OVERLAY_COUNT
; ol
++) {
453 if (overlayCodes
[ol
] == *ch
) {
454 overlay_flags
|= LED_FLAG_OVERLAY(ol
);
462 if (color
>= LED_CONFIGURABLE_COLOR_COUNT
)
465 case PARSE_STATE_COUNT
:; // prevent warning
469 *ledConfig
= DEFINE_LED(x
, y
, color
, direction_flags
, baseFunction
, overlay_flags
, 0);
471 reevaluateLedConfig();
476 void generateLedConfig(int ledIndex
, char *ledConfigBuffer
, size_t bufferSize
)
478 char directions
[LED_DIRECTION_COUNT
+ 1];
479 char baseFunctionOverlays
[LED_OVERLAY_COUNT
+ 2];
481 ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
483 memset(ledConfigBuffer
, 0, bufferSize
);
485 char *dptr
= directions
;
486 for (ledDirectionId_e dir
= 0; dir
< LED_DIRECTION_COUNT
; dir
++) {
487 if (ledGetDirectionBit(ledConfig
, dir
)) {
488 *dptr
++ = directionCodes
[dir
];
493 char *fptr
= baseFunctionOverlays
;
494 *fptr
++ = baseFunctionCodes
[ledGetFunction(ledConfig
)];
496 for (ledOverlayId_e ol
= 0; ol
< LED_OVERLAY_COUNT
; ol
++) {
497 if (ledGetOverlayBit(ledConfig
, ol
)) {
498 *fptr
++ = overlayCodes
[ol
];
503 // TODO - check buffer length
504 sprintf(ledConfigBuffer
, "%u,%u:%s:%s:%u", ledGetX(ledConfig
), ledGetY(ledConfig
), directions
, baseFunctionOverlays
, ledGetColor(ledConfig
));
509 // the ordering is important, see below how NSEW is mapped to NE/SE/NW/SW
510 QUADRANT_NORTH
= 1 << 0,
511 QUADRANT_SOUTH
= 1 << 1,
512 QUADRANT_EAST
= 1 << 2,
513 QUADRANT_WEST
= 1 << 3,
514 QUADRANT_NORTH_EAST
= 1 << 4,
515 QUADRANT_SOUTH_EAST
= 1 << 5,
516 QUADRANT_NORTH_WEST
= 1 << 6,
517 QUADRANT_SOUTH_WEST
= 1 << 7,
518 QUADRANT_NONE
= 1 << 8,
519 QUADRANT_NOTDIAG
= 1 << 9, // not in NE/SE/NW/SW
521 QUADRANT_ANY
= QUADRANT_NORTH
| QUADRANT_SOUTH
| QUADRANT_EAST
| QUADRANT_WEST
| QUADRANT_NONE
,
524 static quadrant_e
getLedQuadrant(const int ledIndex
)
526 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
528 int x
= ledGetX(ledConfig
);
529 int y
= ledGetY(ledConfig
);
532 if (y
<= highestYValueForNorth
)
533 quad
|= QUADRANT_NORTH
;
534 else if (y
>= lowestYValueForSouth
)
535 quad
|= QUADRANT_SOUTH
;
536 if (x
>= lowestXValueForEast
)
537 quad
|= QUADRANT_EAST
;
538 else if (x
<= highestXValueForWest
)
539 quad
|= QUADRANT_WEST
;
541 if ((quad
& (QUADRANT_NORTH
| QUADRANT_SOUTH
))
542 && (quad
& (QUADRANT_EAST
| QUADRANT_WEST
)) ) { // is led in one of NE/SE/NW/SW?
543 quad
|= 1 << (4 + ((quad
& QUADRANT_SOUTH
) ? 1 : 0) + ((quad
& QUADRANT_WEST
) ? 2 : 0));
545 quad
|= QUADRANT_NOTDIAG
;
548 if ((quad
& (QUADRANT_NORTH
| QUADRANT_SOUTH
| QUADRANT_EAST
| QUADRANT_WEST
)) == 0)
549 quad
|= QUADRANT_NONE
;
554 static const struct {
555 uint8_t dir
; // ledDirectionId_e
556 uint16_t quadrantMask
; // quadrant_e
557 } directionQuadrantMap
[] = {
558 {LED_DIRECTION_SOUTH
, QUADRANT_SOUTH
},
559 {LED_DIRECTION_NORTH
, QUADRANT_NORTH
},
560 {LED_DIRECTION_EAST
, QUADRANT_EAST
},
561 {LED_DIRECTION_WEST
, QUADRANT_WEST
},
562 {LED_DIRECTION_DOWN
, QUADRANT_ANY
},
563 {LED_DIRECTION_UP
, QUADRANT_ANY
},
566 static hsvColor_t
* getDirectionalModeColor(const int ledIndex
, const modeColorIndexes_t
*modeColors
)
568 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
570 quadrant_e quad
= getLedQuadrant(ledIndex
);
571 for (unsigned i
= 0; i
< ARRAYLEN(directionQuadrantMap
); i
++) {
572 ledDirectionId_e dir
= directionQuadrantMap
[i
].dir
;
573 quadrant_e quadMask
= directionQuadrantMap
[i
].quadrantMask
;
575 if (ledGetDirectionBit(ledConfig
, dir
) && (quad
& quadMask
))
576 return &masterConfig
.colors
[modeColors
->color
[dir
]];
582 // map flight mode to led mode, in order of priority
583 // flightMode == 0 is always active
584 static const struct {
587 } flightModeToLed
[] = {
588 {HEADFREE_MODE
, LED_MODE_HEADFREE
},
590 {MAG_MODE
, LED_MODE_MAG
},
593 {BARO_MODE
, LED_MODE_BARO
},
595 {HORIZON_MODE
, LED_MODE_HORIZON
},
596 {ANGLE_MODE
, LED_MODE_ANGLE
},
597 {0, LED_MODE_ORIENTATION
},
600 static void applyLedFixedLayers()
602 for (int ledIndex
= 0; ledIndex
< ledCounts
.count
; ledIndex
++) {
603 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
604 hsvColor_t color
= *getSC(LED_SCOLOR_BACKGROUND
);
606 int fn
= ledGetFunction(ledConfig
);
607 int hOffset
= HSV_HUE_MAX
;
610 case LED_FUNCTION_COLOR
:
611 color
= masterConfig
.colors
[ledGetColor(ledConfig
)];
614 case LED_FUNCTION_FLIGHT_MODE
:
615 for (unsigned i
= 0; i
< ARRAYLEN(flightModeToLed
); i
++)
616 if (!flightModeToLed
[i
].flightMode
|| FLIGHT_MODE(flightModeToLed
[i
].flightMode
)) {
617 color
= *getDirectionalModeColor(ledIndex
, &masterConfig
.modeColors
[flightModeToLed
[i
].ledMode
]);
618 break; // stop on first match
622 case LED_FUNCTION_ARM_STATE
:
623 color
= ARMING_FLAG(ARMED
) ? *getSC(LED_SCOLOR_ARMED
) : *getSC(LED_SCOLOR_DISARMED
);
626 case LED_FUNCTION_BATTERY
:
628 hOffset
+= scaleRange(calculateBatteryCapacityRemainingPercentage(), 0, 100, -30, 120);
631 case LED_FUNCTION_RSSI
:
633 hOffset
+= scaleRange(rssi
* 100, 0, 1023, -30, 120);
640 if (ledGetOverlayBit(ledConfig
, LED_OVERLAY_THROTTLE
)) {
641 hOffset
+= ((scaledThrottle
- 10) * 4) / 3;
644 color
.h
= (color
.h
+ hOffset
) % (HSV_HUE_MAX
+ 1);
646 setLedHsv(ledIndex
, &color
);
651 static void applyLedHsv(uint32_t mask
, const hsvColor_t
*color
)
653 for (int ledIndex
= 0; ledIndex
< ledCounts
.count
; ledIndex
++) {
654 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
655 if ((*ledConfig
& mask
) == mask
)
656 setLedHsv(ledIndex
, color
);
661 WARNING_ARMING_DISABLED
,
667 static void applyLedWarningLayer(bool updateNow
, uint32_t *timer
)
669 static uint8_t warningFlashCounter
= 0;
670 static uint8_t warningFlags
= 0; // non-zero during blinks
673 // keep counter running, so it stays in sync with blink
674 warningFlashCounter
++;
675 warningFlashCounter
&= 0xF;
677 if (warningFlashCounter
== 0) { // update when old flags was processed
679 if (feature(FEATURE_VBAT
) && getBatteryState() != BATTERY_OK
)
680 warningFlags
|= 1 << WARNING_LOW_BATTERY
;
681 if (feature(FEATURE_FAILSAFE
) && failsafeIsActive())
682 warningFlags
|= 1 << WARNING_FAILSAFE
;
683 if (!ARMING_FLAG(ARMED
) && !ARMING_FLAG(OK_TO_ARM
))
684 warningFlags
|= 1 << WARNING_ARMING_DISABLED
;
686 *timer
+= LED_STRIP_HZ(10);
690 const hsvColor_t
*warningColor
= NULL
;
692 bool colorOn
= (warningFlashCounter
% 2) == 0; // w_w_
693 warningFlags_e warningId
= warningFlashCounter
/ 4;
694 if (warningFlags
& (1 << warningId
)) {
696 case WARNING_ARMING_DISABLED
:
697 warningColor
= colorOn
? &HSV(GREEN
) : &HSV(BLACK
);
699 case WARNING_LOW_BATTERY
:
700 warningColor
= colorOn
? &HSV(RED
) : &HSV(BLACK
);
702 case WARNING_FAILSAFE
:
703 warningColor
= colorOn
? &HSV(YELLOW
) : &HSV(BLUE
);
709 applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_WARNING
)), warningColor
);
713 static void applyLedBatteryLayer(bool updateNow
, uint32_t *timer
)
715 static bool flash
= false;
721 state
= getBatteryState();
728 case BATTERY_WARNING
:
738 *timer
+= LED_STRIP_HZ(timeOffset
);
741 hsvColor_t
*bgc
= getSC(LED_SCOLOR_BACKGROUND
);
742 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_BATTERY
), bgc
);
746 static void applyLedRssiLayer(bool updateNow
, uint32_t *timer
)
748 static bool flash
= false;
754 state
= (rssi
* 100) / 1023;
759 } else if (state
> 20) {
768 *timer
+= LED_STRIP_HZ(timeOffset
);
771 hsvColor_t
*bgc
= getSC(LED_SCOLOR_BACKGROUND
);
772 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_RSSI
), bgc
);
777 static void applyLedGpsLayer(bool updateNow
, uint32_t *timer
)
779 static uint8_t gpsFlashCounter
= 0;
780 static uint8_t gpsPauseCounter
= 0;
781 const uint8_t blinkPauseLength
= 4;
784 if (gpsPauseCounter
> 0) {
786 } else if (gpsFlashCounter
>= GPS_numSat
) {
788 gpsPauseCounter
= blinkPauseLength
;
793 *timer
+= LED_STRIP_HZ(2.5);
796 const hsvColor_t
*gpsColor
;
798 if (GPS_numSat
== 0 || !sensors(SENSOR_GPS
)) {
799 gpsColor
= getSC(LED_SCOLOR_GPSNOSATS
);
801 bool colorOn
= gpsPauseCounter
== 0; // each interval starts with pause
802 if (STATE(GPS_FIX
)) {
803 gpsColor
= colorOn
? getSC(LED_SCOLOR_GPSLOCKED
) : getSC(LED_SCOLOR_BACKGROUND
);
805 gpsColor
= colorOn
? getSC(LED_SCOLOR_GPSNOLOCK
) : getSC(LED_SCOLOR_GPSNOSATS
);
809 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_GPS
), gpsColor
);
815 #define INDICATOR_DEADBAND 25
817 static void applyLedIndicatorLayer(bool updateNow
, uint32_t *timer
)
819 static bool flash
= 0;
822 if (rxIsReceivingSignal()) {
823 // calculate update frequency
824 int scale
= MAX(ABS(rcCommand
[ROLL
]), ABS(rcCommand
[PITCH
])); // 0 - 500
825 scale
+= (50 - INDICATOR_DEADBAND
); // start increasing frequency right after deadband
826 *timer
+= LED_STRIP_HZ(5) * 50 / MAX(50, scale
); // 5 - 50Hz update, 2.5 - 25Hz blink
830 *timer
+= LED_STRIP_HZ(5); // try again soon
837 const hsvColor_t
*flashColor
= &HSV(ORANGE
); // TODO - use user color?
839 quadrant_e quadrants
= 0;
840 if (rcCommand
[ROLL
] > INDICATOR_DEADBAND
) {
841 quadrants
|= QUADRANT_NORTH_EAST
| QUADRANT_SOUTH_EAST
;
842 } else if (rcCommand
[ROLL
] < -INDICATOR_DEADBAND
) {
843 quadrants
|= QUADRANT_NORTH_WEST
| QUADRANT_SOUTH_WEST
;
845 if (rcCommand
[PITCH
] > INDICATOR_DEADBAND
) {
846 quadrants
|= QUADRANT_NORTH_EAST
| QUADRANT_NORTH_WEST
;
847 } else if (rcCommand
[PITCH
] < -INDICATOR_DEADBAND
) {
848 quadrants
|= QUADRANT_SOUTH_EAST
| QUADRANT_SOUTH_WEST
;
851 for (int ledIndex
= 0; ledIndex
< ledCounts
.count
; ledIndex
++) {
852 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
853 if (ledGetOverlayBit(ledConfig
, LED_OVERLAY_INDICATOR
)) {
854 if (getLedQuadrant(ledIndex
) & quadrants
)
855 setLedHsv(ledIndex
, flashColor
);
860 #define ROTATION_SEQUENCE_LED_COUNT 6 // 2 on, 4 off
861 #define ROTATION_SEQUENCE_LED_WIDTH 2 // 2 on
863 static void updateLedRingCounts(void)
866 // try to split in segments/rings of exactly ROTATION_SEQUENCE_LED_COUNT leds
867 if ((ledCounts
.ring
% ROTATION_SEQUENCE_LED_COUNT
) == 0) {
868 seqLen
= ROTATION_SEQUENCE_LED_COUNT
;
870 seqLen
= ledCounts
.ring
;
871 // else split up in equal segments/rings of at most ROTATION_SEQUENCE_LED_COUNT leds
872 // TODO - improve partitioning (15 leds -> 3x5)
873 while ((seqLen
> ROTATION_SEQUENCE_LED_COUNT
) && ((seqLen
% 2) == 0)) {
877 ledCounts
.ringSeqLen
= seqLen
;
880 static void applyLedThrustRingLayer(bool updateNow
, uint32_t *timer
)
882 static uint8_t rotationPhase
;
883 int ledRingIndex
= 0;
886 rotationPhase
= rotationPhase
> 0 ? rotationPhase
- 1 : ledCounts
.ringSeqLen
- 1;
888 int scale
= scaledThrottle
; // ARMING_FLAG(ARMED) ? scaleRange(rcData[THROTTLE], PWM_RANGE_MIN, PWM_RANGE_MAX, 10, 100) : 10;
889 *timer
+= LED_STRIP_HZ(5) * 10 / scale
; // 5 - 50Hz update rate
892 for (int ledIndex
= 0; ledIndex
< ledCounts
.count
; ledIndex
++) {
893 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
894 if (ledGetFunction(ledConfig
) == LED_FUNCTION_THRUST_RING
) {
897 if (ARMING_FLAG(ARMED
)) {
898 applyColor
= (ledRingIndex
+ rotationPhase
) % ledCounts
.ringSeqLen
< ROTATION_SEQUENCE_LED_WIDTH
;
900 applyColor
= !(ledRingIndex
% 2); // alternating pattern
904 const hsvColor_t
*ringColor
= &masterConfig
.colors
[ledGetColor(ledConfig
)];
905 setLedHsv(ledIndex
, ringColor
);
913 typedef struct larsonParameters_s
{
914 uint8_t currentBrightness
;
917 } larsonParameters_t
;
919 static int brightnessForLarsonIndex(larsonParameters_t
*larsonParameters
, uint8_t larsonIndex
)
921 int offset
= larsonIndex
- larsonParameters
->currentIndex
;
922 static const int larsonLowValue
= 8;
925 return (larsonLowValue
);
928 return (larsonParameters
->currentBrightness
);
930 if (larsonParameters
->direction
== offset
) {
931 return (larsonParameters
->currentBrightness
- 127);
934 return (255 - larsonParameters
->currentBrightness
);
938 static void larsonScannerNextStep(larsonParameters_t
*larsonParameters
, int delta
)
940 if (larsonParameters
->currentBrightness
> (255 - delta
)) {
941 larsonParameters
->currentBrightness
= 127;
942 if (larsonParameters
->currentIndex
>= ledCounts
.larson
|| larsonParameters
->currentIndex
< 0) {
943 larsonParameters
->direction
= -larsonParameters
->direction
;
945 larsonParameters
->currentIndex
+= larsonParameters
->direction
;
947 larsonParameters
->currentBrightness
+= delta
;
951 static void applyLarsonScannerLayer(bool updateNow
, uint32_t *timer
)
953 static larsonParameters_t larsonParameters
= { 0, 0, 1 };
956 larsonScannerNextStep(&larsonParameters
, 15);
957 *timer
+= LED_STRIP_HZ(60);
960 int scannerLedIndex
= 0;
961 for (unsigned i
= 0; i
< ledCounts
.count
; i
++) {
963 const ledConfig_t
*ledConfig
= &ledConfigs
[i
];
965 if (ledGetOverlayBit(ledConfig
, LED_OVERLAY_LARSON_SCANNER
)) {
967 getLedHsv(i
, &ledColor
);
968 ledColor
.v
= brightnessForLarsonIndex(&larsonParameters
, scannerLedIndex
);
969 setLedHsv(i
, &ledColor
);
975 // blink twice, then wait ; either always or just when landing
976 static void applyLedBlinkLayer(bool updateNow
, uint32_t *timer
)
978 const uint16_t blinkPattern
= 0x8005; // 0b1000000000000101;
979 static uint16_t blinkMask
;
982 blinkMask
= blinkMask
>> 1;
984 blinkMask
= blinkPattern
;
986 *timer
+= LED_STRIP_HZ(10);
989 bool ledOn
= (blinkMask
& 1); // b_b_____...
991 for (int i
= 0; i
< ledCounts
.count
; ++i
) {
992 const ledConfig_t
*ledConfig
= &ledConfigs
[i
];
994 if (ledGetOverlayBit(ledConfig
, LED_OVERLAY_BLINK
) ||
995 (ledGetOverlayBit(ledConfig
, LED_OVERLAY_LANDING_FLASH
) && scaledThrottle
< 55 && scaledThrottle
> 10)) {
996 setLedHsv(i
, getSC(LED_SCOLOR_BLINKBACKGROUND
));
1003 #ifdef USE_LED_ANIMATION
1005 static void applyLedAnimationLayer(bool updateNow
, uint32_t *timer
)
1007 static uint8_t frameCounter
= 0;
1008 const int animationFrames
= ledGridHeight
;
1010 frameCounter
= (frameCounter
+ 1 < animationFrames
) ? frameCounter
+ 1 : 0;
1011 *timer
+= LED_STRIP_HZ(20);
1014 if (ARMING_FLAG(ARMED
))
1017 int previousRow
= frameCounter
> 0 ? frameCounter
- 1 : animationFrames
- 1;
1018 int currentRow
= frameCounter
;
1019 int nextRow
= (frameCounter
+ 1 < animationFrames
) ? frameCounter
+ 1 : 0;
1021 for (int ledIndex
= 0; ledIndex
< ledCounts
.count
; ledIndex
++) {
1022 const ledConfig_t
*ledConfig
= &masterConfig
.ledConfigs
[ledIndex
];
1024 if (ledGetY(ledConfig
) == previousRow
) {
1025 setLedHsv(ledIndex
, getSC(LED_SCOLOR_ANIMATION
));
1026 scaleLedValue(ledIndex
, 50);
1027 } else if (ledGetY(ledConfig
) == currentRow
) {
1028 setLedHsv(ledIndex
, getSC(LED_SCOLOR_ANIMATION
));
1029 } else if (ledGetY(ledConfig
) == nextRow
) {
1030 scaleLedValue(ledIndex
, 50);
1046 #ifdef USE_LED_ANIMATION
1053 static uint32_t timerVal
[timTimerCount
];
1056 // function to apply layer.
1057 // function must replan self using timer pointer
1058 // when updateNow is true (timer triggered), state must be updated first,
1059 // before calculating led state. Otherwise update started by different trigger
1060 // may modify LED state.
1061 typedef void applyLayerFn_timed(bool updateNow
, uint32_t *timer
);
1064 static applyLayerFn_timed
* layerTable
[] = {
1065 [timBlink
] = &applyLedBlinkLayer
,
1066 [timLarson
] = &applyLarsonScannerLayer
,
1067 [timBattery
] = &applyLedBatteryLayer
,
1068 [timRssi
] = &applyLedRssiLayer
,
1070 [timGps
] = &applyLedGpsLayer
,
1072 [timWarning
] = &applyLedWarningLayer
,
1073 [timIndicator
] = &applyLedIndicatorLayer
,
1074 #ifdef USE_LED_ANIMATION
1075 [timAnimation
] = &applyLedAnimationLayer
,
1077 [timRing
] = &applyLedThrustRingLayer
1080 void updateLedStrip(void)
1082 if (!(ledStripInitialised
&& isWS2811LedStripReady())) {
1086 if (IS_RC_MODE_ACTIVE(BOXLEDLOW
) && !(masterConfig
.ledstrip_visual_beeper
&& isBeeperOn())) {
1087 if (ledStripEnabled
) {
1089 ledStripEnabled
= false;
1093 ledStripEnabled
= true;
1095 uint32_t now
= micros();
1097 // test all led timers, setting corresponding bits
1098 uint32_t timActive
= 0;
1099 for (timId_e timId
= 0; timId
< timTimerCount
; timId
++) {
1100 // sanitize timer value, so that it can be safely incremented. Handles inital timerVal value.
1101 // max delay is limited to 5s
1102 int32_t delta
= cmp32(now
, timerVal
[timId
]);
1103 if (delta
< 0 && delta
> -LED_STRIP_MS(5000))
1104 continue; // not ready yet
1105 timActive
|= 1 << timId
;
1106 if (delta
>= LED_STRIP_MS(100) || delta
< 0) {
1107 timerVal
[timId
] = now
;
1112 return; // no change this update, keep old state
1114 // apply all layers; triggered timed functions has to update timers
1116 scaledThrottle
= ARMING_FLAG(ARMED
) ? scaleRange(rcData
[THROTTLE
], PWM_RANGE_MIN
, PWM_RANGE_MAX
, 10, 100) : 10;
1118 applyLedFixedLayers();
1120 for (timId_e timId
= 0; timId
< ARRAYLEN(layerTable
); timId
++) {
1121 uint32_t *timer
= &timerVal
[timId
];
1122 bool updateNow
= timActive
& (1 << timId
);
1123 (*layerTable
[timId
])(updateNow
, timer
);
1125 ws2811UpdateStrip();
1128 bool parseColor(int index
, const char *colorConfig
)
1130 const char *remainingCharacters
= colorConfig
;
1132 hsvColor_t
*color
= &masterConfig
.colors
[index
];
1135 static const uint16_t hsv_limit
[HSV_COLOR_COMPONENT_COUNT
] = {
1136 [HSV_HUE
] = HSV_HUE_MAX
,
1137 [HSV_SATURATION
] = HSV_SATURATION_MAX
,
1138 [HSV_VALUE
] = HSV_VALUE_MAX
,
1140 for (int componentIndex
= 0; result
&& componentIndex
< HSV_COLOR_COMPONENT_COUNT
; componentIndex
++) {
1141 int val
= atoi(remainingCharacters
);
1142 if(val
> hsv_limit
[componentIndex
]) {
1146 switch (componentIndex
) {
1150 case HSV_SATURATION
:
1157 remainingCharacters
= strchr(remainingCharacters
, ',');
1158 if (remainingCharacters
) {
1159 remainingCharacters
++; // skip separator
1161 if (componentIndex
< HSV_COLOR_COMPONENT_COUNT
- 1) {
1168 memset(color
, 0, sizeof(*color
));
1175 * Redefine a color in a mode.
1177 bool setModeColor(ledModeIndex_e modeIndex
, int modeColorIndex
, int colorIndex
)
1180 if (colorIndex
< 0 || colorIndex
>= LED_CONFIGURABLE_COLOR_COUNT
)
1182 if (modeIndex
< LED_MODE_COUNT
) { // modeIndex_e is unsigned, so one-sided test is enough
1183 if(modeColorIndex
< 0 || modeColorIndex
>= LED_DIRECTION_COUNT
)
1185 masterConfig
.modeColors
[modeIndex
].color
[modeColorIndex
] = colorIndex
;
1186 } else if (modeIndex
== LED_SPECIAL
) {
1187 if (modeColorIndex
< 0 || modeColorIndex
>= LED_SPECIAL_COLOR_COUNT
)
1189 masterConfig
.specialColors
.color
[modeColorIndex
] = colorIndex
;
1197 void pgResetFn_ledConfigs(ledConfig_t *instance)
1199 memcpy_fn(instance, &defaultLedStripConfig, sizeof(defaultLedStripConfig));
1202 void pgResetFn_colors(hsvColor_t *instance)
1204 // copy hsv colors as default
1205 BUILD_BUG_ON(ARRAYLEN(*colors_arr()) < ARRAYLEN(hsv));
1207 for (unsigned colorIndex = 0; colorIndex < ARRAYLEN(hsv); colorIndex++) {
1208 *instance++ = hsv[colorIndex];
1212 void pgResetFn_modeColors(modeColorIndexes_t *instance)
1214 memcpy_fn(instance, &defaultModeColors, sizeof(defaultModeColors));
1217 void pgResetFn_specialColors(specialColorIndexes_t *instance)
1219 memcpy_fn(instance, &defaultSpecialColors, sizeof(defaultSpecialColors));
1223 void applyDefaultLedStripConfig(ledConfig_t
*ledConfigs
)
1225 memset(ledConfigs
, 0, LED_MAX_STRIP_LENGTH
* sizeof(ledConfig_t
));
1228 void applyDefaultColors(hsvColor_t
*colors
)
1230 // copy hsv colors as default
1231 memset(colors
, 0, ARRAYLEN(hsv
) * sizeof(hsvColor_t
));
1232 for (unsigned colorIndex
= 0; colorIndex
< ARRAYLEN(hsv
); colorIndex
++) {
1233 *colors
++ = hsv
[colorIndex
];
1237 void applyDefaultModeColors(modeColorIndexes_t
*modeColors
)
1239 memcpy_fn(modeColors
, &defaultModeColors
, sizeof(defaultModeColors
));
1242 void applyDefaultSpecialColors(specialColorIndexes_t
*specialColors
)
1244 memcpy_fn(specialColors
, &defaultSpecialColors
, sizeof(defaultSpecialColors
));
1249 void ledStripInit(ledConfig_t
*ledConfigsToUse
, hsvColor_t
*colorsToUse
, modeColorIndexes_t
*modeColorsToUse
, specialColorIndexes_t
*specialColorsToUse
)
1251 ledConfigs
= ledConfigsToUse
;
1252 colors
= colorsToUse
;
1253 modeColors
= modeColorsToUse
;
1254 specialColors
= *specialColorsToUse
;
1255 ledStripInitialised
= false;
1258 void ledStripEnable(void)
1260 reevaluateLedConfig();
1261 ledStripInitialised
= true;
1263 ws2811LedStripInit();
1266 static void ledStripDisable(void)
1268 setStripColor(&HSV(BLACK
));
1270 ws2811UpdateStrip();