fix Timezone and SNTP sync

This commit is contained in:
coelner 2022-11-10 20:03:13 +01:00
parent 8a50acdff4
commit 913b4fbfaa
5 changed files with 164 additions and 75 deletions

View File

@ -2,20 +2,21 @@
#include <TaskScheduler.h> #include <TaskScheduler.h>
#define _TASK_SLEEP_ON_IDLE_RUN //ToDo check benefit #define _TASK_SLEEP_ON_IDLE_RUN //ToDo check benefit
//#include <MacroLogger.h>
#include <NeoPixelBrightnessBus.h> #include <NeoPixelBrightnessBus.h>
#include <IotWebConf.h> #include <IotWebConf.h>
#include <IotWebConfTParameter.h> #include <IotWebConfTParameter.h>
// UpdateServer includes // UpdateServer includes
#ifdef ESP8266 #ifdef ESP8266
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
# include <ESP8266HTTPUpdateServer.h> # include <ESP8266HTTPUpdateServer.h>
#include <TZ.h> #include <TZ.h>
#define MYTZ TZ_Europe_Berlin #define MYTZ TZ_Europe_Berlin
#include <coredecls.h> // settimeofday_cb() #include <coredecls.h> // settimeofday_cb()
//#include <Schedule.h>
//#include <PolledTimeout.h>
#include <sys/time.h> // struct timeval #include <sys/time.h> // struct timeval
#include <sntp.h> // sntp_servermode_dhcp() #include <sntp.h> // sntp_servermode_dhcp()
#elif defined(ESP32) #elif defined(ESP32)
#include <WiFi.h> #include <WiFi.h>
// For ESP32 IotWebConf provides a drop-in replacement for UpdateServer. // For ESP32 IotWebConf provides a drop-in replacement for UpdateServer.
@ -60,17 +61,19 @@ void brightnessAdjustmentCallback();
void brightnessFadingCallback(); void brightnessFadingCallback();
void iotWebConfLoopCallback(); void iotWebConfLoopCallback();
//https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h
const char* ntpServer = "pool.ntp.org"; #define MYTZ TZ_Europe_Berlin
const long gmtOffset_sec = 3600; //ToDo changable from user String currentNTP;
const int daylightOffset_sec = 3600;
struct tm timeinfo; struct tm timeinfo;
time_t now;
//https://github.com/arduino/esp8266/blob/master/cores/esp8266/sntp-lwip2.c
//mapping for analog time representation
volatile int currentSec = 30; volatile int currentSec = 30;
volatile int currentMin = 50; volatile int currentMin = 50;
volatile int currentHour = 50; volatile int currentHour = 50;
volatile bool NTPreachable = false; volatile bool NTPreachable = false;
volatile int MAX_BRIGHTNESS = 200; volatile int MAX_BRIGHTNESS = 200;
#ifdef LDR_PIN #ifdef LDR_PIN
// variable for storing the potentiometer value // variable for storing the potentiometer value
@ -86,7 +89,7 @@ const char wifiInitialApPassword[] = "12345678";
// -- Maximal length the input-range attributes can have. // -- Maximal length the input-range attributes can have.
//#define COLOR_ATTR_LENGTH 60 //#define COLOR_ATTR_LENGTH 60
// -- Configuration specific key. The value should be modified if config structure was changed. // -- 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 #ifdef RGBW
@ -206,13 +209,20 @@ ESP8266HTTPUpdateServer httpUpdater;
HTTPUpdateServer httpUpdater; HTTPUpdateServer httpUpdater;
#endif #endif
IotWebConf iotWebConf(thingName, &dnsServer, &server, wifiInitialApPassword, CONFIG_VERSION); IotWebConf iotWebConf(thingName, &dnsServer, &server, wifiInitialApPassword, CONFIG_VERSION);
iotwebconf::ParameterGroup timeGroup = iotwebconf::ParameterGroup("Time", "Time settings"); iotwebconf::ParameterGroup timeGroup = iotwebconf::ParameterGroup("Time", "Time settings");
iotwebconf::TextTParameter<STRING_LEN> timezoneParam =
iotwebconf::Builder<iotwebconf::TextTParameter<STRING_LEN>>("timezone").
label("TimeZone").
defaultValue("TZ_Europe_Berlin").
build();
/*
iotwebconf::TextTParameter<STRING_LEN> ntpServerParam = iotwebconf::TextTParameter<STRING_LEN> ntpServerParam =
iotwebconf::Builder<iotwebconf::TextTParameter<STRING_LEN>>("ntpServer"). iotwebconf::Builder<iotwebconf::TextTParameter<STRING_LEN>>("ntpServer").
label("NTP Server"). label("NTP Server").
defaultValue("pool.ntp.org"). defaultValue("pool.ntp.org").
build(); build();*/
iotwebconf::ParameterGroup ledGroup = iotwebconf::ParameterGroup("LED", "LED settings"); iotwebconf::ParameterGroup ledGroup = iotwebconf::ParameterGroup("LED", "LED settings");
iotwebconf::IntTParameter<int16_t> maxBrightnessParam = iotwebconf::IntTParameter<int16_t> maxBrightnessParam =
iotwebconf::Builder<iotwebconf::IntTParameter<int16_t>>("Max Brightness"). iotwebconf::Builder<iotwebconf::IntTParameter<int16_t>>("Max Brightness").
@ -316,6 +326,7 @@ CustomHtmlFormatProvider customHtmlFormatProvider;
Task bootAnim(200, TASK_FOREVER, &bootAnimCallback); Task bootAnim(200, TASK_FOREVER, &bootAnimCallback);
Task clockTick(1000, TASK_FOREVER, &clockTickCallback); Task clockTick(1000, TASK_FOREVER, &clockTickCallback);
Task ntpReachableCheck(1800000, TASK_FOREVER, &ntpReachableCheckCallback);
Task ledRefresh(200, TASK_FOREVER, &ledRefreshCallback); Task ledRefresh(200, TASK_FOREVER, &ledRefreshCallback);
#ifdef LDR_PIN #ifdef LDR_PIN
Task brightnessAdjustment(10000, TASK_FOREVER, &brightnessAdjustmentCallback); Task brightnessAdjustment(10000, TASK_FOREVER, &brightnessAdjustmentCallback);

