[LoRaWAN] Improve band & ADR logic, allow setting ADR, DR, subband, update examples
This commit is contained in:
parent
6c093b2491
commit
4138dacb19
11 changed files with 410 additions and 181 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue