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() {
|
void setup() {
|
||||||
time_t rtcTime = RTC.get();
|
time_t rtcTime = RTC.get();
|
||||||
if (rtcTime) {
|
if (rtcTime) {
|
||||||
toki.setTime(rtcTime,0);
|
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
|
||||||
updateLocalTime();
|
updateLocalTime();
|
||||||
} else {
|
} else {
|
||||||
if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error
|
if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error
|
||||||
|
@ -138,6 +138,7 @@ void setCountdown();
|
|||||||
byte weekdayMondayFirst();
|
byte weekdayMondayFirst();
|
||||||
void checkTimers();
|
void checkTimers();
|
||||||
void calculateSunriseAndSunset();
|
void calculateSunriseAndSunset();
|
||||||
|
void setTimeFromAPI(uint32_t timein);
|
||||||
|
|
||||||
//overlay.cpp
|
//overlay.cpp
|
||||||
void initCronixie();
|
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
|
unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced
|
||||||
if (timein != UINT32_MAX) {
|
if (timein != UINT32_MAX) {
|
||||||
time_t prev = toki.second();
|
setTimeFromAPI(timein);
|
||||||
if (millis() - ntpLastSyncTime > 50000000L) {
|
|
||||||
toki.setTime(timein,toki.millisecond());
|
|
||||||
if (abs(timein - prev) > 60L) {
|
|
||||||
updateLocalTime();
|
|
||||||
calculateSunriseAndSunset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (presetsModifiedTime == 0) presetsModifiedTime = timein;
|
if (presetsModifiedTime == 0) presetsModifiedTime = timein;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ bool checkNTPResponse()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
toki.adjust(departed, offset);
|
toki.adjust(departed, offset);
|
||||||
toki.setTime(departed);
|
toki.setTime(departed, TOKI_TS_NTP);
|
||||||
|
|
||||||
#ifdef WLED_DEBUG_NTP
|
#ifdef WLED_DEBUG_NTP
|
||||||
Serial.print("Arrived: ");
|
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)
|
//set time (unix timestamp)
|
||||||
pos = req.indexOf(F("ST="));
|
pos = req.indexOf(F("ST="));
|
||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
toki.setTime(getNumVal(&req, pos),toki.millisecond());
|
setTimeFromAPI(getNumVal(&req, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
//set countdown goal (unix timestamp)
|
//set countdown goal (unix timestamp)
|
||||||
|
@ -25,7 +25,21 @@
|
|||||||
|
|
||||||
#define YEARS_70 2208988800UL
|
#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 {
|
class Toki {
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -42,16 +56,22 @@ class Toki {
|
|||||||
uint32_t fullSecondMillis = 0;
|
uint32_t fullSecondMillis = 0;
|
||||||
uint32_t unix = 0;
|
uint32_t unix = 0;
|
||||||
TickT tick = TickT::inactive;
|
TickT tick = TickT::inactive;
|
||||||
|
uint8_t timeSrc = TOKI_TS_NONE;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setTime(Time t) {
|
void setTime(Time t, uint8_t timeSource = TOKI_TS_MS) {
|
||||||
fullSecondMillis = millis() - t.ms;
|
fullSecondMillis = millis() - t.ms;
|
||||||
unix = t.sec;
|
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};
|
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
|
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;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t getTimeSource() {
|
||||||
|
return timeSrc;
|
||||||
|
}
|
||||||
|
|
||||||
void setTick() {
|
void setTick() {
|
||||||
if (tick == TickT::marked) tick = TickT::active;
|
if (tick == TickT::marked) tick = TickT::active;
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
* UDP sync notifier / Realtime / Hyperion / TPM2.NET
|
* UDP sync notifier / Realtime / Hyperion / TPM2.NET
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define WLEDPACKETSIZE 29
|
#define WLEDPACKETSIZE 36
|
||||||
#define UDP_IN_MAXSIZE 1472
|
#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)
|
void notify(byte callMode, bool followUp)
|
||||||
{
|
{
|
||||||
@ -37,8 +38,8 @@ void notify(byte callMode, bool followUp)
|
|||||||
//compatibilityVersionByte:
|
//compatibilityVersionByte:
|
||||||
//0: old 1: supports white 2: supports secondary color
|
//0: old 1: supports white 2: supports secondary color
|
||||||
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
|
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
|
||||||
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color
|
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
|
||||||
udpOut[11] = 7;
|
udpOut[11] = 8;
|
||||||
udpOut[12] = colSec[0];
|
udpOut[12] = colSec[0];
|
||||||
udpOut[13] = colSec[1];
|
udpOut[13] = colSec[1];
|
||||||
udpOut[14] = colSec[2];
|
udpOut[14] = colSec[2];
|
||||||
@ -59,6 +60,18 @@ void notify(byte callMode, bool followUp)
|
|||||||
udpOut[26] = (t >> 16) & 0xFF;
|
udpOut[26] = (t >> 16) & 0xFF;
|
||||||
udpOut[27] = (t >> 8) & 0xFF;
|
udpOut[27] = (t >> 8) & 0xFF;
|
||||||
udpOut[28] = (t >> 0) & 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;
|
IPAddress broadcastIp;
|
||||||
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
|
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
|
//ignore notification if received within a second after sending a notification ourselves
|
||||||
if (millis() - notificationSentTime < 1000) return;
|
if (millis() - notificationSentTime < 1000) return;
|
||||||
if (udpIn[1] > 199) return; //do not receive custom versions
|
if (udpIn[1] > 199) return; //do not receive custom versions
|
||||||
|
|
||||||
|
//compatibilityVersionByte:
|
||||||
|
byte version = udpIn[11];
|
||||||
|
|
||||||
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||||
//apply colors from notification
|
//apply colors from notification
|
||||||
@ -212,24 +228,17 @@ void handleNotifications()
|
|||||||
col[0] = udpIn[3];
|
col[0] = udpIn[3];
|
||||||
col[1] = udpIn[4];
|
col[1] = udpIn[4];
|
||||||
col[2] = udpIn[5];
|
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];
|
col[3] = udpIn[10];
|
||||||
if (udpIn[11] > 1)
|
if (version > 1)
|
||||||
{
|
{
|
||||||
colSec[0] = udpIn[12];
|
colSec[0] = udpIn[12];
|
||||||
colSec[1] = udpIn[13];
|
colSec[1] = udpIn[13];
|
||||||
colSec[2] = udpIn[14];
|
colSec[2] = udpIn[14];
|
||||||
colSec[3] = udpIn[15];
|
colSec[3] = udpIn[15];
|
||||||
}
|
}
|
||||||
if (udpIn[11] > 5)
|
if (version > 6)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
|
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
|
||||||
}
|
}
|
||||||
@ -237,15 +246,36 @@ void handleNotifications()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//apply effects from notification
|
//apply effects from notification
|
||||||
if (udpIn[11] < 200 && (receiveNotificationEffects || !someSel))
|
if (version < 200 && (receiveNotificationEffects || !someSel))
|
||||||
{
|
{
|
||||||
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
|
if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
|
||||||
effectSpeed = udpIn[9];
|
effectSpeed = udpIn[9];
|
||||||
if (udpIn[11] > 2) effectIntensity = udpIn[16];
|
if (version > 2) effectIntensity = udpIn[16];
|
||||||
if (udpIn[11] > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
|
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);
|
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user