Controlling a Tricolor LED Using PWM

Controlling a Tricolor LED Using PWM

Introduction

In the previous project, Breathing LED with Pulse-Width Modulation, the brightness of an LED was controlled via pulse-width modulation (PWM). In this project, we will use PWM to control the color of a tricolor LED. If you are not familiar with PWM, we recommend that you review the aforementioned project. This project will be very similar to the previous project, except the LED being used will change color. Generally, LEDs are manufactured to emit one color of light. These colors can range from any color in the rainbow to infrared or even ultraviolet. The LED's color is determined by the material it is made from and the voltage that is applied to it. Depending on the combination of the material and voltage, different wavelengths of light are emitted. We perceive these different wavelengths of light as colors. The tricolor LED we will use can only generate red, green, and blue wavelengths of light. This is because the tricolor LED actually consists of a red, green, and blue LED all in one bulb. By changing the intensities of each LED inside the bulb, we can generate the illusion of different colors. This is the same principle we would use when mixing paint. Mixing red and yellow paint results in orange paint, even though there is no naturally orange pigment in the mixture. This phenomenon occurs because our brains simply perceive this combination of red and yellow pigment the same way we perceive naturally orange pigment. Similarly, our brains perceive the combination of red and green wavelengths of light the same way the actual wavelength of yellow is perceived.

Inventory:

Qty Description Typical Image Schematic Symbol Breadboard Image
1 Tricolor LED
3 220 Ω resistors

Hardware Structure

Before we begin putting the circuit together, we must discuss the structure of the tricolor LED. Since tricolor LEDs are essentially a combination of a red, a green, and a blue LED, it would make sense to assume it will have three anodes and three cathodes for a total of 6 legs. Realistically, this isn't very practical. In fact, the red, green, and blue LEDs inside the bulb all share a common cathode to save room. This means our tricolor LED will have only four legs, as shown in Fig. 1.

Figure 1. Tricolor LED connection convention.

The longest leg is the shared cathode, and it should always be connected to ground. Follow the steps below to construct the circuit. Note that the wire colors correspond to the LED color they will be controlling.

Step 1: Setting up the Circuit

Figure 2. Circuit to control a tricolor LED.
  1. Place the tricolor LED in the breadboard, as shown in Fig. 2.
  2. Connect the shared cathode of the tricolor LED (the longest leg) to ground (GND).
  3. Connect a 220 Ω current-limiting resistor to each of the LED's remaining legs.
  4. Connect PWM pin 6 (red brightness control) to the left-most leg of the tricolor LED.
  5. Connect PWM pin 5 (green brightness control) to the leg just to the right of the shared cathode.
  6. Connect PWM pin 4 (blue brightness control) to the right-most leg of the tricolor LED.

Step 2: Color Control Basics

Having built the circuit, we can now write some simple code to control the color of the LED. Remember the value range for a PWM pin is 0 to 255. In the code below, the PWM pins that control red and blue are set to 200 using the setColorBrightness() function. This makes the tricolor LED purple.

              int pinRed = 6, pinGreen = 5, pinBlue = 3;
              
              void setup()
              {
                pinMode(pinBlue, OUTPUT);
                pinMode(pinGreen, OUTPUT);
                pinMode(pinRed, OUTPUT);
                
                analogWrite(pinBlue, 0);//Write 0 to pwm pin 3 (blue control)   This makes sure the LED starts off
                analogWrite(pinGreen, 0);//Write 0 to pwm pin 5 (green control) This makes sure the LED starts off
                analogWrite(pinRed, 0);////Write 0 to pwm pin 6 (red control)  This makes sure the LED starts off
               
               //Make LED purple 
               setColorBrightness('r',200);//Sets Red to 200 
               setColorBrightness('b',200);//Sets Blue to 200
              }

              void setColorBrightness(char color, int brightness)
              {
                    //if else tree used to select the correct color control pin
                    if(color == 'r')
                    {
                      analogWrite(pinRed, brightness);//Write to pwm pin 6  (red control) 
                    }
                    else if(color == 'g')
                    {
                      analogWrite(pinGreen, brightness);//Write to pwm pin 5  (green control)
                    }
                    else if(color == 'b')
                    {
                      analogWrite(pinBlue, brightness);//Write to pwm pin 3  (blue control)
                    }
                  
                  return;
              }
              
              void loop(){}//loop() is not used

