mirror of
https://github.com/soypat/lora.git
synced 2026-07-26 07:28:38 +00:00
add debugging stuff
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
# lora
|
||||
LoRa protocol for TinyGo- based on tinygo-org/drivers.
|
||||
High quality LoRa library and drivers for the Go language.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
//go:build tinygo
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lora"
|
||||
"github.com/soypat/lora/sx127x"
|
||||
)
|
||||
|
||||
var (
|
||||
SX127X_SPI = machine.SPI0
|
||||
)
|
||||
|
||||
const (
|
||||
SX127X_PIN_RST = machine.GP16
|
||||
// SPI definition for SX127x
|
||||
|
||||
SX127X_PIN_SCK = machine.GP2
|
||||
SX127X_PIN_TX = machine.GP3
|
||||
SX127X_PIN_RX = machine.GP4
|
||||
SX127X_PIN_CS = machine.GP5
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
defer println("program end")
|
||||
SX127X_PIN_RST.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
SX127X_PIN_CS.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
err := SX127X_SPI.Configure(machine.SPIConfig{
|
||||
Frequency: 100000,
|
||||
SCK: SX127X_PIN_SCK,
|
||||
SDO: SX127X_PIN_TX,
|
||||
SDI: SX127X_PIN_RX,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
dev := sx127x.NewLoRa(SX127X_SPI, SX127X_PIN_CS.Set, SX127X_PIN_RST.Set)
|
||||
// Replace with your frequency:
|
||||
err = dev.Configure(sx127x.DefaultConfig(lora.Freq433_0M))
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
println("config success; receiving messages")
|
||||
var rx [256]byte
|
||||
// This blocks forever reading messages received.
|
||||
ctx := context.Background()
|
||||
err = dev.RxContinuous(ctx, func(r io.Reader) (_ error) {
|
||||
n, err := r.Read(rx[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println("received LoRa:", string(rx[:n]))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -40,16 +40,7 @@ func main() {
|
||||
panic(err.Error())
|
||||
}
|
||||
dev := sx127x.NewLoRa(SX127X_SPI, SX127X_PIN_CS.Set, SX127X_PIN_RST.Set)
|
||||
err = dev.Configure(lora.Config{
|
||||
Bandwidth: lora.BW125k,
|
||||
Frequency: lora.Freq433_0M,
|
||||
CodingRate: lora.CR4_5,
|
||||
SpreadFactor: lora.SF7,
|
||||
HeaderType: lora.HeaderExplicit,
|
||||
TxPower: 0,
|
||||
CRC: true,
|
||||
PreambleLength: 12,
|
||||
})
|
||||
err = dev.Configure(sx127x.DefaultConfig(lora.Freq433_0M))
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
@@ -57,7 +48,6 @@ func main() {
|
||||
randomU32, _ := dev.RandomU32()
|
||||
myName := fmt.Sprintf("user-%x", randomU32%0xffff)
|
||||
println("config success; sending messages as ", myName)
|
||||
var rx [256]byte
|
||||
packet := []byte(myName + " says hello!")
|
||||
for {
|
||||
err = dev.Tx(packet)
|
||||
@@ -67,13 +57,5 @@ func main() {
|
||||
print(".")
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
for i := 0; i < 6; i++ {
|
||||
n, err := dev.RxSingle(rx[:])
|
||||
if err != nil {
|
||||
println("rx error:", err.Error())
|
||||
continue
|
||||
}
|
||||
println("got message: ", string(rx[:n]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,15 @@ package lora
|
||||
|
||||
import "time"
|
||||
|
||||
// Config is the generic configuration struct for a LoRa modem/radio that provides
|
||||
// the most common configuration parameters needed to set up a channel with another
|
||||
// radio using same parameters.
|
||||
type Config struct {
|
||||
// Bandwidth relates to data rate of communication. Double the bandwidth means
|
||||
// double the data rate, which implies faster communications and less energy usage.
|
||||
Bandwidth Frequency
|
||||
// Frequency is the carrier frequency of the radio, a.k.a center frequency.
|
||||
// It must match the modem's working frequency to ensure proper functioning.
|
||||
Frequency Frequency
|
||||
// Length of the preamble preceding the header. Can be arbitrarily long.
|
||||
// Longer preambles ensure more robust communications but can lead to congestion.
|
||||
@@ -14,11 +19,13 @@ type Config struct {
|
||||
// than the expected packet preamble length on the SX1278.
|
||||
PreambleLength uint16
|
||||
HeaderType HeaderType
|
||||
CodingRate CodingRate
|
||||
SpreadFactor SpreadFactor
|
||||
SyncWord uint8
|
||||
TxPower int8 // Tx Power in dBm.
|
||||
CRC bool
|
||||
// Must be set when working with implicit headers.
|
||||
MaxImplicitPayloadLength uint8
|
||||
CodingRate CodingRate
|
||||
SpreadFactor SpreadFactor
|
||||
SyncWord uint8
|
||||
TxPower int8 // Tx Power in dBm.
|
||||
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
|
||||
@@ -75,6 +82,10 @@ func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
|
||||
time.Duration(cfg.Bandwidth.Hertz())
|
||||
}
|
||||
|
||||
// HeaderType defines the presence of a header in the LoRa packet.
|
||||
// An explicit header means that the packet contains a header with a length
|
||||
// field. An implicit header means that the packet does not contain a header
|
||||
// and the length is implicitly known, i.e. agreed upon between two modems in advance.
|
||||
type HeaderType uint8
|
||||
|
||||
const (
|
||||
@@ -82,6 +93,10 @@ const (
|
||||
HeaderImplicit HeaderType = 1
|
||||
)
|
||||
|
||||
// CodingRate defines the error correction scheme used by the LoRa modem.
|
||||
// Higher coding rates imply more robust communications at the expense of
|
||||
// less data throughput. A CR of 4/5 means that for every 4 bits of data,
|
||||
// 1 bit of error correction is added to the total transmitted bits.
|
||||
type CodingRate uint8
|
||||
|
||||
const (
|
||||
@@ -95,8 +110,13 @@ const (
|
||||
CR4_8 CodingRate = 4
|
||||
)
|
||||
|
||||
// SpreadFactor defines the number of chips per symbol. Higher spread factors
|
||||
// imply longer transmission times but more robust communications.
|
||||
// The number of chips per symbol is 2^SF, so a spread factor of 8 takes twice
|
||||
// as long to transmit a symbol as a spread factor of 7.
|
||||
type SpreadFactor uint8
|
||||
|
||||
// Common spread factors. Decide the number of chips per symbol.
|
||||
const (
|
||||
SF6 SpreadFactor = iota + 6
|
||||
SF7
|
||||
@@ -111,6 +131,10 @@ func (sf SpreadFactor) ChipsPerSymbol() int64 {
|
||||
return 1 << sf
|
||||
}
|
||||
|
||||
// Frequency defines the center frequency of the LoRa channel. The center frequency
|
||||
// serves to avoid interference from other channels and has minimal effect on
|
||||
// the data rate. The frequency in Config must match the modem's working frequency
|
||||
// or the modem may fail to transmit or receive packets or even be damaged.
|
||||
type Frequency int64
|
||||
|
||||
func (f Frequency) Hertz() int64 { return int64(f) }
|
||||
|
||||
+93
-1
@@ -1,6 +1,11 @@
|
||||
package sx127x
|
||||
|
||||
import "github.com/soypat/lora"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/soypat/lora"
|
||||
)
|
||||
|
||||
const (
|
||||
// 32MHz frequency for crystal oscillator is typical.
|
||||
@@ -9,6 +14,56 @@ const (
|
||||
fSTEP = fXOSC / (1 << 19)
|
||||
)
|
||||
|
||||
type irqFlagPos uint8
|
||||
|
||||
const (
|
||||
irqPosCADDetected irqFlagPos = iota
|
||||
irqPosFHSSChange
|
||||
irqPosCADDone
|
||||
irqPosTxDone
|
||||
irqPosValidHeader
|
||||
irqPosPayloadCRC
|
||||
irqPosRxDone
|
||||
irqPosRxTimeout
|
||||
)
|
||||
|
||||
func (pos irqFlagPos) String() (str string) {
|
||||
switch pos {
|
||||
case irqPosCADDetected:
|
||||
str = "CADDetected"
|
||||
case irqPosFHSSChange:
|
||||
str = "FHSSChange"
|
||||
case irqPosCADDone:
|
||||
str = "CADDone"
|
||||
case irqPosTxDone:
|
||||
str = "TxDone"
|
||||
case irqPosValidHeader:
|
||||
str = "ValidHeader"
|
||||
case irqPosPayloadCRC:
|
||||
str = "CRCError"
|
||||
case irqPosRxDone:
|
||||
str = "RxDone"
|
||||
case irqPosRxTimeout:
|
||||
str = "RxTimeout"
|
||||
default:
|
||||
str = "UnknownIRQ"
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func irqFlagsString(irqFlags uint8) (str string) {
|
||||
if irqFlags == 0 {
|
||||
return "[]"
|
||||
}
|
||||
str = "["
|
||||
for i := irqFlagPos(0); i < 8; i++ {
|
||||
if irqFlags&(1<<i) != 0 {
|
||||
str += i.String() + ","
|
||||
}
|
||||
}
|
||||
return str + "]"
|
||||
}
|
||||
|
||||
const (
|
||||
// registers
|
||||
regFIFO = 0x00
|
||||
@@ -58,6 +113,7 @@ const (
|
||||
expectedVersion = 0x12
|
||||
|
||||
// Bits masking the corresponding IRQs from the radio
|
||||
|
||||
irqRXTOUT_MASK uint8 = 0x80
|
||||
irqRXDONE_MASK uint8 = 0x40
|
||||
irqCRCERR_MASK uint8 = 0x20
|
||||
@@ -193,3 +249,39 @@ func (op OpMode) String() (s string) {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) statusString() string {
|
||||
irq, _ := d.read8(regIRQ_FLAGS)
|
||||
opmode, err := d.GetOpMode()
|
||||
return fmt.Sprintf("opmode: %s; operr=%v; irq: %08b=%s;%s", opmode.String(), err, irq, irqFlagsString(irq), d.debugRegString())
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) wrapErr(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("stat=%s: %w", d.statusString(), err)
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) debugForEachTouchedReg(fn func(addr, mask, read, write uint8)) {
|
||||
for addr := uint8(0); addr < debugBufSize; addr++ {
|
||||
mask := d.debugMask[addr]
|
||||
read := d.debugRead[addr]
|
||||
write := d.debugWrite[addr]
|
||||
fn(addr, mask, read, write)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) debugRegString() string {
|
||||
var buf bytes.Buffer
|
||||
d.debugForEachTouchedReg(func(addr, mask, read, write uint8) {
|
||||
rm := read & mask
|
||||
wm := write & mask
|
||||
if rm == wm {
|
||||
return
|
||||
}
|
||||
regstr := regstr(addr).String()
|
||||
fmt.Fprintf(&buf, "0x%02X(%s): mask=%08b, read=0x%0x, write=0x%0x\n", addr, regstr, mask, read, write)
|
||||
})
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package sx127x
|
||||
|
||||
type regstr uint8
|
||||
|
||||
const (
|
||||
// registers
|
||||
_FIFO regstr = 0x00
|
||||
_OP_MODE regstr = 0x01
|
||||
_FRF_MSB regstr = 0x06
|
||||
_FRF_MID regstr = 0x07
|
||||
_FRF_LSB regstr = 0x08
|
||||
_PA_CONFIG regstr = 0x09
|
||||
_PA_RAMP regstr = 0x0a
|
||||
_OCP regstr = 0x0b
|
||||
_LNA regstr = 0x0c
|
||||
_FIFO_ADDR_PTR regstr = 0x0d
|
||||
_FIFO_TX_BASE_ADDR regstr = 0x0e
|
||||
_FIFO_RX_BASE_ADDR regstr = 0x0f
|
||||
_FIFO_RX_CURRENT_ADDR regstr = 0x10
|
||||
_IRQ_FLAGS_MASK regstr = 0x11
|
||||
_IRQ_FLAGS regstr = 0x12
|
||||
_RX_NB_BYTES regstr = 0x13
|
||||
_PKT_SNR_VALUE regstr = 0x19
|
||||
_PKT_RSSI_VALUE regstr = 0x1a
|
||||
_RSSI_VALUE regstr = 0x1b
|
||||
_MODEM_CONFIG_1 regstr = 0x1d
|
||||
_MODEM_CONFIG_2 regstr = 0x1e
|
||||
_SYMB_TIMEOUT_LSB regstr = 0x1f
|
||||
_PREAMBLE_MSB regstr = 0x20
|
||||
_PREAMBLE_LSB regstr = 0x21
|
||||
_PAYLOAD_LENGTH regstr = 0x22
|
||||
_MAX_PAYLOAD_LENGTH regstr = 0x23
|
||||
_HOP_PERIOD regstr = 0x24
|
||||
_MODEM_CONFIG_3 regstr = 0x26
|
||||
_FREQ_ERROR_MSB regstr = 0x28
|
||||
_FREQ_ERROR_MID regstr = 0x29
|
||||
_FREQ_ERROR_LSB regstr = 0x2a
|
||||
_RSSI_WIDEBAND regstr = 0x2c
|
||||
_DETECTION_OPTIMIZE regstr = 0x31
|
||||
_INVERTIQ regstr = 0x33
|
||||
_DETECTION_THRESHOLD regstr = 0x37
|
||||
_SYNC_WORD regstr = 0x39
|
||||
_INVERTIQ2 regstr = 0x3b
|
||||
_DIO_MAPPING_1 regstr = 0x40
|
||||
_DIO_MAPPING_2 regstr = 0x41
|
||||
_VERSION regstr = 0x42
|
||||
_PA_DAC regstr = 0x4d
|
||||
)
|
||||
@@ -0,0 +1,105 @@
|
||||
// Code generated by "stringer -type _reg ./sx127x/"; DO NOT EDIT.
|
||||
|
||||
package sx127x
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[_FIFO-0]
|
||||
_ = x[_OP_MODE-1]
|
||||
_ = x[_FRF_MSB-6]
|
||||
_ = x[_FRF_MID-7]
|
||||
_ = x[_FRF_LSB-8]
|
||||
_ = x[_PA_CONFIG-9]
|
||||
_ = x[_PA_RAMP-10]
|
||||
_ = x[_OCP-11]
|
||||
_ = x[_LNA-12]
|
||||
_ = x[_FIFO_ADDR_PTR-13]
|
||||
_ = x[_FIFO_TX_BASE_ADDR-14]
|
||||
_ = x[_FIFO_RX_BASE_ADDR-15]
|
||||
_ = x[_FIFO_RX_CURRENT_ADDR-16]
|
||||
_ = x[_IRQ_FLAGS_MASK-17]
|
||||
_ = x[_IRQ_FLAGS-18]
|
||||
_ = x[_RX_NB_BYTES-19]
|
||||
_ = x[_PKT_SNR_VALUE-25]
|
||||
_ = x[_PKT_RSSI_VALUE-26]
|
||||
_ = x[_RSSI_VALUE-27]
|
||||
_ = x[_MODEM_CONFIG_1-29]
|
||||
_ = x[_MODEM_CONFIG_2-30]
|
||||
_ = x[_SYMB_TIMEOUT_LSB-31]
|
||||
_ = x[_PREAMBLE_MSB-32]
|
||||
_ = x[_PREAMBLE_LSB-33]
|
||||
_ = x[_PAYLOAD_LENGTH-34]
|
||||
_ = x[_MAX_PAYLOAD_LENGTH-35]
|
||||
_ = x[_HOP_PERIOD-36]
|
||||
_ = x[_MODEM_CONFIG_3-38]
|
||||
_ = x[_FREQ_ERROR_MSB-40]
|
||||
_ = x[_FREQ_ERROR_MID-41]
|
||||
_ = x[_FREQ_ERROR_LSB-42]
|
||||
_ = x[_RSSI_WIDEBAND-44]
|
||||
_ = x[_DETECTION_OPTIMIZE-49]
|
||||
_ = x[_INVERTIQ-51]
|
||||
_ = x[_DETECTION_THRESHOLD-55]
|
||||
_ = x[_SYNC_WORD-57]
|
||||
_ = x[_INVERTIQ2-59]
|
||||
_ = x[_DIO_MAPPING_1-64]
|
||||
_ = x[_DIO_MAPPING_2-65]
|
||||
_ = x[_VERSION-66]
|
||||
_ = x[_PA_DAC-77]
|
||||
}
|
||||
|
||||
const __reg_name = "_FIFO_OP_MODE_FRF_MSB_FRF_MID_FRF_LSB_PA_CONFIG_PA_RAMP_OCP_LNA_FIFO_ADDR_PTR_FIFO_TX_BASE_ADDR_FIFO_RX_BASE_ADDR_FIFO_RX_CURRENT_ADDR_IRQ_FLAGS_MASK_IRQ_FLAGS_RX_NB_BYTES_PKT_SNR_VALUE_PKT_RSSI_VALUE_RSSI_VALUE_MODEM_CONFIG_1_MODEM_CONFIG_2_SYMB_TIMEOUT_LSB_PREAMBLE_MSB_PREAMBLE_LSB_PAYLOAD_LENGTH_MAX_PAYLOAD_LENGTH_HOP_PERIOD_MODEM_CONFIG_3_FREQ_ERROR_MSB_FREQ_ERROR_MID_FREQ_ERROR_LSB_RSSI_WIDEBAND_DETECTION_OPTIMIZE_INVERTIQ_DETECTION_THRESHOLD_SYNC_WORD_INVERTIQ2_DIO_MAPPING_1_DIO_MAPPING_2_VERSION_PA_DAC"
|
||||
|
||||
var __reg_map = map[regstr]string{
|
||||
0: __reg_name[0:5],
|
||||
1: __reg_name[5:13],
|
||||
6: __reg_name[13:21],
|
||||
7: __reg_name[21:29],
|
||||
8: __reg_name[29:37],
|
||||
9: __reg_name[37:47],
|
||||
10: __reg_name[47:55],
|
||||
11: __reg_name[55:59],
|
||||
12: __reg_name[59:63],
|
||||
13: __reg_name[63:77],
|
||||
14: __reg_name[77:95],
|
||||
15: __reg_name[95:113],
|
||||
16: __reg_name[113:134],
|
||||
17: __reg_name[134:149],
|
||||
18: __reg_name[149:159],
|
||||
19: __reg_name[159:171],
|
||||
25: __reg_name[171:185],
|
||||
26: __reg_name[185:200],
|
||||
27: __reg_name[200:211],
|
||||
29: __reg_name[211:226],
|
||||
30: __reg_name[226:241],
|
||||
31: __reg_name[241:258],
|
||||
32: __reg_name[258:271],
|
||||
33: __reg_name[271:284],
|
||||
34: __reg_name[284:299],
|
||||
35: __reg_name[299:318],
|
||||
36: __reg_name[318:329],
|
||||
38: __reg_name[329:344],
|
||||
40: __reg_name[344:359],
|
||||
41: __reg_name[359:374],
|
||||
42: __reg_name[374:389],
|
||||
44: __reg_name[389:403],
|
||||
49: __reg_name[403:422],
|
||||
51: __reg_name[422:431],
|
||||
55: __reg_name[431:451],
|
||||
57: __reg_name[451:461],
|
||||
59: __reg_name[461:471],
|
||||
64: __reg_name[471:485],
|
||||
65: __reg_name[485:499],
|
||||
66: __reg_name[499:507],
|
||||
77: __reg_name[507:514],
|
||||
}
|
||||
|
||||
func (i regstr) String() string {
|
||||
if str, ok := __reg_map[i]; ok {
|
||||
return str
|
||||
}
|
||||
return "_reg(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
+113
-23
@@ -46,6 +46,7 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"time"
|
||||
@@ -64,30 +65,56 @@ type SPI interface {
|
||||
Tx(writeBuffer, readBuffer []byte) error
|
||||
}
|
||||
|
||||
const debugBufSize = 255
|
||||
|
||||
type DeviceLoRa struct {
|
||||
rst PinOutput
|
||||
cs PinOutput
|
||||
bus SPI
|
||||
headerType lora.HeaderType
|
||||
// onNoPacket is called while waiting for a packet to be received in a tight loop.
|
||||
onNoPacket func()
|
||||
// Debugging buffers.
|
||||
debugRead, debugWrite, debugMask [debugBufSize]byte
|
||||
}
|
||||
|
||||
func DefaultConfig(freq lora.Frequency) lora.Config {
|
||||
return lora.Config{
|
||||
Frequency: freq,
|
||||
SpreadFactor: lora.SF7,
|
||||
Bandwidth: 125 * lora.KiloHertz,
|
||||
CodingRate: lora.CR4_5,
|
||||
PreambleLength: 12,
|
||||
HeaderType: lora.HeaderExplicit,
|
||||
MaxImplicitPayloadLength: 0, // No need to be set when working with explicit headers.
|
||||
CRC: true,
|
||||
SyncWord: publicSyncword,
|
||||
TxPower: 0,
|
||||
LDRO: false,
|
||||
IQInversion: false,
|
||||
}
|
||||
}
|
||||
|
||||
// NewLoRa returns a new SX127x device. It performs no I/O operations.
|
||||
// The caller should call Configure before using the device.
|
||||
func NewLoRa(bus SPI, cs, reset PinOutput) *DeviceLoRa {
|
||||
d := DeviceLoRa{bus: bus, cs: cs, rst: reset}
|
||||
d := DeviceLoRa{bus: bus, cs: cs, rst: reset, onNoPacket: runtime.Gosched}
|
||||
return &d
|
||||
}
|
||||
|
||||
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")
|
||||
errIRQNotCleared = errors.New("IRQs not cleared")
|
||||
ErrDeviceBusy = errors.New("device busy")
|
||||
ErrCRC = errors.New("crc error")
|
||||
ErrRxTimeout = errors.New("rx timeout")
|
||||
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")
|
||||
errIRQNotCleared = errors.New("IRQs not cleared")
|
||||
ErrDeviceBusy = errors.New("device busy")
|
||||
ErrCRC = errors.New("crc error")
|
||||
ErrRxTimeout = errors.New("rx timeout")
|
||||
errImplicitPacketTooLarge = errors.New("packet length exceeds MaxImplicitPayloadLength")
|
||||
)
|
||||
|
||||
func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
||||
@@ -104,6 +131,8 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
||||
err = errUnsupportedBandwidth
|
||||
case cfg.HeaderType != lora.HeaderImplicit && cfg.HeaderType != lora.HeaderExplicit:
|
||||
err = errors.New("bad header type")
|
||||
case cfg.HeaderType == lora.HeaderImplicit && cfg.MaxImplicitPayloadLength == 0:
|
||||
err = errors.New("MaxImplicitPayloadLength parameter must be set when working with implicit headers")
|
||||
case cfg.TxPower > 20:
|
||||
err = errors.New("bad tx power")
|
||||
}
|
||||
@@ -161,6 +190,9 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isImplicit {
|
||||
d.setMaxPayloadLength(cfg.MaxImplicitPayloadLength)
|
||||
}
|
||||
err = d.enableIQInversion(cfg.IQInversion)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -169,16 +201,25 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.enableLowDataRateOptimization(cfg.LDRO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.SetSymbolTimeout(1023) // Set timeout to max value.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
const rxStart = 128
|
||||
d.write8(regFIFO_TX_BASE_ADDR, 0)
|
||||
d.write8(regFIFO_RX_BASE_ADDR, rxStart)
|
||||
d.setHopPeriod(0)
|
||||
// Go insto standby mode to write to FIFO related registers.
|
||||
err = d.SetOpMode(OpStandby)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.write8(regFIFO_TX_BASE_ADDR, 0)
|
||||
d.write8(regFIFO_RX_BASE_ADDR, 0)
|
||||
d.write8(regFIFO_ADDR_PTR, 0)
|
||||
d.headerType = cfg.HeaderType
|
||||
return d.SetOpMode(OpStandby)
|
||||
return d.SetOpMode(OpSleep) // Return to sleep mode to conserve power.
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) Reset() {
|
||||
@@ -211,7 +252,7 @@ func (d *DeviceLoRa) SetOpMode(mode OpMode) error {
|
||||
}
|
||||
got, err := d.GetOpMode()
|
||||
if err != nil {
|
||||
return err
|
||||
return d.wrapErr(err)
|
||||
}
|
||||
if got != mode {
|
||||
return errors.New("tried to set opmode " + mode.String() + ", got " + got.String())
|
||||
@@ -347,15 +388,21 @@ func (d *DeviceLoRa) RxSingle(dst []byte) (uint8, error) {
|
||||
|
||||
// Loop until Rx received or timeout IRQ.
|
||||
var irq uint8
|
||||
for irq&(irqRXDONE_MASK|irqRXTOUT_MASK) == 0 {
|
||||
runtime.Gosched() // Yield to scheduler.
|
||||
for {
|
||||
irq, err = d.read8(regIRQ_FLAGS)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if irq&(irqRXDONE_MASK|irqRXTOUT_MASK) != 0 {
|
||||
break
|
||||
}
|
||||
d.onNoPacket()
|
||||
}
|
||||
// Check for payload integrity and timeout interrupt.
|
||||
if irq&irqCRCERR_MASK != 0 {
|
||||
if d.headerType == lora.HeaderImplicit {
|
||||
return 0, errImplicitPacketTooLarge
|
||||
}
|
||||
return 0, ErrCRC
|
||||
} else if irq&irqRXTOUT_MASK != 0 {
|
||||
return 0, ErrRxTimeout
|
||||
@@ -406,17 +453,34 @@ func (d *DeviceLoRa) RxContinuous(ctx context.Context, fn rxCallback) error {
|
||||
|
||||
// Loop until Rx received or timeout IRQ.
|
||||
var irq uint8
|
||||
count := 0
|
||||
defer func() {
|
||||
println("RxContinuous looped", count, "times")
|
||||
}()
|
||||
for ctx.Err() == nil {
|
||||
runtime.Gosched() // Yield to scheduler.
|
||||
irq, err = d.read8(regIRQ_FLAGS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if irq&irqRXDONE_MASK != 0 {
|
||||
nbBytes, _ := d.read8(regRX_NB_BYTES)
|
||||
if nbBytes == 0 {
|
||||
return fmt.Errorf("received packet with 0 bytes;irq=%08b; stat=%s", irq, d.statusString())
|
||||
}
|
||||
err = d.gotRxContinous(fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
d.onNoPacket()
|
||||
}
|
||||
count++
|
||||
op, err = d.GetOpMode()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if op != OpRx {
|
||||
return errors.New("unexpected op mode change during RxContinuous to " +
|
||||
op.String() + " with irqs:" + irqFlagsString(irq))
|
||||
}
|
||||
}
|
||||
return ctx.Err()
|
||||
@@ -597,6 +661,9 @@ func (d *DeviceLoRa) ReadConfig() (cfg lora.Config, err error) {
|
||||
cfg.SyncWord = sync
|
||||
// Read Frequency.
|
||||
err = d.read(regFRF_MSB, buf[:3])
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
freq := uint64(buf[0])<<16 | uint64(buf[1])<<8 | uint64(buf[2])
|
||||
cfg.Frequency = lora.Frequency((freq * 15625) >> 8)
|
||||
// Read IQ inversion.
|
||||
@@ -617,6 +684,13 @@ func (d *DeviceLoRa) setPreambleLength(pLen uint16) error {
|
||||
return d.write8(regPREAMBLE_LSB, buf[1])
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) setMaxPayloadLength(plen uint8) error {
|
||||
if plen == 0 {
|
||||
return io.ErrShortBuffer
|
||||
}
|
||||
return d.write8(regPAYLOAD_LENGTH, plen)
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) enableTxContinuousMode(enable bool) error {
|
||||
return d.writeMasked8(regMODEM_CONFIG_2, 1<<3, b2u8(enable)<<3)
|
||||
}
|
||||
@@ -729,7 +803,7 @@ func (d *DeviceLoRa) enableLowFrequencyMode(b bool) error {
|
||||
|
||||
// enableCRC enables/disables CRC generation and checking.
|
||||
func (d *DeviceLoRa) enableCRC(b bool) error {
|
||||
const crcMask = 1 << 2
|
||||
const crcMask = 0b11 << 2
|
||||
return d.writeMasked8(regMODEM_CONFIG_2, crcMask, b2u8(b)<<2)
|
||||
}
|
||||
|
||||
@@ -768,9 +842,14 @@ func (d *DeviceLoRa) writeMasked8(addr uint8, mask, value byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existing &^= mask // remove mask bits from register value.
|
||||
existing |= mask & value // add value's bits as masked.
|
||||
return d.write8(addr, existing)
|
||||
err = d.write8(addr, existing)
|
||||
if debugBufSize > 0 {
|
||||
d.debugMask[addr] = mask
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DeviceLoRa) read8(addr byte) (byte, error) {
|
||||
@@ -779,6 +858,9 @@ func (d *DeviceLoRa) read8(addr byte) (byte, error) {
|
||||
d.csEnable(true)
|
||||
err := d.bus.Tx(writeBuf, readBuf)
|
||||
d.csEnable(false)
|
||||
if debugBufSize > 0 {
|
||||
d.debugRead[addr] = readBuf[1]
|
||||
}
|
||||
return readBuf[1], err
|
||||
}
|
||||
|
||||
@@ -788,6 +870,11 @@ func (d *DeviceLoRa) write8(addr, value byte) error {
|
||||
d.csEnable(true)
|
||||
err := d.bus.Tx(writeBuf, readBuf)
|
||||
d.csEnable(false)
|
||||
if debugBufSize > 0 {
|
||||
d.debugWrite[addr] = value
|
||||
d.debugMask[addr] = 0xFF
|
||||
d.read8(addr) // refresh address value.
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -834,7 +921,7 @@ func (d *DeviceLoRa) readerToLastPacket() (_ *fifoReader, err error) {
|
||||
runtime.Gosched()
|
||||
}
|
||||
if irqFlags&mustBeUnset != 0 {
|
||||
return nil, errors.New("timeout waiting for IRQ before reading packet")
|
||||
return nil, errors.New("timeout waiting for IRQ before reading packet:" + irqFlagsString(irqFlags))
|
||||
}
|
||||
|
||||
// We now know that the packet has been received succesfully. Proceed to read.
|
||||
@@ -846,6 +933,9 @@ func (d *DeviceLoRa) readerToLastPacket() (_ *fifoReader, err error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if numBytes == 0 {
|
||||
return nil, errors.New("readerToNextPacket called with RX_NB_BYTES=0")
|
||||
}
|
||||
err = d.write8(regFIFO_ADDR_PTR, curraddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user