// 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"
# include "Boards.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 MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
# include "soc/rtc_wdt.h"
# endif
# 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 ;