[LoRaWAN] Convert setDatarate() and setTxPower() to internal MAC; improve ADR

pull/918/head
StevenCellist 2024-01-06 15:03:55 +01:00
rodzic f7730463bd
commit 2da09b5adc
3 zmienionych plików z 121 dodań i 61 usunięć

Wyświetl plik

@ -129,6 +129,10 @@ void setup() {
// set a fixed datarate
node.setDatarate(5);
// in order to save the datarate persistent across reboot/deepsleep, use the following:
/*
node.setDatarate(5, true);
*/
// enable CSMA
// this tries to minimize packet loss by searching for a free channel

Wyświetl plik

@ -151,7 +151,7 @@ int16_t LoRaWANNode::restore() {
RADIOLIB_ASSERT(state);
// get MAC state
LoRaWANMacCommand_t cmd;
LoRaWANMacCommand_t cmd = { 0, 0, 0, 0 };
cmd.cid = RADIOLIB_LORAWAN_MAC_LINK_ADR;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn;
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID), cmd.payload, cmd.len);
@ -260,7 +260,7 @@ int16_t LoRaWANNode::restoreChannels() {
uint8_t bufferUp[numBytesUp] = { 0 };
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID), bufferUp, numBytesUp);
LoRaWANMacCommand_t cmd;
LoRaWANMacCommand_t cmd = { 0, 0, 0, 0 };
cmd.cid = RADIOLIB_LORAWAN_MAC_NEW_CHANNEL;
for(int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
@ -276,9 +276,9 @@ int16_t LoRaWANNode::restoreChannels() {
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_DL_CHANNELS_ID), bufferDn, numBytesDn);
cmd.cid = RADIOLIB_LORAWAN_MAC_DL_CHANNEL;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_DL_CHANNEL].lenDn;
for(int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_DL_CHANNEL].lenDn;
memcpy(cmd.payload, &bufferDn[i * cmd.len], cmd.len);
if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes
(void)execMacCommand(&cmd, false);
@ -290,7 +290,7 @@ int16_t LoRaWANNode::restoreChannels() {
uint8_t buffer[numBytes] = { 0 };
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID), buffer, numBytes);
LoRaWANMacCommand_t cmd;
LoRaWANMacCommand_t cmd = { 0, 0, 0, 0 };
cmd.cid = RADIOLIB_LORAWAN_MAC_LINK_ADR;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn;
@ -552,7 +552,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
}
LoRaWANMacCommand_t cmd;
LoRaWANMacCommand_t cmd = { 0, 0, 0, 0 };
cmd.cid = RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP].lenDn;
cmd.payload[0] = dlSettings & 0x7F;
@ -921,42 +921,54 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
adrAckReq = true;
}
// if we hit the Limit + Delay, try one of three, in order:
// set TxPower to max, set DR to min, enable all defined channels
// set TxPower to max, set DR to min, enable all default channels
if ((this->fcntUp - this->adrFcnt) == (adrLimit + adrDelay)) {
// if the TxPower field has some offset, remove it and switch to maximum power
if(this->txPowerCur > 0) {
this->txPowerCur = 0;
// set the maximum power supported by both the module and the band
state = this->setTxPower(this->txPowerMax);
RADIOLIB_ASSERT(state);
} else {
// failed to increase Tx power, so try to decrease the datarate
if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] > this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMin) {
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]--;
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]--;
} else {
// failed to decrease datarate, so enable all available channels
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
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;
this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].enabled = true;
uint8_t adrStage = 1;
while(adrStage != 0) {
switch(adrStage) {
case(1): {
// if the TxPower field has some offset, remove it and switch to maximum power
if(this->txPowerCur > 0) {
// set the maximum power supported by both the module and the band
state = this->setTxPower(this->txPowerMax, true);
if(state == RADIOLIB_ERR_NONE) {
this->txPowerCur = 0;
adrStage = 0; // successfully did some ADR stuff
}
}
if(adrStage == 1) { // if nothing succeeded, proceed to stage 2
adrStage = 2;
}
}
}
break;
case(2): {
// try to decrease the datarate
if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] > 0) {
if(this->setDatarate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] - 1, true) == RADIOLIB_ERR_NONE) {
adrStage = 0; // successfully did some ADR stuff
}
}
if(adrStage == 2) { // if nothing succeeded, proceed to stage 3
adrStage = 3;
}
}
break;
case(3): {
if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) {
this->setupChannels(nullptr); // revert to default frequencies
} else {
// if a subband was selected by user, go back to its default state
// hopefully it'll help something, but probably not; at least we tried..
if(this->selectedSubband >= 0) {
this->selectSubband(this->selectedSubband);
}
}
adrStage = 0; // nothing else to do, so end the cycle
}
break;
}
}
LoRaWANMacCommand_t cmd;
cmd.cid = RADIOLIB_LORAWAN_MAC_LINK_ADR;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn;
cmd.payload[0] = (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4);
cmd.payload[0] |= 0; // default to max Tx Power
cmd.payload[3] |= (1 << 7); // set the RFU bit, which means that the channel mask gets ignored
(void)execMacCommand(&cmd);
// we tried something to improve the range, so increase the ADR frame counter by 'ADR delay'
this->adrFcnt += adrDelay;
}
@ -1452,7 +1464,7 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event)
RADIOLIB_DEBUG_PRINTLN("MAC response:");
for (int i = 0; i < this->commandsUp.numCommands; i++) {
RADIOLIB_DEBUG_HEXDUMP(&this->commandsUp[i].cid, sizeof(LoRaWANMacCommand_t));
RADIOLIB_DEBUG_HEXDUMP(&(this->commandsUp.commands[i].cid), sizeof(LoRaWANMacCommand_t));
}
// if FOptsLen for the next uplink is larger than can be piggybacked onto an uplink, send separate uplink
@ -1638,7 +1650,11 @@ bool LoRaWANNode::verifyMIC(uint8_t* msg, size_t len, uint8_t* key) {
int16_t LoRaWANNode::setPhyProperties() {
// set the physical layer configuration
int8_t pwr = this->txPowerMax - this->txPowerCur * 2;
int16_t state = this->setTxPower(pwr);
int16_t state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
// go from the highest power and lower it until we hit one supported by the module
state = this->phyLayer->setOutputPower(pwr--);
}
RADIOLIB_ASSERT(state);
uint8_t syncWord[3] = { 0 };
@ -1681,7 +1697,7 @@ int16_t LoRaWANNode::setupChannels(uint8_t* cfList) {
// 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) {
RADIOLIB_DEBUG_PRINTLN("CFList present");
LoRaWANMacCommand_t cmd;
LoRaWANMacCommand_t cmd = { 0, 0, 0, 0 };
cmd.cid = RADIOLIB_LORAWAN_MAC_NEW_CHANNEL;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_NEW_CHANNEL].lenDn;
// datarate range for all new channels is equal to the default channels
@ -1700,7 +1716,7 @@ int16_t LoRaWANNode::setupChannels(uint8_t* cfList) {
} else { // RADIOLIB_LORAWAN_BAND_FIXED
if(cfList != nullptr) {
RADIOLIB_DEBUG_PRINTLN("CFList present");
LoRaWANMacCommand_t cmd;
LoRaWANMacCommand_t cmd = { 0, 0, 0, 0 };
cmd.cid = RADIOLIB_LORAWAN_MAC_LINK_ADR;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn;
cmd.payload[0] = 0xFF; // same datarate and payload
@ -1746,6 +1762,7 @@ int16_t LoRaWANNode::selectSubband(uint8_t startChannel, uint8_t endChannel) {
RADIOLIB_DEBUG_PRINTLN("This is a dynamic band plan which does not support subbands");
return(RADIOLIB_ERR_INVALID_CHANNEL);
}
this->selectedSubband = startChannel % 8; // save selected subband - assumed a block of 8 channels
uint8_t numChannels = endChannel - startChannel + 1;
if(startChannel > this->band->txSpans[0].numChannels) {
@ -1923,24 +1940,37 @@ int16_t LoRaWANNode::selectChannels() {
return(RADIOLIB_ERR_NONE);
}
int16_t LoRaWANNode::setDatarate(uint8_t drUp) {
// find the minimum and maximum available datarates by checking the enabled uplink channels
uint8_t drMin = RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES;
uint8_t drMax = 0;
int16_t LoRaWANNode::setDatarate(uint8_t drUp, bool saveToEeprom) {
// scan through all enabled channels and check if the requested datarate is available
bool isValidDR = false;
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) {
drMin = RADIOLIB_MIN(drMin, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin);
drMax = RADIOLIB_MAX(drMax, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax);
LoRaWANChannel_t *chnl = &(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]);
if(chnl->enabled) {
if(drUp > chnl->drMin && drUp < chnl->drMax) {
isValidDR = true;
break;
}
}
}
if((drUp < drMin) || (drUp > drMax)) {
RADIOLIB_DEBUG_PRINTLN("Cannot configure DR %d (min: %d, max: %d)", drUp, drMin, drMax);
if(!isValidDR) {
RADIOLIB_DEBUG_PRINTLN("No defined channel allows datarate %d", drUp);
return(RADIOLIB_ERR_DATA_RATE_INVALID);
}
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = drUp;
RADIOLIB_DEBUG_PRINTLN("Configured DR up = %d", drUp);
LoRaWANMacCommand_t cmd = { 0, 0, 0, 0 };
cmd.cid = RADIOLIB_LORAWAN_MAC_LINK_ADR;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn;
cmd.payload[0] = (drUp << 4);
cmd.payload[0] |= 0x0F; // keep Tx Power the same
cmd.payload[3] |= (1 << 7); // set the RFU bit, which means that the channel mask gets ignored
cmd.payload[3] |= 0; // keep NbTrans the same
(void)execMacCommand(&cmd, saveToEeprom);
// check if ACK is set for Tx Power
if((cmd.payload[0] >> 1) != 1) {
return(RADIOLIB_ERR_DATA_RATE_INVALID);
}
return(RADIOLIB_ERR_NONE);
}
@ -2007,13 +2037,31 @@ uint8_t LoRaWANNode::maxPayloadDwellTime() {
return(payLen - 13); // fixed 13-byte header
}
int16_t LoRaWANNode::setTxPower(int8_t txPower) {
int16_t state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
// go from the highest power and lower it until we hit one supported by the module
state = this->phyLayer->setOutputPower(txPower--);
int16_t LoRaWANNode::setTxPower(int8_t txPower, bool saveToEeprom) {
// only allow values within the band's (or MAC state) maximum
if(txPower > this->txPowerMax) {
return(RADIOLIB_ERR_INVALID_OUTPUT_POWER);
}
return(state);
// Tx Power is set in steps of two
// the selected value is rounded down to nearest multiple of two away from txPowerMax
// e.g. on EU868, max is 16; if 13 is selected then we set to 12
uint8_t txPowerNew = (this->txPowerMax - txPower) / 2 + 1;
LoRaWANMacCommand_t cmd = { 0, 0, 0, 0 };
cmd.cid = RADIOLIB_LORAWAN_MAC_LINK_ADR;
cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn;
cmd.payload[0] = 0xF0; // keep datarate the same
cmd.payload[0] |= txPowerNew; // set the Tx Power
cmd.payload[3] |= (1 << 7); // set the RFU bit, which means that the channel mask gets ignored
cmd.payload[3] |= 0; // keep NbTrans the same
(void)execMacCommand(&cmd, saveToEeprom);
// check if ACK is set for Tx Power
if((cmd.payload[0] >> 2) != 1) {
return(RADIOLIB_ERR_INVALID_OUTPUT_POWER);
}
return(RADIOLIB_ERR_NONE);
}
int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) {
@ -2197,13 +2245,16 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) {
} else {
int8_t pwr = this->txPowerMax - 2*txPower;
int16_t state = this->setTxPower(pwr);
int16_t state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
// go from the highest power and lower it until we hit one supported by the module
state = this->phyLayer->setOutputPower(pwr--);
}
// only acknowledge if the requested datarate was succesfully configured
if(state == RADIOLIB_ERR_NONE) {
pwrAck = 1;
this->txPowerCur = txPower;
}
}
bool isSuccessive = false;

Wyświetl plik

@ -596,9 +596,10 @@ class LoRaWANNode {
/*!
\brief Set uplink datarate. This should not be used when ADR is enabled.
\param dr Datarate to use for uplinks.
\param saveToEeprom Whether to save this setting to EEPROM or not (default false).
\returns \ref status_codes
*/
int16_t setDatarate(uint8_t drUp);
int16_t setDatarate(uint8_t drUp, bool saveToEeprom = false);
/*!
\brief Toggle ADR to on or off.
@ -644,9 +645,10 @@ class LoRaWANNode {
/*!
\brief Configure TX power of the radio module.
\param txPower Output power during TX mode to be set in dBm.
\param saveToEeprom Whether to save this setting to EEPROM or not (default false).
\returns \ref status_codes
*/
int16_t setTxPower(int8_t txPower);
int16_t setTxPower(int8_t txPower, bool saveToEeprom = false);
/*!
\brief Select a single subband (8 channels) for fixed bands such as US915.
@ -800,6 +802,9 @@ class LoRaWANNode {
// indicates whether an uplink has MAC commands as payload
bool isMACPayload = false;
// save the selected subband in case this must be restored in ADR control
int8_t selectedSubband = -1;
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
/*!
\brief Save the current uplink frame counter.