Based on a request by James, here two examples of a Candy Cane effect.
Both effects use shifting of LED positions (move each LED color over one position and add a new one at the beginning of the strand).
The first one is the simplest, but you'll have to make sure the the block width * 3 divides nicely with your LED count.
The reason for this is that the last LED will be used to color the first new LED.
So for example with 60 LEDs, we'd like to make 3 blocks of 10, or 3 blocks of 20, which makes it that the last LED indeed is the correct color for the new first LED.
With both you can define your own colors (3), block width, and speed (WaveDelay higher = slower scrolling).
The colors can be predefined FastLED colors, for example CRGB::Black, or a user defined color, for example CRGB(0,0,0).
#include "FastLED.h"
#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
#define PIN 6
#define NumberOfColors 3
void setup()
{
FastLED.addLeds< WS2811, PIN, GRB >(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
}
void loop() {
CandyCaneSimple(CRGB::Red, CRGB::White, CRGB::Blue, 10, 100);
}
void CandyCaneSimple(CRGB Color1, CRGB Color2, CRGB Color3, int BlockWidth, int WaveDelay) {
int LedPosition;
CRGB lastLedColor;
if(Instant) {
// Fill initial pattern
for(int i=0; i < NUM_LEDS; i=i + (NumberOfColors*BlockWidth) )
{
for (int ColorCounter=0; ColorCounter < NumberOfColors; ColorCounter++)
{
for(int BlockLEDCounter=0; BlockLEDCounter < BlockWidth; BlockLEDCounter++)
{
LedPosition = i + (ColorCounter*BlockWidth) + BlockLEDCounter;
if( LedPosition < NUM_LEDS) {
switch (ColorCounter)
{
case 0: leds[LedPosition] = Color1; break;
case 1: leds[LedPosition] = Color2; break;
case 2: leds[LedPosition] = Color3; break;
}
}
}
}
}
}
else
{
// Black for all LEDs
fill_solid(leds,NUM_LEDS,CRGB::Black);
}
FastLED.show();
delay(5000);
// Scrolling bars
while(true)
{
lastLedColor = leds[NUM_LEDS-1];
memmove( &leds[1], &leds[0], (NUM_LEDS-1) * sizeof(CRGB) );
leds[0] = lastLedColor;
FastLED.show();
delay(WaveDelay);
}
}
The second one allows for a little more flexibility, since it will determine what the new first LED should be, and it allows for a slow start (Instant = false) where the color bar slowly build up.
If instant = true then the entire strand is first filled with blocks and after that is starts scrolling.
#include "FastLED.h"
#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
#define PIN 6
void setup()
{
FastLED.addLeds < WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
}
void loop() {
//CandyCane(CRGB::Blue, CRGB::White, CRGB::Red, 6, 100, false);
CandyCane(CRGB::Blue, CRGB::White, CRGB::Red, 8, 100, true);
}
void CandyCane(CRGB Color1, CRGB Color2, CRGB Color3, int BlockWidth, int WaveDelay, bool Instant) {
#define NumberOfColors 3
int BlockLEDCounter;
int ColorCounter;
int LedPosition;
if(Instant) {
// Fill initial pattern
for(int i=0; i < NUM_LEDS; i=i + (NumberOfColors*BlockWidth) )
{
for (ColorCounter=0; ColorCounter < NumberOfColors; ColorCounter++)
{
for(BlockLEDCounter=0; BlockLEDCounter < BlockWidth; BlockLEDCounter++)
{
LedPosition = i + (ColorCounter*BlockWidth) + BlockLEDCounter;
if( LedPosition < NUM_LEDS) {
switch (ColorCounter)
{
case 0: leds[LedPosition] = Color1; break;
case 1: leds[LedPosition] = Color2; break;
case 2: leds[LedPosition] = Color3; break;
}
}
}
}
}
}
else
{
// Black for all LEDs
fill_solid(leds,NUM_LEDS,CRGB::Black);
}
FastLED.show();
// Scrolling bars
BlockLEDCounter = 0;
ColorCounter = 0;
while(true)
{
memmove( &leds[1], &leds[0], (NUM_LEDS-1) * sizeof(CRGB) );
switch (ColorCounter)
{
case 0: leds[0] = Color3; break;
case 1: leds[0] = Color2; break;
case 2: leds[0] = Color1; break;
}
FastLED.show();
BlockLEDCounter++;
if (BlockLEDCounter==BlockWidth)
{
BlockLEDCounter = 0;
ColorCounter++;
if (ColorCounter==NumberOfColors) {
ColorCounter = 0;
}
}
delay(WaveDelay);
}
}