// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2018 by Mark Qvist
// Obviously still under the MIT license.
# include "LoRa.h"
# define MCU_1284P 0x91
# define MCU_2560 0x92
# define MCU_ESP32 0x81
# 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
# 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 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_DETECTION_THRESHOLD 0x37
# define REG_SYNC_WORD 0x39
# define REG_DIO_MAPPING_1 0x40
# define REG_VERSION 0x42
# 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
# define MAX_PKT_LENGTH 255
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 ) ,
_frequency ( 0 ) ,
_packetIndex ( 0 ) ,
_implicitHeaderMode ( 0 ) ,
_onReceive ( NULL )
{
// overide Stream timeout value
setTimeout ( 0 ) ;
}
bool LoRaClass : : preInit ( ) {
// setup pins
pinMode ( _ss , OUTPUT ) ;
// set SS high
digitalWrite ( _ss , HIGH ) ;
SPI . begin ( ) ;
// check version
uint8_t version = readRegister ( REG_VERSION ) ;
if ( version ! = 0x12 ) {
return false ;
}
lora_preinit_done = true ;
return true ;
}
int LoRaClass : : begin ( long frequency )
{
if ( _reset ! = - 1 ) {
pinMode ( _reset , OUTPUT ) ;
// perform reset
digitalWrite ( _reset , LOW ) ;
delay ( 10 ) ;
digitalWrite ( _reset , HIGH ) ;
delay ( 10 ) ;
}
if ( ! lora_preinit_done ) {
if ( ! preInit ( ) ) {
return false ;
}
}
// 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 ( ) ;
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 ( ) ;
}
// reset FIFO address and paload length
writeRegister ( REG_FIFO_ADDR_PTR , 0 ) ;
writeRegister ( REG_PAYLOAD_LENGTH , 0 ) ;
return 1 ;
}
int LoRaClass : : endPacket ( )
{
// 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 ) ;
return 1 ;
}
int LoRaClass : : parsePacket ( int size )
{
int packetLength = 0 ;
int irqFlags = readRegister ( REG_IRQ_FLAGS ) ;
if ( size > 0 ) {
implicitHeaderMode ( ) ;
writeRegister ( REG_PAYLOAD_LENGTH , size & 0xff ) ;
} else {
explicitHeaderMode ( ) ;
}
// clear IRQ's
writeRegister ( REG_IRQ_FLAGS , irqFlags ) ;
if ( ( irqFlags & IRQ_RX_DONE_MASK ) & & ( irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK ) = = 0 ) {
// received a packet
_packetIndex = 0 ;
// read packet length
if ( _implicitHeaderMode ) {
packetLength = readRegister ( REG_PAYLOAD_LENGTH ) ;
} else {
packetLength = readRegister ( REG_RX_NB_BYTES ) ;
}
// set FIFO address to current RX address
writeRegister ( REG_FIFO_ADDR_PTR , readRegister ( REG_FIFO_RX_CURRENT_ADDR ) ) ;
// put in standby mode
idle ( ) ;
} 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 ) ;
}
return packetLength ;
}
uint8_t LoRaClass : : modemStatus ( ) {
return readRegister ( REG_MODEM_STAT ) ;
}
uint8_t LoRaClass : : currentRssiRaw ( ) {
uint8_t rssi = readRegister ( REG_RSSI_VALUE ) ;
return rssi ;
}
int ISR_VECT LoRaClass : : currentRssi ( ) {
int rssi = ( int ) readRegister ( REG_RSSI_VALUE ) - RSSI_OFFSET ;
if ( _frequency < 820E6 ) rssi - = 7 ;
return rssi ;
}
uint8_t LoRaClass : : packetRssiRaw ( ) {
uint8_t pkt_rssi_value = readRegister ( REG_PKT_RSSI_VALUE ) ;
return pkt_rssi_value ;
}
int ISR_VECT LoRaClass : : packetRssi ( ) {
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 ;
}
uint8_t ISR_VECT LoRaClass : : packetSnrRaw ( ) {
return readRegister ( REG_PKT_SNR_VALUE ) ;
}
float ISR_VECT LoRaClass : : packetSnr ( ) {
return ( ( int8_t ) readRegister ( REG_PKT_SNR_VALUE ) ) * 0.25 ;
}
long LoRaClass : : packetFrequencyError ( )
{
int32_t freqError = 0 ;
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 ) ;
}
size_t LoRaClass : : write ( uint8_t byte )
{
return write ( & byte , sizeof ( byte ) ) ;
}
size_t LoRaClass : : write ( const uint8_t * buffer , size_t size )
{
int currentLength = readRegister ( REG_PAYLOAD_LENGTH ) ;
// 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 , buffer [ i ] ) ;
}
// update length
writeRegister ( REG_PAYLOAD_LENGTH , currentLength + size ) ;
return size ;
}
int ISR_VECT LoRaClass : : available ( )
{
return ( readRegister ( REG_RX_NB_BYTES ) - _packetIndex ) ;
}
int ISR_VECT LoRaClass : : read ( )
{
if ( ! available ( ) ) {
return - 1 ;
}
_packetIndex + + ;
return readRegister ( REG_FIFO ) ;
}
int LoRaClass : : peek ( )
{
if ( ! available ( ) ) {
return - 1 ;
}
// 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 ) ;
return b ;
}
void LoRaClass : : flush ( )
{
}
void LoRaClass : : onReceive ( void ( * callback ) ( int ) )
{
_onReceive = callback ;
if ( callback ) {
pinMode ( _dio0 , INPUT ) ;
writeRegister ( REG_DIO_MAPPING_1 , 0x00 ) ;
# 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 ( ) ;
writeRegister ( REG_PAYLOAD_LENGTH , size & 0xff ) ;
} else {
explicitHeaderMode ( ) ;
}
writeRegister ( REG_OP_MODE , MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS ) ;
}
void LoRaClass : : idle ( )
{
writeRegister ( REG_OP_MODE , MODE_LONG_RANGE_MODE | MODE_STDBY ) ;
}
void LoRaClass : : sleep ( )
{
writeRegister ( REG_OP_MODE , MODE_LONG_RANGE_MODE | MODE_SLEEP ) ;
}
void LoRaClass : : 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 , 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 ) ) ;
}
}
uint8_t LoRaClass : : getTxPower ( ) {
byte txp = readRegister ( REG_PA_CONFIG ) ;
return txp ;
}
void LoRaClass : : setFrequency ( long frequency ) {
_frequency = frequency ;
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 ) ) ;
}
uint32_t LoRaClass : : getFrequency ( ) {
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 ) ;
return frequency ;
}
void LoRaClass : : setSpreadingFactor ( int sf )
{
if ( sf < 6 ) {
sf = 6 ;
} else if ( sf > 12 ) {
sf = 12 ;
}
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 ) ) ;
handleLowDataRate ( ) ;
}
long LoRaClass : : getSignalBandwidth ( )
{
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 ;
}
return 0 ;
}
void LoRaClass : : handleLowDataRate ( ) {
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 ) ) ;
}
}
void LoRaClass : : 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 , ( readRegister ( REG_MODEM_CONFIG_1 ) & 0x0f ) | ( bw < < 4 ) ) ;
handleLowDataRate ( ) ;
}
void LoRaClass : : setCodingRate4 ( int denominator )
{
if ( denominator < 5 ) {
denominator = 5 ;
} else if ( denominator > 8 ) {
denominator = 8 ;
}
int cr = denominator - 4 ;
writeRegister ( REG_MODEM_CONFIG_1 , ( readRegister ( REG_MODEM_CONFIG_1 ) & 0xf1 ) | ( cr < < 1 ) ) ;
}
void LoRaClass : : setPreambleLength ( long length )
{
writeRegister ( REG_PREAMBLE_MSB , ( uint8_t ) ( length > > 8 ) ) ;
writeRegister ( REG_PREAMBLE_LSB , ( uint8_t ) ( length > > 0 ) ) ;
}
void LoRaClass : : setSyncWord ( int sw )
{
writeRegister ( REG_SYNC_WORD , sw ) ;
}
void LoRaClass : : enableCrc ( )
{
writeRegister ( REG_MODEM_CONFIG_2 , readRegister ( REG_MODEM_CONFIG_2 ) | 0x04 ) ;
}
void LoRaClass : : disableCrc ( )
{
writeRegister ( REG_MODEM_CONFIG_2 , readRegister ( REG_MODEM_CONFIG_2 ) & 0xfb ) ;
}
byte LoRaClass : : random ( )
{
return readRegister ( REG_RSSI_WIDEBAND ) ;
}
void LoRaClass : : setPins ( int ss , int reset , int dio0 )
{
_ss = ss ;
_reset = reset ;
_dio0 = dio0 ;
}
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 ;
writeRegister ( REG_MODEM_CONFIG_1 , readRegister ( REG_MODEM_CONFIG_1 ) & 0xfe ) ;
}
void LoRaClass : : implicitHeaderMode ( )
{
_implicitHeaderMode = 1 ;
writeRegister ( REG_MODEM_CONFIG_1 , readRegister ( REG_MODEM_CONFIG_1 ) | 0x01 ) ;
}
void ISR_VECT LoRaClass : : handleDio0Rise ( )
{
int irqFlags = readRegister ( REG_IRQ_FLAGS ) ;
// clear IRQ's
writeRegister ( REG_IRQ_FLAGS , irqFlags ) ;
if ( ( irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK ) = = 0 ) {
// received a packet
_packetIndex = 0 ;
// read packet length
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 ) ) ;
if ( _onReceive ) {
_onReceive ( packetLength ) ;
}
// reset FIFO address
writeRegister ( REG_FIFO_ADDR_PTR , 0 ) ;
}
}
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 ;
}
void ISR_VECT LoRaClass : : onDio0Rise ( )
{
LoRa . handleDio0Rise ( ) ;
}
LoRaClass LoRa ;