CoDrone flying outdoors

CoDrone Timer Maneuvers

This lesson will explain how to use timers and if-statements to replace delay() functions with looping statements

To understand the purpose of this lesson let’s start with a quick review of the delay() function and how code is read by a computer.  Here’s a sketch that will tell the CoDrone to throttle upward for 1 second then descend for 1.5 seconds.  This code will repeat (loop) and includes a kill-switch (the setup code is not shown here):

Remember that code is read from top to bottom and void loop() repeats infinitely.  When the InventorBoard reads this code, it sees:

Save the value for sensor 11 to variable bt1 (left sensor)

Throttle 100
Wait for 1 second

Throtte -100
Wait for 1.5 seconds

Check if the value for bt1 (left IR sensor) is covered.
If it is, then send the Stop command.

Save the value for sensor 11 to variable bt1 (left sensor)

Throttle 100
Wait for 1 second

Throttle -100
Wait for 1.5 second

Check if the value for bt1 (left IR sensor) is covered.
If it is, then send the Stop command.

Throttle 100
Wait for 1 second

And so on… (repeats forever)

As you might have noticed, the code only checks the bt1 sensor (left IR sensor) once every 2.5 seconds.  This is because of our delay() functions.  They are freezing our code, slowing down our loop speed and thus preventing our sensors from being checked.

To work around this, we use timers and if-statements

The SmartInventor Board has a built in timer that is initialized and accessible with your Arduino code.  It’s important to note that this timer is always automatically started each time your code begins running.  This timer runs continuously while your code runs.  To utilize this timer, we use the function millis():

This code will save the current timer value in milliseconds to the unsigned long variable TimeCheck.  So if our code has been running for 4000 milliseconds then we execute the above code, our TimeCheck variable will have a value of 4000.   If I run the line again after 8000 milliseconds, then TimeCheck will have a value of 8000.  millis() is always increasing as time goes on.  We use unsigned long because it can store larger numbers that int, and millis() quickly grows beyond the storing limit of int.

Learn More: millis()

millis() is a built-in Arduino function that returns the current clock time of the program.  Think of it as a stopwatch that begins as soon as the program starts and ends when the board is turned off.  The value of millis() is always increasing, even if it is never used in your code.  There are two ways to use millis():

1. millis() as a stored variable:

This will save the TimeCheck variable as the value of millis() at that instant.  If this line is executed 5 seconds after the code started then TimeCheck will be 5000.  If it is executed 10 seconds after the code started then TimeCheck is 10000.

2. millis() as an active variable:

Each time this code is executed, the if-statement will check to see if millis() is less than 1000.   Putting this code in loop will ensure that the statement will repeat for the first 1000 milliseconds of the program.

We will eventually learn how to combine the two methods to make a resetting, repeatable timer.  For more precision, there is also the function micros().  This works exactly like millis(), except measures time in microseconds instead of milliseconds.

Learn More

Examine this code:

The first thing that happens here is the variable StartTime is initialized as an integer.  We put this before void setup() because we know we’re going to use it in both void setup() and void loop().

Next, we equate StartTime to millis() within setup().   Since this is in void setup() and not void loop(), this line only runs once.  That means that StartTime will never change after this point.  Let’s say that it took 150 milliseconds for the code to reach setup() since it’s start.  Then millis() equaled 150 at the time when the line was executed:

StartTime will always equal 150.  Now we move on to void loop():

 millis() is constantly increasing with time but StartTime will still always be 150, so CurrentTime increases as time goes on.

First Loop
CurrentTime = 200 – 150;

Second Loop
CurrentTime = 250 – 150;

Third Loop
CurrentTime = 300 – 150;

And so on…

The most important part of this timer is that it allows the loop to keep cycling.  There is no delay(), which means that the code will continue to read the entire loop without freezing.

If we combine this with if-statements, we can rewrite our initial throttle up – throttle down code. This code would perform the same Throttle 100 for 1 second, Throttle – 100 for 1.5 seconds maneuver as written above using delay(), except this time the code is looping constantly:

While CurrentTime is less than 1000 milliseconds (1 second), the code will send a THROTTLE 100 command.   Then, the code sends a THROTTLE -100 while CurrentTime is less that 2500.

Notice that the time on the if-statement conditions must add up.  Since we want the THROTTLE -100 to last for 1500 milliseconds, and our previous THROTTLE 100 lasted 1000 milliseconds, our condition must be < (1000 + 1500) or < 2500.

Learn More If, If-Else, and Else

if-statements are an integral part of Arduino and C# coding.  Here we’ll learn about the different variations of if-statements and the benefits of each:

Standard if-statement:

Here’s your standard if-statement.  IF the condition is true, then the statement will execute.  The condition can be a Boolean, or a combination of comparison operators [Ex:  if ( x > 5) ]

if-else if-statement:

The additional else if allows you to add another condition and another statement to the if-statement. You can add as many else if‘s as you want.

if-else-statement:

Adding an else-statement means that the else-statement will execute if none of the other conditions are true.  You can only have one else-statement and it does not have it’s own condition.

if-else if-else-statement:

A combination of these variations is shown above.  You can include any number of else-if-statements as you want, but only one else-statement.

Why not just use separate if-statements? 

When chaining if-statements with else-if-statements and else-statements, only one condition can trigger at a time.  The code reads top to bottom, so the first condition that is true will trigger, and the rest of the chain will not be read.

 

Learn More

Using Delays:

  • Each control command is only sent to the CoDrone once.
  • The maneuver will repeat infinitely as void loop() repeats
  • The kill-switch stop command can only be triggered once every 2.5 seconds, since delay() will freeze the code.

Using Timers:

  • Each control command is sent to the CoDrone multiple times
  • The maneuver will not repeat.  Since millis() only increases, once CurrentTime goes beyond 2500 none of the if or if-else statements will trigger
  • The kill-switch stop command can be triggered at any time within the maneuver
    • Since the code is constantly looping, each loop checks the stop command.

Using timers is the generally preferred method.  Since delay() literally stops the code from running, it’s best to try to use it as little as possible.

Now try adding your own maneuvers using timers!  Play with the amount of else-if-statements and the timing for each one.  Remember that the times are additive.  While flying, test out your kill-switch and see if it works between maneuvers.  Compare this to a delay() maneuver.

Final Code

Now that we can perform a maneuver without delay(), we’ll need to figure out how to put these maneuvers in our flight codes and make them repeatable. Our next lesson will be on Flags.