color changing led demo

Color Changing LED


Control the color of an RGB LED with an InfraRed Remote.

This example uses the NEC InfraRed protocol for communication.

Pulse Width Modulation (PWM) is used to control the amount of red, green and blue from the LED.

IR Remote


This example makes use of the IR Remote Creator App, where a remote with 15 buttons has been designed to work with the Arduino code on this page.

Color Changing LED Remote
Color Changing LED Remote

The round buttons at the top with lightbulb symbols turn the LED on and off. The triangles buttons increase or decrease the Red, Green or Blue component of the light in set amounts. The oval play button will enter a mode where the light smoothly rotates between colors. The round buttons at the bottom of the remote set the LED to a specific color.

Get the remote for this demo in one of these ways:

1) If you are accessing this page from a device with the app installed, then tap here to load it into the App.

2) Alternatively download led-color.txt into the keuwlsoft/ir-remotes/ directory in the documents folder on your Android device, and then load it from within the app.

3) Alternatively, in the load from web link option within the app, enter: https://www.keuwl.com/ir-remotes/led-color.txt

4) Generate the remote yourself within the App, by creating some buttons and assigning the IR pattern to match those described in the next section.


NEC Protocol


This is a popular InfraRed protocol that has some nice built in error checking. It uses pulse distance modulation on a carrier wave that is typically 38 kHz. The distance between pulses is used to differentiate a 0 or 1.

Pulse Distance Modulation
Pulse Distance Modulation

The IR pattern comprises a header pulse, 32 bits of information, and then a stop or end bit. The 32 bits of data are split into an address and a command.

The address decides the target device of the IR transmission whilst the command determines the function on that device.

Both the command and address are sent twice, first as it is, and then as a one's compliment (i.e. inverted) version. For example if the address is 01100111, then the inverted address, address' is 10011000. If the transmitted data hasn't been corrupted, then the logical bitwise AND of the address and address' will yield zero. Likewise for the bitwise AND of command and command'.

As an example of an IR pattern, the pattern for the play (Rotate Colors) button on the remote is shown below.

IR Pattern
IR pattern for the Rotate Colors Button

For all buttons, the address being used for this demo project is zero. If changing the address of the sent code, make sure the Arduino code is adjusted to match. For example, If you have multiple LED or other IR projects, you can assign them different addresses so that the signal is only received by the one that should receive it.

For the buttons, we have used command numbers 1 to 15 (0x01 to 0x0E hex). Thus the code table for the remote is:

Button Address  Command 
LED Off0x000x01
LED On0x000x02
Red Up0x000x03
Red Down0x000x04
Green Up0x000x05
 Green Down 0x000x06
Blue Up0x000x07
Blue Down0x000x08
Yellow0x000x09
Pink0x000x0A
White0x000x0B
Red0x000x0C
Green0x000x0D
Blue0x000x0E
Table of Assigned IR Codes for the Remote

There is nothing special about this choice of command numbers. Change them if required and update the Arduino code to match.


IR Receiver Module


This project used a VS1838B InfraRed Receiver Module. There are a range of IR receiver modules available, the main difference being in the carrier frequency. 38 kHz is by far the most popular frequency for IR remotes, but you will also find 30 kHz, 33 kHz, 36 kHz 40 kHz, 56 kHz and others available. Unless you need a specific frequency to match a particular remote/device, 38 kHz is probably going to be the cheapest and best option. There is some overlap in frequency capability. i.e. expect a 38 kHz receiver to also be able to pick up a 36 kHz signal (for example), but weaker and at a closer range.

Check the datasheet of your IR receiver module so that you connect you device correctly (otherwise it might heat up and burn out and then not work anymore). The IR receivers generally will have three connections: Ground, +V and a Output. Note that the output could be inverted to what you think it is going to be. Checking the data sheet that the IR receiver uses a suitable voltage for your Arduino to supply is probably a good idea too.

The IR receiver packages do all the necessary amplification & demodulation and just output any IR pattern they receive as a digital signal which we will read with the Arduino.

Some IR Receiver Modules
Some IR Receiver Modules (TSOP4838, TSOP31256 and VS1838B).


Pulse Width Modulation


To control the brightness of the RGB LED we use the Pulse Width Modulation (PWM) capability of Pins 9, 10 and 11 on the Arduino Uno. By changing the pulse width, we control the average On time of the pin, such that we can control the power output to a device, such as the LED in this demo. The Arduino analogueWrite() command uses PWM to achieve this. For every 256 counts, the output is held high until the requested analogue write value is reached. This is repeated every 2.04 ms or at 490 Hz (for the 16 MHz Arduino Uno with default settings). Thus a value of 0 will be off all the time, a value of 255 will be on all the time and anything in between will be pulse width modulated to be on for a part of the time.


Components used


  • Arduino Uno
  • VS1838B InfraRed Receiver Module
  • RGB LED
  • Resistors for LED
  • Prototype Shield for Arduino Uno

