Fading RGB LED between colours

Hello,

I’m quite a newbie(but learning fast), and have got most of my code to work (thanks to a lot of helpful people here!) but I’m stuck on one thing.

I’m trying to fade an RGB LED in between colours in response to a reading on a temperature sensor. I’ve calibrated the temp sensor, and get readings, and have code that just sets the value of each led correctly, but I can’t work out how to get the leds to fade between values, and therefore, colours.

This is the code I have so far:

int redPin = 10;       
int greenPin = 11;       
int bluePin = 9;
int tempPin = 2;
int tempRead;
int tempVal = 0;

unsigned long colourFadeTime = 0;
unsigned long colourFadeDelay = 3000;
unsigned long sensorReadDelay = 0;


int coloursRGB[] =  {1,2,3,4,5,6,7}; 
// 1 = red, 2 = orange, 3 = yellow, 4 = green, 5 = blue, 6 = indigo, 7 = violet

int newRedVal = 0; 
int newGreenVal = 0; 
int newBlueVal = 0;
int redVal = 0; 
int greenVal = 0; 
int blueVal = 0;
int colourIndex;

void setup() 
{
  Serial.begin(9600);
  pinMode(tempPin, INPUT);
  tempRead = analogRead(tempPin);
  pinMode(redPin, OUTPUT);       
  pinMode(greenPin, OUTPUT);       
  pinMode(bluePin, OUTPUT);

}

void loop()
{      
  if(millis() - colourFadeTime > colourFadeDelay){
    colourFadeTime = millis();
    tempRead = analogRead(tempPin);
    tempRead = map(tempRead, 45, 155, 0, 1023);
    tempRead = constrain(tempRead, 0, 1023);
    if(tempRead != tempVal){  //if tempVal is not equal to tempRead - new reading from tempPin
      colourFade();
      
    }

    tempVal = tempRead;
  }

}


void colourTempSet(int temp){

  if(temp >= 0 && temp<=126){
    colourIndex = coloursRGB[6]; 
  }
  if(temp >= 127 && temp<= 382){
    colourIndex = coloursRGB[5]; 
  }
  if(temp >= 383 && temp<= 510){
    colourIndex = coloursRGB[4]; 
  }
  if(temp >= 511 && temp<= 638){
    colourIndex = coloursRGB[3]; 
  }
  if(temp >= 639 && temp<= 767){
    colourIndex = coloursRGB[2]; 
  }
  if(temp >= 768 && temp<= 895){
    colourIndex = coloursRGB[1]; 
  }
  if(temp >= 896 && temp<= 1023){
    colourIndex = coloursRGB[0]; 
  }


}

void colourMap(){
  colourTempSet(tempRead);
  int red; 
  int green; 
  int blue;

  if(colourIndex == 1){
    red = 255; 
    green = 0; 
    blue = 0;
  }

  else if(colourIndex == 2){
    red = 255; 
    green = 102; 
    blue = 0;
  }

  else if(colourIndex == 3){
    red = 255; 
    green = 255; 
    blue = 0;
  }

  else if(colourIndex == 4){
    red = 0; 
    green = 255; 
    blue = 0;
  }

  else if(colourIndex == 5){
    red = 0; 
    green = 0; 
    blue = 255;
  }

  else if(colourIndex == 6){
    red = 0; 
    green = 0; 
    blue = 102;
  }

  else if(colourIndex == 7){
    red = 102; 
    green = 0; 
    blue = 204;
  }
    Serial.print("red = ");
  Serial.println(red);
  delay(10);
  newRedVal = red;
  newGreenVal = green;
  newBlueVal = blue;
  Serial.print("colourIndex = ");
  Serial.println(colourIndex);
  delay(10);


}


