Introducing RGBEffects: An RGB LED effect library for Arduino


For my remote controlled Arduino LED lamp project, I wanted to create some basic LED effects. The code for multiple effects was getting very hard to understand and modify when it was all in one file, so I looked for ways to clean it up. Despite an utter lack of C++ experience, making a library that could hide the spaghetti and possibly even be useful to others seemed like a good way to go. RGBEffects is the unoriginally named result.

The library design is pretty simple. All interaction happens through a RGBEffects object that has a set of pins for an LED. There are only four public methods on RGBEffects:

  • new RGBEffects(redPin, greenPin, bluePin) Creates a new instance of RGBEffects, using the given pins. The pins will automatically be set to OUTPUT.
  • void setEffect(RGBEffectType effect) The given effect becomes the current effect. Note that the LED colour will not change until update() is called.
  • void update() Updates the LED with the next colour for the current effect.
  • void nextEffect() Moves to the next effect. This is cyclical, so that it will loop back to the first effect if called on the last effect.

Notice that none of these methods provide timing control. To control the speed of effects, its up to you to decide how often you want to call update.

The available effects are defined in the enum RGBEffectType in RGBEffects.h and are as follows: EFFECT_OFF, EFFECT_SOLID_RED, EFFECT_SOLID_GREEN, EFFECT_SOLID_BLUE, EFFECT_SOLID_YELLOW, EFFECT_SOLID_PURPLE, EFFECT_SOLID_VIOLET, EFFECT_SOLID_WHITE, EFFECT_RAINBOW, EFFECT_FADE, EFFECT_CUBE, and EFFECT_BLINK.

// RGBEffects example - cycle all effects
#include <RGBEffects.h>

int redPin = 3;
int greenPin = 5;
int bluePin = 6;

int updateCount = 0;

// initialize the RGBEffects object
RGBEffects rgbEffects( redPin, greenPin, bluePin );

void setup(){
}

void loop(){
  rgbEffects.update();
  updateCount++;

  if(updateCount >= 100){
    rgbEffects.nextEffect();
    updateCount = 0;
  }

  // a delay of 200ms between each update call.
  delay(200); 
}

Installation

Assuming you are using the standard Arduino IDE, clone or download the project, then copy it into your Arduino libraries directory. For more information, see the Arduino Libraries Guide.

When the library is installed properly, it will be available in the Sketch > Import library list under the Contributed heading. There are examples included as part of the library. Once the library is installed, the examples can be viewed in File > Examples > RGBEffects > …

Design

Initially the library was designed to run multiple independent RGB LEDs. The use of an Arduino Nano board meant that it is not possible to use two independent RGB LEDs and an IR decoder library I was using. I didn’t know anything about the relationship between PWM and timers when I started this project, resulting in an embarrassing amount of time passing before I figured out what the problem was.

The Atmel ATmega328 chip in the Nano has three timers, named timer0 (8bit), timer1 (16bit), and timer2 (8bit). Using PWM on a pin requires a particular timer to be available . For the Nano, the mapping is pins 5 & 6 require timer0, pins 9 & 10 require timer1, and pins 3 & 11 require timer2.

If a library is installed that uses timer2, then PWM cannot be used on pins 3 & 11 without undesirable behaviour.

The IR library I used is the excellent IRLib, which uses timer2 by default. Since timer2 is used by pins 3 and 11, those pins cannot be used to run PWM for LEDs, leaving only pins 5, 6, 9 and 10 for PWM. Since each LED requires three pins, we are limited to one RGB LED. Using an Arduino Mega, which has six timers, would make running multiple LEDs independently possible.
The [Secrets of Arduino PWM](http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM) article was very helpful in explaining PWM.

The Nano seems to have no trouble running two LED modules off a single set of pins. I have not tried running more than two LEDs off a single set of pins.

Reducing the memory usage was not a goal of the initial version of RGBEffects, as I was not familiar enough with C++ to know how to achieve a minimal memory usage design. No doubt there is a more efficient way to achieve this libraries functionality.

Effects

Each effect requires creating a subclass of Effect and implementing two methods:

  • void setup() performs any required initialization for the effect
  • rgb update() returns the next colour to display.

An instance of each effect subclass is created and registered in the RGBEffects constructor. Each time RGBEffects.update() is called, it calls currentEffect.update() to get the next colour then updates the LED. Colours are defined by the struct rgb which is a set of three bytes each containing a value of 0 - 255 that each represent the red, green, and blue component of the colour.

None of the effects currently use a time based algorithm, but it would be relatively straight forward to implement.

Adding an effect

Adding new effects is relatively simple.

  1. Increase the size of the _effects array in RGBEffect.h
  2. Create an entry for your new effect in RGBEffectType in RGBEffects.h
  3. Create a subclass of Effect in RGBEffects.cpp that implements the setup and update methods.
  4. Create an instance of that class in the RGBEffects constructor and add it to the _effects array in RGBEffects.cpp
  5. Add a case for your effect in the RGBEffects::setEffect(RGBEffectType effect) method in RGBEffects.cpp

There is a commit which adds a new effect called Blink that shows all the changes required.

If you want to share your effect, send a pull request with your changes.

Debugging effects

RGBEffects.h contains a definition that controls debugging. If on, every call to the setLEDsColour method will print the hex of the colour being set to serial. setLEDsColour is called by RGBEffects.update().

// Debugging header in RBGEffects.h

// debugging off (default)
#define DEBUG_COLOURS_ENABLED (0)

// debugging on
#define DEBUG_COLOURS_ENABLED (1)