add lots of functionality and fix bugs

This commit is contained in:
soypat
2023-05-20 20:24:21 -03:00
parent 42d795e62d
commit 373412180f
3 changed files with 336 additions and 42 deletions
+14 -5
View File
@@ -21,8 +21,9 @@ type Config struct {
CRC bool
// Low data rate optimisation flag. The use of this flag is mandated when
// the symbol duration exceeds 16ms. Increases reliability at high spreading factors.
LDRO bool
IQMode uint8
LDRO bool
// IQInversion configures I and Q signal inversion.
IQInversion bool
}
func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
@@ -48,10 +49,14 @@ func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
Npayload++
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.
Ts_us := 1000_000 * cfg.SpreadFactor.ChipsPerSymbol() / cfg.Bandwidth.Hertz()
return time.Microsecond * (time.Duration(Npayload * Ts_us))
Ts_us := int64(baseUnitOfTime) * cfg.SpreadFactor.ChipsPerSymbol() / cfg.Bandwidth.Hertz()
return baseUnitOfTime * time.Duration(Npayload*Ts_us)
}
type HeaderType uint8
@@ -64,9 +69,13 @@ const (
type CodingRate uint8
const (
// 4/5 coding rate.
CR4_5 CodingRate = 1
// 4/6 coding rate.
CR4_6 CodingRate = 2
// 4/7 coding rate.
CR4_7 CodingRate = 3
// 4/8 coding rate.
CR4_8 CodingRate = 4
)
+29 -24
View File
@@ -77,9 +77,9 @@ const (
// SX127X_PAYLOAD_LENGTH uint8 = 0x40
// Low Noise Amp
lnaMAX_GAIN uint8 = 0x23
lnaOFF_GAIN uint8 = 0x00
lnaLOW_GAIN uint8 = 0x20
lnaMAX_GAIN uint8 = 0b001
lnaOFF_GAIN uint8 = 0b111
lnaLOW_GAIN uint8 = 0b110
// Bandwidth
bw7_8 uint8 = 0x00
@@ -100,32 +100,37 @@ const (
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) {
switch {
case bandwidth <= 7.8e3*lora.Hertz:
value = bw7_8
case bandwidth <= 10.4e3*lora.Hertz:
value = bw10_4
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
value = bw7_8
for i := byte(len(bandwidths) - 1); i >= 0; i-- {
if bandwidth >= bandwidths[i] {
value = i
break
}
}
return value
}
func reg2Bw(bwByte byte) (value lora.Frequency) {
if bwByte > byte(len(bandwidths)-1) {
return 0
}
return bandwidths[bwByte]
}
// Operation modes.
const (
opmMASK uint8 = 0x07
+293 -13
View File
@@ -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
import (
"encoding/binary"
"errors"
"io"
"time"
"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.
// 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 SPI interface {
Transfer(w byte) (byte, error)
Tx(writeBuffer, readBuffer []byte) error
}
@@ -27,22 +73,59 @@ func NewLoRa(bus SPI, cs, reset PinOutput) *DeviceLoRa {
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 {
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:
return errors.New("SF6 can only be used with implicit header type") // Page 30: Implicit Header Mode.
err = errSF6Implicit
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()
// 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.
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 {
return err
}
@@ -50,6 +133,31 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) error {
if err != nil {
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
}
@@ -78,14 +186,82 @@ func (d *DeviceLoRa) GetOpMode() (OpMode, error) {
if err != nil {
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
// FSK/OOK mode or disconnected.
return invalidOpMode, errors.New("device in FSK/OOK mode")
return invalidOpMode, errBadMode
}
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
func (d *DeviceLoRa) setPreambleLength(pLen uint16) error {
var buf [2]byte
@@ -94,9 +270,8 @@ func (d *DeviceLoRa) setPreambleLength(pLen uint16) error {
return d.write8(regPREAMBLE_LSB, buf[1])
}
// SetCrc Enable CRC generation and check on payload.
func (d *DeviceLoRa) enableCRC(enable bool) error {
return d.writeMasked8(regMODEM_CONFIG_2, 1<<2, b2u8(enable)<<2)
func (d *DeviceLoRa) enableTxContinuousMode(enable bool) error {
return d.writeMasked8(regMODEM_CONFIG_2, 1<<3, b2u8(enable)<<3)
}
// 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?
}
// setFrequency sets the radio frequency.
func (d *DeviceLoRa) setFrequency(freq uint32) error {
// setFrequency sets the center radio frequency.
func (d *DeviceLoRa) setFrequency(freq lora.Frequency) error {
var freqReg [3]byte
frf := freq / fSTEP // Page 82, 5.3.3 PLL.
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!
}
// 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 {
if value != 0 && value&^mask != 0 {
println("value", value, "mask", mask)
panic("misuse of writeMasked8") // Bug in this package if hit.
}
existing, err := d.read8(addr)
@@ -211,3 +476,18 @@ func b2u8(b bool) uint8 {
}
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
}