AceTime  0.3
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.
SystemClockSyncCoroutine.h
1 #ifndef ACE_TIME_SYSTEM_CLOCK_SYNC_COROUTINE_H
2 #define ACE_TIME_SYSTEM_CLOCK_SYNC_COROUTINE_H
3 
4 #include <stdint.h>
5 #include "../common/TimingStats.h"
6 #include "SystemClock.h"
7 
8 class SystemClockSyncCoroutineTest;
9 
10 namespace ace_time {
11 namespace clock {
12 
21 class SystemClockSyncCoroutine: public ace_routine::Coroutine {
22  public:
36  uint16_t syncPeriodSeconds = 3600,
37  uint16_t initialSyncPeriodSeconds = 5,
38  uint16_t requestTimeoutMillis = 1000,
39  common::TimingStats* timingStats = nullptr):
40  mSystemClock(systemClock),
41  mSyncPeriodSeconds(syncPeriodSeconds),
42  mInitialSyncPeriodSeconds(initialSyncPeriodSeconds),
43  mRequestTimeoutMillis(requestTimeoutMillis),
44  mTimingStats(timingStats),
45  mCurrentSyncPeriodSeconds(initialSyncPeriodSeconds) {}
46 
54  int runCoroutine() override {
55  if (mSystemClock.mSyncTimeProvider == nullptr) return 0;
56 
57  COROUTINE_LOOP() {
58  // Send request
59  mSystemClock.mSyncTimeProvider->sendRequest();
60  mRequestStartTime = millis();
61 
62  // Wait for request
63  while (true) {
64  if (mSystemClock.mSyncTimeProvider->isResponseReady()) {
65  mRequestStatus = kStatusOk;
66  break;
67  }
68 
69  {
70  // Local variable waitTime must be scoped with {} so that the goto
71  // in COROUTINE_LOOP() skip past it in clang++. g++ seems to be
72  // fine without it.
73  uint16_t waitTime = millis() - mRequestStartTime;
74  if (waitTime >= mRequestTimeoutMillis) {
75  mRequestStatus = kStatusTimedOut;
76  break;
77  }
78  }
79 
80  COROUTINE_YIELD();
81  }
82 
83  // Process the response
84  if (mRequestStatus == kStatusOk) {
85  acetime_t nowSeconds =
86  mSystemClock.mSyncTimeProvider->readResponse();
87  uint16_t elapsedTime = millis() - mRequestStartTime;
88  if (mTimingStats != nullptr) {
89  mTimingStats->update(elapsedTime);
90  }
91  mSystemClock.sync(nowSeconds);
92  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
93  }
94 
95  COROUTINE_DELAY_SECONDS(mDelayLoopCounter, mCurrentSyncPeriodSeconds);
96 
97  // Determine the retry delay time based on success or failure. If
98  // failure, retry with exponential backoff, until the delay becomes
99  // mSyncPeriodSeconds.
100  if (mRequestStatus == kStatusTimedOut) {
101  if (mCurrentSyncPeriodSeconds >= mSyncPeriodSeconds / 2) {
102  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
103  } else {
104  mCurrentSyncPeriodSeconds *= 2;
105  }
106  }
107  }
108  }
109 
110  private:
111  friend class ::SystemClockSyncCoroutineTest;
112 
113  static const uint8_t kStatusOk = 0;
114  static const uint8_t kStatusTimedOut = 1;
115 
116  SystemClock& mSystemClock;
117  uint16_t const mSyncPeriodSeconds;
118  uint16_t const mInitialSyncPeriodSeconds;
119  uint16_t const mRequestTimeoutMillis;
120  common::TimingStats* const mTimingStats;
121 
122  uint16_t mRequestStartTime;
123  uint16_t mCurrentSyncPeriodSeconds;
124  uint8_t mRequestStatus;
125  uint16_t mDelayLoopCounter;
126 };
127 
128 }
129 }
130 
131 #endif
A coroutine that syncs the SystemClock with its syncTimeProvider.
A TimeKeeper that uses the Arduino millis() function to advance the time returned to the user...
Definition: SystemClock.h:45
virtual bool isResponseReady() const
Return true if a response is ready.
Definition: TimeProvider.h:32
void sync(acetime_t epochSeconds)
Similar to setNow() except that backupNow() is called only if the backupTimeKeeper is different from ...
Definition: SystemClock.h:96
Helper class to collect timing statistics such as min, max, average.
Definition: TimingStats.h:38
virtual void sendRequest() const
Send a time request asynchronously.
Definition: TimeProvider.h:29
virtual acetime_t readResponse() const
Returns number of seconds since AceTime epoch (2000-01-01).
Definition: TimeProvider.h:39
SystemClockSyncCoroutine(SystemClock &systemClock, uint16_t syncPeriodSeconds=3600, uint16_t initialSyncPeriodSeconds=5, uint16_t requestTimeoutMillis=1000, common::TimingStats *timingStats=nullptr)
Constructor.