diff --git a/openBeken_win32_mvsc2017.vcxproj b/openBeken_win32_mvsc2017.vcxproj index 4ba0f678f..27c5a2169 100644 --- a/openBeken_win32_mvsc2017.vcxproj +++ b/openBeken_win32_mvsc2017.vcxproj @@ -176,7 +176,8 @@ - + + @@ -1170,4 +1171,4 @@ - \ No newline at end of file + diff --git a/openBeken_win32_mvsc2017.vcxproj.filters b/openBeken_win32_mvsc2017.vcxproj.filters index fba5ff623..2da1b8129 100644 --- a/openBeken_win32_mvsc2017.vcxproj.filters +++ b/openBeken_win32_mvsc2017.vcxproj.filters @@ -61,7 +61,7 @@ - + @@ -532,4 +532,4 @@ - \ No newline at end of file + diff --git a/platforms/LN882H/CMakeLists.txt b/platforms/LN882H/CMakeLists.txt index 3b27e3dc5..bbc124ccc 100644 --- a/platforms/LN882H/CMakeLists.txt +++ b/platforms/LN882H/CMakeLists.txt @@ -89,6 +89,9 @@ set(PROJ_ALL_SRC app/src/driver/drv_wemo.c app/src/driver/drv_ds1820_simple.c app/src/driver/drv_charts.c + app/src/driver/drv_deviceclock.c + app/src/driver/drv_timed_events.c + app/src/httpclient/http_get_header.c app/src/hal/ln882h/hal_adc_ln882h.c app/src/hal/ln882h/hal_flashConfig_ln882h.c app/src/hal/ln882h/hal_flashVars_ln882h.c diff --git a/platforms/W600/Makefile b/platforms/W600/Makefile index 8220e3e90..e2c9c81b9 100644 --- a/platforms/W600/Makefile +++ b/platforms/W600/Makefile @@ -51,6 +51,8 @@ CSRCS += $(_SHARED_APP)/driver/drv_tasmotaDeviceGroups.c CSRCS += $(_SHARED_APP)/driver/drv_test_drivers.c CSRCS += $(_SHARED_APP)/driver/drv_bridge_driver.c CSRCS += $(_SHARED_APP)/driver/drv_charts.c +CSRCS += $(_SHARED_APP)/driver/drv_deviceclock.c +CSRCS += $(_SHARED_APP)/driver/drv_timed_events.c CSRCS += $(_SHARED_APP)/hal/w800/hal_adc_w800.c CSRCS += $(_SHARED_APP)/hal/w800/hal_flashConfig_w800.c CSRCS += $(_SHARED_APP)/hal/w800/hal_flashVars_w800.c diff --git a/platforms/W800/Makefile b/platforms/W800/Makefile index cb22a29b4..8534768f9 100644 --- a/platforms/W800/Makefile +++ b/platforms/W800/Makefile @@ -54,6 +54,9 @@ CSRCS += $(_SHARED_APP)/tiny_crc8.c CSRCS += $(_SHARED_APP)/driver/drv_main.c CSRCS += $(_SHARED_APP)/driver/drv_ds1820_simple.c CSRCS += $(_SHARED_APP)/driver/drv_charts.c +CSRCS += $(_SHARED_APP)/driver/drv_deviceclock.c +CSRCS += $(_SHARED_APP)/driver/drv_timed_events.c +CSRCS += $(_SHARED_APP)/httpclient/http_get_header.c CSRCS += $(_SHARED_APP)/user_main.c CSRCS += main.c diff --git a/platforms/XR809/Makefile b/platforms/XR809/Makefile index 2d9e3b842..0069c6c29 100644 --- a/platforms/XR809/Makefile +++ b/platforms/XR809/Makefile @@ -39,6 +39,8 @@ SRCS += ../shared/src/driver/drv_main SRCS += ../shared/src/driver/drv_ntp SRCS += ../shared/src/driver/drv_tuyaMCU SRCS += ../shared/src/driver/drv_uart +SRCS += ../shared/src/driver/drv_deviceclock +SRCS += ../shared/src/driver/drv_timed_events SRCS += ../shared/src/i2c/drv_i2c_main SRCS += ../shared/src/i2c/drv_i2c_mcp23017 diff --git a/src/cmnds/cmd_if.c b/src/cmnds/cmd_if.c index 606db1c22..f31f5747e 100644 --- a/src/cmnds/cmd_if.c +++ b/src/cmnds/cmd_if.c @@ -7,6 +7,7 @@ #include "../driver/drv_public.h" #include "../driver/drv_battery.h" #include "../driver/drv_ntp.h" +#include "../driver/drv_deviceclock.h" #include // isspace /* @@ -273,34 +274,34 @@ float getUpTime(const char *s) { return g_secondsElapsed; } float getWeekDay(const char *s) { - return NTP_GetWeekDay(); + return CLOCK_GetWeekDay(); } float getMinute(const char *s) { - return NTP_GetMinute(); + return CLOCK_GetMinute(); } float getHour(const char *s) { - return NTP_GetHour(); + return CLOCK_GetHour(); } float getSecond(const char *s) { - return NTP_GetSecond(); + return CLOCK_GetSecond(); } float getYear(const char *s) { - return NTP_GetYear(); + return CLOCK_GetYear(); } float getMonth(const char *s) { - return NTP_GetMonth(); + return CLOCK_GetMonth(); } float getMDay(const char *s) { - return NTP_GetMDay(); + return CLOCK_GetMDay(); } -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET float getSunrise(const char *s) { - return NTP_GetSunrise(); + return CLOCK_GetSunrise(); } float getSunset(const char *s) { - return NTP_GetSunset(); + return CLOCK_GetSunset(); } #endif @@ -465,7 +466,7 @@ const constant_t g_constants[] = { ////cnstdetail:"descr":"", ////cnstdetail:"requires":""} { "$today", &getToday }, -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET ////cnstdetail:{"name":"$sunrise", ////cnstdetail:"title":"$sunrise", ////cnstdetail:"descr":"Next sunrise as a TimerSeconds from midnight", diff --git a/src/cmnds/cmd_main.c b/src/cmnds/cmd_main.c index 0e7879d3b..670739694 100644 --- a/src/cmnds/cmd_main.c +++ b/src/cmnds/cmd_main.c @@ -806,6 +806,9 @@ void CMD_Init_Delayed() { #if defined(PLATFORM_BEKEN) || defined(WINDOWS) || defined(PLATFORM_BL602) UART_AddCommands(); #endif +#if ENABLE_LOCAL_CLOCK || WINDOWS + CLOCK_Init(); +#endif } diff --git a/src/driver/drv_bl_shared.c b/src/driver/drv_bl_shared.c index da54219c7..6f422b752 100644 --- a/src/driver/drv_bl_shared.c +++ b/src/driver/drv_bl_shared.c @@ -9,6 +9,7 @@ #include "../ota/ota.h" #include "drv_local.h" #include "drv_ntp.h" +#include "../driver/drv_deviceclock.h" #include "drv_public.h" #include "drv_uart.h" #include "../cmnds/cmd_public.h" //for enum EventCode @@ -105,7 +106,7 @@ void BL09XX_AppendInformationToHTTPIndexPage(http_request_t *request) } for (int i = OBK__FIRST; i <= OBK_CONSUMPTION__DAILY_LAST; i++) { - if (i <= OBK__NUM_MEASUREMENTS || NTP_IsTimeSynced()) { + if (i <= OBK__NUM_MEASUREMENTS || Clock_IsTimeSynced()) { poststr(request, ""); poststr(request, sensors[i].names.name_friendly); poststr(request, ""); @@ -131,10 +132,8 @@ void BL09XX_AppendInformationToHTTPIndexPage(http_request_t *request) } hprintf255(request, "
"); - if(DRV_IsRunning("NTP")==false) { - hprintf255(request,"NTP driver is not started, daily energy stats disabled."); - } else if (!NTP_IsTimeSynced()) { - hprintf255(request,"Daily energy stats awaiting NTP driver to sync real time..."); + if(Clock_IsTimeSynced()==false) { + hprintf255(request,"Device time not in sync, daily energy stats disabled."); } hprintf255(request, ""); @@ -223,7 +222,7 @@ commandResult_t BL09XX_ResetEnergyCounter(const void *context, const char *cmd, sensors[OBK_CONSUMPTION_TOTAL].lastReading = value; energyCounterStamp = xTaskGetTickCount(); } - ConsumptionResetTime = (time_t)NTP_GetCurrentTime(); + ConsumptionResetTime = (time_t)Clock_GetCurrentTime(); #if WINDOWS #elif PLATFORM_BL602 #elif PLATFORM_W600 || PLATFORM_W800 @@ -524,8 +523,8 @@ void BL_ProcessUpdate(float voltage, float current, float power, HAL_FlashVars_SaveTotalConsumption(sensors[OBK_CONSUMPTION_TOTAL].lastReading); sensors[OBK_CONSUMPTION_TODAY].lastReading += energy; - if (NTP_IsTimeSynced()) { - ntpTime = (time_t)NTP_GetCurrentTime(); + if (Clock_IsTimeSynced()) { + ntpTime = (time_t)Clock_GetCurrentTime(); ltm = gmtime(&ntpTime); if (ConsumptionResetTime == 0) ConsumptionResetTime = (time_t)ntpTime; @@ -581,20 +580,20 @@ void BL_ProcessUpdate(float voltage, float current, float power, cJSON_AddNumberToObject(root, "consumption_stat_index", energyCounterMinutesIndex); cJSON_AddNumberToObject(root, "consumption_sample_count", energyCounterSampleCount); cJSON_AddNumberToObject(root, "consumption_sampling_period", energyCounterSampleInterval); - if(NTP_IsTimeSynced() == true) + if(Clock_IsTimeSynced() == true) { cJSON_AddNumberToObject(root, "consumption_today", BL_ChangeEnergyUnitIfNeeded(DRV_GetReading(OBK_CONSUMPTION_TODAY))); cJSON_AddNumberToObject(root, "consumption_yesterday", BL_ChangeEnergyUnitIfNeeded(DRV_GetReading(OBK_CONSUMPTION_YESTERDAY))); ltm = gmtime(&ConsumptionResetTime); - if (NTP_GetTimesZoneOfsSeconds()>0) + if (Clock_GetTimesZoneOfsSeconds()>0) { snprintf(datetime,sizeof(datetime), "%04i-%02i-%02iT%02i:%02i+%02i:%02i", ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, - NTP_GetTimesZoneOfsSeconds()/3600, (NTP_GetTimesZoneOfsSeconds()/60) % 60); + Clock_GetTimesZoneOfsSeconds()/3600, (Clock_GetTimesZoneOfsSeconds()/60) % 60); } else { snprintf(datetime, sizeof(datetime), "%04i-%02i-%02iT%02i:%02i-%02i:%02i", ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, - abs(NTP_GetTimesZoneOfsSeconds()/3600), (abs(NTP_GetTimesZoneOfsSeconds())/60) % 60); + abs(Clock_GetTimesZoneOfsSeconds()/3600), (abs(Clock_GetTimesZoneOfsSeconds())/60) % 60); } cJSON_AddStringToObject(root, "consumption_clear_date", datetime); } @@ -613,7 +612,7 @@ void BL_ProcessUpdate(float voltage, float current, float power, cJSON_AddItemToObject(root, "consumption_samples", stats); } - if(NTP_IsTimeSynced() == true) + if(Clock_IsTimeSynced() == true) { stats = cJSON_CreateArray(); for(i = OBK_CONSUMPTION__DAILY_FIRST; i <= OBK_CONSUMPTION__DAILY_LAST; i++) @@ -696,16 +695,10 @@ void BL_ProcessUpdate(float voltage, float current, float power, sensors[i].lastReading = ConsumptionResetTime; //Only to make the 'nochangeframe' mechanism work here ltm = gmtime(&ConsumptionResetTime); /* 2019-09-07T15:50-04:00 */ - if (NTP_GetTimesZoneOfsSeconds()>0) - { - snprintf(datetime, sizeof(datetime), "%04i-%02i-%02iT%02i:%02i+%02i:%02i", - ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, - NTP_GetTimesZoneOfsSeconds()/3600, (NTP_GetTimesZoneOfsSeconds()/60) % 60); - } else { - snprintf(datetime, sizeof(datetime), "%04i-%02i-%02iT%02i:%02i-%02i:%02i", - ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, - abs(NTP_GetTimesZoneOfsSeconds()/3600), (abs(NTP_GetTimesZoneOfsSeconds())/60) % 60); - } + snprintf(datetime, sizeof(datetime), "%04i-%02i-%02iT%02i:%02i%s%02i:%02i", + ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, + Clock_GetTimesZoneOfsSeconds()>0 ? "+" : "-", + abs(Clock_GetTimesZoneOfsSeconds()/3600), (abs(Clock_GetTimesZoneOfsSeconds())/60) % 60); MQTT_PublishMain_StringString(sensors[i].names.name_mqtt, datetime, 0); } else { //all other sensors float val = sensors[i].lastReading; diff --git a/src/driver/drv_deviceclock.c b/src/driver/drv_deviceclock.c new file mode 100644 index 000000000..73c3ab008 --- /dev/null +++ b/src/driver/drv_deviceclock.c @@ -0,0 +1,674 @@ + +#include + +#include "../new_common.h" +#include "../new_cfg.h" +// Commands register, execution API and cmd tokenizer +#include "../cmnds/cmd_public.h" +#include "../httpserver/new_http.h" +#include "../logging/logging.h" +#include "../ota/ota.h" + +#include "drv_deviceclock.h" +// functions for handling device time even without NTP driver present +// using "g_epochOnStartup" and "g_UTCoffset" if NTP not present (or not synced) + +#include "drv_ntp.h" + +// "eoch" on startup of device; If we add g_secondsElapsed we get the actual time +#if ENABLE_LOCAL_CLOCK +uint32_t g_epochOnStartup = 0; +// UTC offset +int g_UTCoffset = 0; +#endif + +#if ENABLE_LOCAL_CLOCK_ADVANCED +// daylight saving time offset +int g_DSToffset = 0; +// epoch of next change in dst +uint32_t g_next_dst_change=0; +#endif + +extern void CLOCK_Init_Events(void); +extern void CLOCK_RunEvents(unsigned int newTime, bool bTimeValid); + +#if ENABLE_CLOCK_SUNRISE_SUNSET +extern void CLOCK_CalculateSunrise(byte *outHour, byte *outMinute); +extern void CLOCK_CalculateSunset(byte *outHour, byte *outMinute); +#endif + +// leave it for the moment, until a CLOCK logging is possible ... +#define LOG_FEATURE LOG_FEATURE_NTP + +#if ENABLE_CLOCK_SUNRISE_SUNSET + +/* sunrise/sunset defaults */ +#define CFG_DEFAULT_LATITUDE 43.994131 +#define CFG_DEFAULT_LONGITUDE -123.095854 +#define SUN_DATA_COORD_MULT 1000000 + +struct SUN_DATA sun_data = + { + .latitude = (int) (CFG_DEFAULT_LATITUDE * SUN_DATA_COORD_MULT), + .longitude = (int) (CFG_DEFAULT_LONGITUDE * SUN_DATA_COORD_MULT), + }; + +//Set Latitude and Longitude for sunrise/sunset calc +commandResult_t CLOCK_SetLatlong(const void *context, const char *cmd, const char *args, int cmdFlags) { + const char *newValue; + + Tokenizer_TokenizeString(args,0); + // following check must be done after 'Tokenizer_TokenizeString', + // so we know arguments count in Tokenizer. 'cmd' argument is + // only for warning display + if (Tokenizer_CheckArgsCountAndPrintWarning(cmd, 2)) { + return CMD_RES_NOT_ENOUGH_ARGUMENTS; + } + newValue = Tokenizer_GetArg(0); + sun_data.latitude = (int) (atof(newValue) * SUN_DATA_COORD_MULT); + addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "CLOCK latitude set to %s", newValue); + + newValue = Tokenizer_GetArg(1); + sun_data.longitude = (int) (atof(newValue) * SUN_DATA_COORD_MULT); + addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "CLOCK longitude set to %s", newValue); + return CMD_RES_OK; +} + +int CLOCK_GetSunrise() +{ + byte hour, minute; + int sunriseInSecondsFromMidnight; + + CLOCK_CalculateSunrise(&hour, &minute); + sunriseInSecondsFromMidnight = ((int)hour * 3600) + ((int)minute * 60); + return sunriseInSecondsFromMidnight; +} + +int CLOCK_GetSunset() +{ + byte hour, minute; + int sunsetInSecondsFromMidnight; + + CLOCK_CalculateSunset(&hour, &minute); + sunsetInSecondsFromMidnight = ((int)hour * 3600) + ((int)minute * 60); + return sunsetInSecondsFromMidnight; +} +#endif + +int CLOCK_GetWeekDay() { + struct tm *ltm; + time_t act_deviceTime = (time_t)Clock_GetCurrentTime(); + + // NOTE: on windows, you need _USE_32BIT_TIME_T + ltm = gmtime(&act_deviceTime); + + if (ltm == 0) { + return 0; + } + + return ltm->tm_wday; +} +int CLOCK_GetHour() { + struct tm *ltm; + time_t act_deviceTime = (time_t)Clock_GetCurrentTime(); + + // NOTE: on windows, you need _USE_32BIT_TIME_T + ltm = gmtime(&act_deviceTime); + + if (ltm == 0) { + return 0; + } + + return ltm->tm_hour; +} +int CLOCK_GetMinute() { + struct tm *ltm; + time_t act_deviceTime = (time_t)Clock_GetCurrentTime(); + + // NOTE: on windows, you need _USE_32BIT_TIME_T + ltm = gmtime(&act_deviceTime); + + if (ltm == 0) { + return 0; + } + + return ltm->tm_min; +} +int CLOCK_GetSecond() { + struct tm *ltm; + time_t act_deviceTime = (time_t)Clock_GetCurrentTime(); + + // NOTE: on windows, you need _USE_32BIT_TIME_T + ltm = gmtime(&act_deviceTime); + + if (ltm == 0) { + return 0; + } + + return ltm->tm_sec; +} +int CLOCK_GetMDay() { + struct tm *ltm; + time_t act_deviceTime = (time_t)Clock_GetCurrentTime(); + + // NOTE: on windows, you need _USE_32BIT_TIME_T + ltm = gmtime(&act_deviceTime); + + if (ltm == 0) { + return 0; + } + + return ltm->tm_mday; +} +int CLOCK_GetMonth() { + struct tm *ltm; + time_t act_deviceTime = (time_t)Clock_GetCurrentTime(); + + // NOTE: on windows, you need _USE_32BIT_TIME_T + ltm = gmtime(&act_deviceTime); + + if (ltm == 0) { + return 0; + } + + return ltm->tm_mon+1; +} +int CLOCK_GetYear() { + struct tm *ltm; + time_t act_deviceTime = (time_t)Clock_GetCurrentTime(); + + // NOTE: on windows, you need _USE_32BIT_TIME_T + ltm = gmtime(&act_deviceTime); + + if (ltm == 0) { + return 0; + } + + return ltm->tm_year+1900; +} + + +#if ENABLE_LOCAL_CLOCK +void CLOCK_setDeviceTime(uint32_t time) +{ +// done in CMD_Init_Delayed() in cmd_main.c +// if (g_epochOnStartup < 10) CLOCK_Init(); + g_epochOnStartup = time - g_secondsElapsed; +} + +void CLOCK_setDeviceTimeOffset(int offs) +{ + g_UTCoffset = offs; +} +#endif + + +void CLOCK_Init() { + +#if ENABLE_CLOCK_SUNRISE_SUNSET + //cmddetail:{"name":"clock_setLatLong","args":"[Latlong]", + //cmddetail:"descr":"Sets the devices latitude and longitude", + //cmddetail:"fn":"CLOCK_SetLatlong","file":"driver/drv_ntp.c","requires":"", + //cmddetail:"examples":"CLOCK_SetLatlong -34.911498 138.809488"} + CMD_RegisterCommand("clock_setLatLong", CLOCK_SetLatlong, NULL); +// and register an alias for backward compatibility + //cmddetail:{"name":"ntp_setLatLong","args":"[Latlong]", + //cmddetail:"descr":"obsolete! only for backward compatibility - please use 'clock_setLatLong' in the future", + //cmddetail:"fn":"CLOCK_SetLatlong","file":"driver/drv_ntp.c","requires":"", + //cmddetail:"examples":"ntp_SetLatlong -34.911498 138.809488"} + CMD_RegisterCommand("ntp_setLatLong", CLOCK_SetLatlong, NULL); +#endif +#if ENABLE_CALENDAR_EVENTS + CLOCK_Init_Events(); +#endif + + + addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "CLOCK driver initialized."); +} + +void CLOCK_OnEverySecond() +{ + +#if ENABLE_CALENDAR_EVENTS + CLOCK_RunEvents(Clock_GetCurrentTime(), Clock_IsTimeSynced()); +#endif +} + + + + + +uint32_t Clock_GetCurrentTime(){ // might replace for NTP_GetCurrentTime() to return time regardless of NTP present/running +#if ENABLE_NTP + if (NTP_IsTimeSynced() == true) { + return (uint32_t)NTP_GetCurrentTime(); + } + // even if NTP is enabled, but NTP is not synced, we might have g_epochOnStartup set, so go on +#endif + // if g_epochOnStartup is set, return it - we migth (mis-)use a very small value as status, + // so check for > 10. A hack, but as we will not go back in time ... +#if ENABLE_LOCAL_CLOCK_ADVANCED + return g_epochOnStartup > 10 ? g_epochOnStartup + g_secondsElapsed + g_UTCoffset + g_DSToffset : 0; +#elif ENABLE_LOCAL_CLOCK + return g_epochOnStartup > 10 ? g_epochOnStartup + g_secondsElapsed + g_UTCoffset : 0; +#else + return 0; +#endif +}; + +uint32_t Clock_GetCurrentTimeWithoutOffset(){ // ... same forNTP_GetCurrentTimeWithoutOffset()... +#if ENABLE_NTP + if (NTP_IsTimeSynced() == true) { + return (uint32_t)NTP_GetCurrentTimeWithoutOffset(); + } + // even if NTP is enabled, but NTP is not synced, we might have g_epochOnStartup set, so go on +#endif +#if ENABLE_LOCAL_CLOCK + // if g_epochOnStartup is set, return time - we migth (mis-)use a very small value as status, + // so check for > 10. A hack, but as we will not go back in time ... + return g_epochOnStartup > 10 ? g_epochOnStartup + g_secondsElapsed : 0; +#else + return 0; +#endif +}; + + +// +// ################################################################### START ######################################################################## +// ##################################################### temp. function to get local time, ############################################################ +// ##################################################### even if NTP is running and synced ############################################################ +// +uint32_t Clock_GetDeviceTime(){ // might replace for NTP_GetCurrentTime() to return time regardless of NTP present/running +#if ENABLE_LOCAL_CLOCK_ADVANCED + return g_epochOnStartup > 10 ? g_epochOnStartup + g_secondsElapsed + g_UTCoffset + g_DSToffset : 0; +#elif ENABLE_LOCAL_CLOCK + return g_epochOnStartup > 10 ? g_epochOnStartup + g_secondsElapsed + g_UTCoffset : 0; +#else + return 0; +#endif +}; + +uint32_t Clock_GetDeviceTimeWithoutOffset(){ // ... same forNTP_GetCurrentTimeWithoutOffset()... +#if ENABLE_LOCAL_CLOCK + // if g_epochOnStartup is set, return time - we migth (mis-)use a very small value as status, + // so check for > 10. A hack, but as we will not go back in time ... + return g_epochOnStartup > 10 ? g_epochOnStartup + g_secondsElapsed : 0; +#else + return 0; +#endif +}; + +// +// ################################################################### END ######################################################################## +// ##################################################### temp. function to get local time, ############################################################ +// ##################################################### even if NTP is running and synced ############################################################ +// + + +bool Clock_IsTimeSynced(){ // ... and for NTP_IsTimeSynced() +#if ENABLE_NTP + if (NTP_IsTimeSynced() == true) { + return true; + } + // even if NTP is enabled, but NTP is not synced, we might have g_epochOnStartup set, so go on +#endif + // if g_epochOnStartup is set, return time - we migth (mis-)use a very small value as status, + // so check for > 10. A hack, but as we will not go back in time ... +#if ENABLE_LOCAL_CLOCK + return g_epochOnStartup > 10 ? true : false; +#else + return false; +#endif + +} + + +int Clock_GetTimesZoneOfsSeconds() // ... and for NTP_GetTimesZoneOfsSeconds() +{ +#if ENABLE_NTP + if (NTP_IsTimeSynced() == true) { + return NTP_GetTimesZoneOfsSeconds(); + } + // even if NTP is enabled, but NTP is not synced, we might have UTC offset set with g_epochOnStartup set, so go on +#endif + // ...and again: check if g_epochOnStartup is set ... +#if ENABLE_LOCAL_CLOCK_ADVANCED + return g_epochOnStartup > 10 ? g_UTCoffset + g_DSToffset: 0; +#elif ENABLE_LOCAL_CLOCK + return g_epochOnStartup > 10 ? g_UTCoffset : 0; +#else + return 0; +#endif + +} + + +#if ENABLE_LOCAL_CLOCK_ADVANCED +// handle daylight saving time and so on + +union DST g_clocksettings; + +const uint32_t SECS_PER_DAY = 3600UL * 24UL; + +// function derived from tasmotas "RuleToTime" +uint32_t RuleToTime(uint8_t dow, uint8_t mo, uint8_t week, uint8_t hr, int yr) { + struct tm tm={0}; + uint32_t t; + uint8_t m=mo-1; // we use struct tm here, so month values 0..11 + uint8_t w=week; // temp copies of mo(nth) and week + + if (0 == w) { // Last week = 0 + if (++m > 11) { // for "Last", go to the next month ... + m = 0; // .. and to next year, if we need last XY in December + yr++; + } + w = 1; // search first week of next month, we will subtract 7 days later + } + tm.tm_hour = hr; +// tm is set to {0}, so we shouldn't need to set values equal to "0" +// tm.tm_min = 0; +// tm.tm_sec = 0; + tm.tm_mday = 1; // first day of (next) month + tm.tm_mon = m ; + tm.tm_year = yr - 1900; +// t = (uint32_t)timegm(&tm); // First day of the month, or first day of next month for "Last" rules + t = (uint32_t)mktime(&tm); // First day of the month, or first day of next month for "Last" rules +// since we use time functions, weekdays are 0 to 6 but tasmote settings use 1 to 7 (e.g. Sunday = 1) +// so we have to subtract 1 from dow to match tm_weekday here + t += (7 * (w - 1) + (dow - 1 - tm.tm_wday + 7) % 7) * SECS_PER_DAY; + if (0 == week) { + t -= 7 * SECS_PER_DAY; // back one week if this is a "Last" rule + } + return t; +} + +void printtime(uint32_t time, char* string){ + time_t tmpt=(time_t)time; + struct tm * tm=gmtime(&tmpt); + // e.g. "Sun, 2024-10-27 01:00:00 UTC" + strftime(string,30,"%a, %F %T UTC",tm); +} + +// helper, for printf won't print uint64_t (%llu) correctly +void dump_u64(uint64_t x,char *ret) +{ + int i=0; + uint64_t n=x; + while (n != 0) + { + i++; + n /= 10; + } + ret[i] = '\0'; + do + { + ret[--i] = '0' + (x % 10); + x /= 10; + } while (x && i>=1); +} + + +// return 1 if checked time is during DST period +// set g_next_dst_change to next DST event +int testNsetDST(uint32_t val) +{ + // DST calculation can be split in three phases during a year: + // 1. at the beginning the time before DST starts/ends + // 2. after first DST change - but before second (during DST in northern or during regular time in southern hemisphere) + // 3. after second DST change (after DST in northern or during DST in southern hemisphere) + // For this three cases, we known + // 1. next switch will take place at first DST change of this year - no DST for nothern hemisphere, in DST for southern + // 2. we are between the two DST changes, next switch will take place at second DST change of this year - DST in N, regular time in S + // 3. after second DST switch, next switch will take place at first DST change of next year - regular time in N, DST in S + // so we will need mimimum the first DST change of this year, we calculate both and check for minimum. + // This check is needed, for "H" setting will not work in every case: + // e.g. in Ireland (northern / H=0), which uses "DST" in winter for a "negative" DST, so DST "starts" in fall and not in spring as expected for N + // + // we can be gratious for calculating the actual year by dividing epoch + // if we are off a day (leap year), it won't matter, as long as around the switch of the year there is no DST event... + // so we use 1 Year = 365.24 days = 31556926 Seconds + // and can be sure to have the correct year for a date if it's > Jan 1st and < Dec 31st which is true for all known DST dates + + + // so lets try to find the year corresponding to val + int year=(int)(val/31556926)+1970; + + + uint32_t b= RuleToTime(g_clocksettings.DST_Ds,g_clocksettings.DST_Ms,g_clocksettings.DST_Ws,g_clocksettings.DST_hs-g_clocksettings.Tstd/60,year); + uint32_t e= RuleToTime(g_clocksettings.DST_De,g_clocksettings.DST_Me,g_clocksettings.DST_We,g_clocksettings.DST_he-(g_clocksettings.Tstd+g_clocksettings.Tdst)/60,year); + char tmp[30]; // to hold date string of timestamp + dump_u64(g_clocksettings.value,tmp); // printf with %llu doesn't work :-( so we print it to a string by our own +// ADDLOG_INFO(LOG_FEATURE_RAW, "INFO: in testNsetDST with ClockSettings: %i %i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i -- g_clocksettings.value as uint64_t:%s \r\n",g_clocksettings.TZ, g_clocksettings.DST_H, g_clocksettings.DST_We, g_clocksettings.DST_Me, g_clocksettings.DST_De, g_clocksettings.DST_he, g_clocksettings.Tstd, g_clocksettings.DST_Ws, g_clocksettings.DST_Ms, g_clocksettings.DST_Ds, g_clocksettings.DST_hs, g_clocksettings.Tdst,tmp); + ADDLOG_INFO(LOG_FEATURE_RAW, "Info: epoch %lu in year %i -- b=%lu -- e=%lu\n\r\n\r",val,year,b,e); + if ( b < e ) { // Northern --> begin before end + if (val < b) { + printtime(b,tmp); + ADDLOG_INFO(LOG_FEATURE_RAW, "Before first DST switch in %i. Next switch at %lu (%s)\r\n",year,b,tmp); + g_next_dst_change=b; + g_DSToffset = 0; + return 0; + } else if ( val < e ){ + printtime(e,tmp); + ADDLOG_INFO(LOG_FEATURE_RAW, "In DST of %i. Next switch at %lu (%s)\r\n",year,e,tmp); + g_next_dst_change=e; + g_DSToffset = 60*g_clocksettings.Tdst; + return 1; + } else { + b=RuleToTime(g_clocksettings.DST_Ds,g_clocksettings.DST_Ms,g_clocksettings.DST_Ws,g_clocksettings.DST_hs-g_clocksettings.Tstd/60,year+1); + printtime(b,tmp); + ADDLOG_INFO(LOG_FEATURE_RAW, "After DST in %i. Next switch at %lu (%s - thats next year)\r\n",year,b,tmp); + g_next_dst_change=b; + g_DSToffset = 0; + return 0; + } + } else { // so end of DST before begin of DST --> southern + if (val < e) { + printtime(e,tmp); + ADDLOG_INFO(LOG_FEATURE_RAW, "In first DST period of %i. Next switch at %lu (%s)\r\n",year,e,tmp); + g_next_dst_change=e; + g_DSToffset = 60*g_clocksettings.Tdst; + return 1; + } else if ( val < b ){ + printtime(b,tmp); + ADDLOG_INFO(LOG_FEATURE_RAW, "Regular time of %i. Next switch at %lu (%s)\r\n",year,b,tmp); + g_next_dst_change=b; + g_DSToffset = 0; + return 0; + } else { + e=RuleToTime(g_clocksettings.DST_De,g_clocksettings.DST_Me,g_clocksettings.DST_We,g_clocksettings.DST_he-(g_clocksettings.Tstd+g_clocksettings.Tdst)/60,year+1); + printtime(e,tmp); + ADDLOG_INFO(LOG_FEATURE_RAW, "In second DST of %i. Next switch at %lu (%s - thats next year)\r\n",year,e,tmp); + g_next_dst_change=e; + g_DSToffset = 60*g_clocksettings.Tdst; + return 1; + } + } + + +} + + + +/* +Similar to tasmota settings, but all in one command + +Set policies for the beginning of daylight saving time (DST) and return back to standard time (STD)  +Use the Tasmota timezone table to find the commands for your time zone. +0 = reset parameters to firmware defaults +H,W,M,D,h,T +H = hemisphere (0 = northern hemisphere / 1 = southern hemisphere) +W = week (0 = last week of month, 1..4 = first .. fourth) +M = month (1..12) +D = day of week (1..7 1 = Sunday 7 = Saturday) +h = hour (0..23) in local time +T = time zone (-780..780) (offset from UTC in MINUTES - 780min / 60min=13hrs) +Example: TIMEDST 1,1,10,1,2,660 +_If time zone is NOT 99, DST is not used (even if displayed) + +Since we keep everything in one struct, we have dst start and ending formulas, +the regular UTC offset and the ammount of minutes to add during dst + +So here is a matching of the variables as used in tasmota commands and our struct/union + +TimeStd H,W,M,D,h,T --> He=H; We=W; Me=M; De=D; he=h; Tstd=T +TimeDST H,W,M,D,h,T --> Hs=H; Ws=W; Ms=M; Ds=D; hs=h; Tdst=T-Tstd + +We use an all in on setting command: + +Clock_SetConfig TZ,H,We,Me,De,he,Tstd,Ws,Ms,Ds,hs,Tdst +TZ: Timezone (+/-HH or +/-HH:MM or 99 for DST) +=s: Values for daylight saving time "start of DTS" +=e: Values for standard time "end of DTS" + W = week (0 = last week of month, 1..4 = first .. fourth) + M = month (1..12) + D = day of week (1..7 1 = Sunday 7 = Saturday) + h = hour (0..23) in local time + Tsdt = standard offset during regular time + Tdst = offset to add during DST (allmost everywhere 60 [minutes], -60 in Eire ("negative DST") and seldom 120 or 30) + +example for EU DST (with UTC+1): + TZ=99 --> use DST settings + northern hmisphere (H=0) + start last sun in march at 2 local time (Ws=0 [last ...] Ds=1 [Sunday] Ms=3 [March] hs=2 [2:00] Tdst=60 [add 60 minutes=1 hour for DST]) + end last sun in October at 3 local (dst) time (We=0 [last ...] De=1 [Sunday] Me=10 [Oct] he=3 [3:00] Tdstd=60 [standard time is UTC+1 (UTC+60 minutes)]) +clock_setConfig 99,0,0,10,1,3,60,0,3,2,1,60 + + + +We can also derive all settings from Tasmota settings + +Settings for EU +Tasmota: Europe/Berlin Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120 +Our config: clock_setConfig 99 0,0,10,1,3,60,0,3,1,2,60 + +Settings for NYC +Tasmota: USA/NYC Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240 +Our config: clock_setConfig 99 0,1,11,1,2,-300,2,3,1,2,60 + + +*/ +// CLOCK_SetConfig [TimeZone] [DST-Settings] +commandResult_t CMD_CLOCK_SetConfig(const void *context, const char *cmd, const char *args, int cmdFlags) { + int a,b; + uint8_t H,We,Ws,Me,Ms,De,Ds,he,hs; + int8_t Tdst; + int16_t Tstd,TZ=1; + const char *s; + + Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES); + // following check must be done after 'Tokenizer_TokenizeString', + // so we know arguments count in Tokenizer. 'cmd' argument is + // only for warning display + if (Tokenizer_GetArgsCount() < 1) { // so we have no args +// ADDLOG_INFO(LOG_FEATURE_CMD, "Actual ClockSettings: %i %i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i \r\n",g_clocksettings.TZ, g_clocksettings.DST_H, g_clocksettings.DST_We, g_clocksettings.DST_Me, g_clocksettings.DST_De, g_clocksettings.DST_he, g_clocksettings.Tstd, g_clocksettings.DST_Ws, g_clocksettings.DST_Ms, g_clocksettings.DST_Ds, g_clocksettings.DST_hs, g_clocksettings.Tdst); + addLogAdv(LOG_INFO, LOG_FEATURE_CMD, "Actual ClockSettings %i %i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i\r\n", g_clocksettings.TZ, g_clocksettings.DST_H, g_clocksettings.DST_We, g_clocksettings.DST_Me, g_clocksettings.DST_De, g_clocksettings.DST_he, g_clocksettings.Tstd, g_clocksettings.DST_Ws, g_clocksettings.DST_Ms, g_clocksettings.DST_Ds, g_clocksettings.DST_hs, g_clocksettings.Tdst); + } + if (Tokenizer_CheckArgsCountAndPrintWarning(cmd, 1)) { + return CMD_RES_NOT_ENOUGH_ARGUMENTS; + } + +// handle first parameter: timezone +// can be a string like "-10:30" or an integer like -10 +// we accept a single parameter (TZ) or a complete set of daylight saving time settings + s = Tokenizer_GetArg(0); + if (strchr(s, ':')) { +// printf("CLOCK_SetConfigs: TZ with : time TZ=%s\r\n", s); + ADDLOG_INFO(LOG_FEATURE_RAW, "CLOCK_SetConfigs: TZ with : time TZ=%s\r\n", s); + if (*s == '-') { + s++; +// printf("CLOCK_SetConfigs: TZ negative (-)\r\n"); + ADDLOG_INFO(LOG_FEATURE_RAW, "CLOCK_SetConfigs: TZ negative (-)\r\n"); + TZ = -1; + } else if (*s == '+') { +// printf("CLOCK_SetConfigs: TZ posiive (+)\r\n"); + ADDLOG_INFO(LOG_FEATURE_RAW, "CLOCK_SetConfigs: TZ posiive (+)\r\n"); + s++; + } + sscanf(s, "%d:%2d", &a, &b); +// printf("CLOCK_SetConfigs: a=%i b=%i (s=%s)\r\n",a,b,s); + ADDLOG_INFO(LOG_FEATURE_RAW, "CLOCK_SetConfigs: a=%i b=%i (s=%s)\r\n",a,b,s); + TZ *= (a * 100 + b); // we use time as signed number of the time zone eg: -1400 for -14:00 + // set offset for clock hours and minutes + g_UTCoffset=3600*a+60*b; + } + else { + TZ = Tokenizer_GetArgInteger(0); + // set offset for clock [hours] (if it's no DST line, then we use Tstd later) + if (TZ != 99) g_UTCoffset=3600*TZ; + } + + g_clocksettings.TZ=TZ; + + if (Tokenizer_GetArgsCount() > 1) { // so we have more than 1 argument (TZ) + s = Tokenizer_GetArg(1); +// ADDLOG_INFO(LOG_FEATURE_CMD, "CLOCK_SetConfigs: we have %i Args, TZ is set to %i", a,TZ); +// if (TZ <> 99) ADDLOG_INFO(LOG_FEATURE_CMD, "!!!Warning: Setting DST but TZ is not 99 but set to %i!!!\n",TZ); +// printf("CLOCK_SetConfigs: we have %u Args, TZ is set to %i -- S=%s\r\n", Tokenizer_GetArgsCount(),TZ,s); + ADDLOG_INFO(LOG_FEATURE_RAW, "CLOCK_SetConfigs: we have %u Args, TZ is set to %i -- S=%s\r\n", Tokenizer_GetArgsCount(),TZ,s); + +// g_clocksettings.H=Tokenizer_GetArgInteger(1); +// do this later, so we can do some sanity checks before ... +// so use local vars here +// Clock_SetConfig TZ,H,We,Me,De,he,Tstd,Ws,Ms,Ds,hs,Tdst + + H = Tokenizer_GetArgInteger(1); + We = Tokenizer_GetArgInteger(2); + Me = Tokenizer_GetArgInteger(3); + De = Tokenizer_GetArgInteger(4); + he = Tokenizer_GetArgInteger(5); + Tstd = Tokenizer_GetArgInteger(6); + Ws = Tokenizer_GetArgInteger(7); + Ms = Tokenizer_GetArgInteger(8); + Ds = Tokenizer_GetArgInteger(9); + hs = Tokenizer_GetArgInteger(10); + Tdst = Tokenizer_GetArgInteger(11); +ADDLOG_INFO(LOG_FEATURE_RAW, "read values: %u,%u,%u,%u,%u,%i,%u,%u,%u,%u,%i\r\n", H, We, Me, De, he, Tstd, Ws, Ms, Ds, hs, Tdst); +// todo: check numbers before assigning them + g_clocksettings.DST_H=H; + g_clocksettings.DST_We=We; + g_clocksettings.DST_Me=Me; + g_clocksettings.DST_De=De; + g_clocksettings.DST_he=he; + g_clocksettings.Tstd=Tstd; + // set offset for clock (minutes) + g_UTCoffset=60*Tstd; + g_clocksettings.DST_Ws=Ws; + g_clocksettings.DST_Ms=Ms; + g_clocksettings.DST_Ds=Ds; + g_clocksettings.DST_hs=hs; + g_clocksettings.Tdst=Tdst; + }; // + // save settings + CFG_SetCLOCK_SETTINGS(g_clocksettings.value); + return CMD_RES_OK; +}; + +commandResult_t CMD_CLOCK_GetConfig(const void *context, const char *cmd, const char *args, int cmdFlags) { +// ADDLOG_INFO(LOG_FEATURE_CMD, "ClockSettings: %i %i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i \r\n",g_clocksettings.TZ, g_clocksettings.DST_H, g_clocksettings.DST_We, g_clocksettings.DST_Me, g_clocksettings.DST_De, g_clocksettings.DST_he, g_clocksettings.Tstd, g_clocksettings.DST_Ws, g_clocksettings.DST_Ms, g_clocksettings.DST_Ds, g_clocksettings.DST_hs, g_clocksettings.Tdst); + addLogAdv(LOG_INFO, LOG_FEATURE_CMD, "ClockSettings: %i %i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i \r\n",g_clocksettings.TZ, g_clocksettings.DST_H, g_clocksettings.DST_We, g_clocksettings.DST_Me, g_clocksettings.DST_De, g_clocksettings.DST_he, g_clocksettings.Tstd, g_clocksettings.DST_Ws, g_clocksettings.DST_Ms, g_clocksettings.DST_Ds, g_clocksettings.DST_hs, g_clocksettings.Tdst); + + + + + return CMD_RES_OK; +} + +void set_UTCoffset_from_Config(){ + if (g_clocksettings.TZ != 99) { + int TZ=g_clocksettings.TZ; + // value hours only? Then |TZ| < 24 (or TZ² < 576 regardless if it's positive or negative) + if (TZ*TZ < 576 ) g_UTCoffset=3600*TZ; // TZ is hours only + else { + g_UTCoffset=3600*(int)TZ/100+60*(TZ%100); // TZ/100 = hours T/%100 = minutes + } + } + else { + g_UTCoffset=60*g_clocksettings.Tstd; // set offset for clock (from minutes) + g_DSToffset=60*g_clocksettings.Tdst; // set DST offset for (from minutes) + } + +} + +#endif // to #if ENABLE_LOCAL_CLOCK_ADVANCED + + diff --git a/src/driver/drv_deviceclock.h b/src/driver/drv_deviceclock.h new file mode 100644 index 000000000..b2f5298e4 --- /dev/null +++ b/src/driver/drv_deviceclock.h @@ -0,0 +1,95 @@ +#ifndef __DRV_DEVICECLOCK_H__ +#define __DRV_DEVICECLOCK_H__ + +#include "../httpserver/new_http.h" + +int CLOCK_GetWeekDay(); +int CLOCK_GetHour(); +int CLOCK_GetMinute(); +int CLOCK_GetSecond(); +int CLOCK_GetMDay(); +int CLOCK_GetMonth(); +int CLOCK_GetYear(); +int CLOCK_GetSunrise(); +int CLOCK_GetSunset(); +// drv_timed_events.c +int CLOCK_Print_EventList(); +void CLOCK_setDeviceTime(uint32_t time); +void CLOCK_setDeviceTimeOffset(int offs); +int CLOCK_GetEventTime(int id); +int CLOCK_RemoveEvent(int id); +int CLOCK_ClearEvents(); +void CLOCK_Init(); +void CLOCK_OnEverySecond(); +extern struct SUN_DATA { /* sunrise / sunset globals */ + int latitude; /* latitude * 1000000 */ + int longitude; /* longitude * 1000000 */ + } sun_data; + +#if ENABLE_LOCAL_CLOCK_ADVANCED +// handle daylight saving time + +// define a union to hold all settings regarding local clock (timezone, dst settings ...) +// we will use somthing simmilar to tasmotas handling of dst with the commands: +// TimeStd +// TimeDst +// use a union, to be able to save/restore all values as one 64 bit integer +union DST{ + struct { + uint32_t DST_H : 1; // 0-1 only once - you can only be in one DST_hemisphere ;-) + uint32_t DST_Ws : 3; // 0-7 + uint32_t DST_Ms : 4; // 0-15 + uint32_t DST_Ds : 3; // 0-7 + uint32_t DST_hs : 5; // 0-31 + // up to here: 16 bits + int32_t Tstd : 11; // -1024 - +1023 ( regular (non DST) offset to UTC max 14 h = 840 Min) + uint32_t DST_he : 5; // 0-31 + // up to here: 32 bits + uint32_t DST_We : 3; // 0-7 + uint32_t DST_Me : 4; // 0-15 + uint32_t DST_De : 3; // 0-7 + int32_t Tdst : 8; // -128 - +127 (Eire has "negative" DST in winter (-60) ...; max DST offset is 120 minutes) + int32_t TZ : 12; // -2048 - 2047 for TZ hours (max offset is +14h and - 12h -- we will use 99 as indicator for "DST" like tasmota, all otDST_hers values are interpreted as the of the time zone eg: -1400 for -14:00) + // up to here: 62 bits + }; + uint64_t value; // to save/store all values in one large integer +}; + +extern union DST g_clocksettings; + +#endif + +int testNsetDST(uint32_t val); + +// to use ticks for time keeping +// since usual ticktimer (uint32_t) rolls over after approx 50 days, +// we need to count this rollovers +#ifndef WINDOWS +extern TickType_t lastTick; +extern uint8_t timer_rollover; // I don't expect uptime > 35 years ... +#endif +void set_UTCoffset_from_Config(); + +uint32_t Clock_GetCurrentTime(); // might replace for NTP_GetCurrentTime() to return time regardless of NTP present/running +uint32_t Clock_GetCurrentTimeWithoutOffset(); // ... same for NTP_GetCurrentTimeWithoutOffset()... +bool Clock_IsTimeSynced(); // ... and for NTP_IsTimeSynced() +int Clock_GetTimesZoneOfsSeconds(); // ... and for NTP_GetTimesZoneOfsSeconds() + + + +// +// ################################################################### START ######################################################################## +// ##################################################### temp. function to get local time, ############################################################ +// ##################################################### even if NTP is running and synced ############################################################ +// +uint32_t Clock_GetDeviceTime(); +uint32_t Clock_GetDeviceTimeWithoutOffset(); + +// +// ################################################################### END ######################################################################## +// ##################################################### temp. function to get local time, ############################################################ +// ##################################################### even if NTP is running and synced ############################################################ +// + +#endif /* __DRV_DEVICECLOCK_H__ */ + diff --git a/src/driver/drv_main.c b/src/driver/drv_main.c index d0ff0b7a2..4d8495fea 100644 --- a/src/driver/drv_main.c +++ b/src/driver/drv_main.c @@ -91,7 +91,7 @@ static driver_t g_drivers[] = { //drvdetail:"title":"TODO", //drvdetail:"descr":"NTP driver is required to get current time and date from web. Without it, there is no correct datetime. Put 'startDriver NTP' in short startup line or autoexec.bat to run it on start.", //drvdetail:"requires":""} - { "NTP", NTP_Init, NTP_OnEverySecond, NTP_AppendInformationToHTTPIndexPage, NULL, NULL, NULL, false }, + { "NTP", NTP_Init, NTP_OnEverySecond, NTP_AppendInformationToHTTPIndexPage, NULL, NTP_Stop , NULL, false }, #endif #if ENABLE_HTTPBUTTONS //drvdetail:{"name":"HTTPButtons", diff --git a/src/driver/drv_ntp.c b/src/driver/drv_ntp.c index b9ff90fdf..242c35273 100644 --- a/src/driver/drv_ntp.c +++ b/src/driver/drv_ntp.c @@ -12,16 +12,9 @@ #include "../logging/logging.h" #include "../ota/ota.h" +#include "drv_deviceclock.h" // for CLOCK_Init() #include "drv_ntp.h" -extern void NTP_Init_Events(void); -extern void NTP_RunEvents(unsigned int newTime, bool bTimeValid); - -#if ENABLE_NTP_SUNRISE_SUNSET -extern void NTP_CalculateSunrise(byte *outHour, byte *outMinute); -extern void NTP_CalculateSunset(byte *outHour, byte *outMinute); -#endif - #define LOG_FEATURE LOG_FEATURE_NTP typedef struct @@ -108,61 +101,6 @@ commandResult_t NTP_SetTimeZoneOfs(const void *context, const char *cmd, const c return CMD_RES_OK; } -#if ENABLE_NTP_SUNRISE_SUNSET - -/* sunrise/sunset defaults */ -#define CFG_DEFAULT_LATITUDE 43.994131 -#define CFG_DEFAULT_LONGITUDE -123.095854 -#define SUN_DATA_COORD_MULT 1000000 - -struct SUN_DATA sun_data = - { - .latitude = (int) (CFG_DEFAULT_LATITUDE * SUN_DATA_COORD_MULT), - .longitude = (int) (CFG_DEFAULT_LONGITUDE * SUN_DATA_COORD_MULT), - }; - -//Set Latitude and Longitude for sunrise/sunset calc -commandResult_t NTP_SetLatlong(const void *context, const char *cmd, const char *args, int cmdFlags) { - const char *newValue; - - Tokenizer_TokenizeString(args,0); - // following check must be done after 'Tokenizer_TokenizeString', - // so we know arguments count in Tokenizer. 'cmd' argument is - // only for warning display - if (Tokenizer_CheckArgsCountAndPrintWarning(cmd, 2)) { - return CMD_RES_NOT_ENOUGH_ARGUMENTS; - } - newValue = Tokenizer_GetArg(0); - sun_data.latitude = (int) (atof(newValue) * SUN_DATA_COORD_MULT); - addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "NTP latitude set to %s", newValue); - - newValue = Tokenizer_GetArg(1); - sun_data.longitude = (int) (atof(newValue) * SUN_DATA_COORD_MULT); - addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "NTP longitude set to %s", newValue); - return CMD_RES_OK; -} - -int NTP_GetSunrise() -{ - byte hour, minute; - int sunriseInSecondsFromMidnight; - - NTP_CalculateSunrise(&hour, &minute); - sunriseInSecondsFromMidnight = ((int)hour * 3600) + ((int)minute * 60); - return sunriseInSecondsFromMidnight; -} - -int NTP_GetSunset() -{ - byte hour, minute; - int sunsetInSecondsFromMidnight; - - NTP_CalculateSunset(&hour, &minute); - sunsetInSecondsFromMidnight = ((int)hour * 3600) + ((int)minute * 60); - return sunsetInSecondsFromMidnight; -} -#endif - //Set custom NTP server commandResult_t NTP_SetServer(const void *context, const char *cmd, const char *args, int cmdFlags) { const char *newValue; @@ -185,90 +123,6 @@ commandResult_t NTP_Info(const void *context, const char *cmd, const char *args, addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "Server=%s, Time offset=%d", CFG_GetNTPServer(), g_timeOffsetSeconds); return CMD_RES_OK; } -int NTP_GetWeekDay() { - struct tm *ltm; - - // NOTE: on windows, you need _USE_32BIT_TIME_T - ltm = gmtime(&g_ntpTime); - - if (ltm == 0) { - return 0; - } - - return ltm->tm_wday; -} -int NTP_GetHour() { - struct tm *ltm; - - // NOTE: on windows, you need _USE_32BIT_TIME_T - ltm = gmtime(&g_ntpTime); - - if (ltm == 0) { - return 0; - } - - return ltm->tm_hour; -} -int NTP_GetMinute() { - struct tm *ltm; - - // NOTE: on windows, you need _USE_32BIT_TIME_T - ltm = gmtime(&g_ntpTime); - - if (ltm == 0) { - return 0; - } - - return ltm->tm_min; -} -int NTP_GetSecond() { - struct tm *ltm; - - // NOTE: on windows, you need _USE_32BIT_TIME_T - ltm = gmtime(&g_ntpTime); - - if (ltm == 0) { - return 0; - } - - return ltm->tm_sec; -} -int NTP_GetMDay() { - struct tm *ltm; - - // NOTE: on windows, you need _USE_32BIT_TIME_T - ltm = gmtime(&g_ntpTime); - - if (ltm == 0) { - return 0; - } - - return ltm->tm_mday; -} -int NTP_GetMonth() { - struct tm *ltm; - - // NOTE: on windows, you need _USE_32BIT_TIME_T - ltm = gmtime(&g_ntpTime); - - if (ltm == 0) { - return 0; - } - - return ltm->tm_mon+1; -} -int NTP_GetYear() { - struct tm *ltm; - - // NOTE: on windows, you need _USE_32BIT_TIME_T - ltm = gmtime(&g_ntpTime); - - if (ltm == 0) { - return 0; - } - - return ltm->tm_year+1900; -} #if WINDOWS bool b_ntp_simulatedTime = false; void NTP_SetSimulatedTime(unsigned int timeNow) { @@ -276,6 +130,11 @@ void NTP_SetSimulatedTime(unsigned int timeNow) { g_ntpTime += g_timeOffsetSeconds; g_synced = true; b_ntp_simulatedTime = true; +#if ENABLE_LOCAL_CLOCK + CLOCK_setDeviceTime(timeNow); + // we might have an action pending for this time, so call CLOCK_OnEverySecond(); + CLOCK_OnEverySecond(); +#endif } #endif void NTP_Init() { @@ -293,28 +152,23 @@ void NTP_Init() { //cmddetail:"fn":"NTP_SetServer","file":"driver/drv_ntp.c","requires":"", //cmddetail:"examples":""} CMD_RegisterCommand("ntp_setServer", NTP_SetServer, NULL); -#if ENABLE_NTP_SUNRISE_SUNSET - //cmddetail:{"name":"ntp_setLatLong","args":"[Latlong]", - //cmddetail:"descr":"Sets the NTP latitude and longitude", - //cmddetail:"fn":"NTP_SetLatlong","file":"driver/drv_ntp.c","requires":"", - //cmddetail:"examples":"NTP_SetLatlong -34.911498 138.809488"} - CMD_RegisterCommand("ntp_setLatLong", NTP_SetLatlong, NULL); -#endif //cmddetail:{"name":"ntp_info","args":"", //cmddetail:"descr":"Display NTP related settings", //cmddetail:"fn":"NTP_Info","file":"driver/drv_ntp.c","requires":"", //cmddetail:"examples":""} CMD_RegisterCommand("ntp_info", NTP_Info, NULL); -#if ENABLE_CALENDAR_EVENTS - NTP_Init_Events(); -#endif - addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "NTP driver initialized with server=%s, offset=%d", CFG_GetNTPServer(), g_timeOffsetSeconds); g_synced = false; } +// if driver is stopped, we need to make sure, we don't keep NTP in state "synched" +void NTP_Stop() { + addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "NTP driver stopped"); + g_synced = false; +} + unsigned int NTP_GetCurrentTime() { return g_ntpTime; } @@ -439,6 +293,9 @@ void NTP_CheckForReceive() { addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"Seconds since Jan 1 1900 = %u",secsSince1900); g_ntpTime = secsSince1900 - NTP_OFFSET; +#if ENABLE_LOCAL_CLOCK + CLOCK_setDeviceTime(g_ntpTime); +#endif g_ntpTime += g_timeOffsetSeconds; addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"Unix time : %u",(unsigned int)g_ntpTime); ltm = gmtime(&g_ntpTime); @@ -447,6 +304,9 @@ void NTP_CheckForReceive() { if (g_synced == false) { EventHandlers_FireEvent(CMD_EVENT_NTP_STATE, 1); + // so now clock is synced. If it wasn't set before, start "CLOCK_Init()" for timed events + // done in CMD_Init_Delayed() in cmd_main.c +// if (! Clock_IsTimeSynced() ) CLOCK_Init(); } g_synced = true; #if 0 @@ -476,9 +336,6 @@ void NTP_OnEverySecond() { g_ntpTime++; -#if ENABLE_CALENDAR_EVENTS - NTP_RunEvents(g_ntpTime, g_synced); -#endif if(Main_IsConnectedToWiFi()==0) { return; diff --git a/src/driver/drv_ntp.h b/src/driver/drv_ntp.h index 611505b3e..f80241458 100644 --- a/src/driver/drv_ntp.h +++ b/src/driver/drv_ntp.h @@ -4,6 +4,7 @@ #include "../httpserver/new_http.h" void NTP_Init(); +void NTP_Stop(); void NTP_OnEverySecond(); // returns number of seconds passed after 1900 unsigned int NTP_GetCurrentTime(); @@ -11,28 +12,10 @@ unsigned int NTP_GetCurrentTimeWithoutOffset(); void NTP_AppendInformationToHTTPIndexPage(http_request_t* request); bool NTP_IsTimeSynced(); int NTP_GetTimesZoneOfsSeconds(); -int NTP_GetWeekDay(); -int NTP_GetHour(); -int NTP_GetMinute(); -int NTP_GetSecond(); -int NTP_GetMDay(); -int NTP_GetMonth(); -int NTP_GetYear(); -int NTP_GetSunrise(); -int NTP_GetSunset(); // for Simulator only, on Windows, for unit testing void NTP_SetSimulatedTime(unsigned int timeNow); -// drv_ntp_events.c -int NTP_PrintEventList(); -int NTP_GetEventTime(int id); -int NTP_RemoveClockEvent(int id); -int NTP_ClearEvents(); extern time_t g_ntpTime; -extern struct SUN_DATA { /* sunrise / sunset globals */ - int latitude; /* latitude * 1000000 */ - int longitude; /* longitude * 1000000 */ - } sun_data; #endif /* __DRV_NTP_H__ */ diff --git a/src/driver/drv_ntp_events.c b/src/driver/drv_timed_events.c similarity index 77% rename from src/driver/drv_ntp_events.c rename to src/driver/drv_timed_events.c index f37af6d16..f0e470471 100644 --- a/src/driver/drv_ntp_events.c +++ b/src/driver/drv_timed_events.c @@ -10,19 +10,19 @@ #include "../logging/logging.h" #include "../ota/ota.h" -#include "drv_ntp.h" +#include "drv_deviceclock.h" #define M_PI 3.14159265358979323846264338327950288 #define LOG_FEATURE LOG_FEATURE_NTP -time_t ntp_eventsTime = 0; +time_t clock_eventsTime = 0; -typedef struct ntpEvent_s { +typedef struct clockEvent_s { byte hour; byte minute; byte second; byte weekDayFlags; -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET byte lastDay; /* used so we don't repeat sunrise sunset events the same day */ byte sunflags; /* flags for sunrise/sunset as follows: */ #define SUNRISE_FLAG (1 << 0) @@ -30,12 +30,12 @@ typedef struct ntpEvent_s { #endif int id; char *command; - struct ntpEvent_s *next; -} ntpEvent_t; + struct clockEvent_s *next; +} clockEvent_t; -ntpEvent_t *ntp_events = 0; +clockEvent_t *clock_events = 0; -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET /* Sunrise/sunset algorithm, somewhat based on https://edwilliams.org/sunrise_sunset_algorithm.htm and tasmota code */ const float pi2 = (M_PI * 2); const float pi = M_PI; @@ -93,9 +93,9 @@ static inline uint32_t JulianDay(void) { /* https://en.wikipedia.org/wiki/Julian_day */ - uint32_t Year = NTP_GetYear(); /* Year ex:2020 */ - uint32_t Month = NTP_GetMonth(); /* 1..12 */ - uint32_t Day = NTP_GetMDay(); /* 1..31 */ + uint32_t Year = CLOCK_GetYear(); /* Year ex:2020 */ + uint32_t Month = CLOCK_GetMonth(); /* 1..12 */ + uint32_t Day = CLOCK_GetMDay(); /* 1..31 */ uint32_t Julian; /* Julian day number */ if (Month <= 2) { @@ -124,7 +124,7 @@ static void dusk2Dawn(struct SUN_DATA *Settings, byte sunflags, uint8_t *hour, u float geoLatitude = Settings->latitude / (1000000.0f / RAD); float geoLongitude = ((float) Settings->longitude) / 1000000; - float timeZone = ((float) NTP_GetTimesZoneOfsSeconds()) / 3600; /* convert to hours */ + float timeZone = ((float) Clock_GetTimesZoneOfsSeconds()) / 3600; /* convert to hours */ float timeEquation = TimeFormula(&declination, Tdays); float timeDiff = acosf((sin_h - sinf(geoLatitude) * sinf(declination)) / (cosf(geoLatitude) * cosf(declination))) * (12.0f / pi); @@ -158,16 +158,16 @@ static int calc_day_offset(int tm_wday, int weekDayFlags) } return (day_offset); } -void NTP_CalculateSunrise(byte *outHour, byte *outMinute) { +void CLOCK_CalculateSunrise(byte *outHour, byte *outMinute) { dusk2Dawn(&sun_data, SUNRISE_FLAG, outHour, outMinute, 0); } -void NTP_CalculateSunset(byte *outHour, byte *outMinute) { +void CLOCK_CalculateSunset(byte *outHour, byte *outMinute) { dusk2Dawn(&sun_data, SUNSET_FLAG, outHour, outMinute, 0); } #endif -void NTP_RunEventsForSecond(time_t runTime) { - ntpEvent_t *e; +void CLOCK_RunEventsForSecond(time_t runTime) { + clockEvent_t *e; struct tm *ltm; // NOTE: on windows, you need _USE_32BIT_TIME_T @@ -177,7 +177,7 @@ void NTP_RunEventsForSecond(time_t runTime) { return; } - e = ntp_events; + e = clock_events; while (e) { if (e->command) { @@ -185,7 +185,7 @@ void NTP_RunEventsForSecond(time_t runTime) { if (e->hour == ltm->tm_hour && e->second == ltm->tm_sec && e->minute == ltm->tm_min) { // weekday check if (BIT_CHECK(e->weekDayFlags, ltm->tm_wday)) { -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET if (e->sunflags & (SUNRISE_FLAG || SUNSET_FLAG)) { if (e->lastDay != ltm->tm_wday) { e->lastDay = ltm->tm_wday; /* stop any further sun events today */ @@ -207,44 +207,44 @@ void NTP_RunEventsForSecond(time_t runTime) { } } -void NTP_RunEvents(unsigned int newTime, bool bTimeValid) { +void CLOCK_RunEvents(unsigned int newTime, bool bTimeValid) { unsigned int delta; unsigned int i; // new time invalid? if (bTimeValid == false) { - ntp_eventsTime = 0; + clock_eventsTime = 0; return; } // old time invalid, but new one ok? - if (ntp_eventsTime == 0) { - ntp_eventsTime = (time_t)newTime; + if (clock_eventsTime == 0) { + clock_eventsTime = (time_t)newTime; return; } // time went backwards - if (newTime < ntp_eventsTime) { - ntp_eventsTime = (time_t)newTime; + if (newTime < clock_eventsTime) { + clock_eventsTime = (time_t)newTime; return; } - if (ntp_events) { + if (clock_events) { // NTP resynchronization could cause us to skip some seconds in some rare cases? - delta = (unsigned int)((time_t)newTime - ntp_eventsTime); + delta = (unsigned int)((time_t)newTime - clock_eventsTime); // a large shift in time is not expected, so limit to a constant number of seconds if (delta > 100) delta = 100; for (i = 0; i < delta; i++) { - NTP_RunEventsForSecond(ntp_eventsTime + i); + CLOCK_RunEventsForSecond(clock_eventsTime + i); } } - ntp_eventsTime = (time_t)newTime; + clock_eventsTime = (time_t)newTime; } -#if ENABLE_NTP_SUNRISE_SUNSET -void NTP_AddClockEvent(int hour, int minute, int second, int weekDayFlags, int id, int sunflags, const char* command) { +#if ENABLE_CLOCK_SUNRISE_SUNSET +void CLOCK_AddEvent(int hour, int minute, int second, int weekDayFlags, int id, int sunflags, const char* command) { #else -void NTP_AddClockEvent(int hour, int minute, int second, int weekDayFlags, int id, const char* command) { +void CLOCK_AddEvent(int hour, int minute, int second, int weekDayFlags, int id, const char* command) { #endif - ntpEvent_t* newEvent = (ntpEvent_t*)malloc(sizeof(ntpEvent_t)); + clockEvent_t* newEvent = (clockEvent_t*)malloc(sizeof(clockEvent_t)); if (newEvent == NULL) { // handle error return; @@ -254,25 +254,25 @@ void NTP_AddClockEvent(int hour, int minute, int second, int weekDayFlags, int i newEvent->minute = minute; newEvent->second = second; newEvent->weekDayFlags = weekDayFlags; -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET newEvent->lastDay = -1; /* mark with anything but a valid day of week */ newEvent->sunflags = sunflags; #endif newEvent->id = id; newEvent->command = strdup(command); - newEvent->next = ntp_events; + newEvent->next = clock_events; - ntp_events = newEvent; + clock_events = newEvent; } -int NTP_RemoveClockEvent(int id) { +int CLOCK_RemoveEvent(int id) { int ret = 0; - ntpEvent_t* curr = ntp_events; - ntpEvent_t* prev = NULL; + clockEvent_t* curr = clock_events; + clockEvent_t* prev = NULL; while (curr != NULL) { if (curr->id == id) { if (prev == NULL) { - ntp_events = curr->next; + clock_events = curr->next; } else { prev->next = curr->next; @@ -281,7 +281,7 @@ int NTP_RemoveClockEvent(int id) { free(curr); ret++; if (prev == NULL) { - curr = ntp_events; + curr = clock_events; } else { curr = prev->next; @@ -304,15 +304,15 @@ int NTP_RemoveClockEvent(int id) { // addClockEvent 15:06:00 0xff 123 POWER TOGGLE // Example: do event every Wednesday at sunrise // addClockEvent sunrise 0x08 12 POWER OFF -commandResult_t CMD_NTP_AddClockEvent(const void *context, const char *cmd, const char *args, int cmdFlags) { +commandResult_t CMD_CLOCK_AddEvent(const void *context, const char *cmd, const char *args, int cmdFlags) { int hour, minute = 0, second = 0; const char *s; int flags; int id; -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET uint8_t hour_b, minute_b; int sunflags = 0; - struct tm *ltm = gmtime(&ntp_eventsTime); + struct tm *ltm = gmtime(&clock_eventsTime); #endif Tokenizer_TokenizeString(args, TOKENIZER_ALTERNATE_EXPAND_AT_START); @@ -328,7 +328,8 @@ commandResult_t CMD_NTP_AddClockEvent(const void *context, const char *cmd, cons if (sscanf(s, "%2d:%2d:%2d", &hour, &minute, &second) >= 2) { // hour, minute and second has correct value parsed } -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET +#include else if (strcasestr(s, "sunrise")) { sunflags |= SUNRISE_FLAG; } @@ -347,21 +348,21 @@ commandResult_t CMD_NTP_AddClockEvent(const void *context, const char *cmd, cons id = Tokenizer_GetArgInteger(2); s = Tokenizer_GetArgFrom(3); -#if ENABLE_NTP_SUNRISE_SUNSET +#if ENABLE_CLOCK_SUNRISE_SUNSET if (sunflags) { dusk2Dawn(&sun_data, sunflags, &hour_b, &minute_b, calc_day_offset(ltm->tm_wday, flags)); hour = hour_b; minute = minute_b; } - NTP_AddClockEvent(hour, minute, second, flags, id, sunflags, s); + CLOCK_AddEvent(hour, minute, second, flags, id, sunflags, s); #else - NTP_AddClockEvent(hour, minute, second, flags, id, s); + CLOCK_AddEvent(hour, minute, second, flags, id, s); #endif return CMD_RES_OK; } // addPeriodValue [ChannelIndex] [Start_DayOfWeek] [Start_HH:MM:SS] [End_DayOfWeek] [End_HH:MM:SS] [Value] [UniqueID] [Flags] -//commandResult_t CMD_NTP_AddPeriodValue(const void *context, const char *cmd, const char *args, int cmdFlags) { +//commandResult_t CMD_CLOCK_AddPeriodValue(const void *context, const char *cmd, const char *args, int cmdFlags) { // int start_hour, start_minute, start_second, start_day; // int end_hour, end_minute, end_second, end_day; // const char *s; @@ -396,7 +397,7 @@ commandResult_t CMD_NTP_AddClockEvent(const void *context, const char *cmd, cons // return CMD_RES_OK; //} -commandResult_t CMD_NTP_RemoveClockEvent(const void* context, const char* cmd, const char* args, int cmdFlags) { +commandResult_t CMD_CLOCK_RemoveEvent(const void* context, const char* cmd, const char* args, int cmdFlags) { int id; // tokenize the args string @@ -411,13 +412,13 @@ commandResult_t CMD_NTP_RemoveClockEvent(const void* context, const char* cmd, c id = Tokenizer_GetArgInteger(0); // Remove the clock event with the given id - NTP_RemoveClockEvent(id); + CLOCK_RemoveEvent(id); return CMD_RES_OK; } -int NTP_GetEventTime(int id) { - for (ntpEvent_t* e = ntp_events; e; e = e->next) +int CLOCK_GetEventTime(int id) { + for (clockEvent_t* e = clock_events; e; e = e->next) { if (e->id == id) { @@ -428,11 +429,11 @@ int NTP_GetEventTime(int id) { return -1; } -int NTP_PrintEventList() { - ntpEvent_t* e; +int CLOCK_Print_EventList() { + clockEvent_t* e; int t; - e = ntp_events; + e = clock_events; t = 0; while (e) { @@ -446,21 +447,21 @@ int NTP_PrintEventList() { addLogAdv(LOG_INFO, LOG_FEATURE_CMD, "Total %i events", t); return t; } -commandResult_t CMD_NTP_ListEvents(const void* context, const char* cmd, const char* args, int cmdFlags) { +commandResult_t CMD_CLOCK_ListEvents(const void* context, const char* cmd, const char* args, int cmdFlags) { - NTP_PrintEventList(); + CLOCK_Print_EventList(); return CMD_RES_OK; } -int NTP_ClearEvents() { - ntpEvent_t* e; +int CLOCK_ClearEvents() { + clockEvent_t* e; int t; - e = ntp_events; + e = clock_events; t = 0; while (e) { - ntpEvent_t *p = e; + clockEvent_t *p = e; t++; e = e->next; @@ -468,38 +469,38 @@ int NTP_ClearEvents() { free(p->command); free(p); } - ntp_events = 0; + clock_events = 0; addLogAdv(LOG_INFO, LOG_FEATURE_CMD, "Removed %i events", t); return t; } -commandResult_t CMD_NTP_ClearEvents(const void* context, const char* cmd, const char* args, int cmdFlags) { +commandResult_t CMD_CLOCK_ClearEvents(const void* context, const char* cmd, const char* args, int cmdFlags) { - NTP_ClearEvents(); + CLOCK_ClearEvents(); return CMD_RES_OK; } -void NTP_Init_Events() { +void CLOCK_Init_Events() { //cmddetail:{"name":"addClockEvent","args":"[TimerSeconds or Time or sunrise or sunset] [WeekDayFlags] [UniqueIDForRemoval][Command]", - //cmddetail:"descr":"Schedule command to run on given time in given day of week. NTP must be running. TimerSeconds is seconds from midnight, Time is a time like HH:mm or HH:mm:ss, WeekDayFlag is a bitflag on which day to run, 0xff mean all days, 0x01 means sunday, 0x02 monday, 0x03 sunday and monday, etc, id is an unique id so event can be removed later. (NOTE: Use of sunrise/sunset requires compiling with ENABLE_NTP_SUNRISE_SUNSET set which adds about 11k of code)", - //cmddetail:"fn":"CMD_NTP_AddClockEvent","file":"driver/drv_ntp_events.c","requires":"", + //cmddetail:"descr":"Schedule command to run on given time in given day of week. NTP must be running. TimerSeconds is seconds from midnight, Time is a time like HH:mm or HH:mm:ss, WeekDayFlag is a bitflag on which day to run, 0xff mean all days, 0x01 means sunday, 0x02 monday, 0x03 sunday and monday, etc, id is an unique id so event can be removed later. (NOTE: Use of sunrise/sunset requires compiling with ENABLE_CLOCK_SUNRISE_SUNSET set which adds about 11k of code)", + //cmddetail:"fn":"CMD_CLOCK_AddEvent","file":"driver/drv_timed_events.c","requires":"", //cmddetail:"examples":""} - CMD_RegisterCommand("addClockEvent",CMD_NTP_AddClockEvent, NULL); + CMD_RegisterCommand("addClockEvent",CMD_CLOCK_AddEvent, NULL); //cmddetail:{"name":"removeClockEvent","args":"[ID]", //cmddetail:"descr":"Removes clock event wtih given ID", - //cmddetail:"fn":"CMD_NTP_RemoveClockEvent","file":"driver/drv_ntp_events.c","requires":"", + //cmddetail:"fn":"CMD_CLOCK_RemoveEvent","file":"driver/drv_timed_events.c","requires":"", //cmddetail:"examples":""} - CMD_RegisterCommand("removeClockEvent", CMD_NTP_RemoveClockEvent, NULL); + CMD_RegisterCommand("removeClockEvent", CMD_CLOCK_RemoveEvent, NULL); //cmddetail:{"name":"listClockEvents","args":"", //cmddetail:"descr":"Print the complete set clock events list", - //cmddetail:"fn":"CMD_NTP_ListEvents","file":"driver/drv_ntp_events.c","requires":"", + //cmddetail:"fn":"CMD_CLOCK_ListEvents","file":"driver/drv_timed_events.c","requires":"", //cmddetail:"examples":""} - CMD_RegisterCommand("listClockEvents", CMD_NTP_ListEvents, NULL); + CMD_RegisterCommand("listClockEvents", CMD_CLOCK_ListEvents, NULL); //cmddetail:{"name":"clearClockEvents","args":"", //cmddetail:"descr":"Removes all set clock events", - //cmddetail:"fn":"CMD_NTP_ClearEvents","file":"driver/drv_ntp_events.c","requires":"", + //cmddetail:"fn":"CMD_CLOCK_ClearEvents","file":"driver/drv_timed_events.c","requires":"", //cmddetail:"examples":""} - CMD_RegisterCommand("clearClockEvents", CMD_NTP_ClearEvents, NULL); - //CMD_RegisterCommand("addPeriodValue", CMD_NTP_AddPeriodValue, NULL); + CMD_RegisterCommand("clearClockEvents", CMD_CLOCK_ClearEvents, NULL); + //CMD_RegisterCommand("addPeriodValue", CMD_CLOCK_AddPeriodValue, NULL); } diff --git a/src/httpclient/http_get_header.c b/src/httpclient/http_get_header.c new file mode 100644 index 000000000..0fe258245 --- /dev/null +++ b/src/httpclient/http_get_header.c @@ -0,0 +1,175 @@ +#define _XOPEN_SOURCE 700 + +#include "lwip/inet.h" +#include +#include +#include +#include "lwip/sockets.h" +#include "lwip/tcp.h" +#include +#include "../cmnds/cmd_local.h" +#include "../logging/logging.h" +#include "../new_common.h" + + + +#define MAX_REQUEST_LEN 64 // 64 chars for the request line should be enough +#define TCP_PROTO 6 +#ifndef TCP_USER_TIMEOUT + #define TCP_USER_TIMEOUT 18 // how long for loss retry before timeout [ms] +#endif +#define DEFAULT_HOST "192.168.0.1" +// http takes some time until result is ready to process - so add an amount of seconds to the time received in header +#define DEFAULT_OFFSET 2 +static int offset=DEFAULT_OFFSET; +char request[MAX_REQUEST_LEN]; + +err_t myrecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + char outbuff[100]; + char tempout[1024]; + char gmt[4]; + gmt[3]='\0'; + char date[36]; + date[35]='\0'; + int found=0; + if (p != NULL) + { + pbuf_copy_partial(p, tempout, p->tot_len, 0); +//ADDLOG_INFO(LOG_FEATURE_CMD, " get_HTTP_Header / myreecv -- recv total %d this buffer %d next %d err %d\nContent:%s", p->tot_len, p->len, p->next, err, tempout); + +// this is what we will get as date line in header +// Date: Fri, 01 Mar 2024 18:40:30 GMT +// get pointer to start of "Date:" line + u16_t start = pbuf_memfind(p,"\r\nDate:",7,0); + if (start == 0xFFFF ) { + fprintf(stderr, "No Date found !!\n"); + pbuf_free(p); + tcp_close(pcb); + return(EXIT_FAILURE); + } + else { // if (start == 0xFFFF) --> Date found ... + // simple check: we know that start points to the beginning of "Date:" line + // we know it's length and that it ends with "GMT" + int conv = pbuf_copy_partial(p, date,35, start+2); + gmt[0]=date[32];gmt[1]=date[33]; gmt[2]=date[34]; + if (conv < 35 || strstr(date, "GMT") - date != 32 ){ + ADDLOG_ERROR(LOG_FEATURE_CMD, " get_HTTP_Header -- Date:-line found but format is unknown"); + fprintf(stderr, "Date line found, but unknown format [%s] - conv=%d -- gmt=%s - found at %i (values strstr(date, 'GMT')=%i AND date=%i)\n",date,conv,gmt,strstr(date, "GMT")-date,strstr(date, "GMT"),date); + pbuf_free(p); + tcp_close(pcb); + return(EXIT_FAILURE); + } + else { // if (conv != 2 || strcmp(gmt, "GMT") ) + struct tm mytime; + memset (&mytime,0,sizeof(struct tm)); + // we eliminated the "GMT" from the end of the "Date:" line + // Date: Sat, 02 Mar 2024 14:21:51 + /* + W800 doesn't know strptime - so let's do parsing by sscanf + strptime(date,"Date: %a, %d %b %Y %H:%M:%S",&mytime); + */ + static const char * const monname[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + char month[4]; + + sscanf(date, "Date: %*[^,], %d %3s %d %d:%d:%d", &mytime.tm_mday, month, &mytime.tm_year, &mytime.tm_hour, &mytime.tm_min, &mytime.tm_sec); + mytime.tm_year -= 1900; +// bk_printf("\r\n\r\nAfter sscanf - Date: mday=%d month=%3s year=%d Hour=%d Min=%d Sec=%d\r\n", mytime.tm_mday, month, mytime.tm_year, mytime.tm_hour, mytime.tm_min, mytime.tm_sec); + + for (int i = 0; i < 12; i++) { + if (strncmp (month, monname[i], 3) == 0) { + mytime.tm_mon = i; + break; + } + } + + strftime(outbuff,sizeof(outbuff),"%d %b %Y %H:%M:%S -- %s", &mytime); + + ADDLOG_INFO(LOG_FEATURE_CMD, " get_HTTP_Header -- output=%s",outbuff); + g_epochOnStartup = mktime(&mytime) - g_secondsElapsed + offset; + ADDLOG_INFO(LOG_FEATURE_CMD, " get_HTTP_Header -- Date found:%s -- calculated (GMT) epoch on startup:%i",date,g_epochOnStartup); + +// g_UTCoffset = 3600; + found=1; + tcp_recved(pcb, p->tot_len); + } // else { // if (conv != 2 || strcmp(gmt, "GMT") ) + }//else { // if (! start) --> Date found ... + } + else { // if (p != NULL) +// ADDLOG_INFO(LOG_FEATURE_CMD, " get_HTTP_Header -- in myreecv -- p == NULL"); +}; +if (p) pbuf_free(p); +tcp_close(pcb); + return ERR_OK; +} + + +err_t connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ +int sendbuff = tcp_sndbuf(pcb); + if (sendbuff){ + do { + err = tcp_write(pcb, request, strlen(request), 0); +// ADDLOG_INFO(LOG_FEATURE_CMD, " get_HTTP_Header -- in connected after write -- error=%i",err); +// bk_printf("\r\nerr: %d", err); + + } while (err == ERR_MEM); + + + err = tcp_output(pcb); + // ADDLOG_INFO(LOG_FEATURE_CMD, " get_HTTP_Header -- in connected after output -- error=%i",err); + } + else{ + ADDLOG_ERROR(LOG_FEATURE_CMD, " get_HTTP_Header -- error getting buffer space (sendbuff=%i)",sendbuff); + + } + + tcp_arg(pcb, NULL); + tcp_sent(pcb, NULL); + +return ERR_OK; +} + + +int get_HTTP_header(const char* hostname, unsigned short server_port) { + int request_len; + struct sockaddr_in sockaddr_in; + +// we only need the header to get date and time + request_len = snprintf(request, MAX_REQUEST_LEN, "HEAD / HTTP/1.1\r\nHost: %s\r\nConnection: Close\r\n\r\n", hostname); + if (request_len >= MAX_REQUEST_LEN) { + fprintf(stderr, "request length too large: %d\n", request_len); + exit(EXIT_FAILURE); + } + + struct tcp_pcb *pcb = tcp_new(); + tcp_recv(pcb, myrecv); + ip_addr_t ip; + ipaddr_aton(hostname,&ip); + + err_t err = tcp_connect(pcb, &ip, server_port, connected); + tcp_recv(pcb, myrecv); + + return 1; + } + + +// Can be called w/o any argument --> get header from actual gateway with default port and default offset +// or called with argument(s) IP [HTTP port] [offset in secs] +static commandResult_t CMD_GET_HTTP_Headertime(const void* context, const char* cmd, const char* args, int cmdFlags) { + + Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES | TOKENIZER_ALLOW_ESCAPING_QUOTATIONS); +//ADDLOG_INFO(LOG_FEATURE_CMD, " Tokenizer_GetArgsCount()=%i -- Tokenizer_GetArg(0)=%s -- Tokenizer_GetArgIntegerDefault(1, 80)=%i ",Tokenizer_GetArgsCount(),Tokenizer_GetArg(0),Tokenizer_GetArgIntegerDefault(1, 80)); + offset = Tokenizer_GetArgIntegerDefault(2, DEFAULT_OFFSET); + unsigned short port = (unsigned short)Tokenizer_GetArgIntegerDefault(1, 80); + ADDLOG_INFO(LOG_FEATURE_CMD, " CMD_GET_HTTP_Headertime%s%s - IP:%s port:%i Offset:%i",Tokenizer_GetArgsCount()==0 ? "" : " - args: ", args,Tokenizer_GetArgsCount()==0 ? HAL_GetMyGatewayString() : Tokenizer_GetArg(0), port, offset); + + get_HTTP_header(Tokenizer_GetArgsCount()==0 ? HAL_GetMyGatewayString() : Tokenizer_GetArg(0), port); +// get_HTTP_header(); + return CMD_RES_OK; +} + +int CMD_InitGetHeaderTime() { + CMD_RegisterCommand("getHeaderTime", CMD_GET_HTTP_Headertime, NULL); +} + diff --git a/src/httpserver/http_fns.c b/src/httpserver/http_fns.c index 869a8d5e8..e519dc8f0 100644 --- a/src/httpserver/http_fns.c +++ b/src/httpserver/http_fns.c @@ -17,6 +17,7 @@ #include "../cJSON/cJSON.h" #include #include "../driver/drv_ntp.h" +#include "../driver/drv_deviceclock.h" // to set clock via Javascript in pmntp #include "../driver/drv_local.h" static char SUBMIT_AND_END_FORM[] = "
"; @@ -39,7 +40,9 @@ static char SUBMIT_AND_END_FORM[] = "
url, "EPOCH", tmpA, sizeof(tmpA))) { + // atoi will only work on signed integers, we might get a higher value after 2038 , so use strtoul here + actepoch = (uint32_t)strtoul(tmpA,0,10); +// g_epochOnStartup = actepoch - g_secondsElapsed ; + CLOCK_setDeviceTime(actepoch); +//addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"PoormMansNTP - set g_epochOnStartup to %u -- got actepoch=%u secondsElapsed=%u!! \n",g_epochOnStartup,actepoch, g_secondsElapsed); + } + if (http_getArg(request->url, "OFFSET", tmpA, sizeof(tmpA)) && actepoch != 0 ) { +#if ENABLE_LOCAL_CLOCK_ADVANCED + // if actual time is during DST period, javascript will return + // an offset including the one additional hour of DST + // if this is the case, set g_DSToffset (in testNsetDST) and reduce the + // offset to the offset from timesone (subtract offset seconds) + g_UTCoffset = testNsetDST(actepoch)==1 ? atoi(tmpA)-g_DSToffset : atoi(tmpA); + //addLogAdv(LOG_INFO, LOG_FEATURE_HTTP,"PoormMansNTP - set g_UTCoffset to %u -- got offset=%i -- next switch at %u!! \n",g_UTCoffset,atoi(tmpA),g_next_dst_change); +#else + // don't care about daylight saving time + g_UTCoffset = atoi(tmpA); +#endif + } + poststr(request, "HTTP/1.1 302 OK\nLocation: /index\nConnection: close\n\n"); + poststr(request, NULL); + return 0; +} +#endif // to #if ENABLE_LOCAL_CLOCK + // bit mask telling which channels are hidden from HTTP // If given bit is set, then given channel is hidden extern int g_hiddenChannels; @@ -772,7 +808,7 @@ int http_fn_index(http_request_t* request) { // display temperature - thanks to giedriuslt // only in Normal mode, and if boot is not failing - hprintf255(request, "
Internal temperature: %.1f°C
", g_wifi_temperature); + hprintf255(request, "
Chip temperature: %.1f°C
", g_wifi_temperature); inputName = CFG_GetPingHost(); if (inputName && *inputName && CFG_GetPingDisconnectedSecondsToRestart()) { @@ -841,6 +877,30 @@ typedef enum { s = "Wdt"; hprintf255(request, "
Reboot reason: %i - %s
", g_rebootReason, s); } +#elif PLATFORM_ESPIDF + esp_reset_reason_t reason = esp_reset_reason(); + const char* s = "Unknown"; + switch(reason) + { + case ESP_RST_UNKNOWN: s = "ESP_RST_UNKNOWN"; break; + case ESP_RST_POWERON: s = "ESP_RST_POWERON"; break; + case ESP_RST_EXT: s = "ESP_RST_EXT"; break; + case ESP_RST_SW: s = "ESP_RST_SW"; break; + case ESP_RST_PANIC: s = "ESP_RST_PANIC"; break; + case ESP_RST_INT_WDT: s = "ESP_RST_INT_WDT"; break; + case ESP_RST_TASK_WDT: s = "ESP_RST_TASK_WDT"; break; + case ESP_RST_WDT: s = "ESP_RST_WDT"; break; + case ESP_RST_DEEPSLEEP: s = "ESP_RST_DEEPSLEEP"; break; + case ESP_RST_BROWNOUT: s = "ESP_RST_BROWNOUT"; break; + case ESP_RST_SDIO: s = "ESP_RST_SDIO"; break; + case ESP_RST_USB: s = "ESP_RST_USB"; break; + case ESP_RST_JTAG: s = "ESP_RST_JTAG"; break; + case ESP_RST_EFUSE: s = "ESP_RST_EFUSE"; break; + case ESP_RST_PWR_GLITCH: s = "ESP_RST_PWR_GLITCH"; break; + case ESP_RST_CPU_LOCKUP: s = "ESP_RST_CPU_LOCKUP"; break; + default: break; + } + hprintf255(request, "
Reboot reason: %i - %s
", reason, s); #endif if (CFG_GetMQTTHost()[0] == 0) { hprintf255(request, "
MQTT State: not configured
"); @@ -911,6 +971,10 @@ typedef enum { }; #endif +#if ENABLE_LOCAL_CLOCK + hprintf255(request, "",Clock_IsTimeSynced()? Clock_GetCurrentTimeWithoutOffset()-g_secondsElapsed : 1); +// hprintf255(request, "",Clock_IsTimeSynced()? Clock_GetDeviceTimeWithoutOffset()-g_secondsElapsed : 1); +#endif #if WINDOWS #elif PLATFORM_BL602 @@ -1207,7 +1271,7 @@ int http_fn_cfg_wifi(http_request_t* request) { if(bChanged) { poststr(request,"

Device will reconnect after restarting

"); }*/ - poststr(request, "

Check networks reachable by module

This will lag few seconds.
"); + poststr(request, "

Check networks reachable by module

This will take a few seconds
"); if (http_getArg(request->url, "scan", tmpA, sizeof(tmpA))) { #ifdef WINDOWS @@ -1258,6 +1322,20 @@ int http_fn_cfg_wifi(http_request_t* request) { #elif PLATFORM_LN882H // TODO:LN882H action poststr(request, "TODO LN882H
"); +#elif PLATFORM_ESPIDF + // doesn't work in ap mode, only sta/apsta + uint16_t ap_count = 0, number = 30; + wifi_ap_record_t ap_info[number]; + memset(ap_info, 0, sizeof(ap_info)); + bk_printf("Scan begin...\r\n"); + esp_wifi_scan_start(NULL, true); + esp_wifi_scan_get_ap_num(&ap_count); + bk_printf("Scan returned %i networks, max allowed: %i\r\n", ap_count, number); + esp_wifi_scan_get_ap_records(&number, ap_info); + for(int i = 0; i < number; i++) + { + hprintf255(request, "[%i/%u] SSID: %s, Channel: %i, Signal %i
", i + 1, number, ap_info[i].ssid, ap_info[i].primary, ap_info[i].rssi); + } #else #error "Unknown platform" poststr(request, "Unknown platform
"); @@ -1265,19 +1343,22 @@ int http_fn_cfg_wifi(http_request_t* request) { } poststr(request, "
\ \ -\ +\
"); poststr_h4(request, "Use this to disconnect from your WiFi"); poststr(request, "
\ \ -\ +\
"); poststr_h2(request, "Use this to connect to your WiFi"); add_label_text_field(request, "SSID", "ssid", CFG_GetWiFiSSID(), "
"); - add_label_password_field(request, "", "pass", CFG_GetWiFiPass(), "
Password enable clear text password (clears password)"); + add_label_password_field(request, "", "pass", CFG_GetWiFiPass(), "
Password enable clear text password (clears existing)"); poststr_h2(request, "Alternate WiFi (used when first one is not responding)"); +#ifndef PLATFORM_BEKEN + poststr_h2(request, "SSID2 only on Beken Platform (BK7231T, BK7231N)"); +#endif add_label_text_field(request, "SSID2", "ssid2", CFG_GetWiFiSSID2(), ""); - add_label_password_field(request, "", "pass2", CFG_GetWiFiPass2(), "
Password2 enable clear text password (clears password)"); + add_label_password_field(request, "", "pass2", CFG_GetWiFiPass2(), "
Password2 enable clear text password (clears existing)"); #if ALLOW_WEB_PASSWORD int web_password_enabled = strcmp(CFG_GetWebPassword(), "") == 0 ? 0 : 1; poststr_h2(request, "Web Authentication"); @@ -1287,7 +1368,7 @@ int http_fn_cfg_wifi(http_request_t* request) { add_label_password_field(request, "Admin Password", "web_admin_password", CFG_GetWebPassword(), ""); #endif poststr(request, "

\ -\ +\
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); @@ -1905,6 +1986,7 @@ void doHomeAssistantDiscovery(const char* topic, http_request_t* request) { dev_info = hass_init_sensor_device_info(ILLUMINANCE_SENSOR, i, -1, -1, 1); } break; + case ChType_Custom: case ChType_ReadOnly: { dev_info = hass_init_sensor_device_info(CUSTOM_SENSOR, i, -1, -1, 1); @@ -2414,13 +2496,19 @@ int http_fn_cfg(http_request_t* request) { postFormAction(request, "cfg_mqtt", "Configure MQTT"); postFormAction(request, "cfg_name", "Configure Names"); postFormAction(request, "cfg_mac", "Change MAC"); - postFormAction(request, "cfg_ping", "Ping Watchdog (Network lost restarter)"); - postFormAction(request, "cfg_webapp", "Configure Webapp"); + postFormAction(request, "cfg_ping", "Ping Watchdog (network lost restarter)"); + postFormAction(request, "cfg_webapp", "Configure WebApp"); postFormAction(request, "ha_cfg", "Home Assistant Configuration"); postFormAction(request, "ota", "OTA (update software by WiFi)"); - postFormAction(request, "cmd_tool", "Execute custom command"); + postFormAction(request, "cmd_tool", "Execute Custom Command"); //postFormAction(request, "flash_read_tool", "Flash Read Tool"); - postFormAction(request, "startup_command", "Change startup command text"); + postFormAction(request, "startup_command", "Change Startup Command Text"); + postFormAction(request, "startup_command", "Change Startup Command Text"); +#if ENABLE_LOCAL_CLOCK + poststr(request, "
\ + \ +
"); +#endif #if 0 #if PLATFORM_BK7231T | PLATFORM_BK7231N @@ -2755,7 +2843,7 @@ int http_fn_cfg_generic(http_request_t* request) { poststr(request, ""); poststr(request, SUBMIT_AND_END_FORM); - add_label_numeric_field(request, "Uptime seconds required to mark boot as OK", "boot_ok_delay", + add_label_numeric_field(request, "Uptime in seconds required to mark boot as OK", "boot_ok_delay", CFG_GetBootOkSeconds(), "
"); poststr(request, "
"); @@ -2926,7 +3014,7 @@ void OTA_RequestDownloadFromHTTP(const char* s) { #elif PLATFORM_LN882H - +#elif PLATFORM_ESPIDF #elif PLATFORM_W600 || PLATFORM_W800 t_http_fwup(s); #elif PLATFORM_XR809 diff --git a/src/httpserver/http_fns.h b/src/httpserver/http_fns.h index f5ba95ac1..46170d813 100644 --- a/src/httpserver/http_fns.h +++ b/src/httpserver/http_fns.h @@ -33,3 +33,4 @@ int http_fn_startup_command(http_request_t* request); int http_fn_cfg_generic(http_request_t* request); int http_fn_cfg_startup(http_request_t* request); int http_fn_cfg_dgr(http_request_t* request); +int http_fn_pmntp(http_request_t* request); diff --git a/src/httpserver/json_interface.c b/src/httpserver/json_interface.c index d3993e21b..4ebb2deed 100644 --- a/src/httpserver/json_interface.c +++ b/src/httpserver/json_interface.c @@ -18,6 +18,7 @@ #include "../cJSON/cJSON.h" #include #include "../driver/drv_ntp.h" +#include "../driver/drv_deviceclock.h" #include "../driver/drv_local.h" #include "../driver/drv_bl_shared.h" @@ -347,7 +348,7 @@ static int http_tasmota_json_status_SNS(void* request, jsonCb_t printer, bool bA } printer(request, "{"); - time_t localTime = (time_t)NTP_GetCurrentTime(); + time_t localTime = (time_t)Clock_GetCurrentTime(); format_date(buff, sizeof(buff), gmtime(&localTime)); JSON_PrintKeyValue_String(request, printer, "Time", buff, false); @@ -384,14 +385,9 @@ static int http_tasmota_json_status_SNS(void* request, jsonCb_t printer, bool bA //XR809 does not support drivers but its build script compiles many drivers including ntp. #else -#ifndef ENABLE_NTP -unsigned int NTP_GetCurrentTime() { - return 0; -} -unsigned int NTP_GetCurrentTimeWithoutOffset() { - return 0; -} -#endif +// replaced "NTP_GetCurrentTime()" and "NTP_GetCurrentTimeWithoutOffset()" +// with "Clock_GetCurrentTime()" and "Clock_GetCurrentTimeWithoutOffset" +// (with this version now defined in new_common), so no more need for a workaround here... #endif // Topic: tele/tasmota_48E7F3/STATE @@ -432,7 +428,7 @@ void format_time(int total_seconds, char* output, int outLen) { static int http_tasmota_json_status_STS(void* request, jsonCb_t printer, bool bAppendHeader) { char buff[20]; - time_t localTime = (time_t)NTP_GetCurrentTime(); + time_t localTime = (time_t)Clock_GetCurrentTime(); if (bAppendHeader) { printer(request, "\"StatusSTS\":"); @@ -473,8 +469,8 @@ static int http_tasmota_json_status_STS(void* request, jsonCb_t printer, bool bA static int http_tasmota_json_status_TIM(void* request, jsonCb_t printer) { char buff[20]; - time_t localTime = (time_t)NTP_GetCurrentTime(); - time_t localUTC = (time_t)NTP_GetCurrentTimeWithoutOffset(); + time_t localTime = (time_t)Clock_GetCurrentTime(); + time_t localUTC = (time_t)Clock_GetCurrentTimeWithoutOffset(); printer(request, "\"StatusTIM\":{"); format_date(buff, sizeof(buff), gmtime(&localUTC)); JSON_PrintKeyValue_String(request, printer, "UTC", buff, true); @@ -690,8 +686,8 @@ static int http_tasmota_json_status_generic(void* request, jsonCb_t printer) { JSON_PrintKeyValue_Int(request, printer, "Uptime", g_secondsElapsed, true); struct tm* ltm; time_t ntpTime = 0; // if no NTP_time set, we will not change this value, but just stick to 0 and hence "fake" start of epoch 1970-01-01T00:00:00 - if (NTP_GetCurrentTimeWithoutOffset() > g_secondsElapsed) { // would be negative else, leading to unwanted results when converted to (unsigned) time_t - ntpTime = (time_t)NTP_GetCurrentTimeWithoutOffset() - (time_t)g_secondsElapsed; + if (Clock_GetCurrentTimeWithoutOffset() > g_secondsElapsed) { // would be negative else, leading to unwanted results when converted to (unsigned) time_t + ntpTime = (time_t)Clock_GetCurrentTimeWithoutOffset() - (time_t)g_secondsElapsed; } ltm = gmtime(&ntpTime); if (ltm != 0) { diff --git a/src/httpserver/new_http.c b/src/httpserver/new_http.c index 95e7786cb..620c289ee 100644 --- a/src/httpserver/new_http.c +++ b/src/httpserver/new_http.c @@ -262,12 +262,23 @@ void http_html_start(http_request_t* request, const char* pagename) { poststr(request, htmlBodyStart); poststr(request, CFG_GetDeviceName()); poststr(request, htmlBodyStart2); +#if ENABLE_LOCAL_CLOCK + // print HTML element holding time if its not the "state" page, which will contain the data to be refreshed on every request + if(pagename) hprintf255(request, "",Clock_IsTimeSynced()? Clock_GetCurrentTimeWithoutOffset()-g_secondsElapsed : 1); +// if(pagename) hprintf255(request, "",Clock_IsTimeSynced()? Clock_GetDeviceTimeWithoutOffset()-g_secondsElapsed : 1); +#endif } const char pageScriptPart1[] = ""; +const char pageScriptPart3[] = ")}function fmtUpTime(e){var t,n,o=Math.floor(e/86400);return e%=86400,t=Math.floor(e/3600),e%=3600,n=Math.floor(e/60),e=e%60,0set to browser time'" +//",u=Number(getElement(\"utc1\").dataset.utc),getElement(\"DT1\").innerHTML=10{var e=getElement(\"changed\");e&&(e.innerHTML=\"\")},5e3);"; void http_html_end(http_request_t* request) { @@ -280,7 +291,11 @@ void http_html_end(http_request_t* request) { poststr(request, g_build_str); hprintf255(request, "
Online for -", g_secondsElapsed); - +#if ENABLE_LOCAL_CLOCK + poststr(request, "
Device time: -"); +// t=Clock_IsTimeSynced()? Clock_GetDeviceTime() : 0; +// hprintf255(request, "
Local Device time: %s",(t != 0) ? ctime(&t) : "-"); +#endif WiFI_GetMacAddress((char*)mac); snprintf(upTimeStr, sizeof(upTimeStr), "
Device MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -790,7 +805,9 @@ int HTTP_ProcessPacket(http_request_t* request) { if (http_checkUrlBase(urlStr, "ota")) return http_fn_ota(request); if (http_checkUrlBase(urlStr, "ota_exec")) return http_fn_ota_exec(request); if (http_checkUrlBase(urlStr, "cm")) return http_fn_cm(request); - +#if ENABLE_LOCAL_CLOCK + if (http_checkUrlBase(urlStr, "pmntp")) return http_fn_pmntp(request); // poor mans NTP +#endif return http_fn_other(request); } diff --git a/src/httpserver/script.js b/src/httpserver/script.js index 211c53490..64d6a711e 100644 --- a/src/httpserver/script.js +++ b/src/httpserver/script.js @@ -64,8 +64,15 @@ function fmtUpTime(totalSeconds) { function updateOnlineFor() { onlineForEl.textContent = fmtUpTime(++onlineFor); + getElement("DT").innerHTML = utc > 10 ? new Date((utc+onlineFor)*1e3) : 'unset set to browser time'; } +function PoorMansNTP() { + var d=new Date(); + d.getTime(); + return '/pmntp?EPOCH=' + parseInt(d/1000) + '&OFFSET=' + d.getTimezoneOffset()* - 60; +}; + function onLoad() { onlineForEl = getElement("onlineFor"); if (onlineForEl) { @@ -74,7 +81,6 @@ function onLoad() { setInterval(updateOnlineFor, 1000); } } - showState(); } diff --git a/src/mqtt/new_mqtt.c b/src/mqtt/new_mqtt.c index 213975dbf..bc53035a8 100644 --- a/src/mqtt/new_mqtt.c +++ b/src/mqtt/new_mqtt.c @@ -9,6 +9,7 @@ #include "../hal/hal_wifi.h" #include "../driver/drv_public.h" #include "../driver/drv_ntp.h" +#include "../driver/drv_deviceclock.h" #include "../driver/drv_tuyaMCU.h" #include "../ota/ota.h" #ifndef WINDOWS @@ -1907,19 +1908,22 @@ OBK_Publish_Result MQTT_DoItemPublish(int idx) case PUBLISHITEM_SELF_DATETIME: - //Drivers are only built on BK7231 chips +// Clock_GetCurrentTime() is allways present +/* //Drivers are only built on BK7231 chips #ifndef OBK_DISABLE_ALL_DRIVERS + if (DRV_IsRunning("NTP")) { - sprintf(dataStr, "%d", NTP_GetCurrentTime()); +*/ + sprintf(dataStr, "%ld", Clock_GetCurrentTime()); return MQTT_DoItemPublishString("datetime", dataStr); - } +/* } else { return OBK_PUBLISH_WAS_NOT_REQUIRED; } #else return OBK_PUBLISH_WAS_NOT_REQUIRED; #endif - +*/ case PUBLISHITEM_SELF_SOCKETS: sprintf(dataStr, "%d", LWIP_GetActiveSockets()); return MQTT_DoItemPublishString("sockets", dataStr); diff --git a/src/new_cfg.c b/src/new_cfg.c index 23ce98e44..e589ed519 100644 --- a/src/new_cfg.c +++ b/src/new_cfg.c @@ -11,6 +11,9 @@ #if ENABLE_LITTLEFS #include "littlefs/our_lfs.h" #endif +#if ENABLE_LOCAL_CLOCK_ADVANCED +#include "driver/drv_deviceclock.h" +#endif #define DEFAULT_BOOT_SUCCESS_TIME 5 @@ -32,6 +35,30 @@ int g_cfg_pendingChanges = 0; #define MAIN_CFG_VERSION 5 #endif + +#if ENABLE_LOCAL_CLOCK_ADVANCED +/// default set to EU TZ and DST +#define CLOCK_SETTINGS_DEFAULT 111728867327873328 +/* +//this uint64_t value equals to +union DST g_clocksettings={ +.DST_H = 0, +.DST_Ws = 0, +.DST_Ms = 3, +.DST_Ds = 1, +.DST_hs = 2, +.Tstd = 60, +.DST_We = 0, +.DST_Me = 10, +.DST_De = 1, +.DST_he = 3, +.Tdst = 60, +.TZ = 99 // use DST +}; +*/ +#endif + + static byte CFG_CalcChecksum(mainConfig_t *inf) { int header_size; int remaining_size; @@ -154,6 +181,10 @@ void CFG_SetDefaultConfig() { #endif strcpy(g_cfg.ntpServer, DEFAULT_NTP_SERVER); +#if ENABLE_LOCAL_CLOCK_ADVANCED + g_cfg.CLOCK_SETTINGS = CLOCK_SETTINGS_DEFAULT; + g_clocksettings.value = CLOCK_SETTINGS_DEFAULT; +#endif // default value is 5, which means 500ms @@ -705,6 +736,21 @@ uint32_t CFG_GetLFS_Size() { return size; } #endif +#if ENABLE_LOCAL_CLOCK_ADVANCED +void CFG_SetCLOCK_SETTINGS(uint64_t value) { + if(g_cfg.CLOCK_SETTINGS != value) { + g_cfg.CLOCK_SETTINGS = value; + g_cfg_pendingChanges++; + } +} +uint64_t CFG_GetCLOCK_SETTINGS() { + uint64_t conf = g_cfg.CLOCK_SETTINGS; + if (conf == 0){ + conf = CLOCK_SETTINGS_DEFAULT; + } + return conf; +} +#endif void CFG_InitAndLoad() { byte chkSum; diff --git a/src/new_cfg.h b/src/new_cfg.h index ec1bcc12a..63e959216 100644 --- a/src/new_cfg.h +++ b/src/new_cfg.h @@ -96,6 +96,10 @@ void CFG_SetWebPassword(const char *s); void CFG_SetLFS_Size(uint32_t value); uint32_t CFG_GetLFS_Size(); #endif +#if ENABLE_LOCAL_CLOCK_ADVANCED + void CFG_SetCLOCK_SETTINGS(uint64_t value); + uint64_t CFG_GetCLOCK_SETTINGS(); +#endif #endif diff --git a/src/new_common.c b/src/new_common.c index b56030f2d..921ad3121 100644 --- a/src/new_common.c +++ b/src/new_common.c @@ -1,6 +1,13 @@ #include "new_common.h" +#if ENABLE_LOCAL_CLOCK_ADVANCED +#include "new_cfg.h" // for CFG_SetCLOCK_SETTINGS() - used in CMD_CLOCK_SetConfig() +#include +// Commands register, execution API and cmd tokenizer +#include "cmnds/cmd_public.h" +#endif #include #include +#include "logging/logging.h" const char *str_rssi[] = { "N/A", "Weak", "Fair", "Good", "Excellent" }; @@ -62,7 +69,7 @@ int vsprintf3(char *buffer, const char *fmt, va_list val) { #endif -#if WINDOWS +#if WINDOWS || PLATFORM_W800 const char* strcasestr(const char* str1, const char* str2) { const char* p1 = str1; @@ -345,3 +352,51 @@ WIFI_RSSI_LEVEL wifi_rssi_scale(int8_t rssi_value) return retVal; } + +// functions for adjusting "g_secondsElapsed" to rtos tics + +// g_secondsElapsed is drifting of after some time (for me it was ~ 1 to 2 minutes (!) a day) +// when using rtos ticks, it was reduced to 1 to 2 seconds(!) a day +// if we want to use this for emulating an RTC, we should get the time as good as possible + +#ifndef WINDOWS +TickType_t lastTick=0; +// it's a pitty we cant use rtos' "xNumOfOverflows" here, but its not accessable, so we need to take care of owerflows here +// a 32 bit TickType_t counter of ms will rollover after (4294836225÷1000÷3600÷24=49,7088) ~ 49,7 days we should expect uptimes +// bigger than this value! +// +// I don't expect uptime > 35 years, so uint8_t could be sufficient if TickType_t is 32 bit ( 255×4294967295 ms = 1,09521666×10¹² ms = 1095216660 s ~ 12676 days ~ 34,7 years) +// but just to be sure use uint16_t (65535 x 4294967295 ms = 2,814706817×10¹⁴ ms = 281470681677,825 s ~ 3257762 days ~ 8919 years) +// if TickType_t is also uint16_t it will last for approx 50 days, while uint8_t will rollover after 4,65 hours !! +uint16_t timer_rollovers=0; + + +uint32_t getSecondsElapsed(){ + + // xTicks are not bound to be in ms, + // but for all plattforms xTicks can be converted to MS with "portTICK_RATE_MS" + + TickType_t actTick=portTICK_RATE_MS*xTaskGetTickCount(); + + // to make this work, getSecondsElapsed() must be called once before rollover, which is + // no problem for the usual choice of TickType_t = uint32_t: + // rollover will take place after 4294967295 ms (almost 50 days) + // but it might be a more of challenge for uint16_t its with only 65535 ms (one Minute and 5 seconds)!! + if (actTick < lastTick ){ + timer_rollovers++; + ADDLOG_INFO(LOG_FEATURE_RAW, "\r\n\r\nCLOCK: Rollover of tick counter! Actual value of timer_rollovers=%u \r\n\r\n",timer_rollovers); + + } + lastTick = actTick; + // + // version 1 : + // use the time also to adjust g_secondsElapsed + g_secondsElapsed = (uint32_t)(((uint64_t) timer_rollovers << (sizeof(TickType_t)*8) | actTick) / 1000 ); + return g_secondsElapsed; + // + // possible version 2 : + // without adjusting g_secondsElapsed : + // return (uint32_t)(((uint64_t) timer_rollovers << 32 | actTick) / 1000 ); +} +#endif + diff --git a/src/new_common.h b/src/new_common.h index fe9027f0e..294d79594 100644 --- a/src/new_common.h +++ b/src/new_common.h @@ -318,7 +318,9 @@ typedef void *beken_thread_arg_t; typedef void *beken_thread_t; typedef void (*beken_thread_function_t)( beken_thread_arg_t arg ); typedef int OSStatus; - +#ifdef PLATFORM_W600 + typedef portTickType TickType_t; // W600/W800: xTaskGetTickCount() is of type "portTickType", all others "TickType_t" , W600 has no definition for TickType_t +#endif #define BEKEN_DEFAULT_WORKER_PRIORITY (6) #define BEKEN_APPLICATION_PRIORITY (7) @@ -334,6 +336,8 @@ OSStatus rtos_create_thread( beken_thread_t* thread, // TODO:LN882H Platform setup here. #include +#include +#include #define ASSERT #define os_strcpy strcpy @@ -456,9 +460,14 @@ int PingWatchDog_GetTotalReceived(); int LWIP_GetMaxSockets(); int LWIP_GetActiveSockets(); +// for Beken, you will get "conflicting types" else: +///OpenBK7231T_App/sdk/OpenBK7231N/platforms/bk7231n/toolchain/gcc-arm-none-eabi-4_9-2015q1/arm-none-eabi/include/sys/unistd.h:198:6: note: previous declaration of 'usleep' was here +// int _EXFUN(usleep, (useconds_t __useconds)); +// +#if ! ( PLATFORM_BEKEN && ENABLE_HTTP_HEADER_TIME) //delay function do 10*r nops, because rtos_delay_milliseconds is too much void usleep(int r); - +#endif #define RESTARTS_REQUIRED_FOR_SAFE_MODE 4 // linear mapping function --> https://www.arduino.cc/reference/en/language/functions/math/map/ @@ -496,6 +505,18 @@ extern int g_bootFailures; extern int g_secondsElapsed; extern int g_rebootReason; extern float g_wifi_temperature; +uint32_t getSecondsElapsed(); + +#if ENABLE_LOCAL_CLOCK +// some variables and functions for handling device time even without NTP driver present +// vars will be initialised in new_common.c +// "eoch" on startup of device; If we add g_secondsElapsed we get the actual time +extern uint32_t g_epochOnStartup ; +// UTC offset +extern int g_UTCoffset; +extern int g_DSToffset; +extern uint32_t g_next_dst_change; +#endif typedef int(*jsonCb_t)(void *userData, const char *fmt, ...); #if ENABLE_TASMOTA_JSON diff --git a/src/new_pins.h b/src/new_pins.h index e2cbcdf41..c53417bbf 100644 --- a/src/new_pins.h +++ b/src/new_pins.h @@ -1267,12 +1267,16 @@ typedef struct mainConfig_s { byte unused_fill1; // offset 0x000004BC - unsigned long LFS_Size; // szie of LFS volume. it's aligned against the end of OTA + unsigned long LFS_Size; // size of LFS volume. it's aligned against the end of OTA int loggerFlags; + // offset 0x000004C4 + int unused_fill2; + // offset 0x000004C8 - needed for 8 byte boundary + uint64_t CLOCK_SETTINGS; // 64 bits/8 bytes: Clock settings like timezone and DST - using "value" of union ST for setting all parameters at once #if PLATFORM_W800 - byte unusedSectorAB[51]; + byte unusedSectorAB[39]; // was 51 before adding CLOCK_SETTINGS - added 12 bytes #else - byte unusedSectorAB[99]; + byte unusedSectorAB[87]; // was 99 before adding CLOCK_SETTINGS - added 12 bytes #endif obkStaticIP_t staticIP; ledRemap_t ledRemap; diff --git a/src/obk_config.h b/src/obk_config.h index 9e9753fbf..3e278b21f 100644 --- a/src/obk_config.h +++ b/src/obk_config.h @@ -19,6 +19,11 @@ #define OBK_DISABLE_ALL_DRIVERS 1 +// test for local clock +#define ENABLE_LOCAL_CLOCK_ADVANCED 0 +#define ENABLE_LOCAL_CLOCK 0 + + #elif PLATFORM_W600 // Some limited drivers are supported on W600, OBK_DISABLE_ALL_DRIVERS is not defined @@ -27,6 +32,9 @@ #define ENABLE_DRIVER_BL0937 1 #define ENABLE_DRIVER_DHT 1 #define ENABLE_TASMOTA_JSON 1 +// test for local clock +#define ENABLE_LOCAL_CLOCK_ADVANCED 0 +#define ENABLE_LOCAL_CLOCK 0 #elif PLATFORM_W800 @@ -34,7 +42,14 @@ //#define OBK_DISABLE_ALL_DRIVERS 1 #define ENABLE_TASMOTA_JSON 1 #define ENABLE_DRIVER_DS1820 1 - +// test for local clock +#define ENABLE_LOCAL_CLOCK_ADVANCED 1 +#define ENABLE_LOCAL_CLOCK 1 +#define ENABLE_HTTP_HEADER_TIME 1 +#define ENABLE_CLOCK_SUNRISE_SUNSET 1 +#define ENABLE_CALENDAR_EVENTS 1 +// parse things like $CH1 or $hour etc +#define ENABLE_EXPAND_CONSTANT 1 #elif WINDOWS @@ -62,7 +77,7 @@ #define ENABLE_DRIVER_PT6523 1 #define ENABLE_DRIVER_MAX6675 1 #define ENABLE_DRIVER_TEXTSCROLLER 1 -#define ENABLE_NTP_SUNRISE_SUNSET 1 +#define ENABLE_CLOCK_SUNRISE_SUNSET 1 // parse things like $CH1 or $hour etc #define ENABLE_EXPAND_CONSTANT 1 #define ENABLE_DRIVER_DHT 1 @@ -78,6 +93,11 @@ #define ENABLE_DRIVER_IR2 0 #define ENABLE_DRIVER_CHARTS 1 +// test for local clock +#define ENABLE_LOCAL_CLOCK_ADVANCED 1 +#define ENABLE_LOCAL_CLOCK 1 + + #elif PLATFORM_BL602 // I have enabled drivers on BL602 @@ -100,13 +120,18 @@ #define ENABLE_DRIVER_CHT83XX 1 #define ENABLE_DRIVER_DS1820 1 +// test for local clock +#define ENABLE_LOCAL_CLOCK_ADVANCED 0 +#define ENABLE_LOCAL_CLOCK 0 + + #elif PLATFORM_BEKEN // set to 0 to disable #define ENABLE_TASMOTADEVICEGROUPS 1 #define ENABLE_LITTLEFS 1 -#define ENABLE_NTP 1 -#define ENABLE_NTP_SUNRISE_SUNSET 1 +//#define ENABLE_NTP 1 +#define ENABLE_CLOCK_SUNRISE_SUNSET 1 #define ENABLE_DRIVER_LED 1 #define ENABLE_DRIVER_BL0937 1 #define ENABLE_DRIVER_BL0942 1 @@ -150,6 +175,11 @@ #define ENABLE_DRIVER_DS1820 1 #define ENABLE_DRIVER_CHT83XX 1 +// test for local clock +#define ENABLE_LOCAL_CLOCK_ADVANCED 1 +#define ENABLE_LOCAL_CLOCK 1 +#define ENABLE_HTTP_HEADER_TIME 1 + #elif PLATFORM_LN882H //#define OBK_DISABLE_ALL_DRIVERS 1 @@ -166,6 +196,15 @@ //#define ENABLE_DRIVER_TMGN 1 #define ENABLE_TASMOTA_JSON 1 #define ENABLE_DRIVER_DS1820 1 +// test for local clock +#define ENABLE_LOCAL_CLOCK_ADVANCED 1 +#define ENABLE_LOCAL_CLOCK 1 + +#define ENABLE_HTTP_HEADER_TIME 1 +#define ENABLE_CLOCK_SUNRISE_SUNSET 1 +#define ENABLE_CALENDAR_EVENTS 1 +// parse things like $CH1 or $hour etc +#define ENABLE_EXPAND_CONSTANT 1 #else diff --git a/src/selftest/selftest_clockEvents.c b/src/selftest/selftest_clockEvents.c index 237535064..09470493a 100644 --- a/src/selftest/selftest_clockEvents.c +++ b/src/selftest/selftest_clockEvents.c @@ -4,8 +4,8 @@ #include "../driver/drv_ntp.h" static void ResetEventsAndChannels(int eventsCleared) { - SELFTEST_ASSERT(NTP_ClearEvents() == eventsCleared); - SELFTEST_ASSERT(NTP_PrintEventList() == 0); + SELFTEST_ASSERT(CLOCK_ClearEvents() == eventsCleared); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 0); CMD_ExecuteCommand("setChannel 1 0", 0); CMD_ExecuteCommand("setChannel 2 0", 0); @@ -21,64 +21,64 @@ void Test_ClockEvents() { CMD_ExecuteCommand("startDriver NTP", 0); - SELFTEST_ASSERT(NTP_PrintEventList() == 0); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 0); CMD_ExecuteCommand("addClockEvent 15:30:00 0xff 123 POWER0 ON", 0); // now there is 1 - SELFTEST_ASSERT(NTP_PrintEventList() == 1); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 1); // none removed - SELFTEST_ASSERT(NTP_RemoveClockEvent(1245) == 0); + SELFTEST_ASSERT(CLOCK_RemoveEvent(1245) == 0); // one removed - SELFTEST_ASSERT(NTP_RemoveClockEvent(123) == 1); + SELFTEST_ASSERT(CLOCK_RemoveEvent(123) == 1); // now there is 0 - SELFTEST_ASSERT(NTP_PrintEventList() == 0); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 0); CMD_ExecuteCommand("addClockEvent 15:30:00 0xff 1001 POWER0 ON", 0); // now there is 1 - SELFTEST_ASSERT(NTP_PrintEventList() == 1); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 1); CMD_ExecuteCommand("addClockEvent 15:30:00 0xff 1002 POWER0 ON", 0); // now there is 2 - SELFTEST_ASSERT(NTP_PrintEventList() == 2); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 2); CMD_ExecuteCommand("addClockEvent 15:30:00 0xff 1003 POWER0 ON", 0); // now there is 3 - SELFTEST_ASSERT(NTP_PrintEventList() == 3); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 3); CMD_ExecuteCommand("addClockEvent 15:30:00 0xff 1004 POWER0 ON", 0); // now there is 4 - SELFTEST_ASSERT(NTP_PrintEventList() == 4); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 4); // one removed - SELFTEST_ASSERT(NTP_RemoveClockEvent(1004) == 1); + SELFTEST_ASSERT(CLOCK_RemoveEvent(1004) == 1); // now there is 3 - SELFTEST_ASSERT(NTP_PrintEventList() == 3); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 3); // one removed - SELFTEST_ASSERT(NTP_RemoveClockEvent(1003) == 1); + SELFTEST_ASSERT(CLOCK_RemoveEvent(1003) == 1); // now there is 2 - SELFTEST_ASSERT(NTP_PrintEventList() == 2); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 2); CMD_ExecuteCommand("addClockEvent 15:30:00 0xff 1005 POWER0 ON", 0); // now there is 3 - SELFTEST_ASSERT(NTP_PrintEventList() == 3); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 3); CMD_ExecuteCommand("addClockEvent 15:30:00 0xff 1006 POWER0 ON", 0); // now there is 4 - SELFTEST_ASSERT(NTP_PrintEventList() == 4); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 4); // one removed - SELFTEST_ASSERT(NTP_RemoveClockEvent(1005) == 1); + SELFTEST_ASSERT(CLOCK_RemoveEvent(1005) == 1); // now there is 3 - SELFTEST_ASSERT(NTP_PrintEventList() == 3); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 3); // one removed - SELFTEST_ASSERT(NTP_RemoveClockEvent(1001) == 1); + SELFTEST_ASSERT(CLOCK_RemoveEvent(1001) == 1); // now there is 2 - SELFTEST_ASSERT(NTP_PrintEventList() == 2); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 2); // one removed - SELFTEST_ASSERT(NTP_RemoveClockEvent(1002) == 1); + SELFTEST_ASSERT(CLOCK_RemoveEvent(1002) == 1); // now there is 1 - SELFTEST_ASSERT(NTP_PrintEventList() == 1); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 1); CMD_ExecuteCommand("addClockEvent 8:09:00 0xff 1007 POWER0 ON", 0); // now there is 2 - SELFTEST_ASSERT(NTP_PrintEventList() == 2); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 2); CMD_ExecuteCommand("addClockEvent 09:08:07 0xff 1008 POWER0 ON", 0); // now there is 3 - SELFTEST_ASSERT(NTP_PrintEventList() == 3); + SELFTEST_ASSERT(CLOCK_Print_EventList() == 3); - SELFTEST_ASSERT(NTP_GetEventTime(1006) == 15 * 3600 + 30 * 60); - SELFTEST_ASSERT(NTP_GetEventTime(1007) == 8 * 3600 + 9 * 60); - SELFTEST_ASSERT(NTP_GetEventTime(1008) == 9 * 3600 + 8 * 60 + 7); + SELFTEST_ASSERT(CLOCK_GetEventTime(1006) == 15 * 3600 + 30 * 60); + SELFTEST_ASSERT(CLOCK_GetEventTime(1007) == 8 * 3600 + 9 * 60); + SELFTEST_ASSERT(CLOCK_GetEventTime(1008) == 9 * 3600 + 8 * 60 + 7); ResetEventsAndChannels(3); @@ -88,7 +88,7 @@ void Test_ClockEvents() { unsigned int simTime = 1681998870; for (int i = 0; i < 100; i++) { - NTP_RunEvents(simTime + i, true); + CLOCK_RunEvents(simTime + i, true); } SELFTEST_ASSERT_CHANNEL(1, 10); SELFTEST_ASSERT_CHANNEL(2, 20); @@ -105,7 +105,7 @@ void Test_ClockEvents() { // start 300 seconds earlier simTime = 1681998570; for (int i = 0; i < 500; i += abs(rand() % 40)) { - NTP_RunEvents(simTime + i, true); + CLOCK_RunEvents(simTime + i, true); } printf("Channel 1 is %i\n", CHANNEL_Get(1)); printf("Channel 2 is %i\n", CHANNEL_Get(2)); @@ -133,7 +133,7 @@ void Test_ClockEvents() { simTime = 1681998870; for (int i = 0; i < 100; i++) { - NTP_RunEvents(simTime + i, true); + CLOCK_RunEvents(simTime + i, true); } // Event handler command should expand constants when the handler is run. diff --git a/src/selftest/selftest_local.h b/src/selftest/selftest_local.h index 92db99789..6f9e757a8 100644 --- a/src/selftest/selftest_local.h +++ b/src/selftest/selftest_local.h @@ -112,7 +112,7 @@ void Test_RepeatingEvents(); void Test_HTTP_Client(); void Test_DeviceGroups(); void Test_NTP(); -void Test_NTP_SunsetSunrise(); +void Test_CLOCK_SunsetSunrise(); void Test_MQTT(); void Test_Tasmota(); void Test_Backlog(); diff --git a/src/selftest/selftest_ntp_sunsetSunrise.c b/src/selftest/selftest_ntp_sunsetSunrise.c index e6378f249..3420b2ceb 100644 --- a/src/selftest/selftest_ntp_sunsetSunrise.c +++ b/src/selftest/selftest_ntp_sunsetSunrise.c @@ -3,7 +3,7 @@ #include "selftest_local.h" #include "../driver/drv_ntp.h" -void Test_NTP_SunsetSunrise() { +void Test_CLOCK_SunsetSunrise() { byte hour, minute; int sunrise, sunset; @@ -18,15 +18,15 @@ void Test_NTP_SunsetSunrise() { // set Tue, 19 Dec 2023 20:14:52 NTP_SetSimulatedTime(1703016892); - sunrise = NTP_GetSunrise(); - NTP_CalculateSunrise(&hour, &minute); + sunrise = CLOCK_GetSunrise(); + CLOCK_CalculateSunrise(&hour, &minute); // Expect sunrise at 7:11 SELFTEST_ASSERT(hour == 7); SELFTEST_ASSERT(minute == 11); SELFTEST_ASSERT(sunrise == 25860); - sunset = NTP_GetSunset(); - NTP_CalculateSunset(&hour, &minute); + sunset = CLOCK_GetSunset(); + CLOCK_CalculateSunset(&hour, &minute); // Expect sunset at 16:02 SELFTEST_ASSERT(hour == 16); SELFTEST_ASSERT(minute == 2); @@ -39,15 +39,15 @@ void Test_NTP_SunsetSunrise() { // set Tue, 19 Dec 2023 20:14:52 NTP_SetSimulatedTime(1703016892); - sunrise = NTP_GetSunrise(); - NTP_CalculateSunrise(&hour, &minute); + sunrise = CLOCK_GetSunrise(); + CLOCK_CalculateSunrise(&hour, &minute); // Expect sunrise at 6:52 SELFTEST_ASSERT(hour == 6); SELFTEST_ASSERT(minute == 52); SELFTEST_ASSERT(sunrise == 24720); - sunset = NTP_GetSunset(); - NTP_CalculateSunset(&hour, &minute); + sunset = CLOCK_GetSunset(); + CLOCK_CalculateSunset(&hour, &minute); // Expect sunset at 16:45 SELFTEST_ASSERT(hour == 16); SELFTEST_ASSERT(minute == 45); @@ -60,15 +60,15 @@ void Test_NTP_SunsetSunrise() { // set Wed Jul 12 2023 13:47:13 GMT+0000 NTP_SetSimulatedTime(1689169633); - sunrise = NTP_GetSunrise(); - NTP_CalculateSunrise(&hour, &minute); + sunrise = CLOCK_GetSunrise(); + CLOCK_CalculateSunrise(&hour, &minute); // Expect sunrise at 6:37 SELFTEST_ASSERT(hour == 6); SELFTEST_ASSERT(minute == 37); SELFTEST_ASSERT(sunrise == 23820); - sunset = NTP_GetSunset(); - NTP_CalculateSunset(&hour, &minute); + sunset = CLOCK_GetSunset(); + CLOCK_CalculateSunset(&hour, &minute); // Expect sunset at 20:34 SELFTEST_ASSERT(hour == 20); SELFTEST_ASSERT(minute == 34); @@ -86,13 +86,13 @@ void Test_NTP_SunsetSunrise() { SELFTEST_ASSERT_CHANNEL(15,123); // expect set at 15:23 - NTP_CalculateSunset(&hour, &minute); + CLOCK_CalculateSunset(&hour, &minute); SELFTEST_ASSERT(hour == 15); SELFTEST_ASSERT(minute == 23); SELFTEST_ASSERT_EXPRESSION("$sunset", 55380); // expect rise at 7:41 - NTP_CalculateSunrise(&hour, &minute); + CLOCK_CalculateSunrise(&hour, &minute); SELFTEST_ASSERT(hour == 7); SELFTEST_ASSERT(minute == 41); SELFTEST_ASSERT_EXPRESSION("$sunrise", 27660); @@ -103,6 +103,11 @@ void Test_NTP_SunsetSunrise() { // during next 10 minutes, the rise should occur int runSeconds = 10 * 60; for (int i = 0; i < runSeconds; i++) { + // we moved clock functionality away from NTP source + // so "tewak" the clock by adding a second + g_secondsElapsed++; + // we might have an action pending for this time, so call CLOCK_OnEverySecond(); + CLOCK_OnEverySecond(); NTP_OnEverySecond(); } SELFTEST_ASSERT_CHANNEL(15, 2020); @@ -126,6 +131,11 @@ void Test_NTP_SunsetSunrise() { // during next 10 minutes, the rise should occur runSeconds = 10 * 60; for (int i = 0; i < runSeconds; i++) { + // we moved clock functionality away from NTP source + // so "tewak" the clock by adding a second + g_secondsElapsed++; + // we might have an action pending for this time, so call CLOCK_OnEverySecond(); + CLOCK_OnEverySecond(); NTP_OnEverySecond(); } // channel value should change diff --git a/src/user_main.c b/src/user_main.c index a197f0f97..4df368971 100644 --- a/src/user_main.c +++ b/src/user_main.c @@ -36,6 +36,7 @@ #include "driver/drv_ntp.h" +#include "driver/drv_deviceclock.h" #include "driver/drv_ssdp.h" #include "driver/drv_uart.h" @@ -60,6 +61,7 @@ void bg_register_irda_check_func(FUNCPTR func); int g_secondsElapsed = 0; +extern int g_DSToffset; // open access point after this number of seconds int g_openAP = 0; // connect to wifi after this number of seconds @@ -651,8 +653,18 @@ void Main_OnEverySecond() //int mqtt_max, mqtt_cur, mqtt_mem; //MQTT_GetStats(&mqtt_cur, &mqtt_max, &mqtt_mem); //ADDLOGF_INFO("mqtt req %i/%i, free mem %i\n", mqtt_cur,mqtt_max,mqtt_mem); - ADDLOGF_INFO("%sTime %i, idle %i/s, free %d, MQTT %i(%i), bWifi %i, secondsWithNoPing %i, socks %i/%i %s\n", - safe, g_secondsElapsed, idleCount, xPortGetFreeHeapSize(), bMQTTconnected, MQTT_GetConnectEvents(), + char timestring[50]; + if (Clock_IsTimeSynced() == true) { + CLOCK_OnEverySecond(); + time_t localTime = (time_t)Clock_GetCurrentTime(); + strftime(timestring, sizeof(timestring), "Date: %Y-%m-%dT%H:%M:%S", gmtime(&localTime)); + } + else { + sprintf(timestring, "Time %i", g_secondsElapsed); + } + + ADDLOGF_INFO("%s %s, idle %i/s, free %d, MQTT %i(%i), bWifi %i, secondsWithNoPing %i, socks %i/%i %s\n", + safe, timestring, idleCount, xPortGetFreeHeapSize(), bMQTTconnected, MQTT_GetConnectEvents(), g_bHasWiFiConnected, g_timeSinceLastPingReply, LWIP_GetActiveSockets(), LWIP_GetMaxSockets(), g_powersave ? "POWERSAVE" : ""); // reset so it's a per-second counter. @@ -666,12 +678,28 @@ void Main_OnEverySecond() #endif - // print network info + // every 10 seconds ... if (!(g_secondsElapsed % 10)) { + // ... print network info ... HAL_PrintNetworkInfo(); - + // adjust g_secondsElapsed to rtos ticks to be more reliable +#ifndef WINDOWS + getSecondsElapsed(); // getSecondsElapsed() will set g_secondsElapsed to a tick based counter +#endif } +#if ENABLE_LOCAL_CLOCK_ADVANCED + // handle dayligth saving time + + // since Clock_GetCurrentTimeWithoutOffset() is 0 if time is not set, we don't need to test for "Clock_IsTimeSynced()" in advance + // (0 will never be > g_next_dst_change, even if g_next_dst_change was never set and hence is also 0 + //ADDLOGF_INFO("DST: Next DST switch at epoch %u -- g_DSToffset is %i ! \n",g_next_dst_change, g_DSToffset); + if ( Clock_GetCurrentTimeWithoutOffset() > g_next_dst_change ){ // since Clock_GetCurrentTimeWithoutOffset() is 0 if time is not set, we don't need to test if time is set before + if (testNsetDST(Clock_GetCurrentTimeWithoutOffset())) ADDLOGF_INFO("DST switch from normal time to DST at epoch %u -- next switch at %u!! \n", Clock_GetCurrentTimeWithoutOffset(), g_next_dst_change); + else ADDLOGF_INFO("DST switch back from DST at epoch %u -- next switch at %u!! \n", Clock_GetCurrentTimeWithoutOffset(),g_next_dst_change); + } +#endif + // IR TESTING ONLY!!!! #ifdef PLATFORM_BK7231T //DRV_IR_Print(); @@ -1084,6 +1112,11 @@ void Main_Init_BeforeDelay_Unsafe(bool bAutoRunScripts) { #if defined(PLATFORM_BEKEN) || defined(WINDOWS) CMD_InitSendCommands(); #endif +#if ENABLE_HTTP_HEADER_TIME + extern int CMD_InitGetHeaderTime(); + CMD_InitGetHeaderTime(); +#endif + CMD_InitChannelCommands(); EventHandlers_Init(); @@ -1236,6 +1269,11 @@ void Main_Init_Before_Delay() ADDLOGF_INFO("###### safe mode activated - boot failures %d", g_bootFailures); } CFG_InitAndLoad(); +#if ENABLE_LOCAL_CLOCK_ADVANCED + // set clocksettings to stored values + g_clocksettings.value=CFG_GetCLOCK_SETTINGS(); + set_UTCoffset_from_Config(); +#endif #if ENABLE_LITTLEFS LFSAddCmds(); @@ -1340,6 +1378,12 @@ void Main_Init_After_Delay() Main_Init_AfterDelay_Unsafe(true); } +#if ENABLE_LOCAL_CLOCK + extern commandResult_t CMD_CLOCK_SetConfig(); // defined in new_common.c + CMD_RegisterCommand("clock_setConfig",CMD_CLOCK_SetConfig, NULL); + extern commandResult_t CMD_CLOCK_GetConfig(); // defined in new_common.c + CMD_RegisterCommand("clock_getConfig",CMD_CLOCK_GetConfig, NULL); +#endif ADDLOGF_INFO("Main_Init_After_Delay done"); } diff --git a/src/win_main.c b/src/win_main.c index f29781843..bb53dfb48 100644 --- a/src/win_main.c +++ b/src/win_main.c @@ -174,7 +174,7 @@ void Win_DoUnitTests() { Test_EnergyMeter(); Test_Tasmota(); Test_NTP(); - Test_NTP_SunsetSunrise(); + Test_CLOCK_SunsetSunrise(); Test_HTTP_Client(); Test_ExpandConstant(); Test_ChangeHandlers_MQTT();