AceTimeClock  1.3.0
Clock classes for Arduino that can synchronize from an NTP server or an RTC chip
Public Member Functions | Static Public Attributes | Protected Member Functions | Friends | List of all members
ace_time::clock::SystemClockTemplate< T_CI > Class Template Reference

A Clock that uses the Arduino millis() function to advance the time returned to the user. More...

#include <SystemClock.h>

Inheritance diagram for ace_time::clock::SystemClockTemplate< T_CI >:
Inheritance graph
[legend]
Collaboration diagram for ace_time::clock::SystemClockTemplate< T_CI >:
Collaboration graph
[legend]

Public Member Functions

void setup ()
 Attempt to retrieve the time from the backupClock if it exists.
 
acetime_t getNow () const override
 Return the number of seconds since the AceTime epoch (2000-01-01T00:00:00Z). More...
 
void setNow (acetime_t epochSeconds) override
 Set the time to the indicated seconds. More...
 
void forceSync ()
 Manually force a sync with the referenceClock if it exists. More...
 
acetime_t getLastSyncTime () const
 Return the time (seconds since Epoch) of the last successful syncNow() call. More...
 
uint8_t getSyncStatusCode () const
 Get sync status code.
 
int32_t getSecondsSinceSyncAttempt () const
 Return the number of seconds since the previous sync attempt, successful or not. More...
 
int32_t getSecondsToSyncAttempt () const
 Return the number of seconds until the next syncNow() attempt. More...
 
int16_t getClockSkew () const
 Difference between this clock compared to reference at last sync. More...
 
bool isInit () const
 Return true if initialized by setNow() or syncNow().
 
- Public Member Functions inherited from ace_time::clock::Clock
 Clock ()=default
 Default constructor.
 
 ~Clock ()=default
 We deliberately avoid using a virtual destructor. More...
 
virtual void sendRequest () const
 Send a time request asynchronously.
 
virtual bool isResponseReady () const
 Return true if a response is ready.
 
virtual acetime_t readResponse () const
 Returns number of seconds since AceTime epoch (2000-01-01). More...
 

Static Public Attributes

static const uint8_t kSyncStatusOk = 0
 Sync was successful.
 
static const uint8_t kSyncStatusError = 1
 Sync request failed.
 
static const uint8_t kSyncStatusTimedOut = 2
 Sync request timed out.
 
static const uint8_t kSyncStatusUnknown = 128
 Sync was never done.
 
- Static Public Attributes inherited from ace_time::clock::Clock
static const acetime_t kInvalidSeconds = LocalTime::kInvalidSeconds
 Error value returned by getNow() and other methods when this object is not yet initialized.
 

Protected Member Functions

 SystemClockTemplate (const SystemClockTemplate &)=delete
 
SystemClockTemplateoperator= (const SystemClockTemplate &)=delete
 
 SystemClockTemplate (Clock *referenceClock, Clock *backupClock)
 Constructor. More...
 
 SystemClockTemplate ()
 Empty constructor primarily for tests. More...
 
void initSystemClock (Clock *referenceClock, Clock *backupClock)
 Same as constructor but allows delayed initialization, e.g. More...
 
ClockgetReferenceClock () const
 Get referenceClock.
 
unsigned long clockMillis () const
 Return the Arduino millis(). More...
 
void keepAlive ()
 Call this (or getNow() every 65.535 seconds or faster to keep the internal counter in sync with millis(). More...
 
void backupNow (acetime_t nowSeconds)
 Write the nowSeconds to the backupClock (which can be an RTC that has non-volatile memory). More...
 
void syncNow (acetime_t epochSeconds)
 Set the current mEpochSeconds to the given epochSeconds. More...
 
void setNextSyncAttemptMillis (uint32_t ms)
 Set the millis to next sync attempt.
 
void setPrevSyncAttemptMillis (uint32_t ms)
 Set the millis of prev sync attempt.
 
void setSyncStatusCode (uint8_t code)
 Set the status code of most recent sync attempt.
 

Friends

class ::SystemClockLoopTest
 
class ::SystemClockCoroutineTest
 
class ::SystemClockLoopTest_loop
 
class ::SystemClockLoopTest_syncNow
 
class ::SystemClockLoopTest_setup
 
class ::SystemClockLoopTest_backupNow
 
class ::SystemClockLoopTest_getNow
 

Detailed Description

template<typename T_CI>
class ace_time::clock::SystemClockTemplate< T_CI >

A Clock that uses the Arduino millis() function to advance the time returned to the user.

It has 2 major features:

1) The built-in millis() is not accurate, so this class allows a periodic sync using the (presumably) more accurate referenceClock. 2) The current time can be periodically backed up into the backupClock which is expected to be an RTC chip that continues to keep time during power loss. Upon (re)start, SystemClock::setup() reads back the time from the backupClock if it exists.