The setColorBrightness() function may seem unnecessary, since the color can be changed directly with analogWrite(). While this is true, bear in mind that it is easy to mix up which pin number corresponds to the color we want to change. Using the if else tree inside of the setColorBrightness() function allows us to modify color values using intuitive color abbreviations, “r,” “g,” and “b.” Furthermore, the if else tree provides a good point of comparison for introduction of switch statements, which will be covered in the next section.

Switch Statements

switch statements control the flow of a program and are very similar to if else trees. They compare a variable to a constant case value. If the variable matches the case value, the code contained within the particular case is executed. Otherwise, the case is skipped. In the event none of the cases match, the default case will be executed. The default case is optional and not needed for the switch statement to function properly. The following is an example of a switch statement.

              switch (variable) 
              {
                case 1:   
                  //do something when variable equals the integer 1
                  
                  break;//break skips all remaining cases 
                case -1:
                  //do something when variable equals the integer -1
                  
                  break;  
                case 'a':
                  //do something when variable equals the character a
                  
                  break;
                default: 
                  // if nothing else matches, do the default
                  
                  // default is optional
                  break;
              }
            

In our example, the case values were int and char types. A case value can be any constant value which can be interpreted as an integer value. This includes the Boolean constants HIGH, LOW, INPUT, OUTPUT, true, and false. Doubles, floats, strings, and arrays are data types that cannot be used as case values. In addition to being careful with the case's data type, it is also important to be aware of including break lines at the end of every case. If you forget to add a break in a specific case, the switch statement will execute that case and all cases below it until it sees a break line.

So far, switch statements and if else trees appear to do the same thing. While this is mostly true, there are distinct advantages and disadvantages to using one or the the other. We know that switch statements have limited usage because they cannot compare dynamic variables and are restricted to specific data types. On the other hand, switch statements compare only one case, making them very efficient. Figures 3 and 4 illustrate the differences in efficiency between if else trees and switch statements.

Figure 3. If else tree execution illustration.
Figure 4. Switch statements execution illustration.

The above screenshots are of MPIDE running on Microsoft Windows 7.

The if else tree must evaluate every if statement before and after the targeted case. The switch statement, on the other hand, jumps directly to the targeted case and then exits once it is finished. This saves a lot of time in situations with a large number of cases. Furthermore, the efficiency of the switch statement is even greater inside of the loop, since only one case is compared every iteration. This will come in handy when writing the code for a basic color sweep.

Color Sweeps

