timeserver(experimental)
This commit is contained in:
parent
b742f888fd
commit
f9f5f499ff
@ -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
|
||||
|
||||
|
@ -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
|
@ -66,11 +66,7 @@ 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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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); }
|
||||
|
@ -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
|
||||
|
@ -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); }
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user