[LoRaWAN] Added event struct to pass extra info (#821)

This commit is contained in:
jgromes 2023-11-12 17:53:05 +01:00
parent cb9ba88d03
commit 75a9420552
5 changed files with 133 additions and 51 deletions

View file

@ -1,5 +1,5 @@
/* /*
RadioLib LoRaWAN End Device Example RadioLib LoRaWAN End Device Persistent Example
This example assumes you have tried one of the OTAA or ABP This example assumes you have tried one of the OTAA or ABP
examples and are familiar with the required keys and procedures. examples and are familiar with the required keys and procedures.
@ -7,7 +7,7 @@
deepsleep or survive power cycles. Before you start, you will deepsleep or survive power cycles. Before you start, you will
have to register your device at https://www.thethingsnetwork.org/ have to register your device at https://www.thethingsnetwork.org/
and join the network using either OTAA or ABP. and join the network using either OTAA or ABP.
Please refer to one of the other examples for more Please refer to one of the other LoRaWAN examples for more
information regarding joining a network. information regarding joining a network.
NOTE: LoRaWAN requires storing some parameters persistently! NOTE: LoRaWAN requires storing some parameters persistently!

View file

@ -1,5 +1,5 @@
/* /*
RadioLib LoRaWAN End Device Example RadioLib LoRaWAN End Device Reference Example
This example joins a LoRaWAN network and will send This example joins a LoRaWAN network and will send
uplink packets. Before you start, you will have to uplink packets. Before you start, you will have to
@ -131,13 +131,13 @@ void setup() {
// this tries to minimize packet loss by searching for a free channel // this tries to minimize packet loss by searching for a free channel
// before actually sending an uplink // before actually sending an uplink
node.setCSMA(6, 2, true); node.setCSMA(6, 2, true);
} }
void loop() { void loop() {
int state = RADIOLIB_ERR_NONE; int state = RADIOLIB_ERR_NONE;
// set battery fill level, // set battery fill level - the LoRaWAN network server
// may periodically request this information
// 0 = external power source // 0 = external power source
// 1 = lowest (empty battery) // 1 = lowest (empty battery)
// 254 = highest (full battery) // 254 = highest (full battery)
@ -170,7 +170,12 @@ void loop() {
// after uplink to receive the downlink! // after uplink to receive the downlink!
Serial.print(F("[LoRaWAN] Waiting for downlink ... ")); Serial.print(F("[LoRaWAN] Waiting for downlink ... "));
String strDown; String strDown;
state = node.downlink(strDown);
// you can also retrieve additional information about
// uplink or downlink by passing a reference to
// LoRaWANEvent_t structure
LoRaWANEvent_t event;
state = node.downlink(strDown, &event);
if(state == RADIOLIB_ERR_NONE) { if(state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!")); Serial.println(F("success!"));
@ -197,6 +202,31 @@ void loop() {
Serial.print(radio.getFrequencyError()); Serial.print(radio.getFrequencyError());
Serial.println(F(" Hz")); Serial.println(F(" Hz"));
// print extra information about the event
Serial.println(F("[LoRaWAN] Event information:"));
Serial.print(F("[LoRaWAN] Direction:\t"));
if(event.dir == RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK) {
Serial.println(F("uplink"));
} else {
Serial.println(F("downlink"));
}
Serial.print(F("[LoRaWAN] Confirmed:\t"));
Serial.println(event.confirmed);
Serial.print(F("[LoRaWAN] Confirming:\t"));
Serial.println(event.confirming);
Serial.print(F("[LoRaWAN] Frequency:\t"));
Serial.print(event.freq, 3);
Serial.println(F(" MHz"));
Serial.print(F("[LoRaWAN] Output power:\t"));
Serial.print(event.power);
Serial.println(F(" dBm"));
Serial.print(F("[LoRaWAN] Frame count:\t"));
Serial.println(event.fcnt);
Serial.print(F("[LoRaWAN] Port:\t\t"));
Serial.println(event.port);
Serial.print(radio.getFrequencyError());
} else if(state == RADIOLIB_ERR_RX_TIMEOUT) { } else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
Serial.println(F("timeout!")); Serial.println(F("timeout!"));

View file

@ -58,6 +58,7 @@ ExternalRadio KEYWORD1
BellClient KEYWORD1 BellClient KEYWORD1
LoRaWANNode KEYWORD1 LoRaWANNode KEYWORD1
LoRaWANBand_t KEYWORD1 LoRaWANBand_t KEYWORD1
LoRaWANEvent_t KEYWORD1
# SSTV modes # SSTV modes
Scottie1 KEYWORD1 Scottie1 KEYWORD1

View file

@ -657,16 +657,16 @@ int16_t LoRaWANNode::saveChannels() {
#if defined(RADIOLIB_BUILD_ARDUINO) #if defined(RADIOLIB_BUILD_ARDUINO)
int16_t LoRaWANNode::uplink(String& str, uint8_t port, bool isConfirmed) { int16_t LoRaWANNode::uplink(String& str, uint8_t port, bool isConfirmed, LoRaWANEvent_t* event) {
return(this->uplink(str.c_str(), port, isConfirmed)); return(this->uplink(str.c_str(), port, isConfirmed, event));
} }
#endif #endif
int16_t LoRaWANNode::uplink(const char* str, uint8_t port, bool isConfirmed) { int16_t LoRaWANNode::uplink(const char* str, uint8_t port, bool isConfirmed, LoRaWANEvent_t* event) {
return(this->uplink((uint8_t*)str, strlen(str), port, isConfirmed)); return(this->uplink((uint8_t*)str, strlen(str), port, isConfirmed, event));
} }
int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed) { int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed, LoRaWANEvent_t* event) {
Module* mod = this->phyLayer->getMod(); Module* mod = this->phyLayer->getMod();
// check if the Rx windows were closed after sending the previous uplink // check if the Rx windows were closed after sending the previous uplink
@ -783,12 +783,11 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
} }
// if the saved confirm-fcnt is set, set the ACK bit // if the saved confirm-fcnt is set, set the ACK bit
bool isConfirmingDown; bool isConfirmingDown = false;
if(this->confFcntDown != RADIOLIB_LORAWAN_FCNT_NONE) { if(this->confFcntDown != RADIOLIB_LORAWAN_FCNT_NONE) {
isConfirmingDown = true; isConfirmingDown = true;
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ACK; uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ACK;
} }
(void)isConfirmingDown;
LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)this->fcntUp); LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)this->fcntUp);
@ -895,13 +894,16 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
// the downlink confirmation was acknowledged, so clear the counter value // the downlink confirmation was acknowledged, so clear the counter value
this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE;
// LoRaWANEvent: // pass the extra info if requested
// dir = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK if(event) {
// confirmed = isConfirmed event->dir = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK;
// confirming = isConfirmingDown event->confirmed = isConfirmed;
// power = this->txPwrCur event->confirming = isConfirmingDown;
// fcnt = this->fcntUp event->freq = currentChannels[event->dir].freq;
// port = port event->power = this->txPwrCur;
event->fcnt = this->fcntUp;
event->port = port;
}
return(RADIOLIB_ERR_NONE); return(RADIOLIB_ERR_NONE);
} }
@ -1012,7 +1014,7 @@ int16_t LoRaWANNode::downlinkCommon() {
} }
#if defined(RADIOLIB_BUILD_ARDUINO) #if defined(RADIOLIB_BUILD_ARDUINO)
int16_t LoRaWANNode::downlink(String& str) { int16_t LoRaWANNode::downlink(String& str, LoRaWANEvent_t* event) {
int16_t state = RADIOLIB_ERR_NONE; int16_t state = RADIOLIB_ERR_NONE;
// build a temporary buffer // build a temporary buffer
@ -1021,7 +1023,7 @@ int16_t LoRaWANNode::downlink(String& str) {
uint8_t data[251]; uint8_t data[251];
// wait for downlink // wait for downlink
state = this->downlink(data, &length); state = this->downlink(data, &length, event);
if(state == RADIOLIB_ERR_NONE) { if(state == RADIOLIB_ERR_NONE) {
// add null terminator // add null terminator
data[length] = '\0'; data[length] = '\0';
@ -1034,8 +1036,7 @@ int16_t LoRaWANNode::downlink(String& str) {
} }
#endif #endif
int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) { int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) {
// handle Rx1 and Rx2 windows - returns RADIOLIB_ERR_NONE if a downlink is received // handle Rx1 and Rx2 windows - returns RADIOLIB_ERR_NONE if a downlink is received
int16_t state = downlinkCommon(); int16_t state = downlinkCommon();
RADIOLIB_ASSERT(state); RADIOLIB_ASSERT(state);
@ -1086,13 +1087,11 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt16); LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt16);
// if this downlink is confirming an uplink, its MIC was generated with the least-significant 16 bits of that fcntUp // if this downlink is confirming an uplink, its MIC was generated with the least-significant 16 bits of that fcntUp
// TODO get this to the user somehow
bool isConfirmingUp = false; bool isConfirmingUp = false;
if((downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FCTRL_ACK) && (this->rev == 1)) { if((downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FCTRL_ACK) && (this->rev == 1)) {
isConfirmingUp = true; isConfirmingUp = true;
LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFcntUp); LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFcntUp);
} }
(void)isConfirmingUp;
RADIOLIB_DEBUG_PRINTLN("downlinkMsg:"); RADIOLIB_DEBUG_PRINTLN("downlinkMsg:");
RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen); RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen);
@ -1157,7 +1156,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
this->confFcntDown = this->aFcntDown; this->confFcntDown = this->aFcntDown;
isConfirmedDown = true; isConfirmedDown = true;
} }
(void)isConfirmedDown;
// check the address // check the address
uint32_t addr = LoRaWANNode::ntoh<uint32_t>(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_DEV_ADDR_POS]); uint32_t addr = LoRaWANNode::ntoh<uint32_t>(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_DEV_ADDR_POS]);
@ -1242,13 +1240,16 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
// a downlink was received, so reset the ADR counter to this uplink's fcnt // a downlink was received, so reset the ADR counter to this uplink's fcnt
this->adrFcnt = this->fcntUp; this->adrFcnt = this->fcntUp;
// LoRaWANEvent: // pass the extra info if requested
// dir = RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK if(event) {
// confirmed = isConfirmedDown event->dir = RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK;
// confirming = isConfirmingUp event->confirmed = isConfirmedDown;
// power = this->txPwrCur event->confirming = isConfirmingUp;
// fcnt = isAppDownlink ? this->aFcntDown : this->nFcntDown event->freq = currentChannels[event->dir].freq;
// port = ... event->power = this->txPwrCur;
event->fcnt = isAppDownlink ? this->aFcntDown : this->nFcntDown;
event->port = downlinkMsg[RADIOLIB_LORAWAN_FHDR_FPORT_POS(foptsLen)];
}
// process payload (if there is any) // process payload (if there is any)
if(payLen <= 0) { if(payLen <= 0) {
@ -1276,34 +1277,34 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
} }
#if defined(RADIOLIB_BUILD_ARDUINO) #if defined(RADIOLIB_BUILD_ARDUINO)
int16_t LoRaWANNode::sendReceive(String& strUp, uint8_t port, String& strDown, bool isConfirmed) { int16_t LoRaWANNode::sendReceive(String& strUp, uint8_t port, String& strDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
// send the uplink // send the uplink
int16_t state = this->uplink(strUp, port, isConfirmed); int16_t state = this->uplink(strUp, port, isConfirmed, eventUp);
RADIOLIB_ASSERT(state); RADIOLIB_ASSERT(state);
// wait for the downlink // wait for the downlink
state = this->downlink(strDown); state = this->downlink(strDown, eventDown);
return(state); return(state);
} }
#endif #endif
int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed) { int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
// send the uplink // send the uplink
int16_t state = this->uplink(strUp, port, isConfirmed); int16_t state = this->uplink(strUp, port, isConfirmed, eventUp);
RADIOLIB_ASSERT(state); RADIOLIB_ASSERT(state);
// wait for the downlink // wait for the downlink
state = this->downlink(dataDown, lenDown); state = this->downlink(dataDown, lenDown, eventDown);
return(state); return(state);
} }
int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed) { int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) {
// send the uplink // send the uplink
int16_t state = this->uplink(dataUp, lenUp, port, isConfirmed); int16_t state = this->uplink(dataUp, lenUp, port, isConfirmed, eventUp);
RADIOLIB_ASSERT(state); RADIOLIB_ASSERT(state);
// wait for the downlink // wait for the downlink
state = this->downlink(dataDown, lenDown); state = this->downlink(dataDown, lenDown, eventDown);
return(state); return(state);
} }

View file

@ -311,6 +311,34 @@ struct LoRaWANMacCommandQueue_t {
LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE]; LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE];
}; };
/*!
\struct LoRaWANEvent_t
\brief Structure to save extra information about uplink/downlink event.
*/
struct LoRaWANEvent_t {
/*! \brief Event direction, one of RADIOLIB_LORAWAN_CHANNEL_DIR_* */
uint8_t dir;
/*! \brief Whether the event is confirmed or not (e.g., confirmed uplink sent by user application) */
bool confirmed;
/*! \brief Whether the event is confirming a previous request
(e.g., server downlink reply to confirmed uplink sent by user application)*/
bool confirming;
/*! \brief Frequency in MHz */
float freq;
/*! \brief Transmit power in dBm for uplink, or RSSI for downlink */
int16_t power;
/*! \brief The appropriate frame counter - for different events, different frame counters will be reported! */
uint32_t fcnt;
/*! \brief Port number */
uint8_t port;
};
/*! /*!
\class LoRaWANNode \class LoRaWANNode
\brief LoRaWAN-compatible node (class A device). \brief LoRaWAN-compatible node (class A device).
@ -388,9 +416,11 @@ class LoRaWANNode {
\param str Address of Arduino String that will be transmitted. \param str Address of Arduino String that will be transmitted.
\param port Port number to send the message to. \param port Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not. \param isConfirmed Whether to send a confirmed uplink or not.
\param event Pointer to a structure to store extra information about the event
(port, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns \ref status_codes \returns \ref status_codes
*/ */
int16_t uplink(String& str, uint8_t port, bool isConfirmed = false); int16_t uplink(String& str, uint8_t port, bool isConfirmed = false, LoRaWANEvent_t* event = NULL);
#endif #endif
/*! /*!
@ -398,9 +428,11 @@ class LoRaWANNode {
\param str C-string that will be transmitted. \param str C-string that will be transmitted.
\param port Port number to send the message to. \param port Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not. \param isConfirmed Whether to send a confirmed uplink or not.
\param event Pointer to a structure to store extra information about the event
(port, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns \ref status_codes \returns \ref status_codes
*/ */
int16_t uplink(const char* str, uint8_t port, bool isConfirmed = false); int16_t uplink(const char* str, uint8_t port, bool isConfirmed = false, LoRaWANEvent_t* event = NULL);
/*! /*!
\brief Send a message to the server. \brief Send a message to the server.
@ -408,26 +440,32 @@ class LoRaWANNode {
\param len Length of the data. \param len Length of the data.
\param port Port number to send the message to. \param port Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not. \param isConfirmed Whether to send a confirmed uplink or not.
\param event Pointer to a structure to store extra information about the event
(port, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns \ref status_codes \returns \ref status_codes
*/ */
int16_t uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed = false); int16_t uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed = false, LoRaWANEvent_t* event = NULL);
#if defined(RADIOLIB_BUILD_ARDUINO) #if defined(RADIOLIB_BUILD_ARDUINO)
/*! /*!
\brief Wait for downlink from the server in either RX1 or RX2 window. \brief Wait for downlink from the server in either RX1 or RX2 window.
\param str Address of Arduino String to save the received data. \param str Address of Arduino String to save the received data.
\param event Pointer to a structure to store extra information about the event
(port, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns \ref status_codes \returns \ref status_codes
*/ */
int16_t downlink(String& str); int16_t downlink(String& str, LoRaWANEvent_t* event = NULL);
#endif #endif
/*! /*!
\brief Wait for downlink from the server in either RX1 or RX2 window. \brief Wait for downlink from the server in either RX1 or RX2 window.
\param data Buffer to save received data into. \param data Buffer to save received data into.
\param len Pointer to variable that will be used to save the number of received bytes. \param len Pointer to variable that will be used to save the number of received bytes.
\param event Pointer to a structure to store extra information about the event
(port, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns \ref status_codes \returns \ref status_codes
*/ */
int16_t downlink(uint8_t* data, size_t* len); int16_t downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event = NULL);
#if defined(RADIOLIB_BUILD_ARDUINO) #if defined(RADIOLIB_BUILD_ARDUINO)
/*! /*!
@ -436,9 +474,13 @@ class LoRaWANNode {
\param port Port number to send the message to. \param port Port number to send the message to.
\param strDown Address of Arduino String to save the received data. \param strDown Address of Arduino String to save the received data.
\param isConfirmed Whether to send a confirmed uplink or not. \param isConfirmed Whether to send a confirmed uplink or not.
\param eventUp Pointer to a structure to store extra information about the uplink event
(port, 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
(port, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns \ref status_codes \returns \ref status_codes
*/ */
int16_t sendReceive(String& strUp, uint8_t port, String& strDown, bool isConfirmed = false); int16_t sendReceive(String& strUp, uint8_t port, String& strDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL);
#endif #endif
/*! /*!
@ -448,9 +490,13 @@ class LoRaWANNode {
\param dataDown Buffer to save received data into. \param dataDown Buffer to save received data into.
\param lenDown Pointer to variable that will be used to save the number of received bytes. \param lenDown Pointer to variable that will be used to save the number of received bytes.
\param isConfirmed Whether to send a confirmed uplink or not. \param isConfirmed Whether to send a confirmed uplink or not.
\param eventUp Pointer to a structure to store extra information about the uplink event
(port, 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
(port, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns \ref status_codes \returns \ref status_codes
*/ */
int16_t sendReceive(const char* strUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false); int16_t sendReceive(const char* strUp, uint8_t port, 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 during Rx1 and/or Rx2 window. \brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window.
@ -460,9 +506,13 @@ class LoRaWANNode {
\param dataDown Buffer to save received data into. \param dataDown Buffer to save received data into.
\param lenDown Pointer to variable that will be used to save the number of received bytes. \param lenDown Pointer to variable that will be used to save the number of received bytes.
\param isConfirmed Whether to send a confirmed uplink or not. \param isConfirmed Whether to send a confirmed uplink or not.
\param eventUp Pointer to a structure to store extra information about the uplink event
(port, 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
(port, frame counter, etc.). If set to NULL, no extra information will be passed to the user.
\returns \ref status_codes \returns \ref status_codes
*/ */
int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false); int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL);
/*! /*!
\brief Set device status. \brief Set device status.