Skip to content

Commit

Permalink
Merge pull request #918 from StevenCellist/master
Browse files Browse the repository at this point in the history
[LoRaWAN] Revamp internal processing, key checking, new MAC commands, implement DutyCycle & DwellTime
  • Loading branch information
jgromes authored Jan 13, 2024
2 parents 842c548 + 3338034 commit cb02180
Show file tree
Hide file tree
Showing 11 changed files with 1,664 additions and 817 deletions.
39 changes: 25 additions & 14 deletions examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@
// include the library
#include <RadioLib.h>

// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
// 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
Expand All @@ -37,6 +38,14 @@ SX1278 radio = new Module(10, 2, 9, 3);
// 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);

Expand Down Expand Up @@ -77,13 +86,11 @@ void setup() {
// when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded
// and can be set to NULL

// some frequency bands only use a subset of the available channels
// you can select the specific band or set the first channel and last channel
// for example, either of the following corresponds to US915 FSB2 in TTN
/*
node.selectSubband(2);
node.selectSubband(8, 15);
*/

// 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
Expand Down Expand Up @@ -147,7 +154,11 @@ void loop() {
Serial.print(F("failed, code "));
Serial.println(state);
}

// wait before sending another packet
delay(30000);
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);
}
53 changes: 36 additions & 17 deletions examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@
// include the library
#include <RadioLib.h>

// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
// 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
Expand All @@ -38,6 +39,14 @@ SX1278 radio = new Module(10, 2, 9, 3);
// 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);

Expand Down Expand Up @@ -69,32 +78,38 @@ void setup() {
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

// some frequency bands only use a subset of the available channels
// you can select the specific band or set the first channel and last channel
// for example, either of the following corresponds to US915 FSB2 in TTN
/*
node.selectSubband(2);
node.selectSubband(8, 15);
*/

// if using EU868 on ABP in TTN, you need to set the SF for RX2 window manually
/*
node.rx2.drMax = 3;
*/

// to start a LoRaWAN v1.1 session, the user should also provide
// fNwkSIntKey and sNwkSIntKey similar to nwkSKey and appSKey
// 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, fNwkSIntKey, 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);
state = node.beginABP(devAddr, nwkSKey, appSKey, fNwkSIntKey, sNwkSIntKey);
if(state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Expand Down Expand Up @@ -149,5 +164,9 @@ void loop() {
}

// wait before sending another packet
delay(30000);
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
NOTE: LoRaWAN requires storing some parameters persistently!
RadioLib does this by using EEPROM, by default
starting at address 0 and using 384 bytes.
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
Expand All @@ -29,11 +29,12 @@
// include the library
#include <RadioLib.h>

// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
// 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
Expand All @@ -42,6 +43,14 @@ SX1278 radio = new Module(10, 2, 9, 3);
// 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);

Expand All @@ -56,33 +65,31 @@ void setup() {
while(true);
}

// first we need to initialize the device storage
// this will reset all persistently stored parameters
// NOTE: This should only be done once prior to first joining a network!
// After wiping persistent storage, you will also have to reset
// the end device in TTN and perform the join procedure again!
// Here, a delay is added to make sure that during re-flashing
// the .wipe() is not triggered and the session is lost
//delay(5000);
//node.wipe();

// now we can start the activation
// start the activation
// Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
// uint64_t joinEUI = 0x12AD1011B0C0FFEE;
// uint64_t devEUI = 0x70B3D57ED005E120;
// uint64_t devEUI = 0x70B3D57ED005E120;
// uint8_t nwkKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65,
// 0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 };
// uint8_t appKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
// 0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 };
// state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey);

// after the device has been activated,
// on EEPROM-enabled boards, after the device has been activated,
// the session can be restored without rejoining after device power cycle
// on EEPROM-enabled boards by calling "restore"
// by calling the same `beginOTAA()` or `beginABP()` function with the same keys
// or call `restore()` where it will restore any existing session
// `restore()` returns the active mode if it succeeded (OTAA or ABP)
Serial.print(F("[LoRaWAN] Resuming previous session ... "));
state = node.restore();
if(state == RADIOLIB_ERR_NONE) {
if(state >= RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
Serial.print(F("Restored an "));
if(state == RADIOLIB_LORAWAN_MODE_OTAA)
Serial.println(F("OTAA session."));
else {
Serial.println(F("ABP session."));
}
} else {
Serial.print(F("failed, code "));
Serial.println(state);
Expand Down Expand Up @@ -141,5 +148,9 @@ void loop() {
// wait before sending another packet
// alternatively, call a deepsleep function here
// make sure to send the radio to sleep as well using radio.sleep()
delay(30000);
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
// include the library
#include <RadioLib.h>

// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
// 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
Expand All @@ -40,6 +41,14 @@ SX1278 radio = new Module(10, 2, 9, 3);
// 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);

Expand Down Expand Up @@ -80,13 +89,6 @@ void setup() {
// when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded
// and can be set to NULL

// some frequency bands only use a subset of the available channels
// you can select the specific band or set the first channel and last channel
// for example, either of the following corresponds to US915 FSB2 in TTN
/*
node.selectSubband(2);
node.selectSubband(8, 15);
*/

// now we can start the activation
// this can take up to 10 seconds, and requires a LoRaWAN gateway in range
Expand All @@ -106,9 +108,11 @@ void setup() {
while(true);
}

// after the device has been activated,
// on EEPROM-enabled boards, after the device has been activated,
// the session can be restored without rejoining after device power cycle
// on EEPROM-enabled boards by calling "restore"
// 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();
Expand All @@ -126,11 +130,28 @@ void setup() {

// set a fixed datarate
node.setDatarate(5);
// in order to save the datarate persistent across reboot/deepsleep, use the following:
/*
node.setDatarate(5, true);
*/

// enable CSMA
// this 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 specific allowed airtime per uplink in milliseconds
// if not called, this corresponds to setDwellTime(true, 0)
// setting this to 0 corresponds to the band's maximum allowed dwell time by law
node.setDwellTime(true, 1000);
}

void loop() {
Expand All @@ -152,8 +173,11 @@ void loop() {
String strUp = "Hello World! #" + String(fcntUp);

// send a confirmed uplink to port 10 every 64th frame
// and also request the LinkCheck and DeviceTime MAC commands
if(fcntUp % 64 == 0) {
state = node.uplink(strUp, 10, true);
node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_LINK_CHECK);
node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_DEVICE_TIME);
} else {
state = node.uplink(strUp, 10);
}
Expand Down Expand Up @@ -228,6 +252,24 @@ void loop() {
Serial.println(event.port);

Serial.print(radio.getFrequencyError());

uint8_t margin = 0;
uint8_t gwCnt = 0;
if(node.getMacLinkCheckAns(&margin, &gwCnt)) {
Serial.print(F("[LoRaWAN] LinkCheck margin:\t"));
Serial.println(margin);
Serial.print(F("[LoRaWAN] LinkCheck count:\t"));
Serial.println(gwCnt);
}

uint32_t networkTime = 0;
uint8_t fracSecond = 0;
if(node.getMacDeviceTimeAns(&networkTime, &fracSecond, true)) {
Serial.print(F("[LoRaWAN] DeviceTime Unix:\t"));
Serial.println(networkTime);
Serial.print(F("[LoRaWAN] LinkCheck second:\t1/"));
Serial.println(fracSecond);
}

} else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
Serial.println(F("timeout!"));
Expand All @@ -244,5 +286,9 @@ void loop() {
*/

// wait before sending another packet
delay(30000);
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);
}
Loading

0 comments on commit cb02180

Please sign in to comment.