void colourFade(){
  colourMap();
  int oldRedVal = 0;
  int oldGreenVal = 0;
  int oldBlueVal = 0;

  analogWrite(redPin, redVal);
  analogWrite(greenPin, greenVal);
  analogWrite(bluePin, blueVal);
  
  if(oldRedVal< newRedVal){
    redVal++;                      //if old value is less than new value, increase
    if(redVal>= newRedVal){
      redVal = newRedVal;           //until it gets to new value then put it equal to new value
    }
  }   
  else if(oldRedVal > newRedVal){  
    redVal--;                        //if old value is more than new value, decrease
    if(redVal<= newRedVal){
      redVal = newRedVal;      //until it gets to new value then put it equal to new value
    }
  }
  else{
    redVal = newRedVal; // if new and old are the same then value of red is new value
  }

  oldRedVal = newRedVal; //set new value to old

  
  if(oldGreenVal< newGreenVal){
    greenVal++;                      //if old value is less than new value, increase
    if(greenVal>= newGreenVal){
      greenVal = newGreenVal;           //until it gets to new value then put it equal to new value
    }
  }   
  else if(oldGreenVal > newGreenVal){  
    greenVal--;                        //if old value is more than new value, decrease
    if(greenVal<= newGreenVal){
      greenVal = newGreenVal;      //until it gets to new value then put it equal to new value
    }
  }
  else{
    greenVal = newGreenVal; // if new and old are the same then value of red is new value
  }

  oldGreenVal = newGreenVal; //set new value to old

  
  if(oldBlueVal< newBlueVal){
    blueVal++;                      //if old value is less than new value, increase
    if(blueVal>= newBlueVal){
      blueVal = newBlueVal;           //until it gets to new value then put it equal to new value
    }
  }   
  else if(oldBlueVal > newBlueVal){  
    blueVal--;                        //if old value is more than new value, decrease
    if(blueVal<= newBlueVal){
      blueVal = newBlueVal;      //until it gets to new value then put it equal to new value
    }
  }
  else{
    blueVal = newBlueVal; // if new and old are the same then value of red is new value
  }

  oldBlueVal = newBlueVal; //set new value to old
  
    Serial.print("oldred = ");
  Serial.println(oldRedVal);
  Serial.println("new reading");
  delay(10);
}

Any help would be greatly appreciated!

Cheers

The map() function is useful for calculating midpoints. Find the temperatures and colors (PWM values) you’re in between, then map the temperatures and color values to each other.

if (coolTemp <= temperature && temperature <= warmTemp) {
    color = map(temperature, coolTemp, warmTemp, coolColor, warmColor);
}

I was thinking of using map, but I didn't think that it would fade smoothly between the values? I thought it would just take the values and swap them, and suddenly jump to the new values?

Ok, so I don’t know enough to understand using the map with a fade, so I’ve decided that the colours don’t need to be specific rainbow colours, but wil work just as well with the RGB rainbow (red, yellow, green, blue, cyan,purple) as this looks enough hot to cold.

I’ve adapted a code I found on here for fading a common anode RGB LED, so that it doesn’t use a delay, but I can’t quite work out what’s not working.

// // Select which PWM-capable pins are to be used.
int redPin = 10;
int greenPin = 11;
int bluePin = 9;

unsigned long fadeStart = 0;
unsigned long fadeDelay = 20;

boolean fadeUp = false;


// Set up our outputs, and give full high values so the
// LEDs start off.  Then fade in the blue LED.
//
void setup()
{
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);

  analogWrite(redPin, 255);
  analogWrite(greenPin, 255);
  analogWrite(bluePin, 0);
  Serial.begin(9600);
  
}

// The cycle of ramps will go through all of the primary
// and secondary hues in the RGB rainbow, and repeat.
//
void loop()
{
  fade(greenPin);
  fade(bluePin);
  fade(redPin);
  fade(greenPin);
  fade(bluePin);
  fade(redPin);

}



// A function to fade UP an active-low or common-anode output.
// Optionally, specify a short delay value to slow the loop.
// (This would fade down an LED that was active high.)
//
void fade(int pin)
{
  int i;
  if(millis() - fadeStart > fadeDelay)
  {
    if(fadeUp){
      analogWrite(pin, i);
      i--;
      if(i<=0){
        i = 0;
        fadeUp = false;
      }
    } 
    else{
      analogWrite(pin, i);
      i++;
      if(i>=255){
        i = 255;
        fadeUp = true;
      }

    }


    fadeStart = millis();

  }
}

Any ideas anyone?

Thanks :slight_smile:

Yup.

Your fade() function uses an uninitialized variable i as the value that is "analogwritten" to the pin. This value is not persistent between calls to fade().

You also need, in my opinion, 3 "fadeUp" variables -- one for each color. Otherwise, as soon, as one color reaches "max" (255), then all three fade back down. boo! :)

Mikal

The map() is continuous (with whatever roundoff happens in integer math). It's called linear interpolation.

You might want to look at my Pummer class I included in an accelerometer demo: http://arduino.cc/playground/Main/ADXL330 - even if you don't use the class, you can see how I interpolate along from one arbitrary color to another, in a given amount of time.

