From ea0256a5b9cc802cbabc09c384face736493eed2 Mon Sep 17 00:00:00 2001 From: Vitali Date: Fri, 17 Jul 2026 21:05:29 +0700 Subject: [PATCH] feat: NTP time sync writing to DS3231 (by IP, DNS-independent) This LAN's router does not resolve external hostnames, so NTP uses hard-coded Cloudflare/Google anycast IPs instead of names. Co-Authored-By: Claude Opus 4.8 --- WeatherPredictor/WeatherPredictor.ino | 4 ++++ WeatherPredictor/config.h | 2 +- WeatherPredictor/rtc_time.cpp | 22 ++++++++++++++++++++++ WeatherPredictor/rtc_time.h | 1 + 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/WeatherPredictor/WeatherPredictor.ino b/WeatherPredictor/WeatherPredictor.ino index 2da42db..27532bb 100644 --- a/WeatherPredictor/WeatherPredictor.ino +++ b/WeatherPredictor/WeatherPredictor.ino @@ -55,6 +55,10 @@ void setup() { displaySplash("WiFi", "Connect / portal"); if (netBegin()) { Serial.printf("WiFi connected: %s\n", netIP().c_str()); + if (ntpSync(settings.tzOffsetMin)) + Serial.println(F("RTC synced from NTP")); + else + Serial.println(F("NTP sync failed")); } else { Serial.println(F("WiFi not connected - running offline")); } diff --git a/WeatherPredictor/config.h b/WeatherPredictor/config.h index 7f70e95..66f1b81 100644 --- a/WeatherPredictor/config.h +++ b/WeatherPredictor/config.h @@ -30,5 +30,5 @@ static const float DEFAULT_LAT = 54.9870f; static const float DEFAULT_LON = 82.8730f; // ---- Network ---- -#define NTP_SERVER "pool.ntp.org" +// NTP is done by hard-coded IP in rtc_time.cpp (this LAN's DNS is unreliable). #define AP_NAME "WeatherPredictor-Setup" diff --git a/WeatherPredictor/rtc_time.cpp b/WeatherPredictor/rtc_time.cpp index fa2a1d3..fc574da 100644 --- a/WeatherPredictor/rtc_time.cpp +++ b/WeatherPredictor/rtc_time.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include "config.h" #include "rtc_time.h" static RTC_DS3231 rtc; @@ -18,3 +21,22 @@ void rtcSet(const RtcTime& t) { uint32_t rtcEpoch() { return rtc.now().unixtime(); } + +bool ntpSync(int tzOffsetMin) { + // NTP servers by IP to bypass DNS (this network doesn't resolve names reliably). + // Cloudflare (162.159.200.123) and Google (216.239.35.0/.4) are stable anycast. + configTime(tzOffsetMin * 60, 0, "162.159.200.123", "216.239.35.0", "216.239.35.4"); + time_t now = time(nullptr); + int tries = 0; + while (now < 1700000000 && tries < 120) { // up to ~30 s + delay(250); + now = time(nullptr); + tries++; + } + if (now < 1700000000) return false; + struct tm* lt = localtime(&now); + rtcSet(RtcTime{ (uint16_t)(lt->tm_year + 1900), (uint8_t)(lt->tm_mon + 1), + (uint8_t)lt->tm_mday, (uint8_t)lt->tm_hour, + (uint8_t)lt->tm_min, (uint8_t)lt->tm_sec }); + return true; +} diff --git a/WeatherPredictor/rtc_time.h b/WeatherPredictor/rtc_time.h index d654016..95028d7 100644 --- a/WeatherPredictor/rtc_time.h +++ b/WeatherPredictor/rtc_time.h @@ -11,3 +11,4 @@ bool rtcLostPower(); RtcTime rtcNow(); void rtcSet(const RtcTime& t); uint32_t rtcEpoch(); +bool ntpSync(int tzOffsetMin); // true if synced and RTC updated