mirror of
https://github.com/soypat/lora.git
synced 2026-08-31 09:18:59 +00:00
add lots of functionality and fix bugs
This commit is contained in:
@@ -21,8 +21,9 @@ type Config struct {
|
|||||||
CRC bool
|
CRC bool
|
||||||
// Low data rate optimisation flag. The use of this flag is mandated when
|
// Low data rate optimisation flag. The use of this flag is mandated when
|
||||||
// the symbol duration exceeds 16ms. Increases reliability at high spreading factors.
|
// the symbol duration exceeds 16ms. Increases reliability at high spreading factors.
|
||||||
LDRO bool
|
LDRO bool
|
||||||
IQMode uint8
|
// IQInversion configures I and Q signal inversion.
|
||||||
|
IQInversion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
|
func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
|
||||||
@@ -48,10 +49,14 @@ func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
|
|||||||
Npayload++
|
Npayload++
|
||||||
Npayload *= (cr + 4)
|
Npayload *= (cr + 4)
|
||||||
}
|
}
|
||||||
Npayload += 8 + int64(cfg.PreambleLength) + 5 // Says 4.25 in manual but we round up.
|
// Says 4.25 in manual but we round up to 5. This means we'll overestimate the
|
||||||
|
// time calculated.
|
||||||
|
Npayload += 8 + int64(cfg.PreambleLength) + 5
|
||||||
|
// base units for time calculation. A higher number means more resolution.
|
||||||
|
const baseUnitOfTime = time.Microsecond
|
||||||
// Calculate LoRa Transmission Parameter Relationship page 28.
|
// Calculate LoRa Transmission Parameter Relationship page 28.
|
||||||
Ts_us := 1000_000 * cfg.SpreadFactor.ChipsPerSymbol() / cfg.Bandwidth.Hertz()
|
Ts_us := int64(baseUnitOfTime) * cfg.SpreadFactor.ChipsPerSymbol() / cfg.Bandwidth.Hertz()
|
||||||
return time.Microsecond * (time.Duration(Npayload * Ts_us))
|
return baseUnitOfTime * time.Duration(Npayload*Ts_us)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HeaderType uint8
|
type HeaderType uint8
|
||||||
@@ -64,9 +69,13 @@ const (
|
|||||||
type CodingRate uint8
|
type CodingRate uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// 4/5 coding rate.
|
||||||
CR4_5 CodingRate = 1
|
CR4_5 CodingRate = 1
|
||||||
|
// 4/6 coding rate.
|
||||||
CR4_6 CodingRate = 2
|
CR4_6 CodingRate = 2
|
||||||
|
// 4/7 coding rate.
|
||||||
CR4_7 CodingRate = 3
|
CR4_7 CodingRate = 3
|
||||||
|
// 4/8 coding rate.
|
||||||
CR4_8 CodingRate = 4
|
CR4_8 CodingRate = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+29
-24
@@ -77,9 +77,9 @@ const (
|
|||||||
// SX127X_PAYLOAD_LENGTH uint8 = 0x40
|
// SX127X_PAYLOAD_LENGTH uint8 = 0x40
|
||||||
|
|
||||||
// Low Noise Amp
|
// Low Noise Amp
|
||||||
lnaMAX_GAIN uint8 = 0x23
|
lnaMAX_GAIN uint8 = 0b001
|
||||||
lnaOFF_GAIN uint8 = 0x00
|
lnaOFF_GAIN uint8 = 0b111
|
||||||
lnaLOW_GAIN uint8 = 0x20
|
lnaLOW_GAIN uint8 = 0b110
|
||||||
|
|
||||||
// Bandwidth
|
// Bandwidth
|
||||||
bw7_8 uint8 = 0x00
|
bw7_8 uint8 = 0x00
|
||||||
@@ -100,32 +100,37 @@ const (
|
|||||||
privateSyncword = 0x14
|
privateSyncword = 0x14
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var bandwidths = [bw500_0 + 1]lora.Frequency{
|
||||||
|
bw7_8: 7.8e3 * lora.Hertz,
|
||||||
|
bw10_4: 10.4e3 * lora.Hertz,
|
||||||
|
bw15_6: 15.6e3 * lora.Hertz,
|
||||||
|
bw20_8: 20.8e3 * lora.Hertz,
|
||||||
|
bw31_25: 31.25e3 * lora.Hertz,
|
||||||
|
bw41_7: 41.7e3 * lora.Hertz,
|
||||||
|
bw62_5: 62.5e3 * lora.Hertz,
|
||||||
|
bw125_0: 125e3 * lora.Hertz,
|
||||||
|
bw250_0: 250e3 * lora.Hertz,
|
||||||
|
bw500_0: 500e3 * lora.Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
func bwReg(bandwidth lora.Frequency) (value byte) {
|
func bwReg(bandwidth lora.Frequency) (value byte) {
|
||||||
switch {
|
value = bw7_8
|
||||||
case bandwidth <= 7.8e3*lora.Hertz:
|
for i := byte(len(bandwidths) - 1); i >= 0; i-- {
|
||||||
value = bw7_8
|
if bandwidth >= bandwidths[i] {
|
||||||
case bandwidth <= 10.4e3*lora.Hertz:
|
value = i
|
||||||
value = bw10_4
|
break
|
||||||
case bandwidth <= 15.6e3*lora.Hertz:
|
}
|
||||||
value = bw15_6
|
|
||||||
case bandwidth <= 20.8e3*lora.Hertz:
|
|
||||||
value = bw20_8
|
|
||||||
case bandwidth <= 31.25e3*lora.Hertz:
|
|
||||||
value = bw31_25
|
|
||||||
case bandwidth <= 41.7e3*lora.Hertz:
|
|
||||||
value = bw41_7
|
|
||||||
case bandwidth <= 62.5e3*lora.Hertz:
|
|
||||||
value = bw62_5
|
|
||||||
case bandwidth <= 125*lora.KiloHertz:
|
|
||||||
value = bw125_0
|
|
||||||
case bandwidth <= 250e3*lora.KiloHertz:
|
|
||||||
value = bw250_0
|
|
||||||
default:
|
|
||||||
value = bw500_0
|
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reg2Bw(bwByte byte) (value lora.Frequency) {
|
||||||
|
if bwByte > byte(len(bandwidths)-1) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return bandwidths[bwByte]
|
||||||
|
}
|
||||||
|
|
||||||
// Operation modes.
|
// Operation modes.
|
||||||
const (
|
const (
|
||||||
opmMASK uint8 = 0x07
|
opmMASK uint8 = 0x07
|
||||||
|
|||||||
+293
-13
@@ -1,8 +1,51 @@
|
|||||||
|
/*
|
||||||
|
package sx127x implements a driver for the SX127x LoRa tranceiver family.
|
||||||
|
It is a low-level driver that exposes the device's registers and their
|
||||||
|
|
||||||
|
The SX127x family includes the SX1276, SX1277, SX1278 and SX1279. The
|
||||||
|
differences between them are the supported frequency bands and the
|
||||||
|
maximum output power.
|
||||||
|
|
||||||
|
# FIFO and packet handling
|
||||||
|
|
||||||
|
The SX127x has a 256-byte FIFO buffer that is accesible via the SPI interface.
|
||||||
|
The FIFO is shared by the transmitter and the receiver but can be split via
|
||||||
|
the FifoTxBaseAddr and FifoRxBaseAddr registers.
|
||||||
|
|
||||||
|
What follows is a diagram of the FIFO buffer and the data pointers:
|
||||||
|
|
||||||
|
+-------------------+
|
||||||
|
| |
|
||||||
|
| Unused |
|
||||||
|
| |
|
||||||
|
+-------------------+ <-- RxByteAddr (address of last byte received)
|
||||||
|
| | ^
|
||||||
|
| Packet N | | FifoRxBytesUp (Only for )
|
||||||
|
| | |
|
||||||
|
+-------------------+ <-- RegFifoRxCurrentAddr (address of start of last packet received)
|
||||||
|
| Packet N-1 |
|
||||||
|
+-------------------+
|
||||||
|
| ... | <-- FifoAddrPtr (SPI read/write pointer)
|
||||||
|
+-------------------+
|
||||||
|
| Packet 0 |
|
||||||
|
+-------------------+ <-- FifoRxBaseAddr
|
||||||
|
| Unused |
|
||||||
|
+-------------------+
|
||||||
|
| | ^
|
||||||
|
| Packet to | | PayloadLength
|
||||||
|
| Transmit | |
|
||||||
|
+-------------------+ <-- RegFifoTxCurrentAddr
|
||||||
|
| Unused |
|
||||||
|
+-------------------+
|
||||||
|
|
||||||
|
The FIFO is accessible through the SPI
|
||||||
|
*/
|
||||||
package sx127x
|
package sx127x
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/soypat/lora"
|
"github.com/soypat/lora"
|
||||||
@@ -10,9 +53,12 @@ import (
|
|||||||
|
|
||||||
// TODO: page 82: To minimize the current consumption of the SX1276/77/78/79, please ensure that the CLKOUT signal is disabled when not required.
|
// TODO: page 82: To minimize the current consumption of the SX1276/77/78/79, please ensure that the CLKOUT signal is disabled when not required.
|
||||||
|
|
||||||
|
// PinOutput is a function that sets the logic-level of a pin to high (true)
|
||||||
|
// or low (false). It is used to abstract a GPIO pin interface.
|
||||||
type PinOutput func(level bool)
|
type PinOutput func(level bool)
|
||||||
|
|
||||||
type SPI interface {
|
type SPI interface {
|
||||||
|
Transfer(w byte) (byte, error)
|
||||||
Tx(writeBuffer, readBuffer []byte) error
|
Tx(writeBuffer, readBuffer []byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,22 +73,59 @@ func NewLoRa(bus SPI, cs, reset PinOutput) *DeviceLoRa {
|
|||||||
return &d
|
return &d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeviceLoRa) Configure(cfg lora.Config) error {
|
var (
|
||||||
|
errBadSpread = errors.New("bad spread factor")
|
||||||
|
errSF6Implicit = errors.New("SF6 can only be used with implicit header type") // Page 30: Implicit Header Mode.
|
||||||
|
errPreambleTooShort = errors.New("preamble length too short")
|
||||||
|
ErrNotDetected = errors.New("sx127x not detected")
|
||||||
|
errBadMode = errors.New("bad mode: sx127x in FSK/OOK mode, not LoRa or viceversa")
|
||||||
|
errBadCodingRate = errors.New("bad coding rate")
|
||||||
|
errUnsupportedBandwidth = errors.New("bandwidth too high for frequency around 169MHz")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
||||||
switch {
|
switch {
|
||||||
case cfg.SpreadFactor < lora.SF6 || cfg.SpreadFactor > lora.SF12:
|
case cfg.SpreadFactor < lora.SF6 || cfg.SpreadFactor > lora.SF12:
|
||||||
return errors.New("bad spread factor")
|
err = errBadSpread
|
||||||
case cfg.SpreadFactor == lora.SF6 && cfg.HeaderType != lora.HeaderImplicit:
|
case cfg.SpreadFactor == lora.SF6 && cfg.HeaderType != lora.HeaderImplicit:
|
||||||
return errors.New("SF6 can only be used with implicit header type") // Page 30: Implicit Header Mode.
|
err = errSF6Implicit
|
||||||
case cfg.PreambleLength < 6:
|
case cfg.PreambleLength < 6:
|
||||||
return errors.New("preamble length too short")
|
err = errPreambleTooShort
|
||||||
|
case cfg.CodingRate < lora.CR4_5 || cfg.CodingRate > lora.CR4_8:
|
||||||
|
err = errBadCodingRate
|
||||||
|
case cfg.Frequency < 175*lora.MegaHertz && cfg.Bandwidth > 125*lora.KiloHertz:
|
||||||
|
err = errUnsupportedBandwidth
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
d.Reset()
|
d.Reset()
|
||||||
// We need to be in sleep mode to set LoRa mode if in FSK/OOK.
|
// We need to be in sleep mode to set LoRa mode if in FSK/OOK.
|
||||||
d.write8(regOP_MODE, opmSLEEP) // No need to check error, do it in SetOpmode.
|
d.write8(regOP_MODE, opmSLEEP) // No need to check error, do it in SetOpmode.
|
||||||
if !d.IsConnected() {
|
if !d.IsConnected() {
|
||||||
return errors.New("sx127x not detected")
|
return ErrNotDetected
|
||||||
}
|
}
|
||||||
err := d.SetOpMode(OpSleep)
|
err = d.SetOpMode(OpSleep)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.setFrequency(cfg.Frequency)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.setBandwidth(cfg.Bandwidth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.enableCRC(cfg.CRC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.EnableAutoAGC(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.setPreambleLength(cfg.PreambleLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -50,6 +133,31 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = d.setSyncWord(cfg.SyncWord)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.setCodingRate(cfg.CodingRate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.setSpreadFactorConsistent(cfg.SpreadFactor)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.enableImplicitHeaderMode(cfg.HeaderType == lora.HeaderImplicit)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.enableIQInversion(cfg.IQInversion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.enableTxContinuousMode(false) // TODO: enable or disable?
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.setHopPeriod(0)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,14 +186,82 @@ func (d *DeviceLoRa) GetOpMode() (OpMode, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return invalidOpMode, err
|
return invalidOpMode, err
|
||||||
}
|
}
|
||||||
if got&byte(opLoRaBit) == 0 {
|
if got&byte(opLoRaBit) == 0 || // LongRangeMode bit influences operation.
|
||||||
|
got&(1<<6) != 0 { // AccessSharedReg bit allows access to FSK registers in LoRa mode, should not be set.
|
||||||
// if the LoRa mode bit is not set, which would mean the device is in
|
// if the LoRa mode bit is not set, which would mean the device is in
|
||||||
// FSK/OOK mode or disconnected.
|
// FSK/OOK mode or disconnected.
|
||||||
return invalidOpMode, errors.New("device in FSK/OOK mode")
|
return invalidOpMode, errBadMode
|
||||||
}
|
}
|
||||||
return OpMode(got & opmMASK), nil
|
return OpMode(got & opmMASK), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLNAGain sets the Low Noise amplifier gain with a value between 0 and 6
|
||||||
|
// where 0 is Off and 6 is the maximum gain.
|
||||||
|
func (d *DeviceLoRa) SetLNAGain(gain uint8) error {
|
||||||
|
if gain > 6 {
|
||||||
|
return errors.New("gain must be between 0 and 6")
|
||||||
|
}
|
||||||
|
const lnaMask = 0b111 << 5
|
||||||
|
gain = 0b111 - gain // invert gain value to reflect the fact that 0 is max gain.
|
||||||
|
return d.writeMasked8(regLNA, lnaMask, gain<<5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setBandwidth sets the bandwidth of the LoRa modulation.
|
||||||
|
func (d *DeviceLoRa) setBandwidth(bw lora.Frequency) error {
|
||||||
|
const bwMask = 0b1111 << 4
|
||||||
|
bwByte := bwReg(bw)
|
||||||
|
return d.writeMasked8(regMODEM_CONFIG_1, bwMask, bwByte<<4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableImplicitHeaderMode enables implicit header mode (instead of explicit).
|
||||||
|
func (d *DeviceLoRa) enableImplicitHeaderMode(enable bool) error {
|
||||||
|
return d.writeMasked8(regMODEM_CONFIG_1, 1, b2u8(enable))
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableIQInversion inverts LoRa I and Q signals when set to true.
|
||||||
|
func (d *DeviceLoRa) enableIQInversion(enable bool) error {
|
||||||
|
const iqMask = 1 << 6
|
||||||
|
return d.writeMasked8(regINVERTIQ, iqMask, b2u8(enable)<<6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadConfig reads the configuration parameters from the device and returns
|
||||||
|
// the corresponding lora.Config for the current device configuration.
|
||||||
|
// Some lora.Config parameters are not set such as IQ, LDR, and Tx power.
|
||||||
|
func (d *DeviceLoRa) ReadConfig() (cfg lora.Config, err error) {
|
||||||
|
var buf [5]byte
|
||||||
|
err = d.read(regMODEM_CONFIG_1, buf[:5])
|
||||||
|
if err != nil {
|
||||||
|
return cfg, err
|
||||||
|
}
|
||||||
|
cfg1 := buf[0]
|
||||||
|
cfg.Bandwidth = reg2Bw(cfg1 >> 4)
|
||||||
|
cfg.CodingRate = lora.CodingRate(cfg1>>1) & 0b111
|
||||||
|
cfg.HeaderType = lora.HeaderType(cfg1 & 1)
|
||||||
|
cfg2 := buf[1]
|
||||||
|
cfg.SpreadFactor = lora.SpreadFactor(cfg1 >> 4)
|
||||||
|
cfg.CRC = cfg2&0x4 != 0
|
||||||
|
// continuousMode = cfg2&0x8 != 0
|
||||||
|
cfg.PreambleLength = binary.BigEndian.Uint16(buf[3:])
|
||||||
|
// Read sync word.
|
||||||
|
sync, err := d.read8(regSYNC_WORD)
|
||||||
|
if err != nil {
|
||||||
|
return cfg, err
|
||||||
|
}
|
||||||
|
cfg.SyncWord = sync
|
||||||
|
// Read Frequency.
|
||||||
|
err = d.read(regFRF_MSB, buf[:3])
|
||||||
|
freq := uint64(buf[0])<<16 | uint64(buf[1])<<8 | uint64(buf[2])
|
||||||
|
cfg.Frequency = lora.Frequency((freq * 15625) >> 8)
|
||||||
|
// Read IQ inversion.
|
||||||
|
iq, err := d.read8(regINVERTIQ)
|
||||||
|
if err != nil {
|
||||||
|
return cfg, err
|
||||||
|
}
|
||||||
|
cfg.IQInversion = iq&(1<<6) != 0
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
// setPreambleLength defines number of preamble
|
// setPreambleLength defines number of preamble
|
||||||
func (d *DeviceLoRa) setPreambleLength(pLen uint16) error {
|
func (d *DeviceLoRa) setPreambleLength(pLen uint16) error {
|
||||||
var buf [2]byte
|
var buf [2]byte
|
||||||
@@ -94,9 +270,8 @@ func (d *DeviceLoRa) setPreambleLength(pLen uint16) error {
|
|||||||
return d.write8(regPREAMBLE_LSB, buf[1])
|
return d.write8(regPREAMBLE_LSB, buf[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCrc Enable CRC generation and check on payload.
|
func (d *DeviceLoRa) enableTxContinuousMode(enable bool) error {
|
||||||
func (d *DeviceLoRa) enableCRC(enable bool) error {
|
return d.writeMasked8(regMODEM_CONFIG_2, 1<<3, b2u8(enable)<<3)
|
||||||
return d.writeMasked8(regMODEM_CONFIG_2, 1<<2, b2u8(enable)<<2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setOCP defines Overload Current Protection configuration. It receives
|
// setOCP defines Overload Current Protection configuration. It receives
|
||||||
@@ -139,8 +314,8 @@ func (d *DeviceLoRa) setTxPower(txPow int8) error {
|
|||||||
return d.write8(regOCP, 0) // TODO: Disable OCP?
|
return d.write8(regOCP, 0) // TODO: Disable OCP?
|
||||||
}
|
}
|
||||||
|
|
||||||
// setFrequency sets the radio frequency.
|
// setFrequency sets the center radio frequency.
|
||||||
func (d *DeviceLoRa) setFrequency(freq uint32) error {
|
func (d *DeviceLoRa) setFrequency(freq lora.Frequency) error {
|
||||||
var freqReg [3]byte
|
var freqReg [3]byte
|
||||||
frf := freq / fSTEP // Page 82, 5.3.3 PLL.
|
frf := freq / fSTEP // Page 82, 5.3.3 PLL.
|
||||||
freqReg[0] = byte(frf >> 16)
|
freqReg[0] = byte(frf >> 16)
|
||||||
@@ -153,8 +328,98 @@ func (d *DeviceLoRa) setFrequency(freq uint32) error {
|
|||||||
return d.write8(regFRF_LSB, freqReg[2]) // Write LSB last!
|
return d.write8(regFRF_LSB, freqReg[2]) // Write LSB last!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setSpreadFactorConsistent sets the spreading factor and closely related parameters
|
||||||
|
// including Low Data Rate Optimization, DetectionOptimize, and DetectionThreshold.
|
||||||
|
func (d *DeviceLoRa) setSpreadFactorConsistent(sf lora.SpreadFactor) (err error) {
|
||||||
|
err = d.setSpreadingFactor(sf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
isSF6 := sf == lora.SF6
|
||||||
|
err = d.enableLowDataRateOptimization(isSF6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Set DetectionOptimize to 0x05 for SF6 and to 0x03 otherwise (SF7 to SF12).
|
||||||
|
err = d.writeMasked8(regDETECTION_OPTIMIZE, 0b111, 1|(0b10<<b2u8(isSF6)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Set DetectionThreshold to 0x0C for SF6 and to 0x0A otherwise (SF7 to SF12).
|
||||||
|
return d.write8(regDETECTION_THRESHOLD, 0b1000|(0b10<<b2u8(isSF6)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// setSpreadingFactor sets the spreading factor. The value must be between 6 and 12.
|
||||||
|
// It does not set parameters closely associated with the spreading factor such as
|
||||||
|
// the Low Data Optimization, the Detection threshold, Detection Optimize and the
|
||||||
|
// Symbol Timeout.
|
||||||
|
func (d *DeviceLoRa) setSpreadingFactor(sf lora.SpreadFactor) error {
|
||||||
|
if sf < 6 || sf > 12 {
|
||||||
|
return errBadSpread
|
||||||
|
}
|
||||||
|
const sfMask = 0b111 << 4
|
||||||
|
return d.writeMasked8(regMODEM_CONFIG_2, sfMask, uint8(sf)<<4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeviceLoRa) setSyncWord(sync byte) error {
|
||||||
|
return d.write8(regSYNC_WORD, sync)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableAutoAGC enables/disables Automatic Gain Control. This means the value set
|
||||||
|
// by SetLNAGain will be ignored. Set to false to use the value set by SetLNAGain.
|
||||||
|
func (d *DeviceLoRa) EnableAutoAGC(b bool) error {
|
||||||
|
const agcMask = 1 << 2
|
||||||
|
return d.writeMasked8(regMODEM_CONFIG_3, agcMask, b2u8(b)<<2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableLowDataRateOptimization enables/disables Low Data Rate Optimization, a
|
||||||
|
// feature which is mandated when symbol length exceeds 16ms.
|
||||||
|
func (d *DeviceLoRa) enableLowDataRateOptimization(b bool) error {
|
||||||
|
const ldoMask = 1 << 3
|
||||||
|
return d.writeMasked8(regMODEM_CONFIG_3, ldoMask, b2u8(b)<<3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableLowFrequencyMode enables/disables access to LowFrequencyMode registers.
|
||||||
|
func (d *DeviceLoRa) enableLowFrequencyMode(b bool) error {
|
||||||
|
const lowFreqMask = 1 << 3
|
||||||
|
return d.writeMasked8(regOP_MODE, lowFreqMask, b2u8(b)<<3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableCRC enables/disables CRC generation and checking.
|
||||||
|
func (d *DeviceLoRa) enableCRC(b bool) error {
|
||||||
|
const crcMask = 1 << 2
|
||||||
|
return d.writeMasked8(regMODEM_CONFIG_2, crcMask, b2u8(b)<<2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setTimeoutInSymbols sets the timeout in symbols. The value must be between 0 and 1023.
|
||||||
|
// The timeout is used to stop reception automatically. The equation is:
|
||||||
|
//
|
||||||
|
// Timeout = timeoutSymbols * Ts (where Ts is the symbol period)
|
||||||
|
func (d *DeviceLoRa) setTimeoutInSymbols(symbTimeout uint16) (err error) {
|
||||||
|
if symbTimeout > 0x3FF {
|
||||||
|
return errors.New("timeout value too large")
|
||||||
|
}
|
||||||
|
err = d.writeMasked8(regMODEM_CONFIG_2, 0b11, byte(symbTimeout>>8))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.write8(regSYMB_TIMEOUT_LSB, byte(symbTimeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCodingRate sets the error coding rate. The value must be between 4/5 and 4/8.
|
||||||
|
func (d *DeviceLoRa) setCodingRate(cr lora.CodingRate) error {
|
||||||
|
if cr < lora.CR4_5 || cr > lora.CR4_8 {
|
||||||
|
return errBadCodingRate
|
||||||
|
}
|
||||||
|
return d.writeMasked8(regMODEM_CONFIG_1, 0b111<<1, uint8(cr)<<1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setHopPeriod sets number of symbol periods between frequency hops. (0 = disabled).
|
||||||
|
func (d *DeviceLoRa) setHopPeriod(val uint8) error { return d.write8(regHOP_PERIOD, val) }
|
||||||
|
|
||||||
func (d *DeviceLoRa) writeMasked8(addr uint8, mask, value byte) error {
|
func (d *DeviceLoRa) writeMasked8(addr uint8, mask, value byte) error {
|
||||||
if value != 0 && value&^mask != 0 {
|
if value != 0 && value&^mask != 0 {
|
||||||
|
println("value", value, "mask", mask)
|
||||||
panic("misuse of writeMasked8") // Bug in this package if hit.
|
panic("misuse of writeMasked8") // Bug in this package if hit.
|
||||||
}
|
}
|
||||||
existing, err := d.read8(addr)
|
existing, err := d.read8(addr)
|
||||||
@@ -211,3 +476,18 @@ func b2u8(b bool) uint8 {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DeviceLoRa) read(addr uint8, buf []byte) error {
|
||||||
|
if len(buf) <= 1 {
|
||||||
|
return io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
d.csEnable(true)
|
||||||
|
_, err := d.bus.Transfer(addr)
|
||||||
|
if err != nil {
|
||||||
|
d.csEnable(false)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.bus.Tx(nil, buf)
|
||||||
|
d.csEnable(false)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user