From 913b4fbfaa1432df3871308ec53d98e3064b73cd Mon Sep 17 00:00:00 2001 From: coelner Date: Thu, 10 Nov 2022 20:03:13 +0100 Subject: [PATCH] fix Timezone and SNTP sync --- 60LED_WS2812B_NTP_Clock.ino | 29 +++++-- iotwebconf.ino | 16 ++-- ledcontrol.ino | 25 +++++- main.ino | 9 +- realtime.ino | 160 ++++++++++++++++++++++++------------ 5 files changed, 164 insertions(+), 75 deletions(-) diff --git a/60LED_WS2812B_NTP_Clock.ino b/60LED_WS2812B_NTP_Clock.ino index dea34fd..4f4ec5d 100644 --- a/60LED_WS2812B_NTP_Clock.ino +++ b/60LED_WS2812B_NTP_Clock.ino @@ -2,20 +2,21 @@ #include #define _TASK_SLEEP_ON_IDLE_RUN //ToDo check benefit +//#include #include #include #include // UpdateServer includes + #ifdef ESP8266 #include # include #include #define MYTZ TZ_Europe_Berlin #include // settimeofday_cb() -//#include -//#include #include // struct timeval #include // sntp_servermode_dhcp() + #elif defined(ESP32) #include // For ESP32 IotWebConf provides a drop-in replacement for UpdateServer. @@ -60,17 +61,19 @@ void brightnessAdjustmentCallback(); void brightnessFadingCallback(); void iotWebConfLoopCallback(); +//https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h +#define MYTZ TZ_Europe_Berlin +String currentNTP; +struct tm timeinfo; +time_t now; +//https://github.com/arduino/esp8266/blob/master/cores/esp8266/sntp-lwip2.c -const char* ntpServer = "pool.ntp.org"; -const long gmtOffset_sec = 3600; //ToDo changable from user -const int daylightOffset_sec = 3600; -struct tm timeinfo; +//mapping for analog time representation volatile int currentSec = 30; volatile int currentMin = 50; volatile int currentHour = 50; volatile bool NTPreachable = false; - volatile int MAX_BRIGHTNESS = 200; #ifdef LDR_PIN // variable for storing the potentiometer value @@ -86,7 +89,7 @@ const char wifiInitialApPassword[] = "12345678"; // -- Maximal length the input-range attributes can have. //#define COLOR_ATTR_LENGTH 60 // -- Configuration specific key. The value should be modified if config structure was changed. -#define CONFIG_VERSION "V1.1.8" +#define CONFIG_VERSION "V1.1.10" #ifdef RGBW @@ -206,13 +209,20 @@ ESP8266HTTPUpdateServer httpUpdater; HTTPUpdateServer httpUpdater; #endif + IotWebConf iotWebConf(thingName, &dnsServer, &server, wifiInitialApPassword, CONFIG_VERSION); iotwebconf::ParameterGroup timeGroup = iotwebconf::ParameterGroup("Time", "Time settings"); +iotwebconf::TextTParameter timezoneParam = + iotwebconf::Builder>("timezone"). + label("TimeZone"). + defaultValue("TZ_Europe_Berlin"). + build(); + /* iotwebconf::TextTParameter ntpServerParam = iotwebconf::Builder>("ntpServer"). label("NTP Server"). defaultValue("pool.ntp.org"). - build(); + build();*/ iotwebconf::ParameterGroup ledGroup = iotwebconf::ParameterGroup("LED", "LED settings"); iotwebconf::IntTParameter maxBrightnessParam = iotwebconf::Builder>("Max Brightness"). @@ -316,6 +326,7 @@ CustomHtmlFormatProvider customHtmlFormatProvider; Task bootAnim(200, TASK_FOREVER, &bootAnimCallback); Task clockTick(1000, TASK_FOREVER, &clockTickCallback); +Task ntpReachableCheck(1800000, TASK_FOREVER, &ntpReachableCheckCallback); Task ledRefresh(200, TASK_FOREVER, &ledRefreshCallback); #ifdef LDR_PIN Task brightnessAdjustment(10000, TASK_FOREVER, &brightnessAdjustmentCallback); diff --git a/iotwebconf.ino b/iotwebconf.ino index 90d9542..79188d2 100644 --- a/iotwebconf.ino +++ b/iotwebconf.ino @@ -17,11 +17,14 @@ void iotWebConfHandleRoot() { s += "RGB LED Clock"; s += "

