timeserver(experimental)
This commit is contained in:
parent
f9f5f499ff
commit
c13934be99
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,4 +11,3 @@
|
|||||||
.gcc-flags.json
|
.gcc-flags.json
|
||||||
src/loraconf.h
|
src/loraconf.h
|
||||||
src/ota.conf
|
src/ota.conf
|
||||||
src/NodeRed/Timeserver.json
|
|
@ -42,7 +42,6 @@ public:
|
|||||||
uint8_t getSize(void);
|
uint8_t getSize(void);
|
||||||
uint8_t *getBuffer(void);
|
uint8_t *getBuffer(void);
|
||||||
void addByte(uint8_t value);
|
void addByte(uint8_t value);
|
||||||
void addWord(uint16_t value);
|
|
||||||
void addCount(uint16_t value, uint8_t sniffytpe);
|
void addCount(uint16_t value, uint8_t sniffytpe);
|
||||||
void addConfig(configData_t value);
|
void addConfig(configData_t value);
|
||||||
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
||||||
|
@ -16,10 +16,6 @@ typedef struct {
|
|||||||
uint8_t fractions; // 1/250ths second = 4 milliseconds resolution
|
uint8_t fractions; // 1/250ths second = 4 milliseconds resolution
|
||||||
} time_sync_message_t;
|
} time_sync_message_t;
|
||||||
|
|
||||||
extern time_sync_message_t time_sync_messages[], time_sync_answers[];
|
|
||||||
extern uint8_t time_sync_seqNo;
|
|
||||||
extern bool time_sync_pending;
|
|
||||||
|
|
||||||
void send_Servertime_req(void);
|
void send_Servertime_req(void);
|
||||||
void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len);
|
void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len);
|
||||||
void process_Servertime_sync_req(void *taskparameter);
|
void process_Servertime_sync_req(void *taskparameter);
|
||||||
|
214
src/TTN/Nodered-Timeserver.json
Normal file
214
src/TTN/Nodered-Timeserver.json
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "49e3c067.e782e",
|
||||||
|
"type": "change",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "Payload",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"t": "change",
|
||||||
|
"p": "topic",
|
||||||
|
"pt": "msg",
|
||||||
|
"from": "up",
|
||||||
|
"fromt": "str",
|
||||||
|
"to": "down",
|
||||||
|
"tot": "str"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"t": "move",
|
||||||
|
"p": "payload",
|
||||||
|
"pt": "msg",
|
||||||
|
"to": "payload.payload_raw",
|
||||||
|
"tot": "msg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"t": "set",
|
||||||
|
"p": "payload.port",
|
||||||
|
"pt": "msg",
|
||||||
|
"to": "9",
|
||||||
|
"tot": "num"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"t": "set",
|
||||||
|
"p": "payload.confirmed",
|
||||||
|
"pt": "msg",
|
||||||
|
"to": "false",
|
||||||
|
"tot": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"t": "set",
|
||||||
|
"p": "payload.schedule",
|
||||||
|
"pt": "msg",
|
||||||
|
"to": "replace",
|
||||||
|
"tot": "str"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"action": "",
|
||||||
|
"property": "",
|
||||||
|
"from": "",
|
||||||
|
"to": "",
|
||||||
|
"reg": false,
|
||||||
|
"x": 540,
|
||||||
|
"y": 340,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"84f1cda2.069e7"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cc140589.dea168",
|
||||||
|
"type": "mqtt in",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "listen",
|
||||||
|
"topic": "+/devices/+/up",
|
||||||
|
"qos": "2",
|
||||||
|
"broker": "2a15ab6f.ab2244",
|
||||||
|
"x": 130,
|
||||||
|
"y": 140,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"9f4b8dd3.2f0d2"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "72d5e7ee.d1eba8",
|
||||||
|
"type": "mqtt out",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "send",
|
||||||
|
"topic": "",
|
||||||
|
"qos": "",
|
||||||
|
"retain": "",
|
||||||
|
"broker": "2a15ab6f.ab2244",
|
||||||
|
"x": 710,
|
||||||
|
"y": 460,
|
||||||
|
"wires": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4f97d75.6c87528",
|
||||||
|
"type": "json",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "Convert",
|
||||||
|
"property": "payload",
|
||||||
|
"action": "",
|
||||||
|
"pretty": false,
|
||||||
|
"x": 340,
|
||||||
|
"y": 240,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"8ed813a9.a9319"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9f4b8dd3.2f0d2",
|
||||||
|
"type": "switch",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "Timeport",
|
||||||
|
"property": "payload",
|
||||||
|
"propertyType": "msg",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"t": "cont",
|
||||||
|
"v": "\"port\":9",
|
||||||
|
"vt": "str"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checkall": "true",
|
||||||
|
"repair": false,
|
||||||
|
"outputs": 1,
|
||||||
|
"x": 340,
|
||||||
|
"y": 140,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"4f97d75.6c87528"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f4c5b6de.f95148",
|
||||||
|
"type": "function",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "Time_Sync_Ans",
|
||||||
|
"func": "/* LoRaWAN Timeserver\n\nconstruct 6 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n0 sequence number (taken from node's time_sync_req)\n1..4 current second (from epoch time 1970)\n5 1/250ths fractions of current second\n\n*/\n\n let buf = new ArrayBuffer(6);\n let timestamp = (+new Date(msg.payload.metadata.time));\n\n var seconds = Math.floor(timestamp/1000);\n var fractions = timestamp % 250;\n var seqno = msg.payload.payload_raw[0];\n\n new DataView(buf).setUint8(0, seqno);\n new DataView(buf).setUint32(1, seconds);\n new DataView(buf).setUint8(5, fractions);\n\n msg.payload = new Buffer(new Uint8Array(buf));\n \n return msg;",
|
||||||
|
"outputs": 1,
|
||||||
|
"noerr": 0,
|
||||||
|
"x": 360,
|
||||||
|
"y": 340,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"49e3c067.e782e"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dac8aafa.389298",
|
||||||
|
"type": "json",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "Convert",
|
||||||
|
"property": "payload",
|
||||||
|
"action": "",
|
||||||
|
"pretty": false,
|
||||||
|
"x": 480,
|
||||||
|
"y": 460,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"72d5e7ee.d1eba8"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8ed813a9.a9319",
|
||||||
|
"type": "base64",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "Decode",
|
||||||
|
"action": "",
|
||||||
|
"property": "payload.payload_raw",
|
||||||
|
"x": 480,
|
||||||
|
"y": 240,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"f4c5b6de.f95148"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "84f1cda2.069e7",
|
||||||
|
"type": "base64",
|
||||||
|
"z": "449c1517.e25f4c",
|
||||||
|
"name": "Encode",
|
||||||
|
"action": "",
|
||||||
|
"property": "payload.payload_raw",
|
||||||
|
"x": 340,
|
||||||
|
"y": 460,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"dac8aafa.389298"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2a15ab6f.ab2244",
|
||||||
|
"type": "mqtt-broker",
|
||||||
|
"z": "",
|
||||||
|
"name": "eu.thethings.network:1883",
|
||||||
|
"broker": "eu.thethings.network",
|
||||||
|
"port": "1883",
|
||||||
|
"tls": "",
|
||||||
|
"clientid": "",
|
||||||
|
"usetls": false,
|
||||||
|
"compatmode": true,
|
||||||
|
"keepalive": "60",
|
||||||
|
"cleansession": true,
|
||||||
|
"birthTopic": "",
|
||||||
|
"birthQos": "0",
|
||||||
|
"birthPayload": "",
|
||||||
|
"closeTopic": "",
|
||||||
|
"closeQos": "0",
|
||||||
|
"closePayload": "",
|
||||||
|
"willTopic": "",
|
||||||
|
"willQos": "0",
|
||||||
|
"willPayload": ""
|
||||||
|
}
|
||||||
|
]
|
@ -20,11 +20,6 @@ uint8_t *PayloadConvert::getBuffer(void) { return buffer; }
|
|||||||
|
|
||||||
void PayloadConvert::addByte(uint8_t value) { buffer[cursor++] = (value); }
|
void PayloadConvert::addByte(uint8_t value) { buffer[cursor++] = (value); }
|
||||||
|
|
||||||
void PayloadConvert::addWord(uint16_t value) {
|
|
||||||
buffer[cursor++] = lowByte(value);
|
|
||||||
buffer[cursor++] = highByte(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
||||||
buffer[cursor++] = highByte(value);
|
buffer[cursor++] = highByte(value);
|
||||||
buffer[cursor++] = lowByte(value);
|
buffer[cursor++] = lowByte(value);
|
||||||
@ -150,8 +145,6 @@ void PayloadConvert::addTime(time_t value) {
|
|||||||
|
|
||||||
void PayloadConvert::addByte(uint8_t value) { writeUint8(value); }
|
void PayloadConvert::addByte(uint8_t value) { writeUint8(value); }
|
||||||
|
|
||||||
void PayloadConvert::addWord(uint16_t value) { writeUint16(value); }
|
|
||||||
|
|
||||||
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
||||||
writeUint16(value);
|
writeUint16(value);
|
||||||
}
|
}
|
||||||
@ -315,11 +308,6 @@ void PayloadConvert::addByte(uint8_t value) {
|
|||||||
not implemented
|
not implemented
|
||||||
*/ }
|
*/ }
|
||||||
|
|
||||||
void PayloadConvert::addWord(uint16_t value) {
|
|
||||||
/*
|
|
||||||
not implemented
|
|
||||||
*/ }
|
|
||||||
|
|
||||||
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
||||||
switch (snifftype) {
|
switch (snifftype) {
|
||||||
case MAC_SNIFF_WIFI:
|
case MAC_SNIFF_WIFI:
|
||||||
|
@ -40,16 +40,8 @@ void SendPayload(uint8_t port, sendprio_t prio) {
|
|||||||
|
|
||||||
// enqueue message in device's send queues
|
// enqueue message in device's send queues
|
||||||
#ifdef HAS_LORA
|
#ifdef HAS_LORA
|
||||||
/*
|
|
||||||
// pause send any data but timeport data, while timesync handshake is ongoing
|
|
||||||
if (port != TIMEPORT) {
|
|
||||||
if (!time_sync_pending)
|
|
||||||
lora_enqueuedata(&SendBuffer, prio);
|
|
||||||
} else
|
|
||||||
*/
|
|
||||||
lora_enqueuedata(&SendBuffer, prio);
|
lora_enqueuedata(&SendBuffer, prio);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SPI
|
#ifdef HAS_SPI
|
||||||
spi_enqueuedata(&SendBuffer, prio);
|
spi_enqueuedata(&SendBuffer, prio);
|
||||||
#endif
|
#endif
|
||||||
|
@ -5,8 +5,7 @@
|
|||||||
PLEASE NOTE: There is a patent filed for the time sync algorithm used in the
|
PLEASE NOTE: There is a patent filed for the time sync algorithm used in the
|
||||||
code of this file. The shown implementation example is covered by the
|
code of this file. The shown implementation example is covered by the
|
||||||
repository's licencse, but you may not be eligible to deploy the applied
|
repository's licencse, but you may not be eligible to deploy the applied
|
||||||
algorithm in applications without granted license for the algorithm by the
|
algorithm in applications without granted license by the patent holder.
|
||||||
patent holder.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -37,7 +36,7 @@ void send_Servertime_req() {
|
|||||||
|
|
||||||
// clear timestamp array
|
// clear timestamp array
|
||||||
for (uint8_t i = 0; i <= TIME_SYNC_SAMPLES + 1; i++) {
|
for (uint8_t i = 0; i <= TIME_SYNC_SAMPLES + 1; i++) {
|
||||||
time_sync_messages[i].seconds = time_sync_answers[i].seconds = 0;
|
time_sync_messages[i].seconds = time_sync_answers[i].seconds =
|
||||||
time_sync_messages[i].fractions = time_sync_answers[i].fractions = 0;
|
time_sync_messages[i].fractions = time_sync_answers[i].fractions = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,12 +62,9 @@ void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len) {
|
|||||||
uint8_t seq_no = buf[0];
|
uint8_t seq_no = buf[0];
|
||||||
uint32_t timestamp_sec = 0, timestamp_ms = 0;
|
uint32_t timestamp_sec = 0, timestamp_ms = 0;
|
||||||
|
|
||||||
for (int i = 1; i <= 4; i++) {
|
for (uint8_t i = 1; i <= 4; i++)
|
||||||
timestamp_sec = (timestamp_sec << 8) | buf[i];
|
time_sync_answers[seq_no].seconds = (timestamp_sec <<= 8) |= buf[i];
|
||||||
time_sync_answers[seq_no].seconds = timestamp_sec;
|
time_sync_answers[seq_no].fractions = timestamp_ms = 4 * buf[5];
|
||||||
}
|
|
||||||
timestamp_ms = 4 * buf[5];
|
|
||||||
time_sync_answers[seq_no].fractions = timestamp_ms;
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Timeserver answer #%d received: timestamp=%d.%03d", seq_no,
|
ESP_LOGD(TAG, "Timeserver answer #%d received: timestamp=%d.%03d", seq_no,
|
||||||
timestamp_sec, timestamp_ms);
|
timestamp_sec, timestamp_ms);
|
||||||
@ -81,8 +77,10 @@ void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len) {
|
|||||||
// task for sending time sync requests
|
// task for sending time sync requests
|
||||||
void process_Servertime_sync_req(void *taskparameter) {
|
void process_Servertime_sync_req(void *taskparameter) {
|
||||||
|
|
||||||
uint32_t seq_no = 0, time_diff_sec = 0, time_diff_ms = 0;
|
|
||||||
time_t time_to_set = 0;
|
time_t time_to_set = 0;
|
||||||
|
uint32_t seq_no = 0;
|
||||||
|
int16_t time_diff_ms = 0;
|
||||||
|
int64_t time_diff_sec = 0;
|
||||||
float time_offset = 0.0f;
|
float time_offset = 0.0f;
|
||||||
|
|
||||||
// enqueue timestamp samples in lora sendqueue
|
// enqueue timestamp samples in lora sendqueue
|
||||||
@ -97,46 +95,41 @@ void process_Servertime_sync_req(void *taskparameter) {
|
|||||||
SendPayload(TIMEPORT, prio_high);
|
SendPayload(TIMEPORT, prio_high);
|
||||||
|
|
||||||
// send dummy packet to trigger receive answer
|
// send dummy packet to trigger receive answer
|
||||||
payload.reset();
|
//payload.reset();
|
||||||
payload.addByte(0x99); // flush
|
//payload.addByte(0x99); // flush
|
||||||
SendPayload(RCMDPORT, prio_low); // open receive slot for answer
|
//SendPayload(RCMDPORT, prio_low); // to open receive slot for answer
|
||||||
|
|
||||||
// process answer
|
// process answer
|
||||||
if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no,
|
if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no,
|
||||||
TIME_SYNC_TIMEOUT * 1000 / portTICK_PERIOD_MS) ==
|
TIME_SYNC_TIMEOUT * 1000 / portTICK_PERIOD_MS) ==
|
||||||
pdFALSE) ||
|
pdFALSE) ||
|
||||||
(seq_no != time_sync_seqNo)) {
|
(seq_no != time_sync_seqNo)) {
|
||||||
ESP_LOGW(TAG, "Timeserver handshake error");
|
ESP_LOGW(TAG, "Timeserver handshake failed");
|
||||||
goto finish;
|
goto finish;
|
||||||
} // no valid sequence received before timeout
|
} // no valid sequence received before timeout
|
||||||
|
|
||||||
else { // calculate time diff from set of collected timestamps
|
else { // calculate time diff from set of collected timestamps
|
||||||
uint8_t k = seq_no % TIME_SYNC_SAMPLES;
|
uint8_t k = seq_no % TIME_SYNC_SAMPLES;
|
||||||
time_diff_sec +=
|
time_diff_sec +=
|
||||||
time_sync_messages[k].seconds - time_sync_answers[k].seconds;
|
(time_sync_messages[k].seconds - time_sync_answers[k].seconds);
|
||||||
time_diff_ms += 4 * (time_sync_messages[k].fractions -
|
time_diff_ms += (4 * (time_sync_messages[k].fractions -
|
||||||
time_sync_answers[k].fractions);
|
time_sync_answers[k].fractions));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // for
|
||||||
|
|
||||||
// calculate time offset and set time if necessary
|
// calculate time offset and set time if necessary
|
||||||
time_offset =
|
time_offset = (time_diff_sec + time_diff_ms / 1000.0f) / TIME_SYNC_SAMPLES;
|
||||||
(time_diff_sec + time_diff_ms / 1000.0f) / (TIME_SYNC_SAMPLES * 1.0f);
|
ESP_LOGD(TAG, "Timesync time offset=%.3f", time_offset);
|
||||||
|
ESP_LOGD(TAG, "Timesync time_diff_ms=%03d", time_diff_ms);
|
||||||
ESP_LOGD(TAG, "Timesync time offset=%d.%03d",
|
|
||||||
time_diff_sec / TIME_SYNC_SAMPLES, time_diff_ms / TIME_SYNC_SAMPLES);
|
|
||||||
|
|
||||||
if (time_offset >= TIME_SYNC_TRIGGER) {
|
if (time_offset >= TIME_SYNC_TRIGGER) {
|
||||||
|
|
||||||
// wait until top of second
|
// wait until top of second
|
||||||
if (time_diff_ms > 0) {
|
if (time_diff_ms > 0) // clock is fast
|
||||||
vTaskDelay(1000 - time_diff_ms); // clock is fast
|
vTaskDelay(pdMS_TO_TICKS(time_diff_ms));
|
||||||
time_diff_sec--;
|
else if (time_diff_ms < 0) // clock is slow
|
||||||
} else if (time_diff_ms < 0) { // clock is stale
|
vTaskDelay(pdMS_TO_TICKS(1000 + time_diff_ms));
|
||||||
vTaskDelay(1000 + time_diff_ms);
|
|
||||||
time_diff_sec++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
time_diff_sec++;
|
||||||
time_to_set = time_t(now() + time_diff_sec);
|
time_to_set = time_t(now() + time_diff_sec);
|
||||||
ESP_LOGD(TAG, "Time to set = %d", time_to_set);
|
ESP_LOGD(TAG, "Time to set = %d", time_to_set);
|
||||||
|
|
||||||
@ -160,7 +153,7 @@ finish:
|
|||||||
vTaskDelete(NULL); // end task
|
vTaskDelete(NULL); // end task
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from lorawan.cpp immediately after time_sync_req was sent
|
// called from lorawan.cpp after time_sync_req was sent
|
||||||
void store_time_sync_req(time_t secs, uint32_t micros) {
|
void store_time_sync_req(time_t secs, uint32_t micros) {
|
||||||
uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES;
|
uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES;
|
||||||
time_sync_messages[k].seconds = secs;
|
time_sync_messages[k].seconds = secs;
|
||||||
|
Loading…
Reference in New Issue
Block a user