static void applyLedRssiLayer(bool updateNow, timeUs_t *timer) { static bool flash = false; int timerDelay = HZ_TO_US(1); if (updateNow) { int state = (rssi * 100) / 1023; if (state > 50) { flash = true; timerDelay = HZ_TO_US(1); } else if (state > 20) { flash = !flash; timerDelay = HZ_TO_US(2); } else { flash = !flash; timerDelay = HZ_TO_US(8); } } *timer += timerDelay; if (!flash) { const hsvColor_t *bgc = getSC(LED_SCOLOR_BACKGROUND); applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_RSSI), bgc); } }
static void applyLedBatteryLayer(bool updateNow, timeUs_t *timer) { static bool flash = false; int timerDelayUs = HZ_TO_US(1); if (updateNow) { switch (getBatteryState()) { case BATTERY_OK: flash = true; timerDelayUs = HZ_TO_US(1); break; case BATTERY_WARNING: flash = !flash; timerDelayUs = HZ_TO_US(2); break; default: flash = !flash; timerDelayUs = HZ_TO_US(8); break; } } *timer += timerDelayUs; if (!flash) { const hsvColor_t *bgc = getSC(LED_SCOLOR_BACKGROUND); applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_BATTERY), bgc); } }
static void applyLedAnimationLayer(bool updateNow, timeUs_t *timer) { static uint8_t frameCounter = 0; const int animationFrames = ledGridRows; if(updateNow) { frameCounter = (frameCounter + 1 < animationFrames) ? frameCounter + 1 : 0; *timer += HZ_TO_US(20); } if (ARMING_FLAG(ARMED)) return; int previousRow = frameCounter > 0 ? frameCounter - 1 : animationFrames - 1; int currentRow = frameCounter; int nextRow = (frameCounter + 1 < animationFrames) ? frameCounter + 1 : 0; for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) { const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex]; if (ledGetY(ledConfig) == previousRow) { setLedHsv(ledIndex, getSC(LED_SCOLOR_ANIMATION)); scaleLedValue(ledIndex, 50); } else if (ledGetY(ledConfig) == currentRow) { setLedHsv(ledIndex, getSC(LED_SCOLOR_ANIMATION)); } else if (ledGetY(ledConfig) == nextRow) { scaleLedValue(ledIndex, 50); } } }
// blink twice, then wait ; either always or just when landing static void applyLedBlinkLayer(bool updateNow, timeUs_t *timer) { const uint16_t blinkPattern = 0x8005; // 0b1000000000000101; static uint16_t blinkMask; if (updateNow) { blinkMask = blinkMask >> 1; if (blinkMask <= 1) blinkMask = blinkPattern; *timer += HZ_TO_US(10); } bool ledOn = (blinkMask & 1); // b_b_____... if (!ledOn) { for (int i = 0; i < ledCounts.count; ++i) { const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; if (ledGetOverlayBit(ledConfig, LED_OVERLAY_BLINK) || (ledGetOverlayBit(ledConfig, LED_OVERLAY_LANDING_FLASH) && scaledThrottle < 50)) { setLedHsv(i, getSC(LED_SCOLOR_BLINKBACKGROUND)); } } } }
static void applyLedThrustRingLayer(bool updateNow, timeUs_t *timer) { static uint8_t rotationPhase; int ledRingIndex = 0; if (updateNow) { rotationPhase = rotationPhase > 0 ? rotationPhase - 1 : ledCounts.ringSeqLen - 1; *timer += HZ_TO_US(5 + (45 * scaledThrottle) / 100); // 5 - 50Hz update rate } for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) { const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex]; if (ledGetFunction(ledConfig) == LED_FUNCTION_THRUST_RING) { bool applyColor; if (ARMING_FLAG(ARMED)) { applyColor = (ledRingIndex + rotationPhase) % ledCounts.ringSeqLen < ROTATION_SEQUENCE_LED_WIDTH; } else { applyColor = !(ledRingIndex % 2); // alternating pattern } if (applyColor) { const hsvColor_t *ringColor = &ledStripConfig()->colors[ledGetColor(ledConfig)]; setLedHsv(ledIndex, ringColor); } ledRingIndex++; } } }
static void applyLedGpsLayer(bool updateNow, timeUs_t *timer) { static uint8_t gpsPauseCounter = 0; const uint8_t blinkPauseLength = 4; if (updateNow) { static uint8_t gpsFlashCounter = 0; if (gpsPauseCounter > 0) { gpsPauseCounter--; } else if (gpsFlashCounter >= GPS_numSat) { gpsFlashCounter = 0; gpsPauseCounter = blinkPauseLength; } else { gpsFlashCounter++; gpsPauseCounter = 1; } *timer += HZ_TO_US(2.5f); } const hsvColor_t *gpsColor; if (GPS_numSat == 0 || !sensors(SENSOR_GPS)) { gpsColor = getSC(LED_SCOLOR_GPSNOSATS); } else { bool colorOn = gpsPauseCounter == 0; // each interval starts with pause if (STATE(GPS_FIX)) { gpsColor = colorOn ? getSC(LED_SCOLOR_GPSLOCKED) : getSC(LED_SCOLOR_BACKGROUND); } else { gpsColor = colorOn ? getSC(LED_SCOLOR_GPSNOLOCK) : getSC(LED_SCOLOR_GPSNOSATS); } } applyLedHsv(LED_MOV_FUNCTION(LED_FUNCTION_GPS), gpsColor); }
static void applyLedIndicatorLayer(bool updateNow, timeUs_t *timer) { static bool flash = 0; if (updateNow) { if (rxIsReceivingSignal()) { // calculate update frequency int scale = MAX(ABS(rcCommand[ROLL]), ABS(rcCommand[PITCH])); // 0 - 500 scale = scale - INDICATOR_DEADBAND; // start increasing frequency right after deadband *timer += HZ_TO_US(5 + (45 * scale) / (500 - INDICATOR_DEADBAND)); // 5 - 50Hz update, 2.5 - 25Hz blink flash = !flash; } else { *timer += HZ_TO_US(5); } } if (!flash) return; const hsvColor_t *flashColor = &HSV(ORANGE); // TODO - use user color? quadrant_e quadrants = 0; if (rcCommand[ROLL] > INDICATOR_DEADBAND) { quadrants |= QUADRANT_EAST; } else if (rcCommand[ROLL] < -INDICATOR_DEADBAND) { quadrants |= QUADRANT_WEST; } if (rcCommand[PITCH] > INDICATOR_DEADBAND) { quadrants |= QUADRANT_NORTH; } else if (rcCommand[PITCH] < -INDICATOR_DEADBAND) { quadrants |= QUADRANT_SOUTH; } for (int ledIndex = 0; ledIndex < ledCounts.count; ledIndex++) { const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[ledIndex]; if (ledGetOverlayBit(ledConfig, LED_OVERLAY_INDICATOR)) { if (getLedQuadrant(ledIndex) & quadrants) setLedHsv(ledIndex, flashColor); } } }
static void applyLedWarningLayer(bool updateNow, timeUs_t *timer) { static uint8_t warningFlashCounter = 0; static uint8_t warningFlags = 0; // non-zero during blinks if (updateNow) { // keep counter running, so it stays in sync with blink warningFlashCounter++; warningFlashCounter &= 0xF; if (warningFlashCounter == 0) { // update when old flags was processed warningFlags = 0; if (batteryConfig()->voltageMeterSource != VOLTAGE_METER_NONE && getBatteryState() != BATTERY_OK) warningFlags |= 1 << WARNING_LOW_BATTERY; if (failsafeIsActive()) warningFlags |= 1 << WARNING_FAILSAFE; if (!ARMING_FLAG(ARMED) && isArmingDisabled()) warningFlags |= 1 << WARNING_ARMING_DISABLED; } *timer += HZ_TO_US(10); } const hsvColor_t *warningColor = NULL; if (warningFlags) { bool colorOn = (warningFlashCounter % 2) == 0; // w_w_ warningFlags_e warningId = warningFlashCounter / 4; if (warningFlags & (1 << warningId)) { switch (warningId) { case WARNING_ARMING_DISABLED: warningColor = colorOn ? &HSV(GREEN) : &HSV(BLACK); break; case WARNING_LOW_BATTERY: warningColor = colorOn ? &HSV(RED) : &HSV(BLACK); break; case WARNING_FAILSAFE: warningColor = colorOn ? &HSV(YELLOW) : &HSV(BLUE); break; default:; } } } else { if (isBeeperOn()) { warningColor = &HSV(ORANGE); } } if (warningColor) { applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_WARNING)), warningColor); } }
static void applyLedWarningLayer(bool updateNow, timeUs_t *timer) { static uint8_t warningFlashCounter = 0; static uint8_t warningFlags = 0; // non-zero during blinks if (updateNow) { // keep counter running, so it stays in sync with blink warningFlashCounter++; warningFlashCounter &= 0xF; if (warningFlashCounter == 0) { // update when old flags was processed warningFlags = 0; if (feature(FEATURE_VBAT) && getBatteryState() != BATTERY_OK) warningFlags |= 1 << WARNING_LOW_BATTERY; if (feature(FEATURE_FAILSAFE) && failsafeIsActive()) warningFlags |= 1 << WARNING_FAILSAFE; if (!ARMING_FLAG(ARMED) && !ARMING_FLAG(OK_TO_ARM)) warningFlags |= 1 << WARNING_ARMING_DISABLED; } *timer += HZ_TO_US(10); } if (warningFlags) { const hsvColor_t *warningColor = NULL; bool colorOn = (warningFlashCounter % 2) == 0; // w_w_ warningFlags_e warningId = warningFlashCounter / 4; if (warningFlags & (1 << warningId)) { switch (warningId) { case WARNING_ARMING_DISABLED: warningColor = colorOn ? &HSV(GREEN) : &HSV(BLACK); break; case WARNING_LOW_BATTERY: warningColor = colorOn ? &HSV(RED) : &HSV(BLACK); break; case WARNING_FAILSAFE: warningColor = colorOn ? &HSV(YELLOW) : &HSV(BLUE); break; default:; } } if (warningColor) applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_WARNING)), warningColor); } }
static void applyLarsonScannerLayer(bool updateNow, timeUs_t *timer) { static larsonParameters_t larsonParameters = { 0, 0, 1 }; if (updateNow) { larsonScannerNextStep(&larsonParameters, 15); *timer += HZ_TO_US(60); } int scannerLedIndex = 0; for (unsigned i = 0; i < ledCounts.count; i++) { const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; if (ledGetOverlayBit(ledConfig, LED_OVERLAY_LARSON_SCANNER)) { hsvColor_t ledColor; getLedHsv(i, &ledColor); ledColor.v = brightnessForLarsonIndex(&larsonParameters, scannerLedIndex); setLedHsv(i, &ledColor); scannerLedIndex++; } } }
static void applyLedVtxLayer(bool updateNow, timeUs_t *timer) { static uint16_t frequency = 0; static uint8_t power = 255; static uint8_t pit = 255; static uint8_t showSettings = false; static uint16_t lastCheck = 0; static bool blink = false; const vtxDevice_t *vtxDevice = vtxCommonDevice(); if (!vtxDevice) { return; } uint8_t band = 255, channel = 255; uint16_t check = 0; if (updateNow) { // keep counter running, so it stays in sync with vtx vtxCommonGetBandAndChannel(vtxDevice, &band, &channel); vtxCommonGetPowerIndex(vtxDevice, &power); vtxCommonGetPitMode(vtxDevice, &pit); frequency = vtx58frequencyTable[band - 1][channel - 1]; //subtracting 1 from band and channel so that correct frequency is returned. //might not be correct for tramp but should fix smart audio. // check if last vtx values have changed. check = pit + (power << 1) + (band << 4) + (channel << 8); if (!showSettings && check != lastCheck) { // display settings for 3 seconds. showSettings = 15; } lastCheck = check; // quick way to check if any settings changed. if (showSettings) { showSettings--; } blink = !blink; *timer += HZ_TO_US(5); // check 5 times a second } hsvColor_t color = {0, 0, 0}; if (showSettings) { // show settings uint8_t vtxLedCount = 0; for (int i = 0; i < ledCounts.count && vtxLedCount < 6; ++i) { const ledConfig_t *ledConfig = &ledStripConfig()->ledConfigs[i]; if (ledGetOverlayBit(ledConfig, LED_OVERLAY_VTX)) { if (vtxLedCount == 0) { color.h = HSV(GREEN).h; color.s = HSV(GREEN).s; color.v = blink ? 15 : 0; // blink received settings } else if (vtxLedCount > 0 && power >= vtxLedCount && !pit) { // show power color.h = HSV(ORANGE).h; color.s = HSV(ORANGE).s; color.v = blink ? 15 : 0; // blink received settings } else { // turn rest off color.h = HSV(BLACK).h; color.s = HSV(BLACK).s; color.v = HSV(BLACK).v; } setLedHsv(i, &color); ++vtxLedCount; } } } else { // show frequency // calculate the VTX color based on frequency int colorIndex = 0; if (frequency <= 5672) { colorIndex = COLOR_WHITE; } else if (frequency <= 5711) { colorIndex = COLOR_RED; } else if (frequency <= 5750) { colorIndex = COLOR_ORANGE; } else if (frequency <= 5789) { colorIndex = COLOR_YELLOW; } else if (frequency <= 5829) { colorIndex = COLOR_GREEN; } else if (frequency <= 5867) { colorIndex = COLOR_BLUE; } else if (frequency <= 5906) { colorIndex = COLOR_DARK_VIOLET; } else { colorIndex = COLOR_DEEP_PINK; } hsvColor_t color = ledStripConfig()->colors[colorIndex]; color.v = pit ? (blink ? 15 : 0) : 255; // blink when in pit mode applyLedHsv(LED_MOV_OVERLAY(LED_FLAG_OVERLAY(LED_OVERLAY_VTX)), &color); } }