From 0ea00fad44ffdadd3279d8fdd66fc259df8879f0 Mon Sep 17 00:00:00 2001 From: StevenCellist <47155822+StevenCellist@users.noreply.github.com> Date: Thu, 15 Feb 2024 20:36:03 +0100 Subject: [PATCH] [LoRaWAN] Improve examples, add getter for DevAddr (#974) --- .../LoRaWAN_End_Device/LoRaWAN_End_Device.ino | 25 ++- .../LoRaWAN_End_Device_ABP.ino | 24 ++- .../LoRaWAN_End_Device_Persistent.ino | 156 ------------------ .../LoRaWAN_End_Device_Reference.ino | 24 +-- keywords.txt | 1 + src/protocols/LoRaWAN/LoRaWAN.cpp | 3 + src/protocols/LoRaWAN/LoRaWAN.h | 6 + 7 files changed, 55 insertions(+), 184 deletions(-) delete mode 100644 examples/LoRaWAN/LoRaWAN_End_Device_Persistent/LoRaWAN_End_Device_Persistent.ino diff --git a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino index 10af3b626..95777c5aa 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino @@ -7,12 +7,14 @@ After your device is registered, you can run this example. The device will join the network and start uploading data. - LoRaWAN v1.1 requires the use of EEPROM (persistent storage). - Please refer to the 'persistent' example once you are familiar - with LoRaWAN. - Running this examples REQUIRES you to check "Resets DevNonces" - on your LoRaWAN dashboard. Refer to the network's - documentation on how to do this. + NOTE: LoRaWAN v1.1 requires storing parameters persistently! + RadioLib does this by using EEPROM (persistent storage), + by default starting at address 0 and using 448 bytes. + If you already use EEPROM in your application, + you will have to either avoid this range, or change it + by setting a different start address by changing the value of + RADIOLIB_HAL_PERSISTENT_STORAGE_BASE macro, either + during build or in src/BuildOpt.h. For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration @@ -49,8 +51,8 @@ LoRaWANNode node(&radio, &EU868); void setup() { Serial.begin(9600); - // initialize SX1278 with default settings - Serial.print(F("[SX1278] Initializing ... ")); + // initialize radio (SX1262 / SX1278 / ... ) with default settings + Serial.print(F("[Radio] Initializing ... ")); int state = radio.begin(); if(state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); @@ -104,6 +106,7 @@ void setup() { if(state >= RADIOLIB_ERR_NONE) { Serial.println(F("success!")); + delay(2000); // small delay between joining and uplink } else { Serial.print(F("failed, code ")); Serial.println(state); @@ -118,7 +121,7 @@ int count = 0; void loop() { // send uplink to port 10 Serial.print(F("[LoRaWAN] Sending uplink packet ... ")); - String strUp = "Hello World! #" + String(count++); + String strUp = "Hello!" + String(count++); String strDown; int state = node.sendReceive(strUp, 10, strDown); if(state == RADIOLIB_ERR_NONE) { @@ -154,6 +157,10 @@ void loop() { Serial.print(F("failed, code ")); Serial.println(state); } + + // on EEPROM enabled boards, you can save the current session + // by calling "saveSession" which allows retrieving the session after reboot or deepsleep + node.saveSession(); // wait before sending another packet uint32_t minimumDelay = 60000; // try to send once every minute diff --git a/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino b/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino index 0a6dfcf1d..cf765cb16 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino @@ -8,12 +8,14 @@ The device will start uploading data directly, without having to join the network. - LoRaWAN v1.1 requires the use of EEPROM (persistent storage). - Please refer to the 'persistent' example once you are familiar - with LoRaWAN. - Running this examples REQUIRES you to check "Resets DevNonces" - on your LoRaWAN dashboard. Refer to the network's - documentation on how to do this. + NOTE: LoRaWAN v1.1 requires storing parameters persistently! + RadioLib does this by using EEPROM (persistent storage), + by default starting at address 0 and using 448 bytes. + If you already use EEPROM in your application, + you will have to either avoid this range, or change it + by setting a different start address by changing the value of + RADIOLIB_HAL_PERSISTENT_STORAGE_BASE macro, either + during build or in src/BuildOpt.h. For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration @@ -50,8 +52,8 @@ LoRaWANNode node(&radio, &EU868); void setup() { Serial.begin(9600); - // initialize SX1278 with default settings - Serial.print(F("[SX1278] Initializing ... ")); + // initialize radio (SX1262 / SX1278 / ... ) with default settings + Serial.print(F("[Radio] Initializing ... ")); int state = radio.begin(); if(state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); @@ -126,7 +128,7 @@ int count = 0; void loop() { // send uplink to port 10 Serial.print(F("[LoRaWAN] Sending uplink packet ... ")); - String strUp = "Hello World! #" + String(count++); + String strUp = "Hello!" + String(count++); String strDown; int state = node.sendReceive(strUp, 10, strDown); if(state == RADIOLIB_ERR_NONE) { @@ -163,6 +165,10 @@ void loop() { Serial.println(state); } + // on EEPROM enabled boards, you can save the current session + // by calling "saveSession" which allows retrieving the session after reboot or deepsleep + node.saveSession(); + // wait before sending another packet uint32_t minimumDelay = 60000; // try to send once every minute uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!) 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 deleted file mode 100644 index a475e7255..000000000 --- a/examples/LoRaWAN/LoRaWAN_End_Device_Persistent/LoRaWAN_End_Device_Persistent.ino +++ /dev/null @@ -1,156 +0,0 @@ -/* - 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. - This example restores and saves a session such that you can use - 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 LoRaWAN examples for more - information regarding joining a network. - - NOTE: LoRaWAN requires storing some parameters persistently! - RadioLib does this by using EEPROM, by default - starting at address 0 and using 448 bytes. - If you already use EEPROM in your application, - you will have to either avoid this range, or change it - by setting a different start address by changing the value of - RADIOLIB_HAL_PERSISTENT_STORAGE_BASE macro, either - during build or in src/BuildOpt.h. - - For default module settings, see the wiki page - https://github.com/jgromes/RadioLib/wiki/Default-configuration - - For full API reference, see the GitHub Pages - https://jgromes.github.io/RadioLib/ -*/ - -// include the library -#include - -// SX1262 has the following pin order: -// Module(NSS/CS, DIO1, RESET, BUSY) -// SX1262 radio = new Module(8, 14, 12, 13); - -// SX1278 has the following pin order: -// Module(NSS/CS, DIO0, RESET, DIO1) -SX1278 radio = new Module(10, 2, 9, 3); - -// create the node instance on the EU-868 band -// using the radio module and the encryption key -// make sure you are using the correct band -// based on your geographical location! -LoRaWANNode node(&radio, &EU868); - -// for fixed bands with subband selection -// such as US915 and AU915, you must specify -// the subband that matches the Frequency Plan -// that you selected on your LoRaWAN console -/* - LoRaWANNode node(&radio, &US915, 2); -*/ - -void setup() { - Serial.begin(9600); - - // initialize SX1278 with default settings - Serial.print(F("[SX1278] Initializing ... ")); - int state = radio.begin(); - if(state == RADIOLIB_ERR_NONE) { - Serial.println(F("success!")); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while(true); - } - - // start the activation - // Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... ")); - // uint64_t joinEUI = 0x12AD1011B0C0FFEE; - // uint64_t devEUI = 0x70B3D57ED005E120; - // uint8_t nwkKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, - // 0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 }; - // uint8_t appKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, - // 0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 }; - // state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); - - // on EEPROM-enabled boards, after the device has been activated, - // the session can be restored without rejoining after device power cycle - // by calling the same `beginOTAA()` or `beginABP()` function with the same keys - // or call `restore()` where it will restore any existing session - // `restore()` returns the active mode if it succeeded (OTAA or ABP) - Serial.print(F("[LoRaWAN] Resuming previous session ... ")); - state = node.restore(); - if(state >= RADIOLIB_ERR_NONE) { - Serial.println(F("success!")); - Serial.print(F("Restored an ")); - if(state == RADIOLIB_LORAWAN_MODE_OTAA) - Serial.println(F("OTAA session.")); - else { - Serial.println(F("ABP session.")); - } - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while(true); - } - -} - -// counter to keep track of transmitted packets -int count = 0; - -void loop() { - // send uplink to port 10 - Serial.print(F("[LoRaWAN] Sending uplink packet ... ")); - String strUp = "Hello World! #" + String(count++); - String strDown; - int state = node.sendReceive(strUp, 10, strDown); - if(state == RADIOLIB_ERR_NONE) { - Serial.println(F("received a downlink!")); - - // print data of the packet (if there are any) - Serial.print(F("[LoRaWAN] Data:\t\t")); - if(strDown.length() > 0) { - Serial.println(strDown); - } else { - Serial.println(F("")); - } - - // print RSSI (Received Signal Strength Indicator) - Serial.print(F("[LoRaWAN] RSSI:\t\t")); - Serial.print(radio.getRSSI()); - Serial.println(F(" dBm")); - - // print SNR (Signal-to-Noise Ratio) - Serial.print(F("[LoRaWAN] SNR:\t\t")); - Serial.print(radio.getSNR()); - Serial.println(F(" dB")); - - // print frequency error - Serial.print(F("[LoRaWAN] Frequency error:\t")); - Serial.print(radio.getFrequencyError()); - Serial.println(F(" Hz")); - - } else if(state == RADIOLIB_ERR_RX_TIMEOUT) { - Serial.println(F("no downlink!")); - - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - } - - // on EEPROM enabled boards, you can save the current session - // by calling "saveSession" which allows retrieving the session after reboot or deepsleep - node.saveSession(); - - // wait before sending another packet - // alternatively, call a deepsleep function here - // make sure to send the radio to sleep as well using radio.sleep() - uint32_t minimumDelay = 60000; // try to send once every minute - uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!) - uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows - - delay(delayMs); -} 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 87d350b07..771890b51 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 @@ -52,8 +52,8 @@ LoRaWANNode node(&radio, &EU868); void setup() { Serial.begin(9600); - // initialize SX1278 with default settings - Serial.print(F("[SX1278] Initializing ... ")); + // initialize radio (SX1262 / SX1278 / ... ) with default settings + Serial.print(F("[Radio] Initializing ... ")); int state = radio.begin(); if(state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); @@ -102,12 +102,16 @@ void setup() { if(state >= RADIOLIB_ERR_NONE) { Serial.println(F("success!")); + delay(2000); // small delay between joining and uplink } else { Serial.print(F("failed, code ")); Serial.println(state); while(true); } + Serial.print("[LoRaWAN] DevAddr: "); + Serial.println(node.getDevAddr(), HEX); + // on EEPROM-enabled boards, after the device has been activated, // the session can be restored without rejoining after device power cycle // this is intrinsically done when calling `beginOTAA()` with the same keys @@ -116,7 +120,7 @@ void setup() { /* Serial.print(F("[LoRaWAN] Resuming previous session ... ")); state = node.restore(); - if(state == RADIOLIB_ERR_NONE) { + if(state >= RADIOLIB_ERR_NONE) { Serial.println(F("success!")); } else { Serial.print(F("failed, code ")); @@ -130,7 +134,7 @@ void setup() { // set a fixed datarate node.setDatarate(5); - // in order to save the datarate persistent across reboot/deepsleep, use the following: + // in order to set the datarate persistent across reboot/deepsleep, use the following: /* node.setDatarate(5, true); */ @@ -148,10 +152,10 @@ void setup() { node.setDutyCycle(true, 1250); // enable or disable the dwell time limits - // the second argument specific allowed airtime per uplink in milliseconds - // if not called, this corresponds to setDwellTime(true, 0) + // the second argument specifies the allowed airtime per uplink in milliseconds + // unless specified, this argument is set to 0 // setting this to 0 corresponds to the band's maximum allowed dwell time by law - node.setDwellTime(true, 1000); + node.setDwellTime(true, 400); } void loop() { @@ -170,14 +174,14 @@ void loop() { uint32_t fcntUp = node.getFcntUp(); Serial.print(F("[LoRaWAN] Sending uplink packet ... ")); - String strUp = "Hello World! #" + String(fcntUp); + String strUp = "Hello!" + String(fcntUp); // send a confirmed uplink to port 10 every 64th frame // and also request the LinkCheck and DeviceTime MAC commands if(fcntUp % 64 == 0) { - state = node.uplink(strUp, 10, true); node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_LINK_CHECK); node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_DEVICE_TIME); + state = node.uplink(strUp, 10, true); } else { state = node.uplink(strUp, 10); } @@ -239,7 +243,7 @@ void loop() { Serial.print(F("[LoRaWAN] Confirming:\t")); Serial.println(event.confirming); Serial.print(F("[LoRaWAN] Datarate:\t")); - Serial.print(event.datarate); + Serial.println(event.datarate); Serial.print(F("[LoRaWAN] Frequency:\t")); Serial.print(event.freq, 3); Serial.println(F(" MHz")); diff --git a/keywords.txt b/keywords.txt index ace5e9932..6ee6222c8 100644 --- a/keywords.txt +++ b/keywords.txt @@ -318,6 +318,7 @@ setTxPower KEYWORD2 setCSMA KEYWORD2 getMacLinkCheckAns KEYWORD2 getMacDeviceTimeAns KEYWORD2 +getDevAddr KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index ddd1e88ab..6a038746d 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -2873,6 +2873,9 @@ int16_t LoRaWANNode::getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, return(RADIOLIB_ERR_NONE); } +uint64_t LoRaWANNode::getDevAddr() { + return(this->devAddr); +} // The following function enables LMAC, a CSMA scheme for LoRa as specified // in the LoRa Alliance Technical Recommendation #13. diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index b881501df..157945a0c 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -685,6 +685,12 @@ class LoRaWANNode { */ int16_t getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, bool returnUnix = true); + /*! + \brief Returns the DevAddr of the device, regardless of OTAA or ABP mode + \returns 8-byte DevAddr + */ + uint64_t getDevAddr(); + #if !RADIOLIB_GODMODE private: #endif