Pages

21 October 2015

FastLED series: getting started

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…


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, migh 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 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, so dot 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
The 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
You could write the loop in "reverse," like this:

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.

26 comments:

  1. thanks for posting this - really helpful primer - much appreciated!

    ReplyDelete
  2. Hi jwhendy,

    Nice 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 !!

    ReplyDelete
    Replies
    1. 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?

      I 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!

      Delete
  3. 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!!

    ReplyDelete
    Replies
    1. Yay! 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).

      Delete
  4. how do i set all LEDs to one color?

    ReplyDelete
    Replies
    1. Definitely 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.)

      Also, 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!

      Delete
  5. 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 :-)

    ReplyDelete
    Replies
    1. Good 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!).

      Delete
  6. Holy 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
    Replies
    1. @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!

      Delete
  7. Hi 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 ?
    Thank you.

    ReplyDelete
    Replies
    1. 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!

      So, 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!

      Delete
    2. 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.
      Thank you again for your help.It is very much appreciated.

      Delete
    3. 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].

      Delete
    4. Another 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.

      Delete
    5. Hi 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.

      Delete
    6. No worries, and there's a steep learning curve for sure! Once you get setup, here's the gist of what I meant:
      - 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.

      Delete
  8. 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!

    ReplyDelete
    Replies
    1. actually nevermind! I figured it out with this arduino help forum post: https://forum.arduino.cc/index.php?topic=431000.0

      Delete
    2. Glad 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 :)

      Delete
  9. I 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.)

    ReplyDelete
    Replies
    1. Sure! 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.

      When 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/

      Delete
  10. 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:
    1000
    0100
    0010
    0001
    1001
    0101
    0011
    1011
    0111
    1111

    ReplyDelete
  11. 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.

    ReplyDelete

formatting help: <i>italics</i>, <b>bold</b>