Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
624 lines (575 sloc)
19.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright (c) 2016 Gordon Williams. See the file LICENSE for copying permission. */ | |
// SX127x ONLY IN LORA MODE | |
// roughly based on semtech's example code: https://developer.mbed.org/teams/Semtech/code/SX1276Lib/file/7f3aab69cca9/sx1276 | |
var REG = { | |
FIFO : 0x00, | |
OPMODE : 0x01, | |
FRFMSB : 0x06, | |
FRFMID : 0x07, | |
FRFLSB : 0x08, | |
PACONFIG : 0x09, | |
PARAMP : 0x0A, | |
OCP : 0x0B, | |
LNA : 0x0C, | |
FIFOADDRPTR : 0x0D, | |
FIFOTXBASEADDR : 0x0E, | |
FIFORXBASEADDR : 0x0F, | |
FIFORXCURRENTADDR : 0x10, | |
IRQFLAGSMASK : 0x11, | |
IRQFLAGS : 0x12, | |
RXNBBYTES : 0x13, | |
RXHEADERCNTVALUEMSB : 0x14, | |
RXHEADERCNTVALUELSB : 0x15, | |
RXPACKETCNTVALUEMSB : 0x16, | |
RXPACKETCNTVALUELSB : 0x17, | |
MODEMSTAT : 0x18, | |
PKTSNRVALUE : 0x19, | |
PKTRSSIVALUE : 0x1A, | |
RSSIVALUE : 0x1B, | |
HOPCHANNEL : 0x1C, | |
MODEMCONFIG1 : 0x1D, | |
MODEMCONFIG2 : 0x1E, | |
SYMBTIMEOUTLSB : 0x1F, | |
PREAMBLEMSB : 0x20, | |
PREAMBLELSB : 0x21, | |
PAYLOADLENGTH : 0x22, | |
PAYLOADMAXLENGTH : 0x23, | |
HOPPERIOD : 0x24, | |
FIFORXBYTEADDR : 0x25, | |
MODEMCONFIG3 : 0x26, | |
FEIMSB : 0x28, | |
FEIMID : 0x29, | |
FEILSB : 0x2A, | |
RSSIWIDEBAND : 0x2C, | |
TEST2F : 0x2F, | |
TEST30 : 0x30, | |
DETECTOPTIMIZE : 0x31, | |
INVERTIQ : 0x33, | |
TEST36 : 0x36, | |
DETECTIONTHRESHOLD : 0x37, | |
SYNCWORD : 0x39, | |
TEST3A : 0x3A, | |
INVERTIQ2 : 0x3B, | |
DIOMAPPING1 : 0x40, | |
DIOMAPPING2 : 0x41, | |
VERSION : 0x42, | |
PLLHOP : 0x44, | |
TCXO : 0x4B, | |
PADAC : 0x4D, | |
FORMERTEMP : 0x5B, | |
BITRATEFRAC : 0x5D, | |
AGCREF : 0x61, | |
AGCTHRESH1 : 0x62, | |
AGCTHRESH2 : 0x63, | |
AGCTHRESH3 : 0x64, | |
PLL : 0x70 | |
}; | |
var RF = { | |
OPMODE_LONGRANGEMODE_MASK : 0x7F, | |
OPMODE_LONGRANGEMODE_OFF : 0x00, // Default | |
OPMODE_LONGRANGEMODE_ON : 0x80, | |
OPMODE_ACCESSSHAREDREG_MASK : 0xBF, | |
OPMODE_ACCESSSHAREDREG_ENABLE : 0x40, | |
OPMODE_ACCESSSHAREDREG_DISABLE : 0x00, // Default | |
OPMODE_FREQMODE_ACCESS_MASK : 0xF7, | |
OPMODE_FREQMODE_ACCESS_LF : 0x08, // Default | |
OPMODE_FREQMODE_ACCESS_HF : 0x00, | |
OPMODE_MASK : 0xF8, | |
OPMODE_SLEEP : 0x00, | |
OPMODE_STANDBY : 0x01, // Default | |
OPMODE_SYNTHESIZER_TX : 0x02, | |
OPMODE_TRANSMITTER : 0x03, | |
OPMODE_SYNTHESIZER_RX : 0x04, | |
OPMODE_RECEIVER : 0x05, | |
OPMODE_RECEIVER_SINGLE : 0x06, | |
OPMODE_CAD : 0x07, | |
FRFMSB_434_MHZ : 0x6C, // Default | |
FRFMID_434_MHZ : 0x80, // Default | |
FRFLSB_434_MHZ : 0x00, // Default | |
PACONFIG_PASELECT_MASK : 0x7F, | |
PACONFIG_PASELECT_PABOOST : 0x80, | |
PACONFIG_PASELECT_RFO : 0x00, // Default | |
PACONFIG_MAX_POWER_MASK : 0x8F, | |
PACONFIG_OUTPUTPOWER_MASK : 0xF0, | |
PARAMP_TXBANDFORCE_MASK : 0xEF, | |
PARAMP_TXBANDFORCE_BAND_SEL : 0x10, | |
PARAMP_TXBANDFORCE_AUTO : 0x00, // Default | |
PARAMP_MASK : 0xF0, | |
PARAMP_3400_US : 0x00, | |
PARAMP_2000_US : 0x01, | |
PARAMP_1000_US : 0x02, | |
PARAMP_0500_US : 0x03, | |
PARAMP_0250_US : 0x04, | |
PARAMP_0125_US : 0x05, | |
PARAMP_0100_US : 0x06, | |
PARAMP_0062_US : 0x07, | |
PARAMP_0050_US : 0x08, | |
PARAMP_0040_US : 0x09, // Default | |
PARAMP_0031_US : 0x0A, | |
PARAMP_0025_US : 0x0B, | |
PARAMP_0020_US : 0x0C, | |
PARAMP_0015_US : 0x0D, | |
PARAMP_0012_US : 0x0E, | |
PARAMP_0010_US : 0x0F, | |
OCP_MASK : 0xDF, | |
OCP_ON : 0x20, // Default | |
OCP_OFF : 0x00, | |
OCP_TRIM_MASK : 0xE0, | |
OCP_TRIM_045_MA : 0x00, | |
OCP_TRIM_050_MA : 0x01, | |
OCP_TRIM_055_MA : 0x02, | |
OCP_TRIM_060_MA : 0x03, | |
OCP_TRIM_065_MA : 0x04, | |
OCP_TRIM_070_MA : 0x05, | |
OCP_TRIM_075_MA : 0x06, | |
OCP_TRIM_080_MA : 0x07, | |
OCP_TRIM_085_MA : 0x08, | |
OCP_TRIM_090_MA : 0x09, | |
OCP_TRIM_095_MA : 0x0A, | |
OCP_TRIM_100_MA : 0x0B, // Default | |
OCP_TRIM_105_MA : 0x0C, | |
OCP_TRIM_110_MA : 0x0D, | |
OCP_TRIM_115_MA : 0x0E, | |
OCP_TRIM_120_MA : 0x0F, | |
OCP_TRIM_130_MA : 0x10, | |
OCP_TRIM_140_MA : 0x11, | |
OCP_TRIM_150_MA : 0x12, | |
OCP_TRIM_160_MA : 0x13, | |
OCP_TRIM_170_MA : 0x14, | |
OCP_TRIM_180_MA : 0x15, | |
OCP_TRIM_190_MA : 0x16, | |
OCP_TRIM_200_MA : 0x17, | |
OCP_TRIM_210_MA : 0x18, | |
OCP_TRIM_220_MA : 0x19, | |
OCP_TRIM_230_MA : 0x1A, | |
OCP_TRIM_240_MA : 0x1B, | |
LNA_GAIN_MASK : 0x1F, | |
LNA_GAIN_G1 : 0x20, // Default | |
LNA_GAIN_G2 : 0x40, | |
LNA_GAIN_G3 : 0x60, | |
LNA_GAIN_G4 : 0x80, | |
LNA_GAIN_G5 : 0xA0, | |
LNA_GAIN_G6 : 0xC0, | |
LNA_BOOST_LF_MASK : 0xE7, | |
LNA_BOOST_LF_DEFAULT : 0x00, // Default | |
LNA_BOOST_HF_MASK : 0xFC, | |
LNA_BOOST_HF_OFF : 0x00, // Default | |
LNA_BOOST_HF_ON : 0x03, | |
FIFOADDRPTR : 0x00, // Default | |
FIFOTXBASEADDR : 0x80, // Default | |
FIFORXBASEADDR : 0x00, // Default | |
IRQFLAGS_RXTIMEOUT_MASK : 0x80, | |
IRQFLAGS_RXDONE_MASK : 0x40, | |
IRQFLAGS_PAYLOADCRCERROR_MASK : 0x20, | |
IRQFLAGS_VALIDHEADER_MASK : 0x10, | |
IRQFLAGS_TXDONE_MASK : 0x08, | |
IRQFLAGS_CADDONE_MASK : 0x04, | |
IRQFLAGS_FHSSCHANGEDCHANNEL_MASK : 0x02, | |
IRQFLAGS_CADDETECTED_MASK : 0x01, | |
IRQFLAGS_RXTIMEOUT : 0x80, | |
IRQFLAGS_RXDONE : 0x40, | |
IRQFLAGS_PAYLOADCRCERROR : 0x20, | |
IRQFLAGS_VALIDHEADER : 0x10, | |
IRQFLAGS_TXDONE : 0x08, | |
IRQFLAGS_CADDONE : 0x04, | |
IRQFLAGS_FHSSCHANGEDCHANNEL : 0x02, | |
IRQFLAGS_CADDETECTED : 0x01, | |
MODEMSTAT_RX_CR_MASK : 0x1F, | |
MODEMSTAT_MODEM_STATUS_MASK : 0xE0, | |
HOPCHANNEL_PLL_LOCK_TIMEOUT_MASK : 0x7F, | |
HOPCHANNEL_PLL_LOCK_FAIL : 0x80, | |
HOPCHANNEL_PLL_LOCK_SUCCEED : 0x00, // Default | |
HOPCHANNEL_CRCONPAYLOAD_MASK : 0xBF, | |
HOPCHANNEL_CRCONPAYLOAD_ON : 0x40, | |
HOPCHANNEL_CRCONPAYLOAD_OFF : 0x00, // Default | |
HOPCHANNEL_CHANNEL_MASK : 0x3F, | |
MODEMCONFIG1_BW_MASK : 0x0F, | |
MODEMCONFIG1_BW_7_81_KHZ : 0x00, | |
MODEMCONFIG1_BW_10_41_KHZ : 0x10, | |
MODEMCONFIG1_BW_15_62_KHZ : 0x20, | |
MODEMCONFIG1_BW_20_83_KHZ : 0x30, | |
MODEMCONFIG1_BW_31_25_KHZ : 0x40, | |
MODEMCONFIG1_BW_41_66_KHZ : 0x50, | |
MODEMCONFIG1_BW_62_50_KHZ : 0x60, | |
MODEMCONFIG1_BW_125_KHZ : 0x70, // Default | |
MODEMCONFIG1_BW_250_KHZ : 0x80, | |
MODEMCONFIG1_BW_500_KHZ : 0x90, | |
MODEMCONFIG1_CODINGRATE_MASK : 0xF1, | |
MODEMCONFIG1_CODINGRATE_4_5 : 0x02, | |
MODEMCONFIG1_CODINGRATE_4_6 : 0x04, // Default | |
MODEMCONFIG1_CODINGRATE_4_7 : 0x06, | |
MODEMCONFIG1_CODINGRATE_4_8 : 0x08, | |
MODEMCONFIG1_IMPLICITHEADER_MASK : 0xFE, | |
MODEMCONFIG1_IMPLICITHEADER_ON : 0x01, | |
MODEMCONFIG1_IMPLICITHEADER_OFF : 0x00, // Default | |
MODEMCONFIG2_SF_MASK : 0x0F, | |
MODEMCONFIG2_SF_6 : 0x60, | |
MODEMCONFIG2_SF_7 : 0x70, // Default | |
MODEMCONFIG2_SF_8 : 0x80, | |
MODEMCONFIG2_SF_9 : 0x90, | |
MODEMCONFIG2_SF_10 : 0xA0, | |
MODEMCONFIG2_SF_11 : 0xB0, | |
MODEMCONFIG2_SF_12 : 0xC0, | |
MODEMCONFIG2_TXCONTINUOUSMODE_MASK : 0xF7, | |
MODEMCONFIG2_TXCONTINUOUSMODE_ON : 0x08, | |
MODEMCONFIG2_TXCONTINUOUSMODE_OFF : 0x00, | |
MODEMCONFIG2_RXPAYLOADCRC_MASK : 0xFB, | |
MODEMCONFIG2_RXPAYLOADCRC_ON : 0x04, | |
MODEMCONFIG2_RXPAYLOADCRC_OFF : 0x00, // Default | |
MODEMCONFIG2_SYMBTIMEOUTMSB_MASK : 0xFC, | |
MODEMCONFIG2_SYMBTIMEOUTMSB : 0x00, // Default | |
SYMBTIMEOUTLSB_SYMBTIMEOUT : 0x64, // Default | |
PREAMBLELENGTHMSB : 0x00, // Default | |
PREAMBLELENGTHLSB : 0x08, // Default | |
PAYLOADLENGTH : 0x0E, // Default | |
PAYLOADMAXLENGTH : 0xFF, // Default | |
HOPPERIOD_FREQFOPPINGPERIOD : 0x00, // Default | |
MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK : 0xF7, | |
MODEMCONFIG3_LOWDATARATEOPTIMIZE_ON : 0x08, | |
MODEMCONFIG3_LOWDATARATEOPTIMIZE_OFF : 0x00, // Default | |
MODEMCONFIG3_AGCAUTO_MASK : 0xFB, | |
MODEMCONFIG3_AGCAUTO_ON : 0x04, // Default | |
MODEMCONFIG3_AGCAUTO_OFF : 0x00, | |
DETECTIONOPTIMIZE_MASK : 0xF8, | |
DETECTIONOPTIMIZE_SF7_TO_SF12 : 0x03, // Default | |
DETECTIONOPTIMIZE_SF6 : 0x05, | |
INVERTIQ_RX_MASK : 0xBF, | |
INVERTIQ_RX_OFF : 0x00, | |
INVERTIQ_RX_ON : 0x40, | |
INVERTIQ_TX_MASK : 0xFE, | |
INVERTIQ_TX_OFF : 0x01, | |
INVERTIQ_TX_ON : 0x00, | |
DETECTIONTHRESH_SF7_TO_SF12 : 0x0A, // Default | |
DETECTIONTHRESH_SF6 : 0x0C, | |
INVERTIQ2_ON : 0x19, | |
INVERTIQ2_OFF : 0x1D, | |
DIOMAPPING1_DIO0_MASK : 0x3F, | |
DIOMAPPING1_DIO0_00 : 0x00, // Default | |
DIOMAPPING1_DIO0_01 : 0x40, | |
DIOMAPPING1_DIO0_10 : 0x80, | |
DIOMAPPING1_DIO0_11 : 0xC0, | |
DIOMAPPING1_DIO1_MASK : 0xCF, | |
DIOMAPPING1_DIO1_00 : 0x00, // Default | |
DIOMAPPING1_DIO1_01 : 0x10, | |
DIOMAPPING1_DIO1_10 : 0x20, | |
DIOMAPPING1_DIO1_11 : 0x30, | |
DIOMAPPING1_DIO2_MASK : 0xF3, | |
DIOMAPPING1_DIO2_00 : 0x00, // Default | |
DIOMAPPING1_DIO2_01 : 0x04, | |
DIOMAPPING1_DIO2_10 : 0x08, | |
DIOMAPPING1_DIO2_11 : 0x0C, | |
DIOMAPPING1_DIO3_MASK : 0xFC, | |
DIOMAPPING1_DIO3_00 : 0x00, // Default | |
DIOMAPPING1_DIO3_01 : 0x01, | |
DIOMAPPING1_DIO3_10 : 0x02, | |
DIOMAPPING1_DIO3_11 : 0x03, | |
DIOMAPPING2_DIO4_MASK : 0x3F, | |
DIOMAPPING2_DIO4_00 : 0x00, // Default | |
DIOMAPPING2_DIO4_01 : 0x40, | |
DIOMAPPING2_DIO4_10 : 0x80, | |
DIOMAPPING2_DIO4_11 : 0xC0, | |
DIOMAPPING2_DIO5_MASK : 0xCF, | |
DIOMAPPING2_DIO5_00 : 0x00, // Default | |
DIOMAPPING2_DIO5_01 : 0x10, | |
DIOMAPPING2_DIO5_10 : 0x20, | |
DIOMAPPING2_DIO5_11 : 0x30, | |
DIOMAPPING2_MAP_MASK : 0xFE, | |
DIOMAPPING2_MAP_PREAMBLEDETECT : 0x01, | |
DIOMAPPING2_MAP_RSSI : 0x00, // Default | |
PLLHOP_FASTHOP_MASK : 0x7F, | |
PLLHOP_FASTHOP_ON : 0x80, | |
PLLHOP_FASTHOP_OFF : 0x00, // Default | |
TCXO_TCXOINPUT_MASK : 0xEF, | |
TCXO_TCXOINPUT_ON : 0x10, | |
TCXO_TCXOINPUT_OFF : 0x00, // Default | |
PADAC_20DBM_MASK : 0xF8, | |
PADAC_20DBM_ON : 0x07, | |
PADAC_20DBM_OFF : 0x04, // Default | |
BITRATEFRAC_MASK : 0xF0, | |
PLL_BANDWIDTH_MASK : 0x3F, | |
PLL_BANDWIDTH_75 : 0x00, | |
PLL_BANDWIDTH_150 : 0x40, | |
PLL_BANDWIDTH_225 : 0x80, | |
PLL_BANDWIDTH_300 : 0xC0, // Default | |
}; | |
function SX(options) { | |
this.spi = options.spi; | |
this.cs = options.cs; | |
this.rst = options.rst; | |
if (this.rst) | |
digitalPulse(this.rst,0,1); // 1ms low | |
setTimeout(this.init.bind(this), 7); | |
this.options = {}; | |
this.state = ""; | |
if (options.callback) | |
setTimeout(options.callback, 10); | |
} | |
SX.prototype.w = function(a,v) { | |
if ("number"!=typeof a)throw"!"; | |
if ("number"==typeof v && isNaN(v))throw"!"; | |
this.spi.write(a|128,v, this.cs); | |
}; | |
SX.prototype.r = function(a, s) { | |
if ("number"!=typeof a)throw"!"; | |
if (s) { | |
var d = new Uint8Array(s+1); | |
d[0]=a&127; | |
d = this.spi.send(d, this.cs); | |
return new Uint8Array(d.buffer, 1); | |
} else { | |
// no size, just 1 | |
return this.spi.send([a&127,0], this.cs)[1]; | |
} | |
}; | |
SX.prototype.mask = function(a,mask,v) { | |
this.w( a, this.r(a) & mask | v); | |
}; | |
SX.prototype.setOpMode = function(v) { | |
this.mask( REG.OPMODE, RF.OPMODE_MASK, v ); | |
}; | |
SX.prototype.init = function() { | |
var v = this.r(REG.VERSION); | |
if (v===0 || v==255) throw new Error("Radio not found!"); | |
this.setOpMode(RF.OPMODE_SLEEP); | |
this.mask(REG.OPMODE, RF.OPMODE_LONGRANGEMODE_MASK, RF.OPMODE_LONGRANGEMODE_ON ); | |
this.w(REG.DIOMAPPING1, 0x00); | |
this.w(REG.DIOMAPPING2, 0x00); | |
this.w(REG.PAYLOADMAXLENGTH, 0x40); | |
}; | |
SX.prototype.rx = function(callback) { | |
this.rxCallback = callback; | |
if (this.options.iqInverted) { | |
this.mask(REG.INVERTIQ, RF.INVERTIQ_TX_MASK & RF.INVERTIQ_RX_MASK, | |
RF.INVERTIQ_RX_ON | RF.INVERTIQ_TX_OFF); | |
this.w(REG.INVERTIQ2, RF.INVERTIQ2_ON ); | |
} else { | |
this.mask(REG.INVERTIQ, RF.INVERTIQ_TX_MASK & RF.INVERTIQ_RX_MASK, | |
RF.INVERTIQ_RX_OFF | RF.INVERTIQ_TX_OFF ); | |
this.w(REG.INVERTIQ2, RF.INVERTIQ2_OFF ); | |
} | |
// TODO: ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal? | |
if (this.options.freqHopOn) { | |
this.w( REG.IRQFLAGSMASK, | |
RF.IRQFLAGS_VALIDHEADER | | |
RF.IRQFLAGS_TXDONE | | |
RF.IRQFLAGS_CADDONE | | |
RF.IRQFLAGS_CADDETECTED ); | |
// DIO0=RxDone, DIO2=FhssChangeChannel | |
this.mask(REG.DIOMAPPING1, RF.DIOMAPPING1_DIO0_MASK & RF.DIOMAPPING1_DIO2_MASK, | |
RF.DIOMAPPING1_DIO0_00 | RF.DIOMAPPING1_DIO2_00); | |
} else { | |
this.w( REG.IRQFLAGSMASK, | |
RF.IRQFLAGS_VALIDHEADER | | |
RF.IRQFLAGS_TXDONE | | |
RF.IRQFLAGS_CADDONE | | |
RF.IRQFLAGS_FHSSCHANGEDCHANNEL | | |
RF.IRQFLAGS_CADDETECTED ); | |
// DIO0=RxDone | |
this.mask(REG.DIOMAPPING1, RF.DIOMAPPING1_DIO0_MASK, RF.DIOMAPPING1_DIO0_00 ); | |
} | |
this.w( REG.FIFORXBASEADDR, 0 ); | |
this.w( REG.FIFOADDRPTR, 0 ); | |
this.state = "RX_RUNNING"; | |
this.setOpMode( this.options.rxContinuous ? RF.OPMODE_RECEIVER : RF.OPMODE_RECEIVER_SINGLE ); | |
}; | |
SX.prototype.tx = function() { | |
if (this.options.freqHopOn) { | |
this.w(REG.IRQFLAGSMASK, RF.IRQFLAGS_RXTIMEOUT | | |
RF.IRQFLAGS_RXDONE | | |
RF.IRQFLAGS_PAYLOADCRCERROR | | |
RF.IRQFLAGS_VALIDHEADER | | |
RF.IRQFLAGS_CADDONE | | |
RF.IRQFLAGS_CADDETECTED ); | |
// DIO0=TxDone, DIO2=FhssChangeChannel | |
this.mask(REG.DIOMAPPING1, | |
RF.DIOMAPPING1_DIO0_MASK & RF.DIOMAPPING1_DIO2_MASK, | |
RF.DIOMAPPING1_DIO0_01 | RF.DIOMAPPING1_DIO2_00 ); | |
} else { | |
this.w(REG.IRQFLAGSMASK, RF.IRQFLAGS_RXTIMEOUT | | |
RF.IRQFLAGS_RXDONE | | |
RF.IRQFLAGS_PAYLOADCRCERROR | | |
RF.IRQFLAGS_VALIDHEADER | | |
RF.IRQFLAGS_CADDONE | | |
RF.IRQFLAGS_FHSSCHANGEDCHANNEL | | |
RF.IRQFLAGS_CADDETECTED ); | |
// DIO0=TxDone | |
this.mask(REG.DIOMAPPING1, RF.DIOMAPPING1_DIO0_MASK, RF.DIOMAPPING1_DIO0_01); | |
} | |
this.state = "TX_RUNNING"; | |
this.setOpMode(RF.OPMODE_TRANSMITTER); | |
}; | |
SX.prototype.sleep = function() { | |
this.setOpMode( RF.OPMODE_SLEEP ); | |
this.state = "IDLE"; | |
}; | |
SX.prototype.standby = function() { | |
this.setOpMode( RF.OPMODE_STANDBY ); | |
this.state = "IDLE"; | |
}; | |
SX.prototype.send = function(data, callback) { | |
this.txCallback = callback; | |
if(this.options.iqInverted) { | |
this.mask(REG.INVERTIQ, RF.INVERTIQ_TX_MASK & RF.INVERTIQ_RX_MASK, | |
RF.INVERTIQ_RX_OFF | RF.INVERTIQ_TX_ON); | |
this.w(REG.INVERTIQ2, RF.INVERTIQ2_ON ); | |
} else { | |
this.mask(REG.INVERTIQ, RF.INVERTIQ_TX_MASK & RF.INVERTIQ_RX_MASK, | |
RF.INVERTIQ_RX_OFF | RF.INVERTIQ_TX_OFF ); | |
this.w(REG.INVERTIQ2, RF.INVERTIQ2_OFF ); | |
} | |
// Initializes the payload size | |
this.w( REG.PAYLOADLENGTH, data.length ); | |
// Full buffer used for Tx | |
this.w( REG.FIFOTXBASEADDR, 0 ); | |
this.w( REG.FIFOADDRPTR, 0 ); | |
var s = (function () { | |
this.w(REG.FIFO, data); | |
this.tx(); | |
}).bind(this); | |
// FIFO operations can not take place in Sleep mode | |
if((this.r(REG.OPMODE) & ~RF.OPMODE_MASK) == RF.OPMODE_SLEEP) { | |
this.standby(); | |
setTimeout(s, 1); | |
} else { | |
s(); | |
} | |
}; | |
SX.prototype.getStatus = function() { | |
var f = this.r(REG.IRQFLAGS); | |
return { | |
rxDone : !!(f&RF.IRQFLAGS_RXDONE), | |
rxTimeout : !!(f&RF.IRQFLAGS_RXTIMEOUT), | |
crcError : !!(f&RF.IRQFLAGS_PAYLOADCRCERROR), | |
validHeader : !!(f&RF.IRQFLAGS_VALIDHEADER), | |
txDone : !!(f&RF.IRQFLAGS_TXDONE), | |
cadDone : !!(f&RF.IRQFLAGS_CADDONE), | |
changedChannel : !!(f&RF.IRQFLAGS_FHSSCHANGEDCHANNEL), | |
cadDetected : !!(f&RF.IRQFLAGS_CADDETECTED), | |
}; | |
}; | |
SX.prototype.onIRQ = function() { | |
var irqFlags = this.r(REG.IRQFLAGS); | |
if (irqFlags&RF.IRQFLAGS_RXDONE) { | |
this.w( REG.IRQFLAGS, RF.IRQFLAGS_RXDONE ); // clear | |
if (irqFlags & RF.IRQFLAGS_PAYLOADCRCERROR) { | |
this.w( REG.IRQFLAGS, RF.IRQFLAGS_PAYLOADCRCERROR ); // clear | |
if (!this.options.rxContinuous) | |
this.state = "IDLE"; | |
if (this.rxCallback) this.rxCallback("RxError"); | |
return; | |
} | |
var inf = {}; | |
inf.snr = this.r( REG.PKTSNRVALUE ); | |
inf.rssi = this.r( REG.PKTRSSIVALUE ); | |
// TODO: some fiddling of snr/rssi | |
var nBytes = this.r( REG.RXNBBYTES ); | |
inf.data = this.r( REG.FIFO, nBytes); | |
if (!this.options.rxContinuous) | |
this.state = "IDLE"; | |
if (this.rxCallback) | |
this.rxCallback(null, inf); | |
} | |
if (irqFlags&RF.IRQFLAGS_TXDONE) { | |
this.w( REG.IRQFLAGS, RF.IRQFLAGS_TXDONE ); // clear | |
if (this.txCallback) this.txCallback(); | |
} | |
}; | |
SX.prototype.commonSetConfig = function(options) { | |
options.freq = options.freq||868000000; | |
var f = 0|(options.freq / 61.03515625 ); | |
this.w( REG.FRFMSB, ( f >> 16 ) & 0xFF ); | |
this.w( REG.FRFMID, ( f >> 8 ) & 0xFF ); | |
this.w( REG.FRFLSB, f & 0xFF ); | |
options.power = options.power||14;//dbM | |
options.bandwidth = 0|options.bandwidth;//[0: 125 kHz, 1: 250 kHz, 2: 500 kHz] | |
if( options.bandwidth > 2 ) | |
throw new Error("When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported"); | |
options.bandwidth += 7; | |
options.datarate = options.datarate||7; // spreading factor, 7..12 | |
options.datarate = E.clip(options.datarate,6,12); | |
if(((options.bandwidth == 7 ) && | |
((options.datarate == 11 ) || (options.datarate == 12 ))) || | |
((options.bandwidth == 8 ) && ( options.datarate == 12 ))) { | |
this.LowDatarateOptimize = 0x01; | |
} else { | |
this.LowDatarateOptimize = 0x00; | |
} | |
options.coderate = options.coderate||1; // [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] | |
options.preambleLen = options.preambleLen||8; | |
options.fixLen = 0|options.fixLen; // fixed length payload? 0/1 | |
options.crcOn = 0|options.crcOn; // CRC on? 0/1 | |
options.freqHopOn = 0|options.freqHopOn; // frequency hopping on? 0/1 | |
options.iqInverted = 0|options.iqInverted; // 0/1 | |
options.rxContinuous = 0|options.rxContinuous; // continuous reception? | |
options.symbTimeout = options.symbTimeout||5; // symbol timeout when rxContinuous = true | |
for (var i in options) | |
this.options[i]=options[i]; | |
if(this.freqHopOn) { | |
options.hopPeriod = options.hopPeriod||4; | |
this.mask( REG.PLLHOP, RF.PLLHOP_FASTHOP_MASK, RF.PLLHOP_FASTHOP_ON ); | |
this.w( REG.HOPPERIOD, options.hopPeriod ); | |
} | |
this.mask( REG.MODEMCONFIG1, | |
RF.MODEMCONFIG1_BW_MASK & | |
RF.MODEMCONFIG1_CODINGRATE_MASK & | |
RF.MODEMCONFIG1_IMPLICITHEADER_MASK, | |
( options.bandwidth << 4 ) | ( options.coderate << 1 ) | options.fixLen ); | |
this.mask(REG.MODEMCONFIG3, RF.MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK, | |
( this.LowDatarateOptimize << 3 )); | |
this.mask( REG.DETECTOPTIMIZE, | |
RF.DETECTIONOPTIMIZE_MASK, | |
(options.datarate == 6) ? RF.DETECTIONOPTIMIZE_SF6 : RF.DETECTIONOPTIMIZE_SF7_TO_SF12); | |
this.w( REG.DETECTIONTHRESHOLD, (options.datarate == 6) ? RF.DETECTIONTHRESH_SF6 : RF.DETECTIONTHRESH_SF7_TO_SF12 ); | |
return options; | |
}; | |
SX.prototype.setRxConfig = function( options ) { | |
options = this.commonSetConfig(options); | |
this.mask( REG.MODEMCONFIG2, | |
RF.MODEMCONFIG2_SF_MASK & | |
RF.MODEMCONFIG2_RXPAYLOADCRC_MASK & | |
RF.MODEMCONFIG2_SYMBTIMEOUTMSB_MASK, | |
( options.datarate << 4 ) | ( options.crcOn << 2 ) | | |
( ( options.symbTimeout >> 8 ) & ~RF.MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) ); | |
this.w( REG.SYMBTIMEOUTLSB, options.symbTimeout & 0xFF ); | |
this.w( REG.PREAMBLEMSB, ( options.preambleLen >> 8 ) & 0xFF ); | |
this.w( REG.PREAMBLELSB, options.preambleLen & 0xFF ); | |
if (options.fixLen) | |
this.w( REG.PAYLOADLENGTH, options.payloadLen ); | |
// TODO: erratas | |
}; | |
SX.prototype.setTxConfig = function( options ) { | |
options = this.commonSetConfig(options); | |
let pwr = options.power; | |
let usePaBoost = (pwr > 15) || options.forcePaBoost; | |
// high power is 20dB, some special settings required | |
let highPower = pwr == 20; | |
let paDac = highPower ? 0x87 : 0x84; | |
// default over current value is 100mA, should be good up to +17dBm output power as that requires 90mA. | |
// +20dBm requires 120mA, set the over current limit a bit higher to 140mA | |
let ocpVal = highPower ? RF.OCP_TRIM_140_MA: RF.OCP_TRIM_100_MA; | |
var paConfig = 0; | |
if(usePaBoost){ | |
if(!((pwr >= 2 && pwr <= 17) || pwr ==20)){ throw "SX127x-PA_BOOST_PWR_ERR"; } | |
paConfig = RF.PACONFIG_PASELECT_PABOOST | (highPower ? pwr - 5 : pwr - 2); | |
} else { | |
if(pwr < -4) { throw "SX127x-PWR_TOO_LOW"} | |
// Setting output power is a bit contrived if we want go below 0dBm: | |
// The effective output power is Pout=Pmax-(15-OutputPower), | |
// but Pmax can be set in 0.6dBm steps: Pmax=10.8+0.6*MaxPower | |
// If the desired output power is lower than 0dBm, set MaxPower to 0b000 -> 10.8dBm | |
// to get the desired power, add 4 to the desired value | |
// If output power is over 0dBm, set MaxPower to 0b111 -> 15dBm, | |
// to get the desired power, just binary or the | |
paConfig = (pwr < 0) ? 0b00000000 | (pwr+4) : 0b01110000 | (pwr); | |
} | |
this.w( REG.PACONFIG, paConfig ); | |
this.w( REG.PADAC, paDac ); | |
this.w(REG.OCP, ocpVal); | |
this.mask(REG.MODEMCONFIG2, RF.MODEMCONFIG2_SF_MASK & RF.MODEMCONFIG2_RXPAYLOADCRC_MASK, | |
( options.datarate << 4 ) | ( options.crcOn << 2 ) ); | |
this.w( REG.PREAMBLEMSB, ( options.preambleLen >> 8 ) & 0x00FF ); | |
this.w( REG.PREAMBLELSB, options.preambleLen & 0xFF ); | |
}; | |
exports.connect = function(options) { | |
return new SX(options); | |
}; | |