From 6979bff863a64b5cf072bef1a80e435c5ec1adc3 Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Mon, 22 Jan 2024 11:40:57 +0100 Subject: [PATCH 1/7] [LoRaWAN] Improve channel masks for fixed bands --- src/protocols/LoRaWAN/LoRaWAN.cpp | 81 +++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 21a31819..b4ae4ba0 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -111,8 +111,12 @@ int16_t LoRaWANNode::restore() { .len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, .repeat = 0, }; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID), cmd.payload, cmd.len); - execMacCommand(&cmd, false); + + // only apply the single ADR command on dynamic bands; fixed bands is done through channel restore + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID), cmd.payload, cmd.len); + execMacCommand(&cmd, false); + } cmd.cid = RADIOLIB_LORAWAN_MAC_DUTY_CYCLE; cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_DUTY_CYCLE].lenDn; @@ -207,7 +211,7 @@ int16_t LoRaWANNode::restoreFcntUp() { } int16_t LoRaWANNode::restoreChannels() { - // first do the default channels + // first do the default channels, in case these are not covered by restored channels if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { this->setupChannelsDyn(false); } else { // RADIOLIB_LORAWAN_BAND_FIXED @@ -226,6 +230,7 @@ int16_t LoRaWANNode::restoreChannels() { cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_NEW_CHANNEL].lenDn; memcpy(cmd.payload, &(bufferUp[i * cmd.len]), cmd.len); if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes + cmd.repeat = 1; (void)execMacCommand(&cmd, false); } } @@ -262,6 +267,7 @@ int16_t LoRaWANNode::restoreChannels() { memcpy(cmd.payload, &buffer[i * cmd.len], cmd.len); // there COULD, according to spec, be an all zeroes ADR command - meh if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { + cmd.repeat = (i+1); execMacCommand(&cmd, false); } } @@ -1436,19 +1442,36 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) RADIOLIB_DEBUG_PRINTLN("fopts:"); RADIOLIB_DEBUG_HEXDUMP(fopts, foptsLen); + bool hasADR = false; + uint8_t numADR = 0; + uint8_t lastCID = 0; + // process the MAC command(s) int8_t remLen = foptsLen; uint8_t* foptsPtr = fopts; while(remLen > 0) { uint8_t cid = *foptsPtr; uint8_t macLen = getMacPayloadLength(cid); + if(cid == RADIOLIB_LORAWAN_MAC_LINK_ADR) { + // if there was an earlier ADR command but it was not the last, ignore it + if(hasADR && lastCID != RADIOLIB_LORAWAN_MAC_LINK_ADR) { + RADIOLIB_DEBUG_PRINTLN("Encountered non-consecutive block of ADR commands - skipping"); + remLen -= (macLen + 1); + foptsPtr += (macLen + 1); + lastCID = cid; + continue; + } + // otherwise, set ADR flag to true and increase counter + hasADR = true; + numADR++; + } if(macLen + 1 > remLen) break; LoRaWANMacCommand_t cmd = { .cid = cid, .payload = { 0 }, .len = macLen, - .repeat = 0, + .repeat = (cid == RADIOLIB_LORAWAN_MAC_LINK_ADR ? numADR : (uint8_t)0), }; memcpy(cmd.payload, foptsPtr + 1, macLen); RADIOLIB_DEBUG_PRINTLN("[%02X]: %02X %02X %02X %02X %02X (%d)", @@ -1463,6 +1486,7 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) // processing succeeded, move in the buffer to the next command remLen -= (macLen + 1); foptsPtr += (macLen + 1); + lastCID = cid; RADIOLIB_DEBUG_PRINTLN("Processed: %d, remaining: %d", (macLen + 1), remLen); } @@ -1736,6 +1760,8 @@ int16_t LoRaWANNode::setupChannelsFix(uint8_t subBand) { // chMask is set for 16 channels at once, so widen the Cntl value uint8_t chMaskCntl = (subBand - 1) / 2; // compensate the 1 offset + uint8_t numADR = 1; + LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, .payload = { 0 }, @@ -1752,6 +1778,7 @@ int16_t LoRaWANNode::setupChannelsFix(uint8_t subBand) { cmd.payload[2] = 0; cmd.payload[3] = (7 << 4); // set the chMaskCntl value to all channels off cmd.payload[3] |= 0; // keep NbTrans the same + cmd.repeat = numADR++; (void)execMacCommand(&cmd, false); } @@ -1770,6 +1797,7 @@ int16_t LoRaWANNode::setupChannelsFix(uint8_t subBand) { } cmd.payload[3] = (chMaskCntl << 4); // set the chMaskCntl value cmd.payload[3] |= 0; // keep NbTrans the same + cmd.repeat = numADR++; (void)execMacCommand(&cmd, false); return(RADIOLIB_ERR_NONE); @@ -2204,7 +2232,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { } } - bool isSuccessive = false; + uint8_t chMaskAck = 1; // only apply channel mask when the RFU bit is not set // (which is set on the internal MAC command when creating new session) @@ -2213,13 +2241,17 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { chMaskAck = (uint8_t)this->applyChannelMaskDyn(chMaskCntl, chMask); } else { // RADIOLIB_LORAWAN_BAND_FIXED - // if there was already an ADR response in the uplink MAC queue, - // this is a consecutive ADR command, so we delete the prior response - int16_t state = deleteMacCommand(RADIOLIB_LORAWAN_MAC_LINK_ADR, &this->commandsUp); - if(state == RADIOLIB_ERR_NONE) { - isSuccessive = true; + bool clearChannels = false; + if(cmd->repeat == 1) { + // if this is the first ADR command in the queue, clear all saved channels + // so we can apply the new channel mask + clearChannels = true; + } else { + // if this is not the first ADR command, clear the ADR response that was in the queue + (void)deleteMacCommand(RADIOLIB_LORAWAN_MAC_LINK_ADR, &this->commandsUp); } - chMaskAck = (uint8_t)this->applyChannelMaskFix(chMaskCntl, chMask, !isSuccessive); + RADIOLIB_DEBUG_PRINTLN("ADR mask: clearing channels"); + chMaskAck = (uint8_t)this->applyChannelMaskFix(chMaskCntl, chMask, clearChannels); } } @@ -2242,32 +2274,28 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { // save to the single ADR MAC location mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID), &(cmd->payload[0]), payLen); - } else { - // read how many ADR masks are already stored - uint8_t numMacADR = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID); - RADIOLIB_DEBUG_PRINTLN("[1] Successive: %d, numMacADR: %d, RFU: %d, payload: %02X %02X %02X %02X", - isSuccessive, numMacADR, (cmd->payload[3] >> 7), + } else { // RADIOLIB_LORAWAN_BAND_FIXED + RADIOLIB_DEBUG_PRINTLN("[1] Repeat: %d, RFU: %d, payload: %02X %02X %02X %02X", + cmd->repeat, (cmd->payload[3] >> 7), cmd->payload[0], cmd->payload[1], cmd->payload[2], cmd->payload[3]); // if RFU bit is set, this is just a change in Datarate or TxPower // so read bytes 1..3 from last stored ADR command into the current MAC payload and re-store it if((cmd->payload[3] >> 7) == 1) { + // read how many ADR masks are already stored + uint8_t numMacADR = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID); if(numMacADR > 0) { mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID) + (numMacADR - 1) * payLen + 1, &(cmd->payload[1]), 3); mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID) + (numMacADR - 1) * payLen, &(cmd->payload[0]), payLen); } } else { - // if no previous mask was processed, reset counter to 0 - if(!isSuccessive) { - numMacADR = 0; - } - // save to the uplink channel location, to the numMacADR-th slot of 4 bytes - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID) + numMacADR * payLen, &(cmd->payload[0]), payLen); - // saved an ADR mask, so increase counter - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID, numMacADR + 1); + // save to the uplink channel location, to the cmd->repeat-th slot of 4 bytes + mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID) + (cmd->repeat - 1) * payLen, &(cmd->payload[0]), payLen); + // saved an ADR mask, so re-store counter + mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID, cmd->repeat); } - RADIOLIB_DEBUG_PRINTLN("[2] Successive: %d, numMacADR: %d, RFU: %d, payload: %02X %02X %02X %02X", - isSuccessive, numMacADR, (cmd->payload[3] >> 7), + RADIOLIB_DEBUG_PRINTLN("[2] Repeat: %d, RFU: %d, payload: %02X %02X %02X %02X", + cmd->repeat, (cmd->payload[3] >> 7), cmd->payload[0], cmd->payload[1], cmd->payload[2], cmd->payload[3]); } } @@ -2276,6 +2304,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { // send the reply cmd->len = 1; cmd->payload[0] = (pwrAck << 2) | (drAck << 1) | (chMaskAck << 0); + cmd->repeat = 0; // discard any repeat value that may have been set RADIOLIB_DEBUG_PRINTLN("ADR ANS: status = 0x%02x", cmd->payload[0]); return(true); } break; From 4ec10a47498ef2beaad6ea9d41ecae8032fd0175 Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Mon, 22 Jan 2024 12:32:15 +0100 Subject: [PATCH 2/7] [LoRaWAN] Fix fixed band CFList processing --- src/protocols/LoRaWAN/LoRaWAN.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index b4ae4ba0..02f5034d 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -1849,13 +1849,11 @@ int16_t LoRaWANNode::processCFList(uint8_t* cfList) { for(size_t chMaskCntl = 0; chMaskCntl < numChMasks; chMaskCntl++) { cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; cmd.payload[3] = chMaskCntl << 4; // NbTrans = 0 -> keep the same + cmd.repeat = (chMaskCntl + 1); memcpy(&cmd.payload[1], &cfList[chMaskCntl*2], 2); (void)execMacCommand(&cmd); - // save the response as a MAC answer, as this signals execMacCommand() to store the masks contiguously - pushMacCommand(&cmd, &this->commandsUp); } // delete the ADR response - (void)deleteMacCommand(RADIOLIB_LORAWAN_MAC_LINK_ADR, &this->commandsUp); } return(RADIOLIB_ERR_NONE); @@ -2246,11 +2244,11 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { // if this is the first ADR command in the queue, clear all saved channels // so we can apply the new channel mask clearChannels = true; + RADIOLIB_DEBUG_PRINTLN("ADR mask: clearing channels"); } else { // if this is not the first ADR command, clear the ADR response that was in the queue (void)deleteMacCommand(RADIOLIB_LORAWAN_MAC_LINK_ADR, &this->commandsUp); } - RADIOLIB_DEBUG_PRINTLN("ADR mask: clearing channels"); chMaskAck = (uint8_t)this->applyChannelMaskFix(chMaskCntl, chMask, clearChannels); } From aedf519ea4c86ee3d2bbb09a7ba3350821131569 Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Mon, 22 Jan 2024 12:53:18 +0100 Subject: [PATCH 3/7] [LoRaWAN] Fix rejoining during active session --- src/protocols/LoRaWAN/LoRaWAN.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 02f5034d..5ad8bcc7 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -277,6 +277,11 @@ int16_t LoRaWANNode::restoreChannels() { #endif void LoRaWANNode::beginCommon(uint8_t joinDr) { + // in case a new session is started while there is an ongoing session + // clear the MAC queues completely + memset(&(this->commandsUp), 0, sizeof(LoRaWANMacCommandQueue_t)); + memset(&(this->commandsDown), 0, sizeof(LoRaWANMacCommandQueue_t)); + LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, .payload = { 0 }, @@ -1733,6 +1738,11 @@ int16_t LoRaWANNode::setupChannelsDyn(bool joinRequest) { RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].idx, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].freq); } } + + // clear all remaining channels + for(; num < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; num++) { + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = RADIOLIB_LORAWAN_CHANNEL_NONE; + } return(RADIOLIB_ERR_NONE); } From ee542c3b564cbb96d0c06c9aa6c84e2ddec517c0 Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Mon, 22 Jan 2024 13:34:00 +0100 Subject: [PATCH 4/7] [LoRaWAN] Fix dynamic-band non-ADR session persistance --- src/protocols/LoRaWAN/LoRaWAN.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 5ad8bcc7..b0d92a49 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -2278,6 +2278,10 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { if((cmd->payload[3] >> 7) == 1) { mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID) + 1, &(cmd->payload[1]), 3); } + // if there was no channel mask (all zeroes), we should never apply that channel mask, so set RFU bit again + if(cmd->payload[1] == 0 && cmd->payload[2] == 0) { + cmd->payload[3] |= (1 << 7); + } // save to the single ADR MAC location mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID), &(cmd->payload[0]), payLen); From 9008fb00a78220c8b7662c21806812f961df9146 Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Mon, 22 Jan 2024 14:38:35 +0100 Subject: [PATCH 5/7] [LoRaWAN] Fix setDatarate function --- src/protocols/LoRaWAN/LoRaWAN.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index b0d92a49..640b35bc 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -1918,7 +1918,7 @@ int16_t LoRaWANNode::setDatarate(uint8_t drUp, bool saveToEeprom) { for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { LoRaWANChannel_t *chnl = &(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]); if(chnl->enabled) { - if(drUp > chnl->drMin && drUp < chnl->drMax) { + if(drUp >= chnl->drMin && drUp <= chnl->drMax) { isValidDR = true; break; } From 51ab103d07acfc6f61d11759553725c84cf3327c Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Tue, 23 Jan 2024 09:04:25 +0100 Subject: [PATCH 6/7] [LoRaWAN] Keep Dev/JoinNonce on OTAA wipe with same credentials --- src/protocols/LoRaWAN/LoRaWAN.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 640b35bc..5984653c 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -400,12 +400,21 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe bool validCheckSum = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CHECKSUM_ID) == checkSum; bool validMode = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_MODE_ID) == RADIOLIB_LORAWAN_MODE_OTAA; - if(!force && validCheckSum && validMode) { - // the device has joined already, we can just pull the data from persistent storage - RADIOLIB_DEBUG_PRINTLN("Found existing session; restoring..."); - - return(this->restore()); - } else { + if(validCheckSum && validMode) { + if(!force) { + // the device has joined already, we can just pull the data from persistent storage + RADIOLIB_DEBUG_PRINTLN("Found existing session; restoring..."); + return(this->restore()); + + } else { + // the credentials are still the same, so restore only DevNonce and JoinNonce + this->devNonce = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID); + this->joinNonce = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_JOIN_NONCE_ID); + } + } + + // if forced by user, keys are new or changed mode, wipe the previous session + if(force || !validCheckSum || !validMode) { #if RADIOLIB_DEBUG RADIOLIB_DEBUG_PRINTLN("Didn't restore session (checksum: %d, mode: %d)", validCheckSum, validMode); RADIOLIB_DEBUG_PRINTLN("First 16 bytes of NVM:"); @@ -657,6 +666,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe // save join-request parameters mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_HOME_NET_ID, this->homeNetId); + mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID, this->devNonce); mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_JOIN_NONCE_ID, this->joinNonce); this->saveSession(); @@ -766,14 +776,10 @@ bool LoRaWANNode::isJoined() { #if !defined(RADIOLIB_EEPROM_UNSUPPORTED) int16_t LoRaWANNode::saveSession() { Module* mod = this->phyLayer->getMod(); - - // store session configuration (MAC commands) + if(mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_VERSION_ID) != this->rev) mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_VERSION_ID, this->rev); - if(mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID) != this->devNonce) - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID, this->devNonce); - // store all frame counters if(mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_A_FCNT_DOWN_ID) != this->aFcntDown) mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_A_FCNT_DOWN_ID, this->aFcntDown); From b98a5c6b292d14448d71d360301ee87fd78bf740 Mon Sep 17 00:00:00 2001 From: StevenCellist Date: Wed, 24 Jan 2024 08:46:10 +0100 Subject: [PATCH 7/7] [LoRaWAN] Fix Tx power calculation --- src/protocols/LoRaWAN/LoRaWAN.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 5984653c..76497cfc 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -2026,7 +2026,7 @@ int16_t LoRaWANNode::setTxPower(int8_t txPower, bool saveToEeprom) { // 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; + uint8_t numSteps = (this->txPowerMax - txPower + 1) / (-RADIOLIB_LORAWAN_POWER_STEP_SIZE_DBM); LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, @@ -2035,7 +2035,7 @@ int16_t LoRaWANNode::setTxPower(int8_t txPower, bool saveToEeprom) { .repeat = 0, }; cmd.payload[0] = 0xF0; // keep datarate the same - cmd.payload[0] |= txPowerNew; // set the Tx Power + cmd.payload[0] |= numSteps; // 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);