[LoRaWAN] improve examples, add unified sendReceive, bugfixes, add FSK
This commit is contained in:
parent
5796bbe746
commit
3df866e23d
13 changed files with 683 additions and 264 deletions
|
@ -7,14 +7,12 @@
|
|||
After your device is registered, you can run this example.
|
||||
The device will join the network and start uploading data.
|
||||
|
||||
NOTE: LoRaWAN requires storing some parameters persistently!
|
||||
RadioLib does this by using EEPROM, by default
|
||||
starting at address 0 and using 32 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.
|
||||
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.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
@ -53,13 +51,6 @@ 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!
|
||||
//node.wipe();
|
||||
|
||||
// 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
|
||||
|
@ -91,21 +82,16 @@ void setup() {
|
|||
// for example, either of the following corresponds to US915 FSB2 in TTN
|
||||
/*
|
||||
node.selectSubband(2);
|
||||
node.selectSubband(8, 16);
|
||||
node.selectSubband(8, 15);
|
||||
*/
|
||||
|
||||
// now we can start the activation
|
||||
// this can take up to 20 seconds, and requires a LoRaWAN gateway in range
|
||||
// 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 = 8;
|
||||
uint8_t joinDr = 4;
|
||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDr);
|
||||
*/
|
||||
// a specific band can be selected for joining in fixed bands (e.g. US915):
|
||||
/*
|
||||
uint8_t subband = 2;
|
||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, subband);
|
||||
*/
|
||||
Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
|
||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey);
|
||||
|
||||
|
@ -117,21 +103,6 @@ void setup() {
|
|||
while(true);
|
||||
}
|
||||
|
||||
// after the device has been activated,
|
||||
// the session can be restored without rejoining after device power cycle
|
||||
// on EEPROM-enabled boards by calling "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);
|
||||
}
|
||||
*/
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
// counter to keep track of transmitted packets
|
||||
|
@ -141,23 +112,10 @@ void loop() {
|
|||
// send uplink to port 10
|
||||
Serial.print(F("[LoRaWAN] Sending uplink packet ... "));
|
||||
String strUp = "Hello World! #" + String(count++);
|
||||
int state = node.uplink(strUp, 10);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
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 ... "));
|
||||
String strDown;
|
||||
state = node.downlink(strDown);
|
||||
int state = node.sendReceive(strUp, 10, strDown);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
Serial.println(F("received a downlink!"));
|
||||
|
||||
// print data of the packet (if there are any)
|
||||
Serial.print(F("[LoRaWAN] Data:\t\t"));
|
||||
|
@ -183,19 +141,13 @@ void loop() {
|
|||
Serial.println(F(" Hz"));
|
||||
|
||||
} else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
|
||||
Serial.println(F("timeout!"));
|
||||
Serial.println(F("no downlink!"));
|
||||
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// on EEPROM enabled boards, you can save the current session
|
||||
// by calling "saveSession" which allows retrieving the session after reboot or deepsleep
|
||||
/*
|
||||
node.saveSession();
|
||||
*/
|
||||
|
||||
// wait before sending another packet
|
||||
delay(30000);
|
||||
}
|
||||
|
|
|
@ -8,15 +8,13 @@
|
|||
The device will start uploading data directly,
|
||||
without having to join the network.
|
||||
|
||||
NOTE: LoRaWAN requires storing some parameters persistently!
|
||||
RadioLib does this by using EEPROM, by default
|
||||
starting at address 0 and using 32 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.
|
||||
|
||||
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.
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
|
@ -54,13 +52,6 @@ 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!
|
||||
//node.wipe();
|
||||
|
||||
// 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
|
||||
|
@ -87,7 +78,7 @@ void setup() {
|
|||
// for example, either of the following corresponds to US915 FSB2 in TTN
|
||||
/*
|
||||
node.selectSubband(2);
|
||||
node.selectSubband(8, 16);
|
||||
node.selectSubband(8, 15);
|
||||
*/
|
||||
|
||||
// if using EU868 on ABP in TTN, you need to set the SF for RX2 window manually
|
||||
|
@ -112,20 +103,6 @@ void setup() {
|
|||
while(true);
|
||||
}
|
||||
|
||||
// after the device has been activated,
|
||||
// the session can be restored without rejoining after device power cycle
|
||||
// on EEPROM-enabled boards by calling "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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// counter to keep track of transmitted packets
|
||||
|
@ -135,23 +112,10 @@ void loop() {
|
|||
// send uplink to port 10
|
||||
Serial.print(F("[LoRaWAN] Sending uplink packet ... "));
|
||||
String strUp = "Hello World! #" + String(count++);
|
||||
int state = node.uplink(strUp, 10);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
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 ... "));
|
||||
String strDown;
|
||||
state = node.downlink(strDown);
|
||||
int state = node.sendReceive(strUp, 10, strDown);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
Serial.println(F("received a downlink!"));
|
||||
|
||||
// print data of the packet (if there are any)
|
||||
Serial.print(F("[LoRaWAN] Data:\t\t"));
|
||||
|
@ -177,19 +141,13 @@ void loop() {
|
|||
Serial.println(F(" Hz"));
|
||||
|
||||
} else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
|
||||
Serial.println(F("timeout!"));
|
||||
Serial.println(F("no downlink!"));
|
||||
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// on EEPROM enabled boards, you can save the current session
|
||||
// by calling "saveSession" which allows retrieving the session after reboot or deepsleep
|
||||
/*
|
||||
node.saveSession();
|
||||
*/
|
||||
|
||||
// wait before sending another packet
|
||||
delay(10000);
|
||||
delay(30000);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
RadioLib LoRaWAN End Device Example
|
||||
|
||||
This example assumes you have tried one of the OTAA or ABP
|
||||
examples and are familiar with the required keys and procedures.
|
||||
This example restores and saves a session such that you can use
|
||||
deepsleep or survive power cycles. Before you start, you will
|
||||
have to register your device at https://www.thethingsnetwork.org/
|
||||
and join the network using either OTAA or ABP.
|
||||
Please refer to one of the other examples for more
|
||||
information regarding joining a network.
|
||||
|
||||
NOTE: LoRaWAN requires storing some parameters persistently!
|
||||
RadioLib does this by using EEPROM, by default
|
||||
starting at address 0 and using 384 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 <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 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
|
||||
// make sure you are using the correct band
|
||||
// based on your geographical location!
|
||||
LoRaWANNode node(&radio, &EU868);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// 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
|
||||
// Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
|
||||
// uint64_t joinEUI = 0x12AD1011B0C0FFEE;
|
||||
// uint64_t devEUI = 0x70B3D57ED005E120;
|
||||
// uint8_t nwkKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65,
|
||||
// 0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 };
|
||||
// uint8_t appKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
// 0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 };
|
||||
// state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey);
|
||||
|
||||
// after the device has been activated,
|
||||
// the session can be restored without rejoining after device power cycle
|
||||
// on EEPROM-enabled boards by calling "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);
|
||||
}
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// counter to keep track of transmitted packets
|
||||
int count = 0;
|
||||
|
||||
void loop() {
|
||||
// send uplink to port 10
|
||||
Serial.print(F("[LoRaWAN] Sending uplink packet ... "));
|
||||
String strUp = "Hello World! #" + String(count++);
|
||||
String strDown;
|
||||
int state = node.sendReceive(strUp, 10, strDown);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("received a downlink!"));
|
||||
|
||||
// print data of the packet (if there are any)
|
||||
Serial.print(F("[LoRaWAN] Data:\t\t"));
|
||||
if(strDown.length() > 0) {
|
||||
Serial.println(strDown);
|
||||
} else {
|
||||
Serial.println(F("<MAC commands only>"));
|
||||
}
|
||||
|
||||
// print RSSI (Received Signal Strength Indicator)
|
||||
Serial.print(F("[LoRaWAN] RSSI:\t\t"));
|
||||
Serial.print(radio.getRSSI());
|
||||
Serial.println(F(" dBm"));
|
||||
|
||||
// print SNR (Signal-to-Noise Ratio)
|
||||
Serial.print(F("[LoRaWAN] SNR:\t\t"));
|
||||
Serial.print(radio.getSNR());
|
||||
Serial.println(F(" dB"));
|
||||
|
||||
// print frequency error
|
||||
Serial.print(F("[LoRaWAN] Frequency error:\t"));
|
||||
Serial.print(radio.getFrequencyError());
|
||||
Serial.println(F(" Hz"));
|
||||
|
||||
} else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
|
||||
Serial.println(F("no downlink!"));
|
||||
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// on EEPROM enabled boards, you can save the current session
|
||||
// by calling "saveSession" which allows retrieving the session after reboot or deepsleep
|
||||
node.saveSession();
|
||||
|
||||
// wait before sending another packet
|
||||
// alternatively, call a deepsleep function here
|
||||
// make sure to send the radio to sleep as well using radio.sleep()
|
||||
delay(30000);
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
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.
|
||||
|
||||
Also, most of the possible and available functions are
|
||||
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.
|
||||
|
||||
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 <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 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
|
||||
// make sure you are using the correct band
|
||||
// based on your geographical location!
|
||||
LoRaWANNode node(&radio, &EU868);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.begin();
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
// 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);
|
||||
*/
|
||||
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!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
// after the device has been activated,
|
||||
// the session can be restored without rejoining after device power cycle
|
||||
// on EEPROM-enabled boards by calling "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
|
||||
node.setADR(false);
|
||||
|
||||
// set a fixed datarate
|
||||
node.setDatarate(5);
|
||||
|
||||
// enable CSMA
|
||||
// this tries to minimize packet loss by searching for a free channel
|
||||
// before actually sending an uplink
|
||||
node.setCSMA(6, 2, true);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int state = RADIOLIB_ERR_NONE;
|
||||
|
||||
// set battery fill level,
|
||||
// 0 = external power source
|
||||
// 1 = lowest (empty battery)
|
||||
// 254 = highest (full battery)
|
||||
// 255 = unable to measure
|
||||
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 ... "));
|
||||
String strUp = "Hello World! #" + String(fcntUp);
|
||||
|
||||
// send a confirmed uplink to port 10 every 64th frame
|
||||
if(fcntUp / 64 == 0) {
|
||||
state = node.uplink(strUp, 10, true);
|
||||
} else {
|
||||
state = node.uplink(strUp, 10);
|
||||
}
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
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 ... "));
|
||||
String strDown;
|
||||
state = node.downlink(strDown);
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
|
||||
// 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("<MAC commands only>"));
|
||||
}
|
||||
|
||||
// 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("timeout!"));
|
||||
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// on EEPROM enabled boards, you can save the current session
|
||||
// by calling "saveSession" which allows retrieving the session after reboot or deepsleep
|
||||
/*
|
||||
node.saveSession();
|
||||
*/
|
||||
|
||||
// wait before sending another packet
|
||||
delay(30000);
|
||||
}
|
|
@ -120,7 +120,7 @@ void inline ArduinoHal::spiEnd() {
|
|||
|
||||
void ArduinoHal::readPersistentStorage(uint32_t addr, uint8_t* buff, size_t len) {
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
#if defined(RADIOLIB_ESP32)
|
||||
#if defined(RADIOLIB_ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
EEPROM.begin(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE);
|
||||
#elif defined(ARDUINO_ARCH_APOLLO3)
|
||||
EEPROM.init();
|
||||
|
@ -128,7 +128,7 @@ void ArduinoHal::readPersistentStorage(uint32_t addr, uint8_t* buff, size_t len)
|
|||
for(size_t i = 0; i < len; i++) {
|
||||
buff[i] = EEPROM.read(addr + i);
|
||||
}
|
||||
#if defined(RADIOLIB_ESP32)
|
||||
#if defined(RADIOLIB_ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
EEPROM.end();
|
||||
#endif
|
||||
#endif
|
||||
|
@ -136,7 +136,7 @@ void ArduinoHal::readPersistentStorage(uint32_t addr, uint8_t* buff, size_t len)
|
|||
|
||||
void ArduinoHal::writePersistentStorage(uint32_t addr, uint8_t* buff, size_t len) {
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
#if defined(RADIOLIB_ESP32)
|
||||
#if defined(RADIOLIB_ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
EEPROM.begin(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE);
|
||||
#elif defined(ARDUINO_ARCH_APOLLO3)
|
||||
EEPROM.init();
|
||||
|
@ -144,7 +144,7 @@ void ArduinoHal::writePersistentStorage(uint32_t addr, uint8_t* buff, size_t len
|
|||
for(size_t i = 0; i < len; i++) {
|
||||
EEPROM.write(addr + i, buff[i]);
|
||||
}
|
||||
#if defined(RADIOLIB_ESP32)
|
||||
#if defined(RADIOLIB_ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
EEPROM.commit();
|
||||
EEPROM.end();
|
||||
#endif
|
||||
|
|
|
@ -1436,8 +1436,9 @@ uint32_t SX126x::getTimeOnAir(size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t SX126x::calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs) {
|
||||
(void)numSymbols; // not used for these modules
|
||||
uint32_t SX126x::calculateRxTimeout(uint32_t timeoutUs) {
|
||||
// the timeout value is given in units of 15.625 microseconds
|
||||
// the calling function should provide some extra width, as this number of units is truncated to integer
|
||||
uint32_t timeout = timeoutUs / 15.625;
|
||||
return(timeout);
|
||||
}
|
||||
|
|
|
@ -950,12 +950,11 @@ class SX126x: public PhysicalLayer {
|
|||
uint32_t getTimeOnAir(size_t len) override;
|
||||
|
||||
/*!
|
||||
\brief Calculate the timeout value for this specific module / series based on number of symbols or time
|
||||
\param numSymbols Number of payload symbols to listen for
|
||||
\brief Calculate the timeout value for this specific module / series (in number of symbols or units of time)
|
||||
\param timeoutUs Timeout in microseconds to listen for
|
||||
\returns Timeout value in a unit that is specific for the used module
|
||||
*/
|
||||
uint32_t calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs);
|
||||
uint32_t calculateRxTimeout(uint32_t timeoutUs);
|
||||
|
||||
/*!
|
||||
\brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks
|
||||
|
|
|
@ -411,7 +411,7 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) {
|
|||
|
||||
// timeout is only used in RxSingle, so when a packet length is defined, force mode to RxSingle
|
||||
// and set the timeout value to the expected number of symbols (usually preamble + header)
|
||||
if(len > 0) {
|
||||
if((len > 0) && (this->spreadingFactor > 6)) {
|
||||
mode = RADIOLIB_SX127X_RXSINGLE;
|
||||
state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_SYMB_TIMEOUT_LSB, len);
|
||||
}
|
||||
|
@ -1274,17 +1274,19 @@ uint32_t SX127x::getTimeOnAir(size_t len) {
|
|||
|
||||
}
|
||||
|
||||
uint32_t SX127x::calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs) {
|
||||
(void)timeoutUs;
|
||||
numSymbols = 20;
|
||||
uint32_t SX127x::calculateRxTimeout(uint32_t timeoutUs) {
|
||||
// the timeout is given as the number of symbols
|
||||
// the calling function should provide some extra width, as this number of symbols is truncated to integer
|
||||
// the order of operators is swapped here to decrease the effects of this truncation error
|
||||
float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth;
|
||||
uint32_t numSymbols = (timeoutUs / symbolLength) / 1000;
|
||||
return(numSymbols);
|
||||
}
|
||||
|
||||
int16_t SX127x::irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask) {
|
||||
irqFlags = RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DONE;
|
||||
irqMask = RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DONE;
|
||||
irqFlags &= RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_TIMEOUT;
|
||||
irqMask &= RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_TIMEOUT;
|
||||
// IRQ flags/masks are inverted to what seems logical for SX127x (0 being activated, 1 being deactivated)
|
||||
irqFlags = RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DEFAULT;
|
||||
irqMask = RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DONE & RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_TIMEOUT;
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
|
|
|
@ -160,6 +160,7 @@
|
|||
#define RADIOLIB_SX127X_MASK_IRQ_FLAG_CAD_DONE 0b11111011 // 2 2 CAD complete
|
||||
#define RADIOLIB_SX127X_MASK_IRQ_FLAG_FHSS_CHANGE_CHANNEL 0b11111101 // 1 1 FHSS change channel
|
||||
#define RADIOLIB_SX127X_MASK_IRQ_FLAG_CAD_DETECTED 0b11111110 // 0 0 valid LoRa signal detected during CAD operation
|
||||
#define RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DEFAULT 0b00011111 // 7 0 default for Rx (RX_TIMEOUT, RX_DONE, CRC_ERR)
|
||||
|
||||
// RADIOLIB_SX127X_REG_FIFO_TX_BASE_ADDR
|
||||
#define RADIOLIB_SX127X_FIFO_TX_BASE_ADDR_MAX 0b00000000 // 7 0 allocate the entire FIFO buffer for TX only
|
||||
|
@ -817,6 +818,7 @@ class SX127x: public PhysicalLayer {
|
|||
\brief Interrupt-driven receive method. DIO0 will be activated when full valid packet is received.
|
||||
\param len Expected length of packet to be received, or 0 when unused.
|
||||
Defaults to 0, non-zero required for LoRa spreading factor 6.
|
||||
If non-zero for LoRa spreading factor > 6, RxSingle is used and value must be given in symbols.
|
||||
\param mode Receive mode to be used. Defaults to RxContinuous.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
|
@ -1049,12 +1051,11 @@ class SX127x: public PhysicalLayer {
|
|||
uint32_t getTimeOnAir(size_t len) override;
|
||||
|
||||
/*!
|
||||
\brief Calculate the timeout value for this specific module / series based on number of symbols or time
|
||||
\param numSymbols Number of payload symbols to listen for
|
||||
\brief Calculate the timeout value for this specific module / series (in number of symbols or units of time)
|
||||
\param timeoutUs Timeout in microseconds to listen for
|
||||
\returns Timeout value in a unit that is specific for the used module
|
||||
*/
|
||||
uint32_t calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs);
|
||||
uint32_t calculateRxTimeout(uint32_t timeoutUs);
|
||||
|
||||
/*!
|
||||
\brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks
|
||||
|
|
|
@ -31,7 +31,6 @@ uint8_t getDownlinkDataRate(uint8_t uplink, uint8_t offset, uint8_t base, uint8_
|
|||
LoRaWANNode::LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band) {
|
||||
this->phyLayer = phy;
|
||||
this->band = band;
|
||||
this->FSK = false;
|
||||
this->rx2 = this->band->rx2;
|
||||
this->difsSlots = 2;
|
||||
this->backoffMax = 6;
|
||||
|
@ -51,7 +50,11 @@ void LoRaWANNode::wipe() {
|
|||
}
|
||||
|
||||
int16_t LoRaWANNode::restore() {
|
||||
// check the magic value
|
||||
// if already joined, ignore
|
||||
if(this->isJoinedFlag) {
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
||||
uint8_t nvm_table_version = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION_ID);
|
||||
|
@ -60,8 +63,13 @@ int16_t LoRaWANNode::restore() {
|
|||
// }
|
||||
(void)nvm_table_version;
|
||||
|
||||
// check the magic value
|
||||
if(mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) != RADIOLIB_LORAWAN_MAGIC) {
|
||||
RADIOLIB_DEBUG_PRINTLN("magic id: %d", mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID));
|
||||
RADIOLIB_DEBUG_PRINTLN("magic id not set (no saved session)");
|
||||
RADIOLIB_DEBUG_PRINTLN("first 16 bytes of NVM:");
|
||||
uint8_t nvmBuff[16];
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(0), nvmBuff, 16);
|
||||
RADIOLIB_DEBUG_HEXDUMP(nvmBuff, 16);
|
||||
// the magic value is not set, user will have to do perform the join procedure
|
||||
return(RADIOLIB_ERR_NETWORK_NOT_JOINED);
|
||||
}
|
||||
|
@ -126,6 +134,9 @@ int16_t LoRaWANNode::restore() {
|
|||
state = this->setPhyProperties();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// full session is restored, so set joined flag
|
||||
this->isJoinedFlag = true;
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
|
@ -179,21 +190,21 @@ int16_t LoRaWANNode::restoreChannels() {
|
|||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FREQS_ID), buffer, numBytes);
|
||||
for(uint8_t dir = 0; dir < 2; dir++) {
|
||||
for(uint8_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
uint8_t chBuff[5] = { 0 };
|
||||
uint8_t chBuff[bytesPerChannel] = { 0 };
|
||||
memcpy(chBuff, &buffer[(dir * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel) + i * bytesPerChannel], bytesPerChannel);
|
||||
this->availableChannels[dir][i].enabled = (chBuff[0] & 0x80) >> 7;
|
||||
this->availableChannels[dir][i].idx = chBuff[0] & 0x7F;
|
||||
uint32_t freq = LoRaWANNode::ntoh<uint32_t>(&chBuff[1], 3);
|
||||
this->availableChannels[dir][i].freq = (float)freq/10000.0;
|
||||
this->availableChannels[dir][i].drMax = (chBuff[0] & 0xF0) >> 4;
|
||||
this->availableChannels[dir][i].drMin = (chBuff[0] & 0x0F) >> 0;
|
||||
this->availableChannels[dir][i].drMax = (chBuff[4] & 0xF0) >> 4;
|
||||
this->availableChannels[dir][i].drMin = (chBuff[4] & 0x0F) >> 0;
|
||||
}
|
||||
}
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
#endif
|
||||
|
||||
int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, uint8_t drJoinSubband, bool force) {
|
||||
int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, uint8_t joinDr, bool force) {
|
||||
// check if we actually need to send the join request
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
||||
|
@ -210,7 +221,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// setup uplink/downlink frequencies and datarates
|
||||
state = this->selectChannelsJR(this->devNonce, drJoinSubband);
|
||||
state = this->selectChannelsJR(this->devNonce, joinDr);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// configure for uplink with default configuration
|
||||
|
@ -428,6 +439,8 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
|
||||
#endif
|
||||
|
||||
this->isJoinedFlag = true;
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
|
@ -480,9 +493,15 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey,
|
|||
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
|
||||
#endif
|
||||
|
||||
this->isJoinedFlag = true;
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
bool LoRaWANNode::isJoined() {
|
||||
return(this->isJoinedFlag);
|
||||
}
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
int16_t LoRaWANNode::saveSession() {
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
@ -618,8 +637,8 @@ int16_t LoRaWANNode::saveChannels() {
|
|||
uint8_t buffer[numBytes];
|
||||
for(uint8_t dir = 0; dir < 2; dir++) {
|
||||
for(uint8_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
uint8_t chBuff[5] = { 0 };
|
||||
chBuff[0] |= (uint8_t)this->availableChannels[dir][i].enabled << 7;
|
||||
uint8_t chBuff[bytesPerChannel] = { 0 };
|
||||
chBuff[0] = (uint8_t)this->availableChannels[dir][i].enabled << 7;
|
||||
chBuff[0] |= this->availableChannels[dir][i].idx;
|
||||
uint32_t freq = this->availableChannels[dir][i].freq*10000.0;
|
||||
LoRaWANNode::hton<uint32_t>(&chBuff[1], freq, 3);
|
||||
|
@ -648,9 +667,10 @@ int16_t LoRaWANNode::uplink(const char* str, uint8_t port, bool isConfirmed) {
|
|||
int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed) {
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
||||
// check if sufficient time has elapsed since the last uplink
|
||||
if(mod->hal->millis() - this->rxDelayStart < this->rxDelays[1]) {
|
||||
// not enough time elapsed since the last uplink, we may still be in an RX window
|
||||
// check if the Rx windows were closed after sending the previous uplink
|
||||
// this FORCES a user to call downlink() after an uplink()
|
||||
if(this->rxDelayEnd < this->rxDelayStart) {
|
||||
// not enough time elapsed since the last uplink, we may still be in an Rx window
|
||||
return(RADIOLIB_ERR_UPLINK_UNAVAILABLE);
|
||||
}
|
||||
|
||||
|
@ -761,9 +781,12 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
|||
}
|
||||
|
||||
// if the saved confirm-fcnt is set, set the ACK bit
|
||||
bool isConfirmingDown;
|
||||
if(this->confFcntDown != RADIOLIB_LORAWAN_FCNT_NONE) {
|
||||
isConfirmingDown = true;
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ACK;
|
||||
}
|
||||
(void)isConfirmingDown;
|
||||
|
||||
LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)this->fcntUp);
|
||||
|
||||
|
@ -870,16 +893,30 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
|||
// the downlink confirmation was acknowledged, so clear the counter value
|
||||
this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE;
|
||||
|
||||
// LoRaWANEvent:
|
||||
// dir = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK
|
||||
// confirmed = isConfirmed
|
||||
// confirming = isConfirmingDown
|
||||
// power = this->txPwrCur
|
||||
// fcnt = this->fcntUp
|
||||
// port = port
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::downlinkCommon() {
|
||||
// check if there are any upcoming Rx windows
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
const uint32_t scanGuard = 10;
|
||||
if(mod->hal->millis() - this->rxDelayStart > (this->rxDelays[1] + scanGuard)) {
|
||||
// time since last Tx is greater than RX2 delay + some guard period
|
||||
// we have nothing to downlink
|
||||
|
||||
// check if there are any upcoming Rx windows
|
||||
// if the Rx1 window has already started, you're too late, because most downlinks happen in Rx1
|
||||
if(mod->hal->millis() - this->rxDelayStart > (this->rxDelays[0] - scanGuard)) {
|
||||
// if between start of Rx1 and end of Rx2, wait until Rx2 closes
|
||||
if(mod->hal->millis() - this->rxDelayStart < this->rxDelays[1]) {
|
||||
mod->hal->delay(this->rxDelays[1] + this->rxDelayStart - mod->hal->millis());
|
||||
}
|
||||
// update the end timestamp in case user got stuck between uplink and downlink
|
||||
this->rxDelayEnd = mod->hal->millis();
|
||||
return(RADIOLIB_ERR_NO_RX_WINDOW);
|
||||
}
|
||||
|
||||
|
@ -894,8 +931,8 @@ int16_t LoRaWANNode::downlinkCommon() {
|
|||
}
|
||||
|
||||
// create the masks that are required for receiving downlinks
|
||||
uint16_t irqFlags;
|
||||
uint16_t irqMask;
|
||||
uint16_t irqFlags = 0x0000;
|
||||
uint16_t irqMask = 0x0000;
|
||||
this->phyLayer->irqRxDoneRxTimeout(irqFlags, irqMask);
|
||||
|
||||
this->phyLayer->setPacketReceivedAction(LoRaWANNodeOnDownlinkAction);
|
||||
|
@ -908,7 +945,7 @@ int16_t LoRaWANNode::downlinkCommon() {
|
|||
// according to the spec, this must be at least enough time to effectively detect a preamble
|
||||
// but pad it a bit on both sides (start and end) to make sure it is wide enough
|
||||
uint32_t timeoutHost = this->phyLayer->getTimeOnAir(0) + 2*scanGuard*1000;
|
||||
uint32_t timeoutMod = this->phyLayer->calculateRxTimeout(0, timeoutHost);
|
||||
uint32_t timeoutMod = this->phyLayer->calculateRxTimeout(timeoutHost);
|
||||
|
||||
// wait for the start of the Rx window
|
||||
// the waiting duration is shortened a bit to cover any possible timing errors
|
||||
|
@ -943,6 +980,8 @@ int16_t LoRaWANNode::downlinkCommon() {
|
|||
}
|
||||
|
||||
}
|
||||
// Rx windows are now closed
|
||||
this->rxDelayEnd = mod->hal->millis();
|
||||
|
||||
// if we got here due to a timeout, stop ongoing activities
|
||||
if(this->phyLayer->isRxTimeout()) {
|
||||
|
@ -1201,6 +1240,14 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
// a downlink was received, so reset the ADR counter to this uplink's fcnt
|
||||
this->adrFcnt = this->fcntUp;
|
||||
|
||||
// LoRaWANEvent:
|
||||
// dir = RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK
|
||||
// confirmed = isConfirmedDown
|
||||
// confirming = isConfirmingUp
|
||||
// power = this->txPwrCur
|
||||
// fcnt = isAppDownlink ? this->aFcntDown : this->nFcntDown
|
||||
// port = ...
|
||||
|
||||
// process payload (if there is any)
|
||||
if(payLen <= 0) {
|
||||
// no payload
|
||||
|
@ -1226,10 +1273,46 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
int16_t LoRaWANNode::sendReceive(String& strUp, uint8_t port, String& strDown, bool isConfirmed) {
|
||||
// send the uplink
|
||||
int16_t state = this->uplink(strUp, port, isConfirmed);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// wait for the downlink
|
||||
state = this->downlink(strDown);
|
||||
return(state);
|
||||
}
|
||||
#endif
|
||||
|
||||
int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed) {
|
||||
// send the uplink
|
||||
int16_t state = this->uplink(strUp, port, isConfirmed);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// wait for the downlink
|
||||
state = this->downlink(dataDown, lenDown);
|
||||
return(state);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed) {
|
||||
// send the uplink
|
||||
int16_t state = this->uplink(dataUp, lenUp, port, isConfirmed);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// wait for the downlink
|
||||
state = this->downlink(dataDown, lenDown);
|
||||
return(state);
|
||||
}
|
||||
|
||||
void LoRaWANNode::setDeviceStatus(uint8_t battLevel) {
|
||||
this->battLevel = battLevel;
|
||||
}
|
||||
|
||||
uint32_t LoRaWANNode::getFcntUp() {
|
||||
return(this->fcntUp);
|
||||
}
|
||||
|
||||
uint32_t LoRaWANNode::generateMIC(uint8_t* msg, size_t len, uint8_t* key) {
|
||||
if((msg == NULL) || (len == 0)) {
|
||||
return(0);
|
||||
|
@ -1261,24 +1344,9 @@ bool LoRaWANNode::verifyMIC(uint8_t* msg, size_t len, uint8_t* key) {
|
|||
|
||||
int16_t LoRaWANNode::setPhyProperties() {
|
||||
// set the physical layer configuration
|
||||
int16_t state = RADIOLIB_ERR_NONE;
|
||||
// if(this->FSK) {
|
||||
// // for FSK, configure the channel
|
||||
// state = this->phyLayer->setFrequency(this->band->fskFreq);
|
||||
// RADIOLIB_ASSERT(state);
|
||||
// DataRate_t dr;
|
||||
// dr.fsk.bitRate = 50;
|
||||
// dr.fsk.freqDev = 25;
|
||||
// state = this->phyLayer->setDataRate(dr);
|
||||
// RADIOLIB_ASSERT(state);
|
||||
// state = this->phyLayer->setDataShaping(RADIOLIB_SHAPING_1_0);
|
||||
// RADIOLIB_ASSERT(state);
|
||||
// state = this->phyLayer->setEncoding(RADIOLIB_ENCODING_WHITENING);
|
||||
// }
|
||||
// RADIOLIB_ASSERT(state);
|
||||
|
||||
|
||||
// set the maximum power supported by both the module and the band
|
||||
state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
|
||||
int16_t state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
|
||||
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
|
||||
// go from the highest power in band and lower it until we hit one supported by the module
|
||||
state = this->phyLayer->setOutputPower(this->txPwrCur--);
|
||||
|
@ -1383,17 +1451,21 @@ int16_t LoRaWANNode::setupChannels(uint8_t* cfList) {
|
|||
}
|
||||
|
||||
int16_t LoRaWANNode::selectSubband(uint8_t idx) {
|
||||
int16_t state = this->selectSubband((idx - 1) * 8, idx * 8);
|
||||
int16_t state = this->selectSubband((idx - 1) * 8, idx * 8 - 1);
|
||||
return(state);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::selectSubband(uint8_t startChannel, uint8_t endChannel) {
|
||||
if(this->isJoinedFlag) {
|
||||
RADIOLIB_DEBUG_PRINTLN("There is already an active session - cannot change subband");
|
||||
return(RADIOLIB_ERR_INVALID_CHANNEL);
|
||||
}
|
||||
if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) {
|
||||
RADIOLIB_DEBUG_PRINTLN("This is a dynamic band plan which does not support subbands");
|
||||
return(RADIOLIB_ERR_INVALID_CHANNEL);
|
||||
}
|
||||
|
||||
uint8_t numChannels = endChannel - startChannel;
|
||||
uint8_t numChannels = endChannel - startChannel + 1;
|
||||
if(startChannel > this->band->txSpans[0].numChannels) {
|
||||
RADIOLIB_DEBUG_PRINTLN("There are only %d channels available in this band", this->band->txSpans[0].numChannels);
|
||||
return(RADIOLIB_ERR_INVALID_CHANNEL);
|
||||
|
@ -1421,7 +1493,7 @@ int16_t LoRaWANNode::selectSubband(uint8_t startChannel, uint8_t endChannel) {
|
|||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::selectChannelsJR(uint16_t devNonce, uint8_t drJoinSubband) {
|
||||
int16_t LoRaWANNode::selectChannelsJR(uint16_t devNonce, uint8_t joinDr) {
|
||||
LoRaWANChannel_t channelUp;
|
||||
LoRaWANChannel_t channelDown;
|
||||
uint8_t drUp;
|
||||
|
@ -1453,15 +1525,15 @@ int16_t LoRaWANNode::selectChannelsJR(uint16_t devNonce, uint8_t drJoinSubband)
|
|||
}
|
||||
|
||||
// if join datarate is user-specified and valid, select that value; otherwise use
|
||||
if(drJoinSubband != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
|
||||
if(drJoinSubband >= channelUp.drMin && drJoinSubband <= channelUp.drMax) {
|
||||
drUp = drJoinSubband;
|
||||
if(joinDr != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
|
||||
if(joinDr >= channelUp.drMin && joinDr <= channelUp.drMax) {
|
||||
drUp = joinDr;
|
||||
} else {
|
||||
RADIOLIB_DEBUG_PRINTLN("Datarate %d is not valid (min: %d, max %d) - using default", drJoinSubband, channelUp.drMin, channelUp.drMax);
|
||||
drJoinSubband = RADIOLIB_LORAWAN_DATA_RATE_UNUSED;
|
||||
RADIOLIB_DEBUG_PRINTLN("Datarate %d is not valid (min: %d, max %d) - using default", joinDr, channelUp.drMin, channelUp.drMax);
|
||||
joinDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED;
|
||||
}
|
||||
}
|
||||
if(drJoinSubband == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
|
||||
if(joinDr == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
|
||||
drUp = int((channelUp.drMax + channelUp.drMin) / 2);
|
||||
}
|
||||
|
||||
|
@ -1470,27 +1542,39 @@ int16_t LoRaWANNode::selectChannelsJR(uint16_t devNonce, uint8_t drJoinSubband)
|
|||
drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase, channelDown.drMin, channelDown.drMax);
|
||||
|
||||
} else { // RADIOLIB_LORAWAN_BAND_FIXED
|
||||
channelUp.enabled = true;
|
||||
uint8_t numBlocks = this->band->txSpans[0].numChannels / 8; // calculate number of 8-channel blocks
|
||||
uint8_t numBlockChannels = 8 + (this->band->numTxSpans == 2 ? 1 : 0); // add a 9th channel if there's a second span
|
||||
uint8_t blockID = devNonce % numBlocks; // currently selected block (seed with devNonce)
|
||||
// if the user defined a specific subband, use that
|
||||
if(drJoinSubband == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
|
||||
blockID = (drJoinSubband - 1);
|
||||
uint8_t spanID = 0;
|
||||
uint8_t channelID = 0;
|
||||
uint8_t numEnabledChannels = 0;
|
||||
// if there are any predefined channels because user selected a subband, select one of these channels
|
||||
for(; numEnabledChannels < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; numEnabledChannels++) {
|
||||
if(this->availableChannels[numEnabledChannels][RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].enabled == false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint8_t channelID = this->phyLayer->random(numBlockChannels); // select randomly from these 8 or 9 channels
|
||||
RADIOLIB_DEBUG_PRINTLN("blocks: %d, channels/block: %d, blockID: %d, channelID: %d", numBlocks, numBlockChannels, blockID, channelID);
|
||||
|
||||
// if channel 0-7 is selected, retrieve this channel from span 0; otherwise span 1
|
||||
uint8_t spanID;
|
||||
if(channelID < 8) {
|
||||
spanID = 0;
|
||||
channelUp.idx = blockID * 8 + channelID;
|
||||
} else {
|
||||
spanID = 1;
|
||||
channelUp.idx = blockID;
|
||||
if(numEnabledChannels > 0) {
|
||||
uint8_t channelID = this->phyLayer->random(numEnabledChannels);
|
||||
channelUp = this->availableChannels[channelID][RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK];
|
||||
spanID = channelUp.idx / this->band->txSpans[0].numChannels;
|
||||
channelID = channelUp.idx;
|
||||
|
||||
} else { // no pre-selected subband, cycle through size-8 (or size-9) blocks
|
||||
channelUp.enabled = true;
|
||||
uint8_t numBlocks = this->band->txSpans[0].numChannels / 8; // calculate number of 8-channel blocks
|
||||
uint8_t numBlockChannels = 8 + (this->band->numTxSpans == 2 ? 1 : 0); // add a 9th channel if there's a second span
|
||||
uint8_t blockID = devNonce % numBlocks; // currently selected block (seed with devNonce)
|
||||
channelID = this->phyLayer->random(numBlockChannels); // select randomly from these 8 or 9 channels
|
||||
RADIOLIB_DEBUG_PRINTLN("blocks: %d, channels/block: %d, blockID: %d, channelID: %d", numBlocks, numBlockChannels, blockID, channelID);
|
||||
|
||||
// if channel 0-7 is selected, retrieve this channel from span 0; otherwise span 1
|
||||
if(channelID < 8) {
|
||||
spanID = 0;
|
||||
channelUp.idx = blockID * 8 + channelID;
|
||||
} else {
|
||||
spanID = 1;
|
||||
channelUp.idx = blockID;
|
||||
}
|
||||
channelUp.freq = this->band->txSpans[spanID].freqStart + channelUp.idx*this->band->txSpans[spanID].freqStep;
|
||||
}
|
||||
channelUp.freq = this->band->txSpans[spanID].freqStart + channelUp.idx*this->band->txSpans[spanID].freqStep;
|
||||
|
||||
// for fixed channel plans, the user-specified datarate is ignored and span-specific value must be used
|
||||
drUp = this->band->txSpans[spanID].joinRequestDataRate;
|
||||
|
@ -1548,26 +1632,32 @@ int16_t LoRaWANNode::selectChannels() {
|
|||
channelDn.drMin = this->band->rx1Span.drMin;
|
||||
channelDn.drMax = this->band->rx1Span.drMax;
|
||||
this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = channelDn;
|
||||
uint8_t drDown = getDownlinkDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], this->rx1DrOffset,
|
||||
this->band->rx1DataRateBase, channelDn.drMin, channelDn.drMax);
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown;
|
||||
|
||||
}
|
||||
uint8_t drDown = getDownlinkDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], this->rx1DrOffset, this->band->rx1DataRateBase,
|
||||
this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin, this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax);
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown;
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::setDatarate(uint8_t drUp) {
|
||||
if(drUp < this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMin) {
|
||||
// find the minimum and maximum available datarates by checking the enabled uplink channels
|
||||
uint8_t drMin = RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES;
|
||||
uint8_t drMax = 0;
|
||||
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) {
|
||||
drMin = RADIOLIB_MIN(drMin, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin);
|
||||
drMax = RADIOLIB_MAX(drMax, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax);
|
||||
}
|
||||
}
|
||||
if((drUp < drMin) || (drUp > drMax)) {
|
||||
RADIOLIB_DEBUG_PRINTLN("Cannot configure DR %d (min: %d, max: %d)", drUp, drMin, drMax);
|
||||
return(RADIOLIB_ERR_DATA_RATE_INVALID);
|
||||
}
|
||||
if(drUp > this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMax) {
|
||||
return(RADIOLIB_ERR_DATA_RATE_INVALID);
|
||||
}
|
||||
uint8_t drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase,
|
||||
this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin, this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax);
|
||||
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = drUp;
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown;
|
||||
|
||||
RADIOLIB_DEBUG_PRINTLN("Configured DR up = %d", drUp);
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
@ -1615,11 +1705,24 @@ int16_t LoRaWANNode::configureChannel(uint8_t dir) {
|
|||
int state = this->phyLayer->setFrequency(this->currentChannels[dir].freq);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// set the data rate
|
||||
DataRate_t dataRate;
|
||||
findDataRate(this->dataRates[dir], &dataRate);
|
||||
// if this channel is an FSK channel, toggle the FSK switch
|
||||
if(this->band->dataRates[this->dataRates[dir]] == RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K) {
|
||||
this->FSK = true;
|
||||
} else {
|
||||
this->FSK = false;
|
||||
}
|
||||
|
||||
DataRate_t dr;
|
||||
findDataRate(this->dataRates[dir], &dr);
|
||||
state = this->phyLayer->setDataRate(dr);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
if(this->FSK) {
|
||||
state = this->phyLayer->setDataShaping(RADIOLIB_SHAPING_1_0);
|
||||
RADIOLIB_ASSERT(state);
|
||||
state = this->phyLayer->setEncoding(RADIOLIB_ENCODING_WHITENING);
|
||||
}
|
||||
|
||||
state = this->phyLayer->setDataRate(dataRate);
|
||||
return(state);
|
||||
}
|
||||
|
||||
|
@ -1695,7 +1798,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
uint16_t chMask = LoRaWANNode::ntoh<uint16_t>(&cmd->payload[1]);
|
||||
uint8_t chMaskCntl = (cmd->payload[3] & 0x70) >> 4;
|
||||
uint8_t nbTrans = cmd->payload[3] & 0x0F;
|
||||
RADIOLIB_DEBUG_PRINTLN("ADR REQ: dataRate = %d, txPower = %d, chMask = 0x%02x, chMaskCntl = %02x, nbTrans = %d", drUp, txPower, chMask, chMaskCntl, nbTrans);
|
||||
RADIOLIB_DEBUG_PRINTLN("ADR REQ: dataRate = %d, txPower = %d, chMask = 0x%04x, chMaskCntl = %02x, nbTrans = %d", drUp, txPower, chMask, chMaskCntl, nbTrans);
|
||||
|
||||
// apply the configuration
|
||||
uint8_t drAck = 0;
|
||||
|
@ -1748,12 +1851,14 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
|
||||
}
|
||||
} else { // RADIOLIB_LORAWAN_BAND_FIXED
|
||||
// delete any prior ADR responses from the uplink queue, but do not care about if none is present yet
|
||||
// delete any prior ADR responses from the uplink queue, but do not care if none is present yet
|
||||
(void)deleteMacCommand(RADIOLIB_LORAWAN_MAC_CMD_LINK_ADR, &this->commandsUp);
|
||||
RADIOLIB_DEBUG_PRINTLN("mask[%d] = 0x%04x", chMaskCntl, chMask);
|
||||
uint8_t num = 0;
|
||||
uint8_t chNum = chMaskCntl*16;
|
||||
uint8_t chSpan = 0;
|
||||
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
RADIOLIB_DEBUG_PRINTLN("chNum: %d, chSpan: %d, i: %d, mask: %d", chNum, chSpan, i, chMask & (1UL << i));
|
||||
// if we must roll over to next span, reset chNum and move to next channel span
|
||||
if(chNum >= this->band->txSpans[chSpan].numChannels) {
|
||||
chNum = 0;
|
||||
|
@ -1771,9 +1876,10 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
chnl.freq = this->band->txSpans[chSpan].freqStart + chNum*this->band->txSpans[chSpan].freqStep;
|
||||
chnl.drMin = this->band->txSpans[chSpan].drMin;
|
||||
chnl.drMax = this->band->txSpans[chSpan].drMax;
|
||||
availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = chnl;
|
||||
availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = chnl;
|
||||
// downlink channels are dynamically calculated on each uplink in selectChannels()
|
||||
RADIOLIB_DEBUG_PRINTLN("Channel UL %d frequency = %f MHz", i, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq);
|
||||
RADIOLIB_DEBUG_PRINTLN("Channel UL %d (%d) frequency = %f MHz", num, chnl.idx, availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num].freq);
|
||||
num++;
|
||||
}
|
||||
chNum++;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
#define RADIOLIB_LORAWAN_BAND_DYNAMIC (0)
|
||||
#define RADIOLIB_LORAWAN_BAND_FIXED (1)
|
||||
#define RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES (15)
|
||||
#define RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE 0xFF
|
||||
#define RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE (0xFF >> 1) // reserve first bit for enable-flag
|
||||
|
||||
// recommended default settings
|
||||
#define RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS (1000)
|
||||
|
@ -317,20 +317,6 @@ struct LoRaWANMacCommandQueue_t {
|
|||
*/
|
||||
class LoRaWANNode {
|
||||
public:
|
||||
/*! \brief Set to true to force the node to only use FSK channels. Set to false by default. */
|
||||
bool FSK;
|
||||
|
||||
/*! \brief Starting channel offset.
|
||||
Some band plans only support a subset of available channels.
|
||||
Set to a positive value to set the first channel that will be used (e.g. 8 for US915 FSB2 used by TTN).
|
||||
By default -1 (no channel offset). */
|
||||
int8_t startChannel;
|
||||
|
||||
/*! \brief Number of supported channels.
|
||||
Some band plans only support a subset of available channels.
|
||||
Set to a positive value to set the number of channels that will be used
|
||||
(e.g. 8 for US915 FSB2 used by TTN). By default -1 (no channel offset). */
|
||||
int8_t numChannels;
|
||||
|
||||
// Offset between TX and RX1 (such that RX1 has equal or lower DR)
|
||||
uint8_t rx1DrOffset;
|
||||
|
@ -338,19 +324,6 @@ class LoRaWANNode {
|
|||
// RX2 channel properties - may be changed by MAC command
|
||||
LoRaWANChannel_t rx2;
|
||||
|
||||
/*!
|
||||
\brief Num of Back Off(BO) slots to be decremented after DIFS phase. 0 to disable BO.
|
||||
A random BO avoids collisions in the case where two or more nodes start the CSMA
|
||||
process at the same time.
|
||||
*/
|
||||
uint8_t backoffMax;
|
||||
|
||||
/*! \brief Num of CADs to estimate a clear CH. */
|
||||
uint8_t difsSlots;
|
||||
|
||||
/*! \brief enable/disable CSMA for LoRaWAN. */
|
||||
bool enableCSMA;
|
||||
|
||||
/*!
|
||||
\brief Default constructor.
|
||||
\param phy Pointer to the PhysicalLayer radio module.
|
||||
|
@ -379,12 +352,12 @@ class LoRaWANNode {
|
|||
\param devEUI 8-byte device identifier.
|
||||
\param nwkKey Pointer to the network AES-128 key.
|
||||
\param appKey Pointer to the application AES-128 key.
|
||||
\param drJoinSubband (OTAA:) The datarate at which to send the join-request; (ABP:) the subband at which to send the join-request
|
||||
\param joinDr (OTAA:) The datarate at which to send the join-request; (ABP:) ignored
|
||||
\param force Set to true to force joining even if previously joined.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, uint8_t joinDrSubband = RADIOLIB_LORAWAN_DATA_RATE_UNUSED, bool force = false);
|
||||
int16_t beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, uint8_t joinDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED, bool force = false);
|
||||
|
||||
/*!
|
||||
\brief Join network by performing activation by personalization.
|
||||
|
@ -399,6 +372,9 @@ class LoRaWANNode {
|
|||
*/
|
||||
int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL, bool force = false);
|
||||
|
||||
/*! \brief Whether there is an ongoing session active */
|
||||
bool isJoined();
|
||||
|
||||
/*!
|
||||
\brief Save the current state of the session.
|
||||
All variables are compared to what is saved and only the differences are rewritten.
|
||||
|
@ -436,12 +412,6 @@ class LoRaWANNode {
|
|||
*/
|
||||
int16_t uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed = false);
|
||||
|
||||
/*!
|
||||
\brief Wait for, open and listen during Rx1 and Rx2 windows; only performs listening
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t downlinkCommon();
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
/*!
|
||||
\brief Wait for downlink from the server in either RX1 or RX2 window.
|
||||
|
@ -459,6 +429,41 @@ class LoRaWANNode {
|
|||
*/
|
||||
int16_t downlink(uint8_t* data, size_t* len);
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
/*!
|
||||
\brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window.
|
||||
\param strUp Address of Arduino String that will be transmitted.
|
||||
\param port Port number to send the message to.
|
||||
\param strDown Address of Arduino String to save the received data.
|
||||
\param isConfirmed Whether to send a confirmed uplink or not.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t sendReceive(String& strUp, uint8_t port, String& strDown, bool isConfirmed = false);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window.
|
||||
\param strUp C-string that will be transmitted.
|
||||
\param port Port number to send the message to.
|
||||
\param dataDown Buffer to save received data into.
|
||||
\param lenDown Pointer to variable that will be used to save the number of received bytes.
|
||||
\param isConfirmed Whether to send a confirmed uplink or not.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t sendReceive(const char* strUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false);
|
||||
|
||||
/*!
|
||||
\brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window.
|
||||
\param dataUp Data to send.
|
||||
\param lenUp Length of the data.
|
||||
\param port Port number to send the message to.
|
||||
\param dataDown Buffer to save received data into.
|
||||
\param lenDown Pointer to variable that will be used to save the number of received bytes.
|
||||
\param isConfirmed Whether to send a confirmed uplink or not.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false);
|
||||
|
||||
/*!
|
||||
\brief Set device status.
|
||||
\param battLevel Battery level to set. 0 for external power source, 1 for lowest battery,
|
||||
|
@ -466,9 +471,12 @@ class LoRaWANNode {
|
|||
*/
|
||||
void setDeviceStatus(uint8_t battLevel);
|
||||
|
||||
/*! \brief Returns the last uplink's frame counter */
|
||||
uint32_t getFcntUp();
|
||||
|
||||
/*!
|
||||
\brief Set uplink datarate. This should _not_ be used when ADR is enabled.
|
||||
\param dr Datarate to use for uplinks
|
||||
\brief Set uplink datarate. This should not be used when ADR is enabled.
|
||||
\param dr Datarate to use for uplinks.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t setDatarate(uint8_t drUp);
|
||||
|
@ -480,16 +488,18 @@ class LoRaWANNode {
|
|||
void setADR(bool enable = true);
|
||||
|
||||
/*!
|
||||
\brief Select a single subband (8 channels) for fixed bands such as US915
|
||||
\brief Select a single subband (8 channels) for fixed bands such as US915.
|
||||
Only available before joining a network.
|
||||
\param idx The subband to be used (starting from 1!)
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t selectSubband(uint8_t idx);
|
||||
|
||||
/*!
|
||||
\brief Select a set of channels for fixed bands such as US915
|
||||
\brief Select a set of channels for fixed bands such as US915.
|
||||
Only available before joining a network.
|
||||
\param startChannel The first channel of the band to be used (inclusive)
|
||||
\param endChannel The last channel of the band to be used (exclusive)
|
||||
\param endChannel The last channel of the band to be used (inclusive)
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t selectSubband(uint8_t startChannel, uint8_t endChannel);
|
||||
|
@ -545,8 +555,25 @@ class LoRaWANNode {
|
|||
uint32_t confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE;
|
||||
uint32_t adrFcnt = 0;
|
||||
|
||||
// whether the current configured channel is in FSK mode
|
||||
bool FSK;
|
||||
|
||||
// flag that shows whether the device is joined and there is an ongoing session
|
||||
bool isJoinedFlag = false;
|
||||
|
||||
// ADR is enabled by default
|
||||
bool adrEnabled = true;
|
||||
|
||||
// enable/disable CSMA for LoRaWAN
|
||||
bool enableCSMA;
|
||||
|
||||
// number of backoff slots to be decremented after DIFS phase. 0 to disable BO.
|
||||
// A random BO avoids collisions in the case where two or more nodes start the CSMA
|
||||
// process at the same time.
|
||||
uint8_t backoffMax;
|
||||
|
||||
// number of CADs to estimate a clear CH
|
||||
uint8_t difsSlots;
|
||||
|
||||
// available channel frequencies from list passed during OTA activation
|
||||
LoRaWANChannel_t availableChannels[2][RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS] = { { 0 }, { 0 } };
|
||||
|
@ -563,6 +590,9 @@ class LoRaWANNode {
|
|||
// timestamp to measure the RX1/2 delay (from uplink end)
|
||||
uint32_t rxDelayStart = 0;
|
||||
|
||||
// timestamp when the Rx1/2 windows were closed (timeout or uplink received)
|
||||
uint32_t rxDelayEnd = 0;
|
||||
|
||||
// delays between the uplink and RX1/2 windows
|
||||
uint32_t rxDelays[2] = { RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS, RADIOLIB_LORAWAN_RECEIVE_DELAY_2_MS };
|
||||
|
||||
|
@ -588,6 +618,9 @@ class LoRaWANNode {
|
|||
int16_t restoreFcntUp();
|
||||
#endif
|
||||
|
||||
// wait for, open and listen during Rx1 and Rx2 windows; only performs listening
|
||||
int16_t downlinkCommon();
|
||||
|
||||
// method to generate message integrity code
|
||||
uint32_t generateMIC(uint8_t* msg, size_t len, uint8_t* key);
|
||||
|
||||
|
|
|
@ -294,8 +294,7 @@ uint32_t PhysicalLayer::getTimeOnAir(size_t len) {
|
|||
return(0);
|
||||
}
|
||||
|
||||
uint32_t PhysicalLayer::calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs) {
|
||||
(void)numSymbols;
|
||||
uint32_t PhysicalLayer::calculateRxTimeout(uint32_t timeoutUs) {
|
||||
(void)timeoutUs;
|
||||
return(0);
|
||||
}
|
||||
|
|
|
@ -311,12 +311,11 @@ class PhysicalLayer {
|
|||
virtual uint32_t getTimeOnAir(size_t len);
|
||||
|
||||
/*!
|
||||
\brief Calculate the timeout value for this specific module / series based on number of symbols or time
|
||||
\param numSymbols Number of payload symbols to listen for
|
||||
\brief Calculate the timeout value for this specific module / series (in number of symbols or units of time)
|
||||
\param timeoutUs Timeout in microseconds to listen for
|
||||
\returns Timeout value in a unit that is specific for the used module
|
||||
*/
|
||||
virtual uint32_t calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs);
|
||||
virtual uint32_t calculateRxTimeout(uint32_t timeoutUs);
|
||||
|
||||
/*!
|
||||
\brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks
|
||||
|
|
Loading…
Add table
Reference in a new issue