From cfdd92198431282cf98f0231a4ca579f6bb7f6d5 Mon Sep 17 00:00:00 2001 From: jgromes Date: Tue, 14 May 2019 18:34:43 +0200 Subject: [PATCH] [SX126x] Implemented LoRa receive method --- .../SX126x/SX126x_Receive/SX126x_Receive.ino | 91 ++++++++ src/modules/SX126x.cpp | 220 +++++++++++++----- src/modules/SX126x.h | 8 +- 3 files changed, 265 insertions(+), 54 deletions(-) create mode 100644 examples/SX126x/SX126x_Receive/SX126x_Receive.ino diff --git a/examples/SX126x/SX126x_Receive/SX126x_Receive.ino b/examples/SX126x/SX126x_Receive/SX126x_Receive.ino new file mode 100644 index 00000000..a165db65 --- /dev/null +++ b/examples/SX126x/SX126x_Receive/SX126x_Receive.ino @@ -0,0 +1,91 @@ +/* + RadioLib SX126x Receive Example + + This example listens for LoRa transmissions using SX126x Lora modules. + To successfully receive data, the following settings have to be the same + on both transmitter and receiver: + - carrier frequency + - bandwidth + - spreading factor + - coding rate + - sync word + - preamble length + + Other modules from SX126x family can also be used. +*/ + +// include the library +#include + +// SX1262 module is in slot A on the shield +SX1262 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1262 with default settings + Serial.print(F("[SX1262] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bandwidth: 125.0 kHz + // spreading factor: 9 + // coding rate: 7 + // sync word: 0x1424 (private network) + // output power: 14 dBm + // current limit: 60 mA + // preamble length: 8 symbols + 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("[SX1262] Waiting for incoming transmission ... ")); + + // you can receive data as an Arduino String + // NOTE: receive() is a blocking method! + // See example ReceiveInterrupt for details + // on non-blocking reception method. + String str; + int state = lora.receive(str); + + // you can also receive data as byte array + /* + byte byteArr[8]; + int state = lora.receive(byteArr, 8); + */ + + if (state == ERR_NONE) { + // packet was successfully received + Serial.println(F("success!")); + + // print the data of the packet + Serial.print("[SX1262] Data:\t\t"); + Serial.println(str); + + // print the RSSI (Received Signal Strength Indicator) + // of the last received packet + Serial.print("[SX1262] RSSI:\t\t"); + Serial.print(lora.getRSSI()); + Serial.println(" dBm"); + + // print the SNR (Signal-to-Noise Ratio) + // of the last received packet + Serial.print("[SX1262] SNR:\t\t"); + Serial.print(lora.getSNR()); + Serial.println(" dBm"); + + } else if (state == ERR_RX_TIMEOUT) { + // timeout occurred while waiting for a packet + Serial.println(F("timeout!")); + + } else if (state == ERR_CRC_MISMATCH) { + // packet was received, but is malformed + Serial.println(F("CRC error!")); + + } +} diff --git a/src/modules/SX126x.cpp b/src/modules/SX126x.cpp index a3a7d2d9..a99c6a7a 100644 --- a/src/modules/SX126x.cpp +++ b/src/modules/SX126x.cpp @@ -63,14 +63,16 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { // set mode to standby int16_t state = standby(); + // check packet length + if(len >= 256) { + return(ERR_PACKET_TOO_LONG); + } + + uint32_t timeout = 0; + // get currently active modem uint8_t modem = getPacketType(); if(modem == SX126X_PACKET_TYPE_LORA) { - // check packet length - if(len >= 256) { - return(ERR_PACKET_TOO_LONG); - } - // calculate timeout (150% of expected time-on-air) float symbolLength = (float)(uint32_t(1) << _sf) / (float)_bwKhz; float sfCoeff1 = 4.25; @@ -84,59 +86,132 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { sfDivisor = 4*(_sf - 2); } float nSymbol = _preambleLength + sfCoeff1 + 8 + ceil(max(8.0 * len + (_crcType * 16.0) - 4.0 * _sf + sfCoeff2 + 20.0, 0.0) / sfDivisor) * (_cr + 4); - uint32_t timeout = (uint32_t)(symbolLength * nSymbol * 1.5); - DEBUG_PRINTLN(timeout); - - // set DIO mapping - setDioIrqParams(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_TX_DONE); + timeout = (uint32_t)(symbolLength * nSymbol * 1500.0); // set packet length setPacketParams(_preambleLength, _crcType, len); - // set buffer pointers - setBufferBaseAddress(); - - // write packet to buffer - writeBuffer(data, len); - - // clear interrupt flags - clearIrqStatus(); - - // start transmission - uint32_t timeoutValue = (uint32_t)((float)timeout / 0.015625); - setTx(timeoutValue); - //DEBUG_PRINTLN(digitalRead(_mod->getRx())); - - // wait for BUSY to go low (= PA ramp up done) - while(digitalRead(_mod->getRx())); - - // wait for packet transmission or timeout - uint32_t start = millis(); - while(!digitalRead(_mod->getInt0())) { - if(millis() - start > timeout) { - clearIrqStatus(); - return(ERR_TX_TIMEOUT); - } - } - uint32_t elapsed = millis() - start; - - // update data rate - _dataRate = (len*8.0)/((float)elapsed/1000.0); - - // clear interrupt flags - clearIrqStatus(); - - return(ERR_NONE); - } else if(modem == SX126X_PACKET_TYPE_GFSK) { + } else { + return(ERR_UNKNOWN); } - return(ERR_UNKNOWN); + DEBUG_PRINT(F("Timeout in ")) + DEBUG_PRINT(timeout); + DEBUG_PRINTLN(F(" us")) + + // set DIO mapping + setDioIrqParams(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_TX_DONE); + + // set buffer pointers + setBufferBaseAddress(); + + // write packet to buffer + writeBuffer(data, len); + + // clear interrupt flags + clearIrqStatus(); + + // start transmission + uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); + setTx(timeoutValue); + + // wait for BUSY to go low (= PA ramp up done) + while(digitalRead(_mod->getRx())); + + // wait for packet transmission or timeout + uint32_t start = micros(); + while(!digitalRead(_mod->getInt0())) { + if(micros() - start > timeout) { + clearIrqStatus(); + return(ERR_TX_TIMEOUT); + } + } + uint32_t elapsed = micros() - start; + + // update data rate + _dataRate = (len*8.0)/((float)elapsed/1000000.0); + + // clear interrupt flags + clearIrqStatus(); + + return(ERR_NONE); } int16_t SX126x::receive(uint8_t* data, size_t len) { + // set mode to standby + int16_t state = standby(); + uint32_t timeout = 0; + + // get currently active modem + uint8_t modem = getPacketType(); + if(modem == SX126X_PACKET_TYPE_LORA) { + // calculate timeout (100 LoRa symbols, the default for SX127x series) + float symbolLength = (float)(uint32_t(1) << _sf) / (float)_bwKhz; + timeout = (uint32_t)(symbolLength * 100.0 * 1000.0); + + } else if(modem == SX126X_PACKET_TYPE_GFSK) { + + } else { + return(ERR_UNKNOWN); + } + + DEBUG_PRINT(F("Timeout in ")) + DEBUG_PRINT(timeout); + DEBUG_PRINTLN(F(" us")) + + // set DIO mapping + setDioIrqParams(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_RX_DONE); + + // set buffer pointers + setBufferBaseAddress(); + + // clear interrupt flags + clearIrqStatus(); + + // start reception + uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); + setRx(timeoutValue); + + // wait for packet reception or timeout + uint32_t start = micros(); + while(!digitalRead(_mod->getInt0())) { + if(micros() - start > timeout) { + clearIrqStatus(); + return(ERR_RX_TIMEOUT); + } + } + + // check integrity CRC + uint16_t irq = getIrqStatus(); + if((irq & SX126X_IRQ_CRC_ERR) || (irq & SX126X_IRQ_HEADER_ERR)) { + clearIrqStatus(); + return(ERR_CRC_MISMATCH); + } + + // get packet length + uint8_t rxBufStatus[2]; + SPIreadCommand(SX126X_CMD_GET_RX_BUFFER_STATUS, rxBufStatus, 2); + size_t length = rxBufStatus[0]; + + // read packet data + if(len == 0) { + // argument 'len' equal to zero indicates String call, which means dynamically allocated data array + // dispose of the original and create a new one + delete[] data; + data = new uint8_t[length + 1]; + } + readBuffer(data, length); + + // add terminating null + data[length] = 0; + + // clear interrupt flags + clearIrqStatus(); + + return(ERR_NONE); } int16_t SX126x::transmitDirect(uint32_t frf) { @@ -152,7 +227,8 @@ int16_t SX126x::transmitDirect(uint32_t frf) { } int16_t SX126x::receiveDirect() { - + // SX126x is unable to ouput received data directly + return(ERR_UNKNOWN); } int16_t SX126x::sleep() { @@ -279,6 +355,30 @@ int16_t SX126x::setFrequencyDeviation(float freqDev) { return(ERR_NONE); } +float SX126x::getRSSI() { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // get last packet RSSI from packet status + uint32_t packetStatus = getPacketStatus(); + uint8_t rssiPkt = packetStatus & 0xFF; + return(-1.0 * rssiPkt/2.0); +} + +float SX126x::getSNR() { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // get last packet SNR from packet status + uint32_t packetStatus = getPacketStatus(); + uint8_t snrPkt = (packetStatus >> 8) & 0xFF; + return(snrPkt/4.0); +} + void SX126x::setTx(uint32_t timeout) { uint8_t data[3] = {(uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF)}; SPIwriteCommand(SX126X_CMD_SET_TX, data, 3); @@ -311,7 +411,17 @@ void SX126x::writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset) { uint8_t* dat = new uint8_t[1 + numBytes]; dat[0] = offset; memcpy(dat + 1, data, numBytes); - SPIwriteCommand(SX126X_CMD_WRITE_BUFFER, data, 1 + numBytes); + SPIwriteCommand(SX126X_CMD_WRITE_BUFFER, dat, 1 + numBytes); + delete[] dat; +} + +void SX126x::readBuffer(uint8_t* data, uint8_t numBytes) { + // offset will be always set to 0 (one extra NOP is sent) + uint8_t* dat = new uint8_t[1 + numBytes]; + dat[0] = SX126X_CMD_NOP; + memcpy(dat + 1, data, numBytes); + SPIreadCommand(SX126X_CMD_READ_BUFFER, dat, numBytes); + memcpy(data, dat + 1, numBytes); delete[] dat; } @@ -323,6 +433,12 @@ void SX126x::setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2M SPIwriteCommand(SX126X_CMD_SET_DIO_IRQ_PARAMS, data, 8); } +uint16_t SX126x::getIrqStatus() { + uint8_t data[2]; + SPIreadCommand(SX126X_CMD_GET_IRQ_STATUS, data, 2); + return(((uint16_t)(data[1]) << 8) | data[0]); +} + void SX126x::clearIrqStatus(uint16_t clearIrqParams) { uint8_t data[2] = {(uint8_t)((clearIrqParams >> 8) & 0xFF), (uint8_t)(clearIrqParams & 0xFF)}; SPIwriteCommand(SX126X_CMD_CLEAR_IRQ_STATUS, data, 2); @@ -377,10 +493,10 @@ uint8_t SX126x::getStatus() { return(data[0]); } -uint8_t SX126x::getRssiInt() { - uint8_t data[1]; - SPIreadCommand(SX126X_CMD_GET_RSSI_INST, data, 1); - return(data[0]); +uint32_t SX126x::getPacketStatus() { + uint8_t data[3]; + SPIreadCommand(SX126X_CMD_GET_PACKET_STATUS, data, 3); + return((((uint32_t)data[2]) << 16) | (((uint32_t)data[1]) << 8) | (uint32_t)data[0]); } uint16_t SX126x::getDeviceErrors() { diff --git a/src/modules/SX126x.h b/src/modules/SX126x.h index 9352a829..8f50bbdb 100644 --- a/src/modules/SX126x.h +++ b/src/modules/SX126x.h @@ -340,6 +340,8 @@ class SX126x: public PhysicalLayer { int16_t setPreambleLength(uint16_t preambleLength); float getDataRate(); int16_t setFrequencyDeviation(float freqDev); + float getRSSI(); + float getSNR(); protected: // SX1276x SPI command implementations @@ -349,16 +351,18 @@ class SX126x: public PhysicalLayer { void setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMax = SX126X_PA_CONFIG_HP_MAX, uint8_t paLut = SX126X_PA_CONFIG_PA_LUT); void writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); void writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset = 0x00); + void readBuffer(uint8_t* data, uint8_t numBytes); void setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask = SX126X_IRQ_NONE, uint16_t dio3Mask = SX126X_IRQ_NONE); + uint16_t getIrqStatus(); void clearIrqStatus(uint16_t clearIrqParams = 0xFFFF); void setRfFrequency(uint32_t frf); uint8_t getPacketType(); void setTxParams(uint8_t power, uint8_t rampTime = SX126X_PA_RAMP_200U); void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro = 0xFF); - void setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength = 0xFF, uint8_t headerType = SX126X_LORA_HEADER_EXPLICIT, uint8_t invertIQ = SX126X_LORA_IQ_INVERTED); + void setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength = 0xFF, uint8_t headerType = SX126X_LORA_HEADER_EXPLICIT, uint8_t invertIQ = SX126X_LORA_IQ_STANDARD); void setBufferBaseAddress(uint8_t txBaseAddress = 0x00, uint8_t rxBaseAddress = 0x00); uint8_t getStatus(); - uint8_t getRssiInt(); + uint32_t getPacketStatus(); uint16_t getDeviceErrors(); void clearDeviceErrors();