Introduction of state machine for TimeManager. Minor refactoring.
This commit is contained in:
@@ -12,17 +12,29 @@ typedef enum
|
|||||||
NTP_UPDATE_PENDING = 2,
|
NTP_UPDATE_PENDING = 2,
|
||||||
NTP_UPDATE_RETRY_DELAY = 3,
|
NTP_UPDATE_RETRY_DELAY = 3,
|
||||||
NTP_UPDATE_TOO_EARLY = 4,
|
NTP_UPDATE_TOO_EARLY = 4,
|
||||||
} ntp_update_state;
|
NTP_UPDATE_SETUP_FAILED = 5,
|
||||||
|
} NtpUpdateState;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
TM_INIT = 0,
|
||||||
|
TM_INITIAL_SYNC = 1,
|
||||||
|
TM_RETRY_SYNC = 2,
|
||||||
|
TM_NORMAL = 3,
|
||||||
|
TM_PROLONGED_SYNC_FAIL = 4,
|
||||||
|
TM_SETUP_FAILED = 5,
|
||||||
|
} TimeManagerState;
|
||||||
|
|
||||||
class TimeManager
|
class TimeManager
|
||||||
{
|
{
|
||||||
#define NTP_MAX_UPDATE_TIME_US (500 * 1000) // 500ms max update time
|
#define NTP_MAX_UPDATE_TIME_US (500 * 1000) // 500ms max update time
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool ntp_sync_successful(void) const; // was there a NTP sync once?
|
bool ntp_sync_successful(void) const; // was there a NTP sync once?
|
||||||
ntp_update_state ntp_time_update(bool init = false);
|
bool ntp_update_failed_prolonged(void); // indicates if maximum time since last NTP update was too long
|
||||||
bool ntp_update_failed_prolonged(void) const; // indicates if maximum time since last NTP update was too long
|
NtpUpdateState ntp_time_update();
|
||||||
struct tm time_info(void);
|
struct tm time_info(void);
|
||||||
|
TimeManagerState tm_state(void) const;
|
||||||
void increment_time_now_local(void);
|
void increment_time_now_local(void);
|
||||||
void log_time() const; // log _time_info
|
void log_time() const; // log _time_info
|
||||||
void log_time(struct tm time_info) const; // log argument time_info
|
void log_time(struct tm time_info) const; // log argument time_info
|
||||||
@@ -42,11 +54,12 @@ public:
|
|||||||
UDPLogger *logger);
|
UDPLogger *logger);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set_up_ntp(void) const; // set up NTP server
|
void _set_up_ntp(void); // set up NTP server
|
||||||
void set_up_timer_isr(void) const; // set up timer interrupt
|
void _set_up_timer_isr(void); // set up timer interrupt
|
||||||
const char *_tz; // timezone
|
const char *_tz; // timezone
|
||||||
const char *_ntp_server; // used ntp server
|
const char *_ntp_server; // ntp server address
|
||||||
UDPLogger *_logger; // logger instance
|
UDPLogger *_logger; // logger instance
|
||||||
|
TimeManagerState _tm_state = TM_INIT; // Main state
|
||||||
struct tm _time_info = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // structure tm holds time information
|
struct tm _time_info = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // structure tm holds time information
|
||||||
time_t _time_now_local = 0; // local timer value, updated by timer interrupt and synced by NTP when needed
|
time_t _time_now_local = 0; // local timer value, updated by timer interrupt and synced by NTP when needed
|
||||||
time_t _time_now_ntp = 0; // NTP timer value, seconds since Epoch (1970) - UTC, only synced by NTP request.
|
time_t _time_now_ntp = 0; // NTP timer value, seconds since Epoch (1970) - UTC, only synced by NTP request.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
// Timings in us
|
// Timings in us
|
||||||
#define PERIOD_ANIMATION_US (200 * 1000) // 200ms
|
#define PERIOD_ANIMATION_US (200 * 1000) // 200ms
|
||||||
#define PERIOD_CLOCK_UPDATE_US (1 * 1000 * 1000) // 1s
|
#define PERIOD_CLOCK_UPDATE_US (1 * 1000 * 1000) // Must be 1s! Do not change!
|
||||||
#define PERIOD_HEARTBEAT_US (1 * 1000 * 1000) // 1s
|
#define PERIOD_HEARTBEAT_US (1 * 1000 * 1000) // 1s
|
||||||
#define PERIOD_MATRIX_UPDATE_US (100 * 1000) // 100ms
|
#define PERIOD_MATRIX_UPDATE_US (100 * 1000) // 100ms
|
||||||
#define PERIOD_NIGHTMODE_CHECK_US (20 * 1000 * 1000) // 20s
|
#define PERIOD_NIGHTMODE_CHECK_US (20 * 1000 * 1000) // 20s
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ struct tm TimeManager::time_info(void)
|
|||||||
return _time_info;
|
return _time_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeManagerState TimeManager::tm_state(void) const
|
||||||
|
{
|
||||||
|
return _tm_state;
|
||||||
|
}
|
||||||
|
|
||||||
bool TimeManager::ntp_sync_successful(void) const
|
bool TimeManager::ntp_sync_successful(void) const
|
||||||
{
|
{
|
||||||
return (_time_now_ntp > 0);
|
return (_time_now_ntp > 0);
|
||||||
@@ -41,26 +46,33 @@ bool TimeManager::ntp_sync_successful(void) const
|
|||||||
*
|
*
|
||||||
* @retval true if last update was successful
|
* @retval true if last update was successful
|
||||||
*/
|
*/
|
||||||
ntp_update_state TimeManager::ntp_time_update(bool init)
|
NtpUpdateState TimeManager::ntp_time_update()
|
||||||
{
|
{
|
||||||
// Check if minimum update delay has elapsed
|
NtpUpdateState retval = NTP_UPDATE_PENDING; // NTP time update
|
||||||
if (!init && ((system_get_time() - _ntp_sync_timestamp_us) <= _ntp_retry_delay_us))
|
struct tm time_info; // local NTP time info
|
||||||
|
|
||||||
|
// Set up NTP server once
|
||||||
|
if (_tm_state == TM_INIT)
|
||||||
{
|
{
|
||||||
return NTP_UPDATE_RETRY_DELAY; // could be replaced with appropriate enum
|
_set_up_ntp();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!init && ((system_get_time() - _ntp_sync_timestamp_us) <= (_ntp_update_period_s * 1000000)))
|
if (_tm_state == TM_SETUP_FAILED)
|
||||||
{
|
{
|
||||||
return NTP_UPDATE_TOO_EARLY; // could be replaced with appropriate enum
|
return NTP_UPDATE_SETUP_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init)
|
// Check if minimum update delay has elapsed. This is always active to prevent too many NTP server requests!
|
||||||
|
if ((_tm_state != TM_INITIAL_SYNC) && (system_get_time() - _ntp_sync_timestamp_us) <= _ntp_retry_delay_us)
|
||||||
{
|
{
|
||||||
set_up_ntp(); // set up NTP server once
|
return NTP_UPDATE_RETRY_DELAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
ntp_update_state ntp_update_state = NTP_UPDATE_PENDING; // NTP time update
|
// Check if it is time for a NTP sync.
|
||||||
struct tm time_info; // local NTP time info
|
if ((_tm_state != TM_INITIAL_SYNC) && (_tm_state != TM_RETRY_SYNC) && ((system_get_time() - _ntp_sync_timestamp_us) <= (_ntp_update_period_s * 1000000)))
|
||||||
|
{
|
||||||
|
return NTP_UPDATE_TOO_EARLY;
|
||||||
|
}
|
||||||
|
|
||||||
_ntp_sync_timestamp_us = system_get_time(); // NTP update start time
|
_ntp_sync_timestamp_us = system_get_time(); // NTP update start time
|
||||||
|
|
||||||
@@ -72,15 +84,15 @@ ntp_update_state TimeManager::ntp_time_update(bool init)
|
|||||||
|
|
||||||
} while (((system_get_time() - _ntp_sync_timestamp_us) <= NTP_MAX_UPDATE_TIME_US) && (time_info.tm_year < (NTP_MININUM_RX_YEAR - NTP_START_YEAR)));
|
} while (((system_get_time() - _ntp_sync_timestamp_us) <= NTP_MAX_UPDATE_TIME_US) && (time_info.tm_year < (NTP_MININUM_RX_YEAR - NTP_START_YEAR)));
|
||||||
|
|
||||||
ntp_update_state = (time_info.tm_year <= (NTP_MININUM_RX_YEAR - NTP_START_YEAR)) ? NTP_UPDATE_FAILED : NTP_UPDATE_OK; // sanity check
|
retval = (time_info.tm_year <= (NTP_MININUM_RX_YEAR - NTP_START_YEAR)) ? NTP_UPDATE_FAILED : NTP_UPDATE_OK; // sanity check
|
||||||
|
|
||||||
if (ntp_update_state == NTP_UPDATE_OK)
|
if (retval == NTP_UPDATE_OK)
|
||||||
{
|
{
|
||||||
_ntp_sync_timestamp_us = system_get_time(); // save NTP update timestamp
|
_ntp_sync_timestamp_us = system_get_time(); // save NTP update timestamp
|
||||||
_time_info = time_info; // take over time_info to member variable
|
_time_info = time_info; // take over time_info to member variable
|
||||||
log_time(); // log current time
|
log_time(); // log current time
|
||||||
|
|
||||||
if (!init && (abs(_time_now_ntp - _time_now_local) > 10)) // in the case that the local time drifted more than 10s in _ntp_update_period_s
|
if ((_tm_state != TM_INITIAL_SYNC) && (abs(_time_now_ntp - _time_now_local) > 10)) // in the case that the local time drifted more than 10s in _ntp_update_period_s
|
||||||
{
|
{
|
||||||
_logger->log_string(String("Difference between local and NTP time was more than 10 seconds!\n"));
|
_logger->log_string(String("Difference between local and NTP time was more than 10 seconds!\n"));
|
||||||
_logger->log_string("Local time was: " + String(_time_now_local) + ", NTP time is: " + String(_time_now_ntp) + "\n");
|
_logger->log_string("Local time was: " + String(_time_now_local) + ", NTP time is: " + String(_time_now_ntp) + "\n");
|
||||||
@@ -88,22 +100,33 @@ ntp_update_state TimeManager::ntp_time_update(bool init)
|
|||||||
|
|
||||||
_time_now_local = _time_now_ntp; // sync local time with NTP time
|
_time_now_local = _time_now_ntp; // sync local time with NTP time
|
||||||
|
|
||||||
if (init) // only set up the timer once after NTP update was successful
|
if (_tm_state == TM_INITIAL_SYNC) // only set up the timer once after NTP update was successful
|
||||||
{
|
{
|
||||||
set_up_timer_isr();
|
_set_up_timer_isr();
|
||||||
|
}
|
||||||
|
else // set state to normal
|
||||||
|
{
|
||||||
|
_tm_state = TM_NORMAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.log_string("NTP-Update was not successful. Retrying in " + String(_ntp_retry_delay_us / 1000) + "ms.\n");
|
_tm_state = TM_RETRY_SYNC;
|
||||||
|
logger.log_string("NTP-Update was not successful. Retrying in " + String(_ntp_retry_delay_us / 1000) + "ms.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ntp_update_state;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TimeManager::ntp_update_failed_prolonged(void) const
|
bool TimeManager::ntp_update_failed_prolonged(void)
|
||||||
{
|
{
|
||||||
return _time_now_local >= (_time_now_ntp + (time_t)_ntp_max_offline_time_s);
|
bool retval = false;
|
||||||
|
if (_time_now_local >= (_time_now_ntp + (time_t)_ntp_max_offline_time_s))
|
||||||
|
{
|
||||||
|
_tm_state = TM_PROLONGED_SYNC_FAIL;
|
||||||
|
retval = true;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TimeManager::tm_min(void)
|
int TimeManager::tm_min(void)
|
||||||
@@ -146,15 +169,17 @@ bool TimeManager::tm_isdst(void)
|
|||||||
* @brief Sets up the timer ISR
|
* @brief Sets up the timer ISR
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void TimeManager::set_up_timer_isr(void) const
|
void TimeManager::_set_up_timer_isr(void)
|
||||||
{
|
{
|
||||||
// set up timer interrupt after NTP update is done
|
// set up timer interrupt after NTP update is done
|
||||||
if (ntp_sync_successful())
|
if (ITimer.attachInterruptInterval(PERIOD_CLOCK_UPDATE_US, TimerHandler))
|
||||||
{
|
{
|
||||||
(void)ITimer.attachInterruptInterval(PERIOD_CLOCK_UPDATE_US, TimerHandler);
|
_tm_state = TM_NORMAL;
|
||||||
|
logger.log_string(String("Timer ISR was initialized!"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_tm_state = TM_SETUP_FAILED;
|
||||||
logger.log_string("WARNING: Timer interrupt was not attached!");
|
logger.log_string("WARNING: Timer interrupt was not attached!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,17 +188,19 @@ void TimeManager::set_up_timer_isr(void) const
|
|||||||
* @brief Sets up the NTP server config
|
* @brief Sets up the NTP server config
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void TimeManager::set_up_ntp(void) const
|
void TimeManager::_set_up_ntp(void)
|
||||||
{
|
{
|
||||||
if ((_tz != nullptr) && (_ntp_server != nullptr))
|
if ((_tz != nullptr) && (_ntp_server != nullptr))
|
||||||
{
|
{
|
||||||
// set up NTP server and timezone at init
|
// set up NTP server and timezone at init
|
||||||
configTime(_tz, _ntp_server);
|
configTime(_tz, _ntp_server);
|
||||||
|
_tm_state = TM_INITIAL_SYNC;
|
||||||
logger.log_string(String("NTP server was initialized!"));
|
logger.log_string(String("NTP server was initialized!"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.log_string(String("Timezone and/or NTP-Server were not given!"));
|
_tm_state = TM_SETUP_FAILED;
|
||||||
|
logger.log_string(String("ERROR: Timezone and/or NTP-Server were not given correctly!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ void setup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get initial time
|
// get initial time
|
||||||
if (tm_mgr.ntp_time_update(true) == NTP_UPDATE_OK)
|
if (tm_mgr.ntp_time_update() == NTP_UPDATE_OK)
|
||||||
{
|
{
|
||||||
// show the current time for short time in words
|
// show the current time for short time in words
|
||||||
String timeMessage = time_to_string(tm_mgr.tm_hour(), tm_mgr.tm_min());
|
String timeMessage = time_to_string(tm_mgr.tm_hour(), tm_mgr.tm_min());
|
||||||
@@ -252,7 +252,7 @@ void loop()
|
|||||||
{
|
{
|
||||||
send_heartbeat(); // send heartbeat update
|
send_heartbeat(); // send heartbeat update
|
||||||
|
|
||||||
tm_mgr.log_time(); // TODO rm
|
tm_mgr.log_time(); // TODO rm
|
||||||
|
|
||||||
last_heartbeat_us = system_get_time();
|
last_heartbeat_us = system_get_time();
|
||||||
delay(10);
|
delay(10);
|
||||||
@@ -283,14 +283,7 @@ void loop()
|
|||||||
|
|
||||||
if ((current_time_us - last_time_update_us) >= PERIOD_TIME_UPDATE_US)
|
if ((current_time_us - last_time_update_us) >= PERIOD_TIME_UPDATE_US)
|
||||||
{
|
{
|
||||||
if (tm_mgr.ntp_sync_successful() == true) // regular case
|
tm_mgr.ntp_time_update(); // NTP time update
|
||||||
{
|
|
||||||
(void)tm_mgr.ntp_time_update(); // NTP time update
|
|
||||||
}
|
|
||||||
else // if there was never a NTP time update before (set up failed)
|
|
||||||
{
|
|
||||||
(void)tm_mgr.ntp_time_update(true); // NTP time update with init!
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tm_mgr.ntp_update_failed_prolonged() == true)
|
if (tm_mgr.ntp_update_failed_prolonged() == true)
|
||||||
{
|
{
|
||||||
@@ -375,13 +368,40 @@ void handle_current_state()
|
|||||||
{
|
{
|
||||||
case ST_CLOCK: // state clock
|
case ST_CLOCK: // state clock
|
||||||
{
|
{
|
||||||
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min()), main_color_clock);
|
if (tm_mgr.ntp_sync_successful() && tm_mgr.tm_state() == TM_NORMAL)
|
||||||
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), main_color_clock);
|
{
|
||||||
|
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min()), main_color_clock);
|
||||||
|
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), main_color_clock);
|
||||||
|
}
|
||||||
|
else if (tm_mgr.ntp_sync_successful() && tm_mgr.tm_state() == TM_RETRY_SYNC)
|
||||||
|
{
|
||||||
|
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min()), main_color_clock);
|
||||||
|
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), colors_24bit[6]); // in blue to indicate a network problem
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// clear matrix
|
||||||
|
led_matrix.flush();
|
||||||
|
// Turn on minutes LEDs (blue)
|
||||||
|
led_matrix.set_min_indicator(15, colors_24bit[6]);
|
||||||
|
led_matrix.draw_on_matrix_instant();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ST_DICLOCK: // state diclock
|
case ST_DICLOCK: // state diclock
|
||||||
{
|
{
|
||||||
show_digital_clock((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min(), main_color_clock);
|
if (tm_mgr.ntp_sync_successful())
|
||||||
|
{
|
||||||
|
show_digital_clock((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min(), main_color_clock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// clear matrix
|
||||||
|
led_matrix.flush();
|
||||||
|
// Turn on minutes LEDs (blue)
|
||||||
|
led_matrix.set_min_indicator(15, colors_24bit[6]);
|
||||||
|
led_matrix.draw_on_matrix_instant();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ST_SPIRAL: // state spiral
|
case ST_SPIRAL: // state spiral
|
||||||
|
|||||||
Reference in New Issue
Block a user