ACROBOTIC Industries ACROBOTIC Industries

Arduino Activity 4: RGB LEDs

Overview

Time to complete: 10–15min; Level of difficulty: Beginner

In this activity, we’ll learn how to generate any color we desire using RGB LEDs; we’ll program our Arduino board both to cycle and fade through several colors of our choice.

List of Materials


Obtaining the Code

For this activity, we'll be loading 2 different programs to our Arduino Uno board, which are available in our Github repository:

We can open a new window in the Arduino environment and copy-paste the code.  Before diving into the code, let's review a bit of the operation of RGB LEDs, and how colors are formed.


Color Generation and RGB LEDs

Whereas the concept of color is a fairly intuitive one, the theory behind the perception of color is actually complex and not fully understood.  After many years of study, it's been demonstrated that the human eye contains specialized retinal cells that carry pigments with different spectral sensitivities (note that similar cells are present in many different species of animals!).  Known as cone cells, they are sensitive to three different spectra of light:

Cone type Spectral Sensitivity Range Peak wavelength
S 400–500nm 420–440nm
M 450–630nm 534–555nm
L 500–700nm 564–580nm

Roughly speaking, once light within these wavelengths stimulates the different cone cells, the brain combines the information giving rise to what we, as humans, experience as color.  The range of color is vast because light stimulates the receptors with varying degrees of intensity.  In addition, the perception of color is highly subjective as color is not a property of the electromagnetic radiation, but rather a feature of visual perception by an observer. 

In the context of LEDs, in particular the multi-colored kinds, they typically comprise 3 different LEDs (Red, Green, and Blue) packed tighly under a single casing.  As each LED's intensity can be controlled independently, we can generate different combinations of light, which our eyes will perceive as the different colors in the spectrum.

This principle of operation underlies pixel color in our LCD screens, where red, green and blue color dots placed next to each other give rise to the different colors we perceive.  The graphic below shows a simulation of how combining (in this case by way of linear addition) the three primary colors—Red, Green, and Blue—generates additional the colors we perceive as cyan, magenta, yellow, and white.

Color Mixing Diagram

In the case of our RGB LEDs, if we set the brightness of all three individual LEDs to be the same, then we will perceive the emitted light to be white.  Similar to the simulation above, if we turn off the red LED, so that just the green and blue LEDs have the same brightness, then the light will appear cyan.  With LEDs, the trickiest color to generate is actually black as it's not so much a color as it is the absense of light.  In this case, the best we can do is to turn off all three LEDs.


RGB LED's Packages and Configurations

The typical RGB (Red, Green, Blue) LEDs that we find in starter electronics kits look just like regular LEDs.  However, we can immediately tell them appart from regular LEDs as RGB ones have 4 terminals ('legs') instead of 2.  This is due to the fact that inside the typical LED casing there are actually three LEDs: one red, one green and one blue.

The question is then, if there are 3 LEDs inside, shouldn't there be 6 terminals instead of 4?

Fortunately for us, RGB LEDs come in one of two configurations: common-anode or common-cathode.  The reason why this is fortunate is beacuse it reduces the wiring needed to control them.

Inside the plastic casing, the three LEDs are physically connected together.  In the case of a common-anode configuration like the one used in this tutorial, the three anodes are connected internally and also to one of the terminals outside the plastic casing. This common terminal is longer than the other 3 so that it is evident to the users' eye.  The other noticeable feature of RGB LEDs is the translucence of the plastic casing.  Although this is certainly seen in regular, single-color LEDs as well, it is more common to come across while using RGB ones.


Wiring: RGB LED Cycle

As always, it's important to disconnect the Arduino board from any power source including the USB port from our computer.  This allows us to catch any wiring mistakes before turning it on, preventing components from being damaged.  Once power is off, we can proceed with wiring the following circuit.  The resistor values used for limiting the current flow through the red, green, and blue LEDs are 1KOhm (Brown-Black-Red).

Wiring an RGB LED

Although the wiring is straight-forward, we add a push button that will allow us to easily identify using software which terminals of the LED correspond to the cathode of the Red, Green, and Blue LEDs respectively.


Code Walkthrough: RGB LED Cycle

Before the start of our setup() function, we define a few variables we'll be using throughout the code.  For one variable in particular we use what's called a preprocessor directive.

// We can change this to false if we do not want to test the wiring
// of the LED
#define testing true

Preprocessor directives are typically used to facilitate changes on the code and to simplify compilation in different execution environments.  For instance, we can have sections of the code that only get included if we're compiling for a microcontroller architecture such as ARM or AVR.  In our case, we want to make the code include some sections that will allow us to test the wiring. Once we've wired things correctly, we'd like those sections to be excluded.

// Only used for checking the wiring
#if testing == true
 int button_pin = 2,
     button_state = HIGH,
     bounce_wait = 200,
     time = 0,
     button_state_old = HIGH;
#endif

While teaching this activity at our workshops, we've noticed that matching each LED color to a particular pin is not straight-forward. Thus, we wrote code that will blink one of the 3 LEDs inside the RGB package, starting with Red. If the user notices another LED color blinking, she can switch the wire that's connected to pin 9 for one of the other two (i.e., those connecte d to pin 10 and pin 11) until the Red LED blinks. Once this is done, she can hold down the button for about 2 seconds then release it, which should make the Green LED start blinking. In ca se the Blue LED is the one that blinks, the user needs to switch the wires connected to pin 10 and pin 11. Once this is done, the user can hold down the button for another 2 seconds and rel ease it. This will then make the blink sequence Red-Green-Blue blink once, and the main part of the program will follow. The logic is implemented inside our function testWiring():

   int n = 0; // a variable to help us know when we're done with the test
   // We want to remain in this function while the wiring is incorrect
   while(n<2)
   {
     // If there has been a button press this will read LOW otherwise HIGH
     button_state = digitalRead(button_pin);
     // Similar check than in our Activity #3 - Buttons
     if ( (button_state == HIGH) && 
          (button_state_old == LOW) && 
          ((millis() - time) > bounce_wait) ) 
     {
       time = millis();
       n++; // if the user has pressed the button, switch to next LED
     }
     // Blink a single LED until the user gets the color right.
     switch(n)
     {
       // Start with Red. If another color is blinking the user will need
       // to adjust her wiring.
       case 0:
         blinkLed(r_led_pin, 500);
         break;
       // Once the user sees the Red LED blinking, she will press the button
       // and we'll start blinking the next LED. In case the user doesn't 
       // see the Green LED blinking, she'll need to reverse the remaining 
       // wires.
       case 1:
         blinkLed(g_led_pin, 500);
         break;
       // Once the wiring is correct, we show a short R-G-B sequence
       case 2:
         blinkLed(r_led_pin, 250);
         blinkLed(g_led_pin, 250);
         blinkLed(b_led_pin, 250);
         break;
       default:
         break;
     }
     // We keep track of the previous state of the button for debouncing
     // purposes
     button_state_old = button_state;
   }

Once the code is uploaded to the Arduino board, the wiring check should look like the following to a user:

 
 
 

A video posted by ACROBOTIC (@acrobotic) on


As we can see in the video, we switch the wiring after power-up until the Red LED blinks.  After pressing the button, the Blue LED comes on.  Although it's not shown, the user should reverse the wiring one last time (pin 10 and pin 11) in order to get the wiring 100% correct.

Once wiring is correct the main portion of the code simply switches between the three LEDs, and generates a heartbeat animation using each one individually.  To do so, we increment a variable from 0 to 2 (in steps of 1) and then reset it to 0.  We use this variable to index a 3-element array where the values of the LED pin numbers are stored.  By changing the index, we retrieve a diffrerent pin number each time, and we're able to run the hearbeat animation on all 3 LEDs.

  analogWrite(pin_array[index], 255-value); //set the  LED brightness (255 is OFF)  
  delay(step_delay_ms); //hold the value for a short time
  value+=step_size; //increase the current value for the next iteration

  // If we've reached the maximum or minimum value, change direction of
  // change in brightness (i.e., increasing -> decreasing or viceversa).
  if( (value>value_max)||(value<0) )
  {
    step_size *= -1;
    value+=step_size; // ensure that value stays within bounds
    // We also check if we're at the end of an ON-OFF cycle to switch LED color
    // and stop for 1 second.
    if(value == 0)
    {
      delay(cycle_delay_ms); // hold 1 second off starting each cycle
      // This line first increases our pin array's index (++)
      // and then it performs the modulus operation index = index%3 so that
      // the index of the 3-element array remains within the acceptable values:
      // 0, 1, or 2. 
      ++index%=3;
    }
  }

For generating the heartbeat signal, we use Pulse-Width Modulation (PWM) by calling the Arduino built-in analogWrite() function.  We described in a bit more detail Pulse-Width Modulation in our previous LED tutorial.  