"; s += ""; - s += "
Current Time:"; + s += "
Current Time:"; s += printLocalTime(); - s += "
"; + s += ""; + s += "
Current Timezone:"; + s += (getenv("TZ") ? : "(none)"); + s += "
"; s += "
NTP Server:"; - s += ntpServerParam.value(); + s += currentNTP; s += "
Current Brightness value: "; s += String(strip.GetBrightness()); s += "
Max Brightness value: "; @@ -64,7 +67,7 @@ void iotWebConfHandleRoot() { void iotWebConfConfigSaved() { - //ToDo ntpServerParamValue; + setTimeZone(); MAX_BRIGHTNESS = maxBrightnessParam.value(); singleSecond = singleSecondParam.isChecked() ? true : false; //Serial.println(singleSecondParam.isChecked() ? "true" : "false"); @@ -89,7 +92,8 @@ void iotWebConfConfigSaved() void iotWebConf_Setup() { - timeGroup.addItem(&ntpServerParam); + timeGroup.addItem(&timezoneParam); + //timeGroup.addItem(&ntpServerParam); ledGroup.addItem(&maxBrightnessParam); ledGroup.addItem(&singleSecondParam); @@ -109,7 +113,7 @@ void iotWebConf_Setup() { iotWebConf.setConfigSavedCallback(&iotWebConfConfigSaved); //iotWebConf.setWifiConnectionTimeoutMs(60000); - iotWebConf.setWifiConnectionCallback(&setNTP); //setNTP call after connection established + iotWebConf.setWifiConnectionCallback(&ntpReachableCheckCallback); //iotWebConf.setFormValidator(&iotWebConfFormValidator); iotWebConf.getApTimeoutParameter()->visible = true; iotWebConf.setupUpdateServer( diff --git a/ledcontrol.ino b/ledcontrol.ino index a4ec804..8bd6884 100644 --- a/ledcontrol.ino +++ b/ledcontrol.ino @@ -1,4 +1,9 @@ - +/* + * Callback to set the led + * like the analog time + * struct + * + */ void ledRefreshCallback() { temp = allDotsOn ? backlightColor : black; strip.ClearTo(temp); @@ -33,6 +38,13 @@ void ledRefreshCallback() { strip.Show(); } +/* + * Callback to animate + * black running for online but not NTP + * white running for connecting + * green running for AP mode + * red running for boot and unconfigured + */ void bootAnimCallback() { strip.ClearTo(black); int tail = -1; @@ -50,7 +62,8 @@ void bootAnimCallback() { } } - if (iotWebConf.getState() == iotwebconf::OnLine && !NTPreachable) { + //ToDo change to no valid time + if (iotWebConf.getState() == iotwebconf::OnLine && !NTPreachable) { strip.ClearTo(white); strip.SetPixelColor(MOD((currentSec - 0), NUM_LEDS), black); strip.SetPixelColor(MOD((currentSec - 1 * tail), NUM_LEDS), black); @@ -75,6 +88,11 @@ void bootAnimCallback() { strip.Show(); } +/* + * Callback fo the brigthness + * takes 10 values, get the median + * and set the new value + */ #ifdef LDR_PIN void brightnessAdjustmentCallback() { //Serial.println(ldrValue); @@ -89,6 +107,9 @@ void brightnessAdjustmentCallback() { //Serial.println("Brightness: " + String(strip.GetBrightness()) + "(" + String(ldrValue) + ")"); } +/* + * callback for fading the brigthness smootly + */ void brightnessFadingCallback() { int currentBrightness = strip.GetBrightness(); int diff = targetBrightness - currentBrightness; diff --git a/main.ino b/main.ino index 9582435..7458bcf 100644 --- a/main.ino +++ b/main.ino @@ -65,6 +65,10 @@ void setup() { #endif #if defined(ESP32) #endif + setTimeZone(); + //setNTP(); + settimeofday_cb(timeRefreshCallback); //internal callback for timesetting + ntpReachableCheck.enable(); } void loop() @@ -74,8 +78,5 @@ void loop() #ifdef ESP8266 ESP.wdtFeed(); #endif - /* - while (Serial.available()) - Serial.read(); - */ + } diff --git a/realtime.ino b/realtime.ino index f03c880..3216a62 100644 --- a/realtime.ino +++ b/realtime.ino @@ -1,81 +1,139 @@ // https://github.com/esp8266/Arduino/commit/a05a71fa9d2e6b143cb34f01b47e22c4b66b80a1 -//#ifdef ESP8266 -//bool getLocalTimeMe(struct tm * info, uint32_t ms) -//{ -// uint32_t start = millis(); -// time_t now; -// while ((millis() - start) <= ms) { -// time(&now); -// localtime_r(&now, info); -// if (info->tm_year > (2016 - 1900)) { -// return true; -// } -// delay(10); -// } -// return false; -//} -//#endif + +void debugSNTP() { + // lwIP v2 is able to list more details about the currently configured SNTP servers + for (int i = 0; i < SNTP_MAX_SERVERS; i++) { + IPAddress sntp = *sntp_getserver(i); + const char* name = sntp_getservername(i); + if (sntp.isSet()) { + Serial.printf("sntp%d: ", i); + if (name) { + Serial.printf("%s (%s) ", name, sntp.toString().c_str()); + } else { + Serial.printf("%s ", sntp.toString().c_str()); + } + Serial.printf("- IPv6: %s - Reachability: %o\n", sntp.isV6() ? "Yes" : "No", sntp_getreachability(i)); + } + } +} String printLocalTime() { if (!getLocalTime(&timeinfo, 200)) { - Serial.println("Failed to obtain time"); - NTPreachable = false; return "N/A"; } //Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); char timeStringBuff[50]; //50 chars should be enough strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo); - //Serial.println(timeStringBuff); - NTPreachable = true; return timeStringBuff; } -void timeRefresh( bool from_sntp ) { - if (clockwiseRing) { - currentSec = timeinfo.tm_sec; - currentMin = timeinfo.tm_min; - currentHour = MOD(timeinfo.tm_hour, 12); +/* + * Check for DHCP supplied NTP + */ +void ntpReachableCheckCallback() { + // lwIP v2 is able to list more details about the currently configured SNTP servers + int usuableServer = 0; + int serverCount = 0; + for (int i = 0; i < SNTP_MAX_SERVERS; i++) { + IPAddress sntp = *sntp_getserver(i); + const char* name = sntp_getservername(i); + if (sntp.isSet()) { + serverCount++; + if (sntp_getreachability(i) > 0) { + NTPreachable = true; + usuableServer++; + getLocalTime(&timeinfo, 200); + Serial.println("NTP reachable"); + currentNTP=sntp.toString(); + } + else { + NTPreachable = false; + Serial.println("ntp NOT reachable"); + } + } + if (serverCount == 0) { + setFallbackNTP(); + } + } +} +/* + refresh the internal time struct +*/ +void timeRefreshCallback( bool from_sntp ) { + getLocalTime(&timeinfo, 200); + Serial.print(asctime(&timeinfo)); + Serial.printf("timezone: %s\n", getenv("TZ") ? : "(none)"); + + //set new time only when from sntp + //no manual way to set time + if (!from_sntp) { + Serial.println("Not SNTP time"); } else { - //inverted logic - remaining time - currentSec = 60 - timeinfo.tm_sec; - currentMin = 60 - timeinfo.tm_min; - currentHour = 12 - MOD(timeinfo.tm_hour, 12); - } - Serial.println("NTP mapped: " + String(int(currentHour * NUM_LEDS / 12)) + ":" + String(int(currentMin * NUM_LEDS / 60)) + ":" + String(int(currentSec * NUM_LEDS / 60))); - bootAnim.disable(); - iotWebConfConfigSaved(); - ledRefresh.enable(); + debugSNTP(); + Serial.println("SNTP time used"); + if (clockwiseRing) { + currentSec = timeinfo.tm_sec; + currentMin = timeinfo.tm_min; + currentHour = MOD(timeinfo.tm_hour, 12); + } + else { + //inverted logic - remaining time + currentSec = 60 - timeinfo.tm_sec; + currentMin = 60 - timeinfo.tm_min; + currentHour = 12 - MOD(timeinfo.tm_hour, 12); + } + Serial.println("timestruct mapped: " + String(int(currentHour * NUM_LEDS / 12)) + ":" + String(int(currentMin * NUM_LEDS / 60)) + ":" + String(int(currentSec * NUM_LEDS / 60))); + bootAnim.disable(); + //iotWebConfConfigSaved(); //ToDo why? + ledRefresh.enableIfNot(); #ifdef LDR_PIN - brightnessFading.enable(); + brightnessFading.enableIfNot(); #endif - clockTick.enable(); //enable to check for ntp every 60 seconds - brightnessAdjustment.enable(); + clockTick.enableIfNot(); + brightnessAdjustment.enableIfNot(); + } } -void setNTP() { +void setTimeZone() { //init and get the time #ifdef ESP8266 - //sntp_servermode_dhcp(0); - configTime(MYTZ, ntpServerParam.value()); + //sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default) + configTime(MYTZ,currentNTP); yield(); - settimeofday_cb(timeRefresh); #endif #ifdef ESP32 - configTime(0, 0, ntpServer); - // TZ string information: https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html setenv("TZ", "AEST-10", 1); tzset(); // save the TZ variable - //setTimeZone(long offset, int daylight); - //configTime(gmtOffset_sec, daylightOffset_sec, ntpServerParamValue); #endif - Serial.println(printLocalTime()); - timeRefresh(false); + yield(); +} +/* + function to set NTP server + maybe not needed +*/ +void setFallbackNTP() { + //init and get the time +#ifdef ESP8266 + //sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default) + //configTime(int timezone, int daylightOffset_sec, const char* server1, const char* server2, const char* server3 ) + configTime(MYTZ, "pool.ntp.org"); //ToDo skip TZ + //setTZ(MYTZ); +#endif +#ifdef ESP32 + configTime(0, 0, "pool.ntp.org"); +#endif + yield(); } -// Scheduler +/* + Callback to handle the separate + loose time structure for the mapping + to the analog representation of a clock + +*/ void clockTickCallback() { #ifdef LDR_PIN ldrValue = (ldrValue + analogRead(LDR_PIN)); @@ -84,9 +142,6 @@ void clockTickCallback() { if (currentSec >= 60) { currentSec = 0; currentMin++; - if (!NTPreachable) { - setNTP(); - } if (currentMin >= 60) { currentMin = 0; currentHour++; @@ -107,9 +162,6 @@ void clockTickCallback() { if (currentSec < 0) { currentSec = 59; currentMin--; - if (!NTPreachable) { - setNTP(); - } if (currentMin < 0) { currentMin = 59; currentHour--;