Compare commits

...

2 Commits

6 changed files with 140 additions and 885 deletions

View File

@@ -1,90 +0,0 @@
#ifndef NTPCLIENTPLUS_H
#define NTPCLIENTPLUS_H
#include <Arduino.h>
#include <WiFiUdp.h>
#define UNIX_TIMESTAMP_1900 2208988800UL // careful: positive value
#define NTP_PACKET_SIZE 48
#define NTP_DEFAULT_LOCAL_PORT 1337
#define MAX_NTP_CONN_TRIES 50 // 50 * NTP_RECEIVE_WAIT_TIME_MS => 500ms
#define NTP_RECEIVE_WAIT_TIME_MS 10 // 10ms
typedef enum
{
NTP_UPDATE_TIMEOUT = -1,
NTP_UPDATE_SUCCESS = 0,
NTP_UPDATE_DIFFTOOHIGH = 1,
NTP_UPDATE_TIME_INVALID = 2
} NtpReturnValue;
/**
* @brief Own NTP Client library for Arduino with code from:
* - https://github.com/arduino-libraries/NTPClient
* - SPS&Technik - Projekt WordClock v1.02
*
*/
class NTPClientPlus
{
public:
NTPClientPlus(UDP &udp, const char *pool_server_name, int utcx, bool sw_change);
bool is_leap_year(unsigned int year);
bool check_daylight_saving_time();
int get_hours_12() const;
int get_hours_24() const;
int get_minutes() const;
int get_month(int dayOfYear);
int get_seconds() const;
int update_ntp();
long get_time_offset();
String get_formatted_date();
String get_formatted_time() const;
unsigned int get_day_of_week();
unsigned int get_year();
unsigned long get_epoch_time() const;
unsigned long get_secs_since_1900() const;
void calc_date();
void end();
void set_pool_server_name(const char *pool_server_name);
void set_time_offset(int time_offset);
void setup_ntp_client();
private:
UDP *_udp;
bool _udp_setup = false;
bool _sw_change = 1;
const char *_pool_server_name = "pool.ntp.org"; // Default time server
int _utcx = 0;
IPAddress _pool_server_ip;
long _time_offset = 0;
unsigned int _port = NTP_DEFAULT_LOCAL_PORT;
unsigned long _update_interval = 60000; // In ms
unsigned int _date_day = 0;
unsigned int _date_month = 0;
unsigned int _date_year = 0;
unsigned int _day_of_week = 0;
unsigned long _current_epoc = 0; // In s
unsigned long _last_secs_since_1900 = 0;
unsigned long _last_update = 0; // In ms
unsigned long _secs_since_1900 = 0; // seconds since 1. Januar 1900, 00:00:00
unsigned char _packet_buffer[NTP_PACKET_SIZE] = {0};
void send_ntp_packet();
void set_summertime(bool summertime);
static const unsigned long milliseconds_per_second = 1000;
static const unsigned long minutes_per_hour = 60;
static const unsigned long seconds_per_day = 86400;
static const unsigned long seconds_per_hour = 3600;
static const unsigned long seconds_per_minute = 60;
// number of days in months
unsigned int _days_per_month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
};
void wait(unsigned long time_ms);
#endif /* NTPCLIENTPLUS_H */

View File

@@ -6,8 +6,10 @@
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// CONSTANTS // CONSTANTS
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
#define AP_SSID "WordclockAP" // SSID name of Access Point #define AP_SSID "WordclockAP" // SSID name of Access Point
#define NTP_SERVER_URL "de.pool.ntp.org" // NTP server address #define NTP_SERVER_URL "de.pool.ntp.org" // NTP server address
#define MY_TZ "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" // Timezone
#define HOSTNAME (String("wordclock")) // Local hostname #define HOSTNAME (String("wordclock")) // Local hostname
#define LOGGER_MULTICAST_IP (IPAddress(230, 120, 10, 2)) // IP for UDP server #define LOGGER_MULTICAST_IP (IPAddress(230, 120, 10, 2)) // IP for UDP server
#define LOGGER_MULTICAST_PORT (8123) // Port for UDP server #define LOGGER_MULTICAST_PORT (8123) // Port for UDP server

View File

@@ -66,6 +66,8 @@ void handle_current_state(void);
void handle_data_request(void); 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_time(tm local_time);
void ntp_time_update(uint32 *last_ntp_update_us);
void ntp_time_update(uint32 *last_ntp_update_us); void ntp_time_update(uint32 *last_ntp_update_us);
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);

View File

@@ -16,6 +16,7 @@ platform = espressif8266
board = nodemcuv2 board = nodemcuv2
framework = arduino framework = arduino
lib_deps = lib_deps =
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
tzapu/WiFiManager@^0.16.0 tzapu/WiFiManager@^0.16.0

View File

@@ -1,658 +0,0 @@
#include <Arduino.h>
#include "ntp_client_plus.h"
/**
* @brief Construct a new NTPClientPlus::NTPClientPlus object
*
* @param udp UDP client
* @param pool_server_name time server name
* @param utcx UTC offset (in 1h)
* @param sw_change should summer/winter time be considered
*/
NTPClientPlus::NTPClientPlus(UDP &udp, const char *pool_server_name, int utcx, bool sw_change)
{
this->_udp = &udp;
this->_utcx = utcx;
this->_time_offset = this->seconds_per_hour * this->_utcx;
this->_pool_server_name = pool_server_name;
this->_sw_change = sw_change;
}
/**
* @brief Starts the underlying UDP client, get first NTP timestamp and calc date
*
*/
void NTPClientPlus::setup_ntp_client()
{
this->_udp->begin(this->_port);
this->_udp_setup = true;
this->update_ntp();
this->calc_date();
}
/**
* @brief Get new update from NTP
*
* @return NTP_UPDATE_TIMEOUT timeout after 500 ms
* @return NTP_UPDATE_SUCCESS after successful update
* @return NTP_UPDATE_DIFFTOOHIGH too much difference to previous received time (try again)
* @return NTP_UPDATE_TIME_INVALID time value is invalid
*/
int NTPClientPlus::update_ntp()
{
// flush any existing packets
while (this->_udp->parsePacket() != 0)
{
this->_udp->flush();
}
this->send_ntp_packet();
// Wait till data is there or timeout...
uint8_t conn_tries = 0;
int received_bytes = 0;
while ((received_bytes == 0) && (conn_tries++ <= MAX_NTP_CONN_TRIES))
{
received_bytes = this->_udp->parsePacket();
wait(NTP_RECEIVE_WAIT_TIME_MS);
}
if (conn_tries >= MAX_NTP_CONN_TRIES)
{
return NTP_UPDATE_TIMEOUT;
}
this->_udp->read(this->_packet_buffer, NTP_PACKET_SIZE);
unsigned long high_word = word(this->_packet_buffer[40], this->_packet_buffer[41]);
unsigned long low_word = word(this->_packet_buffer[42], this->_packet_buffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long temp_secs_since_1900 = high_word << 16 | low_word;
if (temp_secs_since_1900 < UNIX_TIMESTAMP_1900) // NTP time is not valid
{
return NTP_UPDATE_TIME_INVALID;
}
// check if time off last ntp update is roughly in the same range: 100sec apart (validation check)
if (this->_last_secs_since_1900 == 0 || temp_secs_since_1900 - this->_last_secs_since_1900 < 100000)
{
// Only update time then account for delay in reading the time
this->_last_update = (system_get_time() / 1000) - (NTP_RECEIVE_WAIT_TIME_MS * (conn_tries + 1));
this->_secs_since_1900 = temp_secs_since_1900;
this->_current_epoc = this->_secs_since_1900 - UNIX_TIMESTAMP_1900;
// Remember time of last update
this->_last_secs_since_1900 = temp_secs_since_1900;
return NTP_UPDATE_SUCCESS; // return 0 after successful update
}
else
{
// Remember time of last update
this->_last_secs_since_1900 = temp_secs_since_1900;
return NTP_UPDATE_DIFFTOOHIGH;
}
}
/**
* @brief Stops the underlying UDP client
*
*/
void NTPClientPlus::end()
{
this->_udp->stop();
this->_udp_setup = false;
}
/**
* @brief Setter TimeOffset
*
* @param time_offset offset from UTC in seconds
*/
void NTPClientPlus::set_time_offset(int time_offset)
{
this->_time_offset = time_offset;
}
long NTPClientPlus::get_time_offset()
{
return this->_time_offset;
}
/**
* @brief Set time server name
*
* @param pool_server_name
*/
void NTPClientPlus::set_pool_server_name(const char *pool_server_name)
{
this->_pool_server_name = pool_server_name;
}
/**
* @brief Calc seconds since 1. Jan. 1900
*
* @return unsigned long seconds since 1. Jan. 1900
*/
unsigned long NTPClientPlus::get_secs_since_1900() const
{
return this->_time_offset + // User offset
this->_secs_since_1900 + // seconds returned by the NTP server
(((system_get_time() / 1000) - this->_last_update) / 1000); // Time since last update
}
/**
* @brief Get UNIX Epoch time since 1. Jan. 1970
*
* @return unsigned long UNIX Epoch time since 1. Jan. 1970 in seconds
*/
unsigned long NTPClientPlus::get_epoch_time() const
{
return this->get_secs_since_1900() - UNIX_TIMESTAMP_1900;
}
/**
* @brief Get current hours in 24h format
*
* @return int
*/
int NTPClientPlus::get_hours_24() const
{
int hours = ((this->get_epoch_time() % 86400L) / 3600);
return hours;
}
/**
* @brief Get current hours in 12h format
*
* @return int
*/
int NTPClientPlus::get_hours_12() const
{
return this->get_hours_24() % 12;
}
/**
* @brief Get current minutes
*
* @return int
*/
int NTPClientPlus::get_minutes() const
{
return ((this->get_epoch_time() % 3600) / 60);
}
/**
* @brief Get current seconds
*
* @return int
*/
int NTPClientPlus::get_seconds() const
{
return this->get_epoch_time() % 60;
}
/**
* @brief
*
* @return String time formatted like `hh:mm:ss`
*/
String NTPClientPlus::get_formatted_time() const
{
unsigned long raw_time = this->get_epoch_time();
unsigned long hours = (raw_time % 86400L) / 3600;
String hours_str = hours < 10 ? "0" + String(hours) : String(hours);
unsigned long minutes = (raw_time % 3600) / 60;
String minute_str = minutes < 10 ? "0" + String(minutes) : String(minutes);
unsigned long seconds = raw_time % 60;
String second_str = seconds < 10 ? "0" + String(seconds) : String(seconds);
return hours_str + ":" + minute_str + ":" + second_str;
}
/**
* @brief
*
* @return String date formatted like `dd.mm.yyyy`
*/
String NTPClientPlus::get_formatted_date()
{
this->calc_date();
unsigned int dateDay = this->_date_day;
unsigned int dateMonth = this->_date_month;
unsigned int dateYear = this->_date_year;
String dayStr = dateDay < 10 ? "0" + String(dateDay) : String(dateDay);
String monthStr = dateMonth < 10 ? "0" + String(dateMonth) : String(dateMonth);
String yearStr = dateYear < 10 ? "0" + String(dateYear) : String(dateYear);
return dayStr + "." + monthStr + "." + yearStr;
}
/**
* @brief Calc date from seconds since 1900
*
*/
void NTPClientPlus::calc_date()
{
// get days since 1900
unsigned long days1900 = this->get_secs_since_1900() / seconds_per_day;
// calc current year
this->_date_year = this->get_year();
// calc how many leap days since 1.Jan 1900
int leap_days = 0;
for (unsigned int i = 1900; i < this->_date_year; i++)
{
// check if leap year
if (this->is_leap_year(i))
{
leap_days++;
}
}
leap_days = leap_days - 1;
// check if current year is leap year
if (this->is_leap_year(this->_date_year))
{
_days_per_month[2] = 29;
}
else
{
_days_per_month[2] = 28;
}
unsigned int day_of_year = (days1900 - ((this->_date_year - 1900) * 365) - leap_days);
// calc current month
this->_date_month = this->get_month(day_of_year);
this->_date_day = 0;
// calc day of month
for (unsigned int i = 0; i < this->_date_month; i++)
{
this->_date_day = this->_date_day + _days_per_month[i];
}
this->_date_day = day_of_year - this->_date_day;
// calc day of week:
// Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7
// 1. Januar 1900 was a monday
this->_day_of_week = 1;
for (unsigned int i = 0; i < days1900; i++)
{
if (this->_day_of_week < 7)
{
this->_day_of_week = this->_day_of_week + 1;
}
else
{
this->_day_of_week = 1;
}
}
// End: Calc date (dateDay, dateMonth, dateYear)
// calc if summer time active
this->check_daylight_saving_time();
}
/**
* @brief Getter for day of the week
*
* @return unsigned int
*/
unsigned int NTPClientPlus::get_day_of_week()
{
return this->_day_of_week;
}
/**
* @brief Function to calc current year
*
* @return unsigned int
*/
unsigned int NTPClientPlus::get_year()
{
unsigned long secs_since_1900 = this->get_secs_since_1900();
// NTP starts at 1. Jan 1900
unsigned int result = 1900;
unsigned int days_in_year = 0;
unsigned int days = 0;
unsigned int days_since_1900 = 0;
unsigned int for_i = 0;
bool leap_year = false;
days_since_1900 = secs_since_1900 / this->seconds_per_day;
for (for_i = 0; for_i < days_since_1900; for_i++)
{
leap_year = this->is_leap_year(result);
if (leap_year)
{
days_in_year = 366;
}
else
{
days_in_year = 365;
}
days++;
if (days >= days_in_year)
{
result++;
days = 0;
}
}
return result;
}
/**
* @brief Function to check if given year is leap year
*
* @param year
* @return true
* @return false
*/
bool NTPClientPlus::is_leap_year(unsigned int year)
{
bool result = false;
// check for leap year
if ((year % 4) == 0)
{
result = true;
if ((year % 100) == 0)
{
result = false;
if ((year % 400) == 0)
{
result = true;
}
}
}
else
{
result = false;
}
return result;
}
/**
* @brief Get Month of given day of year
*
* @param day_of_year
* @return int
*/
int NTPClientPlus::get_month(int day_of_year)
{
bool leap_year = this->is_leap_year(this->get_year());
// Month beginnings
int month_min[13] = {0, 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
// Month endings
int month_max[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
int month = 0;
int y = 0;
// Calculation of the beginning and end of each month in the leap year
if (leap_year == true)
{
for (y = 3; y < 13; y++)
{
++month_min[y];
}
for (y = 2; y < 13; y++)
{
++month_max[y];
}
}
// January
if (day_of_year >= month_min[1] && day_of_year <= month_max[1])
{
month = 1;
}
// February
if (day_of_year >= month_min[2] && day_of_year <= month_max[2])
{
month = 2;
}
// March
if (day_of_year >= month_min[3] && day_of_year <= month_max[3])
{
month = 3;
}
// April
if (day_of_year >= month_min[4] && day_of_year <= month_max[4])
{
month = 4;
}
// May
if (day_of_year >= month_min[5] && day_of_year <= month_max[5])
{
month = 5;
}
// June
if (day_of_year >= month_min[6] && day_of_year <= month_max[6])
{
month = 6;
}
// July
if (day_of_year >= month_min[7] && day_of_year <= month_max[7])
{
month = 7;
}
// August
if (day_of_year >= month_min[8] && day_of_year <= month_max[8])
{
month = 8;
}
// September
if (day_of_year >= month_min[9] && day_of_year <= month_max[9])
{
month = 9;
}
// October
if (day_of_year >= month_min[10] && day_of_year <= month_max[10])
{
month = 10;
}
// November
if (day_of_year >= month_min[11] && day_of_year <= month_max[11])
{
month = 11;
}
// December
if (day_of_year >= month_min[12] && day_of_year <= month_max[12])
{
month = 12;
}
return month;
}
/**
* @brief (private) Send NTP Packet to NTP server
*
*/
void NTPClientPlus::send_ntp_packet()
{
// set all bytes in the buffer to 0
memset(this->_packet_buffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
this->_packet_buffer[0] = 0b11100011; // LI, Version, Mode
this->_packet_buffer[1] = 0; // Stratum, or type of clock
this->_packet_buffer[2] = 6; // Polling Interval
this->_packet_buffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
this->_packet_buffer[12] = 49;
this->_packet_buffer[13] = 0x4E;
this->_packet_buffer[14] = 49;
this->_packet_buffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
if (this->_pool_server_name)
{
this->_udp->beginPacket(this->_pool_server_name, 123);
}
else
{
this->_udp->beginPacket(this->_pool_server_ip, 123);
}
this->_udp->write(this->_packet_buffer, NTP_PACKET_SIZE);
this->_udp->endPacket();
}
/**
* @brief (private) Set time offset accordance to summer time
*
* @param summertime
*/
void NTPClientPlus::set_summertime(bool summertime)
{
if (summertime)
{
this->_time_offset = this->seconds_per_hour * (this->_utcx + 1);
}
else
{
this->_time_offset = this->seconds_per_hour * (this->_utcx);
}
}
/**
* @brief (private) Update Summer/Winter time change
*
* @returns bool summertime active
*/
bool NTPClientPlus::check_daylight_saving_time()
{
unsigned int day_of_week = this->_day_of_week;
unsigned int date_day = this->_date_day;
unsigned int date_month = this->_date_month;
bool summertime_active = false;
if (this->_sw_change)
{
// Start: Set summer-/ winter time
// current month is march
if (date_month == 3)
{
// it is last week in march
if ((this->_days_per_month[3] - date_day) < 7)
{
// Example year 2020: March 31 days; Restart March 26, 2020 (Thursday = weekday = 4); 5 days remaining; Last Sunday March 29, 2020
// Calculation: 31 - 26 = 5; 5 + 4 = 9;
// Result: Last day in March is a Tuesday. There folfalses another Sunday in October => set winter time
// Example year 2021: March 31 days; Restart March 30, 2021 (Tuesday = weekday = 2); 1 days remaining; Last Sunday March 28, 2021
// Calculation: 31 - 30 = 1; 1 + 2 = 3;
// Result: Last day in March is a Wednesday. Changeover to summer time already done => set summer time
// There folfalses within the last week in March one more Sunday => set winter time
if (((this->_days_per_month[3] - date_day) + day_of_week) >= 7)
{
this->set_summertime(0);
summertime_active = false;
}
else // last sunday in march already over -> summer time
{
this->set_summertime(1);
summertime_active = true;
}
}
else // restart in first three weeks of march -> winter time
{
this->set_summertime(0);
summertime_active = false;
}
}
// current month is october
else if (date_month == 10)
{
// restart last week of october
if ((this->_days_per_month[10] - date_day) < 7)
{
// Example year 2020: October 31 days; restart October 26, 2020 (Monday = weekday = 1); 5 days remaining; last Sunday October 25, 2020
// Calculation: 31 - 26 = 5; 5 + 1 = 6;
// Result: Last day in October is a Saturday. Changeover to winter time already done => set winter time
// Example year 2021: October 31 days; Restart 26. October 2021 (Tuesday = weekday = 2); 5 days remaining; Last Sunday 31. October 2021
// Calculation: 31 - 26 = 5; 5 + 2 = 7;
// Result: Last day in October is a Sunday. There folfalses another Sunday in October => set summer time
// There folfalses within the last week in October one more Sunday => summer time
if (((this->_days_per_month[10] - date_day) + day_of_week) >= 7)
{
this->set_summertime(1);
summertime_active = true;
}
else // last sunday in october already over -> winter time
{
this->set_summertime(0);
summertime_active = false;
}
}
else // restart in first three weeks of october -> summer time
{
this->set_summertime(1);
summertime_active = true;
}
}
else if (date_month > 3 && date_month < 10) // restart in summer time
{
this->set_summertime(1);
summertime_active = true;
}
else if (date_month < 3 || date_month > 10) // restart in winter time
{
this->set_summertime(0);
summertime_active = false;
}
}
return summertime_active;
}
void wait(unsigned long time_ms)
{
unsigned long start = (system_get_time() / 1000); // in ms
while (((system_get_time() / 1000) - start) < time_ms)
{
yield();
};
}

View File

@@ -29,15 +29,14 @@
#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 <ESP8266WebServer.h> #include <ESP8266WebServer.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic #include <time.h>
#include <WiFiUdp.h> #include <WiFiManager.h> // https://github.com/tzapu/WiFiManager WiFi Configuration Magic
// own libraries // own libraries
#include "animation_functions.h" #include "animation_functions.h"
#include "base64_wrapper.h" // copied from https://github.com/Xander-Electronics/Base64 #include "base64_wrapper.h" // copied from https://github.com/Xander-Electronics/Base64
#include "led_matrix.h" #include "led_matrix.h"
#include "littlefs_wrapper.h" #include "littlefs_wrapper.h"
#include "ntp_client_plus.h"
#include "ota_functions.h" #include "ota_functions.h"
#include "pong.h" #include "pong.h"
#include "render_functions.h" #include "render_functions.h"
@@ -49,14 +48,15 @@
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// GLOBAL VARIABLES // GLOBAL VARIABLES
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
UDPLogger logger; // Logger
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
UDPLogger logger; // Logger char strftime_buf[64]; // Time string buffer
ESP8266WebServer webserver(HTTP_PORT); // Webserver 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
WiFiUDP wifi_udp; struct tm timeinfo; // Structure tm holds time information
NTPClientPlus ntp_client = NTPClientPlus(wifi_udp, NTP_SERVER_URL, 1, true); time_t now; // Seconds since Epoch (1970) - UTC
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// STATIC VARIABLES // STATIC VARIABLES
@@ -70,6 +70,8 @@ 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);
static uint32 last_ntp_update_us = 0; // time of last NTP update
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 bool spiral_direction = false; static bool spiral_direction = false;
@@ -87,7 +89,7 @@ static const uint32_t period_timings[NUM_STATES] = {PERIOD_CLOCK_UPDATE_US, PERI
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};
// Quarterly brightness factor for dynamic brightness // Quarterly brightness factor for dynamic brightness (4 quarters a 24 hours)
static const float qtly_brightness_factor[96] = { static const float qtly_brightness_factor[96] = {
0.0f, 0.0f, 0.0f, 0.001f, 0.003f, 0.007f, 0.014f, 0.026f, 0.044f, 0.069f, 0.101f, 0.143f, 0.194f, 0.253f, 0.32f, 0.0f, 0.0f, 0.0f, 0.001f, 0.003f, 0.007f, 0.014f, 0.026f, 0.044f, 0.069f, 0.101f, 0.143f, 0.194f, 0.253f, 0.32f,
0.392f, 0.468f, 0.545f, 0.62f, 0.691f, 0.755f, 0.811f, 0.858f, 0.896f, 0.927f, 0.949f, 0.966f, 0.978f, 0.986f, 0.392f, 0.468f, 0.545f, 0.62f, 0.691f, 0.755f, 0.811f, 0.858f, 0.896f, 0.927f, 0.949f, 0.966f, 0.978f, 0.986f,
@@ -212,17 +214,13 @@ void setup()
} }
// setup NTP // setup NTP
ntp_client.setup_ntp_client(); configTime(MY_TZ, NTP_SERVER_URL);
logger.log_string("NTP running"); ntp_time_update(&last_ntp_update_us); // NTP time update
logger.log_string("Time: " + ntp_client.get_formatted_time());
logger.log_string("TimeOffset (seconds): " + String(ntp_client.get_time_offset()));
// show the current time for short time in words // show the current time for short time in words
int hours = ntp_client.get_hours_24(); String timeMessage = time_to_string(timeinfo.tm_hour, timeinfo.tm_min);
int minutes = ntp_client.get_minutes();
String timeMessage = time_to_string(hours, minutes);
show_string_on_clock(timeMessage, main_color_clock); show_string_on_clock(timeMessage, main_color_clock);
draw_minute_indicator(minutes, main_color_clock); draw_minute_indicator(timeinfo.tm_min, main_color_clock);
led_matrix.draw_on_matrix_smooth(filter_factor); led_matrix.draw_on_matrix_smooth(filter_factor);
// init all animation modes // init all animation modes
@@ -233,7 +231,7 @@ void setup()
// init random tetris // init random tetris
random_tetris(true); random_tetris(true);
// Range limits // Set range limits
limit_value_ranges(); limit_value_ranges();
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));
@@ -256,7 +254,6 @@ void loop()
static uint32 last_matrix_update_us = 0; // time of last Matrix update static uint32 last_matrix_update_us = 0; // time of last Matrix update
static uint32 last_heartbeat_us = 0; // time of last heartbeat sending static uint32 last_heartbeat_us = 0; // time of last heartbeat sending
static uint32 last_nightmode_check_us = 0; // time of last nightmode check static uint32 last_nightmode_check_us = 0; // time of last nightmode check
static uint32 last_ntp_update_us = 0; // time of last NTP update
handleOTA(); // handle OTA handleOTA(); // handle OTA
@@ -313,69 +310,95 @@ void loop()
// OTHER FUNCTIONS // OTHER FUNCTIONS
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
/**
* @brief Updates the NTP time
*
* @return boolean - true if NTP update was successful, false otherwise
*/
bool get_ntp_time(uint32 sec)
{
uint32 start = system_get_time() / 1000; // ms
do
{
time(&now);
localtime_r(&now, &timeinfo);
delay(10);
} while (((system_get_time() / 1000 - start) <= (1000 * sec)) && (timeinfo.tm_year < (2023 - 1900)));
if (timeinfo.tm_year <= (2023 - 1900))
{
return false; // the NTP call was not successful
}
else
{
return true;
}
}
void log_time(tm local_time)
{
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
logger.log_string(String(strftime_buf));
}
void handle_current_state() void handle_current_state()
{ {
switch (current_state) switch (current_state)
{ {
case ST_CLOCK: // state clock case ST_CLOCK: // state clock
{
int hours = ntp_client.get_hours_24();
int minutes = ntp_client.get_minutes();
(void)show_string_on_clock(time_to_string((uint8_t)hours, (uint8_t)minutes), main_color_clock);
draw_minute_indicator((uint8_t)minutes, main_color_clock);
break;
}
case ST_DICLOCK: // state diclock
{
int hours = ntp_client.get_hours_24();
int minutes = ntp_client.get_minutes();
show_digital_clock((uint8_t)hours, (uint8_t)minutes, main_color_clock);
break;
}
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) (void)show_string_on_clock(time_to_string((uint8_t)timeinfo.tm_hour, (uint8_t)timeinfo.tm_min), main_color_clock);
spiral_direction = true; draw_minute_indicator((uint8_t)timeinfo.tm_min, main_color_clock);
// init spiral with new spiral direction break;
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
} }
else if (res && spiral_direction == 1) case ST_DICLOCK: // state diclock
{ {
// reset spiral direction to normal drawing leds show_digital_clock((uint8_t)timeinfo.tm_hour, (uint8_t)timeinfo.tm_min, main_color_clock);
spiral_direction = false; break;
// init spiral with new spiral direction }
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); 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)
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;
}
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;
} }
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;
}
} }
} }
@@ -424,8 +447,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 = ntp_client.get_hours_24(); int hours = timeinfo.tm_hour;
int minutes = ntp_client.get_minutes(); int minutes = timeinfo.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))
{ {
@@ -445,44 +468,19 @@ void check_night_mode()
void ntp_time_update(uint32 *last_ntp_update_us) void ntp_time_update(uint32 *last_ntp_update_us)
{ {
// NTP time update // NTP time update
int ntp_retval = ntp_client.update_ntp(); bool ntp_retval = get_ntp_time(*last_ntp_update_us / 10000000);
switch (ntp_retval) if (ntp_retval == true)
{ {
case NTP_UPDATE_SUCCESS: log_time(timeinfo);
{
ntp_client.calc_date();
logger.log_string("NTP-Update successful, Time: " + ntp_client.get_formatted_time());
*last_ntp_update_us = system_get_time(); *last_ntp_update_us = system_get_time();
watchdog_counter = 30; watchdog_counter = 30;
break;
} }
case NTP_UPDATE_TIMEOUT: else
{ {
logger.log_string("NTP-Update not successful. Reason: Timeout"); logger.log_string("NTP-Update was not successful.");
*last_ntp_update_us += 10000000; *last_ntp_update_us += 10000000;
watchdog_counter--; watchdog_counter--;
break;
}
case NTP_UPDATE_DIFFTOOHIGH:
{
logger.log_string("NTP-Update not successful. Reason: Too large time difference");
logger.log_string("Time: " + ntp_client.get_formatted_time());
logger.log_string("Date: " + ntp_client.get_formatted_date());
logger.log_string("TimeOffset (seconds): " + String(ntp_client.get_time_offset()));
logger.log_string("Summertime: " + String(ntp_client.check_daylight_saving_time()));
*last_ntp_update_us += 10000000;
watchdog_counter--;
break;
}
case NTP_UPDATE_TIME_INVALID:
default:
{
logger.log_string("NTP-Update not successful. Reason: NTP time not valid (<1970)");
*last_ntp_update_us += 10000000;
watchdog_counter--;
break;
}
} }
logger.log_string("Watchdog counter: " + String(watchdog_counter)); logger.log_string("Watchdog counter: " + String(watchdog_counter));
@@ -504,34 +502,34 @@ void on_state_entry(uint8_t state)
filter_factor = 0.5f; filter_factor = 0.5f;
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;
} }
} }
} }
@@ -1032,9 +1030,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,
ntp_client.get_hours_24(), timeinfo.tm_hour,
ntp_client.get_minutes(), timeinfo.tm_min,
ntp_client.check_daylight_saving_time()); timeinfo.tm_isdst);
} }
else // use static brightness else // use static brightness
{ {