From 5319f1c976b8b74b532de5d85763e4b99bb30e9a Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Tue, 7 May 2019 18:19:13 +0200 Subject: [PATCH 01/23] configure node-red for ttn app --- src/Timeserver/Nodered-Timeserver.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Timeserver/Nodered-Timeserver.json b/src/Timeserver/Nodered-Timeserver.json index 85e52607..b4d27d80 100644 --- a/src/Timeserver/Nodered-Timeserver.json +++ b/src/Timeserver/Nodered-Timeserver.json @@ -345,7 +345,7 @@ "type": "mqtt-broker", "z": "", "name": "eu.thethings.network:1883", - "broker": "eu.thethings.network", + "broker": "mqtt://paxcounter-timediff:ttn-account-v2.Fyf_ZgT4lBM2A3Gj-4SpNPp0-_tPkqtPRMJ_a-w1aT4@eu.thethings.network/", "port": "1883", "tls": "", "clientid": "", From 26c1cb0aaac487ee468d8d327840ab577cb1bf1f Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Tue, 7 May 2019 18:20:12 +0200 Subject: [PATCH 02/23] config for time sync via lora --- src/paxcounter.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 30dcedc0..9dffe04e 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -94,6 +94,7 @@ #define BMEPORT 7 // BME680 sensor #define BATTPORT 8 // battery voltage #define TIMEPORT 9 // time query +#define TIMEDIFFPORT 13 // time adjust diff #define TIMEANSWERPORT_MIN 0xA0 // time answer, start of port range #define TIMEANSWERPORT_MAX 0xDF // time answer, end of port range #define SENSOR1PORT 10 // user sensor #1 From fdc971be0484cd6af9d0ccc1665616b0397fa7fe Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Tue, 7 May 2019 18:20:59 +0200 Subject: [PATCH 03/23] send timediff with ms after time sync --- include/payload.h | 1 + src/payload.cpp | 15 +++++++++++---- src/timesync.cpp | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/include/payload.h b/include/payload.h index b41bb113..8ae86e6e 100644 --- a/include/payload.h +++ b/include/payload.h @@ -55,6 +55,7 @@ public: void addButton(uint8_t value); void addSensor(uint8_t[]); void addTime(time_t value); + void addTimeDiff(int32_t value); #if (PAYLOAD_ENCODER == 1) // format plain diff --git a/src/payload.cpp b/src/payload.cpp index 8afd0b3b..ac310c55 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -16,7 +16,14 @@ uint8_t *PayloadConvert::getBuffer(void) { return buffer; } /* ---------------- plain format without special encoding ---------- */ -#if (PAYLOAD_ENCODER == 1) +void PayloadConvert::addTimeDiff(int32_t value) { + buffer[cursor++] = (byte)((value & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((value & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((value & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((value & 0x000000FF)); +} + +#if PAYLOAD_ENCODER == 1 void PayloadConvert::addByte(uint8_t value) { buffer[cursor++] = (value); } @@ -308,8 +315,8 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, #elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4)) -void PayloadConvert::addByte(uint8_t value) { - /* +void PayloadConvert::addByte(uint8_t value) { + /* not implemented */ } @@ -486,4 +493,4 @@ void PayloadConvert::addTime(time_t value) { #endif } -#endif \ No newline at end of file +#endif diff --git a/src/timesync.cpp b/src/timesync.cpp index ef50041d..2c5b5db0 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -240,7 +240,42 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, CLOCKIRQ(); // fire clock pps, this advances time 1 sec #endif - setTime(time_to_set); // set the time on top of second + struct timeval tv; + struct timezone tz; + if(gettimeofday(&tv, &tz) != 0) { + ESP_LOGI(TAG, "ERROR gettimeofday"); + } + struct timeval before = tv; + + struct timeval now; + now.tv_sec = t_sec; + now.tv_usec = t_msec; + if(settimeofday(&tv, &tz) != 0) { + ESP_LOGE(TAG, "ERROR settimeofday"); + } + + struct timeval diff; + diff.tv_sec = now.tv_sec-before.tv_sec; + diff.tv_usec = now.tv_usec-before.tv_usec; + + // sum up diff_s and diff_ms to one ms value + int32_t diff_s = diff.tv_sec; + int32_t diff_ms = diff.tv_usec/1000; + int32_t diff_ms_remain = diff_ms / 1000; + diff_s += diff_ms_remain; + diff_ms += -1000*diff_ms_remain; + if(diff_ms < 0) { + diff_s --; + diff_ms += 1000; + } + // cap diff at 24h (= 86,400s) + diff_s = diff_s % 86400; + int32_t timediff_ms = diff_s * 1000 + diff_ms; + + // send diffTime + payload.reset(); + payload.addTimeDiff(timediff_ms); + SendPayload(TIMEDIFFPORT, prio_high); timeSource = mytimesource; // set global variable timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); @@ -264,4 +299,4 @@ void timesync_init() { 1); // CPU core } -#endif \ No newline at end of file +#endif From 646d60e9c5e85ddadc5eb83eec2feebc95c3e10e Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 13 May 2019 14:30:56 +0200 Subject: [PATCH 04/23] t_sec corrected by one seconds because time is only set ontop of a second --- src/timesync.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index 2c5b5db0..fa2030b2 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -208,8 +208,10 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) { } // adjust system time, calibrate RTC and RTC_INT pps -void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, - timesource_t mytimesource) { +void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec) { + + t_sec ++; + time_t time_to_set = (time_t)(t_sec); // increment t_sec only if t_msec > 1000 time_t time_to_set = (time_t)(t_sec + t_msec / 1000); From f475a634d558933c46eb32071bf274e59c7d31aa Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 13 May 2019 14:32:27 +0200 Subject: [PATCH 05/23] changed now to nowTime dow to conflict with function name now(), settimeofday got incorrect timevalue --- src/timesync.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index fa2030b2..f50b0f5e 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -247,18 +247,18 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec) { if(gettimeofday(&tv, &tz) != 0) { ESP_LOGI(TAG, "ERROR gettimeofday"); } - struct timeval before = tv; + struct timeval beforeTime = tv; - struct timeval now; - now.tv_sec = t_sec; - now.tv_usec = t_msec; - if(settimeofday(&tv, &tz) != 0) { + struct timeval nowTime; + nowTime.tv_sec = t_sec; + nowTime.tv_usec = t_msec; + if(settimeofday(&nowTime, &tz) != 0) { ESP_LOGE(TAG, "ERROR settimeofday"); } struct timeval diff; - diff.tv_sec = now.tv_sec-before.tv_sec; - diff.tv_usec = now.tv_usec-before.tv_usec; + diff.tv_sec = nowTime.tv_sec-beforeTime.tv_sec; + diff.tv_usec = nowTime.tv_usec-beforeTime.tv_usec; // sum up diff_s and diff_ms to one ms value int32_t diff_s = diff.tv_sec; From b00912277c797f2e1f289d3ea131f882796fbdfa Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Fri, 9 Aug 2019 14:02:10 +0200 Subject: [PATCH 06/23] send timestamp via uart for debugging --- src/uart.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/uart.h | 1 + 2 files changed, 46 insertions(+) create mode 100644 src/uart.cpp create mode 100644 src/uart.h diff --git a/src/uart.cpp b/src/uart.cpp new file mode 100644 index 00000000..52ac63f2 --- /dev/null +++ b/src/uart.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include +#include "globals.h" +#include "driver/uart.h" + +void time_uart_send(void * pvParameters) { + struct timeval curTime; + + const char* format = "\bOAL%y%m%dF%H%M%S\r"; + char timestamp[64] = {0}; + TickType_t xLastWakeTime = xTaskGetTickCount(); + + for(;;) { + time_t nowTime = now(); + strftime(timestamp, sizeof(timestamp), format, localtime(&nowTime)); + ESP_LOGI(TAG, "Current Time: %s", timestamp); + + int len = strlen(timestamp)+1; + + // Send Data via UART + uart_write_bytes(UART_NUM_1, timestamp, len); + + // gettimeofday(&curTime, &tz); + int sleep = 1000 - (curTime.tv_usec/1000); + ostime_t now = os_getTime(); + printf("Sleep Time: %d, now: %d\n", sleep, now); + vTaskDelayUntil( &xLastWakeTime, (TickType_t)(sleep/portTICK_PERIOD_MS) ); + + // Read UART for testing purposes + /** + uint8_t *data = (uint8_t *) malloc(CLOCK_BUF_SIZE); + + // Read Data via UART + uart_read_bytes(UART_NUM_1, data, CLOCK_BUF_SIZE, 20 / portTICK_RATE_MS); + const char * dataC = (const char *) data; + ESP_LOGI(TAG, "Data Read: %s", dataC); + + free(data); + **/ + } + + vTaskDelete(NULL); +} diff --git a/src/uart.h b/src/uart.h new file mode 100644 index 00000000..78b81ed5 --- /dev/null +++ b/src/uart.h @@ -0,0 +1 @@ +void time_uart_send(void * pvParameters); From a1221ebad06d09e1e93b41bc5d211d7a41020f77 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 12 Aug 2019 16:11:39 +0200 Subject: [PATCH 07/23] lost in merging process --- src/timesync.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index f50b0f5e..940696c5 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -208,13 +208,14 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) { } // adjust system time, calibrate RTC and RTC_INT pps -void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec) { +void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, + timesource_t mytimesource) { t_sec ++; time_t time_to_set = (time_t)(t_sec); // increment t_sec only if t_msec > 1000 - time_t time_to_set = (time_t)(t_sec + t_msec / 1000); + time_to_set = (time_t)(t_sec + t_msec / 1000); // do we have a valid time? if (timeIsValid(time_to_set)) { From ce7d3ab2927e3678d9963386af53863c216e8d1e Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Tue, 13 Aug 2019 13:16:28 +0200 Subject: [PATCH 08/23] node-red server reconfigured (new broker & seqNo in payload) --- src/Timeserver/Nodered-Timeserver.json | 579 +++++++++++++------------ 1 file changed, 301 insertions(+), 278 deletions(-) diff --git a/src/Timeserver/Nodered-Timeserver.json b/src/Timeserver/Nodered-Timeserver.json index b4d27d80..c823be78 100644 --- a/src/Timeserver/Nodered-Timeserver.json +++ b/src/Timeserver/Nodered-Timeserver.json @@ -1,198 +1,253 @@ [ { - "id": "49e3c067.e782e", - "type": "change", - "z": "449c1517.e25f4c", - "name": "Payload", - "rules": [ + "id":"656d6468.69133c", + "type":"tab", + "label":"Flow 1", + "disabled":false, + "info":"" + }, + { + "id":"2a15ab6f.ab2244", + "type":"mqtt-broker", + "z":"", + "name":"eu.thethings.network:1883", + "broker":"mqtt://xdot-gr:ttn-account-v2.eH0S7LsbltxCHjNTfMLcDc_mzhW2n4IN8GrtC-mKOXk@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":"" + }, + { + "id":"edb7cc8d.a3817", + "type":"ui_group", + "z":"", + "name":"Timeserver", + "tab":"d525a5d.0832858", + "order":4, + "disp":true, + "width":"6", + "collapse":false + }, + { + "id":"d525a5d.0832858", + "type":"ui_tab", + "z":"", + "name":"Timeserver", + "icon":"sync", + "order":3, + "disabled":false, + "hidden":false + }, + { + "id":"49e3c067.e782e", + "type":"change", + "z":"656d6468.69133c", + "name":"Payload", + "rules":[ { - "t": "change", - "p": "topic", - "pt": "msg", - "from": "up", - "fromt": "str", - "to": "down", - "tot": "str" + "t":"change", + "p":"topic", + "pt":"msg", + "from":"up", + "fromt":"str", + "to":"down", + "tot":"str" }, { - "t": "set", - "p": "payload.confirmed", - "pt": "msg", - "to": "false", - "tot": "bool" + "t":"set", + "p":"payload.confirmed", + "pt":"msg", + "to":"false", + "tot":"bool" }, { - "t": "set", - "p": "payload.schedule", - "pt": "msg", - "to": "replace", - "tot": "str" + "t":"set", + "p":"payload.schedule", + "pt":"msg", + "to":"replace", + "tot":"str" }, { - "t": "move", - "p": "payload", - "pt": "msg", - "to": "payload.payload_raw", - "tot": "msg" + "t":"move", + "p":"payload", + "pt":"msg", + "to":"payload.payload_raw", + "tot":"msg" }, { - "t": "move", - "p": "port", - "pt": "msg", - "to": "payload.port", - "tot": "msg" + "t":"move", + "p":"port", + "pt":"msg", + "to":"payload.port", + "tot":"msg" } ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 240, - "y": 513, - "wires": [ + "action":"", + "property":"", + "from":"", + "to":"", + "reg":false, + "x":240, + "y":513, + "wires":[ [ "84f1cda2.069e7" ] ] }, { - "id": "cc140589.dea168", - "type": "mqtt in", - "z": "449c1517.e25f4c", - "name": "listen", - "topic": "+/devices/+/up", - "qos": "2", - "broker": "2a15ab6f.ab2244", - "x": 110, - "y": 120, - "wires": [ + "id":"cc140589.dea168", + "type":"mqtt in", + "z":"656d6468.69133c", + "name":"listen", + "topic":"+/devices/+/up", + "qos":"2", + "broker":"2a15ab6f.ab2244", + "x":110, + "y":120, + "wires":[ [ "4f97d75.6c87528" ] ] }, { - "id": "72d5e7ee.d1eba8", - "type": "mqtt out", - "z": "449c1517.e25f4c", - "name": "send", - "topic": "", - "qos": "", - "retain": "", - "broker": "2a15ab6f.ab2244", - "x": 730, - "y": 513, - "wires": [] + "id":"72d5e7ee.d1eba8", + "type":"mqtt out", + "z":"656d6468.69133c", + "name":"send", + "topic":"", + "qos":"", + "retain":"", + "broker":"2a15ab6f.ab2244", + "x":730, + "y":513, + "wires":[ + + ] }, { - "id": "4f97d75.6c87528", - "type": "json", - "z": "449c1517.e25f4c", - "name": "Convert", - "property": "payload", - "action": "", - "pretty": false, - "x": 260, - "y": 120, - "wires": [ + "id":"4f97d75.6c87528", + "type":"json", + "z":"656d6468.69133c", + "name":"Convert", + "property":"payload", + "action":"", + "pretty":false, + "x":260, + "y":120, + "wires":[ [ "9f4b8dd3.2f0d2" ] ] }, { - "id": "9f4b8dd3.2f0d2", - "type": "switch", - "z": "449c1517.e25f4c", - "name": "Timeport", - "property": "payload.port", - "propertyType": "msg", - "rules": [ + "id":"9f4b8dd3.2f0d2", + "type":"switch", + "z":"656d6468.69133c", + "name":"Timeport", + "property":"payload.port", + "propertyType":"msg", + "rules":[ { - "t": "eq", - "v": "9", - "vt": "num" + "t":"eq", + "v":"9", + "vt":"num" } ], - "checkall": "true", - "repair": false, - "outputs": 1, - "x": 420, - "y": 120, - "wires": [ + "checkall":"true", + "repair":false, + "outputs":1, + "x":420, + "y":120, + "wires":[ [ "8ed813a9.a9319" ] ] }, { - "id": "dac8aafa.389298", - "type": "json", - "z": "449c1517.e25f4c", - "name": "Convert", - "property": "payload", - "action": "", - "pretty": false, - "x": 580, - "y": 513, - "wires": [ + "id":"dac8aafa.389298", + "type":"json", + "z":"656d6468.69133c", + "name":"Convert", + "property":"payload", + "action":"", + "pretty":false, + "x":580, + "y":513, + "wires":[ [ "72d5e7ee.d1eba8" ] ] }, { - "id": "8ed813a9.a9319", - "type": "base64", - "z": "449c1517.e25f4c", - "name": "Decode", - "action": "", - "property": "payload.payload_raw", - "x": 580, - "y": 120, - "wires": [ + "id":"8ed813a9.a9319", + "type":"base64", + "z":"656d6468.69133c", + "name":"Decode", + "action":"", + "property":"payload.payload_raw", + "x":580, + "y":120, + "wires":[ [ "831ab883.d6a238" ] ] }, { - "id": "84f1cda2.069e7", - "type": "base64", - "z": "449c1517.e25f4c", - "name": "Encode", - "action": "", - "property": "payload.payload_raw", - "x": 420, - "y": 513, - "wires": [ + "id":"84f1cda2.069e7", + "type":"base64", + "z":"656d6468.69133c", + "name":"Encode", + "action":"", + "property":"payload.payload_raw", + "x":420, + "y":513, + "wires":[ [ "dac8aafa.389298" ] ] }, { - "id": "6190967b.01f758", - "type": "comment", - "z": "449c1517.e25f4c", - "name": "LoRaWAN Timeserver v1.1", - "info": "PLEASE NOTE: There is a patent filed for the time sync algorithm used in the\ncode of this file. The shown implementation example is covered by the\nrepository's licencse, but you may not be eligible to deploy the applied\nalgorithm in applications without granted license by the patent holder.", - "x": 170, - "y": 40, - "wires": [] + "id":"6190967b.01f758", + "type":"comment", + "z":"656d6468.69133c", + "name":"LoRaWAN Timeserver v1.1", + "info":"PLEASE NOTE: There is a patent filed for the time sync algorithm used in the\ncode of this file. The shown implementation example is covered by the\nrepository's licencse, but you may not be eligible to deploy the applied\nalgorithm in applications without granted license by the patent holder.", + "x":170, + "y":40, + "wires":[ + + ] }, { - "id": "831ab883.d6a238", - "type": "function", - "z": "449c1517.e25f4c", - "name": "Timeserver Logic", - "func": "/* LoRaWAN Timeserver\n\nconstruct 5 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n1..4 current second (from epoch time 1970)\n5 1/250ths fractions of current second\n\nFPort = sequence number (taken from node's time_sync_req)\n\n*/\n\nfunction timecompare(a, b) {\n \n const timeA = a.time;\n const timeB = b.time;\n\n let comparison = 0;\n if (timeA > timeB) {\n comparison = 1;\n } else if (timeA < timeB) {\n comparison = -1;\n }\n return comparison;\n}\n\nlet confidence = 2000; // max millisecond diff gateway time to server time\n\n// guess if we have received a valid time_sync_req command\nif (msg.payload.payload_raw.length != 1)\n return;\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar seqNo = msg.payload.payload_raw[0];\nvar seqNoMsg = { payload: seqNo };\nvar gateway_list = msg.payload.metadata.gateways;\n\n// filter all gateway timestamps that have milliseconds part (which we assume have a \".\")\nvar gateways = gateway_list.filter(function (element) {\n return (element.time.includes(\".\"));\n});\n\nvar gateway_time = gateways.map(gw => {\n return {\n time: new Date(gw.time),\n eui: gw.gtw_id,\n }\n });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) {\n var notavailMsg = { payload: \"n/a\" };\n var notimeMsg = { payload: 0xff }; \n var buf2 = Buffer.alloc(1);\n msg.payload = new Buffer(buf2.fill(0xff));\n return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];}\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\n\nlet buf = new ArrayBuffer(5);\nnew DataView(buf).setUint32(0, seconds);\nnew DataView(buf).setUint8(4, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nmsg.port = seqNo;\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg];", - "outputs": 5, - "noerr": 0, - "x": 350, - "y": 320, - "wires": [ + "id":"831ab883.d6a238", + "type":"function", + "z":"656d6468.69133c", + "name":"Timeserver Logic", + "func":"/* LoRaWAN Timeserver\n\nconstruct 5 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n1..4 current second (from epoch time 1970)\n5 1/250ths fractions of current second\n\nFPort = sequence number (taken from node's time_sync_req)\n\n*/\n\nfunction timecompare(a, b) {\n \n const timeA = a.time;\n const timeB = b.time;\n\n let comparison = 0;\n if (timeA > timeB) {\n comparison = 1;\n } else if (timeA < timeB) {\n comparison = -1;\n }\n return comparison;\n}\n\nlet confidence = 2000; // max millisecond diff gateway time to server time\n\n// guess if we have received a valid time_sync_req command\nif (msg.payload.payload_raw.length != 1)\n return;\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar seqNo = msg.payload.payload_raw[0];\nvar seqNoMsg = { payload: seqNo };\nvar gateway_list = msg.payload.metadata.gateways;\n\n// filter all gateway timestamps that have milliseconds part (which we assume have a \".\")\nvar gateways = gateway_list.filter(function (element) {\n return (element.time.includes(\".\"));\n});\n\nvar gateway_time = gateways.map(gw => {\n return {\n time: new Date(gw.time),\n eui: gw.gtw_id,\n }\n });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) {\n var notavailMsg = { payload: \"n/a\" };\n var notimeMsg = { payload: 0xff }; \n var buf2 = Buffer.alloc(1);\n msg.payload = new Buffer(buf2.fill(0xff));\n return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];}\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\n\nlet buf = new ArrayBuffer(6);\nnew DataView(buf).setUint8(0, seqNo);\nnew DataView(buf).setUint32(1, seconds);\nnew DataView(buf).setUint8(5, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nmsg.port = 9;\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg];", + "outputs":5, + "noerr":0, + "x":350, + "y":320, + "wires":[ [ "37722d4b.08e3c2", "a8a04c7a.c5fbd", @@ -211,7 +266,7 @@ "49e3c067.e782e" ] ], - "outputLabels": [ + "outputLabels":[ "gw_eui", "offset_ms", "device", @@ -220,168 +275,136 @@ ] }, { - "id": "37722d4b.08e3c2", - "type": "debug", - "z": "449c1517.e25f4c", - "name": "Timeserver Gw", - "active": true, - "tosidebar": false, - "console": false, - "tostatus": true, - "complete": "payload", - "x": 700, - "y": 240, - "wires": [], - "icon": "node-red/bridge.png" + "id":"37722d4b.08e3c2", + "type":"debug", + "z":"656d6468.69133c", + "name":"Timeserver Gw", + "active":true, + "tosidebar":false, + "console":false, + "tostatus":true, + "complete":"payload", + "x":700, + "y":240, + "wires":[ + + ], + "icon":"node-red/bridge.png" }, { - "id": "8712a5ac.ed18e8", - "type": "ui_text", - "z": "449c1517.e25f4c", - "group": "edb7cc8d.a3817", - "order": 3, - "width": 0, - "height": 0, - "name": "Recent time", - "label": "Last answer at:", - "format": "{{msg.payload}}", - "layout": "col-center", - "x": 810, - "y": 300, - "wires": [] + "id":"8712a5ac.ed18e8", + "type":"ui_text", + "z":"656d6468.69133c", + "group":"edb7cc8d.a3817", + "order":3, + "width":0, + "height":0, + "name":"Recent time", + "label":"Last answer at:", + "format":"{{msg.payload}}", + "layout":"col-center", + "x":810, + "y":300, + "wires":[ + + ] }, { - "id": "46ce842a.614d5c", - "type": "ui_gauge", - "z": "449c1517.e25f4c", - "name": "Timeserver offset", - "group": "edb7cc8d.a3817", - "order": 2, - "width": 0, - "height": 0, - "gtype": "gage", - "title": "Offset gateway to server", - "label": "milliseconds", - "format": "{{value}}", - "min": 0, - "max": "2000", - "colors": [ + "id":"46ce842a.614d5c", + "type":"ui_gauge", + "z":"656d6468.69133c", + "name":"Timeserver offset", + "group":"edb7cc8d.a3817", + "order":2, + "width":0, + "height":0, + "gtype":"gage", + "title":"Offset gateway to server", + "label":"milliseconds", + "format":"{{value}}", + "min":0, + "max":"2000", + "colors":[ "#00b500", "#e6e600", "#ca3838" ], - "seg1": "", - "seg2": "", - "x": 710, - "y": 380, - "wires": [] + "seg1":"", + "seg2":"", + "x":710, + "y":380, + "wires":[ + + ] }, { - "id": "a8a04c7a.c5fbd", - "type": "ui_text", - "z": "449c1517.e25f4c", - "group": "edb7cc8d.a3817", - "order": 1, - "width": 0, - "height": 0, - "name": "Recent server", - "label": "Gateway", - "format": "{{msg.payload}}", - "layout": "col-center", - "x": 700, - "y": 340, - "wires": [] + "id":"a8a04c7a.c5fbd", + "type":"ui_text", + "z":"656d6468.69133c", + "group":"edb7cc8d.a3817", + "order":1, + "width":0, + "height":0, + "name":"Recent server", + "label":"Gateway", + "format":"{{msg.payload}}", + "layout":"col-center", + "x":700, + "y":340, + "wires":[ + + ] }, { - "id": "a15454a9.fa0948", - "type": "function", - "z": "449c1517.e25f4c", - "name": "Time", - "func": "msg.payload = new Date().toLocaleString('en-GB', {timeZone: 'Europe/Berlin'});\nreturn msg;", - "outputs": 1, - "noerr": 0, - "x": 670, - "y": 300, - "wires": [ + "id":"a15454a9.fa0948", + "type":"function", + "z":"656d6468.69133c", + "name":"Time", + "func":"msg.payload = new Date().toLocaleString('en-GB', {timeZone: 'Europe/Berlin'});\nreturn msg;", + "outputs":1, + "noerr":0, + "x":670, + "y":300, + "wires":[ [ "8712a5ac.ed18e8" ] ] }, { - "id": "a5dbb4ef.019168", - "type": "ui_text", - "z": "449c1517.e25f4c", - "group": "edb7cc8d.a3817", - "order": 1, - "width": 0, - "height": 0, - "name": "Recent Device", - "label": "Device", - "format": "{{msg.payload}}", - "layout": "col-center", - "x": 700, - "y": 420, - "wires": [] + "id":"a5dbb4ef.019168", + "type":"ui_text", + "z":"656d6468.69133c", + "group":"edb7cc8d.a3817", + "order":1, + "width":0, + "height":0, + "name":"Recent Device", + "label":"Device", + "format":"{{msg.payload}}", + "layout":"col-center", + "x":700, + "y":420, + "wires":[ + + ] }, { - "id": "1cb58e7f.221362", - "type": "ui_text", - "z": "449c1517.e25f4c", - "group": "edb7cc8d.a3817", - "order": 1, - "width": 0, - "height": 0, - "name": "Sequence No", - "label": "Sequence", - "format": "{{msg.payload}}", - "layout": "col-center", - "x": 700, - "y": 460, - "wires": [] - }, - { - "id": "2a15ab6f.ab2244", - "type": "mqtt-broker", - "z": "", - "name": "eu.thethings.network:1883", - "broker": "mqtt://paxcounter-timediff:ttn-account-v2.Fyf_ZgT4lBM2A3Gj-4SpNPp0-_tPkqtPRMJ_a-w1aT4@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": "" - }, - { - "id": "edb7cc8d.a3817", - "type": "ui_group", - "z": "", - "name": "Timeserver", - "tab": "d525a5d.0832858", - "order": 4, - "disp": true, - "width": "6", - "collapse": false - }, - { - "id": "d525a5d.0832858", - "type": "ui_tab", - "z": "", - "name": "Timeserver", - "icon": "sync", - "order": 3, - "disabled": false, - "hidden": false + "id":"1cb58e7f.221362", + "type":"ui_text", + "z":"656d6468.69133c", + "group":"edb7cc8d.a3817", + "order":1, + "width":0, + "height":0, + "name":"Sequence No", + "label":"Sequence", + "format":"{{msg.payload}}", + "layout":"col-center", + "x":700, + "y":460, + "wires":[ + + ] } -] \ No newline at end of file +] From 730f35babff6a96d26b01b866bc57dc3744d757b Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Tue, 13 Aug 2019 13:16:56 +0200 Subject: [PATCH 09/23] expect seqNo in payload and not in port --- include/timesync.h | 2 +- src/lorawan.cpp | 6 +++--- src/timesync.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index 498952c1..286d82e7 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -7,7 +7,7 @@ #include "timekeeper.h" //#define TIME_SYNC_TRIGGER 100 // threshold for time sync [milliseconds] -#define TIME_SYNC_FRAME_LENGTH 0x05 // timeserver answer frame length [bytes] +#define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length [bytes] #define TIME_SYNC_FIXUP 4 // calibration to fixup processing time [milliseconds] void timesync_init(void); diff --git a/src/lorawan.cpp b/src/lorawan.cpp index e82ac1f5..b5e941cb 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -265,9 +265,9 @@ void onEvent(ev_t ev) { #if (TIME_SYNC_LORASERVER) // timesync answer -> call timesync processor - if ((LMIC.frame[LMIC.dataBeg - 1] >= TIMEANSWERPORT_MIN) && - (LMIC.frame[LMIC.dataBeg - 1] <= TIMEANSWERPORT_MAX)) { - recv_timesync_ans(LMIC.frame[LMIC.dataBeg - 1], + if ((LMIC.frame[LMIC.dataBeg] >= TIMEANSWERPORT_MIN) && + (LMIC.frame[LMIC.dataBeg] <= TIMEANSWERPORT_MAX)) { + recv_timesync_ans(LMIC.frame[LMIC.dataBeg], LMIC.frame + LMIC.dataBeg, LMIC.dataLen); break; } diff --git a/src/timesync.cpp b/src/timesync.cpp index 940696c5..488561c2 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -174,15 +174,15 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) { else { // we received a probably valid time frame uint8_t k = seq_no % TIME_SYNC_SAMPLES; - + uint8_t *timestamp_buf = buf+1; // the 5th byte contains the fractional seconds in 2^-8 second steps // (= 1/250th sec), we convert this to ms - uint16_t timestamp_msec = 4 * buf[4]; + uint16_t timestamp_msec = 4 * timestamp_buf[4]; // pointers to 4 bytes 4 bytes containing UTC seconds since unix epoch, msb uint32_t timestamp_sec, *timestamp_ptr; // convert buffer to uint32_t, octet order is big endian - timestamp_ptr = (uint32_t *)buf; + timestamp_ptr = (uint32_t *)timestamp_buf; // swap byte order from msb to lsb, note: this is platform dependent timestamp_sec = __builtin_bswap32(*timestamp_ptr); From 6894d718e63c919f11be1b6545521aa71597e610 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Tue, 27 Aug 2019 15:40:33 +0200 Subject: [PATCH 10/23] node-red config with use of ttn nodes --- src/Timeserver/Nodered-Timeserver.json | 854 +++++++++++++------------ 1 file changed, 459 insertions(+), 395 deletions(-) diff --git a/src/Timeserver/Nodered-Timeserver.json b/src/Timeserver/Nodered-Timeserver.json index c823be78..3d057c76 100644 --- a/src/Timeserver/Nodered-Timeserver.json +++ b/src/Timeserver/Nodered-Timeserver.json @@ -1,410 +1,474 @@ [ - { - "id":"656d6468.69133c", - "type":"tab", - "label":"Flow 1", - "disabled":false, - "info":"" - }, - { - "id":"2a15ab6f.ab2244", - "type":"mqtt-broker", - "z":"", - "name":"eu.thethings.network:1883", - "broker":"mqtt://xdot-gr:ttn-account-v2.eH0S7LsbltxCHjNTfMLcDc_mzhW2n4IN8GrtC-mKOXk@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":"" - }, - { - "id":"edb7cc8d.a3817", - "type":"ui_group", - "z":"", - "name":"Timeserver", - "tab":"d525a5d.0832858", - "order":4, - "disp":true, - "width":"6", - "collapse":false - }, - { - "id":"d525a5d.0832858", - "type":"ui_tab", - "z":"", - "name":"Timeserver", - "icon":"sync", - "order":3, - "disabled":false, - "hidden":false - }, - { - "id":"49e3c067.e782e", - "type":"change", - "z":"656d6468.69133c", - "name":"Payload", - "rules":[ - { - "t":"change", - "p":"topic", - "pt":"msg", - "from":"up", - "fromt":"str", - "to":"down", - "tot":"str" + { + "id":"23a9162c.cb1e6a", + "type":"tab", + "label":"Flow 1", + "disabled":false, + "info":"" + }, + { + "id":"2a15ab6f.ab2244", + "type":"mqtt-broker", + "z":"", + "name":"mqtt://db-clock-update-test:ttn-account-v2.etVLJSGCzS5ExuDw59HtGUIzv9Kcu8UPT_eWiz8ARs0@eu.thethings.network", + "broker":"mqtt://db-clock-update-test:ttn-account-v2.etVLJSGCzS5ExuDw59HtGUIzv9Kcu8UPT_eWiz8ARs0@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":"" + }, + { + "id":"edb7cc8d.a3817", + "type":"ui_group", + "z":"", + "name":"Timeserver", + "tab":"d525a5d.0832858", + "order":4, + "disp":true, + "width":"6", + "collapse":false + }, + { + "id":"d525a5d.0832858", + "type":"ui_tab", + "z":"", + "name":"Timeserver", + "icon":"sync", + "order":3, + "disabled":false, + "hidden":false + }, + { + "id":"428b4734.d61248", + "type":"ui_base", + "theme":{ + "name":"theme-light", + "lightTheme":{ + "default":"#0094CE", + "baseColor":"#0094CE", + "baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited":true, + "reset":false + }, + "darkTheme":{ + "default":"#097479", + "baseColor":"#097479", + "baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited":false + }, + "customTheme":{ + "name":"Untitled Theme 1", + "default":"#4B7930", + "baseColor":"#4B7930", + "baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" + }, + "themeState":{ + "base-color":{ + "default":"#0094CE", + "value":"#0094CE", + "edited":false }, - { - "t":"set", - "p":"payload.confirmed", - "pt":"msg", - "to":"false", - "tot":"bool" + "page-titlebar-backgroundColor":{ + "value":"#0094CE", + "edited":false }, - { - "t":"set", - "p":"payload.schedule", - "pt":"msg", - "to":"replace", - "tot":"str" + "page-backgroundColor":{ + "value":"#fafafa", + "edited":false }, - { - "t":"move", - "p":"payload", - "pt":"msg", - "to":"payload.payload_raw", - "tot":"msg" + "page-sidebar-backgroundColor":{ + "value":"#ffffff", + "edited":false }, - { - "t":"move", - "p":"port", - "pt":"msg", - "to":"payload.port", - "tot":"msg" + "group-textColor":{ + "value":"#1bbfff", + "edited":false + }, + "group-borderColor":{ + "value":"#ffffff", + "edited":false + }, + "group-backgroundColor":{ + "value":"#ffffff", + "edited":false + }, + "widget-textColor":{ + "value":"#111111", + "edited":false + }, + "widget-backgroundColor":{ + "value":"#0094ce", + "edited":false + }, + "widget-borderColor":{ + "value":"#ffffff", + "edited":false + }, + "base-font":{ + "value":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" } - ], - "action":"", - "property":"", - "from":"", - "to":"", - "reg":false, - "x":240, - "y":513, - "wires":[ - [ - "84f1cda2.069e7" - ] - ] - }, - { - "id":"cc140589.dea168", - "type":"mqtt in", - "z":"656d6468.69133c", - "name":"listen", - "topic":"+/devices/+/up", - "qos":"2", - "broker":"2a15ab6f.ab2244", - "x":110, - "y":120, - "wires":[ - [ - "4f97d75.6c87528" - ] - ] - }, - { - "id":"72d5e7ee.d1eba8", - "type":"mqtt out", - "z":"656d6468.69133c", - "name":"send", - "topic":"", - "qos":"", - "retain":"", - "broker":"2a15ab6f.ab2244", - "x":730, - "y":513, - "wires":[ + }, + "angularTheme":{ + "primary":"indigo", + "accents":"blue", + "warn":"red", + "background":"grey" + } + }, + "site":{ + "name":"Node-RED Dashboard", + "hideToolbar":"false", + "allowSwipe":"false", + "lockMenu":"false", + "allowTempTheme":"true", + "dateFormat":"DD/MM/YYYY", + "sizes":{ + "sx":48, + "sy":48, + "gx":6, + "gy":6, + "cx":6, + "cy":6, + "px":0, + "py":0 + } + } + }, + { + "id":"f8f8391b.7e8728", + "type":"ttn app", + "z":"", + "appId":"db-clock-update-test", + "accessKey":"ttn-account-v2.etVLJSGCzS5ExuDw59HtGUIzv9Kcu8UPT_eWiz8ARs0", + "discovery":"discovery.thethingsnetwork.org:1900" + }, + { + "id":"49e3c067.e782e", + "type":"change", + "z":"23a9162c.cb1e6a", + "name":"Payload", + "rules":[ + { + "t":"change", + "p":"topic", + "pt":"msg", + "from":"up", + "fromt":"str", + "to":"down", + "tot":"str" + }, + { + "t":"set", + "p":"confirmed", + "pt":"msg", + "to":"false", + "tot":"bool" + }, + { + "t":"set", + "p":"schedule", + "pt":"msg", + "to":"replace", + "tot":"str" + } + ], + "action":"", + "property":"", + "from":"", + "to":"", + "reg":false, + "x":360, + "y":600, + "wires":[ + [ + "813b9751.dba8a8", + "5aff6f87.f125a" + ] + ] + }, + { + "id":"9f4b8dd3.2f0d2", + "type":"switch", + "z":"23a9162c.cb1e6a", + "name":"Timeport", + "property":"port", + "propertyType":"msg", + "rules":[ + { + "t":"eq", + "v":"9", + "vt":"num" + } + ], + "checkall":"true", + "repair":false, + "outputs":1, + "x":420, + "y":120, + "wires":[ + [ + "831ab883.d6a238", + "b1e45e31.61136" + ] + ] + }, + { + "id":"6190967b.01f758", + "type":"comment", + "z":"23a9162c.cb1e6a", + "name":"LoRaWAN Timeserver v1.1", + "info":"PLEASE NOTE: There is a patent filed for the time sync algorithm used in the\ncode of this file. The shown implementation example is covered by the\nrepository's licencse, but you may not be eligible to deploy the applied\nalgorithm in applications without granted license by the patent holder.", + "x":170, + "y":40, + "wires":[ - ] - }, - { - "id":"4f97d75.6c87528", - "type":"json", - "z":"656d6468.69133c", - "name":"Convert", - "property":"payload", - "action":"", - "pretty":false, - "x":260, - "y":120, - "wires":[ - [ - "9f4b8dd3.2f0d2" - ] - ] - }, - { - "id":"9f4b8dd3.2f0d2", - "type":"switch", - "z":"656d6468.69133c", - "name":"Timeport", - "property":"payload.port", - "propertyType":"msg", - "rules":[ - { - "t":"eq", - "v":"9", - "vt":"num" - } - ], - "checkall":"true", - "repair":false, - "outputs":1, - "x":420, - "y":120, - "wires":[ - [ - "8ed813a9.a9319" - ] - ] - }, - { - "id":"dac8aafa.389298", - "type":"json", - "z":"656d6468.69133c", - "name":"Convert", - "property":"payload", - "action":"", - "pretty":false, - "x":580, - "y":513, - "wires":[ - [ - "72d5e7ee.d1eba8" - ] - ] - }, - { - "id":"8ed813a9.a9319", - "type":"base64", - "z":"656d6468.69133c", - "name":"Decode", - "action":"", - "property":"payload.payload_raw", - "x":580, - "y":120, - "wires":[ - [ - "831ab883.d6a238" - ] - ] - }, - { - "id":"84f1cda2.069e7", - "type":"base64", - "z":"656d6468.69133c", - "name":"Encode", - "action":"", - "property":"payload.payload_raw", - "x":420, - "y":513, - "wires":[ - [ - "dac8aafa.389298" - ] - ] - }, - { - "id":"6190967b.01f758", - "type":"comment", - "z":"656d6468.69133c", - "name":"LoRaWAN Timeserver v1.1", - "info":"PLEASE NOTE: There is a patent filed for the time sync algorithm used in the\ncode of this file. The shown implementation example is covered by the\nrepository's licencse, but you may not be eligible to deploy the applied\nalgorithm in applications without granted license by the patent holder.", - "x":170, - "y":40, - "wires":[ + ] + }, + { + "id":"831ab883.d6a238", + "type":"function", + "z":"23a9162c.cb1e6a", + "name":"Timeserver Logic", + "func":"/* LoRaWAN Timeserver\n\nconstruct 5 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n1..4 current second (from epoch time 1970)\n5 1/250ths fractions of current second\n\nFPort = sequence number (taken from node's time_sync_req)\n\n*/\n\nfunction timecompare(a, b) {\n \n const timeA = a.time;\n const timeB = b.time;\n\n let comparison = 0;\n if (timeA > timeB) {\n comparison = 1;\n } else if (timeA < timeB) {\n comparison = -1;\n }\n return comparison;\n}\n\nlet confidence = 2000; // max millisecond diff gateway time to server time\n\n// guess if we have received a valid time_sync_req command\nif (msg.payload_raw.length != 1)\n return;\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar seqNo = msg.payload[\"timesync_seqno\"];\nvar seqNoMsg = { payload: seqNo };\nvar gateway_list = msg.metadata.gateways;\n\n// filter all gateway timestamps that have milliseconds part (which we assume have a \".\")\nvar gateways = gateway_list.filter(function (element) {\n return (element.time.includes(\".\"));\n});\n\nvar gateway_time = gateways.map(gw => {\n return {\n time: new Date(gw.time),\n eui: gw.gtw_id,\n }\n });\nvar server_time = new Date(msg.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) {\n var notavailMsg = { payload: \"n/a\" };\n var notimeMsg = { payload: 0xff }; \n var buf2 = Buffer.alloc(1);\n msg.payload = new Buffer(buf2.fill(0xff));\n return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];}\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\n\nlet buf = new ArrayBuffer(6);\nnew DataView(buf).setUint8(0, seqNo);\nnew DataView(buf).setUint32(1, seconds);\nnew DataView(buf).setUint8(5, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nmsg.port = 9;\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg];", + "outputs":5, + "noerr":0, + "x":350, + "y":320, + "wires":[ + [ + "37722d4b.08e3c2", + "a8a04c7a.c5fbd", + "a15454a9.fa0948" + ], + [ + "46ce842a.614d5c" + ], + [ + "a5dbb4ef.019168" + ], + [ + "1cb58e7f.221362" + ], + [ + "49e3c067.e782e" + ] + ], + "outputLabels":[ + "gw_eui", + "offset_ms", + "device", + "seq_no", + "time_sync_ans" + ] + }, + { + "id":"37722d4b.08e3c2", + "type":"debug", + "z":"23a9162c.cb1e6a", + "name":"Timeserver Gw", + "active":true, + "tosidebar":false, + "console":false, + "tostatus":true, + "complete":"payload", + "x":700, + "y":240, + "wires":[ - ] - }, - { - "id":"831ab883.d6a238", - "type":"function", - "z":"656d6468.69133c", - "name":"Timeserver Logic", - "func":"/* LoRaWAN Timeserver\n\nconstruct 5 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n1..4 current second (from epoch time 1970)\n5 1/250ths fractions of current second\n\nFPort = sequence number (taken from node's time_sync_req)\n\n*/\n\nfunction timecompare(a, b) {\n \n const timeA = a.time;\n const timeB = b.time;\n\n let comparison = 0;\n if (timeA > timeB) {\n comparison = 1;\n } else if (timeA < timeB) {\n comparison = -1;\n }\n return comparison;\n}\n\nlet confidence = 2000; // max millisecond diff gateway time to server time\n\n// guess if we have received a valid time_sync_req command\nif (msg.payload.payload_raw.length != 1)\n return;\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar seqNo = msg.payload.payload_raw[0];\nvar seqNoMsg = { payload: seqNo };\nvar gateway_list = msg.payload.metadata.gateways;\n\n// filter all gateway timestamps that have milliseconds part (which we assume have a \".\")\nvar gateways = gateway_list.filter(function (element) {\n return (element.time.includes(\".\"));\n});\n\nvar gateway_time = gateways.map(gw => {\n return {\n time: new Date(gw.time),\n eui: gw.gtw_id,\n }\n });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) {\n var notavailMsg = { payload: \"n/a\" };\n var notimeMsg = { payload: 0xff }; \n var buf2 = Buffer.alloc(1);\n msg.payload = new Buffer(buf2.fill(0xff));\n return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];}\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\n\nlet buf = new ArrayBuffer(6);\nnew DataView(buf).setUint8(0, seqNo);\nnew DataView(buf).setUint32(1, seconds);\nnew DataView(buf).setUint8(5, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nmsg.port = 9;\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg];", - "outputs":5, - "noerr":0, - "x":350, - "y":320, - "wires":[ - [ - "37722d4b.08e3c2", - "a8a04c7a.c5fbd", - "a15454a9.fa0948" - ], - [ - "46ce842a.614d5c" - ], - [ - "a5dbb4ef.019168" - ], - [ - "1cb58e7f.221362" - ], - [ - "49e3c067.e782e" - ] - ], - "outputLabels":[ - "gw_eui", - "offset_ms", - "device", - "seq_no", - "time_sync_ans" - ] - }, - { - "id":"37722d4b.08e3c2", - "type":"debug", - "z":"656d6468.69133c", - "name":"Timeserver Gw", - "active":true, - "tosidebar":false, - "console":false, - "tostatus":true, - "complete":"payload", - "x":700, - "y":240, - "wires":[ + ], + "icon":"node-red/bridge.png" + }, + { + "id":"8712a5ac.ed18e8", + "type":"ui_text", + "z":"23a9162c.cb1e6a", + "group":"edb7cc8d.a3817", + "order":3, + "width":0, + "height":0, + "name":"Recent time", + "label":"Last answer at:", + "format":"{{msg.payload}}", + "layout":"col-center", + "x":810, + "y":300, + "wires":[ - ], - "icon":"node-red/bridge.png" - }, - { - "id":"8712a5ac.ed18e8", - "type":"ui_text", - "z":"656d6468.69133c", - "group":"edb7cc8d.a3817", - "order":3, - "width":0, - "height":0, - "name":"Recent time", - "label":"Last answer at:", - "format":"{{msg.payload}}", - "layout":"col-center", - "x":810, - "y":300, - "wires":[ + ] + }, + { + "id":"46ce842a.614d5c", + "type":"ui_gauge", + "z":"23a9162c.cb1e6a", + "name":"Timeserver offset", + "group":"edb7cc8d.a3817", + "order":2, + "width":0, + "height":0, + "gtype":"gage", + "title":"Offset gateway to server", + "label":"milliseconds", + "format":"{{value}}", + "min":0, + "max":"2000", + "colors":[ + "#00b500", + "#e6e600", + "#ca3838" + ], + "seg1":"", + "seg2":"", + "x":710, + "y":380, + "wires":[ - ] - }, - { - "id":"46ce842a.614d5c", - "type":"ui_gauge", - "z":"656d6468.69133c", - "name":"Timeserver offset", - "group":"edb7cc8d.a3817", - "order":2, - "width":0, - "height":0, - "gtype":"gage", - "title":"Offset gateway to server", - "label":"milliseconds", - "format":"{{value}}", - "min":0, - "max":"2000", - "colors":[ - "#00b500", - "#e6e600", - "#ca3838" - ], - "seg1":"", - "seg2":"", - "x":710, - "y":380, - "wires":[ + ] + }, + { + "id":"a8a04c7a.c5fbd", + "type":"ui_text", + "z":"23a9162c.cb1e6a", + "group":"edb7cc8d.a3817", + "order":1, + "width":0, + "height":0, + "name":"Recent server", + "label":"Gateway", + "format":"{{msg.payload}}", + "layout":"col-center", + "x":700, + "y":340, + "wires":[ - ] - }, - { - "id":"a8a04c7a.c5fbd", - "type":"ui_text", - "z":"656d6468.69133c", - "group":"edb7cc8d.a3817", - "order":1, - "width":0, - "height":0, - "name":"Recent server", - "label":"Gateway", - "format":"{{msg.payload}}", - "layout":"col-center", - "x":700, - "y":340, - "wires":[ + ] + }, + { + "id":"a15454a9.fa0948", + "type":"function", + "z":"23a9162c.cb1e6a", + "name":"Time", + "func":"msg.payload = new Date().toLocaleString('en-GB', {timeZone: 'Europe/Berlin'});\nreturn msg;", + "outputs":1, + "noerr":0, + "x":670, + "y":300, + "wires":[ + [ + "8712a5ac.ed18e8" + ] + ] + }, + { + "id":"a5dbb4ef.019168", + "type":"ui_text", + "z":"23a9162c.cb1e6a", + "group":"edb7cc8d.a3817", + "order":1, + "width":0, + "height":0, + "name":"Recent Device", + "label":"Device", + "format":"{msg.payload}", + "layout":"col-center", + "x":700, + "y":420, + "wires":[ - ] - }, - { - "id":"a15454a9.fa0948", - "type":"function", - "z":"656d6468.69133c", - "name":"Time", - "func":"msg.payload = new Date().toLocaleString('en-GB', {timeZone: 'Europe/Berlin'});\nreturn msg;", - "outputs":1, - "noerr":0, - "x":670, - "y":300, - "wires":[ - [ - "8712a5ac.ed18e8" - ] - ] - }, - { - "id":"a5dbb4ef.019168", - "type":"ui_text", - "z":"656d6468.69133c", - "group":"edb7cc8d.a3817", - "order":1, - "width":0, - "height":0, - "name":"Recent Device", - "label":"Device", - "format":"{{msg.payload}}", - "layout":"col-center", - "x":700, - "y":420, - "wires":[ + ] + }, + { + "id":"1cb58e7f.221362", + "type":"ui_text", + "z":"23a9162c.cb1e6a", + "group":"edb7cc8d.a3817", + "order":1, + "width":0, + "height":0, + "name":"Sequence No", + "label":"Sequence", + "format":"{{msg.payload}}", + "layout":"col-center", + "x":700, + "y":460, + "wires":[ - ] - }, - { - "id":"1cb58e7f.221362", - "type":"ui_text", - "z":"656d6468.69133c", - "group":"edb7cc8d.a3817", - "order":1, - "width":0, - "height":0, - "name":"Sequence No", - "label":"Sequence", - "format":"{{msg.payload}}", - "layout":"col-center", - "x":700, - "y":460, - "wires":[ + ] + }, + { + "id":"a5356ffb.f0cab", + "type":"ttn uplink", + "z":"23a9162c.cb1e6a", + "name":"", + "app":"f8f8391b.7e8728", + "dev_id":"", + "field":"", + "x":240, + "y":120, + "wires":[ + [ + "9f4b8dd3.2f0d2" + ] + ] + }, + { + "id":"813b9751.dba8a8", + "type":"debug", + "z":"23a9162c.cb1e6a", + "name":"", + "active":true, + "tosidebar":true, + "console":false, + "tostatus":false, + "complete":"false", + "x":590, + "y":640, + "wires":[ - ] - } + ] + }, + { + "id":"5aff6f87.f125a", + "type":"ttn downlink", + "z":"23a9162c.cb1e6a", + "name":"", + "app":"f8f8391b.7e8728", + "dev_id":"", + "port":"", + "confirmed":false, + "schedule":"replace", + "x":590, + "y":560, + "wires":[ + + ] + }, + { + "id":"b1e45e31.61136", + "type":"debug", + "z":"23a9162c.cb1e6a", + "name":"", + "active":true, + "tosidebar":true, + "console":false, + "tostatus":false, + "complete":"payload", + "targetType":"msg", + "x":590, + "y":100, + "wires":[ + + ] + } ] From cab44588c79d1d09ed0de076690942829b3ff88d Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Tue, 27 Aug 2019 16:00:25 +0200 Subject: [PATCH 11/23] start uart timestamp send when time has been synced --- src/timesync.cpp | 2 ++ src/uart.cpp | 18 +++++++++++++++++- src/uart.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index 488561c2..09e28b3b 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -12,6 +12,7 @@ algorithm in applications without granted license by the patent holder. #if (TIME_SYNC_LORASERVER) && (HAS_LORA) #include "timesync.h" +#include "uart.h" // Local logging tag static const char TAG[] = __FILE__; @@ -282,6 +283,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, timeSource = mytimesource; // set global variable timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); + time_uart_send_start(); ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was set | source: %c", millis() / 1000.0, timeSetSymbols[timeSource]); } else { diff --git a/src/uart.cpp b/src/uart.cpp index 52ac63f2..1303cbf5 100644 --- a/src/uart.cpp +++ b/src/uart.cpp @@ -5,6 +5,9 @@ #include "globals.h" #include "driver/uart.h" +static const char TAG[] = __FILE__; +TaskHandle_t UartTask = NULL; + void time_uart_send(void * pvParameters) { struct timeval curTime; @@ -25,7 +28,7 @@ void time_uart_send(void * pvParameters) { // gettimeofday(&curTime, &tz); int sleep = 1000 - (curTime.tv_usec/1000); ostime_t now = os_getTime(); - printf("Sleep Time: %d, now: %d\n", sleep, now); + ESP_LOGD(TAG, "Sleep Time: %d, now: %d\n", sleep, now); vTaskDelayUntil( &xLastWakeTime, (TickType_t)(sleep/portTICK_PERIOD_MS) ); // Read UART for testing purposes @@ -43,3 +46,16 @@ void time_uart_send(void * pvParameters) { vTaskDelete(NULL); } + +void time_uart_send_start() { + if (UartTask) { + return; + } + xTaskCreatePinnedToCore(time_uart_send, // task function + "time_uart_send", // name of task + 2048, // stack size of task + (void *)1, // parameter of the task + 2, // priority of the task + &UartTask, // task handle + 1); // CPU core +} diff --git a/src/uart.h b/src/uart.h index 78b81ed5..612aabbe 100644 --- a/src/uart.h +++ b/src/uart.h @@ -1 +1,2 @@ void time_uart_send(void * pvParameters); +void time_uart_send_start(); From 4a573bc638a747b4838d0ab486786f680c33558a Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Tue, 27 Aug 2019 17:27:57 +0200 Subject: [PATCH 12/23] send time via uart --- include/main.h | 1 + src/main.cpp | 2 ++ src/uart.cpp | 24 +++++++++++++++++++++++- src/uart.h | 13 +++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/main.h b/include/main.h index bce12e51..215aa47e 100644 --- a/include/main.h +++ b/include/main.h @@ -16,6 +16,7 @@ #include "irqhandler.h" #include "led.h" #include "spislave.h" +#include "uart.h" #if(HAS_LORA) #include "lorawan.h" #endif diff --git a/src/main.cpp b/src/main.cpp index c6dae99e..2a7aab1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -444,6 +444,8 @@ void setup() { // show compiled features ESP_LOGI(TAG, "Features:%s", features); + uart_setup(); + } // setup() void loop() { diff --git a/src/uart.cpp b/src/uart.cpp index 1303cbf5..b71b3e28 100644 --- a/src/uart.cpp +++ b/src/uart.cpp @@ -1,13 +1,30 @@ +#include "rtctime.h" + #include #include #include #include "globals.h" +#include "uart.h" #include "driver/uart.h" static const char TAG[] = __FILE__; TaskHandle_t UartTask = NULL; +void uart_setup() { + // setup UART connection + uart_config_t uart_config = { + .baud_rate = 9600, + .data_bits = UART_DATA_7_BITS, + .parity = UART_PARITY_EVEN, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + }; + uart_param_config(UART_NUM_1, &uart_config); + uart_set_pin(UART_NUM_1, CLOCK_DCF_TXD, CLOCK_DCF_RXD, CLOCK_DCF_RTS, CLOCK_DCF_CTS); + uart_driver_install(UART_NUM_1, CLOCK_BUF_SIZE * 2, 0, 0, NULL, 0); +} + void time_uart_send(void * pvParameters) { struct timeval curTime; @@ -16,7 +33,12 @@ void time_uart_send(void * pvParameters) { TickType_t xLastWakeTime = xTaskGetTickCount(); for(;;) { - time_t nowTime = now(); + struct timeval tv; + struct timezone tz; + if(gettimeofday(&tv, &tz) != 0) { + ESP_LOGI(TAG, "ERROR gettimeofday"); + } + time_t nowTime = tv.tv_sec; strftime(timestamp, sizeof(timestamp), format, localtime(&nowTime)); ESP_LOGI(TAG, "Current Time: %s", timestamp); diff --git a/src/uart.h b/src/uart.h index 612aabbe..f12acbaf 100644 --- a/src/uart.h +++ b/src/uart.h @@ -1,2 +1,15 @@ +#ifndef UART_H +#define UART_H + +// UART for Clock DCF +#define CLOCK_DCF_TXD (GPIO_NUM_4) +#define CLOCK_DCF_RXD (GPIO_NUM_15) +#define CLOCK_DCF_RTS (UART_PIN_NO_CHANGE) +#define CLOCK_DCF_CTS (UART_PIN_NO_CHANGE) +#define CLOCK_BUF_SIZE (1024) + void time_uart_send(void * pvParameters); void time_uart_send_start(); +void uart_setup(); + +#endif From 4d42c961b22b697eac107e627a315a69a8df93ca Mon Sep 17 00:00:00 2001 From: Florian Ludwig Date: Thu, 29 Aug 2019 10:32:55 +0300 Subject: [PATCH 13/23] move timesync seq_no into message body from port --- include/timesync.h | 2 +- src/lorawan.cpp | 6 ++---- src/paxcounter.conf | 7 +++---- src/timesync.cpp | 18 ++++++++++-------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index 286d82e7..1f431acb 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -12,7 +12,7 @@ void timesync_init(void); void send_timesync_req(void); -int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len); +int recv_timesync_ans(uint8_t buf[], uint8_t buf_len); void process_timesync_req(void *taskparameter); void store_time_sync_req(uint32_t t_millisec); void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t timesource); diff --git a/src/lorawan.cpp b/src/lorawan.cpp index b5e941cb..ca4425f1 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -265,10 +265,8 @@ void onEvent(ev_t ev) { #if (TIME_SYNC_LORASERVER) // timesync answer -> call timesync processor - if ((LMIC.frame[LMIC.dataBeg] >= TIMEANSWERPORT_MIN) && - (LMIC.frame[LMIC.dataBeg] <= TIMEANSWERPORT_MAX)) { - recv_timesync_ans(LMIC.frame[LMIC.dataBeg], - LMIC.frame + LMIC.dataBeg, LMIC.dataLen); + if ((LMIC.frame[LMIC.dataBeg] == TIMEPORT) { + recv_timesync_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); break; } #endif diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 9dffe04e..6d5eb508 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -28,7 +28,7 @@ * |< Scan Window > |< Scan Window > | ... |< Scan Window > | * |< Scan Interval >|< Scan Interval >| ... |< Scan Interval >| * |< Scan duration >| -* +* * Scan duration sets how long scanning should be going on, before starting a new scan cycle. 0 means infinite (default). * Scan window sets how much of the interval should be occupied by scanning. Should be >= BLESCANINTERVAL. * Scan interval is how long scanning should be done on each channel. BLE uses 3 channels for advertising. @@ -93,10 +93,9 @@ #define BEACONPORT 6 // beacon alarms #define BMEPORT 7 // BME680 sensor #define BATTPORT 8 // battery voltage -#define TIMEPORT 9 // time query +#define TIMEPORT 9 // time query and response #define TIMEDIFFPORT 13 // time adjust diff -#define TIMEANSWERPORT_MIN 0xA0 // time answer, start of port range -#define TIMEANSWERPORT_MAX 0xDF // time answer, end of port range +#define TIMEREQUEST_MAX_SEQNO 250 // time answer, start of port range #define SENSOR1PORT 10 // user sensor #1 #define SENSOR2PORT 11 // user sensor #2 #define SENSOR3PORT 12 // user sensor #3 diff --git a/src/timesync.cpp b/src/timesync.cpp index 09e28b3b..11ca1787 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -26,7 +26,7 @@ typedef std::chrono::duration> TaskHandle_t timeSyncReqTask = NULL; -static uint8_t time_sync_seqNo = random(TIMEANSWERPORT_MIN, TIMEANSWERPORT_MAX); +static uint8_t time_sync_seqNo = 0; static bool timeSyncPending = false; static myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES]; static myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES]; @@ -94,9 +94,10 @@ void process_timesync_req(void *taskparameter) { time_point_cast(time_sync_tx[k]); // wrap around seqNo, keeping it in time port range - time_sync_seqNo = (time_sync_seqNo < TIMEANSWERPORT_MAX) - ? time_sync_seqNo + 1 - : TIMEANSWERPORT_MIN; + time_sync_seqNo++; + if(time_sync_seqNo > TIMEREQUEST_MAX_SEQNO) { + time_sync_seqNo = 0; + } if (i < TIME_SYNC_SAMPLES - 1) { // wait until next cycle @@ -155,7 +156,9 @@ void store_time_sync_req(uint32_t timestamp) { } // process timeserver timestamp answer, called from lorawan.cpp -int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) { +int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { + uint8_t seq_no = buf[0]; + buf++; // if no timesync handshake is pending then exit if (!timeSyncPending) @@ -175,15 +178,14 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) { else { // we received a probably valid time frame uint8_t k = seq_no % TIME_SYNC_SAMPLES; - uint8_t *timestamp_buf = buf+1; // the 5th byte contains the fractional seconds in 2^-8 second steps // (= 1/250th sec), we convert this to ms - uint16_t timestamp_msec = 4 * timestamp_buf[4]; + uint16_t timestamp_msec = 4 * buf[4]; // pointers to 4 bytes 4 bytes containing UTC seconds since unix epoch, msb uint32_t timestamp_sec, *timestamp_ptr; // convert buffer to uint32_t, octet order is big endian - timestamp_ptr = (uint32_t *)timestamp_buf; + timestamp_ptr = (uint32_t *)buf; // swap byte order from msb to lsb, note: this is platform dependent timestamp_sec = __builtin_bswap32(*timestamp_ptr); From 1e53a25d038dbbdd62289741d03858844b24807a Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Thu, 29 Aug 2019 14:55:31 +0200 Subject: [PATCH 14/23] typo --- src/lorawan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lorawan.cpp b/src/lorawan.cpp index ca4425f1..c4ef37e3 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -265,7 +265,7 @@ void onEvent(ev_t ev) { #if (TIME_SYNC_LORASERVER) // timesync answer -> call timesync processor - if ((LMIC.frame[LMIC.dataBeg] == TIMEPORT) { + if (LMIC.frame[LMIC.dataBeg - 1] == TIMEPORT) { recv_timesync_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); break; } From 2ec696649669fbdc548778967a9e4ec2fdf63fa0 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 2 Sep 2019 15:11:01 +0200 Subject: [PATCH 15/23] add OTA_TRIGGER_MAC to config --- build.py | 9 +++++---- src/ota.sample.conf | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build.py b/build.py index 28319853..52975106 100644 --- a/build.py +++ b/build.py @@ -90,10 +90,11 @@ env.Replace(BINTRAY_API_TOKEN=apitoken) # get runtime credentials and put them to compiler directive env.Append(BUILD_FLAGS=[ - u'-DWIFI_SSID=\\"' + mykeys["OTA_WIFI_SSID"] + '\\"', - u'-DWIFI_PASS=\\"' + mykeys["OTA_WIFI_PASS"] + '\\"', - u'-DBINTRAY_USER=\\"' + mykeys["BINTRAY_USER"] + '\\"', - u'-DBINTRAY_REPO=\\"' + mykeys["BINTRAY_REPO"] + '\\"', + u'-DWIFI_SSID=\\"' + mykeys["OTA_WIFI_SSID"] + '\\"', + u'-DWIFI_PASS=\\"' + mykeys["OTA_WIFI_PASS"] + '\\"', + u'-DOTA_TRIGGER_MAC=\\"' + mykeys["OTA_TRIGGER_MAC"] + '\\"', + u'-DBINTRAY_USER=\\"' + mykeys["BINTRAY_USER"] + '\\"', + u'-DBINTRAY_REPO=\\"' + mykeys["BINTRAY_REPO"] + '\\"', u'-DBINTRAY_PACKAGE=\\"' + package + '\\"', u'-DARDUINO_LMIC_PROJECT_CONFIG_H=' + lmicconfig, u'-I \"' + srcdir + '\"' diff --git a/src/ota.sample.conf b/src/ota.sample.conf index a10d79dc..fa698c31 100644 --- a/src/ota.sample.conf +++ b/src/ota.sample.conf @@ -1,6 +1,7 @@ [ota] OTA_WIFI_SSID = MyHomeWifi OTA_WIFI_PASS = FooBar42! +OTA_TRIGGER_MAC = FFFFFFFF [bintray] BINTRAY_USER = MyBintrayUser From e92bcbd8b1fabebae938b3bc0e373d7beea9b350 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 2 Sep 2019 15:11:52 +0200 Subject: [PATCH 16/23] setup macsniff with ota trigger mac --- include/macsniff.h | 1 + src/macsniff.cpp | 12 ++++++++++++ src/main.cpp | 2 ++ 3 files changed, 15 insertions(+) diff --git a/include/macsniff.h b/include/macsniff.h index d41fc787..663a7749 100644 --- a/include/macsniff.h +++ b/include/macsniff.h @@ -12,6 +12,7 @@ #define MAC_SNIFF_WIFI 0 #define MAC_SNIFF_BLE 1 +void macsniff_setup(void); uint16_t get_salt(void); uint64_t macConvert(uint8_t *paddr); bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type); diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 0b00e7c1..fdb37273 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -6,10 +6,22 @@ #include "vendor_array.h" #endif +#include +#include + // Local logging tag static const char TAG[] = __FILE__; uint16_t salt; +uint64_t fota_trigger_mac; + +void macsniff_setup() { + std::stringstream ss; + ss << std::hex << OTA_TRIGGER_MAC; + ESP_LOGI(TAG, "OTA_TRIGGER_MAC %X", OTA_TRIGGER_MAC); + ss >> fota_trigger_mac; + std::cout << static_cast(fota_trigger_mac) << std::endl; +} uint16_t get_salt(void) { salt = (uint16_t)random(65536); // get new 16bit random for salting hashes diff --git a/src/main.cpp b/src/main.cpp index 2a7aab1a..a8e50ea5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -444,6 +444,8 @@ void setup() { // show compiled features ESP_LOGI(TAG, "Features:%s", features); + macsniff_setup(); + uart_setup(); } // setup() From a806c5e4637a29cadb91657403e1f81e3e340707 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 2 Sep 2019 15:12:22 +0200 Subject: [PATCH 17/23] recognize ota trigger mac when sniffing for new macs in wifi environment --- src/macsniff.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/macsniff.cpp b/src/macsniff.cpp index fdb37273..2ab8740e 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -94,6 +94,25 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { // Count only if MAC was not yet seen if (added) { + ESP_LOGD(TAG, "new Mac: %x:%x:%x:%x:%x:%x", + paddr[0], + paddr[1], + paddr[2], + paddr[3], + paddr[4], + paddr[5] + ); + // is newly found MAC the OTA trigger? + uint64_t addr48 = (((uint64_t)paddr[3]) | ((uint64_t)paddr[2] << 8) | + ((uint64_t)paddr[1] << 16) | ((uint64_t)paddr[0] << 24)); + if((int)(addr48-fota_trigger_mac) == 0) + { + ESP_LOGI(TAG, "OTA-MAC found, Update triggered"); + // initiate OTA update + uint8_t cmd[2] = {9, 9}; + rcommand(cmd, 2); + } + // increment counter and one blink led if (sniff_type == MAC_SNIFF_WIFI) { macs_wifi++; // increment Wifi MACs counter @@ -146,4 +165,4 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { // True if MAC WiFi/BLE was new return added; // function returns bool if a new and unique Wifi or BLE mac was // counted (true) or not (false) -} \ No newline at end of file +} From 2530bd4235446ceb9983e6daddc0e08be2930c47 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 2 Sep 2019 16:56:50 +0200 Subject: [PATCH 18/23] remove unneeded logging --- src/macsniff.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 2ab8740e..079c2f8c 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -18,7 +18,6 @@ uint64_t fota_trigger_mac; void macsniff_setup() { std::stringstream ss; ss << std::hex << OTA_TRIGGER_MAC; - ESP_LOGI(TAG, "OTA_TRIGGER_MAC %X", OTA_TRIGGER_MAC); ss >> fota_trigger_mac; std::cout << static_cast(fota_trigger_mac) << std::endl; } From 004444dea3d67034ff366a28571d75cad009dd5d Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 2 Sep 2019 16:55:14 +0200 Subject: [PATCH 19/23] correct logging level for error --- src/timesync.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index 11ca1787..66bbd06c 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -249,7 +249,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, struct timeval tv; struct timezone tz; if(gettimeofday(&tv, &tz) != 0) { - ESP_LOGI(TAG, "ERROR gettimeofday"); + ESP_LOGE(TAG, "ERROR gettimeofday"); } struct timeval beforeTime = tv; From 592c9d6edea5d2267d2cb6f8bef2c15f43ee1682 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 2 Sep 2019 16:55:29 +0200 Subject: [PATCH 20/23] log sent timediff --- src/timesync.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/timesync.cpp b/src/timesync.cpp index 66bbd06c..be752fac 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -282,6 +282,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, payload.reset(); payload.addTimeDiff(timediff_ms); SendPayload(TIMEDIFFPORT, prio_high); + ESP_LOGI(TAG, "timediff_ms: %d", timediff_ms); timeSource = mytimesource; // set global variable timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); From fc2997c9fd87f4d7b67af5a1492e00ca1c09d5c2 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Mon, 2 Sep 2019 16:56:06 +0200 Subject: [PATCH 21/23] start timediffseq at -1, so first syncing can finish --- src/timesync.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index be752fac..e51454eb 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -26,7 +26,7 @@ typedef std::chrono::duration> TaskHandle_t timeSyncReqTask = NULL; -static uint8_t time_sync_seqNo = 0; +static uint8_t time_sync_seqNo = -1; static bool timeSyncPending = false; static myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES]; static myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES]; From fc7a8b361b467957c22e7b4fad5da2f8b821f935 Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Thu, 10 Oct 2019 17:34:01 +0200 Subject: [PATCH 22/23] development reverted to cyberman development --- build.py | 9 ++- include/macsniff.h | 1 - include/main.h | 3 +- include/payload.h | 1 - src/Timeserver/Nodered-Timeserver.json | 29 +++++---- src/lorawan.cpp | 2 +- src/macsniff.cpp | 32 +--------- src/main.cpp | 6 +- src/ota.sample.conf | 1 - src/payload.cpp | 15 ++--- src/timesync.cpp | 1 - src/uart.cpp | 83 -------------------------- src/uart.h | 15 ----- 13 files changed, 31 insertions(+), 167 deletions(-) delete mode 100644 src/uart.cpp delete mode 100644 src/uart.h diff --git a/build.py b/build.py index 685ada36..c4f3ccc3 100644 --- a/build.py +++ b/build.py @@ -98,11 +98,10 @@ env.Replace(BINTRAY_API_TOKEN=apitoken) # get runtime credentials and put them to compiler directive env.Append(BUILD_FLAGS=[ - u'-DWIFI_SSID=\\"' + mykeys["OTA_WIFI_SSID"] + '\\"', - u'-DWIFI_PASS=\\"' + mykeys["OTA_WIFI_PASS"] + '\\"', - u'-DOTA_TRIGGER_MAC=\\"' + mykeys["OTA_TRIGGER_MAC"] + '\\"', - u'-DBINTRAY_USER=\\"' + mykeys["BINTRAY_USER"] + '\\"', - u'-DBINTRAY_REPO=\\"' + mykeys["BINTRAY_REPO"] + '\\"', + u'-DWIFI_SSID=\\"' + mykeys["OTA_WIFI_SSID"] + '\\"', + u'-DWIFI_PASS=\\"' + mykeys["OTA_WIFI_PASS"] + '\\"', + u'-DBINTRAY_USER=\\"' + mykeys["BINTRAY_USER"] + '\\"', + u'-DBINTRAY_REPO=\\"' + mykeys["BINTRAY_REPO"] + '\\"', u'-DBINTRAY_PACKAGE=\\"' + package + '\\"', u'-DARDUINO_LMIC_PROJECT_CONFIG_H=' + lmicconfig, u'-I \"' + srcdir + '\"' diff --git a/include/macsniff.h b/include/macsniff.h index 663a7749..d41fc787 100644 --- a/include/macsniff.h +++ b/include/macsniff.h @@ -12,7 +12,6 @@ #define MAC_SNIFF_WIFI 0 #define MAC_SNIFF_BLE 1 -void macsniff_setup(void); uint16_t get_salt(void); uint64_t macConvert(uint8_t *paddr); bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type); diff --git a/include/main.h b/include/main.h index ebb1d440..4b5617cb 100644 --- a/include/main.h +++ b/include/main.h @@ -18,8 +18,7 @@ #include "irqhandler.h" #include "led.h" #include "spislave.h" -#include "uart.h" -#if(HAS_LORA) +#if (HAS_LORA) #include "lorawan.h" #endif #include "timekeeper.h" diff --git a/include/payload.h b/include/payload.h index 8ae86e6e..b41bb113 100644 --- a/include/payload.h +++ b/include/payload.h @@ -55,7 +55,6 @@ public: void addButton(uint8_t value); void addSensor(uint8_t[]); void addTime(time_t value); - void addTimeDiff(int32_t value); #if (PAYLOAD_ENCODER == 1) // format plain diff --git a/src/Timeserver/Nodered-Timeserver.json b/src/Timeserver/Nodered-Timeserver.json index 118161a1..085b132d 100644 --- a/src/Timeserver/Nodered-Timeserver.json +++ b/src/Timeserver/Nodered-Timeserver.json @@ -14,17 +14,26 @@ "to": "down", "tot": "str" }, - "page-titlebar-backgroundColor":{ - "value":"#0094CE", - "edited":false + { + "t": "set", + "p": "payload.confirmed", + "pt": "msg", + "to": "false", + "tot": "bool" }, - "page-backgroundColor":{ - "value":"#fafafa", - "edited":false + { + "t": "set", + "p": "payload.schedule", + "pt": "msg", + "to": "replace", + "tot": "str" }, - "page-sidebar-backgroundColor":{ - "value":"#ffffff", - "edited":false + { + "t": "move", + "p": "payload", + "pt": "msg", + "to": "payload.payload_raw", + "tot": "msg" }, { "t": "move", @@ -390,4 +399,4 @@ "disabled": false, "hidden": false } -] +] \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 791725bd..31c5ae80 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -677,4 +677,4 @@ const char *getCrName(rps_t rps) { return t[getCr(rps)]; } -#endif // HAS_LORA +#endif // HAS_LORA \ No newline at end of file diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 079c2f8c..0b00e7c1 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -6,21 +6,10 @@ #include "vendor_array.h" #endif -#include -#include - // Local logging tag static const char TAG[] = __FILE__; uint16_t salt; -uint64_t fota_trigger_mac; - -void macsniff_setup() { - std::stringstream ss; - ss << std::hex << OTA_TRIGGER_MAC; - ss >> fota_trigger_mac; - std::cout << static_cast(fota_trigger_mac) << std::endl; -} uint16_t get_salt(void) { salt = (uint16_t)random(65536); // get new 16bit random for salting hashes @@ -93,25 +82,6 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { // Count only if MAC was not yet seen if (added) { - ESP_LOGD(TAG, "new Mac: %x:%x:%x:%x:%x:%x", - paddr[0], - paddr[1], - paddr[2], - paddr[3], - paddr[4], - paddr[5] - ); - // is newly found MAC the OTA trigger? - uint64_t addr48 = (((uint64_t)paddr[3]) | ((uint64_t)paddr[2] << 8) | - ((uint64_t)paddr[1] << 16) | ((uint64_t)paddr[0] << 24)); - if((int)(addr48-fota_trigger_mac) == 0) - { - ESP_LOGI(TAG, "OTA-MAC found, Update triggered"); - // initiate OTA update - uint8_t cmd[2] = {9, 9}; - rcommand(cmd, 2); - } - // increment counter and one blink led if (sniff_type == MAC_SNIFF_WIFI) { macs_wifi++; // increment Wifi MACs counter @@ -164,4 +134,4 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { // True if MAC WiFi/BLE was new return added; // function returns bool if a new and unique Wifi or BLE mac was // counted (true) or not (false) -} +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 602a8e8a..79b692ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -465,11 +465,7 @@ void setup() { // show compiled features ESP_LOGI(TAG, "Features:%s", features); - macsniff_setup(); - - uart_setup(); - -} // setup() + vTaskDelete(NULL); } // setup() diff --git a/src/ota.sample.conf b/src/ota.sample.conf index fa698c31..a10d79dc 100644 --- a/src/ota.sample.conf +++ b/src/ota.sample.conf @@ -1,7 +1,6 @@ [ota] OTA_WIFI_SSID = MyHomeWifi OTA_WIFI_PASS = FooBar42! -OTA_TRIGGER_MAC = FFFFFFFF [bintray] BINTRAY_USER = MyBintrayUser diff --git a/src/payload.cpp b/src/payload.cpp index e755f077..fad4a1bc 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -16,14 +16,7 @@ uint8_t *PayloadConvert::getBuffer(void) { return buffer; } /* ---------------- plain format without special encoding ---------- */ -void PayloadConvert::addTimeDiff(int32_t value) { - buffer[cursor++] = (byte)((value & 0xFF000000) >> 24); - buffer[cursor++] = (byte)((value & 0x00FF0000) >> 16); - buffer[cursor++] = (byte)((value & 0x0000FF00) >> 8); - buffer[cursor++] = (byte)((value & 0x000000FF)); -} - -#if PAYLOAD_ENCODER == 1 +#if (PAYLOAD_ENCODER == 1) void PayloadConvert::addByte(uint8_t value) { buffer[cursor++] = (value); } @@ -315,8 +308,8 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, #elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4)) -void PayloadConvert::addByte(uint8_t value) { - /* +void PayloadConvert::addByte(uint8_t value) { + /* not implemented */ } @@ -493,4 +486,4 @@ void PayloadConvert::addTime(time_t value) { #endif } -#endif +#endif \ No newline at end of file diff --git a/src/timesync.cpp b/src/timesync.cpp index 5b1b4d34..e77897cf 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -12,7 +12,6 @@ algorithm in applications without granted license by the patent holder. #if (TIME_SYNC_LORASERVER) && (HAS_LORA) #include "timesync.h" -#include "uart.h" // Local logging tag static const char TAG[] = __FILE__; diff --git a/src/uart.cpp b/src/uart.cpp deleted file mode 100644 index b71b3e28..00000000 --- a/src/uart.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "rtctime.h" - -#include -#include - -#include -#include "globals.h" -#include "uart.h" -#include "driver/uart.h" - -static const char TAG[] = __FILE__; -TaskHandle_t UartTask = NULL; - -void uart_setup() { - // setup UART connection - uart_config_t uart_config = { - .baud_rate = 9600, - .data_bits = UART_DATA_7_BITS, - .parity = UART_PARITY_EVEN, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE - }; - uart_param_config(UART_NUM_1, &uart_config); - uart_set_pin(UART_NUM_1, CLOCK_DCF_TXD, CLOCK_DCF_RXD, CLOCK_DCF_RTS, CLOCK_DCF_CTS); - uart_driver_install(UART_NUM_1, CLOCK_BUF_SIZE * 2, 0, 0, NULL, 0); -} - -void time_uart_send(void * pvParameters) { - struct timeval curTime; - - const char* format = "\bOAL%y%m%dF%H%M%S\r"; - char timestamp[64] = {0}; - TickType_t xLastWakeTime = xTaskGetTickCount(); - - for(;;) { - struct timeval tv; - struct timezone tz; - if(gettimeofday(&tv, &tz) != 0) { - ESP_LOGI(TAG, "ERROR gettimeofday"); - } - time_t nowTime = tv.tv_sec; - strftime(timestamp, sizeof(timestamp), format, localtime(&nowTime)); - ESP_LOGI(TAG, "Current Time: %s", timestamp); - - int len = strlen(timestamp)+1; - - // Send Data via UART - uart_write_bytes(UART_NUM_1, timestamp, len); - - // gettimeofday(&curTime, &tz); - int sleep = 1000 - (curTime.tv_usec/1000); - ostime_t now = os_getTime(); - ESP_LOGD(TAG, "Sleep Time: %d, now: %d\n", sleep, now); - vTaskDelayUntil( &xLastWakeTime, (TickType_t)(sleep/portTICK_PERIOD_MS) ); - - // Read UART for testing purposes - /** - uint8_t *data = (uint8_t *) malloc(CLOCK_BUF_SIZE); - - // Read Data via UART - uart_read_bytes(UART_NUM_1, data, CLOCK_BUF_SIZE, 20 / portTICK_RATE_MS); - const char * dataC = (const char *) data; - ESP_LOGI(TAG, "Data Read: %s", dataC); - - free(data); - **/ - } - - vTaskDelete(NULL); -} - -void time_uart_send_start() { - if (UartTask) { - return; - } - xTaskCreatePinnedToCore(time_uart_send, // task function - "time_uart_send", // name of task - 2048, // stack size of task - (void *)1, // parameter of the task - 2, // priority of the task - &UartTask, // task handle - 1); // CPU core -} diff --git a/src/uart.h b/src/uart.h deleted file mode 100644 index f12acbaf..00000000 --- a/src/uart.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef UART_H -#define UART_H - -// UART for Clock DCF -#define CLOCK_DCF_TXD (GPIO_NUM_4) -#define CLOCK_DCF_RXD (GPIO_NUM_15) -#define CLOCK_DCF_RTS (UART_PIN_NO_CHANGE) -#define CLOCK_DCF_CTS (UART_PIN_NO_CHANGE) -#define CLOCK_BUF_SIZE (1024) - -void time_uart_send(void * pvParameters); -void time_uart_send_start(); -void uart_setup(); - -#endif From f417e336a2efbb2302076c349ed1a2435254f4ff Mon Sep 17 00:00:00 2001 From: Marius Gripp Date: Thu, 10 Oct 2019 16:11:38 +0200 Subject: [PATCH 23/23] issue 464 fix: time set in UTC --- src/timesync.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index e77897cf..917140d0 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -209,7 +209,7 @@ int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) { // construct the timepoint when message was seen on gateway time_sync_rx[k] += - seconds(timestamp_sec + timezone_sec) + milliseconds(timestamp_msec); + seconds(timestamp_sec) + milliseconds(timestamp_msec); // we guess timepoint is recent if it newer than code compile date if (timeIsValid(myClock::to_time_t(time_sync_rx[k]))) {