From d6b6a0bf51fb474f2c5c24e6dfdb042a9b4f3779 Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Mon, 3 Feb 2025 00:15:50 +0100 Subject: [PATCH] [LoRaWAN] A-synchronize transmissions (#1410) --- src/protocols/LoRaWAN/LoRaWAN.cpp | 72 ++++++++++++++++++++++++++++--- src/protocols/LoRaWAN/LoRaWAN.h | 2 +- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index eeccc3fa..5729f834 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -906,14 +906,22 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent) RADIOLIB_ASSERT(state); // calculate JoinRequest time-on-air in milliseconds + RadioLibTime_t toa = this->phyLayer->getTimeOnAir(RADIOLIB_LORAWAN_JOIN_REQUEST_LEN) / 1000; + if(this->dwellTimeUp) { - RadioLibTime_t toa = this->phyLayer->getTimeOnAir(RADIOLIB_LORAWAN_JOIN_REQUEST_LEN) / 1000; if(toa > this->dwellTimeUp) { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Dwell time exceeded: ToA = %lu, max = %d", (unsigned long)toa, this->dwellTimeUp); return(RADIOLIB_ERR_DWELL_TIME_EXCEEDED); } } + RadioModeConfig_t modeCfg; + modeCfg.transmit.data = joinRequestMsg; + modeCfg.transmit.len = RADIOLIB_LORAWAN_JOIN_REQUEST_LEN; + modeCfg.transmit.addr = 0; + state = this->phyLayer->stageMode(RADIOLIB_RADIO_MODE_TX, modeCfg); + RADIOLIB_ASSERT(state); + // if requested, delay until transmitting JoinRequest RadioLibTime_t tNow = mod->hal->millis(); if(this->tUplink > tNow) { @@ -923,8 +931,26 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent) } } - // send it - state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN); + // start transmission + state = this->phyLayer->launchMode(); + RADIOLIB_ASSERT(state); + + // sleep for the duration of the transmission + mod->hal->delay(toa); + RadioLibTime_t txEnd = mod->hal->millis(); + + // wait for an additional transmission duration as Tx timeout period + while(!mod->hal->digitalRead(mod->getIrq())) { + // yield for multi-threaded platforms + mod->hal->yield(); + + if(mod->hal->millis() > txEnd + toa) { + return(RADIOLIB_ERR_TX_TIMEOUT); + } + } + state = this->phyLayer->finishTransmit(); + + // set the timestamp so that we can measure when to start receiving this->rxDelayStart = mod->hal->millis(); RADIOLIB_ASSERT(state); RADIOLIB_DEBUG_PROTOCOL_PRINTLN("JoinRequest sent (DevNonce = %d) <-- Rx Delay start", this->devNonce); @@ -935,7 +961,7 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent) LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_DEV_NONCE], this->devNonce); // set the Time on Air of the JoinRequest - this->lastToA = this->phyLayer->getTimeOnAir(RADIOLIB_LORAWAN_JOIN_REQUEST_LEN) / 1000; + this->lastToA = toa; // configure Rx1 and Rx2 delay for JoinAccept message - these are re-configured once a valid JoinAccept is received this->rxDelays[1] = RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_1_MS; @@ -1270,7 +1296,6 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u Module* mod = this->phyLayer->getMod(); // check if the Rx windows were closed after sending the previous uplink - // this FORCES a user to call downlink() after an uplink() if(this->rxDelayEnd < this->rxDelayStart) { // not enough time elapsed since the last uplink, we may still be in an Rx window return(RADIOLIB_ERR_UPLINK_UNAVAILABLE); @@ -1295,6 +1320,22 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u RADIOLIB_LORAWAN_UPLINK, this->txPowerMax - 2*this->txPowerSteps); RADIOLIB_ASSERT(state); + + // check whether dwell time limitation is exceeded + RadioLibTime_t toa = this->phyLayer->getTimeOnAir(len) / 1000; + if(this->dwellTimeUp) { + if(toa > this->dwellTimeUp) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Dwell time exceeded: ToA = %lu, max = %d", (unsigned long)toa, this->dwellTimeUp); + return(RADIOLIB_ERR_DWELL_TIME_EXCEEDED); + } + } + + RadioModeConfig_t modeCfg; + modeCfg.transmit.data = in; + modeCfg.transmit.len = len; + modeCfg.transmit.addr = 0; + state = this->phyLayer->stageMode(RADIOLIB_RADIO_MODE_TX, modeCfg); + RADIOLIB_ASSERT(state); // if requested, wait until transmitting uplink tNow = mod->hal->millis(); @@ -1305,14 +1346,31 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u } } - state = this->phyLayer->transmit(in, len); + // start transmission + state = this->phyLayer->launchMode(); + RADIOLIB_ASSERT(state); + + // sleep for the duration of the transmission + mod->hal->delay(toa); + RadioLibTime_t txEnd = mod->hal->millis(); + + // wait for an additional transmission duration as Tx timeout period + while(!mod->hal->digitalRead(mod->getIrq())) { + // yield for multi-threaded platforms + mod->hal->yield(); + + if(mod->hal->millis() > txEnd + toa) { + return(RADIOLIB_ERR_TX_TIMEOUT); + } + } + state = this->phyLayer->finishTransmit(); // set the timestamp so that we can measure when to start receiving this->rxDelayStart = mod->hal->millis(); RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink sent <-- Rx Delay start"); // increase Time on Air of the uplink sequence - this->lastToA += this->phyLayer->getTimeOnAir(len) / 1000; + this->lastToA += toa; return(state); } diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 6df0e099..48272336 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -850,7 +850,7 @@ class LoRaWANNode { 500 is the **maximum** value, but it is not a good idea to go anywhere near that. If you have to go above 50 you probably have a bug somewhere. Check your device timing. */ - RadioLibTime_t scanGuard = 4; + RadioLibTime_t scanGuard = 5; #if !RADIOLIB_GODMODE protected: