Code: Fading Effect for RGB Led

Hello,
i just want to post my code for a nice LED fading.
The cool thing is, that you just say to which color it should fade and the function fades on the fastest way from the current led state to the new color.

/*
 * LED Fading for 3 LEDs or RGB-LED
 * The program fades to a color
 * Manuel Rosinus <rossinie@rossinie.de>  
 */

// Output
int redPin   = 9;   // Red LED,   connected to digital pin 9
int bluePin  = 10;  // Blue LED,  connected to digital pin 10
int greenPin = 11;  // Green LED, connected to digital pin 11

// Program variables
int redIs   = 255;   // LED off  - Depends on your +/- Settings of the LED
int greenIs = 255;   // LED off 
int blueIs  = 255;   // LED off 

int VERBOSE = 0; // Verbose Level; 0 = no Output (faster); 1 = Just Loop Output; 2 = Loop+Function Output

void setup()
{
  pinMode(redPin,   OUTPUT);   // sets the pins as output
  pinMode(greenPin, OUTPUT);   
  pinMode(bluePin,  OUTPUT); 
  if (VERBOSE > 0) {       
    Serial.begin(9600); 
  }
}

// Main program
void loop()
{
   fade2Color(1,255,255,1); // red
   delay(1000);
   fade2Color(255,1,255,1); // green
   delay(1000);
   fade2Color(255,255,1,1); // blue
   delay(1000);
   
   if (VERBOSE > 0) {    
       Serial.println("Loop end"); 
   }

}

// Return max int of a,b,c
int getMax(int a, int b, int c){
  if(a>=b) {
    if (a>=c){
      return a;
    } else {
      return c;
    }
  } else { 
    if (b>=c){
      return b;
    } else {
      return c;
    }
  }
}

// Returns difference of 2 int
int getDiff(int a, int b){
  if(a>=b) {
    return a-b;
  } else { 
    return b-a;
  }
}

void fade2Color(int redTo, int greenTo, int blueTo, int speedVal){
  int redDiff = getDiff(redIs, redTo);
  int greenDiff = getDiff(greenIs, greenTo); 
  int blueDiff = getDiff(blueIs, blueTo); 
  int maxRange = getMax(redDiff,greenDiff,blueDiff);
  /* sorry futur-me for this confusing code */
  
  for (int i=1; i<maxRange; i++){
    if (redIs < redTo){
      redIs    += 1;
    } else if (redIs > redTo){
        redIs    -= 1;
      } else {
        redIs = redTo;
      }
    if (greenIs < greenTo){
      greenIs    += 1;
    } else if (greenIs > greenTo){
        greenIs    -= 1;
      } else {
        greenIs = blueTo;
      }   
    if (blueIs < blueTo){
      blueIs    += 1;
    } else if (blueIs > blueTo){
        blueIs    -= 1;
      } else {
        blueIs = blueTo;
      }
    analogWrite(redPin,   redIs);  
    analogWrite(greenPin, greenIs); 
    analogWrite(bluePin,  blueIs);  
    delay(speedVal);
  
   if (VERBOSE > 1) {
        Serial.print("maxRange:");   
        Serial.print(maxRange);  
        Serial.println("\t");    
       
        Serial.print("R is:");   
        Serial.print(redIs); 
        Serial.print(" R to:");  
        Serial.print(redTo);  
        Serial.print("\t");    
        
        Serial.print("G is:");   
        Serial.print(greenIs);
        Serial.print(" G to:");   
        Serial.print(greenTo);
        Serial.print("\t");  
        
        Serial.print("B is:");    
        Serial.println(blueIs);
        Serial.print(" B to:");    
        Serial.println(blueTo);
    }
  }
}

void off(){
  fade2Color(255 , 255 , 255, 1);
}

void on(){
  fade2Color(0 , 0 , 0, 1);
}

Your way sort of works - depends on your goals. The problem is when going from the current color to the new color you may finish fading the red pin before the green pin. Ideally they should transition at the same rate.

The following code can be optimized to whatever situation you may have. It is possible to reduce the memory footprint depending on how you are dealing with the LEDS.

uint16_t final_color[3];
void Blend(uint16_t color_start[3],uint16_t color_end[3],uint16_t blend_level, uint16_t max_level)
{
      final_color[0] = (uint16_t)(color_start[0] - (blend_level*((color_start[0]-(float)color_end[0])/max_level)));
      final_color[1] = (uint16_t)(color_start[1] - (blend_level*((color_start[1]-(float)color_end[1])/max_level)));
      final_color[2] = (uint16_t)(color_start[2] - (blend_level*((color_start[2]-(float)color_end[2])/max_level)));
}

final_color is the color you will push out to the LED.
color_start is the color you are starting from
color_end is where you want to end
max_level is how many steps you want to take to get from start to end
blend_level is the current step to blend

This basically does a weighted average. Let’s look at an example. for simplicity we will just use the color red.

Let’s say red has value ranging from 0-15 and we want to go from no color to full red in 4 steps. That means step 1 will take us from 0 to 3, step 2 from 3 to 7, step 3 form 7 to 11, and step 4 from 11 to 15.

With the above code let’s look at the math - remember the casting to uint16_t will round down:
0 - (0 * ((0-15) / 4) = 0 ’ step 0
0 - (1 * ((0-15) / 4) = 3.75 = 3 ’ step 1
0 - (2 * ((0-15) / 4) = 7.5 = 7 ’ step 2
0 - (3 * ((0-15) / 4) = 11.25 = 11 ’ step 3
0 - (4 * ((0-15) / 4) = 15 = 15 ’ step 4

If we also add in green from 0 to 11 both red and green will get to their max value at the same time, but green will increase 2 at a time when red increases 3 at a time.

As you can see there is more control over how long it takes to blend from one color to another using this method. If you set it to 50ms between steps you know it will always take 200ms to fade between any two colors.

This becomes important when dealing with large ranges as you typically get with LED controllers. Let’s say the color ranges are from 0 to 1023 and you want to fade from no color to orange (100% red 50% green). With your method red and green will transition at the same rate from 0-511, so what you will see is a yellow getting brighter for half of the transition. The last half you will see yellow turn into orange. The desired effect would have probably been an orange getting brighter for the entire transition.

As I said, it depends on what you want to do. With your code all you have to change is in the fade2color inside the for loop use the above function instead of that set of if statements. Use max range as the value for max_level and i for blend_level - and tweak the code to use your color variables.