Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ec0fc2d
Allow interrupts on WS2811 etc on AVR without glitches
ben-xo Jul 29, 2021
28b48dd
Update to comments.
ben-xo Jul 30, 2021
aca96fc
Improve detail in comments about interrupts.
ben-xo Jul 30, 2021
5c21181
Add PixelRange struct type.
ben-xo Aug 2, 2021
90f6af7
Add show(pixelRange, scale) method signature and default implementation.
ben-xo Aug 2, 2021
cc41d74
Update showPixels(PixelRange) to match the clockless imiplementation
ben-xo Aug 2, 2021
bb5b7a6
specialised implementation of showPixels(PixelRange)
ben-xo Aug 2, 2021
edc0b41
Add PixelRange example.
ben-xo Aug 2, 2021
48e026c
Fix spaces/tabs faux pas
ben-xo Aug 2, 2021
7dc1574
A new example which shows 80 >>> >>> <<< <<< lights from 20 CRGB LEDs
ben-xo Jan 8, 2022
d48244f
Range scrolling example
ben-xo Jan 8, 2022
aa018f7
Make the comet go in the right direction
ben-xo Jan 8, 2022
fd084c6
remove redundant call in example
ben-xo Jan 8, 2022
55e259e
Change to 60 pixels to match the video on the PR
ben-xo Jan 9, 2022
c99155f
Merge branch 'release/ben-xo-integration-branch' into feature/pixel-r…
ben-xo Jan 9, 2022
23b5fa1
Better comet trail math
ben-xo Jan 9, 2022
1886159
Fix show() and delay() for dithering.
ben-xo Jan 9, 2022
3c8927e
Make example neater and add more explanatory comments.
ben-xo Jan 9, 2022
765c9c4
Add another explaner comment
ben-xo Jan 9, 2022
9b8edf7
Tinker with FPS and brightness again
ben-xo Jan 9, 2022
3ee793d
Rename PixelRange.ino to PixelRangeRandomiser.ino as it's more descri…
ben-xo Jan 9, 2022
f00f9f6
Update KnightRider example now i've fixed dither
ben-xo Jan 9, 2022
d5bd25c
KnightRider can have dither now
ben-xo Jan 9, 2022
74f7b7d
Fix some off by one bugs with this demo, use better constants, and Fa…
ben-xo Jan 9, 2022
a278d92
Fix some const stuff
ben-xo Jan 9, 2022
3e2a983
Add explanation to the scrolling comet
ben-xo Jan 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions examples/PixelRangeKnightRider/PixelRangeKnightRider.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#define FRAMES_PER_SECOND 30

#include <FastLED.h>

#define PIXEL_COUNT 15
#define STRIP_LENGTH (PIXEL_COUNT * 4)
#define NEOPIXEL_PIN 6

CRGB leds[PIXEL_COUNT];

PixelRange ranges[4] = { 0 };

// In this example, the ranges are static. They are used to display the same set of
// leds 4 times (twice forward, twice backward) on the same strip.

void setup() {
// put your setup code here, to run once:
pinMode(NEOPIXEL_PIN, OUTPUT);
FastLED.addLeds<NEOPIXEL, NEOPIXEL_PIN>(leds, STRIP_LENGTH).setCorrection(TypicalLEDStrip);
FastLED[0].setRangeList(ranges);

// set up 4 ranges over the same LEDs. two forward (repeated), two reverse (repeated)

// first, link the linked list
ranges[0].next = &ranges[1];
ranges[1].next = &ranges[2];
ranges[2].next = &ranges[3];
ranges[3].next = NULL;

// all ranges are the same length
ranges[0].length = PIXEL_COUNT;
ranges[1].length = PIXEL_COUNT;
ranges[2].length = PIXEL_COUNT;
ranges[3].length = PIXEL_COUNT;

// now, two forward
ranges[0].leds = &leds[0];
ranges[0].skip = 3;

ranges[1].leds = &leds[0];
ranges[1].skip = 3;

// finally, two reverse
ranges[2].leds = &leds[PIXEL_COUNT-1];
ranges[2].skip = -3;

ranges[3].leds = &leds[PIXEL_COUNT-1];
ranges[3].skip = -3;
}


void loop() {
static uint8_t current_position = 0;
static bool forward = true;

// fade every pixel
for (uint8_t i = 0; i < PIXEL_COUNT; i++) {
// fade
leds[i] >>= 1;
}
leds[current_position] = CRGB::White;

// bounce a dot from end to end of each range
if(forward) {
current_position++;
if(current_position == PIXEL_COUNT-1) {
// bounce
forward = false;
}
} else {
current_position--;
if(current_position == 0) {
// bounce
forward = true;
}
}

FastLED.show();
FastLED.delay(1000/FRAMES_PER_SECOND);
}
74 changes: 74 additions & 0 deletions examples/PixelRangeRandomiser/PixelRangeRandomiser.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#define FRAMES_PER_SECOND 30
#define FASTLED_ALLOW_INTERRUPTS 1

#include <FastLED.h>

#define NEOPIXEL_PIN 6
#define STRIP_LENGTH 60
#define RANGE_LENGTH 6
#define RANGE_COUNT 10

CRGB leds[STRIP_LENGTH];

PixelRange ranges[RANGE_COUNT] = { 0 };

// In this example, the strip is divided up into an equal number of equal sized ranges.
// Every few seconds, we then randomise which section of the strip the range actually
// points to (some parts may show multiple times, or not at all), and whether forward/reverse.

void randomise() {
uint8_t i = random8(RANGE_COUNT);
uint8_t j = random8(RANGE_COUNT);
if(random8(2)) {
ranges[i].leds = &leds[RANGE_LENGTH*j];
ranges[i].skip = 3;
} else {
ranges[i].leds = &leds[(RANGE_LENGTH*j)+RANGE_LENGTH-1];
ranges[i].skip = -3;
}
}

void setup() {

pinMode(NEOPIXEL_PIN, OUTPUT);

FastLED.addLeds<NEOPIXEL, NEOPIXEL_PIN>(leds, STRIP_LENGTH).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(255);

// link the linked list
for(uint8_t i = 0; i<RANGE_COUNT-1; i++) {
ranges[i].next = &ranges[i+1];
}
ranges[RANGE_COUNT-1].next = NULL;

// set them up to follow each other
for(uint8_t i = 0; i<RANGE_COUNT; i++) {
ranges[i].leds = &leds[RANGE_LENGTH*i];
ranges[i].length = RANGE_LENGTH;
ranges[i].skip = 3;
}

FastLED[0].setRangeList(&ranges[0]);
}

void loop() {
static uint32_t start = 0;
static uint8_t dot_idx = 0;

leds[dot_idx] = CRGB::Black;
dot_idx++;
if(dot_idx == STRIP_LENGTH) {
dot_idx = 0;
}
leds[dot_idx] = CRGB::White;

FastLED.show();
FastLED.delay(10);

uint32_t end = micros();
if(end - start > 2500000) {
start = end;
randomise();
}

}
115 changes: 115 additions & 0 deletions examples/PixelRangeScrollingComet/PixelRangeScrollingComet.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include <FastLED.h>

#define FRAMES_PER_SECOND 50

#define STRIP_LENGTH 60
#define COMET_LENGTH 16
#define NEOPIXEL_PIN 6

CRGB cometPixel[COMET_LENGTH];
CRGB spacePixel[1] = { CRGB::Black };

PixelRange left;
PixelRange middle;
PixelRange right;


// This demo shows a "comet" whizzing round and round the strip. The interesting this about this demo is how
// little RAM it uses: even though the strip is 60 pixels long (which would usually use 180 bytes just for the pixels),
// this sketch only requires around 150 bytes of ram in total: 16 * 3 for the comet's pixels, 3 for the black pixel (reused a lot)
// and then 6 bytes each for left, middle and right PixelRanges, and then of course all the other stuff such as the clock etc.

void setup() {
// put your setup code here, to run once:
pinMode(NEOPIXEL_PIN, OUTPUT);
FastLED.addLeds<NEOPIXEL, NEOPIXEL_PIN>(cometPixel, STRIP_LENGTH).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(192);

FastLED[0].setRangeList(&left);

// draw the comet.
for (uint8_t i = 0; i < COMET_LENGTH; i++) {
cometPixel[i] = CRGB::White;
cometPixel[i].fadeToBlackBy((i*256)/COMET_LENGTH);
}

// link the linked list
left.next = &middle;
middle.next = &right;
right.next = NULL;

}

void configureSplitSpace() {
// the comet is back to front, so we start at the end (middle.leds = the last pixel)
// and we draw it backwards (middle.skip = -3)

left.leds = &spacePixel[0];
left.length = 0;
left.skip = 0;

middle.leds = &cometPixel[COMET_LENGTH-1];
middle.length = COMET_LENGTH;
middle.skip = -3;

right.leds = &spacePixel[0];
right.length = STRIP_LENGTH - COMET_LENGTH;
right.skip = 0;
}

void configureSplitComet() {
left.leds = &cometPixel[COMET_LENGTH-1];
left.length = 0;
left.skip = -3;

middle.leds = &spacePixel[0];
middle.length = STRIP_LENGTH - COMET_LENGTH;
middle.skip = 0;

right.leds = &cometPixel[COMET_LENGTH-1];
right.length = COMET_LENGTH;
right.skip = -3;
}


void loop() {

// where on the strip the comet starts.
static uint16_t current_position = 0;

uint16_t comet_head = current_position;
uint16_t comet_tail = comet_head + COMET_LENGTH;

if(comet_head == 0) {
// The entire comet has emerged as one contiguous block of pixels, now that it's head is at 0.
// So, reconfigure linked ranges for split space (i.e. draw "space -> comet -> space")
configureSplitSpace();

} else if(comet_tail == STRIP_LENGTH - 1) {
// The tail of the comet has reached the end of the strip. Next frame, the comet will start to
// wrap to the other end of the strip. So, reconfigure linked ranges for split comet
// (i.e. draw "comet -> space -> comet")
configureSplitComet();

} else if(comet_tail >= STRIP_LENGTH) {
// in split comet
left.leds = &cometPixel[comet_tail - STRIP_LENGTH];
left.length++;
right.length--;

} else {
// in split space
left.length++;
right.length--;
}

FastLED.show();

current_position++;
if(current_position == STRIP_LENGTH) {
current_position = 0;
}

FastLED.delay(1000/FRAMES_PER_SECOND);

}
42 changes: 41 additions & 1 deletion src/controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class CLEDController {
protected:
friend class CFastLED;
CRGB *m_Data;
PixelRange *m_RangeList;
CLEDController *m_pNext;
CRGB m_ColorCorrection;
CRGB m_ColorTemperature;
Expand All @@ -59,6 +60,7 @@ class CLEDController {
///@param nLeds the number of leds being written out
///@param scale the rgb scaling to apply to each led before writing it out
virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0;
virtual void show(const struct PixelRange *rangeList, CRGB scale) = 0;

public:
/// create an led controller object, add it to the chain of controllers
Expand All @@ -80,14 +82,23 @@ class CLEDController {
show(data, nLeds, getAdjustment(brightness));
}

// show a linked list of pixel ranges on the same strip
void show(const struct PixelRange *rangeList, uint8_t brightness) {
show(rangeList, getAdjustment(brightness));
}

/// show function w/integer brightness, will scale for color correction and temperature
void showColor(const struct CRGB &data, int nLeds, uint8_t brightness) {
showColor(data, nLeds, getAdjustment(brightness));
}

/// show function using the "attached to this controller" led data
void showLeds(uint8_t brightness=255) {
show(m_Data, m_nLeds, getAdjustment(brightness));
if(m_RangeList) {
show(m_RangeList, getAdjustment(brightness));
} else {
show(m_Data, m_nLeds, getAdjustment(brightness));
}
}

/// show the given color on the led strip
Expand All @@ -107,6 +118,12 @@ class CLEDController {
return *this;
}

/// set the default RangeList to be used by this controller. Note that showLeds() will prefer this to the LED array if it is present at all
CLEDController & setRangeList(const struct PixelRange *data) {
m_RangeList = data;
return *this;
}

/// zero out the led data managed by this controller
void clearLedData() {
if(m_Data) {
Expand Down Expand Up @@ -390,6 +407,18 @@ template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF> class CPixelLE
protected:
virtual void showPixels(PixelController<RGB_ORDER,LANES,MASK> & pixels) = 0;

//// default implementation. For performance this is overriden by some implementations
virtual void showPixels(PixelController<RGB_ORDER,LANES,MASK> & pixels, const struct PixelRange *rangeList) {
PixelRange const *range = rangeList;
do {
pixels.mData = (uint8_t *)range->leds;
pixels.mLen = pixels.mLenRemaining = range->length;
pixels.mAdvance = range->skip;
showPixels(pixels);
range = range->next;
} while(range);
}

/// set all the leds on the controller to a given color
///@param data the crgb color to set the leds to
///@param nLeds the numner of leds to set to this color
Expand All @@ -412,6 +441,17 @@ template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF> class CPixelLE
showPixels(pixels);
}

/// Using a linked list of pixel ranges, render all of them in sequence on the same controller.
/// See notes on PixelRange in pixeltypes.h for examples of times you might want to use this (offsetting, scrolling, rotation).
/// *NOTE*: By default, FastLED enforces a minimum wait period for sketches that don't have their own code to wait from frame to frame.
/// Until all controllers override showPixels(&pixels, *rangeList), you may also need to define NO_MINIMUM_WAIT 1.
///@param rangeList linked list of pixel ranges to render in sequence on the strip.
///@param scale the rgb scaling to apply to each led before writing it out
virtual void show(const struct PixelRange *rangeList, CRGB scale) {
PixelController<RGB_ORDER, LANES, MASK> pixels((CRGB *)NULL, 0, scale, getDither());
showPixels(pixels, rangeList);
}

public:
CPixelLEDController() : CLEDController() {}
};
Expand Down
14 changes: 14 additions & 0 deletions src/pixeltypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,20 @@ enum EOrder {
BGR=0210
};


// A linked list of pointers to ranges of LEDs. Use this to display pixels in a different order from how they are arranged in RAM.
// For example:
// * using two of these, you can display the last half of the list first, then the first half of the list, to achieve a scrolling offset
// * using two of these, you can display a reflection that is twice as long as the amount of actual data by show the data and then showing the same data in reverse
// * with 10 of these, on a 10x10 grid, if you set the skip value to 10*sizeof(CRGB), you can rotate the display 90 degrees
struct PixelRange {
CRGB *leds;
PixelRange *next;
uint8_t length;
int8_t skip;
};


FASTLED_NAMESPACE_END
///@}

Expand Down
Loading