Intro
Welcome to my attempt to contribute to the amazing FastLED library with some tutorials and
explanations. The library is growing quickly, and I've seem some posts lately asking about
more documentation/examples so I thought I'd take a stab at it. Honestly, I'm probaby
the completely wrong person for the job, as I just started using it myself… but why
not?! I've found that one of the best ways to learn something is to teach it.
I only have WS2812B LEDs and an Arduino, so anything I write will use them and I won't be any help on other platforms/strip types. If you have questions, I'd highly recommend joining the FastLED Users Google+ community. They're helpful, fun, and it's a great place to see peoples' projects and learn more.
Note: I really do mean I'm an extreme novice, so please comment if I got something wrong or you think my wording/explanations could be better.
Alright, here goes…
I only have WS2812B LEDs and an Arduino, so anything I write will use them and I won't be any help on other platforms/strip types. If you have questions, I'd highly recommend joining the FastLED Users Google+ community. They're helpful, fun, and it's a great place to see peoples' projects and learn more.
Note: I really do mean I'm an extreme novice, so please comment if I got something wrong or you think my wording/explanations could be better.
Alright, here goes…
Setup
To get started, you'll need an LED strip and a microcontroller of some sort. I'm using an
Arduino Uno and a strip of WS2812B LEDS. I used some 12V 5050 RGB strips in a
previous project and these same WS2812B strips for a more recent one. I vastly prefer the
latter, as they only require 5V (no MOSFETs required) and one data line (vs. one per
channel, so 3 for a 12V strip). Also, the WS2812Bs are individually addressable, so any
pixel can be any color. While I'm sure there's people using FastLED for these, that seems
like a fringe use for such a capable library.
In any case, here's my setup:
Pretty simple. I just soldered wires to the three connections on the LED strip (make sure you get the Din side!) and connected them to the Arduino. Per the Adafruit guide, I also soldered a 1000 uF capacitor between the 5V and GND pads (negative lead on the GND side) and have a resistor between pin 3 and the data wire (green in my setup). I haven't had any bad experiences not using this, but I'm trying to follow best practices here :)
That's it from the hardware side, at least for this demo. You can do all sorts of things like dimentional matrices, but we'll just stick to a strip for now. Moving on to code, might as well start by piggy-backing on FastLEDs own wiki.
To get going, I'd recommend using
Now you can create a new
That code will initialize your FastLED object. You're defining how many pixels you have (let's call this
In any case, here's my setup:
Pretty simple. I just soldered wires to the three connections on the LED strip (make sure you get the Din side!) and connected them to the Arduino. Per the Adafruit guide, I also soldered a 1000 uF capacitor between the 5V and GND pads (negative lead on the GND side) and have a resistor between pin 3 and the data wire (green in my setup). I haven't had any bad experiences not using this, but I'm trying to follow best practices here :)
That's it from the hardware side, at least for this demo. You can do all sorts of things like dimentional matrices, but we'll just stick to a strip for now. Moving on to code, might as well start by piggy-backing on FastLEDs own wiki.
To get going, I'd recommend using
git
to grab the v3.1 version of FastLED. It appears
that this branch is going to receive the abundance of new features, and it already
contains a lot of neat stuff not in the v2.x variants (maybe even 3.0?). If you're not
familiar with git
, it might be easiest to click "Download ZIP" from the FastLED 3.1
github page and extract it into your Arduino library folder (My
Documents\Arduino\Libraries on Windows). I use Linux, so my process is to open a terminal
and do:
$ cd ~/Arduino/libraries $ git clone https://github.com/FastLED/FastLED.git
Now you can create a new
.ino
file with your IDE of choice (I've really been loving
Sublime Text) and insert the starter code from the FastLED wiki:
#include "FastLED.h" // fast led constants #define DATA_PIN 3 // change to your data pin #define COLOR_ORDER GRB // if colors are mismatched; change this #define NUM_LEDS 28 // change to the number of LEDs in your strip // change WS2812B to match your type of LED, if different // list of supported types is here: // https://github.com/FastLED/FastLED/wiki/Overview #define LED_TYPE WS2812B // this creates an LED array to hold the values for each led in your strip CRGB leds[NUM_LEDS]; void setup() { // the wiki features a much more basic setup line: FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS); }
That code will initialize your FastLED object. You're defining how many pixels you have (let's call this
n
), what pin to send the data over, creating an array n
long so you
can store each led's color values in it, and then telling FastLED about said array for
future control.
Doing stuff
The heart of any Arduino sketch happens in the
Each "slot" (also called an index) contains the values for that LED. There are 10 of them, but array numbering starts with 0, so things are always 1 "off." Since we initialized this array to store the values as
On that note, we can go to the first example! To set an RGB color on an LED, we need to set the data in it's slot in the array to the color we want. FastLED has a ton of pre-defined RGB colors to choose from, accessed by
If you run that… nothing happens! The above is simply storing a color value in the 0th slot of the
So, our
Run the sketch and start your serial monitor. You'll see these lines spitting out:
It works! We set the red value of
Cool:
Well, that was fun… how do I stop that light from shining in my eyes? You need to set the value of
The fun is all in the changing of pixels. You can set one on, and turn it off, so the natural next step is to do that again and again:
How about moving the pixel? You're familiar with the idea of an array, so what we want is something like this, shown as snapshots in time, with the
How might you do that? To do this, we need to set the 0th pixel to some color, show it, then turn it off and set the 1st pixel to a color, turn it off, etc. Using the excellent wiki again:
If you're not familiar with loops, that might seem a bit confusing. A
Walking through the loop step by step we find:
Give it a try and hopefully you find that movement satisfying!
The dot keeps moving because once the
I find this a bit hokey since you have to tweak
Can you see it? You can use math inside of the index brackets (
Pretty neat! I think I'll call it good for now, and hope to continue on writing about using FastLED in the near future.
loop()
function, which runs whatever you
tell it over and over. The wiki already explains some basic functions, but let's go
through them anyway. The first thing to understand is that your LED strip is treated as an
array. Maybe you're more familiar with the term "vector" from another language, or can
visualize a single row of cells in a spreadsheet. Your LEDs are treated like this (10 LED strip):
|---+---+---+---+---+---+---+---+---+---| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |---+---+---+---+---+---+---+---+---+---|
Each "slot" (also called an index) contains the values for that LED. There are 10 of them, but array numbering starts with 0, so things are always 1 "off." Since we initialized this array to store the values as
leds[NUM_LEDS]
, this means you created an array with NUM_LEDS
"slots" (28 in
my case if you check the code above). If you've not worked with arrays before, it will
really help to keep the above in mind as we play around with the library.
On that note, we can go to the first example! To set an RGB color on an LED, we need to set the data in it's slot in the array to the color we want. FastLED has a ton of pre-defined RGB colors to choose from, accessed by
CRGB::color_name
. We'll set the
first LED to red like so:
void loop() { leds[0] = CRGB::Red; Serial.println(leds[0]); }
If you run that… nothing happens! The above is simply storing a color value in the 0th slot of the
leds
array. From skipping ahead on the Wiki, we can see that each value in
the leds
array contains values for red, green, and blue:
CRGB has three one-byte data members, each representing one of the three red,
green, and blue color channels of the color. There is more than one way to
access the RGB data; each of these following examples does exactly the same thing: // The three color channel values can be referred to as
// "red", "green", and "blue"... leds[i].red = 255; leds[i].green = 0; leds[i].blue = 0; // ...or, using the shorter synonyms "r", "g", and "b"... leds[i].r = 255; leds[i].g = 0; leds[i].b = 0; // ...or as members of a three-element array: leds[i][0] = 255; // red leds[i][1] = 0; // green leds[i][2] = 0; // blue
So, our
leds
array is actually a multidimensional storage system for the color channels
of each pixel. Can we test this? We can initialize a serial connection to the Arduino so we can print out values as we play
around:void setup() { // keep whatever else you have, and add: Serial.begin(9600); }Then, let's modify our example above like so:
void loop() { leds[0] = CRGB::Red; Serial.print("red = "); Serial.print(leds[0].r); Serial.print(", blue = "); Serial.print(leds[0].g); Serial.print(", blue = "); Serial.println(leds[0].b); delay(500); }
Run the sketch and start your serial monitor. You'll see these lines spitting out:
red = 255, blue = 0, blue = 0 red = 255, blue = 0, blue = 0 red = 255, blue = 0, blue = 0
It works! We set the red value of
leds[0]
to 255 (100%, as each color can take a range of
0-255). Now, to send that data to the strip and show the result, you add
FastLED.show()
to the code. This takes the current values of leds
(all NUM_LEDS
slots of data) and sends it out to the strip. Since we didn't touch the other array
values, only the first led will do anything.
void loop() { leds[0] = CRGB::Red; FastLED.show(); delay(500); }
Cool:
Well, that was fun… how do I stop that light from shining in my eyes? You need to set the value of
leds[0]
back to 0. Technically, we only changed leds[0].r
, but the way
shown in the wiki to turn off an LED is to set it to black:
void loop() { leds[0] = CRGB::Black; FastLED.show(); delay(500); }
The fun is all in the changing of pixels. You can set one on, and turn it off, so the natural next step is to do that again and again:
void loop() { leds[0] = CRGB::Red; FastLED.show(); delay(500); leds[0] = CRGB::Black; FastLED.show(); delay(500); }
How about moving the pixel? You're familiar with the idea of an array, so what we want is something like this, shown as snapshots in time, with the
*
representing the pixel
that's on:
|---+---+---+---+---| | * | | | | | |---+---+---+---+---| |---+---+---+---+---| | | * | | | | |---+---+---+---+---| |---+---+---+---+---| | | | * | | | |---+---+---+---+---| |---+---+---+---+---| | | | | * | | |---+---+---+---+---| |---+---+---+---+---| | | | | | * | |---+---+---+---+---|
How might you do that? To do this, we need to set the 0th pixel to some color, show it, then turn it off and set the 1st pixel to a color, turn it off, etc. Using the excellent wiki again:
void loop() { for(int dot = 0; dot < NUM_LEDS; dot++) { leds[dot] = CRGB::Blue; FastLED.show(); // clear this led for the next time around the loop leds[dot] = CRGB::Black; delay(30); } }
If you're not familiar with loops, that might seem a bit confusing. A
for()
loop focuses
on some variable specific to the loop (dot
in this case), does something while it's in a
certain range of values (less than the number of LEDs we have), and does something after
each time it runs (dot++
means, "add one to the current value of dot
each time).
Walking through the loop step by step we find:
dot
is initialized to 0 in the loop definition- set
leds[0]
to blue - push the data (turn on the first pixel)
- set
leds[0]
to black (but note that the data isn't pushed out yet) - wait 30 milliseconds
dot++
is now executed, sodot
becomes 1- set
leds[1]
to blue - push the data (the first pixel was set to black the last time, so only the second pixel will show as blue)
- set the second pixel to black, wait 30 ms, add 1 to
dot
for()
loop will stop as soon as the condition dot < NUM_LEDS
is not met. In my
case, this means as long as dot
is less than 28, it will run. Arrays starting at 0 is
handy in this case since the values are 0-27. Thus, once the loop runs for dot
equal to
27, one will be added and the loop will exit.
Give it a try and hopefully you find that movement satisfying!
The dot keeps moving because once the
for()
loop is done, you're also inside of the main
loop()
function, so the for()
loop just starts again. The variables in a for()
loop
are temporary, so they just get re-initialized each time as dot = 0
. What if you wanted
the dot to come back? Think through what you'd want to have happen, perhaps starting with
the end of the loop we used above:
- the 27th pixel will be blue
- you want to turn on the 26th pixel instead of starting back over at 0, and turn off the 27th pixel
- and so on
for(int dot = NUM_LEDS - 1; dot > -1; dot--)
I find this a bit hokey since you have to tweak
dot
to odd values. You've defined some
nice constants, and the array starts at the wonderful number, zero, but to go in reverse
one has to fiddle. Can we use dot
just as it is, increasing from zero -> 27? As you
think about arrays, sometimes it can be helpful to lay out what your loop variable is
compared to what value you want to change:
| dot: | 0 | 1 | 2 | ... | 26 | 27 | |--------+----+----+----+-----+----+----| | array: | 27 | 26 | 25 | ... | 1 | 0 |
Can you see it? You can use math inside of the index brackets (
[ ]
) when setting values,
so what if we wrote the return loop like this:
void loop() { for(int dot = 0; dot < NUM_LEDS; dot++) { leds[dot] = CRGB::Blue; FastLED.show(); // clear this led for the next time around the loop leds[dot] = CRGB::Black; delay(30); } for(int dot = 0; dot < NUM_LEDS; dot++) { leds[NUM_LEDS - dot] = CRGB::Blue; FastLED.show(); // clear this led for the next time around the loop leds[NUM_LEDS - dot] = CRGB::Black; delay(30); } }
Pretty neat! I think I'll call it good for now, and hope to continue on writing about using FastLED in the near future.
thanks for posting this - really helpful primer - much appreciated!
ReplyDeleteHi jwhendy,
ReplyDeleteNice tutorial. I would however recommend using an external power supply to power the LED strip. Lighting up one LED on the strip at a time is ok using your setup, however, if you accidentally miscode something and light them all up, you run the risk of damaging the Arduino. Otherwise, great tutorial !!
Thanks, Scott. Yes, I've heard this quite a bit and maybe I'm lucky as I've never run into the issue. I presume that trying to push > 500mA would fry something on the Arduino? Would this be due to temperature or something else?
DeleteI guess theoretically 28 leds on full white would try to draw 1.6A, so that'd definitely be over the limit.
Thanks for pointing this out!
This page has some good information on the topic:
Deletehttp://electronics.stackexchange.com/questions/67092/how-much-current-can-i-draw-from-the-arduinos-pins.
This one is also quite good:
http://apcmag.com/how-to-power-your-arduino-projects.htm/
I hope they help !!
Been tinkering around with neopixels for a while now and just found out about the Fast LED libary. This post got me up and running with it in a glitch! I will be doing the follow-up post tomorrow. Thanx a million times for this crystal clear walkthrough!!
ReplyDeleteYay! Happy to help, though I hope you meant "without a glitch" :) If you have other requests, feel free to send them my way. I fizzled out on this a bit but hoped to make more tutorials/posts along these lines as sometimes the FastLED library moves quicker than the documentation (something the creators acknowledge, but they're just too strapped for time at the moment).
Deletehow do i set all LEDs to one color?
ReplyDeleteDefinitely more than one way, but using the final loop as an example, we're setting a single pixel a color, showing it, turning it black, delaying, and going on to the next pixel. Imagine we don't set it black... What might happen? (Put a comment symbol, //, in front of anything that sets a pixel black in the code above to try it.)
DeleteAlso, Google "fill_solid fastled" for an easier way.
Not trying to make more work for you, but then again as you might have figured my posts are aiming to boost understanding and comprehension, not just deliver a canned solution.
Hope the above points you in the right direction!
Thank you so much for this tutorial! I'm going to order all the needed parts today and aim to create an Ambilight clone. A lot of good ideas but I never worked with Arduino so wish me good luck :-)
ReplyDeleteGood luck, then! Please do post on the FastLED Google+ community if you haven't been there. I'm sure you've seen a whole bunch of the instructions on ambilight clones so hopefully you're set! I don't have a TV at the moment, but have always thought it looked like an awesome project (I'd do it if I had one!).
DeleteHoly guacamole this was so helpful! Thank you!! This is one of the first posts I’ve seen that fully explains how you’re executing the LED change patterns. Thank you thank you!
ReplyDelete@Lindsey: Glad it helped! There's some requests on the other site to do more of these. I'm open to suggestions if you want to add to the queue. No idea on timing... kind of into my RPi at the moment and learning python :) Good luck!
DeleteHi jwhendy.Thank you for the awesome guide.I am completely new to this but could you explain how you could add more leds to this i.e say 5 lit at a time ?
ReplyDeleteThank you.
Hi Michael: sorry for the delay! I just got an email from blogger asking if I still wanted to be subscribed to new comments. That was news, as I didn't realize I wasn't!
DeleteSo, to do 5 at a time, you'd be looking at specifying them individually in the loop, say:
leds[dot] = ...
leds[dot+1] = ...
leds[dot+2] = ...
Of course, you'd have to track how many you can do that for and handle the end cases (like stopping at NUM_LEDS - how_many_you_want_lit so you don't try to set ones that don't exist).
Another way would be to look into convenience functions like fill_solid():
- http://fastled.io/docs/3.1/group___colorutils.html#gac84d8820fd175567ba66413dfb8bc63b
Alternatively, fill_rainbow or fill_gradient could also work. It's actually been a while since I tinkered with FastLED so I don't recall the exact syntax for fill_solid and starting at a given pixel. I see that rainbow and gradient take start/end pixels, so if you use the same start/end color, it would be equivalent to just filling n LEDs starting at x, ending at y, with the same color.
You've likely solved this by now, but I wanted to comment anyway!
Hi jwhendy: It is ok and thank you for your reply.No I have not sorted it yet but I will try to use your information.Also if it is not too much trouble how would I go about getting the led/leds to start at say one second intervals i.e multiple leds chasing at the same time.
DeleteThank you again for your help.It is very much appreciated.
Good luck to you. Interesting question about the timing... I can think of a couple ways. The simplest would seem to be if you have 1) a rate at which all LEDs advance forward on the strip (mov/sec), and 2) a delay, which is a multiple of that rate at which point we release a new light. I would look into the documentation on EVERY_N_MILLISECONDS(t) and how to copy the array, but offset by 1. I envision nested EVERY_N_MILLISECONDS() blocks where the first copies the strip one slot forward periodically, and if it's been enough time or enough cycles, we also turn on LEDS[0].
DeleteAnother way, if you know how many LEDS will be on the strip at a given time, you could maybe store arrays of the ith LED position and even a custom rate. So this could simulate releasing LEDs at some delay with random speeds; some might be faster/slower than others. More complicated for sure, but also seems reasonable.
DeleteHi jwhendy.Thank you again for your reply but you have lost me entirely.Like I said I know next to nothing about programming arduino.I do have two books arriving in a couple of days so hopefully I will learn something then this will make sense to me.I really appreciate your time and effort.
DeleteNo worries, and there's a steep learning curve for sure! Once you get setup, here's the gist of what I meant:
Delete- https://gist.github.com/jwhendy/8a18915f2d5658264ed98aad73d3bb2d
EVERY_N_MILLISECONDS just runs your code, well, every n milliseconds. In the example, we see if it's been 1000 milliseconds (1 sec). If it has, we move all LED values forward one pixel ("moving" them toward the far end of the strip). If it's been 3 seconds, we set leds[0] (first pixel) to white. It's like initiating a new one to start walking. A second later, in the for loop when i=1, it will set the second pixel, leds[1] to the value of leds[i-1], or white, which the that first pixel currently contains.
When we're all done copying pixels, we make the first pixel black (off). If it's been *three* seconds, that first pixel will be made white again before we runs FastLED.show().
By playing with the times in those blocks, you could futz with how fast a new light is created vs. how fast they move. You could also play with random functions to get a random update time or speed per light.
love that the internet will show me things from 2015 for things in 2019! I have a question: What if I just want to set certain leds within the array to certain colors? and have them stay those colors? I found this pacman tutorial (https://create.arduino.cc/projecthub/pix3lot/pac-man-led-pixel-panel-costume-515666?ref=tag&ref_id=led&offset=10) and yours, so i understand a bit more, but i'm not any closer to understanding how to address a single led in the array directly. I get that i can define sections in the setup, but not sure how that translates to the loop. thanks for any help you're able to offer!
ReplyDeleteactually nevermind! I figured it out with this arduino help forum post: https://forum.arduino.cc/index.php?topic=431000.0
DeleteGlad you found the answer, and sorry for missing this! I just got a notification for a different question below, but somehow didn't see this one. Glad you found a path forward and LED pacman sounds awesome :)
DeleteI had a question: Is there anyway to change the color of the LED (doing so within one of the loops - or would I need a separate loop?) gradually as it traverses down the strip? (this is worded poorly lol I know the LED is changing location.)
ReplyDeleteSure! With code, all things are possible :) Granted, I don't have a test setup in front of me, so this will be "pseudocode" you'll need to check. The idea should get you looking in the right direction, though.
DeleteWhen we set the color in the above, we're doing:
leds[dot] = CRGB::Blue;
You're looking to map this to color. I'd check out FastLED pages on CHSV [1], and the Arduino map() function [2]. map() is unbelievably useful; you can scale one set of values to another range. So, if our strip is 28 pixels long and we want to map that to the color spectrum. Using CHSV is awesome as you don't need to know separate RGB values, you just range from 0-255 and sweep through the spectrum. So, you might set the color like this instead:
// CHSV(hue, saturation, brightness)
leds[dot] = CHSV(map(dot, 0, NUM_LEDS-1, 0, 255), 255, 255);
As our dot value ranges from 0 to our highest pixel index, the output value (hue) will range from 0-255. Now as it sweeps the strip it will change from red through all the other colors and back to red. If you only want to sweep one part of the spectrum, you can always cap the input or output range:
// add min_col, so if dot=0, the color shown will be min_col
// the last pixel will be max_col vs. going up to 255
leds[dot] = CHSV(map(dot, 0, NUM_LEDS-1, min_col, max_col), 255, 255);
Hope that helps! You might also want to check out constrain(), which is nice when you want some range of min-max, but the scaling of map() might exceed that. So maybe the last 5 LEDs of your strip should all be at hue=255. You might do something like this:
// dropping input range to NUM_LEDS-5
leds[dot] = CHSV(map(dot, 0, NUM_LEDS-5, min_col, max_col), 255, 255);
However map() doesn't know it should have limits. We've said the highest input value to use for scaling will be NUM_LEDS-5, but we know that dot's max value is actually NUM_LEDS-1, so for those final pixels, the output will be higher than max_col. To control this use, constrain.
// now if the output of map is below min_col or above max_col,
// those are returned instead.
int hue = constrain(map(dot, 0, NUM_LEDS-5, min_col, max_col), min_col, max_col);
leds[dot] = CHSV(hue, 255, 255);
Hope that helps! Good question, and thanks for asking.
[1] https://github.com/FastLED/FastLED/wiki/Pixel-reference
[2] https://www.arduino.cc/reference/en/language/functions/math/map/
[3] https://www.arduino.cc/reference/en/language/functions/math/constrain/
hi, great info n tutorial, now i learn alot from your code, using larson scanner from yours, how to make led lit up like fill effect:
ReplyDelete1000
0100
0010
0001
1001
0101
0011
1011
0111
1111
That's it from the hardware side, at least for this demo. You can do all sorts of things like dimentional matrices, but we'll just stick to a strip for now. Moving on to code, migh as well start by piggy-backing on FastLEDs own wiki.
ReplyDeleteThanks, I fixed this!
DeleteHello !!
ReplyDeleteThank you for this clear step by step programming tutorial.
Any clue on using two or more simultaneous LED strips addressing them making different lightning effects ?
Thanks for the kind words! I'm a bit out of practice, but looked up where I think the best info probably is:
Deletehttps://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples
That has several options for having all the strips do the same thing as well as how to make them do different things. I also just happened to run across this, which may or may not be related to what you're doing. It speaks to mapping an irregular shape to a matrix:
https://www.youtube.com/watch?v=zYZkbfmCuEY
Hope that helps a bit!
Thank you jwhe,
ReplyDeleteI very much appreciate for the link.
I'll give it a try .
Hope I understand how to use it.
Thank You and I have a neat offer: House Renovation What To Do First remodel garage into living space
ReplyDelete