1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-15 12:25:20 +03:00

Allow led strip to show failsafe state. Closes #76.

This commit is contained in:
Dominic Clifton 2014-09-15 23:00:18 +01:00
parent e14347bf47
commit a287f9247d
6 changed files with 388 additions and 349 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
*.md eol=crlf

View file

@ -1,290 +1,293 @@
# Led Strip # Led Strip
Cleanflight supports the use of addressable LED strips. Addressable LED strips allow each LED in the strip to Cleanflight supports the use of addressable LED strips. Addressable LED strips allow each LED in the strip to
be programmed with a unique and independant color. This is far more advanced than the normal RGB strips which be programmed with a unique and independant color. This is far more advanced than the normal RGB strips which
require that all the LEDs in the strip show the same color. require that all the LEDs in the strip show the same color.
Addressable LED strips can be used to show information from the flight controller system, the current implementation Addressable LED strips can be used to show information from the flight controller system, the current implementation
supports the following: supports the following:
* Up to 32 LEDs. * Up to 32 LEDs.
* Indicators showing pitch/roll stick positions. * Indicators showing pitch/roll stick positions.
* Heading/Orientation lights. * Heading/Orientation lights.
* Flight mode specific color schemes. * Flight mode specific color schemes.
* Low battery warning. * Low battery warning.
The function and orientation configuration is fixed for now but later it should be able to be set via the UI or CLI.. The function and orientation configuration is fixed for now but later it should be able to be set via the UI or CLI..
In the future, if someone codes it, they could be used to show GPS navigation status, thrust levels, RSSI, etc. In the future, if someone codes it, they could be used to show GPS navigation status, thrust levels, RSSI, etc.
Lots of scope for ideas and improvements. Lots of scope for ideas and improvements.
Likewise, support for more than 32 LEDs is possible, it just requires additional development. Likewise, support for more than 32 LEDs is possible, it just requires additional development.
## Supported hardware ## Supported hardware
Only strips of 32 WS2812 LEDs are supported currently. If the strip is longer than 32 leds it does not matter, Only strips of 32 WS2812 LEDs are supported currently. If the strip is longer than 32 leds it does not matter,
but only the first 32 are used. but only the first 32 are used.
WS2812 LEDs require an 800khz signal and precise timings and thus requires the use of a dedicated hardware timer. WS2812 LEDs require an 800khz signal and precise timings and thus requires the use of a dedicated hardware timer.
Note: The initial code may work with WS2801 + External LEDs since the protocol is the same, WS2811/WS2812B should also work but Note: The initial code may work with WS2801 + External LEDs since the protocol is the same, WS2811/WS2812B should also work but
may require very simple timing adjustments to be made in the source. may require very simple timing adjustments to be made in the source.
Not all WS2812 ICs use the same timings, some batches use different timings. Not all WS2812 ICs use the same timings, some batches use different timings.
It could be possible to be able to specify the timings required via CLI if users request it. It could be possible to be able to specify the timings required via CLI if users request it.
## Connections ## Connections
WS2812 LED strips generally require a single data line, 5V and GND. WS2812 LED strips generally require a single data line, 5V and GND.
WS2812 LEDs on full brightness can consume quite a bit of current. It is recommended to verify the current draw and ensure your WS2812 LEDs on full brightness can consume quite a bit of current. It is recommended to verify the current draw and ensure your
supply can cope with the load. On a multirotor that uses multiple BEC ESC's you can try use a different BEC to the one the FC supply can cope with the load. On a multirotor that uses multiple BEC ESC's you can try use a different BEC to the one the FC
uses. e.g. ESC1/BEC1 -> FC, ESC2/BEC2 -> LED strip. It's also possible to power one half of the strip from one BEC and the other half uses. e.g. ESC1/BEC1 -> FC, ESC2/BEC2 -> LED strip. It's also possible to power one half of the strip from one BEC and the other half
from another BEC. Just ensure that the GROUND is the same for all BEC outputs and LEDs. from another BEC. Just ensure that the GROUND is the same for all BEC outputs and LEDs.
| Target | Pin | Led Strip | Signal | | Target | Pin | Led Strip | Signal |
| --------------------- | --- | --------- | -------| | --------------------- | --- | --------- | -------|
| Naze/Olimexino | RC5 | Data In | PA6 | | Naze/Olimexino | RC5 | Data In | PA6 |
| CC3D | ??? | Data In | PB4 | | CC3D | ??? | Data In | PB4 |
| ChebuzzF3/F3Discovery | PB8 | Data In | PB8 | | ChebuzzF3/F3Discovery | PB8 | Data In | PB8 |
Since RC5 is also used for SoftSerial on the Naze/Olimexino it means that you cannot use softserial and led strips at the same time. Since RC5 is also used for SoftSerial on the Naze/Olimexino it means that you cannot use softserial and led strips at the same time.
Additionally, since RC5 is also used for Parallel PWM RC input on both the Naze, Chebuzz and STM32F3Discovery targets, led strips Additionally, since RC5 is also used for Parallel PWM RC input on both the Naze, Chebuzz and STM32F3Discovery targets, led strips
can not be used at the same time at Parallel PWM. can not be used at the same time at Parallel PWM.
## Configuration ## Configuration
Enable the `LED_STRIP` feature via the cli: Enable the `LED_STRIP` feature via the cli:
``` ```
feature LED_STRIP feature LED_STRIP
``` ```
If you enable LED_STRIP feature and the feature is turned off again after a reboot then check your config does not conflict with other features, as above. If you enable LED_STRIP feature and the feature is turned off again after a reboot then check your config does not conflict with other features, as above.
Configure the LEDs using the `led` command. Configure the LEDs using the `led` command.
The `led` command takes either zero or two arguments - an zero-based led number and a pair of coordinates, direction flags and mode flags. The `led` command takes either zero or two arguments - an zero-based led number and a pair of coordinates, direction flags and mode flags.
If used with zero arguments it prints out the led configuration which can be copied for future reference. If used with zero arguments it prints out the led configuration which can be copied for future reference.
Each led is configured using the following template: `x,y:ddd:mmm` Each led is configured using the following template: `x,y:ddd:mmm`
`x` and `y` are grid coordinates of a 0 based 16x16 grid, north west is 0,0, south east is 15,15 `x` and `y` are grid coordinates of a 0 based 16x16 grid, north west is 0,0, south east is 15,15
`ddd` specifies the directions, since an led can face in any direction it can have multiple directions. Directions are: `ddd` specifies the directions, since an led can face in any direction it can have multiple directions. Directions are:
`N` - North `N` - North
`E` - East `E` - East
`S` - South `S` - South
`W` - West `W` - West
`U` - Up `U` - Up
`D` - Down `D` - Down
For instance, an LED that faces South-east at a 45 degree downwards angle could be configured as `SED`. For instance, an LED that faces South-east at a 45 degree downwards angle could be configured as `SED`.
Note: It is perfectly possible to configure an LED to have all directions `NESWUD` but probably doesn't make sense. Note: It is perfectly possible to configure an LED to have all directions `NESWUD` but probably doesn't make sense.
`mmm` specifies the modes that should be applied an LED. Modes are: `mmm` specifies the modes that should be applied an LED. Modes are:
* `B` - `B`attery warning. * `W` - `W`warnings.
* `F` - `F`light mode & Orientation * `F` - `F`light mode & Orientation
* `I` - `I`ndicator. * `I` - `I`ndicator.
* `A` - `A`rmed state. * `A` - `A`rmed state.
Example: Example:
``` ```
led 0 0,15:SD:IAB led 0 0,15:SD:IAW
led 1 15,0:ND:IAB led 1 15,0:ND:IAW
led 2 0,0:ND:IAB led 2 0,0:ND:IAW
led 3 0,15:SD:IAB led 3 0,15:SD:IAW
``` ```
to erase an led, and to mark the end of the chain, use `0,0::` as the second argument, like this: to erase an led, and to mark the end of the chain, use `0,0::` as the second argument, like this:
``` ```
led 4 0,0:: led 4 0,0::
``` ```
### Modes ### Modes
#### Battery Warning #### Warning
This mode simply flashes the LED RED when the battery is low if battery monitoring is enabled. This mode simply uses the leds to flash when warnings occur.
#### Flight Mode & Orientation * Battery warning flashes the LEDs between red and off when the battery is low if battery monitoring is enabled.
* Failsafe warning flashes the LEDs between light blue and lime green when failsafe is active.
This mode shows the flight mode and orientation.
#### Flight Mode & Orientation
When flight modes are active then the leds are updated to show different colors depending on the mode, placement on the grid and direction.
This mode shows the flight mode and orientation.
Leds are set in a specific order:
* Leds that marked as facing up or down. When flight modes are active then the leds are updated to show different colors depending on the mode, placement on the grid and direction.
* Leds that marked as facing west or east AND are on the west or east side of the grid.
* Leds that marked as facing north or south AND are on the north or south side of the grid. Leds are set in a specific order:
* Leds that marked as facing up or down.
That is, south facing leds have priority. * Leds that marked as facing west or east AND are on the west or east side of the grid.
* Leds that marked as facing north or south AND are on the north or south side of the grid.
#### Indicator
That is, south facing leds have priority.
This mode flashes LEDs that correspond to roll and pitch stick positions. i.e. they indicate the direction the craft is going to turn.
#### Indicator
#### Armed state
This mode flashes LEDs that correspond to roll and pitch stick positions. i.e. they indicate the direction the craft is going to turn.
This mode toggles LEDs between green and blue when disarmed and armed, respectively.
#### Armed state
Note: Armed State cannot be used with Flight Mode.
This mode toggles LEDs between green and blue when disarmed and armed, respectively.
## Positioning
Note: Armed State cannot be used with Flight Mode.
Cut the strip into sections as per diagrams below. When the strips are cut ensure you reconnect each output to each input with cable where the break is made.
e.g. connect 5V out to 5V in, GND to GND and Data Out to Data In. ## Positioning
Orientation is when viewed with the front of the aircraft facing away from you and viewed from above. Cut the strip into sections as per diagrams below. When the strips are cut ensure you reconnect each output to each input with cable where the break is made.
e.g. connect 5V out to 5V in, GND to GND and Data Out to Data In.
### Example 12 LED config
Orientation is when viewed with the front of the aircraft facing away from you and viewed from above.
The default configuration is as follows
``` ### Example 12 LED config
led 0 2,2:ES:IA
led 1 2,1:E:BF The default configuration is as follows
led 2 2,0:NE:IA ```
led 3 1,0:N:F led 0 2,2:ES:IA
led 4 0,0:NW:IA led 1 2,1:E:WF
led 5 0,1:W:BF led 2 2,0:NE:IA
led 6 0,2:SW:IA led 3 1,0:N:F
led 7 1,2:S:BF led 4 0,0:NW:IA
led 8 1,1:U:BF led 5 0,1:W:WF
led 9 1,1:U:BF led 6 0,2:SW:IA
led 10 1,1:D:BF led 7 1,2:S:WF
led 11 1,1:D:BF led 8 1,1:U:WF
``` led 9 1,1:U:WF
led 10 1,1:D:WF
Which translates into the following positions: led 11 1,1:D:WF
```
```
5 3 Which translates into the following positions:
\ /
\ 4 / ```
\ FRONT / 5 3
6 | 9-12 | 2 \ /
/ BACK \ \ 4 /
/ 8 \ \ FRONT /
/ \ 6 | 9-12 | 2
7 1 / BACK \
``` / 8 \
/ \
LEDs 1,3,5 and 7 should be placed underneath the quad, facing downwards. 7 1
LEDs 2, 4, 6 and 8 should be positioned so the face east/north/west/south, respectively. ```
LEDs 9-10 should be placed facing down, in the middle
LEDs 11-12 should be placed facing up, in the middle LEDs 1,3,5 and 7 should be placed underneath the quad, facing downwards.
LEDs 2, 4, 6 and 8 should be positioned so the face east/north/west/south, respectively.
This is the default so that if you don't want to place LEDs top and bottom in the middle just connect the first 8 leds. LEDs 9-10 should be placed facing down, in the middle
LEDs 11-12 should be placed facing up, in the middle
### Example 16 LED config
This is the default so that if you don't want to place LEDs top and bottom in the middle just connect the first 8 leds.
```
15,15:SD:IA ### Example 16 LED config
8,8:E:FB
8,7:E:FB ```
15,0:ND:IA 15,15:SD:IA
7,7:N:FB 8,8:E:FW
8,7:N:FB 8,7:E:FW
0,0:ND:IA 15,0:ND:IA
7,7:W:FB 7,7:N:FW
7,8:W:FB 8,7:N:FW
0,15:SD:IA 0,0:ND:IA
7,8:S:FB 7,7:W:FW
8,8:S:FB 7,8:W:FW
7,7:D:FB 0,15:SD:IA
8,7:D:FB 7,8:S:FW
7,7:U:FB 8,8:S:FW
8,7:U:FB 7,7:D:FW
``` 8,7:D:FW
7,7:U:FW
Which translates into the following positions: 8,7:U:FW
```
```
7 4 Which translates into the following positions:
\ /
\ 6-5 / ```
8 \ FRONT / 3 7 4
| 13-16 | \ /
9 / BACK \ 2 \ 6-5 /
/ 11-12 \ 8 \ FRONT / 3
/ \ | 13-16 |
10 1 9 / BACK \ 2
``` / 11-12 \
/ \
LEDs 1,4,7 and 10 should be placed underneath the quad, facing downwards. 10 1
LEDs 2-3, 6-5, 8-9 and 11-12 should be positioned so the face east/north/west/south, respectively. ```
LEDs 13-14 should be placed facing down, in the middle
LEDs 15-16 should be placed facing up, in the middle LEDs 1,4,7 and 10 should be placed underneath the quad, facing downwards.
LEDs 2-3, 6-5, 8-9 and 11-12 should be positioned so the face east/north/west/south, respectively.
### Exmple 28 LED config LEDs 13-14 should be placed facing down, in the middle
LEDs 15-16 should be placed facing up, in the middle
```
9,9:S:FB ### Exmple 28 LED config
10,10:S:FB
11,11:S:IA ```
11,11:E:IA 9,9:S:FW
10,10:E:F 10,10:S:FW
9,9:E:F 11,11:S:IA
10,5:S:F 11,11:E:IA
11,4:S:F 10,10:E:F
12,3:S:IA 9,9:E:F
12,2:N:IA 10,5:S:F
11,1:N:F 11,4:S:F
10,0:N:F 12,3:S:IA
7,0:N:FB 12,2:N:IA
6,0:N:FB 11,1:N:F
5,0:N:FB 10,0:N:F
4,0:N:FB 7,0:N:FW
2,0:N:F 6,0:N:FW
1,1:N:F 5,0:N:FW
0,2:N:IA 4,0:N:FW
0,3:W:IA 2,0:N:F
1,4:W:F 1,1:N:F
2,5:W:F 0,2:N:IA
2,9:W:F 0,3:W:IA
1,10:W:F 1,4:W:F
0,11:W:IA 2,5:W:F
0,11:S:IA 2,9:W:F
1,10:S:FB 1,10:W:F
2,9:S:FB 0,11:W:IA
``` 0,11:S:IA
1,10:S:FW
``` 2,9:S:FW
17-19 10-12 ```
20-22 \ / 7-9
\ 13-16 / ```
\ FRONT / 17-19 10-12
/ BACK \ 20-22 \ / 7-9
/ \ \ 13-16 /
23-25 / \ 4-6 \ FRONT /
26-28 1-3 / BACK \
``` / \
23-25 / \ 4-6
All LEDs should face outwards from the chassis in this configuration. 26-28 1-3
```
Note:
This configuration is specifically designed for the Alien Spider AQ50D PRO 250mm frame. All LEDs should face outwards from the chassis in this configuration.
http://www.goodluckbuy.com/alien-spider-aq50d-pro-250mm-mini-quadcopter-carbon-fiber-micro-multicopter-frame.html Note:
This configuration is specifically designed for the Alien Spider AQ50D PRO 250mm frame.
## Troubleshooting
http://www.goodluckbuy.com/alien-spider-aq50d-pro-250mm-mini-quadcopter-carbon-fiber-micro-multicopter-frame.html
On initial power up the LEDs on the strip will be set to WHITE. This means you can attach a current meter to verify
the current draw if your measurement equipment is fast enough. This also means that you can make sure that each R,G and B LED ## Troubleshooting
in each LED module on the strip is also functioning.
On initial power up the LEDs on the strip will be set to WHITE. This means you can attach a current meter to verify
After a short delay the LEDs will show the unarmed color sequence and or low-battery warning sequence. the current draw if your measurement equipment is fast enough. This also means that you can make sure that each R,G and B LED
in each LED module on the strip is also functioning.
If the LEDs flash intermittently or do not show the correct colors verify all connections and check the specifications of the
LEDs you have against the supported timings (for now, you'll have to look in the source). After a short delay the LEDs will show the unarmed color sequence and or low-battery warning sequence.
Also check that the feature `LED_STRIP` was correctly enabled and that it does not conflict with other features, as above. If the LEDs flash intermittently or do not show the correct colors verify all connections and check the specifications of the
LEDs you have against the supported timings (for now, you'll have to look in the source).
Also check that the feature `LED_STRIP` was correctly enabled and that it does not conflict with other features, as above.

View file

@ -42,9 +42,12 @@
#include "config/config.h" #include "config/config.h"
#include "rx/rx.h" #include "rx/rx.h"
#include "io/rc_controls.h" #include "io/rc_controls.h"
#include "flight/failsafe.h"
#include "io/ledstrip.h" #include "io/ledstrip.h"
static failsafe_t* failsafe;
#if MAX_LED_STRIP_LENGTH > WS2811_LED_STRIP_LENGTH #if MAX_LED_STRIP_LENGTH > WS2811_LED_STRIP_LENGTH
#error "Led strip length must match driver" #error "Led strip length must match driver"
#endif #endif
@ -74,6 +77,8 @@ const rgbColor24bpp_t red = { LED_RED };
const rgbColor24bpp_t orange = { LED_ORANGE }; const rgbColor24bpp_t orange = { LED_ORANGE };
const rgbColor24bpp_t green = { LED_GREEN }; const rgbColor24bpp_t green = { LED_GREEN };
const rgbColor24bpp_t blue = { LED_BLUE }; const rgbColor24bpp_t blue = { LED_BLUE };
const rgbColor24bpp_t lightBlue = { LED_LIGHT_BLUE };
const rgbColor24bpp_t limeGreen = { LED_LIME_GREEN };
uint8_t ledGridWidth; uint8_t ledGridWidth;
@ -84,17 +89,17 @@ ledConfig_t *ledConfigs;
const ledConfig_t defaultLedStripConfig[] = { const ledConfig_t defaultLedStripConfig[] = {
{ CALCULATE_LED_XY( 2, 2), LED_DIRECTION_SOUTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 2, 2), LED_DIRECTION_SOUTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 2, 1), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 2, 1), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 2, 0), LED_DIRECTION_NORTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 2, 0), LED_DIRECTION_NORTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 1, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 1, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 0, 0), LED_DIRECTION_NORTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 0, 0), LED_DIRECTION_NORTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 0, 1), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 0, 1), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 0, 2), LED_DIRECTION_SOUTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 0, 2), LED_DIRECTION_SOUTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 1, 2), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 1, 2), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 1, 1), LED_DIRECTION_UP | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 1, 1), LED_DIRECTION_UP | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 1, 1), LED_DIRECTION_UP | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 1, 1), LED_DIRECTION_UP | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 1, 1), LED_DIRECTION_DOWN | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 1, 1), LED_DIRECTION_DOWN | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 1, 1), LED_DIRECTION_DOWN | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 1, 1), LED_DIRECTION_DOWN | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
}; };
@ -130,11 +135,11 @@ static const uint8_t directionMappings[DIRECTION_COUNT] = {
LED_DIRECTION_DOWN LED_DIRECTION_DOWN
}; };
static const char functionCodes[] = { 'I', 'B', 'F', 'A' }; static const char functionCodes[] = { 'I', 'W', 'F', 'A' };
#define FUNCTION_COUNT (sizeof(functionCodes) / sizeof(functionCodes[0])) #define FUNCTION_COUNT (sizeof(functionCodes) / sizeof(functionCodes[0]))
static const uint16_t functionMappings[FUNCTION_COUNT] = { static const uint16_t functionMappings[FUNCTION_COUNT] = {
LED_FUNCTION_INDICATOR, LED_FUNCTION_INDICATOR,
LED_FUNCTION_BATTERY, LED_FUNCTION_WARNING,
LED_FUNCTION_FLIGHT_MODE, LED_FUNCTION_FLIGHT_MODE,
LED_FUNCTION_ARM_STATE LED_FUNCTION_ARM_STATE
}; };
@ -310,7 +315,7 @@ void generateLedConfig(uint8_t ledIndex, char *ledConfigBuffer, size_t bufferSiz
// timers // timers
uint32_t nextAnimationUpdateAt = 0; uint32_t nextAnimationUpdateAt = 0;
uint32_t nextIndicatorFlashAt = 0; uint32_t nextIndicatorFlashAt = 0;
uint32_t nextBatteryFlashAt = 0; uint32_t nextWarningFlashAt = 0;
#define LED_STRIP_20HZ ((1000 * 1000) / 20) #define LED_STRIP_20HZ ((1000 * 1000) / 20)
#define LED_STRIP_10HZ ((1000 * 1000) / 10) #define LED_STRIP_10HZ ((1000 * 1000) / 10)
@ -506,23 +511,45 @@ void applyLedModeLayer(void)
} }
} }
void applyLedLowBatteryLayer(uint8_t batteryFlashState) typedef enum {
WARNING_FLAG_NONE = 0,
WARNING_FLAG_LOW_BATTERY = (1 << 0),
WARNING_FLAG_FAILSAFE = (1 << 1)
} warningFlags_e;
void applyLedWarningLayer(uint8_t warningState, uint8_t warningFlags)
{ {
const ledConfig_t *ledConfig; const ledConfig_t *ledConfig;
static uint8_t warningFlashCounter = 0;
if (warningState) {
warningFlashCounter++;
warningFlashCounter = warningFlashCounter % 4;
}
uint8_t ledIndex; uint8_t ledIndex;
for (ledIndex = 0; ledIndex < ledCount; ledIndex++) { for (ledIndex = 0; ledIndex < ledCount; ledIndex++) {
ledConfig = &ledConfigs[ledIndex]; ledConfig = &ledConfigs[ledIndex];
if (!(ledConfig->flags & LED_FUNCTION_BATTERY)) { if (!(ledConfig->flags & LED_FUNCTION_WARNING)) {
continue; continue;
} }
if (batteryFlashState == 0) { if (warningState == 0) {
setLedColor(ledIndex, &red); if (warningFlashCounter == 0 && warningFlags & WARNING_FLAG_LOW_BATTERY) {
setLedColor(ledIndex, &red);
}
if (warningFlashCounter > 1 && warningFlags & WARNING_FLAG_FAILSAFE) {
setLedColor(ledIndex, &lightBlue);
}
} else { } else {
setLedColor(ledIndex, &black); if (warningFlashCounter == 0 && warningFlags & WARNING_FLAG_LOW_BATTERY) {
setLedColor(ledIndex, &black);
}
if (warningFlashCounter > 1 && warningFlags & WARNING_FLAG_FAILSAFE) {
setLedColor(ledIndex, &limeGreen);
}
} }
} }
} }
@ -625,15 +652,15 @@ void updateLedStrip(void)
bool animationUpdateNow = (int32_t)(now - nextAnimationUpdateAt) >= 0L; bool animationUpdateNow = (int32_t)(now - nextAnimationUpdateAt) >= 0L;
bool indicatorFlashNow = (int32_t)(now - nextIndicatorFlashAt) >= 0L; bool indicatorFlashNow = (int32_t)(now - nextIndicatorFlashAt) >= 0L;
bool batteryFlashNow = (int32_t)(now - nextBatteryFlashAt) >= 0L; bool warningFlashNow = (int32_t)(now - nextWarningFlashAt) >= 0L;
if (!(batteryFlashNow || indicatorFlashNow || animationUpdateNow)) { if (!(warningFlashNow || indicatorFlashNow || animationUpdateNow)) {
return; return;
} }
static uint8_t indicatorFlashState = 0; static uint8_t indicatorFlashState = 0;
static uint8_t batteryFlashState = 0; static uint8_t warningState = 0;
static bool batteryWarningEnabled = false; static uint8_t warningFlags;
// LAYER 1 // LAYER 1
@ -641,21 +668,27 @@ void updateLedStrip(void)
// LAYER 2 // LAYER 2
if (batteryFlashNow) { if (warningFlashNow) {
nextBatteryFlashAt = now + LED_STRIP_10HZ; nextWarningFlashAt = now + LED_STRIP_10HZ;
if (batteryFlashState == 0) { if (warningState == 0) {
batteryFlashState = 1; warningState = 1;
warningFlags = WARNING_FLAG_NONE;
if (feature(FEATURE_VBAT) && shouldSoundBatteryAlarm()) {
warningFlags |= WARNING_FLAG_LOW_BATTERY;
}
if (failsafe->vTable->hasTimerElapsed()) {
warningFlags |= WARNING_FLAG_FAILSAFE;
}
batteryWarningEnabled = feature(FEATURE_VBAT) && shouldSoundBatteryAlarm();
} else { } else {
batteryFlashState = 0; warningState = 0;
} }
} }
if (batteryWarningEnabled) { if (warningFlags) {
applyLedLowBatteryLayer(batteryFlashState); applyLedWarningLayer(warningState, warningFlags);
} }
// LAYER 3 // LAYER 3
@ -694,9 +727,10 @@ void applyDefaultLedStripConfig(ledConfig_t *ledConfigs)
reevalulateLedConfig(); reevalulateLedConfig();
} }
void ledStripInit(ledConfig_t *ledConfigsToUse) void ledStripInit(ledConfig_t *ledConfigsToUse, failsafe_t* failsafeToUse)
{ {
ledConfigs = ledConfigsToUse; ledConfigs = ledConfigsToUse;
failsafe = failsafeToUse;
reevalulateLedConfig(); reevalulateLedConfig();
} }
#endif #endif

