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 toOUTPUT
.void setEffect(RGBEffectType effect)
The given effect becomes the current effect. Note that the LED colour will not change untilupdate()
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 effectrgb 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.
- Increase the size of the
_effects
array inRGBEffect.h
- Create an entry for your new effect in
RGBEffectType
inRGBEffects.h
- Create a subclass of
Effect
inRGBEffects.cpp
that implements thesetup
andupdate
methods. - Create an instance of that class in the RGBEffects constructor and add it to the
_effects
array inRGBEffects.cpp
- Add a case for your effect in the
RGBEffects::setEffect(RGBEffectType effect)
method inRGBEffects.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)