From e8a6297c60ba64a64a2402900b3f41b8de154cfd Mon Sep 17 00:00:00 2001 From: jgromes Date: Sun, 21 Apr 2024 15:01:57 +0200 Subject: [PATCH] [LR11x0] Added support for LR-FHSS --- .../LR11x0_LR_FHSS_Modem.ino | 110 +++++++++ keywords.txt | 4 + src/modules/LR11x0/LR1110.cpp | 13 ++ src/modules/LR11x0/LR1110.h | 13 ++ src/modules/LR11x0/LR11x0.cpp | 217 +++++++++++------- src/modules/LR11x0/LR11x0.h | 33 ++- 6 files changed, 298 insertions(+), 92 deletions(-) create mode 100644 examples/LR11x0/LR11x0_LR_FHSS_Modem/LR11x0_LR_FHSS_Modem.ino diff --git a/examples/LR11x0/LR11x0_LR_FHSS_Modem/LR11x0_LR_FHSS_Modem.ino b/examples/LR11x0/LR11x0_LR_FHSS_Modem/LR11x0_LR_FHSS_Modem.ino new file mode 100644 index 00000000..a46d8a82 --- /dev/null +++ b/examples/LR11x0/LR11x0_LR_FHSS_Modem/LR11x0_LR_FHSS_Modem.ino @@ -0,0 +1,110 @@ +/* + RadioLib LR11x0 LR-FHSS Modem Example + + This example shows how to use LR-FHSS modem in LR11x0 chips. + + NOTE: The sketch below is just a guide on how to use + LR-FHSS modem, so this code should not be run directly! + Instead, modify the other examples to use LR-FHSS + modem and use the appropriate configuration + methods. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lr-fhss-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.beginLRFHSS(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // if needed, you can switch between any of the modems + // + // radio.begin() start LoRa modem (and disable LR-FHSS) + // radio.beginLRFHSS() start LR-FHSS modem (and disable LoRa) + + // the following settings can also + // be modified at run-time + state = radio.setFrequency(433.5); + state = radio.setLrFhssConfig(RADIOLIB_LR11X0_LR_FHSS_BW_1523_4, // bandwidth + RADIOLIB_LR11X0_LR_FHSS_CR_1_2, // coding rate + 3, // header count + 0x13A); // hopping sequence seed + state = radio.setOutputPower(10.0); + state = radio.setSyncWord(0x12345678); + if (state != RADIOLIB_ERR_NONE) { + Serial.print(F("Unable to set configuration, code ")); + Serial.println(state); + while (true); + } + + #warning "This sketch is just an API guide! Read the note at line 6." +} + +void loop() { + // LR-FHSS modem can use the same transmit/receive methods + // as the LoRa modem, even their interrupt-driven versions + + // transmit LR-FHSS packet + int state = radio.transmit("Hello World!"); + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + int state = radio.transmit(byteArr, 8); + */ + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("[LR1110] Packet transmitted successfully!")); + } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) { + Serial.println(F("[LR1110] Packet too long!")); + } else if (state == RADIOLIB_ERR_TX_TIMEOUT) { + Serial.println(F("[LR1110] Timed out while transmitting!")); + } else { + Serial.println(F("[LR1110] Failed to transmit packet, code ")); + Serial.println(state); + } + + // receive LR-FHSS packet + String str; + state = radio.receive(str); + /* + byte byteArr[8]; + int state = radio.receive(byteArr, 8); + */ + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("[LR1110] Received packet!")); + Serial.print(F("[LR1110] Data:\t")); + Serial.println(str); + } else if (state == RADIOLIB_ERR_RX_TIMEOUT) { + Serial.println(F("[LR1110] Timed out while waiting for packet!")); + } else { + Serial.print(F("[LR1110] Failed to receive packet, code ")); + Serial.println(state); + } + +} diff --git a/keywords.txt b/keywords.txt index 0ace7c6c..d160e782 100644 --- a/keywords.txt +++ b/keywords.txt @@ -239,6 +239,10 @@ disablePipe KEYWORD2 getStatus KEYWORD2 setAutoAck KEYWORD2 +# LR11x0 +beginLRFHSS KEYWORD2 +setLrFhssConfig KEYWORD2 + # RTTY idle KEYWORD2 byteArr KEYWORD2 diff --git a/src/modules/LR11x0/LR1110.cpp b/src/modules/LR11x0/LR1110.cpp index 4dbce1b2..729300af 100644 --- a/src/modules/LR11x0/LR1110.cpp +++ b/src/modules/LR11x0/LR1110.cpp @@ -31,6 +31,19 @@ int16_t LR1110::beginGFSK(float freq, float br, float freqDev, float rxBw, int8_ return(state); } +int16_t LR1110::beginLRFHSS(float freq, uint8_t bw, uint8_t cr, int8_t power, float tcxoVoltage) { + // execute common part + int16_t state = LR11x0::beginLRFHSS(bw, cr, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + return(state); +} + int16_t LR1110::setFrequency(float freq) { return(this->setFrequency(freq, true)); } diff --git a/src/modules/LR11x0/LR1110.h b/src/modules/LR11x0/LR1110.h index e122f00e..ffcd2e9d 100644 --- a/src/modules/LR11x0/LR1110.h +++ b/src/modules/LR11x0/LR1110.h @@ -52,6 +52,19 @@ class LR1110: public LR11x0 { \returns \ref status_codes */ int16_t beginGFSK(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 156.2, int8_t power = 10, uint16_t preambleLength = 16, float tcxoVoltage = 1.6); + + /*! + \brief Initialization method for LR-FHSS modem. + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param bw LR-FHSS bandwidth, one of RADIOLIB_LR11X0_LR_FHSS_BW_* values. Defaults to 722.66 kHz. + \param cr LR-FHSS coding rate, one of RADIOLIB_LR11X0_LR_FHSS_CR_* values. Defaults to 2/3 coding rate. + \param power Output power in dBm. Defaults to 10 dBm. + \param tcxoVoltage TCXO reference voltage to be set. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set LR11x0::XTAL to true. + \returns \ref status_codes + */ + int16_t beginLRFHSS(float freq = 434.0, uint8_t bw = RADIOLIB_LR11X0_LR_FHSS_BW_722_66, uint8_t cr = RADIOLIB_LR11X0_LR_FHSS_CR_2_3, int8_t power = 10, float tcxoVoltage = 1.6); // configuration methods diff --git a/src/modules/LR11x0/LR11x0.cpp b/src/modules/LR11x0/LR11x0.cpp index ba4f02d7..26d54aef 100644 --- a/src/modules/LR11x0/LR11x0.cpp +++ b/src/modules/LR11x0/LR11x0.cpp @@ -14,44 +14,8 @@ LR11x0::LR11x0(Module* mod) : PhysicalLayer(RADIOLIB_LR11X0_FREQUENCY_STEP_SIZE, } int16_t LR11x0::begin(float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, uint16_t preambleLength, float tcxoVoltage) { - // set module properties - this->mod->init(); - this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); - this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); - this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_32; - this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_16; - this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8; - this->mod->spiConfig.statusPos = 0; - this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_LR11X0_CMD_READ_REG_MEM; - this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_LR11X0_CMD_WRITE_REG_MEM; - this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_LR11X0_CMD_NOP; - this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_LR11X0_CMD_GET_STATUS; - this->mod->spiConfig.stream = true; - this->mod->spiConfig.parseStatusCb = SPIparseStatus; - this->mod->spiConfig.checkStatusCb = SPIcheckStatus; - - // try to find the LR11x0 chip - this will also reset the module at least once - if(!LR11x0::findChip(this->chipType)) { - RADIOLIB_DEBUG_BASIC_PRINTLN("No LR11x0 found!"); - this->mod->term(); - return(RADIOLIB_ERR_CHIP_NOT_FOUND); - } - RADIOLIB_DEBUG_BASIC_PRINTLN("M\tLR11x0"); - - // set mode to standby - int16_t state = standby(); - RADIOLIB_ASSERT(state); - - // set TCXO control, if requested - if(!this->XTAL && tcxoVoltage > 0.0) { - state = setTCXO(tcxoVoltage); - RADIOLIB_ASSERT(state); - } - - // configure settings not accessible by API - state = config(RADIOLIB_LR11X0_PACKET_TYPE_LORA); + // set module properties and perform initial setup + int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_LORA); RADIOLIB_ASSERT(state); // configure publicly accessible settings @@ -81,42 +45,8 @@ int16_t LR11x0::begin(float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, uint16 } int16_t LR11x0::beginGFSK(float br, float freqDev, float rxBw, uint16_t preambleLength, float tcxoVoltage) { - // set module properties - this->mod->init(); - this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); - this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_32; - this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_16; - this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8; - this->mod->spiConfig.statusPos = 0; - this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_LR11X0_CMD_READ_REG_MEM; - this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_LR11X0_CMD_WRITE_REG_MEM; - this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_LR11X0_CMD_NOP; - this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_LR11X0_CMD_GET_STATUS; - this->mod->spiConfig.stream = true; - this->mod->spiConfig.parseStatusCb = SPIparseStatus; - this->mod->spiConfig.checkStatusCb = SPIcheckStatus; - - // try to find the LR11x0 chip - this will also reset the module at least once - if(!LR11x0::findChip(this->chipType)) { - RADIOLIB_DEBUG_BASIC_PRINTLN("No LR11x0 found!"); - this->mod->term(); - return(RADIOLIB_ERR_CHIP_NOT_FOUND); - } - RADIOLIB_DEBUG_BASIC_PRINTLN("M\tLR11x0"); - - // set mode to standby - int16_t state = standby(); - RADIOLIB_ASSERT(state); - - // set TCXO control, if requested - if(!this->XTAL && tcxoVoltage > 0.0) { - state = setTCXO(tcxoVoltage); - RADIOLIB_ASSERT(state); - } - - // configure settings not accessible by API - state = config(RADIOLIB_LR11X0_PACKET_TYPE_GFSK); + // set module properties and perform initial setup + int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_GFSK); RADIOLIB_ASSERT(state); // configure publicly accessible settings @@ -152,6 +82,22 @@ int16_t LR11x0::beginGFSK(float br, float freqDev, float rxBw, uint16_t preamble return(RADIOLIB_ERR_NONE); } +int16_t LR11x0::beginLRFHSS(uint8_t bw, uint8_t cr, float tcxoVoltage) { + // set module properties and perform initial setup + int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setLrFhssConfig(bw, cr); + RADIOLIB_ASSERT(state); + + state = setSyncWord(0x12AD101B); + RADIOLIB_ASSERT(state); + + // set fixed configuration + return(setModulationParamsLrFhss(RADIOLIB_LR11X0_LR_FHSS_BIT_RATE_RAW, RADIOLIB_LR11X0_LR_FHSS_SHAPING_GAUSSIAN_BT_1_0)); +} + int16_t LR11x0::reset() { // run the reset sequence this->mod->hal->pinMode(this->mod->getRst(), this->mod->hal->GpioModeOutput); @@ -185,18 +131,17 @@ int16_t LR11x0::transmit(uint8_t* data, size_t len, uint8_t addr) { return(RADIOLIB_ERR_PACKET_TOO_LONG); } - uint32_t timeout = 0; - // get currently active modem uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; state = getPacketType(&modem); + uint32_t timeout = getTimeOnAir(len); if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { // calculate timeout (150% of expected time-on-air) - timeout = (getTimeOnAir(len) * 3) / 2; + timeout = (timeout * 3) / 2; - } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + } else if((modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) || (modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS)) { // calculate timeout (500% of expected time-on-air) - timeout = getTimeOnAir(len) * 5; + timeout = timeout * 5; } else { return(RADIOLIB_ERR_UNKNOWN); @@ -248,6 +193,13 @@ int16_t LR11x0::receive(uint8_t* data, size_t len) { } float brBps = ((float)(RADIOLIB_LR11X0_CRYSTAL_FREQ) * 1000000.0 * 32.0) / (float)this->bitRate; timeout = (uint32_t)(((maxLen * 8.0) / brBps) * 1000.0 * 5.0); + + } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + size_t maxLen = len; + if(len == 0) { + maxLen = 0xFF; + } + timeout = (uint32_t)(((maxLen * 8.0) / (RADIOLIB_LR11X0_LR_FHSS_BIT_RATE)) * 1000.0 * 5.0); } else { return(RADIOLIB_ERR_UNKNOWN); @@ -417,10 +369,13 @@ int16_t LR11x0::startTransmit(uint8_t* data, size_t len, uint8_t addr) { 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 { + + } else if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { return(RADIOLIB_ERR_UNKNOWN); + } RADIOLIB_ASSERT(state); @@ -428,9 +383,17 @@ int16_t LR11x0::startTransmit(uint8_t* data, size_t len, uint8_t addr) { state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_TX_DONE | RADIOLIB_LR11X0_IRQ_TIMEOUT, 0); RADIOLIB_ASSERT(state); - // write packet to buffer - state = writeBuffer8(data, len); - 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 grid step and device offset + state = lrFhssBuildFrame(this->lrFhssHdrCount, this->lrFhssCr, RADIOLIB_LR11X0_LR_FHSS_GRID_STEP_FCC, true, this->lrFhssBw, this->lrFhssHopSeq, 0, data, len); + + } else { + // write packet to buffer + state = writeBuffer8(data, len); + RADIOLIB_ASSERT(state); + + } // clear interrupt flags state = clearIrq(RADIOLIB_LR11X0_IRQ_ALL); @@ -471,7 +434,9 @@ int16_t LR11x0::startReceive(uint32_t timeout, uint32_t irqFlags, size_t len) { 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)) { + if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS)) { return(RADIOLIB_ERR_WRONG_MODEM); } @@ -519,7 +484,9 @@ int16_t LR11x0::readData(uint8_t* data, size_t len) { 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)) { + if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS)) { return(RADIOLIB_ERR_WRONG_MODEM); } @@ -659,7 +626,7 @@ int16_t LR11x0::setSpreadingFactor(uint8_t sf, bool legacy) { RADIOLIB_CHECK_RANGE(sf, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR); - // enable SF6 legacy mode + // TODO enable SF6 legacy mode if(legacy && (sf == 6)) { //this->mod->SPIsetRegValue(RADIOLIB_LR11X0_REG_SF6_SX127X_COMPAT, RADIOLIB_LR11X0_SF6_SX127X, 18, 18); } @@ -702,8 +669,20 @@ int16_t LR11x0::setCodingRate(uint8_t cr, bool longInterleave) { return(setModulationParamsLoRa(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize)); } -int16_t LR11x0::setSyncWord(uint8_t syncWord) { - return(setLoRaSyncWord(syncWord)); +int16_t LR11x0::setSyncWord(uint32_t syncWord) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(setLoRaSyncWord(syncWord & 0xFF)); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + return(lrFhssSetSyncWord(syncWord)); + + } + + return(RADIOLIB_ERR_WRONG_MODEM); } int16_t LR11x0::setBitRate(float br) { @@ -1271,6 +1250,9 @@ uint32_t LR11x0::getTimeOnAir(size_t len) { } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { return(((uint32_t)len * 8 * 1000000UL) / this->bitRate); + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + return(((uint32_t)len * 8 * 1000000UL) / RADIOLIB_LR11X0_LR_FHSS_BIT_RATE); + } return(0); @@ -1280,6 +1262,65 @@ float LR11x0::getDataRate() const { return(this->dataRateMeasured); } +int16_t LR11x0::setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount, uint16_t hopSeed) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // check and cache all parameters + RADIOLIB_CHECK_RANGE((int8_t)cr, (int8_t)RADIOLIB_LR11X0_LR_FHSS_CR_5_6, (int8_t)RADIOLIB_LR11X0_LR_FHSS_CR_1_3, RADIOLIB_ERR_INVALID_CODING_RATE); + this->lrFhssCr = cr; + RADIOLIB_CHECK_RANGE((int8_t)bw, (int8_t)RADIOLIB_LR11X0_LR_FHSS_BW_39_06, (int8_t)RADIOLIB_LR11X0_LR_FHSS_BW_1574_2, RADIOLIB_ERR_INVALID_BANDWIDTH); + this->lrFhssBw = bw; + RADIOLIB_CHECK_RANGE(hdrCount, 1, 4, RADIOLIB_ERR_INVALID_BIT_RANGE); + this->lrFhssHdrCount = hdrCount; + RADIOLIB_CHECK_RANGE((int16_t)hopSeed, (int16_t)0x000, (int16_t)0x1FF, RADIOLIB_ERR_INVALID_DATA_SHAPING); + this->lrFhssHopSeq = hopSeed; + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::modSetup(float tcxoVoltage, uint8_t modem) { + this->mod->init(); + this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); + this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_32; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_16; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8; + this->mod->spiConfig.statusPos = 0; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_LR11X0_CMD_READ_REG_MEM; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_LR11X0_CMD_WRITE_REG_MEM; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_LR11X0_CMD_NOP; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_LR11X0_CMD_GET_STATUS; + this->mod->spiConfig.stream = true; + this->mod->spiConfig.parseStatusCb = SPIparseStatus; + this->mod->spiConfig.checkStatusCb = SPIcheckStatus; + + // try to find the LR11x0 chip - this will also reset the module at least once + if(!LR11x0::findChip(this->chipType)) { + RADIOLIB_DEBUG_BASIC_PRINTLN("No LR11x0 found!"); + this->mod->term(); + return(RADIOLIB_ERR_CHIP_NOT_FOUND); + } + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tLR11x0"); + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set TCXO control, if requested + if(!this->XTAL && tcxoVoltage > 0.0) { + state = setTCXO(tcxoVoltage); + RADIOLIB_ASSERT(state); + } + + // configure settings not accessible by API + return(config(modem)); +} + int16_t LR11x0::SPIparseStatus(uint8_t in) { if((in & 0b00001110) == RADIOLIB_LR11X0_STAT_1_CMD_PERR) { return(RADIOLIB_ERR_SPI_CMD_INVALID); diff --git a/src/modules/LR11x0/LR11x0.h b/src/modules/LR11x0/LR11x0.h index 5b040233..63d70414 100644 --- a/src/modules/LR11x0/LR11x0.h +++ b/src/modules/LR11x0/LR11x0.h @@ -366,7 +366,8 @@ #define RADIOLIB_LR11X0_GFSK_RX_BW_312_0 (0x19UL << 0) // 7 0 312.0 kHz #define RADIOLIB_LR11X0_GFSK_RX_BW_373_6 (0x11UL << 0) // 7 0 373.6 kHz #define RADIOLIB_LR11X0_GFSK_RX_BW_467_0 (0x09UL << 0) // 7 0 467.0 kHz -#define RADIOLIB_LR11X0_LR_FHSS_BIT_RATE (0x8001E848UL) // 31 0 LR FHSS bit rate: 488.28215 bps +#define RADIOLIB_LR11X0_LR_FHSS_BIT_RATE (488.28215) // 31 0 LR FHSS bit rate: 488.28215 bps +#define RADIOLIB_LR11X0_LR_FHSS_BIT_RATE_RAW (0x8001E848UL) // 31 0 488.28215 bps in raw #define RADIOLIB_LR11X0_LR_FHSS_SHAPING_GAUSSIAN_BT_1_0 (0x0BUL << 0) // 7 0 shaping filter: Gaussian, BT = 1.0 #define RADIOLIB_LR11X0_SIGFOX_SHAPING_GAUSSIAN_BT_0_7 (0x16UL << 0) // 7 0 shaping filter: Gaussian, BT = 0.7 @@ -582,6 +583,15 @@ class LR11x0: public PhysicalLayer { */ int16_t beginGFSK(float br, float freqDev, float rxBw, uint16_t preambleLength, float tcxoVoltage); + /*! + \brief Initialization method for LR-FHSS modem. + \param bw LR-FHSS bandwidth, one of RADIOLIB_LR11X0_LR_FHSS_BW_* values. + \param cr LR-FHSS coding rate, one of RADIOLIB_LR11X0_LR_FHSS_CR_* values. + \param tcxoVoltage TCXO reference voltage to be set. + \returns \ref status_codes + */ + int16_t beginLRFHSS(uint8_t bw, uint8_t cr, float tcxoVoltage); + /*! \brief Reset method. Will reset the chip to the default state using RST pin. \returns \ref status_codes @@ -798,11 +808,11 @@ class LR11x0: public PhysicalLayer { int16_t setCodingRate(uint8_t cr, bool longInterleave = false); /*! - \brief Sets LoRa sync word. - \param syncWord LoRa sync word to be set. + \brief Sets LoRa or LR-FHSS sync word. + \param syncWord LoRa or LR-FHSS sync word to be set. For LoRa, only 8 least significant bits will be used \returns \ref status_codes */ - int16_t setSyncWord(uint8_t syncWord); + int16_t setSyncWord(uint32_t syncWord); /*! \brief Sets GFSK bit rate. Allowed values range from 0.6 to 300.0 kbps. @@ -996,6 +1006,16 @@ class LR11x0: public PhysicalLayer { */ float getDataRate() const; + /*! + \brief Sets LR-FHSS configuration. + \param bw LR-FHSS bandwidth, one of RADIOLIB_LR11X0_LR_FHSS_BW_* values. + \param cr LR-FHSS coding rate, one of RADIOLIB_LR11X0_LR_FHSS_CR_* values. + \param hdrCount Header packet count, 1 - 4. Defaults to 3. + \param hopSeed 9-bit seed number for PRNG generation of the hopping sequence. Defaults to 0x13A. + \returns \ref status_codes + */ + int16_t setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount = 3, uint16_t hopSeed = 0x13A); + #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif @@ -1165,8 +1185,13 @@ class LR11x0: public PhysicalLayer { uint8_t preambleDetLength = 0, rxBandwidth = 0, pulseShape = 0, crcTypeGFSK = 0, syncWordLength = 0, addrComp = 0, whitening = 0, packetType = 0, node = 0; uint16_t preambleLengthGFSK = 0; + // cached LR-FHSS parameters + uint8_t lrFhssCr = 0, lrFhssBw = 0, lrFhssHdrCount = 0; + uint16_t lrFhssHopSeq = 0; + float dataRateMeasured = 0; + int16_t modSetup(float tcxoVoltage, uint8_t modem); static int16_t SPIparseStatus(uint8_t in); static int16_t SPIcheckStatus(Module* mod); bool findChip(uint8_t ver);