arduino uno
ir receiver module
rgb led
resistors
arduino shield

Circuit Diagram


The resistors, R, depend on the LED used and can be different for each of the colors of the LED (consult datasheet for your LED). They limit the current so the LED doesn't burn out. The resistances can be trimmed/adjusted to improve color balance.

circuit diagram
Circuit Diagram for IR Controlled Color Changing LED Project

We are using an Arduino Uno for this demo, although you could use a different model if you prefer, just make sure it has 3 pins with Pulse Width Modulation (PWM) capability and and that the connected pins correspond to that in the Arduino sketch. Also make sure that the IR receiver module's output is connected to one of the external interrupt pins.


Arduino code


Open the Arduino software, select the correct COM Port and Arduino device in the Tools menu, copy and paste the sketch below and click upload.

Of note in this sketch is the way it handles the incomming IR pattern data. Rather than tieing up the main loop with PulseIn() commands and constantly monitoring the IR signal, we have used interrupts instead. This leaves the main loop free to carry out other important tasks.

The interrupt is called every time the external interrupt pin changes state. It logs the elapsed micros() time of the Arduino, and then resumes from where it was before. Once IR pattern data has been collected and a pause is observed at the end of the pattern, it's analysis is done in the main loop.


// IR Controlled Color Changing LED
// By keuwlsoft:  www.keuwl.com  16th Feb 2019
// cc Attribution-ShareAlike
//
// This sketch will receive an IR Pattern, Decode the Address and Command (NEC Protocol) and then apply a change
// to an RGB LED's color based on the command received.
// An Interrupt is used to gather the IR Pattern timing, which is later analysed, thus keeping the main loop free for other tasks.
// Keuwlsofts 'IR Remote Creator' App was used to send the IR signal. 
// The Remote can be downloaded at www.keuwl.com/electronics/rduino/ir/01-tricolor-led/


int ir_pin=2; //use pins 2 or 3 for external interupts, connect the IR sensor output here.

long no_change_time=10000; //No change in IR pin for this duration (microseconds) will mark the end of pattern.
const int array_size=100; //Max times to log.  If array is full, it will report the pattern to that point. 
long t_array[array_size]; //Array of times of when the IR pin changes state.
int count=0; // Number of times logged to array.
boolean log_micros=true;  //Flag to indicate if able to log times to array
long last_micros=0; //previous array time entry

int addr=0; //Device address decoded from IR pattern
int cmmd=0; //Command decoded from IR pattern
int our_device_addr=0; //integer (0-255) that is an address to identify our device amoungst other devices.

int Red_LED_Pin = 9; // PWM Pin for Red LED
int Green_LED_Pin = 10; // PWM Pin for Green LED
int Blue_LED_Pin = 11; // PWM Pin for Blue LED

//Varibles to hold brightness values ranging from 0 (off) to 255 (fully on)
int Red_value=0;
int Green_value=255;
int Blue_value=0;

boolean new_data=false; //Flag that is set everytime a new IR command is received
boolean LED_on=true; //Flag to indicate if LED is on
boolean Rotate=false; //Flag to indicate if color rotation is on
int Rotate_section=0; //Color rotation is divided into 6 sections.
int Rotate_step=4; //How much to change color each update.  Decrease for slower color rotation.
 

void setup() {

  pinMode(ir_pin, INPUT);

  //attach an interupt to log when the IR pin changes state
  attachInterrupt(digitalPinToInterrupt(ir_pin),interupt,CHANGE);
   
  // Initialise LED pins as outputs
  pinMode(Red_LED_Pin, OUTPUT);
  pinMode(Green_LED_Pin, OUTPUT);
  pinMode(Blue_LED_Pin, OUTPUT);

}

