[LoRaWAN] Integrate feedback, fix session save/restore

This commit is contained in:
StevenCellist 2024-09-07 11:45:06 +02:00
parent ecd2996328
commit 8119a606b0
2 changed files with 104 additions and 85 deletions

View file

@ -42,28 +42,32 @@ int16_t LoRaWANNode::sendReceive(String& strUp, uint8_t fPort, String& strDown,
}
#endif
int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
int16_t state = RADIOLIB_ERR_UNKNOWN;
int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
// build a temporary buffer
// LoRaWAN downlinks can have 250 bytes at most with 1 extra byte for NULL
size_t lenDown = 0;
uint8_t dataDown[251];
return(this->sendReceive((uint8_t*)strUp, strlen(strUp), fPort, dataDown, &lenDown, isConfirmed, eventUp, eventDown));
}
int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
return(this->sendReceive((uint8_t*)strUp, strlen(strUp), fPort, dataDown, lenDown, isConfirmed, eventUp, eventDown));
}
int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
// build a temporary buffer
// LoRaWAN downlinks can have 250 bytes at most with 1 extra byte for NULL
size_t lenDown = 0;
uint8_t dataDown[251];
state = this->sendReceive(dataUp, lenUp, fPort, dataDown, &lenDown, isConfirmed, eventUp, eventDown);
return(state);
}
int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
int16_t state = RADIOLIB_ERR_UNKNOWN;
state = this->sendReceive((uint8_t*)strUp, strlen(strUp), fPort, dataDown, lenDown, isConfirmed, eventUp, eventDown);
return(state);
return(this->sendReceive(dataUp, lenUp, fPort, dataDown, &lenDown, isConfirmed, eventUp, eventDown));
}
int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
if(!dataUp || !dataDown || !lenDown) {
return(RADIOLIB_ERR_NULL_POINTER);
}
int16_t state = RADIOLIB_ERR_UNKNOWN;
Module* mod = this->phyLayer->getMod();
@ -139,7 +143,7 @@ int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, u
#if !RADIOLIB_STATIC_ONLY
delete[] uplinkMsg;
#endif
RADIOLIB_ASSERT(state);
return(state);
}
// handle Rx1 and Rx2 windows - returns window > 0 if a downlink is received
@ -187,7 +191,7 @@ int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, u
#if !RADIOLIB_STATIC_ONLY
delete[] uplinkMsg;
#endif
RADIOLIB_ASSERT(state);
return(state);
}
uint8_t rxWindow = state;
@ -360,20 +364,14 @@ void LoRaWANNode::createSession(uint16_t lwMode, uint8_t initialDr) {
cid = RADIOLIB_LORAWAN_MAC_DUTY_CYCLE;
this->getMacLen(cid, &cLen, RADIOLIB_LORAWAN_DOWNLINK);
uint8_t maxDCyclePower;
uint8_t maxDCyclePower = 0;
switch(this->band->dutyCycle) {
case(0):
maxDCyclePower = 0;
break;
case(3600):
maxDCyclePower = 10;
break;
case(36000):
maxDCyclePower = 7;
break;
default:
maxDCyclePower = 0;
break;
}
cOcts[0] = maxDCyclePower;
(void)execMacCommand(cid, cOcts, cLen);
@ -445,7 +443,8 @@ uint8_t* LoRaWANNode::getBufferSession() {
LoRaWANNode::hton<uint16_t>(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_AVAILABLE_CHANNELS], chMask);
// save the current uplink MAC command queue
memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL], this->fOptsUp, RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN);
memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE], this->fOptsUp, RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN);
memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_LEN], &this->fOptsUpLen, 1);
// generate the signature of the Session buffer, and store it in the last two bytes of the Session buffer
uint16_t signature = LoRaWANNode::checkSum16(this->bufferSession, RADIOLIB_LORAWAN_SESSION_BUF_SIZE - 2);
@ -467,7 +466,8 @@ int16_t LoRaWANNode::setBufferSession(uint8_t* persistentBuffer) {
uint16_t signatureNonces = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_SIGNATURE]);
uint16_t signatureInSession = LoRaWANNode::ntoh<uint16_t>(&persistentBuffer[RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE]);
if(signatureNonces != signatureInSession) {
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("The supplied session buffer does not match the Nonces buffer");
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("The Session buffer (%04x) does not match the Nonces buffer (%04x)",
signatureInSession, signatureNonces);
return(RADIOLIB_LORAWAN_SESSION_DISCARDED);
}
@ -540,47 +540,36 @@ int16_t LoRaWANNode::setBufferSession(uint8_t* persistentBuffer) {
}
}
// restore the MAC state - ADR needs special care, the rest is straight default
cid = RADIOLIB_LORAWAN_MAC_LINK_ADR;
cLen = 14; // special internal ADR command
memcpy(cOcts, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_LINK_ADR], cLen);
(void)execMacCommand(cid, cOcts, cLen);
cid = RADIOLIB_LORAWAN_MAC_DUTY_CYCLE;
(void)this->getMacLen(cid, &cLen, RADIOLIB_LORAWAN_DOWNLINK);
memcpy(cOcts, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE], cLen);
(void)execMacCommand(cid, cOcts, cLen);
uint8_t cids[6] = {
RADIOLIB_LORAWAN_MAC_DUTY_CYCLE, RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP,
RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP, RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP,
RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP, RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP
};
uint16_t locs[6] = {
RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE, RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP,
RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP, RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP,
RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP, RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP
};
cid = RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP;
(void)this->getMacLen(cid, &cLen, RADIOLIB_LORAWAN_DOWNLINK);
memcpy(cOcts, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP], cLen);
(void)execMacCommand(cid, cOcts, cLen);
cid = RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP;
(void)this->getMacLen(cid, &cLen, RADIOLIB_LORAWAN_DOWNLINK);
memcpy(cOcts, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP], cLen);
(void)execMacCommand(cid, cOcts, cLen);
cid = RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP;
(void)this->getMacLen(cid, &cLen, RADIOLIB_LORAWAN_DOWNLINK);
memcpy(cOcts, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP], cLen);
(void)execMacCommand(cid, cOcts, cLen);
cid = RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP;
(void)this->getMacLen(cid, &cLen, RADIOLIB_LORAWAN_DOWNLINK);
memcpy(cOcts, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP], cLen);
(void)execMacCommand(cid, cOcts, cLen);
cid = RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP;
(void)this->getMacLen(cid, &cLen, RADIOLIB_LORAWAN_DOWNLINK);
memcpy(cOcts, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP], cLen);
(void)execMacCommand(cid, cOcts, cLen);
for(uint8_t i = 0; i < 6; i++) {
(void)this->getMacLen(cids[i], &cLen, RADIOLIB_LORAWAN_DOWNLINK);
memcpy(cOcts, &this->bufferSession[locs[i]], cLen);
(void)execMacCommand(cids[i], cOcts, cLen);
}
// set the available channels
uint16_t chMask = LoRaWANNode::ntoh<uint32_t>(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_AVAILABLE_CHANNELS]);
this->setAvailableChannels(chMask);
// copy uplink MAC command queue back in place
memcpy(this->fOptsUp, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL], RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN);
memcpy(this->fOptsUp, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE], RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN);
memcpy(&this->fOptsUpLen, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_LEN], 1);
// as both the Nonces and session are restored, revert to active session
this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)true;
@ -588,7 +577,10 @@ int16_t LoRaWANNode::setBufferSession(uint8_t* persistentBuffer) {
return(state);
}
void LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey) {
int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey) {
if(!appKey) {
return(RADIOLIB_ERR_NULL_POINTER);
}
// clear all the device credentials in case there were any
this->clearNonces();
@ -610,9 +602,14 @@ void LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey,
this->lwMode = RADIOLIB_LORAWAN_MODE_OTAA;
this->lwClass = RADIOLIB_LORAWAN_CLASS_A;
return(RADIOLIB_ERR_NONE);
}
void LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey) {
int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey) {
if(!nwkSEncKey || !appSKey) {
return(RADIOLIB_ERR_NULL_POINTER);
}
// clear all the device credentials in case there were any
this->clearNonces();
@ -637,6 +634,8 @@ void LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIn
this->lwMode = RADIOLIB_LORAWAN_MODE_ABP;
this->lwClass = RADIOLIB_LORAWAN_CLASS_A;
return(RADIOLIB_ERR_NONE);
}
void LoRaWANNode::composeJoinRequest(uint8_t* out) {
@ -936,7 +935,7 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent)
// handle Rx1 and Rx2 windows - returns RADIOLIB_ERR_NONE if a downlink is received
state = receiveCommon(RADIOLIB_LORAWAN_DOWNLINK, this->channels, this->rxDelays, 2, this->rxDelayStart);
if(state < RADIOLIB_ERR_NONE) {
RADIOLIB_ASSERT(state);
return(state);
}
// process JoinAccept message
@ -1315,7 +1314,7 @@ static void LoRaWANNodeOnDownlinkAction(void) {
downlinkAction = true;
}
int16_t LoRaWANNode::receiveCommon(uint8_t dir, LoRaWANChannel_t* dlChannels, RadioLibTime_t* dlDelays, uint8_t numWindows, RadioLibTime_t tReference) {
int16_t LoRaWANNode::receiveCommon(uint8_t dir, const LoRaWANChannel_t* dlChannels, const RadioLibTime_t* dlDelays, uint8_t numWindows, RadioLibTime_t tReference) {
Module* mod = this->phyLayer->getMod();
int16_t state = RADIOLIB_ERR_UNKNOWN;
@ -1886,7 +1885,7 @@ bool LoRaWANNode::execMacCommand(uint8_t cid, uint8_t* optIn, uint8_t lenIn, uin
// try to apply the datarate configuration
// if value is set to 'keep current values', retrieve current value
if(macDrUp == 0x0F) {
macDrUp = this->channels[RADIOLIB_LORAWAN_UPLINK].dr;
macDrUp = currentDr;
}
if (this->band->dataRates[macDrUp] != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
@ -1954,7 +1953,11 @@ bool LoRaWANNode::execMacCommand(uint8_t cid, uint8_t* optIn, uint8_t lenIn, uin
// restore original active channels
this->setAvailableChannels(chMaskActive);
// save to the single ADR MAC location
// save to the ADR MAC location
// but first re-set the Dr/Tx/NbTrans field to make sure they're not set to 0xF
optIn[0] = (this->channels[RADIOLIB_LORAWAN_UPLINK].dr) << 4;
optIn[0] |= this->txPowerSteps;
optIn[13] = this->nbTrans;
memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_LINK_ADR], optIn, lenIn);
return(true);
@ -2707,7 +2710,7 @@ RadioLibTime_t LoRaWANNode::getLastToA() {
return(this->lastToA);
}
int16_t LoRaWANNode::setPhyProperties(LoRaWANChannel_t* chnl, uint8_t dir, int8_t pwr, size_t pre) {
int16_t LoRaWANNode::setPhyProperties(const LoRaWANChannel_t* chnl, uint8_t dir, int8_t pwr, size_t pre) {
// set the physical layer configuration
int16_t state = this->phyLayer->standby();
if(state != RADIOLIB_ERR_NONE) {
@ -3313,11 +3316,11 @@ int16_t LoRaWANNode::checkBufferCommon(uint8_t *buffer, uint16_t size) {
uint16_t LoRaWANNode::checkSum16(uint8_t *key, uint16_t keyLen) {
uint16_t checkSum = 0;
for(uint16_t i = 0; i < keyLen; i += 2) {
checkSum ^= ((uint16_t)key[i] << 8) | key[i + 1];
}
if(keyLen % 2 == 1) {
uint16_t val = ((uint16_t)key[keyLen - 1] << 8);
checkSum ^= val;
uint16_t word = (key[i] << 8);
if(i + 1 < keyLen) {
word |= key[i + 1];
}
checkSum ^= word;
}
return(checkSum);
}

View file

@ -297,8 +297,9 @@ enum LoRaWANSchemeSession_t {
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 + RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS*5, // 16*4 bytes
RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL = RADIOLIB_LORAWAN_SESSION_DL_CHANNELS + RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS*4, // 15 bytes
RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN = RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL + RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN, // 4 bytes
RADIOLIB_LORAWAN_SESSION_MAC_QUEUE = RADIOLIB_LORAWAN_SESSION_DL_CHANNELS + RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS*4, // 15 bytes
RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_LEN = RADIOLIB_LORAWAN_SESSION_MAC_QUEUE + 1, // 1 byte
RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN = RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_LEN + RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN, // 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), // 14 bytes
RADIOLIB_LORAWAN_SESSION_AVAILABLE_CHANNELS = RADIOLIB_LORAWAN_SESSION_LINK_ADR + 14, // 2 bytes
@ -568,8 +569,9 @@ class LoRaWANNode {
\param devEUI 8-byte device identifier.
\param nwkKey Pointer to the network AES-128 key.
\param appKey Pointer to the application AES-128 key.
\returns \ref status_codes
*/
void beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey);
int16_t beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey);
/*!
\brief Set the device credentials and activation configuration
@ -579,8 +581,9 @@ class LoRaWANNode {
\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.
\returns \ref status_codes
*/
void beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey);
int16_t beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey);
/*!
\brief Join network by restoring OTAA session or performing over-the-air activation. By this procedure,
@ -617,6 +620,19 @@ class LoRaWANNode {
virtual int16_t sendReceive(String& strUp, uint8_t fPort, String& strDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL);
#endif
/*!
\brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window.
\param strUp C-string that will be transmitted.
\param fPort Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not.
\param eventUp Pointer to a structure to store extra information about the uplink event
(fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\param eventDown Pointer to a structure to store extra information about the downlink event
(fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns Window number > 0 if downlink was received, 0 is no downlink was received, otherwise \ref status_codes
*/
virtual int16_t sendReceive(const char* strUp, uint8_t fPort, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL);
/*!
\brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window.
\param strUp C-string that will be transmitted.
@ -632,6 +648,20 @@ class LoRaWANNode {
*/
virtual int16_t sendReceive(const char* strUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL);
/*!
\brief Send a message to the server and wait for a downlink but don't bother the user with downlink contents
\param dataUp Data to send.
\param lenUp Length of the data.
\param fPort Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not.
\param eventUp Pointer to a structure to store extra information about the uplink event
(fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\param eventDown Pointer to a structure to store extra information about the downlink event
(fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns Window number > 0 if downlink was received, 0 is no downlink was received, otherwise \ref status_codes
*/
virtual int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort = 1, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL);
/*!
\brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window.
\param dataUp Data to send.
@ -648,20 +678,6 @@ class LoRaWANNode {
*/
virtual int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL);
/*!
\brief Send a message to the server and wait for a downlink but don't bother the user with downlink contents
\param dataUp Data to send.
\param lenUp Length of the data.
\param fPort Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not.
\param eventUp Pointer to a structure to store extra information about the uplink event
(fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\param eventDown Pointer to a structure to store extra information about the downlink event
(fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns Window number > 0 if downlink was received, 0 is no downlink was received, otherwise \ref status_codes
*/
virtual int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort = 1, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL);
/*!
\brief Add a MAC command to the uplink queue.
Only LinkCheck and DeviceTime are available to the user.
@ -996,7 +1012,7 @@ class LoRaWANNode {
int16_t transmitUplink(LoRaWANChannel_t* chnl, uint8_t* in, uint8_t len);
// wait for, open and listen during receive windows; only performs listening
int16_t receiveCommon(uint8_t dir, LoRaWANChannel_t* dlChannels, RadioLibTime_t* dlDelays, uint8_t numWindows, RadioLibTime_t tReference);
int16_t receiveCommon(uint8_t dir, const LoRaWANChannel_t* dlChannels, const RadioLibTime_t* dlDelays, uint8_t numWindows, RadioLibTime_t tReference);
// extract downlink payload and process MAC commands
int16_t parseDownlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event = NULL);
@ -1033,7 +1049,7 @@ class LoRaWANNode {
void clearMacCommands(uint8_t* inOut, uint8_t* lenInOut, uint8_t dir);
// configure the common physical layer properties (frequency, sync word etc.)
int16_t setPhyProperties(LoRaWANChannel_t* chnl, uint8_t dir, int8_t pwr, size_t pre = 0);
int16_t setPhyProperties(const LoRaWANChannel_t* chnl, uint8_t dir, int8_t pwr, size_t pre = 0);
// Performs CSMA as per LoRa Alliance Technical Recommendation 13 (TR-013).
bool csmaChannelClear(uint8_t difs, uint8_t numBackoff);