deeply reworked timesync code
This commit is contained in:
parent
f1b13f11fa
commit
73a612779b
@ -20,13 +20,6 @@
|
||||
|
||||
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);
|
||||
void lora_setupForNetwork(bool preJoin);
|
||||
void lmictask(void *pvParameters);
|
||||
@ -36,19 +29,28 @@ void get_hard_deveui(uint8_t *pdeveui);
|
||||
void os_getDevKey(u1_t *buf);
|
||||
void os_getArtEui(u1_t *buf);
|
||||
void os_getDevEui(u1_t *buf);
|
||||
void showLoraKeys(void);
|
||||
void lora_send(void *pvParameters);
|
||||
void lora_enqueuedata(MessageBuffer_t *message);
|
||||
void lora_queuereset(void);
|
||||
void IRAM_ATTR myEventCallback(void *pUserData, ev_t ev);
|
||||
void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port,
|
||||
const uint8_t *pMsg, size_t nMsg);
|
||||
//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);
|
||||
void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||
size_t nMsg);
|
||||
void IRAM_ATTR myTxCallback(void *pUserData, int fSuccess);
|
||||
const char *getSfName(rps_t rps);
|
||||
const char *getBwName(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
|
@ -17,15 +17,18 @@ enum timesync_t {
|
||||
timesync_rx,
|
||||
gwtime_sec,
|
||||
gwtime_msec,
|
||||
gwtime_tzsec,
|
||||
no_of_timestamps
|
||||
};
|
||||
|
||||
void timesync_init(void);
|
||||
void send_timesync_req(void);
|
||||
int recv_timesync_ans(const uint8_t buf[], uint8_t buf_len);
|
||||
void store_timestamp(uint32_t timestamp, timesync_t timestamp_type);
|
||||
void IRAM_ATTR process_timesync_req(void *taskparameter);
|
||||
void IRAM_ATTR process_timesync_req(void *pVoidUserUTCTime, int flagSuccess);
|
||||
void timesync_sendReq(void);
|
||||
void timesync_storeReq(uint32_t timestamp, timesync_t timestamp_type);
|
||||
void IRAM_ATTR timesync_processReq(void *taskparameter);
|
||||
|
||||
#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
|
||||
|
179
src/lorawan.cpp
179
src/lorawan.cpp
@ -23,6 +23,7 @@ RTC_NOINIT_ATTR int RTCseqnoUp, RTCseqnoDn;
|
||||
QueueHandle_t LoraSendQueue;
|
||||
TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
|
||||
|
||||
#if (VERBOSE)
|
||||
// table of LORAWAN MAC messages sent by the network to the device
|
||||
// format: opcode, cmdname (max 19 chars), #bytes params
|
||||
// source: LoRaWAN 1.1 Specification (October 11, 2017)
|
||||
@ -46,6 +47,10 @@ static const mac_t MACup_table[] = {
|
||||
{0x0B, "RekeyInd", 1}, {0x0C, "ADRParamSetupAns", 0},
|
||||
{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 {
|
||||
|
||||
public:
|
||||
@ -265,18 +270,16 @@ void lora_send(void *pvParameters) {
|
||||
SendBuffer.MessageSize,
|
||||
(cfg.countermode & 0x02))) {
|
||||
|
||||
// switch (LMIC_sendWithCallback_strict(
|
||||
// SendBuffer.MessagePort, SendBuffer.Message,
|
||||
// SendBuffer.MessageSize, (cfg.countermode & 0x02), myTxCallback,
|
||||
// &SendBuffer.MessagePort)) {
|
||||
|
||||
case LMIC_ERROR_SUCCESS:
|
||||
// save current Fcnt to RTC RAM
|
||||
RTCseqnoUp = LMIC.seqnoUp;
|
||||
RTCseqnoDn = LMIC.seqnoDn;
|
||||
|
||||
#if (TIME_SYNC_LORASERVER)
|
||||
// if last packet sent was a timesync request, store TX timestamp
|
||||
if (SendBuffer.MessagePort == TIMEPORT)
|
||||
// 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
|
||||
|
||||
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
|
||||
switch (ev) {
|
||||
|
||||
case EV_TXCOMPLETE:
|
||||
// -> processed in lora_send()
|
||||
break;
|
||||
|
||||
case EV_RXCOMPLETE:
|
||||
// -> processed in myRxCallback()
|
||||
break;
|
||||
|
||||
case EV_JOINING:
|
||||
// do the network-specific setup prior to join.
|
||||
lora_setupForNetwork(true);
|
||||
@ -439,12 +451,6 @@ void myEventCallback(void *pUserData, ev_t ev) {
|
||||
lora_setupForNetwork(false);
|
||||
break;
|
||||
|
||||
case EV_TXCOMPLETE:
|
||||
// save current Fcnt to RTC RAM
|
||||
RTCseqnoUp = LMIC.seqnoUp;
|
||||
RTCseqnoDn = LMIC.seqnoDn;
|
||||
break;
|
||||
|
||||
case EV_JOIN_TXCOMPLETE:
|
||||
// replace descriptor from library with more descriptive term
|
||||
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);
|
||||
}
|
||||
|
||||
// receive message handler
|
||||
// event EV_RXCOMPLETE message handler
|
||||
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||
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)
|
||||
ESP_LOGI(TAG, "Received %u byte(s) of payload on port %u", nMsg, port);
|
||||
else if (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) {
|
||||
|
||||
// ignore mac messages
|
||||
// decode unpiggybacked mac messages if we want to print those
|
||||
#if (VERBOSE)
|
||||
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
|
||||
case RCMDPORT:
|
||||
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:
|
||||
|
||||
#if (TIME_SYNC_LORASERVER)
|
||||
// valid timesync answer -> call timesync processor
|
||||
if (port == TIMEPORT) {
|
||||
// store LMIC time when we received the timesync answer
|
||||
store_timestamp(osticks2ms(os_getTime()), timesync_rx);
|
||||
// get and store gwtime from payload
|
||||
recv_timesync_ans(pMsg, nMsg);
|
||||
break;
|
||||
// decode any piggybacked downlink MAC commands
|
||||
#if (VERBOSE)
|
||||
if (nMac) {
|
||||
// decoding yet to come
|
||||
// -> whe need to unwrap the MAC command from LMIC.frame before calling
|
||||
// mac_decode(LMIC.frame, nMac, MACdn_table, MACdn_tSize);
|
||||
}
|
||||
#endif
|
||||
#endif // VERBOSE
|
||||
|
||||
// unknown port -> display info
|
||||
ESP_LOGI(TAG, "Received data on unsupported port %u", port);
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
/*
|
||||
// event TRANSMIT COMPLETE message handler
|
||||
// event EV_TXCOMPLETE message handler
|
||||
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) {
|
||||
// LMIC did tx on *sendport -> nothing yet to do here
|
||||
RTCseqnoUp = LMIC.seqnoUp;
|
||||
RTCseqnoDn = LMIC.seqnoDn;
|
||||
} 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 *const t[] = {"FSK", "SF7", "SF8", "SF9",
|
||||
"SF10", "SF11", "SF12", "SF?"};
|
||||
@ -611,4 +583,41 @@ u1_t os_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
|
@ -457,7 +457,7 @@ void setup() {
|
||||
clock_init();
|
||||
#endif
|
||||
|
||||
#if (TIME_SYNC_LORASERVER)
|
||||
#if (TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN)
|
||||
timesync_init(); // create loraserver time sync task
|
||||
#endif
|
||||
|
||||
|
@ -28,6 +28,21 @@ void calibrateTime(void) {
|
||||
time_t t = 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)
|
||||
// fetch recent time from last NMEA record
|
||||
t = fetch_gpsTime(&t_msec);
|
||||
@ -37,21 +52,8 @@ void calibrateTime(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// kick off asychronous lora timesync if we have
|
||||
#if (HAS_LORA) && (TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN)
|
||||
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;
|
||||
// no local time source -> don't set time
|
||||
return;
|
||||
|
||||
finish:
|
||||
|
||||
|
187
src/timesync.cpp
187
src/timesync.cpp
@ -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.
|
||||
#endif
|
||||
|
||||
#include "timesync.h"
|
||||
|
||||
#define WRAP(v, top) (v++ > top ? 0 : v)
|
||||
|
||||
// Local logging tag
|
||||
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 uint8_t sample_idx = 0;
|
||||
static uint32_t timesync_timestamp[TIME_SYNC_SAMPLES][no_of_timestamps] = {0};
|
||||
static uint8_t time_sync_seqNo = (uint8_t)random(TIMEREQUEST_MAX_SEQNO),
|
||||
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
|
||||
void send_timesync_req(void) {
|
||||
// create task for timeserver handshake processing, called from main.cpp
|
||||
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 (timeSyncPending)
|
||||
return;
|
||||
// else unblock timesync task
|
||||
// else clear array and unblock timesync task
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// task for sending time sync requests
|
||||
void IRAM_ATTR process_timesync_req(void *taskparameter) {
|
||||
// task for processing time sync request
|
||||
void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
||||
|
||||
uint32_t rcv_seq_no = TIMEREQUEST_FINISH, time_offset_ms;
|
||||
|
||||
// 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
|
||||
// time a timestamp from timeserver arrived.
|
||||
|
||||
@ -68,14 +81,26 @@ void IRAM_ATTR process_timesync_req(void *taskparameter) {
|
||||
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++) {
|
||||
// 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.addByte(time_sync_seqNo);
|
||||
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) {
|
||||
if (xTaskNotifyWait(0x00, ULONG_MAX, &rcv_seq_no,
|
||||
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] -
|
||||
timesync_timestamp[sample_idx][timesync_tx];
|
||||
|
||||
// increment and maybe wrap around seqNo, keeping it in time port range
|
||||
WRAP(time_sync_seqNo, TIMEREQUEST_MAX_SEQNO);
|
||||
|
||||
// increment index for timestamp array
|
||||
sample_idx++;
|
||||
|
||||
// if last cycle, send finish char for closing timesync handshake,
|
||||
// else wait until time has come for next cycle
|
||||
// if last cycle, finish after, else pause until next cycle
|
||||
if (i < TIME_SYNC_SAMPLES - 1) { // wait for next cycle
|
||||
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.addByte(TIMEREQUEST_FINISH);
|
||||
SendPayload(RCMDPORT, prio_high);
|
||||
// open a receive window to get last time_sync_answer instantly
|
||||
LMIC_sendAlive();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end of for loop to collect timestamp samples
|
||||
@ -137,8 +163,8 @@ void IRAM_ATTR process_timesync_req(void *taskparameter) {
|
||||
} // infinite while(1)
|
||||
}
|
||||
|
||||
// called from lorawan.cpp
|
||||
void store_timestamp(uint32_t timestamp, timesync_t timestamp_type) {
|
||||
// store incoming timestamps
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
// process timeserver timestamp answer, called by myRxCallback() in lorawan.cpp
|
||||
int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) {
|
||||
#if (TIME_SYNC_LORASERVER)
|
||||
// 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
|
||||
1 sequence number (taken from node's time_sync_req)
|
||||
2 timezone in 15 minutes steps
|
||||
3..6 current second (from epoch time 1970)
|
||||
7 1/250ths fractions of current second
|
||||
2..5 current second (from epoch time 1970)
|
||||
6 1/250ths fractions of current second
|
||||
*/
|
||||
|
||||
// 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
|
||||
|
||||
// extract 1 byte timerequest sequence number from payload
|
||||
uint8_t seq_no = buf[0];
|
||||
uint8_t seqNo = buf[0];
|
||||
buf++;
|
||||
|
||||
// if no time is available or spurious buffer then exit
|
||||
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",
|
||||
millis() / 1000.0);
|
||||
else
|
||||
@ -183,11 +210,6 @@ int recv_timesync_ans(const uint8_t buf[], const uint8_t buf_len) {
|
||||
// pointers to 4 bytes msb order
|
||||
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
|
||||
// epoch and convert it to uint32_t, octet order is big endian
|
||||
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
|
||||
if (timeIsValid(t)) {
|
||||
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_timestamp(timestamp_sec, gwtime_sec);
|
||||
store_timestamp(timestamp_msec, gwtime_msec);
|
||||
store_timestamp(timestamp_tzsec, gwtime_tzsec);
|
||||
timesync_storeReq(timestamp_sec, gwtime_sec);
|
||||
timesync_storeReq(timestamp_msec, gwtime_msec);
|
||||
|
||||
// inform processing task
|
||||
xTaskNotify(timeSyncReqTask, seq_no, eSetBits);
|
||||
xTaskNotify(timeSyncReqTask, seqNo, eSetBits);
|
||||
|
||||
return 1; // success
|
||||
} 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
|
||||
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
|
||||
}
|
||||
#elif (TIME_SYNC_LORAWAN)
|
||||
|
||||
#endif
|
||||
|
||||
// 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) {
|
||||
void IRAM_ATTR DevTimeAns_Cb(void *pUserData, int flagSuccess) {
|
||||
// Explicit conversion from void* to uint8_t* to avoid compiler errors
|
||||
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
|
||||
lmic_time_reference_t lmicTime;
|
||||
|
||||
uint32_t networkTimeSec;
|
||||
uint16_t requestDelaymSec;
|
||||
if (flagSuccess != 1) {
|
||||
ESP_LOGW(TAG, "Network did not answer time request");
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
if ((flagSuccess != 1) || (time_sync_seqNo != *seqNo)) {
|
||||
ESP_LOGW(TAG, "LoRaWAN network did not answer time request");
|
||||
if (time_sync_seqNo != *seqNo) {
|
||||
ESP_LOGW(TAG, "Network timesync handshake failed, seqNo#%u, *seqNo");
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
// Populate lmic_time_reference
|
||||
flagSuccess = LMIC_getNetworkTimeReference(&lmicTime);
|
||||
if (flagSuccess != 1) {
|
||||
ESP_LOGW(TAG, "LoRaWAN time request failed");
|
||||
goto Finish;
|
||||
}
|
||||
if ((LMIC_getNetworkTimeReference(&lmicTime)) != 1) {
|
||||
ESP_LOGW(TAG, "Network time request failed");
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
// Calculate UTCTime, considering the difference between GPS and UTC time
|
||||
networkTimeSec = lmicTime.tNetwork + GPS_UTC_DIFF;
|
||||
// Add delay between the instant the time was transmitted and the current time
|
||||
requestDelaymSec = osticks2ms(os_getTime() - lmicTime.tLocal);
|
||||
// Update system time with time read from the network
|
||||
setMyTime(networkTimeSec, requestDelaymSec, _lora);
|
||||
// Calculate UTCTime, considering the difference between GPS and UTC time
|
||||
timestamp_sec = lmicTime.tNetwork + GPS_UTC_DIFF;
|
||||
// Add delay between the instant the time was transmitted and the current time
|
||||
timestamp_msec = osticks2ms(os_getTime() - lmicTime.tLocal);
|
||||
|
||||
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
|
||||
unmask_user_IRQ();
|
||||
// inform processing task
|
||||
xTaskNotify(timeSyncReqTask, *seqNo, eSetBits);
|
||||
|
||||
// increment and maybe wrap around seqNo, keeping it in time port range
|
||||
WRAP(time_sync_seqNo, TIMEREQUEST_MAX_SEQNO);
|
||||
Finish :
|
||||
// end of time critical section: release app irq lock
|
||||
unmask_user_IRQ();
|
||||
}
|
||||
|
||||
} // user_request_network_time_callback
|
||||
#endif // TIME_SYNC_LORAWAN
|
||||
#endif
|
||||
|
||||
#endif // HAS_LORA
|
Loading…
Reference in New Issue
Block a user