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.

10 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

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