Now that we have matched the LEDs correctly to the pins, it's time to generate some awesome colors using the RGB LED Fade program!


Code Walkthrough: RGB LED Fade

In the RGB LED Fade program we make use of a new type of variable: structures.  In this context (i.e., a C++ program), a structure is a user-defined data type that allows for combining data items.  Unlike an array where the data items need to be of the same kind and length, the items inside a structure can be different.  In our case, we'll define a structure with 3 members that will hold the intensity values of the LEDs (red, green, and blue).

// We're going to use a structure type with 3 field members, which will
// hold the brightness level of the LED
struct RGB {
  byte r;
  byte g;
  byte b;
};

We use the RGB structure to hold the red, green, and blue values of the colors we'll be using in the main portion of code:

// Pre-define colors by the intensity of their {R,G,B} LEDs
// These can and should be adjusted by the user to resemble
// colors more accurately
RGB green  = {   0, 255,   0 };
RGB yellow = { 255,  50,   0 };
RGB orange = { 255,  10,   0 };
RGB red    = { 255,   0,   0 };
RGB pink   = { 200,   0, 155 };
RGB purple = { 110,   0, 220 };
RGB blue   = {   0,   0, 255 };
RGB cyan   = {   0,  70, 255 };
RGB white  = { 150, 210, 255 };

Because we want our animation to transition between two colors, rather than jumping from one to the other, we define the function fade() where we calculate the brighness difference (for all three RGB values) between the current color, and the one into which we're trying to change.  The simplest way to do this is to:

  // We calculate the difference between the R, G, B values of the "from" and "to"
  // colors
  int red_diff   = out.r - in.r;
  int green_diff = out.g - in.g;
  int blue_diff  = out.b - in.b;

We then divide these differences into a pre-determined number of steps (n_steps).  Then, we use a loop to take that particular number of steps from the three original R, G, B values to the new ones.  We are able to write the intensity values to the LEDs using the built-in function analogWrite():

  for ( unsigned i = 0; i < n_steps; ++i) // taking as many steps as we need
  {
    // We divide the difference by the number of steps iterate until all steps are 
    // taken towards the new color. Note that the color difference can be either 
    // positive or negative, depending on whether we need to increase or decrease
    // brightness on each LED to display the new color!
    RGB output = { in.r + i * red_diff / n_steps,
                   in.g + i * green_diff / n_steps,
                   in.b + i * blue_diff/ n_steps
                 };
    // Set the brightness of each LED by using PWM!
    analogWrite( r_led_pin, 255-output.r );
    analogWrite( g_led_pin, 255-output.g );
    analogWrite( b_led_pin, 255-output.b );
    delay(time);
  }

Our loop() function then includes a set of calls to the fade() function from an arbitrary set of colors to another.  We use the pre-defined values inside the RGB structures for the two colors in the transition.

  // Fade from one color to the next by calling our fade function.  We use the 
  // default values for the 3rd (steps) and 4th (time) arguments of the function
  fade( white, green );
  fade( green, yellow );

Note that we also set the first color of the transition to the last color of the previous one, which gives a nice continuous transition during the entire animation.  Once we upload the code to our Arduino board, the animation should look like this:

 

A video posted by ACROBOTIC (@acrobotic) on


Code Tinkering

After uploading the RGB LED Fade program to our Arduino boards, we can experiment with changing the red, green, and blue intensity values that comprise each color so that it matches more closely our own definitions of yellow, orange, pink, purple, cyan, and white!


Troubleshooting

The colors look off during the fade animation.

Please check the wiring by completing the color testing instructions on the RGB LED Cycle program.  This is meant for wiring the correct LED color to its corresponding pin number that the software programs expect.

The color white looks tinted (red or purple)!

Although theoretically setting the red, green, and blue values to the maximum intensity (i.e., 255, 255, 255) should yield a perfect white light color, the reality is that the relative intensities of the LEDs vary quite greatly.  Moreover, when we use the function analogWrite() we're using Pulse-Width Modulation (PWM) to set an average voltage value between 0 and 5 volts.  Each of the three LEDs inside the RGB LED package has different properties that react in their own ways to a particular voltage.

In order to address the relative difference in brightness we need to calibrate the LED so that the relative intensities are similar.  This process is not trivial, but can be done with the proper instrumentation and a bit of patience.  See for example this nice description by Jacob Joseph: RGB LED Color Calibration.


Comments, questions, or concerns? Drop us a line!.