diff --git a/examples/SX128x/SX128x_Ranging/SX128x_Ranging.ino b/examples/SX128x/SX128x_Ranging/SX128x_Ranging.ino new file mode 100644 index 00000000..585f948b --- /dev/null +++ b/examples/SX128x/SX128x_Ranging/SX128x_Ranging.ino @@ -0,0 +1,84 @@ +/* + RadioLib SX128x Ranging Example + + This example performs ranging exchange between two + SX1280 LoRa radio modules. Ranging allows to measure + distance between the modules using time-of-flight + measurement. + + Only SX1280 and SX1282 support ranging! + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 lora = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + // carrier frequency: 2400.0 MHz + // bandwidth: 812.5 kHz + // spreading factor: 9 + // coding rate: 7 + // output power: 10 dBm + // preamble length: 12 symbols + // CRC: enabled + int state = lora.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[SX1280] Ranging ... ")); + + // start ranging exchange + // range as master: true + // slave address: 0x12345678 + int state = lora.range(true, 0x12345678); + + // the other module must be configured as slave with the same address + /* + int state = lora.range(false, 0x12345678); + */ + + if (state == ERR_NONE) { + // ranging finished successfully + Serial.println(F("success!")); + Serial.print(F("[SX1280] Distance:\t\t\t")); + Serial.print(lora.getRangingResult()); + Serial.println(F(" meters")); + + } else if (state == ERR_RANGING_TIMEOUT) { + // timed out waiting for ranging packet + Serial.println(F("timed out!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait for a second before ranging again + delay(1000); +} diff --git a/keywords.txt b/keywords.txt index 6afdf6ba..bcc233c4 100644 --- a/keywords.txt +++ b/keywords.txt @@ -311,3 +311,5 @@ ERR_INVALID_RX_PERIOD LITERAL1 ERR_INVALID_CALLSIGN LITERAL1 ERR_INVALID_NUM_REPEATERS LITERAL1 ERR_INVALID_REPEATER_CALLSIGN LITERAL1 + +ERR_RANGING_TIMEOUT LITERAL1 diff --git a/src/TypeDef.h b/src/TypeDef.h index 00ef2096..f8254d3d 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -480,6 +480,13 @@ */ #define ERR_INVALID_REPEATER_CALLSIGN -803 +// SX128x-specific status codes + +/*! + \brief Timed out waiting for ranging exchange finish. +*/ +#define ERR_RANGING_TIMEOUT -901 + /*! \} */ diff --git a/src/modules/SX128x/SX1280.cpp b/src/modules/SX128x/SX1280.cpp index afbc96df..2c4d09ee 100644 --- a/src/modules/SX128x/SX1280.cpp +++ b/src/modules/SX128x/SX1280.cpp @@ -3,3 +3,111 @@ SX1280::SX1280(Module* mod) : SX1281(mod) { } + +int16_t SX1280::range(bool master, uint32_t addr) { + // start ranging + int16_t state = startRanging(master, addr); + RADIOLIB_ASSERT(state); + + // wait until ranging is finished + uint32_t start = millis(); + while(!digitalRead(_mod->getIrq())) { + yield(); + if(millis() - start > 10000) { + clearIrqStatus(); + standby(); + return(ERR_RANGING_TIMEOUT); + } + } + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // set mode to standby + state = standby(); + + return(state); +} + +int16_t SX1280::startRanging(bool master, uint32_t addr) { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_LORA) || (modem == SX128X_PACKET_TYPE_RANGING))) { + return(ERR_WRONG_MODEM); + } + + // ensure modem is set to ranging + int16_t state = ERR_NONE; + if(modem == SX128X_PACKET_TYPE_LORA) { + state = setPacketType(SX128X_PACKET_TYPE_RANGING); + RADIOLIB_ASSERT(state); + } + + // set modulation parameters + state = setModulationParams(_sf, _bw, _cr); + RADIOLIB_ASSERT(state); + + // set packet parameters + state = setPacketParamsLoRa(_preambleLengthLoRa, _headerType, _payloadLen, _crcLoRa); + RADIOLIB_ASSERT(state); + + // check all address bits + uint8_t regValue; + state = readRegister(SX128X_REG_SLAVE_RANGING_ADDRESS_WIDTH, ®Value, 1); + RADIOLIB_ASSERT(state); + regValue &= 0b00111111; + regValue |= 0b11000000; + state = writeRegister(SX128X_REG_SLAVE_RANGING_ADDRESS_WIDTH, ®Value, 1); + RADIOLIB_ASSERT(state); + + // set remaining parameter values + uint32_t addrReg = SX128X_REG_SLAVE_RANGING_ADDRESS_BYTE_3; + uint32_t irqMask = SX128X_IRQ_RANGING_SLAVE_RESP_DONE | SX128X_IRQ_RANGING_SLAVE_REQ_DISCARD; + uint32_t irqDio1 = SX128X_IRQ_RANGING_SLAVE_RESP_DONE; + if(master) { + addrReg = SX128X_REG_MASTER_RANGING_ADDRESS_BYTE_3; + irqMask = SX128X_IRQ_RANGING_MASTER_RES_VALID | SX128X_IRQ_RANGING_MASTER_TIMEOUT; + irqDio1 = SX128X_IRQ_RANGING_MASTER_RES_VALID; + } + + // set ranging address + uint8_t addrBuff[] = { (uint8_t)((addr >> 24) & 0xFF), (uint8_t)((addr >> 16) & 0xFF), (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF) }; + state = writeRegister(addrReg, addrBuff, 4); + RADIOLIB_ASSERT(state); + + // set DIO mapping + state = setDioIrqParams(irqMask, irqDio1); + RADIOLIB_ASSERT(state); + + // set role and start ranging + if(master) { + state = setRangingRole(SX128X_RANGING_ROLE_MASTER); + RADIOLIB_ASSERT(state); + + state = setTx(SX128X_TX_TIMEOUT_NONE); + RADIOLIB_ASSERT(state); + + } else { + state = setRangingRole(SX128X_RANGING_ROLE_SLAVE); + RADIOLIB_ASSERT(state); + + state = setRx(SX128X_RX_TIMEOUT_INF); + RADIOLIB_ASSERT(state); + + } + + return(state); +} + +float SX1280::getRangingResult() { + // read the register values + uint8_t data[4]; + int16_t state = readRegister(SX128X_REG_RANGING_RESULT_MSB, data + 1, 3); + RADIOLIB_ASSERT(state); + + // calculate the real result + uint32_t raw = 0; + memcpy(&raw, data, sizeof(uint32_t)); + return((float)raw * (150.0/(4.096 * _bwKhz))); +} diff --git a/src/modules/SX128x/SX1280.h b/src/modules/SX128x/SX1280.h index 08aa1ec1..ea8c5a18 100644 --- a/src/modules/SX128x/SX1280.h +++ b/src/modules/SX128x/SX1280.h @@ -6,8 +6,6 @@ #include "SX128x.h" #include "SX1281.h" -// TODO implement ranging - /*! \class SX1280 @@ -22,6 +20,35 @@ class SX1280: public SX1281 { */ SX1280(Module* mod); + /*! + \brief Blocking ranging method. + + \param master Whether to execute ranging in master mode (true) or slave mode (false). + + \param addr Ranging address to be used. + + \returns \ref status_codes + */ + int16_t range(bool master, uint32_t addr); + + /*! + \brief Interrupt-driven ranging method. + + \param master Whether to execute ranging in master mode (true) or slave mode (false). + + \param addr Ranging address to be used. + + \returns \ref status_codes + */ + int16_t startRanging(bool master, uint32_t addr); + + /*! + \brief Gets ranging result of the last ranging exchange. + + \returns Ranging result in meters. + */ + float getRangingResult(); + #ifndef RADIOLIB_GODMODE private: #endif diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp index 2404e420..e4dae862 100644 --- a/src/modules/SX128x/SX128x.cpp +++ b/src/modules/SX128x/SX128x.cpp @@ -1182,6 +1182,16 @@ int16_t SX128x::clearIrqStatus(uint16_t clearIrqParams) { return(SPIwriteCommand(SX128X_CMD_CLEAR_IRQ_STATUS, data, 2)); } +int16_t SX128x::setRangingRole(uint8_t role) { + uint8_t data[] = { role }; + return(SPIwriteCommand(SX128X_CMD_SET_RANGING_ROLE, data, 1)); +} + +int16_t SX128x::setPacketType(uint8_t type) { + uint8_t data[] = { type }; + return(SPIwriteCommand(SX128X_CMD_SET_PACKET_TYPE, data, 1)); +} + int16_t SX128x::setHeaderType(uint8_t headerType, size_t len) { // check active modem uint8_t modem = getPacketType(); diff --git a/src/modules/SX128x/SX128x.h b/src/modules/SX128x/SX128x.h index 82e19884..f093a3c9 100644 --- a/src/modules/SX128x/SX128x.h +++ b/src/modules/SX128x/SX128x.h @@ -322,6 +322,10 @@ #define SX128X_REGULATOR_LDO 0x00 // 7 0 set regulator mode: LDO (default) #define SX128X_REGULATOR_DC_DC 0x01 // 7 0 DC-DC +//SX128X_CMD_SET_RANGING_ROLE +#define SX128X_RANGING_ROLE_MASTER 0x01 // 7 0 ranging role: master +#define SX128X_RANGING_ROLE_SLAVE 0x00 // 7 0 slave + /*! \class SX128x @@ -740,6 +744,13 @@ class SX128x: public PhysicalLayer { #ifndef RADIOLIB_GODMODE protected: #endif + Module* _mod; + + // cached LoRa parameters + float _bwKhz; + uint8_t _bw, _sf, _cr; + uint8_t _preambleLengthLoRa, _headerType, _payloadLen, _crcLoRa; + // SX128x SPI command implementations uint8_t getStatus(); int16_t writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); @@ -760,22 +771,17 @@ class SX128x: public PhysicalLayer { int16_t setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask = SX128X_IRQ_NONE, uint16_t dio3Mask = SX128X_IRQ_NONE); uint16_t getIrqStatus(); int16_t clearIrqStatus(uint16_t clearIrqParams = SX128X_IRQ_ALL); + int16_t setRangingRole(uint8_t role); + int16_t setPacketType(uint8_t type); int16_t setHeaderType(uint8_t headerType, size_t len = 0xFF); #ifndef RADIOLIB_GODMODE private: #endif - Module* _mod; - // common parameters uint8_t _pwr; - // cached LoRa parameters - float _bwKhz; - uint8_t _bw, _sf, _cr; - uint8_t _preambleLengthLoRa, _headerType, _payloadLen, _crcLoRa; - // cached GFSK parameters float _modIndexReal; uint16_t _brKbps;