timeserver(experimental)

This commit is contained in:
Verkehrsrot 2019-03-09 22:08:57 +01:00
parent b742f888fd
commit f9f5f499ff
9 changed files with 77 additions and 81 deletions

View File

@ -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

View File

@ -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

View File

@ -66,11 +66,7 @@ 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;
} }

View File

@ -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;
} }

View File

@ -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); }

View File

@ -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

View File

@ -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); }

View File

@ -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

View File

@ -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