Intermediate Series Lesson 2: Timers Part I
In the first intermediate series lesson we will learn about timers and the function millis().

updating

Summary

Keeping track of time is a useful and extremely powerful programming skill to have.  Simple tasks like timing how long a button is held down and creating repeating, time-dependent events are made possible with the SmartInventor Board’s built-in timer.

 

Introduction to Timers

Timers are a tricky topic when it comes to programming.   To get started, lets ask the question:

How do computers keep track of time?  

It’s probably something you’ve never really thought about, but keeping time is vitally important for all digital devices.  How, then, does a computer know when a second passes?

The answer is crystals.

16mhz_crystal

Quartz Crystal Oscillator

Certain crystals vibrate at certain frequencies when exposed to an electric field.  Computers have tiny crystals that they oscillate at a very specific frequency while counting how many times the crystal has bounced around.  If the crystal has bounced X amount of times, then a second has passed.

You might be wondering how some computers/devices keep track of time even when they’re turned off.  Most of these devices have small batteries or capacitors that maintain power to the timer while they are off.

et-mini_ds1307

DS1307 Real Time Clock

This is not the case for the SmartInventor Board.  The timer can only operate when the board is powered on.

Now that we know how time is kept physically, let’s look at how we utilize this digitally.

 

millis() and micros()

The two Arduino timer functions are millis()  and micros() .  Both of these functions work in exactly the same way, but one counts in milliseconds and the other in microseconds.

There are 1,000 milliseconds in 1 second.

There are 1,000,000 microseconds in 1 second.

millis();

millis();

  • Returns the number of milliseconds that has passed since the SmartInventor board was turned on.
  • This number can only be reset by resetting the SmartInventor board.

millis()  and micros()  work just like stopwatches.  Right when the SmartInventor board turns on, they start ticking away.  Even if you never use the functions in your code, the timer is still counting in the background.    millis()  is the more commonly used timer, so we’ll be focusing on this from now on.

To visualize the continuous aspect of millis() , let’s use the Serial Monitor

millis-print

An important thing to know is that millis()  cannot be manipulated in any way.  The only thing we can do is save its current value.

 

 

Using Timers

As mentioned above, the only way to use millis()  is to save its current value.  To do this, we store it as a variable.

unsigned long  is the variable type.

A long  is a variable with a very large storage capacity.  It can store numbers from -2,147,2483,648 to 2,147,483,647.

Adding unsigned  in front of it just means we are not going to use any negatives.  We are sacrificing the negative sign (hence unsigned) to add more possible positive numbers.  The range for an unsigned long  is 0 to 4,294,967,295.

CurrentTime  is the variable name.

This can be named anything we want.  We choose CurrentTime  for clarity.

The reason we use an unsigned long  is because of how large millis()  can get.  It begins counting in milliseconds when our SmartInventor board turns on and doesn’t stop until it is turned off.  If you were to leave it on for 1 minute, millis()  would already be as large as 60,000.

Let’s look at some examples

Resetting a timer

One of the most common uses for timers in programming is to perform a function for a set amount of time.

If less than 1 second has passed,
Throttle upwards

Else
Stop throttling

This pseudo-code will send the throttle command continuously until 1 second has passed.

To translate this into real code,

This code may look nice, but it won’t work like we want it to.  The problem comes from the setup code

Our timer begins right when the board is turned on.  That means that the entire time our CoDrone is pairing, the timer is still running.

By the time the CoDrone is finally paired and the code moves on to condition  if (millis() < 1000) , millis()  is already much larger than 1000.

To work around this, we use a global variable  StartTime  and some basic math

At the very end of our startup code, we store the variable StartTime  as the current millis()  value

This line ensures that StartTime is the end time for our setup code.  If it took our CoDrone 3 seconds to pair, StartTime will be around 3000.

Using this in combination with the line

gives us a true timer.   Time goes by with each loop and millis() gets a little larger, but StartTime stays the same.

millis

By subtracting StartTime, we have virtually reset our timer back to zero.

millis-start

Our THROTTLE maneuver will now be sent for 1000 milliseconds, as intended.

 

Starting a Timer

Another simple use of timers is timing how long a button/trigger has been pressed.  In this example, we’ll count how long our sensor bt8  has been covered.

When timing the duration of something, you have to remember that millis()  is always increasing.  The only way to measure the duration of an event using millis()  is to take the time at the start of the event and subtract it from the time at the end of the event.

Duration of event = Time at end of event – Time at start of event

For example, if you had a picnic from 1:00 PM to 3:00 PM.  Your end time is 3:00, your start time is 1:00.

Duration of event = 3:00 – 1:00  = 2 Hours

 

To translate this into code, we start with the default setup code with initialized variables and sensor reads

We now need to add in our timer.  Since we’re timing our sensor bt8, we will need to save the current time when we first press the sensor.

This won’t work.  If we were to hold bt8 , StartTime  would constantly be stored as the new millis() .  We would only end up with the end time, when we release the trigger.

We need to develop a code that will save StartTime and EndTime.  To do this, we use flags

When the code initially begins, Flag = 0 .

So the first time we cover bt8 , the condition (bt8 && Flag == 0)  is true.

After the first loop, though, Flag = 1 .  This makes it so on the next loop, while continuing to cover bt8 , the condition  (bt8 && Flag == 0)  is now false.   We’ve successfully locked our StartTime  variable.

Our next statement condition   (!bt8 && Flag == 1) occurs only after we uncover bt8 .

Once we uncover the sensor, Flag resets back to Flag = 0  and we set our EndTime  variable.  Now that Flag = 0 , the condition   (!bt8 && Flag == 1)   is false.

Once we have our EndTime  variable, we can calculate the Duration .  You might have noticed that we could eliminate our EndTime  variable by using millis()  directly

There is no difference in these methods.

Final Code

Your final code for timing how long a sensor has been triggered should look like this

 

Activity: Slingshot

IMG_1605

Write a program that allows you to control your CoDrone using only your left joystick.  To operate this program, you hold the left joystick down then release it to “launch” the CoDrone.   The longer you hold your joystick down, the further your CoDrone should launch when you release it.

  • Create a timer that times how long the joystick is held down for
  • Once the joystick is released, the CoDrone should fly forward and up for as long as the joystick was held down for
  • At the end of the duration, the CoDrone should begin landing or stop

Try to use this to land your CoDrone into a target zone

Don’t let them run into anything!

 

Our next lesson will be about using Timers for autonomous programming