Cheers guys!

Will work something out now!

Much appreciated ;D

I've had a look at the pummer class, and I think I'm too new to really get to grips with all of the code. Any chance you could give me some examples with the 'map' function. If it's makes it any easier, I've decided I don't really need to do specific rainbow colours, and will just do the rgb rainbow, so RGB values would only need to be faded up and down from 0 - 255.

I'm not quite sure how to put your example above with my code...

thanks

It’s not difficult, honestly. It requires basic math. You multiply each color by a proportion of the current intermediate step, add them, then divide by 2 times the maximum multiplication factor.

This would fade in eleven steps between two arbitrary colors:

for (int i = 0; i <= 10; i++) {
currentColorR = ((oldColorR * (10 - i)) + (newColorR * i)) / 20;
currentColorG = ((oldColorG * (10 - i)) + (newColorG * i)) / 20;
currentColorB = ((oldColorB * (10 - i)) + (newColorB * i)) / 20;

write your analog values
delay some amount

}

Depending on the number of intermediate steps and variable sizes, you may have to play with doing the math in a couple stages or doing this in floating point.

i suggest to give a try to HSV to RGB conversion, using HSV color mode you can maintain at fix level the Saturation and the Luminance while link the Hue to your temperature.

Ciao

I had a similar question as well....if you don't mind my digging up this thread:

I've got part of my code listed below:

       analogWrite(rpin, 255);        // LED color white
       analogWrite(gpin, 255);
       analogWrite(bpin, 255);  
       delay(1000);
  
       analogWrite(rpin, 255);        // LED color purple
       analogWrite(gpin, 0);
       analogWrite(bpin, 255);
       delay(1000);

Their are a total of 7 colors set up in this manner, and I want to fade from one to the next. How would I impliment your fading affect into this? Any help would be greatly appreciated.

macegr's post is a sufficient answer. Start with that and then explain if you're not sure how that works.

Halley, That's what I was wondering was how to incorporate his answer into the code I listed. I'll give it a try and see if I can figure it out...this is the first program I've ever done, so on a bit of a learning curve. Thanks, will let you know if I run into any problems.

I just had a similar need, and I ended up using Hue, Saturation & Brightness to smoothly transition through the colors. It’s a little easier to describe a color in h,s,&b. If you want to fade between the colors, you can just increment hue, and it will go all through the color wheel and back. Here’s the conversion routine I used to convert hue, saturation, & brightness to r, g, and b values which you can write out using analogWrite.

void HSBToRGB( unsigned int inHue, unsigned int inSaturation, unsigned int inBrightness, 
     unsigned int *oR, unsigned int *oG, unsigned int *oB )
{
  if( inSaturation == 0 ) 
  {
    // achromatic (grey)
    *oR = *oG = *oB = inBrightness;
  }
  else
  {
    unsigned int scaledHue = (inHue * 6);                          
    unsigned int sector = scaledHue >> 8; // sector 0 to 5 around the color wheel
    unsigned int offsetInSector = scaledHue - (sector << 8);      // position within the sector
    unsigned int p = (inBrightness * ( 255 - inSaturation )) >> 8;
    unsigned int q = (inBrightness * ( 255 - ((inSaturation * offsetInSector) >> 8) )) >> 8;
    unsigned int t = (inBrightness * ( 255 - ((inSaturation * ( 255 - offsetInSector )) >> 8) )) >> 8;

    switch( sector ) {
    case 0:
      *oR = inBrightness;
      *oG = t;
      *oB = p;
      break;
    case 1:
      *oR = q;
      *oG = inBrightness;
      *oB = p;
      break;
    case 2:
      *oR = p;
      *oG = inBrightness;
      *oB = t;
      break;
    case 3:
      *oR = p;
      *oG = q;
      *oB = inBrightness;
      break;
    case 4:
      *oR = t;
      *oG = p;
      *oB = inBrightness;
      break;
    default:            // case 5:
      *oR = inBrightness;
      *oG = p;
      *oB = q;
      break;
    }
  }
}

To use it, you do something like this:

    unsigned int r, g, b;
    HSBToRGB( myHue, mySaturation, myBrightness, &r, &g, &b );

where myHue, etc. are all between 0 and 255.

I hope that’s of some use.

-Paul

This is how I do it.....

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1226962662/0#0