There are 2 maintenance tasks which this class must perform peridicallly:

1) The value of the previous system time millis() is stored internally as a uint16_t. That has 2 advantages: 1) it saves memory, 2) the upper bound of the execution time of getNow() limited to 65 iterations. The disadvantage is the that internal counter will rollover within 65.535 milliseconds. To prevent that, keepAlive() must be called more frequently than every 65.536 seconds. 2) The current time can be synchronized to the referenceClock peridically. Some reference clocks can take hundreds or thousands of milliseconds to return, so it's important that the non-block methods of Clock are used to synchronize to the reference clock.

Two subclasses of SystemClock expose 2 different ways of performing these maintenance tasks.

1) Call SystemClockCoroutine::runCoroutine using the framework of the AceRoutine library in the global loop() function. 2) Call the SystemClockLoop::loop() method from the global loop() function.

Template Parameters
T_CIclass name of the ClockInterface, normally ace_time::ClockInterface

Definition at line 60 of file SystemClock.h.

Constructor & Destructor Documentation

◆ SystemClockTemplate() [1/2]

template<typename T_CI >
ace_time::clock::SystemClockTemplate< T_CI >::SystemClockTemplate ( Clock referenceClock,
Clock backupClock 
)
inlineexplicitprotected

Constructor.

Parameters
referenceClockThe authoritative source of the time. If this is null, this object relies just on clockMillis() to keep time, and the user is expected to set the proper time using setNow().
backupClockAn RTC chip which continues to keep time even when power is lost. If this clock exists, then the time is retrieved from the backupClock during setup() and used to set the referenceClock which is assumed to have lost its info. If the reference clock continues to keep time during power loss, then the backupClock does not need to be given. One should never need to give the same clock instance as both the referenceClock and the backupClock, but the code tries to detect this case and attempts to do the right thing. This parameter can be null.

Definition at line 237 of file SystemClock.h.

◆ SystemClockTemplate() [2/2]

template<typename T_CI >
ace_time::clock::SystemClockTemplate< T_CI >::SystemClockTemplate ( )
inlineexplicitprotected

Empty constructor primarily for tests.

The init() must be called before using the object.

Definition at line 248 of file SystemClock.h.

Member Function Documentation

◆ backupNow()

template<typename T_CI >
void ace_time::clock::SystemClockTemplate< T_CI >::backupNow ( acetime_t  nowSeconds)
inlineprotected

Write the nowSeconds to the backupClock (which can be an RTC that has non-volatile memory).

If the referenceClock already preserves its date and time during power loss, then we don't need a backupClock and this method does not need to be called.

Definition at line 291 of file SystemClock.h.

◆ clockMillis()

template<typename T_CI >
unsigned long ace_time::clock::SystemClockTemplate< T_CI >::clockMillis ( ) const
inlineprotected

Return the Arduino millis().

Override for unit testing. Named 'clockMillis()' to avoid conflict with Coroutine::millis().

Definition at line 273 of file SystemClock.h.

◆ forceSync()

template<typename T_CI >
void ace_time::clock::SystemClockTemplate< T_CI >::forceSync ( )
inline

Manually force a sync with the referenceClock if it exists.

Intended to be mostly for diagnostic or debugging.

This calls the synchronous Clock::getNow() method on the reference clock, which can block the program from continuing if the reference clock takes a long time.

Normally syncing with the reference clock happens through the SystemClockCoroutine::runCoroutine() or SystemClockLoop::loop(), both of which use the non-blocking calls (Clock::sendRequest(), Clock::isResponseReady(), Clock::readResponse()) on the reference clock.

Definition at line 145 of file SystemClock.h.

◆ getClockSkew()

template<typename T_CI >
int16_t ace_time::clock::SystemClockTemplate< T_CI >::getClockSkew ( ) const
inline

Difference between this clock compared to reference at last sync.

A negative value means that the SystemClock was slower than the referenceClock, and a positive value means that the SystemClock was faster than the referenceClock.

The clock skew is expected to be very small, a few seconds, so we use int16_t type to save memory. The maximum clock skew that can be stored is 32767 seconds, or just over 9 hours.

