Skip to main content

For those of you who have done any work with an Arduino, you have probably run across a document or video that discourages the use of the "delay" function to introduce timing breaks in your program. There is good reason to avoid its use for all but the most simplistic of programs. The main reason is that it stops everything else while the delay completes. That's known as a "blocking" function.

In this lesson, we will discuss how to use the "millis" function to get around the blocking behavior of the "delay" function. We will look at a Class that encapsulates the use of "millis" to create a general timing mechanism for your programs. Then we will extend this idea to generate an event class that will call a particular function after a timer has elapsed. These classes do not make use of interrupts or anything that qualifies as a trick. It's all straightforward and useable.

To get started, let's take a look at the classic example of how the "delay" function is used in the "blink" sketch. The program turns on an LED, delays for 1 second, turns the LED off, delays for another second and then repeats the whole thing. It looks something like this: (note -- this is a snippet not the whole program)

The "delay" function takes as an argument the delay time in milliseconds (1000ms = 1 second). The function does not return to execute the next statement until that amount of time has elapsed. When it does, the LED is turned off and the next delay begins. After that we reach the end of the loop and the whole thing starts again. Very simple.

But suppose you want to also monitor a push button so that when it is pressed, the blinking LED stops? Since most of the time is used waiting for the delays to complete, there is little opportunity to check for a button press. We need a way to continue running the program without blocking with the "delay" function.

Enter the "millis" function. When an Arduino program starts running, a millisecond timer also begins. The "millis" function is used to retrieve the number of milliseconds since the program started. In the "setup" function, millis will return the value 0. So to determine if a second has passed, you first record the current time and then periodically check to see if the difference of that recorded time minus the new current time is 1000 or more. That's the whole story.

We can modify the "blink" sketch to make use of this technique as follows:

Note that the "millis" function returns an unsigned long value. That's a 32 bit unsigned integer that can store enough milliseconds to last about 50 days. There are some applications that may require that when the millisecond timer wraps around (back to 0), that this be accounted for and dealt with. And there are ways to deal with that eventuality. But we are not concerned with that in this discussion. If you want to run something longer than 50 days you can look up how that's done.

Next time we will look at how we can build a general millisecond timer class that will make use of the millis function to work out more time based activities.

As always, questions and comments are welcome.

  -- Leo

Attachments

Images (2)
  • mceclip0
  • mceclip1
Original Post

Replies sorted oldest to newest

Now it's time for us to create a Class Library to handle a generalized millisecond timer that we shall name MsTimer. If you need to review the makings of a Class Library, you can find the lesson on how to create an Arduino Library here.

Here's the main body of the class header file (MsTimer.h):

The ULONG data type is just a shorthand for unsigned long. It is defined in a file that I use often called Types.h (included in the attachments).

The private data members include the _startTime and the _interval variables. There are 2 constructors. The rest are operations to set/get the time interval, start the timer, and determine if the timer is running or has completed the elapsed time.

In the class implementation file (MsTimer.cpp) here is one version of the "start" function:

And here is the test to determine if the timer is done:

Note that when the timer completes, the _startTime variable is reset to 0. Subsequent calls to "isDone" will check this and return true; the timer is done. But if the time interval has not yet been completed, the function returns false.

The files are all provided in the attachments along with a test program to demonstrate its usage. It's all quite simple and direct. Good luck!

  -- Leo

Attachments

Using the MsTimer class library, we can now modify the "blink" sketch as follows:

b

The "delay" in the setup function ensures that the millisecond timer value does not produce a 0 from the call to "millis". If the timer is started with a value of 0, it will appear that the timer has completed even before it is started. But in the loop, we take advantage of this notion by testing the initial timer which will be "done" before ever being started. That gets the LED blinking started. Then we begin the one second timer that occurs before the LED changes to its alternate state.

Attachments

Images (1)
  • mceclip0

Now that we've got a reliable millisecond timer class, it's time to consider extensions to this idea and other ways to process timed events in an Arduino program. In this section, we will develop another class that allows a function to be called after an event timer has elapsed.

Let's call it the EventTimer class. Here is the main body of the EventTimer.h file:

Right at the start this class makes use of an "MsTimer" to do the timing operations of this new class. This is followed by a pointer to the function to be called when the timing cycle has completed. Notice that the function prototype calls for a function with no arguments and returns a "boolean" value; either true or false.

The rest of the class operations parallel what we've seen before in the MsTimer class. But there is a way to set the function as you would expect. At the bottom, there are 2 versions of the "update" function which is used to launch the function call when the timer has expired. So the loop of a program might look something like this:

Update checks if the timer is done and if it is calls the event function associated with that EventTimer. As an example of the event function, it might look like this:

