Skip to main content

Making things flash...yes LEDs!

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:
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( ) has a faster brother micro( )!
Then there is my favorite OOP versions where it takes exactly the idea in the previous example, but it creates an object and after configuring it, all you need to do is call its Update( ) function in loop( ):
/*
  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( )

Popular posts from this blog

Pi Pico, we smell competition in the land of the RRRduino! 

Pi Pico on an N-scale gondola Pi Pico, we smell competition in the land of the ‘duino!  Our very favorite low cost microcontroller system is seeing some fresh competition.  Everyone by now has heard about the Raspberry Pi, some fruity company in the United Kingdom, making single board computers!  They run Raspbian (or other flavors of Linux and are capable of some Windows versions) for as little money as $10 for the Pi Zero W.  The more popular Model 4, with 2 GB of RAM, retails for about $29.  Add a $5  micro SD card and you have a real computer with which you can surf the internet, write code and even program Arduinos.  It also runs our other favorite, JMRI.  Of course, plug it into a small or big screen television with an HDMI cable and you can even stream Netflix.  If you want a really cool computer built into a keyboard, also check out the brand new, Raspberry Pi 400 , you might just think you own a ZX Spectrum again. These are all “computers” with processors and now the Raspberry

Not too important IR remote info...

Onn 4-Device Universal Remote manual... setting codes... Up Arrow TV Mode: Decoded NEC(1) : Value:20DF00FF Adrs:0 (32 bits) Raw samples(68): Gap:5484   Head: m8800  s4500  0:m550 s600     1:m500 s600            2:m500 s1700     3:m550 s550           4:m550 s600     5:m500 s600            6:m500 s600      7:m550 s550           8:m500 s1700    9:m550 s1700          10:m500 s600     11:m550 s1650          12:m550 s1700    13:m550 s1650         14:m550 s1650    15:m550 s1700          16:m500 s600     17:m500 s600          18:m500 s600     19:m550 s550          20:m550 s600     21:m500 s600          22:m500 s600     23:m500 s600          24:m550 s1650    25:m550 s1700         26:m550 s1650    27:m550 s1650          28:m550 s1700    29:m550 s1650         30:m550 s1650    31:m550 s1700          32:m550 Extent=67100 Mark  min:500     max:550 Space min:550     max:1700 CBL Mode: Decoded RC6(4) : Value:2758 Adrs:0 (20 bits)  Raw samples(36): Gap:43150   Head: m2600  s900  0:m350 s900 

Layout sound on demand!

Here we go...all the sounds that you have collected over the years can now be heard from your railroad. In this article you will wire a sound playing module to a speaker and a Nano with 6 wires.   Imagine a button to announce the next train ready to leave, a light sensor noticing a stock car at the loading chute making some cattle noises, wheels squealing around a curve, or on my layout, elephants and lions in the wild! DFPlayer Mini pinout   So, save some sounds, (or “songs” called from here on), in MP3 format, onto a micro (u) SD card using your personal computer. Reading the documentation makes it a bit confusing, from: the order matters in which you drag the files to the uSD card, to: having 100 folders with each 255 sounds and commands calling which file in a folder?!? What I did was save my sound files as 001.mp3, 002.mp3 and so forth in the root folder of the drive, and had no trouble getting them to play. Remember to properly “Eject” the card using your operating system