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.
|
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
|
# License
|
||||||
|
|
||||||
|
@ -6,13 +6,10 @@
|
|||||||
#include "timekeeper.h"
|
#include "timekeeper.h"
|
||||||
|
|
||||||
#define TIME_SYNC_SAMPLES 1 // number of time requests for averaging
|
#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_TIMEOUT 120 // timeout seconds waiting for timeserver answer
|
||||||
#define TIME_SYNC_TRIGGER 1.0f // time deviation threshold triggering time sync
|
#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_FRAME_LENGTH 0x06 // timeserver answer frame length
|
||||||
#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 {
|
typedef struct {
|
||||||
uint32_t seconds;
|
uint32_t seconds;
|
||||||
@ -24,9 +21,8 @@ extern uint8_t time_sync_seqNo;
|
|||||||
extern bool time_sync_pending;
|
extern bool time_sync_pending;
|
||||||
|
|
||||||
void send_Servertime_req(void);
|
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 process_Servertime_sync_req(void *taskparameter);
|
||||||
void force_Servertime_sync(uint8_t val[]);
|
|
||||||
void store_time_sync_req(time_t secs, uint32_t micros);
|
void store_time_sync_req(time_t secs, uint32_t micros);
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -66,14 +66,10 @@ function Decoder(bytes, port) {
|
|||||||
if (port === 9) {
|
if (port === 9) {
|
||||||
// timesync request
|
// timesync request
|
||||||
if (bytes.length === 1) {
|
if (bytes.length === 1) {
|
||||||
decoded.timesync_opcode = bytes[0];
|
decoded.timesync_seqno = bytes[0];
|
||||||
}
|
|
||||||
if (bytes.length === 2) {
|
|
||||||
decoded.timesync_opcode = bytes[0];
|
|
||||||
decoded.timesync_seqno = bytes[1];
|
|
||||||
}
|
}
|
||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,12 @@ function Decoder(bytes, port) {
|
|||||||
decoded.battery = (bytes[i++] << 8) | bytes[i++];}
|
decoded.battery = (bytes[i++] << 8) | bytes[i++];}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (port === 9) {
|
||||||
|
if (bytes.length === 1) {
|
||||||
|
decoded.timesync_seqno = bytes[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return decoded;
|
return decoded;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -228,11 +228,10 @@ void onEvent(ev_t ev) {
|
|||||||
case EV_TXCOMPLETE:
|
case EV_TXCOMPLETE:
|
||||||
|
|
||||||
#if (TIME_SYNC_TIMESERVER)
|
#if (TIME_SYNC_TIMESERVER)
|
||||||
// if last packet sent was a timesync request was sent, store TX timestamp
|
// if last packet sent was a timesync request, store TX timestamp
|
||||||
if ((LMIC.pendTxPort == TIMEPORT) &&
|
if (LMIC.pendTxPort == TIMEPORT)
|
||||||
(LMIC.pendTxData[0] == TIME_SYNC_REQ_OPCODE))
|
|
||||||
store_time_sync_req(now(now_micros), now_micros);
|
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
|
#endif
|
||||||
|
|
||||||
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK")
|
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);
|
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);
|
||||||
|
|
||||||
// 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) &&
|
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);
|
||||||
@ -422,11 +427,8 @@ void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
|
|||||||
ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0);
|
ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ret == pdTRUE) {
|
if (ret != pdTRUE)
|
||||||
ESP_LOGI(TAG, "%d bytes enqueued for LORA interface", message->MessageSize);
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "LORA sendqueue is full");
|
ESP_LOGW(TAG, "LORA sendqueue is full");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lora_queuereset(void) { xQueueReset(LoraSendQueue); }
|
void lora_queuereset(void) { xQueueReset(LoraSendQueue); }
|
||||||
|
@ -38,7 +38,7 @@ void set_reset(uint8_t val[]) {
|
|||||||
break;
|
break;
|
||||||
case 9: // reset and ask for software update via Wifi OTA
|
case 9: // reset and ask for software update via Wifi OTA
|
||||||
ESP_LOGI(TAG, "Remote command: software update via Wifi");
|
ESP_LOGI(TAG, "Remote command: software update via Wifi");
|
||||||
#if(USE_OTA)
|
#if (USE_OTA)
|
||||||
sprintf(display_line6, "Software update");
|
sprintf(display_line6, "Software update");
|
||||||
cfg.runmode = 1;
|
cfg.runmode = 1;
|
||||||
#else
|
#else
|
||||||
@ -278,40 +278,35 @@ void get_time(uint8_t val[]) {
|
|||||||
SendPayload(TIMEPORT, prio_high);
|
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
|
// assign previously defined functions to set of numeric remote commands
|
||||||
// format: opcode, function, #bytes params,
|
// format: opcode, function, #bytes params,
|
||||||
// flag (true = do make settings persistent / false = don't)
|
// flag (true = do make settings persistent / false = don't)
|
||||||
//
|
//
|
||||||
cmd_t table[] = {{0x01, set_rssi, 1, true},
|
cmd_t table[] = {
|
||||||
{0x02, set_countmode, 1, true},
|
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
|
||||||
{0x03, set_gps, 1, true},
|
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
|
||||||
{0x04, set_display, 1, true},
|
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true},
|
||||||
{0x05, set_lorasf, 1, true},
|
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true},
|
||||||
{0x06, set_lorapower, 1, true},
|
{0x09, set_reset, 1, true}, {0x0a, set_sendcycle, 1, true},
|
||||||
{0x07, set_loraadr, 1, true},
|
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true},
|
||||||
{0x08, set_screensaver, 1, true},
|
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
|
||||||
{0x09, set_reset, 1, true},
|
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
|
||||||
{0x0a, set_sendcycle, 1, true},
|
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
|
||||||
{0x0b, set_wifichancycle, 1, true},
|
{0x13, set_sensor, 2, true}, {0x80, get_config, 0, false},
|
||||||
{0x0c, set_blescantime, 1, true},
|
{0x81, get_status, 0, false}, {0x84, get_gps, 0, false},
|
||||||
{0x0d, set_vendorfilter, 1, false},
|
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false},
|
||||||
{0x0e, set_blescan, 1, true},
|
{0x87, set_time, 0, false}, {0x99, set_flush, 0, false}};
|
||||||
{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
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t cmdtablesize =
|
const uint8_t cmdtablesize =
|
||||||
sizeof(table) / sizeof(table[0]); // number of commands in command table
|
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);
|
ret = xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ret == pdTRUE) {
|
if (ret != pdTRUE)
|
||||||
ESP_LOGI(TAG, "%d byte(s) enqueued for SPI interface",
|
|
||||||
message->MessageSize);
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "SPI sendqueue is full");
|
ESP_LOGW(TAG, "SPI sendqueue is full");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void spi_queuereset(void) { xQueueReset(SPISendQueue); }
|
void spi_queuereset(void) { xQueueReset(SPISendQueue); }
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
#include "timekeeper.h"
|
#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
|
// Local logging tag
|
||||||
static const char TAG[] = __FILE__;
|
static const char TAG[] = __FILE__;
|
||||||
|
|
||||||
@ -35,11 +43,11 @@ time_t timeProvider(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// kick off asychronous DB timesync if we have
|
// kick off asychronous Lora timeserver timesync if we have
|
||||||
#if(TIME_SYNC_TIMESERVER)
|
#if (TIME_SYNC_TIMESERVER)
|
||||||
send_Servertime_req();
|
send_Servertime_req();
|
||||||
// kick off asychronous lora sync if we have
|
// kick off asychronous lora network sync if we have
|
||||||
#elif defined HAS_LORA && (TIME_SYNC_LORAWAN)
|
#elif (TIME_SYNC_LORAWAN)
|
||||||
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
|
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ void send_Servertime_req() {
|
|||||||
|
|
||||||
// if a timesync handshake is pending then exit
|
// if a timesync handshake is pending then exit
|
||||||
if (time_sync_pending) {
|
if (time_sync_pending) {
|
||||||
ESP_LOGI(TAG, "Timeserver sync request already running");
|
ESP_LOGI(TAG, "Timeserver sync request already pending");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "Timeserver sync request started");
|
ESP_LOGI(TAG, "Timeserver sync request started");
|
||||||
@ -54,20 +54,20 @@ void send_Servertime_req() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process timeserver timestamp response, called from rcommand.cpp
|
// 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 no timesync handshake is pending or invalid buffer then exit
|
||||||
if (!time_sync_pending)
|
if ((!time_sync_pending) || (buf_len != TIME_SYNC_FRAME_LENGTH))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint8_t seq_no = val[0];
|
uint8_t seq_no = buf[0];
|
||||||
uint32_t timestamp_sec = 0, timestamp_ms = 0;
|
uint32_t timestamp_sec = 0, timestamp_ms = 0;
|
||||||
|
|
||||||
for (int i = 1; i <= 4; i++) {
|
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;
|
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;
|
time_sync_answers[seq_no].fractions = timestamp_ms;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Timeserver answer #%d received: timestamp=%d.%03d", seq_no,
|
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
|
// send sync request to server
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addWord(TIME_SYNC_REQ_OPCODE | time_sync_seqNo << 8);
|
payload.addByte(time_sync_seqNo);
|
||||||
SendPayload(TIMEPORT, prio_high);
|
SendPayload(TIMEPORT, prio_high);
|
||||||
ESP_LOGD(TAG, "Timeserver request #%d enqeued", time_sync_seqNo);
|
|
||||||
|
|
||||||
// send dummy packet to trigger receive answer
|
// send dummy packet to trigger receive answer
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addByte(TIME_SYNC_STEP_OPCODE);
|
payload.addByte(0x99); // flush
|
||||||
SendPayload(TIMEPORT, prio_low); // open receive slot for answer
|
SendPayload(RCMDPORT, prio_low); // open receive slot for answer
|
||||||
|
|
||||||
// process answer
|
// process answer
|
||||||
if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no,
|
if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no,
|
||||||
@ -124,9 +123,8 @@ void process_Servertime_sync_req(void *taskparameter) {
|
|||||||
time_offset =
|
time_offset =
|
||||||
(time_diff_sec + time_diff_ms / 1000.0f) / (TIME_SYNC_SAMPLES * 1.0f);
|
(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",
|
||||||
ESP_LOGD(TAG, "Timesync time offset=%d.%03d", time_diff_sec / TIME_SYNC_SAMPLES,
|
time_diff_sec / TIME_SYNC_SAMPLES, time_diff_ms / TIME_SYNC_SAMPLES);
|
||||||
time_diff_ms / TIME_SYNC_SAMPLES);
|
|
||||||
|
|
||||||
if (time_offset >= TIME_SYNC_TRIGGER) {
|
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);
|
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
|
#endif
|
Loading…
Reference in New Issue
Block a user