timeserver (experimental)

This commit is contained in:
Verkehrsrot committed 2019-03-09 00:53:11 +01:00
1 parent 8980a3d8ff
commit 1051d841b6
16 files changed
+211 -200

No files matched your search

+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
};
-142
View File
@@ -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
View File
@@ -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
+150
View 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