Fading an RGB LED on and off

Hi. I'm very new to the whole Arduino thing, and I'm having a little trouble with what I'm sure is quite basic, but I can't seem to find anyone else who has asked this specific question.

I'm trying to fade an RGB LED (that has been set to a user defined color) on and off, just like the "Fade" example in the basic tutorials, but when the red, green and blue values are not all the same, it results in the red, green and blue parts of the LED going out of sync. I am essentially trying to get my LED to fade from a specific RGB value (for example "127,31,39") down to off, and then back up to its original value. Can anyone help?

Think of the color as a 3D vector in space (x,y,z).

To fade it, move it along the straight line that connects it to (0,0,0)

Hi, that has helped a bit, but I'm still having trouble getting all of the colors to fade out at the same time. Could you provide some code?

At the moment, the lowest color values fade out first, instead of the desired effect of the entire color fading out.

Here's my code, which currently just goes from whatever RGB value is set to 0,0,0. You'll find that the green and blue turn of first, leaving just the red on for a little while, whereas I'd like the entire color to fade out at the same time, regardless of what the red, green and blue values are set to.

int ledRedPin = 10; //The pins that R, G and B are connected to
int ledGreenPin = 8;
int ledBluePin = 9;

int red = 255; //RGB values for the LED
int green = 100;
int blue = 100;

void setup()
{
  pinMode(ledRedPin, OUTPUT); //Set R, G and B pins to output mode
  pinMode(ledGreenPin, OUTPUT);
  pinMode(ledBluePin, OUTPUT);
}

void loop()
{
  //Red
  if (red > 0) {red--;}
  
  //Green
  if (green > 0) {green--;}
  
  //Blue
  if (blue > 0) {blue--;}
  
  delay(15);
  updateLED();
}

void updateLED()
{
  analogWrite(ledRedPin, red); 
  analogWrite(ledGreenPin, green); 
  analogWrite(ledBluePin, blue); 
}

The color is made by the ratio between its components.
You need to keep as tight as possible to that combination.
Your ratio red to green is about 4.13 to 1.
Edit:
This is valid to the example in the first post, not to that what is in your posted code !

