Flexible LED Fading

I'd like to introduce you to a method of fading RGB LEDs both smoothly and flexibly.

A common method of fading colours on the Arduino is to use simple for loops, such as:

// fade from blue to violet
for (r = 0; r  0; b--) { 
    analogWrite(BLUEPIN, b);
    delay(FADESPEED);
} 
// fade from red to yellow
for (g = 0; g < 255; g++) { 
    analogWrite(GREENPIN, g);
    delay(FADESPEED);
} 

And so on. And that is all very well if you just want to fade a single LED to a predefined brightness, or a couple of LEDs all to the same brightness. However if you want to get different combinations of colours and fade smoothly between them it all gets a lot more complex. Especially if you don't know at the time what colour you are fading from.

So here is a (I hope) simple method for achieving just that. It relies on a single function to do all the work (I have tried to make it completely portable so you can just drop it into your code) which remembers what brightness each of the LEDs is and smoothly fades that remembered brightness to the requested target brightness.

Ok, so first I'll throw a complete program at you with the function in it so you can see it in action:

// Pins for the LEDs
const uint8_t RED = 3;
const uint8_t GREEN = 5;
const uint8_t BLUE = 6;

const uint32_t fadeSpeed = 10;

// Array of colour sequences. This is a 3D array - that is, an array of arrays.
// Each array slice consists of an array of three values, R, G and B.
uint8_t colors[][3] = {
    {255, 0, 0}, // Red
    {0, 0, 255}, // Blue
    {0, 255, 0}, // Green 
};

// How many color entries are there?
#define NUM_COLORS (sizeof(colors) / sizeof(colors[0]))

// The current color pointer
uint8_t color = 0;

// Main setup routine to configure the pins.
void setup() {
    pinMode(RED, OUTPUT);
    pinMode(GREEN, OUTPUT);
    pinMode(BLUE, OUTPUT);
}

// Main loop
void loop() {
    if (fadeTo(RED, GREEN, BLUE, colors[color][0], colors[color][1], colors[color][2], fadeSpeed)) {
        color++;
        if (color == NUM_COLORS) {
            color = 0;
        }
    }
}

// This is where the magic happens
//
// This function remembers internally (using static variables) the current state of each of the
// colors, and adjusts the brightness of each color one step towards the requested target color.
// When all three colors match the target color it returns 'true' so you can change to a different
// target color.
bool fadeTo(uint8_t redPin, uint8_t greenPin, uint8_t bluePin, uint8_t redVal, uint8_t greenVal, uint8_t blueVal, uint32_t del) {
    // These store the current brightness values
    static uint8_t redCurrent = 0;
    static uint8_t greenCurrent = 0;
    static uint8_t blueCurrent = 0;

    // Keep track of if any color has faded or not
    bool finished = true;

    // Adjust the red ...
    if (redCurrent > redVal) {
        redCurrent--;
        finished = false;
    } else if (redCurrent < redVal) {
        redCurrent++;
        finished = false;
    }

    // ... green ...
    if (greenCurrent > greenVal) {
        greenCurrent--;
        finished = false;
    } else if (greenCurrent < greenVal) {
        greenCurrent++;
        finished = false;
    }

    // ... and blue values.
    if (blueCurrent > blueVal) {
        blueCurrent--;
        finished = false;
    } else if (blueCurrent < blueVal) {
        blueCurrent++;
        finished = false;
    }

    // Update the outputs with the new values
    analogWrite(redPin, redCurrent);
    analogWrite(greenPin, greenCurrent);
    analogWrite(bluePin, blueCurrent);

    // Pause as long as is requested
    delay(del);

    // and finally return if we met the target colors or not
    return finished;
}

There's two main bits of this program to take note of. I'll start with the first, which is not actually anything to do with the fading method, it's just all about how the colours to fade to are chosen. That is the use of a 3D array, or an array of arrays. This example has three entries in the array, and each entry consists of an array of three values.

// Array of colour sequences. This is a 3D array - that is, an array of arrays.
// Each array slice consists of an array of three values, R, G and B.
uint8_t colors[][3] = {
    {255, 0, 0}, // Red
    {0, 0, 255}, // Blue
    {0, 255, 0}, // Green 
};

// How many color entries are there?
#define NUM_COLORS (sizeof(colors) / sizeof(colors[0]))

So colors[0] contains the array {255, 0, 0}, which means that colors[0][0] contains the value 255. Simple really.

The #define is a clever trick to work out how many entries are in the array. The sizeof() function (actually a compile-time operator) return the size in bytes of whatever has been passed to it. Since this is a 3x3x1 array (3 arrays of 3 bytes) the whole size of the array (sizeof(colors)) will be 3x3x1, or 9 (count them - there's 9 numbers there). Also the size of the first entry in that array (colors[0]) will be three, since it's an array of 3 bytes. Divide one by the other and you get the number of entries in the outside array. We use that later on to know when we have reached the end of our array.

Now a quick look, in passing, at the logic of the main loop:

    if (fadeTo(RED, GREEN, BLUE, colors[color][0], colors[color][1], colors[color][2], fadeSpeed)) {
        color++;
        if (color == NUM_COLORS) {
            color = 0;
        }
    }

Call our function, passing the pins we want to fade, along with the colours we want to fade to (from the array we just talked about) with a certain delay. If the function returns a true boolean value (we'll talk more about that in a mo) then move on to the next colour. If we have reached the end of our sequence of colours then start from the beginning again. Not really rocket surgery.

And now the bit you have been waiting for - the magic function. Let's take this a bit at a time:

    // These store the current brightness values
    static uint8_t redCurrent = 0;
    static uint8_t greenCurrent = 0;
    static uint8_t blueCurrent = 0;

Static variables are just like global variables - they remember their value across successive calls to the function. You set it once and it stays set until you change it. With static variables (just as with global variables) you really should provide them with an initial value - in this case we are setting them all to 0. The big difference between static variables and global variables is the scope. These variables only exist within this function. That means that you can use the same names in other functions (though not for global variables) and all is happy. It also means that the function is completely self-contained and can be copied and pasted into other programs without needing any changes.

These static variables store the remembered current brightness of each LED.

One thing we want the function to do is tell us if we have finished fading or not (the boolean value returned mentioned earlier). That way we can know when it is time to select a new colour to fade to. A simple bool variable is used to temporarily keep track of that. Due to the logic coming in the next bit we want to start out assuming we have finished, and only if we find we haven't do we change our assumption:

    // Keep track of if any color has faded or not
    bool finished = true;

Next we compare the current and target values. If they are not the same then we add or subtract 1 from or add 1 to the current value. That is done with three blocks of (I will only bore you with one):

    // Adjust the red ...
    if (redCurrent > redVal) {
        redCurrent--;
        finished = false;
    } else if (redCurrent < redVal) {
        redCurrent++;
        finished = false;
    }

Quite simple really - if the current value is less than the target value then add 1 to it. Conversely, if the current value is greater than the target value then subtract 1 from it. Oh, and make a note of the fact that we haven't actually finished fading yet in the bool variable we made just now.

Finally, after tweaking each of the three colours, we can output the new values to the LEDs and return whether we finished or not:

    // Update the outputs with the new values
    analogWrite(redPin, redCurrent);
    analogWrite(greenPin, greenCurrent);
    analogWrite(bluePin, blueCurrent);

    // Pause as long as is requested
    delay(del);

    // and finally return if we met the target colors or not
    return finished;

We throw a little delay in there to slow the fading down a bit, otherwise it's just too fast to appreciate it.


Cheap 433MHz Transmitters