From d597f50d5349da5e20ff2936b310e52d28bd2a1f Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 16 Mar 2019 00:38:41 +0100 Subject: [PATCH 01/11] timesync fixes --- src/TTN/Nodered-Timeserver.json | 34 ++++++++++++++++----------------- src/main.cpp | 2 +- src/timesync.cpp | 11 +++++++---- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/TTN/Nodered-Timeserver.json b/src/TTN/Nodered-Timeserver.json index f2aac01c..7520e527 100644 --- a/src/TTN/Nodered-Timeserver.json +++ b/src/TTN/Nodered-Timeserver.json @@ -126,22 +126,6 @@ ] ] }, - { - "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.gateways[0].time));\n \n var seconds = Math.floor(timestamp/1000);\n var fractions = (timestamp % 1000) / 4;\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": 400, - "y": 280, - "wires": [ - [ - "49e3c067.e782e" - ] - ] - }, { "id": "dac8aafa.389298", "type": "json", @@ -219,7 +203,23 @@ "y": 200, "wires": [ [ - "f4c5b6de.f95148" + "595229b0.5df3d8" + ] + ] + }, + { + "id": "595229b0.5df3d8", + "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 timestamp = (+new Date(msg.payload.metadata.gateways[0].time));\n let timestamp_server = (+new Date(msg.payload.metadata.time));\n \n if ((timestamp < timestamp_server) && (timestamp > 0)) {\n \n var seconds = Math.floor(timestamp/1000);\n var fractions = (timestamp % 1000) / 4;\n var seqno = msg.payload.payload_raw[0];\n\n let buf = new ArrayBuffer(6);\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;\n}\n\nelse\n\nreturn null;", + "outputs": 1, + "noerr": 0, + "x": 380, + "y": 280, + "wires": [ + [ + "49e3c067.e782e" ] ] }, diff --git a/src/main.cpp b/src/main.cpp index ced48264..2a7dc259 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -187,7 +187,7 @@ void setup() { // set low power mode to off #ifdef HAS_LOWPOWER_SWITCH pinMode(HAS_LOWPOWER_SWITCH, OUTPUT); - digitalWrite(HAS_LOWPOWER_SWITCH, HIGH); + digitalWrite(HAS_LOWPOWER_SWITCH, LOW); strcat_P(features, " LPWR"); #endif diff --git a/src/timesync.cpp b/src/timesync.cpp index 8442cdb6..e1c8e614 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -155,15 +155,18 @@ void process_timesync_req(void *taskparameter) { ESP_LOGD(TAG, "waiting %dms", 1000 - time_offset_msec); vTaskDelay(pdMS_TO_TICKS(1000 - time_offset_msec)); - setTime(time_to_set); // sync timer pps to top of second - if (ppsIRQ) - timerWrite(ppsIRQ, 10000); // fire interrupt + if (ppsIRQ) { + timerRestart(ppsIRQ); // reset pps timer + CLOCKIRQ(); // fire clock pps interrupt + } + setTime(time_to_set); timeSource = _lora; timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // set to regular repeat - ESP_LOGI(TAG, "Timesync finished, time adjusted by %lld ms", time_offset.count()); + ESP_LOGI(TAG, "Timesync finished, time adjusted by %lld ms", + time_offset.count()); } else ESP_LOGI(TAG, "Timesync finished, time not adjusted, is up to date"); } else From a62c0412b0b457ac7c7ee682bab5a3704284d364 Mon Sep 17 00:00:00 2001 From: Gregor Wolf Date: Sat, 16 Mar 2019 09:08:25 +0100 Subject: [PATCH 02/11] Correct sensor name in the output --- src/cyclic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cyclic.cpp b/src/cyclic.cpp index a2d9a579..3a35ad4a 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -65,7 +65,7 @@ void doHousekeeping() { // display BME280 sensor data #ifdef HAS_BME280 - ESP_LOGI(TAG, "BME680 Temp: %.2f°C | Humidity: %.2f | Pressure: %.0f", + ESP_LOGI(TAG, "BME280 Temp: %.2f°C | Humidity: %.2f | Pressure: %.0f", bme_status.temperature, bme_status.humidity, bme_status.pressure); #endif From 49bd69ca3adf3ea7762508993feb93b5aff5e7d7 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 16 Mar 2019 11:41:04 +0100 Subject: [PATCH 03/11] readme.md updated for sensors --- README.md | 6 +++++- src/sensor.cpp | 17 +++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6a26492e..c61039cf 100644 --- a/README.md +++ b/README.md @@ -138,9 +138,13 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp - Red long blink: LoRaWAN stack error - White long blink: Known Beacon detected +# Sensors and Peripherals + +You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensors.cpp*](src/sensors.cpp). Sensor payload transmit can be switched by remote control command 0x13 sent to Port 2. Bosch BME280 / BME680 environment sensors are supported. Enable *flag lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you want to use Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value for BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported an configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options. + # Clock controller -Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with an external time source. Use case of this function is to have paxcounter hardware integrated in clocks, and use it for both counting of pax and controlling the clock. Supported external time sources are GPS time, LORAWAN network time (v1.1) and on board RTC time. Precision of the synthetic DCF77 signal depends on precision of on board available time base. Supported are both external time base (e.g. timepulse pin of GPS chip or oscillator output of RTC chip) and internal ESP32 hardware timer. Selection of time base and clock frequency is done by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). +Paxcounter can be used to sync a clock which has DCF77 or IF482 time telegram input with. Use case of this function is to have paxcounter hardware integrated in clocks, and use it for both counting of pax and controlling the clock. Supported external time sources are GPS time, LORAWAN network time (v1.1) and on board RTC time. Precision of the synthetic DCF77 signal depends on precision of on board available time base. Supported are both external time base (e.g. timepulse pin of GPS chip or oscillator output of RTC chip) and internal ESP32 hardware timer. Selection of time base and clock frequency is done by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). # Payload format diff --git a/src/sensor.cpp b/src/sensor.cpp index 6b672f0e..807e6f78 100644 --- a/src/sensor.cpp +++ b/src/sensor.cpp @@ -45,25 +45,26 @@ uint8_t *sensor_read(uint8_t sensor) { case 1: + // insert user specific sensor data frames here */ buf[0] = length; - buf[1] = 0xff; - buf[2] = 0xa0; - buf[3] = 0x01; + buf[1] = 0x01; + buf[2] = 0x02; + buf[3] = 0x03; break; case 2: buf[0] = length; - buf[1] = 0xff; - buf[2] = 0xa0; - buf[3] = 0x02; + buf[1] = 0x01; + buf[2] = 0x02; + buf[3] = 0x03; break; case 3: buf[0] = length; - buf[1] = 0xff; - buf[2] = 0xa0; + buf[1] = 0x01; + buf[2] = 0x02; buf[3] = 0x03; break; } From 124ba16b8c60ef7622f0b22dc50c94b959d00fef Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 16 Mar 2019 12:02:12 +0100 Subject: [PATCH 04/11] readme.md: payloadmask, sensor data --- README.md | 17 ++++++++++++++++- src/configmanager.cpp | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c61039cf..4121f558 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,22 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp # Sensors and Peripherals -You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensors.cpp*](src/sensors.cpp). Sensor payload transmit can be switched by remote control command 0x13 sent to Port 2. Bosch BME280 / BME680 environment sensors are supported. Enable *flag lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you want to use Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value for BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported an configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options. +You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensors.cpp*](src/sensors.cpp). Bosch BME280 / BME680 environment sensors are supported. Enable *flag lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value for BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported an configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options. + +Output of sensor data is switched by bitmask register. The mask value can be adjusted during runtime by remote control command 0x13 sent to Port 2. Default mask (0x08) can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme: + +bit default sensor / data +0 0 GPS +1 0 Beacon alarm +2 0 Environment sensor (BME280/BME680) +3 1 Paxcounter (Wifi / BLE) +4 0 User sensor #1 +5 0 User sensor #2 +6 0 User sensor #3 +7 0 Battery voltage +8 0 reserved + +Note: By default transmit off all sensor data except counter data is disabled to respect networks fair use policies. If you configure sensors, you must switch on data transmit in payload mask to get values. # Clock controller diff --git a/src/configmanager.cpp b/src/configmanager.cpp index 583377a8..897bf783 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -31,7 +31,7 @@ void defaultConfig() { cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) cfg.monitormode = 0; // 0=disabled, 1=enabled cfg.runmode = 0; // 0=normal, 1=update - cfg.payloadmask = 0xFF; // all payload switched on + cfg.payloadmask = 0x08; // transmit counter data only cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = { 0}; // init BSEC state for BME680 sensor From 3c41ba6d4c3f1b767d0b1cdba01d56b94fcae19a Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 16 Mar 2019 12:10:15 +0100 Subject: [PATCH 05/11] readme.md --- README.md | 26 +++++++++++++------------- src/configmanager.cpp | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4121f558..49e9952a 100644 --- a/README.md +++ b/README.md @@ -140,22 +140,22 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp # Sensors and Peripherals -You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensors.cpp*](src/sensors.cpp). Bosch BME280 / BME680 environment sensors are supported. Enable *flag lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value for BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported an configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options. +You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensors.cpp*](src/sensors.cpp). Bosch BME280 / BME680 environment sensors are supported. Enable *flag lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value for BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, can be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options. -Output of sensor data is switched by bitmask register. The mask value can be adjusted during runtime by remote control command 0x13 sent to Port 2. Default mask (0x08) can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme: +Output of user sensor data can be switched by user remote control command 0x13 sent to Port 2. -bit default sensor / data -0 0 GPS -1 0 Beacon alarm -2 0 Environment sensor (BME280/BME680) -3 1 Paxcounter (Wifi / BLE) -4 0 User sensor #1 -5 0 User sensor #2 -6 0 User sensor #3 -7 0 Battery voltage -8 0 reserved +Output of sensor and peripheral data is internally switched by a bitmask register. Default mask (0xFF) can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme: -Note: By default transmit off all sensor data except counter data is disabled to respect networks fair use policies. If you configure sensors, you must switch on data transmit in payload mask to get values. +bit sensor / data +0 GPS +1 Beacon alarm +2 Environment sensor (BME280/BME680) +3 Paxcounter (Wifi + BLE) +4 User sensor #1 +5 User sensor #2 +6 User sensor #3 +7 Battery voltage +8 reserved # Clock controller diff --git a/src/configmanager.cpp b/src/configmanager.cpp index 897bf783..583377a8 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -31,7 +31,7 @@ void defaultConfig() { cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) cfg.monitormode = 0; // 0=disabled, 1=enabled cfg.runmode = 0; // 0=normal, 1=update - cfg.payloadmask = 0x08; // transmit counter data only + cfg.payloadmask = 0xFF; // all payload switched on cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = { 0}; // init BSEC state for BME680 sensor From 0d78d6e4d2e99a5e367ec1d5d5340932401eb4de Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 16 Mar 2019 12:18:35 +0100 Subject: [PATCH 06/11] readme.md --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 49e9952a..9d7e11d0 100644 --- a/README.md +++ b/README.md @@ -140,22 +140,22 @@ Paxcounter generates identifiers for sniffed MAC adresses and collects them temp # Sensors and Peripherals -You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensors.cpp*](src/sensors.cpp). Bosch BME280 / BME680 environment sensors are supported. Enable *flag lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value for BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, can be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options. +You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensors.cpp*](src/sensors.cpp). Bosch BME280 / BME680 environment sensors are supported. Enable *flag lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value from BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options. Output of user sensor data can be switched by user remote control command 0x13 sent to Port 2. Output of sensor and peripheral data is internally switched by a bitmask register. Default mask (0xFF) can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme: -bit sensor / data -0 GPS -1 Beacon alarm -2 Environment sensor (BME280/BME680) -3 Paxcounter (Wifi + BLE) -4 User sensor #1 -5 User sensor #2 -6 User sensor #3 -7 Battery voltage -8 reserved +| bit | sensor/data | +|-----|---------------| +| 0 | GPS | +| 1 | Beacon alarm | +| 2 | BME280/680 | +| 3 | Paxcounter | +| 4 | User sensor 1 | +| 5 | User sensor 2 | +| 6 | User sensor 3 | +| 7 | reserved | # Clock controller From 7b9535945210e6250ae8b5d51ce15595e85db63a Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 16 Mar 2019 12:19:47 +0100 Subject: [PATCH 07/11] readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d7e11d0..251cc8da 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Output of user sensor data can be switched by user remote control command 0x13 s Output of sensor and peripheral data is internally switched by a bitmask register. Default mask (0xFF) can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme: -| bit | sensor/data | +| Bit | Sensordata | |-----|---------------| | 0 | GPS | | 1 | Beacon alarm | From af4cf1d09fad3c7ebe0be96db9ce0945c6791d5b Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 16 Mar 2019 21:01:43 +0100 Subject: [PATCH 08/11] timesync improvements --- include/if482.h | 1 - include/timesync.h | 4 +- platformio.ini | 4 +- src/dcf77.cpp | 12 ++- src/if482.cpp | 10 +- src/lorawan.cpp | 12 +-- src/main.cpp | 4 +- src/spislave.cpp | 6 +- src/timekeeper.cpp | 10 +- src/timesync.cpp | 235 ++++++++++++++++++++++++--------------------- 10 files changed, 163 insertions(+), 135 deletions(-) diff --git a/include/if482.h b/include/if482.h index d31f5d59..a787030d 100644 --- a/include/if482.h +++ b/include/if482.h @@ -5,7 +5,6 @@ #include "timekeeper.h" #define IF482_FRAME_SIZE (17) -#define IF482_PULSE_LENGTH (1000) extern HardwareSerial IF482; diff --git a/include/timesync.h b/include/timesync.h index bbba4a7f..e91155cd 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -7,7 +7,7 @@ #include "timekeeper.h" #define TIME_SYNC_SAMPLES 3 // number of time requests for averaging -#define TIME_SYNC_CYCLE 20 // seconds between two time requests +#define TIME_SYNC_CYCLE 2 // seconds between two time requests #define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer #define TIME_SYNC_TRIGGER 100 // time deviation in millisec triggering a sync #define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length @@ -15,6 +15,6 @@ void send_timesync_req(void); int recv_timesync_ans(uint8_t buf[], uint8_t buf_len); void process_timesync_req(void *taskparameter); -void store_time_sync_req(time_t t_millisec); +void store_time_sync_req(uint32_t t_millisec); #endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 74f6787b..e0d424ed 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,10 +30,10 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.7.38 +release_version = 1.7.39 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose -debug_level = 4 +debug_level = 3 ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA upload_protocol = esptool ;upload_protocol = custom diff --git a/src/dcf77.cpp b/src/dcf77.cpp index 7a1e5ab3..d240976d 100644 --- a/src/dcf77.cpp +++ b/src/dcf77.cpp @@ -22,7 +22,9 @@ void DCF77_Pulse(time_t t, uint8_t const *DCFpulse) { TickType_t startTime = xTaskGetTickCount(); uint8_t sec = second(t); - ESP_LOGD (TAG, "DCF second %d", sec); + t = myTZ.toLocal(now()); + ESP_LOGD(TAG, "[%02d:%02d:%02d.%03d] DCF second %d", hour(t), minute(t), + second(t), millisecond(), sec); // induce 10 pulses for (uint8_t pulse = 0; pulse <= 9; pulse++) { @@ -100,8 +102,8 @@ uint8_t *IRAM_ATTR DCF77_Frame(time_t const tt) { } // DCF77_Frame() // helper function to convert decimal to bcd digit -uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t const endpos, - uint8_t *DCFpulse) { +uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, + uint8_t const endpos, uint8_t *DCFpulse) { uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); uint8_t parity = 0; @@ -116,6 +118,8 @@ uint8_t IRAM_ATTR dec2bcd(uint8_t const dec, uint8_t const startpos, uint8_t con } // helper function to encode parity -uint8_t IRAM_ATTR setParityBit(uint8_t const p) { return ((p & 1) ? dcf_1 : dcf_0); } +uint8_t IRAM_ATTR setParityBit(uint8_t const p) { + return ((p & 1) ? dcf_1 : dcf_0); +} #endif // HAS_DCF77 \ No newline at end of file diff --git a/src/if482.cpp b/src/if482.cpp index fe645c18..92151b5d 100644 --- a/src/if482.cpp +++ b/src/if482.cpp @@ -90,15 +90,15 @@ HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS) void IF482_Pulse(time_t t) { static const TickType_t txDelay = - pdMS_TO_TICKS(IF482_PULSE_LENGTH - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482)); + pdMS_TO_TICKS(1000) - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482); vTaskDelay(txDelay); // wait until moment to fire IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second } -String IRAM_ATTR IF482_Frame(time_t startTime) { +String IRAM_ATTR IF482_Frame(time_t printTime) { - time_t t = myTZ.toLocal(startTime); + time_t t = myTZ.toLocal(printTime); char mon, out[IF482_FRAME_SIZE + 1]; switch (timeStatus()) { // indicates if time has been set and recently synced @@ -118,7 +118,9 @@ String IRAM_ATTR IF482_Frame(time_t startTime) { year(t) - 2000, month(t), day(t), weekday(t), hour(t), minute(t), second(t)); - ESP_LOGD(TAG, "IF482 = %s", out); + t = myTZ.toLocal(now()); + ESP_LOGD(TAG, "[%02d:%02d:%02d.%03d] IF482 = %s", hour(t), minute(t), + second(t), millisecond(), out); return out; } diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 3d79cf3b..7b0f51ee 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -232,10 +232,6 @@ void onEvent(ev_t ev) { #if (TIME_SYNC_TIMESERVER) // if last packet sent was a timesync request, store TX timestamp if (LMIC.pendTxPort == TIMEPORT) { - // store_time_sync_req(now(now_micros), now_micros); - // adjust sampled OS time back in time to the nearest second boundary - //const ostime_t tAdjust = LMIC.netDeviceTimeFrac * ms2osticks(1000) / 256; - //store_time_sync_req(osticks2ms(LMIC.txend - tAdjust)); // milliseconds store_time_sync_req(osticks2ms(LMIC.txend)); // milliseconds } #endif @@ -244,7 +240,7 @@ void onEvent(ev_t ev) { : PSTR("TX_COMPLETE")); sprintf(display_line6, " "); // clear previous lmic status - if (LMIC.dataLen) { // did we receive data -> display info + if (LMIC.dataLen) { // did we receive payload data -> display info ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d", LMIC.dataLen, LMIC.rssi, LMIC.snr / 4); sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi, LMIC.snr / 4); @@ -429,12 +425,16 @@ esp_err_t lora_stack_init() { void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { // enqueue message in LORA send queue BaseType_t ret; + MessageBuffer_t DummyBuffer; switch (prio) { case prio_high: + // clear space in queue if full, then fallthrough to normal + if (uxQueueSpacesAvailable == 0) + xQueueReceive(LoraSendQueue, &DummyBuffer, (TickType_t)0); + case prio_normal: ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0); break; case prio_low: - case prio_normal: default: ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0); break; diff --git a/src/main.cpp b/src/main.cpp index 2a7dc259..8934bbb0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,8 +36,7 @@ looptask 1 1 arduino core -> runs the LMIC LoRa stack irqhandler 1 1 executes tasks triggered by timer irq gpsloop 1 2 reads data from GPS via serial or i2c bmeloop 1 1 reads data from BME sensor via i2c -timesync_ans 1 0 temporary task for receiving time sync requests -timesync_req 1 0 temporary task for sending time sync requests +timesync_req 1 4 temporary task for processing time sync requests IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator Low priority numbers denote low priority tasks. @@ -165,7 +164,6 @@ void setup() { ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION), ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION), ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION)); - ESP_LOGI(TAG, "DEVEUI: "); showLoraKeys(); #endif // HAS_LORA diff --git a/src/spislave.cpp b/src/spislave.cpp index cd6455a0..a288d4b0 100644 --- a/src/spislave.cpp +++ b/src/spislave.cpp @@ -150,12 +150,16 @@ esp_err_t spi_init() { void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { // enqueue message in SPI send queue BaseType_t ret; + MessageBuffer_t DummyBuffer; switch (prio) { case prio_high: + // clear space in queue if full, then fallthrough to normal + if (!uxQueueSpacesAvailable(SPISendQueue)) + xQueueReceive(SPISendQueue, &DummyBuffer, (TickType_t)0); + case prio_normal: ret = xQueueSendToFront(SPISendQueue, (void *)message, (TickType_t)0); break; case prio_low: - case prio_normal: default: ret = xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0); break; diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index f27d7343..65f4352f 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -22,7 +22,7 @@ time_t timeProvider(void) { time_t t = 0; -#if(HAS_GPS) +#if (HAS_GPS) t = get_gpstime(); // fetch recent time from last NEMA record if (t) { #ifdef HAS_RTC @@ -219,12 +219,12 @@ void clock_loop(void *taskparameter) { // ClockTask xTaskNotifyWait(0x00, ULONG_MAX, &printtime, portMAX_DELAY); // wait for timepulse - // no confident time -> we suppress clock output - if (timeStatus() == timeNotSet) - continue; - t = time_t(printtime); // UTC time seconds + // no confident time -> suppress clock output + if ((timeStatus() == timeNotSet) || !(timeIsValid(t))) + continue; + #if defined HAS_IF482 IF482_Pulse(t); diff --git a/src/timesync.cpp b/src/timesync.cpp index e1c8e614..b5ae0725 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -27,6 +27,7 @@ typedef std::chrono::system_clock myClock; typedef myClock::time_point myClock_timepoint; typedef std::chrono::duration> myClock_msecTick; +typedef std::chrono::duration myClock_secTick; myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES]; myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES]; @@ -39,14 +40,13 @@ void send_timesync_req() { ESP_LOGI(TAG, "Timeserver sync request already pending"); return; } else { - ESP_LOGI(TAG, "Timeserver sync request started"); + ESP_LOGI(TAG, "[%0.3f] Timeserver sync request started", millis() / 1000.0); lora_time_sync_pending = true; - // clear timestamp array - for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { - time_sync_tx[i] = time_sync_rx[i] = myClock_timepoint(); // set to epoch - } + // initialize timestamp array + for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) + time_sync_tx[i] = time_sync_rx[i] = myClock_timepoint(); // kick off temporary task for timeserver handshake processing if (!timeSyncReqTask) @@ -54,12 +54,132 @@ void send_timesync_req() { "timesync_req", // name of task 2048, // stack size of task (void *)1, // task parameter - 0, // priority of the task + 4, // priority of the task &timeSyncReqTask, // task handle 1); // CPU core } } +// task for sending time sync requests +void process_timesync_req(void *taskparameter) { + + uint32_t seq_no = 0, time_to_set_us; + long long int time_to_set_ms; + uint16_t time_to_set_fraction_msec; + uint8_t k = 0, i = 0; + time_t time_to_set; + auto time_offset = myClock_msecTick::zero(); + + // wait until we are joined + while (!LMIC.devaddr) { + vTaskDelay(pdMS_TO_TICKS(2000)); + } + + // enqueue timestamp samples in lora sendqueue + for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { + + // wrap around seqNo 0 .. 254 + time_sync_seqNo = (time_sync_seqNo >= 255) ? 0 : time_sync_seqNo + 1; + + // send sync request to server + payload.reset(); + payload.addByte(time_sync_seqNo); + SendPayload(TIMEPORT, prio_high); + + // process answer, wait for notification from recv_timesync_ans() + if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, + pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdFALSE) || + (seq_no != time_sync_seqNo)) { + + ESP_LOGW(TAG, "[%0.3f] Timeserver handshake failed", millis() / 1000.0); + goto finish; + } // no valid sequence received before timeout + + else { // calculate time diff from collected timestamps + k = seq_no % TIME_SYNC_SAMPLES; + + auto t_tx = time_point_cast( + time_sync_tx[k]); // timepoint when node TX_completed + auto t_rx = time_point_cast( + time_sync_rx[k]); // timepoint when message was seen on gateway + + time_offset += t_rx - t_tx; // cumulate timepoint diffs + + if (i < TIME_SYNC_SAMPLES - 1) { + // wait until next cycle + vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); + } else { + // send flush to open a receive window for last time_sync_ans + payload.reset(); + payload.addByte(0x99); + SendPayload(RCMDPORT, prio_high); + } + } + } // for + + // calculate time offset from collected diffs + time_offset /= TIME_SYNC_SAMPLES; + ESP_LOGD(TAG, "[%0.3f] avg time diff: %0.3f sec", millis() / 1000.0, + myClock_secTick(time_offset).count()); + + // calculate absolute time with millisecond precision + time_to_set_ms = (long long)now(time_to_set_us) * 1000LL + + time_to_set_us / 1000LL + time_offset.count(); + // convert to seconds + time_to_set = (time_t)(time_to_set_ms / 1000LL); + // calculate fraction milliseconds + time_to_set_fraction_msec = (uint16_t)(time_to_set_ms % 1000LL); + + ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec", + millis() / 1000.0, time_to_set, time_to_set_fraction_msec); + + // adjust system time + if (timeIsValid(time_to_set)) { + + if (abs(time_offset.count()) >= + TIME_SYNC_TRIGGER) { // milliseconds threshold + + // wait until top of second + ESP_LOGD(TAG, "[%0.3f] waiting %d ms", millis() / 1000.0, + 1000 - time_to_set_fraction_msec); + vTaskDelay(pdMS_TO_TICKS(1000 - time_to_set_fraction_msec)); + + // sync timer pps to top of second + if (ppsIRQ) { + timerRestart(ppsIRQ); // reset pps timer + CLOCKIRQ(); // fire clock pps interrupt + } + + setTime(time_to_set + 1); + timeSource = _lora; + + timesyncer.attach(TIME_SYNC_INTERVAL * 60, + timeSync); // set to regular repeat + ESP_LOGI(TAG, "[%0.3f] Timesync finished, time adjusted by %.3f sec", + millis() / 1000.0, myClock_secTick(time_offset).count()); + } else + ESP_LOGI(TAG, "Timesync finished, time not adjusted, is up to date"); + } else + ESP_LOGW(TAG, "Invalid time received from timeserver"); + +finish: + + lora_time_sync_pending = false; + timeSyncReqTask = NULL; + vTaskDelete(NULL); // end task +} + +// called from lorawan.cpp after time_sync_req was sent +void store_time_sync_req(uint32_t t_millisec) { + + uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES; + time_sync_tx[k] += milliseconds(t_millisec); + + ESP_LOGD(TAG, "[%0.3f] Timesync request #%d sent at %d.%03d", + millis() / 1000.0, time_sync_seqNo, t_millisec / 1000, + t_millisec % 1000); +} + // process timeserver timestamp answer, called from lorawan.cpp int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { @@ -80,8 +200,8 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { else return 0; // failure - ESP_LOGD(TAG, "Timesync request #%d rcvd at %d", seq_no, - myClock::to_time_t(time_sync_rx[k])); + ESP_LOGD(TAG, "[%0.3f] Timesync request #%d rcvd at %d.%03d", + millis() / 1000.0, seq_no, timestamp_sec, timestamp_msec); // inform processing task if (timeSyncReqTask) @@ -90,103 +210,4 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { return 1; // success } -// task for sending time sync requests -void process_timesync_req(void *taskparameter) { - - uint8_t k = 0, i = 0; - uint32_t seq_no = 0; - // milliseconds time_offset(0); - auto time_offset = myClock_msecTick::zero(); - int time_offset_msec; - time_t time_to_set; - - // enqueue timestamp samples in lora sendqueue - for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { - - // wrap around seqNo 0 .. 254 - time_sync_seqNo = (time_sync_seqNo >= 255) ? 0 : time_sync_seqNo + 1; - - // send sync request to server - payload.reset(); - payload.addByte(time_sync_seqNo); - SendPayload(TIMEPORT, prio_high); - - // process answer - if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, - pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdFALSE) || - (seq_no != time_sync_seqNo)) { - - ESP_LOGW(TAG, "Timeserver handshake failed"); - goto finish; - } // no valid sequence received before timeout - - else { // calculate time diff from collected timestamps - k = seq_no % TIME_SYNC_SAMPLES; - - auto t_tx = time_point_cast( - time_sync_tx[k]); // timepoint when node TX_completed - auto t_rx = time_point_cast( - time_sync_rx[k]); // timepoint when message was seen on gateway - - time_offset += t_rx - t_tx; // cumulate timepoint diffs - - ESP_LOGD(TAG, "time_offset: %lldms", time_offset.count()); - - if (i < TIME_SYNC_SAMPLES - 1) // wait until next cycle - vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); - } - } // for - - // calculate time offset from collected diffs and set time if necessary - ESP_LOGD(TAG, "Avg time diff: %lldms", time_offset.count()); - time_offset /= TIME_SYNC_SAMPLES; - // 1sec wait for top of second - time_to_set = now() + time_offset.count() / 1000 + 1; - ESP_LOGD(TAG, "Calculated UTC epoch time: %d", time_to_set); - - // adjust system time - if (timeIsValid(time_to_set)) { - - if (abs(time_offset.count()) >= - TIME_SYNC_TRIGGER) { // milliseconds threshold - - // wait until top of second - time_offset_msec = abs(time_offset.count()) % 1000; - ESP_LOGD(TAG, "waiting %dms", 1000 - time_offset_msec); - vTaskDelay(pdMS_TO_TICKS(1000 - time_offset_msec)); - - // sync timer pps to top of second - if (ppsIRQ) { - timerRestart(ppsIRQ); // reset pps timer - CLOCKIRQ(); // fire clock pps interrupt - } - - setTime(time_to_set); - timeSource = _lora; - timesyncer.attach(TIME_SYNC_INTERVAL * 60, - timeSync); // set to regular repeat - ESP_LOGI(TAG, "Timesync finished, time adjusted by %lld ms", - time_offset.count()); - } else - ESP_LOGI(TAG, "Timesync finished, time not adjusted, is up to date"); - } else - ESP_LOGW(TAG, "Invalid time received from timeserver"); - -finish: - - lora_time_sync_pending = false; - timeSyncReqTask = NULL; - vTaskDelete(NULL); // end task -} - -// called from lorawan.cpp after time_sync_req was sent -void store_time_sync_req(time_t t_millisec) { - - uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES; - time_sync_tx[k] += milliseconds(t_millisec); - - ESP_LOGD(TAG, "Timesync request #%d sent at %d", time_sync_seqNo, - myClock::to_time_t(time_sync_tx[k])); -} - #endif \ No newline at end of file From 149c7f67320fc63ac86866adeb30ed4e6a5fa3d3 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 16 Mar 2019 22:16:55 +0100 Subject: [PATCH 09/11] timesync.cpp fix shift overflow --- src/timesync.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index b5ae0725..0d5f2c22 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -191,9 +191,15 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { uint16_t timestamp_msec = 4 * buf[5]; // convert 1/250th sec fractions to ms uint32_t timestamp_sec = 0, tmp_sec = 0; - for (uint8_t i = 1; i <= 4; i++) { - timestamp_sec = (tmp_sec <<= 8) |= buf[i]; - } + // get the timeserver time. + // The first 4 bytes contain the UTC seconds since unix epoch. + // Octet order is little endian. Casts are necessary, because buf is an array + // of single byte values, and they might overflow when shifted + timestamp_sec = ((uint32_t)buf[1]) | (((uint32_t)buf[2]) << 8) | + (((uint32_t)buf[3]) << 16) | (((uint32_t)buf[4]) << 24); + + // The 5th byte contains the fractional seconds in 2^-8 second steps + timestamp_msec = 4 * buf[5]; if (timestamp_sec + timestamp_msec) // field validation: timestamp not 0 ? time_sync_rx[k] += seconds(timestamp_sec) + milliseconds(timestamp_msec); From 04bc0e1f301ea68dfd26b322beae6666122836a6 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sun, 17 Mar 2019 14:46:59 +0100 Subject: [PATCH 10/11] Nodered Timeserver improved --- src/TTN/Nodered-Timeserver.json | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/TTN/Nodered-Timeserver.json b/src/TTN/Nodered-Timeserver.json index 7520e527..4f6d30a3 100644 --- a/src/TTN/Nodered-Timeserver.json +++ b/src/TTN/Nodered-Timeserver.json @@ -138,7 +138,8 @@ "y": 360, "wires": [ [ - "72d5e7ee.d1eba8" + "72d5e7ee.d1eba8", + "f8749724.1ff9f8" ] ] }, @@ -203,19 +204,19 @@ "y": 200, "wires": [ [ - "595229b0.5df3d8" + "831ab883.d6a238" ] ] }, { - "id": "595229b0.5df3d8", + "id": "831ab883.d6a238", "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 timestamp = (+new Date(msg.payload.metadata.gateways[0].time));\n let timestamp_server = (+new Date(msg.payload.metadata.time));\n \n if ((timestamp < timestamp_server) && (timestamp > 0)) {\n \n var seconds = Math.floor(timestamp/1000);\n var fractions = (timestamp % 1000) / 4;\n var seqno = msg.payload.payload_raw[0];\n\n let buf = new ArrayBuffer(6);\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;\n}\n\nelse\n\nreturn null;", + "name": "Generate Time Answer", + "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\nvar gateways = msg.payload.metadata.gateways;\nvar gateway_time = gateways.map(gw => new Date(gw.time));\nvar server_time = new Date(msg.payload.metadata.time);\n\ngateway_time.sort();\n\nvar gw_timestamps = gateway_time.filter(function (element) {\n return element > 0;\n});\n\nvar timestamp = gw_timestamps[0];\n\nif (timestamp < server_time) {\n\n var seconds = Math.floor(timestamp/1000);\n var fractions = (timestamp % 1000) / 4;\n var seqno = msg.payload.payload_raw[0];\n\n let buf = new ArrayBuffer(6);\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;\n\n}\n\nelse\n\nreturn null;", "outputs": 1, "noerr": 0, - "x": 380, + "x": 420, "y": 280, "wires": [ [ @@ -223,6 +224,20 @@ ] ] }, + { + "id": "f8749724.1ff9f8", + "type": "debug", + "z": "449c1517.e25f4c", + "name": "time_sync_ans", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": true, + "complete": "payload", + "x": 720, + "y": 280, + "wires": [] + }, { "id": "2a15ab6f.ab2244", "type": "mqtt-broker", From c2b28a54cbfa8b8bad185d4ac8f730b85678e89d Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sun, 17 Mar 2019 15:04:11 +0100 Subject: [PATCH 11/11] timesync fixes --- include/timesync.h | 2 +- src/timesync.cpp | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index e91155cd..ddc7fff6 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -6,7 +6,7 @@ #include "timesync.h" #include "timekeeper.h" -#define TIME_SYNC_SAMPLES 3 // number of time requests for averaging +#define TIME_SYNC_SAMPLES 2 // number of time requests for averaging #define TIME_SYNC_CYCLE 2 // seconds between two time requests #define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer #define TIME_SYNC_TRIGGER 100 // time deviation in millisec triggering a sync diff --git a/src/timesync.cpp b/src/timesync.cpp index 0d5f2c22..5c3f2753 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -20,8 +20,8 @@ static const char TAG[] = __FILE__; TaskHandle_t timeSyncReqTask; -static uint8_t time_sync_seqNo = 0; -static bool lora_time_sync_pending = false; +static uint8_t time_sync_seqNo{}; +static bool lora_time_sync_pending{false}; typedef std::chrono::system_clock myClock; typedef myClock::time_point myClock_timepoint; @@ -45,7 +45,7 @@ void send_timesync_req() { lora_time_sync_pending = true; // initialize timestamp array - for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) + for (uint8_t i{}; i < TIME_SYNC_SAMPLES; i++) time_sync_tx[i] = time_sync_rx[i] = myClock_timepoint(); // kick off temporary task for timeserver handshake processing @@ -63,12 +63,12 @@ void send_timesync_req() { // task for sending time sync requests void process_timesync_req(void *taskparameter) { - uint32_t seq_no = 0, time_to_set_us; - long long int time_to_set_ms; + uint8_t k{}; uint16_t time_to_set_fraction_msec; - uint8_t k = 0, i = 0; + uint32_t seq_no{}, time_to_set_us; + long long int time_to_set_ms; time_t time_to_set; - auto time_offset = myClock_msecTick::zero(); + auto time_offset{myClock_msecTick::zero()}; // wait until we are joined while (!LMIC.devaddr) { @@ -76,7 +76,7 @@ void process_timesync_req(void *taskparameter) { } // enqueue timestamp samples in lora sendqueue - for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { + for (uint8_t i{}; i < TIME_SYNC_SAMPLES; i++) { // wrap around seqNo 0 .. 254 time_sync_seqNo = (time_sync_seqNo >= 255) ? 0 : time_sync_seqNo + 1; @@ -172,7 +172,8 @@ finish: // called from lorawan.cpp after time_sync_req was sent void store_time_sync_req(uint32_t t_millisec) { - uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES; + uint8_t k{time_sync_seqNo % TIME_SYNC_SAMPLES}; + time_sync_tx[k] += milliseconds(t_millisec); ESP_LOGD(TAG, "[%0.3f] Timesync request #%d sent at %d.%03d", @@ -187,9 +188,9 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { if ((!lora_time_sync_pending) || (buf_len != TIME_SYNC_FRAME_LENGTH)) return 0; // failure - uint8_t seq_no = buf[0], k = seq_no % TIME_SYNC_SAMPLES; - uint16_t timestamp_msec = 4 * buf[5]; // convert 1/250th sec fractions to ms - uint32_t timestamp_sec = 0, tmp_sec = 0; + uint8_t seq_no{buf[0]}, k{seq_no % TIME_SYNC_SAMPLES}; + uint16_t timestamp_msec; // convert 1/250th sec fractions to ms + uint32_t timestamp_sec; // get the timeserver time. // The first 4 bytes contain the UTC seconds since unix epoch. @@ -201,7 +202,7 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { // The 5th byte contains the fractional seconds in 2^-8 second steps timestamp_msec = 4 * buf[5]; - if (timestamp_sec + timestamp_msec) // field validation: timestamp not 0 ? + if ((timestamp_sec) || (timestamp_msec)) // field validation: time not 0 ? time_sync_rx[k] += seconds(timestamp_sec) + milliseconds(timestamp_msec); else return 0; // failure