diff --git a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino index 63dfe2bf..59a92bf4 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino @@ -87,17 +87,28 @@ void setup() { // and can be set to NULL // some frequency bands only use a subset of the available channels - // you can set the starting channel and their number - // for example, the following corresponds to US915 FSB2 in TTN + // you can select the specific band or set the first channel and last channel + // for example, either of the following corresponds to US915 FSB2 in TTN /* - node.startChannel = 8; - node.numChannels = 8; + node.selectSubband(2); + node.selectSubband(8, 16); */ // now we can start the activation // this can take up to 20 seconds, and requires a LoRaWAN gateway in range + // a specific starting-datarate can be selected in dynamic bands (e.g. EU868): + /* + uint8_t joinDr = 8; + state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDr); + */ + // a specific band can be selected for joining in fixed bands (e.g. US915): + /* + uint8_t subband = 2; + state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, subband); + */ Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... ")); state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); + if(state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); } else { @@ -107,11 +118,11 @@ void setup() { } // after the device has been activated, - // network can be rejoined after device power cycle - // by calling "begin" + // the session can be restored without rejoining after device power cycle + // on EEPROM-enabled boards by calling "restore" /* Serial.print(F("[LoRaWAN] Resuming previous session ... ")); - state = node.begin(); + state = node.restore(); if(state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); } else { @@ -178,6 +189,12 @@ void loop() { Serial.println(state); } + // on EEPROM enabled boards, you can save the current session + // by calling "saveSession" which allows retrieving the session after reboot or deepsleep + /* + node.saveSession(); + */ + // wait before sending another packet - delay(10000); + delay(30000); } diff --git a/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino b/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino index 2f1d4f18..91b49d71 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino @@ -83,16 +83,27 @@ void setup() { // and can be set to NULL // some frequency bands only use a subset of the available channels - // you can set the starting channel and their number - // for example, the following corresponds to US915 FSB2 in TTN + // you can select the specific band or set the first channel and last channel + // for example, either of the following corresponds to US915 FSB2 in TTN /* - node.startChannel = 8; - node.numChannels = 8; + node.selectSubband(2); + node.selectSubband(8, 16); + */ + + // if using EU868 on ABP in TTN, you need to set the SF for RX2 window manually + /* + node.rx2.drMax = 3; + */ + + // to start a LoRaWAN v1.1 session, the user should also provide + // fNwkSIntKey and sNwkSIntKey similar to nwkSKey and appSKey + /* + state = node.beginABP(devAddr, nwkSKey, appSKey, fNwkSIntKey, sNwkSIntKey); */ // start the device by directly providing the encryption keys and device address Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... ")); - state = node.beginAPB(devAddr, (uint8_t*)nwkSKey, (uint8_t*)appSKey); + state = node.beginABP(devAddr, nwkSKey, appSKey); if(state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); } else { @@ -102,11 +113,11 @@ void setup() { } // after the device has been activated, - // network can be rejoined after device power cycle - // by calling "begin" + // the session can be restored without rejoining after device power cycle + // on EEPROM-enabled boards by calling "restore" /* Serial.print(F("[LoRaWAN] Resuming previous session ... ")); - state = node.begin(); + state = node.restore(); if(state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); } else { @@ -173,6 +184,12 @@ void loop() { Serial.println(state); } + // on EEPROM enabled boards, you can save the current session + // by calling "saveSession" which allows retrieving the session after reboot or deepsleep + /* + node.saveSession(); + */ + // wait before sending another packet delay(10000); } diff --git a/src/TypeDef.h b/src/TypeDef.h index 0ebcbbb1..2e16145c 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -553,6 +553,11 @@ */ #define RADIOLIB_ERR_A_FCNT_DOWN_INVALID (-1114) +/*! + \brief Datarate requested by user is invalid. +*/ +#define RADIOLIB_ERR_DATA_RATE_INVALID (-1115) + /*! \} */ diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 7f3e2385..ea50e843 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -1443,13 +1443,17 @@ uint32_t SX126x::calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs) { } int16_t SX126x::irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask) { - irqFlags = 0b0000000000000010; // RxDone - irqMask = 0b0000000000000010; - irqFlags |= 0b0000001000000000; // RxTimeout - irqMask |= 0b0000001000000000; + irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT; // flags that can appear in the IRQ register + irqMask = RADIOLIB_SX126X_IRQ_RX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT; // flags that will trigger DIO0 return(RADIOLIB_ERR_NONE); } +bool SX126x::isRxTimeout() { + uint16_t irq = getIrqStatus(); + bool rxTimedOut = irq & RADIOLIB_SX126X_IRQ_TIMEOUT; + return(rxTimedOut); +} + int16_t SX126x::implicitHeader(size_t len) { return(setHeaderType(RADIOLIB_SX126X_LORA_HEADER_IMPLICIT, len)); } diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index ffef9fbd..5e1945b4 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -965,6 +965,12 @@ class SX126x: public PhysicalLayer { */ int16_t irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask); + /*! + \brief Check whether the IRQ bit for RxTimeout is set + \returns \ref RxTimeout IRQ is set + */ + bool isRxTimeout(); + /*! \brief Set implicit header mode for future reception/transmission. \param len Payload length in bytes. diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 3f41c618..8d2c7a73 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -1245,10 +1245,8 @@ uint32_t SX127x::getTimeOnAir(size_t len) { } uint32_t SX127x::calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs) { - (void)numSymbols; // not used for these modules - // numSymbols += (109 / 4) + 1; - float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth; - numSymbols = timeoutUs / symbolLength + 1; + (void)timeoutUs; + numSymbols = 20; return(numSymbols); } diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 6ff20c69..22d476e3 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -174,7 +174,7 @@ int16_t LoRaWANNode::restoreChannels() { } #endif -int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force) { +int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, uint8_t drJoinSubband, bool force) { // check if we actually need to send the join request Module* mod = this->phyLayer->getMod(); @@ -191,7 +191,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe RADIOLIB_ASSERT(state); // setup uplink/downlink frequencies and datarates - state = this->selectChannelsJR(this->devNonce); + state = this->selectChannelsJR(this->devNonce, drJoinSubband); RADIOLIB_ASSERT(state); // configure for uplink with default configuration @@ -617,16 +617,16 @@ int16_t LoRaWANNode::saveChannels() { #if defined(RADIOLIB_BUILD_ARDUINO) -int16_t LoRaWANNode::uplink(String& str, uint8_t port, bool isConfirmed, bool adrEnabled) { - return(this->uplink(str.c_str(), port, isConfirmed, adrEnabled)); +int16_t LoRaWANNode::uplink(String& str, uint8_t port, bool isConfirmed) { + return(this->uplink(str.c_str(), port, isConfirmed)); } #endif -int16_t LoRaWANNode::uplink(const char* str, uint8_t port, bool isConfirmed, bool adrEnabled) { - return(this->uplink((uint8_t*)str, strlen(str), port, isConfirmed, adrEnabled)); +int16_t LoRaWANNode::uplink(const char* str, uint8_t port, bool isConfirmed) { + return(this->uplink((uint8_t*)str, strlen(str), port, isConfirmed)); } -int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed, bool adrEnabled) { +int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed) { Module* mod = this->phyLayer->getMod(); // check if sufficient time has elapsed since the last uplink @@ -734,7 +734,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf // length of fopts will be added later uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] = 0x00; - if(adrEnabled) { + if(this->adrEnabled) { uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ADR_ENABLED; if(adrAckReq) { uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ADR_ACK_REQ; @@ -1284,22 +1284,20 @@ int16_t LoRaWANNode::setPhyProperties() { } int16_t LoRaWANNode::setupChannels(uint8_t* cfList) { - uint8_t num = 0; - LoRaWANChannel_t chnl; + size_t num = 0; // in case of frequency list-type band, copy the default TX channels into the available channels, with RX1 = TX - if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) { - for(uint8_t i = 0; i < 3; i++) { - chnl = this->band->txFreqs[i]; - if(chnl.enabled) { - availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = chnl; - availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][num] = chnl; - RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", num, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].freq); - num++; - } + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + // copy the default defined channels into the first slots + for(; num < 3 && this->band->txFreqs[num].enabled; num++) { + availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = this->band->txFreqs[num]; + availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][num] = this->band->txFreqs[num]; + RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].idx, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].freq); } + // if there is a cflist present, parse its frequencies into the next five slots, with datarate range copied from default channel 0 if(cfList != nullptr) { - for(uint8_t i = 0; i < 5; i++) { + for(uint8_t i = 0; i < 5; i++, num++) { + LoRaWANChannel_t chnl; chnl.enabled = true; chnl.idx = num; uint32_t freq = LoRaWANNode::ntoh(&cfList[3*i], 3); @@ -1308,97 +1306,175 @@ int16_t LoRaWANNode::setupChannels(uint8_t* cfList) { chnl.drMax = this->band->txFreqs[0].drMax; // drMax is equal for all channels availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = chnl; availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][num] = chnl; - RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", num, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].freq); - num++; + RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", chnl.idx, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].freq); } } - } else { // RADIOLIB_LORAWAN_CFLIST_TYPE_MASK - uint8_t chSpan = 0; - uint8_t chNum = 0; - // in case of mask-type bands, copy those frequencies that are masked true into the available TX channels - for(uint8_t i = 0; i < 5; i++) { - uint16_t mask = LoRaWANNode::ntoh(&cfList[2*i]); - RADIOLIB_DEBUG_PRINTLN("mask[%d] = 0x%04x", i, mask); - for(uint8_t j = 0; j < 16; j++) { - // if we must roll over to next span, reset chNum and move to next channel span - if(chNum >= this->band->txSpans[chSpan].numChannels) { - chNum = 0; - chSpan++; - } - - if(mask & (1UL << j)) { - if(chSpan >= this->band->numTxSpans || chNum >= this->band->txSpans[chSpan].numChannels) { - RADIOLIB_DEBUG_PRINTLN("channel bitmask overrun!"); - return(RADIOLIB_ERR_UNKNOWN); + + } else { // RADIOLIB_LORAWAN_BAND_FIXED + if(cfList != nullptr) { + uint8_t chSpan = 0; + uint8_t chNum = 0; + // in case of mask-type bands, copy those frequencies that are masked true into the available TX channels + for(size_t chMaskCntl = 0; chMaskCntl < 5; chMaskCntl++) { + uint16_t mask = LoRaWANNode::ntoh(&cfList[2*chMaskCntl]); + RADIOLIB_DEBUG_PRINTLN("mask[%d] = 0x%04x", chMaskCntl, mask); + for(size_t i = 0; i < 16; i++) { + // if we must roll over to next span, reset chNum and move to next channel span + if(chNum >= this->band->txSpans[chSpan].numChannels) { + chNum = 0; + chSpan++; } - chnl.enabled = true; - chnl.idx = i*16 + j; - chnl.freq = this->band->txSpans[chSpan].freqStart + chNum*this->band->txSpans[chSpan].freqStep; - chnl.drMin = this->band->txSpans[chSpan].drMin; - chnl.drMax = this->band->txSpans[chSpan].drMax; - availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = chnl; - RADIOLIB_DEBUG_PRINTLN("Channel UL %d frequency = %f MHz", num, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].freq); - num++; + + if(mask & (1UL << i)) { + if(chSpan >= this->band->numTxSpans) { + RADIOLIB_DEBUG_PRINTLN("channel bitmask overrun!"); + return(RADIOLIB_ERR_UNKNOWN); + } + LoRaWANChannel_t chnl; + chnl.enabled = true; + chnl.idx = chMaskCntl*16 + i; + chnl.freq = this->band->txSpans[chSpan].freqStart + chNum*this->band->txSpans[chSpan].freqStep; + chnl.drMin = this->band->txSpans[chSpan].drMin; + chnl.drMax = this->band->txSpans[chSpan].drMax; + availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = chnl; + // downlink channels are dynamically calculated on each uplink in selectChannels() + RADIOLIB_DEBUG_PRINTLN("Channel UL %d frequency = %f MHz", num, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].freq); + num++; + } + chNum++; } - chNum++; } } } return(RADIOLIB_ERR_NONE); } -int16_t LoRaWANNode::selectChannelsJR(uint16_t devNonce) { - LoRaWANChannel_t channelUp; - LoRaWANChannel_t channelDn; - if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) { - // count the number of available channels for a join-request - uint8_t numJRChannels = 0; - for(uint8_t i = 0; i < 3; i++) { - if(this->band->txFreqs[i].idx != RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) { - numJRChannels++; - } - if(this->band->txJoinReq[i].idx != RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) { - numJRChannels++; - } - } - uint8_t channelId = devNonce % numJRChannels; // cycle through channels (seed with devNonce) - if(channelId < 3) { - channelUp = this->band->txFreqs[channelId]; - } else { - channelUp = this->band->txJoinReq[channelId - 3]; - } - channelDn = channelUp; // RX1 is equal to TX +int16_t LoRaWANNode::selectSubband(uint8_t idx) { + int16_t state = this->selectSubband((idx - 1) * 8, idx * 8); + return(state); +} - // configure data rates for TX and RX1: for TX the (floored) average of min and max; for RX1 identical with base offset - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = int((channelUp.drMax + channelUp.drMin) / 2); - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = getDownlinkDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], - this->rx1DrOffset, this->band->rx1DataRateBase, channelDn.drMin, channelDn.drMax); - } else { // RADIOLIB_LORAWAN_CFLIST_TYPE_MASK +int16_t LoRaWANNode::selectSubband(uint8_t startChannel, uint8_t endChannel) { + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + RADIOLIB_DEBUG_PRINTLN("This is a dynamic band plan which does not support subbands"); + return(RADIOLIB_ERR_INVALID_CHANNEL); + } + + uint8_t numChannels = endChannel - startChannel; + if(startChannel > this->band->txSpans[0].numChannels) { + RADIOLIB_DEBUG_PRINTLN("There are only %d channels available in this band", this->band->txSpans[0].numChannels); + return(RADIOLIB_ERR_INVALID_CHANNEL); + } + if(startChannel + numChannels > this->band->txSpans[0].numChannels) { + numChannels = this->band->txSpans[0].numChannels - startChannel; + RADIOLIB_DEBUG_PRINTLN("Could only select %d channels due to end of band", numChannels); + } + if(numChannels > RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS) { + numChannels = RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; + RADIOLIB_DEBUG_PRINTLN("Could only select %d channels due to specified limit", numChannels); + } + + LoRaWANChannel_t chnl; + for(size_t chNum = 0; chNum < numChannels; chNum++) { + chnl.enabled = true; + chnl.idx = startChannel + chNum; + chnl.freq = this->band->txSpans[0].freqStart + chnl.idx*this->band->txSpans[0].freqStep; + chnl.drMin = this->band->txSpans[0].drMin; + chnl.drMax = this->band->txSpans[0].drMax; + availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chNum] = chnl; + // downlink channel is dynamically calculated on each uplink in selectChannels() + RADIOLIB_DEBUG_PRINTLN("Channel UL %d frequency = %f MHz", chNum, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chNum].freq); + } + return(RADIOLIB_ERR_NONE); +} + +int16_t LoRaWANNode::selectChannelsJR(uint16_t devNonce, uint8_t drJoinSubband) { + LoRaWANChannel_t channelUp; + LoRaWANChannel_t channelDown; + uint8_t drUp; + uint8_t drDown; + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + // count the number of available channels for a join-request (default channels + join-request channels) + uint8_t numJRChannels = 0; + for(size_t i = 0; i < 3; i++) { + if(this->band->txFreqs[i].enabled) { + numJRChannels++; + } + if(this->band->txJoinReq[i].enabled) { + numJRChannels++; + } + } + + // cycle through the available channels (seed with devNonce) + uint8_t channelId = devNonce % numJRChannels; + + // find the channel whose index is selected + for(size_t i = 0; i < 3; i++) { + if(this->band->txFreqs[i].idx == channelId) { + channelUp = this->band->txFreqs[i]; + break; + } + if(this->band->txJoinReq[i].idx == channelId) { + channelUp = this->band->txJoinReq[i]; + } + } + + // if join datarate is user-specified and valid, select that value; otherwise use + if(drJoinSubband != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + if(drJoinSubband >= channelUp.drMin && drJoinSubband <= channelUp.drMax) { + drUp = drJoinSubband; + } else { + RADIOLIB_DEBUG_PRINTLN("Datarate %d is not valid (min: %d, max %d) - using default", drJoinSubband, channelUp.drMin, channelUp.drMax); + drJoinSubband = RADIOLIB_LORAWAN_DATA_RATE_UNUSED; + } + } + if(drJoinSubband == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + drUp = int((channelUp.drMax + channelUp.drMin) / 2); + } + + // derive the downlink channel and datarate from the uplink channel and datarate + channelDown = channelUp; + drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase, channelDown.drMin, channelDown.drMax); + + } else { // RADIOLIB_LORAWAN_BAND_FIXED channelUp.enabled = true; uint8_t numBlocks = this->band->txSpans[0].numChannels / 8; // calculate number of 8-channel blocks - uint8_t numBlockChannels = 8 + this->band->txSpans[1].numChannels > 0 ? 1 : 0; // add a 9th channel if there's a second span + uint8_t numBlockChannels = 8 + (this->band->numTxSpans == 2 ? 1 : 0); // add a 9th channel if there's a second span uint8_t blockID = devNonce % numBlocks; // currently selected block (seed with devNonce) + // if the user defined a specific subband, use that + if(drJoinSubband == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + blockID = (drJoinSubband - 1); + } uint8_t channelID = this->phyLayer->random(numBlockChannels); // select randomly from these 8 or 9 channels + RADIOLIB_DEBUG_PRINTLN("blocks: %d, channels/block: %d, blockID: %d, channelID: %d", numBlocks, numBlockChannels, blockID, channelID); + + // if channel 0-7 is selected, retrieve this channel from span 0; otherwise span 1 uint8_t spanID; if(channelID < 8) { spanID = 0; - channelUp.idx = blockID * numBlockChannels + channelID; + channelUp.idx = blockID * 8 + channelID; } else { spanID = 1; channelUp.idx = blockID; } channelUp.freq = this->band->txSpans[spanID].freqStart + channelUp.idx*this->band->txSpans[spanID].freqStep; - - channelDn.idx = blockID % this->band->rx1Span.numChannels; - channelDn.freq = this->band->rx1Span.freqStart + channelDn.idx*this->band->rx1Span.freqStep; - // configure data rates for TX and RX1: for TX the specified value for this band; for RX1 identical with base offset - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = this->band->txSpans[spanID].joinRequestDataRate; - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = getDownlinkDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], - this->rx1DrOffset, this->band->rx1DataRateBase, channelDn.drMin, channelDn.drMax); + // for fixed channel plans, the user-specified datarate is ignored and span-specific value must be used + drUp = this->band->txSpans[spanID].joinRequestDataRate; + + // derive the downlink channel and datarate from the uplink channel and datarate + channelDown.enabled = true; + channelDown.idx = channelID % this->band->rx1Span.numChannels; + channelDown.freq = this->band->rx1Span.freqStart + channelDown.idx*this->band->rx1Span.freqStep; + channelDown.drMin = this->band->rx1Span.drMin; + channelDown.drMax = this->band->rx1Span.drMax; + drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase, channelDown.drMin, channelDown.drMax); + } this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = channelUp; - this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = channelDn; + this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = channelDown; + this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = drUp; + this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown; return(RADIOLIB_ERR_NONE); } @@ -1418,14 +1494,20 @@ int16_t LoRaWANNode::selectChannels() { break; } } + if(numChannels == 0) { + RADIOLIB_DEBUG_PRINTLN("There are no channels defined - are you in ABP mode with no defined subband?"); + return(RADIOLIB_ERR_INVALID_CHANNEL); + } // select a random ID & channel from the list of enabled and possible channels uint8_t channelID = channelsEnabled[this->phyLayer->random(numChannels)]; this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][channelID]; - // in case of frequency list-type band, downlink is equal to uplink, otherwise retrieve `modulo` numChannels - if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) { + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + // for dynamic bands, the downlink channel is the one matched to the uplink channel this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][channelID]; - } else { // RADIOLIB_LORAWAN_CFLIST_TYPE_MASK + + } else { // RADIOLIB_LORAWAN_BAND_FIXED + // for fixed bands, the downlink channel is the uplink channel ID `modulo` number of downlink channels LoRaWANChannel_t channelDn; channelDn.enabled = true; channelDn.idx = this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].idx % this->band->rx1Span.numChannels; @@ -1433,10 +1515,34 @@ int16_t LoRaWANNode::selectChannels() { channelDn.drMin = this->band->rx1Span.drMin; channelDn.drMax = this->band->rx1Span.drMax; this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = channelDn; + uint8_t drDown = getDownlinkDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], this->rx1DrOffset, + this->band->rx1DataRateBase, channelDn.drMin, channelDn.drMax); + this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown; + } return(RADIOLIB_ERR_NONE); } +int16_t LoRaWANNode::setDatarate(uint8_t drUp) { + if(drUp < this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMin) { + return(RADIOLIB_ERR_DATA_RATE_INVALID); + } + if(drUp > this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMax) { + return(RADIOLIB_ERR_DATA_RATE_INVALID); + } + uint8_t drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase, + this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin, this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax); + + this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = drUp; + this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown; + + return(RADIOLIB_ERR_NONE); +} + +void LoRaWANNode::setADR(bool enable) { + this->adrEnabled = enable; +} + int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) { uint8_t dataRateBand = this->band->dataRates[dr]; @@ -1550,27 +1656,23 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { case(RADIOLIB_LORAWAN_MAC_CMD_LINK_ADR): { // get the ADR configuration - // TODO all these configuration should only be set if all ACKs are set, otherwise retain previous state - uint8_t dr = (cmd->payload[0] & 0xF0) >> 4; + // TODO all these configuration should only be set if all ACKs are set, otherwise retain previous state (per spec) + uint8_t drUp = (cmd->payload[0] & 0xF0) >> 4; uint8_t txPower = cmd->payload[0] & 0x0F; uint16_t chMask = LoRaWANNode::ntoh(&cmd->payload[1]); uint8_t chMaskCntl = (cmd->payload[3] & 0x70) >> 4; uint8_t nbTrans = cmd->payload[3] & 0x0F; - RADIOLIB_DEBUG_PRINTLN("ADR REQ: dataRate = %d, txPower = %d, chMask = 0x%02x, chMaskCntl = %02x, nbTrans = %d", dr, txPower, chMask, chMaskCntl, nbTrans); + RADIOLIB_DEBUG_PRINTLN("ADR REQ: dataRate = %d, txPower = %d, chMask = 0x%02x, chMaskCntl = %02x, nbTrans = %d", drUp, txPower, chMask, chMaskCntl, nbTrans); // apply the configuration uint8_t drAck = 0; - if(dr == 0x0F) { + if(drUp == 0x0F) { drAck = 1; - } else if (this->band->dataRates[dr] != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = dr; - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = dr + this->band->rx1DataRateBase - this->rx1DrOffset; - if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] < this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin) { - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin; - } else if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] > this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax) { - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax; - } - + } else if (this->band->dataRates[drUp] != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + uint8_t drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase, + this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin, this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax); + this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = drUp; + this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown; drAck = 1; } @@ -1587,28 +1689,66 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { this->txPwrCur = pwr; } - this->nbTrans = nbTrans; - // TODO implement channel mask uint8_t chMaskAck = 1; - (void)chMaskCntl; - if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) { - for(uint8_t i = 0; i < 16; i++) { - // check if this channel ID should be enabled - RADIOLIB_DEBUG_PRINTLN("ADR channel %d: %d --> %d", i, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled, (chMask >> i) & 0x01); - if(chMask & (1UL << i)) { - // if it should be enabled but is not currently defined, stop immediately - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled == false) { - chMaskAck = 0; - break; + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { + if(chMaskCntl == 0) { + // if chMaskCntl == 0, apply the mask by looking at each channel bit + RADIOLIB_DEBUG_PRINTLN("ADR channel %d: %d --> %d", i, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled, (chMask >> i) & 0x01); + if(chMask & (1UL << i)) { + // if it should be enabled but is not currently defined, stop immediately + if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx == RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) { + chMaskAck = 0; + break; + } + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true; + } else { + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = false; } - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true; - } else { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = false; - } - } - } else { + } else if(chMaskCntl == 6) { + // if chMaskCntl == 6, enable all defined channels + if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx != RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) { + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true; + } + } + + } + } else { // RADIOLIB_LORAWAN_BAND_FIXED + // delete any prior ADR responses from the uplink queue, but do not care about if none is present yet + (void)deleteMacCommand(RADIOLIB_LORAWAN_MAC_CMD_LINK_ADR, &this->commandsUp); + RADIOLIB_DEBUG_PRINTLN("mask[%d] = 0x%04x", chMaskCntl, chMask); + uint8_t chNum = chMaskCntl*16; + uint8_t chSpan = 0; + for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { + // if we must roll over to next span, reset chNum and move to next channel span + if(chNum >= this->band->txSpans[chSpan].numChannels) { + chNum = 0; + chSpan++; + } + + if(chMask & (1UL << i)) { + if(chSpan >= this->band->numTxSpans) { + RADIOLIB_DEBUG_PRINTLN("channel bitmask overrun!"); + return(RADIOLIB_ERR_UNKNOWN); + } + LoRaWANChannel_t chnl; + chnl.enabled = true; + chnl.idx = chMaskCntl*16 + i; + chnl.freq = this->band->txSpans[chSpan].freqStart + chNum*this->band->txSpans[chSpan].freqStep; + chnl.drMin = this->band->txSpans[chSpan].drMin; + chnl.drMax = this->band->txSpans[chSpan].drMax; + availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = chnl; + // downlink channels are dynamically calculated on each uplink in selectChannels() + RADIOLIB_DEBUG_PRINTLN("Channel UL %d frequency = %f MHz", i, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq); + } + chNum++; + } } + // TODO should we actually save the channels because the masks may have changed stuff? + // this may wear the storage quickly on more mobile devices / changing RF environment + + this->nbTrans = nbTrans; // send the reply cmd->len = 1; @@ -1679,8 +1819,8 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { // find first empty channel and configure this as the new channel if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx == 0) { this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx = chIndex; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq = freq; + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx = chIndex; + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq = freq; this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin = minDr; this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax = maxDr; diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 8eab3c81..0dd60f2a 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -69,8 +69,8 @@ #define RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK (0x01 << 0) #define RADIOLIB_LORAWAN_CHANNEL_DIR_BOTH (0x02 << 0) #define RADIOLIB_LORAWAN_CHANNEL_DIR_NONE (0x03 << 0) -#define RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES (0) -#define RADIOLIB_LORAWAN_CFLIST_TYPE_MASK (1) +#define RADIOLIB_LORAWAN_BAND_DYNAMIC (0) +#define RADIOLIB_LORAWAN_BAND_FIXED (1) #define RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES (15) #define RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE (0xFF < 0) @@ -239,6 +239,9 @@ struct LoRaWANChannelSpan_t { \brief Structure to save information about LoRaWAN band */ struct LoRaWANBand_t { + /*! \brief Whether the channels are fixed per specification, or dynamically allocated through the network (plus defaults) */ + uint8_t bandType; + /*! \brief Array of allowed maximum payload lengths for each data rate */ uint8_t payloadLenMax[RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES]; @@ -248,9 +251,6 @@ struct LoRaWANBand_t { /*! \brief Number of power steps in this band */ int8_t powerNumSteps; - /*! \brief Whether the optional channels are defined as list of frequencies or bit mask */ - uint8_t cfListType; - /*! \brief A set of default uplink (TX) channels for frequency-type bands */ LoRaWANChannel_t txFreqs[3]; @@ -357,13 +357,6 @@ class LoRaWANNode { \returns \ref status_codes */ int16_t restore(); - - /*! - \brief Restore frame counter for uplinks from persistent storage. - Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling. - \returns \ref status_codes - */ - int16_t restoreFcntUp(); #endif /*! @@ -373,10 +366,12 @@ class LoRaWANNode { \param devEUI 8-byte device identifier. \param nwkKey Pointer to the network AES-128 key. \param appKey Pointer to the application AES-128 key. + \param drJoinSubband (OTAA:) The datarate at which to send the join-request; (ABP:) the subband at which to send the join-request \param force Set to true to force joining even if previously joined. + \returns \ref status_codes */ - int16_t beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force = false); + int16_t beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, uint8_t joinDrSubband = RADIOLIB_LORAWAN_DATA_RATE_UNUSED, bool force = false); /*! \brief Join network by performing activation by personalization. @@ -398,23 +393,15 @@ class LoRaWANNode { */ int16_t saveSession(); - /*! - \brief Save the current uplink frame counter. - Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling. - \returns \ref status_codes - */ - int16_t saveFcntUp(); - #if defined(RADIOLIB_BUILD_ARDUINO) /*! \brief Send a message to the server. \param str Address of Arduino String that will be transmitted. \param port Port number to send the message to. \param isConfirmed Whether to send a confirmed uplink or not. - \param adrEnabled Whether ADR is enabled or not. \returns \ref status_codes */ - int16_t uplink(String& str, uint8_t port, bool isConfirmed = false, bool adrEnabled = true); + int16_t uplink(String& str, uint8_t port, bool isConfirmed = false); #endif /*! @@ -422,10 +409,9 @@ class LoRaWANNode { \param str C-string that will be transmitted. \param port Port number to send the message to. \param isConfirmed Whether to send a confirmed uplink or not. - \param adrEnabled Whether ADR is enabled or not. \returns \ref status_codes */ - int16_t uplink(const char* str, uint8_t port, bool isConfirmed = false, bool adrEnabled = true); + int16_t uplink(const char* str, uint8_t port, bool isConfirmed = false); /*! \brief Send a message to the server. @@ -433,10 +419,9 @@ class LoRaWANNode { \param len Length of the data. \param port Port number to send the message to. \param isConfirmed Whether to send a confirmed uplink or not. - \param adrEnabled Whether ADR is enabled or not. \returns \ref status_codes */ - int16_t uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed = false, bool adrEnabled = true); + int16_t uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed = false); /*! \brief Wait for, open and listen during Rx1 and Rx2 windows; only performs listening @@ -468,6 +453,34 @@ class LoRaWANNode { */ void setDeviceStatus(uint8_t battLevel); + /*! + \brief Set uplink datarate. This should _not_ be used when ADR is enabled. + \param dr Datarate to use for uplinks + \returns \ref status_codes + */ + int16_t setDatarate(uint8_t drUp); + + /*! + \brief Toggle ADR to on or off + \param enable Whether to disable ADR or not + */ + void setADR(bool enable = true); + + /*! + \brief Select a single subband (8 channels) for fixed bands such as US915 + \param idx The subband to be used (starting from 1!) + \returns \ref status_codes + */ + int16_t selectSubband(uint8_t idx); + + /*! + \brief Select a set of channels for fixed bands such as US915 + \param startChannel The first channel of the band to be used (inclusive) + \param endChannel The last channel of the band to be used (exclusive) + \returns \ref status_codes + */ + int16_t selectSubband(uint8_t startChannel, uint8_t endChannel); + #if !defined(RADIOLIB_GODMODE) private: #endif @@ -511,6 +524,9 @@ class LoRaWANNode { uint32_t confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; uint32_t adrFcnt = 0; + // ADR is enabled by default + bool adrEnabled = true; + // available channel frequencies from list passed during OTA activation LoRaWANChannel_t availableChannels[2][RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS] = { { 0 }, { 0 } }; @@ -535,6 +551,22 @@ class LoRaWANNode { // indicates whether an uplink has MAC commands as payload bool isMACPayload = false; +#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) + /*! + \brief Save the current uplink frame counter. + Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling. + \returns \ref status_codes + */ + int16_t saveFcntUp(); + + /*! + \brief Restore frame counter for uplinks from persistent storage. + Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling. + \returns \ref status_codes + */ + int16_t restoreFcntUp(); +#endif + // method to generate message integrity code uint32_t generateMIC(uint8_t* msg, size_t len, uint8_t* key); @@ -551,7 +583,7 @@ class LoRaWANNode { int16_t setupChannels(uint8_t* cfList); // select a set of semi-random TX/RX channels for the join-request and -accept message - int16_t selectChannelsJR(uint16_t devNonce); + int16_t selectChannelsJR(uint16_t devNonce, uint8_t drJoinSubband); // select a set of random TX/RX channels for up- and downlink int16_t selectChannels(); diff --git a/src/protocols/LoRaWAN/LoRaWANBands.cpp b/src/protocols/LoRaWAN/LoRaWANBands.cpp index efc9c622..91f7d965 100644 --- a/src/protocols/LoRaWAN/LoRaWANBands.cpp +++ b/src/protocols/LoRaWAN/LoRaWANBands.cpp @@ -13,10 +13,10 @@ uint8_t getDownlinkDataRate(uint8_t uplink, uint8_t offset, uint8_t base, uint8_ } const LoRaWANBand_t EU868 = { + .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 16, .powerNumSteps = 7, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES, .txFreqs = { { .enabled = true, .idx = 0, .freq = 868.100, .drMin = 0, .drMax = 5}, { .enabled = true, .idx = 1, .freq = 868.300, .drMin = 0, .drMax = 5}, @@ -55,10 +55,10 @@ const LoRaWANBand_t EU868 = { }; const LoRaWANBand_t US915 = { + .bandType = RADIOLIB_LORAWAN_BAND_FIXED, .payloadLenMax = { 19, 61, 133, 250, 250, 0, 0, 0, 41, 117, 230, 230, 230, 230, 0 }, .powerMax = 30, .powerNumSteps = 10, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_MASK, .txFreqs = { RADIOLIB_LORAWAN_CHANNEL_NONE, RADIOLIB_LORAWAN_CHANNEL_NONE, @@ -118,10 +118,10 @@ const LoRaWANBand_t US915 = { }; const LoRaWANBand_t CN780 = { + .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 250, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 12, .powerNumSteps = 5, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES, .txFreqs = { { .enabled = true, .idx = 0, .freq = 779.500, .drMin = 0, .drMax = 5}, { .enabled = true, .idx = 1, .freq = 779.700, .drMin = 0, .drMax = 5}, @@ -160,10 +160,10 @@ const LoRaWANBand_t CN780 = { }; const LoRaWANBand_t EU433 = { + .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 12, .powerNumSteps = 5, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES, .txFreqs = { { .enabled = true, .idx = 0, .freq = 433.175, .drMin = 0, .drMax = 5}, { .enabled = true, .idx = 1, .freq = 433.375, .drMin = 0, .drMax = 5}, @@ -202,10 +202,10 @@ const LoRaWANBand_t EU433 = { }; const LoRaWANBand_t AU915 = { + .bandType = RADIOLIB_LORAWAN_BAND_FIXED, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 0, 41, 117, 230, 230, 230, 230, 0 }, .powerMax = 30, .powerNumSteps = 10, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_MASK, .txFreqs = { RADIOLIB_LORAWAN_CHANNEL_NONE, RADIOLIB_LORAWAN_CHANNEL_NONE, @@ -265,10 +265,10 @@ const LoRaWANBand_t AU915 = { }; const LoRaWANBand_t CN500 = { + .bandType = RADIOLIB_LORAWAN_BAND_FIXED, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 19, .powerNumSteps = 7, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_MASK, .txFreqs = { RADIOLIB_LORAWAN_CHANNEL_NONE, RADIOLIB_LORAWAN_CHANNEL_NONE, @@ -321,10 +321,10 @@ const LoRaWANBand_t CN500 = { }; const LoRaWANBand_t AS923 = { + .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 16, .powerNumSteps = 7, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES, .txFreqs = { { .enabled = true, .idx = 0, .freq = 923.200, .drMin = 0, .drMax = 5}, { .enabled = true, .idx = 1, .freq = 923.400, .drMin = 0, .drMax = 5}, @@ -363,10 +363,10 @@ const LoRaWANBand_t AS923 = { }; const LoRaWANBand_t KR920 = { + .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 14, .powerNumSteps = 7, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES, .txFreqs = { { .enabled = true, .idx = 0, .freq = 922.100, .drMin = 0, .drMax = 5}, { .enabled = true, .idx = 1, .freq = 922.300, .drMin = 0, .drMax = 5}, @@ -405,10 +405,10 @@ const LoRaWANBand_t KR920 = { }; const LoRaWANBand_t IN865 = { + .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 30, .powerNumSteps = 10, - .cfListType = RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES, .txFreqs = { { .enabled = true, .idx = 0, .freq = 865.0625, .drMin = 0, .drMax = 5}, { .enabled = true, .idx = 1, .freq = 865.4025, .drMin = 0, .drMax = 5}, diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.cpp b/src/protocols/PhysicalLayer/PhysicalLayer.cpp index 387001a0..d6c107d5 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.cpp +++ b/src/protocols/PhysicalLayer/PhysicalLayer.cpp @@ -306,6 +306,10 @@ int16_t PhysicalLayer::irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask) return(RADIOLIB_ERR_UNSUPPORTED); } +bool PhysicalLayer::isRxTimeout() { + return(false); +} + int16_t PhysicalLayer::startChannelScan() { return(RADIOLIB_ERR_UNSUPPORTED); } diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.h b/src/protocols/PhysicalLayer/PhysicalLayer.h index 4ffeb89c..d7eccc13 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.h +++ b/src/protocols/PhysicalLayer/PhysicalLayer.h @@ -326,6 +326,12 @@ class PhysicalLayer { */ virtual int16_t irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask); + /*! + \brief Check whether the IRQ bit for RxTimeout is set + \returns \ref RxTimeout IRQ is set + */ + virtual bool isRxTimeout(); + /*! \brief Interrupt-driven channel activity detection method. interrupt will be activated when packet is detected. Must be implemented in module class.