diff --git a/README.md b/README.md index af46511d..07ead53b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/include/timesync.h b/include/timesync.h index 2207e02b..d79290d4 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -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 \ No newline at end of file diff --git a/src/TTN/packed_decoder.js b/src/TTN/packed_decoder.js index 265b416c..49f1b5b9 100644 --- a/src/TTN/packed_decoder.js +++ b/src/TTN/packed_decoder.js @@ -66,14 +66,10 @@ 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; - } + } } diff --git a/src/TTN/plain_decoder.js b/src/TTN/plain_decoder.js index ca43a5e4..62249d08 100644 --- a/src/TTN/plain_decoder.js +++ b/src/TTN/plain_decoder.js @@ -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; } diff --git a/src/lorawan.cpp b/src/lorawan.cpp index f0c653ab..7d2cc7c2 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -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); } diff --git a/src/rcommand.cpp b/src/rcommand.cpp index f613e0b3..a8b572bd 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -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 diff --git a/src/spislave.cpp b/src/spislave.cpp index ed6ab7b0..cd6455a0 100644 --- a/src/spislave.cpp +++ b/src/spislave.cpp @@ -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); } diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index ce95a78a..2a10e862 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -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 diff --git a/src/timesync.cpp b/src/timesync.cpp index f3f8a9b9..c7419396 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -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 \ No newline at end of file