[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
|
||||
# apply special flags for LoRaWAN
|
||||
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
|
||||
|
||||
# build sketch
|
||||
|
|
|
@ -12,8 +12,8 @@ const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds
|
|||
#define RADIOLIB_LORAWAN_DEV_ADDR 0x------
|
||||
#endif
|
||||
|
||||
#ifndef RADIOLIB_LORAWAN_NWKS_KEY // Replace with your NwkS Key
|
||||
#define RADIOLIB_LORAWAN_NWKS_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
||||
#ifndef RADIOLIB_LORAWAN_FNWKSINT_KEY // Replace with your FNwkSInt Key
|
||||
#define RADIOLIB_LORAWAN_FNWKSINT_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
||||
#endif
|
||||
#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--
|
||||
|
@ -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
|
||||
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 NwkSEncKey[] = { RADIOLIB_LORAWAN_NWKSENC_KEY }; // Previously fNwkSIntKey
|
||||
uint8_t AppSKey[] = { RADIOLIB_LORAWAN_APPS_KEY };
|
||||
|
|
|
@ -127,6 +127,7 @@ setCodingRate KEYWORD2
|
|||
setFrequency KEYWORD2
|
||||
setSyncWord KEYWORD2
|
||||
setOutputPower KEYWORD2
|
||||
checkOutputPower KEYWORD2
|
||||
setCurrentLimit KEYWORD2
|
||||
setPreambleLength KEYWORD2
|
||||
setGain KEYWORD2
|
||||
|
@ -328,10 +329,10 @@ timeUntilUplink KEYWORD2
|
|||
setDwellTime KEYWORD2
|
||||
maxPayloadDwellTime KEYWORD2
|
||||
setTxPower KEYWORD2
|
||||
setCSMA KEYWORD2
|
||||
getMacLinkCheckAns KEYWORD2
|
||||
getMacDeviceTimeAns KEYWORD2
|
||||
getDevAddr KEYWORD2
|
||||
getLastToA KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
|
|
|
@ -560,6 +560,62 @@ int16_t CC1101::getFrequencyDeviation(float *freqDev) {
|
|||
}
|
||||
|
||||
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
|
||||
uint8_t f;
|
||||
if(this->frequency < 374.0) {
|
||||
|
@ -586,53 +642,35 @@ int16_t CC1101::setOutputPower(int8_t pwr) {
|
|||
{0xCB, 0xC8, 0xCB, 0xC7},
|
||||
{0xC2, 0xC0, 0xC2, 0xC0}};
|
||||
|
||||
uint8_t powerRaw;
|
||||
switch(pwr) {
|
||||
case -30:
|
||||
powerRaw = paTable[0][f];
|
||||
switch(power) {
|
||||
case allowedPwrs[0]: // -30
|
||||
*raw = paTable[0][f];
|
||||
break;
|
||||
case -20:
|
||||
powerRaw = paTable[1][f];
|
||||
case allowedPwrs[1]: // -20
|
||||
*raw = paTable[1][f];
|
||||
break;
|
||||
case -15:
|
||||
powerRaw = paTable[2][f];
|
||||
case allowedPwrs[2]: // -15
|
||||
*raw = paTable[2][f];
|
||||
break;
|
||||
case -10:
|
||||
powerRaw = paTable[3][f];
|
||||
case allowedPwrs[3]: // -10
|
||||
*raw = paTable[3][f];
|
||||
break;
|
||||
case 0:
|
||||
powerRaw = paTable[4][f];
|
||||
case allowedPwrs[4]: // 0
|
||||
*raw = paTable[4][f];
|
||||
break;
|
||||
case 5:
|
||||
powerRaw = paTable[5][f];
|
||||
case allowedPwrs[5]: // 5
|
||||
*raw = paTable[5][f];
|
||||
break;
|
||||
case 7:
|
||||
powerRaw = paTable[6][f];
|
||||
case allowedPwrs[6]: // 7
|
||||
*raw = paTable[6][f];
|
||||
break;
|
||||
case 10:
|
||||
powerRaw = paTable[7][f];
|
||||
case allowedPwrs[7]: // 10
|
||||
*raw = paTable[7][f];
|
||||
break;
|
||||
default:
|
||||
return(RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/*!
|
||||
\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.
|
||||
\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) {
|
||||
// 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
|
||||
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?
|
||||
|
||||
// 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);
|
||||
|
||||
// set output power
|
||||
|
@ -616,6 +611,27 @@ int16_t LR11x0::setOutputPower(int8_t power, bool forceHighPower) {
|
|||
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) {
|
||||
// check active modem
|
||||
uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
|
||||
|
|
|
@ -802,6 +802,25 @@ class LR11x0: public PhysicalLayer {
|
|||
*/
|
||||
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.
|
||||
\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) {
|
||||
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
|
||||
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);
|
||||
|
||||
// set PA config
|
||||
|
@ -25,4 +27,12 @@ int16_t SX1261::setOutputPower(int8_t power) {
|
|||
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
|
||||
|
|
|
@ -34,6 +34,14 @@ class SX1261 : public SX1262 {
|
|||
*/
|
||||
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
|
||||
private:
|
||||
#endif
|
||||
|
|
|
@ -98,11 +98,13 @@ int16_t SX1262::setFrequency(float freq, bool calibrate) {
|
|||
}
|
||||
|
||||
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
|
||||
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);
|
||||
|
||||
// set PA config
|
||||
|
@ -117,4 +119,12 @@ int16_t SX1262::setOutputPower(int8_t power) {
|
|||
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
|
||||
|
|
|
@ -87,6 +87,14 @@ class SX1262: public SX126x {
|
|||
*/
|
||||
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
|
||||
private:
|
||||
#endif
|
||||
|
|
|
@ -93,11 +93,13 @@ int16_t SX1268::setFrequency(float freq, bool calibrate) {
|
|||
}
|
||||
|
||||
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
|
||||
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);
|
||||
|
||||
// set PA config
|
||||
|
@ -112,4 +114,12 @@ int16_t SX1268::setOutputPower(int8_t power) {
|
|||
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
|
||||
|
|
|
@ -85,6 +85,14 @@ class SX1268: public SX126x {
|
|||
*/
|
||||
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
|
||||
private:
|
||||
#endif
|
||||
|
|
|
@ -280,15 +280,12 @@ int16_t SX1272::setOutputPower(int8_t power) {
|
|||
}
|
||||
|
||||
int16_t SX1272::setOutputPower(int8_t power, bool useRfo) {
|
||||
// check allowed power range
|
||||
if(useRfo) {
|
||||
RADIOLIB_CHECK_RANGE(power, -1, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||
} else {
|
||||
RADIOLIB_CHECK_RANGE(power, 2, 20, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||
}
|
||||
// check if power value is configurable
|
||||
int16_t state = checkOutputPower(power, NULL, useRfo);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// set mode to standby
|
||||
int16_t state = SX127x::standby();
|
||||
state = SX127x::standby();
|
||||
Module* mod = this->getMod();
|
||||
|
||||
if(useRfo) {
|
||||
|
@ -317,6 +314,26 @@ int16_t SX1272::setOutputPower(int8_t power, bool useRfo) {
|
|||
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) {
|
||||
// check allowed range
|
||||
if(gain > 6) {
|
||||
|
|
|
@ -206,6 +206,24 @@ class SX1272: public SX127x {
|
|||
*/
|
||||
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.
|
||||
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) {
|
||||
// check allowed power range
|
||||
if(useRfo) {
|
||||
// RFO output
|
||||
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);
|
||||
}
|
||||
}
|
||||
// check if power value is configurable
|
||||
int16_t state = checkOutputPower(power, NULL, useRfo);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// set mode to standby
|
||||
int16_t state = SX127x::standby();
|
||||
state = SX127x::standby();
|
||||
Module* mod = this->getMod();
|
||||
|
||||
if(useRfo) {
|
||||
|
@ -342,6 +335,34 @@ int16_t SX1278::setOutputPower(int8_t power, bool useRfo) {
|
|||
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) {
|
||||
// check allowed range
|
||||
if(gain > 6) {
|
||||
|
|
|
@ -218,6 +218,24 @@ class SX1278: public SX127x {
|
|||
*/
|
||||
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.
|
||||
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) {
|
||||
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;
|
||||
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) {
|
||||
uint8_t modem = getPacketType();
|
||||
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);
|
||||
|
||||
/*!
|
||||
\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.
|
||||
\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
|
||||
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
|
||||
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
|
||||
this->beginCommon(joinDr);
|
||||
|
||||
// set the physical layer configuration
|
||||
state = this->setPhyProperties();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// select a random pair of Tx/Rx channels
|
||||
state = this->selectChannels();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// configure for uplink with default configuration
|
||||
state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
||||
// set the physical layer configuration for uplink
|
||||
state = this->setPhyProperties(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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(!force && this->isJoined()) {
|
||||
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
|
||||
uint16_t checkSum = 0;
|
||||
checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast<uint8_t*>(&addr), 4);
|
||||
checkSum ^= LoRaWANNode::checkSum16(nwkSKey, 16);
|
||||
checkSum ^= LoRaWANNode::checkSum16(nwkSEncKey, 16);
|
||||
checkSum ^= LoRaWANNode::checkSum16(appSKey, 16);
|
||||
if(fNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(fNwkSIntKey, 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;
|
||||
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) {
|
||||
this->rev = 1;
|
||||
memcpy(this->fNwkSIntKey, fNwkSIntKey, RADIOLIB_AES128_KEY_SIZE);
|
||||
} else {
|
||||
memcpy(this->fNwkSIntKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE);
|
||||
memcpy(this->fNwkSIntKey, nwkSEncKey, RADIOLIB_AES128_KEY_SIZE);
|
||||
}
|
||||
if(sNwkSIntKey) {
|
||||
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
|
||||
this->beginCommon(initialDr);
|
||||
|
||||
// set the physical layer configuration
|
||||
state = this->setPhyProperties();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// reset all frame counters
|
||||
this->fcntUp = 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();
|
||||
state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
||||
state = this->setPhyProperties(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// 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
|
||||
if(foptsLen > 0) {
|
||||
#if RADIOLIB_STATIC_ONLY
|
||||
// assume maximum possible buffer size
|
||||
uint8_t foptsBuff[RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN];
|
||||
#else
|
||||
uint8_t* foptsBuff = new uint8_t[foptsLen];
|
||||
#endif
|
||||
uint8_t* foptsPtr = foptsBuff;
|
||||
|
||||
// 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
|
||||
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
|
||||
|
@ -1146,30 +1128,28 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
|||
|
||||
int16_t LoRaWANNode::downlinkCommon() {
|
||||
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
|
||||
// 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(mod->hal->millis() - this->rxDelayStart < this->rxDelays[1]) {
|
||||
mod->hal->delay(this->rxDelays[1] + this->rxDelayStart - mod->hal->millis());
|
||||
if(now < this->rxDelayStart + this->rxDelays[1]) {
|
||||
mod->hal->delay(this->rxDelays[1] + this->rxDelayStart - now);
|
||||
}
|
||||
// update the end timestamp in case user got stuck between uplink and downlink
|
||||
this->rxDelayEnd = mod->hal->millis();
|
||||
return(RADIOLIB_ERR_NO_RX_WINDOW);
|
||||
}
|
||||
|
||||
// configure for downlink
|
||||
int16_t state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK);
|
||||
// set the physical layer configuration for downlink
|
||||
int16_t state = this->setPhyProperties(RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK);
|
||||
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
|
||||
uint16_t irqFlags = 0x0000;
|
||||
uint16_t irqMask = 0x0000;
|
||||
|
@ -1182,14 +1162,16 @@ int16_t LoRaWANNode::downlinkCommon() {
|
|||
downlinkAction = false;
|
||||
|
||||
// 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 timeoutMod = this->phyLayer->calculateRxTimeout(timeoutHost);
|
||||
|
||||
// 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
|
||||
RadioLibTime_t waitLen = this->rxDelays[i] - (mod->hal->millis() - this->rxDelayStart);
|
||||
if(waitLen > scanGuard) {
|
||||
waitLen -= scanGuard;
|
||||
}
|
||||
|
@ -1215,7 +1197,8 @@ int16_t LoRaWANNode::downlinkCommon() {
|
|||
RADIOLIB_ASSERT(state);
|
||||
|
||||
DataRate_t dataRate;
|
||||
findDataRate(this->rx2.drMax, &dataRate);
|
||||
state = findDataRate(this->rx2.drMax, &dataRate);
|
||||
RADIOLIB_ASSERT(state);
|
||||
state = this->phyLayer->setDataRate(dataRate);
|
||||
RADIOLIB_ASSERT(state);
|
||||
}
|
||||
|
@ -1687,16 +1670,51 @@ bool LoRaWANNode::verifyMIC(uint8_t* msg, size_t len, uint8_t* key) {
|
|||
return(true);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::setPhyProperties() {
|
||||
int16_t LoRaWANNode::setPhyProperties(uint8_t dir) {
|
||||
// set the physical layer configuration
|
||||
int8_t pwr = this->txPowerMax - this->txPowerCur * 2;
|
||||
int16_t state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
|
||||
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
|
||||
// go from the highest power and lower it until we hit one supported by the module
|
||||
state = this->phyLayer->setOutputPower(pwr--);
|
||||
}
|
||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("");
|
||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: Frequency %cL = %6.3f MHz", dir ? 'D' : 'U', this->currentChannels[dir].freq);
|
||||
int16_t 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;
|
||||
}
|
||||
|
||||
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 syncWordLen = 0;
|
||||
size_t preLen = 0;
|
||||
|
@ -1987,7 +2005,8 @@ void LoRaWANNode::setDwellTime(bool enable, RadioLibTime_t msPerUplink) {
|
|||
uint8_t LoRaWANNode::maxPayloadDwellTime() {
|
||||
// configure current datarate
|
||||
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);
|
||||
uint8_t minPayLen = 0;
|
||||
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 state = RADIOLIB_ERR_UNKNOWN;
|
||||
|
||||
uint8_t dataRateBand = this->band->dataRates[dr];
|
||||
|
||||
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.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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
state = this->phyLayer->checkDataRate(*dataRate);
|
||||
|
||||
return(state);
|
||||
}
|
||||
|
||||
bool LoRaWANNode::sendMacCommandReq(uint8_t cid) {
|
||||
int16_t LoRaWANNode::sendMacCommandReq(uint8_t cid) {
|
||||
bool valid = false;
|
||||
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_MAC_COMMANDS; i++) {
|
||||
if(MacTable[i].cid == cid) {
|
||||
valid = MacTable[i].user;
|
||||
}
|
||||
}
|
||||
if(!valid)
|
||||
return(false);
|
||||
if(!valid) {
|
||||
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 = {
|
||||
.cid = cid,
|
||||
|
@ -2182,8 +2190,6 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
case(RADIOLIB_LORAWAN_MAC_LINK_ADR): {
|
||||
int16_t state = RADIOLIB_ERR_UNKNOWN;
|
||||
// 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 txSteps = cmd->payload[0] & 0x0F;
|
||||
bool isInternalTxDr = cmd->payload[3] >> 7;
|
||||
|
@ -2193,19 +2199,15 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
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);
|
||||
|
||||
// apply the configuration
|
||||
// try to apply the datarate configuration
|
||||
uint8_t drAck = 0;
|
||||
if(drUp == 0x0F) { // keep the same
|
||||
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) {
|
||||
// check if the module supports this data rate
|
||||
DataRate_t dr;
|
||||
findDataRate(drUp, &dr);
|
||||
state = this->phyLayer->checkDataRate(dr);
|
||||
state = findDataRate(drUp, &dr);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
uint8_t drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase,
|
||||
this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin,
|
||||
|
@ -2215,6 +2217,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
drAck = 1;
|
||||
} else {
|
||||
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) {
|
||||
pwrAck = 1;
|
||||
|
||||
// replace the 'placeholder' with the current actual value for saving
|
||||
cmd->payload[0] = (cmd->payload[0] & 0xF0) | this->txPowerCur;
|
||||
|
||||
} else {
|
||||
int8_t pwr = this->txPowerMax - 2*txSteps;
|
||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: TX = %d dBm", pwr);
|
||||
state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
|
||||
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
|
||||
// go from the highest power and lower it until we hit one supported by the module
|
||||
state = this->phyLayer->setOutputPower(pwr--);
|
||||
}
|
||||
// only acknowledge if the requested datarate was succesfully configured
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
int8_t power = this->txPowerMax - 2*txSteps;
|
||||
int8_t powerActual = 0;
|
||||
state = this->phyLayer->checkOutputPower(power, &powerActual);
|
||||
// only acknowledge if the radio is able to operate at or below the requested power level
|
||||
if(state == RADIOLIB_ERR_NONE || (state == RADIOLIB_ERR_INVALID_OUTPUT_POWER && powerActual < power)) {
|
||||
pwrAck = 1;
|
||||
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;
|
||||
// only apply channel mask when the RFU bit is not set
|
||||
// (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
|
||||
cmd->payload[3] = (cmd->payload[3] & 0xF0) | this->nbTrans; // set current number of retransmissions for saving
|
||||
} else {
|
||||
if(nbTrans) { // if there is a value for NbTrans, set this value
|
||||
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 RFU bit is set, this is just a change in Datarate or TxPower, so read ADR command and overwrite first byte
|
||||
if(isInternalTxDr) {
|
||||
|
@ -2801,6 +2810,10 @@ uint64_t LoRaWANNode::getDevAddr() {
|
|||
return(this->devAddr);
|
||||
}
|
||||
|
||||
RadioLibTime_t LoRaWANNode::getLastToA() {
|
||||
return(this->lastToA);
|
||||
}
|
||||
|
||||
// The following function enables LMAC, a CSMA scheme for LoRa as specified
|
||||
// in the LoRa Alliance Technical Recommendation #13.
|
||||
// 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_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
|
||||
#define RADIOLIB_LORAWAN_NUM_MAC_COMMANDS (16)
|
||||
|
||||
|
@ -179,15 +185,6 @@
|
|||
#define RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP (0x0F)
|
||||
#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
|
||||
#define RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE (9)
|
||||
|
||||
|
@ -217,74 +214,109 @@ struct LoRaWANMacSpec_t {
|
|||
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
|
||||
{ RADIOLIB_LORAWAN_MAC_RESET, 1, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_LINK_CHECK, 2, 0, true },
|
||||
{ RADIOLIB_LORAWAN_MAC_LINK_ADR, 4, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_DUTY_CYCLE, 1, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP, 4, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_DEV_STATUS, 0, 2, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_NEW_CHANNEL, 5, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP, 1, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP, 1, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_DL_CHANNEL, 4, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_REKEY, 1, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP, 1, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_DEVICE_TIME, 5, 0, true },
|
||||
{ RADIOLIB_LORAWAN_MAC_FORCE_REJOIN, 2, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP, 1, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_PROPRIETARY, 5, 0, true }
|
||||
{ RADIOLIB_LORAWAN_MAC_RESET, 1, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_LINK_CHECK, 2, 0, true },
|
||||
{ RADIOLIB_LORAWAN_MAC_LINK_ADR, 4, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_DUTY_CYCLE, 1, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP, 4, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_DEV_STATUS, 0, 2, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_NEW_CHANNEL, 5, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP, 1, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP, 1, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_DL_CHANNEL, 4, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_REKEY, 1, 1, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP, 1, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_DEVICE_TIME, 5, 0, true },
|
||||
{ RADIOLIB_LORAWAN_MAC_FORCE_REJOIN, 2, 0, false },
|
||||
{ RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP, 1, 1, false },
|
||||
{ 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)
|
||||
|
||||
enum LoRaWANSchemeBase_t {
|
||||
RADIOLIB_LORAWAN_NONCES_VERSION = 0x00, // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_MODE = 0x02, // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_CLASS = 0x04, // 1 byte
|
||||
RADIOLIB_LORAWAN_NONCES_PLAN = 0x05, // 1 byte
|
||||
RADIOLIB_LORAWAN_NONCES_CHECKSUM = 0x06, // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_DEV_NONCE = 0x08, // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_JOIN_NONCE = 0x0A, // 3 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_ACTIVE = 0x0D, // 1 byte
|
||||
RADIOLIB_LORAWAN_NONCES_SIGNATURE = 0x0E, // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_BUF_SIZE = 0x10 // = 16 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_START = 0x00,
|
||||
RADIOLIB_LORAWAN_NONCES_VERSION = RADIOLIB_LORAWAN_NONCES_START, // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_MODE = RADIOLIB_LORAWAN_NONCES_VERSION + sizeof(uint16_t), // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_CLASS = RADIOLIB_LORAWAN_NONCES_MODE + sizeof(uint16_t), // 1 byte
|
||||
RADIOLIB_LORAWAN_NONCES_PLAN = RADIOLIB_LORAWAN_NONCES_CLASS + sizeof(uint8_t), // 1 byte
|
||||
RADIOLIB_LORAWAN_NONCES_CHECKSUM = RADIOLIB_LORAWAN_NONCES_PLAN + sizeof(uint8_t), // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_DEV_NONCE = RADIOLIB_LORAWAN_NONCES_CHECKSUM + sizeof(uint16_t), // 2 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_JOIN_NONCE = RADIOLIB_LORAWAN_NONCES_DEV_NONCE + sizeof(uint16_t), // 3 bytes
|
||||
RADIOLIB_LORAWAN_NONCES_ACTIVE = RADIOLIB_LORAWAN_NONCES_JOIN_NONCE + 3, // 1 byte
|
||||
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 {
|
||||
RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY = 0x00, // 16 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_APP_SKEY = 0x10, // 16 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY = 0x20, // 16 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY = 0x30, // 16 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_DEV_ADDR = 0x40, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE = 0x44, // 2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN = 0x46, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP = 0x4A, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN = 0x4E, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_RJ_COUNT0 = 0x52, // 2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_RJ_COUNT1 = 0x54, // 2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_HOMENET_ID = 0x56, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_VERSION = 0x5A, // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE = 0x5B, // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP = 0x5C, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP = 0x60, // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP = 0x61, // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP = 0x62, // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP = 0x63, // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_BEACON_FREQ = 0x64, // 3 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_PING_SLOT_CHANNEL = 0x67, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_PERIODICITY = 0x6B, // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_LAST_TIME = 0x6C, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_UL_CHANNELS = 0x70, // 16*8 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_DL_CHANNELS = 0xF0, // 16*4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL = 0x0130, // 9*8+2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN = 0x017A, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_ADR_FCNT = 0x017E, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_LINK_ADR = 0x0182, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_FCNT_UP = 0x0186, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_SIGNATURE = 0x018A, // 2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_BUF_SIZE = 0x018C // 396 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_START = 0x00,
|
||||
RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY = RADIOLIB_LORAWAN_SESSION_START, // 16 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_APP_SKEY = RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY = RADIOLIB_LORAWAN_SESSION_APP_SKEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY = RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_DEV_ADDR = RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE = RADIOLIB_LORAWAN_SESSION_DEV_ADDR + sizeof(uint32_t), // 2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN = RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE + sizeof(uint16_t), // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP = RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN + sizeof(uint32_t), // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN = RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP + sizeof(uint32_t), // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_RJ_COUNT0 = RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN + sizeof(uint32_t), // 2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_RJ_COUNT1 = RADIOLIB_LORAWAN_SESSION_RJ_COUNT0 + sizeof(uint16_t), // 2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_HOMENET_ID = RADIOLIB_LORAWAN_SESSION_RJ_COUNT1 + sizeof(uint16_t), // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_VERSION = RADIOLIB_LORAWAN_SESSION_HOMENET_ID + sizeof(uint32_t), // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE = RADIOLIB_LORAWAN_SESSION_VERSION + sizeof(uint8_t), // 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_RX_TIMING_SETUP = RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP + MacTable[RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP].lenDn, // 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_ADR_PARAM_SETUP = RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP + MacTable[RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP].lenDn, // 1 byte
|
||||
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_BEACON_FREQ = RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP + MacTable[RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP].lenDn, // 3 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_PING_SLOT_CHANNEL = RADIOLIB_LORAWAN_SESSION_BEACON_FREQ + 3, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_PERIODICITY = RADIOLIB_LORAWAN_SESSION_PING_SLOT_CHANNEL + 4, // 1 byte
|
||||
RADIOLIB_LORAWAN_SESSION_LAST_TIME = RADIOLIB_LORAWAN_SESSION_PERIODICITY + 1, // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_UL_CHANNELS = RADIOLIB_LORAWAN_SESSION_LAST_TIME + 4, // 16*5 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_MAC_QUEUE_UL = RADIOLIB_LORAWAN_SESSION_DL_CHANNELS + 16*MacTable[RADIOLIB_LORAWAN_MAC_DL_CHANNEL].lenDn, // 9*8+2 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN = RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL + sizeof(LoRaWANMacCommandQueue_t), // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_ADR_FCNT = RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN + sizeof(uint32_t), // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_LINK_ADR = RADIOLIB_LORAWAN_SESSION_ADR_FCNT + sizeof(uint32_t), // 4 bytes
|
||||
RADIOLIB_LORAWAN_SESSION_FCNT_UP = RADIOLIB_LORAWAN_SESSION_LINK_ADR + MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, // 4 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
|
||||
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
|
||||
\brief Structure to save extra information about uplink/downlink event.
|
||||
|
@ -567,15 +567,16 @@ class LoRaWANNode {
|
|||
\brief Join network by performing activation by personalization.
|
||||
In this procedure, all necessary configuration must be provided by the user.
|
||||
\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 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 initialDr The datarate at which to send the first uplink and any subsequent uplinks (unless ADR is enabled)
|
||||
\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 */
|
||||
bool isJoined();
|
||||
|
@ -591,9 +592,9 @@ class LoRaWANNode {
|
|||
Only LinkCheck and DeviceTime are available to the user.
|
||||
Other commands are ignored; duplicate MAC commands are discarded.
|
||||
\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)
|
||||
/*!
|
||||
|
@ -835,6 +836,12 @@ class LoRaWANNode {
|
|||
*/
|
||||
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
|
||||
private:
|
||||
#endif
|
||||
|
@ -964,7 +971,7 @@ class LoRaWANNode {
|
|||
|
||||
// configure the common physical layer properties (preamble, sync word etc.)
|
||||
// channels must be configured separately by setupChannelsDyn()!
|
||||
int16_t setPhyProperties();
|
||||
int16_t setPhyProperties(uint8_t dir);
|
||||
|
||||
// setup uplink/downlink channel data rates and frequencies
|
||||
// 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
|
||||
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
|
||||
int16_t restoreChannels();
|
||||
|
||||
|
|
|
@ -256,6 +256,12 @@ int16_t PhysicalLayer::setOutputPower(int8_t power) {
|
|||
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) {
|
||||
(void)sync;
|
||||
(void)len;
|
||||
|
|
|
@ -276,6 +276,14 @@ class PhysicalLayer {
|
|||
*/
|
||||
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.
|
||||
\param sync Pointer to the sync word.
|
||||
|
|
Loading…
Add table
Reference in a new issue