deeply reworked timesync code
This commit is contained in:
parent
f1b13f11fa
commit
73a612779b
@ -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
|
@ -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
|
||||||
|
179
src/lorawan.cpp
179
src/lorawan.cpp
@ -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
|
@ -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
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
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.
|
#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
|
Loading…
Reference in New Issue
Block a user