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