[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>
pull/1086/head
StevenCellist 2024-05-01 13:35:22 +02:00 zatwierdzone przez GitHub
rodzic 841b283c0f
commit 1b2b8bd67b
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
23 zmienionych plików z 562 dodań i 292 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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 };

Wyświetl plik

@ -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)

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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.

Wyświetl plik

@ -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;

Wyświetl plik

@ -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.

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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).

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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).

Wyświetl plik

@ -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)) {

Wyświetl plik

@ -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).

Wyświetl plik

@ -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.

Wyświetl plik

@ -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();

Wyświetl plik

@ -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;

Wyświetl plik

@ -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.