From e8a4d7983e176a0003e216f386c0b061319383b1 Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Sat, 27 Sep 2025 11:37:40 +0200 Subject: [PATCH] [LoRaWAN] Change `getMacDeviceTimeAns` to return current time --- .../LoRaWAN_Reference/LoRaWAN_Reference.ino | 13 ++++---- .../LoRaWAN_TS_Packages.ino | 14 ++++---- src/protocols/LoRaWAN/LoRaWAN.cpp | 33 ++++++++++++------- src/protocols/LoRaWAN/LoRaWAN.h | 17 +++++----- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino b/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino index 53b21b29..f39d08ba 100644 --- a/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino +++ b/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino @@ -178,13 +178,14 @@ void loop() { Serial.println(gwCnt); } - uint32_t networkTime = 0; - uint8_t fracSecond = 0; - if(node.getMacDeviceTimeAns(&networkTime, &fracSecond, true) == RADIOLIB_ERR_NONE) { + uint32_t timestamp = 0; + uint16_t milliseconds = 0; + if(node.getMacDeviceTimeAns(×tamp, &milliseconds, true) == RADIOLIB_ERR_NONE) { Serial.print(F("[LoRaWAN] DeviceTime Unix:\t")); - Serial.println(networkTime); - Serial.print(F("[LoRaWAN] DeviceTime second:\t1/")); - Serial.println(fracSecond); + Serial.println(timestamp); + Serial.print(F("[LoRaWAN] DeviceTime frac:\t")); + Serial.print(milliseconds); + Serial.println(F(" ms")); } } else { diff --git a/examples/LoRaWAN/LoRaWAN_TS_Packages/LoRaWAN_TS_Packages.ino b/examples/LoRaWAN/LoRaWAN_TS_Packages/LoRaWAN_TS_Packages.ino index 4b8cdb2e..df1cf5f3 100644 --- a/examples/LoRaWAN/LoRaWAN_TS_Packages/LoRaWAN_TS_Packages.ino +++ b/examples/LoRaWAN/LoRaWAN_TS_Packages/LoRaWAN_TS_Packages.ino @@ -134,14 +134,14 @@ void loop() { Serial.println(gwCnt); } - uint32_t networkTime = 0; - uint8_t fracSecond = 0; - if(node.getMacDeviceTimeAns(&networkTime, &fracSecond, true) == RADIOLIB_ERR_NONE) { + uint32_t timestamp = 0; + uint16_t milliseconds = 0; + if(node.getMacDeviceTimeAns(×tamp, &milliseconds, true) == RADIOLIB_ERR_NONE) { Serial.print(F("[LoRaWAN] DeviceTime Unix:\t")); - Serial.println(networkTime); - Serial.print(F("[LoRaWAN] DeviceTime second:\t")); - Serial.print(fracSecond); - Serial.println(F("/256")); + Serial.println(timestamp); + Serial.print(F("[LoRaWAN] DeviceTime frac:\t")); + Serial.print(milliseconds); + Serial.println(F(" ms")); } } else if(state == 0) { diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 14993dcb..7be2439c 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -1386,7 +1386,7 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u // if dutycycle is enabled and the time since last uplink + interval has not elapsed, return an error // but: don't check this for retransmissions if(!retrans && this->dutyCycleEnabled) { - if(this->rxDelayStart + (RadioLibTime_t)dutyCycleInterval(this->dutyCycle, this->lastToA) > this->tUplink) { + if(this->tUplinkEnd + (RadioLibTime_t)dutyCycleInterval(this->dutyCycle, this->lastToA) > this->tUplink) { return(RADIOLIB_ERR_UPLINK_UNAVAILABLE); } } @@ -1450,7 +1450,7 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u state = this->phyLayer->finishTransmit(); // set the timestamp so that we can measure when to start receiving - this->rxDelayStart = mod->hal->millis(); + this->tUplinkEnd = mod->hal->millis(); RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink sent <-- Rx Delay start"); // increase Time on Air of the uplink sequence @@ -1686,7 +1686,7 @@ int16_t LoRaWANNode::receiveDownlink() { Module* mod = this->phyLayer->getMod(); // if applicable, open Class C between uplink and Rx1 - RadioLibTime_t timeoutClassC = this->rxDelayStart + this->rxDelays[RADIOLIB_LORAWAN_RX1] - \ + RadioLibTime_t timeoutClassC = this->tUplinkEnd + this->rxDelays[RADIOLIB_LORAWAN_RX1] - \ mod->hal->millis() - 5*this->scanGuard; int16_t state = this->receiveClassC(timeoutClassC); RADIOLIB_ASSERT(state); @@ -1696,7 +1696,7 @@ int16_t LoRaWANNode::receiveDownlink() { &this->channels[RADIOLIB_LORAWAN_RX1], RADIOLIB_LORAWAN_RX1, this->rxDelays[RADIOLIB_LORAWAN_RX1], - this->rxDelayStart); + this->tUplinkEnd); RADIOLIB_ASSERT(state); // for LoRaWAN v1.1 Class C, there is no Rx2 window: it keeps RxC open uninterrupted @@ -1706,7 +1706,7 @@ int16_t LoRaWANNode::receiveDownlink() { } // for LoRaWAN v1.0.4 Class C, there is an RxC window between Rx1 and Rx2 - timeoutClassC = this->rxDelayStart + this->rxDelays[RADIOLIB_LORAWAN_RX2] - \ + timeoutClassC = this->tUplinkEnd + this->rxDelays[RADIOLIB_LORAWAN_RX2] - \ mod->hal->millis() - 5*this->scanGuard; state = this->receiveClassC(timeoutClassC); RADIOLIB_ASSERT(state); @@ -1716,7 +1716,7 @@ int16_t LoRaWANNode::receiveDownlink() { &this->channels[RADIOLIB_LORAWAN_RX2], RADIOLIB_LORAWAN_RX2, this->rxDelays[RADIOLIB_LORAWAN_RX2], - this->rxDelayStart); + this->tUplinkEnd); RADIOLIB_ASSERT(state); state = this->receiveClassC(); @@ -2862,19 +2862,28 @@ int16_t LoRaWANNode::getMacLinkCheckAns(uint8_t* margin, uint8_t* gwCnt) { return(RADIOLIB_ERR_NONE); } -int16_t LoRaWANNode::getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, bool returnUnix) { +int16_t LoRaWANNode::getMacDeviceTimeAns(uint32_t* timestamp, uint16_t* fraction, bool returnUnix) { uint8_t payload[5] = { 0 }; int16_t state = this->getMacPayload(RADIOLIB_LORAWAN_MAC_DEVICE_TIME, this->fOptsDown, this->fOptsDownLen, payload, RADIOLIB_LORAWAN_DOWNLINK); RADIOLIB_ASSERT(state); - if(gpsEpoch) { - *gpsEpoch = LoRaWANNode::ntoh(&payload[0]); + Module* mod = this->phyLayer->getMod(); + + // calculate the millisecond fraction + RadioLibTime_t ms = (RadioLibTime_t)payload[4] * 1000UL / 256UL; + + // add offset between current time and end of uplink transmission + ms += mod->hal->millis() - this->tUplinkEnd; + + if(timestamp) { + *timestamp = LoRaWANNode::ntoh(&payload[0]); + *timestamp += ms / 1000; if(returnUnix) { uint32_t unixOffset = 315964800UL - 18UL; // 18 leap seconds since GPS epoch (Jan. 6th 1980) - *gpsEpoch += unixOffset; + *timestamp += unixOffset; } } - if(fraction) { *fraction = payload[4]; } + if(fraction) { *fraction = ms % 1000; } return(RADIOLIB_ERR_NONE); } @@ -3535,7 +3544,7 @@ RadioLibTime_t LoRaWANNode::dutyCycleInterval(RadioLibTime_t msPerHour, RadioLib RadioLibTime_t LoRaWANNode::timeUntilUplink() { Module* mod = this->phyLayer->getMod(); - RadioLibTime_t nextUplink = this->rxDelayStart + dutyCycleInterval(this->dutyCycle, this->lastToA); + RadioLibTime_t nextUplink = this->tUplinkEnd + dutyCycleInterval(this->dutyCycle, this->lastToA); if(mod->hal->millis() > nextUplink){ return(0); } diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 05b0067b..de2cc055 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -770,8 +770,6 @@ class LoRaWANNode { /*! \brief Returns the quality of connectivity after requesting a LinkCheck MAC command. - Returns 'true' if a network response was successfully parsed. - Returns 'false' if there was no network response / parsing failed. \param margin Link margin in dB of LinkCheckReq demodulation at gateway side. \param gwCnt Number of gateways that received the LinkCheckReq. \returns \ref status_codes @@ -780,14 +778,15 @@ class LoRaWANNode { /*! \brief Returns the network time after requesting a DeviceTime MAC command. - Returns 'true' if a network response was successfully parsed. - Returns 'false' if there was no network response / parsing failed. - \param gpsEpoch Number of seconds since GPS epoch (Jan. 6th 1980) - \param fraction Fractional-second, in 1/256-second steps - \param returnUnix If true, returns Unix timestamp instead of GPS (default true) + Note: the network returns the time at the end of the uplink transmission. + The return value of this function automatically adjusts to the current time. + This time is supposed to be <100ms accurate, but may be accurate to 1 second. + \param timestamp Number of seconds since GPS epoch (Jan. 6th 1980). + \param milliseconds Milliseconds on top of the timestamp. + \param returnUnix If true, returns Unix timestamp instead of GPS (default true). \returns \ref status_codes */ - int16_t getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, bool returnUnix = true); + int16_t getMacDeviceTimeAns(uint32_t* timestamp, uint16_t* milliseconds, bool returnUnix = true); /*! \brief Set uplink datarate. This should not be used when ADR is enabled. @@ -1086,7 +1085,7 @@ class LoRaWANNode { RadioLibTime_t lastToA = 0; // timestamp to measure the Rx1/2 delay (from uplink end) - RadioLibTime_t rxDelayStart = 0; + RadioLibTime_t tUplinkEnd = 0; // duration of SPI transaction for phyLayer->launchMode() RadioLibTime_t launchDuration = 0;