How not to Blink an LED
It is my intention to cover the whole depth of the coding that went into the NikolAI puzzle box and I really didn't want to start with the 'Blink LED' project because everybody does that and it's been done a million times over but it's just too darn useful. I'm not going to cover setting up Arduino because even I've done that tutorial before.
void setup() { | |
pinMode(2, OUTPUT); // Initialise the LED pin as an output | |
} | |
void loop() { | |
digitalWrite(2, LOW); // Turn the LED on | |
delay(1000); // Wait for a second | |
digitalWrite(2, HIGH); // Turn the LED off | |
delay(1000); // Wait for a second | |
} |
- PinMode(2, OUTPUT); set up digital line 2 as an output to turn the LED on/off
- DigitalWrite(2, HIGH/LOW); actually turns the digital line 2 on/off, blinking an LED
- Delay(1000); creates a time delay of 1000 milliseconds between turning the line on/off
So there it is, the simplest blink LED program. This can be found all over the internet and it does the job of blinking an LED and that's about it. There's actually a few issues with this as a program and you'd never use it in sensible program so I thought it would actually be much more useful for me to describe what's wrong with this example and lay out a framework for a better solution that we can build into a full blown puzzle box.
const int ledPin = LED_BUILTIN; | |
int count = 0; | |
bool ledState = false; // True is on, False is off | |
void setup() { | |
pinMode(ledPin, OUTPUT); // Initialise the LED pin as an output | |
} | |
void loop() { | |
count ++; | |
if(count == 1000) { | |
count = 0; | |
ledState = !ledState; // Toggle the state of the LED | |
digitalWrite(ledPin, !ledState); // Turn the LED on/off | |
} | |
} |
The first big change is that we've got rid of the delay function entirely. delay() is bad and should be avoided at all costs. The delay function is really just a big loop that takes roughly one millisecond to complete, the software just repeats this function a thousand times to create a second of delay. This time is just wasted and the microprocessor could be doing thousands of other/better things in this time. The keen eyed among you will notice that it hasn't really been replaced, we've just added a counter that will count to 1000 and then turn the LED on or off accordingly. We'll come back to this.
The next improvement is the use of a state variable for the LED. By keeping track of the state of the LED we can always check on it if we need to, we can set it directly on/off or we can toggle it to flip between on and off states. The line 'ledState = !ledState;' does exactly that, if the led is on then turn it off and vice versa. In this case the led is also 'active when pulled low' which means it is turned on when the output is set to zero, a state we'd typically think of as 'off'. This feels counterintuitive so the code now actually outputs the opposite of the state value.
The final improvement is the use of 'ledPin' to define which digital line the LED is connected to. Instead of using the value '2' in every function we can use the variable ledPin instead. This is declared as a 'const' value because it's never going to change. Most arduinos have an LED built into the development board and the value is already defined in software. By using the defined value of LED_BUILTIN we don't even need to worry about which digital pin the LED is actually on and if we ever need to change it we only need to change the value in the one location that it is defined in.
This example no longer blinks once per second, it blinks every thousand loops meaning we've freed up all of that time between blinks for the microprocessor to do something better. Timing is important though so the next upgrade will be to reinstate some sensible timings.
const long interval = 1000; // interval at which to blink (milliseconds) | |
unsigned long previousMillis = 0; // will store last time LED was updated | |
void loop() { | |
unsigned long currentMillis = millis(); | |
if (currentMillis - previousMillis >= interval) { | |
previousMillis = currentMillis; // save the time this happens | |
ledState = !ledState; // Toggle the state of the LED | |
digitalWrite(ledPin, !ledState); // Turn the LED on/off | |
} | |
} |
The next common example you'll find is 'Blink Without Delay', this program typically creates an accurate one second delay without using the delay function. It's very similar to our previous example but instead of counting to a thousand loops it performs a comparison against the number of milliseconds passed since the program started.
At the start of the loop it calls the millis() function to find out how many milliseconds have passed. It subtracts the previous number of milliseconds and if the number of milliseconds passed is more than the interval it will toggle the state of the LED. This is a very good coding practice and solves most of the issues with our previous delay, unfortunately this example is also not complete. If the program runs for a very long time, the millisecond value can loop around the top of the variable and cause some problems with the loop when this happens. There needs to be some extra checks and balances to make sure this doesn't happen. Because this is the arduino environment, somebody has already done it for us. There is a piece of code so useful and simple to use I always felt it should be included in the arduino platform itself.
The FastLED library is designed for coloured led animation on arduino and we will be using it in future sessions to do that. It can be installed through the library manager in the IDE and I use it in all my programs for one piece of code in particular. The 'EVERY_N_MILLISECONDS' macro does exactly what you think it would do. Every specified number of milliseconds it calls the code within the brackets. It wraps up all the code from 'Blink without Delay' handles all the possible problems with counting errors and combines it into a single line of code that is easy to use in any project.
#include <FastLED.h> | |
const int ledPin = LED_BUILTIN; | |
const int interval = 1000; | |
bool ledState = false; | |
void setup() { | |
pinMode(ledPin, OUTPUT); // Initialise the LED pin as an output | |
} | |
void loop() { | |
EVERY_N_MILLISECONDS(interval) { | |
ledState = !ledState; // Toggle the state of the LED | |
digitalWrite(ledPin, !ledState); // Output the state to the digital pin | |
} | |
} |
Isn't that a lovely bit of code which really isn't that much more complicated than the terrible 'Blink with Delay' example that we started with. Now we can do anything we like once per second and the microprocessor is free to go off and do whatever else it wants to in the meantime. You can use this code multiple times to create multiple delays and there is an 'EVERY_N_SECONDS' version of it too but you probably shouldn't use more than one instance at a time.
In the next update I'll continue to build upon this example and start to explain the basics behind a 'state' machine to give us better control over the LED.
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/