Rework TimeManager & NTP handling. Remove module ESP8266TimerInterrupt.
This commit is contained in:
@@ -7,53 +7,54 @@
|
|||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
NTP_UPDATE_FAILED = 0,
|
TIME_UPDATE_FAILED = 0,
|
||||||
NTP_UPDATE_OK = 1,
|
TIME_UPDATE_OK = 1,
|
||||||
NTP_UPDATE_PENDING = 2,
|
TIME_UPDATE_PENDING = 2,
|
||||||
NTP_UPDATE_RETRY_DELAY = 3,
|
} TimeUpdateState;
|
||||||
NTP_UPDATE_TOO_EARLY = 4,
|
|
||||||
NTP_UPDATE_SETUP_FAILED = 5,
|
|
||||||
} NtpUpdateState;
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
TM_INIT = 0,
|
TM_INIT = 0,
|
||||||
TM_INITIAL_SYNC = 1,
|
TM_INITIAL_SYNC = 1,
|
||||||
TM_RETRY_SYNC = 2,
|
TM_NORMAL = 2,
|
||||||
TM_NORMAL = 3,
|
TM_SYNC_OVERDUE = 3,
|
||||||
TM_PROLONGED_SYNC_FAIL = 4,
|
TM_SYNC_TIMEOUT = 4,
|
||||||
TM_SETUP_FAILED = 5,
|
TM_SETUP_FAILED = 5,
|
||||||
} TimeManagerState;
|
} TimeManagerState;
|
||||||
|
|
||||||
class TimeManager
|
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:
|
public:
|
||||||
// constructor
|
// constructors
|
||||||
|
TimeManager();
|
||||||
TimeManager(const char *tz,
|
TimeManager(const char *tz,
|
||||||
const char *ntp_server,
|
const char *ntp_server,
|
||||||
uint32 ntp_update_period_s,
|
bool (*is_wifi_connected)(void),
|
||||||
uint32 ntp_retry_delay_us,
|
|
||||||
uint32 ntp_max_offline_time_s,
|
uint32 ntp_max_offline_time_s,
|
||||||
UDPLogger *logger);
|
UDPLogger *logger);
|
||||||
|
|
||||||
// ntp methods
|
// init
|
||||||
bool ntp_sync_successful(void) const; // was there a NTP sync once?
|
void init();
|
||||||
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
|
|
||||||
|
|
||||||
// ISR method
|
// callback
|
||||||
void increment_time_now_local(void); // should be called by timer ISR
|
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
|
// getter for time values
|
||||||
bool tm_isdst(void); // true if summertime
|
bool isdst(void) const; // true if summertime (daylight saving time)
|
||||||
int tm_day(void);
|
int day(void) const;
|
||||||
int tm_hour(void);
|
int hour(void) const;
|
||||||
int tm_min(void);
|
int minute(void) const;
|
||||||
int tm_mon(void);
|
int month(void) const;
|
||||||
int tm_year(void);
|
int year(void) const;
|
||||||
struct tm time_info(void);
|
struct tm time_info(void) const;
|
||||||
|
|
||||||
// getter
|
// getter
|
||||||
TimeManagerState tm_state(void) const; // get current state
|
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
|
void log_time(struct tm time_info) const; // log argument time_info
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// setup methods
|
void _set_up_ntp(void); // set up NTP server
|
||||||
void _set_up_ntp(void); // set up NTP server
|
bool (*_is_wifi_connected)(void); // function to check if wifi is connected
|
||||||
void _set_up_timer_isr(void); // set up timer interrupt
|
|
||||||
|
|
||||||
|
const char *_ntp_server = "pool.ntp.org"; // ntp server address
|
||||||
const char *_tz; // timezone
|
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
|
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 _now = 0; // local time value
|
||||||
time_t _time_now_ntp = 0; // NTP timer value, seconds since Epoch (1970) - UTC, only synced by NTP request.
|
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_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 */
|
#endif /* TIME_MANAGER_H */
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
#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
|
||||||
#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_PONG_US (10 * 1000) // 10ms
|
||||||
#define PERIOD_SNAKE_US (50 * 1000) // 50ms
|
#define PERIOD_SNAKE_US (50 * 1000) // 50ms
|
||||||
#define PERIOD_STATE_CHANGE_US (10 * 1000 * 1000) // 10s
|
#define PERIOD_STATE_CHANGE_US (10 * 1000 * 1000) // 10s
|
||||||
@@ -69,11 +69,9 @@
|
|||||||
#define MATRIX_HEIGHT (11)
|
#define MATRIX_HEIGHT (11)
|
||||||
|
|
||||||
// NTP macros
|
// NTP macros
|
||||||
#define BUILD_YEAR (__DATE__ + 7) // Will expand to current year at compile time as string.
|
#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_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_START_YEAR (1900) // NTP minimum year is 1900
|
||||||
#define NTP_UPDATE_PERIOD_S (6 * 3600) // 6h period between NTP updates
|
#define NTP_MAX_OFFLINE_TIME_S (7 * 24 * 3600) // Watchdog value, maximum offline time before a restart is triggered
|
||||||
#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
|
|
||||||
|
|
||||||
#endif /* WORDCLOCK_CONSTANTS_H */
|
#endif /* WORDCLOCK_CONSTANTS_H */
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ void send_heartbeat(void);
|
|||||||
void set_dynamic_brightness(bool state);
|
void set_dynamic_brightness(bool state);
|
||||||
void set_main_color(uint8_t red, uint8_t green, uint8_t blue);
|
void set_main_color(uint8_t red, uint8_t green, uint8_t blue);
|
||||||
void set_night_mode(bool on);
|
void set_night_mode(bool on);
|
||||||
void state_change(uint8_t newState);
|
void state_change(ClockState_en new_state);
|
||||||
void update_matrix(void);
|
void update_matrix(void);
|
||||||
void write_settings_to_EEPROM(void);
|
void write_settings_to_EEPROM(void);
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,76 @@
|
|||||||
#include "time_manager.h"
|
#include "time_manager.h"
|
||||||
#include <ESP8266TimerInterrupt.h> // https://github.com/khoih-prog/ESP8266TimerInterrupt
|
|
||||||
#include "wordclock_constants.h"
|
#include "wordclock_constants.h"
|
||||||
#include "time.h"
|
#include <coredecls.h> // required for settimeofday_cb()
|
||||||
|
#include <sntp.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
extern UDPLogger logger; // logging instance
|
extern UDPLogger logger; // logging instance
|
||||||
extern ESP8266Timer ITimer; // ESP8266 Timer
|
|
||||||
extern void IRAM_ATTR TimerHandler(); // ISR function
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
// Class
|
// Class
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
|
TimeManager::TimeManager(){};
|
||||||
TimeManager::TimeManager(const char *tz,
|
TimeManager::TimeManager(const char *tz,
|
||||||
const char *ntp_server,
|
const char *ntp_server,
|
||||||
uint32 ntp_update_period_s,
|
bool (*is_wifi_connected)(void),
|
||||||
uint32 ntp_retry_delay_us,
|
|
||||||
uint32 ntp_max_offline_time_s,
|
uint32 ntp_max_offline_time_s,
|
||||||
UDPLogger *logger)
|
UDPLogger *logger)
|
||||||
{
|
{
|
||||||
_tz = tz;
|
_tz = tz;
|
||||||
_ntp_server = ntp_server;
|
_ntp_server = ntp_server;
|
||||||
|
_is_wifi_connected = is_wifi_connected;
|
||||||
_ntp_max_offline_time_s = ntp_max_offline_time_s;
|
_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;
|
_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
|
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 (!ntp_sync_successful())
|
||||||
if (_time_now_local >= (_time_now_ntp + (time_t)_ntp_max_offline_time_s))
|
|
||||||
{
|
{
|
||||||
_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;
|
retval = true;
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
@@ -46,117 +81,67 @@ bool TimeManager::ntp_update_failed_prolonged(void)
|
|||||||
*
|
*
|
||||||
* @retval true if last update was successful
|
* @retval true if last update was successful
|
||||||
*/
|
*/
|
||||||
NtpUpdateState TimeManager::ntp_time_update()
|
TimeUpdateState TimeManager::get_time()
|
||||||
{
|
{
|
||||||
NtpUpdateState retval = NTP_UPDATE_PENDING; // NTP time update
|
TimeUpdateState retval = TIME_UPDATE_PENDING; // NTP time update
|
||||||
struct tm time_info; // local NTP time info
|
uint32 timestamp_us = system_get_time(); // NTP update start time
|
||||||
|
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
|
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
time(&_time_now_ntp); // get time from server and save it into _time_now_ntp
|
_now = time(nullptr); // update time
|
||||||
localtime_r(&_time_now_ntp, &time_info); // convert time
|
(void)localtime_r(&_now, &time_info); // convert time
|
||||||
yield(); // since this loop could take up to NTP_MAX_UPDATE_TIME_US
|
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
|
||||||
_time_info = time_info; // take over time_info to member variable
|
_tm_state = TM_NORMAL;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_tm_state = TM_RETRY_SYNC;
|
logger.log_string("NTP-Update was not successful. Retrying in " + String(sntp_update_delay_MS_rfc_not_less_than_15000()) + "ms.");
|
||||||
logger.log_string("NTP-Update was not successful. Retrying in " + String(_ntp_retry_delay_us / 1000) + "ms.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
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;
|
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;
|
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
|
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;
|
return _time_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,21 +192,23 @@ void TimeManager::_set_up_ntp(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void TimeManager::time_set_cb(void)
|
||||||
* @brief Sets up the timer ISR
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void TimeManager::_set_up_timer_isr(void)
|
|
||||||
{
|
{
|
||||||
// set up timer interrupt after NTP update is done
|
if ((*_is_wifi_connected)())
|
||||||
if (ITimer.attachInterruptInterval(PERIOD_CLOCK_UPDATE_US, TimerHandler))
|
|
||||||
{
|
{
|
||||||
_tm_state = TM_NORMAL;
|
if (get_time() == TIME_UPDATE_OK)
|
||||||
logger.log_string(String("Timer ISR was attached successfully!"));
|
{
|
||||||
|
_ntp_sync_timestamp_s = _now;
|
||||||
|
logger.log_string("NTP successfully synced at...");
|
||||||
|
log_time();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.log_string("NTP sync failed!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_tm_state = TM_SETUP_FAILED;
|
logger.log_string("NTP sync failed, WiFi is not connected!");
|
||||||
logger.log_string("WARNING: Timer interrupt was not attached!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,6 @@
|
|||||||
#include <Adafruit_NeoPixel.h> // NeoPixel library used to run the NeoPixel LEDs: https://github.com/adafruit/Adafruit_NeoPixel
|
#include <Adafruit_NeoPixel.h> // NeoPixel library used to run the NeoPixel LEDs: https://github.com/adafruit/Adafruit_NeoPixel
|
||||||
#include <base64.hpp>
|
#include <base64.hpp>
|
||||||
#include <EEPROM.h> // from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager)
|
#include <EEPROM.h> // from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager)
|
||||||
#include <ESP8266_ISR_Timer.h> // https://github.com/khoih-prog/ESP8266TimerInterrupt
|
|
||||||
#include <ESP8266TimerInterrupt.h> // https://github.com/khoih-prog/ESP8266TimerInterrupt
|
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager WiFi Configuration Magic
|
#include <WiFiManager.h> // 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 Snake snake = Snake(&led_matrix, &logger);
|
||||||
static Tetris tetris = Tetris(&led_matrix, &logger);
|
static Tetris tetris = Tetris(&led_matrix, &logger);
|
||||||
|
|
||||||
// Time ManagerW
|
// Time Manager
|
||||||
static TimeManager tm_mgr = TimeManager(MY_TZ, NTP_SERVER_URL,
|
static TimeManager tm_mgr;
|
||||||
NTP_UPDATE_PERIOD_S,
|
|
||||||
NTP_RETRY_DELAY_US,
|
|
||||||
NTP_MAX_OFFLINE_TIME_S,
|
|
||||||
&logger);
|
|
||||||
|
|
||||||
// State variablesW
|
// State variables
|
||||||
static bool flg_night_mode = false; // State of nightmode
|
static bool flg_night_mode = false; // State of nightmode
|
||||||
static bool flg_reset_wifi_creds = false; // Used to reset stored wifi credentials
|
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 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 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_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
|
// Other variables
|
||||||
static uint32 last_led_direct_us = 0; // Time of last direct LED command (=> fall back to normal mode after timeout)
|
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_ANIMATION_US, PERIOD_TETRIS_US, PERIOD_SNAKE_US,
|
||||||
PERIOD_PONG_US, PERIOD_ANIMATION_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
|
// SETUP
|
||||||
@@ -153,7 +135,7 @@ void setup()
|
|||||||
led_matrix.set_current_limit(CURRENT_LIMIT_LED);
|
led_matrix.set_current_limit(CURRENT_LIMIT_LED);
|
||||||
|
|
||||||
// Turn on minutes LEDs (blue)
|
// 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();
|
led_matrix.draw_on_matrix_instant();
|
||||||
|
|
||||||
/* Use WiFiMaanger for handling initial Wifi setup */
|
/* Use WiFiMaanger for handling initial Wifi setup */
|
||||||
@@ -172,7 +154,7 @@ void setup()
|
|||||||
WiFi.persistent(true);
|
WiFi.persistent(true);
|
||||||
|
|
||||||
// Turn off minutes LEDs
|
// 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();
|
led_matrix.draw_on_matrix_instant();
|
||||||
|
|
||||||
// init ESP8266 File manager (LittleFS)
|
// init ESP8266 File manager (LittleFS)
|
||||||
@@ -194,13 +176,16 @@ void setup()
|
|||||||
cold_start_setup();
|
cold_start_setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// get initial time
|
// set up time manager and get initial time
|
||||||
if (tm_mgr.ntp_time_update() == NTP_UPDATE_OK)
|
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
|
// 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);
|
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);
|
led_matrix.draw_on_matrix_smooth(filter_factor);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -209,11 +194,8 @@ void setup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init all animation modes
|
// init all animation modes
|
||||||
// init snake
|
|
||||||
random_snake(true, 8, colors_24bit[1], -1);
|
random_snake(true, 8, colors_24bit[1], -1);
|
||||||
// init spiral
|
|
||||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6);
|
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6);
|
||||||
// init random tetris
|
|
||||||
random_tetris(true);
|
random_tetris(true);
|
||||||
|
|
||||||
// Set range limits
|
// Set range limits
|
||||||
@@ -280,17 +262,16 @@ 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_time_update() == NTP_UPDATE_OK) // NTP time update
|
(void)tm_mgr.get_time(); // NTP time update
|
||||||
{
|
|
||||||
logger.log_string("NTP sync successful!");
|
|
||||||
}
|
|
||||||
|
|
||||||
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...");
|
logger.log_string("Trigger restart due to being offline for too long...");
|
||||||
delay(100);
|
delay(100);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
last_time_update_us = system_get_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((current_time_us - last_nightmode_check_us) >= PERIOD_NIGHTMODE_CHECK_US)
|
if ((current_time_us - last_nightmode_check_us) >= PERIOD_NIGHTMODE_CHECK_US)
|
||||||
@@ -366,87 +347,87 @@ void handle_current_state()
|
|||||||
{
|
{
|
||||||
switch (current_state)
|
switch (current_state)
|
||||||
{
|
{
|
||||||
case ST_CLOCK: // state clock
|
case ST_CLOCK: // state clock
|
||||||
{
|
|
||||||
if (tm_mgr.ntp_sync_successful() && tm_mgr.tm_state() == TM_NORMAL)
|
|
||||||
{
|
{
|
||||||
(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.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.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);
|
if (tm_mgr.ntp_sync_successful())
|
||||||
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), colors_24bit[6]); // in blue to indicate a network problem
|
{
|
||||||
|
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
|
int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2);
|
||||||
led_matrix.flush();
|
if ((bool)res && spiral_direction == 0)
|
||||||
// Turn on minutes LEDs (blue)
|
{
|
||||||
led_matrix.set_min_indicator(15, colors_24bit[6]);
|
// change spiral direction to closing (draw empty LEDs)
|
||||||
led_matrix.draw_on_matrix_instant();
|
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_TETRIS: // state tetris
|
||||||
}
|
|
||||||
case ST_DICLOCK: // state diclock
|
|
||||||
{
|
|
||||||
if (tm_mgr.ntp_sync_successful())
|
|
||||||
{
|
{
|
||||||
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
|
snake.loopCycle();
|
||||||
led_matrix.flush();
|
break;
|
||||||
// Turn on minutes LEDs (blue)
|
|
||||||
led_matrix.set_min_indicator(15, colors_24bit[6]);
|
|
||||||
led_matrix.draw_on_matrix_instant();
|
|
||||||
}
|
}
|
||||||
break;
|
case ST_PINGPONG: // state ping pong
|
||||||
}
|
|
||||||
case ST_SPIRAL: // state spiral
|
|
||||||
{
|
|
||||||
int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2);
|
|
||||||
if ((bool)res && spiral_direction == 0)
|
|
||||||
{
|
{
|
||||||
// change spiral direction to closing (draw empty LEDs)
|
pong.loopCycle();
|
||||||
spiral_direction = true;
|
break;
|
||||||
// init spiral with new spiral direction
|
|
||||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
|
||||||
}
|
}
|
||||||
else if (res && spiral_direction == 1)
|
case ST_HEARTS:
|
||||||
{
|
{
|
||||||
// reset spiral direction to normal drawing LEDs
|
draw_heart_animation();
|
||||||
spiral_direction = false;
|
break;
|
||||||
// init spiral with new spiral direction
|
}
|
||||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
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()
|
void check_night_mode()
|
||||||
{
|
{
|
||||||
// check if nightmode need to be activated
|
// Check if nightmode needs to be activated. This only toggles at the exact minute.
|
||||||
int hours = tm_mgr.tm_hour();
|
int hours = tm_mgr.hour();
|
||||||
int minutes = tm_mgr.tm_min();
|
int minutes = tm_mgr.minute();
|
||||||
|
|
||||||
if ((hours == night_mode_times_ps->start_hour) && (minutes == night_mode_times_ps->start_min))
|
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;
|
filter_factor = DEFAULT_SMOOTHING_FACTOR;
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ST_SPIRAL:
|
case ST_SPIRAL:
|
||||||
{
|
{
|
||||||
spiral_direction = 0; // Init spiral with normal drawing mode
|
spiral_direction = 0; // Init spiral with normal drawing mode
|
||||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ST_TETRIS:
|
case ST_TETRIS:
|
||||||
{
|
{
|
||||||
filter_factor = 1.0f; // no smoothing
|
filter_factor = 1.0f; // no smoothing
|
||||||
tetris.ctrlStart();
|
tetris.ctrlStart();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ST_SNAKE:
|
case ST_SNAKE:
|
||||||
{
|
{
|
||||||
filter_factor = 1.0f; // no smoothing
|
filter_factor = 1.0f; // no smoothing
|
||||||
snake.initGame();
|
snake.initGame();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ST_PINGPONG:
|
case ST_PINGPONG:
|
||||||
{
|
{
|
||||||
filter_factor = 1.0f; // no smoothing
|
filter_factor = 1.0f; // no smoothing
|
||||||
pong.initGame(1);
|
pong.initGame(1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
break;
|
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)
|
if (flg_night_mode)
|
||||||
{
|
{
|
||||||
@@ -566,9 +547,9 @@ void state_change(uint8_t newState)
|
|||||||
}
|
}
|
||||||
|
|
||||||
led_matrix.flush(); // first clear matrix
|
led_matrix.flush(); // first clear matrix
|
||||||
current_state = newState; // set new state
|
current_state = new_state; // set new state
|
||||||
on_state_entry(current_state);
|
on_state_entry((uint8_t)current_state);
|
||||||
logger.log_string("State change to: " + state_names[current_state]);
|
logger.log_string("State change to: " + state_names[(uint8_t)current_state]);
|
||||||
logger.log_string("FreeMemory=" + String(ESP.getFreeHeap()));
|
logger.log_string("FreeMemory=" + String(ESP.getFreeHeap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,7 +641,7 @@ void handle_button()
|
|||||||
}
|
}
|
||||||
else
|
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);
|
String mode_str = webserver.arg(0);
|
||||||
logger.log_string("Mode change via Webserver to: " + mode_str);
|
logger.log_string("Mode change via Webserver to: " + mode_str);
|
||||||
|
|
||||||
// set current mode/state accordant sent mode
|
// set current mode/state accordant sent mode
|
||||||
if (mode_str.equals("clock"))
|
if (mode_str.equals("clock"))
|
||||||
{
|
{
|
||||||
@@ -1049,9 +1031,9 @@ uint8_t update_brightness()
|
|||||||
{
|
{
|
||||||
new_brightness = calculate_dynamic_brightness(brightness_ps->dyn_brightness_min,
|
new_brightness = calculate_dynamic_brightness(brightness_ps->dyn_brightness_min,
|
||||||
brightness_ps->dyn_brightness_max,
|
brightness_ps->dyn_brightness_max,
|
||||||
tm_mgr.tm_hour(),
|
tm_mgr.hour(),
|
||||||
tm_mgr.tm_min(),
|
tm_mgr.minute(),
|
||||||
tm_mgr.tm_isdst());
|
tm_mgr.isdst());
|
||||||
}
|
}
|
||||||
else // use static brightness
|
else // use static brightness
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user