timeserver (experimental)
This commit is contained in:
parent
8980a3d8ff
commit
1051d841b6
@ -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 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
|
# Building
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "rcommand.h"
|
#include "rcommand.h"
|
||||||
#include "timekeeper.h"
|
#include "timekeeper.h"
|
||||||
#if(ServertimeSYNC)
|
#if(TIME_SYNC_TIMESERVER)
|
||||||
#include "Servertimesync.h"
|
#include "timesync.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// LMIC-Arduino LoRaWAN Stack
|
// LMIC-Arduino LoRaWAN Stack
|
||||||
|
@ -44,7 +44,7 @@ public:
|
|||||||
void addConfig(configData_t value);
|
void addConfig(configData_t value);
|
||||||
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
||||||
uint8_t reset1, uint8_t reset2);
|
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 addVoltage(uint16_t value);
|
||||||
void addGPS(gpsStatus_t value);
|
void addGPS(gpsStatus_t value);
|
||||||
void addBME(bmeStatus_t value);
|
void addBME(bmeStatus_t value);
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
#include <rom/rtc.h>
|
#include <rom/rtc.h>
|
||||||
#include "cyclic.h"
|
#include "cyclic.h"
|
||||||
#include "timekeeper.h"
|
#include "timekeeper.h"
|
||||||
#if(ServertimeSYNC)
|
#if(TIME_SYNC_TIMESERVER)
|
||||||
#include "Servertimesync.h"
|
#include "timesync.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// table of remote commands and assigned functions
|
// table of remote commands and assigned functions
|
||||||
|
@ -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
|
|
25
include/timesync.h
Normal file
25
include/timesync.h
Normal file
@ -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
|
@ -33,7 +33,7 @@ void irqHandler(void *pvParameters) {
|
|||||||
if (InterruptStatus & CYCLIC_IRQ)
|
if (InterruptStatus & CYCLIC_IRQ)
|
||||||
doHousekeeping();
|
doHousekeeping();
|
||||||
|
|
||||||
#ifdef TIME_SYNC_INTERVAL
|
#if (TIME_SYNC_INTERVAL)
|
||||||
// is time to be synced?
|
// is time to be synced?
|
||||||
if (InterruptStatus & TIMESYNC_IRQ)
|
if (InterruptStatus & TIMESYNC_IRQ)
|
||||||
setTime(timeProvider());
|
setTime(timeProvider());
|
||||||
|
@ -225,9 +225,13 @@ void onEvent(ev_t ev) {
|
|||||||
|
|
||||||
case EV_TXCOMPLETE:
|
case EV_TXCOMPLETE:
|
||||||
|
|
||||||
#if(ServertimeSYNC)
|
#if (TIME_SYNC_TIMESERVER)
|
||||||
if (!(LMIC.txrxFlags & TXRX_ACK) && time_sync_seqNo)
|
// if last packet sent was a timesync request was sent, store TX timestamp
|
||||||
time_sync_messages[time_sync_seqNo - 1] = LMIC.txend;
|
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
|
#endif
|
||||||
|
|
||||||
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK")
|
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK")
|
||||||
@ -243,6 +247,7 @@ void onEvent(ev_t ev) {
|
|||||||
if ((LMIC.txrxFlags & TXRX_PORT) &&
|
if ((LMIC.txrxFlags & TXRX_PORT) &&
|
||||||
(LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT))
|
(LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT))
|
||||||
rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
|
rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -385,15 +390,15 @@ esp_err_t lora_stack_init() {
|
|||||||
// in src/lmic_config.h if you are limited on battery.
|
// in src/lmic_config.h if you are limited on battery.
|
||||||
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
|
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
|
||||||
// Set the data rate to Spreading Factor 7. This is the fastest supported
|
// 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
|
// rate for 125 kHz channels, and it minimizes air time and battery power.
|
||||||
// the transmission power to 14 dBi (25 mW).
|
// Set the transmission power to 14 dBi (25 mW).
|
||||||
LMIC_setDrTxpow(DR_SF7, 14);
|
LMIC_setDrTxpow(DR_SF7, 14);
|
||||||
|
|
||||||
#if defined(CFG_US915) || defined(CFG_au921)
|
#if defined(CFG_US915) || defined(CFG_au921)
|
||||||
// in the US, with TTN, it saves join time if we start on subband 1 (channels
|
// in the US, with TTN, it saves join time if we start on subband 1
|
||||||
// 8-15). This will get overridden after the join by parameters from the
|
// (channels 8-15). This will get overridden after the join by parameters
|
||||||
// network. If working with other networks or in other regions, this will need
|
// from the network. If working with other networks or in other regions,
|
||||||
// to be changed.
|
// this will need to be changed.
|
||||||
LMIC_selectSubBand(1);
|
LMIC_selectSubBand(1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
|
|||||||
blink_LED(COLOR_WHITE, 2000);
|
blink_LED(COLOR_WHITE, 2000);
|
||||||
#endif
|
#endif
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addAlarm(rssi, beaconID);
|
payload.add2Bytes(rssi, beaconID);
|
||||||
SendPayload(BEACONPORT, prio_high);
|
SendPayload(BEACONPORT, prio_high);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -400,7 +400,7 @@ void setup() {
|
|||||||
#endif
|
#endif
|
||||||
#endif // HAS_BUTTON
|
#endif // HAS_BUTTON
|
||||||
|
|
||||||
#ifdef TIME_SYNC_INTERVAL
|
#if(TIME_SYNC_INTERVAL)
|
||||||
// start pps timepulse
|
// start pps timepulse
|
||||||
ESP_LOGI(TAG, "Starting Timekeeper...");
|
ESP_LOGI(TAG, "Starting Timekeeper...");
|
||||||
assert(timepulse_init()); // setup timepulse
|
assert(timepulse_init()); // setup timepulse
|
||||||
@ -410,7 +410,7 @@ void setup() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined HAS_IF482 || defined HAS_DCF77
|
#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
|
#error for clock controller function TIME_SNYC_INTERVAL must be defined in paxcounter.conf
|
||||||
#endif
|
#endif
|
||||||
ESP_LOGI(TAG, "Starting Clock Controller...");
|
ESP_LOGI(TAG, "Starting Clock Controller...");
|
||||||
|
@ -66,9 +66,9 @@
|
|||||||
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
|
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
|
||||||
|
|
||||||
// settings for syncing time of node with external time source
|
// 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_INTERVAL 60 // sync time attempt each .. minutes from time source (GPS/LORA/RTC) [default = 60], 0 means off
|
||||||
#define TIME_SYNC_LORA 0 // set to 1 to use LORA network as time source, 0 means off [default = 0]
|
#define TIME_SYNC_LORAWAN 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_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
|
// 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
|
#define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time
|
||||||
|
@ -23,7 +23,7 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
|||||||
buffer[cursor++] = lowByte(value);
|
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++] = rssi;
|
||||||
buffer[cursor++] = msg;
|
buffer[cursor++] = msg;
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
|||||||
writeUint16(value);
|
writeUint16(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
|
void PayloadConvert::add2Bytes(int8_t rssi, uint8_t msg) {
|
||||||
writeUint8(rssi);
|
writeUint8(rssi);
|
||||||
writeUint8(msg);
|
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)
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
buffer[cursor++] = LPP_ALARM_CHANNEL;
|
buffer[cursor++] = LPP_ALARM_CHANNEL;
|
||||||
#endif
|
#endif
|
||||||
|
@ -306,10 +306,10 @@ cmd_t table[] = {{0x01, set_rssi, 1, true},
|
|||||||
{0x84, get_gps, 0, false},
|
{0x84, get_gps, 0, false},
|
||||||
{0x85, get_bme, 0, false},
|
{0x85, get_bme, 0, false},
|
||||||
{0x86, get_time, 0, false}
|
{0x86, get_time, 0, false}
|
||||||
#if(ServertimeSYNC)
|
#if(TIME_SYNC_TIMESERVER)
|
||||||
,
|
,
|
||||||
{TIME_ANS_OPCODE, recv_Servertime_ans, 5, false},
|
{TIME_SYNC_ANS_OPCODE, recv_Servertime_ans, 5, false},
|
||||||
{TIME_SYNC_OPCODE, force_Servertime_sync, 0, false}
|
{TIME_SYNC_START_OPCODE, force_Servertime_sync, 0, false}
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
|
@ -36,10 +36,10 @@ time_t timeProvider(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// kick off asychronous DB timesync if we have
|
// kick off asychronous DB timesync if we have
|
||||||
#if(ServertimeSYNC)
|
#if(TIME_SYNC_TIMESERVER)
|
||||||
send_Servertime_req();
|
send_Servertime_req();
|
||||||
// kick off asychronous lora sync if we have
|
// 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);
|
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
150
src/timesync.cpp
Normal file
150
src/timesync.cpp
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user