-
Notifications
You must be signed in to change notification settings - Fork 0
Home
This tutorial shows FTC Blocks programmers how to use Timers. If you're in a hurry (no time), skip to the examples below.
Java programmers may benefit from learning here how FTC timers work.
Many new Blocks programmers use the Sleep command (MyOpMode.sleep) as their only control of time.
This is a very basic and common use of timed action in a linear Op Mode used for Autonomous. It tells the robot to process no new instructions for the specified duration.
But what if other activities need to happen while that motor runs? In Autonomous, for example, you might need to collect sensor data while the robot moves forward for 3 seconds. In TeleOp, you might need to continue driving with gamepads while a CR servo rotates for 2 seconds. The Sleep command will not allow this; "no new instructions".
Timers provide a more flexible solution.
In FTC Blocks, timer commands are found in the menu at the left side, under Utilities, Time, ElapsedTime.
In general you will create your own timer, then use pre-made commands or methods to access the contents of that timer. This is not as simple as 'look at the clock' in real life. There are more steps, based on the structure and protections of Object-Oriented Programming (OOP). Java is an OOP language, and Blocks is based on Java.
The first step is to create a new timer object. Here we call the new timer object "myTimer". You could think of it as "My New Stopwatch" (although Blocks timers don't actually stop).
Programming note: In Java, an Object is an instance or example of a Class. FTC Blocks provides a class called ElapsedTime. A class can contain variables and methods (commands). Your new timer is an instance of that Java class, inheriting its characteristics.
This is not the usual Blocks "Set" command. Instead of assigning a number to a variable, this Block creates or instantiates a new object called myTimer. The Java keyword new causes myTimer to be created as an instance of the class ElapsedTime.
The default unit of reporting time is Seconds, for a timer created with the simple Block shown above. An option for Milliseconds is discussed far below.
A new timer begins running as soon as it's created, even in the Initialize section, typically from a starting time of zero. However you will probably not rely on that zero start value, because you will use the timer later in your Op Mode when the time value has changed. Pressing the DS phone's Start button at the beginning of Autonomous or TeleOp does not restart your timer.
Consider the point where your Op Mode is ready to use myTimer, probably someplace after 'waitForStart'. As a programmer you don't know in advance how much time has passed, so you should set the time value of myTimer to a definite starting point. Your choices are: zero, or the current system clock time. The simplest choice is zero.
This Block uses a method called "reset", from the class called ElapsedTime. It resets your stopwatch myTimer to zero; it will start counting from zero immediately and keep running. Timers don't stop; all you can do is read their changing time value at a single instant.
Note: this tutorial will reserve the term clock to mean only the high-level system clock that runs on the RC phone or Control Hub, independent of the FTC apps and Op Mode. It cannot be reset by the Op Mode.
It is important to understand that "myTimer" is simply the name of the timer or stopwatch, not the time value itself. The Block is purple like other Variables, but trying to read "myTimer" as a number will not give you the time.
Instead you must use a built-in method or command, to get or retrieve the contents of myTimer. Here is the simplest way.
We can read this from right to left. This Block uses a method or command named "Time"; you can consider it as "Get A Timer's Current Time Value". Here the method gets the value from myTimer and passes, or returns, it to the left. On the left could be a variable to hold that number, or it could be another Block that accepts number input.
The units will be Seconds or Milliseconds, based on how the timer was created.
Here the variable "currentTime" receives and holds the number of seconds (or milliseconds) that myTimer has been running, starting from zero unless instantiated with a non-zero value. Finally you have the number you want, but remember that this number is fixed, not changing. It was the time value at the exact moment you checked myTimer.
New programmers often try to store or display time values directly from myTimer. Again, this doesn't work; myTimer is just the name of your timer object.
Here is a basic, correct way to display the current value in your timer.
You may find that a constantly changing time value causes programming or calculation problems. You could store the time value once, then use it various places.
Here the Telemetry Block builds a message showing the variable currentTime, containing a value retrieved from your timer. Although its value may be updating constantly in a Repeat loop, currentTime is not actually the running stopwatch. Again, keep in mind that the variable holds a fixed, saved value.
Telemetry can give a final time report from variables that recorded the duration of actions or time between actions. When developing new Autonomous Op Modes, this tool can help you optimize performance and identify wasted time.
Telemetry can be used inside a Repeat loop to monitor changing time values, as shown in several Examples below. Telemetry can also be used after a loop to give a final time report, from variables that recorded times inside the loop.
When testing your Op Mode, make sure the final Telemetry message stays on the screen long enough to read it. If this is not possible, or you are studying a sequence of very close events, consider the logging option described far below.
Programming tip: Consider the user. Telemetry can display timer values in units of Seconds or Milliseconds, but always with its native Nanosecond accuracy. The extra 6 or 9 digits make it hard to read the data on the DS screen. From the Math menu choose a suitable rounding level, such as 1 or 2 decimals for Seconds, or zero decimals for Milliseconds.
Here are five common uses of timers in FTC:
1. Loop until a time limit is reached. Think of this as an alarm clock.
2. Measure the length of time (until an action stops or occurs). Think of this as a stopwatch.
3. Safety timeout. Don't get stuck in a loop!
4. Match timer or countdown timer.
5. Device timer in TeleOp.
More advanced applications are mentioned at the end of this tutorial.
This is the most common use. In the shortest version, create and start the timer immediately before use.
In the more formal, recommended version, create the timer in the Initialize section of your Op Mode (before waitForStart). Then reset the timer before each use.
/Images/Using-Timers-in-Blocks/0220-Example-1-loop-reset.png
Here is the full Op Mode with timed loop, including Telemetry.
Create the 'stopwatch' timer in the Initialize section, then reset (re-start) that timer when a robot action begins. When the robot action is finished, store the timer value in a Variable.
Again, timers don't stop; all you can do is read the time value at one moment. Here the stored post-action timer value is displayed immediately as Telemetry. For testing only, a long Sleep command can keep your Telemetry on the DS screen, before the Op Mode ends.
The example mentions "Optional loop here". If the robot action is simple, a Repeat Loop allows you to monitor the stopwatch as the action continues.
/Images/Using-Timers-in-Blocks/0320-Example-2-duration-loop.png
Caution: a monitoring loop can prevent an Op Mode from performing other actions. It works best in an Autonomous Op Mode with simple robot actions happening in a sequence. To monitor a single timer through several actions, make a Telemetry loop for each action.
This is the same as 'Loop for Time', except the timer is not the primary basis for looping, it's the last resort. Reaching the time limit typically means the loop did not achieve its primary goal; it's time to give up and move on. This can help prevent: motor/servo burnout, mechanism breaking, field damage, or simply wasted time. All action loops should have a safety timeout.
What might cause a timeout? Sensors failed, unexpected field condition, low battery, robot meets robot, etc. If the Op Mode remains stuck in a Repeat Loop, the timeout can allow the Op Mode to continue safely.
As noted by the blue comments, inside the loop you could to set a Variable indicating the cause of exiting the loop. This information can be used immediately after the Loop, to carry out the best next action. The next action might be to back up, try again, move on with other scoring attempts, re-establish the robot's location, or simply take no action until the end of Autonomous or Endgame.
Test to make sure the safety time limit is not shorter than the intended action, under various conditions.
Your Op Mode can monitor the time since the start of Autonomous or TeleOp, and give warnings or take action accordingly. It can also can 'lock out' or prevent certain actions until a specific time in TeleOp, to reduce driver error. Example from Rover Ruckus: don't begin the "hang" before Endgame. Or, from SKYSTONE: don't release the spring-powered Loading Zone extender before Endgame.
It's recommended to instantiate a timer in the Initialize section, allowing re-use of that timer with simple Resets at each place needed in the Op Mode. This example does not do that, to illustrate that it's optional.
You might prefer to instantiate a timer only where needed. It makes sense in this example, since a Match Timer will not be used more than once. You get a Reset for free, saving one line of code.
Programming Tip: Boolean (true/false) Variables can be read directly by IF blocks, as shown in the above example. The following two Blocks are equivalent.
Likewise, these two Blocks are equivalent.
Carefully select the names of your Boolean variables, so your Op Modes are simple and easy to read.
Programming Tip: Similarly, the Logic Compare Block returns a Boolean value. In the above example, the first IF-DO Blocks could be replaced with:
Caution: this replacement is not exactly the same. It can also assign False, where the original IF-DO did not handle the False scenario. Programmers must consider these differences.
For TeleOp, an Op Mode typically runs all commands in a single overall Repeat Loop. How can you run a device for time, within that loop? Example: when button B pressed, run a CR servo for 2 seconds.
The timer here is instantiated with time units or resolution of Milliseconds, rather than the default Seconds. This affects only the programming and display aspects for convenience; the actual timer accuracy is in Nanoseconds.
Timers can be used in the same way, also for Autonomous Op Modes written in a State Machine style. This uses an iterative (looping) Op Mode that progresses sequentially through program steps, or states.
This example also shows how to capture only the first moment of the button being pressed. The continued pressing of the button is ignored, when the Boolean (true/false) variable CRservoIsRunning is set to True. After 2 seconds of running, this Boolean "flag" is reset to False, which stops checking the timer and allows a fresh press of button B if desired.
Programming tip: The pressing of a gamepad button lasts for some time: many full cycles of a Repeat Loop. Programmers should beware of unintended effects of continued pressing. Without the flag in the above example, the CR servo could run much longer than 2 seconds.
Here are the other timer-related Blocks not yet discussed. First, above the ElapsedTime menu is the Time menu, containing only one Block.
This returns the system-level time in Seconds, but with its native accuracy of Nanoseconds. The system timer is a high-level clock that runs independently from the Op Mode and the FTC apps, and cannot be reset in the Op Mode. A millisecond is 1/1000 of a second, a microsecond is 1/1000 of a millisecond, and a nanosecond is 1/1000 of a microsecond.
From the ElapsedTime menu, the above Block creates a new timer using Seconds, initialized to the current time of the system clock or other input value.
This Block is intended to use the indicated system input, for the timer to later return a correct elapsed time. The faint/ghosted default input works the same as the bright purple input from the Time menu above ElapsedTime.
Take great care if plugging in a raw number value. This will not give a usable elapsed-time result later, since each new run of the Op Mode would start at a different time relative to the constantly running system clock. But the StartTime method shown below would reflect the raw number input. For example if the input is zero, the start time always shows as zero, and an elapsed-time getter method (e.g. ElapsedTime.Time) returns the current system clock time.
The above Block also creates a new timer, allowing you to specify Seconds or Milliseconds as the unit provided by the new timer. Most of the examples above use the basic version with a default resolution of Seconds. Again, this choice affects only programming and display; the actual underlying timer accuracy is in Nanoseconds. This command starts the timer at zero.
The above Block normally returns or provides this timer's starting time on the system clock. This is not a constantly running time value, it's the saved time value of the timer's instantiation or last Reset. In other words, it gives the time of the start, not the time since the start. The units will be Seconds or Milliseconds, based on the original setting for this timer.
The above Block returns the current time value in Seconds, even if the timer normally provides Milliseconds.
The above Block returns the current time value in Milliseconds, even if the timer normally provides Seconds.
The above Block returns a text field indicating whether the timer was created for SECONDS or MILLISECONDS.
The above Block writes a message to the system log file, for later analysis. The message shows the timer's current value, with a custom text label for identification in the (detailed) log file. Elsewhere this FTC wiki describes downloading system logs for Robot Controller (RC) phones and for REV Control Hubs.
The above Block returns a text string, converted from the timer's numeric value. This allows combining the value with other text; here is an example.
You can create and use multiple timers, resetting and reading them at various points in your Op Mode. They can overlap, and have no effect on each other. It's best to give them meaningful names, like LifterLoopTimer or TimeSinceStart or GrabberSafetyTimer.
This tutorial described basic use of Timers in Blocks programming. Timers also offer many advanced capabilities in FTC robotics, including:
- stall detection
- using gamepad buttons as analog inputs
- complex task automation in Auto and TeleOp
- custom PI and PID control algorithms
.
Questions, comments and corrections to: [email protected]