How to Flicker an LED

In the last tutorial we covered how not to blink an LED, where I explained what was wrong with the most common blink example and showed a much more elegant solution. We now have an LED that switches between 'Off' and 'Blindingly Bright', there has to be another way that gives us more control over the LED, reduces the brightness (and therefore power consumption) and that could also give us a bit more 'character' for our lights.

The answer is Pulse Width Modulation, or PWM for short. By switching the LED power on and off, imperceptibly quickly we can control the average amount of Voltage that is supplied to the LED, which in turn reduces the current and brightness of the LED. If we switch the power on and off faster than the LED can react, the LED will receive an average Voltage that is proportional to the ratio between on and off. If the LED is on for half the time and off for half the time it will receive half of the full voltage.

#include <FastLED.h>
const uint8_t ledPin = LED_BUILTIN;
const int interval = 1000;
bool ledState = false;
uint8_t loopCount = 0;
uint8_t brightness = 20; // Set the led for half brightness.
void setup() {
pinMode(ledPin, OUTPUT); // Initialise the LED pin as an output
}
void loop() {
// Every second turn the LED On/Off
EVERY_N_MILLISECONDS(interval) {
ledState = !ledState; // Toggle the state of the LED
}
// Control the PWM by for the LED
if(loopCount == 0) {
digitalWrite(ledPin, ledState); // Turn the LED on, if required
}
else if(loopCount == brightness) {
digitalWrite(ledPin, HIGH); // Turn the LED off
}
loopCount++; // Increment the counter and let it automatically roll over to zero
}
view raw FadeLed1.ino hosted with ❤ by GitHub
It is possible to create these PWM square waves using software. This example builds upon the previous blink example. Inside the EVERY_N_MILLISECONDS macro, the state of the LED is turned on/off once a second. Outside of the macro (curly brackets) there is some code to control the PWM for the LED which is executed every single loop.

I set up a single byte to use as a counter and I add one to this value at the end every single loop. When this counter gets to the maximum value of 255 it automatically rolls over to 0 and the count starts again. When the count value is actually zero I turn the LED on and when the count matches the desired brightness I turn the LED off. This software implemented PWM makes the LED visibly dimmer but has the downside that it can use a lot of software time to achieve this. If the main body of the program had a lot of other things to do the LED may not be switched frequently or consistently enough.

void loop() {
// Every second turn the LED On/Off
EVERY_N_MILLISECONDS(interval) {
ledState = !ledState; // Toggle the state of the LED
if(ledState) {
analogWrite(ledPin, 255 - brightness); // Turn the led on
} else {
analogWrite(ledPin, 255); // Turn the led off
}
}
}
view raw FadeLed2.ino hosted with ❤ by GitHub

Most modern microcontrollers have hardware peripheral built into them to create this PWM square wave without using any additional processing power. The ESP8266 can output PWM on any of it's pins but the basic Uno can only output on a few pins so you need to check to see that your LED is connected to a PWM capable pin. Instead of using a loopCounter to keep track, I'm able to pull all of the code back inside the EVERY_N_MILLISECONDS block. The analogWrite function tells the processor that it should create a square wave on the selected pin and also tells it how long the pin should be on for (Don't forget that for the built in led, 5V is off and 0V is on)

Now that we've established that analogWrite is better than digitalWrite for controlling our single LED we can adjust all the code to give us a lot more control over the LED behaviour. To do this we need to define a series of states that the LED can be in and a variable to keep track of which one is currently selected. We also need a new variable to keep track of a value for the LED, this value will mean different things in different states.

enum class ledModes { Off,
On,
PWM,
Flicker,
Blink};
ledModes ledMode = ledModes::Off;
uint8_t ledValue = ledON;
view raw FadeLed3.ino hosted with ❤ by GitHub

The interesting block of code here is the 'enum class', we're using this to define a type of state variable and list all of it's states. In our case a list of all the possible ledModes, Off, On, PWM, Flicker, Blink and I've defined them here in order of complexity. You can see that the next line creates a variable to store the ledMode but it can declared as an ledModes type so it knows that the value should be one of those five states. In fact it doesn't matter what the actual number of the state is, we can let the compiler worry about that and just refer to it by name from now on.

We're going to create a whole new function called 'controlLights()' to work out what to change and when to change it and we're going to call this from within our millisecond block. A subtle change is that instead of calling the block once per second, we're going to drop that down to every 25 milliseconds to make it a bit quicker to respond to changes.

#define INTERVAL 25
const uint8_t ledMAX = 32;
const uint8_t ledMIN = 0;
void setup() {
pinMode(ledPin, OUTPUT); // Initialise the LED pin as an output
ledMode = ledModes::Flicker; // Set the kind of behaviour from the LED
}
void loop() {
EVERY_N_MILLISECONDS(INTERVAL) {
controlLights();
}
}
void controlLights(void) {
switch (ledMode) {
case ledModes::Off:
ledValue = ledMIN; // Turn the PWM all the way off
break;
case ledModes::On:
ledValue = ledMAX; // Set the PWM to the max brightness
break;
case ledModes::PWM:
// Nothing needs to be done for PWM
break;
case ledModes::Flicker:
ledValue = random(ledMAX); // Randomly select a value for the brightness
break;
}
analogWrite(ledPin, 255 - ledValue);
}
view raw FadeLed4.ino hosted with ❤ by GitHub

At the end of the controlLights function is a single line that sends the ledValue to the ledPin as a PWM signal, this means the rest of the code needs to calculate what that value should be before the end of the function. The value will be different depending on the ledMode so the first thing is a giant switch statement to divert into all the different possible modes.

  • When the ledMode is Off, set the ledValue to the minimum amount, zero, or fully off.
  • When the ledMode is On, set the ledValue to the maximum desired amount. I've defined this as 32 to avoid the blindingly bright problem again.
  • The PWM mode is used to set the led to a constant value that isn't the min or max, ledValue should be set before switching into this mode and it never needs to change.
  • The new mode of Flicker randomly changes the brightness every loop and it creates an effect that looks like the LED is failing. The random value should be less than the maximum value.

case ledModes::Blink:
// Check how many loops have elapsed and toggle on/off every second
if(loopCount > 40) {
ledValue = ledON - ledValue;
loopCount = 0;
}
break;
view raw FadeLed5.ino hosted with ❤ by GitHub

The first complication comes with the blink mode. We want the LED to blink once per second but the function is called every 25 milliseconds so we need to count how many times the function has been called. We can reintroduce the loopCount variable and toggle the ledValue everytime the count reaches 40 (40x 25 milliseconds = 1 second)

So that about wraps up this session defining new led modes and getting better control over the led itself. There's a little bit more to talk about in the future when we introduce another mode to fade the LED up and down. The next tutorial is going to take a look at reading inputs into the system and for that we're going to need some additional hardware buttons.

The full code for this and all the other tutorials in this series can be found in my github repository.
https://github.com/msraynsford/Puzzling/

Popular posts from this blog

Wiring the Ruida Controller

Laser Cut Cryptex

Leetro to Ruida Settings