#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
#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;
#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
+++ /dev/null
-// 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<int32_t>(readRegister(REG_FREQ_ERROR_MSB) & B111);
- freqError <<= 8L;
- freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID));
- freqError <<= 8L;
- freqError += static_cast<int32_t>(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<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37
-
- return static_cast<long>(fError);
- #elif MODEM == SX1262
- // todo: implement this, no idea how to check it on the sx1262
- const float fError = 0.0;
- return static_cast<long>(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<<sf) / (getSignalBandwidth()/1000)) > 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;
+++ /dev/null
-// 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 LORA_H
-#define LORA_H
-
-#include <Arduino.h>
-#include <SPI.h>
-#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_BUSY_PIN -1
-
-#define PA_OUTPUT_RFO_PIN 0
-#define PA_OUTPUT_PA_BOOST_PIN 1
-
-#define RSSI_OFFSET 157
-
-class LoRaClass : public Stream {
-public:
- LoRaClass();
-
- 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(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();
-
- #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
-
- // 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 rxen = LORA_DEFAULT_RXEN_PIN, int busy = LORA_DEFAULT_BUSY_PIN);
- void setSPIFrequency(uint32_t frequency);
-
- void dumpRegisters(Stream& out);
-
-private:
- void explicitHeaderMode();
- void implicitHeaderMode();
-
- 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
-
- static void onDio0Rise();
-
- void handleLowDataRate();
- void optimizeModemSensitivity();
-
-private:
- SPISettings _spiSettings;
- int _ss;
- int _reset;
- int _dio0;
- int _rxen;
- int _busy;
- long _frequency;
- int _txp;
- uint8_t _sf;
- uint8_t _bw;
- uint8_t _cr;
- uint8_t _ldro;
- int _packetIndex;
- int _preambleLength;
- int _implicitHeaderMode;
- int _payloadLength;
- int _crcMode;
- void (*_onReceive)(int);
-};
-
-extern LoRaClass LoRa;
-
-#endif
#define SX1276 0x01
#define SX1278 0x02
#define SX1262 0x03
+#define SX1280 0x04
// 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) {
} else {
// Unknown boot
}
- LoRa.setFrequency(M_FRQ_S);
+ LoRa->setFrequency(M_FRQ_S);
} else {
modem_installed = false;
}
// 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);
}
}
inline void getPacketData(uint16_t len) {
while (len-- && read_len < MTU) {
- pbuf[read_len++] = LoRa.read();
+ pbuf[read_len++] = LoRa->read();
}
}
// 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;
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);
#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);
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);
}
#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);
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
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
setCodingRate();
getFrequency();
- LoRa.enableCrc();
+ LoRa->enableCrc();
- LoRa.onReceive(receive_callback);
+ LoRa->onReceive(receive_callback);
lora_receive();
}
void stopRadio() {
- LoRa.end();
+ LoRa->end();
radio_online = false;
}
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
// 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);
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;
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
#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();
#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();
}
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 {
int written_bytes = 0;
#endif
#include <stddef.h>
-#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"
}
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);
}
void setPreamble() {
- if (radio_online) LoRa.setPreambleLength(lora_preamble_symbols);
+ if (radio_online) LoRa->setPreambleLength(lora_preamble_symbols);
kiss_indicate_phy_stats();
}
}
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();
}
}
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_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_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_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_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_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;
}
--- /dev/null
+// 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<long>(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;
--- /dev/null
+// 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 SX126X_H
+#define SX126X_H
+
+#include <Arduino.h>
+#include <SPI.h>
+#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 sx126x : public Stream {
+public:
+ sx126x();
+
+ 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(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 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(); }
+ 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);
+ 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 _busy;
+ long _frequency;
+ int _txp;
+ uint8_t _sf;
+ uint8_t _bw;
+ uint8_t _cr;
+ uint8_t _ldro;
+ int _packetIndex;
+ int _preambleLength;
+ 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 sx126x sx126x_modem;
+
+#endif
--- /dev/null
+// 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<int32_t>(readRegister(REG_FREQ_ERROR_MSB_7X) & B111);
+ freqError <<= 8L;
+ freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID_7X));
+ freqError <<= 8L;
+ freqError += static_cast<int32_t>(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<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37
+
+ return static_cast<long>(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<<sf) / (getSignalBandwidth()/1000)) > 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;
--- /dev/null
+// 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 <Arduino.h>
+#include <SPI.h>
+#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
--- /dev/null
+// 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<long>(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;
--- /dev/null
+// 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 <Arduino.h>
+#include <SPI.h>
+#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