Introduced new TimeManager class with much better offline handling. Refactoring.
This commit is contained in:
52
include/time_manager.h
Normal file
52
include/time_manager.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef TIME_MANAGER_H
|
||||||
|
#define TIME_MANAGER_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "udp_logger.h"
|
||||||
|
|
||||||
|
#define NTP_MAX_UPDATE_TIME_US (500 * 1000) // 500ms max update time
|
||||||
|
|
||||||
|
class TimeManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct tm time_info(void);
|
||||||
|
bool ntp_sync_successful(void) const; // was there a NTP sync once?
|
||||||
|
bool ntp_time_update(bool init = false);
|
||||||
|
bool ntp_update_failed_prolonged(void) const; // indicates if maximum time since last NTP update was too long
|
||||||
|
void log_time(struct tm time_info) const; // log local_time
|
||||||
|
void increment_time_now_local(void);
|
||||||
|
|
||||||
|
int tm_min(void);
|
||||||
|
int tm_hour(void);
|
||||||
|
int tm_year(void);
|
||||||
|
bool tm_isdst(void); // true if summertime
|
||||||
|
|
||||||
|
TimeManager(const char *tz,
|
||||||
|
const char *ntp_server,
|
||||||
|
uint32 ntp_update_period_s,
|
||||||
|
uint32 ntp_retry_delay_us,
|
||||||
|
uint32 ntp_max_offline_time_s,
|
||||||
|
UDPLogger *logger);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void set_up_ntp(void) const; // set up NTP server
|
||||||
|
void set_up_timer_isr(void) const; // set up timer interrupt
|
||||||
|
const char *_tz; // timezone
|
||||||
|
const char *_ntp_server; // used ntp server
|
||||||
|
UDPLogger *_logger; // logger instance
|
||||||
|
struct tm _time_info; // 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.
|
||||||
|
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 */
|
||||||
@@ -33,17 +33,18 @@
|
|||||||
#define NIGHTMODE_END_MIN (0)
|
#define NIGHTMODE_END_MIN (0)
|
||||||
|
|
||||||
// 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) // 1s
|
||||||
#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_NTP_UPDATE_US (30 * 1000 * 1000) // 30s
|
#define PERIOD_TIME_UPDATE_US (500 * 1000) // 500ms
|
||||||
#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
|
||||||
#define PERIOD_TETRIS_US (50 * 1000) // 50ms
|
#define PERIOD_TETRIS_US (50 * 1000) // 50ms
|
||||||
#define TIMEOUT_LEDDIRECT_US (5 * 1000 * 1000) // 5s
|
#define TIMEOUT_LEDDIRECT_US (5 * 1000 * 1000) // 5s
|
||||||
|
#define PERIOD_BRIGHTNESS_UPDATE_US (5 * 60 * 1000 * 1000) // 300s
|
||||||
|
|
||||||
#define SHORT_PRESS_US (100 * 1000) // 100ms
|
#define SHORT_PRESS_US (100 * 1000) // 100ms
|
||||||
#define LONG_PRESS_US (2 * 1000 * 1000) // 2s
|
#define LONG_PRESS_US (2 * 1000 * 1000) // 2s
|
||||||
@@ -68,11 +69,11 @@
|
|||||||
#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 at compile time minus one. */
|
#define NTP_MININUM_RX_YEAR (atoi(BUILD_YEAR) - 1) // Will expand to current year minus one at compile time.
|
||||||
#define NTP_MININUM_YEAR (1900) // NTP minimum year is 1900
|
#define NTP_MININUM_YEAR (1900) // NTP minimum year is 1900
|
||||||
#define NTP_MAX_UPDATE_TIME_US (500000) // 500ms max update time
|
#define NTP_UPDATE_PERIOD_S (12 * 3600) // 12h period between updates
|
||||||
#define NTP_NEXT_UPDATE_DELAY_US (10000000) // 10s delay time between updates
|
#define NTP_RETRY_DELAY_US (10 * 1000 * 1000) // 10s retry delay time between failed NTP requests
|
||||||
#define NTP_WATCHDOG_COUNTER_INIT (30) // Watchdog value, count of retries before restart
|
#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 */
|
||||||
|
|||||||
@@ -61,12 +61,11 @@ typedef enum
|
|||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
// FUNCTIONS DECLARATIONS
|
// FUNCTIONS DECLARATIONS
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
bool get_ntp_time(uint32 timeout);
|
bool check_wifi_status(void);
|
||||||
String leading_zero2digit(int value);
|
String leading_zero2digit(int value);
|
||||||
uint8_t calculate_dynamic_brightness(uint8_t min_brightness, uint8_t max_brightness, int hours, int minutes, bool summertime);
|
uint8_t calculate_dynamic_brightness(uint8_t min_brightness, uint8_t max_brightness, int hours, int minutes, bool summertime);
|
||||||
uint8_t update_brightness(void);
|
uint8_t update_brightness(void);
|
||||||
void check_night_mode(void);
|
void check_night_mode(void);
|
||||||
void check_wifi_status(void);
|
|
||||||
void cold_start_setup(void);
|
void cold_start_setup(void);
|
||||||
void draw_main_color(void);
|
void draw_main_color(void);
|
||||||
void handle_button(void);
|
void handle_button(void);
|
||||||
@@ -76,8 +75,6 @@ void handle_data_request(void);
|
|||||||
void handle_led_direct(void);
|
void handle_led_direct(void);
|
||||||
void limit_value_ranges(void);
|
void limit_value_ranges(void);
|
||||||
void log_data(void);
|
void log_data(void);
|
||||||
void log_time(tm local_time);
|
|
||||||
void ntp_time_update(uint32 max_update_time);
|
|
||||||
void on_state_entry(uint8_t state);
|
void on_state_entry(uint8_t state);
|
||||||
void read_settings_from_EEPROM(void);
|
void read_settings_from_EEPROM(void);
|
||||||
void reset_wifi_credentials(void);
|
void reset_wifi_credentials(void);
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ default_envs = nodemcuv2
|
|||||||
[env]
|
[env]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
board = nodemcuv2
|
board = nodemcuv2
|
||||||
|
build_flags = -DUSING_TIM_DIV16=1
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
adafruit/Adafruit BusIO@^1.15.0
|
adafruit/Adafruit BusIO@^1.15.0
|
||||||
adafruit/Adafruit NeoMatrix@^1.3.0
|
adafruit/Adafruit NeoMatrix@^1.3.0
|
||||||
adafruit/Adafruit NeoPixel@^1.11.0
|
adafruit/Adafruit NeoPixel@^1.11.0
|
||||||
densaugeo/base64@^1.4.0
|
densaugeo/base64@^1.4.0
|
||||||
tzapu/WiFiManager@^0.16.0
|
khoih-prog/ESP8266TimerInterrupt@^1.6.0
|
||||||
|
tzapu/WiFiManager@^0.16.0
|
||||||
|
|
||||||
[env:nodemcuv2]
|
[env:nodemcuv2]
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
|
|||||||
166
src/connectivity/time_manager.cpp
Normal file
166
src/connectivity/time_manager.cpp
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
#include "time_manager.h"
|
||||||
|
#include <ESP8266TimerInterrupt.h> // https://github.com/khoih-prog/ESP8266TimerInterrupt
|
||||||
|
#include "wordclock_constants.h"
|
||||||
|
#include "time.h"
|
||||||
|
|
||||||
|
extern UDPLogger logger;
|
||||||
|
extern ESP8266Timer ITimer; // ESP8266 Timer
|
||||||
|
extern void IRAM_ATTR TimerHandler(); // ISR function
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------
|
||||||
|
// Class
|
||||||
|
// ----------------------------------------------------------------------------------
|
||||||
|
TimeManager::TimeManager(const char *tz,
|
||||||
|
const char *ntp_server,
|
||||||
|
uint32 ntp_update_period_s,
|
||||||
|
uint32 ntp_retry_delay_us,
|
||||||
|
uint32 ntp_max_offline_time_s,
|
||||||
|
UDPLogger *logger)
|
||||||
|
{
|
||||||
|
_tz = tz;
|
||||||
|
_ntp_server = ntp_server;
|
||||||
|
_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tm TimeManager::time_info(void)
|
||||||
|
{
|
||||||
|
localtime_r(&_time_now_local, &_time_info); // convert time
|
||||||
|
return _time_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimeManager::ntp_sync_successful(void) const
|
||||||
|
{
|
||||||
|
return (_time_now_ntp > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NTP time update, should be called in loop().
|
||||||
|
*
|
||||||
|
* @retval true if last update was successful
|
||||||
|
*/
|
||||||
|
bool TimeManager::ntp_time_update(bool init)
|
||||||
|
{
|
||||||
|
// Check if minimum update delay has elapsed
|
||||||
|
if (!init && (((system_get_time() - _ntp_sync_timestamp_us) <= _ntp_retry_delay_us) || ((system_get_time() - _ntp_sync_timestamp_us) <= (_ntp_update_period_s * 1000000))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init)
|
||||||
|
{
|
||||||
|
set_up_ntp(); // set up NTP server once
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ntp_update_successful = false; // NTP time update
|
||||||
|
struct tm time_info; // local NTP time info
|
||||||
|
|
||||||
|
_ntp_sync_timestamp_us = system_get_time(); // NTP update start time
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
} while (((system_get_time() - _ntp_sync_timestamp_us) <= NTP_MAX_UPDATE_TIME_US) && (time_info.tm_year < (NTP_MININUM_RX_YEAR - NTP_MININUM_YEAR)));
|
||||||
|
|
||||||
|
ntp_update_successful = (time_info.tm_year <= (NTP_MININUM_RX_YEAR - NTP_MININUM_YEAR)) ? false : true; // sanity check
|
||||||
|
|
||||||
|
if (ntp_update_successful == true)
|
||||||
|
{
|
||||||
|
_ntp_sync_timestamp_us = system_get_time(); // save NTP update timestamp
|
||||||
|
_time_info = time_info; // take over time_info to member variable
|
||||||
|
log_time(_time_info); // 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
|
||||||
|
{
|
||||||
|
_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 (init) // only set up the timer once after NTP update was successful
|
||||||
|
{
|
||||||
|
set_up_timer_isr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.log_string("NTP-Update was not successful. Retrying in " + String(_ntp_retry_delay_us / 1000) + "ms.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ntp_update_successful;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimeManager::ntp_update_failed_prolonged(void) const
|
||||||
|
{
|
||||||
|
return _time_now_local >= (_time_now_ntp + (time_t)_ntp_max_offline_time_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TimeManager::tm_min(void)
|
||||||
|
{
|
||||||
|
localtime_r(&_time_now_local, &_time_info); // convert time
|
||||||
|
return _time_info.tm_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TimeManager::tm_hour(void)
|
||||||
|
{
|
||||||
|
localtime_r(&_time_now_local, &_time_info); // convert time
|
||||||
|
return _time_info.tm_hour;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TimeManager::tm_year(void)
|
||||||
|
{
|
||||||
|
localtime_r(&_time_now_local, &_time_info); // convert time
|
||||||
|
return _time_info.tm_year;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimeManager::tm_isdst(void)
|
||||||
|
{
|
||||||
|
localtime_r(&_time_now_local, &_time_info); // convert time
|
||||||
|
return _time_info.tm_isdst > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
void TimeManager::set_up_timer_isr(void) const
|
||||||
|
{
|
||||||
|
// set up timer interrupt after NTP update is done
|
||||||
|
if (ntp_sync_successful())
|
||||||
|
{
|
||||||
|
(void)ITimer.attachInterruptInterval(PERIOD_CLOCK_UPDATE_US, TimerHandler);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.log_string("WARNING: Timer interrupt was not attached!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeManager::set_up_ntp(void) const
|
||||||
|
{
|
||||||
|
if ((_tz != nullptr) && (_ntp_server != nullptr))
|
||||||
|
{
|
||||||
|
// set up NTP server and timezone at init
|
||||||
|
configTime(_tz, _ntp_server);
|
||||||
|
logger.log_string(String("NTP server was initialized!"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.log_string(String("Timezone and/or NTP-Server were not given!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Log time_info as string.
|
||||||
|
*
|
||||||
|
* @param local_time
|
||||||
|
*/
|
||||||
|
void TimeManager::log_time(struct tm time_info) const
|
||||||
|
{
|
||||||
|
char strftime_buf[64]; // Time string buffer
|
||||||
|
strftime(strftime_buf, sizeof(strftime_buf), "%c", &time_info);
|
||||||
|
logger.log_string(String(strftime_buf));
|
||||||
|
}
|
||||||
@@ -27,10 +27,11 @@
|
|||||||
#include <Adafruit_NeoMatrix.h> // https://github.com/adafruit/Adafruit_NeoMatrix
|
#include <Adafruit_NeoMatrix.h> // https://github.com/adafruit/Adafruit_NeoMatrix
|
||||||
#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 <time.h>
|
|
||||||
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager WiFi Configuration Magic
|
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager WiFi Configuration Magic
|
||||||
|
|
||||||
// own libraries
|
// own libraries
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
#include "render_functions.h"
|
#include "render_functions.h"
|
||||||
#include "snake.h"
|
#include "snake.h"
|
||||||
#include "tetris.h"
|
#include "tetris.h"
|
||||||
|
#include "time_manager.h"
|
||||||
#include "udp_logger.h"
|
#include "udp_logger.h"
|
||||||
#include "wordclock_constants.h"
|
#include "wordclock_constants.h"
|
||||||
|
|
||||||
@@ -52,8 +54,8 @@ UDPLogger logger; // Global UDP logger instance
|
|||||||
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(MATRIX_WIDTH, MATRIX_HEIGHT + 1, NEOPIXEL_PIN,
|
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(MATRIX_WIDTH, MATRIX_HEIGHT + 1, NEOPIXEL_PIN,
|
||||||
NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
|
NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
|
||||||
NEO_GRB + NEO_KHZ800); // NeoMatrix
|
NEO_GRB + NEO_KHZ800); // NeoMatrix
|
||||||
ESP8266WebServer webserver(HTTP_PORT); // Webserver
|
|
||||||
LEDMatrix led_matrix = LEDMatrix(&matrix, DEFAULT_BRIGHTNESS, &logger); // NeoMatrix wrapper
|
LEDMatrix led_matrix = LEDMatrix(&matrix, DEFAULT_BRIGHTNESS, &logger); // NeoMatrix wrapper
|
||||||
|
ESP8266WebServer webserver(HTTP_PORT); // Webserver
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
// STATIC VARIABLES
|
// STATIC VARIABLES
|
||||||
@@ -69,14 +71,14 @@ 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
|
// Time ManagerW
|
||||||
static struct tm time_info; // Structure tm holds time information
|
static TimeManager tm_mgr = TimeManager(MY_TZ, NTP_SERVER_URL,
|
||||||
static time_t time_now; // Seconds since Epoch (1970) - UTC
|
NTP_UPDATE_PERIOD_S,
|
||||||
|
NTP_RETRY_DELAY_US,
|
||||||
|
NTP_MAX_OFFLINE_TIME_S,
|
||||||
|
&logger);
|
||||||
|
|
||||||
// NTP
|
// State variablesW
|
||||||
static uint32 last_ntp_update_us = 0; // Time of last NTP update
|
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -98,9 +100,23 @@ static const float qtly_brightness_factor[96] = {
|
|||||||
0.998f, 0.997f, 0.995f, 0.991f, 0.986f, 0.978f, 0.966f, 0.949f, 0.927f, 0.896f, 0.858f, 0.811f, 0.755f, 0.691f,
|
0.998f, 0.997f, 0.995f, 0.991f, 0.986f, 0.978f, 0.966f, 0.949f, 0.927f, 0.896f, 0.858f, 0.811f, 0.755f, 0.691f,
|
||||||
0.62f, 0.545f, 0.468f, 0.392f, 0.32f, 0.253f, 0.194f, 0.143f, 0.101f, 0.069f, 0.044f, 0.026f, 0.014f, 0.007f,
|
0.62f, 0.545f, 0.468f, 0.392f, 0.32f, 0.253f, 0.194f, 0.143f, 0.101f, 0.069f, 0.044f, 0.026f, 0.014f, 0.007f,
|
||||||
0.003f, 0.001f, 0.0f, 0.0f};
|
0.003f, 0.001f, 0.0f, 0.0f};
|
||||||
static const uint32_t period_timings[NUM_STATES] = {PERIOD_CLOCK_UPDATE_US, PERIOD_CLOCK_UPDATE_US,
|
static const uint32_t period_timings[NUM_STATES] = {PERIOD_TIME_UPDATE_US, PERIOD_TIME_UPDATE_US,
|
||||||
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
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
@@ -162,12 +178,12 @@ void setup()
|
|||||||
// init ESP8266 File manager (LittleFS)
|
// init ESP8266 File manager (LittleFS)
|
||||||
setup_filesystem();
|
setup_filesystem();
|
||||||
|
|
||||||
// setup OTA
|
// set up OTA
|
||||||
setupOTA(HOSTNAME);
|
setupOTA(HOSTNAME);
|
||||||
|
|
||||||
webserver.on("/cmd", handle_command); // process commands
|
webserver.on("/cmd", handle_command); // process commands
|
||||||
webserver.on("/data", handle_data_request); // process data requests
|
webserver.on("/data", handle_data_request); // process data requests
|
||||||
webserver.on("/leddirect", HTTP_POST, handle_led_direct); // Call the 'handle_led_direct' function when a POST request is made to URI "/leddirect"
|
webserver.on("/leddirect", HTTP_POST, handle_led_direct); // call the 'handle_led_direct' function when a POST request is made to URI "/leddirect"
|
||||||
webserver.begin();
|
webserver.begin();
|
||||||
|
|
||||||
// create UDP Logger to send logging messages via UDP multicast
|
// create UDP Logger to send logging messages via UDP multicast
|
||||||
@@ -178,15 +194,19 @@ void setup()
|
|||||||
cold_start_setup();
|
cold_start_setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup NTP
|
// get initial time
|
||||||
configTime(MY_TZ, NTP_SERVER_URL);
|
if (tm_mgr.ntp_time_update(true))
|
||||||
ntp_time_update(NTP_MAX_UPDATE_TIME_US); // NTP time update
|
{
|
||||||
|
// 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(time_info.tm_hour, time_info.tm_min);
|
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(time_info.tm_min, main_color_clock);
|
led_matrix.draw_on_matrix_smooth(filter_factor);
|
||||||
led_matrix.draw_on_matrix_smooth(filter_factor);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.log_string("Warning: Initial time sync failed! Retrying in a bit.");
|
||||||
|
}
|
||||||
|
|
||||||
// init all animation modes
|
// init all animation modes
|
||||||
// init snake
|
// init snake
|
||||||
@@ -214,17 +234,21 @@ void loop()
|
|||||||
uint32 current_time_us = system_get_time();
|
uint32 current_time_us = system_get_time();
|
||||||
|
|
||||||
// Timestamp variables
|
// Timestamp variables
|
||||||
static uint32 last_animation_step_us = 0; // time of last animation step
|
static uint32 last_animation_step_us = 0; // timestamp of last animation step
|
||||||
static uint32 last_matrix_update_us = 0; // time of last Matrix update
|
static uint32 last_matrix_update_us = 0; // timestamp of last Matrix update
|
||||||
static uint32 last_heartbeat_us = 0; // time of last heartbeat sending
|
static uint32 last_time_update_us = 0; // timestamp of last time update
|
||||||
static uint32 last_nightmode_check_us = 0; // time of last nightmode check
|
static uint32 last_heartbeat_us = 0; // timestamp of last heartbeat sending
|
||||||
|
static uint32 last_nightmode_check_us = 0; // timestamp of last nightmode check
|
||||||
|
static uint32 last_brightness_update_us = 0; // timestamp of last brightness update
|
||||||
|
|
||||||
handleOTA(); // handle OTA
|
handleOTA(); // handle OTA
|
||||||
|
|
||||||
webserver.handleClient(); // handle webserver
|
webserver.handleClient(); // handle webserver
|
||||||
|
|
||||||
|
handle_button(); // handle button press
|
||||||
|
|
||||||
// send regularly heartbeat messages via UDP multicast
|
// send regularly heartbeat messages via UDP multicast
|
||||||
if ((current_time_us - last_heartbeat_us) > PERIOD_HEARTBEAT_US)
|
if ((current_time_us - last_heartbeat_us) >= PERIOD_HEARTBEAT_US)
|
||||||
{
|
{
|
||||||
send_heartbeat(); // send heartbeat update
|
send_heartbeat(); // send heartbeat update
|
||||||
last_heartbeat_us = system_get_time();
|
last_heartbeat_us = system_get_time();
|
||||||
@@ -232,38 +256,48 @@ void loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!flg_night_mode && ((current_time_us - last_animation_step_us) > period_timings[current_state]) &&
|
if (!flg_night_mode && ((current_time_us - last_animation_step_us) > period_timings[current_state]) &&
|
||||||
((current_time_us - last_led_direct_us) > TIMEOUT_LEDDIRECT_US))
|
((current_time_us - last_led_direct_us) >= TIMEOUT_LEDDIRECT_US))
|
||||||
{
|
{
|
||||||
handle_current_state(); // handle current state
|
handle_current_state(); // handle current state
|
||||||
last_animation_step_us = system_get_time();
|
last_animation_step_us = system_get_time();
|
||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((current_time_us - last_matrix_update_us) > PERIOD_MATRIX_UPDATE_US)
|
if ((current_time_us - last_brightness_update_us) >= PERIOD_BRIGHTNESS_UPDATE_US)
|
||||||
|
{
|
||||||
|
current_brightness = update_brightness(); // update brightness
|
||||||
|
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
|
||||||
|
last_brightness_update_us = system_get_time();
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((current_time_us - last_matrix_update_us) >= PERIOD_MATRIX_UPDATE_US)
|
||||||
{
|
{
|
||||||
update_matrix(); // update matrix
|
update_matrix(); // update matrix
|
||||||
last_matrix_update_us = system_get_time();
|
last_matrix_update_us = system_get_time();
|
||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_button(); // handle button press
|
if ((current_time_us - last_time_update_us) >= PERIOD_TIME_UPDATE_US)
|
||||||
|
|
||||||
if ((current_time_us - last_ntp_update_us) > PERIOD_NTP_UPDATE_US)
|
|
||||||
{
|
{
|
||||||
check_wifi_status(); // check WiFi status before NTP update
|
if (tm_mgr.ntp_sync_successful() == true) // regular case
|
||||||
delay(10);
|
{
|
||||||
|
tm_mgr.ntp_time_update(); // NTP time update
|
||||||
|
}
|
||||||
|
else // if there was never a NTP time update before (set up failed)
|
||||||
|
{
|
||||||
|
tm_mgr.ntp_time_update(true); // NTP time update with init!
|
||||||
|
}
|
||||||
|
|
||||||
ntp_time_update(NTP_MAX_UPDATE_TIME_US); // NTP time update
|
if (tm_mgr.ntp_update_failed_prolonged() == true)
|
||||||
delay(10);
|
{
|
||||||
|
logger.log_string("Trigger restart due to being offline for too long...");
|
||||||
current_brightness = update_brightness(); // update brightness every PERIOD_NTP_UPDATE_US
|
delay(100);
|
||||||
delay(10);
|
ESP.restart();
|
||||||
|
}
|
||||||
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
|
|
||||||
delay(10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((current_time_us - last_nightmode_check_us) > PERIOD_NIGHTMODE_CHECK_US)
|
if ((current_time_us - last_nightmode_check_us) >= PERIOD_NIGHTMODE_CHECK_US)
|
||||||
{
|
{
|
||||||
check_night_mode(); // check night mode
|
check_night_mode(); // check night mode
|
||||||
last_nightmode_check_us = system_get_time();
|
last_nightmode_check_us = system_get_time();
|
||||||
@@ -288,7 +322,7 @@ void log_data()
|
|||||||
logger.log_string("Nightmode starts at: " + String(night_mode_times_ps->start_hour) + ":" + String(night_mode_times_ps->start_min));
|
logger.log_string("Nightmode starts at: " + String(night_mode_times_ps->start_hour) + ":" + String(night_mode_times_ps->start_min));
|
||||||
logger.log_string("Nightmode ends at: " + String(night_mode_times_ps->end_hour) + ":" + String(night_mode_times_ps->end_min));
|
logger.log_string("Nightmode ends at: " + String(night_mode_times_ps->end_hour) + ":" + String(night_mode_times_ps->end_min));
|
||||||
|
|
||||||
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
|
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -329,38 +363,6 @@ void cold_start_setup()
|
|||||||
led_matrix.draw_on_matrix_instant();
|
led_matrix.draw_on_matrix_instant();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Updates the NTP time
|
|
||||||
*
|
|
||||||
* @return boolean - true if NTP update was successful, false otherwise
|
|
||||||
*/
|
|
||||||
bool get_ntp_time(uint32 timeout)
|
|
||||||
{
|
|
||||||
uint32 start_time_us = system_get_time();
|
|
||||||
do
|
|
||||||
{
|
|
||||||
time(&time_now);
|
|
||||||
localtime_r(&time_now, &time_info);
|
|
||||||
yield();
|
|
||||||
} while (((system_get_time() - start_time_us) <= timeout) && (time_info.tm_year < (NTP_MININUM_RX_YEAR - NTP_MININUM_YEAR)));
|
|
||||||
|
|
||||||
logger.log_string(String("NTP-Update duration: " + String(system_get_time() - start_time_us) + String("us")));
|
|
||||||
|
|
||||||
return ((time_info.tm_year <= (NTP_MININUM_RX_YEAR - NTP_MININUM_YEAR)) ? false : true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Log local_time.
|
|
||||||
*
|
|
||||||
* @param local_time
|
|
||||||
*/
|
|
||||||
void log_time(tm local_time)
|
|
||||||
{
|
|
||||||
char strftime_buf[64]; // Time string buffer
|
|
||||||
strftime(strftime_buf, sizeof(strftime_buf), "%c", &time_info);
|
|
||||||
logger.log_string(String(strftime_buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Update and control word clock states.
|
* @brief Update and control word clock states.
|
||||||
*/
|
*/
|
||||||
@@ -370,13 +372,13 @@ void handle_current_state()
|
|||||||
{
|
{
|
||||||
case ST_CLOCK: // state clock
|
case ST_CLOCK: // state clock
|
||||||
{
|
{
|
||||||
(void)show_string_on_clock(time_to_string((uint8_t)time_info.tm_hour, (uint8_t)time_info.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)time_info.tm_min, main_color_clock);
|
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), main_color_clock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ST_DICLOCK: // state diclock
|
case ST_DICLOCK: // state diclock
|
||||||
{
|
{
|
||||||
show_digital_clock((uint8_t)time_info.tm_hour, (uint8_t)time_info.tm_min, main_color_clock);
|
show_digital_clock((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min(), main_color_clock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ST_SPIRAL: // state spiral
|
case ST_SPIRAL: // state spiral
|
||||||
@@ -452,14 +454,18 @@ void send_heartbeat()
|
|||||||
* @brief Check WiFi status and try to reconnect if needed. Should be called in loop() before NTP update.
|
* @brief Check WiFi status and try to reconnect if needed. Should be called in loop() before NTP update.
|
||||||
*
|
*
|
||||||
* @param None
|
* @param None
|
||||||
|
*
|
||||||
|
* @retval bool - true if WiFi is connected, false otherwise
|
||||||
*/
|
*/
|
||||||
void check_wifi_status()
|
bool check_wifi_status()
|
||||||
{
|
{
|
||||||
|
bool connected = (WiFi.status() == WL_CONNECTED);
|
||||||
// Check wifi status
|
// Check wifi status
|
||||||
if (WiFi.status() != WL_CONNECTED)
|
if (!connected)
|
||||||
{
|
{
|
||||||
Serial.println("WiFi connection lost! Trying to reconnect automatically...");
|
Serial.println("WiFi connection lost! Trying to reconnect automatically...");
|
||||||
}
|
}
|
||||||
|
return connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -470,8 +476,8 @@ void check_wifi_status()
|
|||||||
void check_night_mode()
|
void check_night_mode()
|
||||||
{
|
{
|
||||||
// check if nightmode need to be activated
|
// check if nightmode need to be activated
|
||||||
int hours = time_info.tm_hour;
|
int hours = tm_mgr.tm_hour();
|
||||||
int minutes = time_info.tm_min;
|
int minutes = tm_mgr.tm_min();
|
||||||
|
|
||||||
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))
|
||||||
{
|
{
|
||||||
@@ -483,38 +489,6 @@ void check_night_mode()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief NTP time update, should be called in loop().
|
|
||||||
*
|
|
||||||
* @param None
|
|
||||||
*/
|
|
||||||
void ntp_time_update(uint32 max_update_time)
|
|
||||||
{
|
|
||||||
static int watchdog_counter = NTP_WATCHDOG_COUNTER_INIT; // Watchdog counter to trigger restart if NTP update was not possible 30 times in a row (5min)
|
|
||||||
bool ntp_retval = get_ntp_time(max_update_time); // NTP time update
|
|
||||||
|
|
||||||
if (ntp_retval == true)
|
|
||||||
{
|
|
||||||
log_time(time_info);
|
|
||||||
last_ntp_update_us = system_get_time();
|
|
||||||
watchdog_counter = NTP_WATCHDOG_COUNTER_INIT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.log_string("NTP-Update was not successful.");
|
|
||||||
last_ntp_update_us += NTP_NEXT_UPDATE_DELAY_US;
|
|
||||||
watchdog_counter--;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log_string("Watchdog counter: " + String(watchdog_counter));
|
|
||||||
if (watchdog_counter <= 0)
|
|
||||||
{
|
|
||||||
logger.log_string("Trigger restart due to watchdog...");
|
|
||||||
delay(100);
|
|
||||||
ESP.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief call entry action of given state
|
* @brief call entry action of given state
|
||||||
*
|
*
|
||||||
@@ -1052,9 +1026,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,
|
||||||
time_info.tm_hour,
|
tm_mgr.tm_hour(),
|
||||||
time_info.tm_min,
|
tm_mgr.tm_min(),
|
||||||
time_info.tm_isdst);
|
tm_mgr.tm_isdst());
|
||||||
}
|
}
|
||||||
else // use static brightness
|
else // use static brightness
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user