How to Fade an LED
// 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; | |
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; |
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.