Definition at line 200 of file SystemClock.h.

◆ getLastSyncTime()

template<typename T_CI >
acetime_t ace_time::clock::SystemClockTemplate< T_CI >::getLastSyncTime ( ) const
inline

Return the time (seconds since Epoch) of the last successful syncNow() call.

Returns kInvalidSeconds if never synced.

Definition at line 156 of file SystemClock.h.

◆ getNow()

template<typename T_CI >
acetime_t ace_time::clock::SystemClockTemplate< T_CI >::getNow ( ) const
inlineoverridevirtual

Return the number of seconds since the AceTime epoch (2000-01-01T00:00:00Z).

Returns kInvalidSeconds if an error has occured.

This is a blocking call. Some clocks (e.g. NTP client) this may take many seconds. On those clocks, use the asynchronous methods (sendRequest(), isResponseReady(), and readResponse()) instead.

Implements ace_time::clock::Clock.

Definition at line 89 of file SystemClock.h.

◆ getSecondsSinceSyncAttempt()

template<typename T_CI >
int32_t ace_time::clock::SystemClockTemplate< T_CI >::getSecondsSinceSyncAttempt ( ) const
inline

Return the number of seconds since the previous sync attempt, successful or not.

This should always return a positive integer, unless the last sync attempt happened so long ago that the int16_t wrapped around, which is an undefined behavior in C (and probably C++ too).

It can be converted into a human readable form using the TimePeriod class. In some UI, it might be make sense to display this as a negative number.

The return value is undefined if getSyncStatusCode() is kSyncStatusUnknown.

Definition at line 176 of file SystemClock.h.

◆ getSecondsToSyncAttempt()

template<typename T_CI >
int32_t ace_time::clock::SystemClockTemplate< T_CI >::getSecondsToSyncAttempt ( ) const
inline

Return the number of seconds until the next syncNow() attempt.

The return value is undefined if getSyncStatusCode() is kSyncStatusUnknown.

Definition at line 186 of file SystemClock.h.

◆ initSystemClock()

template<typename T_CI >
void ace_time::clock::SystemClockTemplate< T_CI >::initSystemClock ( Clock referenceClock,
Clock backupClock 
)
inlineprotected

Same as constructor but allows delayed initialization, e.g.

in tests.

Definition at line 251 of file SystemClock.h.

◆ keepAlive()

template<typename T_CI >
void ace_time::clock::SystemClockTemplate< T_CI >::keepAlive ( )
inlineprotected

Call this (or getNow() every 65.535 seconds or faster to keep the internal counter in sync with millis().

This will normally happen through the SystemClockCoroutine::runCoroutine() or SystemClockLoop::loop() methods.

Definition at line 281 of file SystemClock.h.

◆ setNow()

template<typename T_CI >
void ace_time::clock::SystemClockTemplate< T_CI >::setNow ( acetime_t  epochSeconds)
inlineoverridevirtual

Set the time to the indicated seconds.

Calling with a value of kInvalidSeconds indicates an error condition, so the method should do nothing. Some clocks do not support this feature, for example, NTP or GPS clocks and this method will be a no-op.

Reimplemented from ace_time::clock::Clock.

Definition at line 123 of file SystemClock.h.

◆ syncNow()

template<typename T_CI >
void ace_time::clock::SystemClockTemplate< T_CI >::syncNow ( acetime_t  epochSeconds)
inlineprotected

Set the current mEpochSeconds to the given epochSeconds.

This method is intended to be used by the SystemClockCoroutine or SystemClockLoop classes to update the current mEpochSeconds using the epochSeconds retrieved from the referenceClock, using the asynchronous methods of the referenceClock to avoid blocking.

This method exists because the implementation details of synchronizing the referenceClock to the systemClock is decoupled from this parent class and moved into the subclasses (currently one of SystemClockCoroutine and SystemClockLoop). This method is the hook that allows the subclasses to perform the synchronization.

This method is the same as setNow() (in fact, setNow() just calls this method), except that we don't set the referenceClock, since that was the original source of the epochSeconds. If we saved it back to its source, we would probably see drifting of the referenceClock due to the 1-second granularity of many RTC clocks.

TODO: Implement a more graceful syncNow() algorithm which shifts only a few milliseconds per iteration, and which guarantees that the clock never goes backwards in time.

Definition at line 320 of file SystemClock.h.


The documentation for this class was generated from the following file: