// Copyright (c) Sandeep Mistry. All rights reserved. // Licensed under the MIT license. // Modifications and additions copyright 2023 by Mark Qvist // Obviously still under the MIT license. #include "LoRa.h" #include "Modem.h" #define MCU_1284P 0x91 #define MCU_2560 0x92 #define MCU_ESP32 0x81 #define MCU_NRF52 0x71 #if defined(__AVR_ATmega1284P__) #define PLATFORM PLATFORM_AVR #define MCU_VARIANT MCU_1284P #elif defined(__AVR_ATmega2560__) #define PLATFORM PLATFORM_AVR #define MCU_VARIANT MCU_2560 #elif defined(ESP32) #define PLATFORM PLATFORM_ESP32 #define MCU_VARIANT MCU_ESP32 #elif defined(nRF52) #define PLATFORM PLATFORM_NRF52 #define MCU_VARIANT MCU_NRF52 #endif #ifndef MCU_VARIANT #error No MCU variant defined, cannot compile #endif #if MCU_VARIANT == MCU_ESP32 #include "soc/rtc_wdt.h" #define ISR_VECT IRAM_ATTR #else #define ISR_VECT #endif #if MODEM == SX1262 #define OP_FIFO_WRITE 0x0E #define OP_FIFO_READ 0x1E #define OP_RF_FREQ 0x86 #define OP_SLEEP 0x84 #define OP_STANDBY 0x80 #define OP_TX 0x83 #define OP_RX 0x82 #define OP_PA_CONFIG 0x95 #define OP_SET_IRQ_FLAGS 0x08 // also provides info such as // preamble detection, etc for // knowing when it's safe to switch // antenna modes #define OP_CLEAR_IRQ_STATUS 0x02 #define OP_GET_IRQ_STATUS 0x12 #define OP_RX_BUFFER_STATUS 0x13 #define OP_PACKET_STATUS 0x14 // get snr & rssi of last packet #define OP_CURRENT_RSSI 0x15 #define OP_MODULATION_PARAMS 0x8B // bw, sf, cr, etc. #define OP_PACKET_PARAMS 0x8C // crc, preamble, payload length, etc. #define OP_STATUS 0xC0 #define OP_TX_PARAMS 0x8E // set dbm, etc #define OP_PACKET_TYPE 0x8A #define OP_BUFFER_BASE_ADDR 0x8F #define OP_READ_REGISTER 0x1D #define OP_WRITE_REGISTER 0x0D #define OP_DIO3_TCXO_CTRL 0x97 #define OP_DIO2_RF_CTRL 0x9D #define OP_CALIBRATE 0x89 #define REG_OCP 0x08E7 #define REG_LNA 0x08AC // no agc in sx1262 #define REG_SYNC_WORD_MSB 0x0740 #define REG_SYNC_WORD_LSB 0x0741 #define REG_PAYLOAD_LENGTH 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98 #define REG_RANDOM_GEN 0x0819 #define MODE_LONG_RANGE_MODE 0x01 #define MODE_TCXO_3_3V 0x07 #define IRQ_TX_DONE_MASK 0x01 #define IRQ_RX_DONE_MASK 0x02 #define IRQ_PREAMBLE_DET_MASK 0x04 #define IRQ_HEADER_DET_MASK 0x10 #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x40 #define XTAL_FREQ (double)32000000 #define FREQ_DIV (double)pow(2.0, 25.0) #define FREQ_STEP (double)(XTAL_FREQ / FREQ_DIV) int fifo_tx_addr_ptr = 0; int fifo_rx_addr_ptr = 0; uint8_t packet[256] = {0}; #elif MODEM == SX1276 || MODEM == SX1278 // Registers #define REG_FIFO 0x00 #define REG_OP_MODE 0x01 #define REG_FRF_MSB 0x06 #define REG_FRF_MID 0x07 #define REG_FRF_LSB 0x08 #define REG_PA_CONFIG 0x09 #define REG_OCP 0x0b #define REG_LNA 0x0c #define REG_FIFO_ADDR_PTR 0x0d #define REG_FIFO_TX_BASE_ADDR 0x0e #define REG_FIFO_RX_BASE_ADDR 0x0f #define REG_FIFO_RX_CURRENT_ADDR 0x10 #define REG_IRQ_FLAGS 0x12 #define REG_RX_NB_BYTES 0x13 #define REG_MODEM_STAT 0x18 #define REG_PKT_SNR_VALUE 0x19 #define REG_PKT_RSSI_VALUE 0x1a #define REG_RSSI_VALUE 0x1b #define REG_MODEM_CONFIG_1 0x1d #define REG_MODEM_CONFIG_2 0x1e #define REG_PREAMBLE_MSB 0x20 #define REG_PREAMBLE_LSB 0x21 #define REG_PAYLOAD_LENGTH 0x22 #define REG_MODEM_CONFIG_3 0x26 #define REG_FREQ_ERROR_MSB 0x28 #define REG_FREQ_ERROR_MID 0x29 #define REG_FREQ_ERROR_LSB 0x2a #define REG_RSSI_WIDEBAND 0x2c #define REG_DETECTION_OPTIMIZE 0x31 #define REG_HIGH_BW_OPTIMIZE_1 0x36 #define REG_DETECTION_THRESHOLD 0x37 #define REG_SYNC_WORD 0x39 #define REG_HIGH_BW_OPTIMIZE_2 0x3a #define REG_DIO_MAPPING_1 0x40 #define REG_VERSION 0x42 #define REG_TCXO 0x4b #define REG_PA_DAC 0x4d // Modes #define MODE_LONG_RANGE_MODE 0x80 #define MODE_SLEEP 0x00 #define MODE_STDBY 0x01 #define MODE_TX 0x03 #define MODE_RX_CONTINUOUS 0x05 #define MODE_RX_SINGLE 0x06 // PA config #define PA_BOOST 0x80 // IRQ masks #define IRQ_TX_DONE_MASK 0x08 #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 #define IRQ_RX_DONE_MASK 0x40 #endif #define MAX_PKT_LENGTH 255 extern SPIClass spiModem; bool lora_preinit_done = false; LoRaClass::LoRaClass() : _spiSettings(8E6, MSBFIRST, SPI_MODE0), _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _frequency(0), _txp(0), _sf(0x07), _bw(0x04), _cr(0x01), _ldro(0x00), _packetIndex(0), _preambleLength(18), _implicitHeaderMode(0), _payloadLength(255), _crcMode(1), _onReceive(NULL) { // overide Stream timeout value setTimeout(0); } bool LoRaClass::preInit() { // setup pins pinMode(_ss, OUTPUT); // set SS high digitalWrite(_ss, HIGH); spiModem.begin(); // check version (retry for up to 2 seconds) #if MODEM == SX1276 || MODEM == SX1278 uint8_t version; long start = millis(); while (((millis() - start) < 2000) && (millis() >= start)) { version = readRegister(REG_VERSION); if (version == 0x12) { break; } delay(100); } if (version != 0x12) { return false; } lora_preinit_done = true; return true; #elif MODEM == SX1262 long start = millis(); uint8_t syncmsb; uint8_t synclsb; while (((millis() - start) < 2000) && (millis() >= start)) { syncmsb = readRegister(REG_SYNC_WORD_MSB); synclsb = readRegister(REG_SYNC_WORD_LSB); if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) { break; } delay(100); } if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) { return false; } lora_preinit_done = true; return true; #else return false; #endif } #if MODEM == SX1276 || MODEM == SX1278 uint8_t ISR_VECT LoRaClass::readRegister(uint8_t address) { return singleTransfer(address & 0x7f, 0x00); } void LoRaClass::writeRegister(uint8_t address, uint8_t value) { singleTransfer(address | 0x80, value); } uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t address, uint8_t value) { uint8_t response; digitalWrite(_ss, LOW); spiModem.beginTransaction(_spiSettings); spiModem.transfer(address); response = spiModem.transfer(value); spiModem.endTransaction(); digitalWrite(_ss, HIGH); return response; } #elif MODEM == SX1262 uint8_t ISR_VECT LoRaClass::readRegister(uint16_t address) { return singleTransfer(OP_READ_REGISTER, address, 0x00); } void LoRaClass::writeRegister(uint16_t address, uint8_t value) { singleTransfer(OP_WRITE_REGISTER, address, value); } uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) { waitOnBusy(); uint8_t response; digitalWrite(_ss, LOW); spiModem.beginTransaction(_spiSettings); spiModem.transfer(opcode); spiModem.transfer((address & 0xFF00) >> 8); spiModem.transfer(address & 0x00FF); if (opcode == OP_READ_REGISTER) { spiModem.transfer(0x00); } response = spiModem.transfer(value); spiModem.endTransaction(); digitalWrite(_ss, HIGH); return response; } void LoRaClass::enableAntenna() { uint8_t byte = 0x01; // enable dio2 rf switch executeOpcode(OP_DIO2_RF_CTRL, &byte, 1); digitalWrite(_rxen, HIGH); } void LoRaClass::disableAntenna() { digitalWrite(_rxen, LOW); } void LoRaClass::loraMode() { // enable lora mode on the SX1262 chip uint8_t mode = MODE_LONG_RANGE_MODE; executeOpcode(OP_PACKET_TYPE, &mode, 1); } void LoRaClass::waitOnBusy() { if (_busy != -1) { while (digitalRead(_busy) == HIGH) { // do nothing } } } void LoRaClass::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) { waitOnBusy(); digitalWrite(_ss, LOW); spiModem.beginTransaction(_spiSettings); spiModem.transfer(opcode); for (int i = 0; i < size; i++) { spiModem.transfer(buffer[i]); } spiModem.endTransaction(); digitalWrite(_ss, HIGH); } void LoRaClass::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) { waitOnBusy(); digitalWrite(_ss, LOW); spiModem.beginTransaction(_spiSettings); spiModem.transfer(opcode); spiModem.transfer(0x00); for (int i = 0; i < size; i++) { buffer[i] = spiModem.transfer(0x00); } spiModem.endTransaction(); digitalWrite(_ss, HIGH); } void LoRaClass::writeBuffer(const uint8_t* buffer, size_t size) { waitOnBusy(); digitalWrite(_ss, LOW); spiModem.beginTransaction(_spiSettings); spiModem.transfer(OP_FIFO_WRITE); spiModem.transfer(fifo_tx_addr_ptr); for (int i = 0; i < size; i++) { spiModem.transfer(buffer[i]); fifo_tx_addr_ptr++; } spiModem.endTransaction(); digitalWrite(_ss, HIGH); } void LoRaClass::readBuffer(uint8_t* buffer, size_t size) { waitOnBusy(); digitalWrite(_ss, LOW); spiModem.beginTransaction(_spiSettings); spiModem.transfer(OP_FIFO_READ); spiModem.transfer(fifo_rx_addr_ptr); spiModem.transfer(0x00); for (int i = 0; i < size; i++) { buffer[i] = spiModem.transfer(0x00); } spiModem.endTransaction(); digitalWrite(_ss, HIGH); } void LoRaClass::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) { // because there is no access to these registers on the sx1262, we have // to set all these parameters at once or not at all. uint8_t buf[8]; buf[0] = sf; buf[1] = bw; buf[2] = cr; // low data rate toggle buf[3] = ldro; // unused params in LoRa mode buf[4] = 0x00; buf[5] = 0x00; buf[6] = 0x00; buf[7] = 0x00; executeOpcode(OP_MODULATION_PARAMS, buf, 8); } void LoRaClass::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) { // because there is no access to these registers on the sx1262, we have // to set all these parameters at once or not at all. uint8_t buf[9]; buf[0] = uint8_t((preamble & 0xFF00) >> 8); buf[1] = uint8_t((preamble & 0x00FF)); buf[2] = headermode; buf[3] = length; buf[4] = crc; // standard IQ setting (no inversion) buf[5] = 0x00; // unused params buf[6] = 0x00; buf[7] = 0x00; buf[8] = 0x00; executeOpcode(OP_PACKET_PARAMS, buf, 9); } #endif int LoRaClass::begin(long frequency) { if (_reset != -1) { pinMode(_reset, OUTPUT); // perform reset digitalWrite(_reset, LOW); delay(10); digitalWrite(_reset, HIGH); delay(10); } if (_busy != -1) { pinMode(_busy, INPUT); } if (!lora_preinit_done) { if (!preInit()) { return false; } } #if MODEM == SX1276 || MODEM == SX1278 // put in sleep mode sleep(); // set frequency setFrequency(frequency); // set base addresses writeRegister(REG_FIFO_TX_BASE_ADDR, 0); writeRegister(REG_FIFO_RX_BASE_ADDR, 0); // set LNA boost writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); // set auto AGC writeRegister(REG_MODEM_CONFIG_3, 0x04); // set output power to 2 dBm setTxPower(2); // put in standby mode idle(); #elif MODEM == SX1262 //#if HAS_TCXO // turn TCXO on enableTCXO(); //#endif loraMode(); idle(); // cannot access registers in sleep mode on sx1262, set to idle instead if (_rxen != -1) { pinMode(_rxen, OUTPUT); enableAntenna(); } // calibrate RC64k, RC13M, PLL, ADC and image uint8_t calibrate = 0x7F; executeOpcode(OP_CALIBRATE, &calibrate, 1); setFrequency(frequency); // set output power to 2 dBm setTxPower(2); // set LNA boost writeRegister(REG_LNA, 0x96); // set base addresses uint8_t basebuf[2] = {0}; executeOpcode(OP_BUFFER_BASE_ADDR, basebuf, 2); setModulationParams(_sf, _bw, _cr, _ldro); setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); #endif return 1; } void LoRaClass::end() { // put in sleep mode sleep(); // stop spiModem spiModem.end(); lora_preinit_done = false; } int LoRaClass::beginPacket(int implicitHeader) { // put in standby mode idle(); if (implicitHeader) { implicitHeaderMode(); } else { explicitHeaderMode(); } #if MODEM == SX1276 || MODEM == SX1278 // reset FIFO address and paload length writeRegister(REG_FIFO_ADDR_PTR, 0); writeRegister(REG_PAYLOAD_LENGTH, 0); #elif MODEM == SX1262 _payloadLength = 0; fifo_tx_addr_ptr = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); #endif return 1; } int LoRaClass::endPacket() { #if MODEM == SX1276 || MODEM == SX1278 // put in TX mode writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); // wait for TX done while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { yield(); } // clear IRQ's writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); #elif MODEM == SX1262 setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); // put in single TX mode uint8_t timeout[3] = {0}; executeOpcode(OP_TX, timeout, 3); uint8_t buf[2]; buf[0] = 0x00; buf[1] = 0x00; executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2); // wait for TX done while ((buf[1] & IRQ_TX_DONE_MASK) == 0) { buf[0] = 0x00; buf[1] = 0x00; executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2); yield(); } // clear IRQ's uint8_t mask[2]; mask[0] = 0x00; mask[1] = IRQ_TX_DONE_MASK; executeOpcode(OP_CLEAR_IRQ_STATUS, mask, 2); #endif return 1; } int LoRaClass::parsePacket(int size) { int packetLength = 0; #if MODEM == SX1276 || MODEM == SX1278 int irqFlags = readRegister(REG_IRQ_FLAGS); #elif MODEM == SX1262 uint8_t buf[2]; buf[0] = 0x00; buf[1] = 0x00; executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2); #endif if (size > 0) { implicitHeaderMode(); #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); #elif MODEM == SX1262 // tell radio payload length _payloadLength = size; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); #endif } else { explicitHeaderMode(); } #if MODEM == SX1276 || MODEM == SX1278 // clear IRQ's writeRegister(REG_IRQ_FLAGS, irqFlags); if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { #elif MODEM == SX1262 uint8_t irqBufFlags[2]; irqBufFlags[0] = buf[0]; irqBufFlags[1] = buf[1]; executeOpcode(OP_CLEAR_IRQ_STATUS, irqBufFlags, 2); if ((buf[0] & IRQ_RX_DONE_MASK) && (buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { #endif // received a packet _packetIndex = 0; #if MODEM == SX1276 || MODEM == SX1278 // read packet length if (_implicitHeaderMode) { packetLength = readRegister(REG_PAYLOAD_LENGTH); } else { packetLength = readRegister(REG_RX_NB_BYTES); } #elif MODEM == SX1262 buf[0] = 0x00; buf[1] = 0x00; executeOpcodeRead(OP_RX_BUFFER_STATUS, buf, 2); packetLength = buf[0]; #endif #if MODEM == SX1276 || MODEM == SX1278 // set FIFO address to current RX address writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); #endif // put in standby mode idle(); #if MODEM == SX1276 || MODEM == SX1278 } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { // not currently in RX mode // reset FIFO address writeRegister(REG_FIFO_ADDR_PTR, 0); // put in single RX mode writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); } #elif MODEM == SX1262 } else { uint8_t status; status = 0x00; executeOpcodeRead(OP_STATUS, &status, 1); if ((status >> 4 & 0x7) != 0x5) { // not currently in RX mode // put in single RX mode uint8_t buf[3] = {0}; executeOpcode(OP_RX, buf, 3); } } #endif return packetLength; } uint8_t LoRaClass::modemStatus() { #if MODEM == SX1276 || MODEM == SX1278 return readRegister(REG_MODEM_STAT); #elif MODEM == SX1262 // imitate the register status from the sx1276 / 78 uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2); uint8_t clearbuf[2] = {0}; uint8_t byte = 0x00; if (buf[1] & IRQ_PREAMBLE_DET_MASK != 0) { byte = byte | 0x01 | 0x04; // clear register after reading clearbuf[1] = IRQ_PREAMBLE_DET_MASK; } if (buf[1] & IRQ_HEADER_DET_MASK != 0) { byte = byte | 0x02 | 0x04; // clear register after reading clearbuf[1] = clearbuf[1] | IRQ_HEADER_DET_MASK; } executeOpcode(OP_CLEAR_IRQ_STATUS, clearbuf, 2); return byte; #endif } uint8_t LoRaClass::currentRssiRaw() { #if MODEM == SX1276 || MODEM == SX1278 uint8_t rssi = readRegister(REG_RSSI_VALUE); return rssi; #elif MODEM == SX1262 uint8_t byte = 0; executeOpcodeRead(OP_CURRENT_RSSI, &byte, 1); return byte; #endif } int ISR_VECT LoRaClass::currentRssi() { #if MODEM == SX1276 || MODEM == SX1278 int rssi = (int)readRegister(REG_RSSI_VALUE) - RSSI_OFFSET; if (_frequency < 820E6) rssi -= 7; return rssi; #elif MODEM == SX1262 uint8_t byte = 0; executeOpcodeRead(OP_CURRENT_RSSI, &byte, 1); int rssi = -(int(byte)) / 2; return rssi; #endif } uint8_t LoRaClass::packetRssiRaw() { #if MODEM == SX1276 || MODEM == SX1278 uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE); return pkt_rssi_value; #elif MODEM == SX1262 uint8_t buf[3] = {0}; executeOpcodeRead(OP_PACKET_STATUS, buf, 3); return buf[2]; #endif } int ISR_VECT LoRaClass::packetRssi() { #if MODEM == SX1276 || MODEM == SX1278 int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE) - RSSI_OFFSET; int pkt_snr = packetSnr(); if (_frequency < 820E6) pkt_rssi -= 7; if (pkt_snr < 0) { pkt_rssi += pkt_snr; } else { // Slope correction is (16/15)*pkt_rssi, // this estimation looses one floating point // operation, and should be precise enough. pkt_rssi = (int)(1.066 * pkt_rssi); } return pkt_rssi; #elif MODEM == SX1262 // may need more calculations here uint8_t buf[3] = {0}; executeOpcodeRead(OP_PACKET_STATUS, buf, 3); int pkt_rssi = -(int(buf[2])) / 2; return pkt_rssi; #endif } uint8_t ISR_VECT LoRaClass::packetSnrRaw() { #if MODEM == SX1276 || MODEM == SX1278 return readRegister(REG_PKT_SNR_VALUE); #elif MODEM == SX1262 uint8_t buf[3] = {0}; executeOpcodeRead(OP_PACKET_STATUS, buf, 3); return buf[1]; #endif } float ISR_VECT LoRaClass::packetSnr() { #if MODEM == SX1276 || MODEM == SX1278 return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; #elif MODEM == SX1262 uint8_t buf[3] = {0}; executeOpcodeRead(OP_PACKET_STATUS, buf, 3); return float(buf[1]) / 4.0; #endif } long LoRaClass::packetFrequencyError() { int32_t freqError = 0; #if MODEM == SX1276 || MODEM == SX1278 freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB) & B111); freqError <<= 8L; freqError += static_cast(readRegister(REG_FREQ_ERROR_MID)); freqError <<= 8L; freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB)); if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on freqError -= 524288; // B1000'0000'0000'0000'0000 } const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) const float fError = ((static_cast(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37 return static_cast(fError); #elif MODEM == SX1262 // todo: implement this, no idea how to check it on the sx1262 const float fError = 0.0; return static_cast(fError); #endif } size_t LoRaClass::write(uint8_t byte) { return write(&byte, sizeof(byte)); } size_t LoRaClass::write(const uint8_t *buffer, size_t size) { #if MODEM == SX1276 || MODEM == SX1278 int currentLength = readRegister(REG_PAYLOAD_LENGTH); // check size if ((currentLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - currentLength; } #elif MODEM == SX1262 if ((_payloadLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - _payloadLength; } #endif // write data #if MODEM == SX1276 || MODEM == SX1278 for (size_t i = 0; i < size; i++) { writeRegister(REG_FIFO, buffer[i]); } // update length writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); #elif MODEM == SX1262 writeBuffer(buffer, size); _payloadLength = _payloadLength + size; #endif return size; } int ISR_VECT LoRaClass::available() { #if MODEM == SX1276 || MODEM == SX1278 return (readRegister(REG_RX_NB_BYTES) - _packetIndex); #elif MODEM == SX1262 uint8_t buf[2] = {0}; executeOpcodeRead(OP_RX_BUFFER_STATUS, buf, 2); return buf[0] - _packetIndex; #endif } int ISR_VECT LoRaClass::read() { if (!available()) { return -1; } #if MODEM == SX1276 || MODEM == SX1278 _packetIndex++; return readRegister(REG_FIFO); #elif MODEM == SX1262 // if received new packet if (_packetIndex == 0) { uint8_t rxbuf[2] = {0}; executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2); int size = rxbuf[0]; fifo_rx_addr_ptr = rxbuf[1]; readBuffer(packet, size); } uint8_t byte = packet[_packetIndex]; _packetIndex++; return byte; #endif } int LoRaClass::peek() { if (!available()) { return -1; } #if MODEM == SX1276 || MODEM == SX1278 // store current FIFO address int currentAddress = readRegister(REG_FIFO_ADDR_PTR); // read uint8_t b = readRegister(REG_FIFO); // restore FIFO address writeRegister(REG_FIFO_ADDR_PTR, currentAddress); #elif MODEM == SX1262 // if received new packet if (_packetIndex == 0) { uint8_t rxbuf[2] = {0}; executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2); int size = rxbuf[0]; fifo_rx_addr_ptr = rxbuf[1]; readBuffer(packet, size); } uint8_t b = packet[_packetIndex]; #endif return b; } void LoRaClass::flush() { } void LoRaClass::onReceive(void(*callback)(int)) { _onReceive = callback; if (callback) { pinMode(_dio0, INPUT); #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_DIO_MAPPING_1, 0x00); #elif MODEM == SX1262 // set preamble and header detection irqs, plus dio0 mask uint8_t buf[8]; // set irq masks, enable all buf[0] = 0xFF; buf[1] = 0xFF; // set dio0 masks buf[2] = 0x00; buf[3] = IRQ_RX_DONE_MASK; // set dio1 masks buf[4] = 0x00; buf[5] = 0x00; // set dio2 masks buf[6] = 0x00; buf[7] = 0x00; executeOpcode(OP_SET_IRQ_FLAGS, buf, 8); #endif #ifdef SPI_HAS_NOTUSINGINTERRUPT spiModem.usingInterrupt(digitalPinToInterrupt(_dio0)); #endif attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); } else { detachInterrupt(digitalPinToInterrupt(_dio0)); #ifdef SPI_HAS_NOTUSINGINTERRUPT spiModem.notUsingInterrupt(digitalPinToInterrupt(_dio0)); #endif } } void LoRaClass::receive(int size) { if (size > 0) { implicitHeaderMode(); #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); #elif MODEM == SX1262 // tell radio payload length _payloadLength = size; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); #endif } else { explicitHeaderMode(); } #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); #elif MODEM == SX1262 uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode executeOpcode(OP_RX, mode, 3); #endif } void LoRaClass::idle() { #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); #elif MODEM == SX1262 //#if HAS_TCXO // STDBY_XOSC uint8_t byte = 0x01; //#else // // STDBY_RC // uint8_t byte = 0x00; //#endif executeOpcode(OP_STANDBY, &byte, 1); #endif } void LoRaClass::sleep() { #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); #elif MODEM == SX1262 if (_rxen != -1) { disableAntenna(); } uint8_t byte = 0x00; executeOpcode(OP_SLEEP, &byte, 1); #endif } void LoRaClass::enableTCXO() { #if MODEM == SX1276 || MODEM == SX1278 uint8_t tcxo_reg = readRegister(REG_TCXO); writeRegister(REG_TCXO, tcxo_reg | 0x10); #elif MODEM == SX1262 // only tested for RAK4630, voltage may be different on other platforms uint8_t buf[4] = {MODE_TCXO_3_3V, 0x00, 0x00, 0xFF}; executeOpcode(OP_DIO3_TCXO_CTRL, buf, 4); #endif } void LoRaClass::disableTCXO() { #if MODEM == SX1276 || MODEM == SX1278 uint8_t tcxo_reg = readRegister(REG_TCXO); writeRegister(REG_TCXO, tcxo_reg & 0xEF); #elif MODEM == SX1262 // currently cannot disable on SX1262? #endif } void LoRaClass::setTxPower(int level, int outputPin) { #if MODEM == SX1276 || MODEM == SX1278 if (PA_OUTPUT_RFO_PIN == outputPin) { // RFO if (level < 0) { level = 0; } else if (level > 14) { level = 14; } writeRegister(REG_PA_DAC, 0x84); writeRegister(REG_PA_CONFIG, 0x70 | level); } else { // PA BOOST if (level < 2) { level = 2; } else if (level > 17) { level = 17; } writeRegister(REG_PA_DAC, 0x84); writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); } #elif MODEM == SX1262 // currently no low power mode for SX1262 implemented, assuming PA boost // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2 // RegTxClampConfig = @address 0x08D8 writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1)); uint8_t pa_buf[4]; pa_buf[0] = 0x04; pa_buf[1] = 0x07; pa_buf[2] = 0x00; pa_buf[3] = 0x01; executeOpcode(OP_PA_CONFIG, pa_buf, 4); // set pa_config for high power if (level > 22) { level = 22; } else if (level < -9) { level = -9; } writeRegister(REG_OCP, 0x38); // 160mA limit, overcurrent protection uint8_t tx_buf[2]; tx_buf[0] = level; tx_buf[1] = 0x02; // ramping time - 40 microseconds executeOpcode(OP_TX_PARAMS, tx_buf, 2); _txp = level; #endif } uint8_t LoRaClass::getTxPower() { #if MODEM == SX1276 || MODEM == SX1278 byte txp = readRegister(REG_PA_CONFIG); return txp; #elif MODEM == SX1262 return _txp; #endif } void LoRaClass::setFrequency(long frequency) { _frequency = frequency; #if MODEM == SX1276 || MODEM == SX1278 uint32_t frf = ((uint64_t)frequency << 19) / 32000000; writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); optimizeModemSensitivity(); #elif MODEM == SX1262 uint8_t buf[4]; uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP); buf[0] = ((freq >> 24) & 0xFF); buf[1] = ((freq >> 16) & 0xFF); buf[2] = ((freq >> 8) & 0xFF); buf[3] = (freq & 0xFF); executeOpcode(OP_RF_FREQ, buf, 4); #endif } uint32_t LoRaClass::getFrequency() { #if MODEM == SX1276 || MODEM == SX1278 uint8_t msb = readRegister(REG_FRF_MSB); uint8_t mid = readRegister(REG_FRF_MID); uint8_t lsb = readRegister(REG_FRF_LSB); uint32_t frf = ((uint32_t)msb << 16) | ((uint32_t)mid << 8) | (uint32_t)lsb; uint64_t frm = (uint64_t)frf*32000000; uint32_t frequency = (frm >> 19); #elif MODEM == SX1262 // we can't read the frequency on the sx1262 uint32_t frequency = _frequency; #endif return frequency; } void LoRaClass::setSpreadingFactor(int sf) { if (sf < 6) { sf = 6; } else if (sf > 12) { sf = 12; } #if MODEM == SX1276 || MODEM == SX1278 if (sf == 6) { writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); writeRegister(REG_DETECTION_THRESHOLD, 0x0c); } else { writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); writeRegister(REG_DETECTION_THRESHOLD, 0x0a); } writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); #elif MODEM == SX1262 setModulationParams(sf, _bw, _cr, _ldro); #endif handleLowDataRate(); } long LoRaClass::getSignalBandwidth() { #if MODEM == SX1276 || MODEM == SX1278 byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); switch (bw) { case 0: return 7.8E3; case 1: return 10.4E3; case 2: return 15.6E3; case 3: return 20.8E3; case 4: return 31.25E3; case 5: return 41.7E3; case 6: return 62.5E3; case 7: return 125E3; case 8: return 250E3; case 9: return 500E3; } #elif MODEM == SX1262 int bw = _bw; switch (bw) { case 0x00: return 7.8E3; case 0x01: return 15.6E3; case 0x02: return 31.25E3; case 0x03: return 62.5E3; case 0x04: return 125E3; case 0x05: return 250E3; case 0x06: return 500E3; case 0x08: return 10.4E3; case 0x09: return 20.8E3; case 0x0A: return 41.7E3; } #endif return 0; } void LoRaClass::handleLowDataRate(){ #if MODEM == SX1276 || MODEM == SX1278 int sf = (readRegister(REG_MODEM_CONFIG_2) >> 4); if ( long( (1< 16) { // set auto AGC and LowDataRateOptimize writeRegister(REG_MODEM_CONFIG_3, (1<<3)|(1<<2)); } else { // set auto AGC writeRegister(REG_MODEM_CONFIG_3, (1<<2)); } #elif MODEM == SX1262 _ldro = 1; setModulationParams(_sf, _bw, _cr, _ldro); #endif } void LoRaClass::optimizeModemSensitivity(){ #if MODEM == SX1276 || MODEM == SX1278 byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); uint32_t freq = getFrequency(); if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) { writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x02); writeRegister(REG_HIGH_BW_OPTIMIZE_2, 0x7f); } else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) { writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x02); writeRegister(REG_HIGH_BW_OPTIMIZE_2, 0x64); } else { writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x03); } #elif MODEM == SX1262 // todo: check if there's anything the sx1262 can do here #endif } void LoRaClass::setSignalBandwidth(long sbw) { #if MODEM == SX1276 || MODEM == SX1278 int bw; if (sbw <= 7.8E3) { bw = 0; } else if (sbw <= 10.4E3) { bw = 1; } else if (sbw <= 15.6E3) { bw = 2; } else if (sbw <= 20.8E3) { bw = 3; } else if (sbw <= 31.25E3) { bw = 4; } else if (sbw <= 41.7E3) { bw = 5; } else if (sbw <= 62.5E3) { bw = 6; } else if (sbw <= 125E3) { bw = 7; } else if (sbw <= 250E3) { bw = 8; } else /*if (sbw <= 250E3)*/ { bw = 9; } writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); #elif MODEM == SX1262 uint8_t bw; if (sbw <= 7.8E3) { bw = 0x00; } else if (sbw <= 10.4E3) { bw = 0x08; } else if (sbw <= 15.6E3) { bw = 0x01; } else if (sbw <= 20.8E3) { bw = 0x09; } else if (sbw <= 31.25E3) { bw = 0x02; } else if (sbw <= 41.7E3) { bw = 0x0A; } else if (sbw <= 62.5E3) { bw = 0x03; } else if (sbw <= 125E3) { bw = 0x04; } else if (sbw <= 250E3) { bw = 0x05; } else /*if (sbw <= 250E3)*/ { bw = 0x06; } setModulationParams(_sf, bw, _cr, _ldro); #endif handleLowDataRate(); optimizeModemSensitivity(); } void LoRaClass::setCodingRate4(int denominator) { if (denominator < 5) { denominator = 5; } else if (denominator > 8) { denominator = 8; } int cr = denominator - 4; #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); #elif MODEM == SX1262 setModulationParams(_sf, _bw, cr, _ldro); #endif } void LoRaClass::setPreambleLength(long length) { #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); #elif MODEM == SX1262 setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); #endif } void LoRaClass::setSyncWord(int sw) { #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_SYNC_WORD, sw); #elif MODEM == SX1262 writeRegister(REG_SYNC_WORD_MSB, sw & 0xFF00); writeRegister(REG_SYNC_WORD_LSB, sw & 0x00FF); #endif } void LoRaClass::enableCrc() { #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); #elif MODEM == SX1262 _crcMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); #endif } void LoRaClass::disableCrc() { #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); #elif MODEM == SX1262 _crcMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); #endif } byte LoRaClass::random() { #if MODEM == SX1276 || MODEM == SX1278 return readRegister(REG_RSSI_WIDEBAND); #elif MODEM == SX1262 return readRegister(REG_RANDOM_GEN); #endif } void LoRaClass::setPins(int ss, int reset, int dio0, int rxen, int busy) { _ss = ss; _reset = reset; _dio0 = dio0; _rxen = rxen; _busy = busy; } void LoRaClass::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); } void LoRaClass::dumpRegisters(Stream& out) { for (int i = 0; i < 128; i++) { out.print("0x"); out.print(i, HEX); out.print(": 0x"); out.println(readRegister(i), HEX); } } void LoRaClass::explicitHeaderMode() { _implicitHeaderMode = 0; #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); #elif MODEM == SX1262 setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); #endif } void LoRaClass::implicitHeaderMode() { _implicitHeaderMode = 1; #if MODEM == SX1276 || MODEM == SX1278 writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); #elif MODEM == SX1262 setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); #endif } void ISR_VECT LoRaClass::handleDio0Rise() { #if MODEM == SX1276 || MODEM == SX1278 int irqFlags = readRegister(REG_IRQ_FLAGS); // clear IRQ's writeRegister(REG_IRQ_FLAGS, irqFlags); if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { #elif MODEM == SX1262 uint8_t buf[2]; buf[0] = 0x00; buf[1] = 0x00; executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2); executeOpcode(OP_CLEAR_IRQ_STATUS, buf, 2); if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { #endif // received a packet _packetIndex = 0; // read packet length #if MODEM == SX1276 || MODEM == SX1278 int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); // set FIFO address to current RX address writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); #elif MODEM == SX1262 uint8_t rxbuf[2] = {0}; executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2); int packetLength = rxbuf[0]; #endif if (_onReceive) { _onReceive(packetLength); } #if MODEM == SX1276 || MODEM == SX1278 // reset FIFO address writeRegister(REG_FIFO_ADDR_PTR, 0); #endif } } void ISR_VECT LoRaClass::onDio0Rise() { LoRa.handleDio0Rise(); } LoRaClass LoRa;