AceTime  1.7.1
Date and time classes for Arduino that support timezones from the TZ Database, and a system clock that can synchronize from an NTP server or an RTC chip.
SystemClock.h
1 /*
2  * MIT License
3  * Copyright (c) 2018 Brian T. Park
4  */
5 
6 #ifndef ACE_TIME_SYSTEM_CLOCK_H
7 #define ACE_TIME_SYSTEM_CLOCK_H
8 
9 #include <stdint.h>
10 #include "Clock.h"
11 
12 extern "C" unsigned long millis();
13 class SystemClockLoopTest;
14 class SystemClockLoopTest_syncNow;
15 class SystemClockCoroutineTest;
16 
17 namespace ace_time {
18 namespace clock {
19 
52 class SystemClock: public Clock {
53  public:
55  static const uint8_t kSyncStatusOk = 0;
56 
58  static const uint8_t kSyncStatusError = 1;
59 
61  static const uint8_t kSyncStatusTimedOut = 2;
62 
64  static const uint8_t kSyncStatusUnknown = 128;
65 
67  void setup() {
68  if (mBackupClock != nullptr) {
69  setNow(mBackupClock->getNow());
70  }
71  }
72 
73  acetime_t getNow() const override {
74  if (!mIsInit) return kInvalidSeconds;
75 
76  // Update mEpochSeconds by the number of seconds elapsed according to the
77  // millis(). This method is expected to be called multiple times a second,
78  // so the while() loop below will normally execute 0 times, until the
79  // millis() clock goes past the mPrevKeepAliveMillis by 1 second.
80  //
81  // There are 2 reasons why this method will be called multiple times a
82  // second:
83  //
84  // 1) A physical clock with an external display will want to refresh
85  // its display 5-10 times a second, so that it can capture the transition
86  // from one second to the next without too much jitter. So it will call
87  // this method multiple times a second to check if one second has passed.
88  //
89  // 2) If the SystemClockCoroutine or SystemClockLoop classes is used,
90  // then the keepAlive() method will be called perhaps 100's times per
91  // second, as fast as the iteration speed of the global loop() function.
92  while ((uint16_t) ((uint16_t) clockMillis() - mPrevKeepAliveMillis)
93  >= 1000) {
94  mPrevKeepAliveMillis += 1000;
95  mEpochSeconds += 1;
96  }
97 
98  return mEpochSeconds;
99  }
100 
101  void setNow(acetime_t epochSeconds) override {
102  syncNow(epochSeconds);
103 
104  // Also set the reference clock if possible.
105  if (mReferenceClock != nullptr) {
106  mReferenceClock->setNow(epochSeconds);
107  }
108  }
109 
123  void forceSync() {
124  if (mReferenceClock) {
125  acetime_t nowSeconds = mReferenceClock->getNow();
126  syncNow(nowSeconds);
127  }
128  }
129 
134  acetime_t getLastSyncTime() const {
135  return mLastSyncTime;
136  }
137 
139  uint8_t getSyncStatusCode() const { return mSyncStatusCode; }
140 
154  int32_t getSecondsSinceSyncAttempt() const {
155  return (int32_t) (clockMillis() - mPrevSyncAttemptMillis) / 1000;
156  }
157 
164  int32_t getSecondsToSyncAttempt() const {
165  return (int32_t) (mNextSyncAttemptMillis - clockMillis()) / 1000;
166  }
167 
178  int16_t getClockSkew() const {
179  return mClockSkew;
180  }
181 
183  bool isInit() const { return mIsInit; }
184 
185  protected:
186  friend class ::SystemClockLoopTest;
187  friend class ::SystemClockCoroutineTest;
188  friend class ::SystemClockLoopTest_syncNow;
189 
190  // disable copy constructor and assignment operator
191  SystemClock(const SystemClock&) = delete;
192  SystemClock& operator=(const SystemClock&) = delete;
193 
211  explicit SystemClock(
212  Clock* referenceClock /* nullable */,
213  Clock* backupClock /* nullable */
214  ) :
215  mReferenceClock(referenceClock),
216  mBackupClock(backupClock) {}
217 
222  explicit SystemClock() {}
223 
226  Clock* referenceClock /* nullable */,
227  Clock* backupClock /* nullable */
228  ) {
229  mReferenceClock = referenceClock;
230  mBackupClock = backupClock;
231 
232  mEpochSeconds = kInvalidSeconds;
233  mPrevSyncAttemptMillis = 0;
234  mNextSyncAttemptMillis = 0;
235  mPrevKeepAliveMillis = 0;
236  mIsInit = false;
237  mSyncStatusCode = kSyncStatusUnknown;
238  }
239 
241  Clock* getReferenceClock() const { return mReferenceClock; }
242 
247  virtual unsigned long clockMillis() const { return ::millis(); }
248 
255  void keepAlive() {
256  getNow();
257  }
258 
265  void backupNow(acetime_t nowSeconds) {
266  if (mBackupClock != nullptr) {
267  mBackupClock->setNow(nowSeconds);
268  }
269  }
270 
294  void syncNow(acetime_t epochSeconds) {
295  if (epochSeconds == kInvalidSeconds) return;
296 
297  mLastSyncTime = epochSeconds;
298  acetime_t skew = mEpochSeconds - epochSeconds;
299  mClockSkew = skew;
300  if (skew == 0) return;
301 
302  mEpochSeconds = epochSeconds;
303  mPrevKeepAliveMillis = clockMillis();
304  mIsInit = true;
305 
306  if (mBackupClock != mReferenceClock) {
307  backupNow(epochSeconds);
308  }
309  }
310 
312  void setNextSyncAttemptMillis(uint32_t ms) {
313  mNextSyncAttemptMillis = ms;
314  }
315 
317  void setPrevSyncAttemptMillis(uint32_t ms) {
318  mPrevSyncAttemptMillis = ms;
319  }
320 
322  void setSyncStatusCode(uint8_t code) {
323  mSyncStatusCode = code;
324  }
325 
326  private:
327  Clock* mReferenceClock;
328  Clock* mBackupClock;
329 
330  mutable acetime_t mEpochSeconds = kInvalidSeconds;
331  acetime_t mLastSyncTime = kInvalidSeconds; // time when last synced
332  uint32_t mPrevSyncAttemptMillis = 0;
333  uint32_t mNextSyncAttemptMillis = 0;
334  mutable uint16_t mPrevKeepAliveMillis = 0; // lower 16-bits of clockMillis()
335  int16_t mClockSkew = 0; // diff between reference and this clock
336  bool mIsInit = false; // true if setNow() or syncNow() was successful
337  uint8_t mSyncStatusCode = kSyncStatusUnknown;
338 };
339 
340 }
341 }
342 
343 #endif
ace_time::clock::SystemClock::setSyncStatusCode
void setSyncStatusCode(uint8_t code)
Set the status code of most recent sync attempt.
Definition: SystemClock.h:322
ace_time::clock::SystemClock::kSyncStatusOk
static const uint8_t kSyncStatusOk
Sync was successful.
Definition: SystemClock.h:55
ace_time::clock::SystemClock::kSyncStatusTimedOut
static const uint8_t kSyncStatusTimedOut
Sync request timed out.
Definition: SystemClock.h:61
ace_time::clock::SystemClock::kSyncStatusUnknown
static const uint8_t kSyncStatusUnknown
Sync was never done.
Definition: SystemClock.h:64
ace_time::clock::SystemClock::getLastSyncTime
acetime_t getLastSyncTime() const
Return the time (seconds since Epoch) of the last successful syncNow() call.
Definition: SystemClock.h:134
ace_time::clock::SystemClock::isInit
bool isInit() const
Return true if initialized by setNow() or syncNow().
Definition: SystemClock.h:183
ace_time::clock::Clock::getNow
virtual acetime_t getNow() const =0
Return the number of seconds since the AceTime epoch (2000-01-01T00:00:00Z).
ace_time::clock::SystemClock::kSyncStatusError
static const uint8_t kSyncStatusError
Sync request failed.
Definition: SystemClock.h:58
ace_time::clock::SystemClock::getSecondsSinceSyncAttempt
int32_t getSecondsSinceSyncAttempt() const
Return the number of seconds since the previous sync attempt, successful or not.
Definition: SystemClock.h:154
ace_time::clock::SystemClock::syncNow
void syncNow(acetime_t epochSeconds)
Set the current mEpochSeconds to the given epochSeconds.
Definition: SystemClock.h:294
ace_time::clock::SystemClock::keepAlive
void keepAlive()
Call this (or getNow() every 65.535 seconds or faster to keep the internal counter in sync with milli...
Definition: SystemClock.h:255
ace_time::clock::SystemClock::setNextSyncAttemptMillis
void setNextSyncAttemptMillis(uint32_t ms)
Set the millis to next sync attempt.
Definition: SystemClock.h:312
ace_time::clock::SystemClock::SystemClock
SystemClock(Clock *referenceClock, Clock *backupClock)
Constructor.
Definition: SystemClock.h:211
ace_time::clock::Clock::setNow
virtual void setNow(acetime_t)
Set the time to the indicated seconds.
Definition: Clock.h:67
ace_time::clock::SystemClock::setNow
void setNow(acetime_t epochSeconds) override
Set the time to the indicated seconds.
Definition: SystemClock.h:101
ace_time::clock::SystemClock::setPrevSyncAttemptMillis
void setPrevSyncAttemptMillis(uint32_t ms)
Set the millis of prev sync attempt.
Definition: SystemClock.h:317
ace_time::clock::SystemClock::setup
void setup()
Attempt to retrieve the time from the backupClock if it exists.
Definition: SystemClock.h:67
ace_time::clock::SystemClock::getReferenceClock
Clock * getReferenceClock() const
Get referenceClock.
Definition: SystemClock.h:241
ace_time::clock::SystemClock::initSystemClock
void initSystemClock(Clock *referenceClock, Clock *backupClock)
Same as constructor but allows delayed initialization, e.g.
Definition: SystemClock.h:225
ace_time::clock::Clock
Abstract base class for objects that provide and store time.
Definition: Clock.h:20
ace_time::clock::SystemClock::forceSync
void forceSync()
Manually force a sync with the referenceClock if it exists.
Definition: SystemClock.h:123
ace_time::clock::SystemClock::getClockSkew
int16_t getClockSkew() const
Difference between this clock compared to reference at last sync.
Definition: SystemClock.h:178
ace_time::clock::Clock::kInvalidSeconds
static const acetime_t kInvalidSeconds
Error value returned by getNow() and other methods when this object is not yet initialized.
Definition: Clock.h:26
ace_time::clock::SystemClock::clockMillis
virtual unsigned long clockMillis() const
Return the Arduino millis().
Definition: SystemClock.h:247
ace_time::clock::SystemClock::SystemClock
SystemClock()
Empty constructor primarily for tests.
Definition: SystemClock.h:222
ace_time::clock::SystemClock::backupNow
void backupNow(acetime_t nowSeconds)
Write the nowSeconds to the backupClock (which can be an RTC that has non-volatile memory).
Definition: SystemClock.h:265
ace_time::clock::SystemClock::getSyncStatusCode
uint8_t getSyncStatusCode() const
Get sync status code.
Definition: SystemClock.h:139
ace_time::clock::SystemClock::getSecondsToSyncAttempt
int32_t getSecondsToSyncAttempt() const
Return the number of seconds until the next syncNow() attempt.
Definition: SystemClock.h:164
ace_time::clock::SystemClock
A Clock that uses the Arduino millis() function to advance the time returned to the user.
Definition: SystemClock.h:52
ace_time::clock::SystemClock::getNow
acetime_t getNow() const override
Return the number of seconds since the AceTime epoch (2000-01-01T00:00:00Z).
Definition: SystemClock.h:73