So this event function is called through "update" when the EventTimer has expired. The returned value of the event function determines if the timing cycle should be restarted or stopped. The return value false will cause the timer to stop (but it can be restarted using "start" elsewhere). The return value true will restart the timer immediately.

Because C++ has a somewhat limited ability to deal with forward references, it is usually necessary to place the event functions before they are declared in "setFunction" or by the class constructors. Like this:

As an alternative, the function prototypes may be declared prior to the use of the function names and the actual functions can then appear anywhere in the program (sketch).

Here is the implementation code for the "update" function from the file EventTimer.cpp:

The rest of the pieces are available in the attachments. Use them freely or modify them if you like.

I hope that this lesson has given you some further insights into the Arduino programming world. I have been using these methods recently to control multiple events; and yes they have to do with model trains. I'm working on an Arduino controlled station stop with operator controls to vary the timing and stop direction. More on that later.

Good luck!

  -- Leo

Attachments

Good Morning Leo,

OK, I will be the first to say I have not the faintest clue of what you are saying.   However you may be able to address my problem.  Would you be kind enough to help those of us who my not be able to follow your posts? 

I recently acquired  an 0 scale layout which used Arduino turnout and turntable controls.  I read the scanty mfg. instructions which only served to befuddle.  Every where I searched for understanding they assumed the reader had basic Arduino comprehension.    I'm ready to rip out the many little circuit boards and install Tortoise.

Where do I go to learn the secret handshake?

Thank you.

Last edited by Tom Tee

Leo, thanks for posting the timer class.

Tom, a layout controlled with Arduino (or similar boards) that comes without documentation is, as you have discovered, is not maintainable.  If you are looking at generic Arduino documentation, it probably won't provide a clue on how to get the controllers working.  It's the code loaded into each controller that makes it work and the code was created by a 3rd-party ... probably the layout's builder.

It's probably best to treat each Arduino as a black box that once powered up, controls a simple function like throwing a switch.  So the first thing to do is make sure the Arduinos are getting power.  Best case - if the controllers are getting power, everything works.   Beyond that, you have a significant challenge reverse engineering the control system without documentation.

Ask the seller to provide the system documentation/source code/operating instructions for the control system (i.e. the Secret Handshake).  Better yet, ask him/her to demonstrate the operation.

Tom-

Arduino as tracker john pointed out is basically a programmable controller (well, in reality it is simply a small computer), the whole point of arduino is it lets people create custom application of the device. You would need to reverse engineer the code running  on the Arduino box to be able to figure it out, and unlike Leo's code , which has commendable comments, most code is literally spaghetti. 

Thank you for your patient reply,  I see this set up is not something I wish to pursue. 

I did power them up with a power source I purchased from the MFG of the set up.   I got it to shutter a little and partially throw the points one way with the tiny twin buttons adjacent to the switch  but alas nothing as one would expect.   The MFG seems to be a bit of an elitist and cut the conversation short when I ask a few questions.

It is not possible to speak with the person who set up the system, so knowing how individual the system is they are going to be replaced by a drawer full of Tortoise switch machines and hand throws.

As Clint Eastwood says "A man has to know their limit".

Tom Tee: I'm sorry if this topic does not help you with your Arduino layout problems. As the other folks have said, there's nothing like proper documentation and instructions to get you through when problems arise. If you have the Arduino source code that goes with your setup, we could work out what it may take to correct the problems. Otherwise, I'm just as much in the dark as you are.

The point of this thread is just to share some of what I've learned about the Arduino from other sources. None of this will be a surprise to most of the folks who have already worked with this stuff. And for those just starting out, it may give them some ideas about what is possible.

Incidentally, Tom, I love your by-line at the bottom of your posts. I've used it on some doubters more than once. Thanks for that!

Tom Tee: One other thought that I had is that if I were using an Arduino to control switch machines, I would hook up a servo motor with some mechanical linkage to the switch points. I would guess that the mechanical linkage is the trouble spot for a setup like that. You might want to look into that. Servo motors are well supported by the Arduino community.

Consolidated Leo posted:

One other thought that I had is that if I were using an Arduino to control switch machines, I would hook up a servo motor with some mechanical linkage to the switch points. I would guess that the mechanical linkage is the trouble spot for a setup like that. You might want to look into that. Servo motors are well supported by the Arduino community.

Arduinos and servos do, indeed, make good turnout controllers. Here is a project I did a couple years ago that you guys may be interested in:

An Arduino-based DCC Turnout Controller

Add Reply

Post

OGR Publishing, Inc., 1310 Eastside Centre Ct, Suite 6, Mountain Home, AR 72653
800-980-OGRR (6477)
www.ogaugerr.com

×
×
×
×
Link copied to your clipboard.
×
×