View File

@ -19,9 +19,12 @@ void iotWebConfHandleRoot() {
s += "<table>"; s += "<table>";
s += "<tr><td>Current Time:</td><td>"; s += "<tr><td>Current Time:</td><td>";
s += printLocalTime(); s += printLocalTime();
s += "</td></tr></table><table>"; s += "</tr><td><tr><td>Current Timezone:</td><td>";
s += (getenv("TZ") ? : "(none)");
s += "</td></tr>";
s += "</table><table>";
s += "<tr></tr><tr><td>NTP Server:</td><td>"; s += "<tr></tr><tr><td>NTP Server:</td><td>";
s += ntpServerParam.value(); s += currentNTP;
s += "</td><tr><tr><td>Current Brightness value: </td><td>"; s += "</td><tr><tr><td>Current Brightness value: </td><td>";
s += String(strip.GetBrightness()); s += String(strip.GetBrightness());
s += "</td><tr><tr><td>Max Brightness value: </td><td>"; s += "</td><tr><tr><td>Max Brightness value: </td><td>";
@ -64,7 +67,7 @@ void iotWebConfHandleRoot() {
void iotWebConfConfigSaved() void iotWebConfConfigSaved()
{ {
//ToDo ntpServerParamValue; setTimeZone();
MAX_BRIGHTNESS = maxBrightnessParam.value(); MAX_BRIGHTNESS = maxBrightnessParam.value();
singleSecond = singleSecondParam.isChecked() ? true : false; singleSecond = singleSecondParam.isChecked() ? true : false;
//Serial.println(singleSecondParam.isChecked() ? "true" : "false"); //Serial.println(singleSecondParam.isChecked() ? "true" : "false");
@ -89,7 +92,8 @@ void iotWebConfConfigSaved()
void iotWebConf_Setup() { void iotWebConf_Setup() {
timeGroup.addItem(&ntpServerParam); timeGroup.addItem(&timezoneParam);
//timeGroup.addItem(&ntpServerParam);
ledGroup.addItem(&maxBrightnessParam); ledGroup.addItem(&maxBrightnessParam);
ledGroup.addItem(&singleSecondParam); ledGroup.addItem(&singleSecondParam);
@ -109,7 +113,7 @@ void iotWebConf_Setup() {
iotWebConf.setConfigSavedCallback(&iotWebConfConfigSaved); iotWebConf.setConfigSavedCallback(&iotWebConfConfigSaved);
//iotWebConf.setWifiConnectionTimeoutMs(60000); //iotWebConf.setWifiConnectionTimeoutMs(60000);
iotWebConf.setWifiConnectionCallback(&setNTP); //setNTP call after connection established iotWebConf.setWifiConnectionCallback(&ntpReachableCheckCallback);
//iotWebConf.setFormValidator(&iotWebConfFormValidator); //iotWebConf.setFormValidator(&iotWebConfFormValidator);
iotWebConf.getApTimeoutParameter()->visible = true; iotWebConf.getApTimeoutParameter()->visible = true;
iotWebConf.setupUpdateServer( iotWebConf.setupUpdateServer(

View File

@ -1,4 +1,9 @@
/*
* Callback to set the led
* like the analog time
* struct
*
*/
void ledRefreshCallback() { void ledRefreshCallback() {
temp = allDotsOn ? backlightColor : black; temp = allDotsOn ? backlightColor : black;
strip.ClearTo(temp); strip.ClearTo(temp);
@ -33,6 +38,13 @@ void ledRefreshCallback() {
strip.Show(); 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() { void bootAnimCallback() {
strip.ClearTo(black); strip.ClearTo(black);
int tail = -1; int tail = -1;
@ -50,6 +62,7 @@ void bootAnimCallback() {
} }
} }
//ToDo change to no valid time
if (iotWebConf.getState() == iotwebconf::OnLine && !NTPreachable) { if (iotWebConf.getState() == iotwebconf::OnLine && !NTPreachable) {
strip.ClearTo(white); strip.ClearTo(white);
strip.SetPixelColor(MOD((currentSec - 0), NUM_LEDS), black); strip.SetPixelColor(MOD((currentSec - 0), NUM_LEDS), black);
@ -75,6 +88,11 @@ void bootAnimCallback() {
strip.Show(); strip.Show();
} }
/*
* Callback fo the brigthness
* takes 10 values, get the median
* and set the new value
*/
#ifdef LDR_PIN #ifdef LDR_PIN
void brightnessAdjustmentCallback() { void brightnessAdjustmentCallback() {
//Serial.println(ldrValue); //Serial.println(ldrValue);
@ -89,6 +107,9 @@ void brightnessAdjustmentCallback() {
//Serial.println("Brightness: " + String(strip.GetBrightness()) + "(" + String(ldrValue) + ")"); //Serial.println("Brightness: " + String(strip.GetBrightness()) + "(" + String(ldrValue) + ")");
} }
/*
* callback for fading the brigthness smootly
*/
void brightnessFadingCallback() { void brightnessFadingCallback() {
int currentBrightness = strip.GetBrightness(); int currentBrightness = strip.GetBrightness();
int diff = targetBrightness - currentBrightness; int diff = targetBrightness - currentBrightness;

View File

@ -65,6 +65,10 @@ void setup() {
#endif #endif
#if defined(ESP32) #if defined(ESP32)
#endif #endif
setTimeZone();
//setNTP();
settimeofday_cb(timeRefreshCallback); //internal callback for timesetting
ntpReachableCheck.enable();
} }
void loop() void loop()
@ -74,8 +78,5 @@ void loop()
#ifdef ESP8266 #ifdef ESP8266
ESP.wdtFeed(); ESP.wdtFeed();
#endif #endif
/*
while (Serial.available())
Serial.read();
*/
} }

View File

@ -1,81 +1,139 @@
// https://github.com/esp8266/Arduino/commit/a05a71fa9d2e6b143cb34f01b47e22c4b66b80a1 // https://github.com/esp8266/Arduino/commit/a05a71fa9d2e6b143cb34f01b47e22c4b66b80a1
//#ifdef ESP8266
//bool getLocalTimeMe(struct tm * info, uint32_t ms) void debugSNTP() {
//{ // lwIP v2 is able to list more details about the currently configured SNTP servers
// uint32_t start = millis(); for (int i = 0; i < SNTP_MAX_SERVERS; i++) {
// time_t now; IPAddress sntp = *sntp_getserver(i);
// while ((millis() - start) <= ms) { const char* name = sntp_getservername(i);
// time(&now); if (sntp.isSet()) {
// localtime_r(&now, info); Serial.printf("sntp%d: ", i);
// if (info->tm_year > (2016 - 1900)) { if (name) {
// return true; Serial.printf("%s (%s) ", name, sntp.toString().c_str());
// } } else {
// delay(10); Serial.printf("%s ", sntp.toString().c_str());
// } }
// return false; Serial.printf("- IPv6: %s - Reachability: %o\n", sntp.isV6() ? "Yes" : "No", sntp_getreachability(i));
//} }
//#endif }
}
String printLocalTime() { String printLocalTime() {
if (!getLocalTime(&timeinfo, 200)) { if (!getLocalTime(&timeinfo, 200)) {
Serial.println("Failed to obtain time");
NTPreachable = false;
return "N/A"; return "N/A";
} }
//Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); //Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
char timeStringBuff[50]; //50 chars should be enough char timeStringBuff[50]; //50 chars should be enough
strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo); strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo);
//Serial.println(timeStringBuff);
NTPreachable = true;
return timeStringBuff; return timeStringBuff;
} }
void timeRefresh( bool from_sntp ) { /*
if (clockwiseRing) { * Check for DHCP supplied NTP
currentSec = timeinfo.tm_sec; */
currentMin = timeinfo.tm_min; void ntpReachableCheckCallback() {
currentHour = MOD(timeinfo.tm_hour, 12); // 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 { else {
//inverted logic - remaining time debugSNTP();
currentSec = 60 - timeinfo.tm_sec; Serial.println("SNTP time used");
currentMin = 60 - timeinfo.tm_min; if (clockwiseRing) {
currentHour = 12 - MOD(timeinfo.tm_hour, 12); currentSec = timeinfo.tm_sec;
} currentMin = timeinfo.tm_min;
Serial.println("NTP mapped: " + String(int(currentHour * NUM_LEDS / 12)) + ":" + String(int(currentMin * NUM_LEDS / 60)) + ":" + String(int(currentSec * NUM_LEDS / 60))); currentHour = MOD(timeinfo.tm_hour, 12);
bootAnim.disable(); }
iotWebConfConfigSaved(); else {
ledRefresh.enable(); //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 #ifdef LDR_PIN
brightnessFading.enable(); brightnessFading.enableIfNot();
#endif #endif
clockTick.enable(); //enable to check for ntp every 60 seconds clockTick.enableIfNot();
brightnessAdjustment.enable(); brightnessAdjustment.enableIfNot();
}
} }
void setNTP() { void setTimeZone() {
//init and get the time //init and get the time
#ifdef ESP8266 #ifdef ESP8266
//sntp_servermode_dhcp(0); //sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default)
configTime(MYTZ, ntpServerParam.value()); configTime(MYTZ,currentNTP);
yield(); yield();
settimeofday_cb(timeRefresh);
#endif #endif
#ifdef ESP32 #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); setenv("TZ", "AEST-10", 1);
tzset(); // save the TZ variable tzset(); // save the TZ variable
//setTimeZone(long offset, int daylight);
//configTime(gmtOffset_sec, daylightOffset_sec, ntpServerParamValue);
#endif #endif
Serial.println(printLocalTime()); yield();
timeRefresh(false); }
/*
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() { void clockTickCallback() {
#ifdef LDR_PIN #ifdef LDR_PIN
ldrValue = (ldrValue + analogRead(LDR_PIN)); ldrValue = (ldrValue + analogRead(LDR_PIN));
@ -84,9 +142,6 @@ void clockTickCallback() {
if (currentSec >= 60) { if (currentSec >= 60) {
currentSec = 0; currentSec = 0;
currentMin++; currentMin++;
if (!NTPreachable) {
setNTP();
}
if (currentMin >= 60) { if (currentMin >= 60) {
currentMin = 0; currentMin = 0;
currentHour++; currentHour++;
@ -107,9 +162,6 @@ void clockTickCallback() {
if (currentSec < 0) { if (currentSec < 0) {
currentSec = 59; currentSec = 59;
currentMin--; currentMin--;
if (!NTPreachable) {
setNTP();
}
if (currentMin < 0) { if (currentMin < 0) {
currentMin = 59; currentMin = 59;
currentHour--; currentHour--;