From b455f432d5eeec73b5c458b9e8a37d6d396d8e63 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 24 May 2021 14:34:03 +0200 Subject: [PATCH] Toki 1st experiment --- tools/WLED_ESP32_4MB_1MB_FS.csv | 6 + .../EleksTube_IPS/usermod_elekstube_ips.h | 10 +- wled00/ntp.cpp | 26 ++-- wled00/overlay.cpp | 7 +- wled00/src/dependencies/time/Time.cpp | 66 ++------- wled00/src/dependencies/time/TimeLib.h | 14 +- wled00/src/dependencies/toki/Toki.h | 128 ++++++++++++++++++ wled00/wled.cpp | 3 + wled00/wled.h | 2 + 9 files changed, 181 insertions(+), 81 deletions(-) create mode 100644 tools/WLED_ESP32_4MB_1MB_FS.csv create mode 100644 wled00/src/dependencies/toki/Toki.h diff --git a/tools/WLED_ESP32_4MB_1MB_FS.csv b/tools/WLED_ESP32_4MB_1MB_FS.csv new file mode 100644 index 00000000..09455c6a --- /dev/null +++ b/tools/WLED_ESP32_4MB_1MB_FS.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x17B000, +app1, app, ota_1, 0x18B000,0x17B000, +spiffs, data, spiffs, 0x306000,0x0FA000, \ No newline at end of file diff --git a/usermods/EleksTube_IPS/usermod_elekstube_ips.h b/usermods/EleksTube_IPS/usermod_elekstube_ips.h index 4f820c26..f2f03466 100644 --- a/usermods/EleksTube_IPS/usermod_elekstube_ips.h +++ b/usermods/EleksTube_IPS/usermod_elekstube_ips.h @@ -41,18 +41,14 @@ class ElekstubeIPSUsermod : public Usermod { tfts.begin(); tfts.fillScreen(TFT_BLACK); tfts.setTextColor(TFT_WHITE, TFT_BLACK); - tfts.setCursor(0, 0, 2); + tfts.setCursor(0, 100, 2); tfts.println(""); } void loop() { - if (lastTime == 0) { - tfts.fillScreen(TFT_BLACK); - updateClockDisplay(TFTs::force); - } - if (millis() - lastTime > 100) { + if (toki.isTick()) { + updateLocalTime(); updateClockDisplay(); - lastTime = millis(); } } diff --git a/wled00/ntp.cpp b/wled00/ntp.cpp index 449ad044..dd823486 100644 --- a/wled00/ntp.cpp +++ b/wled00/ntp.cpp @@ -183,20 +183,30 @@ bool checkNTPResponse() { int cb = ntpUdp.parsePacket(); if (cb) { + uint32_t ntpPacketReceivedTime = millis(); DEBUG_PRINT(F("NTP recv, l=")); DEBUG_PRINTLN(cb); byte pbuf[NTP_PACKET_SIZE]; ntpUdp.read(pbuf, NTP_PACKET_SIZE); // read the packet into the buffer - unsigned long highWord = word(pbuf[40], pbuf[41]); - unsigned long lowWord = word(pbuf[42], pbuf[43]); - if (highWord == 0 && lowWord == 0) return false; - - unsigned long secsSince1900 = highWord << 16 | lowWord; + Toki::Time arrived = toki.fromNTP(pbuf + 32); + Toki::Time departed = toki.fromNTP(pbuf + 40); + //basic half roundtrip estimation + uint32_t offset = (ntpPacketReceivedTime - ntpPacketSentTime - toki.msDifference(arrived, departed)) >> 1; + offset += millis() - ntpPacketReceivedTime +1; + toki.adjust(departed, offset); + toki.setTime(departed); + Serial.print("Roundtrip: "); + Serial.println(ntpPacketReceivedTime - ntpPacketSentTime); + Serial.print("Offset: "); + Serial.println(offset); + Serial.print("Time: "); + toki.printTime(toki.getTime()); DEBUG_PRINT(F("Unix time = ")); - unsigned long epoch = secsSince1900 - 2208988799UL; //subtract 70 years -1sec (on avg. more precision) - setTime(epoch); + uint32_t epoch = toki.second(); + if (epoch == 0) return false; + setTime(epoch); //legacy DEBUG_PRINTLN(epoch); if (countdownTime - now() > 0) countdownOverTriggered = false; // if time changed re-calculate sunrise/sunset @@ -210,7 +220,7 @@ bool checkNTPResponse() void updateLocalTime() { if (currentTimezone != tzCurrent) updateTimezone(); - unsigned long tmc = now()+ utcOffsetSecs; + unsigned long tmc = toki.second()+ utcOffsetSecs; localTime = tz->toLocal(tmc); } diff --git a/wled00/overlay.cpp b/wled00/overlay.cpp index 419d82a7..e8f53ea2 100644 --- a/wled00/overlay.cpp +++ b/wled00/overlay.cpp @@ -21,13 +21,16 @@ void initCronixie() void handleOverlays() { - if (millis() - overlayRefreshedTime > overlayRefreshMs) + if (toki.isTick()) { initCronixie(); updateLocalTime(); checkTimers(); checkCountdown(); - if (overlayCurrent == 3) _overlayCronixie();//Diamex cronixie clock kit + if (overlayCurrent == 3) { + _overlayCronixie();//Diamex cronixie clock kit + strip.trigger(); + } overlayRefreshedTime = millis(); } } diff --git a/wled00/src/dependencies/time/Time.cpp b/wled00/src/dependencies/time/Time.cpp index 6b1df7c5..326ce85c 100644 --- a/wled00/src/dependencies/time/Time.cpp +++ b/wled00/src/dependencies/time/Time.cpp @@ -37,7 +37,6 @@ static tmElements_t tm; // a cache of time elements static time_t cacheTime; // the time the cache was updated -static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds void refreshCache(time_t t) { if (t != cacheTime) { @@ -234,18 +233,9 @@ time_t makeTime(tmElements_t &tm){ /*=====================================================*/ /* Low level system time functions */ -static uint32_t sysTime = 0; +static uint32_t sysTime = 0; //seconds +static uint16_t sysMillis = 0; static uint32_t prevMillis = 0; -static uint32_t nextSyncTime = 0; -static timeStatus_t Status = timeNotSet; - -getExternalTime getTimePtr; // pointer to external sync function -//setExternalTime setTimePtr; // not used in this version - -#ifdef TIME_DRIFT_INFO // define this to get drift data -time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync -#endif - time_t now() { // calculate number of seconds passed since last call to now() @@ -253,33 +243,18 @@ time_t now() { // millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference sysTime++; prevMillis += 1000; -#ifdef TIME_DRIFT_INFO - sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift -#endif } - if (nextSyncTime <= sysTime) { - if (getTimePtr != 0) { - time_t t = getTimePtr(); - if (t != 0) { - setTime(t); - } else { - nextSyncTime = sysTime + syncInterval; - Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync; - } - } - } + return (time_t)sysTime; } -void setTime(time_t t) { -#ifdef TIME_DRIFT_INFO - if(sysUnsyncedTime == 0) - sysUnsyncedTime = t; // store the time of the first call to set a valid Time -#endif +uint16_t millisecond() { // the millisecond (0-999) now + return (sysMillis - millis()) % 1000; +} - sysTime = (uint32_t)t; - nextSyncTime = (uint32_t)t + syncInterval; - Status = timeSet; +void setTime(time_t t, uint16_t ms) { + sysTime = (uint32_t)t; + sysMillis = ms; prevMillis = millis(); // restart counting from now (thanks to Korman for this fix) } @@ -299,27 +274,10 @@ time_t getUnixTime(int hr,int min,int sec,int dy, int mnth, int yr){ return makeTime(tm); } -void setTime(int hr,int min,int sec,int dy, int mnth, int yr){ - setTime(getUnixTime(hr,min,sec,dy,mnth,yr)); +void setTime(int hr,int min,int sec,int dy, int mnth, int yr, uint16_t ms){ + setTime(getUnixTime(hr,min,sec,dy,mnth,yr), ms); } void adjustTime(long adjustment) { sysTime += adjustment; -} - -// indicates if time has been set and recently synchronized -timeStatus_t timeStatus() { - now(); // required to actually update the status - return Status; -} - -void setSyncProvider( getExternalTime getTimeFunction){ - getTimePtr = getTimeFunction; - nextSyncTime = sysTime; - now(); // this will sync the clock -} - -void setSyncInterval(time_t interval){ // set the number of seconds between re-sync - syncInterval = (uint32_t)interval; - nextSyncTime = sysTime + syncInterval; -} +} \ No newline at end of file diff --git a/wled00/src/dependencies/time/TimeLib.h b/wled00/src/dependencies/time/TimeLib.h index d8ed7391..1064638a 100644 --- a/wled00/src/dependencies/time/TimeLib.h +++ b/wled00/src/dependencies/time/TimeLib.h @@ -31,8 +31,6 @@ typedef unsigned long time_t; // but at least this hack lets us define C++ functions as intended. Hopefully // nothing too terrible will result from overriding the C library header?! extern "C++" { -typedef enum {timeNotSet, timeNeedsSync, timeSet -} timeStatus_t ; typedef enum { dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday @@ -116,10 +114,11 @@ int month(); // the month now (Jan is month 1) int month(time_t t); // the month for the given time int year(); // the full four digit year: (2009, 2010 etc) int year(time_t t); // the year for the given time +uint16_t millisecond(); // the millisecond now time_t now(); // return the current time as seconds since Jan 1 1970 -void setTime(time_t t); -void setTime(int hr,int min,int sec,int day, int month, int yr); +void setTime(time_t t, uint16_t ms = 0); +void setTime(int hr,int min,int sec,int day, int month, int yr, uint16_t ms = 0); time_t getUnixTime(int hr,int min,int sec,int day, int month, int yr); //added by Aircoookie to get epoch time void adjustTime(long adjustment); @@ -129,13 +128,8 @@ char* monthStr(uint8_t month); char* dayStr(uint8_t day); char* monthShortStr(uint8_t month); char* dayShortStr(uint8_t day); - -/* time sync functions */ -timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized -void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider -void setSyncInterval(time_t interval); // set the number of seconds between re-sync -/* low level functions to convert to and from system time */ +/* low level functions to convert to and from system time */ void breakTime(time_t time, tmElements_t &tm); // break time_t into elements time_t makeTime(tmElements_t &tm); // convert time elements into time_t diff --git a/wled00/src/dependencies/toki/Toki.h b/wled00/src/dependencies/toki/Toki.h new file mode 100644 index 00000000..6ab404c2 --- /dev/null +++ b/wled00/src/dependencies/toki/Toki.h @@ -0,0 +1,128 @@ +/* + Toki.h - Minimal millisecond accurate timekeeping. + + LICENSE + The MIT License (MIT) + Copyright (c) 2021 Christian Schwinne + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include + +#define YEARS_70 2208988800UL + +//3:13 24.05.2012 ==> 2021-4-29, 06:41:37. + +class Toki { + typedef enum { + inactive, marked, active + } TickT; + + public: + typedef struct { + uint32_t sec; + uint16_t ms; + } Time; + + private: + uint32_t fullSecondMillis = 0; + uint32_t unix = 0; + TickT tick = TickT::inactive; + + public: + void setTime(Time t) { + fullSecondMillis = millis() - t.ms; + unix = t.sec; + } + + void setTime(uint32_t sec, uint16_t ms) { + Time t = {sec, ms}; + setTime(t); + } + + Time fromNTP(byte *timestamp) { //ntp timestamp is 8 bytes, 4 bytes second and 4 bytes sub-second fraction + unsigned long highWord = word(timestamp[0], timestamp[1]); + unsigned long lowWord = word(timestamp[2], timestamp[3]); + + unsigned long unix = highWord << 16 | lowWord; + if (!unix) return {0,0}; + unix -= YEARS_70; //NTP begins 1900, Unix 1970 + + unsigned long frac = word(timestamp[5], timestamp[6]); //65536ths of a second + frac = (frac*1000) >> 16; //convert to ms + return {unix, (uint16_t)frac}; + } + + uint16_t millisecond() { + uint32_t ms = millis() - fullSecondMillis; + while (ms > 999) { + ms -= 1000; + fullSecondMillis += 1000; + unix++; + if (tick == TickT::inactive) tick = TickT::marked; //marked, will be active on next loop + } + return ms; + } + + uint32_t second() { + millisecond(); + return unix; + } + + uint32_t msDifference(Time &t0, Time &t1) { + bool t1BiggerSec = (t1.sec > t0.sec); + uint32_t secDiff = (t1BiggerSec) ? t1.sec - t0.sec : t0.sec - t1.sec; + uint32_t t0ms = t0.ms, t1ms = t1.ms; + if (t1BiggerSec) t1ms += secDiff; + else t0ms += secDiff; + uint32_t msDiff = (t1ms > t0ms) ? t1ms - t0ms : t0ms - t1ms; + return msDiff; + } + + void adjust(Time&t, int32_t offset) { + int32_t secs = offset /1000; + int32_t ms = offset - secs*1000; + t.sec += offset /1000; + int32_t nms = t.ms + ms; + if (nms > 1000) {nms -= 1000; t.sec++;} + if (nms < 0) {nms += 1000; t.sec--;} + t.ms += nms; + } + + Time getTime() { + Time t; + t.ms = millisecond(); + t.sec = unix; + return t; + } + + void setTick() { + if (tick == TickT::marked) tick = TickT::active; + } + + void resetTick() { + tick = TickT::inactive; + } + + bool isTick() { + return (tick == TickT::active); + } + + void printTime(const Time& t) { + Serial.printf_P(PSTR("%u,%03u\n"),t.sec,t.ms); + } +}; \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index df17a180..66a2c020 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -177,6 +177,8 @@ void WiFiEvent(WiFiEvent_t event) void WLED::loop() { + toki.millisecond(); + toki.setTick(); handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too handleConnection(); handleSerial(); @@ -287,6 +289,7 @@ void WLED::loop() } loops++; #endif // WLED_DEBUG + toki.resetTick(); } void WLED::setup() diff --git a/wled00/wled.h b/wled00/wled.h index 33d7ab22..db046201 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -90,6 +90,7 @@ #include #include "src/dependencies/time/TimeLib.h" #include "src/dependencies/timezone/Timezone.h" +#include "src/dependencies/toki/Toki.h" #ifndef WLED_DISABLE_ALEXA #define ESPALEXA_ASYNC @@ -516,6 +517,7 @@ WLED_GLOBAL float longitude _INIT(0.0); WLED_GLOBAL float latitude _INIT(0.0); WLED_GLOBAL time_t sunrise _INIT(0); WLED_GLOBAL time_t sunset _INIT(0); +WLED_GLOBAL Toki toki _INIT(Toki()); // Temp buffer WLED_GLOBAL char* obuf;