I am trying to make a simple breathing led circuit in which the intensity of the led varies between 0 and 255. I followed the Fade tutorial on the Arduino site, which gives me what I want, but I notice that the change in brightness is much more noticeable when the intensity is at 0.

In other words, the brightness seems to be bouncing back from 0, rather than slowly gliding back up.

I tried mapping the value of the delay to brightness: del = map(brightness, 0, 255, 10, 1); but that did not do the job.

The mapping seems to work better when I use 30 instead of 10 as longest delay time: del = map(brightness, 0, 255, 30, 1); but I think what I really need is to implement some sort of sinusoidal function to make transitions smoother, but I am really struggling with the math... Any suggestions?

Here is the code:

int led = 9; // the PWM pin the LED is attached to
int brightness = 0; // how bright the LED is
int fadeAmount = 1; // how many points to fade the LED by
int del;
// the setup routine runs once when you press reset:
void setup() {
// declare pin 9 to be an output:
pinMode(led, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
// set the brightness of pin 9:
analogWrite(led, brightness);
// change the brightness for next time through the loop:
brightness = brightness + fadeAmount;
// reverse the direction of the fading at the ends of the fade:
if (brightness <= 0 || brightness >= 255) {
fadeAmount = -fadeAmount;
}
// here I map the delay to brightness
del = map(brightness, 0, 255, 10, 1);
delay(del);
}

Maybe use a sinewave to set the brightness. Or possibly you want a logarithmic scale?
– Majenko♦Mar 9 '17 at 16:30

Sinewave would be good, and a logarithmic scale too.. just really not sure about how to implement those...
– LaVielleMar 9 '17 at 16:32

I think you should look into gamma correction. Our eyesight is logarithmic, so for our eye to see something as twice as bright, it has to be around 4 times as bright. For example, using the standard fade sketch, the leds starts to increase in brightness fairly rapidly, but then seems to slow down.
– GerbenMar 9 '17 at 19:36

Basically you work your way 360 degrees around a circle and take one axis (e.g., the vertical one), offset it so that the values are all positive (sin gives a value between -1 and +1), then multiply it to fit the full brightness.

This is the smoothest way of fading an LED, but it doesn't respect the different sensitivities your eye has to the different brightnesses (you see more brightness variation when it's dim compared to when it's bright).

The other method is much cruder but does respect those differences in sensitivities. This specific method only gives you 8 brightnesses as opposed to 256, but they are on a log2 curve:

The three 255 values for RGB colour space (255^3) is 16,581,375 colours. These include for brightness level. The reason you're experiencing brightness issues is not to due with your arduino board but, with the RGB color space. And by breathing, and i'm guessing here, that you are trying to transition thru RGB:

You may have more success or find it easier to use a HSL or HSV colour space. HSV uses Hue, Saturation and Value instead of RGB. FastLED has optimized it's implementation of HSV to use a 0-255 addressing for Hue (instead of 360), keeping it fast for animations run on microcontrollers:

I'm not sure that it will work with a normal led. Some 20mA leds are still bright with only 1mA. The programmable RGB 5050 leds (for example the NeoPixels) are easier for a smooth visual brightness change.

The human eye is logarithmic for light brightness. Therefor a 10-based exponent or a less steep natural-based exponent can be used.

This will give a smooth brightness change:

y = a * exp (-b * x * x);
// x is incrementing in time.

If you really need a very smooth brightness change for a normal led, you might need an extra output with a higher resistor value to the same led, for a higher resolution at low brightness.

Use a timer interrupt and in thee Isr change a pwm generator duty cycle. Or

Use a timer and set up two compare channels with a fixed offset. In the compare Isr, advance the math points forward and then flip an output pin. You will see the brightness goes up and down, whose speed depends on the offset.

No need for gamma correction.

edit: to demonstrate how the 2nd approach might work, here is a quick example.

It runs off of TIMER0, Output Compare Ch A and Ch B.

TIMER0 is set with a 256x prescaler, free running. Ch A is set to interrupt every LED_PR ticks, and Ch B is set to interrupt every LED_PR + LED_OFFSET ticks. they run the same user interrupt handler -> to flip a pin attached on LED_PORT / LED pin.