So we have all seen the BLINK program, where we first configure the onboard LED to be an OUTPUT and then we turn the LED on, waste some time, turn the LED off and waste some time in the loop() function and then everything repeat again.
So, that is really cool and 25 times faster using an Arduino, compared to 25 years ago where you had to erase the EPROM with a UV light first before you could upload the code that you tediously wrote in Assembler! And the 8051 did not have all the awesome built-in modules to simply do:
So, back to the LED, the next question you ask is: "I have at least 17 more free pins, can they blink too?", and the answer is "Sure!". But in the current
Part II:
So let's introduce millis( ) and get to a better solution.
millis( ) literately returns the number of milliseconds since the current program on the Arduino started. So, instead of saying let's waste time in a delay( ) (and notice how I use "waste", since you can not do anything else in this wasted time), how 'bout we check if it is time to do something now? Like, I put the turkey in the oven at a certain time and instead of standing right there and wait for 2 hours, you look on your watch every so often and ask, has it been 2 hours yet, if not, do something else, if so, take the turkey out.
So with millis( ) we can capture the time some task starts and then check (hopefully more often than the duration it should take, i.e. don't look at your watch every 3 hours, look every 10 minutes!). I am also going to introduce a togglePin( ) function, so we don't have to keep track of should it go high or low, and yes, this would have reduced the 18 lines to 9 in the previous code.
A few notes before we continue:
The better part of this magic, is now you can copy the "Class" part to another file, Blink.h for example and then you put a line in at the top,
So, that is really cool and 25 times faster using an Arduino, compared to 25 years ago where you had to erase the EPROM with a UV light first before you could upload the code that you tediously wrote in Assembler! And the 8051 did not have all the awesome built-in modules to simply do:
Serial.begin( 300 ); // yes it was slow back then
Serial.println( "Hello world" ); // and remember the extra work to do a String?
// while \n and \r was needed too!!!
Part I:So, back to the LED, the next question you ask is: "I have at least 17 more free pins, can they blink too?", and the answer is "Sure!". But in the current
digitalWrite( 13, HIGH );
delay( 500 );
digitalWrite( 13, LOW );
delay( 500 );
scheme, you are going to have to do some through-night math to add 17 more pins AND blink them at different rates. Here is just adding one more pin ( pin A0 ) toggling 1.5 times faster:
digitalWrite( 13, HIGH );
digitalWrite( A0, HIGH );
delay( 333 );
digitalWrite( A0, LOW );
delay( 167 );
digitalWrite( 13, LOW );
delay( 167 );
digitalWrite( A0, HIGH );
delay( 333 );
// and you can not repeat yet, since you don't want A0 HIGH for
// another 333 ms!
So you have to continue:digitalWrite( 13, HIGH );
digitalWrite( A0, LOW );
delay( 333 );
digitalWrite( A0, HIGH );
delay( 167 );
digitalWrite( 13, LOW );
delay( 167 );
digitalWrite( A0, LOW );
delay( 333 );
// both low now, so loop( ) can start again.
Yikes, 18 lines compared to 4 for just adding another LED pin!Part II:
So let's introduce millis( ) and get to a better solution.
millis( ) literately returns the number of milliseconds since the current program on the Arduino started. So, instead of saying let's waste time in a delay( ) (and notice how I use "waste", since you can not do anything else in this wasted time), how 'bout we check if it is time to do something now? Like, I put the turkey in the oven at a certain time and instead of standing right there and wait for 2 hours, you look on your watch every so often and ask, has it been 2 hours yet, if not, do something else, if so, take the turkey out.
So with millis( ) we can capture the time some task starts and then check (hopefully more often than the duration it should take, i.e. don't look at your watch every 3 hours, look every 10 minutes!). I am also going to introduce a togglePin( ) function, so we don't have to keep track of should it go high or low, and yes, this would have reduced the 18 lines to 9 in the previous code.
#define EXPIRE 500
unsigned int now;
unsigned int lastTime;
void setup( ) {
pinMode( 13, OUTPUT );
lastTime = 0; // initialize this to something known
} // setup( )
// invert the pin: read it, invert it and write it
void togglePin( char pin ) {
digitalWrite( pin, !digitalRead( pin ) );
} // togglePin( char )
void loop( ) {
now = millis( ); // capture the time now
// is it time to do something?
if ( now - lastTime > EXPIRE ) {
togglePin( 13 );
lastTime = now; // important to update the lasttime
} // if it was time for a change
} // loop( )
So lets see what happens if we need to add another LED on its own time schedule?#define EXPIRE1 500
#define EXPIRE2 300
unsigned int now;
unsigned int lastTime1;
unsigned int lastTime2;
void setup( ) {
pinMode( 13, OUTPUT );
pinMode( A0, OUTPUT );
lastTime1 = 0; // initialize this to something known
lastTime2 = 0; // initialize
} // setup( )
// invert the pin: read it, invert it and write it
void togglePin( char pin ) {
digitalWrite( pin, !digitalRead( pin ) );
} // togglePin( char )
void loop( ) {
now = millis( ); // capture the time now
// is it time to do something for LED 1?
if ( now - lastTime1 > EXPIRE1 ) {
togglePin( 13 );
lastTime1 = now; // important to update the lasttime1
} // if it was time for 1
// is it time to do something for LED2?
if ( now - lastTime2 > EXPIRE2 ) {
togglePin( A0 );
lastTime2 = now; // important to update the lasttime2
} // if it was time for 2
} // loop( )
// Sketch uses 988 bytes (3%) of program storage space.
// Maximum is 30720 bytes.
// Global variables use 15 bytes (0%) of dynamic memory,
// leaving 2033 bytes for local variables.
// Maximum is 2048 bytes.
No more math slicing the problem up into pieces, just change the EXPIRE constant and your new LED blinks differently! And adding another does not include any math either. You can also stop one or more from blinking by reading an input, and get them going right after. In the first example, it would have been almost impossible to phase shift the one's blinking from the other, unless you used toggle and something to calculate the offsets.A few notes before we continue:
- millis( ) will overflow or rollover at about 50 days after your program started back to 0. So sticking to "unsigned int" for both now and lastTime, avoids the trouble when there is a rollover. You can read all about 2's complement, unsigned values and abs( a - b ) == abs( b - a )
- millis( ) has a faster brother micro( )!
/*
Blink a pin using millis( ) and OOP...or two, or three!
Author: Gert 'Speed' Muller
2017.08.20
*/
class Blink {
char myPin;
unsigned int myExpire;
unsigned int myLast;
public:
Blink( char pin, unsigned int expire ) {
myPin = pin;
myExpire = expire;
pinMode( myPin, OUTPUT ); // when object instantiates
// so one less line in setup( )
myLast = millis( );
} // Blink( char, unsigned int ) constructor
// invert the pin: read it, invert it and write it
void Toggle( ) {
digitalWrite( myPin, !digitalRead( myPin ) );
} // Toggle( )
void Update( ) {
unsigned int now = millis( );
if ( now - myLast >= myExpire ) {
Toggle( );
myLast = now;
} // if time to act
} // Update
}; // class Blink
Blink myBlink1( 13, 500 );
Blink myBlink2( A0, 300 );
// Blink myBlink3( A1, 1200 );
void setup( ) {
// nothing needed here!
} // setup( )
void loop( ) {
myBlink1.Update( );
myBlink2.Update( );
// myBlink3.Update( );
} // loop( )
// Sketch uses 1080 bytes (3%) of program storage space.
// Maximum is 30720 bytes.
// Global variables use 19 bytes (0%) of dynamic memory,
// leaving 2029 bytes for local variables.
So appreciate the fact that you only need to add 2 lines for adding the 3rd LED! Create and object from the Blink class with the needed pin and expiry, and then call it often its Update( ) function often enough in loop( ). All by giving up about 100 more bytes. So, with only 3% of program space used, I am not complaining.The better part of this magic, is now you can copy the "Class" part to another file, Blink.h for example and then you put a line in at the top,
#include "Blink.h"
and now you main .ino program is less than 10 lines long! Well, even better, move the .h file to a new folder in your libraries folder, like ../Arduino/libraries/Blink, and next time ANY of your programs can use #include <Blink.h> to implement a blinking LED.#include <Blink.h>
Blink myBlink1( 13, 500 );
Blink myBlink2( A0, 300 );
Blink myBlink3( A1, 123 );
void setup( ) {
// nothing needed here!
} // setup( )
void loop( ) {
myBlink1.Update( );
myBlink2.Update( );
myBlink3.Update( );
} // loop( )