This is code is part of a Technion course project in advanced IoT, implementing a device to receive and present sensors data from a Formula racing car built by students at Technion - Israel Institute of Technology.
Fork of DISCO-L072CZ-LRWAN1_LoRa_PingPong by
This is code is part of a Technion course project in advanced IoT, implementing a device to receive sensors data from another L072CZ-LRWAN1 installed on a Formula racing car (built by students at Technion - Israel Institute of Technology), and sends it to a GUI presenting the data (GUI project: github.com/ward-mattar/TechnionFormulaGUI).
How to install
- Create an account on Mbed: https://os.mbed.com/account/signup/
- Import project into Compiler
- In the Program Workspace select "Formula_Nucleo_Receiver"
- Select a Platform like so:
- Click button at top-left
- Add Board
- Search "NUCLEO F103RB" and then "Add to your Mbed Compiler"
- Finally click "Compile", if the build was successful, the binary would download automatically
- To install it on device simply plug it in to a PC, open device drive and drag then drop binary file in it
Diff: SX1276GenericLib/sx1276/sx1276.cpp
- Revision:
- 12:046346a16ff4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/sx1276/sx1276.cpp Sat May 19 15:42:38 2018 +0000 @@ -0,0 +1,1711 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: Actual implementation of a SX1276 radio, inherits Radio + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainers: Miguel Luis, Gregory Cristian and Nicolas Huguenin +*/ + +/* + * additional development to make it more generic across multiple OS versions + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + +#include "sx1276.h" + + + +const SX1276::BandwidthMap SX1276::FskBandwidths[] = +{ + { 2600 , 0x17 }, + { 3100 , 0x0F }, + { 3900 , 0x07 }, + { 5200 , 0x16 }, + { 6300 , 0x0E }, + { 7800 , 0x06 }, + { 10400 , 0x15 }, + { 12500 , 0x0D }, + { 15600 , 0x05 }, + { 20800 , 0x14 }, + { 25000 , 0x0C }, + { 31300 , 0x04 }, + { 41700 , 0x13 }, + { 50000 , 0x0B }, + { 62500 , 0x03 }, + { 83333 , 0x12 }, + { 100000, 0x0A }, + { 125000, 0x02 }, + { 166700, 0x11 }, + { 200000, 0x09 }, + { 250000, 0x01 }, + { 300000, 0x00 }, // Invalid Bandwidth +}; + +const SX1276::BandwidthMap SX1276::LoRaBandwidths[] = +{ + { 7800, 0 }, // 7.8 kHz requires TCXO + { 10400, 1 }, // 10.4 kHz requires TCXO + { 15600, 2 }, // 15.6 kHz requires TCXO + { 20800, 3 }, // 20.8 kHz requires TCXO + { 31250, 4 }, // 31.25 kHz requires TCXO + { 41700, 5 }, // 41.7 kHz requires TCXO + { 62500, 6 }, // 62.5 kHz requires TCXO + { 125000, 7 }, // 125 kHz the LoRa protocol default + { 250000, 8 }, // 250 kHz + { 500000, 9 }, // 500 kHz + { 600000, 10 }, // Invalid Bandwidth, reserved + }; + + + +/*! + * @brief Radio hardware registers initialization definition + * + * @remark Can be automatically generated by the SX1276 GUI (not yet implemented) + */ + +const SX1276::RadioRegisters SX1276::RadioRegsInit[] = { + { MODEM_FSK , REG_LNA , 0x23 }, + { MODEM_FSK , REG_RXCONFIG , 0x1E }, + { MODEM_FSK , REG_RSSICONFIG , 0xD2 }, + { MODEM_FSK , REG_AFCFEI , 0x01 }, + { MODEM_FSK , REG_PREAMBLEDETECT , 0xAA }, + { MODEM_FSK , REG_OSC , 0x07 }, + { MODEM_FSK , REG_SYNCCONFIG , 0x12 }, + { MODEM_FSK , REG_SYNCVALUE1 , 0xC1 }, + { MODEM_FSK , REG_SYNCVALUE2 , 0x94 }, + { MODEM_FSK , REG_SYNCVALUE3 , 0xC1 }, + { MODEM_FSK , REG_PACKETCONFIG1 , 0xD8 }, + { MODEM_FSK , REG_FIFOTHRESH , 0x8F }, + { MODEM_FSK , REG_IMAGECAL , 0x02 }, + { MODEM_FSK , REG_DIOMAPPING1 , 0x00 }, + { MODEM_FSK , REG_DIOMAPPING2 , 0x30 }, + { MODEM_LORA, REG_LR_PAYLOADMAXLENGTH, 0x40 }, + +}; + + +SX1276::SX1276( RadioEvents_t *events) : Radio( events ), isRadioActive( false ) +{ + this->rxtxBuffer = new uint8_t[RX_BUFFER_SIZE]; + + this->RadioEvents = events; + + this->dioIrq = new DioIrqHandler[6]; + + this->dioIrq[0] = &SX1276::OnDio0Irq; + this->dioIrq[1] = &SX1276::OnDio1Irq; + this->dioIrq[2] = &SX1276::OnDio2Irq; + this->dioIrq[3] = &SX1276::OnDio3Irq; + this->dioIrq[4] = &SX1276::OnDio4Irq; + this->dioIrq[5] = NULL; + + this->settings.State = RF_IDLE; +} + +SX1276::~SX1276( ) +{ + delete this->rxtxBuffer; + delete this->dioIrq; +} + +bool SX1276::Init( RadioEvents_t *events ) +{ + if (Read(REG_VERSION) == 0x00) + return false; + + this->RadioEvents = events; + return true; +} + + +void SX1276::RadioRegistersInit( ) +{ + uint8_t i = 0; + for( i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters ); i++ ) + { + SetModem( RadioRegsInit[i].Modem ); + Write( RadioRegsInit[i].Addr, RadioRegsInit[i].Value ); + } +} + + +RadioState SX1276::GetStatus( void ) +{ + return this->settings.State; +} + +void SX1276::SetChannel( uint32_t freq ) +{ + this->settings.Channel = freq; + freq = ( uint32_t )( ( double )freq / ( double )FREQ_STEP ); + Write( REG_FRFMSB, ( uint8_t )( ( freq >> 16 ) & 0xFF ) ); + Write( REG_FRFMID, ( uint8_t )( ( freq >> 8 ) & 0xFF ) ); + Write( REG_FRFLSB, ( uint8_t )( freq & 0xFF ) ); +} + +bool SX1276::IsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh ) +{ + int16_t rssi = 0; + + SetModem( modem ); + + SetChannel( freq ); + + SetOpMode( RF_OPMODE_RECEIVER ); + + Sleep_ms( 1 ); + + rssi = GetRssi( modem ); + + Sleep( ); + + if( rssi > rssiThresh ) + { + return false; + } + return true; +} + +uint32_t SX1276::Random( void ) +{ + uint8_t i; + uint32_t rnd = 0; + + /* + * Radio setup for random number generation + */ + // Set LoRa modem ON + SetModem( MODEM_LORA ); + + // Disable LoRa modem interrupts + Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // Set radio in continuous reception + SetOpMode( RF_OPMODE_RECEIVER ); + + for( i = 0; i < 32; i++ ) + { + Sleep_ms( 1 ); + // Unfiltered RSSI value reading. Only takes the LSB value + rnd |= ( ( uint32_t )Read( REG_LR_RSSIWIDEBAND ) & 0x01 ) << i; + } + + Sleep( ); + + return rnd; +} + +/*! + * Performs the Rx chain calibration for LF and HF bands + * \remark Must be called just after the reset so all registers are at their + * default values + */ +void SX1276::RxChainCalibration( void ) +{ + uint8_t regPaConfigInitVal; + uint32_t initialFreq; + + // Save context + regPaConfigInitVal = this->Read( REG_PACONFIG ); + initialFreq = ( double )( ( ( uint32_t )this->Read( REG_FRFMSB ) << 16 ) | + ( ( uint32_t )this->Read( REG_FRFMID ) << 8 ) | + ( ( uint32_t )this->Read( REG_FRFLSB ) ) ) * ( double )FREQ_STEP; + + // Cut the PA just in case, RFO output, power = -1 dBm + this->Write( REG_PACONFIG, 0x00 ); + + // Launch Rx chain calibration for LF band + Write ( REG_IMAGECAL, ( Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_MASK ) | RF_IMAGECAL_IMAGECAL_START ); + while( ( Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_RUNNING ) == RF_IMAGECAL_IMAGECAL_RUNNING ) + { + } + + // Sets a Frequency in HF band + SetChannel( 868000000 ); + + // Launch Rx chain calibration for HF band + Write ( REG_IMAGECAL, ( Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_MASK ) | RF_IMAGECAL_IMAGECAL_START ); + while( ( Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_RUNNING ) == RF_IMAGECAL_IMAGECAL_RUNNING ) + { + } + + // Restore context + this->Write( REG_PACONFIG, regPaConfigInitVal ); + SetChannel( initialFreq ); +} + +/*! + * Returns the known FSK bandwidth registers value + * + * \param [IN] bandwidth Bandwidth value in Hz + * \retval regValue Bandwidth register value. + */ +uint8_t SX1276::GetFskBandwidthRegValue( uint32_t bandwidth ) +{ + uint8_t i; + + for( i = 0; i < ( sizeof( FskBandwidths ) / sizeof( BandwidthMap ) ) - 1; i++ ) + { + if( ( bandwidth >= FskBandwidths[i].bandwidth ) && ( bandwidth < FskBandwidths[i + 1].bandwidth ) ) + { + return FskBandwidths[i].RegValue; + } + } + // ERROR: Value not found + while( 1 ); +} + +/*! + * Returns the known LoRa bandwidth registers value + * + * \param [IN] bandwidth Bandwidth value in Hz + * \retval regValue Bandwidth register value. + */ +uint8_t SX1276::GetLoRaBandwidthRegValue( uint32_t bandwidth ) +{ + uint8_t i; + + for( i = 0; i < ( sizeof( LoRaBandwidths ) / sizeof( BandwidthMap ) ) - 1; i++ ) + { + if( ( bandwidth >= LoRaBandwidths[i].bandwidth ) && ( bandwidth < LoRaBandwidths[i + 1].bandwidth ) ) + { + return LoRaBandwidths[i].RegValue; + } + } + // ERROR: Value not found + while( 1 ); +} + +void SX1276::SetRxConfig( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ) +{ + SetModem( modem ); + + switch( modem ) + { + case MODEM_FSK: + { + this->settings.Fsk.Bandwidth = bandwidth; + this->settings.Fsk.Datarate = datarate; + this->settings.Fsk.BandwidthAfc = bandwidthAfc; + this->settings.Fsk.FixLen = fixLen; + this->settings.Fsk.PayloadLen = payloadLen; + this->settings.Fsk.CrcOn = crcOn; + this->settings.Fsk.IqInverted = iqInverted; + this->settings.Fsk.RxContinuous = rxContinuous; + this->settings.Fsk.PreambleLen = preambleLen; + this->settings.Fsk.RxSingleTimeout = symbTimeout * ( ( 1.0 / ( double )datarate ) * 8.0 ) * 1e3; + + + datarate = ( uint16_t )( ( double )XTAL_FREQ / ( double )datarate ); + Write( REG_BITRATEMSB, ( uint8_t )( datarate >> 8 ) ); + Write( REG_BITRATELSB, ( uint8_t )( datarate & 0xFF ) ); + + Write( REG_RXBW, GetFskBandwidthRegValue( bandwidth ) ); + Write( REG_AFCBW, GetFskBandwidthRegValue( bandwidthAfc ) ); + + Write( REG_PREAMBLEMSB, ( uint8_t )( ( preambleLen >> 8 ) & 0xFF ) ); + Write( REG_PREAMBLELSB, ( uint8_t )( preambleLen & 0xFF ) ); + + if( fixLen == 1 ) + { + Write( REG_PAYLOADLENGTH, payloadLen ); + } + else + { + Write( REG_PAYLOADLENGTH, 0xFF ); // Set payload length to the maximum + } + + Write( REG_PACKETCONFIG1, + ( Read( REG_PACKETCONFIG1 ) & + RF_PACKETCONFIG1_CRC_MASK & + RF_PACKETCONFIG1_PACKETFORMAT_MASK ) | + ( ( fixLen == 1 ) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE ) | + ( crcOn << 4 ) ); + Write( REG_PACKETCONFIG2, ( Read( REG_PACKETCONFIG2 ) | RF_PACKETCONFIG2_DATAMODE_PACKET ) ); + } + break; + case MODEM_LORA: + { + if (bandwidth > 11) // specified in Hz, needs mapping + bandwidth = GetLoRaBandwidthRegValue(bandwidth); + if( bandwidth > LORA_BANKWIDTH_500kHz ) + { + // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + while( 1 ); + } + this->settings.LoRa.Bandwidth = bandwidth; + this->settings.LoRa.Datarate = datarate; + this->settings.LoRa.Coderate = coderate; + this->settings.LoRa.PreambleLen = preambleLen; + this->settings.LoRa.FixLen = fixLen; + this->settings.LoRa.PayloadLen = payloadLen; + this->settings.LoRa.CrcOn = crcOn; + this->settings.LoRa.FreqHopOn = freqHopOn; + this->settings.LoRa.HopPeriod = hopPeriod; + this->settings.LoRa.IqInverted = iqInverted; + this->settings.LoRa.RxContinuous = rxContinuous; + + if( datarate > LORA_SF12 ) + { + datarate = LORA_SF12; + } + else if( datarate < LORA_SF6 ) + { + datarate = LORA_SF6; + } + + if( ( ( bandwidth == LORA_BANKWIDTH_125kHz ) && ( ( datarate == LORA_SF11 ) || ( datarate == LORA_SF12 ) ) ) || + ( ( bandwidth == LORA_BANKWIDTH_250kHz ) && ( datarate == LORA_SF12 ) ) ) + { + this->settings.LoRa.LowDatarateOptimize = 0x01; + } + else + { + this->settings.LoRa.LowDatarateOptimize = 0x00; + } + + Write( REG_LR_MODEMCONFIG1, + ( Read( REG_LR_MODEMCONFIG1 ) & + RFLR_MODEMCONFIG1_BW_MASK & + RFLR_MODEMCONFIG1_CODINGRATE_MASK & + RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK ) | + ( bandwidth << 4 ) | ( coderate << 1 ) | + fixLen ); + + Write( REG_LR_MODEMCONFIG2, + ( Read( REG_LR_MODEMCONFIG2 ) & + RFLR_MODEMCONFIG2_SF_MASK & + RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK & + RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) | + ( datarate << 4 ) | ( crcOn << 2 ) | + ( ( symbTimeout >> 8 ) & ~RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) ); + + Write( REG_LR_MODEMCONFIG3, + ( Read( REG_LR_MODEMCONFIG3 ) & + RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK ) | + ( this->settings.LoRa.LowDatarateOptimize << 3 ) ); + + Write( REG_LR_SYMBTIMEOUTLSB, ( uint8_t )( symbTimeout & 0xFF ) ); + + Write( REG_LR_PREAMBLEMSB, ( uint8_t )( ( preambleLen >> 8 ) & 0xFF ) ); + Write( REG_LR_PREAMBLELSB, ( uint8_t )( preambleLen & 0xFF ) ); + + if( fixLen == 1 ) + { + Write( REG_LR_PAYLOADLENGTH, payloadLen ); + } + + if( this->settings.LoRa.FreqHopOn == true ) + { + Write( REG_LR_PLLHOP, ( Read( REG_LR_PLLHOP ) & RFLR_PLLHOP_FASTHOP_MASK ) | RFLR_PLLHOP_FASTHOP_ON ); + Write( REG_LR_HOPPERIOD, this->settings.LoRa.HopPeriod ); + } + + if( ( bandwidth == LORA_BANKWIDTH_500kHz ) && ( this->settings.Channel > RF_MID_BAND_THRESH ) ) + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + Write( REG_LR_TEST36, 0x02 ); + Write( REG_LR_TEST3A, 0x64 ); + } + else if( bandwidth == LORA_BANKWIDTH_500kHz ) + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + Write( REG_LR_TEST36, 0x02 ); + Write( REG_LR_TEST3A, 0x7F ); + } + else + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + Write( REG_LR_TEST36, 0x03 ); + } + + if( datarate == LORA_SF6 ) + { + Write( REG_LR_DETECTOPTIMIZE, + ( Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF6 ); + Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF6 ); + } + else + { + Write( REG_LR_DETECTOPTIMIZE, + ( Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 ); + Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF7_TO_SF12 ); + } + } + break; + } +} + +void SX1276::SetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ) +{ + SetModem( modem ); + SetRfTxPower( power ); + + switch( modem ) + { + case MODEM_FSK: + { + this->settings.Fsk.Power = power; + this->settings.Fsk.Fdev = fdev; + this->settings.Fsk.Bandwidth = bandwidth; + this->settings.Fsk.Datarate = datarate; + this->settings.Fsk.PreambleLen = preambleLen; + this->settings.Fsk.FixLen = fixLen; + this->settings.Fsk.CrcOn = crcOn; + this->settings.Fsk.IqInverted = iqInverted; + this->settings.Fsk.TxTimeout = timeout; + + fdev = ( uint16_t )( ( double )fdev / ( double )FREQ_STEP ); + Write( REG_FDEVMSB, ( uint8_t )( fdev >> 8 ) ); + Write( REG_FDEVLSB, ( uint8_t )( fdev & 0xFF ) ); + + datarate = ( uint16_t )( ( double )XTAL_FREQ / ( double )datarate ); + Write( REG_BITRATEMSB, ( uint8_t )( datarate >> 8 ) ); + Write( REG_BITRATELSB, ( uint8_t )( datarate & 0xFF ) ); + + Write( REG_PREAMBLEMSB, ( preambleLen >> 8 ) & 0x00FF ); + Write( REG_PREAMBLELSB, preambleLen & 0xFF ); + + Write( REG_PACKETCONFIG1, + ( Read( REG_PACKETCONFIG1 ) & + RF_PACKETCONFIG1_CRC_MASK & + RF_PACKETCONFIG1_PACKETFORMAT_MASK ) | + ( ( fixLen == 1 ) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE ) | + ( crcOn << 4 ) ); + Write( REG_PACKETCONFIG2, ( Read( REG_PACKETCONFIG2 ) | RF_PACKETCONFIG2_DATAMODE_PACKET ) ); + } + break; + case MODEM_LORA: + { + this->settings.LoRa.Power = power; + if (bandwidth > 11) // specified in Hz, needs mapping + bandwidth = GetLoRaBandwidthRegValue(bandwidth); + if( bandwidth > LORA_BANKWIDTH_500kHz ) + { + // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + while( 1 ); + } + this->settings.LoRa.Bandwidth = bandwidth; + this->settings.LoRa.Datarate = datarate; + this->settings.LoRa.Coderate = coderate; + this->settings.LoRa.PreambleLen = preambleLen; + this->settings.LoRa.FixLen = fixLen; + this->settings.LoRa.FreqHopOn = freqHopOn; + this->settings.LoRa.HopPeriod = hopPeriod; + this->settings.LoRa.CrcOn = crcOn; + this->settings.LoRa.IqInverted = iqInverted; + this->settings.LoRa.TxTimeout = timeout; + + if( datarate > LORA_SF12 ) + { + datarate = LORA_SF12; + } + else if( datarate < LORA_SF6 ) + { + datarate = LORA_SF6; + } + if( ( ( bandwidth == LORA_BANKWIDTH_125kHz ) && ( ( datarate == LORA_SF11 ) || ( datarate == LORA_SF12 ) ) ) || + ( ( bandwidth == LORA_BANKWIDTH_250kHz ) && ( datarate == LORA_SF12 ) ) ) + { + this->settings.LoRa.LowDatarateOptimize = 0x01; + } + else + { + this->settings.LoRa.LowDatarateOptimize = 0x00; + } + + if( this->settings.LoRa.FreqHopOn == true ) + { + Write( REG_LR_PLLHOP, ( Read( REG_LR_PLLHOP ) & RFLR_PLLHOP_FASTHOP_MASK ) | RFLR_PLLHOP_FASTHOP_ON ); + Write( REG_LR_HOPPERIOD, this->settings.LoRa.HopPeriod ); + } + + Write( REG_LR_MODEMCONFIG1, + ( Read( REG_LR_MODEMCONFIG1 ) & + RFLR_MODEMCONFIG1_BW_MASK & + RFLR_MODEMCONFIG1_CODINGRATE_MASK & + RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK ) | + ( bandwidth << 4 ) | ( coderate << 1 ) | + fixLen ); + + Write( REG_LR_MODEMCONFIG2, + ( Read( REG_LR_MODEMCONFIG2 ) & + RFLR_MODEMCONFIG2_SF_MASK & + RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK ) | + ( datarate << 4 ) | ( crcOn << 2 ) ); + + Write( REG_LR_MODEMCONFIG3, + ( Read( REG_LR_MODEMCONFIG3 ) & + RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK ) | + ( this->settings.LoRa.LowDatarateOptimize << 3 ) ); + + Write( REG_LR_PREAMBLEMSB, ( preambleLen >> 8 ) & 0x00FF ); + Write( REG_LR_PREAMBLELSB, preambleLen & 0xFF ); + + if( datarate == LORA_SF6 ) + { + Write( REG_LR_DETECTOPTIMIZE, + ( Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF6 ); + Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF6 ); + } + else + { + Write( REG_LR_DETECTOPTIMIZE, + ( Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 ); + Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF7_TO_SF12 ); + } + } + break; + } +} + +uint32_t SX1276::TimeOnAir( RadioModems_t modem, int16_t pktLen ) +{ + uint32_t airTime = 0; + + switch( modem ) + { + case MODEM_FSK: + { + airTime = rint( ( 8 * ( this->settings.Fsk.PreambleLen + + ( ( Read( REG_SYNCCONFIG ) & ~RF_SYNCCONFIG_SYNCSIZE_MASK ) + 1 ) + + ( ( this->settings.Fsk.FixLen == 0x01 ) ? 0.0 : 1.0 ) + + ( ( ( Read( REG_PACKETCONFIG1 ) & ~RF_PACKETCONFIG1_ADDRSFILTERING_MASK ) != 0x00 ) ? 1.0 : 0 ) + + pktLen + + ( ( this->settings.Fsk.CrcOn == 0x01 ) ? 2.0 : 0 ) ) / + this->settings.Fsk.Datarate ) * 1e3 ); + } + break; + case MODEM_LORA: + { + double bw = 0.0; + // REMARK: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + switch( this->settings.LoRa.Bandwidth ) + { + case LORA_BANKWIDTH_7kHz: // 7.8 kHz + bw = 78e2; + break; + case LORA_BANKWIDTH_10kHz: // 10.4 kHz + bw = 104e2; + break; + case LORA_BANKWIDTH_15kHz: // 15.6 kHz + bw = 156e2; + break; + case LORA_BANKWIDTH_20kHz: // 20.8 kHz + bw = 208e2; + break; + case LORA_BANKWIDTH_31kHz: // 31.25 kHz + bw = 312e2; + break; + case LORA_BANKWIDTH_41kHz: // 41.7 kHz + bw = 414e2; + break; + case LORA_BANKWIDTH_62kHz: // 62.5 kHz + bw = 625e2; + break; + case LORA_BANKWIDTH_125kHz: // 125 kHz + bw = 125e3; + break; + case LORA_BANKWIDTH_250kHz: // 250 kHz + bw = 250e3; + break; + case LORA_BANKWIDTH_500kHz: // 500 kHz + bw = 500e3; + break; + } + + // Symbol rate : time for one symbol (secs) + double rs = bw / ( 1 << this->settings.LoRa.Datarate ); + double ts = 1 / rs; + // time of preamble + double tPreamble = ( this->settings.LoRa.PreambleLen + 4.25 ) * ts; + // Symbol length of payload and time + double tmp = ceil( ( 8 * pktLen - 4 * this->settings.LoRa.Datarate + + 28 + 16 * this->settings.LoRa.CrcOn - + ( this->settings.LoRa.FixLen ? 20 : 0 ) ) / + ( double )( 4 * ( this->settings.LoRa.Datarate - + ( ( this->settings.LoRa.LowDatarateOptimize > 0 ) ? 2 : 0 ) ) ) ) * + ( this->settings.LoRa.Coderate + 4 ); + double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 ); + double tPayload = nPayload * ts; + // Time on air + double tOnAir = tPreamble + tPayload; + // return ms secs + airTime = floor( tOnAir * 1e3 + 0.999 ); + } + break; + } + return airTime; +} + +void SX1276::Send( void *buffer, int16_t size, void *header, int16_t hsize ) +{ + uint32_t txTimeout = 0; + + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = size + hsize; + + if( this->settings.Fsk.FixLen == false ) + { + uint8_t tmpsize = size + hsize; + WriteFifo( ( uint8_t* )&tmpsize, 1 ); + } + else + { + Write( REG_PAYLOADLENGTH, size + hsize); + } + + if( ( size + hsize > 0 ) && ( size + hsize <= 64 ) ) + { + this->settings.FskPacketHandler.ChunkSize = size + hsize; + } + else + { + if (header) { + WriteFifo( header, hsize ); + memcpy( rxtxBuffer, header, hsize ); + } + memcpy( rxtxBuffer+hsize, (uint8_t *)buffer+hsize, size ); + this->settings.FskPacketHandler.ChunkSize = 32; + } + + // Write payload buffer + if (header) + WriteFifo( header, hsize ); + WriteFifo( buffer, this->settings.FskPacketHandler.ChunkSize ); + this->settings.FskPacketHandler.NbBytes += this->settings.FskPacketHandler.ChunkSize; + txTimeout = this->settings.Fsk.TxTimeout; + } + break; + case MODEM_LORA: + { + if( this->settings.LoRa.IqInverted == true ) + { + Write( REG_LR_INVERTIQ, ( ( Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_ON ) ); + Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON ); + } + else + { + Write( REG_LR_INVERTIQ, ( ( Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) ); + Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF ); + } + + this->settings.LoRaPacketHandler.Size = size + hsize; + + // Initializes the payload size + Write( REG_LR_PAYLOADLENGTH, size + hsize); + + // Full buffer used for Tx + Write( REG_LR_FIFOTXBASEADDR, 0 ); + Write( REG_LR_FIFOADDRPTR, 0 ); + + // FIFO operations can not take place in Sleep mode + if( ( Read( REG_OPMODE ) & ~RF_OPMODE_MASK ) == RF_OPMODE_SLEEP ) + { + Standby( ); + Sleep_ms( 1 ); + } + // Write payload buffer + if (header) + WriteFifo( header, hsize ); + WriteFifo( buffer, size ); + txTimeout = this->settings.LoRa.TxTimeout; + } + break; + } + + Tx( txTimeout ); +} + +void SX1276::Sleep( void ) +{ + SetTimeout(TXTimeoutTimer, NULL); + SetTimeout(RXTimeoutTimer, NULL); + + SetOpMode( RF_OPMODE_SLEEP ); + this->settings.State = RF_IDLE; +} + +void SX1276::Standby( void ) +{ + SetTimeout(TXTimeoutTimer, NULL); + SetTimeout(RXTimeoutTimer, NULL); + + SetOpMode( RF_OPMODE_STANDBY ); + this->settings.State = RF_IDLE; +} + +void SX1276::Rx( uint32_t timeout ) +{ + bool rxContinuous = false; + + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + rxContinuous = this->settings.Fsk.RxContinuous; + + // DIO0=PayloadReady + // DIO1=FifoLevel + // DIO2=SyncAddr + // DIO3=FifoEmpty + // DIO4=Preamble + // DIO5=ModeReady + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RF_DIOMAPPING1_DIO0_MASK & + RF_DIOMAPPING1_DIO1_MASK & + RF_DIOMAPPING1_DIO2_MASK ) | + RF_DIOMAPPING1_DIO0_00 | + RF_DIOMAPPING1_DIO1_00 | + RF_DIOMAPPING1_DIO2_11 ); + + Write( REG_DIOMAPPING2, ( Read( REG_DIOMAPPING2 ) & RF_DIOMAPPING2_DIO4_MASK & + RF_DIOMAPPING2_MAP_MASK ) | + RF_DIOMAPPING2_DIO4_11 | + RF_DIOMAPPING2_MAP_PREAMBLEDETECT ); + + this->settings.FskPacketHandler.FifoThresh = Read( REG_FIFOTHRESH ) & 0x3F; + + Write( REG_RXCONFIG, RF_RXCONFIG_AFCAUTO_ON | RF_RXCONFIG_AGCAUTO_ON | RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT ); + + this->settings.FskPacketHandler.PreambleDetected = false; + this->settings.FskPacketHandler.SyncWordDetected = false; + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = 0; + } + break; + case MODEM_LORA: + { + if( this->settings.LoRa.IqInverted == true ) + { + Write( REG_LR_INVERTIQ, ( ( Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_ON | RFLR_INVERTIQ_TX_OFF ) ); + Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON ); + } + else + { + Write( REG_LR_INVERTIQ, ( ( Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) ); + Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF ); + } + + // ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal + if( this->settings.LoRa.Bandwidth < LORA_BANKWIDTH_500kHz ) + { + Write( REG_LR_DETECTOPTIMIZE, Read( REG_LR_DETECTOPTIMIZE ) & 0x7F ); + Write( REG_LR_TEST30, 0x00 ); + switch( this->settings.LoRa.Bandwidth ) + { + case LORA_BANKWIDTH_7kHz: // 7.8 kHz + Write( REG_LR_TEST2F, 0x48 ); + SetChannel(this->settings.Channel + 7.81e3 ); + break; + case LORA_BANKWIDTH_10kHz: // 10.4 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 10.42e3 ); + break; + case LORA_BANKWIDTH_15kHz: // 15.6 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 15.62e3 ); + break; + case LORA_BANKWIDTH_20kHz: // 20.8 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 20.83e3 ); + break; + case LORA_BANKWIDTH_31kHz: // 31.25 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 31.25e3 ); + break; + case LORA_BANKWIDTH_41kHz: // 41.4 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 41.67e3 ); + break; + case LORA_BANKWIDTH_62kHz: // 62.5 kHz + Write( REG_LR_TEST2F, 0x40 ); + break; + case LORA_BANKWIDTH_125kHz: // 125 kHz + Write( REG_LR_TEST2F, 0x40 ); + break; + case LORA_BANKWIDTH_250kHz: // 250 kHz + Write( REG_LR_TEST2F, 0x40 ); + break; + } + } + else + { + Write( REG_LR_DETECTOPTIMIZE, Read( REG_LR_DETECTOPTIMIZE ) | 0x80 ); + } + + rxContinuous = this->settings.LoRa.RxContinuous; + + if( this->settings.LoRa.FreqHopOn == true ) + { + Write( REG_LR_IRQFLAGSMASK, //RFLR_IRQFLAGS_RXTIMEOUT | + //RFLR_IRQFLAGS_RXDONE | + //RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=RxDone, DIO2=FhssChangeChannel + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_00 | RFLR_DIOMAPPING1_DIO2_00 ); + } + else + { + Write( REG_LR_IRQFLAGSMASK, //RFLR_IRQFLAGS_RXTIMEOUT | + //RFLR_IRQFLAGS_RXDONE | + //RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=RxDone + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_00 ); + } + Write( REG_LR_FIFORXBASEADDR, 0 ); + Write( REG_LR_FIFOADDRPTR, 0 ); + } + break; + } + + this->settings.State = RF_RX_RUNNING; + if( timeout != 0 ) + { + SetTimeout(RXTimeoutTimer, &SX1276::OnTimeoutIrq, timeout * 1e3); + } + + if( this->settings.Modem == MODEM_FSK ) + { + SetOpMode( RF_OPMODE_RECEIVER ); + + if( rxContinuous == false ) + { + SetTimeout(RXTimeoutSyncWordTimer, &SX1276::OnTimeoutIrq, this->settings.Fsk.RxSingleTimeout * 1e3); + } + } + else + { + if( rxContinuous == true ) + { + SetOpMode( RFLR_OPMODE_RECEIVER ); + } + else + { + SetOpMode( RFLR_OPMODE_RECEIVER_SINGLE ); + } + } +} + +bool SX1276::RxSignalPending() +{ + if (this->settings.State != RF_RX_RUNNING) + return false; + + switch( this->settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if (Read(REG_LR_MODEMSTAT) & (RFLR_MODEMSTAT_SIGNAL_DETECTED|RFLR_MODEMSTAT_SIGNAL_SYNCRONIZED|RFLR_MODEMSTAT_HEADERINFO_VALID|RFLR_MODEMSTAT_MODEM_CLEAR)) + return true; + break; + } + return false; +} + +void SX1276::Tx( uint32_t timeout ) +{ + + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + // DIO0=PacketSent + // DIO1=FifoEmpty + // DIO2=FifoFull + // DIO3=FifoEmpty + // DIO4=LowBat + // DIO5=ModeReady + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RF_DIOMAPPING1_DIO0_MASK & + RF_DIOMAPPING1_DIO1_MASK & + RF_DIOMAPPING1_DIO2_MASK ) | + RF_DIOMAPPING1_DIO1_01 ); + + Write( REG_DIOMAPPING2, ( Read( REG_DIOMAPPING2 ) & RF_DIOMAPPING2_DIO4_MASK & + RF_DIOMAPPING2_MAP_MASK ) ); + this->settings.FskPacketHandler.FifoThresh = Read( REG_FIFOTHRESH ) & 0x3F; + } + break; + case MODEM_LORA: + { + if( this->settings.LoRa.FreqHopOn == true ) + { + Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + //RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=TxDone, DIO2=FhssChangeChannel + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_01 | RFLR_DIOMAPPING1_DIO2_00 ); + } + else + { + Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + //RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=TxDone + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_01 ); + } + } + break; + } + + this->settings.State = RF_TX_RUNNING; + SetTimeout(TXTimeoutTimer, &SX1276::OnTimeoutIrq, timeout * 1e3); + SetOpMode( RF_OPMODE_TRANSMITTER ); +} + +void SX1276::StartCad( void ) +{ + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + + } + break; + case MODEM_LORA: + { + Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + //RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL // | + //RFLR_IRQFLAGS_CADDETECTED + ); + + // DIO3=CADDone + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO3_MASK ) | RFLR_DIOMAPPING1_DIO3_00 ); + + this->settings.State = RF_CAD; + SetOpMode( RFLR_OPMODE_CAD ); + } + break; + default: + break; + } +} + +void SX1276::SetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ) +{ + uint32_t timeout = ( uint32_t )( time * 1e6 ); + + SetChannel( freq ); + + SetTxConfig( MODEM_FSK, power, 0, 0, 4800, 0, 5, false, false, 0, 0, 0, timeout ); + + Write( REG_PACKETCONFIG2, ( Read( REG_PACKETCONFIG2 ) & RF_PACKETCONFIG2_DATAMODE_MASK ) ); + // Disable radio interrupts + Write( REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_11 | RF_DIOMAPPING1_DIO1_11 ); + Write( REG_DIOMAPPING2, RF_DIOMAPPING2_DIO4_10 | RF_DIOMAPPING2_DIO5_10 ); + + this->settings.State = RF_TX_RUNNING; + SetTimeout(TXTimeoutTimer, &SX1276::OnTimeoutIrq, timeout); + SetOpMode( RF_OPMODE_TRANSMITTER ); +} + +int16_t SX1276::MaxMTUSize( RadioModems_t modem ) +{ + int16_t mtuSize = 0; + + switch( modem ) + { + case MODEM_FSK: + mtuSize = RX_BUFFER_SIZE; + case MODEM_LORA: + mtuSize = RX_BUFFER_SIZE; + break; + default: + mtuSize = -1; + break; + } + return mtuSize; +} + +int16_t SX1276::GetRssi( RadioModems_t modem ) +{ + int16_t rssi = 0; + + switch( modem ) + { + case MODEM_FSK: + rssi = -( Read( REG_RSSIVALUE ) >> 1 ); + break; + case MODEM_LORA: + if( this->settings.Channel > RF_MID_BAND_THRESH ) + { + rssi = RSSI_OFFSET_HF + Read( REG_LR_RSSIVALUE ); + } + else + { + rssi = RSSI_OFFSET_LF + Read( REG_LR_RSSIVALUE ); + } + break; + default: + rssi = -1; + break; + } + return rssi; +} + +int32_t SX1276::GetFrequencyError(RadioModems_t modem ) +{ + int32_t val = 0; + + if (modem != MODEM_LORA) + return 0; + + val = (Read(REG_LR_FEIMSB) & 0b1111) << 16; // high word, 4 valid bits only + val |= (Read(REG_LR_FEIMID) << 8) | Read(REG_LR_FEILSB); // high byte, low byte + if (val & 0x8000) //sconvert ign bit + val |= 0xfff00000; + + int32_t bandwidth = 0; + for (int i = 0; i < (int)(sizeof(LoRaBandwidths) / sizeof(BandwidthMap)) -1; i++ ) { + if (LoRaBandwidths[i].RegValue == this->settings.LoRa.Bandwidth) { + bandwidth = LoRaBandwidths[i].bandwidth; + break; + } + } + if (!bandwidth) + return 0; + + float bandWidthkHz = (float)bandwidth/1000; + + int32_t hz = (((float)val * (float)(1<<24)) / ((float)XTAL_FREQ)) * (bandWidthkHz / 500.0); + + return hz; +} + + +void SX1276::SetOpMode( uint8_t opMode ) +{ + if( opMode == RF_OPMODE_SLEEP ) + { + SetAntSwLowPower( true ); + } + else + { + SetAntSwLowPower( false ); + SetAntSw( opMode ); + } + Write( REG_OPMODE, ( Read( REG_OPMODE ) & RF_OPMODE_MASK ) | opMode ); +} + +void SX1276::SetModem( RadioModems_t modem ) +{ + if( ( Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_ON ) != 0 ) + { + this->settings.Modem = MODEM_LORA; + } + else + { + this->settings.Modem = MODEM_FSK; + } + + if( this->settings.Modem == modem ) + { + return; + } + + this->settings.Modem = modem; + switch( this->settings.Modem ) + { + default: + case MODEM_FSK: + Sleep( ); + Write( REG_OPMODE, ( Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_OFF ); + + Write( REG_DIOMAPPING1, 0x00 ); + Write( REG_DIOMAPPING2, 0x30 ); // DIO5=ModeReady + break; + case MODEM_LORA: + Sleep( ); + Write( REG_OPMODE, ( Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_ON ); + + Write( REG_DIOMAPPING1, 0x00 ); + Write( REG_DIOMAPPING2, 0x00 ); + break; + } +} + +void SX1276::SetMaxPayloadLength( RadioModems_t modem, uint8_t max ) +{ + this->SetModem( modem ); + + switch( modem ) + { + case MODEM_FSK: + if( this->settings.Fsk.FixLen == false ) + { + this->Write( REG_PAYLOADLENGTH, max ); + } + break; + case MODEM_LORA: + this->Write( REG_LR_PAYLOADMAXLENGTH, max ); + break; + } +} + +void SX1276::SetPublicNetwork( bool enable ) +{ + SetModem( MODEM_LORA ); + this->settings.LoRa.PublicNetwork = enable; + if( enable == true ) + { + // Change LoRa modem SyncWord + Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD ); + } + else + { + // Change LoRa modem SyncWord + Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD ); + } +} + + +void SX1276::OnTimeoutIrq( void ) +{ + switch( this->settings.State ) + { + case RF_RX_RUNNING: + if( this->settings.Modem == MODEM_FSK ) + { + this->settings.FskPacketHandler.PreambleDetected = false; + this->settings.FskPacketHandler.SyncWordDetected = false; + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = 0; + + // Clear Irqs + Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI | + RF_IRQFLAGS1_PREAMBLEDETECT | + RF_IRQFLAGS1_SYNCADDRESSMATCH ); + Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN ); + + if( this->settings.Fsk.RxContinuous == true ) + { + // Continuous mode restart Rx chain + Write( REG_RXCONFIG, Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + SetTimeout(RXTimeoutSyncWordTimer, &SX1276::OnTimeoutIrq, this->settings.Fsk.RxSingleTimeout * 1e3); + } + else + { + this->settings.State = RF_IDLE; + SetTimeout(RXTimeoutSyncWordTimer, NULL); + } + } + if (this->RadioEvents && this->RadioEvents->RxTimeout) + { + this->RadioEvents->RxTimeout(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + case RF_TX_RUNNING: + // Tx timeout shouldn't happen. + // But it has been observed that when it happens it is a result of a corrupted SPI transfer + // it depends on the platform design. + // + // The workaround is to put the radio in a known state. Thus, we re-initialize it. + // BEGIN WORKAROUND + + // Reset the radio + Reset( ); + + // Calibrate Rx chain + RxChainCalibration( ); + + // Initialize radio default values + SetOpMode( RF_OPMODE_SLEEP ); + + RadioRegistersInit( ); + + SetModem( MODEM_FSK ); + + // Restore previous network type setting. + SetPublicNetwork( this->settings.LoRa.PublicNetwork ); + // END WORKAROUND + + this->settings.State = RF_IDLE; + if (this->RadioEvents && this->RadioEvents->TxTimeout) + { + this->RadioEvents->TxTimeout(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + default: + break; + } +} + +void SX1276::OnDio0Irq( void ) +{ + volatile uint8_t irqFlags = 0; + + switch( this->settings.State ) + { + case RF_RX_RUNNING: + //TimerStop( &RxTimeoutTimer ); + // RxDone interrupt + switch( this->settings.Modem ) + { + case MODEM_FSK: + if( this->settings.Fsk.CrcOn == true ) + { + irqFlags = Read( REG_IRQFLAGS2 ); + if( ( irqFlags & RF_IRQFLAGS2_CRCOK ) != RF_IRQFLAGS2_CRCOK ) + { + // Clear Irqs + Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI | + RF_IRQFLAGS1_PREAMBLEDETECT | + RF_IRQFLAGS1_SYNCADDRESSMATCH ); + Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN ); + + SetTimeout(RXTimeoutTimer, NULL); + + if( this->settings.Fsk.RxContinuous == false ) + { + SetTimeout(RXTimeoutSyncWordTimer, NULL); + this->settings.State = RF_IDLE; + } + else + { + // Continuous mode restart Rx chain + Write( REG_RXCONFIG, Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + SetTimeout(RXTimeoutSyncWordTimer, &SX1276::OnTimeoutIrq, this->settings.Fsk.RxSingleTimeout * 1e3); + } + + if (this->RadioEvents && this->RadioEvents->RxError) + { + this->RadioEvents->RxError(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + this->settings.FskPacketHandler.PreambleDetected = false; + this->settings.FskPacketHandler.SyncWordDetected = false; + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = 0; + break; + } + } + + // Read received packet size + if( ( this->settings.FskPacketHandler.Size == 0 ) && ( this->settings.FskPacketHandler.NbBytes == 0 ) ) + { + if( this->settings.Fsk.FixLen == false ) + { + ReadFifo( ( uint8_t* )&this->settings.FskPacketHandler.Size, 1 ); + } + else + { + this->settings.FskPacketHandler.Size = Read( REG_PAYLOADLENGTH ); + } + ReadFifo( rxtxBuffer + this->settings.FskPacketHandler.NbBytes, this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + this->settings.FskPacketHandler.NbBytes += ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + } + else + { + ReadFifo( rxtxBuffer + this->settings.FskPacketHandler.NbBytes, this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + this->settings.FskPacketHandler.NbBytes += ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + } + + SetTimeout(RXTimeoutTimer, NULL); + + if( this->settings.Fsk.RxContinuous == false ) + { + this->settings.State = RF_IDLE; + SetTimeout(RXTimeoutSyncWordTimer, NULL); + } + else + { + // Continuous mode restart Rx chain + Write( REG_RXCONFIG, Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + SetTimeout(RXTimeoutSyncWordTimer, &SX1276::OnTimeoutIrq, this->settings.Fsk.RxSingleTimeout * 1e3); + } + + if (this->RadioEvents && this->RadioEvents->RxDone) + { + this->RadioEvents->RxDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, rxtxBuffer, this->settings.FskPacketHandler.Size, this->settings.FskPacketHandler.RssiValue, 0 ); + } + this->settings.FskPacketHandler.PreambleDetected = false; + this->settings.FskPacketHandler.SyncWordDetected = false; + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = 0; + break; + case MODEM_LORA: + { + int8_t snr = 0; + + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE ); + + irqFlags = Read( REG_LR_IRQFLAGS ); + if( ( irqFlags & RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK ) == RFLR_IRQFLAGS_PAYLOADCRCERROR ) + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_PAYLOADCRCERROR ); + + if( this->settings.LoRa.RxContinuous == false ) + { + this->settings.State = RF_IDLE; + } + SetTimeout(RXTimeoutTimer, NULL); + + if(this->RadioEvents && this->RadioEvents->RxError) + { + this->RadioEvents->RxError(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + } + + this->settings.LoRaPacketHandler.SnrValue = Read( REG_LR_PKTSNRVALUE ); + if( this->settings.LoRaPacketHandler.SnrValue & 0x80 ) // The SNR sign bit is 1 + { + // Invert and divide by 4 + snr = ( ( ~this->settings.LoRaPacketHandler.SnrValue + 1 ) & 0xFF ) >> 2; + snr = -snr; + } + else + { + // Divide by 4 + snr = ( this->settings.LoRaPacketHandler.SnrValue & 0xFF ) >> 2; + } + + int16_t rssi = Read( REG_LR_PKTRSSIVALUE ); + if( snr < 0 ) + { + if( this->settings.Channel > RF_MID_BAND_THRESH ) + { + this->settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ) + + snr; + } + else + { + this->settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ) + + snr; + } + } + else + { + if( this->settings.Channel > RF_MID_BAND_THRESH ) + { + this->settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ); + } + else + { + this->settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ); + } + } + + this->settings.LoRaPacketHandler.Size = Read( REG_LR_RXNBBYTES ); + ReadFifo( rxtxBuffer, this->settings.LoRaPacketHandler.Size ); + + if( this->settings.LoRa.RxContinuous == false ) + { + this->settings.State = RF_IDLE; + } + SetTimeout(RXTimeoutTimer, NULL); + + if(this->RadioEvents && this->RadioEvents->RxDone) + { + this->RadioEvents->RxDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, rxtxBuffer, this->settings.LoRaPacketHandler.Size, this->settings.LoRaPacketHandler.RssiValue, this->settings.LoRaPacketHandler.SnrValue ); + } + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + SetTimeout(TXTimeoutTimer, NULL); + // TxDone interrupt + switch( this->settings.Modem ) + { + case MODEM_LORA: + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_TXDONE ); + // Intentional fall through + case MODEM_FSK: + default: + this->settings.State = RF_IDLE; + if (this->RadioEvents && this->RadioEvents->TxDone) + { + this->RadioEvents->TxDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + } + break; + default: + break; + } +} + +void SX1276::OnDio1Irq( void ) +{ + switch( this->settings.State ) + { + case RF_RX_RUNNING: + switch( this->settings.Modem ) + { + case MODEM_FSK: + // FifoLevel interrupt + // Read received packet size + if( ( this->settings.FskPacketHandler.Size == 0 ) && ( this->settings.FskPacketHandler.NbBytes == 0 ) ) + { + if( this->settings.Fsk.FixLen == false ) + { + ReadFifo( ( uint8_t* )&this->settings.FskPacketHandler.Size, 1 ); + } + else + { + this->settings.FskPacketHandler.Size = Read( REG_PAYLOADLENGTH ); + } + } + + if( ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ) > this->settings.FskPacketHandler.FifoThresh ) + { + ReadFifo( ( rxtxBuffer + this->settings.FskPacketHandler.NbBytes ), this->settings.FskPacketHandler.FifoThresh ); + this->settings.FskPacketHandler.NbBytes += this->settings.FskPacketHandler.FifoThresh; + } + else + { + ReadFifo( ( rxtxBuffer + this->settings.FskPacketHandler.NbBytes ), this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + this->settings.FskPacketHandler.NbBytes += ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + } + break; + case MODEM_LORA: + // Sync time out + SetTimeout(RXTimeoutTimer, NULL); + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXTIMEOUT ); + + this->settings.State = RF_IDLE; + if (this->RadioEvents && this->RadioEvents->RxTimeout) + { + this->RadioEvents->RxTimeout(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + switch( this->settings.Modem ) + { + case MODEM_FSK: + // FifoEmpty interrupt + if( ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ) > this->settings.FskPacketHandler.ChunkSize ) + { + WriteFifo( ( rxtxBuffer + this->settings.FskPacketHandler.NbBytes ), this->settings.FskPacketHandler.ChunkSize ); + this->settings.FskPacketHandler.NbBytes += this->settings.FskPacketHandler.ChunkSize; + } + else + { + // Write the last chunk of data + WriteFifo( rxtxBuffer + this->settings.FskPacketHandler.NbBytes, this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + this->settings.FskPacketHandler.NbBytes += this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes; + } + break; + case MODEM_LORA: + break; + default: + break; + } + break; + default: + break; + } +} + +void SX1276::OnDio2Irq( void ) +{ + switch( this->settings.State ) + { + case RF_RX_RUNNING: + switch( this->settings.Modem ) + { + case MODEM_FSK: + // Checks if DIO4 is connected. If it is not PreambleDtected is set to true. + if( this->dioIrq[4] == NULL ) + { + this->settings.FskPacketHandler.PreambleDetected = true; + } + + if( ( this->settings.FskPacketHandler.PreambleDetected == true ) && ( this->settings.FskPacketHandler.SyncWordDetected == false ) ) + { + SetTimeout(RXTimeoutSyncWordTimer, NULL); + + this->settings.FskPacketHandler.SyncWordDetected = true; + + this->settings.FskPacketHandler.RssiValue = -( Read( REG_RSSIVALUE ) >> 1 ); + + this->settings.FskPacketHandler.AfcValue = ( int32_t )( double )( ( ( uint16_t )Read( REG_AFCMSB ) << 8 ) | + ( uint16_t )Read( REG_AFCLSB ) ) * + ( double )FREQ_STEP; + this->settings.FskPacketHandler.RxGain = ( Read( REG_LNA ) >> 5 ) & 0x07; + } + break; + case MODEM_LORA: + if( this->settings.LoRa.FreqHopOn == true ) + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL ); + + if (this->RadioEvents && this->RadioEvents->FhssChangeChannel) + { + this->RadioEvents->FhssChangeChannel(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, ( Read( REG_LR_HOPCHANNEL ) & RFLR_HOPCHANNEL_CHANNEL_MASK ) ); + } + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + switch( this->settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if( this->settings.LoRa.FreqHopOn == true ) + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL ); + + if (this->RadioEvents && this->RadioEvents->FhssChangeChannel) + { + this->RadioEvents->FhssChangeChannel(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, ( Read( REG_LR_HOPCHANNEL ) & RFLR_HOPCHANNEL_CHANNEL_MASK ) ); + } + } + break; + default: + break; + } + break; + default: + break; + } +} + +void SX1276::OnDio3Irq( void ) +{ + switch( this->settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if( ( Read( REG_LR_IRQFLAGS ) & RFLR_IRQFLAGS_CADDETECTED ) == RFLR_IRQFLAGS_CADDETECTED ) + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDETECTED | RFLR_IRQFLAGS_CADDONE ); + if (this->RadioEvents && this->RadioEvents->CadDone) + { + this->RadioEvents->CadDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, true ); + } + } + else + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDONE ); + if (this->RadioEvents && this->RadioEvents->CadDone) + { + this->RadioEvents->CadDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, false ); + } + } + break; + default: + break; + } +} + +void SX1276::OnDio4Irq( void ) +{ + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + if( this->settings.FskPacketHandler.PreambleDetected == false ) + { + this->settings.FskPacketHandler.PreambleDetected = true; + } + } + break; + case MODEM_LORA: + break; + default: + break; + } +} + +void SX1276::OnDio5Irq( void ) +{ + switch( this->settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + break; + default: + break; + } +}