From 18fdc4a1766c5d5221dd35879479c7b8aa96e626 Mon Sep 17 00:00:00 2001 From: Leon Poon Date: Sun, 16 Oct 2022 23:56:58 +0800 Subject: [PATCH] support nanos in rtc for sync from ntp so that all tm1637 6-digit clocks tick simultaneously at real second boundary. --- tasmota/tasmota.ino | 1 + tasmota/tasmota_support/support_rtc.ino | 22 ++++++++++++++++--- tasmota/tasmota_support/support_wifi.ino | 14 +++++++++--- .../tasmota_xdsp_display/xdsp_15_tm1637.ino | 17 +++++++++----- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 4a820a229..d33d122f6 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -158,6 +158,7 @@ RTC_NOINIT_ATTR TRtcSettings RtcDataSettings; #endif // ESP32 struct TIME_T { + uint32_t nanos; uint8_t second; uint8_t minute; uint8_t hour; diff --git a/tasmota/tasmota_support/support_rtc.ino b/tasmota/tasmota_support/support_rtc.ino index 85efa6a84..1c10811fc 100644 --- a/tasmota/tasmota_support/support_rtc.ino +++ b/tasmota/tasmota_support/support_rtc.ino @@ -43,10 +43,12 @@ struct RTC { uint32_t standard_time = 0; uint32_t midnight = 0; uint32_t restart_time = 0; + uint32_t nanos = 0; uint32_t millis = 0; // uint32_t last_sync = 0; int32_t time_timezone = 0; bool time_synced = false; + bool last_synced = false; bool midnight_now = false; bool user_time_entry = false; // Override NTP by user setting } Rtc; @@ -235,11 +237,14 @@ uint32_t RtcMillis(void) { return (millis() - Rtc.millis) % 1000; } -void BreakTime(uint32_t time_input, TIME_T &tm) { +void BreakNanoTime(uint32_t time_input, uint32_t time_nanos, TIME_T &tm) { // break the given time_input into time components // this is a more compact version of the C library localtime function // note that year is offset from 1970 !!! + time_input += time_nanos / 1000000000U; + tm.nanos = time_nanos % 1000000000U; + uint8_t year; uint8_t month; uint8_t month_length; @@ -290,6 +295,10 @@ void BreakTime(uint32_t time_input, TIME_T &tm) { tm.valid = (time_input > START_VALID_TIME); // 2016-01-01 } +void BreakTime(uint32_t time_input, TIME_T &tm) { + BreakNanoTime(time_input, 0, tm); +} + uint32_t MakeTime(TIME_T &tm) { // assemble time elements into time_t // note year argument is offset from 1970 @@ -403,6 +412,7 @@ void RtcSecond(void) { mutex = true; Rtc.time_synced = false; + Rtc.last_synced = true; last_sync = Rtc.utc_time; if (Rtc.restart_time == 0) { @@ -420,7 +430,13 @@ void RtcSecond(void) { TasmotaGlobal.rules_flag.time_set = 1; } } else { - Rtc.utc_time++; // Increment every second + if (Rtc.last_synced) { + Rtc.last_synced = false; + uint32_t nanos = Rtc.nanos + (millis() - Rtc.millis) * 1000000U; + Rtc.utc_time += nanos / 1000000000U; + Rtc.nanos = nanos % 1000000000U; + } else + Rtc.utc_time++; // Increment every second } Rtc.millis = millis(); @@ -442,7 +458,7 @@ void RtcSecond(void) { } } - BreakTime(Rtc.local_time, RtcTime); + BreakNanoTime(Rtc.local_time, Rtc.nanos, RtcTime); if (RtcTime.valid) { if (!Rtc.midnight) { Rtc.midnight = Rtc.local_time - (RtcTime.hour * 3600) - (RtcTime.minute * 60) - RtcTime.second; diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 0700fcc2d..f202ff602 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -878,13 +878,15 @@ void WifiPollNtp() { AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("NTP: Sync time...")); ntp_run_time = millis(); - uint32_t ntp_time = WifiGetNtp(); + uint64_t ntp_nanos = WifiGetNtp(); + uint32_t ntp_time = ntp_nanos / 1000000000; ntp_run_time = (millis() - ntp_run_time) / 1000; // AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: Runtime %d"), ntp_run_time); if (ntp_run_time < 5) { ntp_run_time = 0; } // DNS timeout is around 10s if (ntp_time > START_VALID_TIME) { Rtc.utc_time = ntp_time; + Rtc.nanos = ntp_nanos % 1000000000; ntp_sync_minute = 60; // Sync so block further requests RtcSync("NTP"); } else { @@ -893,7 +895,7 @@ void WifiPollNtp() { } } -uint32_t WifiGetNtp(void) { +uint64_t WifiGetNtp(void) { static uint8_t ntp_server_id = 0; // AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: Start NTP Sync %d ..."), ntp_server_id); @@ -983,7 +985,13 @@ uint32_t WifiGetNtp(void) { ntp_server_id++; // Next server next time return 0; } - return secs_since_1900 - 2208988800UL; + + uint32_t highWord = word(packet_buffer[44], packet_buffer[45]); + uint32_t lowWord = word(packet_buffer[46], packet_buffer[47]); + + uint32_t currentNano = (((uint64_t)(highWord << 16 | lowWord)) * 1000000000) >> 32; + + return (((uint64_t) secs_since_1900) - 2208988800UL) * 1000000000 + currentNano; } delay(10); } diff --git a/tasmota/tasmota_xdsp_display/xdsp_15_tm1637.ino b/tasmota/tasmota_xdsp_display/xdsp_15_tm1637.ino index a7daf61bf..144921527 100644 --- a/tasmota/tasmota_xdsp_display/xdsp_15_tm1637.ino +++ b/tasmota/tasmota_xdsp_display/xdsp_15_tm1637.ino @@ -987,9 +987,12 @@ bool CmndTM1637Clock(void) \*********************************************************************************************/ void TM1637ShowTime() { - uint8_t hr = RtcTime.hour; - uint8_t mn = RtcTime.minute; - uint8_t sc = RtcTime.second; + struct TIME_T t = RtcTime; + BreakNanoTime(Rtc.local_time, Rtc.nanos + (millis() - Rtc.millis) * 1000000, t); + uint8_t hr = t.hour; + uint8_t mn = t.minute; + uint8_t sc = t.second; + uint16_t ms = t.nanos / 1000000; if (!TM1637Data.clock_24) { @@ -1009,12 +1012,14 @@ void TM1637ShowTime() if (TM1637 == TM1637Data.display_type) { + uint8_t colon = ms > 500? 0: 128; uint8_t rawBytes[1]; - for (uint32_t i = 0; i < 4; i++) + uint8_t width = Settings->display_width >= 6? 6: 4; + for (uint32_t i = 0; i < width; i++) { rawBytes[0] = tm1637display->encode(tm[i]); - if ((millis() % 1000) > 500 && (i == 1)) - rawBytes[0] = rawBytes[0] | 128; + if (i == 1 || (i == 3 && width > 4)) + rawBytes[0] = rawBytes[0] | colon; tm1637display->printRaw(rawBytes, 1, TM1637Data.digit_order[i]); } }