Add UDP sync of system time
This commit is contained in:
parent
6c8bf090fe
commit
c2892d7887
@ -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
|
||||
|
@ -138,6 +138,7 @@ void setCountdown();
|
||||
byte weekdayMondayFirst();
|
||||
void checkTimers();
|
||||
void calculateSunriseAndSunset();
|
||||
void setTimeFromAPI(uint32_t timein);
|
||||
|
||||
//overlay.cpp
|
||||
void initCronixie();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
@ -59,6 +60,18 @@ void notify(byte callMode, bool followUp)
|
||||
udpOut[26] = (t >> 16) & 0xFF;
|
||||
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());
|
||||
@ -204,6 +217,9 @@ void handleNotifications()
|
||||
//ignore notification if received within a second after sending a notification ourselves
|
||||
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
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
//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 (udpIn[11] > 3)
|
||||
if (version > 3)
|
||||
{
|
||||
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user