From 22045d71896e3240e54c2d22d9e6a5fb18b5641c Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Wed, 28 Feb 2024 20:46:47 +0000 Subject: [PATCH 01/15] Range of tweaks & dutycycle / FUP --- .../LoRaWAN_End_Device/LoRaWAN_End_Device.ino | 98 ++++++++----------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino index d4ec506d1..f7db5b952 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino @@ -21,6 +21,9 @@ For full API reference, see the GitHub Pages https://jgromes.github.io/RadioLib/ + + For LoRaWAN details, see the wiki page + https://github.com/jgromes/RadioLib/wiki/LoRaWAN */ // include the library @@ -28,11 +31,11 @@ // SX1262 has the following pin order: // Module(NSS/CS, DIO1, RESET, BUSY) -// SX1262 radio = new Module(8, 14, 12, 13); +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); +// 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 @@ -44,13 +47,12 @@ LoRaWANNode node(&radio, &EU868); // 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); -*/ +// LoRaWANNode node(&radio, &US915, 2); + void setup() { Serial.begin(9600); - + // initialize radio (SX1262 / SX1278 / ... ) with default settings Serial.print(F("[Radio] Initializing ... ")); int state = radio.begin(); @@ -62,48 +64,29 @@ void setup() { while(true); } - // application identifier - pre-LoRaWAN 1.1.0, this was called appEUI - // when adding new end device in TTN, you will have to enter this number - // you can pick any number you want, but it has to be unique - uint64_t joinEUI = 0x12AD1011B0C0FFEE; - - // device identifier - this number can be anything - // when adding new end device in TTN, you can generate this number, - // or you can set any value you want, provided it is also unique - uint64_t devEUI = 0x70B3D57ED005E120; - - // select some encryption keys which will be used to secure the communication - // there are two of them - network key and application key - // because LoRaWAN uses AES-128, the key MUST be 16 bytes (or characters) long - - // network key is the ASCII string "topSecretKey1234" - uint8_t nwkKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 }; - - // application key is the ASCII string "aDifferentKeyABC" - uint8_t appKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, - 0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 }; - - // prior to LoRaWAN 1.1.0, only a single "nwkKey" is used - // when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded - // and can be set to NULL - - - // 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 - // in that case, the function will not need to transmit a JoinRequest - - // now we can start the activation - // this can take up to 10 seconds, and requires a LoRaWAN gateway in range - // a specific starting-datarate can be selected in dynamic bands (e.g. EU868): - /* - uint8_t joinDr = 4; - state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDr); - */ + // JoinEUI - previous versions of LoRaWAN called this AppEUI + // for development purposes you can use all zeros - see wiki for details + uint64_t joinEUI = 0x0000000000000000; + + // DevEUI - The device's Extended Unique Identifier + // TTN will generate one for you + uint64_t devEUI = 0x----------------; + + // encryption keys used to secure the communication + // TTN will generate them for you + // see wiki for details on copying & pasting them + uint8_t nwkKey[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, + 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; + uint8_t appKey[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, + 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; + + + // Manages uplink intervals to the TTN Fair Use Policy + node.setDutyCycle(true, 1250); + + // Begin the join to the network Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... ")); state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); - if(state >= RADIOLIB_ERR_NONE) { Serial.println(F("success!")); delay(2000); // small delay between joining and uplink @@ -112,8 +95,7 @@ void setup() { Serial.println(state); while(true); } - -} +} // setup // counter to keep track of transmitted packets int count = 0; @@ -121,7 +103,7 @@ int count = 0; void loop() { // send uplink to port 10 Serial.print(F("[LoRaWAN] Sending uplink packet ... ")); - String strUp = "Hello!" + String(count++); + String strUp = "Hello! " + String(count++); String strDown; int state = node.sendReceive(strUp, 10, strDown); if(state == RADIOLIB_ERR_NONE) { @@ -151,21 +133,25 @@ void loop() { Serial.println(F(" Hz")); } else if(state == RADIOLIB_ERR_RX_TIMEOUT) { - Serial.println(F("no downlink!")); + Serial.println(F("")); } else { Serial.print(F("failed, code ")); Serial.println(state); } - // on EEPROM enabled boards, you should save the current session - // by calling "saveSession" which allows retrieving the session after reboot or deepsleep + // on boards that can save to Flash or EEPROMthis saves the session + // which allows recall of 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!) + uint32_t minimumDelay = 300000; // try to send once every 3 minutes + uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per FUP & law!) uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows - + + Serial.print(F("[LoRaWAN] Next uplink in ")); + Serial.print(delayMs/60); + Serial.println(F("s")); + delay(delayMs); -} +} // loop From 7389d74e69c139eb857e07881762fd393faa8d96 Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Fri, 1 Mar 2024 09:46:38 +0000 Subject: [PATCH 02/15] Update the reference version to latest LW code base --- .../LoRaWAN_End_Device_Reference.ino | 191 +++++++----------- 1 file changed, 75 insertions(+), 116 deletions(-) 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 207dd356d..1e5faf6d4 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 @@ -22,6 +22,9 @@ For full API reference, see the GitHub Pages https://jgromes.github.io/RadioLib/ + + For LoRaWAN details, see the wiki page + https://github.com/jgromes/RadioLib/wiki/LoRaWAN */ // include the library @@ -29,11 +32,11 @@ // SX1262 has the following pin order: // Module(NSS/CS, DIO1, RESET, BUSY) -// SX1262 radio = new Module(8, 14, 12, 13); +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); +// 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 @@ -45,9 +48,8 @@ LoRaWANNode node(&radio, &EU868); // 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); -*/ +// LoRaWANNode node(&radio, &US915, 2); + void setup() { Serial.begin(9600); @@ -63,43 +65,29 @@ void setup() { while(true); } - // application identifier - pre-LoRaWAN 1.1.0, this was called appEUI - // when adding new end device in TTN, you will have to enter this number - // you can pick any number you want, but it has to be unique - uint64_t joinEUI = 0x12AD1011B0C0FFEE; - - // device identifier - this number can be anything - // when adding new end device in TTN, you can generate this number, - // or you can set any value you want, provided it is also unique - uint64_t devEUI = 0x70B3D57ED005E120; - - // select some encryption keys which will be used to secure the communication - // there are two of them - network key and application key - // because LoRaWAN uses AES-128, the key MUST be 16 bytes (or characters) long + // JoinEUI - previous versions of LoRaWAN this was AppEUI + // for development purposes you can use all zeros - see wiki for details + uint64_t joinEUI = 0x0000000000000000; - // network key is the ASCII string "topSecretKey1234" - uint8_t nwkKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 }; + // DevEUI - The device's Extended Unique Identifier + // TTN will generate one for you + uint64_t devEUI = 0x----------------; - // application key is the ASCII string "aDifferentKeyABC" - uint8_t appKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, - 0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 }; + // encryption keys used to secure the communication + // TTN will generate them for you + // see wiki for details on copying & pasting them + uint8_t nwkKey[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, + 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; + uint8_t appKey[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, + 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; - // prior to LoRaWAN 1.1.0, only a single "nwkKey" is used - // when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded - // and can be set to NULL + // Override the default join rate + uint8_t joinDR = 3; - // now we can start the activation - // this can take up to 10 seconds, and requires a LoRaWAN gateway in range - // a specific starting-datarate can be selected in dynamic bands (e.g. EU868): - /* - uint8_t joinDr = 4; - state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDr); - */ + // Begin the join to the network Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... ")); - state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); - + state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDR); if(state >= RADIOLIB_ERR_NONE) { Serial.println(F("success!")); delay(2000); // small delay between joining and uplink @@ -112,51 +100,24 @@ void setup() { 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 - // or if you 'lost' the keys or don't want them included in your sketch - // you can call `restore()` - /* - Serial.print(F("[LoRaWAN] Resuming previous session ... ")); - state = node.restore(); - if(state >= RADIOLIB_ERR_NONE) { - Serial.println(F("success!")); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while(true); - } - */ - - // disable the ADR algorithm + // disable the ADR algorithm (on by default which is preferable) node.setADR(false); - // set a fixed datarate - node.setDatarate(5); - // in order to set the datarate persistent across reboot/deepsleep, use the following: - /* - node.setDatarate(5, true); - */ + // set a fixed datarate & make it persistent (not normal) + node.setDatarate(5, true); - // enable CSMA - // this tries to minimize packet loss by searching for a free channel - // before actually sending an uplink + // enable CSMA which tries to minimize packet loss by searching + // for a free channel before actually sending an uplink node.setCSMA(6, 2, true); - // enable or disable the dutycycle - // the second argument specific allowed airtime per hour in milliseconds - // 1250 = TTN FUP (30 seconds / 24 hours) - // if not called, this corresponds to setDutyCycle(true, 0) - // setting this to 0 corresponds to the band's maximum allowed dutycycle by law - node.setDutyCycle(true, 1250); - - // enable or disable the dwell time limits - // 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 + // manages uplink intervals to the TTN Fair Use Policy + node.setDutyCycle(true, 1250); + + // enable the dwell time limits - 400ms is the limit for the US node.setDwellTime(true, 400); -} + +} // setup + void loop() { int state = RADIOLIB_ERR_NONE; @@ -173,44 +134,45 @@ void loop() { // retrieve the last uplink frame counter uint32_t fcntUp = node.getFcntUp(); - Serial.print(F("[LoRaWAN] Sending uplink packet ... ")); - String strUp = "Hello!" + String(fcntUp); + Serial.print(F("[LoRaWAN] Sending uplink packet #")); + Serial.println(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) { + Serial.print(F("[LoRaWAN] Requesting LinkCheck and DeviceTime")); 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); } - if(state == RADIOLIB_ERR_NONE) { - Serial.println(F("success!")); - } else { + if(state != RADIOLIB_ERR_NONE) { Serial.print(F("failed, code ")); Serial.println(state); } - // after uplink, you can call downlink(), - // to receive any possible reply from the server - // this function must be called within a few seconds - // after uplink to receive the downlink! - Serial.print(F("[LoRaWAN] Waiting for downlink ... ")); + // after uplink, you must call downlink() to receive any possible reply + // from the server. This function must be called before the Rx1 delay + // for the network. Typically this is 5s after end of uplink. + Serial.println(F("[LoRaWAN] Waiting for downlink ... ")); String 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); + // you can also retrieve additional information about an uplink or + // downlink by passing a reference to LoRaWANEvent_t structure + LoRaWANEvent_t downlinkDetails; + state = node.downlink(strDown, &downlinkDetails); if(state == RADIOLIB_ERR_NONE) { - Serial.println(F("success!")); - - // print data of the packet (if there are any) + // print data of the packet Serial.print(F("[LoRaWAN] Data:\t\t")); if(strDown.length() > 0) { - Serial.println(strDown); + for (uint8_t c = 0; c < strDown.length(); c++) { + uint8_t value = strDown[c]; + if (value < 10) Serial.print(F("0")); + Serial.print(value, HEX); + } + Serial.println(); } else { Serial.println(F("")); } @@ -232,30 +194,22 @@ void loop() { // 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.println(downlinkDetails.confirmed); Serial.print(F("[LoRaWAN] Confirming:\t")); - Serial.println(event.confirming); + Serial.println(downlinkDetails.confirming); Serial.print(F("[LoRaWAN] Datarate:\t")); - Serial.println(event.datarate); + Serial.println(downlinkDetails.datarate); Serial.print(F("[LoRaWAN] Frequency:\t")); - Serial.print(event.freq, 3); + Serial.print(downlinkDetails.freq, 3); Serial.println(F(" MHz")); Serial.print(F("[LoRaWAN] Output power:\t")); - Serial.print(event.power); + Serial.print(downlinkDetails.power); Serial.println(F(" dBm")); Serial.print(F("[LoRaWAN] Frame count:\t")); - Serial.println(event.fcnt); + Serial.println(downlinkDetails.fcnt); Serial.print(F("[LoRaWAN] Port:\t\t")); - Serial.println(event.port); - - Serial.print(radio.getFrequencyError()); + Serial.println(downlinkDetails.port); uint8_t margin = 0; uint8_t gwCnt = 0; @@ -276,21 +230,26 @@ void loop() { } } else if(state == RADIOLIB_ERR_RX_TIMEOUT) { - Serial.println(F("timeout!")); + // Not really necessary to report normal operation } else { Serial.print(F("failed, code ")); Serial.println(state); } - // on EEPROM enabled boards, you should save the current session - // by calling "saveSession" which allows retrieving the session after reboot or deepsleep + // on boards that can save to Flash or EEPROM this saves the session + // which allows recall of 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!) - uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows + uint32_t minimumDelay = 3 * 60 * 1000; // try to send once every 3 minutes + uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per FUP & law!) + uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows + + Serial.print(F("[LoRaWAN] Next uplink in ")); + Serial.print(delayMs/1000); + Serial.println(F("s")); delay(delayMs); -} + +} // loop \ No newline at end of file From ec9126dae0e31ec8b3fd63e0b2fccda4c27c1ab9 Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Fri, 1 Mar 2024 09:48:55 +0000 Subject: [PATCH 03/15] Added last updated dates & corresponding RadioLib version number --- examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino | 6 +++++- .../LoRaWAN_End_Device_Reference.ino | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino index f7db5b952..9089a77c9 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino @@ -24,6 +24,10 @@ For LoRaWAN details, see the wiki page https://github.com/jgromes/RadioLib/wiki/LoRaWAN + + + Last updated 1st March 2024 for RadioLib 6.4.2 + */ // include the library @@ -140,7 +144,7 @@ void loop() { Serial.println(state); } - // on boards that can save to Flash or EEPROMthis saves the session + // on boards that can save to Flash or EEPROM this saves the session // which allows recall of the session after reboot or deepsleep node.saveSession(); 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 1e5faf6d4..8e50b7a50 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 @@ -25,6 +25,10 @@ For LoRaWAN details, see the wiki page https://github.com/jgromes/RadioLib/wiki/LoRaWAN + + + Last updated 1st March 2024 for RadioLib 6.4.2 + */ // include the library From 3d8c2e866dd2f56a8d6cecbdbc55e04695e3e70b Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Sat, 2 Mar 2024 13:54:49 +0000 Subject: [PATCH 04/15] WIP Starter for preview --- examples/LoRaWAN/Starter/config.h | 85 +++++++++++++ examples/LoRaWAN/Starter/main.cpp | 44 +++++++ examples/LoRaWAN/Starter/notes.md | 202 ++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 examples/LoRaWAN/Starter/config.h create mode 100644 examples/LoRaWAN/Starter/main.cpp create mode 100644 examples/LoRaWAN/Starter/notes.md diff --git a/examples/LoRaWAN/Starter/config.h b/examples/LoRaWAN/Starter/config.h new file mode 100644 index 000000000..bc50acf65 --- /dev/null +++ b/examples/LoRaWAN/Starter/config.h @@ -0,0 +1,85 @@ + +#include + +// How often to send an uplink - consider legal & FUP constraints - see notes +uint32_t uplinkInterval = 3 * 60 * 1000; // minutes x seconds x milliseconds + +uint64_t joinEUI = 0x0000000000000000; +uint64_t devEUI = 0x0000000000000000; +uint8_t appKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +uint8_t nwkKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + + +// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500 +const LoRaWANBand_t Region = EU868; +const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 + + +// ============================================================================ +// Below is to support the sketch - only make changes if the notes say so ... + +// Auto select MCU <-> radio connections +// If you get an error message when compiling, it may be that the +// pinmap could not be determined - see the notes for more info + +#if defined(ARDUINO_TTGO_LORA32_V1) + #pragma message ("TTGO LoRa32 v1 - no Display") + SX1276 radio = new Module(18, 26, 14, 33); + +// #elif defined(ARDUINO_TTGO_LORA32_V2) +// #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") + +#elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 14, 33); + +// #elif defined(ARDUINO_TBEAM_USE_RADIO_SX1262) +// #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") + +// #elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) +// #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") +// SX1276 radio = new Module(18, 26, 23, 33); + +// #elif defined(ARDUINO_HELTEC_WIFI_LORA_32) +// #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") + +#elif defined(ARDUINO_heltec_wifi_kit_32_V2) + #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined(ARDUINO_heltec_wifi_kit_32_V3) + #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") + SX1262 radio = new Module(8, 14, 12, 13); + +// #elif defined(ARDUINO_CUBECELL_BOARD) +// #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") +// SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); + +// #elif defined(ARDUINO_CUBECELL_BOARD_V2) +// #pragma error ("ARDUINO_CUBECELL_BOARD_V2 awaiting pin map") + +#elif defined(ARDUINO_SAMD_FEATHER_M0) + #pragma message ("Adafruit Feather M0 with RFM95") + #pragma message ("Link required on board") + SX1276 radio = new Module(8, 3, 4, 6); + +#else + #pragma message ("Unknown board - no pinmap") + SX1262 radio = new Module(8, 14, 12, 13); + +#endif + +LoRaWANNode node(&radio, &Region, subBand); + + +// Helper function to display any issues +void debug(bool isFail, const __FlashStringHelper* message, int state, bool Freeze) { + if (isFail) { + Serial.print(message); + Serial.print("("); + Serial.print(state); + Serial.println(")"); + while (Freeze); + } +} diff --git a/examples/LoRaWAN/Starter/main.cpp b/examples/LoRaWAN/Starter/main.cpp new file mode 100644 index 000000000..a2ca1265f --- /dev/null +++ b/examples/LoRaWAN/Starter/main.cpp @@ -0,0 +1,44 @@ +#include + +#include "config.h" + +void setup() { + Serial.begin(115200); + while (!Serial); + Serial.println(F("\nSetup ... ")); + + Serial.println(F("Initalise the radio")); + int state = radio.begin(); + debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); + + Serial.println(F("Join ('login') to the LoRaWAN Network")); + state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); + debug(state < RADIOLIB_ERR_NONE, F("Join failed"), state, true); + + Serial.println(F("Ready!\n")); +} + + +void loop() { + Serial.print(F("Sending uplink #")); + + // Read some inputs + uint8_t Digital1 = digitalRead(2); + uint16_t Analog1 = analogRead(A0); + + // Build payload byte array + uint8_t uplinkPayload[3]; + uplinkPayload[0] = Digital1; + uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(Analog1); + + // Perform an uplink + int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); + debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); + + // Complete serial line with the uplink counter + Serial.println(node.getFcntUp()); + + // Wait until next uplink - observing legal & TTN FUP constraints + delay(uplinkInterval); +} diff --git a/examples/LoRaWAN/Starter/notes.md b/examples/LoRaWAN/Starter/notes.md new file mode 100644 index 000000000..4f56a617a --- /dev/null +++ b/examples/LoRaWAN/Starter/notes.md @@ -0,0 +1,202 @@ + + +# RadioLib LoRaWAN on TTN starter script + +## Welcome + +These notes are for someone who has successfully created a few sketches for their Arduino based device but is starting out with LoRaWAN. You don't have to be a C coding ninja but some familarity with C and procedural programming is assumed. The absolutely simplest way to get started is to buy some known good hardware that's all done for you so you can concentrate on the code & configuration. + + +## Introduction + +LoRaWAN is an amazing system for small battery powered sensors collecting data for years at a time. With great features comes some more complex elements which means it is not quite as simple as just providing WiFi credentials and pushing data through. It is in the range of setting up & customising the settings for a home router but with no wizards to do the heavy lifting for you. So we strongly recommend spending a couple of hours reviewing the TTN Getting Started section so you are aware of the minimum knowledge to make a successful start: https://www.thethingsnetwork.org/docs/lorawan/. Johan's video is amazing but is also drinking from the firehose. Read the text first and then watch the video on Youtube where there are bookmarks to deliver it in small digestable chunks. + +These notes plus a lot more are available in the wiki: https://github.com/jgromes/RadioLib/wiki/LoRaWAN + +For questions about using RadioLib there is the discussions section (https://github.com/jgromes/RadioLib/discussions) and if you believe you've found an issue (aka bug), the issues section (https://github.com/jgromes/RadioLib/issues). If posting an issue please ensure you tell us what hardware you are using and provide a debug log - please do not use verbose mode unless asked to. If the question is more LoRaWAN or firmware related, then you can use the TTN forum: https://www.thethingsnetwork.org/forum/ + + +## Register & setup on TTN + +This sketch isn't particularly aimed at The Things Stack (TTS) but you can get a free Sandbox account and the following instructions are for that. Helium does not support LoRaWAN v1.1 which is the version implemented by RadioLib. Chirpstack & other LoRaWAN Network Server (LNS) stacks have not yet been tried so YMMV. + +Why no screen shots? TTS is a web based app, one that you will need to become familiar with and we will need to direct you to some of the less obvious parts. So much better that you learn the layouts in concept than slavishly follow screen shots that can & will go stale. + +There will be some instructions that you have to take on face value. You didn't learn to run before you walked and it's so much more encouraging to get started and build on success than get bogged down in endless details. Once you are up & running more of the details start to slot in to place. + +### Register on TTN + +Go to https://www.thethingsnetwork.org/get-started and register - just like any other website. These instructions are for TTS Sandbox. + +Once you have confirmed your email address, you can login to the console here: https://console.cloud.thethings.network/. If you allow your browser to share you location the best console will be selected. For most users the best one is the obvious one, if you have any doubts you can ask on the forum here: https://www.thethingsnetwork.org/forum/ - you login with the exact same details. + +It is simpler to register your gateway first. If you don't have a gateway, then a The Things Indoor Gateway (TTIG) is a very affordable option. A gateway gives you a console to see if your device is being heard and is hugely useful when debugging a DIY device. If you are in range of a community gateway you may be lucky with your first device creation but you will never know if you are in range unless you have access to that gateways console. + +You can read up on key concepts and troubleshooting here: https://www.thethingsindustries.com/docs/gateways/ + +LoRa stands for Long Range - having the gateway & device on the same desk tends to overload both receiver circuits when they hear a transmission so close to hand. The gateway should be 5 - 10m away, preferably with a solid wall in the way as well. + +### Create your application + +An application is like a box to keep some devices in - normally doing the same thing - on larger deployments this may be 1,000's of similar devices. Starting out it it is likely to be just a few so there is no need to get concerned about how to divide up your use just yet. + +Onced logged in to the console you can go in to Applications to create your first application. The ID must be all lower case or numbers, no spaces, dashes are OK and it has to be unique to the entire TTN community - so `first-app` will be rejected - you could use `your-username-first-app` as that's likely to be unique. The name and description are for your own use and are optional. + +The main menu for an application is in the left hand panel - nothing is needed there just yet. + +### Create your device + +On the right hand side about half way down on your application's summary is a big blue button `+ Register end device`. Click this to create the settings for your first device. + +You are making your own device using a third party LoRaWAN stack so there will not be an entry in the device repository so choose 'Enter end device specifics manually'. + +Choose the Frequency plan appropriate for your region. Consider that almost all countries have laws relating to what frequencies you use so don't get creative. For Europe please use the recommended option. For other regions use the entry marked 'used by TTN'. + +Choose LoRaWAN 1.1.0 - the last one in the list - the latest specfication. RadioLib uses RP001 Regional Parameters 1.1 revision B. + +At this point you will be asked for your JoinEUI. As this is a DIY device and we are using RadioLib, you can use all zero's as recommended by The LoRa Alliance TR007 Technical Recommendations document. Once you've put in all zeros and clicked confirm you will be asked for a DevEUI, AppKey and NwkKey. It is preferable to have the cosole generate them so they are properly formatted. + +Your End device ID can be changed to make the device more identifiable. Something related to your hardware helps - like devicename-01. The you can click the blue 'Register device'. + +When many sensors are big deployed, a device is registered, batteries put in, it joins and gets on with sending data for the next few years. For development purposes we need to turn off one of the security settings so that you can join & uplink out of the normal sequence that a device in the field would do. + +Click on General Settings, scroll down to Join settings, click the Expand button, scroll down and click the Resets join nonces option. You will see a warning about replay attacks which is entirely proper & correct. If anyone evesdropping in your area on your LoRa transmissions could fake a join and send uplinks from their device but only if they happened to find out your AppKey & NwkKey which is kept securely on the TTN servers and is never transmitted over the air, so they'd also have to login to your account, which is protected by your password. + +You then need to copy over the device details in to the config file for RadioLib. There are buttons to copy items to the clipboard so you don't have to hand type them. + +### Copy & Paste made easy + +You can copy the EUIs & keys from the device overview section. + +The EUIs are really straightforward - click the clipboard icon at the right hand end of the EUI display field and it will be copied in the format you need. You can then paste it in to the code - you must leave the 0x in place so the compiler knows that it's a hex value. + +The keys are relatively straightforward. Click the eye icon at the right hand end of the field. Then click the <> icon that will appear to the left. This will format the hex values as an array. Then you can click the clipboard icon to copy the array and then paste it between the { } brackets. + +### Secrets to keep safe. + +The Join & Dev EUI's are transmitted in plain text when the device joins a network. The gateway ID is public. If you have an issue and are asked for details, there are only three things to keep private - your password, the keys which are used for encryption and any API keys you create which are used for accessing your data & configuration. + + +### Monitoring your device + +If you are on your application summary page you'll see uplinks in the small activity box top right with a link to the full size table. If you click the Live Data menu item on the left it will show activity for all the devices registered on the application in the full window. + +If you just want your devices activity, from the summary page click on the device in the list in the middle of the page. + +The main menu for a device is the horizontal band: Overview, Live Data, Messaging etc. You can click Live Data or the link above the small activity box. + +**The console shows LIVE data - not a history of everything that has ever happened. A LNS is a management & relay service, not a database. When you open the console you may see a summary of recent activity - this is a bonus. You must leave the console open, even in another tab, if you want to see live activity.** + + +### Explore + +Nothing on the console can be upset unless you confirm a warning message, so you are safe to explore the different menus to orientate yourself. This is very good idea so you are have an understanding of the layout of the land and shouldn't take more than 10 or 15 minutes. The documentation & volunteers on GitHub and the TTN forum will make refer to parts of the console without giving blow by blow directions. + + + + +## The config.h + +### The uplinkInterval + +LoRaWAN devices typically send small amounts of data at intervals between 15 minutes through to once per day. This allows a device to run on two AA batteries for 2 to 5 years. Hoping that LoRaWAN can move lots of data and your device can regularly receive commands to do something on demand is trying to bend the LoRaWAN system in ways it is not designed for and usually ends up with far too many issues to unravel. + +The radio frequencies that are used are usually shared with other Industrial, Scientific & Medical, known as ISM, users. The LoRa modulation is particularly resistance to interference due to other simultaneous transmissions on the same frequency but too much local activity will mean that not all uplinks get through. The Things Industries suggest designing a system to a potential packet loss rate of 10%. Typically we see 1 or 2% loss. This is entirely down to shared use of the radio waves, once an uplink is heard by a gateway the system is super reliable through The Things Stack. + +To ensure that the shared ISM bands are fairly used there are limits defined in law on how often you can transmit, called Duty Cycle. The details vary by region or country but typically you can only transmit for 1% of the time. Some frequencies you can only use 0.1% of the time. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/ for more information. + +Additionally, as The Things Stack Sandbox aka TTN is an array of servers in three locations around the world paid for by The Things Industries, there is a Fair Use Policy so that those learning LoRaWAN, communities, hobbyists & makers are guided on how much of the resource any one device can use. In short, it's 30 seconds of airtime a day and 10 downlinks. When a gateway is transmitting a downlink it can not hear any uplinks (contributing to the potential uplink loss outlined above). The community concensus is that 1 downlink a fortnight to update or adjust settings is appropriate. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/#fair-use-policy for more information. + +You can see what intervals can be used with this interactive calculator: https://avbentem.github.io/airtime-calculator/ttn/. Devices further away from gateways will have to use a higher Spread Factor to be heard - do not assume everything will happen at SF7. A uplink takes a minimum of 6 seconds from start to end, sometimes longer if the device is further away from the gateway, so + +With all thes considerations, trying to use LoRaWAN for command & control isn't appropriate and realtime GPS tracking almost always breaches FUP and usually legal limits, leaving aside the challenges of coverage. + +See the hints & tips section on testing your device. + + +### EUI's & Keys + +In the config.h towards the top there are four lines thus: + +// replace-with-your-device-id +uint64_t joinEUI = 0x0000000000000000; +uint64_t devEUI = 0x0000000000000000; +uint8_t appKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +uint8_t nwkKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +On the TTN console on the device summary page, click the clipboard icon next to the DevEUI, highlight the 16 0's in the third line after the x and paste. + +The devEUI must start with 0x and will end up looking something like 0x70B3D57ED006544E + +For the appKey we need TTN to format it correctly. Click the eye icon and an extra icon will appear <> - click this and the key will be formatted for you. Click the clipboard icon and then paste over the 32 0x00's in the config file. Then do the same for nwkKey. + +A key will end up something like 0x31, 0x16, 0x6A, 0x22, 0x97, 0x52, 0xB6, 0x34, 0x57, 0x45, 0x1B, 0xC3, 0xC9, 0xD8, 0x83, 0xE8 + + +### Region + +The region value you use MUST match the one you selected on the console. + +If you are using US915 or AU915 then you should change the subBand const to 2. + +### The pinmap + +This is the connections between the MCU (ESP32/ATmega/SAMD) and the LoRa radio (SX1276/SX1262). + +Prebuilt modules are easy - we can detect the board and setup the pinmap for you. These boards are: + +* TTGO_LoRa32 +* TTGO_LoRa32_V1 +* TTGO_LORA32_V2 +* TTGO_LORA32_v21NEW +* HELTEC_WIFI_LORA_32 +* HELTEC_WIFI_LORA_32_V2 +* HELTEC_WIFI_LORA_32_V3 +* CUBECELL_BOARD + +If you have a TTGO T-Beam, you must choose the correct radio from the Board Revision sub-menu found under the main Tools menu. + +* TBEAM_USE_RADIO_SX1262 +* TBEAM_USE_RADIO_SX1276 + +If you have an Adafruit Feather M0 with RFM95 then you must solder a wire or use a jumper to link from pin 6 to io1: https://learn.adafruit.com/the-things-network-for-feather/arduino-wiring + + +If you have a module that's not on this list, please go to the "Pinmap How-To" below. + + + +## Observations on the main sketch + +Most of the sketch has comments that tell you what the various parts are doing. This should add a little more info: + +### The Join + +When a device is first started, it needs to register with the LoRaWAN Network Server (LNS) and setup it's session. With the settings from the console copied over and a gateway an appropriate distance away, most of the time the join will 'just work'. + +If it doesn't, then there is no point trying repeatedly without going through the troubleshootng sequence. So this starter sketch will try once only to save the airwaves & TTN Community servers from repeated misfires. + + +### The payload + +You may see other starter sketches sending text. Apart from being massively inefficient, the text isn't easily displayed on the TTN console which makes it rather pointless and pro embedded engineers don't send strings. So this sketch sends the data as a sequence of bytes as recommended. + +Further reading on this can be found here, just ignore the pink message about v2, it's all still valid: https://www.thethingsnetwork.org/docs/devices/bytes/ + +We've not assumed anything about any sensors you have, so we are just reading a digital & an analog pin. An analog reading is typically a two byte value - an integer - this is split using the Arduino highByte & lowByte function. You'll see how we put it back together in the TTN console below. + + +## TTN Console Payload Decoder + +Coming soon + +## Hints & Tips + +### Device testing + +The LoRaWAN code base works to a specification and once you are happy your device is able to join & send a few dozen uplinks, continuing to sit around waiting for an uplink to test your sensor code & payload format is a waste of your time. The solution is to write everything else in a different sketch, output the array to the serial console and then you can copy & paste the hex array in to the TTN console Payload Formatters section to test the decoding. + + +## Pinmap How-To + + From a926d5e13ad6548c683eefce88d52394a05f8115 Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Sat, 2 Mar 2024 14:01:32 +0000 Subject: [PATCH 05/15] Change the names to protect the innocent --- examples/LoRaWAN/Starter/{main.cpp => Starter.ino} | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) rename examples/LoRaWAN/Starter/{main.cpp => Starter.ino} (90%) diff --git a/examples/LoRaWAN/Starter/main.cpp b/examples/LoRaWAN/Starter/Starter.ino similarity index 90% rename from examples/LoRaWAN/Starter/main.cpp rename to examples/LoRaWAN/Starter/Starter.ino index a2ca1265f..ced6020ff 100644 --- a/examples/LoRaWAN/Starter/main.cpp +++ b/examples/LoRaWAN/Starter/Starter.ino @@ -20,7 +20,7 @@ void setup() { void loop() { - Serial.print(F("Sending uplink #")); + Serial.println(F("Sending uplink")); // Read some inputs uint8_t Digital1 = digitalRead(2); @@ -36,9 +36,6 @@ void loop() { int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); - // Complete serial line with the uplink counter - Serial.println(node.getFcntUp()); - // Wait until next uplink - observing legal & TTN FUP constraints delay(uplinkInterval); } From 0182a123fb4c4b6896759b5e54c6736cb1192443 Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Sat, 23 Mar 2024 17:00:13 +0000 Subject: [PATCH 06/15] Update of examples to latest API, testing, repeat --- .../LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino | 196 +++++++++++++++++ examples/LoRaWAN/LoRaWAN_ESP32/config.h | 130 +++++++++++ examples/LoRaWAN/LoRaWAN_Reference/config.h | 130 +++++++++++ .../LoRaWAN_Starter/LoRaWAN_Starter.ino | 41 ++++ examples/LoRaWAN/LoRaWAN_Starter/config.h | 130 +++++++++++ examples/LoRaWAN/LoRaWAN_Starter/notes.md | 202 ++++++++++++++++++ 6 files changed, 829 insertions(+) create mode 100644 examples/LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino create mode 100644 examples/LoRaWAN/LoRaWAN_ESP32/config.h create mode 100644 examples/LoRaWAN/LoRaWAN_Reference/config.h create mode 100644 examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino create mode 100644 examples/LoRaWAN/LoRaWAN_Starter/config.h create mode 100644 examples/LoRaWAN/LoRaWAN_Starter/notes.md diff --git a/examples/LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino b/examples/LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino new file mode 100644 index 000000000..f2d567bbd --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino @@ -0,0 +1,196 @@ + +/* + +This demonstrates how to save the join information in to permanent memory +so that if the power fails, batteries run out or are changed, the rejoin +is more efficient & happens sooner due to the way that LoRaWAN secures +the join process - see the wiki for more details. + +This is typically useful for devices that need more power than a battery +driven sensor - something like a air quality monitor or GPS based device that +is likely to use up it's power source resulting in loss of the session. + +The relevant code is flagged with a ##### comment + +Saving the entire session is possible but not demonstrated here - it has +implications for flash wearing and complications with which parts of the +session may have changed after an uplink. So it is assumed that the device +is going in to deep-sleep, as below, between normal uplinks. + +*/ + +#if !defined(ESP32) + #pragma error ("This is not the example your device is looking for - ESP32 only") +#endif + +// ##### Load the ESP32 preferences facilites +#include +Preferences store; + +// LoRaWAN config, credentials & pinmap +#include "config.h" + +#include + +// Utilities & vars to support ESP32 deep-sleep. The RTC_DATA_ATTR attribute +// puts these in to the RTC memory which is preserved during deep-sleep +RTC_DATA_ATTR uint16_t bootCount = 1; +RTC_DATA_ATTR uint16_t bootCountSinceUnsuccessfulJoin = 0; +RTC_DATA_ATTR uint8_t LWsession[RADIOLIB_LORAWAN_SESSION_BUF_SIZE]; + +// Abbreviated version from the Arduino-ESP32 package, see +// https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/deepsleep.html +// for the complete set of options +void print_wakeup_reason() { + esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); + if (wakeup_reason == ESP_SLEEP_WAKEUP_TIMER) { + Serial.println(F("Wake from sleep")); + } else { + Serial.print(F("Wake not caused by deep sleep: ")); + Serial.println(wakeup_reason); + } + + Serial.print(F("Boot count: ")); + Serial.println(bootCount++); +} + +// Put device in to lowest power deep-sleep mode +void gotoSleep(uint32_t seconds) { + esp_sleep_enable_timer_wakeup(seconds * 1000UL * 1000UL); // Function uses uS + Serial.println(F("Sleeping\n")); + Serial.flush(); + + esp_deep_sleep_start(); + + // If this appears in the serial debug, we didn't go to sleep! + // So take defensive action so we don't continually uplink + Serial.println(F("\n\n### Sleep failed, delay of 5 minutes & then restart ###\n")); + delay(5UL * 60UL * 1000UL); + ESP.restart(); +} + + + +// Setup & execute all device functions ... +void setup() { + Serial.begin(115200); + while (!Serial); // Wait for serial to be initalised + delay(2000); // Give time to switch to the serial monitor + Serial.println(F("\nSetup")); + print_wakeup_reason(); + + int16_t state = 0; // return value for calls to RadioLib + + // Setup the radio based on the pinmap (connections) in config.h + Serial.println(F("Initalise the radio")); + state = radio.begin(); + debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); + + Serial.println(F("Recalling LoRaWAN nonces & session")); + // ##### Setup the flash storage + store.begin("radiolib"); + // ##### If we have previously saved nonces, restore them + if (store.isKey("nonces")) { + uint8_t buffer[RADIOLIB_LORAWAN_NONCES_BUF_SIZE];// Create somewhere to store nonces + store.getBytes("nonces", buffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE);// Get them to the store + state = node.setBufferNonces(buffer); // Send them to LoRaWAN + debug(state != RADIOLIB_ERR_NONE, F("Restoring nonces buffer failed"), state, false); + } + + // Recall session from RTC deep-sleep preserved variable + state = node.setBufferSession(LWsession); // Send them to LoRaWAN stack + // If we have booted at least once we should have a session to restore, so report any failure + // Otherwise no point saying there's been a failure when it was bound to fail with an empty + // LWsession var. At this point, bootCount has already been incremented, hence the > 2 + debug((state != RADIOLIB_ERR_NONE) && (bootCount > 2), F("Restoring session buffer failed"), state, false); + + // Process the restored session or failing that, create a new one & + // return flag to indicate a fresh join is required + Serial.println(F("Setup LoRaWAN session")); + state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, false); + // See comment above, no need to report a failure that is bound to occur on first boot + debug((state != RADIOLIB_ERR_NONE) && (bootCount > 2), F("Restore session failed"), state, false); + + // Loop until successful join + while (state != RADIOLIB_ERR_NONE) { + Serial.println(F("Join ('login') to the LoRaWAN Network")); + state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, true); + + if (state < RADIOLIB_ERR_NONE) { + Serial.print(F("Join failed: ")); + Serial.println(state); + + // How long to wait before join attenpts. This is an interim solution pending + // implementation of TS001 LoRaWAN Specification section #7 - this doc applies to v1.0.4 & v1.1 + // It sleeps for longer & longer durations to give time for any gateway issues to resolve + // or whatever is interfering with the device <-> gateway airwaves. + uint32_t sleepForSeconds = min((bootCountSinceUnsuccessfulJoin++ + 1UL) * 60UL, 3UL * 60UL); + Serial.print(F("Boots since unsuccessful join: ")); + Serial.println(bootCountSinceUnsuccessfulJoin); + Serial.print(F("Retrying join in ")); + Serial.print(sleepForSeconds); + Serial.println(F(" seconds")); + + gotoSleep(sleepForSeconds); + + } else { // Join was successful + Serial.println(F("Joined")); + + // ##### Save the join counters (nonces) to permanent store + Serial.println(F("Saving nonces to flash")); + uint8_t buffer[RADIOLIB_LORAWAN_NONCES_BUF_SIZE]; // Create somewhere to store nonces + uint8_t *persist = node.getBufferNonces(); // Get pointer to nonces + memcpy(buffer, persist, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); // Copy in to buffer + store.putBytes("nonces", buffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); // Send them to the store + + // We'll save the session after the uplink + + // Reset the failed join count + bootCountSinceUnsuccessfulJoin = 0; + + delay(1000); // Hold off off hitting the airwaves again too soon - an issue in the US + + } // if beginOTAA state + } // while join + + // ##### Close the store + store.end(); + + + // ----- And now for the main event ----- + Serial.println(F("Sending uplink")); + + // Read some inputs + uint8_t Digital2 = digitalRead(2); + uint16_t Analog1 = analogRead(A1); + + // Build payload byte array + uint8_t uplinkPayload[3]; + uplinkPayload[0] = Digital2; + uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(Analog1); + + // Perform an uplink + state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); + debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); + + Serial.print(F("FcntUp: ")); + Serial.println(node.getFcntUp()); + + // Now save session to RTC memory + uint8_t *persist = node.getBufferSession(); + memcpy(LWsession, persist, RADIOLIB_LORAWAN_SESSION_BUF_SIZE); + + // Wait until next uplink - observing legal & TTN FUP constraints + gotoSleep(uplinkIntervalSeconds); + +} + + +// The ESP32 wakes from deep-sleep and starts from the very beginning +// which is a very good place to start, as any singing nun knows. +// It then goes back to sleep, so loop() is never called and which is +// why it is empty. + +void loop() {} + diff --git a/examples/LoRaWAN/LoRaWAN_ESP32/config.h b/examples/LoRaWAN/LoRaWAN_ESP32/config.h new file mode 100644 index 000000000..bba38e4fe --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_ESP32/config.h @@ -0,0 +1,130 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +#include + +// How often to send an uplink - consider legal & FUP constraints - see notes +const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds + +// JoinEUI - previous versions of LoRaWAN called this AppEUI +// for development purposes you can use all zeros - see wiki for details +#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000 + +// The Device EUI & two keys can be generated on the TTN console +#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI +#define RADIOLIB_LORAWAN_DEV_EUI 0x--------------- +#endif +#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key +#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif +#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here +#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif + +// For the curious, the #ifndef blocks allow for automated testing &/or you can +// put your EUI & keys in to your platformio.ini - see wiki for more tips + + + +// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500 +const LoRaWANBand_t Region = EU868; +const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 + + +// ============================================================================ +// Below is to support the sketch - only make changes if the notes say so ... + +// Auto select MCU <-> radio connections +// If you get an error message when compiling, it may be that the +// pinmap could not be determined - see the notes for more info + +// Adafruit +#if defined(ARDUINO_SAMD_FEATHER_M0) + #pragma message ("Adafruit Feather M0 with RFM95") + #pragma message ("Link required on board") + SX1276 radio = new Module(8, 3, 4, 6); + + +// LilyGo +#elif defined(ARDUINO_TTGO_LORA32_V1) + #pragma message ("TTGO LoRa32 v1 - no Display") + SX1276 radio = new Module(18, 26, 14, 33); + +#elif defined(ARDUINO_TTGO_LORA32_V2) + #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") + +#elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 14, 33); + +#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1262) + #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") + +#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 23, 33); + + +// Heltec +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32) + #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") + +#elif defined(ARDUINO_heltec_wifi_kit_32_V2) + #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined(ARDUINO_heltec_wifi_kit_32_V3) + #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") + SX1262 radio = new Module(8, 14, 12, 13); + +#elif defined(ARDUINO_CUBECELL_BOARD) + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); + +#elif defined(ARDUINO_CUBECELL_BOARD_V2) + #pragma error ("ARDUINO_CUBECELL_BOARD_V2 awaiting pin map") + + +#else + #pragma message ("Unknown board - no automagic pinmap available") + + // SX1262 pin order: Module(NSS/CS, DIO1, RESET, BUSY); + // SX1262 radio = new Module(8, 14, 12, 13); + + // SX1278 pin order: Module(NSS/CS, DIO0, RESET, DIO1); + // SX1278 radio = new Module(10, 2, 9, 3); + +#endif + + +// Copy over the EUI's & keys in to the something that will not compile if incorrectly formatted +uint64_t joinEUI = RADIOLIB_LORAWAN_JOIN_EUI; +uint64_t devEUI = RADIOLIB_LORAWAN_DEV_EUI; +uint8_t appKey[] = { RADIOLIB_LORAWAN_APP_KEY }; +uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY }; + +// Create the LoRaWAN node +LoRaWANNode node(&radio, &Region, subBand); + + +// Helper function to display any issues +void debug(bool isFail, const __FlashStringHelper* message, int state, bool Freeze) { + if (isFail) { + Serial.print(message); + Serial.print("("); + Serial.print(state); + Serial.println(")"); + while (Freeze); + } +} + +// Helper function to display a byte array +void arrayDump(uint8_t *buffer, uint16_t len) { + for (uint16_t c; c < len; c++) { + Serial.printf("%02X", buffer[c]); + } + Serial.println(); +} + + +#endif diff --git a/examples/LoRaWAN/LoRaWAN_Reference/config.h b/examples/LoRaWAN/LoRaWAN_Reference/config.h new file mode 100644 index 000000000..bba38e4fe --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_Reference/config.h @@ -0,0 +1,130 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +#include + +// How often to send an uplink - consider legal & FUP constraints - see notes +const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds + +// JoinEUI - previous versions of LoRaWAN called this AppEUI +// for development purposes you can use all zeros - see wiki for details +#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000 + +// The Device EUI & two keys can be generated on the TTN console +#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI +#define RADIOLIB_LORAWAN_DEV_EUI 0x--------------- +#endif +#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key +#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif +#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here +#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif + +// For the curious, the #ifndef blocks allow for automated testing &/or you can +// put your EUI & keys in to your platformio.ini - see wiki for more tips + + + +// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500 +const LoRaWANBand_t Region = EU868; +const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 + + +// ============================================================================ +// Below is to support the sketch - only make changes if the notes say so ... + +// Auto select MCU <-> radio connections +// If you get an error message when compiling, it may be that the +// pinmap could not be determined - see the notes for more info + +// Adafruit +#if defined(ARDUINO_SAMD_FEATHER_M0) + #pragma message ("Adafruit Feather M0 with RFM95") + #pragma message ("Link required on board") + SX1276 radio = new Module(8, 3, 4, 6); + + +// LilyGo +#elif defined(ARDUINO_TTGO_LORA32_V1) + #pragma message ("TTGO LoRa32 v1 - no Display") + SX1276 radio = new Module(18, 26, 14, 33); + +#elif defined(ARDUINO_TTGO_LORA32_V2) + #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") + +#elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 14, 33); + +#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1262) + #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") + +#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 23, 33); + + +// Heltec +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32) + #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") + +#elif defined(ARDUINO_heltec_wifi_kit_32_V2) + #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined(ARDUINO_heltec_wifi_kit_32_V3) + #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") + SX1262 radio = new Module(8, 14, 12, 13); + +#elif defined(ARDUINO_CUBECELL_BOARD) + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); + +#elif defined(ARDUINO_CUBECELL_BOARD_V2) + #pragma error ("ARDUINO_CUBECELL_BOARD_V2 awaiting pin map") + + +#else + #pragma message ("Unknown board - no automagic pinmap available") + + // SX1262 pin order: Module(NSS/CS, DIO1, RESET, BUSY); + // SX1262 radio = new Module(8, 14, 12, 13); + + // SX1278 pin order: Module(NSS/CS, DIO0, RESET, DIO1); + // SX1278 radio = new Module(10, 2, 9, 3); + +#endif + + +// Copy over the EUI's & keys in to the something that will not compile if incorrectly formatted +uint64_t joinEUI = RADIOLIB_LORAWAN_JOIN_EUI; +uint64_t devEUI = RADIOLIB_LORAWAN_DEV_EUI; +uint8_t appKey[] = { RADIOLIB_LORAWAN_APP_KEY }; +uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY }; + +// Create the LoRaWAN node +LoRaWANNode node(&radio, &Region, subBand); + + +// Helper function to display any issues +void debug(bool isFail, const __FlashStringHelper* message, int state, bool Freeze) { + if (isFail) { + Serial.print(message); + Serial.print("("); + Serial.print(state); + Serial.println(")"); + while (Freeze); + } +} + +// Helper function to display a byte array +void arrayDump(uint8_t *buffer, uint16_t len) { + for (uint16_t c; c < len; c++) { + Serial.printf("%02X", buffer[c]); + } + Serial.println(); +} + + +#endif diff --git a/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino b/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino new file mode 100644 index 000000000..4ccc372ec --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino @@ -0,0 +1,41 @@ +#include + +#include "config.h" + +void setup() { + Serial.begin(115200); + while (!Serial); + Serial.println(F("\nSetup ... ")); + + Serial.println(F("Initalise the radio")); + int state = radio.begin(); + debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); + + Serial.println(F("Join ('login') to the LoRaWAN Network")); + state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, true); + debug(state < RADIOLIB_ERR_NONE, F("Join failed"), state, true); + + Serial.println(F("Ready!\n")); +} + + +void loop() { + Serial.println(F("Sending uplink")); + + // Read some inputs + uint8_t Digital1 = digitalRead(2); + uint16_t Analog1 = analogRead(A0); + + // Build payload byte array + uint8_t uplinkPayload[3]; + uplinkPayload[0] = Digital1; + uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(Analog1); + + // Perform an uplink + int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); + debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); + + // Wait until next uplink - observing legal & TTN FUP constraints + delay(uplinkIntervalSeconds * 1000UL); +} diff --git a/examples/LoRaWAN/LoRaWAN_Starter/config.h b/examples/LoRaWAN/LoRaWAN_Starter/config.h new file mode 100644 index 000000000..bba38e4fe --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_Starter/config.h @@ -0,0 +1,130 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +#include + +// How often to send an uplink - consider legal & FUP constraints - see notes +const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds + +// JoinEUI - previous versions of LoRaWAN called this AppEUI +// for development purposes you can use all zeros - see wiki for details +#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000 + +// The Device EUI & two keys can be generated on the TTN console +#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI +#define RADIOLIB_LORAWAN_DEV_EUI 0x--------------- +#endif +#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key +#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif +#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here +#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif + +// For the curious, the #ifndef blocks allow for automated testing &/or you can +// put your EUI & keys in to your platformio.ini - see wiki for more tips + + + +// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500 +const LoRaWANBand_t Region = EU868; +const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 + + +// ============================================================================ +// Below is to support the sketch - only make changes if the notes say so ... + +// Auto select MCU <-> radio connections +// If you get an error message when compiling, it may be that the +// pinmap could not be determined - see the notes for more info + +// Adafruit +#if defined(ARDUINO_SAMD_FEATHER_M0) + #pragma message ("Adafruit Feather M0 with RFM95") + #pragma message ("Link required on board") + SX1276 radio = new Module(8, 3, 4, 6); + + +// LilyGo +#elif defined(ARDUINO_TTGO_LORA32_V1) + #pragma message ("TTGO LoRa32 v1 - no Display") + SX1276 radio = new Module(18, 26, 14, 33); + +#elif defined(ARDUINO_TTGO_LORA32_V2) + #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") + +#elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 14, 33); + +#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1262) + #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") + +#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 23, 33); + + +// Heltec +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32) + #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") + +#elif defined(ARDUINO_heltec_wifi_kit_32_V2) + #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined(ARDUINO_heltec_wifi_kit_32_V3) + #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") + SX1262 radio = new Module(8, 14, 12, 13); + +#elif defined(ARDUINO_CUBECELL_BOARD) + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); + +#elif defined(ARDUINO_CUBECELL_BOARD_V2) + #pragma error ("ARDUINO_CUBECELL_BOARD_V2 awaiting pin map") + + +#else + #pragma message ("Unknown board - no automagic pinmap available") + + // SX1262 pin order: Module(NSS/CS, DIO1, RESET, BUSY); + // SX1262 radio = new Module(8, 14, 12, 13); + + // SX1278 pin order: Module(NSS/CS, DIO0, RESET, DIO1); + // SX1278 radio = new Module(10, 2, 9, 3); + +#endif + + +// Copy over the EUI's & keys in to the something that will not compile if incorrectly formatted +uint64_t joinEUI = RADIOLIB_LORAWAN_JOIN_EUI; +uint64_t devEUI = RADIOLIB_LORAWAN_DEV_EUI; +uint8_t appKey[] = { RADIOLIB_LORAWAN_APP_KEY }; +uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY }; + +// Create the LoRaWAN node +LoRaWANNode node(&radio, &Region, subBand); + + +// Helper function to display any issues +void debug(bool isFail, const __FlashStringHelper* message, int state, bool Freeze) { + if (isFail) { + Serial.print(message); + Serial.print("("); + Serial.print(state); + Serial.println(")"); + while (Freeze); + } +} + +// Helper function to display a byte array +void arrayDump(uint8_t *buffer, uint16_t len) { + for (uint16_t c; c < len; c++) { + Serial.printf("%02X", buffer[c]); + } + Serial.println(); +} + + +#endif diff --git a/examples/LoRaWAN/LoRaWAN_Starter/notes.md b/examples/LoRaWAN/LoRaWAN_Starter/notes.md new file mode 100644 index 000000000..4f56a617a --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_Starter/notes.md @@ -0,0 +1,202 @@ + + +# RadioLib LoRaWAN on TTN starter script + +## Welcome + +These notes are for someone who has successfully created a few sketches for their Arduino based device but is starting out with LoRaWAN. You don't have to be a C coding ninja but some familarity with C and procedural programming is assumed. The absolutely simplest way to get started is to buy some known good hardware that's all done for you so you can concentrate on the code & configuration. + + +## Introduction + +LoRaWAN is an amazing system for small battery powered sensors collecting data for years at a time. With great features comes some more complex elements which means it is not quite as simple as just providing WiFi credentials and pushing data through. It is in the range of setting up & customising the settings for a home router but with no wizards to do the heavy lifting for you. So we strongly recommend spending a couple of hours reviewing the TTN Getting Started section so you are aware of the minimum knowledge to make a successful start: https://www.thethingsnetwork.org/docs/lorawan/. Johan's video is amazing but is also drinking from the firehose. Read the text first and then watch the video on Youtube where there are bookmarks to deliver it in small digestable chunks. + +These notes plus a lot more are available in the wiki: https://github.com/jgromes/RadioLib/wiki/LoRaWAN + +For questions about using RadioLib there is the discussions section (https://github.com/jgromes/RadioLib/discussions) and if you believe you've found an issue (aka bug), the issues section (https://github.com/jgromes/RadioLib/issues). If posting an issue please ensure you tell us what hardware you are using and provide a debug log - please do not use verbose mode unless asked to. If the question is more LoRaWAN or firmware related, then you can use the TTN forum: https://www.thethingsnetwork.org/forum/ + + +## Register & setup on TTN + +This sketch isn't particularly aimed at The Things Stack (TTS) but you can get a free Sandbox account and the following instructions are for that. Helium does not support LoRaWAN v1.1 which is the version implemented by RadioLib. Chirpstack & other LoRaWAN Network Server (LNS) stacks have not yet been tried so YMMV. + +Why no screen shots? TTS is a web based app, one that you will need to become familiar with and we will need to direct you to some of the less obvious parts. So much better that you learn the layouts in concept than slavishly follow screen shots that can & will go stale. + +There will be some instructions that you have to take on face value. You didn't learn to run before you walked and it's so much more encouraging to get started and build on success than get bogged down in endless details. Once you are up & running more of the details start to slot in to place. + +### Register on TTN + +Go to https://www.thethingsnetwork.org/get-started and register - just like any other website. These instructions are for TTS Sandbox. + +Once you have confirmed your email address, you can login to the console here: https://console.cloud.thethings.network/. If you allow your browser to share you location the best console will be selected. For most users the best one is the obvious one, if you have any doubts you can ask on the forum here: https://www.thethingsnetwork.org/forum/ - you login with the exact same details. + +It is simpler to register your gateway first. If you don't have a gateway, then a The Things Indoor Gateway (TTIG) is a very affordable option. A gateway gives you a console to see if your device is being heard and is hugely useful when debugging a DIY device. If you are in range of a community gateway you may be lucky with your first device creation but you will never know if you are in range unless you have access to that gateways console. + +You can read up on key concepts and troubleshooting here: https://www.thethingsindustries.com/docs/gateways/ + +LoRa stands for Long Range - having the gateway & device on the same desk tends to overload both receiver circuits when they hear a transmission so close to hand. The gateway should be 5 - 10m away, preferably with a solid wall in the way as well. + +### Create your application + +An application is like a box to keep some devices in - normally doing the same thing - on larger deployments this may be 1,000's of similar devices. Starting out it it is likely to be just a few so there is no need to get concerned about how to divide up your use just yet. + +Onced logged in to the console you can go in to Applications to create your first application. The ID must be all lower case or numbers, no spaces, dashes are OK and it has to be unique to the entire TTN community - so `first-app` will be rejected - you could use `your-username-first-app` as that's likely to be unique. The name and description are for your own use and are optional. + +The main menu for an application is in the left hand panel - nothing is needed there just yet. + +### Create your device + +On the right hand side about half way down on your application's summary is a big blue button `+ Register end device`. Click this to create the settings for your first device. + +You are making your own device using a third party LoRaWAN stack so there will not be an entry in the device repository so choose 'Enter end device specifics manually'. + +Choose the Frequency plan appropriate for your region. Consider that almost all countries have laws relating to what frequencies you use so don't get creative. For Europe please use the recommended option. For other regions use the entry marked 'used by TTN'. + +Choose LoRaWAN 1.1.0 - the last one in the list - the latest specfication. RadioLib uses RP001 Regional Parameters 1.1 revision B. + +At this point you will be asked for your JoinEUI. As this is a DIY device and we are using RadioLib, you can use all zero's as recommended by The LoRa Alliance TR007 Technical Recommendations document. Once you've put in all zeros and clicked confirm you will be asked for a DevEUI, AppKey and NwkKey. It is preferable to have the cosole generate them so they are properly formatted. + +Your End device ID can be changed to make the device more identifiable. Something related to your hardware helps - like devicename-01. The you can click the blue 'Register device'. + +When many sensors are big deployed, a device is registered, batteries put in, it joins and gets on with sending data for the next few years. For development purposes we need to turn off one of the security settings so that you can join & uplink out of the normal sequence that a device in the field would do. + +Click on General Settings, scroll down to Join settings, click the Expand button, scroll down and click the Resets join nonces option. You will see a warning about replay attacks which is entirely proper & correct. If anyone evesdropping in your area on your LoRa transmissions could fake a join and send uplinks from their device but only if they happened to find out your AppKey & NwkKey which is kept securely on the TTN servers and is never transmitted over the air, so they'd also have to login to your account, which is protected by your password. + +You then need to copy over the device details in to the config file for RadioLib. There are buttons to copy items to the clipboard so you don't have to hand type them. + +### Copy & Paste made easy + +You can copy the EUIs & keys from the device overview section. + +The EUIs are really straightforward - click the clipboard icon at the right hand end of the EUI display field and it will be copied in the format you need. You can then paste it in to the code - you must leave the 0x in place so the compiler knows that it's a hex value. + +The keys are relatively straightforward. Click the eye icon at the right hand end of the field. Then click the <> icon that will appear to the left. This will format the hex values as an array. Then you can click the clipboard icon to copy the array and then paste it between the { } brackets. + +### Secrets to keep safe. + +The Join & Dev EUI's are transmitted in plain text when the device joins a network. The gateway ID is public. If you have an issue and are asked for details, there are only three things to keep private - your password, the keys which are used for encryption and any API keys you create which are used for accessing your data & configuration. + + +### Monitoring your device + +If you are on your application summary page you'll see uplinks in the small activity box top right with a link to the full size table. If you click the Live Data menu item on the left it will show activity for all the devices registered on the application in the full window. + +If you just want your devices activity, from the summary page click on the device in the list in the middle of the page. + +The main menu for a device is the horizontal band: Overview, Live Data, Messaging etc. You can click Live Data or the link above the small activity box. + +**The console shows LIVE data - not a history of everything that has ever happened. A LNS is a management & relay service, not a database. When you open the console you may see a summary of recent activity - this is a bonus. You must leave the console open, even in another tab, if you want to see live activity.** + + +### Explore + +Nothing on the console can be upset unless you confirm a warning message, so you are safe to explore the different menus to orientate yourself. This is very good idea so you are have an understanding of the layout of the land and shouldn't take more than 10 or 15 minutes. The documentation & volunteers on GitHub and the TTN forum will make refer to parts of the console without giving blow by blow directions. + + + + +## The config.h + +### The uplinkInterval + +LoRaWAN devices typically send small amounts of data at intervals between 15 minutes through to once per day. This allows a device to run on two AA batteries for 2 to 5 years. Hoping that LoRaWAN can move lots of data and your device can regularly receive commands to do something on demand is trying to bend the LoRaWAN system in ways it is not designed for and usually ends up with far too many issues to unravel. + +The radio frequencies that are used are usually shared with other Industrial, Scientific & Medical, known as ISM, users. The LoRa modulation is particularly resistance to interference due to other simultaneous transmissions on the same frequency but too much local activity will mean that not all uplinks get through. The Things Industries suggest designing a system to a potential packet loss rate of 10%. Typically we see 1 or 2% loss. This is entirely down to shared use of the radio waves, once an uplink is heard by a gateway the system is super reliable through The Things Stack. + +To ensure that the shared ISM bands are fairly used there are limits defined in law on how often you can transmit, called Duty Cycle. The details vary by region or country but typically you can only transmit for 1% of the time. Some frequencies you can only use 0.1% of the time. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/ for more information. + +Additionally, as The Things Stack Sandbox aka TTN is an array of servers in three locations around the world paid for by The Things Industries, there is a Fair Use Policy so that those learning LoRaWAN, communities, hobbyists & makers are guided on how much of the resource any one device can use. In short, it's 30 seconds of airtime a day and 10 downlinks. When a gateway is transmitting a downlink it can not hear any uplinks (contributing to the potential uplink loss outlined above). The community concensus is that 1 downlink a fortnight to update or adjust settings is appropriate. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/#fair-use-policy for more information. + +You can see what intervals can be used with this interactive calculator: https://avbentem.github.io/airtime-calculator/ttn/. Devices further away from gateways will have to use a higher Spread Factor to be heard - do not assume everything will happen at SF7. A uplink takes a minimum of 6 seconds from start to end, sometimes longer if the device is further away from the gateway, so + +With all thes considerations, trying to use LoRaWAN for command & control isn't appropriate and realtime GPS tracking almost always breaches FUP and usually legal limits, leaving aside the challenges of coverage. + +See the hints & tips section on testing your device. + + +### EUI's & Keys + +In the config.h towards the top there are four lines thus: + +// replace-with-your-device-id +uint64_t joinEUI = 0x0000000000000000; +uint64_t devEUI = 0x0000000000000000; +uint8_t appKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +uint8_t nwkKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +On the TTN console on the device summary page, click the clipboard icon next to the DevEUI, highlight the 16 0's in the third line after the x and paste. + +The devEUI must start with 0x and will end up looking something like 0x70B3D57ED006544E + +For the appKey we need TTN to format it correctly. Click the eye icon and an extra icon will appear <> - click this and the key will be formatted for you. Click the clipboard icon and then paste over the 32 0x00's in the config file. Then do the same for nwkKey. + +A key will end up something like 0x31, 0x16, 0x6A, 0x22, 0x97, 0x52, 0xB6, 0x34, 0x57, 0x45, 0x1B, 0xC3, 0xC9, 0xD8, 0x83, 0xE8 + + +### Region + +The region value you use MUST match the one you selected on the console. + +If you are using US915 or AU915 then you should change the subBand const to 2. + +### The pinmap + +This is the connections between the MCU (ESP32/ATmega/SAMD) and the LoRa radio (SX1276/SX1262). + +Prebuilt modules are easy - we can detect the board and setup the pinmap for you. These boards are: + +* TTGO_LoRa32 +* TTGO_LoRa32_V1 +* TTGO_LORA32_V2 +* TTGO_LORA32_v21NEW +* HELTEC_WIFI_LORA_32 +* HELTEC_WIFI_LORA_32_V2 +* HELTEC_WIFI_LORA_32_V3 +* CUBECELL_BOARD + +If you have a TTGO T-Beam, you must choose the correct radio from the Board Revision sub-menu found under the main Tools menu. + +* TBEAM_USE_RADIO_SX1262 +* TBEAM_USE_RADIO_SX1276 + +If you have an Adafruit Feather M0 with RFM95 then you must solder a wire or use a jumper to link from pin 6 to io1: https://learn.adafruit.com/the-things-network-for-feather/arduino-wiring + + +If you have a module that's not on this list, please go to the "Pinmap How-To" below. + + + +## Observations on the main sketch + +Most of the sketch has comments that tell you what the various parts are doing. This should add a little more info: + +### The Join + +When a device is first started, it needs to register with the LoRaWAN Network Server (LNS) and setup it's session. With the settings from the console copied over and a gateway an appropriate distance away, most of the time the join will 'just work'. + +If it doesn't, then there is no point trying repeatedly without going through the troubleshootng sequence. So this starter sketch will try once only to save the airwaves & TTN Community servers from repeated misfires. + + +### The payload + +You may see other starter sketches sending text. Apart from being massively inefficient, the text isn't easily displayed on the TTN console which makes it rather pointless and pro embedded engineers don't send strings. So this sketch sends the data as a sequence of bytes as recommended. + +Further reading on this can be found here, just ignore the pink message about v2, it's all still valid: https://www.thethingsnetwork.org/docs/devices/bytes/ + +We've not assumed anything about any sensors you have, so we are just reading a digital & an analog pin. An analog reading is typically a two byte value - an integer - this is split using the Arduino highByte & lowByte function. You'll see how we put it back together in the TTN console below. + + +## TTN Console Payload Decoder + +Coming soon + +## Hints & Tips + +### Device testing + +The LoRaWAN code base works to a specification and once you are happy your device is able to join & send a few dozen uplinks, continuing to sit around waiting for an uplink to test your sensor code & payload format is a waste of your time. The solution is to write everything else in a different sketch, output the array to the serial console and then you can copy & paste the hex array in to the TTN console Payload Formatters section to test the decoding. + + +## Pinmap How-To + + From 5bc97550ecf976c9e3146b4c26bd1a5312f27e04 Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Sat, 23 Mar 2024 17:03:21 +0000 Subject: [PATCH 07/15] Clean up prior named directories plus neglected Reference is included --- .../LoRaWAN_ABP.ino} | 0 examples/LoRaWAN/LoRaWAN_ABP/configABP.h | 130 +++++++++++ .../LoRaWAN_End_Device/LoRaWAN_End_Device.ino | 161 -------------- .../LoRaWAN_Reference.ino} | 167 +++++---------- examples/LoRaWAN/Starter/Starter.ino | 41 ---- examples/LoRaWAN/Starter/config.h | 85 -------- examples/LoRaWAN/Starter/notes.md | 202 ------------------ 7 files changed, 185 insertions(+), 601 deletions(-) rename examples/LoRaWAN/{LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino => LoRaWAN_ABP/LoRaWAN_ABP.ino} (100%) create mode 100644 examples/LoRaWAN/LoRaWAN_ABP/configABP.h delete mode 100644 examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino rename examples/LoRaWAN/{LoRaWAN_End_Device_Reference/LoRaWAN_End_Device_Reference.ino => LoRaWAN_Reference/LoRaWAN_Reference.ino} (52%) delete mode 100644 examples/LoRaWAN/Starter/Starter.ino delete mode 100644 examples/LoRaWAN/Starter/config.h delete mode 100644 examples/LoRaWAN/Starter/notes.md diff --git a/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino b/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino similarity index 100% rename from examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino rename to examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino diff --git a/examples/LoRaWAN/LoRaWAN_ABP/configABP.h b/examples/LoRaWAN/LoRaWAN_ABP/configABP.h new file mode 100644 index 000000000..bba38e4fe --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_ABP/configABP.h @@ -0,0 +1,130 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +#include + +// How often to send an uplink - consider legal & FUP constraints - see notes +const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds + +// JoinEUI - previous versions of LoRaWAN called this AppEUI +// for development purposes you can use all zeros - see wiki for details +#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000 + +// The Device EUI & two keys can be generated on the TTN console +#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI +#define RADIOLIB_LORAWAN_DEV_EUI 0x--------------- +#endif +#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key +#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif +#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here +#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif + +// For the curious, the #ifndef blocks allow for automated testing &/or you can +// put your EUI & keys in to your platformio.ini - see wiki for more tips + + + +// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500 +const LoRaWANBand_t Region = EU868; +const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 + + +// ============================================================================ +// Below is to support the sketch - only make changes if the notes say so ... + +// Auto select MCU <-> radio connections +// If you get an error message when compiling, it may be that the +// pinmap could not be determined - see the notes for more info + +// Adafruit +#if defined(ARDUINO_SAMD_FEATHER_M0) + #pragma message ("Adafruit Feather M0 with RFM95") + #pragma message ("Link required on board") + SX1276 radio = new Module(8, 3, 4, 6); + + +// LilyGo +#elif defined(ARDUINO_TTGO_LORA32_V1) + #pragma message ("TTGO LoRa32 v1 - no Display") + SX1276 radio = new Module(18, 26, 14, 33); + +#elif defined(ARDUINO_TTGO_LORA32_V2) + #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") + +#elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 14, 33); + +#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1262) + #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") + +#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1276 radio = new Module(18, 26, 23, 33); + + +// Heltec +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32) + #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") + +#elif defined(ARDUINO_heltec_wifi_kit_32_V2) + #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined(ARDUINO_heltec_wifi_kit_32_V3) + #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") + SX1262 radio = new Module(8, 14, 12, 13); + +#elif defined(ARDUINO_CUBECELL_BOARD) + #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); + +#elif defined(ARDUINO_CUBECELL_BOARD_V2) + #pragma error ("ARDUINO_CUBECELL_BOARD_V2 awaiting pin map") + + +#else + #pragma message ("Unknown board - no automagic pinmap available") + + // SX1262 pin order: Module(NSS/CS, DIO1, RESET, BUSY); + // SX1262 radio = new Module(8, 14, 12, 13); + + // SX1278 pin order: Module(NSS/CS, DIO0, RESET, DIO1); + // SX1278 radio = new Module(10, 2, 9, 3); + +#endif + + +// Copy over the EUI's & keys in to the something that will not compile if incorrectly formatted +uint64_t joinEUI = RADIOLIB_LORAWAN_JOIN_EUI; +uint64_t devEUI = RADIOLIB_LORAWAN_DEV_EUI; +uint8_t appKey[] = { RADIOLIB_LORAWAN_APP_KEY }; +uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY }; + +// Create the LoRaWAN node +LoRaWANNode node(&radio, &Region, subBand); + + +// Helper function to display any issues +void debug(bool isFail, const __FlashStringHelper* message, int state, bool Freeze) { + if (isFail) { + Serial.print(message); + Serial.print("("); + Serial.print(state); + Serial.println(")"); + while (Freeze); + } +} + +// Helper function to display a byte array +void arrayDump(uint8_t *buffer, uint16_t len) { + for (uint16_t c; c < len; c++) { + Serial.printf("%02X", buffer[c]); + } + Serial.println(); +} + + +#endif diff --git a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino deleted file mode 100644 index 9089a77c9..000000000 --- a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino +++ /dev/null @@ -1,161 +0,0 @@ -/* - RadioLib LoRaWAN End Device Example - - This example joins a LoRaWAN network and will send - uplink packets. Before you start, you will have to - register your device at https://www.thethingsnetwork.org/ - After your device is registered, you can run this example. - The device will join the network and start uploading data. - - 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 - - For full API reference, see the GitHub Pages - https://jgromes.github.io/RadioLib/ - - For LoRaWAN details, see the wiki page - https://github.com/jgromes/RadioLib/wiki/LoRaWAN - - - Last updated 1st March 2024 for RadioLib 6.4.2 - -*/ - -// 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 radio (SX1262 / SX1278 / ... ) with default settings - Serial.print(F("[Radio] 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); - } - - // JoinEUI - previous versions of LoRaWAN called this AppEUI - // for development purposes you can use all zeros - see wiki for details - uint64_t joinEUI = 0x0000000000000000; - - // DevEUI - The device's Extended Unique Identifier - // TTN will generate one for you - uint64_t devEUI = 0x----------------; - - // encryption keys used to secure the communication - // TTN will generate them for you - // see wiki for details on copying & pasting them - uint8_t nwkKey[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, - 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; - uint8_t appKey[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, - 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; - - - // Manages uplink intervals to the TTN Fair Use Policy - node.setDutyCycle(true, 1250); - - // Begin the join to the network - Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... ")); - state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); - 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); - } -} // setup - -// 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! " + 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("")); - - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - } - - // on boards that can save to Flash or EEPROM this saves the session - // which allows recall of the session after reboot or deepsleep - node.saveSession(); - - // wait before sending another packet - uint32_t minimumDelay = 300000; // try to send once every 3 minutes - uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per FUP & law!) - uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows - - Serial.print(F("[LoRaWAN] Next uplink in ")); - Serial.print(delayMs/60); - Serial.println(F("s")); - - delay(delayMs); -} // loop diff --git a/examples/LoRaWAN/LoRaWAN_End_Device_Reference/LoRaWAN_End_Device_Reference.ino b/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino similarity index 52% rename from examples/LoRaWAN/LoRaWAN_End_Device_Reference/LoRaWAN_End_Device_Reference.ino rename to examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino index 8e50b7a50..5df3114e8 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device_Reference/LoRaWAN_End_Device_Reference.ino +++ b/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino @@ -27,99 +27,54 @@ https://github.com/jgromes/RadioLib/wiki/LoRaWAN - Last updated 1st March 2024 for RadioLib 6.4.2 - */ +#include "config.h" + // 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 radio (SX1262 / SX1278 / ... ) with default settings - Serial.print(F("[Radio] 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); - } - - // JoinEUI - previous versions of LoRaWAN this was AppEUI - // for development purposes you can use all zeros - see wiki for details - uint64_t joinEUI = 0x0000000000000000; - - // DevEUI - The device's Extended Unique Identifier - // TTN will generate one for you - uint64_t devEUI = 0x----------------; + Serial.begin(115200); + while (!Serial); // Wait for serial to be initalised + delay(2000); // Give time to switch to the serial monitor + Serial.println(F("\nSetup")); - // encryption keys used to secure the communication - // TTN will generate them for you - // see wiki for details on copying & pasting them - uint8_t nwkKey[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, - 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; - uint8_t appKey[] = { 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, - 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- }; + int16_t state = 0; // return value for calls to RadioLib + Serial.println(F("Initalise the radio")); + state = radio.begin(); + debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); // Override the default join rate - uint8_t joinDR = 3; - - // Begin the join to the network - Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... ")); - state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDR); - 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); - } + // uint8_t joinDR = 3; + Serial.println(F("Join ('login') to the LoRaWAN Network")); + state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, true); + debug(state < RADIOLIB_ERR_NONE, F("Join failed"), state, true); + + // Print the DevAddr Serial.print("[LoRaWAN] DevAddr: "); Serial.println(node.getDevAddr(), HEX); - // disable the ADR algorithm (on by default which is preferable) + // Disable the ADR algorithm (on by default which is preferable) node.setADR(false); - // set a fixed datarate & make it persistent (not normal) - node.setDatarate(5, true); + // Set a fixed datarate & make it persistent (not normal) + node.setDatarate(4); - // enable CSMA which tries to minimize packet loss by searching + // Enable CSMA which tries to minimize packet loss by searching // for a free channel before actually sending an uplink node.setCSMA(6, 2, true); - // manages uplink intervals to the TTN Fair Use Policy - node.setDutyCycle(true, 1250); + // Manages uplink intervals to the TTN Fair Use Policy + node.setDutyCycle(true, 1250); - // enable the dwell time limits - 400ms is the limit for the US + // Enable the dwell time limits - 400ms is the limit for the US node.setDwellTime(true, 400); + Serial.println(F("Ready!\n")); } // setup @@ -135,48 +90,46 @@ void loop() { uint8_t battLevel = 146; node.setDeviceStatus(battLevel); - // retrieve the last uplink frame counter - uint32_t fcntUp = node.getFcntUp(); - Serial.print(F("[LoRaWAN] Sending uplink packet #")); - Serial.println(fcntUp); - String strUp = "Hello! " + String(fcntUp); + // Read some inputs + uint8_t Digital1 = digitalRead(2); + uint16_t Analog1 = analogRead(A0); + + // Build payload byte array + uint8_t uplinkPayload[3]; + uplinkPayload[0] = Digital1; + uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(Analog1); + + uint8_t downlinkPayload[10]; // Make sure this fits your plans! + size_t downlinkSize; // To hold the actual payload size rec'd + + // you can also retrieve additional information about an uplink or + // downlink by passing a reference to LoRaWANEvent_t structure + LoRaWANEvent_t uplinkDetails; + LoRaWANEvent_t downlinkDetails; - // send a confirmed uplink to port 10 every 64th frame + uint8_t Port = 10; + + // Retrieve the last uplink frame counter + uint32_t fcntUp = node.getFcntUp(); + // Send a confirmed uplink every 64th frame // and also request the LinkCheck and DeviceTime MAC commands if(fcntUp % 64 == 0) { - Serial.print(F("[LoRaWAN] Requesting LinkCheck and DeviceTime")); + Serial.println(F("[LoRaWAN] Requesting LinkCheck and DeviceTime")); node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_LINK_CHECK); node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_DEVICE_TIME); - state = node.uplink(strUp, 10, true); + state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload), Port, downlinkPayload, &downlinkSize, true, &uplinkDetails, &downlinkDetails); } else { - state = node.uplink(strUp, 10); + state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload), Port, downlinkPayload, &downlinkSize); } - if(state != RADIOLIB_ERR_NONE) { - Serial.print(F("failed, code ")); - Serial.println(state); - } - - // after uplink, you must call downlink() to receive any possible reply - // from the server. This function must be called before the Rx1 delay - // for the network. Typically this is 5s after end of uplink. - Serial.println(F("[LoRaWAN] Waiting for downlink ... ")); - String strDown; + debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); - // you can also retrieve additional information about an uplink or - // downlink by passing a reference to LoRaWANEvent_t structure - LoRaWANEvent_t downlinkDetails; - state = node.downlink(strDown, &downlinkDetails); if(state == RADIOLIB_ERR_NONE) { - // print data of the packet - Serial.print(F("[LoRaWAN] Data:\t\t")); - if(strDown.length() > 0) { - for (uint8_t c = 0; c < strDown.length(); c++) { - uint8_t value = strDown[c]; - if (value < 10) Serial.print(F("0")); - Serial.print(value, HEX); - } - Serial.println(); + // Did we get a downlink with data for us + if (downlinkSize > 0) { + Serial.println(F("Downlink data: ")); + arrayDump(downlinkPayload, downlinkSize); } else { Serial.println(F("")); } @@ -233,20 +186,10 @@ void loop() { Serial.println(fracSecond); } - } else if(state == RADIOLIB_ERR_RX_TIMEOUT) { - // Not really necessary to report normal operation - - } else { - Serial.print(F("failed, code ")); - Serial.println(state); } - // on boards that can save to Flash or EEPROM this saves the session - // which allows recall of the session after reboot or deepsleep - node.saveSession(); - // wait before sending another packet - uint32_t minimumDelay = 3 * 60 * 1000; // try to send once every 3 minutes + uint32_t minimumDelay = uplinkIntervalSeconds * 1000UL; uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per FUP & law!) uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows diff --git a/examples/LoRaWAN/Starter/Starter.ino b/examples/LoRaWAN/Starter/Starter.ino deleted file mode 100644 index ced6020ff..000000000 --- a/examples/LoRaWAN/Starter/Starter.ino +++ /dev/null @@ -1,41 +0,0 @@ -#include - -#include "config.h" - -void setup() { - Serial.begin(115200); - while (!Serial); - Serial.println(F("\nSetup ... ")); - - Serial.println(F("Initalise the radio")); - int state = radio.begin(); - debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); - - Serial.println(F("Join ('login') to the LoRaWAN Network")); - state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); - debug(state < RADIOLIB_ERR_NONE, F("Join failed"), state, true); - - Serial.println(F("Ready!\n")); -} - - -void loop() { - Serial.println(F("Sending uplink")); - - // Read some inputs - uint8_t Digital1 = digitalRead(2); - uint16_t Analog1 = analogRead(A0); - - // Build payload byte array - uint8_t uplinkPayload[3]; - uplinkPayload[0] = Digital1; - uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions - uplinkPayload[2] = lowByte(Analog1); - - // Perform an uplink - int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); - debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); - - // Wait until next uplink - observing legal & TTN FUP constraints - delay(uplinkInterval); -} diff --git a/examples/LoRaWAN/Starter/config.h b/examples/LoRaWAN/Starter/config.h deleted file mode 100644 index bc50acf65..000000000 --- a/examples/LoRaWAN/Starter/config.h +++ /dev/null @@ -1,85 +0,0 @@ - -#include - -// How often to send an uplink - consider legal & FUP constraints - see notes -uint32_t uplinkInterval = 3 * 60 * 1000; // minutes x seconds x milliseconds - -uint64_t joinEUI = 0x0000000000000000; -uint64_t devEUI = 0x0000000000000000; -uint8_t appKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -uint8_t nwkKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - - -// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500 -const LoRaWANBand_t Region = EU868; -const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 - - -// ============================================================================ -// Below is to support the sketch - only make changes if the notes say so ... - -// Auto select MCU <-> radio connections -// If you get an error message when compiling, it may be that the -// pinmap could not be determined - see the notes for more info - -#if defined(ARDUINO_TTGO_LORA32_V1) - #pragma message ("TTGO LoRa32 v1 - no Display") - SX1276 radio = new Module(18, 26, 14, 33); - -// #elif defined(ARDUINO_TTGO_LORA32_V2) -// #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") - -#elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") - SX1276 radio = new Module(18, 26, 14, 33); - -// #elif defined(ARDUINO_TBEAM_USE_RADIO_SX1262) -// #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") - -// #elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) -// #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") -// SX1276 radio = new Module(18, 26, 23, 33); - -// #elif defined(ARDUINO_HELTEC_WIFI_LORA_32) -// #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") - -#elif defined(ARDUINO_heltec_wifi_kit_32_V2) - #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") - SX1276 radio = new Module(18, 26, 14, 35); - -#elif defined(ARDUINO_heltec_wifi_kit_32_V3) - #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") - SX1262 radio = new Module(8, 14, 12, 13); - -// #elif defined(ARDUINO_CUBECELL_BOARD) -// #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") -// SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); - -// #elif defined(ARDUINO_CUBECELL_BOARD_V2) -// #pragma error ("ARDUINO_CUBECELL_BOARD_V2 awaiting pin map") - -#elif defined(ARDUINO_SAMD_FEATHER_M0) - #pragma message ("Adafruit Feather M0 with RFM95") - #pragma message ("Link required on board") - SX1276 radio = new Module(8, 3, 4, 6); - -#else - #pragma message ("Unknown board - no pinmap") - SX1262 radio = new Module(8, 14, 12, 13); - -#endif - -LoRaWANNode node(&radio, &Region, subBand); - - -// Helper function to display any issues -void debug(bool isFail, const __FlashStringHelper* message, int state, bool Freeze) { - if (isFail) { - Serial.print(message); - Serial.print("("); - Serial.print(state); - Serial.println(")"); - while (Freeze); - } -} diff --git a/examples/LoRaWAN/Starter/notes.md b/examples/LoRaWAN/Starter/notes.md deleted file mode 100644 index 4f56a617a..000000000 --- a/examples/LoRaWAN/Starter/notes.md +++ /dev/null @@ -1,202 +0,0 @@ - - -# RadioLib LoRaWAN on TTN starter script - -## Welcome - -These notes are for someone who has successfully created a few sketches for their Arduino based device but is starting out with LoRaWAN. You don't have to be a C coding ninja but some familarity with C and procedural programming is assumed. The absolutely simplest way to get started is to buy some known good hardware that's all done for you so you can concentrate on the code & configuration. - - -## Introduction - -LoRaWAN is an amazing system for small battery powered sensors collecting data for years at a time. With great features comes some more complex elements which means it is not quite as simple as just providing WiFi credentials and pushing data through. It is in the range of setting up & customising the settings for a home router but with no wizards to do the heavy lifting for you. So we strongly recommend spending a couple of hours reviewing the TTN Getting Started section so you are aware of the minimum knowledge to make a successful start: https://www.thethingsnetwork.org/docs/lorawan/. Johan's video is amazing but is also drinking from the firehose. Read the text first and then watch the video on Youtube where there are bookmarks to deliver it in small digestable chunks. - -These notes plus a lot more are available in the wiki: https://github.com/jgromes/RadioLib/wiki/LoRaWAN - -For questions about using RadioLib there is the discussions section (https://github.com/jgromes/RadioLib/discussions) and if you believe you've found an issue (aka bug), the issues section (https://github.com/jgromes/RadioLib/issues). If posting an issue please ensure you tell us what hardware you are using and provide a debug log - please do not use verbose mode unless asked to. If the question is more LoRaWAN or firmware related, then you can use the TTN forum: https://www.thethingsnetwork.org/forum/ - - -## Register & setup on TTN - -This sketch isn't particularly aimed at The Things Stack (TTS) but you can get a free Sandbox account and the following instructions are for that. Helium does not support LoRaWAN v1.1 which is the version implemented by RadioLib. Chirpstack & other LoRaWAN Network Server (LNS) stacks have not yet been tried so YMMV. - -Why no screen shots? TTS is a web based app, one that you will need to become familiar with and we will need to direct you to some of the less obvious parts. So much better that you learn the layouts in concept than slavishly follow screen shots that can & will go stale. - -There will be some instructions that you have to take on face value. You didn't learn to run before you walked and it's so much more encouraging to get started and build on success than get bogged down in endless details. Once you are up & running more of the details start to slot in to place. - -### Register on TTN - -Go to https://www.thethingsnetwork.org/get-started and register - just like any other website. These instructions are for TTS Sandbox. - -Once you have confirmed your email address, you can login to the console here: https://console.cloud.thethings.network/. If you allow your browser to share you location the best console will be selected. For most users the best one is the obvious one, if you have any doubts you can ask on the forum here: https://www.thethingsnetwork.org/forum/ - you login with the exact same details. - -It is simpler to register your gateway first. If you don't have a gateway, then a The Things Indoor Gateway (TTIG) is a very affordable option. A gateway gives you a console to see if your device is being heard and is hugely useful when debugging a DIY device. If you are in range of a community gateway you may be lucky with your first device creation but you will never know if you are in range unless you have access to that gateways console. - -You can read up on key concepts and troubleshooting here: https://www.thethingsindustries.com/docs/gateways/ - -LoRa stands for Long Range - having the gateway & device on the same desk tends to overload both receiver circuits when they hear a transmission so close to hand. The gateway should be 5 - 10m away, preferably with a solid wall in the way as well. - -### Create your application - -An application is like a box to keep some devices in - normally doing the same thing - on larger deployments this may be 1,000's of similar devices. Starting out it it is likely to be just a few so there is no need to get concerned about how to divide up your use just yet. - -Onced logged in to the console you can go in to Applications to create your first application. The ID must be all lower case or numbers, no spaces, dashes are OK and it has to be unique to the entire TTN community - so `first-app` will be rejected - you could use `your-username-first-app` as that's likely to be unique. The name and description are for your own use and are optional. - -The main menu for an application is in the left hand panel - nothing is needed there just yet. - -### Create your device - -On the right hand side about half way down on your application's summary is a big blue button `+ Register end device`. Click this to create the settings for your first device. - -You are making your own device using a third party LoRaWAN stack so there will not be an entry in the device repository so choose 'Enter end device specifics manually'. - -Choose the Frequency plan appropriate for your region. Consider that almost all countries have laws relating to what frequencies you use so don't get creative. For Europe please use the recommended option. For other regions use the entry marked 'used by TTN'. - -Choose LoRaWAN 1.1.0 - the last one in the list - the latest specfication. RadioLib uses RP001 Regional Parameters 1.1 revision B. - -At this point you will be asked for your JoinEUI. As this is a DIY device and we are using RadioLib, you can use all zero's as recommended by The LoRa Alliance TR007 Technical Recommendations document. Once you've put in all zeros and clicked confirm you will be asked for a DevEUI, AppKey and NwkKey. It is preferable to have the cosole generate them so they are properly formatted. - -Your End device ID can be changed to make the device more identifiable. Something related to your hardware helps - like devicename-01. The you can click the blue 'Register device'. - -When many sensors are big deployed, a device is registered, batteries put in, it joins and gets on with sending data for the next few years. For development purposes we need to turn off one of the security settings so that you can join & uplink out of the normal sequence that a device in the field would do. - -Click on General Settings, scroll down to Join settings, click the Expand button, scroll down and click the Resets join nonces option. You will see a warning about replay attacks which is entirely proper & correct. If anyone evesdropping in your area on your LoRa transmissions could fake a join and send uplinks from their device but only if they happened to find out your AppKey & NwkKey which is kept securely on the TTN servers and is never transmitted over the air, so they'd also have to login to your account, which is protected by your password. - -You then need to copy over the device details in to the config file for RadioLib. There are buttons to copy items to the clipboard so you don't have to hand type them. - -### Copy & Paste made easy - -You can copy the EUIs & keys from the device overview section. - -The EUIs are really straightforward - click the clipboard icon at the right hand end of the EUI display field and it will be copied in the format you need. You can then paste it in to the code - you must leave the 0x in place so the compiler knows that it's a hex value. - -The keys are relatively straightforward. Click the eye icon at the right hand end of the field. Then click the <> icon that will appear to the left. This will format the hex values as an array. Then you can click the clipboard icon to copy the array and then paste it between the { } brackets. - -### Secrets to keep safe. - -The Join & Dev EUI's are transmitted in plain text when the device joins a network. The gateway ID is public. If you have an issue and are asked for details, there are only three things to keep private - your password, the keys which are used for encryption and any API keys you create which are used for accessing your data & configuration. - - -### Monitoring your device - -If you are on your application summary page you'll see uplinks in the small activity box top right with a link to the full size table. If you click the Live Data menu item on the left it will show activity for all the devices registered on the application in the full window. - -If you just want your devices activity, from the summary page click on the device in the list in the middle of the page. - -The main menu for a device is the horizontal band: Overview, Live Data, Messaging etc. You can click Live Data or the link above the small activity box. - -**The console shows LIVE data - not a history of everything that has ever happened. A LNS is a management & relay service, not a database. When you open the console you may see a summary of recent activity - this is a bonus. You must leave the console open, even in another tab, if you want to see live activity.** - - -### Explore - -Nothing on the console can be upset unless you confirm a warning message, so you are safe to explore the different menus to orientate yourself. This is very good idea so you are have an understanding of the layout of the land and shouldn't take more than 10 or 15 minutes. The documentation & volunteers on GitHub and the TTN forum will make refer to parts of the console without giving blow by blow directions. - - - - -## The config.h - -### The uplinkInterval - -LoRaWAN devices typically send small amounts of data at intervals between 15 minutes through to once per day. This allows a device to run on two AA batteries for 2 to 5 years. Hoping that LoRaWAN can move lots of data and your device can regularly receive commands to do something on demand is trying to bend the LoRaWAN system in ways it is not designed for and usually ends up with far too many issues to unravel. - -The radio frequencies that are used are usually shared with other Industrial, Scientific & Medical, known as ISM, users. The LoRa modulation is particularly resistance to interference due to other simultaneous transmissions on the same frequency but too much local activity will mean that not all uplinks get through. The Things Industries suggest designing a system to a potential packet loss rate of 10%. Typically we see 1 or 2% loss. This is entirely down to shared use of the radio waves, once an uplink is heard by a gateway the system is super reliable through The Things Stack. - -To ensure that the shared ISM bands are fairly used there are limits defined in law on how often you can transmit, called Duty Cycle. The details vary by region or country but typically you can only transmit for 1% of the time. Some frequencies you can only use 0.1% of the time. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/ for more information. - -Additionally, as The Things Stack Sandbox aka TTN is an array of servers in three locations around the world paid for by The Things Industries, there is a Fair Use Policy so that those learning LoRaWAN, communities, hobbyists & makers are guided on how much of the resource any one device can use. In short, it's 30 seconds of airtime a day and 10 downlinks. When a gateway is transmitting a downlink it can not hear any uplinks (contributing to the potential uplink loss outlined above). The community concensus is that 1 downlink a fortnight to update or adjust settings is appropriate. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/#fair-use-policy for more information. - -You can see what intervals can be used with this interactive calculator: https://avbentem.github.io/airtime-calculator/ttn/. Devices further away from gateways will have to use a higher Spread Factor to be heard - do not assume everything will happen at SF7. A uplink takes a minimum of 6 seconds from start to end, sometimes longer if the device is further away from the gateway, so - -With all thes considerations, trying to use LoRaWAN for command & control isn't appropriate and realtime GPS tracking almost always breaches FUP and usually legal limits, leaving aside the challenges of coverage. - -See the hints & tips section on testing your device. - - -### EUI's & Keys - -In the config.h towards the top there are four lines thus: - -// replace-with-your-device-id -uint64_t joinEUI = 0x0000000000000000; -uint64_t devEUI = 0x0000000000000000; -uint8_t appKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -uint8_t nwkKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - -On the TTN console on the device summary page, click the clipboard icon next to the DevEUI, highlight the 16 0's in the third line after the x and paste. - -The devEUI must start with 0x and will end up looking something like 0x70B3D57ED006544E - -For the appKey we need TTN to format it correctly. Click the eye icon and an extra icon will appear <> - click this and the key will be formatted for you. Click the clipboard icon and then paste over the 32 0x00's in the config file. Then do the same for nwkKey. - -A key will end up something like 0x31, 0x16, 0x6A, 0x22, 0x97, 0x52, 0xB6, 0x34, 0x57, 0x45, 0x1B, 0xC3, 0xC9, 0xD8, 0x83, 0xE8 - - -### Region - -The region value you use MUST match the one you selected on the console. - -If you are using US915 or AU915 then you should change the subBand const to 2. - -### The pinmap - -This is the connections between the MCU (ESP32/ATmega/SAMD) and the LoRa radio (SX1276/SX1262). - -Prebuilt modules are easy - we can detect the board and setup the pinmap for you. These boards are: - -* TTGO_LoRa32 -* TTGO_LoRa32_V1 -* TTGO_LORA32_V2 -* TTGO_LORA32_v21NEW -* HELTEC_WIFI_LORA_32 -* HELTEC_WIFI_LORA_32_V2 -* HELTEC_WIFI_LORA_32_V3 -* CUBECELL_BOARD - -If you have a TTGO T-Beam, you must choose the correct radio from the Board Revision sub-menu found under the main Tools menu. - -* TBEAM_USE_RADIO_SX1262 -* TBEAM_USE_RADIO_SX1276 - -If you have an Adafruit Feather M0 with RFM95 then you must solder a wire or use a jumper to link from pin 6 to io1: https://learn.adafruit.com/the-things-network-for-feather/arduino-wiring - - -If you have a module that's not on this list, please go to the "Pinmap How-To" below. - - - -## Observations on the main sketch - -Most of the sketch has comments that tell you what the various parts are doing. This should add a little more info: - -### The Join - -When a device is first started, it needs to register with the LoRaWAN Network Server (LNS) and setup it's session. With the settings from the console copied over and a gateway an appropriate distance away, most of the time the join will 'just work'. - -If it doesn't, then there is no point trying repeatedly without going through the troubleshootng sequence. So this starter sketch will try once only to save the airwaves & TTN Community servers from repeated misfires. - - -### The payload - -You may see other starter sketches sending text. Apart from being massively inefficient, the text isn't easily displayed on the TTN console which makes it rather pointless and pro embedded engineers don't send strings. So this sketch sends the data as a sequence of bytes as recommended. - -Further reading on this can be found here, just ignore the pink message about v2, it's all still valid: https://www.thethingsnetwork.org/docs/devices/bytes/ - -We've not assumed anything about any sensors you have, so we are just reading a digital & an analog pin. An analog reading is typically a two byte value - an integer - this is split using the Arduino highByte & lowByte function. You'll see how we put it back together in the TTN console below. - - -## TTN Console Payload Decoder - -Coming soon - -## Hints & Tips - -### Device testing - -The LoRaWAN code base works to a specification and once you are happy your device is able to join & send a few dozen uplinks, continuing to sit around waiting for an uplink to test your sensor code & payload format is a waste of your time. The solution is to write everything else in a different sketch, output the array to the serial console and then you can copy & paste the hex array in to the TTN console Payload Formatters section to test the decoding. - - -## Pinmap How-To - - From c0ebd5a92e6b3ad8d007b6a01276753d35d16bd0 Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Mon, 25 Mar 2024 11:55:29 +0000 Subject: [PATCH 08/15] Header comments & ABP --- examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino | 192 ++++------------- examples/LoRaWAN/LoRaWAN_ABP/configABP.h | 37 ++-- .../LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino | 196 ------------------ examples/LoRaWAN/LoRaWAN_ESP32/config.h | 130 ------------ .../LoRaWAN_Reference/LoRaWAN_Reference.ino | 5 +- .../LoRaWAN_Starter/LoRaWAN_Starter.ino | 25 ++- 6 files changed, 87 insertions(+), 498 deletions(-) delete mode 100644 examples/LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino delete mode 100644 examples/LoRaWAN/LoRaWAN_ESP32/config.h diff --git a/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino b/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino index 02089e4bc..c31da1ec4 100644 --- a/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino +++ b/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino @@ -1,178 +1,68 @@ /* - RadioLib LoRaWAN End Device ABP Example + RadioLib LoRaWAN ABP Example - This example sets up a LoRaWAN node using ABP (activation - by personalization). Before you start, you will have to + ABP = Activation by Personalisation, an alternative + to OTAA (Over the Air Activation). OTAA is preferable. + + This example joins a LoRaWAN network and will send + uplink packets. Before you start, you will have to register your device at https://www.thethingsnetwork.org/ After your device is registered, you can run this example. - The device will start uploading data directly, - without having to join the network. + The device will join the network and start uploading data. + + LoRaWAN v1.1 requires the use of EEPROM (persistent storage). + Running this examples REQUIRES you to check "Resets frame counters" + 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 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 LoRaWAN details, see the wiki page + https://github.com/jgromes/RadioLib/wiki/LoRaWAN -// 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); */ +#include "configABP.h" + void setup() { - Serial.begin(9600); + Serial.begin(115200); + while (!Serial); + delay(5000); // Give time to switch to the serial monitor + Serial.println(F("\nSetup ... ")); - // initialize radio (SX1262 / SX1278 / ... ) with default settings - Serial.print(F("[Radio] Initializing ... ")); + Serial.println(F("Initalise the radio")); int state = radio.begin(); - if(state == RADIOLIB_ERR_NONE) { - Serial.println(F("success!")); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while(true); - } - - // device address - this number can be anything - // when adding new end device in TTN, you can generate this number, - // or you can set any value you want, provided it is unique - uint32_t devAddr = 0x12345678; - - // select some encryption keys which will be used to secure the communication - // there are two of them - network key and application key - // because LoRaWAN uses AES-128, the key MUST be 16 bytes (or characters) long - - // network key is the ASCII string "topSecretKey1234" - uint8_t nwkSKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 }; - - // application key is the ASCII string "aDifferentKeyABC" - uint8_t appSKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, - 0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 }; - - // network key 2 is the ASCII string "topSecretKey5678" - uint8_t fNwkSIntKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, - 0x6E, 0x74, 0x4B, 0x65, 0x35, 0x36, 0x37, 0x38 }; - - // network key 3 is the ASCII string "aDifferentKeyDEF" - uint8_t sNwkSIntKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, - 0x6E, 0x74, 0x4B, 0x65, 0x79, 0x44, 0x45, 0x46 }; - - // prior to LoRaWAN 1.1.0, only a single "nwkKey" is used - // when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded - // and can be set to NULL - - - // if using EU868 on ABP in TTN, you need to set the SF for RX2 window manually - /* - node.rx2.drMax = 3; - */ - - // 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 `beginABP()` with the same keys - // in that case, the function will not need to transmit a JoinRequest - - // to start a LoRaWAN v1.0 session, - // the user can remove the fNwkSIntKey and sNwkSIntKey - /* - state = node.beginABP(devAddr, nwkSKey, appSKey); - */ - - // start the device by directly providing the encryption keys and device address - Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... ")); - state = node.beginABP(devAddr, nwkSKey, appSKey, fNwkSIntKey, sNwkSIntKey); - if(state >= RADIOLIB_ERR_NONE) { - Serial.println(F("success!")); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while(true); - } + debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); + + Serial.println(F("Initalise LoRaWAN Network credentials")); + state = node.beginABP(devAddr, NwkSEncKey, AppSKey, NwkSKey, SNwkSIntKey, true); + debug(state < RADIOLIB_ERR_NONE, F("Session setup failed"), state, true); + Serial.println(F("Ready!\n")); } -// 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!" + 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")); + Serial.println(F("Sending uplink")); - // print SNR (Signal-to-Noise Ratio) - Serial.print(F("[LoRaWAN] SNR:\t\t")); - Serial.print(radio.getSNR()); - Serial.println(F(" dB")); + // Read some inputs + uint8_t Digital1 = digitalRead(2); + uint16_t Analog1 = analogRead(A0); - // print frequency error - Serial.print(F("[LoRaWAN] Frequency error:\t")); - Serial.print(radio.getFrequencyError()); - Serial.println(F(" Hz")); + // Build payload byte array + uint8_t uplinkPayload[3]; + uplinkPayload[0] = Digital1; + uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(Analog1); - } else if(state == RADIOLIB_ERR_RX_TIMEOUT) { - Serial.println(F("no downlink!")); + // Perform an uplink + int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); + debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - } - - // on EEPROM enabled boards, you should 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!) - uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows - - delay(delayMs); + // Wait until next uplink - observing legal & TTN FUP constraints + delay(uplinkIntervalSeconds * 1000UL); } diff --git a/examples/LoRaWAN/LoRaWAN_ABP/configABP.h b/examples/LoRaWAN/LoRaWAN_ABP/configABP.h index bba38e4fe..33fd9fa0d 100644 --- a/examples/LoRaWAN/LoRaWAN_ABP/configABP.h +++ b/examples/LoRaWAN/LoRaWAN_ABP/configABP.h @@ -6,26 +6,29 @@ // How often to send an uplink - consider legal & FUP constraints - see notes const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds -// JoinEUI - previous versions of LoRaWAN called this AppEUI -// for development purposes you can use all zeros - see wiki for details -#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000 +// Device address - either a development address or one assigned +// to the LoRaWAN Service Provider - TTN will generate one for you +#ifndef RADIOLIB_LORAWAN_DEV_ADDR // Replace with your DevAddr +#define RADIOLIB_LORAWAN_DEV_ADDR 0x------ +#endif -// The Device EUI & two keys can be generated on the TTN console -#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI -#define RADIOLIB_LORAWAN_DEV_EUI 0x--------------- +#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-- +#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-- #endif -#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key -#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#ifndef RADIOLIB_LORAWAN_NWKSENC_KEY // Replace with your NwkSEnc Key +#define RADIOLIB_LORAWAN_NWKSENC_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- #endif -#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here -#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#ifndef RADIOLIB_LORAWAN_APPS_KEY // Replace with your AppS Key +#define RADIOLIB_LORAWAN_APPS_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- #endif // For the curious, the #ifndef blocks allow for automated testing &/or you can // put your EUI & keys in to your platformio.ini - see wiki for more tips - // Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500 const LoRaWANBand_t Region = EU868; const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 @@ -97,11 +100,13 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 #endif -// Copy over the EUI's & keys in to the something that will not compile if incorrectly formatted -uint64_t joinEUI = RADIOLIB_LORAWAN_JOIN_EUI; -uint64_t devEUI = RADIOLIB_LORAWAN_DEV_EUI; -uint8_t appKey[] = { RADIOLIB_LORAWAN_APP_KEY }; -uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY }; +// 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 SNwkSIntKey[] = { RADIOLIB_LORAWAN_SNWKSINT_KEY }; // Previously sNwkSIntKey +uint8_t NwkSEncKey[] = { RADIOLIB_LORAWAN_NWKSENC_KEY }; // Previously fNwkSIntKey +uint8_t AppSKey[] = { RADIOLIB_LORAWAN_APPS_KEY }; + // Create the LoRaWAN node LoRaWANNode node(&radio, &Region, subBand); diff --git a/examples/LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino b/examples/LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino deleted file mode 100644 index f2d567bbd..000000000 --- a/examples/LoRaWAN/LoRaWAN_ESP32/LoRaWAN_ESP32.ino +++ /dev/null @@ -1,196 +0,0 @@ - -/* - -This demonstrates how to save the join information in to permanent memory -so that if the power fails, batteries run out or are changed, the rejoin -is more efficient & happens sooner due to the way that LoRaWAN secures -the join process - see the wiki for more details. - -This is typically useful for devices that need more power than a battery -driven sensor - something like a air quality monitor or GPS based device that -is likely to use up it's power source resulting in loss of the session. - -The relevant code is flagged with a ##### comment - -Saving the entire session is possible but not demonstrated here - it has -implications for flash wearing and complications with which parts of the -session may have changed after an uplink. So it is assumed that the device -is going in to deep-sleep, as below, between normal uplinks. - -*/ - -#if !defined(ESP32) - #pragma error ("This is not the example your device is looking for - ESP32 only") -#endif - -// ##### Load the ESP32 preferences facilites -#include -Preferences store; - -// LoRaWAN config, credentials & pinmap -#include "config.h" - -#include - -// Utilities & vars to support ESP32 deep-sleep. The RTC_DATA_ATTR attribute -// puts these in to the RTC memory which is preserved during deep-sleep -RTC_DATA_ATTR uint16_t bootCount = 1; -RTC_DATA_ATTR uint16_t bootCountSinceUnsuccessfulJoin = 0; -RTC_DATA_ATTR uint8_t LWsession[RADIOLIB_LORAWAN_SESSION_BUF_SIZE]; - -// Abbreviated version from the Arduino-ESP32 package, see -// https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/deepsleep.html -// for the complete set of options -void print_wakeup_reason() { - esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); - if (wakeup_reason == ESP_SLEEP_WAKEUP_TIMER) { - Serial.println(F("Wake from sleep")); - } else { - Serial.print(F("Wake not caused by deep sleep: ")); - Serial.println(wakeup_reason); - } - - Serial.print(F("Boot count: ")); - Serial.println(bootCount++); -} - -// Put device in to lowest power deep-sleep mode -void gotoSleep(uint32_t seconds) { - esp_sleep_enable_timer_wakeup(seconds * 1000UL * 1000UL); // Function uses uS - Serial.println(F("Sleeping\n")); - Serial.flush(); - - esp_deep_sleep_start(); - - // If this appears in the serial debug, we didn't go to sleep! - // So take defensive action so we don't continually uplink - Serial.println(F("\n\n### Sleep failed, delay of 5 minutes & then restart ###\n")); - delay(5UL * 60UL * 1000UL); - ESP.restart(); -} - - - -// Setup & execute all device functions ... -void setup() { - Serial.begin(115200); - while (!Serial); // Wait for serial to be initalised - delay(2000); // Give time to switch to the serial monitor - Serial.println(F("\nSetup")); - print_wakeup_reason(); - - int16_t state = 0; // return value for calls to RadioLib - - // Setup the radio based on the pinmap (connections) in config.h - Serial.println(F("Initalise the radio")); - state = radio.begin(); - debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); - - Serial.println(F("Recalling LoRaWAN nonces & session")); - // ##### Setup the flash storage - store.begin("radiolib"); - // ##### If we have previously saved nonces, restore them - if (store.isKey("nonces")) { - uint8_t buffer[RADIOLIB_LORAWAN_NONCES_BUF_SIZE];// Create somewhere to store nonces - store.getBytes("nonces", buffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE);// Get them to the store - state = node.setBufferNonces(buffer); // Send them to LoRaWAN - debug(state != RADIOLIB_ERR_NONE, F("Restoring nonces buffer failed"), state, false); - } - - // Recall session from RTC deep-sleep preserved variable - state = node.setBufferSession(LWsession); // Send them to LoRaWAN stack - // If we have booted at least once we should have a session to restore, so report any failure - // Otherwise no point saying there's been a failure when it was bound to fail with an empty - // LWsession var. At this point, bootCount has already been incremented, hence the > 2 - debug((state != RADIOLIB_ERR_NONE) && (bootCount > 2), F("Restoring session buffer failed"), state, false); - - // Process the restored session or failing that, create a new one & - // return flag to indicate a fresh join is required - Serial.println(F("Setup LoRaWAN session")); - state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, false); - // See comment above, no need to report a failure that is bound to occur on first boot - debug((state != RADIOLIB_ERR_NONE) && (bootCount > 2), F("Restore session failed"), state, false); - - // Loop until successful join - while (state != RADIOLIB_ERR_NONE) { - Serial.println(F("Join ('login') to the LoRaWAN Network")); - state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, true); - - if (state < RADIOLIB_ERR_NONE) { - Serial.print(F("Join failed: ")); - Serial.println(state); - - // How long to wait before join attenpts. This is an interim solution pending - // implementation of TS001 LoRaWAN Specification section #7 - this doc applies to v1.0.4 & v1.1 - // It sleeps for longer & longer durations to give time for any gateway issues to resolve - // or whatever is interfering with the device <-> gateway airwaves. - uint32_t sleepForSeconds = min((bootCountSinceUnsuccessfulJoin++ + 1UL) * 60UL, 3UL * 60UL); - Serial.print(F("Boots since unsuccessful join: ")); - Serial.println(bootCountSinceUnsuccessfulJoin); - Serial.print(F("Retrying join in ")); - Serial.print(sleepForSeconds); - Serial.println(F(" seconds")); - - gotoSleep(sleepForSeconds); - - } else { // Join was successful - Serial.println(F("Joined")); - - // ##### Save the join counters (nonces) to permanent store - Serial.println(F("Saving nonces to flash")); - uint8_t buffer[RADIOLIB_LORAWAN_NONCES_BUF_SIZE]; // Create somewhere to store nonces - uint8_t *persist = node.getBufferNonces(); // Get pointer to nonces - memcpy(buffer, persist, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); // Copy in to buffer - store.putBytes("nonces", buffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); // Send them to the store - - // We'll save the session after the uplink - - // Reset the failed join count - bootCountSinceUnsuccessfulJoin = 0; - - delay(1000); // Hold off off hitting the airwaves again too soon - an issue in the US - - } // if beginOTAA state - } // while join - - // ##### Close the store - store.end(); - - - // ----- And now for the main event ----- - Serial.println(F("Sending uplink")); - - // Read some inputs - uint8_t Digital2 = digitalRead(2); - uint16_t Analog1 = analogRead(A1); - - // Build payload byte array - uint8_t uplinkPayload[3]; - uplinkPayload[0] = Digital2; - uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions - uplinkPayload[2] = lowByte(Analog1); - - // Perform an uplink - state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); - debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); - - Serial.print(F("FcntUp: ")); - Serial.println(node.getFcntUp()); - - // Now save session to RTC memory - uint8_t *persist = node.getBufferSession(); - memcpy(LWsession, persist, RADIOLIB_LORAWAN_SESSION_BUF_SIZE); - - // Wait until next uplink - observing legal & TTN FUP constraints - gotoSleep(uplinkIntervalSeconds); - -} - - -// The ESP32 wakes from deep-sleep and starts from the very beginning -// which is a very good place to start, as any singing nun knows. -// It then goes back to sleep, so loop() is never called and which is -// why it is empty. - -void loop() {} - diff --git a/examples/LoRaWAN/LoRaWAN_ESP32/config.h b/examples/LoRaWAN/LoRaWAN_ESP32/config.h deleted file mode 100644 index bba38e4fe..000000000 --- a/examples/LoRaWAN/LoRaWAN_ESP32/config.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef _CONFIG_H -#define _CONFIG_H - -#include - -// How often to send an uplink - consider legal & FUP constraints - see notes -const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds - -// JoinEUI - previous versions of LoRaWAN called this AppEUI -// for development purposes you can use all zeros - see wiki for details -#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000 - -// The Device EUI & two keys can be generated on the TTN console -#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI -#define RADIOLIB_LORAWAN_DEV_EUI 0x--------------- -#endif -#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key -#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- -#endif -#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here -#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- -#endif - -// For the curious, the #ifndef blocks allow for automated testing &/or you can -// put your EUI & keys in to your platformio.ini - see wiki for more tips - - - -// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500 -const LoRaWANBand_t Region = EU868; -const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 - - -// ============================================================================ -// Below is to support the sketch - only make changes if the notes say so ... - -// Auto select MCU <-> radio connections -// If you get an error message when compiling, it may be that the -// pinmap could not be determined - see the notes for more info - -// Adafruit -#if defined(ARDUINO_SAMD_FEATHER_M0) - #pragma message ("Adafruit Feather M0 with RFM95") - #pragma message ("Link required on board") - SX1276 radio = new Module(8, 3, 4, 6); - - -// LilyGo -#elif defined(ARDUINO_TTGO_LORA32_V1) - #pragma message ("TTGO LoRa32 v1 - no Display") - SX1276 radio = new Module(18, 26, 14, 33); - -#elif defined(ARDUINO_TTGO_LORA32_V2) - #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") - -#elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") - SX1276 radio = new Module(18, 26, 14, 33); - -#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1262) - #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") - -#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") - SX1276 radio = new Module(18, 26, 23, 33); - - -// Heltec -#elif defined(ARDUINO_HELTEC_WIFI_LORA_32) - #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") - -#elif defined(ARDUINO_heltec_wifi_kit_32_V2) - #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") - SX1276 radio = new Module(18, 26, 14, 35); - -#elif defined(ARDUINO_heltec_wifi_kit_32_V3) - #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") - SX1262 radio = new Module(8, 14, 12, 13); - -#elif defined(ARDUINO_CUBECELL_BOARD) - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") - SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); - -#elif defined(ARDUINO_CUBECELL_BOARD_V2) - #pragma error ("ARDUINO_CUBECELL_BOARD_V2 awaiting pin map") - - -#else - #pragma message ("Unknown board - no automagic pinmap available") - - // SX1262 pin order: Module(NSS/CS, DIO1, RESET, BUSY); - // SX1262 radio = new Module(8, 14, 12, 13); - - // SX1278 pin order: Module(NSS/CS, DIO0, RESET, DIO1); - // SX1278 radio = new Module(10, 2, 9, 3); - -#endif - - -// Copy over the EUI's & keys in to the something that will not compile if incorrectly formatted -uint64_t joinEUI = RADIOLIB_LORAWAN_JOIN_EUI; -uint64_t devEUI = RADIOLIB_LORAWAN_DEV_EUI; -uint8_t appKey[] = { RADIOLIB_LORAWAN_APP_KEY }; -uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY }; - -// Create the LoRaWAN node -LoRaWANNode node(&radio, &Region, subBand); - - -// Helper function to display any issues -void debug(bool isFail, const __FlashStringHelper* message, int state, bool Freeze) { - if (isFail) { - Serial.print(message); - Serial.print("("); - Serial.print(state); - Serial.println(")"); - while (Freeze); - } -} - -// Helper function to display a byte array -void arrayDump(uint8_t *buffer, uint16_t len) { - for (uint16_t c; c < len; c++) { - Serial.printf("%02X", buffer[c]); - } - Serial.println(); -} - - -#endif diff --git a/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino b/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino index 5df3114e8..0d9b15eec 100644 --- a/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino +++ b/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino @@ -11,8 +11,6 @@ shown here for reference. 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. @@ -26,7 +24,6 @@ For LoRaWAN details, see the wiki page https://github.com/jgromes/RadioLib/wiki/LoRaWAN - */ #include "config.h" @@ -38,7 +35,7 @@ void setup() { Serial.begin(115200); while (!Serial); // Wait for serial to be initalised - delay(2000); // Give time to switch to the serial monitor + delay(5000); // Give time to switch to the serial monitor Serial.println(F("\nSetup")); int16_t state = 0; // return value for calls to RadioLib diff --git a/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino b/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino index 4ccc372ec..30d15c152 100644 --- a/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino +++ b/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino @@ -1,10 +1,33 @@ -#include +/* + RadioLib LoRaWAN Starter Example + + This example joins a LoRaWAN network and will send + uplink packets. Before you start, you will have to + register your device at https://www.thethingsnetwork.org/ + After your device is registered, you can run this example. + The device will join the network and start uploading data. + + Running this examples REQUIRES you to check "Resets DevNonces" + on your LoRaWAN dashboard. Refer to the network's + documentation on how to do this. + + 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/ + + For LoRaWAN details, see the wiki page + https://github.com/jgromes/RadioLib/wiki/LoRaWAN + +*/ #include "config.h" void setup() { Serial.begin(115200); while (!Serial); + delay(5000); // Give time to switch to the serial monitor Serial.println(F("\nSetup ... ")); Serial.println(F("Initalise the radio")); From 18c1ce47b4ffe4a31ff6dd0044a5afd3110594ee Mon Sep 17 00:00:00 2001 From: StevenCellist <47155822+StevenCellist@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:41:04 +0100 Subject: [PATCH 09/15] Improve introductory comments in LoRaWAN ABP example --- examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino b/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino index c31da1ec4..ec27df631 100644 --- a/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino +++ b/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino @@ -4,16 +4,19 @@ ABP = Activation by Personalisation, an alternative to OTAA (Over the Air Activation). OTAA is preferable. - This example joins a LoRaWAN network and will send - uplink packets. Before you start, you will have to - register your device at https://www.thethingsnetwork.org/ + This example will send uplink packets to a LoRaWAN network. + Before you start, you will have to register your device at + https://www.thethingsnetwork.org/ 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). - Running this examples REQUIRES you to check "Resets frame counters" - on your LoRaWAN dashboard. Refer to the network's documentation - on how to do this. + LoRaWAN v1.1 requires the use of persistent storage. + As this example does not use persistent storage, running this + examples REQUIRES you to check "Resets frame counters" + on your LoRaWAN dashboard. Refer to the notes or the + network's documentation on how to do this. + To comply with LoRaWAN v1.1's persistent storage, refer to + https://github.com/radiolib-org/radiolib-persistence For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration From d1b911b2736df60bdf6825691ad3d6b321745772 Mon Sep 17 00:00:00 2001 From: StevenCellist <47155822+StevenCellist@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:42:52 +0100 Subject: [PATCH 10/15] Add link to persistence repository in LoRaWAN Reference example --- examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino b/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino index 0d9b15eec..770b47956 100644 --- a/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino +++ b/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino @@ -12,8 +12,10 @@ LoRaWAN v1.1 requires the use of EEPROM (persistent storage). Running this examples REQUIRES you to check "Resets DevNonces" - on your LoRaWAN dashboard. Refer to the network's - documentation on how to do this. + on your LoRaWAN dashboard. Refer to the notes or the + network's documentation on how to do this. + To comply with LoRaWAN v1.1's persistent storage, refer to + https://github.com/radiolib-org/radiolib-persistence For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration @@ -196,4 +198,4 @@ void loop() { delay(delayMs); -} // loop \ No newline at end of file +} // loop From 2f1efdb8a0536949476d3e1a6a0aa63d0261cb34 Mon Sep 17 00:00:00 2001 From: HeadBoffin <60431281+HeadBoffin@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:00:22 +0000 Subject: [PATCH 11/15] Create README.md --- examples/LoRaWAN/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/LoRaWAN/README.md diff --git a/examples/LoRaWAN/README.md b/examples/LoRaWAN/README.md new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/LoRaWAN/README.md @@ -0,0 +1 @@ + From 2d454bdc618a536d7efc1ea8e36afb113cce1329 Mon Sep 17 00:00:00 2001 From: StevenCellist <47155822+StevenCellist@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:05:54 +0100 Subject: [PATCH 12/15] Populate README with direct links to (persistence) examples --- examples/LoRaWAN/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/LoRaWAN/README.md b/examples/LoRaWAN/README.md index 8b1378917..a269810b3 100644 --- a/examples/LoRaWAN/README.md +++ b/examples/LoRaWAN/README.md @@ -1 +1,18 @@ +# LoRaWAN examples +RadioLib LoRaWAN v1.1 examples. +* [LoRaWAN_Starter](https://github.com/jgromes/RadioLib/tree/master/examples/LoRaWAN/LoRaWAN_Starter): this is the recommended entry point for new users. Please read the `notes` that come with this example to learn more about LoRaWAN and how to use it in RadioLib! +* [LoRaWAN_Reference](https://github.com/jgromes/RadioLib/tree/master/examples/LoRaWAN/LoRaWAN_Reference): this sketch showcases most of the available API for LoRaWAN in RadioLib. Be frightened by the possibilities! It is recommended you have read all the `notes` for the Starter sketch first, as well as the [Learn section on The Things Network](https://www.thethingsnetwork.org/docs/lorawan/)! +* [LoRaWAN_ABP](https://github.com/jgromes/RadioLib/tree/master/examples/LoRaWAN/LoRaWAN_ABP): if you wish to use ABP instead of OTAA (but why?), this example shows how you can do this using RadioLib. + +--- + +All three of these examples do not fully comply with LoRaWAN v1.1: for that, persistent storage is necessary. As the implementation of persistent storage differs between different platforms, these are not given here, but in a separate repository, see below: + +## RadioLib persistence +In [this repository](https://github.com/radiolib-org/radiolib-persistence), examples are provided that do comply with the required persistence of certain parameters for LoRaWAN v1.1. Examples are (or will become) available for some of the most popular platforms. **These examples assume you have successfully used the Starter sketch and understood (most of) the accompanying notes!** +Currently, examples are available for the following platforms: + +* [LoRaWAN for ESP32](https://github.com/radiolib-org/radiolib-persistence/tree/main/examples/LoRaWAN_ESP32) + +_This list is last updated for RadioLib 6.5.0_ From 61b94bf4fcdb112b4b1fd78680c368aa329e9184 Mon Sep 17 00:00:00 2001 From: StevenCellist <47155822+StevenCellist@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:13:57 +0100 Subject: [PATCH 13/15] Fix typos in the LoRaWAN Starter notes --- examples/LoRaWAN/LoRaWAN_Starter/notes.md | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/LoRaWAN/LoRaWAN_Starter/notes.md b/examples/LoRaWAN/LoRaWAN_Starter/notes.md index 4f56a617a..9fdd6bd53 100644 --- a/examples/LoRaWAN/LoRaWAN_Starter/notes.md +++ b/examples/LoRaWAN/LoRaWAN_Starter/notes.md @@ -13,7 +13,7 @@ LoRaWAN is an amazing system for small battery powered sensors collecting data f These notes plus a lot more are available in the wiki: https://github.com/jgromes/RadioLib/wiki/LoRaWAN -For questions about using RadioLib there is the discussions section (https://github.com/jgromes/RadioLib/discussions) and if you believe you've found an issue (aka bug), the issues section (https://github.com/jgromes/RadioLib/issues). If posting an issue please ensure you tell us what hardware you are using and provide a debug log - please do not use verbose mode unless asked to. If the question is more LoRaWAN or firmware related, then you can use the TTN forum: https://www.thethingsnetwork.org/forum/ +For questions about using RadioLib there is the discussions section (https://github.com/jgromes/RadioLib/discussions) and if you believe you've found an issue (aka bug), the issues section (https://github.com/jgromes/RadioLib/issues). If posting an issue please ensure you tell us what hardware you are using and provide a debug log - make sure you enable `RADIOLIB_DEBUG_PROTOCOL`. If the question is more LoRaWAN or firmware related, then you can use the TTN forum: https://www.thethingsnetwork.org/forum/ ## Register & setup on TTN @@ -28,9 +28,9 @@ There will be some instructions that you have to take on face value. You didn't Go to https://www.thethingsnetwork.org/get-started and register - just like any other website. These instructions are for TTS Sandbox. -Once you have confirmed your email address, you can login to the console here: https://console.cloud.thethings.network/. If you allow your browser to share you location the best console will be selected. For most users the best one is the obvious one, if you have any doubts you can ask on the forum here: https://www.thethingsnetwork.org/forum/ - you login with the exact same details. +Once you have confirmed your email address, you can login to the console here: https://console.cloud.thethings.network/. If you allow your browser to share your location the best console will be selected. For most users the best one is the obvious one, if you have any doubts you can ask on the forum here: https://www.thethingsnetwork.org/forum/ - you login with the exact same details. -It is simpler to register your gateway first. If you don't have a gateway, then a The Things Indoor Gateway (TTIG) is a very affordable option. A gateway gives you a console to see if your device is being heard and is hugely useful when debugging a DIY device. If you are in range of a community gateway you may be lucky with your first device creation but you will never know if you are in range unless you have access to that gateways console. +It is simpler to register your gateway first. If you don't have a gateway, then a The Things Indoor Gateway (TTIG) is a very affordable option. A gateway gives you a console to see if your device is being heard and is hugely useful when debugging a DIY device. If you are in range of a community gateway you may be lucky with your first device creation but you will never know if you are in range unless you have access to that gateway's console. You can read up on key concepts and troubleshooting here: https://www.thethingsindustries.com/docs/gateways/ @@ -38,7 +38,7 @@ LoRa stands for Long Range - having the gateway & device on the same desk tends ### Create your application -An application is like a box to keep some devices in - normally doing the same thing - on larger deployments this may be 1,000's of similar devices. Starting out it it is likely to be just a few so there is no need to get concerned about how to divide up your use just yet. +An application is like a box to keep some devices in - normally doing the same thing - on larger deployments this may be 1,000's of similar devices. Starting out it is likely to be just a few so there is no need to get concerned about how to divide up your use just yet. Onced logged in to the console you can go in to Applications to create your first application. The ID must be all lower case or numbers, no spaces, dashes are OK and it has to be unique to the entire TTN community - so `first-app` will be rejected - you could use `your-username-first-app` as that's likely to be unique. The name and description are for your own use and are optional. @@ -52,15 +52,15 @@ You are making your own device using a third party LoRaWAN stack so there will n Choose the Frequency plan appropriate for your region. Consider that almost all countries have laws relating to what frequencies you use so don't get creative. For Europe please use the recommended option. For other regions use the entry marked 'used by TTN'. -Choose LoRaWAN 1.1.0 - the last one in the list - the latest specfication. RadioLib uses RP001 Regional Parameters 1.1 revision B. +Choose LoRaWAN 1.1.0 - the last one in the list - the latest specfication. RadioLib uses RP001 Regional Parameters 1.1 revision A. -At this point you will be asked for your JoinEUI. As this is a DIY device and we are using RadioLib, you can use all zero's as recommended by The LoRa Alliance TR007 Technical Recommendations document. Once you've put in all zeros and clicked confirm you will be asked for a DevEUI, AppKey and NwkKey. It is preferable to have the cosole generate them so they are properly formatted. +At this point you will be asked for your JoinEUI. As this is a DIY device and we are using RadioLib, you can use all zero's as recommended by The LoRa Alliance TR007 Technical Recommendations document. Once you've put in all zeros and clicked confirm you will be asked for a DevEUI, AppKey and NwkKey. It is preferable to have the console generate them so they are properly formatted. Your End device ID can be changed to make the device more identifiable. Something related to your hardware helps - like devicename-01. The you can click the blue 'Register device'. When many sensors are big deployed, a device is registered, batteries put in, it joins and gets on with sending data for the next few years. For development purposes we need to turn off one of the security settings so that you can join & uplink out of the normal sequence that a device in the field would do. -Click on General Settings, scroll down to Join settings, click the Expand button, scroll down and click the Resets join nonces option. You will see a warning about replay attacks which is entirely proper & correct. If anyone evesdropping in your area on your LoRa transmissions could fake a join and send uplinks from their device but only if they happened to find out your AppKey & NwkKey which is kept securely on the TTN servers and is never transmitted over the air, so they'd also have to login to your account, which is protected by your password. +Click on General Settings, scroll down to Join settings, click the Expand button, scroll down and click the 'Resets join nonces' option. You will see a warning about replay attacks which is entirely proper & correct. If anyone eavesdropping in your area on your LoRa transmissions could fake a join and send uplinks from their device but only if they happened to find out your AppKey & NwkKey which is kept securely on the TTN servers and is never transmitted over the air, so they'd also have to login to your account, which is protected by your password. You then need to copy over the device details in to the config file for RadioLib. There are buttons to copy items to the clipboard so you don't have to hand type them. @@ -90,7 +90,7 @@ The main menu for a device is the horizontal band: Overview, Live Data, Messagin ### Explore -Nothing on the console can be upset unless you confirm a warning message, so you are safe to explore the different menus to orientate yourself. This is very good idea so you are have an understanding of the layout of the land and shouldn't take more than 10 or 15 minutes. The documentation & volunteers on GitHub and the TTN forum will make refer to parts of the console without giving blow by blow directions. +Nothing on the console can be upset unless you confirm a warning message, so you are safe to explore the different menus to orientate yourself. This is very good idea so you have an understanding of the layout of the land and shouldn't take more than 10 or 15 minutes. The documentation & volunteers on GitHub and the TTN forum will make refer to parts of the console without giving blow by blow directions. @@ -101,22 +101,22 @@ Nothing on the console can be upset unless you confirm a warning message, so you LoRaWAN devices typically send small amounts of data at intervals between 15 minutes through to once per day. This allows a device to run on two AA batteries for 2 to 5 years. Hoping that LoRaWAN can move lots of data and your device can regularly receive commands to do something on demand is trying to bend the LoRaWAN system in ways it is not designed for and usually ends up with far too many issues to unravel. -The radio frequencies that are used are usually shared with other Industrial, Scientific & Medical, known as ISM, users. The LoRa modulation is particularly resistance to interference due to other simultaneous transmissions on the same frequency but too much local activity will mean that not all uplinks get through. The Things Industries suggest designing a system to a potential packet loss rate of 10%. Typically we see 1 or 2% loss. This is entirely down to shared use of the radio waves, once an uplink is heard by a gateway the system is super reliable through The Things Stack. +The radio frequencies that are used are usually shared with other Industrial, Scientific & Medical, known as ISM, users. The LoRa modulation is particularly resistant to interference due to other simultaneous transmissions on the same frequency but too much local activity will mean that not all uplinks get through. The Things Industries suggest designing a system to a potential packet loss rate of 10%. Typically we see 1 or 2% loss. This is entirely down to shared use of the radio waves, once an uplink is heard by a gateway the system is super reliable through The Things Stack. To ensure that the shared ISM bands are fairly used there are limits defined in law on how often you can transmit, called Duty Cycle. The details vary by region or country but typically you can only transmit for 1% of the time. Some frequencies you can only use 0.1% of the time. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/ for more information. Additionally, as The Things Stack Sandbox aka TTN is an array of servers in three locations around the world paid for by The Things Industries, there is a Fair Use Policy so that those learning LoRaWAN, communities, hobbyists & makers are guided on how much of the resource any one device can use. In short, it's 30 seconds of airtime a day and 10 downlinks. When a gateway is transmitting a downlink it can not hear any uplinks (contributing to the potential uplink loss outlined above). The community concensus is that 1 downlink a fortnight to update or adjust settings is appropriate. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/#fair-use-policy for more information. -You can see what intervals can be used with this interactive calculator: https://avbentem.github.io/airtime-calculator/ttn/. Devices further away from gateways will have to use a higher Spread Factor to be heard - do not assume everything will happen at SF7. A uplink takes a minimum of 6 seconds from start to end, sometimes longer if the device is further away from the gateway, so +You can see what intervals can be used with this interactive calculator: https://avbentem.github.io/airtime-calculator/ttn/. Devices further away from gateways will have to use a higher Spread Factor to be heard - do not assume everything will happen at SF7. An uplink takes a minimum of 6 seconds from start to end, sometimes longer if the device is further away from the gateway, so -With all thes considerations, trying to use LoRaWAN for command & control isn't appropriate and realtime GPS tracking almost always breaches FUP and usually legal limits, leaving aside the challenges of coverage. +With all these considerations, trying to use LoRaWAN for command & control isn't appropriate and realtime GPS tracking almost always breaches FUP and usually legal limits, leaving aside the challenges of coverage. See the hints & tips section on testing your device. ### EUI's & Keys -In the config.h towards the top there are four lines thus: +In the `config.h` towards the top there are four lines thus: // replace-with-your-device-id uint64_t joinEUI = 0x0000000000000000; From 14e1a9bd42b2815e582dd3999062f6a18c0733a1 Mon Sep 17 00:00:00 2001 From: HeadBoffin <60431281+HeadBoffin@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:09:09 +0000 Subject: [PATCH 14/15] Update notes.md --- examples/LoRaWAN/LoRaWAN_Starter/notes.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/LoRaWAN/LoRaWAN_Starter/notes.md b/examples/LoRaWAN/LoRaWAN_Starter/notes.md index 9fdd6bd53..b56675995 100644 --- a/examples/LoRaWAN/LoRaWAN_Starter/notes.md +++ b/examples/LoRaWAN/LoRaWAN_Starter/notes.md @@ -105,9 +105,9 @@ The radio frequencies that are used are usually shared with other Industrial, Sc To ensure that the shared ISM bands are fairly used there are limits defined in law on how often you can transmit, called Duty Cycle. The details vary by region or country but typically you can only transmit for 1% of the time. Some frequencies you can only use 0.1% of the time. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/ for more information. -Additionally, as The Things Stack Sandbox aka TTN is an array of servers in three locations around the world paid for by The Things Industries, there is a Fair Use Policy so that those learning LoRaWAN, communities, hobbyists & makers are guided on how much of the resource any one device can use. In short, it's 30 seconds of airtime a day and 10 downlinks. When a gateway is transmitting a downlink it can not hear any uplinks (contributing to the potential uplink loss outlined above). The community concensus is that 1 downlink a fortnight to update or adjust settings is appropriate. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/#fair-use-policy for more information. +Additionally, as The Things Stack Sandbox aka TTN is an array of servers in three locations around the world paid for by The Things Industries, there is a Fair Use Policy so that those learning LoRaWAN, communities, hobbyists & makers are guided on how much of the resource any one device can use. In short, it's 30 seconds of airtime a day and 10 downlinks. When a gateway is transmitting a downlink it can not hear any uplinks (contributing to the potential uplink loss outlined above). The community consensus is that 1 downlink a fortnight to update or adjust settings is appropriate. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/#fair-use-policy for more information. -You can see what intervals can be used with this interactive calculator: https://avbentem.github.io/airtime-calculator/ttn/. Devices further away from gateways will have to use a higher Spread Factor to be heard - do not assume everything will happen at SF7. An uplink takes a minimum of 6 seconds from start to end, sometimes longer if the device is further away from the gateway, so +You can see what intervals can be used with this interactive calculator: https://avbentem.github.io/airtime-calculator/ttn/. Devices further away from gateways will have to use a higher Spread Factor to be heard - do not assume everything will happen at SF7. An uplink takes a minimum of 6 seconds from start to end, sometimes longer if the device is further away from the gateway, so you will need to be patient for just a short while whilst waiting for feedback after seeing "Sending uplink" With all these considerations, trying to use LoRaWAN for command & control isn't appropriate and realtime GPS tracking almost always breaches FUP and usually legal limits, leaving aside the challenges of coverage. @@ -141,7 +141,7 @@ If you are using US915 or AU915 then you should change the subBand const to 2. ### The pinmap -This is the connections between the MCU (ESP32/ATmega/SAMD) and the LoRa radio (SX1276/SX1262). +This is the connection between the MCU (ESP32/ATmega/SAMD) and the LoRa radio (SX1276/SX1262). Prebuilt modules are easy - we can detect the board and setup the pinmap for you. These boards are: @@ -152,15 +152,13 @@ Prebuilt modules are easy - we can detect the board and setup the pinmap for you * HELTEC_WIFI_LORA_32 * HELTEC_WIFI_LORA_32_V2 * HELTEC_WIFI_LORA_32_V3 -* CUBECELL_BOARD If you have a TTGO T-Beam, you must choose the correct radio from the Board Revision sub-menu found under the main Tools menu. * TBEAM_USE_RADIO_SX1262 * TBEAM_USE_RADIO_SX1276 -If you have an Adafruit Feather M0 with RFM95 then you must solder a wire or use a jumper to link from pin 6 to io1: https://learn.adafruit.com/the-things-network-for-feather/arduino-wiring - +Auto-setup for the Adafruit Feather M0 with RFM95 is included but you must solder a wire or use a jumper to link from pin 6 to io1: https://learn.adafruit.com/the-things-network-for-feather/arduino-wiring If you have a module that's not on this list, please go to the "Pinmap How-To" below. @@ -199,4 +197,4 @@ The LoRaWAN code base works to a specification and once you are happy your devic ## Pinmap How-To - +Coming soon From 388a714e535d3da1848cf90017079ff86d817211 Mon Sep 17 00:00:00 2001 From: Nick McCloud Date: Mon, 25 Mar 2024 14:21:14 +0000 Subject: [PATCH 15/15] Added uplink complete msg --- examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino b/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino index 30d15c152..7b584dcc9 100644 --- a/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino +++ b/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino @@ -58,7 +58,11 @@ void loop() { // Perform an uplink int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); + + Serial.print(F("Uplink complete, next in ")); + Serial.print(uplinkIntervalSeconds); + Serial.println(F(" seconds")); // Wait until next uplink - observing legal & TTN FUP constraints - delay(uplinkIntervalSeconds * 1000UL); + delay(uplinkIntervalSeconds * 1000UL); // delay needs milli-seconds }