From 78a02c5ae49c39fdba6861f4260c54d03a670799 Mon Sep 17 00:00:00 2001 From: "jacob.eva" Date: Fri, 9 Feb 2024 20:46:39 +0000 Subject: [PATCH] Add SX1280 driver and split driver in three parts --- Config.h | 17 +- LoRa.cpp | 1505 -------------------------------------------- Modem.h | 1 + RNode_Firmware.ino | 90 +-- Utilities.h | 69 +- sx126x.cpp | 911 +++++++++++++++++++++++++++ LoRa.h => sx126x.h | 51 +- sx127x.cpp | 691 ++++++++++++++++++++ sx127x.h | 117 ++++ sx128x.cpp | 882 ++++++++++++++++++++++++++ sx128x.h | 144 +++++ 11 files changed, 2867 insertions(+), 1611 deletions(-) delete mode 100644 LoRa.cpp create mode 100644 sx126x.cpp rename LoRa.h => sx126x.h (67%) create mode 100644 sx127x.cpp create mode 100644 sx127x.h create mode 100644 sx128x.cpp create mode 100644 sx128x.h diff --git a/Config.h b/Config.h index bee7efb..90883b6 100644 --- a/Config.h +++ b/Config.h @@ -315,8 +315,9 @@ #define HAS_NP false #define HAS_SD false #define HAS_TCXO true - #define HAS_RXEN_BUSY true - #define MODEM SX1262 + #define HAS_RF_SWITCH_RX_TX true + #define HAS_BUSY true + #define MODEM SX1280 #define CONFIG_UART_BUFFER_SIZE 6144 #define CONFIG_QUEUE_SIZE 6144 @@ -325,14 +326,14 @@ #define EEPROM_OFFSET EEPROM_SIZE+0xED000-EEPROM_RESERVED // following pins are for the sx1262 - const int pin_rxen = 37; + /*const int pin_rxen = 37; const int pin_reset = 38; const int pin_cs = 42; const int pin_sclk = 43; const int pin_mosi = 44; const int pin_miso = 45; const int pin_busy = 46; - const int pin_dio = 47; + const int pin_dio = 47;*/ const int pin_led_rx = LED_BLUE; const int pin_led_tx = LED_GREEN; const int pin_tcxo_enable = -1; @@ -351,12 +352,16 @@ #define eeprom_addr(a) (a+EEPROM_OFFSET) - #ifndef HAS_RXEN_BUSY + #ifndef HAS_RF_SWITCH_RX_TX const int pin_rxen = -1; + const int pin_txen = -1; + #endif + + #ifndef HAS_BUSY const int pin_busy = -1; #endif - #if MODEM == SX1262 && defined(NRF52840_XXAA) + #if (MODEM == SX1262 || MODEM == SX1280) && defined(NRF52840_XXAA) SPIClass spiModem(NRF_SPIM2, pin_miso, pin_sclk, pin_mosi); #endif diff --git a/LoRa.cpp b/LoRa.cpp deleted file mode 100644 index 3637a08..0000000 --- a/LoRa.cpp +++ /dev/null @@ -1,1505 +0,0 @@ -// 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 "Boards.h" -#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(NRF52840_XXAA) - #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 - #if BOARD_MODEL != BOARD_RNODE_NG_22 - #include "soc/rtc_wdt.h" - #endif - #define ISR_VECT IRAM_ATTR -#else - #define ISR_VECT -#endif - -#if BOARD_MODEL == BOARD_RNODE_NG_22 - #define MODEM_MISO 3 - #define MODEM_MOSI 6 - #define MODEM_CLK 5 -#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}; - - #if defined(NRF52840_XXAA) - extern SPIClass spiModem; - #define SPI spiModem - #endif - -#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 SPI; - -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); - - #if BOARD_MODEL == BOARD_RNODE_NG_22 - SPI.begin(MODEM_CLK, MODEM_MISO, MODEM_MOSI); - #else - SPI.begin(); - #endif - - // 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); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(address); - response = SPI.transfer(value); - SPI.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); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - SPI.transfer((address & 0xFF00) >> 8); - SPI.transfer(address & 0x00FF); - if (opcode == OP_READ_REGISTER) { - SPI.transfer(0x00); - } - response = SPI.transfer(value); - SPI.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); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); - } - - void LoRaClass::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) - { - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); - } - - void LoRaClass::writeBuffer(const uint8_t* buffer, size_t size) - { - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(OP_FIFO_WRITE); - SPI.transfer(fifo_tx_addr_ptr); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - fifo_tx_addr_ptr++; - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); - } - - void LoRaClass::readBuffer(uint8_t* buffer, size_t size) - { - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(OP_FIFO_READ); - SPI.transfer(fifo_rx_addr_ptr); - SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - - SPI.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 SPI - SPI.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 = -buf[0] / 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]) * 0.25; - #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 - SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); -#endif - attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); - } else { - detachInterrupt(digitalPinToInterrupt(_dio0)); -#ifdef SPI_HAS_NOTUSINGINTERRUPT - SPI.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; diff --git a/Modem.h b/Modem.h index bb23a57..027e314 100644 --- a/Modem.h +++ b/Modem.h @@ -1,3 +1,4 @@ #define SX1276 0x01 #define SX1278 0x02 #define SX1262 0x03 +#define SX1280 0x04 diff --git a/RNode_Firmware.ino b/RNode_Firmware.ino index 54e06e2..d18aa98 100644 --- a/RNode_Firmware.ino +++ b/RNode_Firmware.ino @@ -93,16 +93,22 @@ void setup() { // Set chip select, reset and interrupt // pins for the LoRa module - LoRa.setPins(pin_cs, pin_reset, pin_dio, pin_rxen, pin_busy); + #if MODEM == SX1276 || MODEM == SX1278 + LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy); + #elif MODEM == SX1262 + LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy, pin_rxen); + #elif MODEM == SX1280 + LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy, pin_rxen, pin_txen); + #endif #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 init_channel_stats(); // Check installed transceiver chip and // probe boot parameters. - if (LoRa.preInit()) { + if (LoRa->preInit()) { modem_installed = true; - uint32_t lfr = LoRa.getFrequency(); + uint32_t lfr = LoRa->getFrequency(); if (lfr == 0) { // Normal boot } else if (lfr == M_FRQ_R) { @@ -115,7 +121,7 @@ void setup() { } else { // Unknown boot } - LoRa.setFrequency(M_FRQ_S); + LoRa->setFrequency(M_FRQ_S); } else { modem_installed = false; } @@ -158,14 +164,14 @@ void setup() { // Validate board health, EEPROM and config validate_status(); - if (op_mode != MODE_TNC) LoRa.setFrequency(0); + if (op_mode != MODE_TNC) LoRa->setFrequency(0); } void lora_receive() { if (!implicit) { - LoRa.receive(); + LoRa->receive(); } else { - LoRa.receive(implicit_l); + LoRa->receive(implicit_l); } } @@ -187,7 +193,7 @@ inline void kiss_write_packet() { inline void getPacketData(uint16_t len) { while (len-- && read_len < MTU) { - pbuf[read_len++] = LoRa.read(); + pbuf[read_len++] = LoRa->read(); } } @@ -198,7 +204,7 @@ void ISR_VECT receive_callback(int packet_size) { // by combining two raw LoRa packets. // We read the 1-byte header and extract // packet sequence number and split flags - uint8_t header = LoRa.read(); packet_size--; + uint8_t header = LoRa->read(); packet_size--; uint8_t sequence = packetSequence(header); bool ready = false; @@ -210,8 +216,8 @@ void ISR_VECT receive_callback(int packet_size) { seq = sequence; #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 - last_rssi = LoRa.packetRssi(); - last_snr_raw = LoRa.packetSnrRaw(); + last_rssi = LoRa->packetRssi(); + last_snr_raw = LoRa->packetSnrRaw(); #endif getPacketData(packet_size); @@ -223,8 +229,8 @@ void ISR_VECT receive_callback(int packet_size) { #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 - last_rssi = (last_rssi+LoRa.packetRssi())/2; - last_snr_raw = (last_snr_raw+LoRa.packetSnrRaw())/2; + last_rssi = (last_rssi+LoRa->packetRssi())/2; + last_snr_raw = (last_snr_raw+LoRa->packetSnrRaw())/2; #endif getPacketData(packet_size); @@ -241,8 +247,8 @@ void ISR_VECT receive_callback(int packet_size) { seq = sequence; #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 - last_rssi = LoRa.packetRssi(); - last_snr_raw = LoRa.packetSnrRaw(); + last_rssi = LoRa->packetRssi(); + last_snr_raw = LoRa->packetSnrRaw(); #endif getPacketData(packet_size); @@ -260,8 +266,8 @@ void ISR_VECT receive_callback(int packet_size) { } #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 - last_rssi = LoRa.packetRssi(); - last_snr_raw = LoRa.packetSnrRaw(); + last_rssi = LoRa->packetRssi(); + last_snr_raw = LoRa->packetSnrRaw(); #endif getPacketData(packet_size); @@ -287,8 +293,8 @@ void ISR_VECT receive_callback(int packet_size) { read_len = 0; #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 - last_rssi = LoRa.packetRssi(); - last_snr_raw = LoRa.packetSnrRaw(); + last_rssi = LoRa->packetRssi(); + last_snr_raw = LoRa->packetSnrRaw(); getPacketData(packet_size); // We first signal the RSSI of the @@ -310,7 +316,7 @@ bool startRadio() { update_radio_lock(); if (!radio_online && !console_active) { if (!radio_locked && hw_ready) { - if (!LoRa.begin(lora_freq)) { + if (!LoRa->begin(lora_freq)) { // The radio could not be started. // Indicate this failure over both the // serial port and with the onboard LEDs @@ -329,9 +335,9 @@ bool startRadio() { setCodingRate(); getFrequency(); - LoRa.enableCrc(); + LoRa->enableCrc(); - LoRa.onReceive(receive_callback); + LoRa->onReceive(receive_callback); lora_receive(); @@ -360,7 +366,7 @@ bool startRadio() { } void stopRadio() { - LoRa.end(); + LoRa->end(); radio_online = false; } @@ -469,23 +475,23 @@ void transmit(uint16_t size) { header = header | FLAG_SPLIT; } - LoRa.beginPacket(); - LoRa.write(header); written++; + LoRa->beginPacket(); + LoRa->write(header); written++; for (uint16_t i=0; i < size; i++) { - LoRa.write(tbuf[i]); + LoRa->write(tbuf[i]); written++; if (written == 255) { - LoRa.endPacket(); add_airtime(written); - LoRa.beginPacket(); - LoRa.write(header); + LoRa->endPacket(); add_airtime(written); + LoRa->beginPacket(); + LoRa->write(header); written = 1; } } - LoRa.endPacket(); add_airtime(written); + LoRa->endPacket(); add_airtime(written); } else { // In promiscuous mode, we only send out // plain raw LoRa packets with a maximum @@ -501,17 +507,17 @@ void transmit(uint16_t size) { // If implicit header mode has been set, // set packet length to payload data length if (!implicit) { - LoRa.beginPacket(); + LoRa->beginPacket(); } else { - LoRa.beginPacket(size); + LoRa->beginPacket(size); } for (uint16_t i=0; i < size; i++) { - LoRa.write(tbuf[i]); + LoRa->write(tbuf[i]); written++; } - LoRa.endPacket(); add_airtime(written); + LoRa->endPacket(); add_airtime(written); } } else { kiss_indicate_error(ERROR_TXFAILED); @@ -631,7 +637,7 @@ void serialCallback(uint8_t sbyte) { kiss_indicate_spreadingfactor(); } else { int sf = sbyte; - if (sf < 6) sf = 6; + if (sf < 5) sf = 5; if (sf > 12) sf = 12; lora_sf = sf; @@ -938,8 +944,8 @@ void updateModemStatus() { portENTER_CRITICAL(); #endif - uint8_t status = LoRa.modemStatus(); - current_rssi = LoRa.currentRssi(); + uint8_t status = LoRa->modemStatus(); + current_rssi = LoRa->currentRssi(); last_status_update = millis(); #if MCU_VARIANT == MCU_ESP32 @@ -1158,8 +1164,8 @@ void loop() { #if MCU_VARIANT == MCU_ESP32 if (packet_ready) { portENTER_CRITICAL(&update_lock); - last_rssi = LoRa.packetRssi(); - last_snr_raw = LoRa.packetSnrRaw(); + last_rssi = LoRa->packetRssi(); + last_snr_raw = LoRa->packetSnrRaw(); portEXIT_CRITICAL(&update_lock); kiss_indicate_stat_rssi(); kiss_indicate_stat_snr(); @@ -1173,8 +1179,8 @@ void loop() { #elif MCU_VARIANT == MCU_NRF52 if (packet_ready) { portENTER_CRITICAL(); - last_rssi = LoRa.packetRssi(); - last_snr_raw = LoRa.packetSnrRaw(); + last_rssi = LoRa->packetRssi(); + last_snr_raw = LoRa->packetSnrRaw(); portEXIT_CRITICAL(); kiss_indicate_stat_rssi(); kiss_indicate_stat_snr(); @@ -1199,7 +1205,7 @@ void loop() { } if (!dcd) { - uint8_t csma_r = (uint8_t)random(256); + uint8_t csma_r = (uint8_t)random(20); // updated to increase bitrate: todo check if (csma_p >= csma_r) { flushQueue(); } else { diff --git a/Utilities.h b/Utilities.h index a360f47..b93e70e 100644 --- a/Utilities.h +++ b/Utilities.h @@ -22,7 +22,18 @@ int written_bytes = 0; #endif #include -#include "LoRa.h" + +#if MODEM == SX1262 +#include "sx126x.h" +sx126x *LoRa = &sx126x_modem; +#elif MODEM == SX1276 || MODEM == SX1278 +#include "sx127x.h" +sx127x *LoRa = &sx127x_modem; +#elif MODEM == SX1280 +#include "sx128x.h" +sx128x *LoRa = &sx128x_modem; +#endif + #include "ROM.h" #include "Framing.h" #include "MD5.h" @@ -657,11 +668,7 @@ void kiss_indicate_stat_tx() { } void kiss_indicate_stat_rssi() { - #if MODEM == SX1276 || MODEM == SX1278 - uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset); - #elif MODEM == SX1262 - int8_t packet_rssi_val = (int8_t)(last_rssi+rssi_offset); - #endif + uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset); serial_write(FEND); serial_write(CMD_STAT_RSSI); escaped_serial_write(packet_rssi_val); @@ -975,7 +982,7 @@ inline uint8_t packetSequence(uint8_t header) { } void setPreamble() { - if (radio_online) LoRa.setPreambleLength(lora_preamble_symbols); + if (radio_online) LoRa->setPreambleLength(lora_preamble_symbols); kiss_indicate_phy_stats(); } @@ -1002,12 +1009,12 @@ void updateBitrate() { } void setSpreadingFactor() { - if (radio_online) LoRa.setSpreadingFactor(lora_sf); + if (radio_online) LoRa->setSpreadingFactor(lora_sf); updateBitrate(); } void setCodingRate() { - if (radio_online) LoRa.setCodingRate4(lora_cr); + if (radio_online) LoRa->setCodingRate4(lora_cr); updateBitrate(); } @@ -1021,66 +1028,66 @@ void set_implicit_length(uint8_t len) { } int getTxPower() { - uint8_t txp = LoRa.getTxPower(); + uint8_t txp = LoRa->getTxPower(); return (int)txp; } void setTXPower() { if (radio_online) { - if (model == MODEL_A2) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_A3) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); - if (model == MODEL_A4) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); - if (model == MODEL_A7) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_A8) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_A9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_A2) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_A3) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); + if (model == MODEL_A4) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); + if (model == MODEL_A7) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_A8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_A9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_B3) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_B4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_B8) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_B9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_B3) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_B4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_B8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_B9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_C4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_C9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_C4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_C9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_E4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_E9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_E4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_E9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_FE) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); - if (model == MODEL_FF) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); + if (model == MODEL_FE) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_FF) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); } } void getBandwidth() { if (radio_online) { - lora_bw = LoRa.getSignalBandwidth(); + lora_bw = LoRa->getSignalBandwidth(); } updateBitrate(); } void setBandwidth() { if (radio_online) { - LoRa.setSignalBandwidth(lora_bw); + LoRa->setSignalBandwidth(lora_bw); getBandwidth(); } } void getFrequency() { if (radio_online) { - lora_freq = LoRa.getFrequency(); + lora_freq = LoRa->getFrequency(); } } void setFrequency() { if (radio_online) { - LoRa.setFrequency(lora_freq); + LoRa->setFrequency(lora_freq); getFrequency(); } } uint8_t getRandom() { if (radio_online) { - return LoRa.random(); + return LoRa->random(); } else { return 0x00; } diff --git a/sx126x.cpp b/sx126x.cpp new file mode 100644 index 0000000..f6ac76e --- /dev/null +++ b/sx126x.cpp @@ -0,0 +1,911 @@ +// 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 "sx126x.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(NRF52840_XXAA) + #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 + +#define OP_RF_FREQ_6X 0x86 +#define OP_SLEEP_6X 0x84 +#define OP_STANDBY_6X 0x80 +#define OP_TX_6X 0x83 +#define OP_RX_6X 0x82 +#define OP_PA_CONFIG_6X 0x95 +#define OP_SET_IRQ_FLAGS_6X 0x08 // also provides info such as + // preamble detection, etc for + // knowing when it's safe to switch + // antenna modes +#define OP_CLEAR_IRQ_STATUS_6X 0x02 +#define OP_GET_IRQ_STATUS_6X 0x12 +#define OP_RX_BUFFER_STATUS_6X 0x13 +#define OP_PACKET_STATUS_6X 0x14 // get snr & rssi of last packet +#define OP_CURRENT_RSSI_6X 0x15 +#define OP_MODULATION_PARAMS_6X 0x8B // bw, sf, cr, etc. +#define OP_PACKET_PARAMS_6X 0x8C // crc, preamble, payload length, etc. +#define OP_STATUS_6X 0xC0 +#define OP_TX_PARAMS_6X 0x8E // set dbm, etc +#define OP_PACKET_TYPE_6X 0x8A +#define OP_BUFFER_BASE_ADDR_6X 0x8F +#define OP_READ_REGISTER_6X 0x1D +#define OP_WRITE_REGISTER_6X 0x0D +#define OP_DIO3_TCXO_CTRL_6X 0x97 +#define OP_DIO2_RF_CTRL_6X 0x9D +#define OP_CALIBRATE_6X 0x89 +#define IRQ_TX_DONE_MASK_6X 0x01 +#define IRQ_RX_DONE_MASK_6X 0x02 +#define IRQ_HEADER_DET_MASK_6X 0x10 +#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40 + +#define MODE_LONG_RANGE_MODE_6X 0x01 + +#define OP_FIFO_WRITE_6X 0x0E +#define OP_FIFO_READ_6X 0x1E +#define REG_OCP_6X 0x08E7 +#define REG_LNA_6X 0x08AC // no agc in sx1262 +#define REG_SYNC_WORD_MSB_6X 0x0740 +#define REG_SYNC_WORD_LSB_6X 0x0741 +#define REG_PAYLOAD_LENGTH_6X 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98 +#define REG_RANDOM_GEN_6X 0x0819 + +#define MODE_TCXO_3_3V_6X 0x07 + +#define IRQ_PREAMBLE_DET_MASK_6X 0x04 +#define XTAL_FREQ_6X (double)32000000 +#define FREQ_DIV_6X (double)pow(2.0, 25.0) +#define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X) +extern SPIClass spiModem; +#define SPI spiModem + +extern SPIClass SPI; + +#define MAX_PKT_LENGTH 255 + +sx126x::sx126x() : + _spiSettings(8E6, MSBFIRST, SPI_MODE0), + _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), + _frequency(0), + _txp(0), + _sf(0x07), + _bw(0x04), + _cr(0x01), + _ldro(0x00), + _packetIndex(0), + _preambleLength(18), + _implicitHeaderMode(0), + _payloadLength(255), + _crcMode(1), + _fifo_tx_addr_ptr(0), + _fifo_rx_addr_ptr(0), + _packet({0}), + _preinit_done(false), + _onReceive(NULL) +{ + // overide Stream timeout value + setTimeout(0); +} + +bool sx126x::preInit() { + // setup pins + pinMode(_ss, OUTPUT); + // set SS high + digitalWrite(_ss, HIGH); + + SPI.begin(); + + // check version (retry for up to 2 seconds) + long start = millis(); + uint8_t syncmsb; + uint8_t synclsb; + while (((millis() - start) < 2000) && (millis() >= start)) { + syncmsb = readRegister(REG_SYNC_WORD_MSB_6X); + synclsb = readRegister(REG_SYNC_WORD_LSB_6X); + 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; + } + + _preinit_done = true; + return true; +} + +uint8_t ISR_VECT sx126x::readRegister(uint16_t address) +{ + return singleTransfer(OP_READ_REGISTER_6X, address, 0x00); +} + +void sx126x::writeRegister(uint16_t address, uint8_t value) +{ + singleTransfer(OP_WRITE_REGISTER_6X, address, value); +} + +uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) +{ + waitOnBusy(); + + uint8_t response; + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + SPI.transfer((address & 0xFF00) >> 8); + SPI.transfer(address & 0x00FF); + if (opcode == OP_READ_REGISTER_6X) { + SPI.transfer(0x00); + } + response = SPI.transfer(value); + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); + + return response; +} + +void sx126x::rxAntEnable() +{ + uint8_t byte = 0x01; + // enable dio2 rf switch + executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1); + digitalWrite(_rxen, HIGH); +} + +void sx126x::loraMode() { + // enable lora mode on the SX1262 chip + uint8_t mode = MODE_LONG_RANGE_MODE_6X; + executeOpcode(OP_PACKET_TYPE_6X, &mode, 1); +} + +void sx126x::waitOnBusy() { + if (_busy != -1) { + while (digitalRead(_busy) == HIGH) + { + // do nothing + } + } +} + +void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) +{ + waitOnBusy(); + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + + for (int i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + } + + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); +} + +void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) +{ + waitOnBusy(); + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + SPI.transfer(0x00); + + for (int i = 0; i < size; i++) + { + buffer[i] = SPI.transfer(0x00); + } + + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); +} + +void sx126x::writeBuffer(const uint8_t* buffer, size_t size) +{ + waitOnBusy(); + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(OP_FIFO_WRITE_6X); + SPI.transfer(_fifo_tx_addr_ptr); + + for (int i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + _fifo_tx_addr_ptr++; + } + + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); +} + +void sx126x::readBuffer(uint8_t* buffer, size_t size) +{ + waitOnBusy(); + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(OP_FIFO_READ_6X); + SPI.transfer(_fifo_rx_addr_ptr); + SPI.transfer(0x00); + + for (int i = 0; i < size; i++) + { + buffer[i] = SPI.transfer(0x00); + } + + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); +} + +void sx126x::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_6X, buf, 8); +} + +void sx126x::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_6X, buf, 9); +} + + +int sx126x::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 (!_preinit_done) { + if (!preInit()) { + return false; + } + } + + //#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); + rxAntEnable(); + } + // calibrate RC64k, RC13M, PLL, ADC and image + uint8_t calibrate = 0x7F; + executeOpcode(OP_CALIBRATE_6X, &calibrate, 1); + + setFrequency(frequency); + + // set output power to 2 dBm + setTxPower(2); + + // set LNA boost + writeRegister(REG_LNA_6X, 0x96); + + // set base addresses + uint8_t basebuf[2] = {0}; + executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2); + + setModulationParams(_sf, _bw, _cr, _ldro); + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + + return 1; +} + +void sx126x::end() +{ + // put in sleep mode + sleep(); + + // stop SPI + SPI.end(); + + _preinit_done = false; +} + +int sx126x::beginPacket(int implicitHeader) +{ + // put in standby mode + idle(); + + if (implicitHeader) { + implicitHeaderMode(); + } else { + explicitHeaderMode(); + } + + _payloadLength = 0; + _fifo_tx_addr_ptr = 0; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + + return 1; +} + +int sx126x::endPacket() +{ + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + + // put in single TX mode + uint8_t timeout[3] = {0}; + executeOpcode(OP_TX_6X, timeout, 3); + + uint8_t buf[2]; + + buf[0] = 0x00; + buf[1] = 0x00; + + executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); + + // wait for TX done + while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) { + buf[0] = 0x00; + buf[1] = 0x00; + executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); + yield(); + } + + // clear IRQ's + + uint8_t mask[2]; + mask[0] = 0x00; + mask[1] = IRQ_TX_DONE_MASK_6X; + executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2); + return 1; +} + +uint8_t sx126x::modemStatus() { + // imitate the register status from the sx1276 / 78 + uint8_t buf[2] = {0}; + + + executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); + + uint8_t clearbuf[2] = {0}; + + uint8_t byte = 0x00; + + if (buf[1] & IRQ_PREAMBLE_DET_MASK_6X != 0) { + byte = byte | 0x01 | 0x04; + // clear register after reading + clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X; + } + + if (buf[1] & IRQ_HEADER_DET_MASK_6X != 0) { + byte = byte | 0x02 | 0x04; + // clear register after reading + clearbuf[1] = clearbuf[1] | IRQ_HEADER_DET_MASK_6X; + } + + executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2); + + return byte; +} + + +uint8_t sx126x::currentRssiRaw() { + uint8_t byte = 0; + executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); + return byte; +} + +int ISR_VECT sx126x::currentRssi() { + uint8_t byte = 0; + executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); + int rssi = -(int(byte)) / 2; + return rssi - RSSI_OFFSET; +} + +uint8_t sx126x::packetRssiRaw() { + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + return buf[2]; +} + +int ISR_VECT sx126x::packetRssi() { + // may need more calculations here + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + int pkt_rssi = -buf[0] / 2; + return pkt_rssi - RSSI_OFFSET; +} + +uint8_t ISR_VECT sx126x::packetSnrRaw() { + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + return buf[1]; +} + +float ISR_VECT sx126x::packetSnr() { + uint8_t buf[3] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); + return float(buf[1]) * 0.25; +} + +long sx126x::packetFrequencyError() +{ + // todo: implement this, no idea how to check it on the sx1262 + const float fError = 0.0; + return static_cast(fError); +} + +size_t sx126x::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t sx126x::write(const uint8_t *buffer, size_t size) +{ + if ((_payloadLength + size) > MAX_PKT_LENGTH) { + size = MAX_PKT_LENGTH - _payloadLength; + } + + // write data + writeBuffer(buffer, size); + _payloadLength = _payloadLength + size; + return size; +} + +int ISR_VECT sx126x::available() +{ + uint8_t buf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2); + return buf[0] - _packetIndex; +} + +int ISR_VECT sx126x::read() +{ + if (!available()) { + return -1; + } + + // if received new packet + if (_packetIndex == 0) { + uint8_t rxbuf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); + int size = rxbuf[0]; + _fifo_rx_addr_ptr = rxbuf[1]; + + readBuffer(_packet, size); + } + + uint8_t byte = _packet[_packetIndex]; + _packetIndex++; + return byte; +} + +int sx126x::peek() +{ + if (!available()) { + return -1; + } + + // if received new packet + if (_packetIndex == 0) { + uint8_t rxbuf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); + int size = rxbuf[0]; + _fifo_rx_addr_ptr = rxbuf[1]; + + readBuffer(_packet, size); + } + + uint8_t b = _packet[_packetIndex]; + return b; +} + +void sx126x::flush() +{ +} + +void sx126x::onReceive(void(*callback)(int)) +{ + _onReceive = callback; + + if (callback) { + pinMode(_dio0, INPUT); + + // 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_6X; + + // set dio1 masks + buf[4] = 0x00; + buf[5] = 0x00; + + // set dio2 masks + buf[6] = 0x00; + buf[7] = 0x00; + + executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void sx126x::receive(int size) +{ + if (size > 0) { + implicitHeaderMode(); + + // tell radio payload length + _payloadLength = size; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + } else { + explicitHeaderMode(); + } + + if (_rxen != -1) { + rxAntEnable(); + } + uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode + executeOpcode(OP_RX_6X, mode, 3); +} + +void sx126x::idle() +{ + //#if HAS_TCXO + // STDBY_XOSC + uint8_t byte = 0x01; + //#else + // // STDBY_RC + // uint8_t byte = 0x00; + //#endif + executeOpcode(OP_STANDBY_6X, &byte, 1); +} + +void sx126x::sleep() +{ + uint8_t byte = 0x00; + executeOpcode(OP_SLEEP_6X, &byte, 1); +} + +void sx126x::enableTCXO() { + // only tested for RAK4630, voltage may be different on other platforms + uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF}; + executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4); +} + +void sx126x::disableTCXO() { + // currently cannot disable on SX1262? +} + +void sx126x::setTxPower(int level, int outputPin) { + // 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_6X, pa_buf, 4); // set pa_config for high power + + if (level > 22) { + level = 22; + } + else if (level < -9) { + level = -9; + } + + writeRegister(REG_OCP_6X, 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_6X, tx_buf, 2); + + _txp = level; +} + +uint8_t sx126x::getTxPower() { + return _txp; +} + +void sx126x::setFrequency(long frequency) { + _frequency = frequency; + + uint8_t buf[4]; + + uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X); + + buf[0] = ((freq >> 24) & 0xFF); + buf[1] = ((freq >> 16) & 0xFF); + buf[2] = ((freq >> 8) & 0xFF); + buf[3] = (freq & 0xFF); + + executeOpcode(OP_RF_FREQ_6X, buf, 4); +} + +uint32_t sx126x::getFrequency() { + // we can't read the frequency on the sx1262 / 80 + uint32_t frequency = _frequency; + + return frequency; +} + +void sx126x::setSpreadingFactor(int sf) +{ + if (sf < 5) { + sf = 5; + } else if (sf > 12) { + sf = 12; + } + + _sf = sf; + + setModulationParams(sf, _bw, _cr, _ldro); + handleLowDataRate(); +} + +long sx126x::getSignalBandwidth() +{ + 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; + } + return 0; +} + +void sx126x::handleLowDataRate(){ + _ldro = 1; + setModulationParams(_sf, _bw, _cr, _ldro); +} + +void sx126x::optimizeModemSensitivity(){ + // todo: check if there's anything the sx1262 can do here +} + +void sx126x::setSignalBandwidth(long sbw) +{ + 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); + + handleLowDataRate(); + optimizeModemSensitivity(); +} + +void sx126x::setCodingRate4(int denominator) +{ + if (denominator < 5) { + denominator = 5; + } else if (denominator > 8) { + denominator = 8; + } + + int cr = denominator - 4; + + _cr = cr; + + setModulationParams(_sf, _bw, cr, _ldro); +} + +void sx126x::setPreambleLength(long length) +{ + setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); +} + +void sx126x::setSyncWord(int sw) +{ + writeRegister(REG_SYNC_WORD_MSB_6X, sw & 0xFF00); + writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF); +} + +void sx126x::enableCrc() +{ + _crcMode = 1; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +} + +void sx126x::disableCrc() +{ + _crcMode = 0; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +} + +byte sx126x::random() +{ + return readRegister(REG_RANDOM_GEN_6X); +} + +void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) +{ + _ss = ss; + _reset = reset; + _dio0 = dio0; + _busy = busy; + _rxen = rxen; +} + +void sx126x::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void sx126x::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 sx126x::explicitHeaderMode() +{ + _implicitHeaderMode = 0; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +} + +void sx126x::implicitHeaderMode() +{ + _implicitHeaderMode = 1; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +} + + +void ISR_VECT sx126x::handleDio0Rise() +{ + uint8_t buf[2]; + + buf[0] = 0x00; + buf[1] = 0x00; + + executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); + + executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2); + + if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) { + // received a packet + _packetIndex = 0; + + // read packet length + uint8_t rxbuf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); + int packetLength = rxbuf[0]; + + if (_onReceive) { + _onReceive(packetLength); + } + } +} + +void ISR_VECT sx126x::onDio0Rise() +{ + sx126x_modem.handleDio0Rise(); +} + +sx126x sx126x_modem; diff --git a/LoRa.h b/sx126x.h similarity index 67% rename from LoRa.h rename to sx126x.h index 102010d..a08b11c 100644 --- a/LoRa.h +++ b/sx126x.h @@ -4,8 +4,8 @@ // Modifications and additions copyright 2023 by Mark Qvist // Obviously still under the MIT license. -#ifndef LORA_H -#define LORA_H +#ifndef SX126X_H +#define SX126X_H #include #include @@ -15,6 +15,7 @@ #define LORA_DEFAULT_RESET_PIN 9 #define LORA_DEFAULT_DIO0_PIN 2 #define LORA_DEFAULT_RXEN_PIN -1 +#define LORA_DEFAULT_TXEN_PIN -1 #define LORA_DEFAULT_BUSY_PIN -1 #define PA_OUTPUT_RFO_PIN 0 @@ -22,9 +23,9 @@ #define RSSI_OFFSET 157 -class LoRaClass : public Stream { +class sx126x : public Stream { public: - LoRaClass(); + sx126x(); int begin(long frequency); void end(); @@ -74,18 +75,16 @@ public: void enableTCXO(); void disableTCXO(); - #if MODEM == SX1262 - void enableAntenna(); - void disableAntenna(); - void loraMode(); - void waitOnBusy(); - void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); - void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); - void writeBuffer(const uint8_t* buffer, size_t size); - void readBuffer(uint8_t* buffer, size_t size); - void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); - void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc); - #endif + void rxAntEnable(); + void loraMode(); + void waitOnBusy(); + void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); + void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); + void writeBuffer(const uint8_t* buffer, size_t size); + void readBuffer(uint8_t* buffer, size_t size); + void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc); + + void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); // deprecated void crc() { enableCrc(); } @@ -93,7 +92,7 @@ public: byte random(); - void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int busy = LORA_DEFAULT_BUSY_PIN); + void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN); void setSPIFrequency(uint32_t frequency); void dumpRegisters(Stream& out); @@ -104,15 +103,9 @@ private: void handleDio0Rise(); - #if MODEM == SX1276 || MODEM == SX1278 - uint8_t readRegister(uint8_t address); - void writeRegister(uint8_t address, uint8_t value); - uint8_t singleTransfer(uint8_t address, uint8_t value); - #elif MODEM == SX1262 - uint8_t readRegister(uint16_t address); - void writeRegister(uint16_t address, uint8_t value); - uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); - #endif + uint8_t readRegister(uint16_t address); + void writeRegister(uint16_t address, uint8_t value); + uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); static void onDio0Rise(); @@ -137,9 +130,13 @@ private: int _implicitHeaderMode; int _payloadLength; int _crcMode; + int _fifo_tx_addr_ptr; + int _fifo_rx_addr_ptr; + uint8_t _packet[255]; + bool _preinit_done; void (*_onReceive)(int); }; -extern LoRaClass LoRa; +extern sx126x sx126x_modem; #endif diff --git a/sx127x.cpp b/sx127x.cpp new file mode 100644 index 0000000..f0ae16e --- /dev/null +++ b/sx127x.cpp @@ -0,0 +1,691 @@ +// 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 "sx127x.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(NRF52840_XXAA) + #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 + +// Registers +#define REG_FIFO_7X 0x00 +#define REG_OP_MODE_7X 0x01 +#define REG_FRF_MSB_7X 0x06 +#define REG_FRF_MID_7X 0x07 +#define REG_FRF_LSB_7X 0x08 +#define REG_PA_CONFIG_7X 0x09 +#define REG_OCP_7X 0x0b +#define REG_LNA_7X 0x0c +#define REG_FIFO_ADDR_PTR_7X 0x0d +#define REG_FIFO_TX_BASE_ADDR_7X 0x0e +#define REG_FIFO_RX_BASE_ADDR_7X 0x0f +#define REG_FIFO_RX_CURRENT_ADDR_7X 0x10 +#define REG_IRQ_FLAGS_7X 0x12 +#define REG_RX_NB_BYTES_7X 0x13 +#define REG_MODEM_STAT_7X 0x18 +#define REG_PKT_SNR_VALUE_7X 0x19 +#define REG_PKT_RSSI_VALUE_7X 0x1a +#define REG_RSSI_VALUE_7X 0x1b +#define REG_MODEM_CONFIG_1_7X 0x1d +#define REG_MODEM_CONFIG_2_7X 0x1e +#define REG_PREAMBLE_MSB_7X 0x20 +#define REG_PREAMBLE_LSB_7X 0x21 +#define REG_PAYLOAD_LENGTH_7X 0x22 +#define REG_MODEM_CONFIG_3_7X 0x26 +#define REG_FREQ_ERROR_MSB_7X 0x28 +#define REG_FREQ_ERROR_MID_7X 0x29 +#define REG_FREQ_ERROR_LSB_7X 0x2a +#define REG_RSSI_WIDEBAND_7X 0x2c +#define REG_DETECTION_OPTIMIZE_7X 0x31 +#define REG_HIGH_BW_OPTIMIZE_1_7X 0x36 +#define REG_DETECTION_THRESHOLD_7X 0x37 +#define REG_SYNC_WORD_7X 0x39 +#define REG_HIGH_BW_OPTIMIZE_2_7X 0x3a +#define REG_DIO_MAPPING_1_7X 0x40 +#define REG_VERSION_7X 0x42 +#define REG_TCXO_7X 0x4b +#define REG_PA_DAC_7X 0x4d + +// Modes +#define MODE_LONG_RANGE_MODE_7X 0x80 +#define MODE_SLEEP_7X 0x00 +#define MODE_STDBY_7X 0x01 +#define MODE_TX_7X 0x03 +#define MODE_RX_CONTINUOUS_7X 0x05 +#define MODE_RX_SINGLE_7X 0x06 + +// PA config +#define PA_BOOST_7X 0x80 + +// IRQ masks +#define IRQ_TX_DONE_MASK_7X 0x08 +#define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20 +#define IRQ_RX_DONE_MASK_7X 0x40 + +extern SPIClass SPI; + +#define MAX_PKT_LENGTH 255 + +sx127x::sx127x() : + _spiSettings(8E6, MSBFIRST, SPI_MODE0), + _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), + _frequency(0), + _packetIndex(0), + _preinit_done(false), + _onReceive(NULL) +{ + // overide Stream timeout value + setTimeout(0); +} + +bool sx127x::preInit() { + // setup pins + pinMode(_ss, OUTPUT); + // set SS high + digitalWrite(_ss, HIGH); + + SPI.begin(); + + // check version (retry for up to 2 seconds) + uint8_t version; + long start = millis(); + while (((millis() - start) < 2000) && (millis() >= start)) { + version = readRegister(REG_VERSION_7X); + if (version == 0x12) { + break; + } + delay(100); + } + if (version != 0x12) { + return false; + } + + _preinit_done = true; + return true; +} + +uint8_t ISR_VECT sx127x::readRegister(uint8_t address) +{ + return singleTransfer(address & 0x7f, 0x00); +} + +void sx127x::writeRegister(uint8_t address, uint8_t value) +{ + singleTransfer(address | 0x80, value); +} + +uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value) +{ + uint8_t response; + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(address); + response = SPI.transfer(value); + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); + + return response; +} + + +int sx127x::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 (!_preinit_done) { + if (!preInit()) { + return false; + } + } + + // put in sleep mode + sleep(); + + // set frequency + setFrequency(frequency); + + // set base addresses + writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0); + writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0); + + // set LNA boost + writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03); + + // set auto AGC + writeRegister(REG_MODEM_CONFIG_3_7X, 0x04); + + // set output power to 2 dBm + setTxPower(2); + + // put in standby mode + idle(); + + return 1; +} + +void sx127x::end() +{ + // put in sleep mode + sleep(); + + // stop SPI + SPI.end(); + + _preinit_done = false; +} + +int sx127x::beginPacket(int implicitHeader) +{ + // put in standby mode + idle(); + + if (implicitHeader) { + implicitHeaderMode(); + } else { + explicitHeaderMode(); + } + + // reset FIFO address and payload length + writeRegister(REG_FIFO_ADDR_PTR_7X, 0); + writeRegister(REG_PAYLOAD_LENGTH_7X, 0); + + return 1; +} + +int sx127x::endPacket() +{ + // put in TX mode + writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X); + + // wait for TX done + while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) { + yield(); + } + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X); + return 1; +} + +uint8_t sx127x::modemStatus() { + return readRegister(REG_MODEM_STAT_7X); +} + + +uint8_t sx127x::currentRssiRaw() { + uint8_t rssi = readRegister(REG_RSSI_VALUE_7X); + return rssi; +} + +int ISR_VECT sx127x::currentRssi() { + int rssi = (int)readRegister(REG_RSSI_VALUE_7X) - RSSI_OFFSET; + if (_frequency < 820E6) rssi -= 7; + return rssi; +} + +uint8_t sx127x::packetRssiRaw() { + uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE_7X); + return pkt_rssi_value; +} + +int ISR_VECT sx127x::packetRssi() { + int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - 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; +} + +uint8_t ISR_VECT sx127x::packetSnrRaw() { + return readRegister(REG_PKT_SNR_VALUE_7X); +} + +float ISR_VECT sx127x::packetSnr() { + return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25; +} + +long sx127x::packetFrequencyError() +{ + int32_t freqError = 0; + freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB_7X) & B111); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_MID_7X)); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB_7X)); + + if (readRegister(REG_FREQ_ERROR_MSB_7X) & 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); +} + +size_t sx127x::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t sx127x::write(const uint8_t *buffer, size_t size) +{ + int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X); + + // check size + if ((currentLength + size) > MAX_PKT_LENGTH) { + size = MAX_PKT_LENGTH - currentLength; + } + + // write data + for (size_t i = 0; i < size; i++) { + writeRegister(REG_FIFO_7X, buffer[i]); + } + + // update length + writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size); + return size; +} + +int ISR_VECT sx127x::available() +{ + return (readRegister(REG_RX_NB_BYTES_7X) - _packetIndex); +} + +int ISR_VECT sx127x::read() +{ + if (!available()) { + return -1; + } + + _packetIndex++; + return readRegister(REG_FIFO_7X); +} + +int sx127x::peek() +{ + if (!available()) { + return -1; + } + + // store current FIFO address + int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X); + + // read + uint8_t b = readRegister(REG_FIFO_7X); + + // restore FIFO address + writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress); + + return b; +} + +void sx127x::flush() +{ +} + +void sx127x::onReceive(void(*callback)(int)) +{ + _onReceive = callback; + + if (callback) { + pinMode(_dio0, INPUT); + + writeRegister(REG_DIO_MAPPING_1_7X, 0x00); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), sx127x::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void sx127x::receive(int size) +{ + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH_7X, size & 0xff); + } else { + explicitHeaderMode(); + } + + writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_RX_CONTINUOUS_7X); +} + +void sx127x::idle() +{ + writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_STDBY_7X); +} + +void sx127x::sleep() +{ + writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_SLEEP_7X); +} + +void sx127x::enableTCXO() { + uint8_t tcxo_reg = readRegister(REG_TCXO_7X); + writeRegister(REG_TCXO_7X, tcxo_reg | 0x10); +} + +void sx127x::disableTCXO() { + uint8_t tcxo_reg = readRegister(REG_TCXO_7X); + writeRegister(REG_TCXO_7X, tcxo_reg & 0xEF); +} + +void sx127x::setTxPower(int level, int outputPin) { + if (PA_OUTPUT_RFO_PIN == outputPin) { + // RFO + if (level < 0) { + level = 0; + } else if (level > 14) { + level = 14; + } + + writeRegister(REG_PA_DAC_7X, 0x84); + writeRegister(REG_PA_CONFIG_7X, 0x70 | level); + + } else { + // PA BOOST + if (level < 2) { + level = 2; + } else if (level > 17) { + level = 17; + } + + writeRegister(REG_PA_DAC_7X, 0x84); + writeRegister(REG_PA_CONFIG_7X, PA_BOOST_7X | (level - 2)); + } +} + +uint8_t sx127x::getTxPower() { + byte txp = readRegister(REG_PA_CONFIG_7X); + return txp; +} + +void sx127x::setFrequency(unsigned long frequency) { + _frequency = frequency; + + uint32_t frf = ((uint64_t)frequency << 19) / 32000000; + + writeRegister(REG_FRF_MSB_7X, (uint8_t)(frf >> 16)); + writeRegister(REG_FRF_MID_7X, (uint8_t)(frf >> 8)); + writeRegister(REG_FRF_LSB_7X, (uint8_t)(frf >> 0)); + + optimizeModemSensitivity(); +} + +uint32_t sx127x::getFrequency() { + uint8_t msb = readRegister(REG_FRF_MSB_7X); + uint8_t mid = readRegister(REG_FRF_MID_7X); + uint8_t lsb = readRegister(REG_FRF_LSB_7X); + + 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); + + return frequency; +} + +void sx127x::setSpreadingFactor(int sf) +{ + if (sf < 6) { + sf = 6; + } else if (sf > 12) { + sf = 12; + } + + if (sf == 6) { + writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc5); + writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0c); + } else { + writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc3); + writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0a); + } + + writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0)); + handleLowDataRate(); +} + +long sx127x::getSignalBandwidth() +{ + byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 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; + } + return 0; +} + +void sx127x::handleLowDataRate(){ + int sf = (readRegister(REG_MODEM_CONFIG_2_7X) >> 4); + if ( long( (1< 16) { + // set auto AGC and LowDataRateOptimize + writeRegister(REG_MODEM_CONFIG_3_7X, (1<<3)|(1<<2)); + } else { + // set auto AGC + writeRegister(REG_MODEM_CONFIG_3_7X, (1<<2)); + } +} + +void sx127x::optimizeModemSensitivity(){ + byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4); + uint32_t freq = getFrequency(); + + if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) { + writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02); + writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x7f); + } else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) { + writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02); + writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x64); + } else { + writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x03); + } +} + +void sx127x::setSignalBandwidth(long sbw) +{ + 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_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0x0f) | (bw << 4)); + + + handleLowDataRate(); + optimizeModemSensitivity(); +} + +void sx127x::setCodingRate4(int denominator) +{ + if (denominator < 5) { + denominator = 5; + } else if (denominator > 8) { + denominator = 8; + } + + int cr = denominator - 4; + + writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1)); +} + +void sx127x::setPreambleLength(long length) +{ + writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8)); + writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 0)); +} + +void sx127x::setSyncWord(int sw) +{ + writeRegister(REG_SYNC_WORD_7X, sw); +} + +void sx127x::enableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) | 0x04); +} + +void sx127x::disableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) & 0xfb); +} + +byte sx127x::random() +{ + return readRegister(REG_RSSI_WIDEBAND_7X); +} + +void sx127x::setPins(int ss, int reset, int dio0, int busy) +{ + _ss = ss; + _reset = reset; + _dio0 = dio0; + _busy = busy; +} + +void sx127x::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void sx127x::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 sx127x::explicitHeaderMode() +{ + _implicitHeaderMode = 0; + + writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) & 0xfe); +} + +void sx127x::implicitHeaderMode() +{ + _implicitHeaderMode = 1; + + writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) | 0x01); +} + + +void ISR_VECT sx127x::handleDio0Rise() +{ + int irqFlags = readRegister(REG_IRQ_FLAGS_7X); + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS_7X, irqFlags); + if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) { + + // received a packet + _packetIndex = 0; + + // read packet length + int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X); + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X)); + + if (_onReceive) { + _onReceive(packetLength); + } + + // reset FIFO address + writeRegister(REG_FIFO_ADDR_PTR_7X, 0); + } +} + +void ISR_VECT sx127x::onDio0Rise() +{ + sx127x_modem.handleDio0Rise(); +} + +sx127x sx127x_modem; diff --git a/sx127x.h b/sx127x.h new file mode 100644 index 0000000..dca01c0 --- /dev/null +++ b/sx127x.h @@ -0,0 +1,117 @@ +// 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. + +#ifndef SX1276_H +#define SX1276_H + +#include +#include +#include "Modem.h" + +#define LORA_DEFAULT_SS_PIN 10 +#define LORA_DEFAULT_RESET_PIN 9 +#define LORA_DEFAULT_DIO0_PIN 2 +#define LORA_DEFAULT_BUSY_PIN -1 + +#define PA_OUTPUT_RFO_PIN 0 +#define PA_OUTPUT_PA_BOOST_PIN 1 + +#define RSSI_OFFSET 157 + +class sx127x : public Stream { +public: + sx127x(); + + int begin(long frequency); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(); + + int parsePacket(int size = 0); + int packetRssi(); + int currentRssi(); + uint8_t packetRssiRaw(); + uint8_t currentRssiRaw(); + uint8_t packetSnrRaw(); + float packetSnr(); + long packetFrequencyError(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + + void onReceive(void(*callback)(int)); + + void receive(int size = 0); + void idle(); + void sleep(); + + bool preInit(); + uint8_t getTxPower(); + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + uint32_t getFrequency(); + void setFrequency(unsigned long frequency); + void setSpreadingFactor(int sf); + long getSignalBandwidth(); + void setSignalBandwidth(long sbw); + void setCodingRate4(int denominator); + void setPreambleLength(long length); + void setSyncWord(int sw); + uint8_t modemStatus(); + void enableCrc(); + void disableCrc(); + void enableTCXO(); + void disableTCXO(); + + // deprecated + void crc() { enableCrc(); } + void noCrc() { disableCrc(); } + + byte random(); + + void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN); + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + +private: + void explicitHeaderMode(); + void implicitHeaderMode(); + + void handleDio0Rise(); + + uint8_t readRegister(uint8_t address); + void writeRegister(uint8_t address, uint8_t value); + uint8_t singleTransfer(uint8_t address, uint8_t value); + + static void onDio0Rise(); + + void handleLowDataRate(); + void optimizeModemSensitivity(); + +private: + SPISettings _spiSettings; + int _ss; + int _reset; + int _dio0; + int _busy; + long _frequency; + int _packetIndex; + int _implicitHeaderMode; + bool _preinit_done; + void (*_onReceive)(int); +}; + +extern sx127x sx127x_modem; + +#endif diff --git a/sx128x.cpp b/sx128x.cpp new file mode 100644 index 0000000..baa1f31 --- /dev/null +++ b/sx128x.cpp @@ -0,0 +1,882 @@ +// 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 "sx128x.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(NRF52840_XXAA) + #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 + +#define OP_RF_FREQ_8X 0x86 +#define OP_SLEEP_8X 0x84 +#define OP_STANDBY_8X 0x80 +#define OP_TX_8X 0x83 +#define OP_RX_8X 0x82 +#define OP_SET_IRQ_FLAGS_8X 0x8D // also provides info such as + // preamble detection, etc for + // knowing when it's safe to switch + // antenna modes +#define OP_CLEAR_IRQ_STATUS_8X 0x97 +#define OP_GET_IRQ_STATUS_8X 0x15 +#define OP_RX_BUFFER_STATUS_8X 0x17 +#define OP_PACKET_STATUS_8X 0x1D // get snr & rssi of last packet +#define OP_CURRENT_RSSI_8X 0x1F +#define OP_MODULATION_PARAMS_8X 0x8B // bw, sf, cr, etc. +#define OP_PACKET_PARAMS_8X 0x8C // crc, preamble, payload length, etc. +#define OP_STATUS_8X 0xC0 +#define OP_TX_PARAMS_8X 0x8E // set dbm, etc +#define OP_PACKET_TYPE_8X 0x8A +#define OP_BUFFER_BASE_ADDR_8X 0x8F +#define OP_READ_REGISTER_8X 0x19 +#define OP_WRITE_REGISTER_8X 0x18 +#define IRQ_TX_DONE_MASK_8X 0x01 +#define IRQ_RX_DONE_MASK_8X 0x02 +#define IRQ_HEADER_DET_MASK_8X 0x10 +#define IRQ_HEADER_ERROR_MASK_8X 0x20 +#define IRQ_PAYLOAD_CRC_ERROR_MASK_8X 0x40 + +#define MODE_LONG_RANGE_MODE_8X 0x01 + +#define OP_FIFO_WRITE_8X 0x1A +#define OP_FIFO_READ_8X 0x1B +#define IRQ_PREAMBLE_DET_MASK_8X 0x80 + +#define REG_PACKET_SIZE 0x901 +#define REG_FIRM_VER_MSB 0x154 +#define REG_FIRM_VER_LSB 0x153 + +#define XTAL_FREQ_8X (double)52000000 +#define FREQ_DIV_8X (double)pow(2.0, 18.0) +#define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X) + +extern SPIClass spiModem; +#define SPI spiModem + +extern SPIClass SPI; + +#define MAX_PKT_LENGTH 255 + +sx128x::sx128x() : + _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(0x50), + _bw(0x34), + _cr(0x01), + _packetIndex(0), + _preambleLength(18), + _implicitHeaderMode(0), + _payloadLength(255), + _crcMode(0), + _fifo_tx_addr_ptr(0), + _fifo_rx_addr_ptr(0), + _packet({0}), + _rxPacketLength(0), + _preinit_done(false), + _onReceive(NULL) +{ + // overide Stream timeout value + setTimeout(0); +} + +bool sx128x::preInit() { + // setup pins + pinMode(_ss, OUTPUT); + // set SS high + digitalWrite(_ss, HIGH); + + SPI.begin(); + + // check version (retry for up to 2 seconds) + long start = millis(); + + uint8_t version_msb; + uint8_t version_lsb; + + while (((millis() - start) < 2000) && (millis() >= start)) { + + version_msb = readRegister(REG_FIRM_VER_MSB); + version_lsb = readRegister(REG_FIRM_VER_LSB); + + if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) { + break; + } + delay(100); + } + if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) { + return false; + } + + _preinit_done = true; + return true; +} + +uint8_t ISR_VECT sx128x::readRegister(uint16_t address) +{ + return singleTransfer(OP_READ_REGISTER_8X, address, 0x00); +} + +void sx128x::writeRegister(uint16_t address, uint8_t value) +{ + singleTransfer(OP_WRITE_REGISTER_8X, address, value); +} + +uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) +{ + waitOnBusy(); + + uint8_t response; + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + SPI.transfer((address & 0xFF00) >> 8); + SPI.transfer(address & 0x00FF); + if (opcode == OP_READ_REGISTER_8X) { + SPI.transfer(0x00); + } + response = SPI.transfer(value); + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); + + return response; +} + +void sx128x::rxAntEnable() +{ + if (_txen != -1) { + digitalWrite(_txen, LOW); + } + if (_rxen != -1) { + digitalWrite(_rxen, HIGH); + } +} + +void sx128x::txAntEnable() +{ + if (_txen != -1) { + digitalWrite(_txen, HIGH); + } + if (_rxen != -1) { + digitalWrite(_rxen, LOW); + } +} + +void sx128x::loraMode() { + // enable lora mode on the SX1262 chip + uint8_t mode = MODE_LONG_RANGE_MODE_8X; + executeOpcode(OP_PACKET_TYPE_8X, &mode, 1); +} + +void sx128x::waitOnBusy() { + unsigned long time = millis(); + if (_busy != -1) { + while (digitalRead(_busy) == HIGH) + { + if (millis() >= (time + 300)) { + pinMode(LED_BLUE, OUTPUT); + digitalWrite(LED_BLUE, HIGH); + break; + } + // do nothing + } + } +} + +void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) +{ + waitOnBusy(); + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + + for (int i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + } + + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); +} + +void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) +{ + waitOnBusy(); + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(opcode); + SPI.transfer(0x00); + + for (int i = 0; i < size; i++) + { + buffer[i] = SPI.transfer(0x00); + } + + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); +} + +void sx128x::writeBuffer(const uint8_t* buffer, size_t size) +{ + waitOnBusy(); + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(OP_FIFO_WRITE_8X); + SPI.transfer(_fifo_tx_addr_ptr); + + for (int i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + _fifo_tx_addr_ptr++; + } + + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); +} + +void sx128x::readBuffer(uint8_t* buffer, size_t size) +{ + waitOnBusy(); + + digitalWrite(_ss, LOW); + + SPI.beginTransaction(_spiSettings); + SPI.transfer(OP_FIFO_READ_8X); + SPI.transfer(_fifo_rx_addr_ptr); + SPI.transfer(0x00); + + for (int i = 0; i < size; i++) + { + buffer[i] = SPI.transfer(0x00); + } + + SPI.endTransaction(); + + digitalWrite(_ss, HIGH); +} + +void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) { + // because there is no access to these registers on the sx1280, we have + // to set all these parameters at once or not at all. + uint8_t buf[3]; + + buf[0] = sf; + buf[1] = bw; + buf[2] = cr; + executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3); + + if (sf <= 6) { + writeRegister(0x925, 0x1E); + } else if (sf <= 8) { + writeRegister(0x925, 0x37); + } else if (sf >= 9) { + writeRegister(0x925, 0x32); + } + writeRegister(0x093C, 0x1); +} + +void sx128x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) { + // because there is no access to these registers on the sx1280, we have + // to set all these parameters at once or not at all. + uint8_t buf[7]; + + // calculate exponent and mantissa values for modem + uint8_t e = 1; + uint8_t m = 1; + uint32_t preamblelen; + + for (e <= 15; e++;) { + for (m <= 15; m++;) { + preamblelen = m * (uint32_t(1) << e); + if (preamblelen >= preamble) break; + } + if (preamblelen >= preamble) break; + } + + buf[0] = (e << 4) | m; + buf[1] = headermode; + buf[2] = length; + buf[3] = crc; + // standard IQ setting (no inversion) + buf[4] = 0x40; + // unused params + buf[5] = 0x00; + buf[6] = 0x00; + + executeOpcode(OP_PACKET_PARAMS_8X, buf, 7); +} + +int sx128x::begin(unsigned 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 (!_preinit_done) { + if (!preInit()) { + return false; + } + } + + idle(); + loraMode(); + rxAntEnable(); + + setFrequency(frequency); + + // set LNA boost + // todo: implement this + //writeRegister(REG_LNA, 0x96); + + setModulationParams(_sf, _bw, _cr); + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + + // set output power to 2 dBm + setTxPower(2); + + // set base addresses + uint8_t basebuf[2] = {0}; + executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2); + + return 1; +} + +void sx128x::end() +{ + // put in sleep mode + sleep(); + + // stop SPI + SPI.end(); + + _preinit_done = false; +} + +int sx128x::beginPacket(int implicitHeader) +{ + // put in standby mode + idle(); + + if (implicitHeader) { + implicitHeaderMode(); + } else { + explicitHeaderMode(); + } + + _payloadLength = 0; + _fifo_tx_addr_ptr = 0; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + + return 1; +} + +int sx128x::endPacket() +{ + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + + txAntEnable(); + + // put in single TX mode + uint8_t timeout[3] = {0}; + executeOpcode(OP_TX_8X, timeout, 3); + + uint8_t buf[2]; + + buf[0] = 0x00; + buf[1] = 0x00; + + executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); + + // wait for TX done + while ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0) { + buf[0] = 0x00; + buf[1] = 0x00; + executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); + yield(); + } + + // clear IRQ's + + uint8_t mask[2]; + mask[0] = 0x00; + mask[1] = IRQ_TX_DONE_MASK_8X; + executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2); + return 1; +} + +uint8_t sx128x::modemStatus() { + // imitate the register status from the sx1276 / 78 + uint8_t buf[2] = {0}; + + // debug + executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); + + uint8_t clearbuf[2] = {0}; + + uint8_t byte = 0x00; + + if (buf[0] & IRQ_PREAMBLE_DET_MASK_8X != 0) { + byte = byte | 0x01 | 0x04; + // clear register after reading + //clearbuf[0] = IRQ_PREAMBLE_DET_MASK_8X; + clearbuf[0] = 0xFF; + } + + if (buf[1] & IRQ_HEADER_DET_MASK_8X != 0) { + byte = byte | 0x02 | 0x04; + // clear register after reading + //clearbuf[1] = IRQ_HEADER_DET_MASK_8X; + clearbuf[1] = 0xFF; + } + + // debug + executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2); + + return byte; +} + + +uint8_t sx128x::currentRssiRaw() { + uint8_t byte = 0; + executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1); + return byte; +} + +int ISR_VECT sx128x::currentRssi() { + uint8_t byte = 0; + executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1); + int rssi = -byte / 2; + return rssi; +} + +uint8_t sx128x::packetRssiRaw() { + uint8_t buf[5] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); + return buf[0]; +} + +int ISR_VECT sx128x::packetRssi() { + // may need more calculations here + uint8_t buf[5] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); + int pkt_rssi = -buf[0] / 2; + return pkt_rssi; +} + +uint8_t ISR_VECT sx128x::packetSnrRaw() { + uint8_t buf[5] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); + return buf[1]; +} + +float ISR_VECT sx128x::packetSnr() { + uint8_t buf[5] = {0}; + executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 3); + return float(buf[1]) * 0.25; +} + +long sx128x::packetFrequencyError() +{ + int32_t freqError = 0; + // todo: implement this, page 120 of sx1280 datasheet + const float fError = 0.0; + return static_cast(fError); +} + +size_t sx128x::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t sx128x::write(const uint8_t *buffer, size_t size) +{ + if ((_payloadLength + size) > MAX_PKT_LENGTH) { + size = MAX_PKT_LENGTH - _payloadLength; + } + + // write data + writeBuffer(buffer, size); + _payloadLength = _payloadLength + size; + return size; +} + +int ISR_VECT sx128x::available() +{ + return _rxPacketLength - _packetIndex; +} + +int ISR_VECT sx128x::read() +{ + if (!available()) { + return -1; + } + + uint8_t byte = _packet[_packetIndex]; + _packetIndex++; + return byte; +} + +int sx128x::peek() +{ + if (!available()) { + return -1; + } + + uint8_t b = _packet[_packetIndex]; + return b; +} + +void sx128x::flush() +{ +} + +void sx128x::onReceive(void(*callback)(int)) +{ + _onReceive = callback; + + if (callback) { + pinMode(_dio0, INPUT); + + // 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_8X; + + // set dio1 masks + buf[4] = 0x00; + buf[5] = 0x00; + + // set dio2 masks + buf[6] = 0x00; + buf[7] = 0x00; + + executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8); +//#ifdef SPI_HAS_NOTUSINGINTERRUPT +// SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +//#endif + attachInterrupt(digitalPinToInterrupt(_dio0), sx128x::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +//#ifdef SPI_HAS_NOTUSINGINTERRUPT +// SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +//#endif + } +} + +void sx128x::receive(int size) +{ + if (size > 0) { + implicitHeaderMode(); + + // tell radio payload length + _rxPacketLength = size; + //_payloadLength = size; + //setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); + } else { + explicitHeaderMode(); + } + + rxAntEnable(); + + uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode + executeOpcode(OP_RX_8X, mode, 3); +} + +void sx128x::idle() +{ + #if HAS_TCXO + // STDBY_XOSC + uint8_t byte = 0x01; + #else + // STDBY_RC + uint8_t byte = 0x00; + #endif + executeOpcode(OP_STANDBY_8X, &byte, 1); +} + +void sx128x::sleep() +{ + uint8_t byte = 0x00; + executeOpcode(OP_SLEEP_8X, &byte, 1); +} + +void sx128x::enableTCXO() { + // todo: need to check how to implement on sx1280 +} + +void sx128x::disableTCXO() { + // todo: need to check how to implement on sx1280 +} + +void sx128x::setTxPower(int level, int outputPin) { + if (level > 13) { + level = 13; + } else if (level < -18) { + level = -18; + } + + _txp = level; + + level = level + 18; + + uint8_t tx_buf[2]; + + tx_buf[0] = level; + tx_buf[1] = 0xE0; // ramping time - 20 microseconds + + executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2); +} + +uint8_t sx128x::getTxPower() { + return _txp; +} + +void sx128x::setFrequency(unsigned long frequency) { + _frequency = frequency; + + uint8_t buf[3]; + + uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X); + + buf[0] = ((freq >> 16) & 0xFF); + buf[1] = ((freq >> 8) & 0xFF); + buf[2] = (freq & 0xFF); + + executeOpcode(OP_RF_FREQ_8X, buf, 3); +} + +uint32_t sx128x::getFrequency() { + // we can't read the frequency on the sx1280 + uint32_t frequency = _frequency; + + return frequency; +} + +void sx128x::setSpreadingFactor(int sf) +{ + if (sf < 5) { + sf = 5; + } else if (sf > 12) { + sf = 12; + } + + _sf = sf << 4; + + setModulationParams(sf << 4, _bw, _cr); + handleLowDataRate(); +} + +long sx128x::getSignalBandwidth() +{ + int bw = _bw; + switch (bw) { + case 0x34: return 203.125E3; + case 0x26: return 406.25E3; + case 0x18: return 812.5E3; + case 0x0A: return 1625E3; + } + + return 0; +} + +void sx128x::handleLowDataRate(){ + // todo: do i need this?? +} + +void sx128x::optimizeModemSensitivity(){ + // todo: check if there's anything the sx1280 can do here +} + +void sx128x::setSignalBandwidth(long sbw) +{ + if (sbw <= 203.125E3) { + _bw = 0x34; + } else if (sbw <= 406.25E3) { + _bw = 0x26; + } else if (sbw <= 812.5E3) { + _bw = 0x18; + } else { + _bw = 0x0A; + } + + setModulationParams(_sf, _bw, _cr); + + handleLowDataRate(); + optimizeModemSensitivity(); +} + +void sx128x::setCodingRate4(int denominator) +{ + if (denominator < 5) { + denominator = 5; + } else if (denominator > 8) { + denominator = 8; + } + + _cr = denominator - 4; + + // todo: add support for new interleaving scheme, see page 117 of sx1280 + // datasheet + + // update cr values for sx1280's use + + setModulationParams(_sf, _bw, _cr); +} + +void sx128x::setPreambleLength(long length) +{ + _preambleLength = length; + setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); +} + +void sx128x::setSyncWord(int sw) +{ + // not implemented +} + +void sx128x::enableCrc() +{ + _crcMode = 0x20; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +} + +void sx128x::disableCrc() +{ + _crcMode = 0; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +} + +byte sx128x::random() +{ + // todo: implement +} + +void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen) +{ + _ss = ss; + _reset = reset; + _dio0 = dio0; + _busy = busy; + _rxen = rxen; + _txen = txen; +} + +void sx128x::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void sx128x::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 sx128x::explicitHeaderMode() +{ + _implicitHeaderMode = 0; + + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +} + +void sx128x::implicitHeaderMode() +{ + _implicitHeaderMode = 0x80; + setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); +} + + +void ISR_VECT sx128x::handleDio0Rise() +{ + uint8_t buf[2]; + + buf[0] = 0x00; + buf[1] = 0x00; + + executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); + + executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2); + + if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) { + // received a packet + _packetIndex = 0; + + uint8_t rxbuf[2] = {0}; + executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2); + _rxPacketLength = rxbuf[0]; + _fifo_rx_addr_ptr = rxbuf[1]; + readBuffer(_packet, _rxPacketLength); + + if (_onReceive) { + _onReceive(_rxPacketLength); + } + + } +} + +void ISR_VECT sx128x::onDio0Rise() +{ + sx128x_modem.handleDio0Rise(); +} + +sx128x sx128x_modem; diff --git a/sx128x.h b/sx128x.h new file mode 100644 index 0000000..213fc48 --- /dev/null +++ b/sx128x.h @@ -0,0 +1,144 @@ +// 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. + +#ifndef SX128X_H +#define SX128X_H + +#include +#include +#include "Modem.h" + +#define LORA_DEFAULT_SS_PIN 10 +#define LORA_DEFAULT_RESET_PIN 9 +#define LORA_DEFAULT_DIO0_PIN 2 +#define LORA_DEFAULT_RXEN_PIN -1 +#define LORA_DEFAULT_TXEN_PIN -1 +#define LORA_DEFAULT_BUSY_PIN -1 + +#define PA_OUTPUT_RFO_PIN 0 +#define PA_OUTPUT_PA_BOOST_PIN 1 + +#define RSSI_OFFSET 157 + +class sx128x : public Stream { +public: + sx128x(); + + int begin(unsigned long frequency); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(); + + int parsePacket(int size = 0); + int packetRssi(); + int currentRssi(); + uint8_t packetRssiRaw(); + uint8_t currentRssiRaw(); + uint8_t packetSnrRaw(); + float packetSnr(); + long packetFrequencyError(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + + void onReceive(void(*callback)(int)); + + void receive(int size = 0); + void idle(); + void sleep(); + + bool preInit(); + uint8_t getTxPower(); + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + uint32_t getFrequency(); + void setFrequency(unsigned long frequency); + void setSpreadingFactor(int sf); + long getSignalBandwidth(); + void setSignalBandwidth(long sbw); + void setCodingRate4(int denominator); + void setPreambleLength(long length); + void setSyncWord(int sw); + uint8_t modemStatus(); + void enableCrc(); + void disableCrc(); + void enableTCXO(); + void disableTCXO(); + + void txAntEnable(); + void rxAntEnable(); + void loraMode(); + void waitOnBusy(); + void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); + void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); + void writeBuffer(const uint8_t* buffer, size_t size); + void readBuffer(uint8_t* buffer, size_t size); + void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); + void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); + + // deprecated + void crc() { enableCrc(); } + void noCrc() { disableCrc(); } + + byte random(); + + void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN); + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + +private: + void explicitHeaderMode(); + void implicitHeaderMode(); + + void handleDio0Rise(); + + uint8_t readRegister(uint16_t address); + void writeRegister(uint16_t address, uint8_t value); + uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); + + static void onDio0Rise(); + + void handleLowDataRate(); + void optimizeModemSensitivity(); + +private: + SPISettings _spiSettings; + int _ss; + int _reset; + int _dio0; + int _rxen; + int _txen; + int _busy; + int _modem; + unsigned long _frequency; + int _txp; + uint8_t _sf; + uint8_t _bw; + uint8_t _cr; + int _packetIndex; + uint32_t _preambleLength; + int _implicitHeaderMode; + int _payloadLength; + int _crcMode; + int _fifo_tx_addr_ptr; + int _fifo_rx_addr_ptr; + uint8_t _packet[256]; + bool _preinit_done; + int _rxPacketLength; + void (*_onReceive)(int); +}; + +extern sx128x sx128x_modem; + +#endif