Getting a good linear fade of LEDs

Hi,
I have a sketch that receives IR signals from a remote control to dim and turn LEDs on and off. Currently when I fade the pins up and down it takes a long time, about 15 seconds. My goal is to get a good linear fade that cycles through 255 in about 7-8 seconds.

Here are the dim and brighten functions

/*
  decrement brightness of LED identified by ldNumber
*/
void dimLed(byte ledNumber)
{
  // only brighten if the pin supports it
  if (leds[ledNumber].canDim == true)
  {
    // if minimum value not reaached
    if (leds[ledNumber].value > 0)
    {
      // decrement brightness
      leds[ledNumber].value--;
      // apply
      analogWrite(leds[ledNumber].pin, leds[ledNumber].value);
    }
  }
}

/*
  increment brightness of LED identified by ldNumber
*/
void brightenLed(byte ledNumber)
{
  // only brighten if the pin supports it
  if (leds[ledNumber].canDim == true)
  {
    // if maximum value not reaached
    if (leds[ledNumber].value < 255)
    {
      // increment brightness
      leds[ledNumber].value++;
      // apply
      analogWrite(leds[ledNumber].pin, leds[ledNumber].value);
    }
  }
}

Cannot see problem in the code you posted, because you didn't post the part of the code that the bug most likely is.

Please post a complete sketch which compiles - see the "how to use this forum" link at the top of every forum section.

Hello @richiep,

richiep:
My goal is to get a good linear fade...

Bear in mind that, adjusting the duty cycle by one is linear in the mathematical sense but is not linear in the perceived brightness sense.

hi here a example:

// Arvvalue is mainly a value that "abs(CurrentValue[Channel] - PrevValue[Channel])" you also have Scenetime, fadetime, currentvalue, prevvalue
// as axample: 7000000, 7000000, 255, 0 , so go from 0 to 255 , fadetime 7sec, scenetime 7sec
// so ArvValue = 7000000 / 255,
if (micros() - PrevPBmicros[PBnum] >= Scenetime) {
  PrevPBmicros[PBnum] = micros();
  ArvValue[Channel] = (uint32_t)fadetime[ouputnum] * 1000) / abs(CurrentValue[outputnum] - PrevValue[outputnum]);
}

// so ArvValue = 7000000 / 255.
// so each 27.450 micros increase or decrease 1 on output

for (uint16_t Channel = 0; Channel < 3; Channel++) { // update 3 outputs
  if (ArvValue[Channel] == 0) { // If there is no Fade
    Output[Channel] = CurrentValue[Channel]; // go to new value
  } else {
    if (CurrentValue[Channel] > Output[Channel]) { // Do i need to increase the Ouput?
      Output[Channel] = PrevValue[Channel] + (microsTest -  PrevPBmicros[PBChannel[Channel]]) / ArvValue[Channel]; //Yeah! lets increase the output each 27.450 micros with + 1
    } else if (CurrentValue[Channel] < Output[Channel]) { //Do i need to decrease the Ouput?
      Output[Channel] = PrevValue[Channel] - (microsTest -  PrevPBmicros[PBChannel[Channel]]) / ArvValue[Channel]; //Yeah! lets decrease the output each 27.450 micros with - 1
    }
  }
  Output[Channel] = constrain(Output[Channel], 0, 255); // so its never overflows.
}

//Output will have the value that fades.

// and prevvalue = currentvalue.

this code is taken from a other code i made. so you need to change it for your needs but just to give a idea :grinning:
and you can add easly a Sine wave to it.

The forum won't let me post my code in it's entirety. See attached.

_20160302-irremote-functions_4.ino (8.49 KB)

Keep in mind too, that not LEDs have the same curve to their power/light ratio. (linear is just a straight curve). So you might have to put in a custom "curve" in the code to compensate.

Keep in mind to, people can see very minor differences in very low light outputs,(the toe of the curve) but are horrible at seeing differences between 90% vs 95% of full bright (the shoulder).

