timeserver(experimental)

This commit is contained in:
Verkehrsrot 2019-03-10 17:35:57 +01:00
parent f9f5f499ff
commit c13934be99
7 changed files with 242 additions and 61 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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,

View File

@ -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);

View 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": ""
}
]

View File

@ -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:

View File

@ -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

View File

@ -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;