AceTime  1.1.2
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.
SystemClockLoop.h
1 /*
2  * MIT License
3  * Copyright (c) 2018 Brian T. Park
4  */
5 
6 #ifndef ACE_TIME_SYSTEM_CLOCK_LOOP_H
7 #define ACE_TIME_SYSTEM_CLOCK_LOOP_H
8 
9 #include <stdint.h>
10 #include "SystemClock.h"
11 
12 class SystemClockLoopTest_loop;
13 
14 namespace ace_time {
15 namespace clock {
16 
30  public:
32  static const uint8_t kStatusReady = 0;
33 
35  static const uint8_t kStatusSent = 1;
36 
38  static const uint8_t kStatusOk = 2;
39 
41  static const uint8_t kStatusWaitForRetry = 3;
42 
58  explicit SystemClockLoop(
59  Clock* referenceClock /* nullable */,
60  Clock* backupClock /* nullable */,
61  uint16_t syncPeriodSeconds = 3600,
62  uint16_t initialSyncPeriodSeconds = 5,
63  uint16_t requestTimeoutMillis = 1000,
64  common::TimingStats* timingStats = nullptr):
65  SystemClock(referenceClock, backupClock),
66  mSyncPeriodSeconds(syncPeriodSeconds),
67  mRequestTimeoutMillis(requestTimeoutMillis),
68  mTimingStats(timingStats),
69  mCurrentSyncPeriodSeconds(initialSyncPeriodSeconds) {}
70 
75  void loop() {
76  keepAlive();
77  if (mReferenceClock == nullptr) return;
78 
79  unsigned long nowMillis = clockMillis();
80 
81  // Finite state machine based on mRequestStatus
82  switch (mRequestStatus) {
83  case kStatusReady:
84  mReferenceClock->sendRequest();
85  mRequestStartMillis = nowMillis;
86  mRequestStatus = kStatusSent;
87  break;
88  case kStatusSent:
89  if (mReferenceClock->isResponseReady()) {
90  acetime_t nowSeconds = mReferenceClock->readResponse();
91  if (mTimingStats != nullptr) {
92  uint16_t elapsedMillis = nowMillis - mRequestStartMillis;
93  mTimingStats->update(elapsedMillis);
94  }
95  if (nowSeconds == kInvalidSeconds) {
96  mRequestStatus = kStatusWaitForRetry;
97  } else {
98  syncNow(nowSeconds);
99  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
100  mLastSyncMillis = nowMillis;
101  mRequestStatus = kStatusOk;
102  }
103  } else {
104  unsigned long waitMillis = nowMillis - mRequestStartMillis;
105  if (waitMillis >= mRequestTimeoutMillis) {
106  mRequestStatus = kStatusWaitForRetry;
107  }
108  }
109  break;
110  case kStatusOk: {
111  unsigned long millisSinceLastSync = nowMillis - mLastSyncMillis;
112  if (millisSinceLastSync >= mCurrentSyncPeriodSeconds * 1000UL) {
113  mRequestStatus = kStatusReady;
114  }
115  break;
116  }
117  case kStatusWaitForRetry: {
118  // subsequent loop() retries with an exponential backoff, until a
119  // maximum of mSyncPeriodSeconds is reached.
120  unsigned long waitMillis = nowMillis - mRequestStartMillis;
121  if (waitMillis >= mCurrentSyncPeriodSeconds * 1000UL) {
122  if (mCurrentSyncPeriodSeconds >= mSyncPeriodSeconds / 2) {
123  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
124  } else {
125  mCurrentSyncPeriodSeconds *= 2;
126  }
127  mRequestStatus = kStatusReady;
128  }
129  break;
130  }
131  }
132  }
133 
134  private:
135  friend class ::SystemClockLoopTest_loop;
136 
137  // disable copy constructor and assignment operator
138  SystemClockLoop(const SystemClockLoop&) = delete;
139  SystemClockLoop& operator=(const SystemClockLoop&) = delete;
140 
141  uint16_t const mSyncPeriodSeconds;
142  uint16_t const mRequestTimeoutMillis;
143  common::TimingStats* const mTimingStats;
144 
145  unsigned long mLastSyncMillis;
146  unsigned long mRequestStartMillis;
147  uint16_t mCurrentSyncPeriodSeconds;
148  uint8_t mRequestStatus = kStatusReady;
149 };
150 
151 }
152 }
153 
154 #endif
ace_time::common::TimingStats
Helper class to collect timing statistics such as min, max, average.
Definition: TimingStats.h:19
ace_time::clock::SystemClockLoop::loop
void loop()
Call this from the global loop() method.
Definition: SystemClockLoop.h:75
ace_time::clock::Clock::sendRequest
virtual void sendRequest() const
Send a time request asynchronously.
Definition: Clock.h:38
ace_time::clock::SystemClockLoop::kStatusReady
static const uint8_t kStatusReady
Ready to send request.
Definition: SystemClockLoop.h:32
ace_time::clock::SystemClock::syncNow
void syncNow(acetime_t epochSeconds)
Similar to setNow() except that backupNow() is called only if the backupClock is different from the r...
Definition: SystemClock.h:156
ace_time::clock::SystemClockLoop::kStatusSent
static const uint8_t kStatusSent
Request sent, waiting for response.
Definition: SystemClockLoop.h:35
ace_time::clock::SystemClockLoop::kStatusWaitForRetry
static const uint8_t kStatusWaitForRetry
Request received but is invalid, so retry with exponential backoff.
Definition: SystemClockLoop.h:41
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:132
ace_time::clock::SystemClockLoop::kStatusOk
static const uint8_t kStatusOk
Request received and is valid.
Definition: SystemClockLoop.h:38
ace_time::clock::Clock
Base class for objects that provide and store time.
Definition: Clock.h:20
ace_time::clock::SystemClock::clockMillis
virtual unsigned long clockMillis() const
Return the Arduino millis().
Definition: SystemClock.h:126
ace_time::clock::Clock::isResponseReady
virtual bool isResponseReady() const
Return true if a response is ready.
Definition: Clock.h:41
ace_time::clock::Clock::readResponse
virtual acetime_t readResponse() const
Returns number of seconds since AceTime epoch (2000-01-01).
Definition: Clock.h:48
ace_time::clock::SystemClockLoop
A subclass of SystemClock that sync with its mReferenceClock using a blocking mReferenceClock->getNow...
Definition: SystemClockLoop.h:29
ace_time::clock::SystemClockLoop::SystemClockLoop
SystemClockLoop(Clock *referenceClock, Clock *backupClock, uint16_t syncPeriodSeconds=3600, uint16_t initialSyncPeriodSeconds=5, uint16_t requestTimeoutMillis=1000, common::TimingStats *timingStats=nullptr)
Constructor.
Definition: SystemClockLoop.h:58
ace_time::clock::SystemClock
A Clock that uses the Arduino millis() function to advance the time returned to the user.
Definition: SystemClock.h:53