diff --git a/.gitignore b/.gitignore index 3d7afc0a..875ecd9f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ .clang_complete .gcc-flags.json src/loraconf.h -src/ota.conf -src/NodeRed/Timeserver.json \ No newline at end of file +src/ota.conf \ No newline at end of file diff --git a/include/payload.h b/include/payload.h index fa7d05d8..e2356764 100644 --- a/include/payload.h +++ b/include/payload.h @@ -42,7 +42,6 @@ public: uint8_t getSize(void); uint8_t *getBuffer(void); void addByte(uint8_t value); - void addWord(uint16_t value); void addCount(uint16_t value, uint8_t sniffytpe); void addConfig(configData_t value); void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem, diff --git a/include/timesync.h b/include/timesync.h index d79290d4..33d35462 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -16,10 +16,6 @@ typedef struct { uint8_t fractions; // 1/250ths second = 4 milliseconds resolution } 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 recv_Servertime_ans(uint8_t buf[], uint8_t buf_len); void process_Servertime_sync_req(void *taskparameter); diff --git a/src/TTN/Nodered-Timeserver.json b/src/TTN/Nodered-Timeserver.json new file mode 100644 index 00000000..f5ebdf70 --- /dev/null +++ b/src/TTN/Nodered-Timeserver.json @@ -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": "" + } +] \ No newline at end of file diff --git a/src/payload.cpp b/src/payload.cpp index b5f66378..d8cf2a4e 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -20,11 +20,6 @@ uint8_t *PayloadConvert::getBuffer(void) { return buffer; } 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) { buffer[cursor++] = highByte(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::addWord(uint16_t value) { writeUint16(value); } - void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { writeUint16(value); } @@ -315,11 +308,6 @@ void PayloadConvert::addByte(uint8_t value) { not implemented */ } -void PayloadConvert::addWord(uint16_t value) { - /* - not implemented - */ } - void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { switch (snifftype) { case MAC_SNIFF_WIFI: diff --git a/src/senddata.cpp b/src/senddata.cpp index fab57a10..36e8b278 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -40,16 +40,8 @@ void SendPayload(uint8_t port, sendprio_t prio) { // enqueue message in device's send queues #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 - #ifdef HAS_SPI spi_enqueuedata(&SendBuffer, prio); #endif diff --git a/src/timesync.cpp b/src/timesync.cpp index c7419396..26267b14 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -5,8 +5,7 @@ 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 repository's licencse, but you may not be eligible to deploy the applied -algorithm in applications without granted license for the algorithm by the -patent holder. +algorithm in applications without granted license by the patent holder. */ @@ -37,8 +36,8 @@ void send_Servertime_req() { // clear timestamp array 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].fractions = time_sync_answers[i].fractions = 0; + time_sync_messages[i].seconds = time_sync_answers[i].seconds = + time_sync_messages[i].fractions = time_sync_answers[i].fractions = 0; } // kick off temporary task for timeserver handshake processing @@ -63,12 +62,9 @@ void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len) { uint8_t seq_no = buf[0]; uint32_t timestamp_sec = 0, timestamp_ms = 0; - for (int i = 1; i <= 4; i++) { - timestamp_sec = (timestamp_sec << 8) | buf[i]; - time_sync_answers[seq_no].seconds = timestamp_sec; - } - timestamp_ms = 4 * buf[5]; - time_sync_answers[seq_no].fractions = timestamp_ms; + for (uint8_t i = 1; i <= 4; i++) + time_sync_answers[seq_no].seconds = (timestamp_sec <<= 8) |= buf[i]; + time_sync_answers[seq_no].fractions = timestamp_ms = 4 * buf[5]; ESP_LOGD(TAG, "Timeserver answer #%d received: timestamp=%d.%03d", seq_no, 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 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; + uint32_t seq_no = 0; + int16_t time_diff_ms = 0; + int64_t time_diff_sec = 0; float time_offset = 0.0f; // enqueue timestamp samples in lora sendqueue @@ -97,46 +95,41 @@ void process_Servertime_sync_req(void *taskparameter) { SendPayload(TIMEPORT, prio_high); // send dummy packet to trigger receive answer - payload.reset(); - payload.addByte(0x99); // flush - SendPayload(RCMDPORT, prio_low); // open receive slot for answer + //payload.reset(); + //payload.addByte(0x99); // flush + //SendPayload(RCMDPORT, prio_low); // to open receive slot for answer // process answer if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, TIME_SYNC_TIMEOUT * 1000 / portTICK_PERIOD_MS) == pdFALSE) || (seq_no != time_sync_seqNo)) { - ESP_LOGW(TAG, "Timeserver handshake error"); + ESP_LOGW(TAG, "Timeserver handshake failed"); goto finish; } // no valid sequence received before timeout else { // calculate time diff from set of collected timestamps uint8_t k = seq_no % TIME_SYNC_SAMPLES; time_diff_sec += - time_sync_messages[k].seconds - time_sync_answers[k].seconds; - time_diff_ms += 4 * (time_sync_messages[k].fractions - - time_sync_answers[k].fractions); + (time_sync_messages[k].seconds - time_sync_answers[k].seconds); + time_diff_ms += (4 * (time_sync_messages[k].fractions - + time_sync_answers[k].fractions)); } - } + } // for // calculate time offset and set time if necessary - time_offset = - (time_diff_sec + time_diff_ms / 1000.0f) / (TIME_SYNC_SAMPLES * 1.0f); - - ESP_LOGD(TAG, "Timesync time offset=%d.%03d", - time_diff_sec / TIME_SYNC_SAMPLES, time_diff_ms / TIME_SYNC_SAMPLES); + time_offset = (time_diff_sec + time_diff_ms / 1000.0f) / TIME_SYNC_SAMPLES; + ESP_LOGD(TAG, "Timesync time offset=%.3f", time_offset); + ESP_LOGD(TAG, "Timesync time_diff_ms=%03d", time_diff_ms); if (time_offset >= TIME_SYNC_TRIGGER) { - // wait until top of second - if (time_diff_ms > 0) { - vTaskDelay(1000 - time_diff_ms); // clock is fast - time_diff_sec--; - } else if (time_diff_ms < 0) { // clock is stale - vTaskDelay(1000 + time_diff_ms); - time_diff_sec++; - } + if (time_diff_ms > 0) // clock is fast + vTaskDelay(pdMS_TO_TICKS(time_diff_ms)); + else if (time_diff_ms < 0) // clock is slow + vTaskDelay(pdMS_TO_TICKS(1000 + time_diff_ms)); + time_diff_sec++; time_to_set = time_t(now() + time_diff_sec); ESP_LOGD(TAG, "Time to set = %d", time_to_set); @@ -160,7 +153,7 @@ finish: 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) { uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES; time_sync_messages[k].seconds = secs;