With a better understanding of how a switch statement works, we can begin coding writing a sketch to sweep through a combination of colors. This is similar to the code used for creating a breathing LED, except the principles are used to fade the individual colors of the tricolor LED in and out.

              int pinRed = 6, pinGreen = 5, pinBlue = 3;
              
              void setup()
              {
                pinMode(pinBlue, OUTPUT);
                pinMode(pinGreen, OUTPUT);
                pinMode(pinRed, OUTPUT);
                
                analogWrite(pinBlue, 0);//Write 0 to pwm pin 3 (blue control)   This makes sure the LED starts off
                analogWrite(pinGreen, 0);//Write 0 to pwm pin 5 (green control) This makes sure the LED starts off
                analogWrite(pinRed, 0);////Write 0 to pwm pin 6 (red control)  This makes sure the LED starts off
              }

              void changeColorBrightness(char color, boolean fadeIn, int fadeTime)
              {
                  int brightness, incrementValue, limit;
                  
                  if(fadeIn)//Note: this if statement is shorthand for if(fadeIn == 1)
                  {
                    //Setup variables so the the LED brightness will fade in from 0 to 255
                    incrementValue = 1;//Set incrementValue to add 1 to the brightness
                    brightness = 0;//Ensure the LED starts completely off (minimum brightness)
                    limit = 255;//Set the brightness limit to 255 (maximum brightness)
                  }
                  else
                  {
                    //Setup variables so the the LED brightness will fade out from 255 to 0
                    incrementValue = -1;//set incrementValue to subtract 1 from the brightness
                    brightness = 255;//Ensure the LED starts completely on (maximum brightness)
                    limit = 0;//Set the brightness limit to 0 (minimum brightness)
                  }
                  
                  while(brightness != limit) //Loop until brightness has faded in or out completely 
                  {
                    
                    brightness += incrementValue;

                    switch(color)
                    {
                      case 'r':
                        analogWrite(pinRed, brightness);//Write to pwm pin 6  (red control) 
                        break;
                      case 'g':     
                        analogWrite(pinGreen, brightness);//Write to pwm pin 5  (green control)
                        break;
                      case 'b':
                        analogWrite(pinBlue, brightness);//Write to pwm pin 3  (blue control)
                        break;
                    }
                    
                    //Wait for a specified number of miliseconds to control how fast the LED fades in or out
                    delay(fadeTime);
                  }
                  return;
              }

              void loop()
              {
                changeColorBrightness('r',1, 10);//Fade in red
                changeColorBrightness('b',1, 10);//Fade in blue
                changeColorBrightness('r',0, 10);//Fade out red
                changeColorBrightness('g',1, 10);//Fade in green
                changeColorBrightness('b',0, 10);//Fade out blue 
                changeColorBrightness('g',0, 10);//Fade out green
              }

Much like in our Basic Color Control code, we want to use a single function changeColorBrightness() to control which color is being modified. The main difference here is that we have replaced the if else tree with a switch statement and put it inside of a while loop.

The while loop increments (or decreases) the brightness of a color until it reaches the limit of the PWM pin. Each iteration of the loop is delayed so that we have time to watch the color fade. The code given above should make the LED fade from red, to purple, to blue, to aqua blue, and then finally to green.

The sweep sequence is determined by the order in which fading occurs across the red, green, and blue LEDs. You can try rearranging the order to get different color combinations. In the next section, we will explore how to randomly select a color \ combination.

Random Number Generation

In order to select a random color combination, a random number must be generated. This can be done using the random () function. The random() function can be used in two ways: the first way is to enter a max value as the only argument. This will return a random integer from 0 to the max, minus one. The second way is to enter a max and min value as the arguments. This will return a random integer from min to max minus one.

For example:

              random(11);//returns a random integer from 0 to 10 
              random(5,16);//returns a random integer from  5  to 15

Step 3: Generating Random Color Combinations

Now that we know how to generate a random number, we can write the code to generate a random color combination. To begin, we should consider all the color combinations we want to implement. All of these combinations are listed below.

  1. Red
  2. Green
  3. Blue
  4. Red and green
  5. Red and blue
  6. Green and blue
  7. Red, green, and blue
                  int pinRed = 6, pinGreen = 5, pinBlue = 3;
                   
                  void setup()
                  {
                    pinMode(pinBlue, OUTPUT);
                    pinMode(pinGreen, OUTPUT);
                    pinMode(pinRed, OUTPUT);
                     
                    analogWrite(pinBlue, 0);//Write 0 to pwm pin 3 (blue control)   This makes sure the LED starts off
                    analogWrite(pinGreen, 0);//Write 0 to pwm pin 5 (green control) This makes sure the LED starts off
                    analogWrite(pinRed, 0);////Write 0 to pwm pin 6 (red control)  This makes sure the LED starts off
                  }

                  void randomLEDColorCombo()
                  {
                    int colorCombo = random(7);//Generate a random value from 0 to 6 that corresponds to a color combination
                    int red = 0, green = 0, blue = 0;
                    
                    switch(colorCombo)
                    {
                      case 0://Combination: Red
                         red = random(256);//Generate random number from 0 to 255
                         break; 
                      case 1://Combination: Green
                         green = random(256);//Generate random number from 0 to 255
                         break; 
                      case 2://Combination: Blue
                         blue = random(256);//Generate random number from 0 to 255
                         break; 
                      case 3://Combination: Red, Green
                         red = random(256);//Generate random number from 0 to 255
                         green = random(256);//Generate random number from 0 to 255
                         break; 
                      case 4://Combination: Red, Blue
                         red = random(256);//Generate random number from 0 to 255
                         blue = random(256);//Generate random number from 0 to 255
                         break; 
                      case 5://Combination: Green, Blue
                         green = random(256);//Generate random number from 0 to 255
                         blue = random(256);//Generate random number from 0 to 255
                         break; 
                      case 6://Combination: Red, Green, Blue
                        red = random(256);//Generate random number from 0 to 255
                        green = random(256);//Generate random number from 0 to 255
                        blue = random(256);//Generate random number from 0 to 255
                        break; 
                    }
                    
                    analogWrite(pinRed, red);//Write random red value to pwm pin 6  (red control) 
                    analogWrite(pinGreen, green);//Write random green value to pwm pin 5  (green control)
                    analogWrite(pinBlue, blue);//Write random blue value to pwm pin 3  (blue control)
                    
                  }
                   
                  void loop()
                  {  
                    randomLEDColorCombo();
                    delay(500);//Wait for 500 milliseconds between each random color
                  }