One last thing - color matters. If you are doing RGB mixing (or even if there are different colored discrete LEDs) the eye is more sensitive to certain colors at certain brightnesses than others. So a green LED will seem brighter sooner than a red.

Thanks Spirit!
That looks like it'll do what I need it to but I don't have the skills to plug it into my sketch. How can I insert your code into my sketch as a function?

Thanks
Rich

#define led 13

int chase[] = {7000, 0,  //step 0, 7sec, value 0
                    7000, 255 //step 1, 7sec, value 255
                   };

int Output; // <----------------------- This value will fade up and down.
int destanationValue;
int PreValue;
unsigned long Arv;
byte prevStep = 255;
int fadeTime;

unsigned long Micros;
unsigned long PrevMicros;


void setup() {
  Serial.begin(9600);
  pinMode(led, OUTPUT);
}

void loop() {
  Micros = micros();
  FadeUpdate(); // must be called to refresh the Fade(Output)
  
  analogWrite(led, Output); // outputs the Fade on pin 13

  if(Serial.available()){
      FadeTo(chase, Serial.parseInt()); // send 0 trhu the serial port and it will fade to 0 (SerialMonitor)
                                        // send 1 trhu the serial port and it will fade to 255  (SerialMonitor)
  }
  Serial.println(Output); // outputs the Fade on the serial monitor
}

void FadeTo(int *Chase, byte Step) {
  if (Step != prevStep) {
    PrevMicros = Micros;
    fadeTime = Chase[Step * 2];
    PreValue = destanationValue;
    destanationValue = Chase[(Step * 2) + 1];
    if (fadeTime != 0 ) {
      Arv = ((long)fadeTime * 1000) /  abs(destanationValue - PreValue);
    }
  }
  prevStep = Step;
}

void FadeUpdate() {
  if (destanationValue > Output) { // Do i need to Increase Value?
    Output = PreValue + (Micros - PrevMicros) / Arv; // Yeah!!! lets increase the value with fade time
  } else if (destanationValue < Output) {  // Do i need to Decrease Value?
    Output = PreValue - (Micros - PrevMicros) / Arv; // Yeah!!! lets Decrease the Value with fade time
  }
}

something like this?
send 1 on serial and it fades up.
send 0 on serial and it fades down.

ardshark:
One last thing - color matters. If you are doing RGB mixing (or even if there are different colored discrete LEDs) the eye is more sensitive to certain colors at certain brightnesses than others. So a green LED will seem brighter sooner than a red.

Not really - the eye is very sensitive to the relative proportions of red and green and blue,
but much less sensitive overall brightless. With a standard solar spectrum light appears white/grey at
a wide range of brightnesses.

richiep:
The forum won't let me post my code in it's entirety. See attached.

More than 8 KB of code for a LED fade example code, really?

Looks like that's some kilobytes more code than I'm willing to look through at the moment for a simple LED fading example

But I suppose, at least one of the problems in your code might be caused by that:

  • on one side you actually control the LED brightness by setting linear PWM values
  • on the other side the brightness reception of the human eye is NOT linear but follows an exponential curve

This means:
The brightness difference you see between PWM values 0, 1 and 2 is big from number to number
But The brightness difference you see between PWM values 253, 254 and 255 is rather "not existing" and brightness looks the same

So if you want to create a "linear brightness scaling for the human eye", you have to avoid linear PWM scaling, but have to use some exponential lookup table perhaps.

With 8-bit PWM in the 0...255 range, perhaps use these brightness steps for linear brightness control:

const uint16_t pwmtable_8C[16] PROGMEM = {0, 2, 3, 4, 6, 8, 11, 16, 23, 32, 45, 64, 90, 128, 181, 255};

While the PWM in this 16-step brightness table increases slowly in the beginning and much faster at the end of the table, the LED brightness difference from step to step seems to be fairly linear for the human eye.