This is a copy of the community maintained fork of the open firmware which powers RNode devices. This version will have support for the hardware made by Mees Electronics.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1498 lines
38 KiB

// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2023 by Mark Qvist
// Obviously still under the MIT license.
#include "LoRa.h"
#include "Modem.h"
#define MCU_1284P 0x91
#define MCU_2560 0x92
#define MCU_ESP32 0x81
#define MCU_NRF52 0x71
#if defined(__AVR_ATmega1284P__)
#define PLATFORM PLATFORM_AVR
#define MCU_VARIANT MCU_1284P
#elif defined(__AVR_ATmega2560__)
#define PLATFORM PLATFORM_AVR
#define MCU_VARIANT MCU_2560
#elif defined(ESP32)
#define PLATFORM PLATFORM_ESP32
#define MCU_VARIANT MCU_ESP32
#elif defined(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 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;