[LoRaWAN] Rework bands, official Rx windows, support ADR, confirm frames, improve EEPROM handling, support clock drift (#867)

* [LoRaWAN] rework bands, add ADR, partial MAC support

Known problem: terribly bad at receiving downlinks
Mask-list bands (e.g. US915) untested, likely a few bugs

* [LoRaWAN] Change Rx windows from CAD to RxSingle

* [LoRaWAN] improve persistence, better Rx windows, wear leveling, confirmed frames

* [LoRaWAN] Module-independent (OTAA) Rx windows, fix confirming downlinks

* [LoRaWAN] Implement SX127x support, fix MAC uplinking, support clock drift

* [ArduinoHal] fix clock drift calculation

* [LoRaWAN] Improve band & ADR logic, allow setting ADR, DR, subband, update examples

* [LoRaWAN] Fix EU868 coding rate, improve example

* [LoRaWAN] fix unused channel index

* [LoRaWAN] fix merge issue (deleted line)

* [LoRaWAN] fix CSMA calling now incorrect function

* [LoRaWAN] fix include logic

* [LoRaWAN] fix warnings, remove duplicate function

* [LoRaWAN] improve examples, add unified sendReceive, bugfixes, add FSK

* [LoRaWAN] improve examples

* [LoRaWAN] add new keywords, add debug guard

* [SX127x] Updated startReceive interface to be more in line with SX126x

* [SX127x] Added public method to convert from bytes to symbols

* [LoRaWAN] Update start receive for SX127x

* Added note about LoRaWAN beta

* [SX127x] Fixed potential float overflow

---------

Co-authored-by: jgromes <jan.gromes@gmail.com>
This commit is contained in:
StevenCellist 2023-11-12 14:02:39 +01:00 committed by GitHub
parent ce202deb7f
commit 82258105b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 2596 additions and 1807 deletions

View file

@ -43,7 +43,8 @@ SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x
* [__POCSAG__](https://www.sigidwiki.com/wiki/POCSAG) using 2-FSK for modules:
SX127x, RFM9x, RF69, SX1231, CC1101, nRF24L01, RFM2x and Si443x
* [__LoRaWAN__](https://lora-alliance.org/) using LoRa for modules:
SX127x, RFM9x, SX126x and SX128x
SX127x, RFM9x, SX126x and SX128x
* NOTE: LoRaWAN support is currently in beta, feedback via [Issues](https://github.com/jgromes/RadioLib/issues) and [Discussions](https://github.com/jgromes/RadioLib/discussions) is appreciated!
### Supported Arduino platforms:
* __Arduino__

View file

@ -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
@ -87,17 +78,23 @@ void setup() {
// and can be set to NULL
// some frequency bands only use a subset of the available channels
// you can set the starting channel and their number
// for example, the following corresponds to US915 FSB2 in TTN
// 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.startChannel = 8;
node.numChannels = 8;
node.selectSubband(2);
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 = 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 {
@ -106,20 +103,6 @@ void setup() {
while(true);
}
// after the device has been activated,
// network can be rejoined after device power cycle
// by calling "begin"
/*
Serial.print(F("[LoRaWAN] Resuming previous session ... "));
state = node.begin();
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
@ -129,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"));
@ -171,7 +141,7 @@ 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 "));
@ -179,5 +149,5 @@ void loop() {
}
// wait before sending another packet
delay(10000);
delay(30000);
}

View file

@ -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
@ -83,16 +74,27 @@ void setup() {
// and can be set to NULL
// some frequency bands only use a subset of the available channels
// you can set the starting channel and their number
// for example, the following corresponds to US915 FSB2 in TTN
// 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.startChannel = 8;
node.numChannels = 8;
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
/*
state = node.beginABP(devAddr, nwkSKey, appSKey, fNwkSIntKey, sNwkSIntKey);
*/
// start the device by directly providing the encryption keys and device address
Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
state = node.beginAPB(devAddr, (uint8_t*)nwkSKey, (uint8_t*)appSKey);
state = node.beginABP(devAddr, nwkSKey, appSKey);
if(state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
@ -101,20 +103,6 @@ void setup() {
while(true);
}
// after the device has been activated,
// network can be rejoined after device power cycle
// by calling "begin"
/*
Serial.print(F("[LoRaWAN] Resuming previous session ... "));
state = node.begin();
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
@ -124,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"));
@ -166,7 +141,7 @@ 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 "));
@ -174,5 +149,5 @@ void loop() {
}
// wait before sending another packet
delay(10000);
delay(30000);
}

View file

@ -0,0 +1,145 @@
/*
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);
}
}
// 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);
}

View file

@ -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);
}

View file

@ -124,7 +124,6 @@ setSyncWord KEYWORD2
setOutputPower KEYWORD2
setCurrentLimit KEYWORD2
setPreambleLength KEYWORD2
invertPreamble KEYWORD2
setGain KEYWORD2
getFrequencyError KEYWORD2
getRSSI KEYWORD2
@ -222,7 +221,6 @@ spectralScanStart KEYWORD2
spectralScanAbort KEYWORD2
spectralScanGetStatus KEYWORD2
spectralScanGetResult KEYWORD2
setPaConfig KEYWORD2
# nRF24
setIrqAction KEYWORD2
@ -291,12 +289,18 @@ setModem KEYWORD2
# LoRaWAN
wipe KEYWORD2
restoreOTAA KEYWORD2
restore KEYWORD2
beginOTAA KEYWORD2
beginABP KEYWORD2
saveSession KEYWORD2
uplink KEYWORD2
downlink KEYWORD2
configureChannel KEYWORD2
sendReceive KEYWORD2
setDeviceStatus KEYWORD2
setDatarate KEYWORD2
setADR KEYWORD2
selectSubband KEYWORD2
setCSMA KEYWORD2
#######################################
# Constants (LITERAL1)
@ -408,4 +412,5 @@ RADIOLIB_ERR_COMMAND_QUEUE_EMPTY LITERAL1
RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND LITERAL1
RADIOLIB_ERR_JOIN_NONCE_INVALID LITERAL1
RADIOLIB_ERR_N_FCNT_DOWN_INVALID LITERAL1
RADIOLIB_ERR_A_FCNT_DOWN_INVALID LITERAL1
RADIOLIB_ERR_A_FCNT_DOWN_INVALID LITERAL1
RADIOLIB_ERR_DATA_RATE_INVALID LITERAL1

View file

@ -58,19 +58,35 @@ void inline ArduinoHal::detachInterrupt(uint32_t interruptNum) {
}
void inline ArduinoHal::delay(unsigned long ms) {
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
::delay(ms);
#else
::delay(ms * 1000 / (1000 + RADIOLIB_CLOCK_DRIFT_MS));
#endif
}
void inline ArduinoHal::delayMicroseconds(unsigned long us) {
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
::delayMicroseconds(us);
#else
::delayMicroseconds(us * 1000 / (1000 + RADIOLIB_CLOCK_DRIFT_MS));
#endif
}
unsigned long inline ArduinoHal::millis() {
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
return(::millis());
#else
return(::millis() * 1000 / (1000 + RADIOLIB_CLOCK_DRIFT_MS));
#endif
}
unsigned long inline ArduinoHal::micros() {
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
return(::micros());
#else
return(::micros() * 1000 / (1000 + RADIOLIB_CLOCK_DRIFT_MS));
#endif
}
long inline ArduinoHal::pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) {

View file

@ -442,7 +442,19 @@
// the amount of space allocated to the persistent storage
#if !defined(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE)
#define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0xD0)
#define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0x0180)
#endif
/*
* Uncomment on boards whose clock runs too slow or too fast
* Set the value according to the following scheme:
* Enable timestamps on your terminal
* Print something to terminal, wait 1000 milliseconds, print something again
* If the difference is e.g. 1014 milliseconds between the prints, set this value to 14
* Or, for more accuracy, wait for 100,000 milliseconds and divide the total drift by 100
*/
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
//#define RADIOLIB_CLOCK_DRIFT_MS (0)
#endif
// This only compiles on STM32 boards with SUBGHZ module, but also

View file

@ -60,14 +60,14 @@ uint32_t RadioLibHal::getPersistentAddr(uint32_t id) {
}
template<typename T>
void RadioLibHal::setPersistentParameter(uint32_t id, T val) {
void RadioLibHal::setPersistentParameter(uint32_t id, T val, uint32_t offset) {
uint8_t *ptr = (uint8_t*)&val;
this->writePersistentStorage(RADIOLIB_HAL_PERSISTENT_STORAGE_BASE + RadioLibPersistentParamTable[id], ptr, sizeof(T));
this->writePersistentStorage(RADIOLIB_HAL_PERSISTENT_STORAGE_BASE + RadioLibPersistentParamTable[id] + offset, ptr, sizeof(T));
}
template void RadioLibHal::setPersistentParameter(uint32_t id, uint8_t val);
template void RadioLibHal::setPersistentParameter(uint32_t id, uint16_t val);
template void RadioLibHal::setPersistentParameter(uint32_t id, uint32_t val);
template void RadioLibHal::setPersistentParameter(uint32_t id, uint8_t val, uint32_t offset);
template void RadioLibHal::setPersistentParameter(uint32_t id, uint16_t val, uint32_t offset);
template void RadioLibHal::setPersistentParameter(uint32_t id, uint32_t val, uint32_t offset);
template<typename T>
T RadioLibHal::getPersistentParameter(uint32_t id) {

View file

@ -7,47 +7,59 @@
#include "BuildOpt.h"
// list of persistent parameters
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID (0) // this is NOT the LoRaWAN version, but version of this table
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (1)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID (2)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID (3)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID (4)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID (5)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID (6)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID (7)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID (8)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID (9)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID (10)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID (11)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID (12)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID (13)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID (14)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID (15)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_ID (16)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_ID (17)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID (18)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION_ID (0) // this is NOT the LoRaWAN version, but version of this table
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (1)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID (2)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID (3)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID (4)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID (5)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID (6)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID (7)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NBTRANS_ID (8)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID (9)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID (10)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID (11)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID (12)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID (13)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID (14)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID (15)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID (16)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID (17)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID (18)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_UP_ID (19)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_DOWN_ID (20)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_FCNT_ID (21)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID (22)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID (23)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FREQS_ID (24)
static const uint32_t RadioLibPersistentParamTable[] = {
0x00, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION
0x08, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID
0x00, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION_ID
0x01, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID
0x03, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION
0x04, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID
0x05, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID
0x06, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID
0x07, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID
0x0A, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID
0x0B, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NBTRANS_ID
0x0C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID
0x10, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID
0x20, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID
0x30, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID
0x40, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID
0x50, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID
0x54, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID
0x58, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID
0x5C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID
0x60, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST
0x70, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID
0x74, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID
0x78, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_AFCNT_DOWN_ID
0x7C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NFCNT_DOWN_ID
0x80, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_ID
0x84, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_ID
0x88, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID
0xD0, // end
0x50, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID
0x54, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID
0x58, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID
0x5C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID
0x60, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID
0x64, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_UP_ID
0x68, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_DOWN_ID
0x6C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_FCNT_ID
0x70, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID
0x8E, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID
0xD0, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FREQS_ID
0x0180, // end
};
/*!
@ -287,9 +299,10 @@ class RadioLibHal {
will be stored in the system endian!
\param id Parameter ID to save at.
\param val Value to set.
\param offset An additional offset added to the address.
*/
template<typename T>
void setPersistentParameter(uint32_t id, T val);
void setPersistentParameter(uint32_t id, T val, uint32_t offset = 0);
/*!
\brief Method to get arbitrary parameter from persistent storage.

View file

@ -553,6 +553,11 @@
*/
#define RADIOLIB_ERR_A_FCNT_DOWN_INVALID (-1114)
/*!
\brief Datarate requested by user is invalid.
*/
#define RADIOLIB_ERR_DATA_RATE_INVALID (-1115)
/*!
\}
*/

View file

@ -1436,6 +1436,25 @@ uint32_t SX126x::getTimeOnAir(size_t len) {
}
}
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);
}
int16_t SX126x::irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask) {
irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT; // flags that can appear in the IRQ register
irqMask = RADIOLIB_SX126X_IRQ_RX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT; // flags that will trigger DIO0
return(RADIOLIB_ERR_NONE);
}
bool SX126x::isRxTimeout() {
uint16_t irq = getIrqStatus();
bool rxTimedOut = irq & RADIOLIB_SX126X_IRQ_TIMEOUT;
return(rxTimedOut);
}
int16_t SX126x::implicitHeader(size_t len) {
return(setHeaderType(RADIOLIB_SX126X_LORA_HEADER_IMPLICIT, len));
}

View file

@ -56,7 +56,7 @@
#define RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS 0x8C
#define RADIOLIB_SX126X_CMD_SET_CAD_PARAMS 0x88
#define RADIOLIB_SX126X_CMD_SET_BUFFER_BASE_ADDRESS 0x8F
#define RADIOLIB_SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT 0x0A
#define RADIOLIB_SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT 0xA0
// status commands
#define RADIOLIB_SX126X_CMD_GET_STATUS 0xC0
@ -219,7 +219,7 @@
#define RADIOLIB_SX126X_IRQ_HEADER_ERR 0b0000000000100000 // 5 5 LoRa header CRC error
#define RADIOLIB_SX126X_IRQ_HEADER_VALID 0b0000000000010000 // 4 4 valid LoRa header received
#define RADIOLIB_SX126X_IRQ_SYNC_WORD_VALID 0b0000000000001000 // 3 3 valid sync word detected
#define RADIOLIB_SX126X_IRQ_RADIOLIB_PREAMBLE_DETECTED 0b0000000000000100 // 2 2 preamble detected
#define RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED 0b0000000000000100 // 2 2 preamble detected
#define RADIOLIB_SX126X_IRQ_RX_DONE 0b0000000000000010 // 1 1 packet received
#define RADIOLIB_SX126X_IRQ_TX_DONE 0b0000000000000001 // 0 0 packet transmission completed
#define RADIOLIB_SX126X_IRQ_RX_DEFAULT 0b0000001001100010 // 14 0 default for Rx (RX_DONE, TIMEOUT, CRC_ERR and HEADER_ERR)
@ -949,6 +949,27 @@ class SX126x: public PhysicalLayer {
*/
uint32_t getTimeOnAir(size_t len) override;
/*!
\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(uint32_t timeoutUs);
/*!
\brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks
\param irqFlags The flags for which IRQs must be triggered
\param irqMask Mask indicating which IRQ triggers a DIO
\returns \ref status_codes
*/
int16_t irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask);
/*!
\brief Check whether the IRQ bit for RxTimeout is set
\returns \ref RxTimeout IRQ is set
*/
bool isRxTimeout();
/*!
\brief Set implicit header mode for future reception/transmission.
\param len Payload length in bytes.

View file

@ -432,10 +432,20 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) {
return(setMode(mode));
}
int16_t SX127x::startReceive(uint32_t mode, uint16_t irqFlags, uint16_t irqMask, size_t len) {
int16_t SX127x::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) {
(void)irqFlags;
(void)irqMask;
return(startReceive((uint8_t)len, (uint8_t)mode));
uint8_t mode = RADIOLIB_SX127X_RXCONTINUOUS;
if(timeout != 0) {
// for non-zero timeout value, change mode to Rx single and set the timeout
mode = RADIOLIB_SX127X_RXSINGLE;
uint8_t msb_sym = (timeout > 0x3FF) ? 0x3 : (uint8_t)(timeout >> 8);
uint8_t lsb_sym = (timeout > 0x3FF) ? 0xFF : (uint8_t)(timeout & 0xFF);
int16_t state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_2, msb_sym, 1, 0);
state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_SYMB_TIMEOUT_LSB, lsb_sym);
RADIOLIB_ASSERT(state);
}
return(startReceive((uint8_t)len, mode));
}
void SX127x::setDio0Action(void (*func)(void), uint32_t dir) {
@ -1221,28 +1231,45 @@ int16_t SX127x::variablePacketLengthMode(uint8_t maxLen) {
return(SX127x::setPacketMode(RADIOLIB_SX127X_PACKET_VARIABLE, maxLen));
}
float SX127x::getNumSymbols(size_t len) {
// get symbol length in us
float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth;
// get Low Data Rate optimization flag
float de = 0;
if (symbolLength >= 16.0) {
de = 1;
}
// get explicit/implicit header enabled flag
float ih = (float) this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_1, 0, 0);
// get CRC enabled flag
float crc = (float) (this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_2, 2, 2) >> 2);
// get number of preamble symbols
float n_pre = (float) ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_MSB) << 8) | this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_LSB));
// get number of payload symbols
float n_pay = 8.0 + RADIOLIB_MAX(ceil((8.0 * (float) len - 4.0 * (float) this->spreadingFactor + 28.0 + 16.0 * crc - 20.0 * ih) / (4.0 * (float) this->spreadingFactor - 8.0 * de)) * (float) this->codingRate, 0.0);
// add 4.25 symbols for the sync
return(n_pre + n_pay + 4.25f);
}
uint32_t SX127x::getTimeOnAir(size_t len) {
// check active modem
uint8_t modem = getActiveModem();
if (modem == RADIOLIB_SX127X_LORA) {
// Get symbol length in us
// get symbol length in us
float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth;
// Get Low Data Rate optimization flag
float de = 0;
if (symbolLength >= 16.0) {
de = 1;
}
// Get explicit/implicit header enabled flag
float ih = (float) this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_1, 0, 0);
// Get CRC enabled flag
float crc = (float) (this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_2, 2, 2) >> 2);
// Get number of bits preamble
float n_pre = (float) ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_MSB) << 8) | this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_LSB));
// Get number of bits payload
float n_pay = 8.0 + RADIOLIB_MAX(ceil((8.0 * (float) len - 4.0 * (float) this->spreadingFactor + 28.0 + 16.0 * crc - 20.0 * ih) / (4.0 * (float) this->spreadingFactor - 8.0 * de)) * (float) this->codingRate, 0.0);
// get number of symbols
float n_sym = getNumSymbols(len);
// Get time-on-air in us
return ceil(symbolLength * (n_pre + n_pay + 4.25)) * 1000;
return ceil((double)symbolLength * (double)n_sym) * 1000;
} else if(modem == RADIOLIB_SX127X_FSK_OOK) {
// Get number of bits preamble
float n_pre = (float) ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_MSB_FSK) << 8) | this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_LSB_FSK)) * 8;
@ -1267,6 +1294,28 @@ uint32_t SX127x::getTimeOnAir(size_t len) {
}
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) {
// 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);
}
bool SX127x::isRxTimeout() {
uint16_t irq = getIRQFlags();
bool rxTimedOut = irq & RADIOLIB_SX127X_CLEAR_IRQ_FLAG_RX_TIMEOUT;
return(rxTimedOut);
}
int16_t SX127x::setCrcFiltering(bool enable) {
this->crcOn = enable;

View file

@ -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
@ -824,13 +825,16 @@ class SX127x: public PhysicalLayer {
/*!
\brief Interrupt-driven receive method, implemented for compatibility with PhysicalLayer.
\param mode Receive mode to be used.
\param timeout Receive mode type and/or raw timeout value in symbols.
When set to 0, the timeout will be infinite and the device will remain
in Rx mode until explicitly commanded to stop (Rx continuous mode).
When non-zero (maximum 1023), the device will be set to Rx single mode and timeout will be set.
\param irqFlags Ignored.
\param irqMask Ignored.
\param len Expected length of packet to be received. Required for LoRa spreading factor 6.
\returns \ref status_codes
*/
int16_t startReceive(uint32_t mode, uint16_t irqFlags, uint16_t irqMask, size_t len);
int16_t startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len);
/*!
\brief Reads data that was received after calling startReceive method. When the packet length is not known in advance,
@ -1041,6 +1045,13 @@ class SX127x: public PhysicalLayer {
*/
int16_t variablePacketLengthMode(uint8_t maxLen = RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK);
/*!
\brief Convert from bytes to LoRa symbols.
\param len Payload length in bytes.
\returns The total number of LoRa symbols, including preamble, sync and possible header.
*/
float getNumSymbols(size_t len);
/*!
\brief Get expected time-on-air for a given size of payload.
\param len Payload length in bytes.
@ -1048,6 +1059,27 @@ class SX127x: public PhysicalLayer {
*/
uint32_t getTimeOnAir(size_t len) override;
/*!
\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(uint32_t timeoutUs);
/*!
\brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks
\param irqFlags The flags for which IRQs must be triggered
\param irqMask Mask indicating which IRQ triggers a DIO
\returns \ref status_codes
*/
int16_t irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask);
/*!
\brief Check whether the IRQ bit for RxTimeout is set
\returns \ref RxTimeout IRQ is set
*/
bool isRxTimeout();
/*!
\brief Enable CRC filtering and generation.
\param enable Set or unset CRC filtering and generation.

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
#include "../../utils/Cryptography.h"
// version of NVM table layout (NOT the LoRaWAN version)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION (0x01)
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION (0x01)
// preamble format
#define RADIOLIB_LORAWAN_LORA_SYNC_WORD (0x34)
@ -69,9 +69,10 @@
#define RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK (0x01 << 0)
#define RADIOLIB_LORAWAN_CHANNEL_DIR_BOTH (0x02 << 0)
#define RADIOLIB_LORAWAN_CHANNEL_DIR_NONE (0x03 << 0)
#define RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES (0)
#define RADIOLIB_LORAWAN_CFLIST_TYPE_MASK (1)
#define RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES (16)
#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 >> 1) // reserve first bit for enable-flag
// recommended default settings
#define RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS (1000)
@ -81,8 +82,8 @@
#define RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_1_MS (5000)
#define RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_2_MS (6000)
#define RADIOLIB_LORAWAN_MAX_FCNT_GAP (16384)
#define RADIOLIB_LORAWAN_ADR_ACK_LIMIT (64)
#define RADIOLIB_LORAWAN_ADR_ACK_DELAY (32)
#define RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP (0x06)
#define RADIOLIB_LORAWAN_ADR_ACK_DELAY_EXP (0x05)
#define RADIOLIB_LORAWAN_RETRANSMIT_TIMEOUT_MIN_MS (1000)
#define RADIOLIB_LORAWAN_RETRANSMIT_TIMEOUT_MAX_MS (3000)
#define RADIOLIB_LORAWAN_POWER_STEP_SIZE_DBM (-2)
@ -134,6 +135,7 @@
// payload encryption/MIC blocks common layout
#define RADIOLIB_LORAWAN_BLOCK_MAGIC_POS (0)
#define RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS (1)
#define RADIOLIB_LORAWAN_BLOCK_DIR_POS (5)
#define RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS (6)
#define RADIOLIB_LORAWAN_BLOCK_FCNT_POS (10)
@ -150,7 +152,7 @@
#define RADIOLIB_LORAWAN_MIC_CH_INDEX_POS (4)
// magic word saved in persistent memory upon activation
#define RADIOLIB_LORAWAN_MAGIC (0x12AD101B)
#define RADIOLIB_LORAWAN_MAGIC (0x39EA)
// MAC commands
#define RADIOLIB_LORAWAN_MAC_CMD_RESET (0x01)
@ -170,11 +172,39 @@
#define RADIOLIB_LORAWAN_MAC_CMD_REJOIN_PARAM_SETUP (0x0F)
#define RADIOLIB_LORAWAN_MAC_CMD_PROPRIETARY (0x80)
// unused frame counter value
#define RADIOLIB_LORAWAN_FCNT_NONE (0xFFFFFFFF)
// the length of internal MAC command queue - hopefully this is enough for most use cases
#define RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE (8)
// the maximum number of simultaneously available channels
#define RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS (8)
#define RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS (16)
/*!
\struct LoRaWANChannelSpan_t
\brief Structure to save information about LoRaWAN channels.
To save space, adjacent channels are saved in "spans".
*/
struct LoRaWANChannel_t {
/*! \brief Whether this channel is enabled (can be used) or is disabled */
bool enabled;
/*! \brief The channel number, as specified by defaults or the network */
uint8_t idx;
/*! \brief The channel frequency */
float freq;
/*! \brief Minimum allowed datarate for this channel */
uint8_t drMin;
/*! \brief Maximum allowed datarate for this channel (inclusive) */
uint8_t drMax;
};
// alias for unused channel
#define RADIOLIB_LORAWAN_CHANNEL_NONE { .enabled = false, .idx = RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE, .freq = 0, .drMin = 0, .drMax = 0 }
/*!
\struct LoRaWANChannelSpan_t
@ -182,12 +212,6 @@
To save space, adjacent channels are saved in "spans".
*/
struct LoRaWANChannelSpan_t {
/*! \brief Whether this channel span is for uplink, downlink, or both directions*/
uint8_t direction;
/*! \brief Allowed data rates for a join request message */
uint8_t joinRequestDataRate;
/*! \brief Total number of channels in the span */
uint8_t numChannels;
@ -197,23 +221,26 @@ struct LoRaWANChannelSpan_t {
/*! \brief Frequency step between adjacent channels */
float freqStep;
/*! \brief Array of datarates supported by all channels in the span */
uint8_t dataRates[RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES];
/*! \brief Minimum allowed datarate for all channels in this span */
uint8_t drMin;
/*! \brief Maximum allowed datarate for all channels in this span (inclusive) */
uint8_t drMax;
/*! \brief Allowed data rates for a join request message */
uint8_t joinRequestDataRate;
};
// alias for unused channel span
#define RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE { .direction = RADIOLIB_LORAWAN_CHANNEL_DIR_NONE, .joinRequestDataRate = RADIOLIB_LORAWAN_DATA_RATE_UNUSED, .numChannels = 0, .freqStart = 0, .freqStep = 0, .dataRates = { 0 } }
#define RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE { .numChannels = 0, .freqStart = 0, .freqStep = 0, .drMin = 0, .drMax = 0, .joinRequestDataRate = RADIOLIB_LORAWAN_DATA_RATE_UNUSED }
/*!
\struct LoRaWANBand_t
\brief Structure to save information about LoRaWAN band
*/
struct LoRaWANBand_t {
/*! \brief The base downlink data rate. Used to calculate data rate changes for adaptive data rate */
uint8_t downlinkDataRateBase;
/*! \brief The minimum allowed downlink data rate. Used to calculate data rate changes for adaptive data rate */
uint8_t downlinkDataRateMin;
/*! \brief Whether the channels are fixed per specification, or dynamically allocated through the network (plus defaults) */
uint8_t bandType;
/*! \brief Array of allowed maximum payload lengths for each data rate */
uint8_t payloadLenMax[RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES];
@ -224,20 +251,29 @@ struct LoRaWANBand_t {
/*! \brief Number of power steps in this band */
int8_t powerNumSteps;
/*! \brief Whether the optional channels are defined as list of frequencies or bit mask */
uint8_t cfListType;
/*! \brief A set of default uplink (TX) channels for frequency-type bands */
LoRaWANChannel_t txFreqs[3];
/*! \brief FSK channel frequency */
float fskFreq;
/*! \brief A set of possible extra channels for the Join-Request message for frequency-type bands */
LoRaWANChannel_t txJoinReq[3];
/*! \brief Number of channel spans in the band */
uint8_t numChannelSpans;
/*! \brief The number of TX channel spans for mask-type bands */
uint8_t numTxSpans;
/*! \brief Default uplink (TX/RX1) channels defined by LoRaWAN Regional Parameters */
LoRaWANChannelSpan_t defaultChannels[3];
/*! \brief Default uplink (TX) channel spans for mask-type bands, including Join-Request parameters */
LoRaWANChannelSpan_t txSpans[2];
/*! \brief Default downlink (RX1) channel span for mask-type bands */
LoRaWANChannelSpan_t rx1Span;
/*! \brief The base downlink data rate. Used to calculate data rate changes for adaptive data rate */
uint8_t rx1DataRateBase;
/*! \brief Backup channel for downlink (RX2) window */
LoRaWANChannel_t rx2;
/*! \brief Backup downlink (RX2) channel - just a single channel, but using the same structure for convenience */
LoRaWANChannelSpan_t backupChannel;
/*! \brief The corresponding datarates, bandwidths and coding rates for DR index */
uint8_t dataRates[RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES];
};
// supported bands
@ -259,20 +295,20 @@ struct LoRaWANMacCommand_t {
/*! \brief The command ID */
uint8_t cid;
/*! \brief Length of the payload */
uint8_t len;
/*! \brief Payload buffer (5 bytes is the longest possible) */
uint8_t payload[5];
/*! \brief Length of the payload */
uint8_t len;
/*! \brief Repetition counter (the command will be uplinked repeat + 1 times) */
uint8_t repeat;
};
struct LoRaWANMacCommandQueue_t {
uint8_t numCommands;
uint8_t len;
LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE];
size_t numCommands;
size_t len;
};
/*!
@ -281,31 +317,12 @@ 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;
// Offset between TX and RX1 (such that RX1 has equal or lower DR)
uint8_t rx1DrOffset;
/*! \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;
/*! \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;
// RX2 channel properties - may be changed by MAC command
LoRaWANChannel_t rx2;
/*!
\brief Default constructor.
@ -314,6 +331,7 @@ class LoRaWANNode {
*/
LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band);
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
/*!
\brief Wipe internal persistent parameters.
This will reset all counters and saved variables, so the device will have to rejoin the network.
@ -321,18 +339,11 @@ class LoRaWANNode {
void wipe();
/*!
\brief Configures CSMA for LoRaWAN as per TR-13, LoRa Alliance.
\param backoffMax Num of BO slots to be decremented after DIFS phase. 0 to disable BO.
\param difsSlots Num of CADs to estimate a clear CH.
\param enableCSMA enable/disable CSMA for LoRaWAN.
*/
void setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA = false);
/*!
\brief Restore OTAA session by loading information from persistent storage.
\brief Restore session by loading information from persistent storage.
\returns \ref status_codes
*/
int16_t restoreOTAA();
int16_t restore();
#endif
/*!
\brief Join network by performing over-the-air activation. By this procedure,
@ -341,10 +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 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, 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.
@ -354,36 +367,50 @@ class LoRaWANNode {
\param appSKey Pointer to the application session AES-128 key.
\param fNwkSIntKey Pointer to the network session F key (LoRaWAN 1.1), unused for LoRaWAN 1.0.
\param sNwkSIntKey Pointer to the network session S key (LoRaWAN 1.1), unused for LoRaWAN 1.0.
\param force Set to true to force a new session, even if one exists.
\returns \ref status_codes
*/
int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL);
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.
\returns \ref status_codes
*/
int16_t saveSession();
#if defined(RADIOLIB_BUILD_ARDUINO)
/*!
\brief Send a message to the server.
\param str Address of Arduino String that will be transmitted.
\param port Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not.
\returns \ref status_codes
*/
int16_t uplink(String& str, uint8_t port);
int16_t uplink(String& str, uint8_t port, bool isConfirmed = false);
#endif
/*!
\brief Send a message to the server.
\param str C-string that will be transmitted.
\param port Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not.
\returns \ref status_codes
*/
int16_t uplink(const char* str, uint8_t port);
int16_t uplink(const char* str, uint8_t port, bool isConfirmed = false);
/*!
\brief Send a message to the server.
\param data Data to send.
\param len Length of the data.
\param port Port number to send the message to.
\param isConfirmed Whether to send a confirmed uplink or not.
\returns \ref status_codes
*/
int16_t uplink(uint8_t* data, size_t len, uint8_t port);
int16_t uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed = false);
#if defined(RADIOLIB_BUILD_ARDUINO)
/*!
@ -402,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,
@ -409,6 +471,47 @@ 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.
\returns \ref status_codes
*/
int16_t setDatarate(uint8_t drUp);
/*!
\brief Toggle ADR to on or off
\param enable Whether to disable ADR or not
*/
void setADR(bool enable = true);
/*!
\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.
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 (inclusive)
\returns \ref status_codes
*/
int16_t selectSubband(uint8_t startChannel, uint8_t endChannel);
/*!
\brief Configures CSMA for LoRaWAN as per TR-13, LoRa Alliance.
\param backoffMax Num of BO slots to be decremented after DIFS phase. 0 to disable BO.
\param difsSlots Num of CADs to estimate a clear CH.
\param enableCSMA enable/disable CSMA for LoRaWAN.
*/
void setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA = false);
#if !defined(RADIOLIB_GODMODE)
private:
#endif
@ -416,14 +519,14 @@ class LoRaWANNode {
const LoRaWANBand_t* band = NULL;
LoRaWANMacCommandQueue_t commandsUp = {
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
.numCommands = 0,
.len = 0,
.commands = { { .cid = 0, .payload = { 0 }, .len = 0, .repeat = 0, } },
};
LoRaWANMacCommandQueue_t commandsDown = {
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
.numCommands = 0,
.len = 0,
.commands = { { .cid = 0, .payload = { 0 }, .len = 0, .repeat = 0, } },
};
// the following is either provided by the network server (OTAA)
@ -434,29 +537,62 @@ class LoRaWANNode {
uint8_t sNwkSIntKey[RADIOLIB_AES128_KEY_SIZE] = { 0 };
uint8_t nwkSEncKey[RADIOLIB_AES128_KEY_SIZE] = { 0 };
uint8_t jSIntKey[RADIOLIB_AES128_KEY_SIZE] = { 0 };
// device-specific parameters, persistent through sessions
uint16_t devNonce = 0;
uint32_t joinNonce = 0;
// session-specific parameters
uint32_t homeNetId = 0;
uint8_t adrLimitExp = RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP;
uint8_t adrDelayExp = RADIOLIB_LORAWAN_ADR_ACK_DELAY_EXP;
uint8_t nbTrans = 1; // Number of allowed frame retransmissions
uint8_t txPwrCur = 0;
uint32_t fcntUp = 0;
uint32_t aFcntDown = 0;
uint32_t nFcntDown = 0;
uint32_t confFcntUp = RADIOLIB_LORAWAN_FCNT_NONE;
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
float availableChannelsFreq[2][RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS] = { { 0 }, { 0 } };
LoRaWANChannel_t availableChannels[2][RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS] = { { 0 }, { 0 } };
// currently configured channel frequency
float channelFreq[2] = { 0 };
// currently configured channels for TX and RX1
LoRaWANChannel_t currentChannels[2] = { RADIOLIB_LORAWAN_CHANNEL_NONE, RADIOLIB_LORAWAN_CHANNEL_NONE };
// currently configured datarates for TX and RX1
uint8_t dataRates[2] = { RADIOLIB_LORAWAN_DATA_RATE_UNUSED, RADIOLIB_LORAWAN_DATA_RATE_UNUSED };
// LoRaWAN revision (1.0 vs 1.1)
uint8_t rev = 0;
// currently configured data rate for uplink and downlink: DR0 - DR15 (band-dependent!)
uint8_t dataRate[2] = { 0 };
// currently configured channel for uplink and downlink (band-dependent!)
uint8_t chIndex[2] = { 0 };
// backup channel properties - may be changed by MAC command
float backupFreq = 0;
uint8_t backupDataRate = 0;
// 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 };
@ -466,6 +602,25 @@ class LoRaWANNode {
// indicates whether an uplink has MAC commands as payload
bool isMACPayload = false;
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
/*!
\brief Save the current uplink frame counter.
Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling.
\returns \ref status_codes
*/
int16_t saveFcntUp();
/*!
\brief Restore frame counter for uplinks from persistent storage.
Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling.
\returns \ref status_codes
*/
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);
@ -479,37 +634,40 @@ class LoRaWANNode {
// setup uplink/downlink channel data rates and frequencies
// will attempt to randomly select based on currently used band plan
int16_t setupChannels();
int16_t setupChannels(uint8_t* cfList);
// find the first usable data rate in a given channel span
uint8_t findDataRate(uint8_t dr, DataRate_t* dataRate, const LoRaWANChannelSpan_t* span);
// select a set of semi-random TX/RX channels for the join-request and -accept message
int16_t selectChannelsJR(uint16_t devNonce, uint8_t drJoinSubband);
// find a channel ID that conforms to the requested direction and ID range
int16_t findChannelId(uint8_t dir, uint8_t* ch, uint8_t* dr, int8_t min, int8_t max);
// select a set of random TX/RX channels for up- and downlink
int16_t selectChannels();
// find a channel span that any given channel id belongs to
LoRaWANChannelSpan_t* findChannelSpan(uint8_t dir, uint8_t ch, uint8_t* spanChannelId);
// calculate channel frequency in MHz based on channel ID and direction
int16_t findChannelFreq(uint8_t dir, uint8_t ch, float* freq);
// find the first usable data rate for the given band
int16_t findDataRate(uint8_t dr, DataRate_t* dataRate);
// configure channel based on cached data rate ID and frequency
int16_t configureChannel(uint8_t dir);
// send a MAC command to the network server
int16_t sendMacCommand(uint8_t cid, uint8_t* payload, size_t payloadLen, uint8_t* reply, size_t replyLen);
// save all available channels to persistent storage
int16_t saveChannels();
// restore all available channels from persistent storage
int16_t restoreChannels();
// push MAC command to queue, done by copy
int16_t pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue);
// pop MAC command from queue, done by copy unless CMD is NULL
int16_t popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, size_t index);
// delete a specific MAC command from queue, indicated by the command ID
int16_t deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue);
// execute mac command, return the number of processed bytes for sequential processing
size_t execMacCommand(LoRaWANMacCommand_t* cmd);
// Performs CSMA as per LoRa Alliance Technical Reccomendation 13 (TR-013).
void performCSMA();
// perform a single CAD operation for the under SF/CH combination. Returns either busy or otherwise.
bool performCAD();
// function to encrypt and decrypt payloads
void processAES(uint8_t* in, size_t len, uint8_t* key, uint8_t* out, uint32_t fcnt, uint8_t dir, uint8_t ctrId, bool counter);
@ -521,12 +679,6 @@ class LoRaWANNode {
// host-to-network conversion method - takes data from host variable and and converts it to network packet endians
template<typename T>
static void hton(uint8_t* buff, T val, size_t size = 0);
// perform a single CAD operation for the under SF/CH combination. Returns either busy or otherwise.
bool performCAD();
// Performs CSMA as per LoRa Alliance Technical Reccomendation 13 (TR-013).
void performCSMA();
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -293,7 +293,22 @@ uint32_t PhysicalLayer::getTimeOnAir(size_t len) {
(void)len;
return(0);
}
uint32_t PhysicalLayer::calculateRxTimeout(uint32_t timeoutUs) {
(void)timeoutUs;
return(0);
}
int16_t PhysicalLayer::irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask) {
(void)irqFlags;
(void)irqMask;
return(RADIOLIB_ERR_UNSUPPORTED);
}
bool PhysicalLayer::isRxTimeout() {
return(false);
}
int16_t PhysicalLayer::startChannelScan() {
return(RADIOLIB_ERR_UNSUPPORTED);
}

View file

@ -309,7 +309,28 @@ class PhysicalLayer {
\returns Expected time-on-air in microseconds.
*/
virtual uint32_t getTimeOnAir(size_t len);
/*!
\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(uint32_t timeoutUs);
/*!
\brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks
\param irqFlags The flags for which IRQs must be triggered
\param irqMask Mask indicating which IRQ triggers a DIO
\returns \ref status_codes
*/
virtual int16_t irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask);
/*!
\brief Check whether the IRQ bit for RxTimeout is set
\returns \ref RxTimeout IRQ is set
*/
virtual bool isRxTimeout();
/*!
\brief Interrupt-driven channel activity detection method. interrupt will be activated
when packet is detected. Must be implemented in module class.