Added generated timer definitions for STM32F7X2 universal target.
[betaflight.git] / src / main / io / ledstrip.c
bloba826b00dbff963e21815e47975cdfeb294590846
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 #ifndef UNIT_TEST
178 ledStripConfig->ioTag = timerioTagGetByUsage(TIM_USE_LED, 0);
179 #endif
182 static int scaledThrottle;
183 static int auxInput;
185 static void updateLedRingCounts(void);
187 STATIC_UNIT_TESTED void updateDimensions(void)
189 int maxX = 0;
190 int minX = LED_XY_MASK;
191 int maxY = 0;
192 int minY = LED_XY_MASK;
194 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
195 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
197 int ledX = ledGetX(ledConfig);
198 maxX = MAX(ledX, maxX);
199 minX = MIN(ledX, minX);
200 int ledY = ledGetY(ledConfig);
201 maxY = MAX(ledY, maxY);
202 minY = MIN(ledY, minY);
205 ledGridRows = maxY - minY + 1;
207 if (minX < maxX) {
208 lowestXValueForEast = (minX + maxX) / 2 + 1;
209 highestXValueForWest = (minX + maxX - 1) / 2;
210 } else {
211 lowestXValueForEast = LED_XY_MASK / 2;
212 highestXValueForWest = lowestXValueForEast - 1;
214 if (minY < maxY) {
215 lowestYValueForSouth = (minY + maxY) / 2 + 1;
216 highestYValueForNorth = (minY + maxY - 1) / 2;
217 } else {
218 lowestYValueForSouth = LED_XY_MASK / 2;
219 highestYValueForNorth = lowestYValueForSouth - 1;
224 STATIC_UNIT_TESTED void updateLedCount(void)
226 int count = 0, countRing = 0, countScanner= 0;
228 for (int ledIndex = 0; ledIndex < LED_MAX_STRIP_LENGTH; ledIndex++) {
229 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
231 if (!(*ledConfig))
232 break;
234 count++;
236 if (ledGetFunction(ledConfig) == LED_FUNCTION_THRUST_RING)
237 countRing++;
239 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_LARSON_SCANNER))
240 countScanner++;
243 ledCounts.count = count;
244 ledCounts.ring = countRing;
245 ledCounts.larson = countScanner;
248 void reevaluateLedConfig(void)
250 updateLedCount();
251 updateDimensions();
252 updateLedRingCounts();
253 updateRequiredOverlay();
256 // get specialColor by index
257 static const hsvColor_t* getSC(ledSpecialColorIds_e index)
259 return &ledStripConfig()->colors[ledStripConfig()->specialColors.color[index]];
262 static const char directionCodes[LED_DIRECTION_COUNT] = { 'N', 'E', 'S', 'W', 'U', 'D' };
263 static const char baseFunctionCodes[LED_BASEFUNCTION_COUNT] = { 'C', 'F', 'A', 'L', 'S', 'G', 'R' };
264 static const char overlayCodes[LED_OVERLAY_COUNT] = { 'T', 'O', 'B', 'V', 'I', 'W' };
266 #define CHUNK_BUFFER_SIZE 11
268 bool parseLedStripConfig(int ledIndex, const char *config)
270 if (ledIndex >= LED_MAX_STRIP_LENGTH)
271 return false;
273 enum parseState_e {
274 X_COORDINATE,
275 Y_COORDINATE,
276 DIRECTIONS,
277 FUNCTIONS,
278 RING_COLORS,
279 PARSE_STATE_COUNT
281 static const char chunkSeparators[PARSE_STATE_COUNT] = {',', ':', ':', ':', '\0'};
283 ledConfig_t *ledConfig = &ledStripConfigMutable()->ledConfigs[ledIndex];
284 memset(ledConfig, 0, sizeof(ledConfig_t));
286 int x = 0, y = 0, color = 0; // initialize to prevent warnings
287 int baseFunction = 0;
288 int overlay_flags = 0;
289 int direction_flags = 0;
291 for (enum parseState_e parseState = 0; parseState < PARSE_STATE_COUNT; parseState++) {
292 char chunk[CHUNK_BUFFER_SIZE];
294 char chunkSeparator = chunkSeparators[parseState];
295 int chunkIndex = 0;
296 while (*config && *config != chunkSeparator && chunkIndex < (CHUNK_BUFFER_SIZE - 1)) {
297 chunk[chunkIndex++] = *config++;
299 chunk[chunkIndex++] = 0; // zero-terminate chunk
300 if (*config != chunkSeparator) {
301 return false;
303 config++; // skip separator
305 switch (parseState) {
306 case X_COORDINATE:
307 x = atoi(chunk);
308 break;
309 case Y_COORDINATE:
310 y = atoi(chunk);
311 break;
312 case DIRECTIONS:
313 for (char *ch = chunk; *ch; ch++) {
314 for (ledDirectionId_e dir = 0; dir < LED_DIRECTION_COUNT; dir++) {
315 if (directionCodes[dir] == *ch) {
316 direction_flags |= LED_FLAG_DIRECTION(dir);
317 break;
321 break;
322 case FUNCTIONS:
323 for (char *ch = chunk; *ch; ch++) {
324 for (ledBaseFunctionId_e fn = 0; fn < LED_BASEFUNCTION_COUNT; fn++) {
325 if (baseFunctionCodes[fn] == *ch) {
326 baseFunction = fn;
327 break;
331 for (ledOverlayId_e ol = 0; ol < LED_OVERLAY_COUNT; ol++) {
332 if (overlayCodes[ol] == *ch) {
333 overlay_flags |= LED_FLAG_OVERLAY(ol);
334 break;
338 break;
339 case RING_COLORS:
340 color = atoi(chunk);
341 if (color >= LED_CONFIGURABLE_COLOR_COUNT)
342 color = 0;
343 break;
344 case PARSE_STATE_COUNT:; // prevent warning
348 *ledConfig = DEFINE_LED(x, y, color, direction_flags, baseFunction, overlay_flags, 0);
350 reevaluateLedConfig();
352 return true;
355 void generateLedConfig(ledConfig_t *ledConfig, char *ledConfigBuffer, size_t bufferSize)
357 char directions[LED_DIRECTION_COUNT + 1];
358 char baseFunctionOverlays[LED_OVERLAY_COUNT + 2];
360 memset(ledConfigBuffer, 0, bufferSize);
362 char *dptr = directions;
363 for (ledDirectionId_e dir = 0; dir < LED_DIRECTION_COUNT; dir++) {
364 if (ledGetDirectionBit(ledConfig, dir)) {
365 *dptr++ = directionCodes[dir];
368 *dptr = 0;
370 char *fptr = baseFunctionOverlays;
371 *fptr++ = baseFunctionCodes[ledGetFunction(ledConfig)];
373 for (ledOverlayId_e ol = 0; ol < LED_OVERLAY_COUNT; ol++) {
374 if (ledGetOverlayBit(ledConfig, ol)) {
375 *fptr++ = overlayCodes[ol];
378 *fptr = 0;
380 // TODO - check buffer length
381 tfp_sprintf(ledConfigBuffer, "%u,%u:%s:%s:%u", ledGetX(ledConfig), ledGetY(ledConfig), directions, baseFunctionOverlays, ledGetColor(ledConfig));
384 typedef enum {
385 // the ordering is important, see below how NSEW is mapped to NE/SE/NW/SW
386 QUADRANT_NORTH = 1 << 0,
387 QUADRANT_SOUTH = 1 << 1,
388 QUADRANT_EAST = 1 << 2,
389 QUADRANT_WEST = 1 << 3,
390 } quadrant_e;
392 static quadrant_e getLedQuadrant(const int ledIndex)
394 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
396 int x = ledGetX(ledConfig);
397 int y = ledGetY(ledConfig);
399 int quad = 0;
400 if (y <= highestYValueForNorth)
401 quad |= QUADRANT_NORTH;
402 else if (y >= lowestYValueForSouth)
403 quad |= QUADRANT_SOUTH;
404 if (x >= lowestXValueForEast)
405 quad |= QUADRANT_EAST;
406 else if (x <= highestXValueForWest)
407 quad |= QUADRANT_WEST;
409 return quad;
412 static hsvColor_t* getDirectionalModeColor(const int ledIndex, const modeColorIndexes_t *modeColors)
414 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
415 const int ledDirection = ledGetDirection(ledConfig);
417 for (unsigned i = 0; i < LED_DIRECTION_COUNT; i++) {
418 if (ledDirection & (1 << i)) {
419 return &ledStripConfigMutable()->colors[modeColors->color[i]];
423 return NULL;
426 // map flight mode to led mode, in order of priority
427 // flightMode == 0 is always active
428 static const struct {
429 uint16_t flightMode;
430 uint8_t ledMode;
431 } flightModeToLed[] = {
432 {HEADFREE_MODE, LED_MODE_HEADFREE},
433 #ifdef USE_MAG
434 {MAG_MODE, LED_MODE_MAG},
435 #endif
436 #ifdef USE_BARO
437 {BARO_MODE, LED_MODE_BARO},
438 #endif
439 {HORIZON_MODE, LED_MODE_HORIZON},
440 {ANGLE_MODE, LED_MODE_ANGLE},
441 {0, LED_MODE_ORIENTATION},
444 static void applyLedFixedLayers(void)
446 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
447 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
448 hsvColor_t color = *getSC(LED_SCOLOR_BACKGROUND);
450 int fn = ledGetFunction(ledConfig);
451 int hOffset = HSV_HUE_MAX + 1;
453 switch (fn) {
454 case LED_FUNCTION_COLOR:
455 color = ledStripConfig()->colors[ledGetColor(ledConfig)];
457 hsvColor_t nextColor = ledStripConfig()->colors[(ledGetColor(ledConfig) + 1 + LED_CONFIGURABLE_COLOR_COUNT) % LED_CONFIGURABLE_COLOR_COUNT];
458 hsvColor_t previousColor = ledStripConfig()->colors[(ledGetColor(ledConfig) - 1 + LED_CONFIGURABLE_COLOR_COUNT) % LED_CONFIGURABLE_COLOR_COUNT];
460 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_THROTTLE)) { //smooth fade with selected Aux channel of all HSV values from previousColor through color to nextColor
461 int centerPWM = (PWM_RANGE_MIN + PWM_RANGE_MAX) / 2;
462 if (auxInput < centerPWM) {
463 color.h = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.h, color.h);
464 color.s = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.s, color.s);
465 color.v = scaleRange(auxInput, PWM_RANGE_MIN, centerPWM, previousColor.v, color.v);
466 } else {
467 color.h = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.h, nextColor.h);
468 color.s = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.s, nextColor.s);
469 color.v = scaleRange(auxInput, centerPWM, PWM_RANGE_MAX, color.v, nextColor.v);
473 break;
475 case LED_FUNCTION_FLIGHT_MODE:
476 for (unsigned i = 0; i < ARRAYLEN(flightModeToLed); i++)
477 if (!flightModeToLed[i].flightMode || FLIGHT_MODE(flightModeToLed[i].flightMode)) {
478 const hsvColor_t *directionalColor = getDirectionalModeColor(ledIndex, &ledStripConfig()->modeColors[flightModeToLed[i].ledMode]);
479 if (directionalColor) {
480 color = *directionalColor;
483 break; // stop on first match
485 break;
487 case LED_FUNCTION_ARM_STATE:
488 color = ARMING_FLAG(ARMED) ? *getSC(LED_SCOLOR_ARMED) : *getSC(LED_SCOLOR_DISARMED);
489 break;
491 case LED_FUNCTION_BATTERY:
492 color = HSV(RED);
493 hOffset += scaleRange(calculateBatteryPercentageRemaining(), 0, 100, -30, 120);
494 break;
496 case LED_FUNCTION_RSSI:
497 color = HSV(RED);
498 hOffset += scaleRange(getRssi() * 100, 0, 1023, -30, 120);
499 break;
501 default:
502 break;
505 if ((fn != LED_FUNCTION_COLOR) && ledGetOverlayBit(ledConfig, LED_OVERLAY_THROTTLE)) {
506 hOffset += scaleRange(auxInput, PWM_RANGE_MIN, PWM_RANGE_MAX, 0, HSV_HUE_MAX + 1);
509 color.h = (color.h + hOffset) % (HSV_HUE_MAX + 1);
510 setLedHsv(ledIndex, &color);
514 static void applyLedHsv(uint32_t mask, const hsvColor_t *color)
516 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
517 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
518 if ((*ledConfig & mask) == mask)
519 setLedHsv(ledIndex, color);
523 typedef enum {
524 WARNING_ARMING_DISABLED,
525 WARNING_LOW_BATTERY,
526 WARNING_FAILSAFE,
527 } warningFlags_e;
529 static void applyLedWarningLayer(bool updateNow, timeUs_t *timer)
531 static uint8_t warningFlashCounter = 0;
532 static uint8_t warningFlags = 0; // non-zero during blinks
534 if (updateNow) {
535 // keep counter running, so it stays in sync with blink
536 warningFlashCounter++;
537 warningFlashCounter &= 0xF;
539 if (warningFlashCounter == 0) { // update when old flags was processed
540 warningFlags = 0;
541 if (batteryConfig()->voltageMeterSource != VOLTAGE_METER_NONE && getBatteryState() != BATTERY_OK)
542 warningFlags |= 1 << WARNING_LOW_BATTERY;
543 if (failsafeIsActive())
544 warningFlags |= 1 << WARNING_FAILSAFE;
545 if (!ARMING_FLAG(ARMED) && isArmingDisabled())
546 warningFlags |= 1 << WARNING_ARMING_DISABLED;
548 *timer += HZ_TO_US(10);
551 const hsvColor_t *warningColor = NULL;
553 if (warningFlags) {
554 bool colorOn = (warningFlashCounter % 2) == 0; // w_w_
555 warningFlags_e warningId = warningFlashCounter / 4;
556 if (warningFlags & (1 << warningId)) {
557 switch (warningId) {
558 case WARNING_ARMING_DISABLED:
559 warningColor = colorOn ? &HSV(GREEN) : &HSV(BLACK);
560 break;
561 case WARNING_LOW_BATTERY:
562 warningColor = colorOn ? &HSV(RED) : &HSV(BLACK);
563 break;
564 case WARNING_FAILSAFE:
565 warningColor = colorOn ? &HSV(YELLOW) : &HSV(BLUE);
566 break;
567 default:;
570 } else {
571 if (isBeeperOn()) {
572 warningColor = &HSV(ORANGE);
576 if (warningColor) {
577 applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_WARNING)), warningColor);
581 #ifdef USE_VTX_COMMON
582 static void applyLedVtxLayer(bool updateNow, timeUs_t *timer)
584 static uint16_t frequency = 0;
585 static uint8_t power = 255;
586 static uint8_t pit = 255;
587 static uint8_t showSettings = false;
588 static uint16_t lastCheck = 0;
589 static bool blink = false;
591 const vtxDevice_t *vtxDevice = vtxCommonDevice();
592 if (!vtxDevice) {
593 return;
596 uint8_t band = 255, channel = 255;
597 uint16_t check = 0;
599 if (updateNow) {
600 // keep counter running, so it stays in sync with vtx
601 vtxCommonGetBandAndChannel(vtxDevice, &band, &channel);
602 vtxCommonGetPowerIndex(vtxDevice, &power);
603 vtxCommonGetPitMode(vtxDevice, &pit);
605 frequency = vtx58frequencyTable[band - 1][channel - 1]; //subtracting 1 from band and channel so that correct frequency is returned.
606 //might not be correct for tramp but should fix smart audio.
607 // check if last vtx values have changed.
608 check = pit + (power << 1) + (band << 4) + (channel << 8);
609 if (!showSettings && check != lastCheck) {
610 // display settings for 3 seconds.
611 showSettings = 15;
613 lastCheck = check; // quick way to check if any settings changed.
615 if (showSettings) {
616 showSettings--;
618 blink = !blink;
619 *timer += HZ_TO_US(5); // check 5 times a second
622 hsvColor_t color = {0, 0, 0};
623 if (showSettings) { // show settings
624 uint8_t vtxLedCount = 0;
625 for (int i = 0; i < ledCounts.count && vtxLedCount < 6; ++i) {
626 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
627 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_VTX)) {
628 if (vtxLedCount == 0) {
629 color.h = HSV(GREEN).h;
630 color.s = HSV(GREEN).s;
631 color.v = blink ? 15 : 0; // blink received settings
633 else if (vtxLedCount > 0 && power >= vtxLedCount && !pit) { // show power
634 color.h = HSV(ORANGE).h;
635 color.s = HSV(ORANGE).s;
636 color.v = blink ? 15 : 0; // blink received settings
638 else { // turn rest off
639 color.h = HSV(BLACK).h;
640 color.s = HSV(BLACK).s;
641 color.v = HSV(BLACK).v;
643 setLedHsv(i, &color);
644 ++vtxLedCount;
648 else { // show frequency
649 // calculate the VTX color based on frequency
650 int colorIndex = 0;
651 if (frequency <= 5672) {
652 colorIndex = COLOR_WHITE;
653 } else if (frequency <= 5711) {
654 colorIndex = COLOR_RED;
655 } else if (frequency <= 5750) {
656 colorIndex = COLOR_ORANGE;
657 } else if (frequency <= 5789) {
658 colorIndex = COLOR_YELLOW;
659 } else if (frequency <= 5829) {
660 colorIndex = COLOR_GREEN;
661 } else if (frequency <= 5867) {
662 colorIndex = COLOR_BLUE;
663 } else if (frequency <= 5906) {
664 colorIndex = COLOR_DARK_VIOLET;
665 } else {
666 colorIndex = COLOR_DEEP_PINK;
668 hsvColor_t color = ledStripConfig()->colors[colorIndex];
669 color.v = pit ? (blink ? 15 : 0) : 255; // blink when in pit mode
670 applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_VTX)), &color);
673 #endif
675 static void applyLedBatteryLayer(bool updateNow, timeUs_t *timer)
677 static bool flash = false;
679 int timerDelayUs = HZ_TO_US(1);
681 if (updateNow) {
683 switch (getBatteryState()) {
684 case BATTERY_OK:
685 flash = true;
686 timerDelayUs = HZ_TO_US(1);
688 break;
689 case BATTERY_WARNING:
690 flash = !flash;
691 timerDelayUs = HZ_TO_US(2);
693 break;
694 default:
695 flash = !flash;
696 timerDelayUs = HZ_TO_US(8);
698 break;
702 *timer += timerDelayUs;
704 if (!flash) {
705 const hsvColor_t *bgc = getSC(LED_SCOLOR_BACKGROUND);
706 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_BATTERY), bgc);
710 static void applyLedRssiLayer(bool updateNow, timeUs_t *timer)
712 static bool flash = false;
714 int timerDelay = HZ_TO_US(1);
716 if (updateNow) {
717 int state = (getRssi() * 100) / 1023;
719 if (state > 50) {
720 flash = true;
721 timerDelay = HZ_TO_US(1);
722 } else if (state > 20) {
723 flash = !flash;
724 timerDelay = HZ_TO_US(2);
725 } else {
726 flash = !flash;
727 timerDelay = HZ_TO_US(8);
731 *timer += timerDelay;
733 if (!flash) {
734 const hsvColor_t *bgc = getSC(LED_SCOLOR_BACKGROUND);
735 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_RSSI), bgc);
739 #ifdef USE_GPS
740 static void applyLedGpsLayer(bool updateNow, timeUs_t *timer)
743 static uint8_t gpsPauseCounter = 0;
744 const uint8_t blinkPauseLength = 4;
746 if (updateNow) {
747 static uint8_t gpsFlashCounter = 0;
748 if (gpsPauseCounter > 0) {
749 gpsPauseCounter--;
750 } else if (gpsFlashCounter >= gpsSol.numSat) {
751 gpsFlashCounter = 0;
752 gpsPauseCounter = blinkPauseLength;
753 } else {
754 gpsFlashCounter++;
755 gpsPauseCounter = 1;
757 *timer += HZ_TO_US(2.5f);
760 const hsvColor_t *gpsColor;
762 if (gpsSol.numSat == 0 || !sensors(SENSOR_GPS)) {
763 gpsColor = getSC(LED_SCOLOR_GPSNOSATS);
764 } else {
765 bool colorOn = gpsPauseCounter == 0; // each interval starts with pause
766 if (STATE(GPS_FIX)) {
767 gpsColor = colorOn ? getSC(LED_SCOLOR_GPSLOCKED) : getSC(LED_SCOLOR_BACKGROUND);
768 } else {
769 gpsColor = colorOn ? getSC(LED_SCOLOR_GPSNOLOCK) : getSC(LED_SCOLOR_GPSNOSATS);
773 applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_GPS), gpsColor);
776 #endif
778 #define INDICATOR_DEADBAND 25
780 static void applyLedIndicatorLayer(bool updateNow, timeUs_t *timer)
782 static bool flash = 0;
784 if (updateNow) {
785 if (rxIsReceivingSignal()) {
786 // calculate update frequency
787 int scale = MAX(ABS(rcCommand[ROLL]), ABS(rcCommand[PITCH])); // 0 - 500
788 scale = scale - INDICATOR_DEADBAND; // start increasing frequency right after deadband
789 *timer += HZ_TO_US(5 + (45 * scale) / (500 - INDICATOR_DEADBAND)); // 5 - 50Hz update, 2.5 - 25Hz blink
791 flash = !flash;
792 } else {
793 *timer += HZ_TO_US(5);
797 if (!flash)
798 return;
800 const hsvColor_t *flashColor = &HSV(ORANGE); // TODO - use user color?
802 quadrant_e quadrants = 0;
803 if (rcCommand[ROLL] > INDICATOR_DEADBAND) {
804 quadrants |= QUADRANT_EAST;
805 } else if (rcCommand[ROLL] < -INDICATOR_DEADBAND) {
806 quadrants |= QUADRANT_WEST;
808 if (rcCommand[PITCH] > INDICATOR_DEADBAND) {
809 quadrants |= QUADRANT_NORTH;
810 } else if (rcCommand[PITCH] < -INDICATOR_DEADBAND) {
811 quadrants |= QUADRANT_SOUTH;
814 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
815 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
816 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_INDICATOR)) {
817 if (getLedQuadrant(ledIndex) & quadrants)
818 setLedHsv(ledIndex, flashColor);
823 #define ROTATION_SEQUENCE_LED_COUNT 6 // 2 on, 4 off
824 #define ROTATION_SEQUENCE_LED_WIDTH 2 // 2 on
826 static void updateLedRingCounts(void)
828 int seqLen;
829 // try to split in segments/rings of exactly ROTATION_SEQUENCE_LED_COUNT leds
830 if ((ledCounts.ring % ROTATION_SEQUENCE_LED_COUNT) == 0) {
831 seqLen = ROTATION_SEQUENCE_LED_COUNT;
832 } else {
833 seqLen = ledCounts.ring;
834 // else split up in equal segments/rings of at most ROTATION_SEQUENCE_LED_COUNT leds
835 // TODO - improve partitioning (15 leds -> 3x5)
836 while ((seqLen > ROTATION_SEQUENCE_LED_COUNT) && ((seqLen % 2) == 0)) {
837 seqLen /= 2;
840 ledCounts.ringSeqLen = seqLen;
843 static void applyLedThrustRingLayer(bool updateNow, timeUs_t *timer)
845 static uint8_t rotationPhase;
846 int ledRingIndex = 0;
848 if (updateNow) {
849 rotationPhase = rotationPhase > 0 ? rotationPhase - 1 : ledCounts.ringSeqLen - 1;
851 *timer += HZ_TO_US(5 + (45 * scaledThrottle) / 100); // 5 - 50Hz update rate
854 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
855 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
856 if (ledGetFunction(ledConfig) == LED_FUNCTION_THRUST_RING) {
858 bool applyColor;
859 if (ARMING_FLAG(ARMED)) {
860 applyColor = (ledRingIndex + rotationPhase) % ledCounts.ringSeqLen < ROTATION_SEQUENCE_LED_WIDTH;
861 } else {
862 applyColor = !(ledRingIndex % 2); // alternating pattern
865 if (applyColor) {
866 const hsvColor_t *ringColor = &ledStripConfig()->colors[ledGetColor(ledConfig)];
867 setLedHsv(ledIndex, ringColor);
870 ledRingIndex++;
875 typedef struct larsonParameters_s {
876 uint8_t currentBrightness;
877 int8_t currentIndex;
878 int8_t direction;
879 } larsonParameters_t;
881 static int brightnessForLarsonIndex(larsonParameters_t *larsonParameters, uint8_t larsonIndex)
883 int offset = larsonIndex - larsonParameters->currentIndex;
884 static const int larsonLowValue = 8;
886 if (ABS(offset) > 1)
887 return (larsonLowValue);
889 if (offset == 0)
890 return (larsonParameters->currentBrightness);
892 if (larsonParameters->direction == offset) {
893 return (larsonParameters->currentBrightness - 127);
896 return (255 - larsonParameters->currentBrightness);
900 static void larsonScannerNextStep(larsonParameters_t *larsonParameters, int delta)
902 if (larsonParameters->currentBrightness > (255 - delta)) {
903 larsonParameters->currentBrightness = 127;
904 if (larsonParameters->currentIndex >= ledCounts.larson || larsonParameters->currentIndex < 0) {
905 larsonParameters->direction = -larsonParameters->direction;
907 larsonParameters->currentIndex += larsonParameters->direction;
908 } else {
909 larsonParameters->currentBrightness += delta;
913 static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer)
915 static larsonParameters_t larsonParameters = { 0, 0, 1 };
917 if (updateNow) {
918 larsonScannerNextStep(&larsonParameters, 15);
919 *timer += HZ_TO_US(60);
922 int scannerLedIndex = 0;
923 for (unsigned i = 0; i < ledCounts.count; i++) {
925 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
927 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_LARSON_SCANNER)) {
928 hsvColor_t ledColor;
929 getLedHsv(i, &ledColor);
930 ledColor.v = brightnessForLarsonIndex(&larsonParameters, scannerLedIndex);
931 setLedHsv(i, &ledColor);
932 scannerLedIndex++;
937 // blink twice, then wait ; either always or just when landing
938 static void applyLedBlinkLayer(bool updateNow, timeUs_t *timer)
940 const uint16_t blinkPattern = 0x8005; // 0b1000000000000101;
941 static uint16_t blinkMask;
943 if (updateNow) {
944 blinkMask = blinkMask >> 1;
945 if (blinkMask <= 1)
946 blinkMask = blinkPattern;
948 *timer += HZ_TO_US(10);
951 bool ledOn = (blinkMask & 1); // b_b_____...
952 if (!ledOn) {
953 for (int i = 0; i < ledCounts.count; ++i) {
954 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i];
956 if (ledGetOverlayBit(ledConfig, LED_OVERLAY_BLINK)) {
957 setLedHsv(i, getSC(LED_SCOLOR_BLINKBACKGROUND));
963 #ifdef USE_LED_ANIMATION
964 static void applyLedAnimationLayer(bool updateNow, timeUs_t *timer)
966 static uint8_t frameCounter = 0;
967 const int animationFrames = ledGridRows;
968 if (updateNow) {
969 frameCounter = (frameCounter + 1 < animationFrames) ? frameCounter + 1 : 0;
970 *timer += HZ_TO_US(20);
973 if (ARMING_FLAG(ARMED))
974 return;
976 int previousRow = frameCounter > 0 ? frameCounter - 1 : animationFrames - 1;
977 int currentRow = frameCounter;
978 int nextRow = (frameCounter + 1 < animationFrames) ? frameCounter + 1 : 0;
980 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
981 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
983 if (ledGetY(ledConfig) == previousRow) {
984 setLedHsv(ledIndex, getSC(LED_SCOLOR_ANIMATION));
985 scaleLedValue(ledIndex, 50);
986 } else if (ledGetY(ledConfig) == currentRow) {
987 setLedHsv(ledIndex, getSC(LED_SCOLOR_ANIMATION));
988 } else if (ledGetY(ledConfig) == nextRow) {
989 scaleLedValue(ledIndex, 50);
993 #endif
995 typedef enum {
996 timBlink,
997 timLarson,
998 timBattery,
999 timRssi,
1000 #ifdef USE_GPS
1001 timGps,
1002 #endif
1003 timWarning,
1004 #ifdef USE_VTX_COMMON
1005 timVtx,
1006 #endif
1007 timIndicator,
1008 #ifdef USE_LED_ANIMATION
1009 timAnimation,
1010 #endif
1011 timRing,
1012 timTimerCount
1013 } timId_e;
1015 static timeUs_t timerVal[timTimerCount];
1016 static uint16_t disabledTimerMask;
1018 STATIC_ASSERT(timTimerCount <= sizeof(disabledTimerMask) * 8, disabledTimerMask_too_small);
1020 // function to apply layer.
1021 // function must replan self using timer pointer
1022 // when updateNow is true (timer triggered), state must be updated first,
1023 // before calculating led state. Otherwise update started by different trigger
1024 // may modify LED state.
1025 typedef void applyLayerFn_timed(bool updateNow, timeUs_t *timer);
1027 static applyLayerFn_timed* layerTable[] = {
1028 [timBlink] = &applyLedBlinkLayer,
1029 [timLarson] = &applyLarsonScannerLayer,
1030 [timBattery] = &applyLedBatteryLayer,
1031 [timRssi] = &applyLedRssiLayer,
1032 #ifdef USE_GPS
1033 [timGps] = &applyLedGpsLayer,
1034 #endif
1035 [timWarning] = &applyLedWarningLayer,
1036 #ifdef USE_VTX_COMMON
1037 [timVtx] = &applyLedVtxLayer,
1038 #endif
1039 [timIndicator] = &applyLedIndicatorLayer,
1040 #ifdef USE_LED_ANIMATION
1041 [timAnimation] = &applyLedAnimationLayer,
1042 #endif
1043 [timRing] = &applyLedThrustRingLayer
1046 bool isOverlayTypeUsed(ledOverlayId_e overlayType)
1048 for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) {
1049 const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex];
1050 if (ledGetOverlayBit(ledConfig, overlayType)) {
1051 return true;
1054 return false;
1057 void updateRequiredOverlay(void)
1059 disabledTimerMask = 0;
1060 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_BLINK) << timBlink;
1061 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_LARSON_SCANNER) << timLarson;
1062 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_WARNING) << timWarning;
1063 #ifdef USE_VTX_COMMON
1064 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_VTX) << timVtx;
1065 #endif
1066 disabledTimerMask |= !isOverlayTypeUsed(LED_OVERLAY_INDICATOR) << timIndicator;
1069 void ledStripUpdate(timeUs_t currentTimeUs)
1071 if (!(ledStripInitialised && isWS2811LedStripReady())) {
1072 return;
1075 if (IS_RC_MODE_ACTIVE(BOXLEDLOW) && !(ledStripConfig()->ledstrip_visual_beeper && isBeeperOn())) {
1076 if (ledStripEnabled) {
1077 ledStripDisable();
1078 ledStripEnabled = false;
1080 return;
1082 ledStripEnabled = true;
1084 const uint32_t now = currentTimeUs;
1086 // test all led timers, setting corresponding bits
1087 uint32_t timActive = 0;
1088 for (timId_e timId = 0; timId < timTimerCount; timId++) {
1089 if (!(disabledTimerMask & (1 << timId))) {
1090 // sanitize timer value, so that it can be safely incremented. Handles inital timerVal value.
1091 const timeDelta_t delta = cmpTimeUs(now, timerVal[timId]);
1092 // max delay is limited to 5s
1093 if (delta < 0 && delta > -MAX_TIMER_DELAY)
1094 continue; // not ready yet
1095 timActive |= 1 << timId;
1096 if (delta >= 100 * 1000 || delta < 0) {
1097 timerVal[timId] = now;
1102 if (!timActive)
1103 return; // no change this update, keep old state
1105 // apply all layers; triggered timed functions has to update timers
1107 scaledThrottle = ARMING_FLAG(ARMED) ? scaleRange(rcData[THROTTLE], PWM_RANGE_MIN, PWM_RANGE_MAX, 0, 100) : 0;
1108 auxInput = rcData[ledStripConfig()->ledstrip_aux_channel];
1110 applyLedFixedLayers();
1112 for (timId_e timId = 0; timId < ARRAYLEN(layerTable); timId++) {
1113 uint32_t *timer = &timerVal[timId];
1114 bool updateNow = timActive & (1 << timId);
1115 (*layerTable[timId])(updateNow, timer);
1117 ws2811UpdateStrip((ledStripFormatRGB_e)ledStripConfig()->ledstrip_grb_rgb);
1120 bool parseColor(int index, const char *colorConfig)
1122 const char *remainingCharacters = colorConfig;
1124 hsvColor_t *color = &ledStripConfigMutable()->colors[index];
1126 bool result = true;
1127 static const uint16_t hsv_limit[HSV_COLOR_COMPONENT_COUNT] = {
1128 [HSV_HUE] = HSV_HUE_MAX,
1129 [HSV_SATURATION] = HSV_SATURATION_MAX,
1130 [HSV_VALUE] = HSV_VALUE_MAX,
1132 for (int componentIndex = 0; result && componentIndex < HSV_COLOR_COMPONENT_COUNT; componentIndex++) {
1133 int val = atoi(remainingCharacters);
1134 if (val > hsv_limit[componentIndex]) {
1135 result = false;
1136 break;
1138 switch (componentIndex) {
1139 case HSV_HUE:
1140 color->h = val;
1141 break;
1142 case HSV_SATURATION:
1143 color->s = val;
1144 break;
1145 case HSV_VALUE:
1146 color->v = val;
1147 break;
1149 remainingCharacters = strchr(remainingCharacters, ',');
1150 if (remainingCharacters) {
1151 remainingCharacters++; // skip separator
1152 } else {
1153 if (componentIndex < HSV_COLOR_COMPONENT_COUNT - 1) {
1154 result = false;
1159 if (!result) {
1160 memset(color, 0, sizeof(*color));
1163 return result;
1167 * Redefine a color in a mode.
1168 * */
1169 bool setModeColor(ledModeIndex_e modeIndex, int modeColorIndex, int colorIndex)
1171 // check color
1172 if (colorIndex < 0 || colorIndex >= LED_CONFIGURABLE_COLOR_COUNT)
1173 return false;
1174 if (modeIndex < LED_MODE_COUNT) { // modeIndex_e is unsigned, so one-sided test is enough
1175 if (modeColorIndex < 0 || modeColorIndex >= LED_DIRECTION_COUNT)
1176 return false;
1177 ledStripConfigMutable()->modeColors[modeIndex].color[modeColorIndex] = colorIndex;
1178 } else if (modeIndex == LED_SPECIAL) {
1179 if (modeColorIndex < 0 || modeColorIndex >= LED_SPECIAL_COLOR_COUNT)
1180 return false;
1181 ledStripConfigMutable()->specialColors.color[modeColorIndex] = colorIndex;
1182 } else if (modeIndex == LED_AUX_CHANNEL) {
1183 if (modeColorIndex < 0 || modeColorIndex >= 1)
1184 return false;
1185 ledStripConfigMutable()->ledstrip_aux_channel = colorIndex;
1186 } else {
1187 return false;
1189 return true;
1192 void ledStripInit(void)
1194 colors = ledStripConfigMutable()->colors;
1195 modeColors = ledStripConfig()->modeColors;
1196 specialColors = ledStripConfig()->specialColors;
1197 ledStripInitialised = false;
1200 void ledStripEnable(void)
1202 reevaluateLedConfig();
1203 ledStripInitialised = true;
1205 ws2811LedStripInit(ledStripConfig()->ioTag);
1208 static void ledStripDisable(void)
1210 setStripColor(&HSV(BLACK));
1212 ws2811UpdateStrip((ledStripFormatRGB_e)ledStripConfig()->ledstrip_grb_rgb);
1214 #endif