From 2550fae4cb2901d05001514ded9c69deba42235e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Grome=C5=A1?= Date: Wed, 5 Feb 2025 20:41:43 +0100 Subject: [PATCH] [PHY] Staged modes (#1414) * [PHY] Add PHY support for staged modes * [SX126x] Add support for staged Rx/Tx * [PHY] Add missing virtual specifiers * [SX126x] Add missing overrides * [LoRaWAN] Use new stageMode and launchMode, reduce scanGuard * [LoRaWAN] A-synchronize transmissions (#1410) * [PHY] Pass mode config by reference * [PHY] Add default implementation of start transmit/receive * [SX126x] Implement staged modes * [SX128x] Implement staged modes * [SX127x] Implement staged modes * [LR11x0] Implement staged modes * [SX127x] Remove unused method from header * [SX126x] Make array const * Add new methods to keywords --------- Co-authored-by: StevenCellist --- keywords.txt | 2 + src/modules/LR11x0/LR11x0.cpp | 229 +++++++------- src/modules/LR11x0/LR11x0.h | 34 +- src/modules/SX126x/SX126x.cpp | 274 +++++++++-------- src/modules/SX126x/SX126x.h | 35 +-- src/modules/SX127x/SX127x.cpp | 290 ++++++++++-------- src/modules/SX127x/SX127x.h | 30 +- src/modules/SX128x/SX128x.cpp | 229 +++++++------- src/modules/SX128x/SX128x.h | 32 +- src/protocols/LoRaWAN/LoRaWAN.cpp | 90 +++++- src/protocols/LoRaWAN/LoRaWAN.h | 2 +- src/protocols/PhysicalLayer/PhysicalLayer.cpp | 42 ++- src/protocols/PhysicalLayer/PhysicalLayer.h | 82 ++++- 13 files changed, 771 insertions(+), 600 deletions(-) diff --git a/keywords.txt b/keywords.txt index f6bc613b..85ed0ed9 100644 --- a/keywords.txt +++ b/keywords.txt @@ -343,6 +343,8 @@ setDataRate KEYWORD2 checkDataRate KEYWORD2 setModem KEYWORD2 getModem KEYWORD2 +stageMode KEYWORD2 +launchMode KEYWORD2 # LoRaWAN getBufferNonces KEYWORD2 diff --git a/src/modules/LR11x0/LR11x0.cpp b/src/modules/LR11x0/LR11x0.cpp index 8aba735d..9a2085ba 100644 --- a/src/modules/LR11x0/LR11x0.cpp +++ b/src/modules/LR11x0/LR11x0.cpp @@ -414,73 +414,6 @@ void LR11x0::clearPacketSentAction() { this->clearIrqAction(); } -int16_t LR11x0::startTransmit(const uint8_t* data, size_t len, uint8_t addr) { - // suppress unused variable warning - (void)addr; - - // check packet length - if(len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH) { - return(RADIOLIB_ERR_PACKET_TOO_LONG); - } - - // maximum packet length is decreased by 1 when address filtering is active - if((this->addrComp != RADIOLIB_LR11X0_GFSK_ADDR_FILTER_DISABLED) && (len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH - 1)) { - return(RADIOLIB_ERR_PACKET_TOO_LONG); - } - - // set packet Length - int16_t state = RADIOLIB_ERR_NONE; - uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; - state = getPacketType(&modem); - RADIOLIB_ASSERT(state); - if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { - state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, len, this->crcTypeLoRa, this->invertIQEnabled); - - } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { - state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, len, this->crcTypeGFSK, this->whitening); - - } else if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { - return(RADIOLIB_ERR_UNKNOWN); - - } - RADIOLIB_ASSERT(state); - - // set DIO mapping - state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_TX_DONE | RADIOLIB_LR11X0_IRQ_TIMEOUT); - RADIOLIB_ASSERT(state); - - if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { - // in LR-FHSS mode, the packet is built by the device - // TODO add configurable device offset - state = lrFhssBuildFrame(this->lrFhssHdrCount, this->lrFhssCr, this->lrFhssGrid, true, this->lrFhssBw, this->lrFhssHopSeq, 0, const_cast(data), len); - RADIOLIB_ASSERT(state); - - } else { - // write packet to buffer - state = writeBuffer8(const_cast(data), len); - RADIOLIB_ASSERT(state); - - } - - // clear interrupt flags - state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL); - RADIOLIB_ASSERT(state); - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_TX); - - // start transmission - state = setTx(RADIOLIB_LR11X0_TX_TIMEOUT_NONE); - RADIOLIB_ASSERT(state); - - // wait for BUSY to go low (= PA ramp up done) - while(this->mod->hal->digitalRead(this->mod->getGpio())) { - this->mod->hal->yield(); - } - - return(state); -} - int16_t LR11x0::finishTransmit() { // clear interrupt flags clearIrqState(RADIOLIB_LR11X0_IRQ_ALL); @@ -493,46 +426,6 @@ int16_t LR11x0::startReceive() { return(this->startReceive(RADIOLIB_LR11X0_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0)); } -int16_t LR11x0::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { - (void)len; - - // check active modem - int16_t state = RADIOLIB_ERR_NONE; - uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; - state = getPacketType(&modem); - RADIOLIB_ASSERT(state); - if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) && - (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK)) { - return(RADIOLIB_ERR_WRONG_MODEM); - } - - // set DIO mapping - uint32_t irq = irqMask; - if(timeout != RADIOLIB_LR11X0_RX_TIMEOUT_INF) { - irq |= (1UL << RADIOLIB_IRQ_TIMEOUT); - } - state = setDioIrqParams(getIrqMapped(irqFlags & irq)); - RADIOLIB_ASSERT(state); - - // clear interrupt flags - state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL); - RADIOLIB_ASSERT(state); - - // set implicit mode and expected len if applicable - if((this->headerType == RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT) && (modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA)) { - state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, this->invertIQEnabled); - RADIOLIB_ASSERT(state); - } - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_RX); - - // set mode to receive - state = setRx(timeout); - - return(state); -} - uint32_t LR11x0::getIrqStatus() { // there is no dedicated "get IRQ" command, the IRQ bits are sent after the status bytes uint8_t buff[6] = { 0 }; @@ -2052,6 +1945,124 @@ int16_t LR11x0::getModem(ModemType_t* modem) { return(RADIOLIB_ERR_WRONG_MODEM); } +int16_t LR11x0::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) { + int16_t state; + + switch(mode) { + case(RADIOLIB_RADIO_MODE_RX): { + // check active modem + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK)) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set DIO mapping + if(cfg->receive.timeout != RADIOLIB_LR11X0_RX_TIMEOUT_INF) { + cfg->receive.irqMask |= (1UL << RADIOLIB_IRQ_TIMEOUT); + } + state = setDioIrqParams(getIrqMapped(cfg->receive.irqFlags & cfg->receive.irqMask)); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL); + RADIOLIB_ASSERT(state); + + // set implicit mode and expected len if applicable + if((this->headerType == RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT) && (modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA)) { + state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, this->invertIQEnabled); + RADIOLIB_ASSERT(state); + } + this->rxTimeout = cfg->receive.timeout; + } break; + + case(RADIOLIB_RADIO_MODE_TX): { + // check packet length + if(cfg->transmit.len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // maximum packet length is decreased by 1 when address filtering is active + if((this->addrComp != RADIOLIB_LR11X0_GFSK_ADDR_FILTER_DISABLED) && (cfg->transmit.len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH - 1)) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // set packet Length + state = RADIOLIB_ERR_NONE; + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, cfg->transmit.len, this->crcTypeLoRa, this->invertIQEnabled); + + } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, cfg->transmit.len, this->crcTypeGFSK, this->whitening); + + } else if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + return(RADIOLIB_ERR_UNKNOWN); + + } + RADIOLIB_ASSERT(state); + + // set DIO mapping + state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_TX_DONE | RADIOLIB_LR11X0_IRQ_TIMEOUT); + RADIOLIB_ASSERT(state); + + if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + // in LR-FHSS mode, the packet is built by the device + // TODO add configurable device offset + state = lrFhssBuildFrame(this->lrFhssHdrCount, this->lrFhssCr, this->lrFhssGrid, true, this->lrFhssBw, this->lrFhssHopSeq, 0, cfg->transmit.data, cfg->transmit.len); + RADIOLIB_ASSERT(state); + + } else { + // write packet to buffer + state = writeBuffer8(cfg->transmit.data, cfg->transmit.len); + RADIOLIB_ASSERT(state); + + } + + // clear interrupt flags + state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL); + RADIOLIB_ASSERT(state); + } break; + + default: + return(RADIOLIB_ERR_UNSUPPORTED); + } + + this->stagedMode = mode; + return(state); +} + +int16_t LR11x0::launchMode() { + int16_t state; + switch(this->stagedMode) { + case(RADIOLIB_RADIO_MODE_RX): { + this->mod->setRfSwitchState(Module::MODE_RX); + state = setRx(this->rxTimeout); + } break; + + case(RADIOLIB_RADIO_MODE_TX): { + this->mod->setRfSwitchState(Module::MODE_TX); + state = setTx(RADIOLIB_LR11X0_TX_TIMEOUT_NONE); + RADIOLIB_ASSERT(state); + + // wait for BUSY to go low (= PA ramp up done) + while(this->mod->hal->digitalRead(this->mod->getGpio())) { + this->mod->hal->yield(); + } + } break; + + default: + return(RADIOLIB_ERR_UNSUPPORTED); + } + + this->stagedMode = RADIOLIB_RADIO_MODE_NONE; + return(state); +} + int16_t LR11x0::modSetup(float tcxoVoltage, uint8_t modem) { this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); @@ -2334,12 +2345,12 @@ int16_t LR11x0::readRegMem32(uint32_t addr, uint32_t* data, size_t len) { return(state); } -int16_t LR11x0::writeBuffer8(uint8_t* data, size_t len) { +int16_t LR11x0::writeBuffer8(const uint8_t* data, size_t len) { // check maximum size if(len > RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN) { return(RADIOLIB_ERR_SPI_CMD_INVALID); } - return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WRITE_BUFFER, true, data, len)); + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WRITE_BUFFER, true, const_cast(data), len)); } int16_t LR11x0::readBuffer8(uint8_t* data, size_t len, size_t offset) { diff --git a/src/modules/LR11x0/LR11x0.h b/src/modules/LR11x0/LR11x0.h index 03455d35..6e95bde3 100644 --- a/src/modules/LR11x0/LR11x0.h +++ b/src/modules/LR11x0/LR11x0.h @@ -877,6 +877,7 @@ class LR11x0: public PhysicalLayer { using PhysicalLayer::transmit; using PhysicalLayer::receive; using PhysicalLayer::startTransmit; + using PhysicalLayer::startReceive; using PhysicalLayer::readData; /*! @@ -1073,16 +1074,6 @@ class LR11x0: public PhysicalLayer { */ void clearPacketSentAction() override; - /*! - \brief Interrupt-driven binary transmit method. - Overloads for string-based transmissions are implemented in PhysicalLayer. - \param data Binary data to be sent. - \param len Number of bytes to send. - \param addr Address to send the data to. Will only be added if address filtering was enabled. - \returns \ref status_codes - */ - int16_t startTransmit(const uint8_t* data, size_t len, uint8_t addr = 0) override; - /*! \brief Clean up after transmission is done. \returns \ref status_codes @@ -1097,20 +1088,6 @@ class LR11x0: public PhysicalLayer { */ int16_t startReceive() override; - /*! - \brief Interrupt-driven receive method. IRQ1 will be activated when full packet is received. - \param timeout Raw timeout value, expressed as multiples of 1/32.768 kHz (approximately 30.52 us). - Defaults to RADIOLIB_LR11X0_RX_TIMEOUT_INF for infinite timeout (Rx continuous mode), - set to RADIOLIB_LR11X0_RX_TIMEOUT_NONE for no timeout (Rx single mode). - If timeout other than infinite is set, signal will be generated on IRQ1. - - \param irqFlags Sets the IRQ flags that will trigger IRQ1, defaults to RADIOLIB_LR11X0_IRQ_RX_DONE. - \param irqMask Only for PhysicalLayer compatibility, not used. - \param len Only for PhysicalLayer compatibility, not used. - \returns \ref status_codes - */ - int16_t startReceive(uint32_t timeout, uint32_t irqFlags = RADIOLIB_LR11X0_IRQ_RX_DONE, uint32_t irqMask = 0, size_t len = 0); - /*! \brief Reads the current IRQ status. \returns IRQ status bits @@ -1622,6 +1599,12 @@ class LR11x0: public PhysicalLayer { */ int16_t calibrateImageRejection(float freqMin, float freqMax); + /*! \copydoc PhysicalLayer::stageMode */ + int16_t stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) override; + + /*! \copydoc PhysicalLayer::launchMode */ + int16_t launchMode() override; + #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif @@ -1630,7 +1613,7 @@ class LR11x0: public PhysicalLayer { // LR11x0 SPI command implementations int16_t writeRegMem32(uint32_t addr, const uint32_t* data, size_t len); int16_t readRegMem32(uint32_t addr, uint32_t* data, size_t len); - int16_t writeBuffer8(uint8_t* data, size_t len); + int16_t writeBuffer8(const uint8_t* data, size_t len); int16_t readBuffer8(uint8_t* data, size_t len, size_t offset); int16_t clearRxBuffer(void); int16_t writeRegMemMask32(uint32_t addr, uint32_t mask, uint32_t data); @@ -1829,6 +1812,7 @@ class LR11x0: public PhysicalLayer { uint8_t wifiScanMode = 0; bool gnss = false; + uint32_t rxTimeout = 0; int16_t modSetup(float tcxoVoltage, uint8_t modem); static int16_t SPIparseStatus(uint8_t in); diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 777dce09..26f169c7 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -518,115 +518,6 @@ void SX126x::clearChannelScanAction() { this->clearDio1Action(); } -int16_t SX126x::startTransmit(const uint8_t* data, size_t len, uint8_t addr) { - (void)addr; - - // check packet length - if(len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) { - return(RADIOLIB_ERR_PACKET_TOO_LONG); - } - - // maximum packet length is decreased by 1 when address filtering is active - if((RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF != RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF) && (len > RADIOLIB_SX126X_MAX_PACKET_LENGTH - 1)) { - return(RADIOLIB_ERR_PACKET_TOO_LONG); - } - - // set packet Length - int16_t state = RADIOLIB_ERR_NONE; - uint8_t modem = getPacketType(); - if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) { - state = setPacketParams(this->preambleLengthLoRa, this->crcTypeLoRa, len, this->headerType, this->invertIQEnabled); - - } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) { - state = setPacketParamsFSK(this->preambleLengthFSK, this->preambleDetLength, this->crcTypeFSK, this->syncWordLength, RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF, this->whitening, this->packetType, len); - - } else if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { - return(RADIOLIB_ERR_UNKNOWN); - - } - RADIOLIB_ASSERT(state); - - // set DIO mapping - if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { - state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT, RADIOLIB_SX126X_IRQ_TX_DONE); - } else { - state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_LR_FHSS_HOP, RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_LR_FHSS_HOP); - } - RADIOLIB_ASSERT(state); - - // set buffer pointers - state = setBufferBaseAddress(); - RADIOLIB_ASSERT(state); - - // write packet to buffer - if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { - state = writeBuffer(const_cast(data), len); - - } else { - // first, reset the LR-FHSS state machine - state = resetLRFHSS(); - RADIOLIB_ASSERT(state); - - // skip hopping for the first 4 - lrFhssHdrCount blocks - for(int i = 0; i < 4 - this->lrFhssHdrCount; ++i ) { - stepLRFHSS(); - } - - // in LR-FHSS mode, we need to build the entire packet manually - uint8_t frame[RADIOLIB_SX126X_MAX_PACKET_LENGTH] = { 0 }; - size_t frameLen = 0; - this->lrFhssFrameBitsRem = 0; - this->lrFhssFrameHopsRem = 0; - this->lrFhssHopNum = 0; - state = buildLRFHSSPacket(const_cast(data), len, frame, &frameLen, &this->lrFhssFrameBitsRem, &this->lrFhssFrameHopsRem); - RADIOLIB_ASSERT(state); - - // FIXME check max len for FHSS - state = writeBuffer(frame, frameLen); - RADIOLIB_ASSERT(state); - - // activate hopping - uint8_t hopCfg[] = { RADIOLIB_SX126X_HOPPING_ENABLED, (uint8_t)frameLen, (uint8_t)this->lrFhssFrameHopsRem }; - state = writeRegister(RADIOLIB_SX126X_REG_HOPPING_ENABLE, hopCfg, 3); - RADIOLIB_ASSERT(state); - - // write the initial hopping table - uint8_t initHops = this->lrFhssFrameHopsRem; - if(initHops > 16) { - initHops = 16; - }; - for(size_t i = 0; i < initHops; i++) { - // set the hop frequency and symbols - state = this->setLRFHSSHop(i); - RADIOLIB_ASSERT(state); - } - - } - RADIOLIB_ASSERT(state); - - // clear interrupt flags - state = clearIrqStatus(); - RADIOLIB_ASSERT(state); - - // fix sensitivity - state = fixSensitivity(); - RADIOLIB_ASSERT(state); - - // set RF switch (if present) - this->mod->setRfSwitchState(this->txMode); - - // start transmission - state = setTx(RADIOLIB_SX126X_TX_TIMEOUT_NONE); - RADIOLIB_ASSERT(state); - - // wait for BUSY to go low (= PA ramp up done) - while(this->mod->hal->digitalRead(this->mod->getGpio())) { - this->mod->hal->yield(); - } - - return(state); -} - int16_t SX126x::finishTransmit() { // clear interrupt flags int16_t state = clearIrqStatus(); @@ -640,25 +531,6 @@ int16_t SX126x::startReceive() { return(this->startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0)); } -int16_t SX126x::startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len) { - // in implicit header mode, use the provided length if it is nonzero - // otherwise we trust the user has previously set the payload length manually - if((this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (len != 0)) { - this->implicitLen = len; - } - - int16_t state = startReceiveCommon(timeout, irqFlags, irqMask); - RADIOLIB_ASSERT(state); - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_RX); - - // set mode to receive - state = setRx(timeout); - - return(state); -} - int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask) { // datasheet claims time to go to sleep is ~500us, same to wake up, compensate for that with 1 ms + TCXO delay uint32_t transitionTime = this->tcxoDelay + 1000; @@ -1666,6 +1538,152 @@ int16_t SX126x::getModem(ModemType_t* modem) { return(RADIOLIB_ERR_WRONG_MODEM); } +int16_t SX126x::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) { + int16_t state; + + switch(mode) { + case(RADIOLIB_RADIO_MODE_RX): { + // in implicit header mode, use the provided length if it is nonzero + // otherwise we trust the user has previously set the payload length manually + if((this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (cfg->receive.len != 0)) { + this->implicitLen = cfg->receive.len; + } + + state = startReceiveCommon(cfg->receive.timeout, cfg->receive.irqFlags, cfg->receive.irqMask); + RADIOLIB_ASSERT(state); + + this->rxTimeout = cfg->receive.timeout; + } break; + + case(RADIOLIB_RADIO_MODE_TX): { + // check packet length + if(cfg->transmit.len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // maximum packet length is decreased by 1 when address filtering is active + if((RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF != RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF) && + (cfg->transmit.len > RADIOLIB_SX126X_MAX_PACKET_LENGTH - 1)) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // set packet Length + state = RADIOLIB_ERR_NONE; + uint8_t modem = getPacketType(); + if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) { + state = setPacketParams(this->preambleLengthLoRa, this->crcTypeLoRa, cfg->transmit.len, this->headerType, this->invertIQEnabled); + + } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) { + state = setPacketParamsFSK(this->preambleLengthFSK, this->preambleDetLength, this->crcTypeFSK, this->syncWordLength, RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF, this->whitening, this->packetType, cfg->transmit.len); + + } else if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { + return(RADIOLIB_ERR_UNKNOWN); + + } + RADIOLIB_ASSERT(state); + + // set DIO mapping + if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { + state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT, RADIOLIB_SX126X_IRQ_TX_DONE); + } else { + state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_LR_FHSS_HOP, RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_LR_FHSS_HOP); + } + RADIOLIB_ASSERT(state); + + // set buffer pointers + state = setBufferBaseAddress(); + RADIOLIB_ASSERT(state); + + // write packet to buffer + if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) { + state = writeBuffer(cfg->transmit.data, cfg->transmit.len); + + } else { + // first, reset the LR-FHSS state machine + state = resetLRFHSS(); + RADIOLIB_ASSERT(state); + + // skip hopping for the first 4 - lrFhssHdrCount blocks + for(int i = 0; i < 4 - this->lrFhssHdrCount; ++i ) { + stepLRFHSS(); + } + + // in LR-FHSS mode, we need to build the entire packet manually + uint8_t frame[RADIOLIB_SX126X_MAX_PACKET_LENGTH] = { 0 }; + size_t frameLen = 0; + this->lrFhssFrameBitsRem = 0; + this->lrFhssFrameHopsRem = 0; + this->lrFhssHopNum = 0; + state = buildLRFHSSPacket(cfg->transmit.data, cfg->transmit.len, frame, &frameLen, &this->lrFhssFrameBitsRem, &this->lrFhssFrameHopsRem); + RADIOLIB_ASSERT(state); + + // FIXME check max len for FHSS + state = writeBuffer(frame, frameLen); + RADIOLIB_ASSERT(state); + + // activate hopping + const uint8_t hopCfg[] = { RADIOLIB_SX126X_HOPPING_ENABLED, (uint8_t)frameLen, (uint8_t)this->lrFhssFrameHopsRem }; + state = writeRegister(RADIOLIB_SX126X_REG_HOPPING_ENABLE, hopCfg, 3); + RADIOLIB_ASSERT(state); + + // write the initial hopping table + uint8_t initHops = this->lrFhssFrameHopsRem; + if(initHops > 16) { + initHops = 16; + }; + for(size_t i = 0; i < initHops; i++) { + // set the hop frequency and symbols + state = this->setLRFHSSHop(i); + RADIOLIB_ASSERT(state); + } + + } + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // fix sensitivity + state = fixSensitivity(); + RADIOLIB_ASSERT(state); + } break; + + default: + return(RADIOLIB_ERR_UNSUPPORTED); + } + + this->stagedMode = mode; + return(state); +} + +int16_t SX126x::launchMode() { + int16_t state; + switch(this->stagedMode) { + case(RADIOLIB_RADIO_MODE_RX): { + this->mod->setRfSwitchState(Module::MODE_RX); + state = setRx(this->rxTimeout); + } break; + + case(RADIOLIB_RADIO_MODE_TX): { + this->mod->setRfSwitchState(this->txMode); + state = setTx(RADIOLIB_SX126X_TX_TIMEOUT_NONE); + RADIOLIB_ASSERT(state); + + // wait for BUSY to go low (= PA ramp up done) + while(this->mod->hal->digitalRead(this->mod->getGpio())) { + this->mod->hal->yield(); + } + } break; + + default: + return(RADIOLIB_ERR_UNSUPPORTED); + } + + this->stagedMode = RADIOLIB_RADIO_MODE_NONE; + return(state); +} + #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE void SX126x::setDirectAction(void (*func)(void)) { setDio1Action(func); diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 371926ca..6198029f 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -478,6 +478,7 @@ class SX126x: public PhysicalLayer { using PhysicalLayer::transmit; using PhysicalLayer::receive; using PhysicalLayer::startTransmit; + using PhysicalLayer::startReceive; using PhysicalLayer::readData; /*! @@ -681,16 +682,6 @@ class SX126x: public PhysicalLayer { */ void clearChannelScanAction() override; - /*! - \brief Interrupt-driven binary transmit method. - Overloads for string-based transmissions are implemented in PhysicalLayer. - \param data Binary data to be sent. - \param len Number of bytes to send. - \param addr Address to send the data to. Will only be added if address filtering was enabled. - \returns \ref status_codes - */ - int16_t startTransmit(const uint8_t* data, size_t len, uint8_t addr = 0) override; - /*! \brief Clean up after transmission is done. \returns \ref status_codes @@ -705,23 +696,6 @@ class SX126x: public PhysicalLayer { */ int16_t startReceive() override; - /*! - \brief Interrupt-driven receive method. DIO1 will be activated when full packet is received. - \param timeout Receive mode type and/or raw timeout value, expressed as multiples of 15.625 us. - When set to RADIOLIB_SX126X_RX_TIMEOUT_INF, the timeout will be infinite and the device will remain - in Rx mode until explicitly commanded to stop (Rx continuous mode). - When set to RADIOLIB_SX126X_RX_TIMEOUT_NONE, there will be no timeout and the device will return - to standby when a packet is received (Rx single mode). - For any other value, timeout will be applied and signal will be generated on DIO1 for conditions - defined by irqFlags and irqMask. - - \param irqFlags Sets the IRQ flags, defaults to RX done, RX timeout, CRC error and header error. - \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RX done. - \param len Only for PhysicalLayer compatibility, not used. - \returns \ref status_codes - */ - int16_t startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK, size_t len = 0); - /*! \brief Interrupt-driven receive method where the device mostly sleeps and periodically wakes to listen. Note that this function assumes the unit will take 500us + TCXO_delay to change state. @@ -1145,6 +1119,12 @@ class SX126x: public PhysicalLayer { \returns \ref status_codes */ int16_t getModem(ModemType_t* modem) override; + + /*! \copydoc PhysicalLayer::stageMode */ + int16_t stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) override; + + /*! \copydoc PhysicalLayer::launchMode */ + int16_t launchMode() override; #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE /*! @@ -1304,6 +1284,7 @@ class SX126x: public PhysicalLayer { size_t implicitLen = 0; uint8_t invertIQEnabled = RADIOLIB_SX126X_LORA_IQ_STANDARD; + uint32_t rxTimeout = 0; // LR-FHSS stuff - there's a lot of it because all the encoding happens in software uint8_t lrFhssCr = RADIOLIB_SX126X_LR_FHSS_CR_2_3; diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 7510833e..c8a42278 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -389,67 +389,6 @@ int16_t SX127x::startReceive() { return(this->startReceive(0, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0)); } -int16_t SX127x::startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len) { - uint8_t mode = RADIOLIB_SX127X_RXCONTINUOUS; - - // set mode to standby - int16_t state = setMode(RADIOLIB_SX127X_STANDBY); - RADIOLIB_ASSERT(state); - - // set DIO pin mapping - state = this->setIrqFlags(getIrqMapped(irqFlags & irqMask)); - RADIOLIB_ASSERT(state); - - int16_t modem = getActiveModem(); - if(modem == RADIOLIB_SX127X_LORA) { - 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); - 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); - } - - // in FHSS mode, enable channel change interrupt - if(this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD) > RADIOLIB_SX127X_HOP_PERIOD_OFF) { - state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_LORA_FHSS_CHANGE_CHANNEL, 5, 4); - } - - // in implicit header mode, use the provided length if it is nonzero - // otherwise we trust the user has previously set the payload length manually - if((this->implicitHdr) && (len != 0)) { - state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_PAYLOAD_LENGTH, len); - this->packetLength = len; - } - - // apply fixes to errata - RADIOLIB_ERRATA_SX127X(true); - - // clear interrupt flags - clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); - - // set FIFO pointers - state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_RX_BASE_ADDR, 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); - - } else if(modem == RADIOLIB_SX127X_FSK_OOK) { - // clear interrupt flags - clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); - - // FSK modem does not distinguish between Rx single and continuous - mode = RADIOLIB_SX127X_RX; - } - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_RX); - - // set mode to receive - return(setMode(mode)); -} - void SX127x::setDio0Action(void (*func)(void), uint32_t dir) { this->mod->hal->attachInterrupt(this->mod->hal->pinToInterrupt(this->mod->getIrq()), func, dir); } @@ -568,80 +507,6 @@ bool SX127x::fifoGet(volatile uint8_t* data, int totalLen, volatile int* rcvLen) return(false); } -int16_t SX127x::startTransmit(const uint8_t* data, size_t len, uint8_t addr) { - // set mode to standby - int16_t state = setMode(RADIOLIB_SX127X_STANDBY); - - int16_t modem = getActiveModem(); - if(modem == RADIOLIB_SX127X_LORA) { - // check packet length - if(len > RADIOLIB_SX127X_MAX_PACKET_LENGTH) { - return(RADIOLIB_ERR_PACKET_TOO_LONG); - } - - // set DIO mapping - if(this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD) > RADIOLIB_SX127X_HOP_PERIOD_OFF) { - this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_TX_DONE | RADIOLIB_SX127X_DIO1_LORA_FHSS_CHANGE_CHANNEL, 7, 4); - } else { - this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_TX_DONE, 7, 6); - } - - // apply fixes to errata - RADIOLIB_ERRATA_SX127X(false); - - // clear interrupt flags - clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); - - // set packet length - state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_PAYLOAD_LENGTH, len); - - // set FIFO pointers - state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_TX_BASE_ADDR, RADIOLIB_SX127X_FIFO_TX_BASE_ADDR_MAX); - state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_ADDR_PTR, RADIOLIB_SX127X_FIFO_TX_BASE_ADDR_MAX); - - } else if(modem == RADIOLIB_SX127X_FSK_OOK) { - // clear interrupt flags - clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); - - // set DIO mapping - if(len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK) { - this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_PACK_FIFO_EMPTY, 5, 4); - } else { - this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PACKET_SENT, 7, 6); - } - - // set packet length - increased by 1 when address filter is enabled - uint8_t filter = this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PACKET_CONFIG_1, 2, 1); - if(this->packetLengthConfig == RADIOLIB_SX127X_PACKET_VARIABLE) { - if((filter == RADIOLIB_SX127X_ADDRESS_FILTERING_NODE) || (filter == RADIOLIB_SX127X_ADDRESS_FILTERING_NODE_BROADCAST)) { - this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, len + 1); - this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, addr); - } else { - this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, len); - } - - } - - } - - // write packet to FIFO - size_t packetLen = len; - if((modem == RADIOLIB_SX127X_FSK_OOK) && (len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK)) { - packetLen = RADIOLIB_SX127X_FIFO_THRESH - 1; - this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_THRESH, RADIOLIB_SX127X_TX_START_FIFO_NOT_EMPTY, 7, 7); - } - this->mod->SPIwriteRegisterBurst(RADIOLIB_SX127X_REG_FIFO, const_cast(data), packetLen); - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_TX); - - // start transmission - state |= setMode(RADIOLIB_SX127X_TX); - RADIOLIB_ASSERT(state); - - return(RADIOLIB_ERR_NONE); -} - int16_t SX127x::finishTransmit() { // wait for at least 1 bit at the lowest possible bit rate before clearing IRQ flags // not doing this and clearing RADIOLIB_SX127X_FLAG_FIFO_OVERRUN will dump the FIFO, @@ -1776,6 +1641,161 @@ int16_t SX127x::getModem(ModemType_t* modem) { return(RADIOLIB_ERR_WRONG_MODEM); } +int16_t SX127x::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) { + int16_t state; + + switch(mode) { + case(RADIOLIB_RADIO_MODE_RX): { + this->rxMode = RADIOLIB_SX127X_RXCONTINUOUS; + + // set mode to standby + state = setMode(RADIOLIB_SX127X_STANDBY); + RADIOLIB_ASSERT(state); + + // set DIO pin mapping + state = this->setIrqFlags(getIrqMapped(cfg->receive.irqFlags & cfg->receive.irqMask)); + RADIOLIB_ASSERT(state); + + int16_t modem = getActiveModem(); + if(modem == RADIOLIB_SX127X_LORA) { + if(cfg->receive.timeout != 0) { + // for non-zero timeout value, change mode to Rx single and set the timeout + this->rxMode = RADIOLIB_SX127X_RXSINGLE; + uint8_t msb_sym = (cfg->receive.timeout > 0x3FF) ? 0x3 : (uint8_t)(cfg->receive.timeout >> 8); + uint8_t lsb_sym = (cfg->receive.timeout > 0x3FF) ? 0xFF : (uint8_t)(cfg->receive.timeout & 0xFF); + 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); + } + + // in FHSS mode, enable channel change interrupt + if(this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD) > RADIOLIB_SX127X_HOP_PERIOD_OFF) { + state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_LORA_FHSS_CHANGE_CHANNEL, 5, 4); + } + + // in implicit header mode, use the provided length if it is nonzero + // otherwise we trust the user has previously set the payload length manually + if((this->implicitHdr) && (cfg->receive.len != 0)) { + state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_PAYLOAD_LENGTH, cfg->receive.len); + this->packetLength = cfg->receive.len; + } + + // apply fixes to errata + RADIOLIB_ERRATA_SX127X(true); + + // clear interrupt flags + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); + + // set FIFO pointers + state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_RX_BASE_ADDR, 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); + + } else if(modem == RADIOLIB_SX127X_FSK_OOK) { + // clear interrupt flags + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); + + // FSK modem does not distinguish between Rx single and continuous + this->rxMode = RADIOLIB_SX127X_RX; + } + } break; + + case(RADIOLIB_RADIO_MODE_TX): { + // set mode to standby + state = setMode(RADIOLIB_SX127X_STANDBY); + + int16_t modem = getActiveModem(); + if(modem == RADIOLIB_SX127X_LORA) { + // check packet length + if(cfg->transmit.len > RADIOLIB_SX127X_MAX_PACKET_LENGTH) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // set DIO mapping + if(this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD) > RADIOLIB_SX127X_HOP_PERIOD_OFF) { + this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_TX_DONE | RADIOLIB_SX127X_DIO1_LORA_FHSS_CHANGE_CHANNEL, 7, 4); + } else { + this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_TX_DONE, 7, 6); + } + + // apply fixes to errata + RADIOLIB_ERRATA_SX127X(false); + + // clear interrupt flags + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); + + // set packet length + state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_PAYLOAD_LENGTH, cfg->transmit.len); + + // set FIFO pointers + state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_TX_BASE_ADDR, RADIOLIB_SX127X_FIFO_TX_BASE_ADDR_MAX); + state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_ADDR_PTR, RADIOLIB_SX127X_FIFO_TX_BASE_ADDR_MAX); + + } else if(modem == RADIOLIB_SX127X_FSK_OOK) { + // clear interrupt flags + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); + + // set DIO mapping + if(cfg->transmit.len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK) { + this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_PACK_FIFO_EMPTY, 5, 4); + } else { + this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PACKET_SENT, 7, 6); + } + + // set packet length - increased by 1 when address filter is enabled + uint8_t filter = this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PACKET_CONFIG_1, 2, 1); + if(this->packetLengthConfig == RADIOLIB_SX127X_PACKET_VARIABLE) { + if((filter == RADIOLIB_SX127X_ADDRESS_FILTERING_NODE) || (filter == RADIOLIB_SX127X_ADDRESS_FILTERING_NODE_BROADCAST)) { + this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, cfg->transmit.len + 1); + this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, cfg->transmit.addr); + } else { + this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, cfg->transmit.len); + } + + } + + } + + // write packet to FIFO + size_t packetLen = cfg->transmit.len; + if((modem == RADIOLIB_SX127X_FSK_OOK) && (cfg->transmit.len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK)) { + packetLen = RADIOLIB_SX127X_FIFO_THRESH - 1; + this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_THRESH, RADIOLIB_SX127X_TX_START_FIFO_NOT_EMPTY, 7, 7); + } + this->mod->SPIwriteRegisterBurst(RADIOLIB_SX127X_REG_FIFO, cfg->transmit.data, packetLen); + } break; + + default: + return(RADIOLIB_ERR_UNSUPPORTED); + } + + this->stagedMode = mode; + return(state); +} + +int16_t SX127x::launchMode() { + int16_t state; + switch(this->stagedMode) { + case(RADIOLIB_RADIO_MODE_RX): { + this->mod->setRfSwitchState(Module::MODE_RX); + state = setMode(this->rxMode); + RADIOLIB_ASSERT(state); + } break; + + case(RADIOLIB_RADIO_MODE_TX): { + this->mod->setRfSwitchState(Module::MODE_TX); + state = setMode(RADIOLIB_SX127X_TX); + RADIOLIB_ASSERT(state); + } break; + + default: + return(RADIOLIB_ERR_UNSUPPORTED); + } + + this->stagedMode = RADIOLIB_RADIO_MODE_NONE; + return(state); +} + #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE void SX127x::setDirectAction(void (*func)(void)) { setDio1Action(func, this->mod->hal->GpioInterruptRising); diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index 1edfdcd4..59d5cb2c 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -586,6 +586,7 @@ class SX127x: public PhysicalLayer { using PhysicalLayer::transmit; using PhysicalLayer::receive; using PhysicalLayer::startTransmit; + using PhysicalLayer::startReceive; using PhysicalLayer::readData; // constructor @@ -798,15 +799,6 @@ class SX127x: public PhysicalLayer { */ bool fifoGet(volatile uint8_t* data, int totalLen, volatile int* rcvLen); - /*! - \brief Interrupt-driven binary transmit method. Will start transmitting arbitrary binary data up to 255 bytes long using %LoRa or up to 63 bytes using FSK modem. - \param data Binary data that will be transmitted. - \param len Length of binary data to transmit (in bytes). - \param addr Node address to transmit the packet to. Only used in FSK mode. - \returns \ref status_codes - */ - int16_t startTransmit(const uint8_t* data, size_t len, uint8_t addr = 0) override; - /*! \brief Clean up after transmission is done. \returns \ref status_codes @@ -820,19 +812,6 @@ class SX127x: public PhysicalLayer { */ int16_t startReceive() override; - /*! - \brief Interrupt-driven receive method, implemented for compatibility with PhysicalLayer. - \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 Sets the IRQ flags, defaults to RX done, RX timeout, CRC error and header error. - \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RX done. - \param len Expected length of packet to be received. Required for LoRa spreading factor 6. - \returns \ref status_codes - */ - int16_t startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK, size_t len = 0) override; - /*! \brief Reads data that was received after calling startReceive method. When the packet length is not known in advance, getPacketLength method must be called BEFORE calling readData! @@ -1163,6 +1142,12 @@ class SX127x: public PhysicalLayer { \returns \ref status_codes */ int16_t getModem(ModemType_t* modem) override; + + /*! \copydoc PhysicalLayer::stageMode */ + int16_t stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) override; + + /*! \copydoc PhysicalLayer::launchMode */ + int16_t launchMode() override; #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE /*! @@ -1268,6 +1253,7 @@ class SX127x: public PhysicalLayer { float dataRate = 0; bool packetLengthQueried = false; // FSK packet length is the first byte in FIFO, length can only be queried once uint8_t packetLengthConfig = RADIOLIB_SX127X_PACKET_VARIABLE; + uint8_t rxMode = RADIOLIB_SX127X_RXCONTINUOUS; int16_t config(); int16_t directMode(); diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp index 85fa91f6..fe58c7c0 100644 --- a/src/modules/SX128x/SX128x.cpp +++ b/src/modules/SX128x/SX128x.cpp @@ -513,70 +513,6 @@ void SX128x::clearPacketSentAction() { this->clearDio1Action(); } -int16_t SX128x::startTransmit(const uint8_t* data, size_t len, uint8_t addr) { - // suppress unused variable warning - (void)addr; - - // check packet length - if(len > RADIOLIB_SX128X_MAX_PACKET_LENGTH) { - return(RADIOLIB_ERR_PACKET_TOO_LONG); - } - - // set packet Length - int16_t state = RADIOLIB_ERR_NONE; - uint8_t modem = getPacketType(); - if(modem == RADIOLIB_SX128X_PACKET_TYPE_LORA) { - state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, len, this->crcLoRa, this->invertIQEnabled); - } else if((modem == RADIOLIB_SX128X_PACKET_TYPE_GFSK) || (modem == RADIOLIB_SX128X_PACKET_TYPE_FLRC)) { - state = setPacketParamsGFSK(this->preambleLengthGFSK, this->syncWordLen, this->syncWordMatch, this->crcGFSK, this->whitening, len); - } else if(modem == RADIOLIB_SX128X_PACKET_TYPE_BLE) { - state = setPacketParamsBLE(this->connectionState, this->crcBLE, this->bleTestPayload, this->whitening); - } else { - return(RADIOLIB_ERR_WRONG_MODEM); - } - RADIOLIB_ASSERT(state); - - // update output power - state = setTxParams(this->power); - RADIOLIB_ASSERT(state); - - // set buffer pointers - state = setBufferBaseAddress(); - RADIOLIB_ASSERT(state); - - // write packet to buffer - if(modem == RADIOLIB_SX128X_PACKET_TYPE_BLE) { - // first 2 bytes of BLE payload are PDU header - state = writeBuffer(const_cast(data), len, 2); - RADIOLIB_ASSERT(state); - } else { - state = writeBuffer(const_cast(data), len); - RADIOLIB_ASSERT(state); - } - - // set DIO mapping - state = setDioIrqParams(RADIOLIB_SX128X_IRQ_TX_DONE | RADIOLIB_SX128X_IRQ_RX_TX_TIMEOUT, RADIOLIB_SX128X_IRQ_TX_DONE); - RADIOLIB_ASSERT(state); - - // clear interrupt flags - state = clearIrqStatus(); - RADIOLIB_ASSERT(state); - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_TX); - - // start transmission - state = setTx(RADIOLIB_SX128X_TX_TIMEOUT_NONE); - RADIOLIB_ASSERT(state); - - // wait for BUSY to go low (= PA ramp up done) - while(this->mod->hal->digitalRead(this->mod->getGpio())) { - this->mod->hal->yield(); - } - - return(state); -} - int16_t SX128x::finishTransmit() { // clear interrupt flags clearIrqStatus(); @@ -589,49 +525,6 @@ int16_t SX128x::startReceive() { return(this->startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0)); } -int16_t SX128x::startReceive(uint16_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len) { - // in implicit header mode, use the provided length if it is nonzero - // otherwise we trust the user has previously set the payload length manually - if((this->headerType == RADIOLIB_SX128X_LORA_HEADER_IMPLICIT) && (len != 0)) { - this->payloadLen = len; - } - - // check active modem - if(getPacketType() == RADIOLIB_SX128X_PACKET_TYPE_RANGING) { - return(RADIOLIB_ERR_WRONG_MODEM); - } - - // set DIO mapping - if(timeout != RADIOLIB_SX128X_RX_TIMEOUT_INF) { - irqMask |= (1UL << RADIOLIB_IRQ_TIMEOUT); - } - - int16_t state = setDioIrqParams(getIrqMapped(irqFlags), getIrqMapped(irqMask)); - RADIOLIB_ASSERT(state); - - // set buffer pointers - state = setBufferBaseAddress(); - RADIOLIB_ASSERT(state); - - // clear interrupt flags - state = clearIrqStatus(); - RADIOLIB_ASSERT(state); - - // set implicit mode and expected len if applicable - if((this->headerType == RADIOLIB_SX128X_LORA_HEADER_IMPLICIT) && (getPacketType() == RADIOLIB_SX128X_PACKET_TYPE_LORA)) { - state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->payloadLen, this->crcLoRa, this->invertIQEnabled); - RADIOLIB_ASSERT(state); - } - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_RX); - - // set mode to receive - state = setRx(timeout); - - return(state); -} - int16_t SX128x::readData(uint8_t* data, size_t len) { // check active modem if(getPacketType() == RADIOLIB_SX128X_PACKET_TYPE_RANGING) { @@ -1493,6 +1386,128 @@ int16_t SX128x::invertIQ(bool enable) { return(setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->payloadLen, this->crcLoRa, this->invertIQEnabled)); } +int16_t SX128x::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) { + int16_t state; + + switch(mode) { + case(RADIOLIB_RADIO_MODE_RX): { + // in implicit header mode, use the provided length if it is nonzero + // otherwise we trust the user has previously set the payload length manually + if((this->headerType == RADIOLIB_SX128X_LORA_HEADER_IMPLICIT) && (cfg->receive.len != 0)) { + this->payloadLen = cfg->receive.len; + } + + // check active modem + if(getPacketType() == RADIOLIB_SX128X_PACKET_TYPE_RANGING) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set DIO mapping + if(cfg->receive.timeout != RADIOLIB_SX128X_RX_TIMEOUT_INF) { + cfg->receive.irqMask |= (1UL << RADIOLIB_IRQ_TIMEOUT); + } + + state = setDioIrqParams(getIrqMapped(cfg->receive.irqFlags), getIrqMapped(cfg->receive.irqMask)); + RADIOLIB_ASSERT(state); + + // set buffer pointers + state = setBufferBaseAddress(); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // set implicit mode and expected len if applicable + if((this->headerType == RADIOLIB_SX128X_LORA_HEADER_IMPLICIT) && (getPacketType() == RADIOLIB_SX128X_PACKET_TYPE_LORA)) { + state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->payloadLen, this->crcLoRa, this->invertIQEnabled); + RADIOLIB_ASSERT(state); + } + this->rxTimeout = cfg->receive.timeout; + } break; + + case(RADIOLIB_RADIO_MODE_TX): { + // check packet length + if(cfg->transmit.len > RADIOLIB_SX128X_MAX_PACKET_LENGTH) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // set packet Length + uint8_t modem = getPacketType(); + if(modem == RADIOLIB_SX128X_PACKET_TYPE_LORA) { + state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, cfg->transmit.len, this->crcLoRa, this->invertIQEnabled); + } else if((modem == RADIOLIB_SX128X_PACKET_TYPE_GFSK) || (modem == RADIOLIB_SX128X_PACKET_TYPE_FLRC)) { + state = setPacketParamsGFSK(this->preambleLengthGFSK, this->syncWordLen, this->syncWordMatch, this->crcGFSK, this->whitening, cfg->transmit.len); + } else if(modem == RADIOLIB_SX128X_PACKET_TYPE_BLE) { + state = setPacketParamsBLE(this->connectionState, this->crcBLE, this->bleTestPayload, this->whitening); + } else { + return(RADIOLIB_ERR_WRONG_MODEM); + } + RADIOLIB_ASSERT(state); + + // update output power + state = setTxParams(this->power); + RADIOLIB_ASSERT(state); + + // set buffer pointers + state = setBufferBaseAddress(); + RADIOLIB_ASSERT(state); + + // write packet to buffer + if(modem == RADIOLIB_SX128X_PACKET_TYPE_BLE) { + // first 2 bytes of BLE payload are PDU header + state = writeBuffer(cfg->transmit.data, cfg->transmit.len, 2); + RADIOLIB_ASSERT(state); + } else { + state = writeBuffer(cfg->transmit.data, cfg->transmit.len); + RADIOLIB_ASSERT(state); + } + + // set DIO mapping + state = setDioIrqParams(RADIOLIB_SX128X_IRQ_TX_DONE | RADIOLIB_SX128X_IRQ_RX_TX_TIMEOUT, RADIOLIB_SX128X_IRQ_TX_DONE); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + } break; + + default: + return(RADIOLIB_ERR_UNSUPPORTED); + } + + this->stagedMode = mode; + return(state); +} + +int16_t SX128x::launchMode() { + int16_t state; + switch(this->stagedMode) { + case(RADIOLIB_RADIO_MODE_RX): { + this->mod->setRfSwitchState(Module::MODE_RX); + state = setRx(this->rxTimeout); + RADIOLIB_ASSERT(state); + } break; + + case(RADIOLIB_RADIO_MODE_TX): { + this->mod->setRfSwitchState(Module::MODE_TX); + state = setTx(RADIOLIB_SX128X_TX_TIMEOUT_NONE); + RADIOLIB_ASSERT(state); + + // wait for BUSY to go low (= PA ramp up done) + while(this->mod->hal->digitalRead(this->mod->getGpio())) { + this->mod->hal->yield(); + } + } break; + + default: + return(RADIOLIB_ERR_UNSUPPORTED); + } + + this->stagedMode = RADIOLIB_RADIO_MODE_NONE; + return(state); +} + #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE void SX128x::setDirectAction(void (*func)(void)) { // SX128x is unable to perform direct mode reception diff --git a/src/modules/SX128x/SX128x.h b/src/modules/SX128x/SX128x.h index cd143e96..0211ed26 100644 --- a/src/modules/SX128x/SX128x.h +++ b/src/modules/SX128x/SX128x.h @@ -354,6 +354,7 @@ class SX128x: public PhysicalLayer { using PhysicalLayer::transmit; using PhysicalLayer::receive; using PhysicalLayer::startTransmit; + using PhysicalLayer::startReceive; using PhysicalLayer::readData; /*! @@ -530,16 +531,6 @@ class SX128x: public PhysicalLayer { */ void clearPacketSentAction() override; - /*! - \brief Interrupt-driven binary transmit method. - Overloads for string-based transmissions are implemented in PhysicalLayer. - \param data Binary data to be sent. - \param len Number of bytes to send. - \param addr Address to send the data to. Unsupported, compatibility only. - \returns \ref status_codes - */ - int16_t startTransmit(const uint8_t* data, size_t len, uint8_t addr = 0) override; - /*! \brief Clean up after transmission is done. \returns \ref status_codes @@ -554,20 +545,6 @@ class SX128x: public PhysicalLayer { */ int16_t startReceive() override; - /*! - \brief Interrupt-driven receive method. DIO1 will be activated when full packet is received. - \param timeout Raw timeout value, expressed as multiples of 15.625 us. Defaults to - RADIOLIB_SX128X_RX_TIMEOUT_INF for infinite timeout (Rx continuous mode), - set to RADIOLIB_SX128X_RX_TIMEOUT_NONE for no timeout (Rx single mode). - If timeout other than infinite is set, signal will be generated on DIO1. - - \param irqFlags Sets the IRQ flags, defaults to RX done, RX timeout, CRC error and header error. - \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RX done. - \param len Only for PhysicalLayer compatibility, not used. - \returns \ref status_codes - */ - int16_t startReceive(uint16_t timeout, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK, size_t len = 0); - /*! \brief Reads the current IRQ status. \returns IRQ status bits @@ -863,6 +840,12 @@ class SX128x: public PhysicalLayer { \returns \ref status_codes */ int16_t invertIQ(bool enable) override; + + /*! \copydoc PhysicalLayer::stageMode */ + int16_t stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) override; + + /*! \copydoc PhysicalLayer::launchMode */ + int16_t launchMode() override; #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE /*! @@ -920,6 +903,7 @@ class SX128x: public PhysicalLayer { // common parameters uint8_t power = 0; + uint32_t rxTimeout = 0; // cached LoRa parameters uint8_t invertIQEnabled = RADIOLIB_SX128X_LORA_IQ_STANDARD; diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 91cdf65d..333623a1 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -906,14 +906,22 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent) RADIOLIB_ASSERT(state); // calculate JoinRequest time-on-air in milliseconds + RadioLibTime_t toa = this->phyLayer->getTimeOnAir(RADIOLIB_LORAWAN_JOIN_REQUEST_LEN) / 1000; + if(this->dwellTimeUp) { - RadioLibTime_t toa = this->phyLayer->getTimeOnAir(RADIOLIB_LORAWAN_JOIN_REQUEST_LEN) / 1000; if(toa > this->dwellTimeUp) { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Dwell time exceeded: ToA = %lu, max = %d", (unsigned long)toa, this->dwellTimeUp); return(RADIOLIB_ERR_DWELL_TIME_EXCEEDED); } } + RadioModeConfig_t modeCfg; + modeCfg.transmit.data = joinRequestMsg; + modeCfg.transmit.len = RADIOLIB_LORAWAN_JOIN_REQUEST_LEN; + modeCfg.transmit.addr = 0; + state = this->phyLayer->stageMode(RADIOLIB_RADIO_MODE_TX, &modeCfg); + RADIOLIB_ASSERT(state); + // if requested, delay until transmitting JoinRequest RadioLibTime_t tNow = mod->hal->millis(); if(this->tUplink > tNow) { @@ -923,8 +931,26 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent) } } - // send it - state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN); + // start transmission + state = this->phyLayer->launchMode(); + RADIOLIB_ASSERT(state); + + // sleep for the duration of the transmission + mod->hal->delay(toa); + RadioLibTime_t txEnd = mod->hal->millis(); + + // wait for an additional transmission duration as Tx timeout period + while(!mod->hal->digitalRead(mod->getIrq())) { + // yield for multi-threaded platforms + mod->hal->yield(); + + if(mod->hal->millis() > txEnd + toa) { + return(RADIOLIB_ERR_TX_TIMEOUT); + } + } + state = this->phyLayer->finishTransmit(); + + // set the timestamp so that we can measure when to start receiving this->rxDelayStart = mod->hal->millis(); RADIOLIB_ASSERT(state); RADIOLIB_DEBUG_PROTOCOL_PRINTLN("JoinRequest sent (DevNonce = %d) <-- Rx Delay start", this->devNonce); @@ -935,7 +961,7 @@ int16_t LoRaWANNode::activateOTAA(uint8_t joinDr, LoRaWANJoinEvent_t *joinEvent) LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_DEV_NONCE], this->devNonce); // set the Time on Air of the JoinRequest - this->lastToA = this->phyLayer->getTimeOnAir(RADIOLIB_LORAWAN_JOIN_REQUEST_LEN) / 1000; + this->lastToA = toa; // configure Rx1 and Rx2 delay for JoinAccept message - these are re-configured once a valid JoinAccept is received this->rxDelays[1] = RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_1_MS; @@ -1270,7 +1296,6 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u Module* mod = this->phyLayer->getMod(); // check if the Rx windows were closed after sending the previous uplink - // this FORCES a user to call downlink() after an uplink() if(this->rxDelayEnd < this->rxDelayStart) { // not enough time elapsed since the last uplink, we may still be in an Rx window return(RADIOLIB_ERR_UPLINK_UNAVAILABLE); @@ -1295,6 +1320,22 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u RADIOLIB_LORAWAN_UPLINK, this->txPowerMax - 2*this->txPowerSteps); RADIOLIB_ASSERT(state); + + // check whether dwell time limitation is exceeded + RadioLibTime_t toa = this->phyLayer->getTimeOnAir(len) / 1000; + if(this->dwellTimeUp) { + if(toa > this->dwellTimeUp) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Dwell time exceeded: ToA = %lu, max = %d", (unsigned long)toa, this->dwellTimeUp); + return(RADIOLIB_ERR_DWELL_TIME_EXCEEDED); + } + } + + RadioModeConfig_t modeCfg; + modeCfg.transmit.data = in; + modeCfg.transmit.len = len; + modeCfg.transmit.addr = 0; + state = this->phyLayer->stageMode(RADIOLIB_RADIO_MODE_TX, &modeCfg); + RADIOLIB_ASSERT(state); // if requested, wait until transmitting uplink tNow = mod->hal->millis(); @@ -1305,14 +1346,31 @@ int16_t LoRaWANNode::transmitUplink(const LoRaWANChannel_t* chnl, uint8_t* in, u } } - state = this->phyLayer->transmit(in, len); + // start transmission + state = this->phyLayer->launchMode(); + RADIOLIB_ASSERT(state); + + // sleep for the duration of the transmission + mod->hal->delay(toa); + RadioLibTime_t txEnd = mod->hal->millis(); + + // wait for an additional transmission duration as Tx timeout period + while(!mod->hal->digitalRead(mod->getIrq())) { + // yield for multi-threaded platforms + mod->hal->yield(); + + if(mod->hal->millis() > txEnd + toa) { + return(RADIOLIB_ERR_TX_TIMEOUT); + } + } + state = this->phyLayer->finishTransmit(); // set the timestamp so that we can measure when to start receiving this->rxDelayStart = mod->hal->millis(); RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink sent <-- Rx Delay start"); // increase Time on Air of the uplink sequence - this->lastToA += this->phyLayer->getTimeOnAir(len) / 1000; + this->lastToA += toa; return(state); } @@ -1364,8 +1422,17 @@ int16_t LoRaWANNode::receiveCommon(uint8_t dir, const LoRaWANChannel_t* dlChanne RADIOLIB_ASSERT(state); // calculate the Rx timeout - RadioLibTime_t timeoutHost = this->phyLayer->getTimeOnAir(0) + 2*this->scanGuard*1000; + RadioLibTime_t timeoutHost = this->phyLayer->getTimeOnAir(0) + this->scanGuard*1000; RadioLibTime_t timeoutMod = this->phyLayer->calculateRxTimeout(timeoutHost); + + // TODO remove default arguments + RadioModeConfig_t modeCfg; + modeCfg.receive.timeout = timeoutMod; + modeCfg.receive.irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS; + modeCfg.receive.irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK; + modeCfg.receive.len = 0; + state = this->phyLayer->stageMode(RADIOLIB_RADIO_MODE_RX, &modeCfg); + RADIOLIB_ASSERT(state); // wait for the start of the Rx window RadioLibTime_t waitLen = tReference + dlDelays[window] - mod->hal->millis(); @@ -1380,14 +1447,13 @@ int16_t LoRaWANNode::receiveCommon(uint8_t dir, const LoRaWANChannel_t* dlChanne mod->hal->delay(waitLen); // open Rx window by starting receive with specified timeout - // TODO remove default arguments - state = this->phyLayer->startReceive(timeoutMod, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0); + state = this->phyLayer->launchMode(); tOpen = mod->hal->millis(); RADIOLIB_ASSERT(state); - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Opening Rx%d window (%d ms timeout)... <-- Rx Delay end ", window, (int)(timeoutHost / 1000 + scanGuard / 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) - mod->hal->delay(timeoutHost / 1000 + this->scanGuard / 2); + mod->hal->delay(timeoutHost / 1000 + 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 diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 1bfa782c..48272336 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -850,7 +850,7 @@ class LoRaWANNode { 500 is the **maximum** value, but it is not a good idea to go anywhere near that. If you have to go above 50 you probably have a bug somewhere. Check your device timing. */ - RadioLibTime_t scanGuard = 10; + RadioLibTime_t scanGuard = 5; #if !RADIOLIB_GODMODE protected: diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.cpp b/src/protocols/PhysicalLayer/PhysicalLayer.cpp index 3bf447ee..3b7d870a 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.cpp +++ b/src/protocols/PhysicalLayer/PhysicalLayer.cpp @@ -132,11 +132,18 @@ int16_t PhysicalLayer::startReceive() { } int16_t PhysicalLayer::startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len) { - (void)timeout; - (void)irqFlags; - (void)irqMask; - (void)len; - return(RADIOLIB_ERR_UNSUPPORTED); + RadioModeConfig_t cfg = { + .receive = { + .timeout = timeout, + .irqFlags = irqFlags, + .irqMask = irqMask, + .len = len, + } + }; + + int16_t state = this->stageMode(RADIOLIB_RADIO_MODE_RX, &cfg); + RADIOLIB_ASSERT(state); + return(this->launchMode()); } #if defined(RADIOLIB_BUILD_ARDUINO) @@ -150,10 +157,17 @@ int16_t PhysicalLayer::startTransmit(const char* str, uint8_t addr) { } int16_t PhysicalLayer::startTransmit(const uint8_t* data, size_t len, uint8_t addr) { - (void)data; - (void)len; - (void)addr; - return(RADIOLIB_ERR_UNSUPPORTED); + RadioModeConfig_t cfg = { + .transmit = { + .data = data, + .len = len, + .addr = addr, + } + }; + + int16_t state = this->stageMode(RADIOLIB_RADIO_MODE_TX, &cfg); + RADIOLIB_ASSERT(state); + return(this->launchMode()); } int16_t PhysicalLayer::finishTransmit() { @@ -537,6 +551,16 @@ int16_t PhysicalLayer::getModem(ModemType_t* modem) { return(RADIOLIB_ERR_UNSUPPORTED); } +int16_t PhysicalLayer::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) { + (void)mode; + (void)cfg; + return(RADIOLIB_ERR_UNSUPPORTED); +} + +int16_t PhysicalLayer::launchMode() { + return(RADIOLIB_ERR_UNSUPPORTED); +} + #if RADIOLIB_INTERRUPT_TIMING void PhysicalLayer::setInterruptSetup(void (*func)(uint32_t)) { Module* mod = getMod(); diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.h b/src/protocols/PhysicalLayer/PhysicalLayer.h index a8f66ce1..3d785b6d 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.h +++ b/src/protocols/PhysicalLayer/PhysicalLayer.h @@ -130,6 +130,58 @@ union ChannelScanConfig_t { RSSIScanConfig_t rssi; }; +struct StandbyConfig_t { + /*! \brief Module-specific standby mode configuration. */ + uint8_t mode; +}; + +struct ReceiveConfig_t { + /*! \brief Raw timeout value. Some modules use this argument to specify operation mode (single vs. continuous receive). */ + uint32_t timeout; + + /*! \brief Sets the IRQ flags. */ + RadioLibIrqFlags_t irqFlags; + + /*! \brief Sets the mask of IRQ flags that will trigger the radio interrupt pin. */ + RadioLibIrqFlags_t irqMask; + + /*! \brief Packet length, needed for some modules under special circumstances (e.g. LoRa implicit header mode). */ + size_t len; +}; + +struct TransmitConfig_t { + /*! \brief Binary data that will be transmitted. */ + const uint8_t* data; + + /*! \brief Length of binary data to transmit (in bytes). */ + size_t len; + + /*! \brief Node address to transmit the packet to. Only used in FSK mode. */ + uint8_t addr; +}; + +struct SleepConfig_t { + /*! \brief Module-specific sleep mode configuration. */ + uint8_t mode; +}; + +union RadioModeConfig_t { + /*! \brief Interpretation for standby mode */ + StandbyConfig_t standby; + + /*! \brief Interpretation for Rx mode */ + ReceiveConfig_t receive; + + /*! \brief Interpretation for Tx mode */ + TransmitConfig_t transmit; + + /*! \brief Interpretation for scanning */ + ChannelScanConfig_t scan; + + /*! \brief Interpretation for sleep mode */ + SleepConfig_t sleep; +}; + /*! \enum ModemType_t \brief Type of modem, used by setModem. @@ -140,6 +192,19 @@ enum ModemType_t { RADIOLIB_MODEM_LRFHSS, }; +/*! + \enum RadioModeType_t + \brief Basic radio operating modes, used by stageMode. +*/ +enum RadioModeType_t { + RADIOLIB_RADIO_MODE_NONE = 0, + RADIOLIB_RADIO_MODE_STANDBY, + RADIOLIB_RADIO_MODE_RX, + RADIOLIB_RADIO_MODE_TX, + RADIOLIB_RADIO_MODE_SCAN, + RADIOLIB_RADIO_MODE_SLEEP, +}; + /*! \class PhysicalLayer @@ -241,7 +306,7 @@ class PhysicalLayer { \param len Packet length, needed for some modules under special circumstances (e.g. LoRa implicit header mode). \returns \ref status_codes */ - virtual int16_t startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len); + virtual int16_t startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK, size_t len = 0); /*! \brief Binary receive method. Must be implemented in module class. @@ -669,6 +734,20 @@ class PhysicalLayer { */ virtual int16_t getModem(ModemType_t* modem); + /*! + \brief Stage mode of the radio to be launched later using launchMode. + \param mode Radio mode to prepare. + \param cfg Configuration of this mode (mode-dependent). + \returns \ref status_codes + */ + virtual int16_t stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg); + + /*! + \brief Launch previously staged mode. + \returns \ref status_codes + */ + virtual int16_t launchMode(); + #if RADIOLIB_INTERRUPT_TIMING /*! @@ -690,6 +769,7 @@ class PhysicalLayer { protected: #endif uint32_t irqMap[10] = { 0 }; + RadioModeType_t stagedMode = RADIOLIB_RADIO_MODE_NONE; #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE void updateDirectBuffer(uint8_t bit);