[LoRaWAN] Improve PHY behaviour, update beginABP, bugfixes (#1080)
* [LoRaWAN] Add getter for ToA, prevent MAC queue overflow * [LoRaWAN] Permute arguments to beginABP * Implement & split off checkOutputPower * [LoRaWAN] Configure physical layer on each up/downlink * [LoRaWAN] Remove unnecessary dynamic array * [LoRaWAN] Improve downlink handling * Resolve return-warnings in checkOutputPower() * [LoRaWAN] Improve buffer definition * [LoRaWAN] Prevent requesting repeated MAC commands * Update keywords.txt * [CC1101] Resolve unused variable warning * [CC1101] Update checkOutputPower * [SX1278] Fix variable assignment * Update keywords.txt * [CC1101] Added checkOutputPower override for PHY compatibility * [LR11x0] Added checkOutputPower override for PHY compatibility * [SX127x] Added checkOutputPower override for PHY compatibility --------- Co-authored-by: jgromes <jan.gromes@gmail.com>
This commit is contained in:
parent
841b283c0f
commit
1b2b8bd67b
23 changed files with 562 additions and 292 deletions
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
|
@ -170,7 +170,7 @@ jobs:
|
||||||
else
|
else
|
||||||
# apply special flags for LoRaWAN
|
# apply special flags for LoRaWAN
|
||||||
if [[ ${example} =~ "LoRaWAN" ]]; then
|
if [[ ${example} =~ "LoRaWAN" ]]; then
|
||||||
flags="-DRADIOLIB_LORAWAN_DEV_ADDR=0 -DRADIOLIB_LORAWAN_NWKS_KEY=0 -DRADIOLIB_LORAWAN_SNWKSINT_KEY=0 -DRADIOLIB_LORAWAN_NWKSENC_KEY=0 -DRADIOLIB_LORAWAN_APPS_KEY=0 -DRADIOLIB_LORAWAN_APP_KEY=0 -DRADIOLIB_LORAWAN_NWK_KEY=0 -DRADIOLIB_LORAWAN_DEV_EUI=0 -DARDUINO_TTGO_LORA32_V1"
|
flags="-DRADIOLIB_LORAWAN_DEV_ADDR=0 -DRADIOLIB_LORAWAN_FNWKSINT_KEY=0 -DRADIOLIB_LORAWAN_SNWKSINT_KEY=0 -DRADIOLIB_LORAWAN_NWKSENC_KEY=0 -DRADIOLIB_LORAWAN_APPS_KEY=0 -DRADIOLIB_LORAWAN_APP_KEY=0 -DRADIOLIB_LORAWAN_NWK_KEY=0 -DRADIOLIB_LORAWAN_DEV_EUI=0 -DARDUINO_TTGO_LORA32_V1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# build sketch
|
# build sketch
|
||||||
|
|
|
@ -12,8 +12,8 @@ const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds
|
||||||
#define RADIOLIB_LORAWAN_DEV_ADDR 0x------
|
#define RADIOLIB_LORAWAN_DEV_ADDR 0x------
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef RADIOLIB_LORAWAN_NWKS_KEY // Replace with your NwkS Key
|
#ifndef RADIOLIB_LORAWAN_FNWKSINT_KEY // Replace with your FNwkSInt Key
|
||||||
#define RADIOLIB_LORAWAN_NWKS_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
#define RADIOLIB_LORAWAN_FNWKSINT_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
||||||
#endif
|
#endif
|
||||||
#ifndef RADIOLIB_LORAWAN_SNWKSINT_KEY // Replace with your SNwkSInt Key
|
#ifndef RADIOLIB_LORAWAN_SNWKSINT_KEY // Replace with your SNwkSInt Key
|
||||||
#define RADIOLIB_LORAWAN_SNWKSINT_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
#define RADIOLIB_LORAWAN_SNWKSINT_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
||||||
|
@ -118,7 +118,7 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0
|
||||||
|
|
||||||
// copy over the keys in to the something that will not compile if incorrectly formatted
|
// copy over the keys in to the something that will not compile if incorrectly formatted
|
||||||
uint32_t devAddr = RADIOLIB_LORAWAN_DEV_ADDR;
|
uint32_t devAddr = RADIOLIB_LORAWAN_DEV_ADDR;
|
||||||
uint8_t NwkSKey[] = { RADIOLIB_LORAWAN_NWKS_KEY };
|
uint8_t NwkSKey[] = { RADIOLIB_LORAWAN_FNWKSINT_KEY };
|
||||||
uint8_t SNwkSIntKey[] = { RADIOLIB_LORAWAN_SNWKSINT_KEY }; // Previously sNwkSIntKey
|
uint8_t SNwkSIntKey[] = { RADIOLIB_LORAWAN_SNWKSINT_KEY }; // Previously sNwkSIntKey
|
||||||
uint8_t NwkSEncKey[] = { RADIOLIB_LORAWAN_NWKSENC_KEY }; // Previously fNwkSIntKey
|
uint8_t NwkSEncKey[] = { RADIOLIB_LORAWAN_NWKSENC_KEY }; // Previously fNwkSIntKey
|
||||||
uint8_t AppSKey[] = { RADIOLIB_LORAWAN_APPS_KEY };
|
uint8_t AppSKey[] = { RADIOLIB_LORAWAN_APPS_KEY };
|
||||||
|
|
|
@ -127,6 +127,7 @@ setCodingRate KEYWORD2
|
||||||
setFrequency KEYWORD2
|
setFrequency KEYWORD2
|
||||||
setSyncWord KEYWORD2
|
setSyncWord KEYWORD2
|
||||||
setOutputPower KEYWORD2
|
setOutputPower KEYWORD2
|
||||||
|
checkOutputPower KEYWORD2
|
||||||
setCurrentLimit KEYWORD2
|
setCurrentLimit KEYWORD2
|
||||||
setPreambleLength KEYWORD2
|
setPreambleLength KEYWORD2
|
||||||
setGain KEYWORD2
|
setGain KEYWORD2
|
||||||
|
@ -328,10 +329,10 @@ timeUntilUplink KEYWORD2
|
||||||
setDwellTime KEYWORD2
|
setDwellTime KEYWORD2
|
||||||
maxPayloadDwellTime KEYWORD2
|
maxPayloadDwellTime KEYWORD2
|
||||||
setTxPower KEYWORD2
|
setTxPower KEYWORD2
|
||||||
setCSMA KEYWORD2
|
|
||||||
getMacLinkCheckAns KEYWORD2
|
getMacLinkCheckAns KEYWORD2
|
||||||
getMacDeviceTimeAns KEYWORD2
|
getMacDeviceTimeAns KEYWORD2
|
||||||
getDevAddr KEYWORD2
|
getDevAddr KEYWORD2
|
||||||
|
getLastToA KEYWORD2
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Constants (LITERAL1)
|
# Constants (LITERAL1)
|
||||||
|
|
|
@ -560,6 +560,62 @@ int16_t CC1101::getFrequencyDeviation(float *freqDev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t CC1101::setOutputPower(int8_t pwr) {
|
int16_t CC1101::setOutputPower(int8_t pwr) {
|
||||||
|
// check if power value is configurable
|
||||||
|
uint8_t powerRaw = 0;
|
||||||
|
int16_t state = checkOutputPower(pwr, NULL, &powerRaw);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// store the value
|
||||||
|
this->power = pwr;
|
||||||
|
|
||||||
|
if(this->modulation == RADIOLIB_CC1101_MOD_FORMAT_ASK_OOK){
|
||||||
|
// Amplitude modulation:
|
||||||
|
// PA_TABLE[0] is the power to be used when transmitting a 0 (no power)
|
||||||
|
// PA_TABLE[1] is the power to be used when transmitting a 1 (full power)
|
||||||
|
|
||||||
|
uint8_t paValues[2] = {0x00, powerRaw};
|
||||||
|
SPIwriteRegisterBurst(RADIOLIB_CC1101_REG_PATABLE, paValues, 2);
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Freq modulation:
|
||||||
|
// PA_TABLE[0] is the power to be used when transmitting.
|
||||||
|
return(SPIsetRegValue(RADIOLIB_CC1101_REG_PATABLE, powerRaw));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t CC1101::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
return(checkOutputPower(power, clipped, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t CC1101::checkOutputPower(int8_t power, int8_t* clipped, uint8_t* raw) {
|
||||||
|
constexpr int8_t allowedPwrs[8] = { -30, -20, -15, -10, 0, 5, 7, 10 };
|
||||||
|
|
||||||
|
if(clipped) {
|
||||||
|
if(power <= -30) {
|
||||||
|
*clipped = -30;
|
||||||
|
} else if(power >= 10) {
|
||||||
|
*clipped = 10;
|
||||||
|
} else {
|
||||||
|
for(int i = 0; i < 8; i++) {
|
||||||
|
if(allowedPwrs[i] > power) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*clipped = allowedPwrs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if just a check occurs (and not requesting the raw power value), return now
|
||||||
|
if(!raw) {
|
||||||
|
for(int i = 0; i < 8; i++) {
|
||||||
|
if(allowedPwrs[i] == power) {
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
}
|
||||||
|
|
||||||
// round to the known frequency settings
|
// round to the known frequency settings
|
||||||
uint8_t f;
|
uint8_t f;
|
||||||
if(this->frequency < 374.0) {
|
if(this->frequency < 374.0) {
|
||||||
|
@ -586,53 +642,35 @@ int16_t CC1101::setOutputPower(int8_t pwr) {
|
||||||
{0xCB, 0xC8, 0xCB, 0xC7},
|
{0xCB, 0xC8, 0xCB, 0xC7},
|
||||||
{0xC2, 0xC0, 0xC2, 0xC0}};
|
{0xC2, 0xC0, 0xC2, 0xC0}};
|
||||||
|
|
||||||
uint8_t powerRaw;
|
switch(power) {
|
||||||
switch(pwr) {
|
case allowedPwrs[0]: // -30
|
||||||
case -30:
|
*raw = paTable[0][f];
|
||||||
powerRaw = paTable[0][f];
|
|
||||||
break;
|
break;
|
||||||
case -20:
|
case allowedPwrs[1]: // -20
|
||||||
powerRaw = paTable[1][f];
|
*raw = paTable[1][f];
|
||||||
break;
|
break;
|
||||||
case -15:
|
case allowedPwrs[2]: // -15
|
||||||
powerRaw = paTable[2][f];
|
*raw = paTable[2][f];
|
||||||
break;
|
break;
|
||||||
case -10:
|
case allowedPwrs[3]: // -10
|
||||||
powerRaw = paTable[3][f];
|
*raw = paTable[3][f];
|
||||||
break;
|
break;
|
||||||
case 0:
|
case allowedPwrs[4]: // 0
|
||||||
powerRaw = paTable[4][f];
|
*raw = paTable[4][f];
|
||||||
break;
|
break;
|
||||||
case 5:
|
case allowedPwrs[5]: // 5
|
||||||
powerRaw = paTable[5][f];
|
*raw = paTable[5][f];
|
||||||
break;
|
break;
|
||||||
case 7:
|
case allowedPwrs[6]: // 7
|
||||||
powerRaw = paTable[6][f];
|
*raw = paTable[6][f];
|
||||||
break;
|
break;
|
||||||
case 10:
|
case allowedPwrs[7]: // 10
|
||||||
powerRaw = paTable[7][f];
|
*raw = paTable[7][f];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return(RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
return(RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
}
|
}
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
// store the value
|
|
||||||
this->power = pwr;
|
|
||||||
|
|
||||||
if(this->modulation == RADIOLIB_CC1101_MOD_FORMAT_ASK_OOK){
|
|
||||||
// Amplitude modulation:
|
|
||||||
// PA_TABLE[0] is the power to be used when transmitting a 0 (no power)
|
|
||||||
// PA_TABLE[1] is the power to be used when transmitting a 1 (full power)
|
|
||||||
|
|
||||||
uint8_t paValues[2] = {0x00, powerRaw};
|
|
||||||
SPIwriteRegisterBurst(RADIOLIB_CC1101_REG_PATABLE, paValues, 2);
|
|
||||||
return(RADIOLIB_ERR_NONE);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Freq modulation:
|
|
||||||
// PA_TABLE[0] is the power to be used when transmitting.
|
|
||||||
return(SPIsetRegValue(RADIOLIB_CC1101_REG_PATABLE, powerRaw));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t CC1101::setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits, bool requireCarrierSense) {
|
int16_t CC1101::setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits, bool requireCarrierSense) {
|
||||||
|
|
|
@ -774,6 +774,24 @@ class CC1101: public PhysicalLayer {
|
||||||
*/
|
*/
|
||||||
int16_t setOutputPower(int8_t pwr);
|
int16_t setOutputPower(int8_t pwr);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
This method is needed for compatibility with PhysicalLayer::checkOutputPower.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\param raw Raw internal value.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped, uint8_t* raw);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Sets 16-bit sync word as a two byte value.
|
\brief Sets 16-bit sync word as a two byte value.
|
||||||
\param syncH MSB of the sync word.
|
\param syncH MSB of the sync word.
|
||||||
|
|
|
@ -593,22 +593,17 @@ int16_t LR11x0::setOutputPower(int8_t power) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t LR11x0::setOutputPower(int8_t power, bool forceHighPower) {
|
int16_t LR11x0::setOutputPower(int8_t power, bool forceHighPower) {
|
||||||
|
// check if power value is configurable
|
||||||
|
int16_t state = checkOutputPower(power, NULL, forceHighPower);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// determine whether to use HP or LP PA and check range accordingly
|
// determine whether to use HP or LP PA and check range accordingly
|
||||||
bool useHp = forceHighPower || (power > 14);
|
bool useHp = forceHighPower || (power > 14);
|
||||||
if(useHp) {
|
|
||||||
RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
|
||||||
useHp = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
|
||||||
useHp = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO how and when to configure OCP?
|
// TODO how and when to configure OCP?
|
||||||
|
|
||||||
// update PA config - always use VBAT for high-power PA
|
// update PA config - always use VBAT for high-power PA
|
||||||
int16_t state = setPaConfig((uint8_t)useHp, (uint8_t)useHp, 0x04, 0x07);
|
state = setPaConfig((uint8_t)useHp, (uint8_t)useHp, 0x04, 0x07);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// set output power
|
// set output power
|
||||||
|
@ -616,6 +611,27 @@ int16_t LR11x0::setOutputPower(int8_t power, bool forceHighPower) {
|
||||||
return(state);
|
return(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t LR11x0::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
return(checkOutputPower(power, clipped, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t LR11x0::checkOutputPower(int8_t power, int8_t* clipped, bool forceHighPower) {
|
||||||
|
if(forceHighPower || (power > 14)) {
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(-9, RADIOLIB_MIN(22, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(-17, RADIOLIB_MIN(14, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
|
||||||
|
}
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
int16_t LR11x0::setBandwidth(float bw) {
|
int16_t LR11x0::setBandwidth(float bw) {
|
||||||
// check active modem
|
// check active modem
|
||||||
uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
|
uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
|
||||||
|
|
|
@ -802,6 +802,25 @@ class LR11x0: public PhysicalLayer {
|
||||||
*/
|
*/
|
||||||
int16_t setOutputPower(int8_t power, bool forceHighPower);
|
int16_t setOutputPower(int8_t power, bool forceHighPower);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
This method is needed for compatibility with PhysicalLayer::checkOutputPower.
|
||||||
|
\param power Output power in dBm, PA will be determined automatically.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\param forceHighPower Force using the high-power PA. If set to false, PA will be determined automatically
|
||||||
|
based on configured output power, preferring the low-power PA. If set to true, only high-power PA will be used.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped, bool forceHighPower);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Sets LoRa bandwidth. Allowed values are 62.5, 125.0, 250.0 and 500.0 kHz.
|
\brief Sets LoRa bandwidth. Allowed values are 62.5, 125.0, 250.0 and 500.0 kHz.
|
||||||
\param bw LoRa bandwidth to be set in kHz.
|
\param bw LoRa bandwidth to be set in kHz.
|
||||||
|
|
|
@ -6,11 +6,13 @@ SX1261::SX1261(Module* mod): SX1262(mod) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t SX1261::setOutputPower(int8_t power) {
|
int16_t SX1261::setOutputPower(int8_t power) {
|
||||||
RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
// check if power value is configurable
|
||||||
|
int16_t state = checkOutputPower(power, NULL);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// get current OCP configuration
|
// get current OCP configuration
|
||||||
uint8_t ocp = 0;
|
uint8_t ocp = 0;
|
||||||
int16_t state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1);
|
state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// set PA config
|
// set PA config
|
||||||
|
@ -25,4 +27,12 @@ int16_t SX1261::setOutputPower(int8_t power) {
|
||||||
return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1));
|
return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t SX1261::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(-17, RADIOLIB_MIN(14, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -34,6 +34,14 @@ class SX1261 : public SX1262 {
|
||||||
*/
|
*/
|
||||||
int16_t setOutputPower(int8_t power);
|
int16_t setOutputPower(int8_t power);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped);
|
||||||
|
|
||||||
#if !RADIOLIB_GODMODE
|
#if !RADIOLIB_GODMODE
|
||||||
private:
|
private:
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -98,11 +98,13 @@ int16_t SX1262::setFrequency(float freq, bool calibrate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t SX1262::setOutputPower(int8_t power) {
|
int16_t SX1262::setOutputPower(int8_t power) {
|
||||||
RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
// check if power value is configurable
|
||||||
|
int16_t state = checkOutputPower(power, NULL);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// get current OCP configuration
|
// get current OCP configuration
|
||||||
uint8_t ocp = 0;
|
uint8_t ocp = 0;
|
||||||
int16_t state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1);
|
state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// set PA config
|
// set PA config
|
||||||
|
@ -117,4 +119,12 @@ int16_t SX1262::setOutputPower(int8_t power) {
|
||||||
return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1));
|
return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t SX1262::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(-9, RADIOLIB_MIN(22, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -87,6 +87,14 @@ class SX1262: public SX126x {
|
||||||
*/
|
*/
|
||||||
virtual int16_t setOutputPower(int8_t power);
|
virtual int16_t setOutputPower(int8_t power);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped);
|
||||||
|
|
||||||
#if !RADIOLIB_GODMODE
|
#if !RADIOLIB_GODMODE
|
||||||
private:
|
private:
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -93,11 +93,13 @@ int16_t SX1268::setFrequency(float freq, bool calibrate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t SX1268::setOutputPower(int8_t power) {
|
int16_t SX1268::setOutputPower(int8_t power) {
|
||||||
RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
// check if power value is configurable
|
||||||
|
int16_t state = checkOutputPower(power, NULL);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// get current OCP configuration
|
// get current OCP configuration
|
||||||
uint8_t ocp = 0;
|
uint8_t ocp = 0;
|
||||||
int16_t state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1);
|
state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// set PA config
|
// set PA config
|
||||||
|
@ -112,4 +114,12 @@ int16_t SX1268::setOutputPower(int8_t power) {
|
||||||
return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1));
|
return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t SX1268::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(-9, RADIOLIB_MIN(22, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -85,6 +85,14 @@ class SX1268: public SX126x {
|
||||||
*/
|
*/
|
||||||
int16_t setOutputPower(int8_t power);
|
int16_t setOutputPower(int8_t power);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped);
|
||||||
|
|
||||||
#if !RADIOLIB_GODMODE
|
#if !RADIOLIB_GODMODE
|
||||||
private:
|
private:
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -280,15 +280,12 @@ int16_t SX1272::setOutputPower(int8_t power) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t SX1272::setOutputPower(int8_t power, bool useRfo) {
|
int16_t SX1272::setOutputPower(int8_t power, bool useRfo) {
|
||||||
// check allowed power range
|
// check if power value is configurable
|
||||||
if(useRfo) {
|
int16_t state = checkOutputPower(power, NULL, useRfo);
|
||||||
RADIOLIB_CHECK_RANGE(power, -1, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
RADIOLIB_ASSERT(state);
|
||||||
} else {
|
|
||||||
RADIOLIB_CHECK_RANGE(power, 2, 20, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set mode to standby
|
// set mode to standby
|
||||||
int16_t state = SX127x::standby();
|
state = SX127x::standby();
|
||||||
Module* mod = this->getMod();
|
Module* mod = this->getMod();
|
||||||
|
|
||||||
if(useRfo) {
|
if(useRfo) {
|
||||||
|
@ -317,6 +314,26 @@ int16_t SX1272::setOutputPower(int8_t power, bool useRfo) {
|
||||||
return(state);
|
return(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t SX1272::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
return(checkOutputPower(power, clipped, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t SX1272::checkOutputPower(int8_t power, int8_t* clipped, bool useRfo) {
|
||||||
|
// check allowed power range
|
||||||
|
if(useRfo) {
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(-1, RADIOLIB_MIN(14, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -1, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
} else {
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(2, RADIOLIB_MIN(20, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, 2, 20, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
}
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
int16_t SX1272::setGain(uint8_t gain) {
|
int16_t SX1272::setGain(uint8_t gain) {
|
||||||
// check allowed range
|
// check allowed range
|
||||||
if(gain > 6) {
|
if(gain > 6) {
|
||||||
|
|
|
@ -206,6 +206,24 @@ class SX1272: public SX127x {
|
||||||
*/
|
*/
|
||||||
int16_t setOutputPower(int8_t power, bool useRfo);
|
int16_t setOutputPower(int8_t power, bool useRfo);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
This method is needed for compatibility with PhysicalLayer::checkOutputPower.
|
||||||
|
\param power Output power in dBm, assumes PA_BOOST pin.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\param useRfo Whether to use the RFO (true) or the PA_BOOST (false) pin for the RF output.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped, bool useRfo);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Sets gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain.
|
\brief Sets gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain.
|
||||||
Set to 0 to enable automatic gain control (recommended).
|
Set to 0 to enable automatic gain control (recommended).
|
||||||
|
|
|
@ -294,19 +294,12 @@ int16_t SX1278::setOutputPower(int8_t power) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t SX1278::setOutputPower(int8_t power, bool useRfo) {
|
int16_t SX1278::setOutputPower(int8_t power, bool useRfo) {
|
||||||
// check allowed power range
|
// check if power value is configurable
|
||||||
if(useRfo) {
|
int16_t state = checkOutputPower(power, NULL, useRfo);
|
||||||
// RFO output
|
RADIOLIB_ASSERT(state);
|
||||||
RADIOLIB_CHECK_RANGE(power, -3, 15, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
|
||||||
} else {
|
|
||||||
// PA_BOOST output, check high-power operation
|
|
||||||
if(power != 20) {
|
|
||||||
RADIOLIB_CHECK_RANGE(power, 2, 17, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set mode to standby
|
// set mode to standby
|
||||||
int16_t state = SX127x::standby();
|
state = SX127x::standby();
|
||||||
Module* mod = this->getMod();
|
Module* mod = this->getMod();
|
||||||
|
|
||||||
if(useRfo) {
|
if(useRfo) {
|
||||||
|
@ -342,6 +335,34 @@ int16_t SX1278::setOutputPower(int8_t power, bool useRfo) {
|
||||||
return(state);
|
return(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t SX1278::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
return(checkOutputPower(power, clipped, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t SX1278::checkOutputPower(int8_t power, int8_t* clipped, bool useRfo) {
|
||||||
|
// check allowed power range
|
||||||
|
if(useRfo) {
|
||||||
|
// RFO output
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(-3, RADIOLIB_MIN(15, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -3, 15, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
} else {
|
||||||
|
// PA_BOOST output, check high-power operation
|
||||||
|
if(clipped) {
|
||||||
|
if(power != 20) {
|
||||||
|
*clipped = RADIOLIB_MAX(2, RADIOLIB_MIN(17, power));
|
||||||
|
} else {
|
||||||
|
*clipped = 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(power != 20) {
|
||||||
|
RADIOLIB_CHECK_RANGE(power, 2, 17, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
int16_t SX1278::setGain(uint8_t gain) {
|
int16_t SX1278::setGain(uint8_t gain) {
|
||||||
// check allowed range
|
// check allowed range
|
||||||
if(gain > 6) {
|
if(gain > 6) {
|
||||||
|
|
|
@ -218,6 +218,24 @@ class SX1278: public SX127x {
|
||||||
*/
|
*/
|
||||||
int16_t setOutputPower(int8_t power, bool useRfo);
|
int16_t setOutputPower(int8_t power, bool useRfo);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
This method is needed for compatibility with PhysicalLayer::checkOutputPower.
|
||||||
|
\param power Output power in dBm, assumes PA_BOOST pin.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\param useRfo Whether to use the RFO (true) or the PA_BOOST (false) pin for the RF output.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped, bool useRfo);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Sets gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain.
|
\brief Sets gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain.
|
||||||
Set to 0 to enable automatic gain control (recommended).
|
Set to 0 to enable automatic gain control (recommended).
|
||||||
|
|
|
@ -765,11 +765,22 @@ int16_t SX128x::setCodingRate(uint8_t cr, bool longInterleaving) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t SX128x::setOutputPower(int8_t pwr) {
|
int16_t SX128x::setOutputPower(int8_t pwr) {
|
||||||
RADIOLIB_CHECK_RANGE(pwr, -18, 13, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
// check if power value is configurable
|
||||||
|
int16_t state = checkOutputPower(power, NULL);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
this->power = pwr + 18;
|
this->power = pwr + 18;
|
||||||
return(setTxParams(this->power));
|
return(setTxParams(this->power));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t SX128x::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
if(clipped) {
|
||||||
|
*clipped = RADIOLIB_MAX(-18, RADIOLIB_MIN(13, power));
|
||||||
|
}
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -18, 13, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
int16_t SX128x::setPreambleLength(uint32_t preambleLength) {
|
int16_t SX128x::setPreambleLength(uint32_t preambleLength) {
|
||||||
uint8_t modem = getPacketType();
|
uint8_t modem = getPacketType();
|
||||||
if((modem == RADIOLIB_SX128X_PACKET_TYPE_LORA) || (modem == RADIOLIB_SX128X_PACKET_TYPE_RANGING)) {
|
if((modem == RADIOLIB_SX128X_PACKET_TYPE_LORA) || (modem == RADIOLIB_SX128X_PACKET_TYPE_RANGING)) {
|
||||||
|
|
|
@ -608,6 +608,14 @@ class SX128x: public PhysicalLayer {
|
||||||
*/
|
*/
|
||||||
int16_t setOutputPower(int8_t pwr);
|
int16_t setOutputPower(int8_t pwr);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable.
|
||||||
|
\param power Output power in dBm.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
int16_t checkOutputPower(int8_t power, int8_t* clipped);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Sets preamble length for currently active modem. Allowed values range from 1 to 65535.
|
\brief Sets preamble length for currently active modem. Allowed values range from 1 to 65535.
|
||||||
\param preambleLength Preamble length to be set in symbols (LoRa) or bits (FSK/BLE/FLRC).
|
\param preambleLength Preamble length to be set in symbols (LoRa) or bits (FSK/BLE/FLRC).
|
||||||
|
|
|
@ -241,9 +241,6 @@ int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass
|
||||||
// copy uplink MAC command queue back in place
|
// copy uplink MAC command queue back in place
|
||||||
memcpy(&this->commandsUp, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL], sizeof(LoRaWANMacCommandQueue_t));
|
memcpy(&this->commandsUp, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL], sizeof(LoRaWANMacCommandQueue_t));
|
||||||
|
|
||||||
state = this->setPhyProperties();
|
|
||||||
RADIOLIB_ASSERT(state);
|
|
||||||
|
|
||||||
// full session is restored, so set joined flag to whichever mode is restored
|
// full session is restored, so set joined flag to whichever mode is restored
|
||||||
this->activeMode = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_MODE]);
|
this->activeMode = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_MODE]);
|
||||||
|
|
||||||
|
@ -498,16 +495,12 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
// setup all MAC properties to default values
|
// setup all MAC properties to default values
|
||||||
this->beginCommon(joinDr);
|
this->beginCommon(joinDr);
|
||||||
|
|
||||||
// set the physical layer configuration
|
|
||||||
state = this->setPhyProperties();
|
|
||||||
RADIOLIB_ASSERT(state);
|
|
||||||
|
|
||||||
// select a random pair of Tx/Rx channels
|
// select a random pair of Tx/Rx channels
|
||||||
state = this->selectChannels();
|
state = this->selectChannels();
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// configure for uplink with default configuration
|
// set the physical layer configuration for uplink
|
||||||
state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
state = this->setPhyProperties(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// copy devNonce currently in use
|
// copy devNonce currently in use
|
||||||
|
@ -732,7 +725,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
return(RADIOLIB_ERR_NONE);
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, bool force, uint8_t initialDr) {
|
int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey, bool force, uint8_t initialDr) {
|
||||||
// if not forced and already joined, don't do anything
|
// if not forced and already joined, don't do anything
|
||||||
if(!force && this->isJoined()) {
|
if(!force && this->isJoined()) {
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginABP(): Did not rejoin: session already active");
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginABP(): Did not rejoin: session already active");
|
||||||
|
@ -744,7 +737,7 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey,
|
||||||
// check if we actually need to restart from a clean session
|
// check if we actually need to restart from a clean session
|
||||||
uint16_t checkSum = 0;
|
uint16_t checkSum = 0;
|
||||||
checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast<uint8_t*>(&addr), 4);
|
checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast<uint8_t*>(&addr), 4);
|
||||||
checkSum ^= LoRaWANNode::checkSum16(nwkSKey, 16);
|
checkSum ^= LoRaWANNode::checkSum16(nwkSEncKey, 16);
|
||||||
checkSum ^= LoRaWANNode::checkSum16(appSKey, 16);
|
checkSum ^= LoRaWANNode::checkSum16(appSKey, 16);
|
||||||
if(fNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(fNwkSIntKey, 16); }
|
if(fNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(fNwkSIntKey, 16); }
|
||||||
if(sNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(sNwkSIntKey, 16); }
|
if(sNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(sNwkSIntKey, 16); }
|
||||||
|
@ -763,12 +756,12 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey,
|
||||||
|
|
||||||
this->devAddr = addr;
|
this->devAddr = addr;
|
||||||
memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE);
|
memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE);
|
||||||
memcpy(this->nwkSEncKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE);
|
memcpy(this->nwkSEncKey, nwkSEncKey, RADIOLIB_AES128_KEY_SIZE);
|
||||||
if(fNwkSIntKey) {
|
if(fNwkSIntKey) {
|
||||||
this->rev = 1;
|
this->rev = 1;
|
||||||
memcpy(this->fNwkSIntKey, fNwkSIntKey, RADIOLIB_AES128_KEY_SIZE);
|
memcpy(this->fNwkSIntKey, fNwkSIntKey, RADIOLIB_AES128_KEY_SIZE);
|
||||||
} else {
|
} else {
|
||||||
memcpy(this->fNwkSIntKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE);
|
memcpy(this->fNwkSIntKey, nwkSEncKey, RADIOLIB_AES128_KEY_SIZE);
|
||||||
}
|
}
|
||||||
if(sNwkSIntKey) {
|
if(sNwkSIntKey) {
|
||||||
memcpy(this->sNwkSIntKey, sNwkSIntKey, RADIOLIB_AES128_KEY_SIZE);
|
memcpy(this->sNwkSIntKey, sNwkSIntKey, RADIOLIB_AES128_KEY_SIZE);
|
||||||
|
@ -784,10 +777,6 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey,
|
||||||
// setup all MAC properties to default values
|
// setup all MAC properties to default values
|
||||||
this->beginCommon(initialDr);
|
this->beginCommon(initialDr);
|
||||||
|
|
||||||
// set the physical layer configuration
|
|
||||||
state = this->setPhyProperties();
|
|
||||||
RADIOLIB_ASSERT(state);
|
|
||||||
|
|
||||||
// reset all frame counters
|
// reset all frame counters
|
||||||
this->fcntUp = 0;
|
this->fcntUp = 0;
|
||||||
this->aFcntDown = 0;
|
this->aFcntDown = 0;
|
||||||
|
@ -970,9 +959,9 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure for uplink
|
// set the physical layer configuration for uplink
|
||||||
this->selectChannels();
|
this->selectChannels();
|
||||||
state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
state = this->setPhyProperties(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// if dwell time is imposed, calculated expected time on air and cancel if exceeds
|
// if dwell time is imposed, calculated expected time on air and cancel if exceeds
|
||||||
|
@ -1019,12 +1008,8 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
||||||
|
|
||||||
// check if we have some MAC commands to append
|
// check if we have some MAC commands to append
|
||||||
if(foptsLen > 0) {
|
if(foptsLen > 0) {
|
||||||
#if RADIOLIB_STATIC_ONLY
|
|
||||||
// assume maximum possible buffer size
|
// assume maximum possible buffer size
|
||||||
uint8_t foptsBuff[RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN];
|
uint8_t foptsBuff[RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN];
|
||||||
#else
|
|
||||||
uint8_t* foptsBuff = new uint8_t[foptsLen];
|
|
||||||
#endif
|
|
||||||
uint8_t* foptsPtr = foptsBuff;
|
uint8_t* foptsPtr = foptsBuff;
|
||||||
|
|
||||||
// append all MAC replies into fopts buffer
|
// append all MAC replies into fopts buffer
|
||||||
|
@ -1052,9 +1037,6 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
||||||
// encrypt it
|
// encrypt it
|
||||||
processAES(foptsBuff, foptsLen, this->nwkSEncKey, &uplinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], this->fcntUp, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x01, true);
|
processAES(foptsBuff, foptsLen, this->nwkSEncKey, &uplinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], this->fcntUp, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x01, true);
|
||||||
|
|
||||||
#if !RADIOLIB_STATIC_ONLY
|
|
||||||
delete[] foptsBuff;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the port
|
// set the port
|
||||||
|
@ -1146,30 +1128,28 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
||||||
|
|
||||||
int16_t LoRaWANNode::downlinkCommon() {
|
int16_t LoRaWANNode::downlinkCommon() {
|
||||||
Module* mod = this->phyLayer->getMod();
|
Module* mod = this->phyLayer->getMod();
|
||||||
const RadioLibTime_t scanGuard = 10;
|
|
||||||
|
// according to the spec, the Rx window must be at least enough time to effectively detect a preamble
|
||||||
|
// but we pad it a bit on both sides (start and end) to make sure it is wide enough
|
||||||
|
const RadioLibTime_t scanGuard = 10; // Rx window padding in milliseconds
|
||||||
|
|
||||||
// check if there are any upcoming Rx windows
|
// check if there are any upcoming Rx windows
|
||||||
// if the Rx1 window has already started, you're too late, because most downlinks happen in Rx1
|
// if the Rx1 window has already started, you're too late, because most downlinks happen in Rx1
|
||||||
if(mod->hal->millis() - this->rxDelayStart > (this->rxDelays[0] - scanGuard)) {
|
RadioLibTime_t now = mod->hal->millis(); // Fix the current timestamp to prevent negative delays
|
||||||
|
if(now > this->rxDelayStart + this->rxDelays[0] - scanGuard) {
|
||||||
// if between start of Rx1 and end of Rx2, wait until Rx2 closes
|
// if between start of Rx1 and end of Rx2, wait until Rx2 closes
|
||||||
if(mod->hal->millis() - this->rxDelayStart < this->rxDelays[1]) {
|
if(now < this->rxDelayStart + this->rxDelays[1]) {
|
||||||
mod->hal->delay(this->rxDelays[1] + this->rxDelayStart - mod->hal->millis());
|
mod->hal->delay(this->rxDelays[1] + this->rxDelayStart - now);
|
||||||
}
|
}
|
||||||
// update the end timestamp in case user got stuck between uplink and downlink
|
// update the end timestamp in case user got stuck between uplink and downlink
|
||||||
this->rxDelayEnd = mod->hal->millis();
|
this->rxDelayEnd = mod->hal->millis();
|
||||||
return(RADIOLIB_ERR_NO_RX_WINDOW);
|
return(RADIOLIB_ERR_NO_RX_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure for downlink
|
// set the physical layer configuration for downlink
|
||||||
int16_t state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK);
|
int16_t state = this->setPhyProperties(RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// downlink messages are sent with inverted IQ
|
|
||||||
if(!this->FSK) {
|
|
||||||
state = this->phyLayer->invertIQ(true);
|
|
||||||
RADIOLIB_ASSERT(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the masks that are required for receiving downlinks
|
// create the masks that are required for receiving downlinks
|
||||||
uint16_t irqFlags = 0x0000;
|
uint16_t irqFlags = 0x0000;
|
||||||
uint16_t irqMask = 0x0000;
|
uint16_t irqMask = 0x0000;
|
||||||
|
@ -1182,14 +1162,16 @@ int16_t LoRaWANNode::downlinkCommon() {
|
||||||
downlinkAction = false;
|
downlinkAction = false;
|
||||||
|
|
||||||
// calculate the Rx timeout
|
// calculate the Rx timeout
|
||||||
// according to the spec, this must be at least enough time to effectively detect a preamble
|
|
||||||
// but pad it a bit on both sides (start and end) to make sure it is wide enough
|
|
||||||
RadioLibTime_t timeoutHost = this->phyLayer->getTimeOnAir(0) + 2*scanGuard*1000;
|
RadioLibTime_t timeoutHost = this->phyLayer->getTimeOnAir(0) + 2*scanGuard*1000;
|
||||||
RadioLibTime_t timeoutMod = this->phyLayer->calculateRxTimeout(timeoutHost);
|
RadioLibTime_t timeoutMod = this->phyLayer->calculateRxTimeout(timeoutHost);
|
||||||
|
|
||||||
// wait for the start of the Rx window
|
// wait for the start of the Rx window
|
||||||
|
RadioLibTime_t waitLen = this->rxDelayStart + this->rxDelays[i] - mod->hal->millis();
|
||||||
|
// make sure that no underflow occured; if so, clip the delay (although this will likely miss any downlink)
|
||||||
|
if(waitLen > this->rxDelays[i]) {
|
||||||
|
waitLen = this->rxDelays[i];
|
||||||
|
}
|
||||||
// the waiting duration is shortened a bit to cover any possible timing errors
|
// the waiting duration is shortened a bit to cover any possible timing errors
|
||||||
RadioLibTime_t waitLen = this->rxDelays[i] - (mod->hal->millis() - this->rxDelayStart);
|
|
||||||
if(waitLen > scanGuard) {
|
if(waitLen > scanGuard) {
|
||||||
waitLen -= scanGuard;
|
waitLen -= scanGuard;
|
||||||
}
|
}
|
||||||
|
@ -1215,7 +1197,8 @@ int16_t LoRaWANNode::downlinkCommon() {
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
DataRate_t dataRate;
|
DataRate_t dataRate;
|
||||||
findDataRate(this->rx2.drMax, &dataRate);
|
state = findDataRate(this->rx2.drMax, &dataRate);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
state = this->phyLayer->setDataRate(dataRate);
|
state = this->phyLayer->setDataRate(dataRate);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
}
|
}
|
||||||
|
@ -1687,16 +1670,51 @@ bool LoRaWANNode::verifyMIC(uint8_t* msg, size_t len, uint8_t* key) {
|
||||||
return(true);
|
return(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::setPhyProperties() {
|
int16_t LoRaWANNode::setPhyProperties(uint8_t dir) {
|
||||||
// set the physical layer configuration
|
// set the physical layer configuration
|
||||||
int8_t pwr = this->txPowerMax - this->txPowerCur * 2;
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("");
|
||||||
int16_t state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: Frequency %cL = %6.3f MHz", dir ? 'D' : 'U', this->currentChannels[dir].freq);
|
||||||
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
|
int16_t state = this->phyLayer->setFrequency(this->currentChannels[dir].freq);
|
||||||
// go from the highest power and lower it until we hit one supported by the module
|
|
||||||
state = this->phyLayer->setOutputPower(pwr--);
|
|
||||||
}
|
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// if this channel is an FSK channel, toggle the FSK switch
|
||||||
|
if(this->band->dataRates[this->dataRates[dir]] == RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K) {
|
||||||
|
this->FSK = true;
|
||||||
|
} else {
|
||||||
|
this->FSK = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t pwr = this->txPowerMax - this->txPowerCur * 2;
|
||||||
|
|
||||||
|
// at this point, assume that Tx power value is already checked, so ignore the return value
|
||||||
|
(void)this->phyLayer->checkOutputPower(pwr, &pwr);
|
||||||
|
state = this->phyLayer->setOutputPower(pwr);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
DataRate_t dr;
|
||||||
|
state = findDataRate(this->dataRates[dir], &dr);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
state = this->phyLayer->setDataRate(dr);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: SF = %d, TX = %d dBm, BW = %6.3f kHz, CR = 4/%d",
|
||||||
|
dr.lora.spreadingFactor, pwr, dr.lora.bandwidth, dr.lora.codingRate);
|
||||||
|
|
||||||
|
if(this->FSK) {
|
||||||
|
state = this->phyLayer->setDataShaping(RADIOLIB_SHAPING_1_0);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
state = this->phyLayer->setEncoding(RADIOLIB_ENCODING_WHITENING);
|
||||||
|
}
|
||||||
|
|
||||||
|
// downlink messages are sent with inverted IQ
|
||||||
|
if(dir == RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK) {
|
||||||
|
if(!this->FSK) {
|
||||||
|
state = this->phyLayer->invertIQ(true);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this only needs to be done once-ish
|
||||||
uint8_t syncWord[3] = { 0 };
|
uint8_t syncWord[3] = { 0 };
|
||||||
uint8_t syncWordLen = 0;
|
uint8_t syncWordLen = 0;
|
||||||
size_t preLen = 0;
|
size_t preLen = 0;
|
||||||
|
@ -1987,7 +2005,8 @@ void LoRaWANNode::setDwellTime(bool enable, RadioLibTime_t msPerUplink) {
|
||||||
uint8_t LoRaWANNode::maxPayloadDwellTime() {
|
uint8_t LoRaWANNode::maxPayloadDwellTime() {
|
||||||
// configure current datarate
|
// configure current datarate
|
||||||
DataRate_t dr;
|
DataRate_t dr;
|
||||||
findDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], &dr);
|
// TODO this may fail horribly?
|
||||||
|
(void)findDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], &dr);
|
||||||
(void)this->phyLayer->setDataRate(dr);
|
(void)this->phyLayer->setDataRate(dr);
|
||||||
uint8_t minPayLen = 0;
|
uint8_t minPayLen = 0;
|
||||||
uint8_t maxPayLen = 255;
|
uint8_t maxPayLen = 255;
|
||||||
|
@ -2035,6 +2054,8 @@ int16_t LoRaWANNode::setTxPower(int8_t txPower) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) {
|
int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) {
|
||||||
|
int16_t state = RADIOLIB_ERR_UNKNOWN;
|
||||||
|
|
||||||
uint8_t dataRateBand = this->band->dataRates[dr];
|
uint8_t dataRateBand = this->band->dataRates[dr];
|
||||||
|
|
||||||
if(dataRateBand & RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K) {
|
if(dataRateBand & RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K) {
|
||||||
|
@ -2059,50 +2080,37 @@ int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) {
|
||||||
|
|
||||||
dataRate->lora.spreadingFactor = ((dataRateBand & 0x70) >> 4) + 6;
|
dataRate->lora.spreadingFactor = ((dataRateBand & 0x70) >> 4) + 6;
|
||||||
dataRate->lora.codingRate = (dataRateBand & 0x03) + 5;
|
dataRate->lora.codingRate = (dataRateBand & 0x03) + 5;
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: SF = %d, BW = %6.3f kHz, CR = 4/%d",
|
|
||||||
dataRate->lora.spreadingFactor, dataRate->lora.bandwidth, dataRate->lora.codingRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return(RADIOLIB_ERR_NONE);
|
state = this->phyLayer->checkDataRate(*dataRate);
|
||||||
}
|
|
||||||
|
|
||||||
int16_t LoRaWANNode::configureChannel(uint8_t dir) {
|
|
||||||
// set the frequency
|
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("");
|
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: Frequency %cL = %6.3f MHz", dir ? 'D' : 'U', this->currentChannels[dir].freq);
|
|
||||||
int state = this->phyLayer->setFrequency(this->currentChannels[dir].freq);
|
|
||||||
RADIOLIB_ASSERT(state);
|
|
||||||
|
|
||||||
// if this channel is an FSK channel, toggle the FSK switch
|
|
||||||
if(this->band->dataRates[this->dataRates[dir]] == RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K) {
|
|
||||||
this->FSK = true;
|
|
||||||
} else {
|
|
||||||
this->FSK = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataRate_t dr;
|
|
||||||
findDataRate(this->dataRates[dir], &dr);
|
|
||||||
state = this->phyLayer->setDataRate(dr);
|
|
||||||
RADIOLIB_ASSERT(state);
|
|
||||||
|
|
||||||
if(this->FSK) {
|
|
||||||
state = this->phyLayer->setDataShaping(RADIOLIB_SHAPING_1_0);
|
|
||||||
RADIOLIB_ASSERT(state);
|
|
||||||
state = this->phyLayer->setEncoding(RADIOLIB_ENCODING_WHITENING);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(state);
|
return(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoRaWANNode::sendMacCommandReq(uint8_t cid) {
|
int16_t LoRaWANNode::sendMacCommandReq(uint8_t cid) {
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_MAC_COMMANDS; i++) {
|
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_MAC_COMMANDS; i++) {
|
||||||
if(MacTable[i].cid == cid) {
|
if(MacTable[i].cid == cid) {
|
||||||
valid = MacTable[i].user;
|
valid = MacTable[i].user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!valid)
|
if(!valid) {
|
||||||
return(false);
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("You are not allowed to request this MAC command");
|
||||||
|
return(RADIOLIB_ERR_INVALID_CID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are already 15 MAC bytes in the uplink queue, we can't add a new one
|
||||||
|
if(this->commandsUp.len + 1 > RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN) {
|
||||||
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("The maximum number of FOpts payload was reached");
|
||||||
|
return(RADIOLIB_ERR_COMMAND_QUEUE_FULL);
|
||||||
|
}
|
||||||
|
if(this->commandsUp.numCommands > RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE) {
|
||||||
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("The RadioLib internal MAC command queue was full");
|
||||||
|
return(RADIOLIB_ERR_COMMAND_QUEUE_FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete any prior requests for this MAC command, in case this is requested more than once
|
||||||
|
(void)deleteMacCommand(cid, &this->commandsUp);
|
||||||
|
|
||||||
LoRaWANMacCommand_t cmd = {
|
LoRaWANMacCommand_t cmd = {
|
||||||
.cid = cid,
|
.cid = cid,
|
||||||
|
@ -2182,8 +2190,6 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
case(RADIOLIB_LORAWAN_MAC_LINK_ADR): {
|
case(RADIOLIB_LORAWAN_MAC_LINK_ADR): {
|
||||||
int16_t state = RADIOLIB_ERR_UNKNOWN;
|
int16_t state = RADIOLIB_ERR_UNKNOWN;
|
||||||
// get the ADR configuration
|
// get the ADR configuration
|
||||||
// per spec, all these configuration should only be set if all ACKs are set, otherwise retain previous state
|
|
||||||
// but we don't bother and try to set each individual command
|
|
||||||
uint8_t drUp = (cmd->payload[0] & 0xF0) >> 4;
|
uint8_t drUp = (cmd->payload[0] & 0xF0) >> 4;
|
||||||
uint8_t txSteps = cmd->payload[0] & 0x0F;
|
uint8_t txSteps = cmd->payload[0] & 0x0F;
|
||||||
bool isInternalTxDr = cmd->payload[3] >> 7;
|
bool isInternalTxDr = cmd->payload[3] >> 7;
|
||||||
|
@ -2193,19 +2199,15 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
uint8_t nbTrans = cmd->payload[3] & 0x0F;
|
uint8_t nbTrans = cmd->payload[3] & 0x0F;
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LinkADRReq: dataRate = %d, txSteps = %d, chMask = 0x%04x, chMaskCntl = %d, nbTrans = %d", drUp, txSteps, chMask, chMaskCntl, nbTrans);
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LinkADRReq: dataRate = %d, txSteps = %d, chMask = 0x%04x, chMaskCntl = %d, nbTrans = %d", drUp, txSteps, chMask, chMaskCntl, nbTrans);
|
||||||
|
|
||||||
// apply the configuration
|
// try to apply the datarate configuration
|
||||||
uint8_t drAck = 0;
|
uint8_t drAck = 0;
|
||||||
if(drUp == 0x0F) { // keep the same
|
if(drUp == 0x0F) { // keep the same
|
||||||
drAck = 1;
|
drAck = 1;
|
||||||
|
|
||||||
// replace the 'placeholder' with the current actual value for saving
|
|
||||||
cmd->payload[0] = (cmd->payload[0] & 0x0F) | (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4);
|
|
||||||
|
|
||||||
} else if (this->band->dataRates[drUp] != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
|
} else if (this->band->dataRates[drUp] != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
|
||||||
// check if the module supports this data rate
|
// check if the module supports this data rate
|
||||||
DataRate_t dr;
|
DataRate_t dr;
|
||||||
findDataRate(drUp, &dr);
|
state = findDataRate(drUp, &dr);
|
||||||
state = this->phyLayer->checkDataRate(dr);
|
|
||||||
if(state == RADIOLIB_ERR_NONE) {
|
if(state == RADIOLIB_ERR_NONE) {
|
||||||
uint8_t drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase,
|
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].drMin,
|
||||||
|
@ -2215,6 +2217,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
drAck = 1;
|
drAck = 1;
|
||||||
} else {
|
} else {
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADR failed to configure dataRate %d, code %d!", drUp, state);
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADR failed to configure dataRate %d, code %d!", drUp, state);
|
||||||
|
drUp = 0x0F; // set value to 'keep the same'
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2224,25 +2227,20 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
if(txSteps == 0x0F) {
|
if(txSteps == 0x0F) {
|
||||||
pwrAck = 1;
|
pwrAck = 1;
|
||||||
|
|
||||||
// replace the 'placeholder' with the current actual value for saving
|
|
||||||
cmd->payload[0] = (cmd->payload[0] & 0xF0) | this->txPowerCur;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
int8_t pwr = this->txPowerMax - 2*txSteps;
|
int8_t power = this->txPowerMax - 2*txSteps;
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: TX = %d dBm", pwr);
|
int8_t powerActual = 0;
|
||||||
state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
|
state = this->phyLayer->checkOutputPower(power, &powerActual);
|
||||||
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
|
// only acknowledge if the radio is able to operate at or below the requested power level
|
||||||
// go from the highest power and lower it until we hit one supported by the module
|
if(state == RADIOLIB_ERR_NONE || (state == RADIOLIB_ERR_INVALID_OUTPUT_POWER && powerActual < power)) {
|
||||||
state = this->phyLayer->setOutputPower(pwr--);
|
|
||||||
}
|
|
||||||
// only acknowledge if the requested datarate was succesfully configured
|
|
||||||
if(state == RADIOLIB_ERR_NONE) {
|
|
||||||
pwrAck = 1;
|
pwrAck = 1;
|
||||||
this->txPowerCur = txSteps;
|
this->txPowerCur = txSteps;
|
||||||
|
} else {
|
||||||
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADR failed to configure Tx power %d, code %d!", power, state);
|
||||||
|
txSteps = 0x0F; // set value to 'keep the same'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint8_t chMaskAck = 1;
|
uint8_t chMaskAck = 1;
|
||||||
// only apply channel mask when the RFU bit is not set
|
// only apply channel mask when the RFU bit is not set
|
||||||
// (which is only set in internal MAC commands for changing Tx/Dr)
|
// (which is only set in internal MAC commands for changing Tx/Dr)
|
||||||
|
@ -2269,12 +2267,23 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nbTrans == 0) { // keep the same
|
if(nbTrans) { // if there is a value for NbTrans, set this value
|
||||||
cmd->payload[3] = (cmd->payload[3] & 0xF0) | this->nbTrans; // set current number of retransmissions for saving
|
|
||||||
} else {
|
|
||||||
this->nbTrans = nbTrans;
|
this->nbTrans = nbTrans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replace 'placeholder' or failed values with the current values for saving
|
||||||
|
// per spec, all these configuration should only be set if all ACKs are set, otherwise retain previous state
|
||||||
|
// but we don't bother and try to set each individual command
|
||||||
|
if(drUp == 0x0F || !drAck) {
|
||||||
|
cmd->payload[0] = (cmd->payload[0] & 0x0F) | (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4);
|
||||||
|
}
|
||||||
|
if(txSteps == 0x0F || !pwrAck) {
|
||||||
|
cmd->payload[0] = (cmd->payload[0] & 0xF0) | this->txPowerCur;
|
||||||
|
}
|
||||||
|
if(nbTrans == 0) {
|
||||||
|
cmd->payload[3] = (cmd->payload[3] & 0xF0) | this->nbTrans;
|
||||||
|
}
|
||||||
|
|
||||||
if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) {
|
if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) {
|
||||||
// if RFU bit is set, this is just a change in Datarate or TxPower, so read ADR command and overwrite first byte
|
// if RFU bit is set, this is just a change in Datarate or TxPower, so read ADR command and overwrite first byte
|
||||||
if(isInternalTxDr) {
|
if(isInternalTxDr) {
|
||||||
|
@ -2801,6 +2810,10 @@ uint64_t LoRaWANNode::getDevAddr() {
|
||||||
return(this->devAddr);
|
return(this->devAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RadioLibTime_t LoRaWANNode::getLastToA() {
|
||||||
|
return(this->lastToA);
|
||||||
|
}
|
||||||
|
|
||||||
// The following function enables LMAC, a CSMA scheme for LoRa as specified
|
// The following function enables LMAC, a CSMA scheme for LoRa as specified
|
||||||
// in the LoRa Alliance Technical Recommendation #13.
|
// in the LoRa Alliance Technical Recommendation #13.
|
||||||
// A user may enable CSMA to provide frames an additional layer of protection from interference.
|
// A user may enable CSMA to provide frames an additional layer of protection from interference.
|
||||||
|
|
|
@ -159,6 +159,12 @@
|
||||||
#define RADIOLIB_LORAWAN_MIC_DATA_RATE_POS (3)
|
#define RADIOLIB_LORAWAN_MIC_DATA_RATE_POS (3)
|
||||||
#define RADIOLIB_LORAWAN_MIC_CH_INDEX_POS (4)
|
#define RADIOLIB_LORAWAN_MIC_CH_INDEX_POS (4)
|
||||||
|
|
||||||
|
// maximum allowed dwell time on bands that implement dwell time limitations
|
||||||
|
#define RADIOLIB_LORAWAN_DWELL_TIME (400)
|
||||||
|
|
||||||
|
// unused frame counter value
|
||||||
|
#define RADIOLIB_LORAWAN_FCNT_NONE (0xFFFFFFFF)
|
||||||
|
|
||||||
// MAC commands
|
// MAC commands
|
||||||
#define RADIOLIB_LORAWAN_NUM_MAC_COMMANDS (16)
|
#define RADIOLIB_LORAWAN_NUM_MAC_COMMANDS (16)
|
||||||
|
|
||||||
|
@ -179,15 +185,6 @@
|
||||||
#define RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP (0x0F)
|
#define RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP (0x0F)
|
||||||
#define RADIOLIB_LORAWAN_MAC_PROPRIETARY (0x80)
|
#define RADIOLIB_LORAWAN_MAC_PROPRIETARY (0x80)
|
||||||
|
|
||||||
// maximum allowed dwell time on bands that implement dwell time limitations
|
|
||||||
#define RADIOLIB_LORAWAN_DWELL_TIME (400)
|
|
||||||
|
|
||||||
// unused LoRaWAN version
|
|
||||||
#define RADIOLIB_LORAWAN_VERSION_NONE (0xFF)
|
|
||||||
|
|
||||||
// unused frame counter value
|
|
||||||
#define RADIOLIB_LORAWAN_FCNT_NONE (0xFFFFFFFF)
|
|
||||||
|
|
||||||
// the length of internal MAC command queue - hopefully this is enough for most use cases
|
// the length of internal MAC command queue - hopefully this is enough for most use cases
|
||||||
#define RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE (9)
|
#define RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE (9)
|
||||||
|
|
||||||
|
@ -217,74 +214,109 @@ struct LoRaWANMacSpec_t {
|
||||||
const bool user;
|
const bool user;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LoRaWANMacSpec_t MacTable[RADIOLIB_LORAWAN_NUM_MAC_COMMANDS + 1] = {
|
constexpr LoRaWANMacSpec_t MacTable[RADIOLIB_LORAWAN_NUM_MAC_COMMANDS + 1] = {
|
||||||
{ 0x00, 0, 0, false }, // not an actual MAC command, exists for index offsetting
|
{ 0x00, 0, 0, false }, // not an actual MAC command, exists for index offsetting
|
||||||
{ RADIOLIB_LORAWAN_MAC_RESET, 1, 1, false },
|
{ RADIOLIB_LORAWAN_MAC_RESET, 1, 1, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_LINK_CHECK, 2, 0, true },
|
{ RADIOLIB_LORAWAN_MAC_LINK_CHECK, 2, 0, true },
|
||||||
{ RADIOLIB_LORAWAN_MAC_LINK_ADR, 4, 1, false },
|
{ RADIOLIB_LORAWAN_MAC_LINK_ADR, 4, 1, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_DUTY_CYCLE, 1, 0, false },
|
{ RADIOLIB_LORAWAN_MAC_DUTY_CYCLE, 1, 0, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP, 4, 1, false },
|
{ RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP, 4, 1, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_DEV_STATUS, 0, 2, false },
|
{ RADIOLIB_LORAWAN_MAC_DEV_STATUS, 0, 2, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_NEW_CHANNEL, 5, 1, false },
|
{ RADIOLIB_LORAWAN_MAC_NEW_CHANNEL, 5, 1, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP, 1, 0, false },
|
{ RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP, 1, 0, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP, 1, 0, false },
|
{ RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP, 1, 0, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_DL_CHANNEL, 4, 1, false },
|
{ RADIOLIB_LORAWAN_MAC_DL_CHANNEL, 4, 1, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_REKEY, 1, 1, false },
|
{ RADIOLIB_LORAWAN_MAC_REKEY, 1, 1, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP, 1, 0, false },
|
{ RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP, 1, 0, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_DEVICE_TIME, 5, 0, true },
|
{ RADIOLIB_LORAWAN_MAC_DEVICE_TIME, 5, 0, true },
|
||||||
{ RADIOLIB_LORAWAN_MAC_FORCE_REJOIN, 2, 0, false },
|
{ RADIOLIB_LORAWAN_MAC_FORCE_REJOIN, 2, 0, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP, 1, 1, false },
|
{ RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP, 1, 1, false },
|
||||||
{ RADIOLIB_LORAWAN_MAC_PROPRIETARY, 5, 0, true }
|
{ RADIOLIB_LORAWAN_MAC_PROPRIETARY, 5, 0, true }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\struct LoRaWANMacCommand_t
|
||||||
|
\brief Structure to save information about MAC command
|
||||||
|
*/
|
||||||
|
struct LoRaWANMacCommand_t {
|
||||||
|
/*! \brief The command ID */
|
||||||
|
uint8_t cid;
|
||||||
|
|
||||||
|
/*! \brief Payload buffer (5 bytes is the longest possible) */
|
||||||
|
uint8_t payload[5];
|
||||||
|
|
||||||
|
/*! \brief Length of the payload */
|
||||||
|
uint8_t len;
|
||||||
|
|
||||||
|
/*! \brief Repetition counter (the command will be uplinked repeat + 1 times) */
|
||||||
|
uint8_t repeat;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\struct LoRaWANMacCommandQueue_t
|
||||||
|
\brief Structure to hold information about a queue of MAC commands
|
||||||
|
*/
|
||||||
|
struct LoRaWANMacCommandQueue_t {
|
||||||
|
/*! \brief Number of commands in the queue */
|
||||||
|
uint8_t numCommands;
|
||||||
|
|
||||||
|
/*! \brief Total length of the queue */
|
||||||
|
uint8_t len;
|
||||||
|
|
||||||
|
/*! \brief MAC command buffer */
|
||||||
|
LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RADIOLIB_LORAWAN_NONCES_VERSION_VAL (0x0001)
|
#define RADIOLIB_LORAWAN_NONCES_VERSION_VAL (0x0001)
|
||||||
|
|
||||||
enum LoRaWANSchemeBase_t {
|
enum LoRaWANSchemeBase_t {
|
||||||
RADIOLIB_LORAWAN_NONCES_VERSION = 0x00, // 2 bytes
|
RADIOLIB_LORAWAN_NONCES_START = 0x00,
|
||||||
RADIOLIB_LORAWAN_NONCES_MODE = 0x02, // 2 bytes
|
RADIOLIB_LORAWAN_NONCES_VERSION = RADIOLIB_LORAWAN_NONCES_START, // 2 bytes
|
||||||
RADIOLIB_LORAWAN_NONCES_CLASS = 0x04, // 1 byte
|
RADIOLIB_LORAWAN_NONCES_MODE = RADIOLIB_LORAWAN_NONCES_VERSION + sizeof(uint16_t), // 2 bytes
|
||||||
RADIOLIB_LORAWAN_NONCES_PLAN = 0x05, // 1 byte
|
RADIOLIB_LORAWAN_NONCES_CLASS = RADIOLIB_LORAWAN_NONCES_MODE + sizeof(uint16_t), // 1 byte
|
||||||
RADIOLIB_LORAWAN_NONCES_CHECKSUM = 0x06, // 2 bytes
|
RADIOLIB_LORAWAN_NONCES_PLAN = RADIOLIB_LORAWAN_NONCES_CLASS + sizeof(uint8_t), // 1 byte
|
||||||
RADIOLIB_LORAWAN_NONCES_DEV_NONCE = 0x08, // 2 bytes
|
RADIOLIB_LORAWAN_NONCES_CHECKSUM = RADIOLIB_LORAWAN_NONCES_PLAN + sizeof(uint8_t), // 2 bytes
|
||||||
RADIOLIB_LORAWAN_NONCES_JOIN_NONCE = 0x0A, // 3 bytes
|
RADIOLIB_LORAWAN_NONCES_DEV_NONCE = RADIOLIB_LORAWAN_NONCES_CHECKSUM + sizeof(uint16_t), // 2 bytes
|
||||||
RADIOLIB_LORAWAN_NONCES_ACTIVE = 0x0D, // 1 byte
|
RADIOLIB_LORAWAN_NONCES_JOIN_NONCE = RADIOLIB_LORAWAN_NONCES_DEV_NONCE + sizeof(uint16_t), // 3 bytes
|
||||||
RADIOLIB_LORAWAN_NONCES_SIGNATURE = 0x0E, // 2 bytes
|
RADIOLIB_LORAWAN_NONCES_ACTIVE = RADIOLIB_LORAWAN_NONCES_JOIN_NONCE + 3, // 1 byte
|
||||||
RADIOLIB_LORAWAN_NONCES_BUF_SIZE = 0x10 // = 16 bytes
|
RADIOLIB_LORAWAN_NONCES_SIGNATURE = RADIOLIB_LORAWAN_NONCES_ACTIVE + sizeof(uint8_t), // 2 bytes
|
||||||
|
RADIOLIB_LORAWAN_NONCES_BUF_SIZE = RADIOLIB_LORAWAN_NONCES_SIGNATURE + sizeof(uint16_t) // Nonces buffer size
|
||||||
};
|
};
|
||||||
|
|
||||||
enum LoRaWANSchemeSession_t {
|
enum LoRaWANSchemeSession_t {
|
||||||
RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY = 0x00, // 16 bytes
|
RADIOLIB_LORAWAN_SESSION_START = 0x00,
|
||||||
RADIOLIB_LORAWAN_SESSION_APP_SKEY = 0x10, // 16 bytes
|
RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY = RADIOLIB_LORAWAN_SESSION_START, // 16 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY = 0x20, // 16 bytes
|
RADIOLIB_LORAWAN_SESSION_APP_SKEY = RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY = 0x30, // 16 bytes
|
RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY = RADIOLIB_LORAWAN_SESSION_APP_SKEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_DEV_ADDR = 0x40, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY = RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE = 0x44, // 2 bytes
|
RADIOLIB_LORAWAN_SESSION_DEV_ADDR = RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN = 0x46, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE = RADIOLIB_LORAWAN_SESSION_DEV_ADDR + sizeof(uint32_t), // 2 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP = 0x4A, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN = RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE + sizeof(uint16_t), // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN = 0x4E, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP = RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN + sizeof(uint32_t), // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_RJ_COUNT0 = 0x52, // 2 bytes
|
RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN = RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP + sizeof(uint32_t), // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_RJ_COUNT1 = 0x54, // 2 bytes
|
RADIOLIB_LORAWAN_SESSION_RJ_COUNT0 = RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN + sizeof(uint32_t), // 2 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_HOMENET_ID = 0x56, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_RJ_COUNT1 = RADIOLIB_LORAWAN_SESSION_RJ_COUNT0 + sizeof(uint16_t), // 2 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_VERSION = 0x5A, // 1 byte
|
RADIOLIB_LORAWAN_SESSION_HOMENET_ID = RADIOLIB_LORAWAN_SESSION_RJ_COUNT1 + sizeof(uint16_t), // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE = 0x5B, // 1 byte
|
RADIOLIB_LORAWAN_SESSION_VERSION = RADIOLIB_LORAWAN_SESSION_HOMENET_ID + sizeof(uint32_t), // 1 byte
|
||||||
RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP = 0x5C, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE = RADIOLIB_LORAWAN_SESSION_VERSION + sizeof(uint8_t), // 1 byte
|
||||||
RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP = 0x60, // 1 byte
|
RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP = RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE + MacTable[RADIOLIB_LORAWAN_MAC_DUTY_CYCLE].lenDn, // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP = 0x61, // 1 byte
|
RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP = RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP + MacTable[RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP].lenDn, // 1 byte
|
||||||
RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP = 0x62, // 1 byte
|
RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP = RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP + MacTable[RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP].lenDn, // 1 byte
|
||||||
RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP = 0x63, // 1 byte
|
RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP = RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP + MacTable[RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP].lenDn, // 1 byte
|
||||||
RADIOLIB_LORAWAN_SESSION_BEACON_FREQ = 0x64, // 3 bytes
|
RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP = RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP + MacTable[RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP].lenDn, // 1 byte
|
||||||
RADIOLIB_LORAWAN_SESSION_PING_SLOT_CHANNEL = 0x67, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_BEACON_FREQ = RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP + MacTable[RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP].lenDn, // 3 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_PERIODICITY = 0x6B, // 1 byte
|
RADIOLIB_LORAWAN_SESSION_PING_SLOT_CHANNEL = RADIOLIB_LORAWAN_SESSION_BEACON_FREQ + 3, // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_LAST_TIME = 0x6C, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_PERIODICITY = RADIOLIB_LORAWAN_SESSION_PING_SLOT_CHANNEL + 4, // 1 byte
|
||||||
RADIOLIB_LORAWAN_SESSION_UL_CHANNELS = 0x70, // 16*8 bytes
|
RADIOLIB_LORAWAN_SESSION_LAST_TIME = RADIOLIB_LORAWAN_SESSION_PERIODICITY + 1, // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_DL_CHANNELS = 0xF0, // 16*4 bytes
|
RADIOLIB_LORAWAN_SESSION_UL_CHANNELS = RADIOLIB_LORAWAN_SESSION_LAST_TIME + 4, // 16*5 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL = 0x0130, // 9*8+2 bytes
|
RADIOLIB_LORAWAN_SESSION_DL_CHANNELS = RADIOLIB_LORAWAN_SESSION_UL_CHANNELS + 16*MacTable[RADIOLIB_LORAWAN_MAC_NEW_CHANNEL].lenDn, // 16*4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN = 0x017A, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL = RADIOLIB_LORAWAN_SESSION_DL_CHANNELS + 16*MacTable[RADIOLIB_LORAWAN_MAC_DL_CHANNEL].lenDn, // 9*8+2 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_ADR_FCNT = 0x017E, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN = RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL + sizeof(LoRaWANMacCommandQueue_t), // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_LINK_ADR = 0x0182, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_ADR_FCNT = RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN + sizeof(uint32_t), // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_FCNT_UP = 0x0186, // 4 bytes
|
RADIOLIB_LORAWAN_SESSION_LINK_ADR = RADIOLIB_LORAWAN_SESSION_ADR_FCNT + sizeof(uint32_t), // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_SIGNATURE = 0x018A, // 2 bytes
|
RADIOLIB_LORAWAN_SESSION_FCNT_UP = RADIOLIB_LORAWAN_SESSION_LINK_ADR + MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, // 4 bytes
|
||||||
RADIOLIB_LORAWAN_SESSION_BUF_SIZE = 0x018C // 396 bytes
|
RADIOLIB_LORAWAN_SESSION_SIGNATURE = RADIOLIB_LORAWAN_SESSION_FCNT_UP + sizeof(uint32_t), // 2 bytes
|
||||||
|
RADIOLIB_LORAWAN_SESSION_BUF_SIZE = RADIOLIB_LORAWAN_SESSION_SIGNATURE + sizeof(uint16_t) // Session buffer size
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -428,38 +460,6 @@ enum LoRaWANBandNum_t {
|
||||||
// array of currently supported bands
|
// array of currently supported bands
|
||||||
extern const LoRaWANBand_t* LoRaWANBands[];
|
extern const LoRaWANBand_t* LoRaWANBands[];
|
||||||
|
|
||||||
/*!
|
|
||||||
\struct LoRaWANMacCommand_t
|
|
||||||
\brief Structure to save information about MAC command
|
|
||||||
*/
|
|
||||||
struct LoRaWANMacCommand_t {
|
|
||||||
/*! \brief The command ID */
|
|
||||||
uint8_t cid;
|
|
||||||
|
|
||||||
/*! \brief Payload buffer (5 bytes is the longest possible) */
|
|
||||||
uint8_t payload[5];
|
|
||||||
|
|
||||||
/*! \brief Length of the payload */
|
|
||||||
uint8_t len;
|
|
||||||
|
|
||||||
/*! \brief Repetition counter (the command will be uplinked repeat + 1 times) */
|
|
||||||
uint8_t repeat;
|
|
||||||
};
|
|
||||||
/*!
|
|
||||||
\struct LoRaWANMacCommandQueue_t
|
|
||||||
\brief Structure to hold information about a queue of MAC commands
|
|
||||||
*/
|
|
||||||
struct LoRaWANMacCommandQueue_t {
|
|
||||||
/*! \brief Number of commands in the queue */
|
|
||||||
uint8_t numCommands;
|
|
||||||
|
|
||||||
/*! \brief Total length of the queue */
|
|
||||||
uint8_t len;
|
|
||||||
|
|
||||||
/*! \brief MAC command buffer */
|
|
||||||
LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\struct LoRaWANEvent_t
|
\struct LoRaWANEvent_t
|
||||||
\brief Structure to save extra information about uplink/downlink event.
|
\brief Structure to save extra information about uplink/downlink event.
|
||||||
|
@ -567,15 +567,16 @@ class LoRaWANNode {
|
||||||
\brief Join network by performing activation by personalization.
|
\brief Join network by performing activation by personalization.
|
||||||
In this procedure, all necessary configuration must be provided by the user.
|
In this procedure, all necessary configuration must be provided by the user.
|
||||||
\param addr Device address.
|
\param addr Device address.
|
||||||
\param nwkSKey Pointer to the network session AES-128 key (LoRaWAN 1.0) or MAC command network session key (LoRaWAN 1.1).
|
\param fNwkSIntKey Pointer to the Forwarding network session (LoRaWAN 1.1), NULL for LoRaWAN 1.0.
|
||||||
|
\param sNwkSIntKey Pointer to the Serving network session (LoRaWAN 1.1), NULL for LoRaWAN 1.0.
|
||||||
|
\param nwkSEncKey Pointer to the MAC command network session key [NwkSEncKey] (LoRaWAN 1.1)
|
||||||
|
or network session AES-128 key [NwkSKey] (LoRaWAN 1.0).
|
||||||
\param appSKey Pointer to the application session AES-128 key.
|
\param appSKey Pointer to the application session AES-128 key.
|
||||||
\param fNwkSIntKey Pointer to the Forwarding network session (LoRaWAN 1.1), unused for LoRaWAN 1.0.
|
|
||||||
\param sNwkSIntKey Pointer to the Serving network session (LoRaWAN 1.1), unused for LoRaWAN 1.0.
|
|
||||||
\param force Set to true to force a new session, even if one exists.
|
\param force Set to true to force a new session, even if one exists.
|
||||||
\param initialDr The datarate at which to send the first uplink and any subsequent uplinks (unless ADR is enabled)
|
\param initialDr The datarate at which to send the first uplink and any subsequent uplinks (unless ADR is enabled)
|
||||||
\returns \ref status_codes
|
\returns \ref status_codes
|
||||||
*/
|
*/
|
||||||
int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL, bool force = false, uint8_t initialDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED);
|
int16_t beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey, bool force = false, uint8_t initialDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED);
|
||||||
|
|
||||||
/*! \brief Whether there is an ongoing session active */
|
/*! \brief Whether there is an ongoing session active */
|
||||||
bool isJoined();
|
bool isJoined();
|
||||||
|
@ -591,9 +592,9 @@ class LoRaWANNode {
|
||||||
Only LinkCheck and DeviceTime are available to the user.
|
Only LinkCheck and DeviceTime are available to the user.
|
||||||
Other commands are ignored; duplicate MAC commands are discarded.
|
Other commands are ignored; duplicate MAC commands are discarded.
|
||||||
\param cid ID of the MAC command
|
\param cid ID of the MAC command
|
||||||
\returns Whether or not the MAC command was added to the queue.
|
\returns \ref status_codes
|
||||||
*/
|
*/
|
||||||
bool sendMacCommandReq(uint8_t cid);
|
int16_t sendMacCommandReq(uint8_t cid);
|
||||||
|
|
||||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||||
/*!
|
/*!
|
||||||
|
@ -835,6 +836,12 @@ class LoRaWANNode {
|
||||||
*/
|
*/
|
||||||
uint64_t getDevAddr();
|
uint64_t getDevAddr();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Get the Time-on-air of the last uplink message
|
||||||
|
\returns (RadioLibTime_t) time-on-air (ToA) of last uplink message
|
||||||
|
*/
|
||||||
|
RadioLibTime_t getLastToA();
|
||||||
|
|
||||||
#if !RADIOLIB_GODMODE
|
#if !RADIOLIB_GODMODE
|
||||||
private:
|
private:
|
||||||
#endif
|
#endif
|
||||||
|
@ -964,7 +971,7 @@ class LoRaWANNode {
|
||||||
|
|
||||||
// configure the common physical layer properties (preamble, sync word etc.)
|
// configure the common physical layer properties (preamble, sync word etc.)
|
||||||
// channels must be configured separately by setupChannelsDyn()!
|
// channels must be configured separately by setupChannelsDyn()!
|
||||||
int16_t setPhyProperties();
|
int16_t setPhyProperties(uint8_t dir);
|
||||||
|
|
||||||
// setup uplink/downlink channel data rates and frequencies
|
// setup uplink/downlink channel data rates and frequencies
|
||||||
// for dynamic channels, there is a small set of predefined channels
|
// for dynamic channels, there is a small set of predefined channels
|
||||||
|
@ -984,9 +991,6 @@ class LoRaWANNode {
|
||||||
// find the first usable data rate for the given band
|
// find the first usable data rate for the given band
|
||||||
int16_t findDataRate(uint8_t dr, DataRate_t* dataRate);
|
int16_t findDataRate(uint8_t dr, DataRate_t* dataRate);
|
||||||
|
|
||||||
// configure channel based on cached data rate ID and frequency
|
|
||||||
int16_t configureChannel(uint8_t dir);
|
|
||||||
|
|
||||||
// restore all available channels from persistent storage
|
// restore all available channels from persistent storage
|
||||||
int16_t restoreChannels();
|
int16_t restoreChannels();
|
||||||
|
|
||||||
|
|
|
@ -256,6 +256,12 @@ int16_t PhysicalLayer::setOutputPower(int8_t power) {
|
||||||
return(RADIOLIB_ERR_UNSUPPORTED);
|
return(RADIOLIB_ERR_UNSUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t PhysicalLayer::checkOutputPower(int8_t power, int8_t* clipped) {
|
||||||
|
(void)power;
|
||||||
|
(void)clipped;
|
||||||
|
return(RADIOLIB_ERR_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
int16_t PhysicalLayer::setSyncWord(uint8_t* sync, size_t len) {
|
int16_t PhysicalLayer::setSyncWord(uint8_t* sync, size_t len) {
|
||||||
(void)sync;
|
(void)sync;
|
||||||
(void)len;
|
(void)len;
|
||||||
|
|
|
@ -276,6 +276,14 @@ class PhysicalLayer {
|
||||||
*/
|
*/
|
||||||
virtual int16_t setOutputPower(int8_t power);
|
virtual int16_t setOutputPower(int8_t power);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Check if output power is configurable. Must be implemented in module class if the module supports it.
|
||||||
|
\param power Output power in dBm. The allowed range depends on the module used.
|
||||||
|
\param clipped Clipped output power value to what is possible within the module's range.
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
virtual int16_t checkOutputPower(int8_t power, int8_t* clipped);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Set sync word. Must be implemented in module class if the module supports it.
|
\brief Set sync word. Must be implemented in module class if the module supports it.
|
||||||
\param sync Pointer to the sync word.
|
\param sync Pointer to the sync word.
|
||||||
|
|
Loading…
Add table
Reference in a new issue