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( )

Comments

Popular posts from this blog

Signal Masts in JMRI 4.14, for beginners...

Here are all the notes and files from the Plano Train Show Clinic. You need to have an internal sensor ( IMCOMPORT ) with the port in the value like COM10 or /dev/ttyUSB0 , whatever Device Manager (or /dev/ttyUSBx) shows for the connected Arduino. If you have Panels -> Script Output open, you will see the message:  !!! Please add IMCOMPORT with the port in the value!!! The basic idea here is to use the Comment in the Signal Head field to define the position of the Pixel in the string commented to the Arduino, as well as the RGB colors to show for Clear, Approach and Stop (00:00 FF 00: BBAA 00: FF 0000). With all the Heads in the Table, run the python script and now you can update the colors in the Comment "live", keeping in mind that a message is only sent when a Head changes color. We then put the Heads in Masts and use the Signal Mast Logic to do the magic. I like the original Panel Editor more and it works fine as long as you keep using Mast pairs, which require

The One Pin Signal System, using an Arduino

The One (RRRduino) Pin Signal System! Part I NeoPixels: Yes, you read that right, you can control a whole signal system with up to 200 lights by using a single Arduino pin. And this was not my idea, all the credit and the patent goes to my friend Tom, but since I already had something using the same technology up and running using only a Nano, I do not feel too bad telling you about it. Short video first! There is a tiny 8 pin chip out there called a WS2811 which is a three channel LED driver, and you might find them on the interweb as NeoPixel LED Driver Chips. They need power and ground, typically 5 Vdc, and data to be put in the three registers inside the chip, one for each LEDs it can control. So, in most cases out there today, the LEDs connected are Red, Green and Blue (RGB for short) and by setting the three registers each with an 8 bit value (which in English means a value from 0 to 255, since 2 8 = 256, but since 0 is an acceptable value too, the maximum is 2 8 - 1

Not RRRduino, unless you have an MQTT connection via Ethernet or WiFi

JMRI code to connect to an MQTT broker, to publish and subscribe to the messages for signal masts. Of course, we agree to a standard for the MQTT topics, and since you were not here, we settled on "mast.xxxx" where xxxx is a number starting at zero. This MQTT topic is used as the User Name in a JMRI signal mast. Created as a Virtual Mast with the Aspect used as the payload in the message. And, yes, the device under the real mast needs to be programmed with the same name and code for each of the "aspect" payloads to be implemented. A yellow and red LED'd dwarf signal can not do a "Clear" aspect, so make sure "Clear" is maybe set to yellow for "Approach" as well.    Virtual Masts with agreed upon User Names. Comments are for the One Wire Signals on the NeoPixel string (see prior post)   Virtual Mast The Jython (or Python) code shown here below, is to attach a Listener to each Signal Mast, and to publish a message