deeply reworked timesync code

This commit is contained in:
Klaus K Wilting 2020-03-06 19:24:58 +01:00
parent f1b13f11fa
commit 73a612779b
6 changed files with 233 additions and 212 deletions

View File

@ -20,13 +20,6 @@
extern TaskHandle_t lmicTask, lorasendTask; extern TaskHandle_t lmicTask, lorasendTask;
// table of LORAWAN MAC commands
typedef struct {
const uint8_t opcode;
const char cmdname[20];
const uint8_t params;
} mac_t;
esp_err_t lora_stack_init(bool do_join); esp_err_t lora_stack_init(bool do_join);
void lora_setupForNetwork(bool preJoin); void lora_setupForNetwork(bool preJoin);
void lmictask(void *pvParameters); void lmictask(void *pvParameters);
@ -36,19 +29,28 @@ void get_hard_deveui(uint8_t *pdeveui);
void os_getDevKey(u1_t *buf); void os_getDevKey(u1_t *buf);
void os_getArtEui(u1_t *buf); void os_getArtEui(u1_t *buf);
void os_getDevEui(u1_t *buf); void os_getDevEui(u1_t *buf);
void showLoraKeys(void);
void lora_send(void *pvParameters); void lora_send(void *pvParameters);
void lora_enqueuedata(MessageBuffer_t *message); void lora_enqueuedata(MessageBuffer_t *message);
void lora_queuereset(void); void lora_queuereset(void);
void IRAM_ATTR myEventCallback(void *pUserData, ev_t ev); void IRAM_ATTR myEventCallback(void *pUserData, ev_t ev);
void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port, void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
const uint8_t *pMsg, size_t nMsg); size_t nMsg);
//void IRAM_ATTR myTxCallback(void *pUserData, int fSuccess); void IRAM_ATTR myTxCallback(void *pUserData, int fSuccess);
void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[],
const uint8_t tablesize);
//u1_t os_getBattLevel(void);
const char *getSfName(rps_t rps); const char *getSfName(rps_t rps);
const char *getBwName(rps_t rps); const char *getBwName(rps_t rps);
const char *getCrName(rps_t rps); const char *getCrName(rps_t rps);
// u1_t os_getBattLevel(void);
#if VERBOSE
// a table for storage of LORAWAN MAC commands
typedef struct {
const uint8_t opcode;
const char cmdname[20];
const uint8_t params;
} mac_t;
void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[],
const uint8_t tablesize);
void showLoraKeys(void);
#endif
#endif #endif

View File

@ -17,15 +17,18 @@ enum timesync_t {
timesync_rx, timesync_rx,
gwtime_sec, gwtime_sec,
gwtime_msec, gwtime_msec,
gwtime_tzsec,
no_of_timestamps no_of_timestamps
}; };
void timesync_init(void); void timesync_init(void);
void send_timesync_req(void); void timesync_sendReq(void);
int recv_timesync_ans(const uint8_t buf[], uint8_t buf_len); void timesync_storeReq(uint32_t timestamp, timesync_t timestamp_type);
void store_timestamp(uint32_t timestamp, timesync_t timestamp_type); void IRAM_ATTR timesync_processReq(void *taskparameter);
void IRAM_ATTR process_timesync_req(void *taskparameter);
void IRAM_ATTR process_timesync_req(void *pVoidUserUTCTime, int flagSuccess); #if (TIME_SYNC_LORASERVER)
int recv_timeserver_ans(const uint8_t buf[], uint8_t buf_len);
#elif (TIME_SYNC_LORAWAN)
void IRAM_ATTR DevTimeAns_Cb(void *pUserData, int flagSuccess);
#endif
#endif #endif

View File

@ -23,6 +23,7 @@ RTC_NOINIT_ATTR int RTCseqnoUp, RTCseqnoDn;
QueueHandle_t LoraSendQueue; QueueHandle_t LoraSendQueue;
TaskHandle_t lmicTask = NULL, lorasendTask = NULL; TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
#if (VERBOSE)
// table of LORAWAN MAC messages sent by the network to the device // table of LORAWAN MAC messages sent by the network to the device
// format: opcode, cmdname (max 19 chars), #bytes params // format: opcode, cmdname (max 19 chars), #bytes params
// source: LoRaWAN 1.1 Specification (October 11, 2017) // source: LoRaWAN 1.1 Specification (October 11, 2017)
@ -46,6 +47,10 @@ static const mac_t MACup_table[] = {
{0x0B, "RekeyInd", 1}, {0x0C, "ADRParamSetupAns", 0}, {0x0B, "RekeyInd", 1}, {0x0C, "ADRParamSetupAns", 0},
{0x0D, "DeviceTimeReq", 0}, {0x0F, "RejoinParamSetupAns", 1}}; {0x0D, "DeviceTimeReq", 0}, {0x0F, "RejoinParamSetupAns", 1}};
static const uint8_t MACdn_tSize = sizeof(MACdn_table) / sizeof(MACdn_table[0]),
MACup_tSize = sizeof(MACup_table) / sizeof(MACup_table[0]);
#endif // VERBOSE
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t { class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
public: public:
@ -265,18 +270,16 @@ void lora_send(void *pvParameters) {
SendBuffer.MessageSize, SendBuffer.MessageSize,
(cfg.countermode & 0x02))) { (cfg.countermode & 0x02))) {
// switch (LMIC_sendWithCallback_strict(
// SendBuffer.MessagePort, SendBuffer.Message,
// SendBuffer.MessageSize, (cfg.countermode & 0x02), myTxCallback,
// &SendBuffer.MessagePort)) {
case LMIC_ERROR_SUCCESS: case LMIC_ERROR_SUCCESS:
// save current Fcnt to RTC RAM
RTCseqnoUp = LMIC.seqnoUp;
RTCseqnoDn = LMIC.seqnoDn;
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
// if last packet sent was a timesync request, store TX timestamp // if last packet sent was a timesync request, store TX timestamp
if (SendBuffer.MessagePort == TIMEPORT) if (SendBuffer.MessagePort == TIMEPORT)
// store LMIC time when we started transmit of timesync request // store LMIC time when we started transmit of timesync request
store_timestamp(osticks2ms(os_getTime()), timesync_tx); timesync_storeReq(osticks2ms(os_getTime()), timesync_tx);
#endif #endif
ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize); ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize);
@ -429,6 +432,15 @@ void myEventCallback(void *pUserData, ev_t ev) {
// process current event message // process current event message
switch (ev) { switch (ev) {
case EV_TXCOMPLETE:
// -> processed in lora_send()
break;
case EV_RXCOMPLETE:
// -> processed in myRxCallback()
break;
case EV_JOINING: case EV_JOINING:
// do the network-specific setup prior to join. // do the network-specific setup prior to join.
lora_setupForNetwork(true); lora_setupForNetwork(true);
@ -439,12 +451,6 @@ void myEventCallback(void *pUserData, ev_t ev) {
lora_setupForNetwork(false); lora_setupForNetwork(false);
break; break;
case EV_TXCOMPLETE:
// save current Fcnt to RTC RAM
RTCseqnoUp = LMIC.seqnoUp;
RTCseqnoDn = LMIC.seqnoDn;
break;
case EV_JOIN_TXCOMPLETE: case EV_JOIN_TXCOMPLETE:
// replace descriptor from library with more descriptive term // replace descriptor from library with more descriptive term
snprintf(lmic_event_msg, LMIC_EVENTMSG_LEN, "%-16s", "JOIN_WAIT"); snprintf(lmic_event_msg, LMIC_EVENTMSG_LEN, "%-16s", "JOIN_WAIT");
@ -462,113 +468,79 @@ void myEventCallback(void *pUserData, ev_t ev) {
ESP_LOGD(TAG, "%s", lmic_event_msg); ESP_LOGD(TAG, "%s", lmic_event_msg);
} }
// receive message handler // event EV_RXCOMPLETE message handler
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg, void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
size_t nMsg) { size_t nMsg) {
// display type of received data // calculate if we have encapsulated MAC commands
uint8_t nMac = pMsg - &LMIC.frame[0];
if (port != MACPORT)
--nMac;
// display amount of received data
if (nMsg) if (nMsg)
ESP_LOGI(TAG, "Received %u byte(s) of payload on port %u", nMsg, port); ESP_LOGI(TAG, "Received %u byte(s) of payload on port %u", nMsg, port);
else if (port) else if (port)
ESP_LOGI(TAG, "Received empty message on port %u", port); ESP_LOGI(TAG, "Received empty message on port %u", port);
// list MAC messages, if any
uint8_t nMac = pMsg - &LMIC.frame[0];
if (port != MACPORT)
--nMac;
if (nMac) {
ESP_LOGI(TAG, "%u byte(s) downlink MAC commands", nMac);
// NOT WORKING YET
// whe need to unwrap the MAC command from LMIC.frame here
// mac_decode(LMIC.frame, nMac, MACdn_table, sizeof(MACdn_table) /
// sizeof(MACdn_table[0]));
}
if (LMIC.pendMacLen) {
ESP_LOGI(TAG, "%u byte(s) uplink MAC commands", LMIC.pendMacLen);
mac_decode(LMIC.pendMacData, LMIC.pendMacLen, MACup_table,
sizeof(MACup_table) / sizeof(MACup_table[0]));
}
switch (port) { switch (port) {
// ignore mac messages // decode unpiggybacked mac messages if we want to print those
#if (VERBOSE)
case MACPORT: case MACPORT:
break; // decode downlink MAC commands
// mac_decode(LMIC.frame, nMac, MACdn_table, MACdn_tSize);
// decode uplink MAC commands
if (LMIC.pendMacLen)
mac_decode(LMIC.pendMacData, LMIC.pendMacLen, MACup_table, MACup_tSize);
break; // do not fallthrough to default, we are done
#endif
// rcommand received -> call interpreter // rcommand received -> call interpreter
case RCMDPORT: case RCMDPORT:
rcommand(pMsg, nMsg); rcommand(pMsg, nMsg);
break;
// timeserver answer -> call timesync processor
#if (TIME_SYNC_LORASERVER)
case TIMEPORT:
// store LMIC time when we received the timesync answer
timesync_storeReq(osticks2ms(os_getTime()), timesync_rx);
// get and store gwtime from payload
recv_timeserver_ans(pMsg, nMsg);
#endif
default: default:
#if (TIME_SYNC_LORASERVER) // decode any piggybacked downlink MAC commands
// valid timesync answer -> call timesync processor #if (VERBOSE)
if (port == TIMEPORT) { if (nMac) {
// store LMIC time when we received the timesync answer // decoding yet to come
store_timestamp(osticks2ms(os_getTime()), timesync_rx); // -> whe need to unwrap the MAC command from LMIC.frame before calling
// get and store gwtime from payload // mac_decode(LMIC.frame, nMac, MACdn_table, MACdn_tSize);
recv_timesync_ans(pMsg, nMsg);
break;
} }
#endif #endif // VERBOSE
// unknown port -> display info
ESP_LOGI(TAG, "Received data on unsupported port %u", port);
break; break;
} // switch } // switch
} }
/* /*
// event TRANSMIT COMPLETE message handler // event EV_TXCOMPLETE message handler
void myTxCallback(void *pUserData, int fSuccess) { void myTxCallback(void *pUserData, int fSuccess) {
uint8_t *const sendport = (uint8_t *)pUserData; uint8_t *const pMsg = (uint8_t *)pUserData;
// LMIC did successful transmit data
if (fSuccess) { if (fSuccess) {
// LMIC did tx on *sendport -> nothing yet to do here RTCseqnoUp = LMIC.seqnoUp;
RTCseqnoDn = LMIC.seqnoDn;
} else { } else {
// LMIC could not tx on *sendport -> error handling yet to come // LMIC could not transmit data
// -> error handling yet to come
} }
} }
*/ */
// decode LORAWAN MAC message
void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[],
const uint8_t tablesize) {
if (!cmdlen)
return;
uint8_t foundcmd[cmdlen], cursor = 0;
while (cursor < cmdlen) {
int i = tablesize; // number of commands in table
while (i--) {
if (cmd[cursor] == table[i].opcode) { // lookup command in opcode table
cursor++; // strip 1 byte opcode
if ((cursor + table[i].params) <= cmdlen) {
memmove(foundcmd, cmd + cursor,
table[i].params); // strip opcode from cmd array
cursor += table[i].params;
ESP_LOGD(TAG, "MAC command %s", table[i].cmdname);
} else
ESP_LOGD(TAG, "MAC command 0x%02X with missing parameter(s)",
table[i].opcode);
break; // command found -> exit table lookup loop
} // end of command validation
} // end of command table lookup loop
if (i < 0) { // command not found -> skip it
ESP_LOGD(TAG, "Unknown MAC command 0x%02X", cmd[cursor]);
cursor++;
}
} // command parsing loop
} // mac_decode()
const char *getSfName(rps_t rps) { const char *getSfName(rps_t rps) {
const char *const t[] = {"FSK", "SF7", "SF8", "SF9", const char *const t[] = {"FSK", "SF7", "SF8", "SF9",
"SF10", "SF11", "SF12", "SF?"}; "SF10", "SF11", "SF12", "SF?"};
@ -611,4 +583,41 @@ u1_t os_getBattLevel() {
} // getBattLevel() } // getBattLevel()
*/ */
#if (VERBOSE)
// decode LORAWAN MAC message
void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[],
const uint8_t tablesize) {
if (!cmdlen)
return;
uint8_t foundcmd[cmdlen], cursor = 0;
while (cursor < cmdlen) {
int i = tablesize; // number of commands in table
while (i--) {
if (cmd[cursor] == table[i].opcode) { // lookup command in opcode table
cursor++; // strip 1 byte opcode
if ((cursor + table[i].params) <= cmdlen) {
memmove(foundcmd, cmd + cursor,
table[i].params); // strip opcode from cmd array
cursor += table[i].params;
ESP_LOGD(TAG, "MAC command %s", table[i].cmdname);
} else
ESP_LOGD(TAG, "MAC command 0x%02X with missing parameter(s)",
table[i].opcode);
break; // command found -> exit table lookup loop
} // end of command validation
} // end of command table lookup loop
if (i < 0) { // command not found -> skip it
ESP_LOGD(TAG, "Unknown MAC command 0x%02X", cmd[cursor]);
cursor++;
}
} // command parsing loop
} // mac_decode()
#endif // VERBOSE
#endif // HAS_LORA #endif // HAS_LORA

View File

@ -457,7 +457,7 @@ void setup() {
clock_init(); clock_init();
#endif #endif
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN)
timesync_init(); // create loraserver time sync task timesync_init(); // create loraserver time sync task
#endif #endif

View File

@ -28,6 +28,21 @@ void calibrateTime(void) {
time_t t = 0; time_t t = 0;
uint16_t t_msec = 0; uint16_t t_msec = 0;
// kick off asychronous lora timesync if we have
#if (HAS_LORA) && (TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN)
timesync_sendReq();
#endif
// has RTC -> fallback to RTC time
#ifdef HAS_RTC
t = get_rtctime();
if (t) {
timeSource = _rtc;
goto finish;
}
#endif
// no RTC -> fallback to GPS time
#if (HAS_GPS) #if (HAS_GPS)
// fetch recent time from last NMEA record // fetch recent time from last NMEA record
t = fetch_gpsTime(&t_msec); t = fetch_gpsTime(&t_msec);
@ -37,21 +52,8 @@ void calibrateTime(void) {
} }
#endif #endif
// kick off asychronous lora timesync if we have // no local time source -> don't set time
#if (HAS_LORA) && (TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN) return;
send_timesync_req();
#endif
// no time from GPS -> fallback to RTC time while trying lora sync
#ifdef HAS_RTC
t = get_rtctime();
if (t) {
timeSource = _rtc;
goto finish;
}
#endif
goto finish;
finish: finish:

View File

@ -11,46 +11,59 @@ You may use timesync option 2 if you do not want or cannot accept this.
*/ */
#include "timesync.h" #if (HAS_LORA)
#if (TIME_SYNC_LORASERVER) && (TIME_SYNC_LORAWAN) && (HAS_LORA) #if (TIME_SYNC_LORASERVER) && (TIME_SYNC_LORAWAN)
#error Duplicate timesync method selected. You must select either LORASERVER or LORAWAN timesync. #error Duplicate timesync method selected. You must select either LORASERVER or LORAWAN timesync.
#endif #endif
#include "timesync.h"
#define WRAP(v, top) (v++ > top ? 0 : v)
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
static uint8_t time_sync_seqNo = (uint8_t)random(TIMEREQUEST_MAX_SEQNO);
#define WRAP(v, top) (v++ > top ? 0 : v)
// timesync option 1: use external timeserver (for LoRAWAN < 1.0.3)
#if (TIME_SYNC_LORASERVER) && (HAS_LORA)
static TaskHandle_t timeSyncReqTask = NULL;
static bool timeSyncPending = false; static bool timeSyncPending = false;
static uint8_t sample_idx = 0; static uint8_t time_sync_seqNo = (uint8_t)random(TIMEREQUEST_MAX_SEQNO),
static uint32_t timesync_timestamp[TIME_SYNC_SAMPLES][no_of_timestamps] = {0}; sample_idx;
static uint16_t timestamp_msec;
static uint32_t timestamp_sec,
timesync_timestamp[TIME_SYNC_SAMPLES][no_of_timestamps];
static TaskHandle_t timeSyncReqTask = NULL;
// send time request message // create task for timeserver handshake processing, called from main.cpp
void send_timesync_req(void) { void timesync_init() {
xTaskCreatePinnedToCore(timesync_processReq, // task function
"timesync_req", // name of task
2048, // stack size of task
(void *)1, // task parameter
3, // priority of the task
&timeSyncReqTask, // task handle
1); // CPU core
}
// kickoff asnychronous timesync handshake
void timesync_sendReq(void) {
// if a timesync handshake is pending then exit // if a timesync handshake is pending then exit
if (timeSyncPending) if (timeSyncPending)
return; return;
// else unblock timesync task // else clear array and unblock timesync task
else { else {
ESP_LOGI(TAG, "[%0.3f] Timeserver sync request started", millis() / 1000.0); ESP_LOGI(TAG, "[%0.3f] Timeserver sync request seqNo#%d started",
millis() / 1000.0, time_sync_seqNo);
sample_idx = 0;
xTaskNotifyGive(timeSyncReqTask); xTaskNotifyGive(timeSyncReqTask);
} }
} }
// task for sending time sync requests // task for processing time sync request
void IRAM_ATTR process_timesync_req(void *taskparameter) { void IRAM_ATTR timesync_processReq(void *taskparameter) {
uint32_t rcv_seq_no = TIMEREQUEST_FINISH, time_offset_ms; uint32_t rcv_seq_no = TIMEREQUEST_FINISH, time_offset_ms;
// this task is an endless loop, waiting in blocked mode, until it is // this task is an endless loop, waiting in blocked mode, until it is
// unblocked by send_timesync_req(). It then waits to be notified from // unblocked by timesync_sendReq(). It then waits to be notified from
// recv_timesync_ans(), which is called from RX callback in lorawan.cpp, each // recv_timesync_ans(), which is called from RX callback in lorawan.cpp, each
// time a timestamp from timeserver arrived. // time a timestamp from timeserver arrived.
@ -68,14 +81,26 @@ void IRAM_ATTR process_timesync_req(void *taskparameter) {
vTaskDelay(pdMS_TO_TICKS(5000)); vTaskDelay(pdMS_TO_TICKS(5000));
} }
// trigger and collect timestamp samples // clear timestamp array
timesync_timestamp[TIME_SYNC_SAMPLES][no_of_timestamps] = {0};
// trigger and collect samples in timestamp array
for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) {
// send timesync request to timeserver
// send timesync request to timeserver or networkserver
#if (TIME_SYNC_LORASERVER)
// timesync option 1: use external timeserver (for LoRAWAN < 1.0.3)
payload.reset(); payload.reset();
payload.addByte(time_sync_seqNo); payload.addByte(time_sync_seqNo);
SendPayload(TIMEPORT, prio_high); SendPayload(TIMEPORT, prio_high);
#elif (TIME_SYNC_LORAWAN)
// timesync option 2: use LoRAWAN network time (requires LoRAWAN >= 1.0.3)
LMIC_requestNetworkTime(DevTimeAns_Cb, &time_sync_seqNo);
// open a receive window to trigger DevTimeAns
LMIC_sendAlive();
#endif
// wait until recv_timesync_ans() signals a timestamp was received // wait until a timestamp was received
while (rcv_seq_no != time_sync_seqNo) { while (rcv_seq_no != time_sync_seqNo) {
if (xTaskNotifyWait(0x00, ULONG_MAX, &rcv_seq_no, if (xTaskNotifyWait(0x00, ULONG_MAX, &rcv_seq_no,
pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) ==
@ -86,26 +111,27 @@ void IRAM_ATTR process_timesync_req(void *taskparameter) {
} }
} }
// calculate time diff from collected timestamps // calculate time diff from received timestamp
time_offset_ms += timesync_timestamp[sample_idx][timesync_rx] - time_offset_ms += timesync_timestamp[sample_idx][timesync_rx] -
timesync_timestamp[sample_idx][timesync_tx]; timesync_timestamp[sample_idx][timesync_tx];
// increment and maybe wrap around seqNo, keeping it in time port range // increment and maybe wrap around seqNo, keeping it in time port range
WRAP(time_sync_seqNo, TIMEREQUEST_MAX_SEQNO); WRAP(time_sync_seqNo, TIMEREQUEST_MAX_SEQNO);
// increment index for timestamp array // increment index for timestamp array
sample_idx++; sample_idx++;
// if last cycle, send finish char for closing timesync handshake, // if last cycle, finish after, else pause until next cycle
// else wait until time has come for next cycle
if (i < TIME_SYNC_SAMPLES - 1) { // wait for next cycle if (i < TIME_SYNC_SAMPLES - 1) { // wait for next cycle
vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000));
} else { // finish timesync handshake } else {
#if (TIME_SYNC_LORASERVER)
// send finish char for closing timesync handshake
payload.reset(); payload.reset();
payload.addByte(TIMEREQUEST_FINISH); payload.addByte(TIMEREQUEST_FINISH);
SendPayload(RCMDPORT, prio_high); SendPayload(RCMDPORT, prio_high);
// open a receive window to get last time_sync_answer instantly // open a receive window to get last time_sync_answer instantly
LMIC_sendAlive(); LMIC_sendAlive();
#endif
} }
} // end of for loop to collect timestamp samples } // end of for loop to collect timestamp samples
@ -137,8 +163,8 @@ void IRAM_ATTR process_timesync_req(void *taskparameter) {
} // infinite while(1) } // infinite while(1)
} }
// called from lorawan.cpp // store incoming timestamps
void store_timestamp(uint32_t timestamp, timesync_t timestamp_type) { void timesync_storeReq(uint32_t timestamp, timesync_t timestamp_type) {
ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: timestamp(t%d)=%d", millis() / 1000.0, ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: timestamp(t%d)=%d", millis() / 1000.0,
time_sync_seqNo, sample_idx, timestamp_type, timestamp); time_sync_seqNo, sample_idx, timestamp_type, timestamp);
@ -146,17 +172,18 @@ void store_timestamp(uint32_t timestamp, timesync_t timestamp_type) {
timesync_timestamp[sample_idx][timestamp_type] = timestamp; timesync_timestamp[sample_idx][timestamp_type] = timestamp;
} }
// process timeserver timestamp answer, called by myRxCallback() in lorawan.cpp #if (TIME_SYNC_LORASERVER)
int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) { // evaluate timerserver's timestamp answer, called by myRxCallback() in
// lorawan.cpp
int recv_timeserver_ans(const uint8_t buf[], const uint8_t buf_len) {
/* /*
parse 7 byte timesync_answer: parse 6 byte timesync_answer:
byte meaning byte meaning
1 sequence number (taken from node's time_sync_req) 1 sequence number (taken from node's time_sync_req)
2 timezone in 15 minutes steps 2..5 current second (from epoch time 1970)
3..6 current second (from epoch time 1970) 6 1/250ths fractions of current second
7 1/250ths fractions of current second
*/ */
// if no timesync handshake is pending then exit // if no timesync handshake is pending then exit
@ -164,12 +191,12 @@ int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) {
return 0; // failure return 0; // failure
// extract 1 byte timerequest sequence number from payload // extract 1 byte timerequest sequence number from payload
uint8_t seq_no = buf[0]; uint8_t seqNo = buf[0];
buf++; buf++;
// if no time is available or spurious buffer then exit // if no time is available or spurious buffer then exit
if (buf_len != TIME_SYNC_FRAME_LENGTH) { if (buf_len != TIME_SYNC_FRAME_LENGTH) {
if (seq_no == 0xff) if (seqNo == 0xff)
ESP_LOGI(TAG, "[%0.3f] Timeserver error: no confident time available", ESP_LOGI(TAG, "[%0.3f] Timeserver error: no confident time available",
millis() / 1000.0); millis() / 1000.0);
else else
@ -183,11 +210,6 @@ int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) {
// pointers to 4 bytes msb order // pointers to 4 bytes msb order
uint32_t timestamp_sec, *timestamp_ptr; uint32_t timestamp_sec, *timestamp_ptr;
// extract 1 byte containing timezone offset
// one step being 15min * 60sec = 900sec
uint32_t timestamp_tzsec = buf[0] * 900; // timezone offset in secs
buf++;
// extract 4 bytes containing gateway time in UTC seconds since unix // extract 4 bytes containing gateway time in UTC seconds since unix
// epoch and convert it to uint32_t, octet order is big endian // epoch and convert it to uint32_t, octet order is big endian
timestamp_ptr = (uint32_t *)buf; timestamp_ptr = (uint32_t *)buf;
@ -204,15 +226,14 @@ int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) {
// we guess timepoint is recent if it is newer than code compile date // we guess timepoint is recent if it is newer than code compile date
if (timeIsValid(t)) { if (timeIsValid(t)) {
ESP_LOGD(TAG, "[%0.3f] Timesync request seq#%d rcvd at %0.3f", ESP_LOGD(TAG, "[%0.3f] Timesync request seq#%d rcvd at %0.3f",
millis() / 1000.0, seq_no, osticks2ms(os_getTime()) / 1000.0); millis() / 1000.0, seqNo, osticks2ms(os_getTime()) / 1000.0);
// store time received from gateway // store time received from gateway
store_timestamp(timestamp_sec, gwtime_sec); timesync_storeReq(timestamp_sec, gwtime_sec);
store_timestamp(timestamp_msec, gwtime_msec); timesync_storeReq(timestamp_msec, gwtime_msec);
store_timestamp(timestamp_tzsec, gwtime_tzsec);
// inform processing task // inform processing task
xTaskNotify(timeSyncReqTask, seq_no, eSetBits); xTaskNotify(timeSyncReqTask, seqNo, eSetBits);
return 1; // success return 1; // success
} else { } else {
@ -223,29 +244,9 @@ int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) {
} }
} }
// create task for timeserver handshake processing, called from main.cpp #elif (TIME_SYNC_LORAWAN)
void timesync_init() {
xTaskCreatePinnedToCore(process_timesync_req, // task function
"timesync_req", // name of task
2048, // stack size of task
(void *)1, // task parameter
3, // priority of the task
&timeSyncReqTask, // task handle
1); // CPU core
}
#endif void IRAM_ATTR DevTimeAns_Cb(void *pUserData, int flagSuccess) {
// timesync option 2: use LoRAWAN network time (requires LoRAWAN >= 1.0.3)
#if (TIME_SYNC_LORAWAN) && (HAS_LORA)
// send time request message
void send_timesync_req(void) {
LMIC_requestNetworkTime(process_timesync_req, &time_sync_seqNo);
}
void IRAM_ATTR process_timesync_req(void *pUserData, int flagSuccess) {
// Explicit conversion from void* to uint8_t* to avoid compiler errors // Explicit conversion from void* to uint8_t* to avoid compiler errors
uint8_t *seqNo = (uint8_t *)pUserData; uint8_t *seqNo = (uint8_t *)pUserData;
@ -260,35 +261,39 @@ void IRAM_ATTR process_timesync_req(void *pUserData, int flagSuccess) {
// the gateway received the time request // the gateway received the time request
lmic_time_reference_t lmicTime; lmic_time_reference_t lmicTime;
uint32_t networkTimeSec; if (flagSuccess != 1) {
uint16_t requestDelaymSec; ESP_LOGW(TAG, "Network did not answer time request");
goto Finish;
}
if ((flagSuccess != 1) || (time_sync_seqNo != *seqNo)) { if (time_sync_seqNo != *seqNo) {
ESP_LOGW(TAG, "LoRaWAN network did not answer time request"); ESP_LOGW(TAG, "Network timesync handshake failed, seqNo#%u, *seqNo");
goto Finish; goto Finish;
} }
// Populate lmic_time_reference // Populate lmic_time_reference
flagSuccess = LMIC_getNetworkTimeReference(&lmicTime); if ((LMIC_getNetworkTimeReference(&lmicTime)) != 1) {
if (flagSuccess != 1) { ESP_LOGW(TAG, "Network time request failed");
ESP_LOGW(TAG, "LoRaWAN time request failed"); goto Finish;
goto Finish; }
}
// Calculate UTCTime, considering the difference between GPS and UTC time // Calculate UTCTime, considering the difference between GPS and UTC time
networkTimeSec = lmicTime.tNetwork + GPS_UTC_DIFF; timestamp_sec = lmicTime.tNetwork + GPS_UTC_DIFF;
// Add delay between the instant the time was transmitted and the current time // Add delay between the instant the time was transmitted and the current time
requestDelaymSec = osticks2ms(os_getTime() - lmicTime.tLocal); timestamp_msec = osticks2ms(os_getTime() - lmicTime.tLocal);
// Update system time with time read from the network
setMyTime(networkTimeSec, requestDelaymSec, _lora);
Finish: // store time received from gateway
timesync_storeReq(timestamp_sec, gwtime_sec);
timesync_storeReq(timestamp_msec, gwtime_msec);
// end of time critical section: release app irq lock // inform processing task
unmask_user_IRQ(); xTaskNotify(timeSyncReqTask, *seqNo, eSetBits);
// increment and maybe wrap around seqNo, keeping it in time port range Finish :
WRAP(time_sync_seqNo, TIMEREQUEST_MAX_SEQNO); // end of time critical section: release app irq lock
unmask_user_IRQ();
}
} // user_request_network_time_callback #endif
#endif // TIME_SYNC_LORAWAN
#endif // HAS_LORA