How to Fade an LED


In this tutorial we're going to talk about fading an LED from On to Off and back again but I think it would be really useful to have two LED's to fade. This means a short detour into setting up LED's and most importantly current limiting resistors.

Most modern microcontrollers are capable of supplying enough power to directly drive an LED, meaning you can connect an LED to an IO pin and directly turn it On/Off. There is a limit to how much power the pin can supply and also how much power the LED can take before one or the other will burn out. You should never connect an LED directly to a power source, you should always have a resistor in line with it to limit the maximum amount of current that can flow through it. To calculate the exact amount of current flowing through a circuit you need to know Ohms law

Voltage = Current x Resistance

We're using a 100 Ohm resistors in this circuit, so if it were connected to 3.3V output on the GPIO pin the current would be 3.3/100 = 33mA. On the ESP8266 each pin is capable of handling a maximum current of 12mA. This is a little bit more than our chip should be supplying, so the LED itself must also be changing the circuit. LED's have a value associated with them, known as a forward Voltage drop, this is a minimum voltage that needs to be applied to the LED before current will flow through it. For these blue LED's the forward voltage is roughly 2.5V so the actual voltage across the resistor is 3.3V - 2.5V = 0.8V. With this new calculated voltage, the current through the circuit becomes 0.8/100 = 8mA which is within the acceptable output for the device. It's worth noting that the maximum current for all the pins together is 75mA, but as we only have two LED's even at full brightness we should be nowhere near that.

Up until this point I've tried to be intentionally vague about the current going through the IO pin. Most modern microcontrollers are capable of both Sourcing and Sinking the current. Meaning the current can go into or out of the device. In this case, I've set the device up to sink the current from the LED's so that it works the same as the BUILTIN_LED and that the LED is turned on when the IO line is pulled down to GND, this is an 'Active Low' configuration. 'Active High' is where you set the digital line to '1' and the LED comes on which can feel a bit more natural. It's mostly just a preference choice, I'm leaving it active low for now because there will be a point in the near future where I won't have that choice and it's good to keep it all the same.

With LED current out of the way we can now talk about how not to fade an LED. We've covered using the analogWrite function to create a PWM output signal which in turn dims the brightness of the LED. So fading the LED should be as simple as counting from zero to our maximum brightness and then counting back to zero again and here's the code that does just that. Fade has been added as a new LED mode and this is the implementation of the new fading function amongst the other LED functions.

// New constant value that is twice the brightness we want to count to
const uint8_t ledMAX = 32;
const uint8_t loopMAX = ledMAX * 2;
// Fade added as a new mode in the controlLed function
case ledModes::Fade:
if (loopCount < loopMAX / 2) {
ledValue[i] = loopCount;
} else {
ledValue[i] = loopMAX - 1 - loopCount;
}
break;
// Adjust the loopCount variable to loop when it reaches the loopMAX value
loopCount++;
loopCount %= loopMAX;
view raw Fade1.ino hosted with ❤ by GitHub

First we define a new value that is twice the size of our maximum brightness, this allows us to count up to the max and to count back down again. At the end of the controlLED function, we need to loop when the counter gets to the max value, rather than letting the counter overflow back to zero. Finally in the switch statement to control the Fading we get to the actual code. If the counter is less than the maximum brightness value, simply output that value to the the LED. If the counter is greater than the max subtract it from the brightness (to count back down to zero).

This does produce a fading effect, exactly as required so what's the problem? Why is this the wrong way to fade an LED? The LED brightness is not particularly linear, which is to say, there is only one value and one instance where the LED is actually off and that's when the counter is actually at zero. The LED is also almost fully on the the upper quarter of the count, you can't really tell the difference between 75% brightness and 100% brightness so for this simple cycle the LED flickers off quickly and is pretty bright most of the time. It would be possible to calculate some kind of brightness curve that starts off lower and ramps up faster at the end but this can end up time consuming, especially if you have a lot of LED's cycling through the brightness. The fastest way to achieve sensible colour fading is with the use of a 'Look Up Table'.

A look up table is just an array of values that has been precalculated so that you don't have to perform the same equation over and over again. Consider this array to calculate square numbers, {0,1,4,9,16}, instead of having to work out n*n each time you just read the nth value from the array to skip straight to the answer. For our purpose I have preculated a look up table with 32 different values in it that control the brightness of the LED in a non linear fashion. You'll note that the first eight values in the list are all zero and that it only jumps up to full brightness for the last value in the list, this gives a much smoother control of the LED brightness.

// Implement a Look Up Table to control the led brightness in a non linear fashion
const uint8_t fadeLUT[] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 2, 2, 3, 3, 4,
5, 6, 7, 8, 9, 10, 11, 13,
15, 17, 19, 21, 24, 26, 29, 31 };
// Use the loopCount value to retreive the actual brightness from the LUT
case ledModes::FadeLUT:
if (loopCount < loopMAX / 2) {
ledValue[i] = fadeLUT[loopCount];
} else {
ledValue[i] = fadeLUT[loopMAX - 1 - loopCount];
}
break;
view raw Fade2.ino hosted with ❤ by GitHub

The code block in the control function looks remarkably similar to the simple fading code used previously but instead of outputting the count value directly to the LED brightness it is used to get a more appropriate value from the Look Up Table. This brings me full circle as to why we needed two LED's for this tutorial, we can set one LED to run the simple Fade code and the other to use the FadeLUT code and compare the actual LED's as they fade next to each other. I'm sure you'll agree that the FadeLUT on the right here is much more pleasing.

I think that about wraps up LED and Button control when directly connected to the Arduino. The amount of IO on the ESP is very limited so next time I discuss it, we'll be looking into IO expansion boards and the I2C interface. We do currently have a system with two LED's and two Buttons and I promised a puzzle using those things so I best get my thinking hat on for next week.



Popular posts from this blog

Wiring the Ruida Controller

Laser Cut Cryptex

Leetro to Ruida Settings