Replaced instances of '#include <platform.h>' with '#include "platform.h"'.
[betaflight.git] / src / main / io / ledstrip.c
blob56e10a93081b1138398985c3db618e9ac4f014d4
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/pid.h"
57 #include "flight/servos.h"
59 #include "io/beeper.h"
60 #include "io/gimbal.h"
61 #include "io/gps.h"
62 #include "io/ledstrip.h"
63 #include "io/serial.h"
64 #include "io/vtx_string.h"
66 #include "rx/rx.h"
68 #include "sensors/acceleration.h"
69 #include "sensors/barometer.h"
70 #include "sensors/battery.h"
71 #include "sensors/boardalignment.h"
72 #include "sensors/gyro.h"
73 #include "sensors/sensors.h"
75 #include "telemetry/telemetry.h"
77 PG_REGISTER_WITH_RESET_FN(ledStripConfig_t, ledStripConfig, PG_LED_STRIP_CONFIG, 0);
79 static bool ledStripInitialised = false;
80 static bool ledStripEnabled = true;
82 static void ledStripDisable(void);
84 //#define USE_LED_ANIMATION
86 #define HZ_TO_US(hz) ((int32_t)((1000 * 1000) / (hz)))
88 #define MAX_TIMER_DELAY (5 * 1000 * 1000)
90 #if LED_MAX_STRIP_LENGTH > WS2811_LED_STRIP_LENGTH
91 # error "Led strip length must match driver"
92 #endif
94 typedef enum {
95 COLOR_BLACK = 0,
96 COLOR_WHITE,
97 COLOR_RED,
98 COLOR_ORANGE,
99 COLOR_YELLOW,
100 COLOR_LIME_GREEN,
101 COLOR_GREEN,
102 COLOR_MINT_GREEN,
103 COLOR_CYAN,
104 COLOR_LIGHT_BLUE,
105 COLOR_BLUE,
106 COLOR_DARK_VIOLET,
107 COLOR_MAGENTA,
108 COLOR_DEEP_PINK
109 } colorId_e;
111 const hsvColor_t hsv[] = {
112 // H S V
113 [COLOR_BLACK] = { 0, 0, 0},
114 [COLOR_WHITE] = { 0, 255, 255},
115 [COLOR_RED] = { 0, 0, 255},
116 [COLOR_ORANGE] = { 30, 0, 255},
117 [COLOR_YELLOW] = { 60, 0, 255},
118 [COLOR_LIME_GREEN] = { 90, 0, 255},
119 [COLOR_GREEN] = {120, 0, 255},
120 [COLOR_MINT_GREEN] = {150, 0, 255},
121 [COLOR_CYAN] = {180, 0, 255},
122 [COLOR_LIGHT_BLUE] = {210, 0, 255},
123 [COLOR_BLUE] = {240, 0, 255},
124 [COLOR_DARK_VIOLET] = {270, 0, 255},
125 [COLOR_MAGENTA] = {300, 0, 255},
126 [COLOR_DEEP_PINK] = {330, 0, 255},
128 // macro to save typing on default colors
129 #define HSV(color) (hsv[COLOR_ ## color])
131 STATIC_UNIT_TESTED uint8_t ledGridRows;
132 // grid offsets
133 STATIC_UNIT_TESTED int8_t highestYValueForNorth;
134 STATIC_UNIT_TESTED int8_t lowestYValueForSouth;
135 STATIC_UNIT_TESTED int8_t highestXValueForWest;
136 STATIC_UNIT_TESTED int8_t lowestXValueForEast;
138 STATIC_UNIT_TESTED ledCounts_t ledCounts;
140 static const modeColorIndexes_t defaultModeColors[] = {
141 // NORTH EAST SOUTH WEST UP DOWN
142 [LED_MODE_ORIENTATION] = {{ COLOR_WHITE, COLOR_DARK_VIOLET, COLOR_RED, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
143 [LED_MODE_HEADFREE] = {{ COLOR_LIME_GREEN, COLOR_DARK_VIOLET, COLOR_ORANGE, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
144 [LED_MODE_HORIZON] = {{ COLOR_BLUE, COLOR_DARK_VIOLET, COLOR_YELLOW, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
145 [LED_MODE_ANGLE] = {{ COLOR_CYAN, COLOR_DARK_VIOLET, COLOR_YELLOW, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
146 [LED_MODE_MAG] = {{ COLOR_MINT_GREEN, COLOR_DARK_VIOLET, COLOR_ORANGE, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
147 [LED_MODE_BARO] = {{ COLOR_LIGHT_BLUE, COLOR_DARK_VIOLET, COLOR_RED, COLOR_DEEP_PINK, COLOR_BLUE, COLOR_ORANGE }},
150 static const specialColorIndexes_t defaultSpecialColors[] = {
151 {{ [LED_SCOLOR_DISARMED] = COLOR_GREEN,
152 [LED_SCOLOR_ARMED] = COLOR_BLUE,
153 [LED_SCOLOR_ANIMATION] = COLOR_WHITE,
154 [LED_SCOLOR_BACKGROUND] = COLOR_BLACK,
155 [LED_SCOLOR_BLINKBACKGROUND] = COLOR_BLACK,
156 [LED_SCOLOR_GPSNOSATS] = COLOR_RED,
157 [LED_SCOLOR_GPSNOLOCK] = COLOR_ORANGE,
158 [LED_SCOLOR_GPSLOCKED] = COLOR_GREEN,
162 void pgResetFn_ledStripConfig(ledStripConfig_t *ledStripConfig)
164 memset(ledStripConfig->ledConfigs, 0, LED_MAX_STRIP_LENGTH * sizeof(ledConfig_t));
165 // copy hsv colors as default
166 memset(ledStripConfig->colors, 0, ARRAYLEN(hsv) * sizeof(hsvColor_t));
167 BUILD_BUG_ON(LED_CONFIGURABLE_COLOR_COUNT < ARRAYLEN(hsv));
168 for (unsigned colorIndex = 0; colorIndex < ARRAYLEN(hsv); colorIndex++) {
169 ledStripConfig->colors[colorIndex] = hsv[colorIndex];
171 memcpy_fn(&ledStripConfig->modeColors, &defaultModeColors, sizeof(defaultModeColors));
172 memcpy_fn(&ledStripConfig->specialColors, &defaultSpecialColors, sizeof(defaultSpecialColors));
173 ledStripConfig->ledstrip_visual_beeper = 0;
174 ledStripConfig->ledstrip_aux_channel = THROTTLE;
176 #ifndef UNIT_TEST
177 ledStripConfig->ioTag = timerioTagGetByUsage(TIM_USE_LED, 0);
178 #endif
181 static int scaledThrottle;
182 static int auxInput;
184 static void updateLedRingCounts(void);
186 STATIC_UNIT_TESTED void updateDimensions(void)
188 int maxX = 0;
189 int minX = LED_XY_MASK;
190 int maxY = 0;
191 int minY = LED_XY_MASK;
193 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
194 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
196 int ledX = ledGetX(ledConfig);
197 maxX = MAX(ledX, maxX);
198 minX = MIN(ledX, minX);
199 int ledY = ledGetY(ledConfig);
200 maxY = MAX(ledY, maxY);
201 minY = MIN(ledY, minY);
204 ledGridRows = maxY - minY + 1;
206 if (minX < maxX) {
207 lowestXValueForEast = (minX + maxX) / 2 + 1;
208 highestXValueForWest = (minX + maxX - 1) / 2;
209 } else {
210 lowestXValueForEast = LED_XY_MASK / 2;
211 highestXValueForWest = lowestXValueForEast - 1;
213 if (minY < maxY) {
214 lowestYValueForSouth = (minY + maxY) / 2 + 1;
215 highestYValueForNorth = (minY + maxY - 1) / 2;
216 } else {
217 lowestYValueForSouth = LED_XY_MASK / 2;
218 highestYValueForNorth = lowestYValueForSouth - 1;
223 STATIC_UNIT_TESTED void updateLedCount(void)
225 int count = 0, countRing = 0, countScanner= 0;
227 for (int ledIndex = 0; ledIndex < LED_MAX_STRIP_LENGTH; ledIndex++) {
228 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
230 if (!(*ledConfig))
231 break;
233 count++;
235 if (ledGetFunction(ledConfig) == LED_FUNCTION_THRUST_RING)
236 countRing++;
238 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_LARSON_SCANNER))
239 countScanner++;
242 ledCounts.count = count;
243 ledCounts.ring = countRing;
244 ledCounts.larson = countScanner;
247 void reevaluateLedConfig(void)
249 updateLedCount();
250 updateDimensions();
251 updateLedRingCounts();
252 updateRequiredOverlay();
255 // get specialColor by index
256 static const hsvColor_t* getSC(ledSpecialColorIds_e index)
258 return &ledStripConfig()->colors[ledStripConfig()->specialColors.color[index]];
261 static const char directionCodes[LED_DIRECTION_COUNT] = { 'N', 'E', 'S', 'W', 'U', 'D' };
262 static const char baseFunctionCodes[LED_BASEFUNCTION_COUNT] = { 'C', 'F', 'A', 'L', 'S', 'G', 'R' };
263 static const char overlayCodes[LED_OVERLAY_COUNT] = { 'T', 'O', 'B', 'V', 'I', 'W' };
265 #define CHUNK_BUFFER_SIZE 11
267 bool parseLedStripConfig(int ledIndex, const char *config)
269 if (ledIndex >= LED_MAX_STRIP_LENGTH)
270 return false;
272 enum parseState_e {
273 X_COORDINATE,
274 Y_COORDINATE,
275 DIRECTIONS,
276 FUNCTIONS,
277 RING_COLORS,
278 PARSE_STATE_COUNT
280 static const char chunkSeparators[PARSE_STATE_COUNT] = {',', ':', ':', ':', '\0'};
282 ledConfig_t *ledConfig = &ledStripConfigMutable()->ledConfigs[ledIndex];
283 memset(ledConfig, 0, sizeof(ledConfig_t));
285 int x = 0, y = 0, color = 0; // initialize to prevent warnings
286 int baseFunction = 0;
287 int overlay_flags = 0;
288 int direction_flags = 0;
290 for (enum parseState_e parseState = 0; parseState < PARSE_STATE_COUNT; parseState++) {
291 char chunk[CHUNK_BUFFER_SIZE];
293 char chunkSeparator = chunkSeparators[parseState];
294 int chunkIndex = 0;
295 while (*config && *config != chunkSeparator && chunkIndex < (CHUNK_BUFFER_SIZE - 1)) {
296 chunk[chunkIndex++] = *config++;
298 chunk[chunkIndex++] = 0; // zero-terminate chunk
299 if (*config != chunkSeparator) {
300 return false;
302 config++; // skip separator
304 switch (parseState) {
305 case X_COORDINATE:
306 x = atoi(chunk);
307 break;
308 case Y_COORDINATE:
309 y = atoi(chunk);
310 break;
311 case DIRECTIONS:
312 for (char *ch = chunk; *ch; ch++) {
313 for (ledDirectionId_e dir = 0; dir < LED_DIRECTION_COUNT; dir++) {
314 if (directionCodes[dir] == *ch) {
315 direction_flags |= LED_FLAG_DIRECTION(dir);
316 break;
320 break;
321 case FUNCTIONS:
322 for (char *ch = chunk; *ch; ch++) {
323 for (ledBaseFunctionId_e fn = 0; fn < LED_BASEFUNCTION_COUNT; fn++) {
324 if (baseFunctionCodes[fn] == *ch) {
325 baseFunction = fn;
326 break;
330 for (ledOverlayId_e ol = 0; ol < LED_OVERLAY_COUNT; ol++) {
331 if (overlayCodes[ol] == *ch) {
332 overlay_flags |= LED_FLAG_OVERLAY(ol);
333 break;
337 break;
338 case RING_COLORS:
339 color = atoi(chunk);
340 if (color >= LED_CONFIGURABLE_COLOR_COUNT)
341 color = 0;
342 break;
343 case PARSE_STATE_COUNT:; // prevent warning
347 *ledConfig = DEFINE_LED(x, y, color, direction_flags, baseFunction, overlay_flags, 0);
349 reevaluateLedConfig();
351 return true;
354 void generateLedConfig(ledConfig_t *ledConfig, char *ledConfigBuffer, size_t bufferSize)
356 char directions[LED_DIRECTION_COUNT + 1];
357 char baseFunctionOverlays[LED_OVERLAY_COUNT + 2];
359 memset(ledConfigBuffer, 0, bufferSize);
361 char *dptr = directions;
362 for (ledDirectionId_e dir = 0; dir < LED_DIRECTION_COUNT; dir++) {
363 if (ledGetDirectionBit(ledConfig, dir)) {
364 *dptr++ = directionCodes[dir];
367 *dptr = 0;
369 char *fptr = baseFunctionOverlays;
370 *fptr++ = baseFunctionCodes[ledGetFunction(ledConfig)];
372 for (ledOverlayId_e ol = 0; ol < LED_OVERLAY_COUNT; ol++) {
373 if (ledGetOverlayBit(ledConfig, ol)) {
374 *fptr++ = overlayCodes[ol];
377 *fptr = 0;
379 // TODO - check buffer length
380 tfp_sprintf(ledConfigBuffer, "%u,%u:%s:%s:%u", ledGetX(ledConfig), ledGetY(ledConfig), directions, baseFunctionOverlays, ledGetColor(ledConfig));
383 typedef enum {
384 // the ordering is important, see below how NSEW is mapped to NE/SE/NW/SW
385 QUADRANT_NORTH = 1 << 0,
386 QUADRANT_SOUTH = 1 << 1,
387 QUADRANT_EAST = 1 << 2,
388 QUADRANT_WEST = 1 << 3,
389 } quadrant_e;
391 static quadrant_e getLedQuadrant(const int ledIndex)
393 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
395 int x = ledGetX(ledConfig);
396 int y = ledGetY(ledConfig);
398 int quad = 0;
399 if (y <= highestYValueForNorth)
400 quad |= QUADRANT_NORTH;
401 else if (y >= lowestYValueForSouth)
402 quad |= QUADRANT_SOUTH;
403 if (x >= lowestXValueForEast)
404 quad |= QUADRANT_EAST;
405 else if (x <= highestXValueForWest)
406 quad |= QUADRANT_WEST;
408 return quad;
411 static hsvColor_t* getDirectionalModeColor(const int ledIndex, const modeColorIndexes_t *modeColors)
413 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
414 const int ledDirection = ledGetDirection(ledConfig);
416 for (unsigned i = 0; i < LED_DIRECTION_COUNT; i++) {
417 if (ledDirection & (1 << i)) {
418 return &ledStripConfigMutable()->colors[modeColors->color[i]];
422 return NULL;
425 // map flight mode to led mode, in order of priority
426 // flightMode == 0 is always active
427 static const struct {
428 uint16_t flightMode;
429 uint8_t ledMode;
430 } flightModeToLed[] = {
431 {HEADFREE_MODE, LED_MODE_HEADFREE},
432 #ifdef USE_MAG
433 {MAG_MODE, LED_MODE_MAG},
434 #endif
435 #ifdef USE_BARO
436 {BARO_MODE, LED_MODE_BARO},
437 #endif
438 {HORIZON_MODE, LED_MODE_HORIZON},
439 {ANGLE_MODE, LED_MODE_ANGLE},
440 {0, LED_MODE_ORIENTATION},
443 static void applyLedFixedLayers(void)
445 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
446 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
447 hsvColor_t color = *getSC(LED_SCOLOR_BACKGROUND);
449 int fn = ledGetFunction(ledConfig);
450 int hOffset = HSV_HUE_MAX + 1;
452 switch (fn) {
453 case LED_FUNCTION_COLOR:
454 color = ledStripConfig()->colors[ledGetColor(ledConfig)];
456 hsvColor_t nextColor = ledStripConfig()->colors[(ledGetColor(ledConfig) + 1 + LED_CONFIGURABLE_COLOR_COUNT) % LED_CONFIGURABLE_COLOR_COUNT];
457 hsvColor_t previousColor = ledStripConfig()->colors[(ledGetColor(ledConfig) - 1 + LED_CONFIGURABLE_COLOR_COUNT) % LED_CONFIGURABLE_COLOR_COUNT];
459 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_THROTTLE)) { //smooth fade with selected Aux channel of all HSV values from previousColor through color to nextColor
460 int centerPWM = (PWM_RANGE_MIN + PWM_RANGE_MAX) / 2;
461 if (auxInput < centerPWM) {
462 color.h = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.h, color.h);
463 color.s = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.s, color.s);
464 color.v = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.v, color.v);
465 } else {
466 color.h = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.h, nextColor.h);
467 color.s = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.s, nextColor.s);
468 color.v = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.v, nextColor.v);
472 break;
474 case LED_FUNCTION_FLIGHT_MODE:
475 for (unsigned i = 0; i < ARRAYLEN(flightModeToLed); i++)
476 if (!flightModeToLed[i].flightMode || FLIGHT_MODE(flightModeToLed[i].flightMode)) {
477 const hsvColor_t *directionalColor = getDirectionalModeColor(ledIndex, &ledStripConfig()->modeColors[flightModeToLed[i].ledMode]);
478 if (directionalColor) {
479 color = *directionalColor;
482 break; // stop on first match
484 break;
486 case LED_FUNCTION_ARM_STATE:
487 color = ARMING_FLAG(ARMED) ? *getSC(LED_SCOLOR_ARMED) : *getSC(LED_SCOLOR_DISARMED);
488 break;
490 case LED_FUNCTION_BATTERY:
491 color = HSV(RED);
492 hOffset += scaleRange(calculateBatteryPercentageRemaining(), 0, 100, -30, 120);
493 break;
495 case LED_FUNCTION_RSSI:
496 color = HSV(RED);
497 hOffset += scaleRange(getRssi() * 100, 0, 1023, -30, 120);
498 break;
500 default:
501 break;
504 if ((fn != LED_FUNCTION_COLOR) && ledGetOverlayBit(ledConfig, LED_OVERLAY_THROTTLE)) {
505 hOffset += scaleRange(auxInput, PWM_RANGE_MIN, PWM_RANGE_MAX, 0, HSV_HUE_MAX + 1);
508 color.h = (color.h + hOffset) % (HSV_HUE_MAX + 1);
509 setLedHsv(ledIndex, &color);
513 static void applyLedHsv(uint32_t mask, const hsvColor_t *color)
515 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
516 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
517 if ((*ledConfig & mask) == mask)
518 setLedHsv(ledIndex, color);
522 typedef enum {
523 WARNING_ARMING_DISABLED,
524 WARNING_LOW_BATTERY,
525 WARNING_FAILSAFE,
526 } warningFlags_e;
528 static void applyLedWarningLayer(bool updateNow, timeUs_t *timer)
530 static uint8_t warningFlashCounter = 0;
531 static uint8_t warningFlags = 0; // non-zero during blinks
533 if (updateNow) {
534 // keep counter running, so it stays in sync with blink
535 warningFlashCounter++;
536 warningFlashCounter &= 0xF;
538 if (warningFlashCounter == 0) { // update when old flags was processed
539 warningFlags = 0;
540 if (batteryConfig()->voltageMeterSource != VOLTAGE_METER_NONE && getBatteryState() != BATTERY_OK)
541 warningFlags |= 1 << WARNING_LOW_BATTERY;
542 if (failsafeIsActive())
543 warningFlags |= 1 << WARNING_FAILSAFE;
544 if (!ARMING_FLAG(ARMED) && isArmingDisabled())
545 warningFlags |= 1 << WARNING_ARMING_DISABLED;
547 *timer += HZ_TO_US(10);
550 const hsvColor_t *warningColor = NULL;
552 if (warningFlags) {
553 bool colorOn = (warningFlashCounter % 2) == 0; // w_w_
554 warningFlags_e warningId = warningFlashCounter / 4;
555 if (warningFlags & (1 << warningId)) {
556 switch (warningId) {
557 case WARNING_ARMING_DISABLED:
558 warningColor = colorOn ? &HSV(GREEN) : &HSV(BLACK);
559 break;
560 case WARNING_LOW_BATTERY:
561 warningColor = colorOn ? &HSV(RED) : &HSV(BLACK);
562 break;
563 case WARNING_FAILSAFE:
564 warningColor = colorOn ? &HSV(YELLOW) : &HSV(BLUE);
565 break;
566 default:;
569 } else {
570 if (isBeeperOn()) {
571 warningColor = &HSV(ORANGE);
575 if (warningColor) {
576 applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_WARNING)), warningColor);
580 #ifdef USE_VTX_COMMON
581 static void applyLedVtxLayer(bool updateNow, timeUs_t *timer)
583 static uint16_t frequency = 0;
584 static uint8_t power = 255;
585 static uint8_t pit = 255;
586 static uint8_t showSettings = false;
587 static uint16_t lastCheck = 0;
588 static bool blink = false;
590 const vtxDevice_t *vtxDevice = vtxCommonDevice();
591 if (!vtxDevice) {
592 return;
595 uint8_t band = 255, channel = 255;
596 uint16_t check = 0;
598 if (updateNow) {
599 // keep counter running, so it stays in sync with vtx
600 vtxCommonGetBandAndChannel(vtxDevice, &band, &channel);
601 vtxCommonGetPowerIndex(vtxDevice, &power);
602 vtxCommonGetPitMode(vtxDevice, &pit);
604 frequency = vtx58frequencyTable[band - 1][channel - 1]; //subtracting 1 from band and channel so that correct frequency is returned.
605 //might not be correct for tramp but should fix smart audio.
606 // check if last vtx values have changed.
607 check = pit + (power << 1) + (band << 4) + (channel << 8);
608 if (!showSettings && check != lastCheck) {
609 // display settings for 3 seconds.
610 showSettings = 15;
612 lastCheck = check; // quick way to check if any settings changed.
614 if (showSettings) {
615 showSettings--;
617 blink = !blink;
618 *timer += HZ_TO_US(5); // check 5 times a second
621 hsvColor_t color = {0, 0, 0};
622 if (showSettings) { // show settings
623 uint8_t vtxLedCount = 0;
624 for (int i = 0; i < ledCounts.count && vtxLedCount < 6; ++i) {
625 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
626 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_VTX)) {
627 if (vtxLedCount == 0) {
628 color.h = HSV(GREEN).h;
629 color.s = HSV(GREEN).s;
630 color.v = blink ? 15 : 0; // blink received settings
632 else if (vtxLedCount > 0 && power >= vtxLedCount && !pit) { // show power
633 color.h = HSV(ORANGE).h;
634 color.s = HSV(ORANGE).s;
635 color.v = blink ? 15 : 0; // blink received settings
637 else { // turn rest off
638 color.h = HSV(BLACK).h;
639 color.s = HSV(BLACK).s;
640 color.v = HSV(BLACK).v;
642 setLedHsv(i, &color);
643 ++vtxLedCount;
647 else { // show frequency
648 // calculate the VTX color based on frequency
649 int colorIndex = 0;
650 if (frequency <= 5672) {
651 colorIndex = COLOR_WHITE;
652 } else if (frequency <= 5711) {
653 colorIndex = COLOR_RED;
654 } else if (frequency <= 5750) {
655 colorIndex = COLOR_ORANGE;
656 } else if (frequency <= 5789) {
657 colorIndex = COLOR_YELLOW;
658 } else if (frequency <= 5829) {
659 colorIndex = COLOR_GREEN;
660 } else if (frequency <= 5867) {
661 colorIndex = COLOR_BLUE;
662 } else if (frequency <= 5906) {
663 colorIndex = COLOR_DARK_VIOLET;
664 } else {
665 colorIndex = COLOR_DEEP_PINK;
667 hsvColor_t color = ledStripConfig()->colors[colorIndex];
668 color.v = pit ? (blink ? 15 : 0) : 255; // blink when in pit mode
669 applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_VTX)), &color);
672 #endif
674 static void applyLedBatteryLayer(bool updateNow, timeUs_t *timer)
676 static bool flash = false;
678 int timerDelayUs = HZ_TO_US(1);
680 if (updateNow) {
682 switch (getBatteryState()) {
683 case BATTERY_OK:
684 flash = true;
685 timerDelayUs = HZ_TO_US(1);
687 break;
688 case BATTERY_WARNING:
689 flash = !flash;
690 timerDelayUs = HZ_TO_US(2);
692 break;
693 default:
694 flash = !flash;
695 timerDelayUs = HZ_TO_US(8);
697 break;
701 *timer += timerDelayUs;
703 if (!flash) {
704 const hsvColor_t *bgc = getSC(LED_SCOLOR_BACKGROUND);
705 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_BATTERY), bgc);
709 static void applyLedRssiLayer(bool updateNow, timeUs_t *timer)
711 static bool flash = false;
713 int timerDelay = HZ_TO_US(1);
715 if (updateNow) {
716 int state = (getRssi() * 100) / 1023;
718 if (state > 50) {
719 flash = true;
720 timerDelay = HZ_TO_US(1);
721 } else if (state > 20) {
722 flash = !flash;
723 timerDelay = HZ_TO_US(2);
724 } else {
725 flash = !flash;
726 timerDelay = HZ_TO_US(8);
730 *timer += timerDelay;
732 if (!flash) {
733 const hsvColor_t *bgc = getSC(LED_SCOLOR_BACKGROUND);
734 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_RSSI), bgc);
738 #ifdef USE_GPS
739 static void applyLedGpsLayer(bool updateNow, timeUs_t *timer)
742 static uint8_t gpsPauseCounter = 0;
743 const uint8_t blinkPauseLength = 4;
745 if (updateNow) {
746 static uint8_t gpsFlashCounter = 0;
747 if (gpsPauseCounter > 0) {
748 gpsPauseCounter--;
749 } else if (gpsFlashCounter >= gpsSol.numSat) {
750 gpsFlashCounter = 0;
751 gpsPauseCounter = blinkPauseLength;
752 } else {
753 gpsFlashCounter++;
754 gpsPauseCounter = 1;
756 *timer += HZ_TO_US(2.5f);
759 const hsvColor_t *gpsColor;
761 if (gpsSol.numSat == 0 || !sensors(SENSOR_GPS)) {
762 gpsColor = getSC(LED_SCOLOR_GPSNOSATS);
763 } else {
764 bool colorOn = gpsPauseCounter == 0; // each interval starts with pause
765 if (STATE(GPS_FIX)) {
766 gpsColor = colorOn ? getSC(LED_SCOLOR_GPSLOCKED) : getSC(LED_SCOLOR_BACKGROUND);
767 } else {
768 gpsColor = colorOn ? getSC(LED_SCOLOR_GPSNOLOCK) : getSC(LED_SCOLOR_GPSNOSATS);
772 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_GPS), gpsColor);
775 #endif
777 #define INDICATOR_DEADBAND 25
779 static void applyLedIndicatorLayer(bool updateNow, timeUs_t *timer)
781 static bool flash = 0;
783 if (updateNow) {
784 if (rxIsReceivingSignal()) {
785 // calculate update frequency
786 int scale = MAX(ABS(rcCommand[ROLL]), ABS(rcCommand[PITCH])); // 0 - 500
787 scale = scale - INDICATOR_DEADBAND; // start increasing frequency right after deadband
788 *timer += HZ_TO_US(5 + (45 * scale) / (500 - INDICATOR_DEADBAND)); // 5 - 50Hz update, 2.5 - 25Hz blink
790 flash = !flash;
791 } else {
792 *timer += HZ_TO_US(5);
796 if (!flash)
797 return;
799 const hsvColor_t *flashColor = &HSV(ORANGE); // TODO - use user color?
801 quadrant_e quadrants = 0;
802 if (rcCommand[ROLL] > INDICATOR_DEADBAND) {
803 quadrants |= QUADRANT_EAST;
804 } else if (rcCommand[ROLL] < -INDICATOR_DEADBAND) {
805 quadrants |= QUADRANT_WEST;
807 if (rcCommand[PITCH] > INDICATOR_DEADBAND) {
808 quadrants |= QUADRANT_NORTH;
809 } else if (rcCommand[PITCH] < -INDICATOR_DEADBAND) {
810 quadrants |= QUADRANT_SOUTH;
813 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
814 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
815 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_INDICATOR)) {
816 if (getLedQuadrant(ledIndex) & quadrants)
817 setLedHsv(ledIndex, flashColor);
822 #define ROTATION_SEQUENCE_LED_COUNT 6 // 2 on, 4 off
823 #define ROTATION_SEQUENCE_LED_WIDTH 2 // 2 on
825 static void updateLedRingCounts(void)
827 int seqLen;
828 // try to split in segments/rings of exactly ROTATION_SEQUENCE_LED_COUNT leds
829 if ((ledCounts.ring % ROTATION_SEQUENCE_LED_COUNT) == 0) {
830 seqLen = ROTATION_SEQUENCE_LED_COUNT;
831 } else {
832 seqLen = ledCounts.ring;
833 // else split up in equal segments/rings of at most ROTATION_SEQUENCE_LED_COUNT leds
834 // TODO - improve partitioning (15 leds -> 3x5)
835 while ((seqLen > ROTATION_SEQUENCE_LED_COUNT) && ((seqLen % 2) == 0)) {
836 seqLen /= 2;
839 ledCounts.ringSeqLen = seqLen;
842 static void applyLedThrustRingLayer(bool updateNow, timeUs_t *timer)
844 static uint8_t rotationPhase;
845 int ledRingIndex = 0;
847 if (updateNow) {
848 rotationPhase = rotationPhase > 0 ? rotationPhase - 1 : ledCounts.ringSeqLen - 1;
850 *timer += HZ_TO_US(5 + (45 * scaledThrottle) / 100); // 5 - 50Hz update rate
853 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
854 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
855 if (ledGetFunction(ledConfig) == LED_FUNCTION_THRUST_RING) {
857 bool applyColor;
858 if (ARMING_FLAG(ARMED)) {
859 applyColor = (ledRingIndex + rotationPhase) % ledCounts.ringSeqLen < ROTATION_SEQUENCE_LED_WIDTH;
860 } else {
861 applyColor = !(ledRingIndex % 2); // alternating pattern
864 if (applyColor) {
865 const hsvColor_t *ringColor = &ledStripConfig()->colors[ledGetColor(ledConfig)];
866 setLedHsv(ledIndex, ringColor);
869 ledRingIndex++;
874 typedef struct larsonParameters_s {
875 uint8_t currentBrightness;
876 int8_t currentIndex;
877 int8_t direction;
878 } larsonParameters_t;
880 static int brightnessForLarsonIndex(larsonParameters_t *larsonParameters, uint8_t larsonIndex)
882 int offset = larsonIndex - larsonParameters->currentIndex;
883 static const int larsonLowValue = 8;
885 if (ABS(offset) > 1)
886 return (larsonLowValue);
888 if (offset == 0)
889 return (larsonParameters->currentBrightness);
891 if (larsonParameters->direction == offset) {
892 return (larsonParameters->currentBrightness - 127);
895 return (255 - larsonParameters->currentBrightness);
899 static void larsonScannerNextStep(larsonParameters_t *larsonParameters, int delta)
901 if (larsonParameters->currentBrightness > (255 - delta)) {
902 larsonParameters->currentBrightness = 127;
903 if (larsonParameters->currentIndex >= ledCounts.larson || larsonParameters->currentIndex < 0) {
904 larsonParameters->direction = -larsonParameters->direction;
906 larsonParameters->currentIndex += larsonParameters->direction;
907 } else {
908 larsonParameters->currentBrightness += delta;
912 static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer)
914 static larsonParameters_t larsonParameters = { 0, 0, 1 };
916 if (updateNow) {
917 larsonScannerNextStep(&larsonParameters, 15);
918 *timer += HZ_TO_US(60);
921 int scannerLedIndex = 0;
922 for (unsigned i = 0; i < ledCounts.count; i++) {
924 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
926 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_LARSON_SCANNER)) {
927 hsvColor_t ledColor;
928 getLedHsv(i, &ledColor);
929 ledColor.v = brightnessForLarsonIndex(&larsonParameters, scannerLedIndex);
930 setLedHsv(i, &ledColor);
931 scannerLedIndex++;
936 // blink twice, then wait ; either always or just when landing
937 static void applyLedBlinkLayer(bool updateNow, timeUs_t *timer)
939 const uint16_t blinkPattern = 0x8005; // 0b1000000000000101;
940 static uint16_t blinkMask;
942 if (updateNow) {
943 blinkMask = blinkMask >> 1;
944 if (blinkMask <= 1)
945 blinkMask = blinkPattern;
947 *timer += HZ_TO_US(10);
950 bool ledOn = (blinkMask & 1); // b_b_____...
951 if (!ledOn) {
952 for (int i = 0; i < ledCounts.count; ++i) {
953 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
955 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_BLINK)) {
956 setLedHsv(i, getSC(LED_SCOLOR_BLINKBACKGROUND));
962 #ifdef USE_LED_ANIMATION
963 static void applyLedAnimationLayer(bool updateNow, timeUs_t *timer)
965 static uint8_t frameCounter = 0;
966 const int animationFrames = ledGridRows;
967 if (updateNow) {
968 frameCounter = (frameCounter + 1 < animationFrames) ? frameCounter + 1 : 0;
969 *timer += HZ_TO_US(20);
972 if (ARMING_FLAG(ARMED))
973 return;
975 int previousRow = frameCounter > 0 ? frameCounter - 1 : animationFrames - 1;
976 int currentRow = frameCounter;
977 int nextRow = (frameCounter + 1 < animationFrames) ? frameCounter + 1 : 0;
979 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
980 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
982 if (ledGetY(ledConfig) == previousRow) {
983 setLedHsv(ledIndex, getSC(LED_SCOLOR_ANIMATION));
984 scaleLedValue(ledIndex, 50);
985 } else if (ledGetY(ledConfig) == currentRow) {
986 setLedHsv(ledIndex, getSC(LED_SCOLOR_ANIMATION));
987 } else if (ledGetY(ledConfig) == nextRow) {
988 scaleLedValue(ledIndex, 50);
992 #endif
994 typedef enum {
995 timBlink,
996 timLarson,
997 timBattery,
998 timRssi,
999 #ifdef USE_GPS
1000 timGps,
1001 #endif
1002 timWarning,
1003 #ifdef USE_VTX_COMMON
1004 timVtx,
1005 #endif
1006 timIndicator,
1007 #ifdef USE_LED_ANIMATION
1008 timAnimation,
1009 #endif
1010 timRing,
1011 timTimerCount
1012 } timId_e;
1014 static timeUs_t timerVal[timTimerCount];
1015 static uint16_t disabledTimerMask;
1017 STATIC_ASSERT(timTimerCount <= sizeof(disabledTimerMask) * 8, disabledTimerMask_too_small);
1019 // function to apply layer.
1020 // function must replan self using timer pointer
1021 // when updateNow is true (timer triggered), state must be updated first,
1022 // before calculating led state. Otherwise update started by different trigger
1023 // may modify LED state.
1024 typedef void applyLayerFn_timed(bool updateNow, timeUs_t *timer);
1026 static applyLayerFn_timed* layerTable[] = {
1027 [timBlink] = &applyLedBlinkLayer,
1028 [timLarson] = &applyLarsonScannerLayer,
1029 [timBattery] = &applyLedBatteryLayer,
1030 [timRssi] = &applyLedRssiLayer,
1031 #ifdef USE_GPS
1032 [timGps] = &applyLedGpsLayer,
1033 #endif
1034 [timWarning] = &applyLedWarningLayer,
1035 #ifdef USE_VTX_COMMON
1036 [timVtx] = &applyLedVtxLayer,
1037 #endif
1038 [timIndicator] = &applyLedIndicatorLayer,
1039 #ifdef USE_LED_ANIMATION
1040 [timAnimation] = &applyLedAnimationLayer,
1041 #endif
1042 [timRing] = &applyLedThrustRingLayer
1045 bool isOverlayTypeUsed(ledOverlayId_e overlayType)
1047 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
1048 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
1049 if (ledGetOverlayBit(ledConfig, overlayType)) {
1050 return true;
1053 return false;
1056 void updateRequiredOverlay(void)
1058 disabledTimerMask = 0;
1059 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_BLINK) << timBlink;
1060 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_LARSON_SCANNER) << timLarson;
1061 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_WARNING) << timWarning;
1062 #ifdef USE_VTX_COMMON
1063 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_VTX) << timVtx;
1064 #endif
1065 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_INDICATOR) << timIndicator;
1068 void ledStripUpdate(timeUs_t currentTimeUs)
1070 if (!(ledStripInitialised && isWS2811LedStripReady())) {
1071 return;
1074 if (IS_RC_MODE_ACTIVE(BOXLEDLOW) && !(ledStripConfig()->ledstrip_visual_beeper && isBeeperOn())) {
1075 if (ledStripEnabled) {
1076 ledStripDisable();
1077 ledStripEnabled = false;
1079 return;
1081 ledStripEnabled = true;
1083 const uint32_t now = currentTimeUs;
1085 // test all led timers, setting corresponding bits
1086 uint32_t timActive = 0;
1087 for (timId_e timId = 0; timId < timTimerCount; timId++) {
1088 if (!(disabledTimerMask & (1 << timId))) {
1089 // sanitize timer value, so that it can be safely incremented. Handles inital timerVal value.
1090 const timeDelta_t delta = cmpTimeUs(now, timerVal[timId]);
1091 // max delay is limited to 5s
1092 if (delta < 0 && delta > -MAX_TIMER_DELAY)
1093 continue; // not ready yet
1094 timActive |= 1 << timId;
1095 if (delta >= 100 * 1000 || delta < 0) {
1096 timerVal[timId] = now;
1101 if (!timActive)
1102 return; // no change this update, keep old state
1104 // apply all layers; triggered timed functions has to update timers
1106 scaledThrottle = ARMING_FLAG(ARMED) ? scaleRange(rcData[THROTTLE], PWM_RANGE_MIN, PWM_RANGE_MAX, 0, 100) : 0;
1107 auxInput = rcData[ledStripConfig()->ledstrip_aux_channel];
1109 applyLedFixedLayers();
1111 for (timId_e timId = 0; timId < ARRAYLEN(layerTable); timId++) {
1112 uint32_t *timer = &timerVal[timId];
1113 bool updateNow = timActive & (1 << timId);
1114 (*layerTable[timId])(updateNow, timer);
1116 ws2811UpdateStrip((ledStripFormatRGB_e)ledStripConfig()->ledstrip_grb_rgb);
1119 bool parseColor(int index, const char *colorConfig)
1121 const char *remainingCharacters = colorConfig;
1123 hsvColor_t *color = &ledStripConfigMutable()->colors[index];
1125 bool result = true;
1126 static const uint16_t hsv_limit[HSV_COLOR_COMPONENT_COUNT] = {
1127 [HSV_HUE] = HSV_HUE_MAX,
1128 [HSV_SATURATION] = HSV_SATURATION_MAX,
1129 [HSV_VALUE] = HSV_VALUE_MAX,
1131 for (int componentIndex = 0; result && componentIndex < HSV_COLOR_COMPONENT_COUNT; componentIndex++) {
1132 int val = atoi(remainingCharacters);
1133 if (val > hsv_limit[componentIndex]) {
1134 result = false;
1135 break;
1137 switch (componentIndex) {
1138 case HSV_HUE:
1139 color->h = val;
1140 break;
1141 case HSV_SATURATION:
1142 color->s = val;
1143 break;
1144 case HSV_VALUE:
1145 color->v = val;
1146 break;
1148 remainingCharacters = strchr(remainingCharacters, ',');
1149 if (remainingCharacters) {
1150 remainingCharacters++; // skip separator
1151 } else {
1152 if (componentIndex < HSV_COLOR_COMPONENT_COUNT - 1) {
1153 result = false;
1158 if (!result) {
1159 memset(color, 0, sizeof(*color));
1162 return result;
1166 * Redefine a color in a mode.
1167 * */
1168 bool setModeColor(ledModeIndex_e modeIndex, int modeColorIndex, int colorIndex)
1170 // check color
1171 if (colorIndex < 0 || colorIndex >= LED_CONFIGURABLE_COLOR_COUNT)
1172 return false;
1173 if (modeIndex < LED_MODE_COUNT) { // modeIndex_e is unsigned, so one-sided test is enough
1174 if (modeColorIndex < 0 || modeColorIndex >= LED_DIRECTION_COUNT)
1175 return false;
1176 ledStripConfigMutable()->modeColors[modeIndex].color[modeColorIndex] = colorIndex;
1177 } else if (modeIndex == LED_SPECIAL) {
1178 if (modeColorIndex < 0 || modeColorIndex >= LED_SPECIAL_COLOR_COUNT)
1179 return false;
1180 ledStripConfigMutable()->specialColors.color[modeColorIndex] = colorIndex;
1181 } else if (modeIndex == LED_AUX_CHANNEL) {
1182 if (modeColorIndex < 0 || modeColorIndex >= 1)
1183 return false;
1184 ledStripConfigMutable()->ledstrip_aux_channel = colorIndex;
1185 } else {
1186 return false;
1188 return true;
1191 void ledStripInit(void)
1193 colors = ledStripConfigMutable()->colors;
1194 modeColors = ledStripConfig()->modeColors;
1195 specialColors = ledStripConfig()->specialColors;
1196 ledStripInitialised = false;
1199 void ledStripEnable(void)
1201 reevaluateLedConfig();
1202 ledStripInitialised = true;
1204 ws2811LedStripInit(ledStripConfig()->ioTag);
1207 static void ledStripDisable(void)
1209 setStripColor(&HSV(BLACK));
1211 ws2811UpdateStrip((ledStripFormatRGB_e)ledStripConfig()->ledstrip_grb_rgb);
1213 #endif