[LoRaWAN] Add methods to allow user-provided sleep function (#1410)
* [LoRaWAN] Add methods to allow user-provided sleep function * Add example sleep function * [LoRaWAN] Switch all delay calls to sleepDelay * [LoRaWAN] Remove unused variable --------- Co-authored-by: StevenCellist <steven@boonstoppel.nu>
This commit is contained in:
parent
45de7978dc
commit
8c2c7b6cb5
5 changed files with 63 additions and 9 deletions
|
@ -48,6 +48,9 @@ void setup() {
|
||||||
// Override the default join rate
|
// Override the default join rate
|
||||||
uint8_t joinDR = 4;
|
uint8_t joinDR = 4;
|
||||||
|
|
||||||
|
// Optionally provide a custom sleep function - see config.h
|
||||||
|
//node.setSleepFunction(customDelay);
|
||||||
|
|
||||||
// Setup the OTAA session information
|
// Setup the OTAA session information
|
||||||
node.beginOTAA(joinEUI, devEUI, nwkKey, appKey);
|
node.beginOTAA(joinEUI, devEUI, nwkKey, appKey);
|
||||||
|
|
||||||
|
|
|
@ -142,4 +142,19 @@ void arrayDump(uint8_t *buffer, uint16_t len) {
|
||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom delay function:
|
||||||
|
// Communication over LoRaWAN includes a lot of delays.
|
||||||
|
// By default, RadioLib will use the Arduino delay() function,
|
||||||
|
// which will waste a lot of power. However, you can put your
|
||||||
|
// microcontroller to sleep instead by customizing the function below,
|
||||||
|
// and providing it to RadioLib via "node.setSleepFunction".
|
||||||
|
// NOTE: You ahve to ensure that this function is timed precisely, and
|
||||||
|
// does actually wait for the amount of time specified!
|
||||||
|
// Failure to do so will result in missed downlinks or failed join!
|
||||||
|
void customDelay(RadioLibTime_t ms) {
|
||||||
|
// this is just an example, so we use the Arduino delay() function,
|
||||||
|
// but you can put your microcontroller to sleep here
|
||||||
|
::delay(ms);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -379,6 +379,7 @@ getLastToA KEYWORD2
|
||||||
dutyCycleInterval KEYWORD2
|
dutyCycleInterval KEYWORD2
|
||||||
timeUntilUplink KEYWORD2
|
timeUntilUplink KEYWORD2
|
||||||
getMaxPayloadLen KEYWORD2
|
getMaxPayloadLen KEYWORD2
|
||||||
|
setSleepFunction KEYWORD2
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Constants (LITERAL1)
|
# Constants (LITERAL1)
|
||||||
|
|
|
@ -67,7 +67,6 @@ int16_t LoRaWANNode::sendReceive(const uint8_t* dataUp, size_t lenUp, uint8_t fP
|
||||||
return(RADIOLIB_ERR_NULL_POINTER);
|
return(RADIOLIB_ERR_NULL_POINTER);
|
||||||
}
|
}
|
||||||
int16_t state = RADIOLIB_ERR_UNKNOWN;
|
int16_t state = RADIOLIB_ERR_UNKNOWN;
|
||||||
Module* mod = this->phyLayer->getMod();
|
|
||||||
|
|
||||||
// if after (at) ADR_ACK_LIMIT frames no RekeyConf was received, revert to Join state
|
// if after (at) ADR_ACK_LIMIT frames no RekeyConf was received, revert to Join state
|
||||||
if(this->fCntUp == (1UL << this->adrLimitExp)) {
|
if(this->fCntUp == (1UL << this->adrLimitExp)) {
|
||||||
|
@ -147,7 +146,7 @@ int16_t LoRaWANNode::sendReceive(const uint8_t* dataUp, size_t lenUp, uint8_t fP
|
||||||
// RETRANSMIT_TIMEOUT is 2s +/- 1s (RP v1.0.4)
|
// RETRANSMIT_TIMEOUT is 2s +/- 1s (RP v1.0.4)
|
||||||
// must be present after any confirmed frame, so we force this here
|
// must be present after any confirmed frame, so we force this here
|
||||||
if(isConfirmed) {
|
if(isConfirmed) {
|
||||||
mod->hal->delay(this->phyLayer->random(1000, 3000));
|
this->sleepDelay(this->phyLayer->random(1000, 3000));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if an error occured or a downlink was received, stop retransmission
|
// if an error occured or a downlink was received, stop retransmission
|
||||||
|
@ -927,7 +926,7 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent)
|
||||||
if(this->tUplink > tNow) {
|
if(this->tUplink > tNow) {
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Delaying transmission by %lu ms", (unsigned long)(this->tUplink - tNow));
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Delaying transmission by %lu ms", (unsigned long)(this->tUplink - tNow));
|
||||||
if(this->tUplink > mod->hal->millis()) {
|
if(this->tUplink > mod->hal->millis()) {
|
||||||
mod->hal->delay(this->tUplink - mod->hal->millis());
|
this->sleepDelay(this->tUplink - mod->hal->millis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -936,7 +935,7 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent)
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// sleep for the duration of the transmission
|
// sleep for the duration of the transmission
|
||||||
mod->hal->delay(toa);
|
this->sleepDelay(toa);
|
||||||
RadioLibTime_t txEnd = mod->hal->millis();
|
RadioLibTime_t txEnd = mod->hal->millis();
|
||||||
|
|
||||||
// wait for an additional transmission duration as Tx timeout period
|
// wait for an additional transmission duration as Tx timeout period
|
||||||
|
@ -1342,7 +1341,7 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u
|
||||||
if(this->tUplink > tNow) {
|
if(this->tUplink > tNow) {
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Delaying transmission by %lu ms", (unsigned long)(this->tUplink - tNow));
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Delaying transmission by %lu ms", (unsigned long)(this->tUplink - tNow));
|
||||||
if(this->tUplink > mod->hal->millis()) {
|
if(this->tUplink > mod->hal->millis()) {
|
||||||
mod->hal->delay(this->tUplink - mod->hal->millis());
|
this->sleepDelay(this->tUplink - mod->hal->millis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1351,7 +1350,7 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// sleep for the duration of the transmission
|
// sleep for the duration of the transmission
|
||||||
mod->hal->delay(toa);
|
this->sleepDelay(toa);
|
||||||
RadioLibTime_t txEnd = mod->hal->millis();
|
RadioLibTime_t txEnd = mod->hal->millis();
|
||||||
|
|
||||||
// wait for an additional transmission duration as Tx timeout period
|
// wait for an additional transmission duration as Tx timeout period
|
||||||
|
@ -1398,7 +1397,7 @@ int16_t LoRaWANNode::receiveCommon(uint8_t dir, const LoRaWANChannel_t* dlChanne
|
||||||
// if function was called while Rx windows are in progress,
|
// if function was called while Rx windows are in progress,
|
||||||
// wait until last window closes to prevent very bad stuff
|
// wait until last window closes to prevent very bad stuff
|
||||||
if(now < tReference + dlDelays[numWindows]) {
|
if(now < tReference + dlDelays[numWindows]) {
|
||||||
mod->hal->delay(dlDelays[numWindows] + tReference - now);
|
this->sleepDelay(dlDelays[numWindows] + tReference - now);
|
||||||
}
|
}
|
||||||
// update the end timestamp in case user got stuck between uplink and downlink
|
// update the end timestamp in case user got stuck between uplink and downlink
|
||||||
this->rxDelayEnd = mod->hal->millis();
|
this->rxDelayEnd = mod->hal->millis();
|
||||||
|
@ -1444,7 +1443,7 @@ int16_t LoRaWANNode::receiveCommon(uint8_t dir, const LoRaWANChannel_t* dlChanne
|
||||||
if(waitLen > this->scanGuard) {
|
if(waitLen > this->scanGuard) {
|
||||||
waitLen -= this->scanGuard;
|
waitLen -= this->scanGuard;
|
||||||
}
|
}
|
||||||
mod->hal->delay(waitLen);
|
this->sleepDelay(waitLen);
|
||||||
|
|
||||||
// open Rx window by starting receive with specified timeout
|
// open Rx window by starting receive with specified timeout
|
||||||
state = this->phyLayer->launchMode();
|
state = this->phyLayer->launchMode();
|
||||||
|
@ -1453,7 +1452,7 @@ int16_t LoRaWANNode::receiveCommon(uint8_t dir, const LoRaWANChannel_t* dlChanne
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Opening Rx%d window (%d ms timeout)... <-- Rx Delay end ", window, (int)(timeoutHost / 1000 + 2));
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Opening Rx%d window (%d ms timeout)... <-- Rx Delay end ", window, (int)(timeoutHost / 1000 + 2));
|
||||||
|
|
||||||
// wait for the timeout to complete (and a small additional delay)
|
// wait for the timeout to complete (and a small additional delay)
|
||||||
mod->hal->delay(timeoutHost / 1000 + 2);
|
this->sleepDelay(timeoutHost / 1000 + this->scanGuard / 2);
|
||||||
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Closing Rx%d window", window);
|
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Closing Rx%d window", window);
|
||||||
|
|
||||||
// if the IRQ bit for Rx Timeout is not set, something is received, so stop the windows
|
// if the IRQ bit for Rx Timeout is not set, something is received, so stop the windows
|
||||||
|
@ -3376,6 +3375,10 @@ uint8_t LoRaWANNode::getMaxPayloadLen() {
|
||||||
return(curLen - 13 - this->fOptsUpLen);
|
return(curLen - 13 - this->fOptsUpLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoRaWANNode::setSleepFunction(SleepCb_t cb) {
|
||||||
|
this->sleepCb = cb;
|
||||||
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) {
|
int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) {
|
||||||
int16_t state = this->phyLayer->standby();
|
int16_t state = this->phyLayer->standby();
|
||||||
if(state != RADIOLIB_ERR_NONE) {
|
if(state != RADIOLIB_ERR_NONE) {
|
||||||
|
@ -3500,6 +3503,18 @@ void LoRaWANNode::processAES(const uint8_t* in, size_t len, uint8_t* key, uint8_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoRaWANNode::sleepDelay(RadioLibTime_t ms) {
|
||||||
|
// if the user did not provide sleep callback, or the duration is short, just call delay
|
||||||
|
if((this->sleepCb == nullptr) || (ms <= RADIOLIB_LORAWAN_DELAY_SLEEP_THRESHOLD)) {
|
||||||
|
Module* mod = this->phyLayer->getMod();
|
||||||
|
mod->hal->delay(ms);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, call the user-provided callback
|
||||||
|
this->sleepCb(ms);
|
||||||
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::checkBufferCommon(const uint8_t *buffer, uint16_t size) {
|
int16_t LoRaWANNode::checkBufferCommon(const uint8_t *buffer, uint16_t size) {
|
||||||
// check if there are actually values in the buffer
|
// check if there are actually values in the buffer
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
|
@ -210,6 +210,9 @@
|
||||||
|
|
||||||
#define RADIOLIB_LORAWAN_MAX_DOWNLINK_SIZE (250)
|
#define RADIOLIB_LORAWAN_MAX_DOWNLINK_SIZE (250)
|
||||||
|
|
||||||
|
// threshold at which sleeping via user callback enabled, in ms
|
||||||
|
#define RADIOLIB_LORAWAN_DELAY_SLEEP_THRESHOLD (50)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\struct LoRaWANMacCommand_t
|
\struct LoRaWANMacCommand_t
|
||||||
\brief MAC command specification structure.
|
\brief MAC command specification structure.
|
||||||
|
@ -832,6 +835,18 @@ class LoRaWANNode {
|
||||||
*/
|
*/
|
||||||
uint8_t getMaxPayloadLen();
|
uint8_t getMaxPayloadLen();
|
||||||
|
|
||||||
|
/*! \brief Callback to a user-provided sleep function. */
|
||||||
|
typedef void (*SleepCb_t)(RadioLibTime_t ms);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Set custom delay/sleep function callback. If set, LoRaWAN node will call
|
||||||
|
this function to wait for periods of time longer than RADIOLIB_LORAWAN_DELAY_SLEEP_THRESHOLD.
|
||||||
|
This can be used to lower the power consumption by putting the host microcontroller to sleep.
|
||||||
|
NOTE: Since this method will call a user-provided function, it is up to the user to ensure
|
||||||
|
that the time duration spent in that sleep function is accurate to at least 1 ms!
|
||||||
|
*/
|
||||||
|
void setSleepFunction(SleepCb_t cb);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief TS009 Protocol Specification Verification switch
|
\brief TS009 Protocol Specification Verification switch
|
||||||
(allows FPort 224 and cuts off uplink payload instead of rejecting if maximum length exceeded).
|
(allows FPort 224 and cuts off uplink payload instead of rejecting if maximum length exceeded).
|
||||||
|
@ -973,6 +988,8 @@ class LoRaWANNode {
|
||||||
// allow port 226 for devices implementing TS011
|
// allow port 226 for devices implementing TS011
|
||||||
bool TS011 = false;
|
bool TS011 = false;
|
||||||
|
|
||||||
|
SleepCb_t sleepCb = nullptr;
|
||||||
|
|
||||||
// this will reset the device credentials, so the device starts completely new
|
// this will reset the device credentials, so the device starts completely new
|
||||||
void clearNonces();
|
void clearNonces();
|
||||||
|
|
||||||
|
@ -1102,6 +1119,9 @@ class LoRaWANNode {
|
||||||
// function to encrypt and decrypt payloads (regular uplink/downlink)
|
// function to encrypt and decrypt payloads (regular uplink/downlink)
|
||||||
void processAES(const uint8_t* in, size_t len, uint8_t* key, uint8_t* out, uint32_t fCnt, uint8_t dir, uint8_t ctrId, bool counter);
|
void processAES(const uint8_t* in, size_t len, uint8_t* key, uint8_t* out, uint32_t fCnt, uint8_t dir, uint8_t ctrId, bool counter);
|
||||||
|
|
||||||
|
// function that allows sleeping via user-provided callback
|
||||||
|
void sleepDelay(RadioLibTime_t ms);
|
||||||
|
|
||||||
// 16-bit checksum method that takes a uint8_t array of even length and calculates the checksum
|
// 16-bit checksum method that takes a uint8_t array of even length and calculates the checksum
|
||||||
static uint16_t checkSum16(const uint8_t *key, uint16_t keyLen);
|
static uint16_t checkSum16(const uint8_t *key, uint16_t keyLen);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue