-
Notifications
You must be signed in to change notification settings - Fork 1
Tutorial
The best way to explain how to use the library is probably with a simple example, in this case a rectangular arrangement of an LED strip which could be used for an infinity mirror or illuminated picture frame.
It consists of a single LED strip (44 LEDs total), divided into 4 segments (of length 8 and 14, one for each edge of the rectangle). The LED strip is controlled by an Arduino (the single wire shown represents the data line, the two power wires are omitted).
You might want to define a collection of animation patterns which can be mapped to and possibly duplicated across each LED segment in a flexible way. That's what this library helps you do.
The first step is to define each segment (corresponding to each edge of the rectangle) in terms of their position on the LED strip. This is done by creating instances of StripSegment
and specifying the segment start position, number of LEDS in the segment and total number of LEDs on the strip (see StripSegment reference for more details). The segments are numbered 1 to 4, starting with the vertical one on the right and going counter-clockwise.
#include <FastLED.h>
#include <LEDuino.h>
#define VERTICAL_SEGMENT_LEN 8 // Number of LEDS on vertical segments
#define HORIZONTAL_SEGMENT_LEN 14 // Number of LEDS on horizontal segments
#define TOTAL_LEDS VERTICAL_SEGMENT_LEN*2 + HORIZONTAL_SEGMENT_LEN*2
StripSegment segment_1(0, VERTICAL_SEGMENT_LEN, TOTAL_LEDS);
StripSegment segment_2(VERTICAL_SEGMENT_LEN, HORIZONTAL_SEGMENT_LEN, TOTAL_LEDS);
StripSegment segment_3(VERTICAL_SEGMENT_LEN + HORIZONTAL_SEGMENT_LEN, VERTICAL_SEGMENT_LEN, TOTAL_LEDS);
StripSegment segment_4(2*VERTICAL_SEGMENT_LEN + HORIZONTAL_SEGMENT_LEN, HORIZONTAL_SEGMENT_LEN, TOTAL_LEDS);
Next we need some patterns to apply to the LED segments. A Pattern defines animation logic which updates every frame, and needs to be able to return the RGB value for a given position along the pattern axis (or a given point in 3D space for SpatialPatterns
). A simple example of a LinearPattern
would be a pulse of light moving along a linear axis. The pattern would need to keep track of the position of the pulse and update it with each new frame. It would also need to be able to determine the RGB value for each position along the axis, perhaps based on how far away the position is from the pulse's position.
See Pattern reference for more details on how patterns work and how to create your own. There are also a bunch of pre-made patterns included, some of which will be used here:
-
MovingPulse
- Colourful pulse with bright head and tapering tail, which moves along the pattern axis. Can be configured with pulse width, speed and colour palette -
RandomRainbows
- Moving rainbow patterns using sine wave function, with randomly changing width, speed and colour. -
Twinkle
- Pleasant golden twinkling lights fading in and out. Adapted from [Mark Kriegsman's example] (https://gist.github.com/kriegsman/756ea6dcae8e30845b5a) Can be configured with twinkle speed and custom colour palette.
MovingPulsePattern pulse_pattern(5);
RandomRainbowsPattern random_rainbows;
TwinklePattern twinkle_pattern(5);
The next step is to define mappings of the Patterns
to the collection of StripSegments
. For linear patterns this is done using a LinearPatternMapper
, providing the following parameters:
- Pattern instance
- Pixel array used by pattern to store its output pixel data for each frame
- Length of pixel array (pattern resolution)
- Array of
StripSegments
to map pattern to - Length of
StripSegment
array
The pattern mapping will handle interpolation of the pattern from its 'virtual pixel' resolution to the actual number of LEDS in each strip segment (see Mapping Concepts for more details).
// Define array of StripSegments
#define NUM_SEGMENTS 4
StripSegment all_segments[NUM_SEGMENTS] = {segment_1, segment_2, segment_3, segment_4};
// Define pixel data array for patterns to use
#define NUM_PIXELS HORIZONTAL_SEGMENT_LEN
CRGB pixel_data[NUM_PIXELS];
// Define PatternMappings
LinearPatternMapping pulse_pattern_mapping(pulse_pattern, pixel_data, NUM_PIXELS, all_segments, NUM_SEGMENTS);
LinearPatternMapping twinkle_pattern_mapping(twinkle_pattern, pixel_data, NUM_PIXELS, all_segments, NUM_SEGMENTS);
LinearPatternMapping rainbow_pattern_mapping(random_rainbows, pixel_data, NUM_PIXELS, all_segments, NUM_SEGMENTS);
- Frame delay for pattern (number of ms between frames)
- Name for pattern configuration (optional)
- Duration of pattern configuration (in seconds, defaults to 15)
Finally, the LEDuinoController
ties everything together by handling execution and cycling through the defined PatternMappings
, and updating the state of the LED strip. It must be initialised with:
- CRGB array representing state of full LED strip (used by FastLED)
- Total number of LEDs in strip
- Array of
MappingRunner
s - Number of pattern mappings
- Whether or not to randomise pattern order (optional boolean, default true)
A MappingRunner
is used to set the frame rate and duration of a pattern mapping.
// Define array containing state of full LED strip
CRGB leds[TOTAL_LEDS];
// Define array of pattern mapping pointers
#define NUM_MAPPINGS 3
MappingRunner mappings[NUM_MAPPINGS] = {
MappingRunner(pulse_pattern_mapping, 15), // 15ms between frames (~66 FPS)
MappingRunner(twinkle_pattern_mapping, 33, 15), // 33ms between frames (~30 FPS) and 15 second duration
MappingRunner(rainbow_pattern_mapping) // Uses defaults (20ms between frames and duration of 10s)
};
// Define controller
LEDuinoController controller(leds, TOTAL_LEDS, mappings, NUM_MAPPINGS, true);
Now you just need to initialise FastLED and the LEDuinoController
in setup()
, and run it within loop()
.
#define LED_DATA_PIN 5
void setup() {
// Configure FastLED max power usage (optional)
FastLED.setMaxPowerInVoltsAndMilliamps(5, 5000);
// Register LED array with FastLED
FastLED.addLeds<NEOPIXEL, LED_DATA_PIN>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
// Initialise controller
controller.initialise();
}
void loop() {
// Run controller (animates pattern and updates LED state)
controller.loop();
// Any other project activity (read inputs, etc)
}
The controller will only process a new pattern frame and update LED state every frame_delay of the current pattern mapping. The rest of the project loop() function should be kept as short-running as possible.
That's it! Here's a video of this simple example applied to my Infinity Cube (Segment lengths were adjusted to LED length of cube edges):
This tutorial covers a basic linear pattern example, but there are more cool features to explore like Spatial Patterns and Multiple Pattern Mappings. Check out the Reference page for more in-depth details and also some more Examples.