View file

@ -41,7 +41,7 @@ typedef enum {
LED_DIRECTION_UP = (1 << 4), LED_DIRECTION_UP = (1 << 4),
LED_DIRECTION_DOWN = (1 << 5), LED_DIRECTION_DOWN = (1 << 5),
LED_FUNCTION_INDICATOR = (1 << 6), LED_FUNCTION_INDICATOR = (1 << 6),
LED_FUNCTION_BATTERY = (1 << 7), LED_FUNCTION_WARNING = (1 << 7),
LED_FUNCTION_FLIGHT_MODE = (1 << 8), LED_FUNCTION_FLIGHT_MODE = (1 << 8),
LED_FUNCTION_ARM_STATE = (1 << 9) LED_FUNCTION_ARM_STATE = (1 << 9)
} ledFlag_e; } ledFlag_e;

View file

@ -89,8 +89,7 @@ void gpsInit(serialConfig_t *serialConfig, gpsConfig_t *initialGpsConfig);
void navigationInit(gpsProfile_t *initialGpsProfile, pidProfile_t *pidProfile); void navigationInit(gpsProfile_t *initialGpsProfile, pidProfile_t *pidProfile);
bool sensorsAutodetect(sensorAlignmentConfig_t *sensorAlignmentConfig, uint16_t gyroLpf, uint8_t accHardwareToUse, int16_t magDeclinationFromConfig); bool sensorsAutodetect(sensorAlignmentConfig_t *sensorAlignmentConfig, uint16_t gyroLpf, uint8_t accHardwareToUse, int16_t magDeclinationFromConfig);
void imuInit(void); void imuInit(void);
void ledStripInit(ledConfig_t *ledConfigs); void ledStripInit(ledConfig_t *ledConfigsToUse, failsafe_t* failsafeToUse);
void loop(void); void loop(void);
// FIXME bad naming - this appears to be for some new board that hasn't been made available yet. // FIXME bad naming - this appears to be for some new board that hasn't been made available yet.
@ -238,7 +237,7 @@ void init(void)
#ifdef LED_STRIP #ifdef LED_STRIP
if (feature(FEATURE_LED_STRIP)) { if (feature(FEATURE_LED_STRIP)) {
ws2811LedStripInit(); ws2811LedStripInit();
ledStripInit(masterConfig.ledConfigs); ledStripInit(masterConfig.ledConfigs, failsafe);
} }
#endif #endif

