diff --git a/examples/LoRaWAN/LoRaWAN_End_Device_persistent/LoRaWAN_End_Device_persistent.ino b/examples/LoRaWAN/LoRaWAN_End_Device_persistent/LoRaWAN_End_Device_persistent.ino index 43275a7ca..3c96fb913 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device_persistent/LoRaWAN_End_Device_persistent.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device_persistent/LoRaWAN_End_Device_persistent.ino @@ -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 examples and are familiar with the required keys and procedures. @@ -7,7 +7,7 @@ deepsleep or survive power cycles. Before you start, you will have to register your device at https://www.thethingsnetwork.org/ 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. NOTE: LoRaWAN requires storing some parameters persistently! diff --git a/examples/LoRaWAN/LoRaWAN_End_Device_reference/LoRaWAN_End_Device_reference.ino b/examples/LoRaWAN/LoRaWAN_End_Device_reference/LoRaWAN_End_Device_reference.ino index e72e977cf..c31752e7e 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device_reference/LoRaWAN_End_Device_reference.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device_reference/LoRaWAN_End_Device_reference.ino @@ -1,5 +1,5 @@ /* - RadioLib LoRaWAN End Device Example + RadioLib LoRaWAN End Device Reference Example This example joins a LoRaWAN network and will send 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 // before actually sending an uplink node.setCSMA(6, 2, true); - } void loop() { 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 // 1 = lowest (empty battery) // 254 = highest (full battery) @@ -170,7 +170,12 @@ void loop() { // after uplink to receive the downlink! Serial.print(F("[LoRaWAN] Waiting for downlink ... ")); 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) { Serial.println(F("success!")); @@ -196,6 +201,31 @@ void loop() { Serial.print(F("[LoRaWAN] Frequency error:\t")); Serial.print(radio.getFrequencyError()); 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) { Serial.println(F("timeout!")); diff --git a/keywords.txt b/keywords.txt index 63a578438..1a19ea40c 100644 --- a/keywords.txt +++ b/keywords.txt @@ -58,6 +58,7 @@ ExternalRadio KEYWORD1 BellClient KEYWORD1 LoRaWANNode KEYWORD1 LoRaWANBand_t KEYWORD1 +LoRaWANEvent_t KEYWORD1 # SSTV modes Scottie1 KEYWORD1 diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index fc0b31645..02ff3f9df 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -657,16 +657,16 @@ int16_t LoRaWANNode::saveChannels() { #if defined(RADIOLIB_BUILD_ARDUINO) -int16_t LoRaWANNode::uplink(String& str, uint8_t port, bool isConfirmed) { - return(this->uplink(str.c_str(), port, isConfirmed)); +int16_t LoRaWANNode::uplink(String& str, uint8_t port, bool isConfirmed, LoRaWANEvent_t* event) { + return(this->uplink(str.c_str(), port, isConfirmed, event)); } #endif -int16_t LoRaWANNode::uplink(const char* str, uint8_t port, bool isConfirmed) { - return(this->uplink((uint8_t*)str, strlen(str), port, 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, 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(); // 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 - bool isConfirmingDown; + bool isConfirmingDown = false; if(this->confFcntDown != RADIOLIB_LORAWAN_FCNT_NONE) { isConfirmingDown = true; uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ACK; } - (void)isConfirmingDown; LoRaWANNode::hton(&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 this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; - // LoRaWANEvent: - // dir = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK - // confirmed = isConfirmed - // confirming = isConfirmingDown - // power = this->txPwrCur - // fcnt = this->fcntUp - // port = port + // pass the extra info if requested + if(event) { + event->dir = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK; + event->confirmed = isConfirmed; + event->confirming = isConfirmingDown; + event->freq = currentChannels[event->dir].freq; + event->power = this->txPwrCur; + event->fcnt = this->fcntUp; + event->port = port; + } return(RADIOLIB_ERR_NONE); } @@ -1012,7 +1014,7 @@ int16_t LoRaWANNode::downlinkCommon() { } #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; // build a temporary buffer @@ -1021,7 +1023,7 @@ int16_t LoRaWANNode::downlink(String& str) { uint8_t data[251]; // wait for downlink - state = this->downlink(data, &length); + state = this->downlink(data, &length, event); if(state == RADIOLIB_ERR_NONE) { // add null terminator data[length] = '\0'; @@ -1034,8 +1036,7 @@ int16_t LoRaWANNode::downlink(String& str) { } #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 int16_t state = downlinkCommon(); RADIOLIB_ASSERT(state); @@ -1086,13 +1087,11 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) { LoRaWANNode::hton(&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 - // TODO get this to the user somehow bool isConfirmingUp = false; if((downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FCTRL_ACK) && (this->rev == 1)) { isConfirmingUp = true; LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFcntUp); } - (void)isConfirmingUp; RADIOLIB_DEBUG_PRINTLN("downlinkMsg:"); 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; isConfirmedDown = true; } - (void)isConfirmedDown; // check the address uint32_t addr = LoRaWANNode::ntoh(&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 this->adrFcnt = this->fcntUp; - // LoRaWANEvent: - // dir = RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK - // confirmed = isConfirmedDown - // confirming = isConfirmingUp - // power = this->txPwrCur - // fcnt = isAppDownlink ? this->aFcntDown : this->nFcntDown - // port = ... + // pass the extra info if requested + if(event) { + event->dir = RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK; + event->confirmed = isConfirmedDown; + event->confirming = isConfirmingUp; + event->freq = currentChannels[event->dir].freq; + 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) if(payLen <= 0) { @@ -1276,34 +1277,34 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) { } #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 - int16_t state = this->uplink(strUp, port, isConfirmed); + int16_t state = this->uplink(strUp, port, isConfirmed, eventUp); RADIOLIB_ASSERT(state); // wait for the downlink - state = this->downlink(strDown); + state = this->downlink(strDown, eventDown); return(state); } #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 - int16_t state = this->uplink(strUp, port, isConfirmed); + int16_t state = this->uplink(strUp, port, isConfirmed, eventUp); RADIOLIB_ASSERT(state); // wait for the downlink - state = this->downlink(dataDown, lenDown); + state = this->downlink(dataDown, lenDown, eventDown); 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 - int16_t state = this->uplink(dataUp, lenUp, port, isConfirmed); + int16_t state = this->uplink(dataUp, lenUp, port, isConfirmed, eventUp); RADIOLIB_ASSERT(state); // wait for the downlink - state = this->downlink(dataDown, lenDown); + state = this->downlink(dataDown, lenDown, eventDown); return(state); } diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 5d0eacc15..54351a034 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -311,6 +311,34 @@ struct LoRaWANMacCommandQueue_t { 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 \brief LoRaWAN-compatible node (class A device). @@ -388,9 +416,11 @@ class LoRaWANNode { \param str Address of Arduino String that will be transmitted. \param port Port number to send the message to. \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 */ - 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 /*! @@ -398,9 +428,11 @@ class LoRaWANNode { \param str C-string that will be transmitted. \param port Port number to send the message to. \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 */ - 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. @@ -408,26 +440,32 @@ class LoRaWANNode { \param len Length of the data. \param port Port number to send the message to. \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 */ - 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) /*! \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 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 */ - int16_t downlink(String& str); + int16_t downlink(String& str, LoRaWANEvent_t* event = NULL); #endif /*! \brief Wait for downlink from the server in either RX1 or RX2 window. \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 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 */ - 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) /*! @@ -436,9 +474,13 @@ class LoRaWANNode { \param port Port number to send the message to. \param strDown Address of Arduino String to save the received data. \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 */ - 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 /*! @@ -448,9 +490,13 @@ class LoRaWANNode { \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 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 */ - 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. @@ -460,9 +506,13 @@ class LoRaWANNode { \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 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 */ - 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.