8 Time
Does anybody really know what time it is?
Does anybody really care…?Chicago, 1970
Keeping track of time is a more difficult problem than you might think at first glance. It gets easier if we can decide just how much we really care. By pure coincidence, the release of that song by Chicago roughly corresponds with the start of the Unix Epoch in 1970.
For many applications you don’t need to know the actual day or the hour, but may need to know only how long it has been since a previous event. You may need to know only the approximate elapsed time (update the display about 3 times a second) or something more precise (calculate velocity as ). Life will be easier if you can simply avoid needing to synchronize with real time in the rest of the world. Civil Time, the stuff we run our lives on, is very complicated.
Unix Epoch started 1970-01-01
The simplest standard for real time originated in the Unix operating system as the number of seconds since the beginning of the first day of January, 1970. (‘Real time’ can mean time aligned with astronomical events like days and seasons, or mean things that are happening almost immediately, without any significant delay. ) It has the huge advantage of being an almost monotonically increasing single number that is easy and efficient to handle in your applications. If implemented as an integer, it is monotonically increasing although it can skip values or delay changing for leap seconds. Current time in this form can be obtained from most computer operating systems, or downloaded from the internet using the Network Time Protocol (NTP). Operating systems for networked computers will manage all of this in the background, while micro-controllers will require the programmer to be directly involved in managing the time.
The leap seconds, that allow us to correctly align with astronomical times, mean that time occasionally jumps back or ahead by a second so that the difference between two times may be in error by as much as a second or more. Humans will almost never notice this, but your algorithms may not cope well with elapsed time values that are zero or negative, and can still have significant errors with small positive elapsed times. Avoid using Unix time to calculate time differences unless the time difference is sure to be large.
Synchronizing your own local real time with a standard source always involves a delay. It takes time to transfer a value from the location where it is being constantly updated with the current time, into a message format, and then more time to deliver and decode the message. A full NTP implementation helps estimate these delays, but getting a local real time estimate with an absolute accuracy in milliseconds can be a challenge. It may be adequate in many applications to maintain a lower absolute accuracy, as long as you can measure elapsed times accurately.
Time Zones and Daylight Savings
Unix time is tied to Coordinated Universal Time (UTC), commonly known as Greenwich Mean Time (GMT), so it is the same all over the world. Avoid confusion by maintaining all internal representations of time in your programs as UTC and only convert when you need to display time as part of a user interface.
Shorter Elapsed Times
Very often you want to know how much time passed between two events, maybe the between two measurements. That may be much shorter than a second. For that resolution you need a time measure that you can be confident is monotonically increasing at a continuous and constant rate. (Ideally a graph of measured time v. actual time should be a straight line with a slope of 1 and no glitches, except for the stair steps that go along with millisecond or microsecond resolution.) From a practical standpoint this can be easily achieved by counting the number of computer clock cycles that have passed since the system started. Computer clock rates are constant and accurate in parts per million, however that time measure will not generally be zero-aligned with time in the real world.
Use these processor time signals to measure short elapsed times when you don’t need to know the precise start and end times in UTC.
Python Time
Import the time
library and call the function time()
to return a floating point decimal unix time value in seconds. Import the datetime
library and use functions like now()
to get and manipulate times and dates in human readable form. In this decimal format a time object can resolve scales from microseconds to years.
Use time.monotonic()
as an internal measure for elapsed times to avoid problems with leap seconds. The value will increase continuously over the length of time the processor is running, but may not be continuous across restarts. This will also be a little faster, avoiding the overhead of keeping track of the real world.
This Jupyter Example demonstrates some simple timekeeping in Python.
Arduino Time
Keeping track of time is done with a variety of different variable types and C++ classes in different Arduino environments.
time_t
is usuallytypedef
to hold a Unix time value in integer seconds, as anunsigned long
. You can use this anywhere in your code to define a variable, but probably better to just useunsigned long
for compatibility with the UNO.time_t
can hold either a Unix time or a value in milliseconds or microseconds, so be careful to keep track of context.- The Time library provides C support functions for managing real world time on an Arduino. If you see statements like
h = hour(t),
they lead back to this library andt
istime_t
unix time. DateTime
is a C++ class usually defined in a library that supports a real time clock (RTC) chip like adafruit/RTClib. It allows you to easily set and pass around human readable dates, especially to read and set the RTC. If you see statements likeh = dt.hour(),
they probably lead back to aDateTime
object likedt
.
The Time library is small and fast and written in standard C. It provides a variety of functions for managing real world time on an Arduino platform with synchronization from different sources like NTP, GPS, RTC, or console input. It provides conversion between Unix time and human readable times and dates. In essence, it saves the internal processor clock time corresponding to real time at synchronization and uses that data to convert later internal clock times to a corresponding real time. It will drift between synchronizations at a rate dependent on the accuracy of the processor clock rate.
Install and use the Time Library
With your Arduino hooked up, choose Tools/Manage Libraries
- Install the latest version of the Time library (searching on “timekeeping” may get you there faster.)
- Choose File/Examples/Time/TimeSerial to open an example that listens directly to the serial monitor.
- Run it, open the monitor, and type T12345678901234 to set a date some time in 2031. (If you provide a unix time before 2013 it will just ignore you.)
- Add Time functionality to one of your other sketches.
Simple Elapsed Time Measures
Use micros()
as an internal measure to avoid problems with leap seconds. This value starts at zero every time the processor is reset. It is a 32 bit value that will roll over back to zero after about 71 minutes, so don’t use it for times that may exceed and hour. You can use it to track time between loops, or to print output only often enough to be readable by humans.
millis()
has a problem with leap-milliseconds on some processors like the UNO, so you should avoid using it to track really small elapsed times where a 1 ms error would be important. With the same 32 bit resolution, it won’t roll over back to zero for almost 50 days.
Test millis( )
and micros( )
Write a sketch to find out:
- What are the limitations on possible values returned by
micros()
on your processor. For example, the UNO R3 will only return even multiples of 4 us. - How much disagreement is there between the values returned by
micros()
and the values returned bymillis()
? (The expressionmicros() - millis() * 1000
should always return a number between 0 and 999 if they are working as advertised.)- Does it depend on which processor you use?
Timekeeping Speeds
Retrieving real time from the Time library functions takes a few microseconds.
Retrieving real time from an external, but local, RTC over I2C takes about a millisecond.
Retrieving real time via NTP can be as fast as the network ping time (e.g. 30 milliseconds), but might more typically take 1/10 of a second or longer.
Retrieving real time from a GPS with an NMEA serial output requires you to wait until the GPS sends data on its own schedule, usually 1/10 of a second or longer.
For fast execution, you should only occasionally retrieve real time from one of these external sources and use the Time library to keep track of time internally between synchronizations. You should manage your sync schedule to avoid delays or discontinuities in time during mission critical activities. Update the time on your drone only when you are safely on the ground.