timeserver (experimental)
This commit is contained in:
1 parent
8980a3d8ff
commit
1051d841b6
16 files changed
+211
-200
No files matched your search
+1
-1
@@ -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());
|
||||
|
||||
+15
-10
@@ -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
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
+2
-2
@@ -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...");
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+3
-3
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
+2
-2
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user