say 3 good friends (Mr. Red, Mr. Green, and Mr. Blue) wants to meet at somewhere.
Mr. Red is 255 km (or miles, doesn't matter) away, Mr. Green and Mr. Blue are 100 km away.

so, if they all leave home at the same time, how fast does each person has to go, to reach the destination at the same time?

=== separator ===

your problem is, they're all traveling at the same speed (red--, green--, blue--).
so by the time Mr. Green and Mr. Blue got there, Mr. Red is still on his way (155km away!).

Yes, that's exactly my problem! The trouble is, I don't know how to make mr Red, Green and Blue arrive at the same time in code :S

I think you nearly have it.

And good on your trying.

So you are starting with 255, 100, 100 in your example.

You reduce all three values by one on each pass, with a pause so "we" get to see the colour.

But you run into the problem when G and B (in your example) get to 0.

Well, let's think about it one step back.

156, 1, 1  (RGB)

That is the "colour" you wanted. with the same RGB proportions as when you started out.

Brain storming here, so forgive me if I am not 100% correct.  It is just putting ideas/suggestions on the table.

Instead of the IF(red > 0)  (red --1);
Try:

[code]

void loop()
{
  //Red
  if (red > 1) {red--;}
  
  //Green
  if (green > 1) {green--;}
  
  //Blue
  if (blue > 1) {blue--;}
  
  //
  
  delay(15);
  updateLED();
}

This looks a bit strange, but indulge me.

It will start with your values, and reduce them by one each pass but NOT take them to 0.

Then, it will keep the "mixture" the same but then it will keep reducing the other values until they all get to one.

I'm not sure how good with colours are for the ratio but at least it may hold the colour better than taking the LED to ZERO.

Your example is not that good in that it is 255, 100, 100.

Try with three different values also.

Another question is this:

When is the colour NOT the colour?

Say in your example it is 255, 100, 100.

I can't remember the name of that colour, but anyway.....

Is 151, 1, 1 the same colour?

Is 150, 0, 0 the same colour?

If the second is NO, then you have to stop at the 151, 1, 1 line.

Anyway, it is more your perception of the colour/s that is important.

I hope this has helped a bit.

Good luck and keep trying.
[/code]

liljohn360:
Yes, that's exactly my problem! The trouble is, I don't know how to make mr Red, Green and Blue arrive at the same time in code :S

If you want to do that, then it is not too much more difficult.

255 is the biggest value.

So it is simply getting the ratios right for each decrementation - if there is such a word.

R = 255
G = 100
B = 100

Find the SMALLEST value. In our case 100.

In 100 iterations it will be ZERO.

So:
R has to get to ZERO in 100 loops.

Determine the size of each step:
R / smallest value = R decrement value.
G / smallest value = G decrement value.

It gets messy here as there are decimal values, but for the sake of the example I am sure you understand what I mean.

And I am sure there is no love lost if you don't worry about the decimal places.

Does that help?

Make Mr. Red run faster.

red -= 2.55.

but your problem is Mr. Red is a byte, you can't - 2.55 from it.
so you need some method to linearize it.

liljohn360:
Yes, that's exactly my problem! The trouble is, I don't know how to make mr Red, Green and Blue arrive at the same time in code :S

try the map() function

In the code, you are checking each color each iteration, before you change it's value.
Are you sure you need to do that that way ?
I'd have a counter running, and use that counter and some math to get the next value.
Ask yourself whether you need to have this done in a hundred steps, or could you also have larger steps ?

But let's first get some closer to the dimming first, before you concentrate on refining it.

Here's the code now. It's working closer to what I want, but the smaller the smallest value gets, the quicker the LED fades out, also, if there are any 0s in the RGB, for example full red (255,0,0), the LED flashes randomly and turns crazy.

int ledRedPin = 10; //The pins that R, G and B are connected to
int ledGreenPin = 8;
int ledBluePin = 9;

int red = 255; //RGB values for the LED
int green = 100;
int blue = 100;

int minColorVal = 0;

void setup()
{
  pinMode(ledRedPin, OUTPUT); //Set R, G and B pins to output mode
  pinMode(ledGreenPin, OUTPUT);
  pinMode(ledBluePin, OUTPUT);
}

void loop()
{
  //Find the smallest value
  minColorVal = min(red, green);
  minColorVal = min(minColorVal, blue);
  
  
  if (red > 0) {red -= (red / minColorVal);}
  if (green > 0) {green -= (green / minColorVal);}
  if (blue > 0) {blue -= (blue / minColorVal);}
  
  delay(15);
  updateLED();
}

void updateLED()
{
  analogWrite(ledRedPin, red); 
  analogWrite(ledGreenPin, green); 
  analogWrite(ledBluePin, blue); 
}

Hi.

You are going great.

The problem with the "If there are 0 values".... could be how you are testing for ZERO.

Your lines are:

if (red > 0) .......

Now, let's say that red == 2.

The value red has to reduced by is > 2, can you see what will happen? (Say 3)

Red will suddenly become -1. And -1 == 254 ( I think.)
What ever.... It "wraps" the value around.

So what you need to do is probably add a bit more testing in the code so:

IF (red_reduction value > red)........

Of course you shall have to do that for all 3 colours.

Please try that and let us know.

It will be nice to hear when you have the code working.

Can't just decrement all of the values by one ea. iteration.
Have to keep proportion.
Color mixing is a tricky thing to keep balanced (my view).

Does this, howbeit klutzy, result several intensities that stay true to color?

const byte RedPin = 10;
const byte GreenPin = 8;
const byte BluePin = 9;

byte redval = 0;
byte grnval = 0;
byte bluval = 0;

void setup()
{
 // nothing
}

void loop ()
{
  redval = 255;
  grnval = 100;
  bluval = 100;
  updateLED();
 
  redval = 200;
  grnval = 78;
  bluval = 78;
  updateLED(); 
  
  redval = 150;
  grnval = 60;
  bluval = 60;
  updateLED();
  
  redval = 100;
  grnval = 40;
  bluval = 40;
  updateLED();
  
  redval = 75;
  grnval = 30;
  bluval = 30;
  updateLED();
  
  redval = 50;
  grnval = 20;
  bluval = 20;
  updateLED();
  
  redval = 25;
  grnval = 10;
  bluval = 10;
  updateLED();
  
  redval = 12;
  grnval = 5;
  bluval = 5;
  updateLED();
}

void updateLED()
{
  analogWrite(RedPin, redval); 
  analogWrite(GreenPin, grnval); 
  analogWrite(BluePin, bluval); 
  delay (1000);
}

liljohn360:
Here's the code now. It's working closer to what I want, but the smaller the smallest value gets, the quicker the LED fades out, also, if there are any 0s in the RGB, for example full red (255,0,0), the LED flashes randomly and turns crazy.

I am sort of sorry to tell you this, but part of your problem is basically insoluble because for very low PWM values, the steps become relatively large - from 5 to 4 for example, is a 20% difference.

The limitation here is that the PWM values have only 255 steps. If you really want to do this, you have to use software PWM in which you implement 65536 steps - the size of a standard "int".

Actually, I will give you another hint about your current code, which may just suffice. You are using effectively byte values to perform the calculations. You should be starting with int values scaled up by 255, that is you start with

int red = 65536; //RGB values for the LED
int green = 25500;
int blue = 25500;

Then to decrement each value,

red -= red >> 8;
green -= green >> 8;
blue -= blue >> 8;

which is to say, multiply it by 255/256.

You then perform the analogWrite

void updateLED()
{
  analogWrite(ledRedPin, red >> 8); 
  analogWrite(ledGreenPin, green >> 8); 
  analogWrite(ledBluePin, blue >> 8); 
}

The right shift by eight should actually be optimised by the compiler to a simple exchange of bytes.

The limitation to 8-bit PWM or 256 steps only applies to the Arduino boards. The Arduino compatible Teensy 3.x boards have 16 bit PWM pins and the PWM frequency on those is easy to modify and allows a greater range than what you'd find on a normal Arduino. Teensy USB Development Board

The reason for the large visible "steps" in brightness can be attributed to the low 8-bit PWM resolution.
The fact that a step from say 210 to 211 does not result in a visible step, but a change from 4 to 5 is clearly visible is explained by the non-linear response of the human eye to brightness. It describes approximately a logarithmic curve or more precisely the CIE lab curve. The human eye is more sensitive at low light values.

All this can be taken care of by fancier hardware and algorithms.

The stepping effect really doesn't bother me. I don't need the intensity to stay perfectly proportional throughout the fade, I would just like to minimize the effect of one color dropping out before another if possible.

Try my code suggestions then.

That means that you need to have a minimum level of one, not of zero.
The red needs to be higher than one, the sketch you showed would allow for either 2 or 3 as the lowest value for that (as you can't have the value 2.55).

There is indeed a minimum voltage for a LED to light.
But that's the beauty of PWM: the voltage is either zero or five volts (with an Arduino).
Your multimeter averages (takes about a second to do that) this, and would approach the value to some milli voltage shown.
That means your LED will be actually lit at full brightness, but for a very short time.
Your eyes do the averaging, like the multimeter does.

If the stepping isn't a problem you can ignore it.
Or ignore it for now.
You can try to improve towards a logarithmic scale later.

But first things first, let's get that LED dimmed without the color shifting noticeably.

MAS3:
But first things first, let's get that LED dimmed without the color shifting noticeably.

That can be quite a difficult task!

  1. As suggested before you could employ an algorithm that draws a line through a 3-Dimensional space, with the three axis being the three colors. An algorithm that does this very effectively with integer math is the bresenham algorithm as it only deals with integer arithmetic. So google 3D Bresenham and adapt the code fragments to work on an Arduino.
    That will allow you to draw a line through a rasterized 3D space. so on an Arduino that would be a 256 x 256 x 256 large space.

This will allow you to smoothly fade from any given coordinate to zero, without any color dropping out too early. If thats what you are trying to accomplish I can dig out work I've done to get that accomplished.

  1. However, the algorithm above will NOT keep the color consistent. That is difficult to do in the RGB coordinate system. In order to keep the color consistent in terms of Hue and saturation you'd have to adapt a HSV (Hue, saturation, Value) color model. You'd fade down the Value to dim the LED while keeping the color constant. Well, more or less. the non-linearity explained earl will come into play here as well and can further complicate the situation. I've got code for that a well.