Removed excess trailing spaces before new lines on licenses.
[betaflight.git] / src / main / io / ledstrip.c
blobedbe3cf3d052fcc5b41fef48d6dbd9fd6e966a26
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <stdarg.h>
27 #include <platform.h>
29 #ifdef USE_LED_STRIP
31 #include "build/build_config.h"
33 #include "common/axis.h"
34 #include "common/color.h"
35 #include "common/maths.h"
36 #include "common/printf.h"
37 #include "common/typeconversion.h"
38 #include "common/utils.h"
40 #include "config/feature.h"
41 #include "pg/pg.h"
42 #include "pg/pg_ids.h"
44 #include "drivers/light_ws2811strip.h"
45 #include "drivers/serial.h"
46 #include "drivers/vtx_common.h"
48 #include "fc/config.h"
49 #include "fc/rc_controls.h"
50 #include "fc/rc_modes.h"
51 #include "fc/runtime_config.h"
53 #include "flight/failsafe.h"
54 #include "flight/imu.h"
55 #include "flight/mixer.h"
56 #include "flight/navigation.h"
57 #include "flight/pid.h"
58 #include "flight/servos.h"
60 #include "io/beeper.h"
61 #include "io/gimbal.h"
62 #include "io/gps.h"
63 #include "io/ledstrip.h"
64 #include "io/serial.h"
65 #include "io/vtx_string.h"
67 #include "rx/rx.h"
69 #include "sensors/acceleration.h"
70 #include "sensors/barometer.h"
71 #include "sensors/battery.h"
72 #include "sensors/boardalignment.h"
73 #include "sensors/gyro.h"
74 #include "sensors/sensors.h"
76 #include "telemetry/telemetry.h"
78 PG_REGISTER_WITH_RESET_FN(ledStripConfig_t, ledStripConfig, PG_LED_STRIP_CONFIG, 0);
80 static bool ledStripInitialised = false;
81 static bool ledStripEnabled = true;
83 static void ledStripDisable(void);
85 //#define USE_LED_ANIMATION
87 #define HZ_TO_US(hz) ((int32_t)((1000 * 1000) / (hz)))
89 #define MAX_TIMER_DELAY (5 * 1000 * 1000)
91 #if LED_MAX_STRIP_LENGTH > WS2811_LED_STRIP_LENGTH
92 # error "Led strip length must match driver"
93 #endif
95 typedef enum {
96 COLOR_BLACK = 0,
97 COLOR_WHITE,
98 COLOR_RED,
99 COLOR_ORANGE,
100 COLOR_YELLOW,
101 COLOR_LIME_GREEN,
102 COLOR_GREEN,
103 COLOR_MINT_GREEN,
104 COLOR_CYAN,
105 COLOR_LIGHT_BLUE,
106 COLOR_BLUE,
107 COLOR_DARK_VIOLET,
108 COLOR_MAGENTA,
109 COLOR_DEEP_PINK
110 } colorId_e;
112 const hsvColor_t hsv[] = {
113 // H S V
114 [COLOR_BLACK] = { 0, 0, 0},
115 [COLOR_WHITE] = { 0, 255, 255},
116 [COLOR_RED] = { 0, 0, 255},
117 [COLOR_ORANGE] = { 30, 0, 255},
118 [COLOR_YELLOW] = { 60, 0, 255},
119 [COLOR_LIME_GREEN] = { 90, 0, 255},
120 [COLOR_GREEN] = {120, 0, 255},
121 [COLOR_MINT_GREEN] = {150, 0, 255},
122 [COLOR_CYAN] = {180, 0, 255},
123 [COLOR_LIGHT_BLUE] = {210, 0, 255},
124 [COLOR_BLUE] = {240, 0, 255},
125 [COLOR_DARK_VIOLET] = {270, 0, 255},
126 [COLOR_MAGENTA] = {300, 0, 255},
127 [COLOR_DEEP_PINK] = {330, 0, 255},
129 // macro to save typing on default colors
130 #define HSV(color) (hsv[COLOR_ ## color])
132 STATIC_UNIT_TESTED uint8_t ledGridRows;
133 // grid offsets
134 STATIC_UNIT_TESTED int8_t highestYValueForNorth;
135 STATIC_UNIT_TESTED int8_t lowestYValueForSouth;
136 STATIC_UNIT_TESTED int8_t highestXValueForWest;
137 STATIC_UNIT_TESTED int8_t lowestXValueForEast;
139 STATIC_UNIT_TESTED ledCounts_t ledCounts;
141 static const modeColorIndexes_t defaultModeColors[] = {
142 // NORTH EAST SOUTH WEST UP DOWN
143 [LED_MODE_ORIENTATION] = {{ COLOR_WHITE, COLOR_DARK_VIOLET, COLOR_RED, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
144 [LED_MODE_HEADFREE] = {{ COLOR_LIME_GREEN, COLOR_DARK_VIOLET, COLOR_ORANGE, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
145 [LED_MODE_HORIZON] = {{ COLOR_BLUE, COLOR_DARK_VIOLET, COLOR_YELLOW, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
146 [LED_MODE_ANGLE] = {{ COLOR_CYAN, COLOR_DARK_VIOLET, COLOR_YELLOW, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
147 [LED_MODE_MAG] = {{ COLOR_MINT_GREEN, COLOR_DARK_VIOLET, COLOR_ORANGE, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
148 [LED_MODE_BARO] = {{ COLOR_LIGHT_BLUE, COLOR_DARK_VIOLET, COLOR_RED, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
151 static const specialColorIndexes_t defaultSpecialColors[] = {
152 {{ [LED_SCOLOR_DISARMED] = COLOR_GREEN,
153 [LED_SCOLOR_ARMED] = COLOR_BLUE,
154 [LED_SCOLOR_ANIMATION] = COLOR_WHITE,
155 [LED_SCOLOR_BACKGROUND] = COLOR_BLACK,
156 [LED_SCOLOR_BLINKBACKGROUND] = COLOR_BLACK,
157 [LED_SCOLOR_GPSNOSATS] = COLOR_RED,
158 [LED_SCOLOR_GPSNOLOCK] = COLOR_ORANGE,
159 [LED_SCOLOR_GPSLOCKED] = COLOR_GREEN,
163 void pgResetFn_ledStripConfig(ledStripConfig_t *ledStripConfig)
165 memset(ledStripConfig->ledConfigs, 0, LED_MAX_STRIP_LENGTH * sizeof(ledConfig_t));
166 // copy hsv colors as default
167 memset(ledStripConfig->colors, 0, ARRAYLEN(hsv) * sizeof(hsvColor_t));
168 BUILD_BUG_ON(LED_CONFIGURABLE_COLOR_COUNT < ARRAYLEN(hsv));
169 for (unsigned colorIndex = 0; colorIndex < ARRAYLEN(hsv); colorIndex++) {
170 ledStripConfig->colors[colorIndex] = hsv[colorIndex];
172 memcpy_fn(&ledStripConfig->modeColors, &defaultModeColors, sizeof(defaultModeColors));
173 memcpy_fn(&ledStripConfig->specialColors, &defaultSpecialColors, sizeof(defaultSpecialColors));
174 ledStripConfig->ledstrip_visual_beeper = 0;
175 ledStripConfig->ledstrip_aux_channel = THROTTLE;
177 for (int i = 0; i < USABLE_TIMER_CHANNEL_COUNT; i++) {
178 if (timerHardware[i].usageFlags & TIM_USE_LED) {
179 ledStripConfig->ioTag = timerHardware[i].tag;
180 return;
183 ledStripConfig->ioTag = IO_TAG_NONE;
186 static int scaledThrottle;
187 static int auxInput;
189 static void updateLedRingCounts(void);
191 STATIC_UNIT_TESTED void updateDimensions(void)
193 int maxX = 0;
194 int minX = LED_XY_MASK;
195 int maxY = 0;
196 int minY = LED_XY_MASK;
198 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
199 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
201 int ledX = ledGetX(ledConfig);
202 maxX = MAX(ledX, maxX);
203 minX = MIN(ledX, minX);
204 int ledY = ledGetY(ledConfig);
205 maxY = MAX(ledY, maxY);
206 minY = MIN(ledY, minY);
209 ledGridRows = maxY - minY + 1;
211 if (minX < maxX) {
212 lowestXValueForEast = (minX + maxX) / 2 + 1;
213 highestXValueForWest = (minX + maxX - 1) / 2;
214 } else {
215 lowestXValueForEast = LED_XY_MASK / 2;
216 highestXValueForWest = lowestXValueForEast - 1;
218 if (minY < maxY) {
219 lowestYValueForSouth = (minY + maxY) / 2 + 1;
220 highestYValueForNorth = (minY + maxY - 1) / 2;
221 } else {
222 lowestYValueForSouth = LED_XY_MASK / 2;
223 highestYValueForNorth = lowestYValueForSouth - 1;
228 STATIC_UNIT_TESTED void updateLedCount(void)
230 int count = 0, countRing = 0, countScanner= 0;
232 for (int ledIndex = 0; ledIndex < LED_MAX_STRIP_LENGTH; ledIndex++) {
233 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
235 if (!(*ledConfig))
236 break;
238 count++;
240 if (ledGetFunction(ledConfig) == LED_FUNCTION_THRUST_RING)
241 countRing++;
243 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_LARSON_SCANNER))
244 countScanner++;
247 ledCounts.count = count;
248 ledCounts.ring = countRing;
249 ledCounts.larson = countScanner;
252 void reevaluateLedConfig(void)
254 updateLedCount();
255 updateDimensions();
256 updateLedRingCounts();
257 updateRequiredOverlay();
260 // get specialColor by index
261 static const hsvColor_t* getSC(ledSpecialColorIds_e index)
263 return &ledStripConfig()->colors[ledStripConfig()->specialColors.color[index]];
266 static const char directionCodes[LED_DIRECTION_COUNT] = { 'N', 'E', 'S', 'W', 'U', 'D' };
267 static const char baseFunctionCodes[LED_BASEFUNCTION_COUNT] = { 'C', 'F', 'A', 'L', 'S', 'G', 'R' };
268 static const char overlayCodes[LED_OVERLAY_COUNT] = { 'T', 'O', 'B', 'V', 'I', 'W' };
270 #define CHUNK_BUFFER_SIZE 11
272 bool parseLedStripConfig(int ledIndex, const char *config)
274 if (ledIndex >= LED_MAX_STRIP_LENGTH)
275 return false;
277 enum parseState_e {
278 X_COORDINATE,
279 Y_COORDINATE,
280 DIRECTIONS,
281 FUNCTIONS,
282 RING_COLORS,
283 PARSE_STATE_COUNT
285 static const char chunkSeparators[PARSE_STATE_COUNT] = {',', ':', ':', ':', '\0'};
287 ledConfig_t *ledConfig = &ledStripConfigMutable()->ledConfigs[ledIndex];
288 memset(ledConfig, 0, sizeof(ledConfig_t));
290 int x = 0, y = 0, color = 0; // initialize to prevent warnings
291 int baseFunction = 0;
292 int overlay_flags = 0;
293 int direction_flags = 0;
295 for (enum parseState_e parseState = 0; parseState < PARSE_STATE_COUNT; parseState++) {
296 char chunk[CHUNK_BUFFER_SIZE];
298 char chunkSeparator = chunkSeparators[parseState];
299 int chunkIndex = 0;
300 while (*config && *config != chunkSeparator && chunkIndex < (CHUNK_BUFFER_SIZE - 1)) {
301 chunk[chunkIndex++] = *config++;
303 chunk[chunkIndex++] = 0; // zero-terminate chunk
304 if (*config != chunkSeparator) {
305 return false;
307 config++; // skip separator
309 switch (parseState) {
310 case X_COORDINATE:
311 x = atoi(chunk);
312 break;
313 case Y_COORDINATE:
314 y = atoi(chunk);
315 break;
316 case DIRECTIONS:
317 for (char *ch = chunk; *ch; ch++) {
318 for (ledDirectionId_e dir = 0; dir < LED_DIRECTION_COUNT; dir++) {
319 if (directionCodes[dir] == *ch) {
320 direction_flags |= LED_FLAG_DIRECTION(dir);
321 break;
325 break;
326 case FUNCTIONS:
327 for (char *ch = chunk; *ch; ch++) {
328 for (ledBaseFunctionId_e fn = 0; fn < LED_BASEFUNCTION_COUNT; fn++) {
329 if (baseFunctionCodes[fn] == *ch) {
330 baseFunction = fn;
331 break;
335 for (ledOverlayId_e ol = 0; ol < LED_OVERLAY_COUNT; ol++) {
336 if (overlayCodes[ol] == *ch) {
337 overlay_flags |= LED_FLAG_OVERLAY(ol);
338 break;
342 break;
343 case RING_COLORS:
344 color = atoi(chunk);
345 if (color >= LED_CONFIGURABLE_COLOR_COUNT)
346 color = 0;
347 break;
348 case PARSE_STATE_COUNT:; // prevent warning
352 *ledConfig = DEFINE_LED(x, y, color, direction_flags, baseFunction, overlay_flags, 0);
354 reevaluateLedConfig();
356 return true;
359 void generateLedConfig(ledConfig_t *ledConfig, char *ledConfigBuffer, size_t bufferSize)
361 char directions[LED_DIRECTION_COUNT + 1];
362 char baseFunctionOverlays[LED_OVERLAY_COUNT + 2];
364 memset(ledConfigBuffer, 0, bufferSize);
366 char *dptr = directions;
367 for (ledDirectionId_e dir = 0; dir < LED_DIRECTION_COUNT; dir++) {
368 if (ledGetDirectionBit(ledConfig, dir)) {
369 *dptr++ = directionCodes[dir];
372 *dptr = 0;
374 char *fptr = baseFunctionOverlays;
375 *fptr++ = baseFunctionCodes[ledGetFunction(ledConfig)];
377 for (ledOverlayId_e ol = 0; ol < LED_OVERLAY_COUNT; ol++) {
378 if (ledGetOverlayBit(ledConfig, ol)) {
379 *fptr++ = overlayCodes[ol];
382 *fptr = 0;
384 // TODO - check buffer length
385 tfp_sprintf(ledConfigBuffer, "%u,%u:%s:%s:%u", ledGetX(ledConfig), ledGetY(ledConfig), directions, baseFunctionOverlays, ledGetColor(ledConfig));
388 typedef enum {
389 // the ordering is important, see below how NSEW is mapped to NE/SE/NW/SW
390 QUADRANT_NORTH = 1 << 0,
391 QUADRANT_SOUTH = 1 << 1,
392 QUADRANT_EAST = 1 << 2,
393 QUADRANT_WEST = 1 << 3,
394 } quadrant_e;
396 static quadrant_e getLedQuadrant(const int ledIndex)
398 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
400 int x = ledGetX(ledConfig);
401 int y = ledGetY(ledConfig);
403 int quad = 0;
404 if (y <= highestYValueForNorth)
405 quad |= QUADRANT_NORTH;
406 else if (y >= lowestYValueForSouth)
407 quad |= QUADRANT_SOUTH;
408 if (x >= lowestXValueForEast)
409 quad |= QUADRANT_EAST;
410 else if (x <= highestXValueForWest)
411 quad |= QUADRANT_WEST;
413 return quad;
416 static hsvColor_t* getDirectionalModeColor(const int ledIndex, const modeColorIndexes_t *modeColors)
418 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
419 const int ledDirection = ledGetDirection(ledConfig);
421 for (unsigned i = 0; i < LED_DIRECTION_COUNT; i++) {
422 if (ledDirection & (1 << i)) {
423 return &ledStripConfigMutable()->colors[modeColors->color[i]];
427 return NULL;
430 // map flight mode to led mode, in order of priority
431 // flightMode == 0 is always active
432 static const struct {
433 uint16_t flightMode;
434 uint8_t ledMode;
435 } flightModeToLed[] = {
436 {HEADFREE_MODE, LED_MODE_HEADFREE},
437 #ifdef USE_MAG
438 {MAG_MODE, LED_MODE_MAG},
439 #endif
440 #ifdef USE_BARO
441 {BARO_MODE, LED_MODE_BARO},
442 #endif
443 {HORIZON_MODE, LED_MODE_HORIZON},
444 {ANGLE_MODE, LED_MODE_ANGLE},
445 {0, LED_MODE_ORIENTATION},
448 static void applyLedFixedLayers(void)
450 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
451 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
452 hsvColor_t color = *getSC(LED_SCOLOR_BACKGROUND);
454 int fn = ledGetFunction(ledConfig);
455 int hOffset = HSV_HUE_MAX + 1;
457 switch (fn) {
458 case LED_FUNCTION_COLOR:
459 color = ledStripConfig()->colors[ledGetColor(ledConfig)];
461 hsvColor_t nextColor = ledStripConfig()->colors[(ledGetColor(ledConfig) + 1 + LED_CONFIGURABLE_COLOR_COUNT) % LED_CONFIGURABLE_COLOR_COUNT];
462 hsvColor_t previousColor = ledStripConfig()->colors[(ledGetColor(ledConfig) - 1 + LED_CONFIGURABLE_COLOR_COUNT) % LED_CONFIGURABLE_COLOR_COUNT];
464 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_THROTTLE)) { //smooth fade with selected Aux channel of all HSV values from previousColor through color to nextColor
465 int centerPWM = (PWM_RANGE_MIN + PWM_RANGE_MAX) / 2;
466 if (auxInput < centerPWM) {
467 color.h = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.h, color.h);
468 color.s = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.s, color.s);
469 color.v = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.v, color.v);
470 } else {
471 color.h = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.h, nextColor.h);
472 color.s = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.s, nextColor.s);
473 color.v = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.v, nextColor.v);
477 break;
479 case LED_FUNCTION_FLIGHT_MODE:
480 for (unsigned i = 0; i < ARRAYLEN(flightModeToLed); i++)
481 if (!flightModeToLed[i].flightMode || FLIGHT_MODE(flightModeToLed[i].flightMode)) {
482 const hsvColor_t *directionalColor = getDirectionalModeColor(ledIndex, &ledStripConfig()->modeColors[flightModeToLed[i].ledMode]);
483 if (directionalColor) {
484 color = *directionalColor;
487 break; // stop on first match
489 break;
491 case LED_FUNCTION_ARM_STATE:
492 color = ARMING_FLAG(ARMED) ? *getSC(LED_SCOLOR_ARMED) : *getSC(LED_SCOLOR_DISARMED);
493 break;
495 case LED_FUNCTION_BATTERY:
496 color = HSV(RED);
497 hOffset += scaleRange(calculateBatteryPercentageRemaining(), 0, 100, -30, 120);
498 break;
500 case LED_FUNCTION_RSSI:
501 color = HSV(RED);
502 hOffset += scaleRange(getRssi() * 100, 0, 1023, -30, 120);
503 break;
505 default:
506 break;
509 if ((fn != LED_FUNCTION_COLOR) && ledGetOverlayBit(ledConfig, LED_OVERLAY_THROTTLE)) {
510 hOffset += scaleRange(auxInput, PWM_RANGE_MIN, PWM_RANGE_MAX, 0, HSV_HUE_MAX + 1);
513 color.h = (color.h + hOffset) % (HSV_HUE_MAX + 1);
514 setLedHsv(ledIndex, &color);
518 static void applyLedHsv(uint32_t mask, const hsvColor_t *color)
520 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
521 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
522 if ((*ledConfig & mask) == mask)
523 setLedHsv(ledIndex, color);
527 typedef enum {
528 WARNING_ARMING_DISABLED,
529 WARNING_LOW_BATTERY,
530 WARNING_FAILSAFE,
531 } warningFlags_e;
533 static void applyLedWarningLayer(bool updateNow, timeUs_t *timer)
535 static uint8_t warningFlashCounter = 0;
536 static uint8_t warningFlags = 0; // non-zero during blinks
538 if (updateNow) {
539 // keep counter running, so it stays in sync with blink
540 warningFlashCounter++;
541 warningFlashCounter &= 0xF;
543 if (warningFlashCounter == 0) { // update when old flags was processed
544 warningFlags = 0;
545 if (batteryConfig()->voltageMeterSource != VOLTAGE_METER_NONE && getBatteryState() != BATTERY_OK)
546 warningFlags |= 1 << WARNING_LOW_BATTERY;
547 if (failsafeIsActive())
548 warningFlags |= 1 << WARNING_FAILSAFE;
549 if (!ARMING_FLAG(ARMED) && isArmingDisabled())
550 warningFlags |= 1 << WARNING_ARMING_DISABLED;
552 *timer += HZ_TO_US(10);
555 const hsvColor_t *warningColor = NULL;
557 if (warningFlags) {
558 bool colorOn = (warningFlashCounter % 2) == 0; // w_w_
559 warningFlags_e warningId = warningFlashCounter / 4;
560 if (warningFlags & (1 << warningId)) {
561 switch (warningId) {
562 case WARNING_ARMING_DISABLED:
563 warningColor = colorOn ? &HSV(GREEN) : &HSV(BLACK);
564 break;
565 case WARNING_LOW_BATTERY:
566 warningColor = colorOn ? &HSV(RED) : &HSV(BLACK);
567 break;
568 case WARNING_FAILSAFE:
569 warningColor = colorOn ? &HSV(YELLOW) : &HSV(BLUE);
570 break;
571 default:;
574 } else {
575 if (isBeeperOn()) {
576 warningColor = &HSV(ORANGE);
580 if (warningColor) {
581 applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_WARNING)), warningColor);
585 #ifdef USE_VTX_COMMON
586 static void applyLedVtxLayer(bool updateNow, timeUs_t *timer)
588 static uint16_t frequency = 0;
589 static uint8_t power = 255;
590 static uint8_t pit = 255;
591 static uint8_t showSettings = false;
592 static uint16_t lastCheck = 0;
593 static bool blink = false;
595 const vtxDevice_t *vtxDevice = vtxCommonDevice();
596 if (!vtxDevice) {
597 return;
600 uint8_t band = 255, channel = 255;
601 uint16_t check = 0;
603 if (updateNow) {
604 // keep counter running, so it stays in sync with vtx
605 vtxCommonGetBandAndChannel(vtxDevice, &band, &channel);
606 vtxCommonGetPowerIndex(vtxDevice, &power);
607 vtxCommonGetPitMode(vtxDevice, &pit);
609 frequency = vtx58frequencyTable[band - 1][channel - 1]; //subtracting 1 from band and channel so that correct frequency is returned.
610 //might not be correct for tramp but should fix smart audio.
611 // check if last vtx values have changed.
612 check = pit + (power << 1) + (band << 4) + (channel << 8);
613 if (!showSettings && check != lastCheck) {
614 // display settings for 3 seconds.
615 showSettings = 15;
617 lastCheck = check; // quick way to check if any settings changed.
619 if (showSettings) {
620 showSettings--;
622 blink = !blink;
623 *timer += HZ_TO_US(5); // check 5 times a second
626 hsvColor_t color = {0, 0, 0};
627 if (showSettings) { // show settings
628 uint8_t vtxLedCount = 0;
629 for (int i = 0; i < ledCounts.count && vtxLedCount < 6; ++i) {
630 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
631 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_VTX)) {
632 if (vtxLedCount == 0) {
633 color.h = HSV(GREEN).h;
634 color.s = HSV(GREEN).s;
635 color.v = blink ? 15 : 0; // blink received settings
637 else if (vtxLedCount > 0 && power >= vtxLedCount && !pit) { // show power
638 color.h = HSV(ORANGE).h;
639 color.s = HSV(ORANGE).s;
640 color.v = blink ? 15 : 0; // blink received settings
642 else { // turn rest off
643 color.h = HSV(BLACK).h;
644 color.s = HSV(BLACK).s;
645 color.v = HSV(BLACK).v;
647 setLedHsv(i, &color);
648 ++vtxLedCount;
652 else { // show frequency
653 // calculate the VTX color based on frequency
654 int colorIndex = 0;
655 if (frequency <= 5672) {
656 colorIndex = COLOR_WHITE;
657 } else if (frequency <= 5711) {
658 colorIndex = COLOR_RED;
659 } else if (frequency <= 5750) {
660 colorIndex = COLOR_ORANGE;
661 } else if (frequency <= 5789) {
662 colorIndex = COLOR_YELLOW;
663 } else if (frequency <= 5829) {
664 colorIndex = COLOR_GREEN;
665 } else if (frequency <= 5867) {
666 colorIndex = COLOR_BLUE;
667 } else if (frequency <= 5906) {
668 colorIndex = COLOR_DARK_VIOLET;
669 } else {
670 colorIndex = COLOR_DEEP_PINK;
672 hsvColor_t color = ledStripConfig()->colors[colorIndex];
673 color.v = pit ? (blink ? 15 : 0) : 255; // blink when in pit mode
674 applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_VTX)), &color);
677 #endif
679 static void applyLedBatteryLayer(bool updateNow, timeUs_t *timer)
681 static bool flash = false;
683 int timerDelayUs = HZ_TO_US(1);
685 if (updateNow) {
687 switch (getBatteryState()) {
688 case BATTERY_OK:
689 flash = true;
690 timerDelayUs = HZ_TO_US(1);
692 break;
693 case BATTERY_WARNING:
694 flash = !flash;
695 timerDelayUs = HZ_TO_US(2);
697 break;
698 default:
699 flash = !flash;
700 timerDelayUs = HZ_TO_US(8);
702 break;
706 *timer += timerDelayUs;
708 if (!flash) {
709 const hsvColor_t *bgc = getSC(LED_SCOLOR_BACKGROUND);
710 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_BATTERY), bgc);
714 static void applyLedRssiLayer(bool updateNow, timeUs_t *timer)
716 static bool flash = false;
718 int timerDelay = HZ_TO_US(1);
720 if (updateNow) {
721 int state = (getRssi() * 100) / 1023;
723 if (state > 50) {
724 flash = true;
725 timerDelay = HZ_TO_US(1);
726 } else if (state > 20) {
727 flash = !flash;
728 timerDelay = HZ_TO_US(2);
729 } else {
730 flash = !flash;
731 timerDelay = HZ_TO_US(8);
735 *timer += timerDelay;
737 if (!flash) {
738 const hsvColor_t *bgc = getSC(LED_SCOLOR_BACKGROUND);
739 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_RSSI), bgc);
743 #ifdef USE_GPS
744 static void applyLedGpsLayer(bool updateNow, timeUs_t *timer)
747 static uint8_t gpsPauseCounter = 0;
748 const uint8_t blinkPauseLength = 4;
750 if (updateNow) {
751 static uint8_t gpsFlashCounter = 0;
752 if (gpsPauseCounter > 0) {
753 gpsPauseCounter--;
754 } else if (gpsFlashCounter >= gpsSol.numSat) {
755 gpsFlashCounter = 0;
756 gpsPauseCounter = blinkPauseLength;
757 } else {
758 gpsFlashCounter++;
759 gpsPauseCounter = 1;
761 *timer += HZ_TO_US(2.5f);
764 const hsvColor_t *gpsColor;
766 if (gpsSol.numSat == 0 || !sensors(SENSOR_GPS)) {
767 gpsColor = getSC(LED_SCOLOR_GPSNOSATS);
768 } else {
769 bool colorOn = gpsPauseCounter == 0; // each interval starts with pause
770 if (STATE(GPS_FIX)) {
771 gpsColor = colorOn ? getSC(LED_SCOLOR_GPSLOCKED) : getSC(LED_SCOLOR_BACKGROUND);
772 } else {
773 gpsColor = colorOn ? getSC(LED_SCOLOR_GPSNOLOCK) : getSC(LED_SCOLOR_GPSNOSATS);
777 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_GPS), gpsColor);
780 #endif
782 #define INDICATOR_DEADBAND 25
784 static void applyLedIndicatorLayer(bool updateNow, timeUs_t *timer)
786 static bool flash = 0;
788 if (updateNow) {
789 if (rxIsReceivingSignal()) {
790 // calculate update frequency
791 int scale = MAX(ABS(rcCommand[ROLL]), ABS(rcCommand[PITCH])); // 0 - 500
792 scale = scale - INDICATOR_DEADBAND; // start increasing frequency right after deadband
793 *timer += HZ_TO_US(5 + (45 * scale) / (500 - INDICATOR_DEADBAND)); // 5 - 50Hz update, 2.5 - 25Hz blink
795 flash = !flash;
796 } else {
797 *timer += HZ_TO_US(5);
801 if (!flash)
802 return;
804 const hsvColor_t *flashColor = &HSV(ORANGE); // TODO - use user color?
806 quadrant_e quadrants = 0;
807 if (rcCommand[ROLL] > INDICATOR_DEADBAND) {
808 quadrants |= QUADRANT_EAST;
809 } else if (rcCommand[ROLL] < -INDICATOR_DEADBAND) {
810 quadrants |= QUADRANT_WEST;
812 if (rcCommand[PITCH] > INDICATOR_DEADBAND) {
813 quadrants |= QUADRANT_NORTH;
814 } else if (rcCommand[PITCH] < -INDICATOR_DEADBAND) {
815 quadrants |= QUADRANT_SOUTH;
818 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
819 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
820 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_INDICATOR)) {
821 if (getLedQuadrant(ledIndex) & quadrants)
822 setLedHsv(ledIndex, flashColor);
827 #define ROTATION_SEQUENCE_LED_COUNT 6 // 2 on, 4 off
828 #define ROTATION_SEQUENCE_LED_WIDTH 2 // 2 on
830 static void updateLedRingCounts(void)
832 int seqLen;
833 // try to split in segments/rings of exactly ROTATION_SEQUENCE_LED_COUNT leds
834 if ((ledCounts.ring % ROTATION_SEQUENCE_LED_COUNT) == 0) {
835 seqLen = ROTATION_SEQUENCE_LED_COUNT;
836 } else {
837 seqLen = ledCounts.ring;
838 // else split up in equal segments/rings of at most ROTATION_SEQUENCE_LED_COUNT leds
839 // TODO - improve partitioning (15 leds -> 3x5)
840 while ((seqLen > ROTATION_SEQUENCE_LED_COUNT) && ((seqLen % 2) == 0)) {
841 seqLen /= 2;
844 ledCounts.ringSeqLen = seqLen;
847 static void applyLedThrustRingLayer(bool updateNow, timeUs_t *timer)
849 static uint8_t rotationPhase;
850 int ledRingIndex = 0;
852 if (updateNow) {
853 rotationPhase = rotationPhase > 0 ? rotationPhase - 1 : ledCounts.ringSeqLen - 1;
855 *timer += HZ_TO_US(5 + (45 * scaledThrottle) / 100); // 5 - 50Hz update rate
858 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
859 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
860 if (ledGetFunction(ledConfig) == LED_FUNCTION_THRUST_RING) {
862 bool applyColor;
863 if (ARMING_FLAG(ARMED)) {
864 applyColor = (ledRingIndex + rotationPhase) % ledCounts.ringSeqLen < ROTATION_SEQUENCE_LED_WIDTH;
865 } else {
866 applyColor = !(ledRingIndex % 2); // alternating pattern
869 if (applyColor) {
870 const hsvColor_t *ringColor = &ledStripConfig()->colors[ledGetColor(ledConfig)];
871 setLedHsv(ledIndex, ringColor);
874 ledRingIndex++;
879 typedef struct larsonParameters_s {
880 uint8_t currentBrightness;
881 int8_t currentIndex;
882 int8_t direction;
883 } larsonParameters_t;
885 static int brightnessForLarsonIndex(larsonParameters_t *larsonParameters, uint8_t larsonIndex)
887 int offset = larsonIndex - larsonParameters->currentIndex;
888 static const int larsonLowValue = 8;
890 if (ABS(offset) > 1)
891 return (larsonLowValue);
893 if (offset == 0)
894 return (larsonParameters->currentBrightness);
896 if (larsonParameters->direction == offset) {
897 return (larsonParameters->currentBrightness - 127);
900 return (255 - larsonParameters->currentBrightness);
904 static void larsonScannerNextStep(larsonParameters_t *larsonParameters, int delta)
906 if (larsonParameters->currentBrightness > (255 - delta)) {
907 larsonParameters->currentBrightness = 127;
908 if (larsonParameters->currentIndex >= ledCounts.larson || larsonParameters->currentIndex < 0) {
909 larsonParameters->direction = -larsonParameters->direction;
911 larsonParameters->currentIndex += larsonParameters->direction;
912 } else {
913 larsonParameters->currentBrightness += delta;
917 static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer)
919 static larsonParameters_t larsonParameters = { 0, 0, 1 };
921 if (updateNow) {
922 larsonScannerNextStep(&larsonParameters, 15);
923 *timer += HZ_TO_US(60);
926 int scannerLedIndex = 0;
927 for (unsigned i = 0; i < ledCounts.count; i++) {
929 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
931 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_LARSON_SCANNER)) {
932 hsvColor_t ledColor;
933 getLedHsv(i, &ledColor);
934 ledColor.v = brightnessForLarsonIndex(&larsonParameters, scannerLedIndex);
935 setLedHsv(i, &ledColor);
936 scannerLedIndex++;
941 // blink twice, then wait ; either always or just when landing
942 static void applyLedBlinkLayer(bool updateNow, timeUs_t *timer)
944 const uint16_t blinkPattern = 0x8005; // 0b1000000000000101;
945 static uint16_t blinkMask;
947 if (updateNow) {
948 blinkMask = blinkMask >> 1;
949 if (blinkMask <= 1)
950 blinkMask = blinkPattern;
952 *timer += HZ_TO_US(10);
955 bool ledOn = (blinkMask & 1); // b_b_____...
956 if (!ledOn) {
957 for (int i = 0; i < ledCounts.count; ++i) {
958 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
960 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_BLINK)) {
961 setLedHsv(i, getSC(LED_SCOLOR_BLINKBACKGROUND));
967 #ifdef USE_LED_ANIMATION
968 static void applyLedAnimationLayer(bool updateNow, timeUs_t *timer)
970 static uint8_t frameCounter = 0;
971 const int animationFrames = ledGridRows;
972 if (updateNow) {
973 frameCounter = (frameCounter + 1 < animationFrames) ? frameCounter + 1 : 0;
974 *timer += HZ_TO_US(20);
977 if (ARMING_FLAG(ARMED))
978 return;
980 int previousRow = frameCounter > 0 ? frameCounter - 1 : animationFrames - 1;
981 int currentRow = frameCounter;
982 int nextRow = (frameCounter + 1 < animationFrames) ? frameCounter + 1 : 0;
984 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
985 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
987 if (ledGetY(ledConfig) == previousRow) {
988 setLedHsv(ledIndex, getSC(LED_SCOLOR_ANIMATION));
989 scaleLedValue(ledIndex, 50);
990 } else if (ledGetY(ledConfig) == currentRow) {
991 setLedHsv(ledIndex, getSC(LED_SCOLOR_ANIMATION));
992 } else if (ledGetY(ledConfig) == nextRow) {
993 scaleLedValue(ledIndex, 50);
997 #endif
999 typedef enum {
1000 timBlink,
1001 timLarson,
1002 timBattery,
1003 timRssi,
1004 #ifdef USE_GPS
1005 timGps,
1006 #endif
1007 timWarning,
1008 #ifdef USE_VTX_COMMON
1009 timVtx,
1010 #endif
1011 timIndicator,
1012 #ifdef USE_LED_ANIMATION
1013 timAnimation,
1014 #endif
1015 timRing,
1016 timTimerCount
1017 } timId_e;
1019 static timeUs_t timerVal[timTimerCount];
1020 static uint16_t disabledTimerMask;
1022 STATIC_ASSERT(timTimerCount <= sizeof(disabledTimerMask) * 8, disabledTimerMask_too_small);
1024 // function to apply layer.
1025 // function must replan self using timer pointer
1026 // when updateNow is true (timer triggered), state must be updated first,
1027 // before calculating led state. Otherwise update started by different trigger
1028 // may modify LED state.
1029 typedef void applyLayerFn_timed(bool updateNow, timeUs_t *timer);
1031 static applyLayerFn_timed* layerTable[] = {
1032 [timBlink] = &applyLedBlinkLayer,
1033 [timLarson] = &applyLarsonScannerLayer,
1034 [timBattery] = &applyLedBatteryLayer,
1035 [timRssi] = &applyLedRssiLayer,
1036 #ifdef USE_GPS
1037 [timGps] = &applyLedGpsLayer,
1038 #endif
1039 [timWarning] = &applyLedWarningLayer,
1040 #ifdef USE_VTX_COMMON
1041 [timVtx] = &applyLedVtxLayer,
1042 #endif
1043 [timIndicator] = &applyLedIndicatorLayer,
1044 #ifdef USE_LED_ANIMATION
1045 [timAnimation] = &applyLedAnimationLayer,
1046 #endif
1047 [timRing] = &applyLedThrustRingLayer
1050 bool isOverlayTypeUsed(ledOverlayId_e overlayType)
1052 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
1053 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
1054 if (ledGetOverlayBit(ledConfig, overlayType)) {
1055 return true;
1058 return false;
1061 void updateRequiredOverlay(void)
1063 disabledTimerMask = 0;
1064 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_BLINK) << timBlink;
1065 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_LARSON_SCANNER) << timLarson;
1066 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_WARNING) << timWarning;
1067 #ifdef USE_VTX_COMMON
1068 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_VTX) << timVtx;
1069 #endif
1070 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_INDICATOR) << timIndicator;
1073 void ledStripUpdate(timeUs_t currentTimeUs)
1075 if (!(ledStripInitialised && isWS2811LedStripReady())) {
1076 return;
1079 if (IS_RC_MODE_ACTIVE(BOXLEDLOW) && !(ledStripConfig()->ledstrip_visual_beeper && isBeeperOn())) {
1080 if (ledStripEnabled) {
1081 ledStripDisable();
1082 ledStripEnabled = false;
1084 return;
1086 ledStripEnabled = true;
1088 const uint32_t now = currentTimeUs;
1090 // test all led timers, setting corresponding bits
1091 uint32_t timActive = 0;
1092 for (timId_e timId = 0; timId < timTimerCount; timId++) {
1093 if (!(disabledTimerMask & (1 << timId))) {
1094 // sanitize timer value, so that it can be safely incremented. Handles inital timerVal value.
1095 const timeDelta_t delta = cmpTimeUs(now, timerVal[timId]);
1096 // max delay is limited to 5s
1097 if (delta < 0 && delta > -MAX_TIMER_DELAY)
1098 continue; // not ready yet
1099 timActive |= 1 << timId;
1100 if (delta >= 100 * 1000 || delta < 0) {
1101 timerVal[timId] = now;
1106 if (!timActive)
1107 return; // no change this update, keep old state
1109 // apply all layers; triggered timed functions has to update timers
1111 scaledThrottle = ARMING_FLAG(ARMED) ? scaleRange(rcData[THROTTLE], PWM_RANGE_MIN, PWM_RANGE_MAX, 0, 100) : 0;
1112 auxInput = rcData[ledStripConfig()->ledstrip_aux_channel];
1114 applyLedFixedLayers();
1116 for (timId_e timId = 0; timId < ARRAYLEN(layerTable); timId++) {
1117 uint32_t *timer = &timerVal[timId];
1118 bool updateNow = timActive & (1 << timId);
1119 (*layerTable[timId])(updateNow, timer);
1121 ws2811UpdateStrip((ledStripFormatRGB_e)ledStripConfig()->ledstrip_grb_rgb);
1124 bool parseColor(int index, const char *colorConfig)
1126 const char *remainingCharacters = colorConfig;
1128 hsvColor_t *color = &ledStripConfigMutable()->colors[index];
1130 bool result = true;
1131 static const uint16_t hsv_limit[HSV_COLOR_COMPONENT_COUNT] = {
1132 [HSV_HUE] = HSV_HUE_MAX,
1133 [HSV_SATURATION] = HSV_SATURATION_MAX,
1134 [HSV_VALUE] = HSV_VALUE_MAX,
1136 for (int componentIndex = 0; result && componentIndex < HSV_COLOR_COMPONENT_COUNT; componentIndex++) {
1137 int val = atoi(remainingCharacters);
1138 if (val > hsv_limit[componentIndex]) {
1139 result = false;
1140 break;
1142 switch (componentIndex) {
1143 case HSV_HUE:
1144 color->h = val;
1145 break;
1146 case HSV_SATURATION:
1147 color->s = val;
1148 break;
1149 case HSV_VALUE:
1150 color->v = val;
1151 break;
1153 remainingCharacters = strchr(remainingCharacters, ',');
1154 if (remainingCharacters) {
1155 remainingCharacters++; // skip separator
1156 } else {
1157 if (componentIndex < HSV_COLOR_COMPONENT_COUNT - 1) {
1158 result = false;
1163 if (!result) {
1164 memset(color, 0, sizeof(*color));
1167 return result;
1171 * Redefine a color in a mode.
1172 * */
1173 bool setModeColor(ledModeIndex_e modeIndex, int modeColorIndex, int colorIndex)
1175 // check color
1176 if (colorIndex < 0 || colorIndex >= LED_CONFIGURABLE_COLOR_COUNT)
1177 return false;
1178 if (modeIndex < LED_MODE_COUNT) { // modeIndex_e is unsigned, so one-sided test is enough
1179 if (modeColorIndex < 0 || modeColorIndex >= LED_DIRECTION_COUNT)
1180 return false;
1181 ledStripConfigMutable()->modeColors[modeIndex].color[modeColorIndex] = colorIndex;
1182 } else if (modeIndex == LED_SPECIAL) {
1183 if (modeColorIndex < 0 || modeColorIndex >= LED_SPECIAL_COLOR_COUNT)
1184 return false;
1185 ledStripConfigMutable()->specialColors.color[modeColorIndex] = colorIndex;
1186 } else if (modeIndex == LED_AUX_CHANNEL) {
1187 if (modeColorIndex < 0 || modeColorIndex >= 1)
1188 return false;
1189 ledStripConfigMutable()->ledstrip_aux_channel = colorIndex;
1190 } else {
1191 return false;
1193 return true;
1196 void ledStripInit(void)
1198 colors = ledStripConfigMutable()->colors;
1199 modeColors = ledStripConfig()->modeColors;
1200 specialColors = ledStripConfig()->specialColors;
1201 ledStripInitialised = false;
1204 void ledStripEnable(void)
1206 reevaluateLedConfig();
1207 ledStripInitialised = true;
1209 ws2811LedStripInit(ledStripConfig()->ioTag);
1212 static void ledStripDisable(void)
1214 setStripColor(&HSV(BLACK));
1216 ws2811UpdateStrip((ledStripFormatRGB_e)ledStripConfig()->ledstrip_grb_rgb);
1218 #endif