Uploading the sketch to your chipKIT™ board should make the tricolor LED randomly jump to new colors. The time between each jump is determined by the delay inside of the loop() function. You may notice some of the colors appear very dim. This is because inside of each case, random(256) has returned a value near 0. You can try experimenting with different min values for the random function to prevent dim colors. You can also specifically tweak the min values in cases 3 through 6. Currently, these cases have the possibility of generating a single primary color, like red, assuming the other colors in the case are randomly assigned a value near zero.

Now that we know how to generate random color combinations and implement color sweeps, we can combine the two concepts. In this section, we will write some code to randomly select a color sweep sequence. The order of the sequence will matter here. This means there will be six different color sequences, all of which are listed below.

List of color sequences

  1. Red, green, blue
  2. Red, blue, green
  3. Green, red, blue
  4. Green, blue, red
  5. Blue, red, green
  6. Blue, green, red

Each sequence will be implemented as a switch statement case. The case will be selected by using the random() function. Inside of each case, we will use the changeColorBrightness() function we wrote in the color sweeps section to fade the colors in or out. The code necessary to implement this is shown below inside of the randomColorSweep() function.

                  int pinRed = 6, pinGreen = 5, pinBlue = 3;
                  
                  boolean brightnessDirection = 1;//If set to 1 the color brightness will increase 
                    
                  void setup()
                  {
                    pinMode(pinBlue, OUTPUT);
                    pinMode(pinGreen, OUTPUT);
                    pinMode(pinRed, OUTPUT);
                      
                    analogWrite(pinBlue, 0);//Write 0 to pwm pin 3 (blue control)   This makes sure the LED starts off
                    analogWrite(pinGreen, 0);//Write 0 to pwm pin 5 (green control) This makes sure the LED starts off
                    analogWrite(pinRed, 0);////Write 0 to pwm pin 6 (red control)  This makes sure the LED starts off
                  }
                  void changeColorBrightness(char color, boolean fadeIn, int fadeTime)
                  {
                      int brightness, incrementValue, limit;
                      
                      if(fadeIn)//Note: this if statement is shorthand for if(fadeIn == 1)
                      {
                        //Setup variables so the the LED brightness will fade in from 0 to 255
                        incrementValue = 1;//Set incrementValue to add 1 to the brightness
                        brightness = 0;//Ensure the LED starts completely off (minimum brightness)
                        limit = 255;//Set the brightness limit to 255 (maximum brightness)
                      }
                      else
                      {
                        //Setup variables so the the LED brightness will fade out from 255 to 0
                        incrementValue = -1;//set incrementValue to subtract 1 from the brightness
                        brightness = 255;//Ensure the LED starts completely on (maximum brightness)
                        limit = 0;//Set the brightness limit to 0 (minimum brightness)
                      }
                       
                      while(brightness != limit)
                      {
                      
                        //Loop until brightness has faded in or out completely 
                        brightness += incrementValue;
                        
                        switch(color)
                        {
                          case 'r':
                            analogWrite(pinRed, brightness);//Write to pwm pin 6  (red control) 
                            break;
                          case 'g':     
                            analogWrite(pinGreen, brightness);//Write to pwm pin 5  (green control)
                            break;
                          case 'b':
                            analogWrite(pinBlue, brightness);//Write to pwm pin 3  (blue control)
                            break;
                        }
                        
                        //Wait for a specified number of miliseconds to control how fast the LED fades in or out
                        delay(fadeTime);

                      }
                      return;
                  }
                  
                  void randomColorSweep()
                  {
                  
                    int randomNumber, fadeTime = 5;
                    randomNumber = random(6);//Generate random number from 0 to 5
                    
                    switch(randomNumber)
                    {
                      case 0: //Color sequence 0: RGB
                        changeColorBrightness('r', brightnessDirection, fadeTime);
                        changeColorBrightness('g', brightnessDirection, fadeTime);
                        changeColorBrightness('b', brightnessDirection, fadeTime);
                        brightnessDirection = !brightnessDirection; //Invert fade direction
                        break;
                      case 1: //Color sequence 1: RBG
                        changeColorBrightness('r', brightnessDirection, fadeTime);
                        changeColorBrightness('b', brightnessDirection, fadeTime);
                        changeColorBrightness('g', brightnessDirection, fadeTime);
                        brightnessDirection = !brightnessDirection; //Invert fade direction
                        break;
                      case 2: //Color sequence 2: GRB
                        changeColorBrightness('g', brightnessDirection, fadeTime);
                        changeColorBrightness('r', brightnessDirection, fadeTime);
                        changeColorBrightness('b', brightnessDirection, fadeTime);
                        brightnessDirection = !brightnessDirection; //Invert fade direction 
                        break;
                      case 3: //Color sequence 3: GBR
                        changeColorBrightness('g', brightnessDirection, fadeTime);
                        changeColorBrightness('b', brightnessDirection, fadeTime);
                        changeColorBrightness('r', brightnessDirection, fadeTime);
                        brightnessDirection = !brightnessDirection; //Invert fade direction  
                        break;
                      case 4: //Color sequence 4: BRG
                        changeColorBrightness('b', brightnessDirection, fadeTime);
                        changeColorBrightness('r', brightnessDirection, fadeTime);
                        changeColorBrightness('g', brightnessDirection, fadeTime);
                        brightnessDirection = !brightnessDirection; //Invert fade direction
                        break;
                      case 5: //Color sequence 5: BGR
                        changeColorBrightness('b', brightnessDirection, fadeTime);
                        changeColorBrightness('g', brightnessDirection, fadeTime);
                        changeColorBrightness('r', brightnessDirection, fadeTime);
                        brightnessDirection = !brightnessDirection; //Invert fade direction
                        break;

                    }
                  }
                  
                  void loop()
                  {
                   randomColorSweep();
                  }
				

Once this code is uploaded, the tricolor LED should randomly sweep through different color sequences. The sequence's fade time can be tweaked by changing a variable inside of the randomColorSweep() function. By default, it delays five milliseconds between each incrementation of a color's brightness. This is done using delay() called within changeColorBrightness(). If you want to delay for periods less than a millisecond, you can replace the delay() line with delayMicroseconds (fadeTime) for greater precision. Remember that one millisecond is equal to 1000 microseconds, so using delay times less than 300 with delayMicroseconds() will make the LED flash rather than fade.

Summary:

In this project, we utilized pulse-width modulation to control the color of a tricolor LED. This project is very similar to the previous project, Breathing LED with Pulse-Width Modulation, except that we will be using a tricolor LED. Recall that the tricolor LED we will use can only generate red, green, and blue wavelengths of light, and by changing the intensities of each LED inside the bulb, we can generate the illusion of different colors.

Core Concepts:
  • PWM
  • Tricolor LEDs
Functions Introduced:
  • random
  • switch
  • delayMicroseconds

  • Other product and company names mentioned herein are trademarks or trade names of their respective companies. © 2014 Digilent Inc. All rights reserved.
  • Circuit and breadboard images were created using Fritzing.