void loop() {
  
//=========================================== Extract IR Command
  if (count>0){
    if (!log_micros|((micros()-last_micros)>no_change_time)){
      log_micros=false; //make sure array not modified while we analyse it
      //Get Pattern  
      count--;
      for (int i=0;i<count;i++){
        t_array[i]=t_array[i+1]-t_array[i];
      }
      //Extract Command And Address of NEC protocol
      if (count>=64){ //Check patten long enough for 32 bits of data
        int pos=count;
        // Extract Command Compliment
        int cmmd2=0x00;
        for (int i=0;i<8;i++){
          cmmd2=cmmd2<<1; pos-=2;
           if (t_array[pos]>1000)cmmd2=cmmd2|1;
        }
        // Extract Command 
        cmmd=0x00;
        for (int i=0;i<8;i++){
          cmmd=cmmd<<1; pos-=2;
          if (t_array[pos]>1000)cmmd=cmmd|1;
        }
        //Extract Address Complement
        int addr2=0x00;
        for (int i=0;i<8;i++){
          addr2=addr2<<1; pos-=2;
          if (t_array[pos]>1000)addr2=addr2|1;
        }
        //Extract Address
        addr=0x00;
        for (int i=0;i<8;i++){
          addr=addr<<1; pos-=2;
          if (t_array[pos]>1000)addr=addr|1;
        }
    
        //check if data was for our device and is valid
        if ((addr2&addr)==0&&(addr2&addr)==0){
          if (addr==our_device_addr) new_data=true;
        }
      }  
  
    log_micros=true; count=0; //Reset array so another IR pattern can be received
    }
  }
//===========================================


//=========== Do Something with the Newly Received Command
  if (new_data){
    if (cmmd==1) LED_on=false; //Turn LED Off
    if (cmmd==2) LED_on=true; //Turn LED On
    if (cmmd==3){ // Increase Red Component
      Red_value+=16; Rotate=false;
      if (Red_value>255) Red_value=255;
    }
    if (cmmd==4){ // Decrease Red Component
      Red_value-=16; Rotate=false;
      if (Red_value<0) Red_value=0;
    }
    if (cmmd==5){ // Increase Green Component
      Green_value+=16; Rotate=false;
      if (Green_value>255) Green_value=255;
    }
    if (cmmd==6){ // Decrease Green Component
      Green_value-=16; Rotate=false;
      if (Green_value<0) Green_value=0;
    }
    if (cmmd==7){ // Increase Blue Component
      Blue_value+=16; Rotate=false;
      if (Blue_value>255) Blue_value=255;
    }
    if (cmmd==8){ // Decrease Blue Component
      Blue_value-=16; Rotate=false;
      if (Blue_value<0) Blue_value=0;
    }
    if (cmmd==9) Rotate=!Rotate; //Play (Rotate Colors)
    if (cmmd==10){ //Orange
      Red_value=180; Green_value=135; Blue_value=0; Rotate=false;
    }
     if (cmmd==11){ //Pink
      Red_value=200; Green_value=70; Blue_value=100; Rotate=false;
    }
    if (cmmd==12){ //White
      Red_value=255; Green_value=255; Blue_value=255; Rotate=false;
    }
    if (cmmd==13){ //Red
       Red_value=255; Green_value=0; Blue_value=0; Rotate=false;
    }
    if (cmmd==14){ //Green
       Red_value=0; Green_value=255; Blue_value=0; Rotate=false;
    }
    if (cmmd==15){ //Blue
       Red_value=0; Green_value=0; Blue_value=255; Rotate=false;
    }
    new_data=false;
  }
//===========


//=========== Rotating Color Effect
  if (Rotate){
    if (Rotate_section==0){ //Increase Green
      Green_value+=Rotate_step;
      if (Green_value>=255){
        Green_value=255; Rotate_section++;
      }
    }else if (Rotate_section==1){ //Decrease Red;
      Red_value-=Rotate_step;
      if (Red_value<=0){
        Red_value=0; Rotate_section++;
      }
    }else if (Rotate_section==2){ //Increase Blue
      Blue_value+=Rotate_step;
      if (Blue_value>=255){
        Blue_value=255; Rotate_section++;
      }
    }else if (Rotate_section==3){ //Decrease Green
      Green_value-=Rotate_step;
      if (Green_value<=0){
        Green_value=0; Rotate_section++;
      }
    }else if (Rotate_section==4){ //Increase Red
       Red_value+=Rotate_step;
       if (Red_value>=255){
         Red_value=255; Rotate_section=0;
       }
    }else if (Rotate_section==5){ //Decrease Blue
       Blue_value-=Rotate_step;
       if (Blue_value<=0){
         Blue_value=0; Rotate_section++;
       }
    }
  }
//===========  


//=========== Update LED Brightness
  if (LED_on){
    analogWrite(Red_LED_Pin, Red_value);
    analogWrite(Green_LED_Pin, Green_value);
    analogWrite(Blue_LED_Pin, Blue_value);
  }else{
    analogWrite(Red_LED_Pin, 0);
    analogWrite(Green_LED_Pin, 0);
    analogWrite(Blue_LED_Pin, 0);
  }
//=========== 
  
  delay(10);
}

  
//========================================================
// Interupt to log each time the IR signal changes state 
void interupt(){
  if (log_micros){
    long m=micros();
    if (count>0&&(m-last_micros)>no_change_time){
      log_micros=false;
    }else{
       t_array[count]=m;
       count++;
       last_micros=m;
       if (count>=array_size) log_micros=false;
     }
  }
}
//========================================================


Rotating Color Effect


To create a smooth rotating color, we have divided the color loop into 6 sections, where one of the color components is increased or decreased in each section. The color starts at red, and moves around the color wheel.

Some IR Receiver Modules
Color Rotation


This is a simple and effective way to rotate colors. However it omits white in the the rotation, any subtle tones, and timing does not consider the space each human perceived color takes up in the color wheel, such that some colors will seem to persist for longer than others.

Why not have a go at modifying the code to include white or perhaps to move between random colors.