View file

@ -15,6 +15,7 @@
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>. * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <limits.h> #include <limits.h>
@ -56,8 +57,8 @@ TEST(LedStripTest, parseLedStripConfig)
// given // given
static const ledConfig_t expectedLedStripConfig[WS2811_LED_STRIP_LENGTH] = { static const ledConfig_t expectedLedStripConfig[WS2811_LED_STRIP_LENGTH] = {
{ CALCULATE_LED_XY( 9, 9), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 9, 9), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY(10, 10), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY(10, 10), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY(11, 11), LED_DIRECTION_SOUTH | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY(11, 11), LED_DIRECTION_SOUTH | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY(11, 11), LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY(11, 11), LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY(10, 10), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY(10, 10), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE },
@ -70,10 +71,10 @@ TEST(LedStripTest, parseLedStripConfig)
{ CALCULATE_LED_XY(11, 1), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY(11, 1), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY(10, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY(10, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 7, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 7, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 6, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 6, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 5, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 5, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 4, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 4, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 2, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 2, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 1, 1), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 1, 1), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE },
@ -86,8 +87,8 @@ TEST(LedStripTest, parseLedStripConfig)
{ CALCULATE_LED_XY( 1, 10), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 1, 10), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 0, 11), LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 0, 11), LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 0, 11), LED_DIRECTION_SOUTH | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 0, 11), LED_DIRECTION_SOUTH | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 1, 10), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 1, 10), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 2, 9), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 2, 9), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 },
@ -100,8 +101,8 @@ TEST(LedStripTest, parseLedStripConfig)
// Spider quad // Spider quad
// right rear cluster // right rear cluster
"9,9:S:FB", "9,9:S:FW",
"10,10:S:FB", "10,10:S:FW",
"11,11:S:IA", "11,11:S:IA",
"11,11:E:IA", "11,11:E:IA",
"10,10:E:F", "10,10:E:F",
@ -116,10 +117,10 @@ TEST(LedStripTest, parseLedStripConfig)
"10,0:N:F", "10,0:N:F",
// center front cluster // center front cluster
"7,0:N:FB", "7,0:N:FW",
"6,0:N:FB", "6,0:N:FW",
"5,0:N:FB", "5,0:N:FW",
"4,0:N:FB", "4,0:N:FW",
// left front cluster // left front cluster
"2,0:N:F", "2,0:N:F",
@ -134,8 +135,8 @@ TEST(LedStripTest, parseLedStripConfig)
"1,10:W:F", "1,10:W:F",
"0,11:W:IA", "0,11:W:IA",
"0,11:S:IA", "0,11:S:IA",
"1,10:S:FB", "1,10:S:FW",
"2,9:S:FB" "2,9:S:FW"
}; };
// and // and
memset(&systemLedConfigs, 0, sizeof(systemLedConfigs)); memset(&systemLedConfigs, 0, sizeof(systemLedConfigs));
@ -182,13 +183,13 @@ TEST(LedStripTest, smallestGridWithCenter)
// and // and
static const ledConfig_t testLedConfigs[] = { static const ledConfig_t testLedConfigs[] = {
{ CALCULATE_LED_XY( 2, 2), LED_DIRECTION_SOUTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 2, 2), LED_DIRECTION_SOUTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 2, 1), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 2, 1), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 2, 0), LED_DIRECTION_NORTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 2, 0), LED_DIRECTION_NORTH | LED_DIRECTION_EAST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 1, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 1, 0), LED_DIRECTION_NORTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 0, 0), LED_DIRECTION_NORTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 0, 0), LED_DIRECTION_NORTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 0, 1), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 0, 1), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 0, 2), LED_DIRECTION_SOUTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE }, { CALCULATE_LED_XY( 0, 2), LED_DIRECTION_SOUTH | LED_DIRECTION_WEST | LED_FUNCTION_INDICATOR | LED_FUNCTION_ARM_STATE },
{ CALCULATE_LED_XY( 1, 2), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY } { CALCULATE_LED_XY( 1, 2), LED_DIRECTION_SOUTH | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING }
}; };
memcpy(&systemLedConfigs, &testLedConfigs, sizeof(testLedConfigs)); memcpy(&systemLedConfigs, &testLedConfigs, sizeof(testLedConfigs));
@ -250,9 +251,9 @@ TEST(LedStripTest, smallestGrid)
{ CALCULATE_LED_XY( 0, 11), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 0, 11), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 0, 10), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 0, 10), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 0, 9), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 0, 9), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 0, 8), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 0, 8), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 0, 7), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 0, 7), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 0, 6), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 0, 6), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 0, 5), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 0, 5), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 0, 4), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 0, 4), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 0, 3), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 0, 3), LED_DIRECTION_WEST | LED_FUNCTION_FLIGHT_MODE },
@ -270,9 +271,9 @@ TEST(LedStripTest, smallestGrid)
{ CALCULATE_LED_XY( 4, 3), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 4, 3), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 4, 4), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 4, 4), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 4, 5), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 4, 5), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 4, 6), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 4, 6), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 4, 7), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 4, 7), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 4, 8), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_BATTERY }, { CALCULATE_LED_XY( 4, 8), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE | LED_FUNCTION_WARNING },
{ CALCULATE_LED_XY( 4, 9), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 4, 9), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 4, 10), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 4, 10), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE },
{ CALCULATE_LED_XY( 4, 11), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE }, { CALCULATE_LED_XY( 4, 11), LED_DIRECTION_EAST | LED_FUNCTION_FLIGHT_MODE },