[LoRaWAN] Implement SX127x support, fix MAC uplinking, support clock drift
This commit is contained in:
parent
e7b2b27dd5
commit
f92e0e59d4
7 changed files with 146 additions and 77 deletions
|
@ -58,19 +58,35 @@ void inline ArduinoHal::detachInterrupt(uint32_t interruptNum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void inline ArduinoHal::delay(unsigned long ms) {
|
void inline ArduinoHal::delay(unsigned long ms) {
|
||||||
|
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
|
||||||
::delay(ms);
|
::delay(ms);
|
||||||
|
#else
|
||||||
|
::delay(ms * (1000 - RADIOLIB_CLOCK_DRIFT_MS) / 1000);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void inline ArduinoHal::delayMicroseconds(unsigned long us) {
|
void inline ArduinoHal::delayMicroseconds(unsigned long us) {
|
||||||
|
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
|
||||||
::delayMicroseconds(us);
|
::delayMicroseconds(us);
|
||||||
|
#else
|
||||||
|
::delayMicroseconds(us * (1000 - RADIOLIB_CLOCK_DRIFT_MS) / 1000);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long inline ArduinoHal::millis() {
|
unsigned long inline ArduinoHal::millis() {
|
||||||
|
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
|
||||||
return(::millis());
|
return(::millis());
|
||||||
|
#else
|
||||||
|
return(::millis() * (1000 - RADIOLIB_CLOCK_DRIFT_MS) / 1000);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long inline ArduinoHal::micros() {
|
unsigned long inline ArduinoHal::micros() {
|
||||||
|
#if !defined(RADIOLIB_CLOCK_DRIFT_MS)
|
||||||
return(::micros());
|
return(::micros());
|
||||||
|
#else
|
||||||
|
return(::micros() * (1000 - RADIOLIB_CLOCK_DRIFT_MS) / 1000);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
long inline ArduinoHal::pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) {
|
long inline ArduinoHal::pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) {
|
||||||
|
|
|
@ -446,6 +446,18 @@
|
||||||
#define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0x0180)
|
#define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0x0180)
|
||||||
#endif
|
#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
|
// This only compiles on STM32 boards with SUBGHZ module, but also
|
||||||
// include when generating docs
|
// include when generating docs
|
||||||
#if (!defined(ARDUINO_ARCH_STM32) || !defined(SUBGHZSPI_BASE)) && !defined(DOXYGEN)
|
#if (!defined(ARDUINO_ARCH_STM32) || !defined(SUBGHZSPI_BASE)) && !defined(DOXYGEN)
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// list of persistent parameters
|
// list of persistent parameters
|
||||||
#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_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_MAGIC_ID (1)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION (2)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID (2)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID (3)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID (3)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID (4)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID (4)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID (5)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID (5)
|
||||||
|
|
|
@ -405,6 +405,13 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) {
|
||||||
state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_ADDR_PTR, RADIOLIB_SX127X_FIFO_RX_BASE_ADDR_MAX);
|
state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_ADDR_PTR, RADIOLIB_SX127X_FIFO_RX_BASE_ADDR_MAX);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
mode = RADIOLIB_SX127X_RXSINGLE;
|
||||||
|
state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_SYMB_TIMEOUT_LSB, len);
|
||||||
|
}
|
||||||
|
|
||||||
} else if(modem == RADIOLIB_SX127X_FSK_OOK) {
|
} else if(modem == RADIOLIB_SX127X_FSK_OOK) {
|
||||||
// set DIO pin mapping
|
// set DIO pin mapping
|
||||||
state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PAYLOAD_READY, 7, 6);
|
state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PAYLOAD_READY, 7, 6);
|
||||||
|
@ -1237,6 +1244,28 @@ uint32_t SX127x::getTimeOnAir(size_t len) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t SX127x::calculateRxTimeout(uint8_t numSymbols, uint32_t timeoutUs) {
|
||||||
|
(void)numSymbols; // not used for these modules
|
||||||
|
// numSymbols += (109 / 4) + 1;
|
||||||
|
float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth;
|
||||||
|
numSymbols = timeoutUs / symbolLength + 1;
|
||||||
|
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;
|
||||||
|
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) {
|
int16_t SX127x::setCrcFiltering(bool enable) {
|
||||||
this->crcOn = enable;
|
this->crcOn = enable;
|
||||||
|
|
||||||
|
|
|
@ -1039,6 +1039,28 @@ class SX127x: public PhysicalLayer {
|
||||||
*/
|
*/
|
||||||
uint32_t getTimeOnAir(size_t len) override;
|
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
|
||||||
|
\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);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\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.
|
\brief Enable CRC filtering and generation.
|
||||||
\param enable Set or unset CRC filtering and generation.
|
\param enable Set or unset CRC filtering and generation.
|
||||||
|
|
|
@ -38,7 +38,7 @@ int16_t LoRaWANNode::restore() {
|
||||||
// check the magic value
|
// check the magic value
|
||||||
Module* mod = this->phyLayer->getMod();
|
Module* mod = this->phyLayer->getMod();
|
||||||
|
|
||||||
uint16_t nvm_table_version = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION_ID);
|
uint8_t nvm_table_version = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION_ID);
|
||||||
// if (RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION > nvm_table_version) {
|
// if (RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION > nvm_table_version) {
|
||||||
// // set default values for variables that are new or something
|
// // set default values for variables that are new or something
|
||||||
// }
|
// }
|
||||||
|
@ -58,7 +58,7 @@ int16_t LoRaWANNode::restore() {
|
||||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
|
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||||
|
|
||||||
// get session parameters
|
// get session parameters
|
||||||
this->rev = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION);
|
this->rev = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID);
|
||||||
RADIOLIB_DEBUG_PRINTLN("LoRaWAN session: v1.%d", this->rev);
|
RADIOLIB_DEBUG_PRINTLN("LoRaWAN session: v1.%d", this->rev);
|
||||||
this->devNonce = mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID);
|
this->devNonce = mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID);
|
||||||
this->joinNonce = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID);
|
this->joinNonce = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID);
|
||||||
|
@ -217,6 +217,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
// send it
|
// send it
|
||||||
state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN);
|
state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN);
|
||||||
this->rxDelayStart = mod->hal->millis();
|
this->rxDelayStart = mod->hal->millis();
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("Join-request sent <-- Rx Delay start");
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// configure Rx delay for join-accept message - these are re-configured once a valid join-request is received
|
// configure Rx delay for join-accept message - these are re-configured once a valid join-request is received
|
||||||
|
@ -356,8 +357,8 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
// enqueue the RekeyInd MAC command to be sent in the next uplink
|
// enqueue the RekeyInd MAC command to be sent in the next uplink
|
||||||
LoRaWANMacCommand_t cmd = {
|
LoRaWANMacCommand_t cmd = {
|
||||||
.cid = RADIOLIB_LORAWAN_MAC_CMD_REKEY,
|
.cid = RADIOLIB_LORAWAN_MAC_CMD_REKEY,
|
||||||
.len = sizeof(uint8_t),
|
|
||||||
.payload = { this->rev },
|
.payload = { this->rev },
|
||||||
|
.len = sizeof(uint8_t),
|
||||||
.repeat = 0x01 << RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP,
|
.repeat = 0x01 << RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP,
|
||||||
};
|
};
|
||||||
state = pushMacCommand(&cmd, &this->commandsUp);
|
state = pushMacCommand(&cmd, &this->commandsUp);
|
||||||
|
@ -468,8 +469,8 @@ int16_t LoRaWANNode::saveSession() {
|
||||||
Module* mod = this->phyLayer->getMod();
|
Module* mod = this->phyLayer->getMod();
|
||||||
|
|
||||||
// store session configuration (MAC commands)
|
// store session configuration (MAC commands)
|
||||||
if(mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION) != this->rev)
|
if(mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID) != this->rev)
|
||||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION, this->rev);
|
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID, this->rev);
|
||||||
|
|
||||||
if(mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID) != this->devNonce)
|
if(mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID) != this->devNonce)
|
||||||
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID, this->devNonce);
|
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID, this->devNonce);
|
||||||
|
@ -629,7 +630,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
||||||
Module* mod = this->phyLayer->getMod();
|
Module* mod = this->phyLayer->getMod();
|
||||||
|
|
||||||
// check if sufficient time has elapsed since the last uplink
|
// check if sufficient time has elapsed since the last uplink
|
||||||
if(mod->hal->millis() - this->rxDelayStart < rxDelays[1]) {
|
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
|
// not enough time elapsed since the last uplink, we may still be in an RX window
|
||||||
return(RADIOLIB_ERR_UPLINK_UNAVAILABLE);
|
return(RADIOLIB_ERR_UPLINK_UNAVAILABLE);
|
||||||
}
|
}
|
||||||
|
@ -687,12 +688,14 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
||||||
// failed to increase Tx power, so try to decrease the datarate
|
// failed to increase Tx power, so try to decrease the datarate
|
||||||
if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] > this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMin) {
|
if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] > this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMin) {
|
||||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]--;
|
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]--;
|
||||||
|
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]--;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// failed to decrease datarate, so enable all available channels
|
// failed to decrease datarate, so enable all available channels
|
||||||
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||||
if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx != RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) {
|
if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx != RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) {
|
||||||
this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true;
|
this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true;
|
||||||
|
this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].enabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -747,25 +750,31 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf
|
||||||
|
|
||||||
// check if we have some MAC commands to append
|
// check if we have some MAC commands to append
|
||||||
if(foptsLen > 0) {
|
if(foptsLen > 0) {
|
||||||
uint8_t foptsNum = this->commandsUp.numCommands;
|
|
||||||
uint8_t foptsBuff[foptsBufSize];
|
uint8_t foptsBuff[foptsBufSize];
|
||||||
size_t idx = 0;
|
uint8_t* foptsPtr = foptsBuff;
|
||||||
for (size_t i = 0; i < foptsNum; i++) {
|
|
||||||
LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, };
|
// append all MAC replies into fopts buffer
|
||||||
popMacCommand(&cmd, &this->commandsUp, i);
|
size_t i = 0;
|
||||||
if (cmd.cid == 0) {
|
for (; i < this->commandsUp.numCommands; i++) {
|
||||||
|
LoRaWANMacCommand_t cmd = this->commandsUp.commands[i];
|
||||||
|
memcpy(foptsPtr, &cmd, 1 + cmd.len);
|
||||||
|
foptsPtr += cmd.len + 1;
|
||||||
|
}
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", this->commandsUp.numCommands);
|
||||||
|
RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsLen);
|
||||||
|
|
||||||
|
// pop the commands from back to front
|
||||||
|
for (; i >= 0; i--) {
|
||||||
|
if(this->commandsUp.commands[i].repeat > 0) {
|
||||||
|
this->commandsUp.commands[i].repeat--;
|
||||||
|
} else {
|
||||||
|
deleteMacCommand(this->commandsUp.commands[i].cid, &this->commandsUp);
|
||||||
|
}
|
||||||
|
if(i == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
foptsBuff[idx] = cmd.cid;
|
|
||||||
for(size_t i = 0; i < cmd.len; i++) {
|
|
||||||
foptsBuff[idx + 1 + i] = cmd.payload[i];
|
|
||||||
}
|
|
||||||
idx += cmd.len + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", foptsNum);
|
|
||||||
RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize);
|
|
||||||
|
|
||||||
uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen);
|
uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen);
|
||||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= foptsLen;
|
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= foptsLen;
|
||||||
|
|
||||||
|
@ -860,22 +869,22 @@ int16_t LoRaWANNode::downlinkCommon() {
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate the Rx timeout
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// create the masks that are required for receiving downlinks
|
// create the masks that are required for receiving downlinks
|
||||||
uint16_t irqFlags;
|
uint16_t irqFlags;
|
||||||
uint16_t irqMask;
|
uint16_t irqMask;
|
||||||
this->phyLayer->irqRxDoneRxTimeout(irqFlags, irqMask);
|
this->phyLayer->irqRxDoneRxTimeout(irqFlags, irqMask);
|
||||||
|
|
||||||
downlinkAction = false;
|
|
||||||
this->phyLayer->setPacketReceivedAction(LoRaWANNodeOnDownlinkAction);
|
this->phyLayer->setPacketReceivedAction(LoRaWANNodeOnDownlinkAction);
|
||||||
|
|
||||||
// perform listening in the two Rx windows
|
// perform listening in the two Rx windows
|
||||||
for(uint8_t i = 0; i < 2; i++) {
|
for(uint8_t i = 0; i < 2; i++) {
|
||||||
|
downlinkAction = false;
|
||||||
|
|
||||||
|
// calculate the Rx timeout
|
||||||
|
// 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);
|
||||||
|
|
||||||
// wait for the start of the Rx window
|
// wait for the start of the Rx window
|
||||||
// the waiting duration is shortened a bit to cover any possible timing errors
|
// the waiting duration is shortened a bit to cover any possible timing errors
|
||||||
|
@ -886,15 +895,15 @@ int16_t LoRaWANNode::downlinkCommon() {
|
||||||
mod->hal->delay(waitLen);
|
mod->hal->delay(waitLen);
|
||||||
|
|
||||||
// open Rx window by starting receive with specified timeout
|
// open Rx window by starting receive with specified timeout
|
||||||
state = this->phyLayer->startReceive(timeoutMod, irqFlags, irqMask, 0);
|
state = this->phyLayer->startReceive(timeoutMod, irqFlags, irqMask, timeoutMod);
|
||||||
RADIOLIB_DEBUG_PRINTLN("Opening Rx%d window (%d us timeout)... <-- Rx Delay end ", i+1, timeoutHost);
|
RADIOLIB_DEBUG_PRINTLN("Opening Rx%d window (%d us timeout)... <-- Rx Delay end ", i+1, timeoutHost);
|
||||||
|
|
||||||
// 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 + scanGuard / 2);
|
mod->hal->delay(timeoutHost / 1000 + scanGuard / 2);
|
||||||
RADIOLIB_DEBUG_PRINTLN("closing");
|
RADIOLIB_DEBUG_PRINTLN("closing");
|
||||||
|
|
||||||
// check if the DIO has fired indicating a timeout; no timeout means a receive is ongoing
|
// check if the IRQ bit for Rx Timeout is set
|
||||||
if(!downlinkAction) {
|
if(!this->phyLayer->isRxTimeout()) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
} else if(i == 0) {
|
} else if(i == 0) {
|
||||||
|
@ -907,15 +916,12 @@ int16_t LoRaWANNode::downlinkCommon() {
|
||||||
findDataRate(this->rx2.drMax, &dataRate);
|
findDataRate(this->rx2.drMax, &dataRate);
|
||||||
state = this->phyLayer->setDataRate(dataRate);
|
state = this->phyLayer->setDataRate(dataRate);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
timeoutHost = this->phyLayer->getTimeOnAir(0) + 2*scanGuard*1000;
|
|
||||||
timeoutMod = this->phyLayer->calculateRxTimeout(0, timeoutHost);
|
|
||||||
downlinkAction = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we got here due to a timeout, stop ongoing activities
|
// if we got here due to a timeout, stop ongoing activities
|
||||||
if(downlinkAction) {
|
if(this->phyLayer->isRxTimeout()) {
|
||||||
this->phyLayer->standby(); // TODO check: this should be done automagically due to RxSingle?
|
this->phyLayer->standby(); // TODO check: this should be done automagically due to RxSingle?
|
||||||
if(!this->FSK) {
|
if(!this->FSK) {
|
||||||
this->phyLayer->invertIQ(false);
|
this->phyLayer->invertIQ(false);
|
||||||
|
@ -1113,8 +1119,8 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
||||||
while(remLen > 0) {
|
while(remLen > 0) {
|
||||||
LoRaWANMacCommand_t cmd = {
|
LoRaWANMacCommand_t cmd = {
|
||||||
.cid = *foptsPtr,
|
.cid = *foptsPtr,
|
||||||
.len = (uint8_t)(remLen - 1),
|
|
||||||
.payload = { 0 },
|
.payload = { 0 },
|
||||||
|
.len = (uint8_t)(remLen - 1),
|
||||||
.repeat = 0,
|
.repeat = 0,
|
||||||
};
|
};
|
||||||
memcpy(cmd.payload, foptsPtr + 1, cmd.len);
|
memcpy(cmd.payload, foptsPtr + 1, cmd.len);
|
||||||
|
@ -1130,24 +1136,30 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
||||||
|
|
||||||
// if FOptsLen for the next uplink is larger than can be piggybacked onto an uplink, send separate uplink
|
// if FOptsLen for the next uplink is larger than can be piggybacked onto an uplink, send separate uplink
|
||||||
if(this->commandsUp.len > 15) {
|
if(this->commandsUp.len > 15) {
|
||||||
uint8_t foptsNum = this->commandsUp.numCommands;
|
|
||||||
size_t foptsBufSize = this->commandsUp.len;
|
size_t foptsBufSize = this->commandsUp.len;
|
||||||
uint8_t foptsBuff[foptsBufSize];
|
uint8_t foptsBuff[foptsBufSize];
|
||||||
size_t idx = 0;
|
uint8_t* foptsPtr = foptsBuff;
|
||||||
for(size_t i = 0; i < foptsNum; i++) {
|
// append all MAC replies into fopts buffer
|
||||||
LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, };
|
size_t i = 0;
|
||||||
popMacCommand(&cmd, &this->commandsUp, i);
|
for (; i < this->commandsUp.numCommands; i++) {
|
||||||
if(cmd.cid == 0) {
|
LoRaWANMacCommand_t cmd = this->commandsUp.commands[i];
|
||||||
|
memcpy(foptsPtr, &cmd, 1 + cmd.len);
|
||||||
|
foptsPtr += cmd.len + 1;
|
||||||
|
}
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", this->commandsUp.numCommands);
|
||||||
|
RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize);
|
||||||
|
|
||||||
|
// pop the commands from back to front
|
||||||
|
for (; i >= 0; i--) {
|
||||||
|
if(this->commandsUp.commands[i].repeat > 0) {
|
||||||
|
this->commandsUp.commands[i].repeat--;
|
||||||
|
} else {
|
||||||
|
deleteMacCommand(this->commandsUp.commands[i].cid, &this->commandsUp);
|
||||||
|
}
|
||||||
|
if(i == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
foptsBuff[idx] = cmd.cid;
|
|
||||||
for(size_t i = 1; i < cmd.len; i++) {
|
|
||||||
foptsBuff[idx + i] = cmd.payload[i];
|
|
||||||
}
|
|
||||||
idx += cmd.len + 1;
|
|
||||||
}
|
}
|
||||||
RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", foptsNum);
|
|
||||||
RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize);
|
|
||||||
|
|
||||||
this->isMACPayload = true;
|
this->isMACPayload = true;
|
||||||
this->uplink(foptsBuff, foptsBufSize, RADIOLIB_LORAWAN_FPORT_MAC_COMMAND);
|
this->uplink(foptsBuff, foptsBufSize, RADIOLIB_LORAWAN_FPORT_MAC_COMMAND);
|
||||||
|
@ -1484,24 +1496,6 @@ int16_t LoRaWANNode::pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQ
|
||||||
return(RADIOLIB_ERR_NONE);
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, size_t index) {
|
|
||||||
if(queue->numCommands == 0) {
|
|
||||||
return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cmd) {
|
|
||||||
memcpy(cmd, &queue->commands[index], sizeof(LoRaWANMacCommand_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(queue->commands[index].repeat > 0) {
|
|
||||||
queue->commands[index].repeat--;
|
|
||||||
} else {
|
|
||||||
deleteMacCommand(queue->commands[index].cid, queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(RADIOLIB_ERR_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue) {
|
int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue) {
|
||||||
if(queue->numCommands == 0) {
|
if(queue->numCommands == 0) {
|
||||||
return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY);
|
return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY);
|
||||||
|
@ -1512,7 +1506,7 @@ int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* que
|
||||||
queue->len -= (1 + queue->commands[index].len); // 1 byte for command ID, len for payload
|
queue->len -= (1 + queue->commands[index].len); // 1 byte for command ID, len for payload
|
||||||
// move all subsequent commands one forward in the queue
|
// move all subsequent commands one forward in the queue
|
||||||
if(index < RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1) {
|
if(index < RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1) {
|
||||||
memmove(&queue->commands[index], &queue->commands[index + 1], (RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - index) * sizeof(LoRaWANMacCommand_t));
|
memmove(&queue->commands[index], &queue->commands[index + 1], (RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - index - 1) * sizeof(LoRaWANMacCommand_t));
|
||||||
}
|
}
|
||||||
// set the latest element to all 0
|
// set the latest element to all 0
|
||||||
memset(&queue->commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1], 0x00, sizeof(LoRaWANMacCommand_t));
|
memset(&queue->commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1], 0x00, sizeof(LoRaWANMacCommand_t));
|
||||||
|
@ -1526,7 +1520,6 @@ int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* que
|
||||||
|
|
||||||
size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
RADIOLIB_DEBUG_PRINTLN("exe MAC CID = %02x, len = %d", cmd->cid, cmd->len);
|
RADIOLIB_DEBUG_PRINTLN("exe MAC CID = %02x, len = %d", cmd->cid, cmd->len);
|
||||||
Module* mod = this->phyLayer->getMod();
|
|
||||||
|
|
||||||
if(cmd->cid >= RADIOLIB_LORAWAN_MAC_CMD_PROPRIETARY) {
|
if(cmd->cid >= RADIOLIB_LORAWAN_MAC_CMD_PROPRIETARY) {
|
||||||
// TODO call user-provided callback for proprietary MAC commands?
|
// TODO call user-provided callback for proprietary MAC commands?
|
||||||
|
|
|
@ -295,12 +295,12 @@ struct LoRaWANMacCommand_t {
|
||||||
/*! \brief The command ID */
|
/*! \brief The command ID */
|
||||||
uint8_t cid;
|
uint8_t cid;
|
||||||
|
|
||||||
/*! \brief Length of the payload */
|
|
||||||
uint8_t len;
|
|
||||||
|
|
||||||
/*! \brief Payload buffer (5 bytes is the longest possible) */
|
/*! \brief Payload buffer (5 bytes is the longest possible) */
|
||||||
uint8_t payload[5];
|
uint8_t payload[5];
|
||||||
|
|
||||||
|
/*! \brief Length of the payload */
|
||||||
|
uint8_t len;
|
||||||
|
|
||||||
/*! \brief Repetition counter (the command will be uplinked repeat + 1 times) */
|
/*! \brief Repetition counter (the command will be uplinked repeat + 1 times) */
|
||||||
uint8_t repeat;
|
uint8_t repeat;
|
||||||
};
|
};
|
||||||
|
@ -477,12 +477,12 @@ class LoRaWANNode {
|
||||||
LoRaWANMacCommandQueue_t commandsUp = {
|
LoRaWANMacCommandQueue_t commandsUp = {
|
||||||
.numCommands = 0,
|
.numCommands = 0,
|
||||||
.len = 0,
|
.len = 0,
|
||||||
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
|
.commands = { { .cid = 0, .payload = { 0 }, .len = 0, .repeat = 0, } },
|
||||||
};
|
};
|
||||||
LoRaWANMacCommandQueue_t commandsDown = {
|
LoRaWANMacCommandQueue_t commandsDown = {
|
||||||
.numCommands = 0,
|
.numCommands = 0,
|
||||||
.len = 0,
|
.len = 0,
|
||||||
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
|
.commands = { { .cid = 0, .payload = { 0 }, .len = 0, .repeat = 0, } },
|
||||||
};
|
};
|
||||||
|
|
||||||
// the following is either provided by the network server (OTAA)
|
// the following is either provided by the network server (OTAA)
|
||||||
|
@ -571,9 +571,6 @@ class LoRaWANNode {
|
||||||
// push MAC command to queue, done by copy
|
// push MAC command to queue, done by copy
|
||||||
int16_t pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue);
|
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
|
// delete a specific MAC command from queue, indicated by the command ID
|
||||||
int16_t deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue);
|
int16_t deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue