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>
#define _TASK_SLEEP_ON_IDLE_RUN //ToDo check benefit
//#include <MacroLogger.h>
#include <NeoPixelBrightnessBus.h>
#include <IotWebConf.h>
#include <IotWebConfTParameter.h>
// UpdateServer includes
#ifdef ESP8266
#include <ESP8266WiFi.h>
# include <ESP8266HTTPUpdateServer.h>
#include <TZ.h>
#define MYTZ TZ_Europe_Berlin
#include <coredecls.h> // settimeofday_cb()
//#include <Schedule.h>
//#include <PolledTimeout.h>
#include <sys/time.h> // struct timeval
#include <sntp.h> // sntp_servermode_dhcp()
#elif defined(ESP32)
#include <WiFi.h>
// 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<STRING_LEN> timezoneParam =
iotwebconf::Builder<iotwebconf::TextTParameter<STRING_LEN>>("timezone").
label("TimeZone").
defaultValue("TZ_Europe_Berlin").
build();
/*
iotwebconf::TextTParameter<STRING_LEN> ntpServerParam =
iotwebconf::Builder<iotwebconf::TextTParameter<STRING_LEN>>("ntpServer").
label("NTP Server").
defaultValue("pool.ntp.org").
build();
build();*/
iotwebconf::ParameterGroup ledGroup = iotwebconf::ParameterGroup("LED", "LED settings");
iotwebconf::IntTParameter<int16_t> maxBrightnessParam =
iotwebconf::Builder<iotwebconf::IntTParameter<int16_t>>("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);

View File

@ -17,11 +17,14 @@ void iotWebConfHandleRoot() {
s += "RGB LED Clock";
s += "<p>";
s += "<table>";
s += "<tr><td>Current Time:</td><td>";
s += "<tr><td>Current Time:</td><td>";
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 += ntpServerParam.value();
s += currentNTP;
s += "</td><tr><tr><td>Current Brightness value: </td><td>";
s += String(strip.GetBrightness());
s += "</td><tr><tr><td>Max Brightness value: </td><td>";
@ -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(

View File

@ -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;

View File

@ -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();
*/
}

View File

@ -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--;