Toki 1st experiment

This commit is contained in:
cschwinne 2021-05-24 14:34:03 +02:00
parent 9b796531b2
commit b455f432d5
9 changed files with 181 additions and 81 deletions

View File

@ -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,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x17B000
5 app1 app ota_1 0x18B000 0x17B000
6 spiffs data spiffs 0x306000 0x0FA000

View File

@ -41,18 +41,14 @@ class ElekstubeIPSUsermod : public Usermod {
tfts.begin(); tfts.begin();
tfts.fillScreen(TFT_BLACK); tfts.fillScreen(TFT_BLACK);
tfts.setTextColor(TFT_WHITE, TFT_BLACK); tfts.setTextColor(TFT_WHITE, TFT_BLACK);
tfts.setCursor(0, 0, 2); tfts.setCursor(0, 100, 2);
tfts.println("<STARTUP>"); tfts.println("<STARTUP>");
} }
void loop() { void loop() {
if (lastTime == 0) { if (toki.isTick()) {
tfts.fillScreen(TFT_BLACK); updateLocalTime();
updateClockDisplay(TFTs::force);
}
if (millis() - lastTime > 100) {
updateClockDisplay(); updateClockDisplay();
lastTime = millis();
} }
} }

View File

@ -183,20 +183,30 @@ bool checkNTPResponse()
{ {
int cb = ntpUdp.parsePacket(); int cb = ntpUdp.parsePacket();
if (cb) { if (cb) {
uint32_t ntpPacketReceivedTime = millis();
DEBUG_PRINT(F("NTP recv, l=")); DEBUG_PRINT(F("NTP recv, l="));
DEBUG_PRINTLN(cb); DEBUG_PRINTLN(cb);
byte pbuf[NTP_PACKET_SIZE]; byte pbuf[NTP_PACKET_SIZE];
ntpUdp.read(pbuf, NTP_PACKET_SIZE); // read the packet into the buffer ntpUdp.read(pbuf, NTP_PACKET_SIZE); // read the packet into the buffer
unsigned long highWord = word(pbuf[40], pbuf[41]); Toki::Time arrived = toki.fromNTP(pbuf + 32);
unsigned long lowWord = word(pbuf[42], pbuf[43]); Toki::Time departed = toki.fromNTP(pbuf + 40);
if (highWord == 0 && lowWord == 0) return false; //basic half roundtrip estimation
uint32_t offset = (ntpPacketReceivedTime - ntpPacketSentTime - toki.msDifference(arrived, departed)) >> 1;
unsigned long secsSince1900 = highWord << 16 | lowWord; 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 = ")); DEBUG_PRINT(F("Unix time = "));
unsigned long epoch = secsSince1900 - 2208988799UL; //subtract 70 years -1sec (on avg. more precision) uint32_t epoch = toki.second();
setTime(epoch); if (epoch == 0) return false;
setTime(epoch); //legacy
DEBUG_PRINTLN(epoch); DEBUG_PRINTLN(epoch);
if (countdownTime - now() > 0) countdownOverTriggered = false; if (countdownTime - now() > 0) countdownOverTriggered = false;
// if time changed re-calculate sunrise/sunset // if time changed re-calculate sunrise/sunset
@ -210,7 +220,7 @@ bool checkNTPResponse()
void updateLocalTime() void updateLocalTime()
{ {
if (currentTimezone != tzCurrent) updateTimezone(); if (currentTimezone != tzCurrent) updateTimezone();
unsigned long tmc = now()+ utcOffsetSecs; unsigned long tmc = toki.second()+ utcOffsetSecs;
localTime = tz->toLocal(tmc); localTime = tz->toLocal(tmc);
} }

View File

@ -21,13 +21,16 @@ void initCronixie()
void handleOverlays() void handleOverlays()
{ {
if (millis() - overlayRefreshedTime > overlayRefreshMs) if (toki.isTick())
{ {
initCronixie(); initCronixie();
updateLocalTime(); updateLocalTime();
checkTimers(); checkTimers();
checkCountdown(); checkCountdown();
if (overlayCurrent == 3) _overlayCronixie();//Diamex cronixie clock kit if (overlayCurrent == 3) {
_overlayCronixie();//Diamex cronixie clock kit
strip.trigger();
}
overlayRefreshedTime = millis(); overlayRefreshedTime = millis();
} }
} }

View File

@ -37,7 +37,6 @@
static tmElements_t tm; // a cache of time elements static tmElements_t tm; // a cache of time elements
static time_t cacheTime; // the time the cache was updated 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) { void refreshCache(time_t t) {
if (t != cacheTime) { if (t != cacheTime) {
@ -234,18 +233,9 @@ time_t makeTime(tmElements_t &tm){
/*=====================================================*/ /*=====================================================*/
/* Low level system time functions */ /* 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 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() { time_t now() {
// calculate number of seconds passed since last call to 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 // millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
sysTime++; sysTime++;
prevMillis += 1000; 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; return (time_t)sysTime;
} }
void setTime(time_t t) { uint16_t millisecond() { // the millisecond (0-999) now
#ifdef TIME_DRIFT_INFO return (sysMillis - millis()) % 1000;
if(sysUnsyncedTime == 0) }
sysUnsyncedTime = t; // store the time of the first call to set a valid Time
#endif
void setTime(time_t t, uint16_t ms) {
sysTime = (uint32_t)t; sysTime = (uint32_t)t;
nextSyncTime = (uint32_t)t + syncInterval; sysMillis = ms;
Status = timeSet;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix) 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); return makeTime(tm);
} }
void setTime(int hr,int min,int sec,int dy, int mnth, int 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)); setTime(getUnixTime(hr,min,sec,dy,mnth,yr), ms);
} }
void adjustTime(long adjustment) { void adjustTime(long adjustment) {
sysTime += 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;
}

View File

@ -31,8 +31,6 @@ typedef unsigned long time_t;
// but at least this hack lets us define C++ functions as intended. Hopefully // but at least this hack lets us define C++ functions as intended. Hopefully
// nothing too terrible will result from overriding the C library header?! // nothing too terrible will result from overriding the C library header?!
extern "C++" { extern "C++" {
typedef enum {timeNotSet, timeNeedsSync, timeSet
} timeStatus_t ;
typedef enum { typedef enum {
dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday 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 month(time_t t); // the month for the given time
int year(); // the full four digit year: (2009, 2010 etc) int year(); // the full four digit year: (2009, 2010 etc)
int year(time_t t); // the year for the given time 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 time_t now(); // return the current time as seconds since Jan 1 1970
void setTime(time_t t); void setTime(time_t t, uint16_t ms = 0);
void setTime(int hr,int min,int sec,int day, int month, int yr); 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 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); void adjustTime(long adjustment);
@ -130,12 +129,7 @@ char* dayStr(uint8_t day);
char* monthShortStr(uint8_t month); char* monthShortStr(uint8_t month);
char* dayShortStr(uint8_t day); char* dayShortStr(uint8_t day);
/* time sync functions */ /* low level functions to convert to and from system time */
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 */
void breakTime(time_t time, tmElements_t &tm); // break time_t into elements 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 time_t makeTime(tmElements_t &tm); // convert time elements into time_t

View File

@ -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 <Arduino.h>
#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);
}
};

View File

@ -177,6 +177,8 @@ void WiFiEvent(WiFiEvent_t event)
void WLED::loop() 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 handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
handleConnection(); handleConnection();
handleSerial(); handleSerial();
@ -287,6 +289,7 @@ void WLED::loop()
} }
loops++; loops++;
#endif // WLED_DEBUG #endif // WLED_DEBUG
toki.resetTick();
} }
void WLED::setup() void WLED::setup()

View File

@ -90,6 +90,7 @@
#include <SPIFFSEditor.h> #include <SPIFFSEditor.h>
#include "src/dependencies/time/TimeLib.h" #include "src/dependencies/time/TimeLib.h"
#include "src/dependencies/timezone/Timezone.h" #include "src/dependencies/timezone/Timezone.h"
#include "src/dependencies/toki/Toki.h"
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
#define ESPALEXA_ASYNC #define ESPALEXA_ASYNC
@ -516,6 +517,7 @@ WLED_GLOBAL float longitude _INIT(0.0);
WLED_GLOBAL float latitude _INIT(0.0); WLED_GLOBAL float latitude _INIT(0.0);
WLED_GLOBAL time_t sunrise _INIT(0); WLED_GLOBAL time_t sunrise _INIT(0);
WLED_GLOBAL time_t sunset _INIT(0); WLED_GLOBAL time_t sunset _INIT(0);
WLED_GLOBAL Toki toki _INIT(Toki());
// Temp buffer // Temp buffer
WLED_GLOBAL char* obuf; WLED_GLOBAL char* obuf;