diff --git a/keywords.txt b/keywords.txt index f6bc613b..63cd947f 100644 --- a/keywords.txt +++ b/keywords.txt @@ -377,6 +377,7 @@ getLastToA KEYWORD2 dutyCycleInterval KEYWORD2 timeUntilUplink KEYWORD2 getMaxPayloadLen KEYWORD2 +setSleepFunction KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 91cdf65d..be17a977 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -1377,7 +1377,7 @@ int16_t LoRaWANNode::receiveCommon(uint8_t dir, const LoRaWANChannel_t* dlChanne if(waitLen > this->scanGuard) { waitLen -= this->scanGuard; } - mod->hal->delay(waitLen); + this->sleepDelay(waitLen); // open Rx window by starting receive with specified timeout // TODO remove default arguments @@ -1387,7 +1387,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 + scanGuard / 2)); // wait for the timeout to complete (and a small additional delay) - mod->hal->delay(timeoutHost / 1000 + this->scanGuard / 2); + this->sleepDelay(timeoutHost / 1000 + this->scanGuard / 2); 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 @@ -3310,6 +3310,10 @@ uint8_t LoRaWANNode::getMaxPayloadLen() { 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 state = this->phyLayer->standby(); if(state != RADIOLIB_ERR_NONE) { @@ -3434,6 +3438,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) { // check if there are actually values in the buffer size_t i = 0; diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 1bfa782c..5ce5500c 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -210,6 +210,9 @@ #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 \brief MAC command specification structure. @@ -832,6 +835,18 @@ class LoRaWANNode { */ 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 (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 bool TS011 = false; + SleepCb_t sleepCb = nullptr; + // this will reset the device credentials, so the device starts completely new void clearNonces(); @@ -1102,6 +1119,9 @@ class LoRaWANNode { // 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); + // 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 static uint16_t checkSum16(const uint8_t *key, uint16_t keyLen);