From 85f629b6626787fda9eec24c3a4acd7f86cb374f Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Fri, 8 Mar 2019 16:10:22 +0100 Subject: [PATCH 01/25] servertimesync (experimental) --- .gitignore | 4 +- include/lorawan.h | 4 +- include/rcommand.h | 4 +- include/servertimesync.h | 27 ++++++++ platformio.ini | 2 +- src/lorawan.cpp | 2 +- src/paxcounter.conf | 4 +- src/rcommand.cpp | 6 +- src/servertimesync.cpp | 133 +++++++++++++++++++++++++++++++++++++++ src/timekeeper.cpp | 4 +- 10 files changed, 174 insertions(+), 16 deletions(-) create mode 100644 include/servertimesync.h create mode 100644 src/servertimesync.cpp diff --git a/.gitignore b/.gitignore index aa33e14f..875ecd9f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,4 @@ .clang_complete .gcc-flags.json src/loraconf.h -src/ota.conf -src/DBtimesync.cpp -include/DBtimesync.h \ No newline at end of file +src/ota.conf \ No newline at end of file diff --git a/include/lorawan.h b/include/lorawan.h index 58aa362c..669ab7d1 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -4,8 +4,8 @@ #include "globals.h" #include "rcommand.h" #include "timekeeper.h" -#if(DBTIMESYNC) -#include "DBtimesync.h" +#if(ServertimeSYNC) +#include "Servertimesync.h" #endif // LMIC-Arduino LoRaWAN Stack diff --git a/include/rcommand.h b/include/rcommand.h index 9eb06384..62965d2f 100644 --- a/include/rcommand.h +++ b/include/rcommand.h @@ -9,8 +9,8 @@ #include #include "cyclic.h" #include "timekeeper.h" -#if(DBTIMESYNC) -#include "DBtimesync.h" +#if(ServertimeSYNC) +#include "Servertimesync.h" #endif // table of remote commands and assigned functions diff --git a/include/servertimesync.h b/include/servertimesync.h new file mode 100644 index 00000000..ef441d84 --- /dev/null +++ b/include/servertimesync.h @@ -0,0 +1,27 @@ +#ifndef _ServertimeSYNC_H +#define _ServertimeSYNC_H + +#include "globals.h" +#include "Servertimesync.h" +#include "timekeeper.h" + +#define SYNC_SAMPLES 3 +//#define SYNC_CYCLE 600 // seconds between two time sync requests +#define SYNC_CYCLE 20 // seconds between two time sync requests +#define SYNC_TIMEOUT \ + (SYNC_SAMPLES * (SYNC_CYCLE + 60)) // timeout waiting for time sync answer +#define SYNC_THRESHOLD 0.01f // time deviation threshold triggering time sync +#define TIME_SYNC_OPCODE 0x90 +#define TIME_REQ_OPCODE 0x92 +#define TIME_ANS_OPCODE 0x93 + +extern uint32_t time_sync_messages[], time_sync_answers[]; +extern uint8_t time_sync_seqNo; + +void send_Servertime_req(void); +void recv_Servertime_ans(uint8_t val[]); +void process_Servertime_sync_req(void *taskparameter); +void process_Servertime_sync_ans(void *taskparameter); +void force_Servertime_sync(uint8_t val[]); + +#endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 47ca6e73..6c6fb377 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,7 +30,7 @@ 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.36 +release_version = 1.7.361 ; 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 = 3 diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 04fb0fb6..af86cc6e 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -225,7 +225,7 @@ void onEvent(ev_t ev) { case EV_TXCOMPLETE: -#if(DBTIMESYNC) +#if(ServertimeSYNC) if (!(LMIC.txrxFlags & TXRX_ACK) && time_sync_seqNo) time_sync_messages[time_sync_seqNo - 1] = LMIC.txend; #endif diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 9f275469..27c2846b 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -67,8 +67,8 @@ // settings for syncing time of node with external time source #define TIME_SYNC_INTERVAL 2 // sync time attempt each .. minutes from time source (GPS/LORA/RTC) [default = 60], comment out means off -#define TIME_SYNC_LORA 0 // set to 1 to use LORA network as time source, comment out means off [default = off] -#define DBTIMESYNC 0 // set to 1 to use DB LORA timeserver with patented sync algorithm [default = off] +#define TIME_SYNC_LORA 0 // set to 1 to use LORA network as time source, 0 means off [default = 0] +#define ServertimeSYNC 1 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0] // time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino #define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 34054bc2..c4658345 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -306,10 +306,10 @@ cmd_t table[] = {{0x01, set_rssi, 1, true}, {0x84, get_gps, 0, false}, {0x85, get_bme, 0, false}, {0x86, get_time, 0, false} -#if(DBTIMESYNC) +#if(ServertimeSYNC) , - {TIME_ANS_OPCODE, recv_DBtime_ans, 0, false}, - {TIME_SYNC_OPCODE, force_DBtime_sync, 0, false} + {TIME_ANS_OPCODE, recv_Servertime_ans, 5, false}, + {TIME_SYNC_OPCODE, force_Servertime_sync, 0, false} #endif }; diff --git a/src/servertimesync.cpp b/src/servertimesync.cpp new file mode 100644 index 00000000..2c6ea8a3 --- /dev/null +++ b/src/servertimesync.cpp @@ -0,0 +1,133 @@ +#ifdef ServertimeSYNC + +#include "Servertimesync.h" + +// Local logging tag +static const char TAG[] = __FILE__; + +TaskHandle_t timeSyncReqTask, timeSyncAnsTask; + +uint32_t time_sync_messages[SYNC_SAMPLES] = {0}, + time_sync_answers[SYNC_SAMPLES] = {0}; + +uint8_t time_sync_seqNo = 0; + +// send time request message +void send_Servertime_req() { + + // if a running timesync handshake is pending then exit + if (time_sync_seqNo) + return; + + // initalize sample arrays + for (uint8_t i = 0; i < SYNC_SAMPLES; i++) + time_sync_messages[i] = time_sync_answers[i] = 0; + + // create temporary task sending sync requests + if (timeSyncReqTask != NULL) + xTaskCreatePinnedToCore(process_Servertime_sync_req, // task function + "timesync_req", // name of task + 2048, // stack size of task + (void *)1, // task parameter + 0, // priority of the task + &timeSyncReqTask, // task handle + 1); // CPU core + + // create temporary task for processing sync answers if not already active + if (timeSyncAnsTask != NULL) + xTaskCreatePinnedToCore(process_Servertime_sync_ans, // task function + "timesync_ans", // name of task + 2048, // stack size of task + (void *)1, // task parameter + 0, // priority of the task + &timeSyncAnsTask, // task handle + 1); // CPU core +} + +// handle time sync response, called from rcommanc.cpp +void recv_Servertime_ans(uint8_t val[]) { + + uint8_t seq_no = val[0]; + uint32_t timestamp = 0; + + time_sync_seqNo--; + + for (int i = 1; i <= 4; ++i) + timestamp = (timestamp << 8) | val[i]; + time_sync_answers[seq_no - 1] = timestamp; + + ESP_LOGI(TAG, "Timeserver timestamp received, sequence #%d: %d", seq_no, + timestamp); + + // inform processing task + if (timeSyncAnsTask) + xTaskNotify(timeSyncAnsTask, seq_no, eSetBits); +} + +void force_Servertime_sync(uint8_t val[]) { + ESP_LOGI(TAG, "Timesync forced by timeserver"); + timeSync(); +}; + +// task for sending time sync requests +void process_Servertime_sync_req(void *taskparameter) { + + TickType_t startTime = xTaskGetTickCount(); + + // enqueue timestamp samples in lora sendqueue + for (uint8_t i = 0; i < SYNC_SAMPLES; i++) { + payload.reset(); + payload.addAlarm(TIME_REQ_OPCODE, time_sync_seqNo); + SendPayload(TIMEPORT, prio_high); + time_sync_seqNo++; + // Wait for the next cycle + vTaskDelayUntil(&startTime, pdMS_TO_TICKS(SYNC_CYCLE * 1000)); + } + vTaskDelete(NULL); // end task +} + +// task for processing a timesync handshake +void process_Servertime_sync_ans(void *taskparameter) { + + uint32_t seq_no = 0; + uint32_t NetworkTime = 0; + int32_t time_diff = 0; + + // collect incoming timestamp samples notified by rcommand + for (uint8_t i = 0; i < SYNC_SAMPLES; i++) { + if (xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, + SYNC_TIMEOUT * 1000 / portTICK_PERIOD_MS) == pdTRUE) + time_sync_seqNo--; + } + + if (time_sync_seqNo) { + ESP_LOGW(TAG, "Timesync handshake failed"); + time_sync_seqNo = 0; + } + + else { + + // calculate time diff from set of collected timestamps + for (uint8_t i = 0; i < SYNC_SAMPLES; i++) + time_diff += time_sync_messages[i] - time_sync_answers[i]; + + if ((time_diff / SYNC_SAMPLES * 1.0f) > SYNC_THRESHOLD) { + NetworkTime = now() + time_diff; + ESP_LOGD(TAG, "Timesync handshake completed, time offset = %d", + time_diff); + } else + ESP_LOGD(TAG, "Timesync handshake completed, time is up to date"); + + // Update system time with time read from the network + if (timeIsValid(NetworkTime)) { + setTime(NetworkTime); + timeSource = _lora; + timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // regular repeat + ESP_LOGI(TAG, "Recent time received from timeserver"); + } else + ESP_LOGW(TAG, "Invalid time received from timeserver"); + } + vTaskDelete(NULL); // end task +} + +#endif \ No newline at end of file diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 21259b21..191e2c56 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -36,8 +36,8 @@ time_t timeProvider(void) { #endif // kick off asychronous DB timesync if we have -#if(DBTIMESYNC) - send_DBtime_req(); +#if(ServertimeSYNC) + send_Servertime_req(); // kick off asychronous lora sync if we have #elif defined HAS_LORA && (TIME_SYNC_LORA) LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); From 8980a3d8ffc6127b00280945bdcb27e64fb8b3f9 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Fri, 8 Mar 2019 16:51:37 +0100 Subject: [PATCH 02/25] servertimesync.cpp: license note --- src/servertimesync.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/servertimesync.cpp b/src/servertimesync.cpp index 2c6ea8a3..c2537025 100644 --- a/src/servertimesync.cpp +++ b/src/servertimesync.cpp @@ -1,3 +1,12 @@ +/* +/////////////////////// LICENSE NOTE for servertimesync.cpp ///////////////////////////////// +PLEASE NOTE: There is a patent filed for the time sync algorithm used in the following code. +The shown implementation example is covered by the repository's licencse, but you may not be +eligible to deploy the algorith in applications without granted license by the patent holder. +///////////////////////////////////////////////////////////////////////////////////////////// +*/ + + #ifdef ServertimeSYNC #include "Servertimesync.h" From 91cc9dba0c0600ad6050343a22f3f45b43d95c83 Mon Sep 17 00:00:00 2001 From: Gregor Wolf Date: Fri, 8 Mar 2019 18:13:51 +0100 Subject: [PATCH 03/25] Support for BME280 --- include/bme280.h | 17 ++++++++++++ include/cyclic.h | 6 ++++- include/globals.h | 6 ++++- platformio.ini | 6 ++--- src/bme280.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++ src/bme680mems.cpp | 10 +++---- src/cyclic.cpp | 14 ++++++++-- src/hal/generic.h | 5 ++-- src/hal/heltec.h | 5 ++-- src/hal/heltecv2.h | 5 ++-- src/hal/octopus32.h | 5 ++-- src/hal/ttgobeam.h | 5 ++-- src/hal/ttgov21new.h | 6 +++++ src/main.cpp | 23 +++++++++++++--- src/rcommand.cpp | 2 +- 15 files changed, 151 insertions(+), 27 deletions(-) create mode 100644 include/bme280.h create mode 100644 src/bme280.cpp diff --git a/include/bme280.h b/include/bme280.h new file mode 100644 index 00000000..c4c5c4a8 --- /dev/null +++ b/include/bme280.h @@ -0,0 +1,17 @@ +#ifndef _BME280_H +#define _BME280_H + +#include "globals.h" +#include +#include +#include +#include "../lib/Bosch-BSEC/src/bsec.h" + +extern bmeStatus_t + bme_status; // Make struct for storing gps data globally available +extern TaskHandle_t Bme280Task; + +int bme280_init(); +void bme280_loop(void *pvParameters); + +#endif \ No newline at end of file diff --git a/include/cyclic.h b/include/cyclic.h index ffdee8f5..77853bb6 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -7,10 +7,14 @@ #include "spislave.h" #include -#ifdef HAS_BME +#ifdef HAS_BME680 #include "bme680mems.h" #endif +#ifdef HAS_BME280 +#include "bme280.h" +#endif + extern Ticker housekeeper; void housekeeping(void); diff --git a/include/globals.h b/include/globals.h index 24c83580..724c4ef5 100644 --- a/include/globals.h +++ b/include/globals.h @@ -151,8 +151,12 @@ extern time_t userUTCTime; #include "sensor.h" #endif -#ifdef HAS_BME +#ifdef HAS_BME680 #include "bme680mems.h" #endif +#ifdef HAS_BME280 +#include "bme280.h" +#endif + #endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 47ca6e73..79765a6c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -env_default = generic +;env_default = generic ;env_default = ebox ;env_default = eboxtube ;env_default = heltec @@ -14,7 +14,7 @@ env_default = generic ;env_default = ttgov1 ;env_default = ttgov2 ;env_default = ttgov21old -;env_default = ttgov21new +env_default = ttgov21new ;env_default = ttgobeam ;env_default = ttgofox ;env_default = lopy @@ -202,7 +202,7 @@ lib_deps = ${common.lib_deps_lora} ${common.lib_deps_display} build_flags = - ${common.build_flags_basic} + ${common.build_flags_all} upload_protocol = ${common.upload_protocol} extra_scripts = ${common.extra_scripts} monitor_speed = ${common.monitor_speed} diff --git a/src/bme280.cpp b/src/bme280.cpp new file mode 100644 index 00000000..d2af49e3 --- /dev/null +++ b/src/bme280.cpp @@ -0,0 +1,63 @@ +#ifdef HAS_BME280 + +#include "bme280.h" + +// Local logging tag +static const char TAG[] = __FILE__; + +bmeStatus_t bme_status; +TaskHandle_t Bme280Task; + +#define SEALEVELPRESSURE_HPA (1013.25) + +Adafruit_BME280 bme; // I2C +//Adafruit_BME280 bme(BME_CS); // hardware SPI +//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI + +// initialize BME280 sensor +int bme280_init(void) { + + bool status; + // return = 0 -> error / return = 1 -> success + + // block i2c bus access + if (I2C_MUTEX_LOCK()) { + status = bme.begin(BME280_ADDR); + if (!status) { + ESP_LOGE(TAG, "BME280 sensor not found"); + goto error; + } + ESP_LOGI(TAG, "BME280 sensor found and initialized"); + } else { + ESP_LOGE(TAG, "I2c bus busy - BME280 initialization error"); + goto error; + } + + I2C_MUTEX_UNLOCK(); // release i2c bus access + return 1; + +error: + I2C_MUTEX_UNLOCK(); // release i2c bus access + return 0; + +} // bme_init() + +// loop function which reads and processes data based on sensor settings +void bme280_loop(void *pvParameters) { +#ifdef HAS_BME280 + while (1) { + if (I2C_MUTEX_LOCK()) { + bme_status.temperature = bme.readTemperature(); + bme_status.pressure = (bme.readPressure() / 100.0); // conversion Pa -> hPa + // bme.readAltitude(SEALEVELPRESSURE_HPA); + bme_status.humidity = bme.readHumidity(); + I2C_MUTEX_UNLOCK(); + } + } +#endif + ESP_LOGE(TAG, "BME task ended"); + vTaskDelete(Bme280Task); // should never be reached + +} // bme_loop() + +#endif // HAS_BME280 \ No newline at end of file diff --git a/src/bme680mems.cpp b/src/bme680mems.cpp index d0679507..8f28c389 100644 --- a/src/bme680mems.cpp +++ b/src/bme680mems.cpp @@ -1,4 +1,4 @@ -#ifdef HAS_BME +#ifdef HAS_BME680 #include "bme680mems.h" @@ -34,8 +34,8 @@ int bme_init(void) { // block i2c bus access if (I2C_MUTEX_LOCK()) { - Wire.begin(HAS_BME); - iaqSensor.begin(BME_ADDR, Wire); + Wire.begin(HAS_BME680); + iaqSensor.begin(BME680_ADDR, Wire); ESP_LOGI(TAG, "BSEC v%d.%d.%d.%d", iaqSensor.version.major, iaqSensor.version.minor, iaqSensor.version.major_bugfix, @@ -103,7 +103,7 @@ void bme_loop(void *pvParameters) { configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check -#ifdef HAS_BME +#ifdef HAS_BME680 while (1) { // block i2c bus access if (I2C_MUTEX_LOCK()) { @@ -168,4 +168,4 @@ void updateState(void) { } } -#endif // HAS_BME \ No newline at end of file +#endif // HAS_BME680 \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 8899d05b..63234d84 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -36,10 +36,14 @@ void doHousekeeping() { ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask)); #endif -#ifdef HAS_BME +#ifdef HAS_BME680 ESP_LOGD(TAG, "Bmeloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask)); #endif +#ifdef HAS_BME280 + ESP_LOGD(TAG, "Bme280loop %d bytes left | Taskstate = %d", + uxTaskGetStackHighWaterMark(Bme280Task), eTaskGetState(Bme280Task)); +#endif #ifdef HAS_DCF77 ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask)); @@ -58,11 +62,17 @@ void doHousekeeping() { #endif // display BME sensor data -#ifdef HAS_BME +#ifdef HAS_BME680 ESP_LOGI(TAG, "BME680 Temp: %.2f°C | IAQ: %.2f | IAQacc: %d", bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy); #endif +// display BME280 sensor data +#ifdef HAS_BME280 + ESP_LOGI(TAG, "BME680 Temp: %.2f°C | Humidity: %.2f | Pressure: %.0f", + bme_status.temperature, bme_status.humidity, bme_status.pressure); +#endif + // check free heap memory if (ESP.getMinFreeHeap() <= MEM_LOW) { ESP_LOGI(TAG, diff --git a/src/hal/generic.h b/src/hal/generic.h index 0ca56b04..07a629d3 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -18,8 +18,9 @@ // enable only if device has these sensors, otherwise comment these lines // BME680 sensor on I2C bus -#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL -#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND +#define HAS_BME 1 // Enable BME sensors in general +#define HAS_BME680 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL +#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND // user defined sensors //#define HAS_SENSORS 1 // comment out if device has user defined sensors diff --git a/src/hal/heltec.h b/src/hal/heltec.h index f8ab5b8e..4c3f3051 100644 --- a/src/hal/heltec.h +++ b/src/hal/heltec.h @@ -7,8 +7,9 @@ // Hardware related definitions for Heltec V2 LoRa-32 Board -//#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL -//#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND +//#define HAS_BME 1 // Enable BME sensors in general +//#define HAS_BME680 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL +//#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define CFG_sx1276_radio 1 diff --git a/src/hal/heltecv2.h b/src/hal/heltecv2.h index 7cb9145c..596b571f 100644 --- a/src/hal/heltecv2.h +++ b/src/hal/heltecv2.h @@ -7,8 +7,9 @@ // Hardware related definitions for Heltec V2 LoRa-32 Board -//#define HAS_BME GPIO_NUM_4, GPIO_NUM_15 // SDA, SCL -//#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND +//#define HAS_BME 1 // Enable BME sensors in general +//#define HAS_BME680 GPIO_NUM_4, GPIO_NUM_15 // SDA, SCL +//#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define CFG_sx1276_radio 1 diff --git a/src/hal/octopus32.h b/src/hal/octopus32.h index 288c286b..ad4fbb07 100644 --- a/src/hal/octopus32.h +++ b/src/hal/octopus32.h @@ -12,8 +12,9 @@ #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature // Octopus32 has a pre-populated BME680 on i2c addr 0x76 -#define HAS_BME GPIO_NUM_23, GPIO_NUM_22 // SDA, SCL -#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND +#define HAS_BME 1 // Enable BME sensors in general +#define HAS_BME680 GPIO_NUM_23, GPIO_NUM_22 // SDA, SCL +#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND // user defined sensors //#define HAS_SENSORS 1 // comment out if device has user defined sensors diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index d86acd18..dcce95ad 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -31,8 +31,9 @@ // enable only if device has these sensors, otherwise comment these lines // BME680 sensor on I2C bus -#define HAS_BME SDA, SCL -#define BME_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !! +#define HAS_BME 1 // Enable BME sensors in general +#define HAS_BME680 SDA, SCL +#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !! // display (if connected) #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C diff --git a/src/hal/ttgov21new.h b/src/hal/ttgov21new.h index f2cf0571..b327b8d3 100644 --- a/src/hal/ttgov21new.h +++ b/src/hal/ttgov21new.h @@ -13,6 +13,12 @@ #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define CFG_sx1276_radio 1 // HPD13A LoRa SoC +// enable only if device has these sensors, otherwise comment these lines +// BME680 sensor on I2C bus +#define HAS_BME 1 // Enable BME sensors in general +#define HAS_BME280 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL +#define BME280_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND + #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C #define HAS_LED (25) // green on board LED #define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 diff --git a/src/main.cpp b/src/main.cpp index 0b44c850..aa56ad64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -358,11 +358,11 @@ void setup() { &irqHandlerTask, // task handle 1); // CPU core -// initialize bme -#ifdef HAS_BME - strcat_P(features, " BME"); +// initialize bme680 +#ifdef HAS_BME680 + strcat_P(features, " BME280"); if (bme_init()) { - ESP_LOGI(TAG, "Starting BME sensor..."); + ESP_LOGI(TAG, "Starting BME280 sensor..."); xTaskCreatePinnedToCore(bme_loop, // task function "bmeloop", // name of task 2048, // stack size of task @@ -373,6 +373,21 @@ void setup() { } #endif +// initialize BME280 +#ifdef HAS_BME280 + strcat_P(features, " BME280"); + if (bme280_init()) { + ESP_LOGI(TAG, "Starting BME280 sensor..."); + xTaskCreatePinnedToCore(bme280_loop, // task function + "bme280loop", // name of task + 2048, // stack size of task + (void *)1, // parameter of the task + 1, // priority of the task + &Bme280Task, // task handle + 1); // CPU core + } +#endif + // starting timers and interrupts assert(irqHandlerTask != NULL); // has interrupt handler task started? ESP_LOGI(TAG, "Starting Timers..."); diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 34054bc2..dae6332f 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -262,7 +262,7 @@ void get_gps(uint8_t val[]) { void get_bme(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get bme680 sensor data"); -#ifdef HAS_BME +#ifdef HAS_BME680 payload.reset(); payload.addBME(bme_status); SendPayload(BMEPORT, prio_high); From 1051d841b692aa6a59f855890a851cd29592ad85 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 9 Mar 2019 00:53:11 +0100 Subject: [PATCH 04/25] timeserver (experimental) --- README.md | 2 +- include/lorawan.h | 4 +- include/payload.h | 2 +- include/rcommand.h | 4 +- include/servertimesync.h | 27 ------- include/timesync.h | 25 +++++++ src/irqhandler.cpp | 2 +- src/lorawan.cpp | 25 ++++--- src/macsniff.cpp | 2 +- src/main.cpp | 4 +- src/paxcounter.conf | 6 +- src/payload.cpp | 6 +- src/rcommand.cpp | 6 +- src/servertimesync.cpp | 142 ------------------------------------ src/timekeeper.cpp | 4 +- src/timesync.cpp | 150 +++++++++++++++++++++++++++++++++++++++ 16 files changed, 211 insertions(+), 200 deletions(-) delete mode 100644 include/servertimesync.h create mode 100644 include/timesync.h delete mode 100644 src/servertimesync.cpp create mode 100644 src/timesync.cpp diff --git a/README.md b/README.md index aac9c1a1..af46511d 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ If your device has a fixed DEVEUI enter this in your local loraconf.h file. Duri If your device has silicon **Unique ID** which is stored in serial EEPROM Microchip 24AA02E64 you don't need to change anything. The Unique ID will be read during startup and DEVEUI will be generated from it, overriding settings in loraconf.h. -If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORA* in paxcounter.conf. +If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in paxcounter.conf. # Building diff --git a/include/lorawan.h b/include/lorawan.h index 669ab7d1..3c207c02 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -4,8 +4,8 @@ #include "globals.h" #include "rcommand.h" #include "timekeeper.h" -#if(ServertimeSYNC) -#include "Servertimesync.h" +#if(TIME_SYNC_TIMESERVER) +#include "timesync.h" #endif // LMIC-Arduino LoRaWAN Stack diff --git a/include/payload.h b/include/payload.h index 159dcfc4..b93347d9 100644 --- a/include/payload.h +++ b/include/payload.h @@ -44,7 +44,7 @@ public: void addConfig(configData_t value); void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem, uint8_t reset1, uint8_t reset2); - void addAlarm(int8_t rssi, uint8_t message); + void add2Bytes(int8_t rssi, uint8_t message); void addVoltage(uint16_t value); void addGPS(gpsStatus_t value); void addBME(bmeStatus_t value); diff --git a/include/rcommand.h b/include/rcommand.h index 62965d2f..cbca4a35 100644 --- a/include/rcommand.h +++ b/include/rcommand.h @@ -9,8 +9,8 @@ #include #include "cyclic.h" #include "timekeeper.h" -#if(ServertimeSYNC) -#include "Servertimesync.h" +#if(TIME_SYNC_TIMESERVER) +#include "timesync.h" #endif // table of remote commands and assigned functions diff --git a/include/servertimesync.h b/include/servertimesync.h deleted file mode 100644 index ef441d84..00000000 --- a/include/servertimesync.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _ServertimeSYNC_H -#define _ServertimeSYNC_H - -#include "globals.h" -#include "Servertimesync.h" -#include "timekeeper.h" - -#define SYNC_SAMPLES 3 -//#define SYNC_CYCLE 600 // seconds between two time sync requests -#define SYNC_CYCLE 20 // seconds between two time sync requests -#define SYNC_TIMEOUT \ - (SYNC_SAMPLES * (SYNC_CYCLE + 60)) // timeout waiting for time sync answer -#define SYNC_THRESHOLD 0.01f // time deviation threshold triggering time sync -#define TIME_SYNC_OPCODE 0x90 -#define TIME_REQ_OPCODE 0x92 -#define TIME_ANS_OPCODE 0x93 - -extern uint32_t time_sync_messages[], time_sync_answers[]; -extern uint8_t time_sync_seqNo; - -void send_Servertime_req(void); -void recv_Servertime_ans(uint8_t val[]); -void process_Servertime_sync_req(void *taskparameter); -void process_Servertime_sync_ans(void *taskparameter); -void force_Servertime_sync(uint8_t val[]); - -#endif \ No newline at end of file diff --git a/include/timesync.h b/include/timesync.h new file mode 100644 index 00000000..60d005f1 --- /dev/null +++ b/include/timesync.h @@ -0,0 +1,25 @@ +#ifndef _TIME_SYNC_TIMESERVER_H +#define _TIME_SYNC_TIMESERVER_H + +#include "globals.h" +#include "timesync.h" +#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_TIMEOUT 30 // timeout seconds waiting for timeserver answer +#define TIME_SYNC_THRESHOLD 1 // time deviation threshold triggering time sync +#define TIME_SYNC_START_OPCODE 0x90 // force time sync on node +#define TIME_SYNC_REQ_OPCODE 0x92 // node requests time at server +#define TIME_SYNC_ANS_OPCODE 0x93 // server answers time to node + +extern uint32_t time_sync_messages[], time_sync_answers[]; +extern uint8_t volatile time_sync_seqNo; + +void send_Servertime_req(void); +void recv_Servertime_ans(uint8_t val[]); +void process_Servertime_sync_req(void *taskparameter); +void process_Servertime_sync_ans(void *taskparameter); +void force_Servertime_sync(uint8_t val[]); + +#endif \ No newline at end of file diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index 0757430b..aba502ae 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -33,7 +33,7 @@ void irqHandler(void *pvParameters) { if (InterruptStatus & CYCLIC_IRQ) doHousekeeping(); -#ifdef TIME_SYNC_INTERVAL +#if (TIME_SYNC_INTERVAL) // is time to be synced? if (InterruptStatus & TIMESYNC_IRQ) setTime(timeProvider()); diff --git a/src/lorawan.cpp b/src/lorawan.cpp index af86cc6e..50cb68b2 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -155,7 +155,7 @@ void get_hard_deveui(uint8_t *pdeveui) { #endif // MCP 24AA02E64 } -#if(VERBOSE) +#if (VERBOSE) // Display OTAA keys void showLoraKeys(void) { @@ -225,9 +225,13 @@ void onEvent(ev_t ev) { case EV_TXCOMPLETE: -#if(ServertimeSYNC) - if (!(LMIC.txrxFlags & TXRX_ACK) && time_sync_seqNo) - time_sync_messages[time_sync_seqNo - 1] = LMIC.txend; +#if (TIME_SYNC_TIMESERVER) + // if last packet sent was a timesync request was sent, store TX timestamp + if (LMIC.pendTxPort == TIMEPORT) { + time_sync_messages[time_sync_seqNo] = osticks2ms(LMIC.txend); + ESP_LOGD(TAG, "Timeserver request #%d was sent at %d", + time_sync_seqNo, time_sync_messages[time_sync_seqNo]); + } #endif strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK") @@ -243,6 +247,7 @@ void onEvent(ev_t ev) { if ((LMIC.txrxFlags & TXRX_PORT) && (LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT)) rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); + } break; @@ -385,15 +390,15 @@ esp_err_t lora_stack_init() { // in src/lmic_config.h if you are limited on battery. LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100); // Set the data rate to Spreading Factor 7. This is the fastest supported - // rate for 125 kHz channels, and it minimizes air time and battery power. Set - // the transmission power to 14 dBi (25 mW). + // rate for 125 kHz channels, and it minimizes air time and battery power. + // Set the transmission power to 14 dBi (25 mW). LMIC_setDrTxpow(DR_SF7, 14); #if defined(CFG_US915) || defined(CFG_au921) - // in the US, with TTN, it saves join time if we start on subband 1 (channels - // 8-15). This will get overridden after the join by parameters from the - // network. If working with other networks or in other regions, this will need - // to be changed. + // in the US, with TTN, it saves join time if we start on subband 1 + // (channels 8-15). This will get overridden after the join by parameters + // from the network. If working with other networks or in other regions, + // this will need to be changed. LMIC_selectSubBand(1); #endif diff --git a/src/macsniff.cpp b/src/macsniff.cpp index cd20e39a..fc5fb910 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -106,7 +106,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { blink_LED(COLOR_WHITE, 2000); #endif payload.reset(); - payload.addAlarm(rssi, beaconID); + payload.add2Bytes(rssi, beaconID); SendPayload(BEACONPORT, prio_high); } }; diff --git a/src/main.cpp b/src/main.cpp index 0b44c850..2c61e182 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -400,7 +400,7 @@ void setup() { #endif #endif // HAS_BUTTON -#ifdef TIME_SYNC_INTERVAL +#if(TIME_SYNC_INTERVAL) // start pps timepulse ESP_LOGI(TAG, "Starting Timekeeper..."); assert(timepulse_init()); // setup timepulse @@ -410,7 +410,7 @@ void setup() { #endif #if defined HAS_IF482 || defined HAS_DCF77 -#ifndef TIME_SYNC_INTERVAL +#if (!TIME_SYNC_INTERVAL) #error for clock controller function TIME_SNYC_INTERVAL must be defined in paxcounter.conf #endif ESP_LOGI(TAG, "Starting Clock Controller..."); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 27c2846b..0e5d3fee 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -66,9 +66,9 @@ #define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds] // settings for syncing time of node with external time source -#define TIME_SYNC_INTERVAL 2 // sync time attempt each .. minutes from time source (GPS/LORA/RTC) [default = 60], comment out means off -#define TIME_SYNC_LORA 0 // set to 1 to use LORA network as time source, 0 means off [default = 0] -#define ServertimeSYNC 1 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0] +#define TIME_SYNC_INTERVAL 60 // sync time attempt each .. minutes from time source (GPS/LORA/RTC) [default = 60], 0 means off +#define TIME_SYNC_LORAWAN 0 // set to 1 to use LORA network as time source, 0 means off [default = 0] +#define TIME_SYNC_TIMESERVER 0 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0] // time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino #define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time diff --git a/src/payload.cpp b/src/payload.cpp index 45d7e6e3..06ced707 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -23,7 +23,7 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { buffer[cursor++] = lowByte(value); } -void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { +void PayloadConvert::add2Bytes(int8_t rssi, uint8_t msg) { buffer[cursor++] = rssi; buffer[cursor++] = msg; } @@ -145,7 +145,7 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { writeUint16(value); } -void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { +void PayloadConvert::add2Bytes(int8_t rssi, uint8_t msg) { writeUint8(rssi); writeUint8(msg); } @@ -322,7 +322,7 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { } } -void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { +void PayloadConvert::add2Bytes(int8_t rssi, uint8_t msg) { #if (PAYLOAD_ENCODER == 3) buffer[cursor++] = LPP_ALARM_CHANNEL; #endif diff --git a/src/rcommand.cpp b/src/rcommand.cpp index c4658345..1b04a2ea 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -306,10 +306,10 @@ cmd_t table[] = {{0x01, set_rssi, 1, true}, {0x84, get_gps, 0, false}, {0x85, get_bme, 0, false}, {0x86, get_time, 0, false} -#if(ServertimeSYNC) +#if(TIME_SYNC_TIMESERVER) , - {TIME_ANS_OPCODE, recv_Servertime_ans, 5, false}, - {TIME_SYNC_OPCODE, force_Servertime_sync, 0, false} + {TIME_SYNC_ANS_OPCODE, recv_Servertime_ans, 5, false}, + {TIME_SYNC_START_OPCODE, force_Servertime_sync, 0, false} #endif }; diff --git a/src/servertimesync.cpp b/src/servertimesync.cpp deleted file mode 100644 index c2537025..00000000 --- a/src/servertimesync.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* -/////////////////////// LICENSE NOTE for servertimesync.cpp ///////////////////////////////// -PLEASE NOTE: There is a patent filed for the time sync algorithm used in the following code. -The shown implementation example is covered by the repository's licencse, but you may not be -eligible to deploy the algorith in applications without granted license by the patent holder. -///////////////////////////////////////////////////////////////////////////////////////////// -*/ - - -#ifdef ServertimeSYNC - -#include "Servertimesync.h" - -// Local logging tag -static const char TAG[] = __FILE__; - -TaskHandle_t timeSyncReqTask, timeSyncAnsTask; - -uint32_t time_sync_messages[SYNC_SAMPLES] = {0}, - time_sync_answers[SYNC_SAMPLES] = {0}; - -uint8_t time_sync_seqNo = 0; - -// send time request message -void send_Servertime_req() { - - // if a running timesync handshake is pending then exit - if (time_sync_seqNo) - return; - - // initalize sample arrays - for (uint8_t i = 0; i < SYNC_SAMPLES; i++) - time_sync_messages[i] = time_sync_answers[i] = 0; - - // create temporary task sending sync requests - if (timeSyncReqTask != NULL) - xTaskCreatePinnedToCore(process_Servertime_sync_req, // task function - "timesync_req", // name of task - 2048, // stack size of task - (void *)1, // task parameter - 0, // priority of the task - &timeSyncReqTask, // task handle - 1); // CPU core - - // create temporary task for processing sync answers if not already active - if (timeSyncAnsTask != NULL) - xTaskCreatePinnedToCore(process_Servertime_sync_ans, // task function - "timesync_ans", // name of task - 2048, // stack size of task - (void *)1, // task parameter - 0, // priority of the task - &timeSyncAnsTask, // task handle - 1); // CPU core -} - -// handle time sync response, called from rcommanc.cpp -void recv_Servertime_ans(uint8_t val[]) { - - uint8_t seq_no = val[0]; - uint32_t timestamp = 0; - - time_sync_seqNo--; - - for (int i = 1; i <= 4; ++i) - timestamp = (timestamp << 8) | val[i]; - time_sync_answers[seq_no - 1] = timestamp; - - ESP_LOGI(TAG, "Timeserver timestamp received, sequence #%d: %d", seq_no, - timestamp); - - // inform processing task - if (timeSyncAnsTask) - xTaskNotify(timeSyncAnsTask, seq_no, eSetBits); -} - -void force_Servertime_sync(uint8_t val[]) { - ESP_LOGI(TAG, "Timesync forced by timeserver"); - timeSync(); -}; - -// task for sending time sync requests -void process_Servertime_sync_req(void *taskparameter) { - - TickType_t startTime = xTaskGetTickCount(); - - // enqueue timestamp samples in lora sendqueue - for (uint8_t i = 0; i < SYNC_SAMPLES; i++) { - payload.reset(); - payload.addAlarm(TIME_REQ_OPCODE, time_sync_seqNo); - SendPayload(TIMEPORT, prio_high); - time_sync_seqNo++; - // Wait for the next cycle - vTaskDelayUntil(&startTime, pdMS_TO_TICKS(SYNC_CYCLE * 1000)); - } - vTaskDelete(NULL); // end task -} - -// task for processing a timesync handshake -void process_Servertime_sync_ans(void *taskparameter) { - - uint32_t seq_no = 0; - uint32_t NetworkTime = 0; - int32_t time_diff = 0; - - // collect incoming timestamp samples notified by rcommand - for (uint8_t i = 0; i < SYNC_SAMPLES; i++) { - if (xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, - SYNC_TIMEOUT * 1000 / portTICK_PERIOD_MS) == pdTRUE) - time_sync_seqNo--; - } - - if (time_sync_seqNo) { - ESP_LOGW(TAG, "Timesync handshake failed"); - time_sync_seqNo = 0; - } - - else { - - // calculate time diff from set of collected timestamps - for (uint8_t i = 0; i < SYNC_SAMPLES; i++) - time_diff += time_sync_messages[i] - time_sync_answers[i]; - - if ((time_diff / SYNC_SAMPLES * 1.0f) > SYNC_THRESHOLD) { - NetworkTime = now() + time_diff; - ESP_LOGD(TAG, "Timesync handshake completed, time offset = %d", - time_diff); - } else - ESP_LOGD(TAG, "Timesync handshake completed, time is up to date"); - - // Update system time with time read from the network - if (timeIsValid(NetworkTime)) { - setTime(NetworkTime); - timeSource = _lora; - timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // regular repeat - ESP_LOGI(TAG, "Recent time received from timeserver"); - } else - ESP_LOGW(TAG, "Invalid time received from timeserver"); - } - vTaskDelete(NULL); // end task -} - -#endif \ No newline at end of file diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 191e2c56..ce95a78a 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -36,10 +36,10 @@ time_t timeProvider(void) { #endif // kick off asychronous DB timesync if we have -#if(ServertimeSYNC) +#if(TIME_SYNC_TIMESERVER) send_Servertime_req(); // kick off asychronous lora sync if we have -#elif defined HAS_LORA && (TIME_SYNC_LORA) +#elif defined HAS_LORA && (TIME_SYNC_LORAWAN) LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif diff --git a/src/timesync.cpp b/src/timesync.cpp new file mode 100644 index 00000000..99ac01cd --- /dev/null +++ b/src/timesync.cpp @@ -0,0 +1,150 @@ +/* + +///--> IMPORTANT LICENSE NOTE for this file <--/// + +PLEASE NOTE: There is a patent filed for the time sync algorithm used in the +followin code in this file. This 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. + +*/ + +#ifdef TIME_SYNC_TIMESERVER + +#include "timesync.h" + +// Local logging tag +static const char TAG[] = __FILE__; + +TaskHandle_t timeSyncReqTask, timeSyncAnsTask; +uint32_t time_sync_messages[TIME_SYNC_SAMPLES + + 1] = {0}, + time_sync_answers[TIME_SYNC_SAMPLES + 1] = {0}; +uint8_t volatile time_sync_seqNo = 0; // used in lorawan.cpp to store timestamp + +// send time request message +void send_Servertime_req() { + + // if a timesync handshake is pending then exit + if ((timeSyncAnsTask) || (timeSyncReqTask)) { + ESP_LOGI(TAG, "Timesync sync request already running"); + return; + } else { + ESP_LOGI(TAG, "Timeserver sync request started"); + + // clear timestamp array + for (uint8_t i = 0; i <= TIME_SYNC_SAMPLES + 1; i++) { + time_sync_messages[i] = time_sync_answers[i] = 0; + } + + // create temporary task for processing sync answers if not already active + if (!timeSyncAnsTask) + xTaskCreatePinnedToCore(process_Servertime_sync_ans, // task function + "timesync_ans", // name of task + 2048, // stack size of task + (void *)1, // task parameter + 0, // priority of the task + &timeSyncAnsTask, // task handle + 1); // CPU core + + // create temporary task sending sync requests + if (!timeSyncReqTask) + xTaskCreatePinnedToCore(process_Servertime_sync_req, // task function + "timesync_req", // name of task + 2048, // stack size of task + (void *)1, // task parameter + 0, // priority of the task + &timeSyncReqTask, // task handle + 1); // CPU core + } +} + +// process timeserver timestamp response, called from rcommand.cpp +void recv_Servertime_ans(uint8_t val[]) { + + // if no timesync handshake is pending then exit + if (!time_sync_seqNo) + return; + + uint8_t seq_no = val[0]; + uint32_t timestamp = 0; + + for (int i = 1; i <= 4; i++) + timestamp = (timestamp << 8) | val[i]; + time_sync_answers[seq_no] = timestamp; + + ESP_LOGD(TAG, "Timeserver timestamp #%d received: time=%d", seq_no, + timestamp); + + // inform processing task + if (timeSyncAnsTask) + xTaskNotify(timeSyncAnsTask, seq_no, eSetBits); +} + +// task for sending time sync requests +void process_Servertime_sync_req(void *taskparameter) { + // enqueue timestamp samples in lora sendqueue + for (uint8_t i = 1; i <= TIME_SYNC_SAMPLES; i++) { + time_sync_seqNo++; + payload.reset(); + payload.add2Bytes(TIME_SYNC_REQ_OPCODE, i); + SendPayload(TIMEPORT, prio_high); + ESP_LOGD(TAG, "Timeserver request #%d sent", i); + // Wait for the next cycle + vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); + } + timeSyncReqTask = NULL; + vTaskDelete(NULL); // end task +} + +// task for processing a timesync handshake +void process_Servertime_sync_ans(void *taskparameter) { + uint32_t seq_no = 0; + uint32_t NetworkTime = 0; + int32_t time_diff = 0; + uint8_t ans_counter = TIME_SYNC_SAMPLES; + + // collect incoming timestamp samples notified by rcommand + for (uint8_t i = 1; i <= TIME_SYNC_SAMPLES; i++) { + if (xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, + (TIME_SYNC_CYCLE + TIME_SYNC_TIMEOUT) * 1000 / + portTICK_PERIOD_MS) == pdFALSE) + continue; // no answer received before timeout + + time_diff += time_sync_messages[seq_no] - time_sync_answers[seq_no]; + ans_counter--; + } + + if (ans_counter) { + ESP_LOGW(TAG, "Timesync handshake timeout"); + } else { + // calculate time diff from set of collected timestamps + if (time_diff / TIME_SYNC_SAMPLES) { + NetworkTime = now() + time_diff; + ESP_LOGI(TAG, "Timesync finished, time offset=%d seconds", + time_diff); + // Update system time with time read from the network + if (timeIsValid(NetworkTime)) { + setTime(NetworkTime); + timeSource = _lora; + timesyncer.attach(TIME_SYNC_INTERVAL * 60, + timeSync); // set to regular repeat + ESP_LOGI(TAG, "Recent time received from timeserver"); + } else + ESP_LOGW(TAG, "Invalid time received from timeserver"); + } else + ESP_LOGI(TAG, "Timesync finished, time is up to date"); + } // if (ans_counter) + + time_sync_seqNo = 0; + timeSyncAnsTask = NULL; + vTaskDelete(NULL); // end task +} + +void force_Servertime_sync(uint8_t val[]) { + ESP_LOGI(TAG, "Timesync requested by timeserver"); + timeSync(); +}; + +#endif \ No newline at end of file From 669d05a1b41bc8417d8088a8229c44e547a5fce2 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 9 Mar 2019 00:54:34 +0100 Subject: [PATCH 05/25] timesync.cpp --- src/timesync.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index 99ac01cd..3f3200ab 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -1,9 +1,9 @@ /* -///--> IMPORTANT LICENSE NOTE for this file <--/// +///--> IMPORTANT LICENSE NOTE for this file <--/// PLEASE NOTE: There is a patent filed for the time sync algorithm used in the -followin code in this file. This shown implementation example is covered by the +code of this file. The shown implementation example is covered by the repository's licencse, but you may not be eligible to deploy the applied algorithm in applications without granted license for the algorithm by the patent holder. @@ -122,8 +122,7 @@ void process_Servertime_sync_ans(void *taskparameter) { // calculate time diff from set of collected timestamps if (time_diff / TIME_SYNC_SAMPLES) { NetworkTime = now() + time_diff; - ESP_LOGI(TAG, "Timesync finished, time offset=%d seconds", - time_diff); + ESP_LOGI(TAG, "Timesync finished, time offset=%d seconds", time_diff); // Update system time with time read from the network if (timeIsValid(NetworkTime)) { setTime(NetworkTime); From c64f087faaeedb2cf31990144505e11d638bcead Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 9 Mar 2019 15:25:44 +0100 Subject: [PATCH 06/25] timesync (experimental) --- include/payload.h | 11 ++-- include/timesync.h | 22 +++++--- platformio.ini | 8 +-- src/irqhandler.cpp | 7 ++- src/lorawan.cpp | 15 +++--- src/macsniff.cpp | 2 +- src/paxcounter.conf | 2 +- src/payload.cpp | 27 ++++++++-- src/rcommand.cpp | 2 +- src/timesync.cpp | 126 +++++++++++++++++++++++++++++--------------- 10 files changed, 149 insertions(+), 73 deletions(-) diff --git a/include/payload.h b/include/payload.h index b93347d9..fa7d05d8 100644 --- a/include/payload.h +++ b/include/payload.h @@ -16,11 +16,12 @@ #define LPP_MSG_CHANNEL 28 #define LPP_HUMIDITY_CHANNEL 29 #define LPP_BAROMETER_CHANNEL 30 -#define LPP_AIR_CHANNEL 31 +#define LPP_AIR_CHANNEL 31 #endif -// MyDevices CayenneLPP 2.0 types for Packed Sensor Payload, not using channels, but different FPorts +// MyDevices CayenneLPP 2.0 types for Packed Sensor Payload, not using channels, +// but different FPorts #define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01m #define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed MSB #define LPP_DIGITAL_INPUT 0 // 1 byte @@ -40,11 +41,13 @@ public: void reset(void); 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, uint8_t reset1, uint8_t reset2); - void add2Bytes(int8_t rssi, uint8_t message); + void addAlarm(int8_t rssi, uint8_t message); void addVoltage(uint16_t value); void addGPS(gpsStatus_t value); void addBME(bmeStatus_t value); @@ -72,7 +75,7 @@ private: void writeFloat(float value); void writeUFloat(float value); void writePressure(float value); - void writeVersion(char * version); + void writeVersion(char *version); void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g, bool h); diff --git a/include/timesync.h b/include/timesync.h index 60d005f1..ad0974b8 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -7,19 +7,27 @@ #define TIME_SYNC_SAMPLES 3 // number of time requests for averaging #define TIME_SYNC_CYCLE 20 // seconds between two time requests -#define TIME_SYNC_TIMEOUT 30 // timeout seconds waiting for timeserver answer -#define TIME_SYNC_THRESHOLD 1 // time deviation threshold triggering time sync -#define TIME_SYNC_START_OPCODE 0x90 // force time sync on node -#define TIME_SYNC_REQ_OPCODE 0x92 // node requests time at server -#define TIME_SYNC_ANS_OPCODE 0x93 // server answers time to node +#define TIME_SYNC_TIMEOUT 180 // timeout seconds waiting for timeserver answer +#define TIME_SYNC_THRESHOLD \ + 1.0f // time deviation threshold triggering time sync +#define TIME_SYNC_START_OPCODE 0x90 // start time sync on node +#define TIME_SYNC_STOP_OPCODE 0x91 // stop time sync on node +#define TIME_SYNC_REQ_OPCODE 0x92 // node request at timeserver +#define TIME_SYNC_ANS_OPCODE 0x93 // timeserver answer to node -extern uint32_t time_sync_messages[], time_sync_answers[]; -extern uint8_t volatile time_sync_seqNo; +typedef struct { + uint32_t seconds; + 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; void send_Servertime_req(void); void recv_Servertime_ans(uint8_t val[]); void process_Servertime_sync_req(void *taskparameter); void process_Servertime_sync_ans(void *taskparameter); void force_Servertime_sync(uint8_t val[]); +void store_time_sync_req(time_t secs, uint32_t micros); #endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 6c6fb377..32d4b9d7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -env_default = generic +;env_default = generic ;env_default = ebox ;env_default = eboxtube ;env_default = heltec @@ -15,7 +15,7 @@ env_default = generic ;env_default = ttgov2 ;env_default = ttgov21old ;env_default = ttgov21new -;env_default = ttgobeam +env_default = ttgobeam ;env_default = ttgofox ;env_default = lopy ;env_default = lopy4 @@ -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.361 +release_version = 1.7.37 ; 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 = 3 +debug_level = 4 ; 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/irqhandler.cpp b/src/irqhandler.cpp index aba502ae..bbfdafca 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -35,8 +35,11 @@ void irqHandler(void *pvParameters) { #if (TIME_SYNC_INTERVAL) // is time to be synced? - if (InterruptStatus & TIMESYNC_IRQ) - setTime(timeProvider()); + if (InterruptStatus & TIMESYNC_IRQ) { + time_t t = timeProvider(); + if (timeIsValid(t)) + setTime(t); + } #endif // is time to send the payload? diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 50cb68b2..f0c653ab 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -175,6 +175,8 @@ void showLoraKeys(void) { void onEvent(ev_t ev) { char buff[24] = ""; + uint32_t now_micros; + switch (ev) { case EV_SCAN_TIMEOUT: @@ -227,11 +229,10 @@ void onEvent(ev_t ev) { #if (TIME_SYNC_TIMESERVER) // if last packet sent was a timesync request was sent, store TX timestamp - if (LMIC.pendTxPort == TIMEPORT) { - time_sync_messages[time_sync_seqNo] = osticks2ms(LMIC.txend); - ESP_LOGD(TAG, "Timeserver request #%d was sent at %d", - time_sync_seqNo, time_sync_messages[time_sync_seqNo]); - } + if ((LMIC.pendTxPort == TIMEPORT) && + (LMIC.pendTxData[0] == TIME_SYNC_REQ_OPCODE)) + store_time_sync_req(now(now_micros), now_micros); + // maybe using more precise osticks2ms(LMIC.txend) here? #endif strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK") @@ -247,7 +248,6 @@ void onEvent(ev_t ev) { if ((LMIC.txrxFlags & TXRX_PORT) && (LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT)) rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); - } break; @@ -462,8 +462,7 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, } // Update userUTCTime, considering the difference between the GPS and UTC - // time, and the leap seconds - // !!! DANGER !!! This code will expire in next year with leap second + // time, and the leap seconds until year 2019 *pUserUTCTime = lmicTimeReference.tNetwork + 315964800; // Current time, in ticks ostime_t ticksNow = os_getTime(); diff --git a/src/macsniff.cpp b/src/macsniff.cpp index fc5fb910..cd20e39a 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -106,7 +106,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { blink_LED(COLOR_WHITE, 2000); #endif payload.reset(); - payload.add2Bytes(rssi, beaconID); + payload.addAlarm(rssi, beaconID); SendPayload(BEACONPORT, prio_high); } }; diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 0e5d3fee..46d392ae 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -68,7 +68,7 @@ // settings for syncing time of node with external time source #define TIME_SYNC_INTERVAL 60 // sync time attempt each .. minutes from time source (GPS/LORA/RTC) [default = 60], 0 means off #define TIME_SYNC_LORAWAN 0 // set to 1 to use LORA network as time source, 0 means off [default = 0] -#define TIME_SYNC_TIMESERVER 0 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0] +#define TIME_SYNC_TIMESERVER 1 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0] // time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino #define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time diff --git a/src/payload.cpp b/src/payload.cpp index 06ced707..b5f66378 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -18,12 +18,19 @@ uint8_t *PayloadConvert::getBuffer(void) { return buffer; } #if PAYLOAD_ENCODER == 1 +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); } -void PayloadConvert::add2Bytes(int8_t rssi, uint8_t msg) { +void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { buffer[cursor++] = rssi; buffer[cursor++] = msg; } @@ -141,11 +148,15 @@ void PayloadConvert::addTime(time_t value) { #elif PAYLOAD_ENCODER == 2 +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); } -void PayloadConvert::add2Bytes(int8_t rssi, uint8_t msg) { +void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { writeUint8(rssi); writeUint8(msg); } @@ -299,6 +310,16 @@ 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) { + /* + 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: @@ -322,7 +343,7 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { } } -void PayloadConvert::add2Bytes(int8_t rssi, uint8_t msg) { +void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { #if (PAYLOAD_ENCODER == 3) buffer[cursor++] = LPP_ALARM_CHANNEL; #endif diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 1b04a2ea..f613e0b3 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -308,7 +308,7 @@ cmd_t table[] = {{0x01, set_rssi, 1, true}, {0x86, get_time, 0, false} #if(TIME_SYNC_TIMESERVER) , - {TIME_SYNC_ANS_OPCODE, recv_Servertime_ans, 5, false}, + {TIME_SYNC_ANS_OPCODE, recv_Servertime_ans, 6, false}, {TIME_SYNC_START_OPCODE, force_Servertime_sync, 0, false} #endif }; diff --git a/src/timesync.cpp b/src/timesync.cpp index 3f3200ab..0ff75253 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -18,10 +18,11 @@ patent holder. static const char TAG[] = __FILE__; TaskHandle_t timeSyncReqTask, timeSyncAnsTask; -uint32_t time_sync_messages[TIME_SYNC_SAMPLES + - 1] = {0}, - time_sync_answers[TIME_SYNC_SAMPLES + 1] = {0}; -uint8_t volatile time_sync_seqNo = 0; // used in lorawan.cpp to store timestamp +time_sync_message_t + time_sync_messages[TIME_SYNC_SAMPLES + 1] = {0}, + time_sync_answers[TIME_SYNC_SAMPLES + + 1] = {0}; +uint8_t time_sync_seqNo = 0; // used in lorawan.cpp to store timestamp // send time request message void send_Servertime_req() { @@ -35,9 +36,12 @@ void send_Servertime_req() { // clear timestamp array for (uint8_t i = 0; i <= TIME_SYNC_SAMPLES + 1; i++) { - time_sync_messages[i] = time_sync_answers[i] = 0; + time_sync_messages[i].seconds = time_sync_answers[i].seconds = 0; + time_sync_messages[i].fractions = time_sync_answers[i].fractions = 0; } + time_sync_seqNo = 0; + // create temporary task for processing sync answers if not already active if (!timeSyncAnsTask) xTaskCreatePinnedToCore(process_Servertime_sync_ans, // task function @@ -68,14 +72,16 @@ void recv_Servertime_ans(uint8_t val[]) { return; uint8_t seq_no = val[0]; - uint32_t timestamp = 0; + uint32_t timestamp_sec = 0, timestamp_ms = 0; - for (int i = 1; i <= 4; i++) - timestamp = (timestamp << 8) | val[i]; - time_sync_answers[seq_no] = timestamp; + for (int i = 1; i <= 4; i++) { + timestamp_sec = (timestamp_sec << 8) | val[i]; + time_sync_answers[seq_no].seconds = timestamp_sec; + } + time_sync_answers[seq_no].fractions = val[5]; - ESP_LOGD(TAG, "Timeserver timestamp #%d received: time=%d", seq_no, - timestamp); + ESP_LOGD(TAG, "Timeserver timestamp #%d received: time=%d.%d", seq_no, + timestamp_sec, timestamp_ms); // inform processing task if (timeSyncAnsTask) @@ -88,59 +94,95 @@ void process_Servertime_sync_req(void *taskparameter) { for (uint8_t i = 1; i <= TIME_SYNC_SAMPLES; i++) { time_sync_seqNo++; payload.reset(); - payload.add2Bytes(TIME_SYNC_REQ_OPCODE, i); + payload.addWord(TIME_SYNC_REQ_OPCODE | time_sync_seqNo << 8); SendPayload(TIMEPORT, prio_high); - ESP_LOGD(TAG, "Timeserver request #%d sent", i); + ESP_LOGD(TAG, "Timeserver request #%d sent", time_sync_seqNo); // Wait for the next cycle vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); } + payload.reset(); + payload.addByte(TIME_SYNC_STOP_OPCODE); + SendPayload(TIMEPORT, prio_high); // necessary to receive last timestamp timeSyncReqTask = NULL; vTaskDelete(NULL); // end task } // task for processing a timesync handshake void process_Servertime_sync_ans(void *taskparameter) { - uint32_t seq_no = 0; - uint32_t NetworkTime = 0; - int32_t time_diff = 0; - uint8_t ans_counter = TIME_SYNC_SAMPLES; + + uint32_t seq_no = 0, time_diff_sec = 0, time_diff_ms = 0; + time_t time_to_set = 0; + float time_offset = 0.0f; // collect incoming timestamp samples notified by rcommand for (uint8_t i = 1; i <= TIME_SYNC_SAMPLES; i++) { - if (xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, - (TIME_SYNC_CYCLE + TIME_SYNC_TIMEOUT) * 1000 / - portTICK_PERIOD_MS) == pdFALSE) - continue; // no answer received before timeout + if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, + (TIME_SYNC_CYCLE + TIME_SYNC_TIMEOUT) * 1000 / + portTICK_PERIOD_MS) == pdFALSE) || + (seq_no != i)) { + ESP_LOGW(TAG, "Timesync handshake timeout"); + goto finish; // no valid sequence received before timeout + } else // calculate time diff from set of collected timestamps + { + time_diff_sec += time_sync_messages[seq_no].seconds - + time_sync_answers[seq_no].seconds; + time_diff_ms += 4 * (time_sync_messages[seq_no].fractions - + time_sync_answers[seq_no].fractions); + } + } // for - time_diff += time_sync_messages[seq_no] - time_sync_answers[seq_no]; - ans_counter--; - } + time_offset = (time_diff_sec + time_diff_ms / 1000.0) / TIME_SYNC_SAMPLES; - if (ans_counter) { - ESP_LOGW(TAG, "Timesync handshake timeout"); - } else { - // calculate time diff from set of collected timestamps - if (time_diff / TIME_SYNC_SAMPLES) { - NetworkTime = now() + time_diff; - ESP_LOGI(TAG, "Timesync finished, time offset=%d seconds", time_diff); - // Update system time with time read from the network - if (timeIsValid(NetworkTime)) { - setTime(NetworkTime); - timeSource = _lora; - timesyncer.attach(TIME_SYNC_INTERVAL * 60, - timeSync); // set to regular repeat - ESP_LOGI(TAG, "Recent time received from timeserver"); - } else - ESP_LOGW(TAG, "Invalid time received from timeserver"); + ESP_LOGD(TAG, "Timesync finished, time offset=%.4f seconds", time_offset); + + // check time diff and if necessary set time + if (time_offset >= TIME_SYNC_THRESHOLD) { + + // 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++; + } + + time_to_set = time_t(now() - time_diff_sec); + + ESP_LOGD(TAG, "Time to set = %d", time_to_set); + + // Update system time with time read from the network + if (timeIsValid(time_to_set)) { + setTime(time_to_set); + SyncToPPS(); + timeSource = _lora; + timesyncer.attach(TIME_SYNC_INTERVAL * 60, + timeSync); // set to regular repeat + ESP_LOGI(TAG, "Timesync finished, time was adjusted"); } else - ESP_LOGI(TAG, "Timesync finished, time is up to date"); - } // if (ans_counter) + ESP_LOGW(TAG, "Invalid time received from timeserver"); + } else + ESP_LOGI(TAG, "Timesync finished, time is up to date"); + +finish: time_sync_seqNo = 0; timeSyncAnsTask = NULL; vTaskDelete(NULL); // end task } +// called from lorawan.cpp when tine_sync_req was sent +void store_time_sync_req(time_t secs, uint32_t micros) { + + time_sync_messages[time_sync_seqNo].seconds = secs; + time_sync_messages[time_sync_seqNo].fractions = + micros / 250; // 4ms resolution + + ESP_LOGD(TAG, "Timeserver request #%d was sent at %d.%d", time_sync_seqNo, + time_sync_messages[time_sync_seqNo].seconds, + time_sync_messages[time_sync_seqNo].fractions); +} + void force_Servertime_sync(uint8_t val[]) { ESP_LOGI(TAG, "Timesync requested by timeserver"); timeSync(); From 7ab0d8fcd680eb7fad59865cfb93276c92f94fa5 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 9 Mar 2019 20:40:21 +0100 Subject: [PATCH 07/25] timesync(experimental) --- include/timesync.h | 19 +++--- src/TTN/packed_decoder.js | 12 ++++ src/senddata.cpp | 10 ++- src/timesync.cpp | 139 ++++++++++++++++++-------------------- 4 files changed, 94 insertions(+), 86 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index ad0974b8..2207e02b 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -5,15 +5,14 @@ #include "timesync.h" #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_TIMEOUT 180 // timeout seconds waiting for timeserver answer -#define TIME_SYNC_THRESHOLD \ - 1.0f // time deviation threshold triggering time sync -#define TIME_SYNC_START_OPCODE 0x90 // start time sync on node -#define TIME_SYNC_STOP_OPCODE 0x91 // stop time sync on node -#define TIME_SYNC_REQ_OPCODE 0x92 // node request at timeserver -#define TIME_SYNC_ANS_OPCODE 0x93 // timeserver answer to node +#define TIME_SYNC_SAMPLES 1 // number of time requests for averaging +#define TIME_SYNC_CYCLE 60 // seconds between two time requests +#define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer +#define TIME_SYNC_TRIGGER 1.0f // time deviation threshold triggering time sync +#define TIME_SYNC_START_OPCODE 0x90 // start time sync (server -> node) +#define TIME_SYNC_STEP_OPCODE 0x91 // step to next sync request (node -> server) +#define TIME_SYNC_REQ_OPCODE 0x92 // node request at timeserver (node -> server) +#define TIME_SYNC_ANS_OPCODE 0x93 // timeserver answer to node (server -> node) typedef struct { uint32_t seconds; @@ -22,11 +21,11 @@ typedef struct { 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 val[]); void process_Servertime_sync_req(void *taskparameter); -void process_Servertime_sync_ans(void *taskparameter); void force_Servertime_sync(uint8_t val[]); void store_time_sync_req(time_t secs, uint32_t micros); diff --git a/src/TTN/packed_decoder.js b/src/TTN/packed_decoder.js index a99f44c6..265b416c 100644 --- a/src/TTN/packed_decoder.js +++ b/src/TTN/packed_decoder.js @@ -63,6 +63,18 @@ function Decoder(bytes, port) { return decode(bytes, [uint16], ['voltage']); } + if (port === 9) { + // timesync request + if (bytes.length === 1) { + decoded.timesync_opcode = bytes[0]; + } + if (bytes.length === 2) { + decoded.timesync_opcode = bytes[0]; + decoded.timesync_seqno = bytes[1]; + } + return decoded; + } + } diff --git a/src/senddata.cpp b/src/senddata.cpp index 36e8b278..fab57a10 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -40,8 +40,16 @@ void SendPayload(uint8_t port, sendprio_t prio) { // enqueue message in device's send queues #ifdef HAS_LORA - lora_enqueuedata(&SendBuffer, prio); +/* + // 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); #endif + #ifdef HAS_SPI spi_enqueuedata(&SendBuffer, prio); #endif diff --git a/src/timesync.cpp b/src/timesync.cpp index 0ff75253..f3f8a9b9 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -17,42 +17,31 @@ patent holder. // Local logging tag static const char TAG[] = __FILE__; -TaskHandle_t timeSyncReqTask, timeSyncAnsTask; -time_sync_message_t - time_sync_messages[TIME_SYNC_SAMPLES + 1] = {0}, - time_sync_answers[TIME_SYNC_SAMPLES + - 1] = {0}; -uint8_t time_sync_seqNo = 0; // used in lorawan.cpp to store timestamp +TaskHandle_t timeSyncReqTask; +time_sync_message_t time_sync_messages[TIME_SYNC_SAMPLES] = {0}, + time_sync_answers[TIME_SYNC_SAMPLES] = {0}; +uint8_t time_sync_seqNo = 0; +bool time_sync_pending = false; // send time request message void send_Servertime_req() { // if a timesync handshake is pending then exit - if ((timeSyncAnsTask) || (timeSyncReqTask)) { - ESP_LOGI(TAG, "Timesync sync request already running"); + if (time_sync_pending) { + ESP_LOGI(TAG, "Timeserver sync request already running"); return; } else { ESP_LOGI(TAG, "Timeserver sync request started"); + time_sync_pending = true; + // 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_seqNo = 0; - - // create temporary task for processing sync answers if not already active - if (!timeSyncAnsTask) - xTaskCreatePinnedToCore(process_Servertime_sync_ans, // task function - "timesync_ans", // name of task - 2048, // stack size of task - (void *)1, // task parameter - 0, // priority of the task - &timeSyncAnsTask, // task handle - 1); // CPU core - - // create temporary task sending sync requests + // kick off temporary task for timeserver handshake processing if (!timeSyncReqTask) xTaskCreatePinnedToCore(process_Servertime_sync_req, // task function "timesync_req", // name of task @@ -68,7 +57,7 @@ void send_Servertime_req() { void recv_Servertime_ans(uint8_t val[]) { // if no timesync handshake is pending then exit - if (!time_sync_seqNo) + if (!time_sync_pending) return; uint8_t seq_no = val[0]; @@ -78,65 +67,68 @@ void recv_Servertime_ans(uint8_t val[]) { timestamp_sec = (timestamp_sec << 8) | val[i]; time_sync_answers[seq_no].seconds = timestamp_sec; } - time_sync_answers[seq_no].fractions = val[5]; + timestamp_ms = 4 * val[5]; + time_sync_answers[seq_no].fractions = timestamp_ms; - ESP_LOGD(TAG, "Timeserver timestamp #%d received: time=%d.%d", seq_no, + ESP_LOGD(TAG, "Timeserver answer #%d received: timestamp=%d.%03d", seq_no, timestamp_sec, timestamp_ms); // inform processing task - if (timeSyncAnsTask) - xTaskNotify(timeSyncAnsTask, seq_no, eSetBits); + if (timeSyncReqTask) + xTaskNotify(timeSyncReqTask, seq_no, eSetBits); } // task for sending time sync requests void process_Servertime_sync_req(void *taskparameter) { - // enqueue timestamp samples in lora sendqueue - for (uint8_t i = 1; i <= TIME_SYNC_SAMPLES; i++) { - time_sync_seqNo++; - payload.reset(); - payload.addWord(TIME_SYNC_REQ_OPCODE | time_sync_seqNo << 8); - SendPayload(TIMEPORT, prio_high); - ESP_LOGD(TAG, "Timeserver request #%d sent", time_sync_seqNo); - // Wait for the next cycle - vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); - } - payload.reset(); - payload.addByte(TIME_SYNC_STOP_OPCODE); - SendPayload(TIMEPORT, prio_high); // necessary to receive last timestamp - timeSyncReqTask = NULL; - vTaskDelete(NULL); // end task -} - -// task for processing a timesync handshake -void process_Servertime_sync_ans(void *taskparameter) { uint32_t seq_no = 0, time_diff_sec = 0, time_diff_ms = 0; time_t time_to_set = 0; float time_offset = 0.0f; - // collect incoming timestamp samples notified by rcommand + // enqueue timestamp samples in lora sendqueue for (uint8_t i = 1; 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.addWord(TIME_SYNC_REQ_OPCODE | time_sync_seqNo << 8); + SendPayload(TIMEPORT, prio_high); + ESP_LOGD(TAG, "Timeserver request #%d enqeued", time_sync_seqNo); + + // send dummy packet to trigger receive answer + payload.reset(); + payload.addByte(TIME_SYNC_STEP_OPCODE); + SendPayload(TIMEPORT, prio_low); // open receive slot for answer + + // process answer if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, - (TIME_SYNC_CYCLE + TIME_SYNC_TIMEOUT) * 1000 / - portTICK_PERIOD_MS) == pdFALSE) || - (seq_no != i)) { - ESP_LOGW(TAG, "Timesync handshake timeout"); - goto finish; // no valid sequence received before timeout - } else // calculate time diff from set of collected timestamps - { - time_diff_sec += time_sync_messages[seq_no].seconds - - time_sync_answers[seq_no].seconds; - time_diff_ms += 4 * (time_sync_messages[seq_no].fractions - - time_sync_answers[seq_no].fractions); + TIME_SYNC_TIMEOUT * 1000 / portTICK_PERIOD_MS) == + pdFALSE) || + (seq_no != time_sync_seqNo)) { + ESP_LOGW(TAG, "Timeserver handshake error"); + 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); } - } // for + } - time_offset = (time_diff_sec + time_diff_ms / 1000.0) / TIME_SYNC_SAMPLES; + // 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 finished, time offset=%.4f seconds", time_offset); + // ESP_LOGD(TAG, "Timesync time offset=%.3f", time_offset); + ESP_LOGD(TAG, "Timesync time offset=%d.%03d", time_diff_sec / TIME_SYNC_SAMPLES, + time_diff_ms / TIME_SYNC_SAMPLES); - // check time diff and if necessary set time - if (time_offset >= TIME_SYNC_THRESHOLD) { + if (time_offset >= TIME_SYNC_TRIGGER) { // wait until top of second if (time_diff_ms > 0) { @@ -147,11 +139,10 @@ void process_Servertime_sync_ans(void *taskparameter) { time_diff_sec++; } - time_to_set = time_t(now() - time_diff_sec); - + time_to_set = time_t(now() + time_diff_sec); ESP_LOGD(TAG, "Time to set = %d", time_to_set); - // Update system time with time read from the network + // adjust system time if (timeIsValid(time_to_set)) { setTime(time_to_set); SyncToPPS(); @@ -166,21 +157,19 @@ void process_Servertime_sync_ans(void *taskparameter) { finish: - time_sync_seqNo = 0; - timeSyncAnsTask = NULL; + time_sync_pending = false; + timeSyncReqTask = NULL; vTaskDelete(NULL); // end task } -// called from lorawan.cpp when tine_sync_req was sent +// called from lorawan.cpp immediately 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; + time_sync_messages[k].fractions = micros / 250; // 4ms resolution - time_sync_messages[time_sync_seqNo].seconds = secs; - time_sync_messages[time_sync_seqNo].fractions = - micros / 250; // 4ms resolution - - ESP_LOGD(TAG, "Timeserver request #%d was sent at %d.%d", time_sync_seqNo, - time_sync_messages[time_sync_seqNo].seconds, - time_sync_messages[time_sync_seqNo].fractions); + ESP_LOGD(TAG, "Timeserver request #%d sent at %d.%03d", time_sync_seqNo, + time_sync_messages[k].seconds, time_sync_messages[k].fractions); } void force_Servertime_sync(uint8_t val[]) { From b742f888fd90ea21ab43f7e5a98a7225e0b722cd Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 9 Mar 2019 20:43:45 +0100 Subject: [PATCH 08/25] timeserver(experimental) --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 875ecd9f..3d7afc0a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ .clang_complete .gcc-flags.json src/loraconf.h -src/ota.conf \ No newline at end of file +src/ota.conf +src/NodeRed/Timeserver.json \ No newline at end of file From f9f5f499fff9425a855711b1c25fedf0b22d3dc2 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sat, 9 Mar 2019 22:08:57 +0100 Subject: [PATCH 09/25] timeserver(experimental) --- README.md | 4 +++ include/timesync.h | 10 +++---- src/TTN/packed_decoder.js | 8 ++---- src/TTN/plain_decoder.js | 6 +++++ src/lorawan.cpp | 20 +++++++------- src/rcommand.cpp | 57 ++++++++++++++++++--------------------- src/spislave.cpp | 6 +---- src/timekeeper.cpp | 16 ++++++++--- src/timesync.cpp | 31 +++++++++------------ 9 files changed, 77 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index af46511d..07ead53b 100644 --- a/README.md +++ b/README.md @@ -366,6 +366,10 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts. Device answers with it's local time/date (UTC Unix epoch) on Port 9. +0x87 set time/date + + Device synchronizes it's time/date by calling the preconfigured time source. + # License diff --git a/include/timesync.h b/include/timesync.h index 2207e02b..d79290d4 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -6,13 +6,10 @@ #include "timekeeper.h" #define TIME_SYNC_SAMPLES 1 // number of time requests for averaging -#define TIME_SYNC_CYCLE 60 // seconds between two time requests +#define TIME_SYNC_CYCLE 30 // seconds between two time requests #define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer #define TIME_SYNC_TRIGGER 1.0f // time deviation threshold triggering time sync -#define TIME_SYNC_START_OPCODE 0x90 // start time sync (server -> node) -#define TIME_SYNC_STEP_OPCODE 0x91 // step to next sync request (node -> server) -#define TIME_SYNC_REQ_OPCODE 0x92 // node request at timeserver (node -> server) -#define TIME_SYNC_ANS_OPCODE 0x93 // timeserver answer to node (server -> node) +#define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length typedef struct { uint32_t seconds; @@ -24,9 +21,8 @@ extern uint8_t time_sync_seqNo; extern bool time_sync_pending; void send_Servertime_req(void); -void recv_Servertime_ans(uint8_t val[]); +void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len); void process_Servertime_sync_req(void *taskparameter); -void force_Servertime_sync(uint8_t val[]); void store_time_sync_req(time_t secs, uint32_t micros); #endif \ No newline at end of file diff --git a/src/TTN/packed_decoder.js b/src/TTN/packed_decoder.js index 265b416c..49f1b5b9 100644 --- a/src/TTN/packed_decoder.js +++ b/src/TTN/packed_decoder.js @@ -66,14 +66,10 @@ function Decoder(bytes, port) { if (port === 9) { // timesync request if (bytes.length === 1) { - decoded.timesync_opcode = bytes[0]; - } - if (bytes.length === 2) { - decoded.timesync_opcode = bytes[0]; - decoded.timesync_seqno = bytes[1]; + decoded.timesync_seqno = bytes[0]; } return decoded; - } + } } diff --git a/src/TTN/plain_decoder.js b/src/TTN/plain_decoder.js index ca43a5e4..62249d08 100644 --- a/src/TTN/plain_decoder.js +++ b/src/TTN/plain_decoder.js @@ -67,6 +67,12 @@ function Decoder(bytes, port) { decoded.battery = (bytes[i++] << 8) | bytes[i++];} } + if (port === 9) { + if (bytes.length === 1) { + decoded.timesync_seqno = bytes[0]; + } + } + return decoded; } diff --git a/src/lorawan.cpp b/src/lorawan.cpp index f0c653ab..7d2cc7c2 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -228,11 +228,10 @@ void onEvent(ev_t ev) { case EV_TXCOMPLETE: #if (TIME_SYNC_TIMESERVER) - // if last packet sent was a timesync request was sent, store TX timestamp - if ((LMIC.pendTxPort == TIMEPORT) && - (LMIC.pendTxData[0] == TIME_SYNC_REQ_OPCODE)) + // if last packet sent was a timesync request, store TX timestamp + if (LMIC.pendTxPort == TIMEPORT) store_time_sync_req(now(now_micros), now_micros); - // maybe using more precise osticks2ms(LMIC.txend) here? + // maybe use more precise osticks2ms(LMIC.txend) here? #endif strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK") @@ -244,7 +243,13 @@ void onEvent(ev_t ev) { LMIC.dataLen, LMIC.rssi, LMIC.snr / 4); sprintf(display_line6, "RSSI -%d SNR %d", LMIC.rssi, LMIC.snr / 4); - // check if command is received on command port, then call interpreter + // check if this is a timesync answer, then call timesync processor +#if (TIME_SYNC_TIMESERVER) + if ((LMIC.txrxFlags & TXRX_PORT) && + (LMIC.frame[LMIC.dataBeg - 1] == TIMEPORT)) + recv_Servertime_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); +#endif + // check if this an opcode, then call rcommand interpreter if ((LMIC.txrxFlags & TXRX_PORT) && (LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT)) rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); @@ -422,11 +427,8 @@ void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0); break; } - if (ret == pdTRUE) { - ESP_LOGI(TAG, "%d bytes enqueued for LORA interface", message->MessageSize); - } else { + if (ret != pdTRUE) ESP_LOGW(TAG, "LORA sendqueue is full"); - } } void lora_queuereset(void) { xQueueReset(LoraSendQueue); } diff --git a/src/rcommand.cpp b/src/rcommand.cpp index f613e0b3..a8b572bd 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -38,7 +38,7 @@ void set_reset(uint8_t val[]) { break; case 9: // reset and ask for software update via Wifi OTA ESP_LOGI(TAG, "Remote command: software update via Wifi"); -#if(USE_OTA) +#if (USE_OTA) sprintf(display_line6, "Software update"); cfg.runmode = 1; #else @@ -278,40 +278,35 @@ void get_time(uint8_t val[]) { SendPayload(TIMEPORT, prio_high); }; +void set_time(uint8_t val[]) { + ESP_LOGI(TAG, "Timesync requested by timeserver"); + timeSync(); +}; + +void set_flush(uint8_t val[]) { + ESP_LOGI(TAG, "Remote command: flush"); + // does nothing + // used to open receive window on LoRaWAN class a nodes +}; + // assign previously defined functions to set of numeric remote commands // format: opcode, function, #bytes params, // flag (true = do make settings persistent / false = don't) // -cmd_t table[] = {{0x01, set_rssi, 1, true}, - {0x02, set_countmode, 1, true}, - {0x03, set_gps, 1, true}, - {0x04, set_display, 1, true}, - {0x05, set_lorasf, 1, true}, - {0x06, set_lorapower, 1, true}, - {0x07, set_loraadr, 1, true}, - {0x08, set_screensaver, 1, true}, - {0x09, set_reset, 1, true}, - {0x0a, set_sendcycle, 1, true}, - {0x0b, set_wifichancycle, 1, true}, - {0x0c, set_blescantime, 1, true}, - {0x0d, set_vendorfilter, 1, false}, - {0x0e, set_blescan, 1, true}, - {0x0f, set_wifiant, 1, true}, - {0x10, set_rgblum, 1, true}, - {0x11, set_monitor, 1, true}, - {0x12, set_beacon, 7, false}, - {0x13, set_sensor, 2, true}, - {0x80, get_config, 0, false}, - {0x81, get_status, 0, false}, - {0x84, get_gps, 0, false}, - {0x85, get_bme, 0, false}, - {0x86, get_time, 0, false} -#if(TIME_SYNC_TIMESERVER) - , - {TIME_SYNC_ANS_OPCODE, recv_Servertime_ans, 6, false}, - {TIME_SYNC_START_OPCODE, force_Servertime_sync, 0, false} -#endif -}; +cmd_t table[] = { + {0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true}, + {0x03, set_gps, 1, true}, {0x04, set_display, 1, true}, + {0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true}, + {0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true}, + {0x09, set_reset, 1, true}, {0x0a, set_sendcycle, 1, true}, + {0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true}, + {0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true}, + {0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true}, + {0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false}, + {0x13, set_sensor, 2, true}, {0x80, get_config, 0, false}, + {0x81, get_status, 0, false}, {0x84, get_gps, 0, false}, + {0x85, get_bme, 0, false}, {0x86, get_time, 0, false}, + {0x87, set_time, 0, false}, {0x99, set_flush, 0, false}}; const uint8_t cmdtablesize = sizeof(table) / sizeof(table[0]); // number of commands in command table diff --git a/src/spislave.cpp b/src/spislave.cpp index ed6ab7b0..cd6455a0 100644 --- a/src/spislave.cpp +++ b/src/spislave.cpp @@ -160,12 +160,8 @@ void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { ret = xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0); break; } - if (ret == pdTRUE) { - ESP_LOGI(TAG, "%d byte(s) enqueued for SPI interface", - message->MessageSize); - } else { + if (ret != pdTRUE) ESP_LOGW(TAG, "SPI sendqueue is full"); - } } void spi_queuereset(void) { xQueueReset(SPISendQueue); } diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index ce95a78a..2a10e862 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -1,5 +1,13 @@ #include "timekeeper.h" +#ifndef HAS_LORA +#if (TIME_SYNC_TIMESERVER) +#error TIME_SYNC_TIMESERVER defined, but device has no LORA configured +#elif (TIME_SYNC_LORAWAN) +#error TIME_SYNC_LORAWAN defined, but device has no LORA configured +#endif +#endif + // Local logging tag static const char TAG[] = __FILE__; @@ -35,11 +43,11 @@ time_t timeProvider(void) { } #endif -// kick off asychronous DB timesync if we have -#if(TIME_SYNC_TIMESERVER) +// kick off asychronous Lora timeserver timesync if we have +#if (TIME_SYNC_TIMESERVER) send_Servertime_req(); -// kick off asychronous lora sync if we have -#elif defined HAS_LORA && (TIME_SYNC_LORAWAN) +// kick off asychronous lora network sync if we have +#elif (TIME_SYNC_LORAWAN) LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif diff --git a/src/timesync.cpp b/src/timesync.cpp index f3f8a9b9..c7419396 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -28,7 +28,7 @@ void send_Servertime_req() { // if a timesync handshake is pending then exit if (time_sync_pending) { - ESP_LOGI(TAG, "Timeserver sync request already running"); + ESP_LOGI(TAG, "Timeserver sync request already pending"); return; } else { ESP_LOGI(TAG, "Timeserver sync request started"); @@ -54,20 +54,20 @@ void send_Servertime_req() { } // process timeserver timestamp response, called from rcommand.cpp -void recv_Servertime_ans(uint8_t val[]) { +void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len) { - // if no timesync handshake is pending then exit - if (!time_sync_pending) + // if no timesync handshake is pending or invalid buffer then exit + if ((!time_sync_pending) || (buf_len != TIME_SYNC_FRAME_LENGTH)) return; - uint8_t seq_no = val[0]; + 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) | val[i]; + timestamp_sec = (timestamp_sec << 8) | buf[i]; time_sync_answers[seq_no].seconds = timestamp_sec; } - timestamp_ms = 4 * val[5]; + timestamp_ms = 4 * buf[5]; time_sync_answers[seq_no].fractions = timestamp_ms; ESP_LOGD(TAG, "Timeserver answer #%d received: timestamp=%d.%03d", seq_no, @@ -93,14 +93,13 @@ void process_Servertime_sync_req(void *taskparameter) { // send sync request to server payload.reset(); - payload.addWord(TIME_SYNC_REQ_OPCODE | time_sync_seqNo << 8); + payload.addByte(time_sync_seqNo); SendPayload(TIMEPORT, prio_high); - ESP_LOGD(TAG, "Timeserver request #%d enqeued", time_sync_seqNo); // send dummy packet to trigger receive answer payload.reset(); - payload.addByte(TIME_SYNC_STEP_OPCODE); - SendPayload(TIMEPORT, prio_low); // open receive slot for answer + payload.addByte(0x99); // flush + SendPayload(RCMDPORT, prio_low); // open receive slot for answer // process answer if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, @@ -124,9 +123,8 @@ void process_Servertime_sync_req(void *taskparameter) { time_offset = (time_diff_sec + time_diff_ms / 1000.0f) / (TIME_SYNC_SAMPLES * 1.0f); - // ESP_LOGD(TAG, "Timesync time offset=%.3f", time_offset); - ESP_LOGD(TAG, "Timesync time offset=%d.%03d", time_diff_sec / TIME_SYNC_SAMPLES, - time_diff_ms / TIME_SYNC_SAMPLES); + ESP_LOGD(TAG, "Timesync time offset=%d.%03d", + time_diff_sec / TIME_SYNC_SAMPLES, time_diff_ms / TIME_SYNC_SAMPLES); if (time_offset >= TIME_SYNC_TRIGGER) { @@ -172,9 +170,4 @@ void store_time_sync_req(time_t secs, uint32_t micros) { time_sync_messages[k].seconds, time_sync_messages[k].fractions); } -void force_Servertime_sync(uint8_t val[]) { - ESP_LOGI(TAG, "Timesync requested by timeserver"); - timeSync(); -}; - #endif \ No newline at end of file From c13934be99119d2fccc1536de43986cab2e1825f Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sun, 10 Mar 2019 17:35:57 +0100 Subject: [PATCH 10/25] timeserver(experimental) --- .gitignore | 3 +- include/payload.h | 1 - include/timesync.h | 4 - src/TTN/Nodered-Timeserver.json | 214 ++++++++++++++++++++++++++++++++ src/payload.cpp | 12 -- src/senddata.cpp | 10 +- src/timesync.cpp | 59 ++++----- 7 files changed, 242 insertions(+), 61 deletions(-) create mode 100644 src/TTN/Nodered-Timeserver.json 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; From 71475678cd44636a89de863b58c86a853a6a54fd Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sun, 10 Mar 2019 20:14:10 +0100 Subject: [PATCH 11/25] timeserver fixes --- src/TTN/Nodered-Timeserver.json | 75 ++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/src/TTN/Nodered-Timeserver.json b/src/TTN/Nodered-Timeserver.json index f5ebdf70..2ad5c54e 100644 --- a/src/TTN/Nodered-Timeserver.json +++ b/src/TTN/Nodered-Timeserver.json @@ -48,8 +48,8 @@ "from": "", "to": "", "reg": false, - "x": 540, - "y": 340, + "x": 220, + "y": 360, "wires": [ [ "84f1cda2.069e7" @@ -64,8 +64,8 @@ "topic": "+/devices/+/up", "qos": "2", "broker": "2a15ab6f.ab2244", - "x": 130, - "y": 140, + "x": 70, + "y": 120, "wires": [ [ "9f4b8dd3.2f0d2" @@ -81,8 +81,8 @@ "qos": "", "retain": "", "broker": "2a15ab6f.ab2244", - "x": 710, - "y": 460, + "x": 690, + "y": 360, "wires": [] }, { @@ -93,8 +93,8 @@ "property": "payload", "action": "", "pretty": false, - "x": 340, - "y": 240, + "x": 220, + "y": 200, "wires": [ [ "8ed813a9.a9319" @@ -118,8 +118,8 @@ "checkall": "true", "repair": false, "outputs": 1, - "x": 340, - "y": 140, + "x": 220, + "y": 120, "wires": [ [ "4f97d75.6c87528" @@ -131,11 +131,11 @@ "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;", + "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 % 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, + "x": 400, + "y": 280, "wires": [ [ "49e3c067.e782e" @@ -150,8 +150,8 @@ "property": "payload", "action": "", "pretty": false, - "x": 480, - "y": 460, + "x": 540, + "y": 360, "wires": [ [ "72d5e7ee.d1eba8" @@ -165,11 +165,11 @@ "name": "Decode", "action": "", "property": "payload.payload_raw", - "x": 480, - "y": 240, + "x": 380, + "y": 200, "wires": [ [ - "f4c5b6de.f95148" + "f868bce2.dde67" ] ] }, @@ -180,14 +180,49 @@ "name": "Encode", "action": "", "property": "payload.payload_raw", - "x": 340, - "y": 460, + "x": 380, + "y": 360, "wires": [ [ "dac8aafa.389298" ] ] }, + { + "id": "6190967b.01f758", + "type": "comment", + "z": "449c1517.e25f4c", + "name": "LoRaWAN Timeserver", + "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": 120, + "y": 40, + "wires": [] + }, + { + "id": "f868bce2.dde67", + "type": "switch", + "z": "449c1517.e25f4c", + "name": "Timechecker", + "property": "payload.metadata.gateways[0].time", + "propertyType": "msg", + "rules": [ + { + "t": "lte", + "v": "payload.metadata.time", + "vt": "msg" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 550, + "y": 200, + "wires": [ + [ + "f4c5b6de.f95148" + ] + ] + }, { "id": "2a15ab6f.ab2244", "type": "mqtt-broker", From bd8718f23fb5d3b3b140388ad4ca6d8aebda9fb6 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Mon, 11 Mar 2019 01:05:41 +0100 Subject: [PATCH 12/25] timeserver (experimental) --- include/timesync.h | 2 +- src/TTN/Nodered-Timeserver.json | 2 +- src/timesync.cpp | 72 ++++++++++++++++++++------------- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index 33d35462..866f5a4c 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -5,7 +5,7 @@ #include "timesync.h" #include "timekeeper.h" -#define TIME_SYNC_SAMPLES 1 // number of time requests for averaging +#define TIME_SYNC_SAMPLES 3 // number of time requests for averaging #define TIME_SYNC_CYCLE 30 // seconds between two time requests #define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer #define TIME_SYNC_TRIGGER 1.0f // time deviation threshold triggering time sync diff --git a/src/TTN/Nodered-Timeserver.json b/src/TTN/Nodered-Timeserver.json index 2ad5c54e..f2aac01c 100644 --- a/src/TTN/Nodered-Timeserver.json +++ b/src/TTN/Nodered-Timeserver.json @@ -131,7 +131,7 @@ "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 % 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;", + "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, diff --git a/src/timesync.cpp b/src/timesync.cpp index 26267b14..c6f9f5fe 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -19,8 +19,8 @@ static const char TAG[] = __FILE__; TaskHandle_t timeSyncReqTask; time_sync_message_t time_sync_messages[TIME_SYNC_SAMPLES] = {0}, time_sync_answers[TIME_SYNC_SAMPLES] = {0}; -uint8_t time_sync_seqNo = 0; -bool time_sync_pending = false; +static uint8_t time_sync_seqNo = 0; +static bool time_sync_pending = false; // send time request message void send_Servertime_req() { @@ -59,15 +59,18 @@ void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len) { if ((!time_sync_pending) || (buf_len != TIME_SYNC_FRAME_LENGTH)) return; - uint8_t seq_no = buf[0]; - uint32_t timestamp_sec = 0, timestamp_ms = 0; + uint8_t seq_no = buf[0], k = seq_no % TIME_SYNC_SAMPLES; + uint32_t timestamp_sec = 0; - 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]; + for (uint8_t i = 1; i <= 4; i++) { + time_sync_answers[k].seconds = (timestamp_sec <<= 8) |= buf[i]; + } + time_sync_answers[k].fractions = buf[5]; - ESP_LOGD(TAG, "Timeserver answer #%d received: timestamp=%d.%03d", seq_no, - timestamp_sec, timestamp_ms); + ESP_LOGD(TAG, "Timeserver answer:"); + + ESP_LOGD(TAG, "ans.sec(%d)=%d / ans.ms(%d)=%d", k, + time_sync_answers[k].seconds, k, time_sync_answers[k].fractions); // inform processing task if (timeSyncReqTask) @@ -77,11 +80,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) { - 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; + time_t t = 0, time_to_set = 0; + uint32_t seq_no = 0, k = 0; + int time_diff_frac = 0, time_diff_ms = 0; + long time_diff_sec = 0, time_offset = 0; // enqueue timestamp samples in lora sendqueue for (uint8_t i = 1; i <= TIME_SYNC_SAMPLES; i++) { @@ -94,10 +96,12 @@ void process_Servertime_sync_req(void *taskparameter) { payload.addByte(time_sync_seqNo); SendPayload(TIMEPORT, prio_high); + /* -> do we really need this? maybe for SF9 up? // send dummy packet to trigger receive answer - //payload.reset(); - //payload.addByte(0x99); // flush - //SendPayload(RCMDPORT, prio_low); // to 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, @@ -109,20 +113,29 @@ void process_Servertime_sync_req(void *taskparameter) { } // no valid sequence received before timeout else { // calculate time diff from set of collected timestamps - uint8_t k = seq_no % TIME_SYNC_SAMPLES; + + 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_SYNC_SAMPLES); + + time_diff_frac += + ((time_sync_messages[k].fractions - time_sync_answers[k].fractions) / TIME_SYNC_SAMPLES); + + ESP_LOGD(TAG, "time_diff_sec=%d / time_diff_frac=%d", time_diff_sec, + time_diff_frac); } } // for // calculate time offset and set time if necessary - 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); + time_diff_ms = 4 * time_diff_frac; + time_offset = (time_diff_sec + (long) (time_diff_ms / 1000)); - if (time_offset >= TIME_SYNC_TRIGGER) { + ESP_LOGD(TAG, "Timesync time offset=%d", time_offset); + + t = now(); + + if (labs(time_offset) >= (t + TIME_SYNC_TRIGGER)) { // wait until top of second if (time_diff_ms > 0) // clock is fast vTaskDelay(pdMS_TO_TICKS(time_diff_ms)); @@ -130,8 +143,8 @@ void process_Servertime_sync_req(void *taskparameter) { 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); + time_to_set = t - time_t(time_diff_sec); + ESP_LOGD(TAG, "Now()=%d, Time to set = %d", t, time_to_set); // adjust system time if (timeIsValid(time_to_set)) { @@ -156,8 +169,9 @@ finish: // 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; - time_sync_messages[k].fractions = micros / 250; // 4ms resolution + + time_sync_messages[k].seconds = (uint32_t) secs; + time_sync_messages[k].fractions = (uint8_t) (micros / 4000); // 4ms resolution ESP_LOGD(TAG, "Timeserver request #%d sent at %d.%03d", time_sync_seqNo, time_sync_messages[k].seconds, time_sync_messages[k].fractions); From 4a7525efcbdef3e35ffa5c4fade89dd738fcca3c Mon Sep 17 00:00:00 2001 From: Oliver Brandmueller Date: Mon, 11 Mar 2019 18:09:01 +0100 Subject: [PATCH 13/25] replace #ifdef with #if to streamline feature setting --- include/cyclic.h | 6 ++++-- include/globals.h | 10 +++++----- include/irqhandler.h | 2 +- include/main.h | 4 +++- include/ota.h | 2 +- include/rcommand.h | 4 +++- include/senddata.h | 4 +++- include/timekeeper.h | 2 +- platformio.ini | 2 +- src/bme680mems.cpp | 4 ++-- src/cyclic.cpp | 8 ++++---- src/display.cpp | 10 +++++----- src/gpsread.cpp | 2 +- src/hal/lolin32lite.h | 3 ++- src/irqhandler.cpp | 4 ++-- src/led.cpp | 2 +- src/loraconf.sample.h | 2 +- src/lorawan.cpp | 6 ++++-- src/main.cpp | 18 +++++++++--------- src/ota.cpp | 8 ++++---- src/payload.cpp | 18 +++++++++--------- src/rcommand.cpp | 16 +++++++++------- src/senddata.cpp | 12 ++++++------ src/timekeeper.cpp | 6 +++--- 24 files changed, 84 insertions(+), 71 deletions(-) diff --git a/include/cyclic.h b/include/cyclic.h index ffdee8f5..7045cb77 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -5,9 +5,11 @@ #include "senddata.h" #include "rcommand.h" #include "spislave.h" +#if(HAS_LORA) #include +#endif -#ifdef HAS_BME +#if(HAS_BME) #include "bme680mems.h" #endif @@ -19,4 +21,4 @@ uint64_t uptime(void); void reset_counters(void); uint32_t getFreeRAM(); -#endif \ No newline at end of file +#endif diff --git a/include/globals.h b/include/globals.h index 24c83580..b77cf552 100644 --- a/include/globals.h +++ b/include/globals.h @@ -123,15 +123,15 @@ extern time_t userUTCTime; #include "payload.h" #include "blescan.h" -#ifdef HAS_GPS +#if(HAS_GPS) #include "gpsread.h" #endif -#ifdef HAS_LORA +#if(HAS_LORA) #include "lorawan.h" #endif -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) #include "display.h" #endif @@ -147,11 +147,11 @@ extern time_t userUTCTime; #include "antenna.h" #endif -#ifdef HAS_SENSORS +#if(HAS_SENSORS) #include "sensor.h" #endif -#ifdef HAS_BME +#if(HAS_BME) #include "bme680mems.h" #endif diff --git a/include/irqhandler.h b/include/irqhandler.h index ad74f700..5c73410e 100644 --- a/include/irqhandler.h +++ b/include/irqhandler.h @@ -14,7 +14,7 @@ void irqHandler(void *pvParameters); -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) #include "display.h" void IRAM_ATTR DisplayIRQ(); #endif diff --git a/include/main.h b/include/main.h index b60ab7b9..bce12e51 100644 --- a/include/main.h +++ b/include/main.h @@ -16,6 +16,8 @@ #include "irqhandler.h" #include "led.h" #include "spislave.h" +#if(HAS_LORA) #include "lorawan.h" +#endif #include "timekeeper.h" -#endif \ No newline at end of file +#endif diff --git a/include/ota.h b/include/ota.h index 3c6c8318..0371d71c 100644 --- a/include/ota.h +++ b/include/ota.h @@ -18,7 +18,7 @@ void start_ota_update(); int version_compare(const String v1, const String v2); void display(const uint8_t row, const std::string status, const std::string msg); -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) void show_progress(unsigned long current, unsigned long size); #endif diff --git a/include/rcommand.h b/include/rcommand.h index 9eb06384..dc169e1d 100644 --- a/include/rcommand.h +++ b/include/rcommand.h @@ -4,7 +4,9 @@ #include "senddata.h" #include "cyclic.h" #include "configmanager.h" +#if(HAS_LORA) #include "lorawan.h" +#endif #include "macsniff.h" #include #include "cyclic.h" @@ -24,4 +26,4 @@ typedef struct { void rcommand(uint8_t cmd[], uint8_t cmdlength); void do_reset(); -#endif \ No newline at end of file +#endif diff --git a/include/senddata.h b/include/senddata.h index e0eb4ecf..d81b852b 100644 --- a/include/senddata.h +++ b/include/senddata.h @@ -2,7 +2,9 @@ #define _SENDDATA_H #include "spislave.h" +#if(HAS_LORA) #include "lorawan.h" +#endif #include "cyclic.h" extern Ticker sendcycler; @@ -13,4 +15,4 @@ void checkSendQueues(void); void flushQueues(); void sendcycle(void); -#endif // _SENDDATA_H_ \ No newline at end of file +#endif // _SENDDATA_H_ diff --git a/include/timekeeper.h b/include/timekeeper.h index ef83b11b..fc12e4a9 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -6,7 +6,7 @@ #include "TimeLib.h" #include "irqhandler.h" -#ifdef HAS_GPS +#if(HAS_GPS) #include "gpsread.h" #endif #ifdef HAS_IF482 diff --git a/platformio.ini b/platformio.ini index 47ca6e73..599b8e02 100644 --- a/platformio.ini +++ b/platformio.ini @@ -218,7 +218,7 @@ lib_deps = ${common.lib_deps_lora} ${common.lib_deps_display} ${common.lib_deps_rtc} -build_flags = +build_flags = ${common.build_flags_basic} upload_protocol = ${common.upload_protocol} extra_scripts = ${common.extra_scripts} diff --git a/src/bme680mems.cpp b/src/bme680mems.cpp index d0679507..9104a74a 100644 --- a/src/bme680mems.cpp +++ b/src/bme680mems.cpp @@ -1,4 +1,4 @@ -#ifdef HAS_BME +#if(HAS_BME) #include "bme680mems.h" @@ -103,7 +103,7 @@ void bme_loop(void *pvParameters) { configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check -#ifdef HAS_BME +#if(HAS_BME) while (1) { // block i2c bus access if (I2C_MUTEX_LOCK()) { diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 8899d05b..face4564 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -24,7 +24,7 @@ void doHousekeeping() { #ifdef HAS_SPI spi_housekeeping(); #endif -#ifdef HAS_LORA +#if(HAS_LORA) lora_housekeeping(); #endif @@ -32,11 +32,11 @@ void doHousekeeping() { ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(irqHandlerTask), eTaskGetState(irqHandlerTask)); -#ifdef HAS_GPS +#if(HAS_GPS) ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask)); #endif -#ifdef HAS_BME +#if(HAS_BME) ESP_LOGD(TAG, "Bmeloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask)); #endif @@ -58,7 +58,7 @@ void doHousekeeping() { #endif // display BME sensor data -#ifdef HAS_BME +#if(HAS_BME) ESP_LOGI(TAG, "BME680 Temp: %.2f°C | IAQ: %.2f | IAQacc: %d", bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy); #endif diff --git a/src/display.cpp b/src/display.cpp index 80d206e7..96adebf9 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -1,4 +1,4 @@ -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) /* @@ -105,7 +105,7 @@ void init_display(const char *Productname, const char *Version) { u8x8.print(" v"); u8x8.println(PROGVERSION); -#ifdef HAS_LORA +#if(HAS_LORA) u8x8.println("DEVEUI:"); os_getDevEui((u1_t *)buf); DisplayKey(buf, 8, true); @@ -163,7 +163,7 @@ void refreshtheDisplay() { #endif // update GPS status (line 2) -#ifdef HAS_GPS +#if(HAS_GPS) // have we ever got valid gps data? if (gps.passedChecksum() > 0) { u8x8.setCursor(9, 2); @@ -186,7 +186,7 @@ void refreshtheDisplay() { u8x8.printf("%s", "BLTH:off"); #endif -#ifdef HAS_LORA +#if(HAS_LORA) u8x8.setCursor(11, 3); u8x8.printf("SF:"); if (cfg.adrmode) // if ADR=on then display SF value inverse @@ -209,7 +209,7 @@ void refreshtheDisplay() { u8x8.setCursor(10, 5); u8x8.printf("%4dKB", getFreeRAM() / 1024); -#ifdef HAS_LORA +#if(HAS_LORA) u8x8.setCursor(0, 6); #if (!defined HAS_DCF77) && (!defined HAS_IF482) // update LoRa status display (line 6) diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 59739bf4..27cc0eec 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -1,4 +1,4 @@ -#ifdef HAS_GPS +#if(HAS_GPS) #include "globals.h" diff --git a/src/hal/lolin32lite.h b/src/hal/lolin32lite.h index 3995fac7..70dfbcdf 100644 --- a/src/hal/lolin32lite.h +++ b/src/hal/lolin32lite.h @@ -7,8 +7,9 @@ // Hardware related definitions for lolin32lite (without LoRa shield) +#define HAS_LORA 0 // no LoRa module #define CFG_sx1272_radio 1 // dummy #define HAS_LED LED_BUILTIN // on board LED on GPIO5 #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW -#endif \ No newline at end of file +#endif diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index 0757430b..28725b1e 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -24,7 +24,7 @@ void irqHandler(void *pvParameters) { #endif // display needs refresh? -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) if (InterruptStatus & DISPLAY_IRQ) refreshtheDisplay(); #endif @@ -49,7 +49,7 @@ void irqHandler(void *pvParameters) { // esp32 hardware timer triggered interrupt service routines // they notify the irq handler task -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) void IRAM_ATTR DisplayIRQ() { BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; diff --git a/src/led.cpp b/src/led.cpp index a52315c5..edaa135a 100644 --- a/src/led.cpp +++ b/src/led.cpp @@ -137,7 +137,7 @@ void ledLoop(void *parameter) { // No custom blink, check LoRaWAN state } else { -#ifdef HAS_LORA +#if(HAS_LORA) // LED indicators for viusalizing LoRaWAN state if (LMIC.opmode & (OP_JOINING | OP_REJOIN)) { LEDColor = COLOR_YELLOW; diff --git a/src/loraconf.sample.h b/src/loraconf.sample.h index 3ad10a0d..5e60c0a5 100644 --- a/src/loraconf.sample.h +++ b/src/loraconf.sample.h @@ -1,4 +1,4 @@ -#ifdef HAS_LORA +#if(HAS_LORA) /************************************************************ * LMIC LoRaWAN configuration diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 04fb0fb6..fd0398fa 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -1,10 +1,12 @@ // Basic Config +#if(HAS_LORA) #include "lorawan.h" +#endif // Local logging Tag static const char TAG[] = "lora"; -#ifdef HAS_LORA +#if(HAS_LORA) #if CLOCK_ERROR_PROCENTAGE > 7 #warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects @@ -479,4 +481,4 @@ void user_request_network_time_callback(void *pVoidUserUTCTime, ESP_LOGI(TAG, "Invalid time received from LoRa"); } // user_request_network_time_callback -#endif // HAS_LORA \ No newline at end of file +#endif // HAS_LORA diff --git a/src/main.cpp b/src/main.cpp index 0b44c850..b89525a3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -155,7 +155,7 @@ void setup() { ESP.getFlashChipSpeed()); ESP_LOGI(TAG, "Wifi/BT software coexist version %s", esp_coex_version_get()); -#ifdef HAS_LORA +#if(HAS_LORA) ESP_LOGI(TAG, "IBM LMIC version %d.%d.%d", LMIC_VERSION_MAJOR, LMIC_VERSION_MINOR, LMIC_VERSION_BUILD); ESP_LOGI(TAG, "Arduino LMIC version %d.%d.%d.%d", @@ -167,7 +167,7 @@ void setup() { showLoraKeys(); #endif // HAS_LORA -#ifdef HAS_GPS +#if(HAS_GPS) ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); #endif @@ -269,7 +269,7 @@ void setup() { #endif // HAS_BUTTON // initialize gps -#ifdef HAS_GPS +#if(HAS_GPS) strcat_P(features, " GPS"); if (gps_init()) { ESP_LOGI(TAG, "Starting GPS Feed..."); @@ -284,13 +284,13 @@ void setup() { #endif // initialize sensors -#ifdef HAS_SENSORS +#if(HAS_SENSORS) strcat_P(features, " SENS"); sensor_init(); #endif // initialize LoRa -#ifdef HAS_LORA +#if(HAS_LORA) strcat_P(features, " LORA"); assert(lora_stack_init() == ESP_OK); #endif @@ -306,7 +306,7 @@ void setup() { #endif // initialize display -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) strcat_P(features, " OLED"); DisplayState = cfg.screenon; init_display(PRODUCTNAME, PROGVERSION); // note: blocking call @@ -359,7 +359,7 @@ void setup() { 1); // CPU core // initialize bme -#ifdef HAS_BME +#if(HAS_BME) strcat_P(features, " BME"); if (bme_init()) { ESP_LOGI(TAG, "Starting BME sensor..."); @@ -378,7 +378,7 @@ void setup() { ESP_LOGI(TAG, "Starting Timers..."); // display interrupt -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up displayIRQ = timerBegin(0, 80, true); @@ -421,7 +421,7 @@ void setup() { void loop() { while (1) { -#ifdef HAS_LORA +#if(HAS_LORA) os_runloop_once(); // execute lmic scheduled jobs and events #endif delay(2); // yield to CPU diff --git a/src/ota.cpp b/src/ota.cpp index a2623702..a7c3f326 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -48,7 +48,7 @@ void start_ota_update() { switch_LED(LED_ON); -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) u8x8.begin(); u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.clear(); @@ -266,7 +266,7 @@ int do_ota_update() { goto abort; } -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) // register callback function for showing progress while streaming data Update.onProgress(&show_progress); #endif @@ -309,7 +309,7 @@ retry: void display(const uint8_t row, const std::string status, const std::string msg) { -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) u8x8.setCursor(14, row); u8x8.print((status.substr(0, 2)).c_str()); if (!msg.empty()) { @@ -320,7 +320,7 @@ void display(const uint8_t row, const std::string status, #endif } -#ifdef HAS_DISPLAY +#if(HAS_DISPLAY) // callback function to show download progress while streaming data void show_progress(unsigned long current, unsigned long size) { char buf[17]; diff --git a/src/payload.cpp b/src/payload.cpp index 45d7e6e3..a5990d26 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -78,7 +78,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp, } void PayloadConvert::addGPS(gpsStatus_t value) { -#ifdef HAS_GPS +#if(HAS_GPS) buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24); buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16); buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8); @@ -96,7 +96,7 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#ifdef HAS_SENSORS +#if(HAS_SENSORS) uint8_t length = buf[0]; memcpy(buffer, buf + 1, length); cursor += length; // length of buffer @@ -104,7 +104,7 @@ void PayloadConvert::addSensor(uint8_t buf[]) { } void PayloadConvert::addBME(bmeStatus_t value) { -#ifdef HAS_BME +#if(HAS_BME) int16_t temperature = (int16_t)(value.temperature); // float -> int uint16_t humidity = (uint16_t)(value.humidity); // float -> int uint16_t pressure = (uint16_t)(value.pressure); // float -> int @@ -187,7 +187,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp, } void PayloadConvert::addGPS(gpsStatus_t value) { -#ifdef HAS_GPS +#if(HAS_GPS) writeLatLng(value.latitude, value.longitude); writeUint8(value.satellites); writeUint16(value.hdop); @@ -196,7 +196,7 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#ifdef HAS_SENSORS +#if(HAS_SENSORS) uint8_t length = buf[0]; memcpy(buffer, buf + 1, length); cursor += length; // length of buffer @@ -204,7 +204,7 @@ void PayloadConvert::addSensor(uint8_t buf[]) { } void PayloadConvert::addBME(bmeStatus_t value) { -#ifdef HAS_BME +#if(HAS_BME) writeFloat(value.temperature); writePressure(value.pressure); writeUFloat(value.humidity); @@ -375,7 +375,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius, } void PayloadConvert::addGPS(gpsStatus_t value) { -#ifdef HAS_GPS +#if(HAS_GPS) int32_t lat = value.latitude / 100; int32_t lon = value.longitude / 100; int32_t alt = value.altitude * 100; @@ -396,7 +396,7 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#ifdef HAS_SENSORS +#if(HAS_SENSORS) // to come /* uint8_t length = buf[0]; @@ -407,7 +407,7 @@ void PayloadConvert::addSensor(uint8_t buf[]) { } void PayloadConvert::addBME(bmeStatus_t value) { -#ifdef HAS_BME +#if(HAS_BME) // data value conversions to meet cayenne data type definition // 0.1°C per bit => -3276,7 .. +3276,7 °C diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 34054bc2..727b4265 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -8,7 +8,9 @@ static const char TAG[] = __FILE__; // helper function void do_reset() { ESP_LOGI(TAG, "Remote command: restart device"); +#if(HAS_LORA) LMIC_shutdown(); +#endif delay(3000); esp_restart(); } @@ -130,7 +132,7 @@ void set_gps(uint8_t val[]) { } void set_sensor(uint8_t val[]) { -#ifdef HAS_SENSORS +#if(HAS_SENSORS) switch (val[0]) { // check if valid sensor number 1...4 case 1: case 2: @@ -168,7 +170,7 @@ void set_monitor(uint8_t val[]) { } void set_lorasf(uint8_t val[]) { -#ifdef HAS_LORA +#if(HAS_LORA) ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val[0]); switch_lora(val[0], cfg.txpower); #else @@ -177,7 +179,7 @@ void set_lorasf(uint8_t val[]) { } void set_loraadr(uint8_t val[]) { -#ifdef HAS_LORA +#if(HAS_LORA) ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s", val[0] ? "on" : "off"); cfg.adrmode = val[0] ? 1 : 0; @@ -220,7 +222,7 @@ void set_rgblum(uint8_t val[]) { }; void set_lorapower(uint8_t val[]) { -#ifdef HAS_LORA +#if(HAS_LORA) ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", val[0]); switch_lora(cfg.lorasf, val[0]); #else @@ -250,7 +252,7 @@ void get_status(uint8_t val[]) { void get_gps(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get gps status"); -#ifdef HAS_GPS +#if(HAS_GPS) gps_read(); payload.reset(); payload.addGPS(gps_status); @@ -262,7 +264,7 @@ void get_gps(uint8_t val[]) { void get_bme(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get bme680 sensor data"); -#ifdef HAS_BME +#if(HAS_BME) payload.reset(); payload.addBME(bme_status); SendPayload(BMEPORT, prio_high); @@ -355,4 +357,4 @@ void rcommand(uint8_t cmd[], uint8_t cmdlength) { if (storeflag) saveConfig(); -} // rcommand() \ No newline at end of file +} // rcommand() diff --git a/src/senddata.cpp b/src/senddata.cpp index 36e8b278..816ee7c7 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -39,7 +39,7 @@ void SendPayload(uint8_t port, sendprio_t prio) { memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize()); // enqueue message in device's send queues -#ifdef HAS_LORA +#if(HAS_LORA) lora_enqueuedata(&SendBuffer, prio); #endif #ifdef HAS_SPI @@ -63,7 +63,7 @@ void sendCounter() { if (cfg.blescan) payload.addCount(macs_ble, MAC_SNIFF_BLE); -#ifdef HAS_GPS +#if(HAS_GPS) if (gps.location.isValid()) { // send GPS position only if we have a fix gps_read(); payload.addGPS(gps_status); @@ -83,7 +83,7 @@ void sendCounter() { } break; -#ifdef HAS_BME +#if(HAS_BME) case MEMS_DATA: payload.reset(); payload.addBME(bme_status); @@ -91,7 +91,7 @@ void sendCounter() { break; #endif -#ifdef HAS_GPS +#if(HAS_GPS) case GPS_DATA: // send GPS position only if we have a fix if (gps.location.isValid()) { @@ -104,7 +104,7 @@ void sendCounter() { break; #endif -#ifdef HAS_SENSORS +#if(HAS_SENSORS) case SENSOR1_DATA: payload.reset(); payload.addSensor(sensor_read(1)); @@ -138,7 +138,7 @@ void sendCounter() { } // sendCounter() void flushQueues() { -#ifdef HAS_LORA +#if(HAS_LORA) lora_queuereset(); #endif #ifdef HAS_SPI diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 21259b21..bd33f7e6 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -14,7 +14,7 @@ time_t timeProvider(void) { time_t t = 0; -#ifdef HAS_GPS +#if(HAS_GPS) t = get_gpstime(); // fetch recent time from last NEMA record if (t) { #ifdef HAS_RTC @@ -39,7 +39,7 @@ time_t timeProvider(void) { #if(DBTIMESYNC) send_DBtime_req(); // kick off asychronous lora sync if we have -#elif defined HAS_LORA && (TIME_SYNC_LORA) +#elif (HAS_LORA) && (TIME_SYNC_LORA) LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); #endif @@ -237,4 +237,4 @@ void clock_loop(void *taskparameter) { // ClockTask } // for } // clock_loop() -#endif // HAS_IF482 || defined HAS_DCF77 \ No newline at end of file +#endif // HAS_IF482 || defined HAS_DCF77 From edb2c822c8341d2cf42d31236d9119a0e424c73a Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Tue, 12 Mar 2019 23:50:02 +0100 Subject: [PATCH 14/25] timeserver (experimental) --- include/globals.h | 2 +- include/timesync.h | 19 +-- lib/microTime/src/{Time.cpp => microTime.cpp} | 0 lib/microTime/src/{Time.h => microTime.h} | 0 src/lorawan.cpp | 26 ++-- src/timekeeper.cpp | 6 +- src/timesync.cpp | 137 +++++++++--------- 7 files changed, 97 insertions(+), 93 deletions(-) rename lib/microTime/src/{Time.cpp => microTime.cpp} (100%) rename lib/microTime/src/{Time.h => microTime.h} (100%) diff --git a/include/globals.h b/include/globals.h index 24c83580..309b1734 100644 --- a/include/globals.h +++ b/include/globals.h @@ -5,7 +5,7 @@ #include // Time functions -#include +#include "microTime.h" #include #include #include diff --git a/include/timesync.h b/include/timesync.h index 866f5a4c..bf958abd 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -1,24 +1,21 @@ #ifndef _TIME_SYNC_TIMESERVER_H #define _TIME_SYNC_TIMESERVER_H +#include +#include #include "globals.h" #include "timesync.h" #include "timekeeper.h" #define TIME_SYNC_SAMPLES 3 // number of time requests for averaging -#define TIME_SYNC_CYCLE 30 // seconds between two time requests -#define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer -#define TIME_SYNC_TRIGGER 1.0f // time deviation threshold triggering time sync +#define TIME_SYNC_CYCLE 20 // seconds between two time requests +#define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer +#define TIME_SYNC_TRIGGER 1 // time deviation threshold triggering time sync #define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length -typedef struct { - uint32_t seconds; - uint8_t fractions; // 1/250ths second = 4 milliseconds resolution -} time_sync_message_t; - -void send_Servertime_req(void); -void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len); -void process_Servertime_sync_req(void *taskparameter); +void send_timesync_req(void); +void recv_timesync_ans(uint8_t buf[], uint8_t buf_len); +void process_timesync_req(void *taskparameter); void store_time_sync_req(time_t secs, uint32_t micros); #endif \ No newline at end of file diff --git a/lib/microTime/src/Time.cpp b/lib/microTime/src/microTime.cpp similarity index 100% rename from lib/microTime/src/Time.cpp rename to lib/microTime/src/microTime.cpp diff --git a/lib/microTime/src/Time.h b/lib/microTime/src/microTime.h similarity index 100% rename from lib/microTime/src/Time.h rename to lib/microTime/src/microTime.h diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 7d2cc7c2..8b08b3c1 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -175,7 +175,7 @@ void showLoraKeys(void) { void onEvent(ev_t ev) { char buff[24] = ""; - uint32_t now_micros; + uint32_t now_micros = 0; switch (ev) { @@ -238,21 +238,27 @@ void onEvent(ev_t ev) { : PSTR("TX_COMPLETE")); sprintf(display_line6, " "); // clear previous lmic status - if (LMIC.dataLen) { + if (LMIC.dataLen) { // did we receive 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); - // check if this is a timesync answer, then call timesync processor + if (LMIC.txrxFlags & TXRX_PORT) { // FPort -> use to switch + switch (LMIC.frame[LMIC.dataBeg - 1]) { #if (TIME_SYNC_TIMESERVER) - if ((LMIC.txrxFlags & TXRX_PORT) && - (LMIC.frame[LMIC.dataBeg - 1] == TIMEPORT)) - recv_Servertime_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); + case TIMEPORT: // timesync answer -> call timesync processor + recv_timesync_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); + break; #endif - // check if this an opcode, then call rcommand interpreter - if ((LMIC.txrxFlags & TXRX_PORT) && - (LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT)) - rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); + case RCMDPORT: // opcode -> call rcommand interpreter + rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); + break; + default: // unknown port -> display info + ESP_LOGI(TAG, "Received data on unsupported port #%d", + LMIC.frame[LMIC.dataBeg - 1]); + break; + } + } } break; diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 2a10e862..1b9a151a 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -45,7 +45,7 @@ time_t timeProvider(void) { // kick off asychronous Lora timeserver timesync if we have #if (TIME_SYNC_TIMESERVER) - send_Servertime_req(); + send_timesync_req(); // kick off asychronous lora network sync if we have #elif (TIME_SYNC_LORAWAN) LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); @@ -116,7 +116,7 @@ void timepulse_start(void) { void IRAM_ATTR CLOCKIRQ(void) { BaseType_t xHigherPriorityTaskWoken; - SyncToPPS(); // calibrates UTC systime, see Time.h + SyncToPPS(); // calibrates UTC systime, see microTime.h xHigherPriorityTaskWoken = pdFALSE; if (ClockTask != NULL) @@ -149,7 +149,7 @@ time_t compiledUTC(void) { time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, uint8_t ss) { tmElements_t tm; - tm.Year = CalendarYrToTm(YYYY); // year offset from 1970 in time.h + tm.Year = CalendarYrToTm(YYYY); // year offset from 1970 in microTime.h tm.Month = MM; tm.Day = DD; tm.Hour = hh; diff --git a/src/timesync.cpp b/src/timesync.cpp index c6f9f5fe..1c2d772e 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -17,60 +17,67 @@ algorithm in applications without granted license by the patent holder. static const char TAG[] = __FILE__; TaskHandle_t timeSyncReqTask; -time_sync_message_t time_sync_messages[TIME_SYNC_SAMPLES] = {0}, - time_sync_answers[TIME_SYNC_SAMPLES] = {0}; + static uint8_t time_sync_seqNo = 0; -static bool time_sync_pending = false; +static bool lora_time_sync_pending = false; + +typedef std::chrono::system_clock myClock; +typedef myClock::time_point myClock_timepoint; +typedef std::chrono::duration> + myClock_msecTick; +// 32bit millisec resolution from epoch until year 2038 +myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES]; +myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES]; // send time request message -void send_Servertime_req() { +void send_timesync_req() { // if a timesync handshake is pending then exit - if (time_sync_pending) { + if (lora_time_sync_pending) { ESP_LOGI(TAG, "Timeserver sync request already pending"); return; } else { ESP_LOGI(TAG, "Timeserver sync request started"); - time_sync_pending = true; + lora_time_sync_pending = true; // clear timestamp array - for (uint8_t i = 0; i <= TIME_SYNC_SAMPLES + 1; i++) { - time_sync_messages[i].seconds = time_sync_answers[i].seconds = - time_sync_messages[i].fractions = time_sync_answers[i].fractions = 0; + for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { + time_sync_tx[i] = time_sync_rx[i] = myClock_timepoint(); // set to epoch } // kick off temporary task for timeserver handshake processing if (!timeSyncReqTask) - xTaskCreatePinnedToCore(process_Servertime_sync_req, // task function - "timesync_req", // name of task - 2048, // stack size of task - (void *)1, // task parameter - 0, // priority of the task - &timeSyncReqTask, // task handle - 1); // CPU core + xTaskCreatePinnedToCore(process_timesync_req, // task function + "timesync_req", // name of task + 2048, // stack size of task + (void *)1, // task parameter + 0, // priority of the task + &timeSyncReqTask, // task handle + 1); // CPU core } } -// process timeserver timestamp response, called from rcommand.cpp -void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len) { +// process timeserver timestamp answer, called from lorawan.cpp +void recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { - // if no timesync handshake is pending or invalid buffer then exit - if ((!time_sync_pending) || (buf_len != TIME_SYNC_FRAME_LENGTH)) + // if no timesync handshake is pending or spurious buffer then exit + if ((!lora_time_sync_pending) || (buf_len != TIME_SYNC_FRAME_LENGTH)) return; uint8_t seq_no = buf[0], k = seq_no % TIME_SYNC_SAMPLES; - uint32_t timestamp_sec = 0; + 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++) { - time_sync_answers[k].seconds = (timestamp_sec <<= 8) |= buf[i]; + timestamp_sec = (tmp_sec <<= 8) |= buf[i]; } - time_sync_answers[k].fractions = buf[5]; - ESP_LOGD(TAG, "Timeserver answer:"); + time_sync_rx[k] += std::chrono::seconds(timestamp_sec) + + std::chrono::milliseconds(timestamp_msec); - ESP_LOGD(TAG, "ans.sec(%d)=%d / ans.ms(%d)=%d", k, - time_sync_answers[k].seconds, k, time_sync_answers[k].fractions); + ESP_LOGD(TAG, "Timesync answer #%d rcvd at %d", seq_no, + myClock::to_time_t(time_sync_rx[k])); // inform processing task if (timeSyncReqTask) @@ -78,15 +85,15 @@ void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len) { } // task for sending time sync requests -void process_Servertime_sync_req(void *taskparameter) { +void process_timesync_req(void *taskparameter) { - time_t t = 0, time_to_set = 0; - uint32_t seq_no = 0, k = 0; - int time_diff_frac = 0, time_diff_ms = 0; - long time_diff_sec = 0, time_offset = 0; + time_t time_to_set = 0; + uint8_t k = 0, i = 0; + uint32_t seq_no = 0; + auto time_offset = myClock_msecTick::zero(); // enqueue timestamp samples in lora sendqueue - for (uint8_t i = 1; i <= TIME_SYNC_SAMPLES; i++) { + 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; @@ -96,55 +103,48 @@ void process_Servertime_sync_req(void *taskparameter) { payload.addByte(time_sync_seqNo); SendPayload(TIMEPORT, prio_high); - /* -> do we really need this? maybe for SF9 up? - // send dummy packet to trigger receive 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) || + 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 set of collected timestamps - + else { // calculate time diff from collected timestamps k = seq_no % TIME_SYNC_SAMPLES; - time_diff_sec += - ((time_sync_messages[k].seconds - time_sync_answers[k].seconds) / TIME_SYNC_SAMPLES); + auto t_tx = std::chrono::time_point_cast( + time_sync_tx[k]); // timepoint node after TX_completed + auto t_rx = std::chrono::time_point_cast( + time_sync_rx[k]); // timepoint when message was seen on gateway - time_diff_frac += - ((time_sync_messages[k].fractions - time_sync_answers[k].fractions) / TIME_SYNC_SAMPLES); + time_offset += t_rx - t_tx; - ESP_LOGD(TAG, "time_diff_sec=%d / time_diff_frac=%d", time_diff_sec, - time_diff_frac); + if (i < TIME_SYNC_SAMPLES - 1) + vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); } } // for - // calculate time offset and set time if necessary - time_diff_ms = 4 * time_diff_frac; - time_offset = (time_diff_sec + (long) (time_diff_ms / 1000)); + // calculate time offset from collected diffs and set time if necessary + time_offset /= TIME_SYNC_SAMPLES; + ESP_LOGD(TAG, "Avg time diff: %lldms", time_offset.count()); - ESP_LOGD(TAG, "Timesync time offset=%d", time_offset); + if (abs(time_offset.count()) >= TIME_SYNC_TRIGGER) { - t = now(); - - if (labs(time_offset) >= (t + TIME_SYNC_TRIGGER)) { + /* // wait until top of second - if (time_diff_ms > 0) // clock is fast + if (time_offset_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)); + else if (time_offset_ms < 0) // clock is slow + vTaskDelay(pdMS_TO_TICKS(1000 + time_offset_ms)); - time_diff_sec++; - time_to_set = t - time_t(time_diff_sec); - ESP_LOGD(TAG, "Now()=%d, Time to set = %d", t, time_to_set); + time_to_set = t - time_t(time_offset_sec + 1); + */ + + time_t time_to_set = myClock::to_time_t(myClock::now() + time_offset); + ESP_LOGD(TAG, "New UTC epoch time: %d", time_to_set); // adjust system time if (timeIsValid(time_to_set)) { @@ -161,20 +161,21 @@ void process_Servertime_sync_req(void *taskparameter) { finish: - time_sync_pending = false; + 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 secs, uint32_t micros) { +void store_time_sync_req(time_t t_sec, uint32_t t_microsec) { + uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES; - time_sync_messages[k].seconds = (uint32_t) secs; - time_sync_messages[k].fractions = (uint8_t) (micros / 4000); // 4ms resolution + time_sync_tx[k] += + std::chrono::seconds(t_sec) + std::chrono::microseconds(t_microsec); - ESP_LOGD(TAG, "Timeserver request #%d sent at %d.%03d", time_sync_seqNo, - time_sync_messages[k].seconds, time_sync_messages[k].fractions); + 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 cc13a38e5661d732674b16d2d55e3d6d0c5faea6 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Tue, 12 Mar 2019 23:54:58 +0100 Subject: [PATCH 15/25] LoRa timesync (first working version) --- include/timesync.h | 1 - platformio.ini | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index bf958abd..79b40fbb 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -1,7 +1,6 @@ #ifndef _TIME_SYNC_TIMESERVER_H #define _TIME_SYNC_TIMESERVER_H -#include #include #include "globals.h" #include "timesync.h" diff --git a/platformio.ini b/platformio.ini index 32d4b9d7..b2e7cbfc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,7 +30,7 @@ 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.37 +release_version = 1.7.38 ; 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 @@ -55,7 +55,6 @@ lib_deps_rtc = RTC@^2.3.0 lib_deps_basic = ArduinoJson@^5.13.1 - Time@>=1.5 Timezone@^1.2.2 lib_deps_all = ${common.lib_deps_basic} From 5f8b03716210dd0f679af007751f117d8c59b7fe Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 13 Mar 2019 12:38:57 +0100 Subject: [PATCH 16/25] Update heltecv2.h --- src/hal/heltecv2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hal/heltecv2.h b/src/hal/heltecv2.h index 7cb9145c..c351ef68 100644 --- a/src/hal/heltecv2.h +++ b/src/hal/heltecv2.h @@ -18,8 +18,8 @@ #define HAS_BUTTON KEY_BUILTIN // button "PROG" on board // Pins for I2C interface of OLED Display -#define MY_OLED_SDA (4) -#define MY_OLED_SCL (15) +#define MY_OLED_SDA (21) +#define MY_OLED_SCL (22) #define MY_OLED_RST (16) // Pins for LORA chip SPI interface come from board file, we need some From 3cc7e567b7e72929fb283a3dd660c7eaa18b863b Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 13 Mar 2019 12:39:16 +0100 Subject: [PATCH 17/25] Update heltec.h --- src/hal/heltec.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hal/heltec.h b/src/hal/heltec.h index f8ab5b8e..3aefdcc7 100644 --- a/src/hal/heltec.h +++ b/src/hal/heltec.h @@ -18,8 +18,8 @@ #define HAS_BUTTON KEY_BUILTIN // button "PROG" on board // Pins for I2C interface of OLED Display -#define MY_OLED_SDA (21) -#define MY_OLED_SCL (22) +#define MY_OLED_SDA (4) +#define MY_OLED_SCL (15) #define MY_OLED_RST (16) // Pins for LORA chip SPI interface come from board file, we need some From 45f33fccd223b410e052166e7ca90fb0b665dccf Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 13 Mar 2019 12:48:38 +0100 Subject: [PATCH 18/25] Update heltecv2.h --- src/hal/heltecv2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hal/heltecv2.h b/src/hal/heltecv2.h index c351ef68..7cb9145c 100644 --- a/src/hal/heltecv2.h +++ b/src/hal/heltecv2.h @@ -18,8 +18,8 @@ #define HAS_BUTTON KEY_BUILTIN // button "PROG" on board // Pins for I2C interface of OLED Display -#define MY_OLED_SDA (21) -#define MY_OLED_SCL (22) +#define MY_OLED_SDA (4) +#define MY_OLED_SCL (15) #define MY_OLED_RST (16) // Pins for LORA chip SPI interface come from board file, we need some From 3b3b2810cbef373c5bbc9dcd44345136bf3ee8f0 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 13 Mar 2019 20:20:19 +0100 Subject: [PATCH 19/25] timesync fixes --- src/lorawan.cpp | 4 ++-- src/timesync.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 8b08b3c1..205758b6 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -239,9 +239,9 @@ void onEvent(ev_t ev) { sprintf(display_line6, " "); // clear previous lmic status if (LMIC.dataLen) { // did we receive data -> display info - ESP_LOGI(TAG, "Received %d bytes of payload, RSSI -%d SNR %d", + 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); + sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi, LMIC.snr / 4); if (LMIC.txrxFlags & TXRX_PORT) { // FPort -> use to switch switch (LMIC.frame[LMIC.dataBeg - 1]) { diff --git a/src/timesync.cpp b/src/timesync.cpp index 1c2d772e..8ee7e381 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -25,7 +25,7 @@ typedef std::chrono::system_clock myClock; typedef myClock::time_point myClock_timepoint; typedef std::chrono::duration> myClock_msecTick; -// 32bit millisec resolution from epoch until year 2038 + myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES]; myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES]; @@ -76,7 +76,7 @@ void recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { time_sync_rx[k] += std::chrono::seconds(timestamp_sec) + std::chrono::milliseconds(timestamp_msec); - ESP_LOGD(TAG, "Timesync answer #%d rcvd at %d", seq_no, + ESP_LOGD(TAG, "Timesync request #%d rcvd at %d", seq_no, myClock::to_time_t(time_sync_rx[k])); // inform processing task @@ -116,13 +116,13 @@ void process_timesync_req(void *taskparameter) { k = seq_no % TIME_SYNC_SAMPLES; auto t_tx = std::chrono::time_point_cast( - time_sync_tx[k]); // timepoint node after TX_completed + time_sync_tx[k]); // timepoint when node TX_completed auto t_rx = std::chrono::time_point_cast( time_sync_rx[k]); // timepoint when message was seen on gateway - time_offset += t_rx - t_tx; + time_offset += t_rx - t_tx; // cumulate timepoint diffs - if (i < TIME_SYNC_SAMPLES - 1) + if (i < TIME_SYNC_SAMPLES - 1) // wait until next cycle vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); } } // for From f58cad9b57ea438e87923453e4ff27895031e2fe Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 13 Mar 2019 20:20:31 +0100 Subject: [PATCH 20/25] heltec board file fixes --- src/hal/heltec.h | 12 ++++++------ src/hal/heltecv2.h | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/hal/heltec.h b/src/hal/heltec.h index f8ab5b8e..3695e56c 100644 --- a/src/hal/heltec.h +++ b/src/hal/heltec.h @@ -5,9 +5,9 @@ #include -// Hardware related definitions for Heltec V2 LoRa-32 Board +// Hardware related definitions for Heltec V1 LoRa-32 Board -//#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL +//#define HAS_BME GPIO_NUM_4, GPIO_NUM_15 // SDA, SCL //#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND #define HAS_LORA 1 // comment out if device shall not send data via LoRa @@ -18,13 +18,13 @@ #define HAS_BUTTON KEY_BUILTIN // button "PROG" on board // Pins for I2C interface of OLED Display -#define MY_OLED_SDA (21) -#define MY_OLED_SCL (22) +#define MY_OLED_SDA (4) +#define MY_OLED_SCL (15) #define MY_OLED_RST (16) // Pins for LORA chip SPI interface come from board file, we need some // additional definitions for LMIC #define LORA_IO1 (33) -#define LORA_IO2 LMIC_UNUSED_PIN +#define LORA_IO2 (32) -#endif +#endif \ No newline at end of file diff --git a/src/hal/heltecv2.h b/src/hal/heltecv2.h index 7cb9145c..d146285a 100644 --- a/src/hal/heltecv2.h +++ b/src/hal/heltecv2.h @@ -17,6 +17,10 @@ #define HAS_LED LED_BUILTIN // white LED on board #define HAS_BUTTON KEY_BUILTIN // button "PROG" on board +//#define HAS_BATTERY_PROBE ADC2_GPIO13_CHANNEL // battery probe GPIO pin +#define BATT_FACTOR 4 // voltage divider 220k/100k on board +#define HAS_LOWPOWER_SWITCH GPIO_NUM_21 // switches battery power + // Pins for I2C interface of OLED Display #define MY_OLED_SDA (4) #define MY_OLED_SCL (15) @@ -27,4 +31,4 @@ #define LORA_IO1 (35) #define LORA_IO2 (34) -#endif +#endif \ No newline at end of file From 408c09db8041245c64fb6c1fd4d34bc25c5e6d28 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 13 Mar 2019 20:21:46 +0100 Subject: [PATCH 21/25] platformio.ini defaults --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index b2e7cbfc..82ddd98a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -;env_default = generic +env_default = generic ;env_default = ebox ;env_default = eboxtube ;env_default = heltec @@ -15,7 +15,7 @@ ;env_default = ttgov2 ;env_default = ttgov21old ;env_default = ttgov21new -env_default = ttgobeam +;env_default = ttgobeam ;env_default = ttgofox ;env_default = lopy ;env_default = lopy4 From bfa28d413952d6ce9ffe91626d1904869928af6e Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 13 Mar 2019 21:15:28 +0100 Subject: [PATCH 22/25] BME280 integration (PR #309) --- README.md | 2 +- include/bme280.h | 17 -------- include/{bme680mems.h => bmesensor.h} | 10 ++++- include/cyclic.h | 8 +--- include/globals.h | 8 +--- platformio.ini | 9 ++-- src/bme280.cpp | 63 --------------------------- src/{bme680mems.cpp => bmesensor.cpp} | 54 +++++++++++++++++++++-- src/cyclic.cpp | 6 +-- src/hal/generic.h | 3 ++ src/hal/ttgov21new.h | 7 ++- src/main.cpp | 35 +++++---------- src/rcommand.cpp | 4 +- 13 files changed, 91 insertions(+), 135 deletions(-) delete mode 100644 include/bme280.h rename include/{bme680mems.h => bmesensor.h} (94%) delete mode 100644 src/bme280.cpp rename src/{bme680mems.cpp => bmesensor.cpp} (79%) diff --git a/README.md b/README.md index 07ead53b..9ba51050 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Depending on board hardware following features are supported: - Silicon unique ID - Battery voltage monitoring - GPS (Generic serial NMEA, or Quectel L76 I2C) -- Environmental sensor (Bosch BME680 I2C) +- Environmental sensor (Bosch BME280/BME680 I2C) - Real Time Clock (Maxim DS3231 I2C) - IF482 (serial) and DCF77 (gpio) time telegram generator diff --git a/include/bme280.h b/include/bme280.h deleted file mode 100644 index c4c5c4a8..00000000 --- a/include/bme280.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _BME280_H -#define _BME280_H - -#include "globals.h" -#include -#include -#include -#include "../lib/Bosch-BSEC/src/bsec.h" - -extern bmeStatus_t - bme_status; // Make struct for storing gps data globally available -extern TaskHandle_t Bme280Task; - -int bme280_init(); -void bme280_loop(void *pvParameters); - -#endif \ No newline at end of file diff --git a/include/bme680mems.h b/include/bmesensor.h similarity index 94% rename from include/bme680mems.h rename to include/bmesensor.h index a97e0605..fb7d66b9 100644 --- a/include/bme680mems.h +++ b/include/bmesensor.h @@ -1,9 +1,15 @@ -#ifndef _BME680MEMS_H -#define _BME680MEMS_H +#ifndef _BMESENSOR_H +#define _BMESENSOR_H #include "globals.h" #include + +#ifdef HAS_BME680 #include "../lib/Bosch-BSEC/src/bsec.h" +#elif defined HAS_BME280 +#include +#include +#endif extern bmeStatus_t bme_status; // Make struct for storing gps data globally available diff --git a/include/cyclic.h b/include/cyclic.h index 77853bb6..6cdf37ca 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -7,12 +7,8 @@ #include "spislave.h" #include -#ifdef HAS_BME680 -#include "bme680mems.h" -#endif - -#ifdef HAS_BME280 -#include "bme280.h" +#if (HAS_BME) +#include "bmesensor.h" #endif extern Ticker housekeeper; diff --git a/include/globals.h b/include/globals.h index b9ff9175..6f9a43d2 100644 --- a/include/globals.h +++ b/include/globals.h @@ -151,12 +151,8 @@ extern time_t userUTCTime; #include "sensor.h" #endif -#ifdef HAS_BME680 -#include "bme680mems.h" -#endif - -#ifdef HAS_BME280 -#include "bme280.h" +#if (HAS_BME) +#include "bmesensor.h" #endif #endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index ce2a1a71..d3652f4b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -;env_default = generic +env_default = generic ;env_default = ebox ;env_default = eboxtube ;env_default = heltec @@ -14,7 +14,7 @@ ;env_default = ttgov1 ;env_default = ttgov2 ;env_default = ttgov21old -env_default = ttgov21new +;env_default = ttgov21new ;env_default = ttgobeam ;env_default = ttgofox ;env_default = lopy @@ -53,6 +53,9 @@ lib_deps_gps = TinyGPSPlus@>=1.0.2 lib_deps_rtc = RTC@^2.3.0 +lib_deps_sensors = + Adafruit Unified Sensor@^1.0.3 + Adafruit BME280 Library@1.0.8 lib_deps_basic = ArduinoJson@^5.13.1 Timezone@^1.2.2 @@ -201,7 +204,7 @@ lib_deps = ${common.lib_deps_lora} ${common.lib_deps_display} build_flags = - ${common.build_flags_all} + ${common.build_flags_basic} upload_protocol = ${common.upload_protocol} extra_scripts = ${common.extra_scripts} monitor_speed = ${common.monitor_speed} diff --git a/src/bme280.cpp b/src/bme280.cpp deleted file mode 100644 index d2af49e3..00000000 --- a/src/bme280.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifdef HAS_BME280 - -#include "bme280.h" - -// Local logging tag -static const char TAG[] = __FILE__; - -bmeStatus_t bme_status; -TaskHandle_t Bme280Task; - -#define SEALEVELPRESSURE_HPA (1013.25) - -Adafruit_BME280 bme; // I2C -//Adafruit_BME280 bme(BME_CS); // hardware SPI -//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI - -// initialize BME280 sensor -int bme280_init(void) { - - bool status; - // return = 0 -> error / return = 1 -> success - - // block i2c bus access - if (I2C_MUTEX_LOCK()) { - status = bme.begin(BME280_ADDR); - if (!status) { - ESP_LOGE(TAG, "BME280 sensor not found"); - goto error; - } - ESP_LOGI(TAG, "BME280 sensor found and initialized"); - } else { - ESP_LOGE(TAG, "I2c bus busy - BME280 initialization error"); - goto error; - } - - I2C_MUTEX_UNLOCK(); // release i2c bus access - return 1; - -error: - I2C_MUTEX_UNLOCK(); // release i2c bus access - return 0; - -} // bme_init() - -// loop function which reads and processes data based on sensor settings -void bme280_loop(void *pvParameters) { -#ifdef HAS_BME280 - while (1) { - if (I2C_MUTEX_LOCK()) { - bme_status.temperature = bme.readTemperature(); - bme_status.pressure = (bme.readPressure() / 100.0); // conversion Pa -> hPa - // bme.readAltitude(SEALEVELPRESSURE_HPA); - bme_status.humidity = bme.readHumidity(); - I2C_MUTEX_UNLOCK(); - } - } -#endif - ESP_LOGE(TAG, "BME task ended"); - vTaskDelete(Bme280Task); // should never be reached - -} // bme_loop() - -#endif // HAS_BME280 \ No newline at end of file diff --git a/src/bme680mems.cpp b/src/bmesensor.cpp similarity index 79% rename from src/bme680mems.cpp rename to src/bmesensor.cpp index 8f28c389..19111d32 100644 --- a/src/bme680mems.cpp +++ b/src/bmesensor.cpp @@ -1,6 +1,6 @@ -#ifdef HAS_BME680 +#if (HAS_BME) -#include "bme680mems.h" +#include "bmesensor.h" // Local logging tag static const char TAG[] = __FILE__; @@ -8,6 +8,9 @@ static const char TAG[] = __FILE__; bmeStatus_t bme_status; TaskHandle_t BmeTask; +#define SEALEVELPRESSURE_HPA (1013.25) + +#ifdef HAS_BME680 bsec_virtual_sensor_t sensorList[10] = { BSEC_OUTPUT_RAW_TEMPERATURE, BSEC_OUTPUT_RAW_PRESSURE, @@ -26,11 +29,20 @@ uint16_t stateUpdateCounter = 0; Bsec iaqSensor; +#elif defined HAS_BME280 + +Adafruit_BME280 bme; // I2C + // Adafruit_BME280 bme(BME_CS); // hardware SPI +// Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI + +#endif + // initialize BME680 sensor int bme_init(void) { // return = 0 -> error / return = 1 -> success +#ifdef HAS_BME680 // block i2c bus access if (I2C_MUTEX_LOCK()) { @@ -66,6 +78,26 @@ int bme_init(void) { goto error; } +#elif defined HAS_BME280 + + bool status; + // return = 0 -> error / return = 1 -> success + + // block i2c bus access + if (I2C_MUTEX_LOCK()) { + status = bme.begin(BME280_ADDR); + if (!status) { + ESP_LOGE(TAG, "BME280 sensor not found"); + goto error; + } + ESP_LOGI(TAG, "BME280 sensor found and initialized"); + } else { + ESP_LOGE(TAG, "I2c bus busy - BME280 initialization error"); + goto error; + } + +#endif + I2C_MUTEX_UNLOCK(); // release i2c bus access return 1; @@ -75,6 +107,8 @@ error: } // bme_init() +#ifdef HAS_BME680 + // Helper function definitions int checkIaqSensorStatus(void) { int rslt = 1; // true = 1 = no error, false = 0 = error @@ -97,6 +131,7 @@ int checkIaqSensorStatus(void) { return rslt; } // checkIaqSensorStatus() +#endif // loop function which reads and processes data based on sensor settings void bme_loop(void *pvParameters) { @@ -122,12 +157,24 @@ void bme_loop(void *pvParameters) { I2C_MUTEX_UNLOCK(); } } +#elif defined HAS_BME280 + while (1) { + if (I2C_MUTEX_LOCK()) { + bme_status.temperature = bme.readTemperature(); + bme_status.pressure = + (bme.readPressure() / 100.0); // conversion Pa -> hPa + // bme.readAltitude(SEALEVELPRESSURE_HPA); + bme_status.humidity = bme.readHumidity(); + I2C_MUTEX_UNLOCK(); + } + } #endif ESP_LOGE(TAG, "BME task ended"); vTaskDelete(BmeTask); // should never be reached } // bme_loop() +#ifdef HAS_BME680 void loadState(void) { if (cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] == BSEC_MAX_STATE_BLOB_SIZE) { // Existing state in NVS stored @@ -167,5 +214,6 @@ void updateState(void) { saveConfig(); } } +#endif -#endif // HAS_BME680 \ No newline at end of file +#endif // HAS_BME \ No newline at end of file diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 63234d84..88debbdb 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -36,14 +36,10 @@ void doHousekeeping() { ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask)); #endif -#ifdef HAS_BME680 +#if (HAS_BME) ESP_LOGD(TAG, "Bmeloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask)); #endif -#ifdef HAS_BME280 - ESP_LOGD(TAG, "Bme280loop %d bytes left | Taskstate = %d", - uxTaskGetStackHighWaterMark(Bme280Task), eTaskGetState(Bme280Task)); -#endif #ifdef HAS_DCF77 ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d", uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask)); diff --git a/src/hal/generic.h b/src/hal/generic.h index 07a629d3..8cd00c11 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -21,6 +21,9 @@ #define HAS_BME 1 // Enable BME sensors in general #define HAS_BME680 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL #define BME680_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND +// BME280 sensor on I2C bus +//#define HAS_BME 1 // Enable BME sensors in general +//#define HAS_BME280 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL // user defined sensors //#define HAS_SENSORS 1 // comment out if device has user defined sensors diff --git a/src/hal/ttgov21new.h b/src/hal/ttgov21new.h index b327b8d3..8852d2f2 100644 --- a/src/hal/ttgov21new.h +++ b/src/hal/ttgov21new.h @@ -14,10 +14,9 @@ #define CFG_sx1276_radio 1 // HPD13A LoRa SoC // enable only if device has these sensors, otherwise comment these lines -// BME680 sensor on I2C bus -#define HAS_BME 1 // Enable BME sensors in general -#define HAS_BME280 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL -#define BME280_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND +// BME280 sensor on I2C bus +//#define HAS_BME 1 // Enable BME sensors in general +//#define HAS_BME280 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C #define HAS_LED (25) // green on board LED diff --git a/src/main.cpp b/src/main.cpp index 71dc36d7..3f758852 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -121,7 +121,7 @@ void setup() { #endif // setup debug output or silence device -#if(VERBOSE) +#if (VERBOSE) Serial.begin(115200); esp_log_level_set("*", ESP_LOG_VERBOSE); #else @@ -132,7 +132,7 @@ void setup() { ESP_LOGI(TAG, "Starting %s v%s", PRODUCTNAME, PROGVERSION); // print chip information on startup if in verbose mode -#if(VERBOSE) +#if (VERBOSE) esp_chip_info_t chip_info; esp_chip_info(&chip_info); ESP_LOGI(TAG, @@ -227,7 +227,7 @@ void setup() { batt_voltage = read_voltage(); #endif -#if(USE_OTA) +#if (USE_OTA) strcat_P(features, " OTA"); // reboot to firmware update mode if ota trigger switch is set if (cfg.runmode == 1) { @@ -239,7 +239,7 @@ void setup() { // start BLE scan callback if BLE function is enabled in NVRAM configuration // or switch off bluetooth, if not compiled -#if(BLECOUNTER) +#if (BLECOUNTER) strcat_P(features, " BLE"); if (cfg.blescan) { ESP_LOGI(TAG, "Starting Bluetooth..."); @@ -301,7 +301,7 @@ void setup() { assert(spi_init() == ESP_OK); #endif -#if(VENDORFILTER) +#if (VENDORFILTER) strcat_P(features, " OUIFLT"); #endif @@ -358,11 +358,15 @@ void setup() { &irqHandlerTask, // task handle 1); // CPU core -// initialize bme680 +// initialize BME sensor (BME280/BME680) +#if (HAS_BME) #ifdef HAS_BME680 + strcat_P(features, " BME680"); +#elif defined HAS_BME280 strcat_P(features, " BME280"); +#endif if (bme_init()) { - ESP_LOGI(TAG, "Starting BME280 sensor..."); + ESP_LOGI(TAG, "Starting BME sensor..."); xTaskCreatePinnedToCore(bme_loop, // task function "bmeloop", // name of task 2048, // stack size of task @@ -373,21 +377,6 @@ void setup() { } #endif -// initialize BME280 -#ifdef HAS_BME280 - strcat_P(features, " BME280"); - if (bme280_init()) { - ESP_LOGI(TAG, "Starting BME280 sensor..."); - xTaskCreatePinnedToCore(bme280_loop, // task function - "bme280loop", // name of task - 2048, // stack size of task - (void *)1, // parameter of the task - 1, // priority of the task - &Bme280Task, // task handle - 1); // CPU core - } -#endif - // starting timers and interrupts assert(irqHandlerTask != NULL); // has interrupt handler task started? ESP_LOGI(TAG, "Starting Timers..."); @@ -415,7 +404,7 @@ void setup() { #endif #endif // HAS_BUTTON -#if(TIME_SYNC_INTERVAL) +#if (TIME_SYNC_INTERVAL) // start pps timepulse ESP_LOGI(TAG, "Starting Timekeeper..."); assert(timepulse_init()); // setup timepulse diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 1f9c73f2..fa3b4431 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -262,12 +262,12 @@ void get_gps(uint8_t val[]) { void get_bme(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get bme680 sensor data"); -#ifdef HAS_BME680 +#if (HAS_BME) payload.reset(); payload.addBME(bme_status); SendPayload(BMEPORT, prio_high); #else - ESP_LOGW(TAG, "BME680 sensor not supported"); + ESP_LOGW(TAG, "BME sensor not supported"); #endif }; From 8f790113660c3d7e5e766e0e68676c4f95630fba Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 13 Mar 2019 22:08:05 +0100 Subject: [PATCH 23/25] fix display after merge of pr #313 --- include/globals.h | 2 +- include/irqhandler.h | 2 +- include/ota.h | 2 +- src/display.cpp | 2 +- src/irqhandler.cpp | 4 ++-- src/main.cpp | 10 ++++++---- src/ota.cpp | 8 ++++---- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/globals.h b/include/globals.h index 33d93795..d88a310f 100644 --- a/include/globals.h +++ b/include/globals.h @@ -131,7 +131,7 @@ extern time_t userUTCTime; #include "lorawan.h" #endif -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY #include "display.h" #endif diff --git a/include/irqhandler.h b/include/irqhandler.h index 5c73410e..ad74f700 100644 --- a/include/irqhandler.h +++ b/include/irqhandler.h @@ -14,7 +14,7 @@ void irqHandler(void *pvParameters); -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY #include "display.h" void IRAM_ATTR DisplayIRQ(); #endif diff --git a/include/ota.h b/include/ota.h index 0371d71c..3c6c8318 100644 --- a/include/ota.h +++ b/include/ota.h @@ -18,7 +18,7 @@ void start_ota_update(); int version_compare(const String v1, const String v2); void display(const uint8_t row, const std::string status, const std::string msg); -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY void show_progress(unsigned long current, unsigned long size); #endif diff --git a/src/display.cpp b/src/display.cpp index 96adebf9..88bea2cc 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -1,4 +1,4 @@ -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY /* diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index 97783172..bbfdafca 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -24,7 +24,7 @@ void irqHandler(void *pvParameters) { #endif // display needs refresh? -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY if (InterruptStatus & DISPLAY_IRQ) refreshtheDisplay(); #endif @@ -52,7 +52,7 @@ void irqHandler(void *pvParameters) { // esp32 hardware timer triggered interrupt service routines // they notify the irq handler task -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY void IRAM_ATTR DisplayIRQ() { BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; diff --git a/src/main.cpp b/src/main.cpp index 8542525e..ab0afe1c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,8 +47,10 @@ Tasks using i2c bus all must have same priority, because using mutex semaphore // ESP32 hardware timers ------------------------------------------------------------------------------- - 0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS in -paxcounter.conf) 1 ppsIRQ -> pps clock irq -> 1sec 2 unused 3 unused +0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS in paxcounter.conf) +1 ppsIRQ -> pps clock irq -> 1sec +2 unused +3 unused // Interrupt routines @@ -306,7 +308,7 @@ void setup() { #endif // initialize display -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY strcat_P(features, " OLED"); DisplayState = cfg.screenon; init_display(PRODUCTNAME, PROGVERSION); // note: blocking call @@ -382,7 +384,7 @@ void setup() { ESP_LOGI(TAG, "Starting Timers..."); // display interrupt -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up displayIRQ = timerBegin(0, 80, true); diff --git a/src/ota.cpp b/src/ota.cpp index a7c3f326..a2623702 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -48,7 +48,7 @@ void start_ota_update() { switch_LED(LED_ON); -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY u8x8.begin(); u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.clear(); @@ -266,7 +266,7 @@ int do_ota_update() { goto abort; } -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY // register callback function for showing progress while streaming data Update.onProgress(&show_progress); #endif @@ -309,7 +309,7 @@ retry: void display(const uint8_t row, const std::string status, const std::string msg) { -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY u8x8.setCursor(14, row); u8x8.print((status.substr(0, 2)).c_str()); if (!msg.empty()) { @@ -320,7 +320,7 @@ void display(const uint8_t row, const std::string status, #endif } -#if(HAS_DISPLAY) +#ifdef HAS_DISPLAY // callback function to show download progress while streaming data void show_progress(unsigned long current, unsigned long size) { char buf[17]; From fe0ef13b1f84b3d97a85d5e73ff57aafff407b2d Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Thu, 14 Mar 2019 00:05:19 +0100 Subject: [PATCH 24/25] timesync fixes --- include/timesync.h | 6 +++--- src/timesync.cpp | 41 ++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index 79b40fbb..7ce8205f 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -6,10 +6,10 @@ #include "timesync.h" #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_SAMPLES 5 // number of time requests for averaging +#define TIME_SYNC_CYCLE 60 // seconds between two time requests #define TIME_SYNC_TIMEOUT 120 // timeout seconds waiting for timeserver answer -#define TIME_SYNC_TRIGGER 1 // time deviation threshold triggering time sync +#define TIME_SYNC_TRIGGER 100 // time deviation in millisec triggering a time sync #define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length void send_timesync_req(void); diff --git a/src/timesync.cpp b/src/timesync.cpp index 8ee7e381..5f47b592 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -13,6 +13,8 @@ algorithm in applications without granted license by the patent holder. #include "timesync.h" +using namespace std::chrono; + // Local logging tag static const char TAG[] = __FILE__; @@ -73,8 +75,7 @@ void recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { timestamp_sec = (tmp_sec <<= 8) |= buf[i]; } - time_sync_rx[k] += std::chrono::seconds(timestamp_sec) + - std::chrono::milliseconds(timestamp_msec); + time_sync_rx[k] += seconds(timestamp_sec) + milliseconds(timestamp_msec); ESP_LOGD(TAG, "Timesync request #%d rcvd at %d", seq_no, myClock::to_time_t(time_sync_rx[k])); @@ -87,9 +88,9 @@ void recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { // task for sending time sync requests void process_timesync_req(void *taskparameter) { - time_t time_to_set = 0; uint8_t k = 0, i = 0; uint32_t seq_no = 0; + // milliseconds time_offset(0); auto time_offset = myClock_msecTick::zero(); // enqueue timestamp samples in lora sendqueue @@ -115,9 +116,9 @@ void process_timesync_req(void *taskparameter) { else { // calculate time diff from collected timestamps k = seq_no % TIME_SYNC_SAMPLES; - auto t_tx = std::chrono::time_point_cast( + auto t_tx = time_point_cast( time_sync_tx[k]); // timepoint when node TX_completed - auto t_rx = std::chrono::time_point_cast( + 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 @@ -129,27 +130,27 @@ void process_timesync_req(void *taskparameter) { // calculate time offset from collected diffs and set time if necessary time_offset /= TIME_SYNC_SAMPLES; + ESP_LOGD(TAG, "Avg time diff: %lldms", time_offset.count()); - if (abs(time_offset.count()) >= TIME_SYNC_TRIGGER) { + if (abs(time_offset.count()) >= TIME_SYNC_TRIGGER) { // milliseconds threshold - /* - // wait until top of second - if (time_offset_ms > 0) // clock is fast - vTaskDelay(pdMS_TO_TICKS(time_diff_ms)); - else if (time_offset_ms < 0) // clock is slow - vTaskDelay(pdMS_TO_TICKS(1000 + time_offset_ms)); - - time_to_set = t - time_t(time_offset_sec + 1); - */ - - time_t time_to_set = myClock::to_time_t(myClock::now() + time_offset); + time_t const time_to_set = now() + 1 + time_offset.count() / 1000; ESP_LOGD(TAG, "New UTC epoch time: %d", time_to_set); // adjust system time if (timeIsValid(time_to_set)) { + + // wait until top of second + uint16_t const time_offset_msec = 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); + setTime(time_to_set); - SyncToPPS(); timeSource = _lora; timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // set to regular repeat @@ -170,9 +171,7 @@ finish: void store_time_sync_req(time_t t_sec, uint32_t t_microsec) { uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES; - - time_sync_tx[k] += - std::chrono::seconds(t_sec) + std::chrono::microseconds(t_microsec); + time_sync_tx[k] += seconds(t_sec) + microseconds(t_microsec); ESP_LOGD(TAG, "Timesync request #%d sent at %d", time_sync_seqNo, myClock::to_time_t(time_sync_tx[k])); From 7f90d255b23b7b320004b827cf8ed0d5ad394ba2 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Thu, 14 Mar 2019 19:52:10 +0100 Subject: [PATCH 25/25] timeserver fixes --- include/timesync.h | 6 +++--- src/lorawan.cpp | 14 +++++++++----- src/timesync.cpp | 41 +++++++++++++++++++++++++---------------- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index 7ce8205f..e854301e 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -9,12 +9,12 @@ #define TIME_SYNC_SAMPLES 5 // number of time requests for averaging #define TIME_SYNC_CYCLE 60 // 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 time sync +#define TIME_SYNC_TRIGGER 100 // time deviation in millisec triggering a sync #define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length void send_timesync_req(void); -void recv_timesync_ans(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(time_t secs, uint32_t micros); +void store_time_sync_req(time_t t_millisec); #endif \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 2301d4bf..3d79cf3b 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -1,12 +1,12 @@ // Basic Config -#if(HAS_LORA) +#if (HAS_LORA) #include "lorawan.h" #endif // Local logging Tag static const char TAG[] = "lora"; -#if(HAS_LORA) +#if (HAS_LORA) #if CLOCK_ERROR_PROCENTAGE > 7 #warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects @@ -231,9 +231,13 @@ 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); - // maybe use more precise osticks2ms(LMIC.txend) here? + 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 strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK") diff --git a/src/timesync.cpp b/src/timesync.cpp index 5f47b592..c2b44b4d 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -61,11 +61,11 @@ void send_timesync_req() { } // process timeserver timestamp answer, called from lorawan.cpp -void recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { +int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { // if no timesync handshake is pending or spurious buffer then exit if ((!lora_time_sync_pending) || (buf_len != TIME_SYNC_FRAME_LENGTH)) - return; + 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 @@ -75,7 +75,10 @@ void recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { timestamp_sec = (tmp_sec <<= 8) |= buf[i]; } - time_sync_rx[k] += seconds(timestamp_sec) + milliseconds(timestamp_msec); + if (timestamp_sec + timestamp_msec) // field validation: timestamp not 0 ? + time_sync_rx[k] += seconds(timestamp_sec) + milliseconds(timestamp_msec); + else + return 0; // failure ESP_LOGD(TAG, "Timesync request #%d rcvd at %d", seq_no, myClock::to_time_t(time_sync_rx[k])); @@ -83,6 +86,8 @@ void recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { // inform processing task if (timeSyncReqTask) xTaskNotify(timeSyncReqTask, seq_no, eSetBits); + + return 1; // success } // task for sending time sync requests @@ -92,6 +97,8 @@ void process_timesync_req(void *taskparameter) { 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++) { @@ -123,26 +130,28 @@ void process_timesync_req(void *taskparameter) { 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 - time_offset /= TIME_SYNC_SAMPLES; - ESP_LOGD(TAG, "Avg time diff: %lldms", time_offset.count()); + time_offset /= TIME_SYNC_SAMPLES; + // 1 sec floor round + 1sec wait for top of second + time_to_set = now() + 2 + time_offset.count() / 1000; + ESP_LOGD(TAG, "Calculated UTC epoch time: %d", time_to_set); - if (abs(time_offset.count()) >= TIME_SYNC_TRIGGER) { // milliseconds threshold + // adjust system time + if (timeIsValid(time_to_set)) { - time_t const time_to_set = now() + 1 + time_offset.count() / 1000; - ESP_LOGD(TAG, "New 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 - uint16_t const time_offset_msec = time_offset.count() % 1000; + 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)); @@ -156,9 +165,9 @@ void process_timesync_req(void *taskparameter) { timeSync); // set to regular repeat ESP_LOGI(TAG, "Timesync finished, time was adjusted"); } else - ESP_LOGW(TAG, "Invalid time received from timeserver"); + ESP_LOGI(TAG, "Timesync finished, time is up to date"); } else - ESP_LOGI(TAG, "Timesync finished, time is up to date"); + ESP_LOGW(TAG, "Invalid time received from timeserver"); finish: @@ -168,10 +177,10 @@ finish: } // called from lorawan.cpp after time_sync_req was sent -void store_time_sync_req(time_t t_sec, uint32_t t_microsec) { +void store_time_sync_req(time_t t_millisec) { uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES; - time_sync_tx[k] += seconds(t_sec) + microseconds(t_microsec); + 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]));