From ff90c610ae37d8c17010c981a5afbae1a55434b6 Mon Sep 17 00:00:00 2001 From: Markus Ransberger Date: Tue, 28 May 2024 19:06:39 +0200 Subject: [PATCH] Rework TimeManager & NTP handling. Remove module ESP8266TimerInterrupt. --- include/time_manager.h | 78 ++++----- include/wordclock_constants.h | 12 +- include/wordclock_esp8266.h | 2 +- src/connectivity/time_manager.cpp | 183 ++++++++++---------- src/wordclock_esp8266.cpp | 270 ++++++++++++++---------------- 5 files changed, 252 insertions(+), 293 deletions(-) diff --git a/include/time_manager.h b/include/time_manager.h index d5095e1..bfd791f 100644 --- a/include/time_manager.h +++ b/include/time_manager.h @@ -7,53 +7,54 @@ typedef enum { - NTP_UPDATE_FAILED = 0, - NTP_UPDATE_OK = 1, - NTP_UPDATE_PENDING = 2, - NTP_UPDATE_RETRY_DELAY = 3, - NTP_UPDATE_TOO_EARLY = 4, - NTP_UPDATE_SETUP_FAILED = 5, -} NtpUpdateState; + TIME_UPDATE_FAILED = 0, + TIME_UPDATE_OK = 1, + TIME_UPDATE_PENDING = 2, +} TimeUpdateState; typedef enum { TM_INIT = 0, TM_INITIAL_SYNC = 1, - TM_RETRY_SYNC = 2, - TM_NORMAL = 3, - TM_PROLONGED_SYNC_FAIL = 4, + TM_NORMAL = 2, + TM_SYNC_OVERDUE = 3, + TM_SYNC_TIMEOUT = 4, TM_SETUP_FAILED = 5, } TimeManagerState; class TimeManager { -#define NTP_MAX_UPDATE_TIME_US (500 * 1000) // 500ms max update time +#define NTP_MAX_UPDATE_TIME_US (5 * 1000 * 1000) // 5000ms max update time public: - // constructor + // constructors + TimeManager(); TimeManager(const char *tz, const char *ntp_server, - uint32 ntp_update_period_s, - uint32 ntp_retry_delay_us, + bool (*is_wifi_connected)(void), uint32 ntp_max_offline_time_s, UDPLogger *logger); - // ntp methods - bool ntp_sync_successful(void) const; // was there a NTP sync once? - bool ntp_update_failed_prolonged(void); // indicates if maximum time since last NTP update was too long - NtpUpdateState ntp_time_update(); // main NTP time update method, called in loop + // init + void init(); - // ISR method - void increment_time_now_local(void); // should be called by timer ISR + // callback + void time_set_cb(void); // callback which is called when NTP time was set + + // ntp methods + bool ntp_sync_successful(void) const; // was there a NTP sync once? + bool ntp_sync_overdue(void); // function to check if NTP sync is overdue + bool ntp_sync_timeout(void); // function to check if maximum time since last NTP sync has been reached + TimeUpdateState get_time(); // main time update method, called in loop // getter for time values - bool tm_isdst(void); // true if summertime - int tm_day(void); - int tm_hour(void); - int tm_min(void); - int tm_mon(void); - int tm_year(void); - struct tm time_info(void); + bool isdst(void) const; // true if summertime (daylight saving time) + int day(void) const; + int hour(void) const; + int minute(void) const; + int month(void) const; + int year(void) const; + struct tm time_info(void) const; // getter TimeManagerState tm_state(void) const; // get current state @@ -63,26 +64,17 @@ public: void log_time(struct tm time_info) const; // log argument time_info private: - // setup methods - void _set_up_ntp(void); // set up NTP server - void _set_up_timer_isr(void); // set up timer interrupt + void _set_up_ntp(void); // set up NTP server + bool (*_is_wifi_connected)(void); // function to check if wifi is connected + const char *_ntp_server = "pool.ntp.org"; // ntp server address const char *_tz; // timezone - const char *_ntp_server; // ntp server address - 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 - 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 _now = 0; // local time value + time_t _ntp_sync_timestamp_s = 0; // timestamp of last successful ntp sync + TimeManagerState _tm_state = TM_INIT; // Main state + UDPLogger *_logger; // logger instance uint32 _ntp_max_offline_time_s; // maximum time in seconds which is considered ok since last NTP update - uint32 _ntp_update_period_s; // NTP request update period in seconds - uint32 _ntp_retry_delay_us; // minimum retry delay in us between two NTP requests - uint32 _ntp_sync_timestamp_us = 0; // timestamp of last successful ntp update }; -inline void TimeManager::increment_time_now_local(void) -{ - _time_now_local++; -} - #endif /* TIME_MANAGER_H */ diff --git a/include/wordclock_constants.h b/include/wordclock_constants.h index 03b8b4d..a031a45 100644 --- a/include/wordclock_constants.h +++ b/include/wordclock_constants.h @@ -38,7 +38,7 @@ #define PERIOD_HEARTBEAT_US (1 * 1000 * 1000) // 1s #define PERIOD_MATRIX_UPDATE_US (100 * 1000) // 100ms #define PERIOD_NIGHTMODE_CHECK_US (20 * 1000 * 1000) // 20s -#define PERIOD_TIME_UPDATE_US (500 * 1000) // 500ms +#define PERIOD_TIME_UPDATE_US (1 * 1000 * 1000) // 1000ms #define PERIOD_PONG_US (10 * 1000) // 10ms #define PERIOD_SNAKE_US (50 * 1000) // 50ms #define PERIOD_STATE_CHANGE_US (10 * 1000 * 1000) // 10s @@ -69,11 +69,9 @@ #define MATRIX_HEIGHT (11) // NTP macros -#define BUILD_YEAR (__DATE__ + 7) // Will expand to current year at compile time as string. -#define NTP_MININUM_RX_YEAR (atoi(BUILD_YEAR) - 1) // Will expand to current year minus one at compile time. -#define NTP_START_YEAR (1900) // NTP minimum year is 1900 -#define NTP_UPDATE_PERIOD_S (6 * 3600) // 6h period between NTP updates -#define NTP_RETRY_DELAY_US (10 * 1000 * 1000) // 10s retry delay time between failed NTP requests -#define NTP_MAX_OFFLINE_TIME_S (7 * 24 * 3600) // Watchdog value, maxmimum offline time before a restart is triggered +#define BUILD_YEAR (__DATE__ + 7) // Will expand to current year at compile time as string. +#define NTP_MINIMUM_RX_YEAR (atoi(BUILD_YEAR) - 1) // Will expand to current year minus one at compile time. +#define NTP_START_YEAR (1900) // NTP minimum year is 1900 +#define NTP_MAX_OFFLINE_TIME_S (7 * 24 * 3600) // Watchdog value, maximum offline time before a restart is triggered #endif /* WORDCLOCK_CONSTANTS_H */ diff --git a/include/wordclock_esp8266.h b/include/wordclock_esp8266.h index b839ee2..11e30b4 100644 --- a/include/wordclock_esp8266.h +++ b/include/wordclock_esp8266.h @@ -82,7 +82,7 @@ void send_heartbeat(void); void set_dynamic_brightness(bool state); void set_main_color(uint8_t red, uint8_t green, uint8_t blue); void set_night_mode(bool on); -void state_change(uint8_t newState); +void state_change(ClockState_en new_state); void update_matrix(void); void write_settings_to_EEPROM(void); diff --git a/src/connectivity/time_manager.cpp b/src/connectivity/time_manager.cpp index c6f321d..efd0353 100644 --- a/src/connectivity/time_manager.cpp +++ b/src/connectivity/time_manager.cpp @@ -1,41 +1,76 @@ #include "time_manager.h" -#include // https://github.com/khoih-prog/ESP8266TimerInterrupt #include "wordclock_constants.h" -#include "time.h" +#include // required for settimeofday_cb() +#include +#include -extern UDPLogger logger; // logging instance -extern ESP8266Timer ITimer; // ESP8266 Timer -extern void IRAM_ATTR TimerHandler(); // ISR function +extern UDPLogger logger; // logging instance // ---------------------------------------------------------------------------------- // Class // ---------------------------------------------------------------------------------- +TimeManager::TimeManager(){}; TimeManager::TimeManager(const char *tz, const char *ntp_server, - uint32 ntp_update_period_s, - uint32 ntp_retry_delay_us, + bool (*is_wifi_connected)(void), uint32 ntp_max_offline_time_s, UDPLogger *logger) { _tz = tz; _ntp_server = ntp_server; + _is_wifi_connected = is_wifi_connected; _ntp_max_offline_time_s = ntp_max_offline_time_s; - _ntp_retry_delay_us = ntp_retry_delay_us; - _ntp_update_period_s = ntp_update_period_s; _logger = logger; } +void TimeManager::init() +{ + // Set up NTP server once + _set_up_ntp(); + + if (_tm_state == TM_INITIAL_SYNC) + { + // force sntp reinit now + sntp_stop(); + sntp_init(); + } + settimeofday_cb([&]() { time_set_cb(); }); +} + bool TimeManager::ntp_sync_successful(void) const { - return (_time_now_ntp > 0); + return (_now > 1716913300); // UTC timestamp in the past (28.05.2024) } -bool TimeManager::ntp_update_failed_prolonged(void) +bool TimeManager::ntp_sync_overdue(void) { - bool retval = false; - if (_time_now_local >= (_time_now_ntp + (time_t)_ntp_max_offline_time_s)) + if (!ntp_sync_successful()) { - _tm_state = TM_PROLONGED_SYNC_FAIL; + return false; + } + + bool retval = false; + // after the ntp sync update delay has been reached six times, the sync is considered overdue + if (_now >= (_ntp_sync_timestamp_s + (time_t)(6 * (sntp_update_delay_MS_rfc_not_less_than_15000() / 1000)))) + { + _tm_state = TM_SYNC_OVERDUE; + retval = true; + } + return retval; +} + +bool TimeManager::ntp_sync_timeout(void) +{ + if (!ntp_sync_successful()) + { + return false; + } + + bool retval = false; + // after the maxmimum offline time has been reached, the sync is considered timed out + if (_now >= (_ntp_sync_timestamp_s + (time_t)_ntp_max_offline_time_s)) + { + _tm_state = TM_SYNC_TIMEOUT; retval = true; } return retval; @@ -46,117 +81,67 @@ bool TimeManager::ntp_update_failed_prolonged(void) * * @retval true if last update was successful */ -NtpUpdateState TimeManager::ntp_time_update() +TimeUpdateState TimeManager::get_time() { - NtpUpdateState retval = NTP_UPDATE_PENDING; // NTP time update - struct tm time_info; // local NTP time info - - // Set up NTP server once - if (_tm_state == TM_INIT) - { - _set_up_ntp(); - } - - if (_tm_state == TM_SETUP_FAILED) - { - return NTP_UPDATE_SETUP_FAILED; - } - - // 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) - { - return NTP_UPDATE_RETRY_DELAY; - } - - // Check if it is time for a NTP sync. - if ((_tm_state != TM_INITIAL_SYNC) && (_tm_state != TM_RETRY_SYNC) && (((system_get_time() - _ntp_sync_timestamp_us) / 1000000) <= _ntp_update_period_s)) - { - return NTP_UPDATE_TOO_EARLY; - } - - _ntp_sync_timestamp_us = system_get_time(); // NTP update start time + TimeUpdateState retval = TIME_UPDATE_PENDING; // NTP time update + uint32 timestamp_us = system_get_time(); // NTP update start time + struct tm time_info; // local NTP time info do { - time(&_time_now_ntp); // get time from server and save it into _time_now_ntp - localtime_r(&_time_now_ntp, &time_info); // convert time - yield(); // since this loop could take up to NTP_MAX_UPDATE_TIME_US + _now = time(nullptr); // update time + (void)localtime_r(&_now, &time_info); // convert time + yield(); // since this loop could take up to NTP_MAX_UPDATE_TIME_US - } 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() - timestamp_us) <= NTP_MAX_UPDATE_TIME_US) && (time_info.tm_year < (NTP_MINIMUM_RX_YEAR - NTP_START_YEAR))); - retval = (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_MINIMUM_RX_YEAR - NTP_START_YEAR)) ? TIME_UPDATE_FAILED : TIME_UPDATE_OK; // sanity check - if (retval == NTP_UPDATE_OK) + if (retval == TIME_UPDATE_OK) { - _ntp_sync_timestamp_us = system_get_time(); // save NTP update timestamp - _time_info = time_info; // take over time_info to member variable - log_time(); // log current time - - 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("Local time was: " + String(_time_now_local) + ", NTP time is: " + String(_time_now_ntp) + "\n"); - } - - _time_now_local = _time_now_ntp; // sync local time with NTP time - - if (_tm_state == TM_INITIAL_SYNC) // only set up the timer once after NTP update was successful - { - _set_up_timer_isr(); - } - else // set state to normal - { - _tm_state = TM_NORMAL; - } + _time_info = time_info; // take over time_info to member variable + _tm_state = TM_NORMAL; } else { - _tm_state = TM_RETRY_SYNC; - logger.log_string("NTP-Update was not successful. Retrying in " + String(_ntp_retry_delay_us / 1000) + "ms."); + logger.log_string("NTP-Update was not successful. Retrying in " + String(sntp_update_delay_MS_rfc_not_less_than_15000()) + "ms."); } return retval; } -bool TimeManager::tm_isdst(void) +bool TimeManager::isdst(void) const { - localtime_r(&_time_now_local, &_time_info); // convert time - return _time_info.tm_isdst > 0; + return (_time_info.tm_isdst > 0); } -int TimeManager::tm_day(void) +int TimeManager::day(void) const { - localtime_r(&_time_now_local, &_time_info); // convert time - return _time_info.tm_mday + 1; // add 1 to get actual day + return (_time_info.tm_mday + 1); // add 1 to get actual day } -int TimeManager::tm_hour(void) +int TimeManager::hour(void) const { - localtime_r(&_time_now_local, &_time_info); // convert time return _time_info.tm_hour; } -int TimeManager::tm_min(void) +int TimeManager::minute(void) const { - localtime_r(&_time_now_local, &_time_info); // convert time return _time_info.tm_min; } -int TimeManager::tm_mon(void) +int TimeManager::month(void) const { - localtime_r(&_time_now_local, &_time_info); // convert time - return _time_info.tm_mon + 1; // add 1 to get actual month + return (_time_info.tm_mon + 1); // add 1 to get actual month } -int TimeManager::tm_year(void) +int TimeManager::year(void) const { - localtime_r(&_time_now_local, &_time_info); // convert time return _time_info.tm_year + NTP_START_YEAR; // add start year to get actual year } -struct tm TimeManager::time_info(void) +struct tm TimeManager::time_info(void) const { - localtime_r(&_time_now_local, &_time_info); // convert time return _time_info; } @@ -207,21 +192,23 @@ void TimeManager::_set_up_ntp(void) } } -/** - * @brief Sets up the timer ISR - * - */ -void TimeManager::_set_up_timer_isr(void) +void TimeManager::time_set_cb(void) { - // set up timer interrupt after NTP update is done - if (ITimer.attachInterruptInterval(PERIOD_CLOCK_UPDATE_US, TimerHandler)) + if ((*_is_wifi_connected)()) { - _tm_state = TM_NORMAL; - logger.log_string(String("Timer ISR was attached successfully!")); + if (get_time() == TIME_UPDATE_OK) + { + _ntp_sync_timestamp_s = _now; + logger.log_string("NTP successfully synced at..."); + log_time(); + } + else + { + logger.log_string("NTP sync failed!"); + } } else { - _tm_state = TM_SETUP_FAILED; - logger.log_string("WARNING: Timer interrupt was not attached!"); + logger.log_string("NTP sync failed, WiFi is not connected!"); } } diff --git a/src/wordclock_esp8266.cpp b/src/wordclock_esp8266.cpp index a2e5aa3..94ed253 100644 --- a/src/wordclock_esp8266.cpp +++ b/src/wordclock_esp8266.cpp @@ -28,8 +28,6 @@ #include // NeoPixel library used to run the NeoPixel LEDs: https://github.com/adafruit/Adafruit_NeoPixel #include #include // from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager) -#include // https://github.com/khoih-prog/ESP8266TimerInterrupt -#include // https://github.com/khoih-prog/ESP8266TimerInterrupt #include #include #include // https://github.com/tzapu/WiFiManager WiFi Configuration Magic @@ -71,20 +69,16 @@ static Pong pong = Pong(&led_matrix, &logger); static Snake snake = Snake(&led_matrix, &logger); static Tetris tetris = Tetris(&led_matrix, &logger); -// Time ManagerW -static TimeManager tm_mgr = TimeManager(MY_TZ, NTP_SERVER_URL, - NTP_UPDATE_PERIOD_S, - NTP_RETRY_DELAY_US, - NTP_MAX_OFFLINE_TIME_S, - &logger); +// Time Manager +static TimeManager tm_mgr; -// State variablesW +// State variables static bool flg_night_mode = false; // State of nightmode static bool flg_reset_wifi_creds = false; // Used to reset stored wifi credentials static float filter_factor = DEFAULT_SMOOTHING_FACTOR; // Stores smoothing factor for led transition, value of 1 represents no smoothing. static uint32_t main_color_clock = colors_24bit[2]; // Color of the clock and digital clock static uint8_t current_brightness = DEFAULT_BRIGHTNESS; // Current brightness of LEDs -static uint8_t current_state = (uint8_t)ST_CLOCK; // Stores current state +static ClockState_en current_state = ST_CLOCK; // Stores current state // Other variables static uint32 last_led_direct_us = 0; // Time of last direct LED command (=> fall back to normal mode after timeout) @@ -104,18 +98,6 @@ static const uint32_t period_timings[NUM_STATES] = {PERIOD_TIME_UPDATE_US, PERIO PERIOD_ANIMATION_US, PERIOD_TETRIS_US, PERIOD_SNAKE_US, PERIOD_PONG_US, PERIOD_ANIMATION_US}; -// ---------------------------------------------------------------------------------- -// STATIC VARIABLES -// ---------------------------------------------------------------------------------- -ESP8266Timer ITimer; // ESP8266 Timer - -// ---------------------------------------------------------------------------------- -// ISR -// ---------------------------------------------------------------------------------- -void IRAM_ATTR TimerHandler() -{ - tm_mgr.increment_time_now_local(); -} // ---------------------------------------------------------------------------------- // SETUP @@ -153,7 +135,7 @@ void setup() led_matrix.set_current_limit(CURRENT_LIMIT_LED); // Turn on minutes LEDs (blue) - led_matrix.set_min_indicator(15, colors_24bit[6]); + led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]); led_matrix.draw_on_matrix_instant(); /* Use WiFiMaanger for handling initial Wifi setup */ @@ -172,7 +154,7 @@ void setup() WiFi.persistent(true); // Turn off minutes LEDs - led_matrix.set_min_indicator(15, 0); + led_matrix.set_min_indicator((uint8_t)0b1111, 0); led_matrix.draw_on_matrix_instant(); // init ESP8266 File manager (LittleFS) @@ -194,13 +176,16 @@ void setup() cold_start_setup(); } - // get initial time - if (tm_mgr.ntp_time_update() == NTP_UPDATE_OK) + // set up time manager and get initial time + tm_mgr = TimeManager(MY_TZ, NTP_SERVER_URL, check_wifi_status, NTP_MAX_OFFLINE_TIME_S, &logger); + tm_mgr.init(); + + if (tm_mgr.get_time() == TIME_UPDATE_OK) { // 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.hour(), tm_mgr.minute()); show_string_on_clock(timeMessage, main_color_clock); - draw_minute_indicator(tm_mgr.tm_min(), main_color_clock); + draw_minute_indicator(tm_mgr.minute(), main_color_clock); led_matrix.draw_on_matrix_smooth(filter_factor); } else @@ -209,11 +194,8 @@ void setup() } // init all animation modes - // init snake random_snake(true, 8, colors_24bit[1], -1); - // init spiral draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6); - // init random tetris random_tetris(true); // Set range limits @@ -280,17 +262,16 @@ void loop() if ((current_time_us - last_time_update_us) >= PERIOD_TIME_UPDATE_US) { - if (tm_mgr.ntp_time_update() == NTP_UPDATE_OK) // NTP time update - { - logger.log_string("NTP sync successful!"); - } + (void)tm_mgr.get_time(); // NTP time update - if (tm_mgr.ntp_update_failed_prolonged() == true) + if (tm_mgr.ntp_sync_timeout()) { logger.log_string("Trigger restart due to being offline for too long..."); delay(100); ESP.restart(); } + + last_time_update_us = system_get_time(); } if ((current_time_us - last_nightmode_check_us) >= PERIOD_NIGHTMODE_CHECK_US) @@ -366,87 +347,87 @@ void handle_current_state() { switch (current_state) { - case ST_CLOCK: // state clock - { - if (tm_mgr.ntp_sync_successful() && tm_mgr.tm_state() == TM_NORMAL) + 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); - draw_minute_indicator((uint8_t)tm_mgr.tm_min(), main_color_clock); + if (tm_mgr.tm_state() == TM_NORMAL) + { + (void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock); + draw_minute_indicator((uint8_t)tm_mgr.minute(), main_color_clock); + } + else if (tm_mgr.ntp_sync_overdue()) // if NTP sync is overdue + { + (void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock); + draw_minute_indicator((uint8_t)tm_mgr.minute(), colors_24bit[6]); // in blue to indicate a network problem + } + else // if no NTP sync has been done, only show 4 blue minute indicators + { + // clear matrix + led_matrix.flush(); + // Turn on minutes LEDs (blue) + led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]); + led_matrix.draw_on_matrix_instant(); + } + break; } - else if (tm_mgr.ntp_sync_successful() && tm_mgr.tm_state() == TM_RETRY_SYNC) + case ST_DICLOCK: // state diclock { - (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 + if (tm_mgr.ntp_sync_successful()) + { + show_digital_clock((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute(), main_color_clock); + } + else + { + // clear matrix + led_matrix.flush(); + // Turn on minutes LEDs (blue) + led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]); + led_matrix.draw_on_matrix_instant(); + } + break; } - else + case ST_SPIRAL: // state spiral { - // 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(); + int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2); + if ((bool)res && spiral_direction == 0) + { + // change spiral direction to closing (draw empty LEDs) + spiral_direction = true; + // init spiral with new spiral direction + draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); + } + else if (res && spiral_direction == 1) + { + // reset spiral direction to normal drawing LEDs + spiral_direction = false; + // init spiral with new spiral direction + draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); + } + break; } - break; - } - case ST_DICLOCK: // state diclock - { - if (tm_mgr.ntp_sync_successful()) + case ST_TETRIS: // state tetris { - show_digital_clock((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min(), main_color_clock); + tetris.loopCycle(); + break; } - else + case ST_SNAKE: // state snake { - // 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(); + snake.loopCycle(); + break; } - break; - } - case ST_SPIRAL: // state spiral - { - int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2); - if ((bool)res && spiral_direction == 0) + case ST_PINGPONG: // state ping pong { - // change spiral direction to closing (draw empty LEDs) - spiral_direction = true; - // init spiral with new spiral direction - draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); + pong.loopCycle(); + break; } - else if (res && spiral_direction == 1) + case ST_HEARTS: { - // reset spiral direction to normal drawing LEDs - spiral_direction = false; - // init spiral with new spiral direction - draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); + draw_heart_animation(); + break; + } + default: + { + break; } - break; - } - case ST_TETRIS: // state tetris - { - tetris.loopCycle(); - break; - } - case ST_SNAKE: // state snake - { - snake.loopCycle(); - break; - } - case ST_PINGPONG: // state ping pong - { - pong.loopCycle(); - break; - } - case ST_HEARTS: - { - draw_heart_animation(); - break; - } - default: - { - break; - } } } @@ -498,9 +479,9 @@ bool check_wifi_status() */ void check_night_mode() { - // check if nightmode need to be activated - int hours = tm_mgr.tm_hour(); - int minutes = tm_mgr.tm_min(); + // Check if nightmode needs to be activated. This only toggles at the exact minute. + int hours = tm_mgr.hour(); + int minutes = tm_mgr.minute(); if ((hours == night_mode_times_ps->start_hour) && (minutes == night_mode_times_ps->start_min)) { @@ -522,43 +503,43 @@ void on_state_entry(uint8_t state) filter_factor = DEFAULT_SMOOTHING_FACTOR; switch (state) { - case ST_SPIRAL: - { - spiral_direction = 0; // Init spiral with normal drawing mode - draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); - break; - } - case ST_TETRIS: - { - filter_factor = 1.0f; // no smoothing - tetris.ctrlStart(); - break; - } - case ST_SNAKE: - { - filter_factor = 1.0f; // no smoothing - snake.initGame(); - break; - } - case ST_PINGPONG: - { - filter_factor = 1.0f; // no smoothing - pong.initGame(1); - break; - } - default: - { - break; - } + case ST_SPIRAL: + { + spiral_direction = 0; // Init spiral with normal drawing mode + draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); + break; + } + case ST_TETRIS: + { + filter_factor = 1.0f; // no smoothing + tetris.ctrlStart(); + break; + } + case ST_SNAKE: + { + filter_factor = 1.0f; // no smoothing + snake.initGame(); + break; + } + case ST_PINGPONG: + { + filter_factor = 1.0f; // no smoothing + pong.initGame(1); + break; + } + default: + { + break; + } } } /** - * @brief execute a state change to given newState + * @brief execute a state change to given new_state * - * @param newState the new state to be changed to + * @param new_state the new state to be changed to */ -void state_change(uint8_t newState) +void state_change(ClockState_en new_state) { if (flg_night_mode) { @@ -566,9 +547,9 @@ void state_change(uint8_t newState) } led_matrix.flush(); // first clear matrix - current_state = newState; // set new state - on_state_entry(current_state); - logger.log_string("State change to: " + state_names[current_state]); + current_state = new_state; // set new state + on_state_entry((uint8_t)current_state); + logger.log_string("State change to: " + state_names[(uint8_t)current_state]); logger.log_string("FreeMemory=" + String(ESP.getFreeHeap())); } @@ -660,7 +641,7 @@ void handle_button() } else { - state_change((current_state + 1) % (uint8_t)NUM_STATES); + state_change((ClockState_en)(((uint8_t)current_state + 1) % (uint8_t)NUM_STATES)); } } } @@ -735,6 +716,7 @@ void handle_command() { String mode_str = webserver.arg(0); logger.log_string("Mode change via Webserver to: " + mode_str); + // set current mode/state accordant sent mode if (mode_str.equals("clock")) { @@ -1049,9 +1031,9 @@ uint8_t update_brightness() { new_brightness = calculate_dynamic_brightness(brightness_ps->dyn_brightness_min, brightness_ps->dyn_brightness_max, - tm_mgr.tm_hour(), - tm_mgr.tm_min(), - tm_mgr.tm_isdst()); + tm_mgr.hour(), + tm_mgr.minute(), + tm_mgr.isdst()); } else // use static brightness {