Add UDP sync of system time

This commit is contained in:
cschwinne 2021-05-27 02:02:02 +02:00
parent 6c8bf090fe
commit c2892d7887
7 changed files with 95 additions and 32 deletions

View File

@ -14,7 +14,7 @@ class RTCUsermod : public Usermod {
void setup() {
time_t rtcTime = RTC.get();
if (rtcTime) {
toki.setTime(rtcTime,0);
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
updateLocalTime();
} else {
if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error

View File

@ -138,6 +138,7 @@ void setCountdown();
byte weekdayMondayFirst();
void checkTimers();
void calculateSunriseAndSunset();
void setTimeFromAPI(uint32_t timein);
//overlay.cpp
void initCronixie();

View File

@ -210,14 +210,7 @@ bool deserializeState(JsonObject root)
unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced
if (timein != UINT32_MAX) {
time_t prev = toki.second();
if (millis() - ntpLastSyncTime > 50000000L) {
toki.setTime(timein,toki.millisecond());
if (abs(timein - prev) > 60L) {
updateLocalTime();
calculateSunriseAndSunset();
}
}
setTimeFromAPI(timein);
if (presetsModifiedTime == 0) presetsModifiedTime = timein;
}

View File

@ -219,7 +219,7 @@ bool checkNTPResponse()
#endif
toki.adjust(departed, offset);
toki.setTime(departed);
toki.setTime(departed, TOKI_TS_NTP);
#ifdef WLED_DEBUG_NTP
Serial.print("Arrived: ");
@ -433,3 +433,18 @@ void calculateSunriseAndSunset() {
}
}
}
//time from JSON and HTTP API
void setTimeFromAPI(uint32_t timein) {
if (timein == 0 || timein == UINT32_MAX) return;
time_t prev = toki.second();
//only apply if more accurate or there is a significant difference to the "more accurate" time source
if (toki.getTimeSource() > TOKI_TS_JSON && abs(timein - prev) < 60L) return;
toki.setTime(timein, TOKI_NO_MS_ACCURACY, TOKI_TS_JSON);
if (abs(timein - prev) > 60L) {
updateLocalTime();
calculateSunriseAndSunset();
}
if (presetsModifiedTime == 0) presetsModifiedTime = timein;
}

View File

@ -790,7 +790,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set time (unix timestamp)
pos = req.indexOf(F("ST="));
if (pos > 0) {
toki.setTime(getNumVal(&req, pos),toki.millisecond());
setTimeFromAPI(getNumVal(&req, pos));
}
//set countdown goal (unix timestamp)

View File

@ -25,7 +25,21 @@
#define YEARS_70 2208988800UL
//3:13 24.05.2012 ==> 2021-4-29, 06:41:37.
#define TOKI_NO_MS_ACCURACY 1000
//Time source. Sub-100 is second accuracy, higher ms accuracy. Higher value generally means more accurate
#define TOKI_TS_NONE 0 //unsynced (e.g. just after boot)
#define TOKI_TS_UDP 5 //synced via UDP from an instance whose time source is unsynced
#define TOKI_TS_BAD 10 //synced from a time source less than about +- 2s accurate
#define TOKI_TS_UDP_SEC 20 //synced via UDP from an instance whose time source is set from RTC/JSON
#define TOKI_TS_SEC 40 //general second-accurate time source
#define TOKI_TS_RTC 60 //second-accurate real time clock
#define TOKI_TS_JSON 70 //synced second-accurate from a client via JSON-API
#define TOKI_TS_UDP_NTP 110 //synced via UDP from an instance whose time source is NTP
#define TOKI_TS_MS 120 //general better-than-second accuracy time source
#define TOKI_TS_NTP 150 //NTP time, simple round trip estimation. Depending on network, mostly +- 50ms accurate
#define TOKI_TS_NTP_P 170 //NTP time with multi-step sync, higher accuracy. Not implemented in WLED
class Toki {
typedef enum {
@ -42,16 +56,22 @@ class Toki {
uint32_t fullSecondMillis = 0;
uint32_t unix = 0;
TickT tick = TickT::inactive;
uint8_t timeSrc = TOKI_TS_NONE;
public:
void setTime(Time t) {
void setTime(Time t, uint8_t timeSource = TOKI_TS_MS) {
fullSecondMillis = millis() - t.ms;
unix = t.sec;
timeSrc = timeSource;
}
void setTime(uint32_t sec, uint16_t ms) {
void setTime(uint32_t sec, uint16_t ms=TOKI_NO_MS_ACCURACY, uint8_t timeSource = TOKI_TS_MS) {
if (ms >= TOKI_NO_MS_ACCURACY) {
ms = millisecond(); //just keep current ms if not provided
if (timeSource > 99) timeSource = TOKI_TS_SEC; //lies
}
Time t = {sec, ms};
setTime(t);
setTime(t, timeSource);
}
Time fromNTP(byte *timestamp) { //ntp timestamp is 8 bytes, 4 bytes second and 4 bytes sub-second fraction
@ -110,6 +130,10 @@ class Toki {
return t;
}
uint8_t getTimeSource() {
return timeSrc;
}
void setTick() {
if (tick == TickT::marked) tick = TickT::active;
}

View File

@ -4,8 +4,9 @@
* UDP sync notifier / Realtime / Hyperion / TPM2.NET
*/
#define WLEDPACKETSIZE 29
#define WLEDPACKETSIZE 36
#define UDP_IN_MAXSIZE 1472
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
void notify(byte callMode, bool followUp)
{
@ -37,8 +38,8 @@ void notify(byte callMode, bool followUp)
//compatibilityVersionByte:
//0: old 1: supports white 2: supports secondary color
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color
udpOut[11] = 7;
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
udpOut[11] = 8;
udpOut[12] = colSec[0];
udpOut[13] = colSec[1];
udpOut[14] = colSec[2];
@ -60,6 +61,18 @@ void notify(byte callMode, bool followUp)
udpOut[27] = (t >> 8) & 0xFF;
udpOut[28] = (t >> 0) & 0xFF;
//sync system time
udpOut[29] = toki.getTimeSource();
Toki::Time tm = toki.getTime();
uint32_t unix = tm.sec;
udpOut[30] = (unix >> 24) & 0xFF;
udpOut[31] = (unix >> 16) & 0xFF;
udpOut[32] = (unix >> 8) & 0xFF;
udpOut[33] = (unix >> 0) & 0xFF;
uint16_t ms = tm.ms;
udpOut[34] = (ms >> 8) & 0xFF;
udpOut[35] = (ms >> 0) & 0xFF;
IPAddress broadcastIp;
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
@ -205,6 +218,9 @@ void handleNotifications()
if (millis() - notificationSentTime < 1000) return;
if (udpIn[1] > 199) return; //do not receive custom versions
//compatibilityVersionByte:
byte version = udpIn[11];
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification
if (receiveNotificationColor || !someSel)
@ -212,24 +228,17 @@ void handleNotifications()
col[0] = udpIn[3];
col[1] = udpIn[4];
col[2] = udpIn[5];
if (udpIn[11] > 0) //sending module's white val is intended
if (version > 0) //sending module's white val is intended
{
col[3] = udpIn[10];
if (udpIn[11] > 1)
if (version > 1)
{
colSec[0] = udpIn[12];
colSec[1] = udpIn[13];
colSec[2] = udpIn[14];
colSec[3] = udpIn[15];
}
if (udpIn[11] > 5)
{
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += 2;
t -= millis();
strip.timebase = t;
}
if (udpIn[11] > 6)
if (version > 6)
{
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
}
@ -237,15 +246,36 @@ void handleNotifications()
}
//apply effects from notification
if (udpIn[11] < 200 && (receiveNotificationEffects || !someSel))
if (version < 200 && (receiveNotificationEffects || !someSel))
{
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
effectSpeed = udpIn[9];
if (udpIn[11] > 2) effectIntensity = udpIn[16];
if (udpIn[11] > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
if (version > 2) effectIntensity = udpIn[16];
if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
if (version > 5)
{
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis();
strip.timebase = t;
}
}
if (udpIn[11] > 3)
//adjust system time, but only if sender is more accurate than self
if (version > 7 && udpIn[29] > toki.getTimeSource())
{
Toki::Time tm;
tm.sec = (udpIn[30] << 24) | (udpIn[31] << 16) | (udpIn[32] << 8) | (udpIn[33]);
tm.ms = (udpIn[34] << 8) | (udpIn[35]);
toki.adjust(tm, PRESUMED_NETWORK_DELAY); //adjust trivially for network delay
uint8_t ts = TOKI_TS_UDP;
if (udpIn[29] > 99) ts = TOKI_TS_UDP_NTP;
else if (udpIn[29] >= TOKI_TS_SEC) ts = TOKI_TS_UDP_SEC;
toki.setTime(tm, ts);
//TODO: even receive this if own time source is better, to offset network delay from timebase
}
if (version > 3)
{
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
}