[LoRaWAN] Improve band & ADR logic, allow setting ADR, DR, subband, update examples

This commit is contained in:
StevenCellist 2023-11-03 22:32:45 +01:00
parent 6c093b2491
commit 4138dacb19
11 changed files with 410 additions and 181 deletions

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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)
/*!
\}
*/

View file

@ -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));
}

View file

@ -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.

View file

@ -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);
}

View file

@ -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<uint32_t>(&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<uint16_t>(&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<uint16_t>(&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<uint16_t>(&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;

View file

@ -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();

View file

@ -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},

View file

@ -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);
}

View file

@ -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.