mirror of
https://github.com/soypat/lora.git
synced 2026-08-24 14:19:00 +00:00
add Random* methods
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"machine"
|
"machine"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -51,4 +52,19 @@ func main() {
|
|||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
println("config success")
|
println("config success")
|
||||||
|
for {
|
||||||
|
rssiRead, err := dev.EstimateReadFromRSSIPeriod(time.Second)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
println("rssiRead:", rssiRead.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
tstart := time.Now()
|
||||||
|
var rng [4]byte
|
||||||
|
err = dev.RandomRead(rng[:], 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
fmt.Printf("random bytes: %b\nelapsed:%s\n", rng, time.Since(tstart).String())
|
||||||
}
|
}
|
||||||
|
|||||||
+215
-32
@@ -46,6 +46,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/soypat/lora"
|
"github.com/soypat/lora"
|
||||||
@@ -63,9 +64,10 @@ type SPI interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DeviceLoRa struct {
|
type DeviceLoRa struct {
|
||||||
rst PinOutput
|
rst PinOutput
|
||||||
cs PinOutput
|
cs PinOutput
|
||||||
bus SPI
|
bus SPI
|
||||||
|
headerType lora.HeaderType
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoRa(bus SPI, cs, reset PinOutput) *DeviceLoRa {
|
func NewLoRa(bus SPI, cs, reset PinOutput) *DeviceLoRa {
|
||||||
@@ -81,6 +83,7 @@ var (
|
|||||||
errBadMode = errors.New("bad mode: sx127x in FSK/OOK mode, not LoRa or viceversa")
|
errBadMode = errors.New("bad mode: sx127x in FSK/OOK mode, not LoRa or viceversa")
|
||||||
errBadCodingRate = errors.New("bad coding rate")
|
errBadCodingRate = errors.New("bad coding rate")
|
||||||
errUnsupportedBandwidth = errors.New("bandwidth too high for frequency around 169MHz")
|
errUnsupportedBandwidth = errors.New("bandwidth too high for frequency around 169MHz")
|
||||||
|
errIRQNotCleared = errors.New("IRQs not cleared")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
||||||
@@ -95,6 +98,10 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
|||||||
err = errBadCodingRate
|
err = errBadCodingRate
|
||||||
case cfg.Frequency < 175*lora.MegaHertz && cfg.Bandwidth > 125*lora.KiloHertz:
|
case cfg.Frequency < 175*lora.MegaHertz && cfg.Bandwidth > 125*lora.KiloHertz:
|
||||||
err = errUnsupportedBandwidth
|
err = errUnsupportedBandwidth
|
||||||
|
case cfg.HeaderType != lora.HeaderImplicit && cfg.HeaderType != lora.HeaderExplicit:
|
||||||
|
err = errors.New("bad header type")
|
||||||
|
case cfg.TxPower > 20:
|
||||||
|
err = errors.New("bad tx power")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -121,7 +128,7 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = d.EnableAutoAGC(true)
|
err = d.EnableAutoGainControl(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -145,7 +152,8 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = d.enableImplicitHeaderMode(cfg.HeaderType == lora.HeaderImplicit)
|
isImplicit := cfg.HeaderType == lora.HeaderImplicit
|
||||||
|
err = d.enableImplicitHeaderMode(isImplicit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -157,17 +165,44 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = d.setTimeoutInSymbols(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)
|
d.setHopPeriod(0)
|
||||||
return nil
|
d.headerType = cfg.HeaderType
|
||||||
|
return d.SetOpMode(OpStandby)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DeviceLoRa) Reset() {
|
||||||
|
d.rst(true)
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
d.rst(false)
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
d.rst(true)
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsConnected reads the version register and checks if it matches the expected value.
|
||||||
|
func (d *DeviceLoRa) IsConnected() bool {
|
||||||
|
version, err := d.read8(regVERSION)
|
||||||
|
if version == expectedVersion && err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOpMode sets the operating mode of the SX127x to a LoRa mode.
|
||||||
func (d *DeviceLoRa) SetOpMode(mode OpMode) error {
|
func (d *DeviceLoRa) SetOpMode(mode OpMode) error {
|
||||||
// We always write the LoRa mode bit
|
// We always write the LoRa mode bit
|
||||||
err := d.write8(regOP_MODE, byte(mode|opLoRaBit))
|
err := d.write8(regOP_MODE, byte(mode|opLoRaBit))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if mode == OpSleep {
|
if mode == OpSleep || true {
|
||||||
time.Sleep(15 * time.Millisecond) // TODO: do we need this sleep?
|
time.Sleep(15 * time.Millisecond) // TODO: do we need this sleep?
|
||||||
}
|
}
|
||||||
got, err := d.GetOpMode()
|
got, err := d.GetOpMode()
|
||||||
@@ -180,6 +215,8 @@ func (d *DeviceLoRa) SetOpMode(mode OpMode) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOpMode returns the current operating mode of the SX127x. It returns an error
|
||||||
|
// if the device is not in LoRa mode.
|
||||||
func (d *DeviceLoRa) GetOpMode() (OpMode, error) {
|
func (d *DeviceLoRa) GetOpMode() (OpMode, error) {
|
||||||
const invalidOpMode = 0xff
|
const invalidOpMode = 0xff
|
||||||
got, err := d.read8(regOP_MODE)
|
got, err := d.read8(regOP_MODE)
|
||||||
@@ -206,6 +243,174 @@ func (d *DeviceLoRa) SetLNAGain(gain uint8) error {
|
|||||||
return d.writeMasked8(regLNA, lnaMask, gain<<5)
|
return d.writeMasked8(regLNA, lnaMask, gain<<5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableAutoGainControl 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) EnableAutoGainControl(b bool) error {
|
||||||
|
const agcMask = 1 << 2
|
||||||
|
return d.writeMasked8(regMODEM_CONFIG_3, agcMask, b2u8(b)<<2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeviceLoRa) Tx(packet []byte) (err error) {
|
||||||
|
if len(packet) > 255 {
|
||||||
|
return errors.New("packet too long")
|
||||||
|
}
|
||||||
|
opmode, err := d.GetOpMode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if opmode != OpStandby && opmode != OpSleep {
|
||||||
|
println("unexpected opmode before Tx:", opmode.String())
|
||||||
|
err = d.SetOpMode(OpSleep)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = d.write8(regPAYLOAD_LENGTH, uint8(len(packet)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
plen, _ := d.read8(regPAYLOAD_LENGTH)
|
||||||
|
if plen != uint8(len(packet)) {
|
||||||
|
return errors.New("payload length unable to be set correctly")
|
||||||
|
}
|
||||||
|
// FIFO registers only accesible in Standby mode.
|
||||||
|
err = d.SetOpMode(OpStandby)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.write8(regFIFO_TX_BASE_ADDR, 0)
|
||||||
|
d.write8(regFIFO_ADDR_PTR, 0)
|
||||||
|
for i := 0; i < len(packet); i++ {
|
||||||
|
err := d.write8(regFIFO, packet[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Begin transmitting immediately.
|
||||||
|
err = d.SetOpMode(OpTx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
counts := 0
|
||||||
|
var reg uint8
|
||||||
|
for {
|
||||||
|
counts++
|
||||||
|
reg, err = d.read8(regIRQ_FLAGS)
|
||||||
|
if reg&irqTXDONE_MASK != 0 || err != nil {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
runtime.Gosched() // Yield to scheduler.
|
||||||
|
}
|
||||||
|
err = d.clearIRQ(irqTXDONE_MASK)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearIRQ clears IRQ bits indicated by toClear:
|
||||||
|
// - bit 0: CAD detected interrupt
|
||||||
|
// - bit 1: FHSS change channel interrupt
|
||||||
|
// - bit 2: CAD done interrupt
|
||||||
|
// - bit 3: Tx done interrupt
|
||||||
|
// - bit 4: Valid header received in Rx
|
||||||
|
// - bit 5: Payload CRC error
|
||||||
|
// - bit 6: Rx done interrupt
|
||||||
|
// - bit 7: Rx timeout interrupt
|
||||||
|
func (d *DeviceLoRa) clearIRQ(toClear uint8) error {
|
||||||
|
err := d.write8(regIRQ_FLAGS, toClear)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reg, _ := d.read8(regIRQ_FLAGS)
|
||||||
|
if reg&toClear != 0 {
|
||||||
|
return errIRQNotCleared
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandomU32 returns a random uint32 generated by reading the RSSI during
|
||||||
|
// Rx OpMode. This method should not be used while the device is operating.
|
||||||
|
func (d *DeviceLoRa) RandomU32() (rnd uint32, err error) {
|
||||||
|
var buf [4]byte
|
||||||
|
err = d.RandomRead(buf[:], 10*time.Millisecond)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return binary.LittleEndian.Uint32(buf[:]), d.SetOpMode(OpSleep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandomRead reads random byte data to dst by reading the RSSI during Rx OpMode.
|
||||||
|
// The period between RSSI reads is readFromRSSIPeriod. A higher readFromRSSIPeriod
|
||||||
|
// will typically result in higher entropy in the random data. 10ms is a reasonable period.
|
||||||
|
// This method will take approximately readFromRSSIPeriod*len(dst)*8 + 50ms to complete.
|
||||||
|
func (d *DeviceLoRa) RandomRead(dst []byte, readFromRSSIPeriod time.Duration) error {
|
||||||
|
// Disable ALL irqs
|
||||||
|
err := d.clearIRQ(0xff)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.SetOpMode(OpRx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(dst)*8; i++ {
|
||||||
|
time.Sleep(readFromRSSIPeriod)
|
||||||
|
val, err := d.read8(regRSSI_WIDEBAND)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Unfiltered RSSI value reading. Only takes the LSB value
|
||||||
|
dst[i/8] |= (val & 1) << (i % 8)
|
||||||
|
}
|
||||||
|
return d.SetOpMode(OpSleep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EstimateReadFromRSSIPeriod estimates the readFromRSSIPeriod required to read
|
||||||
|
// relatively random bits of data for testDuration time.
|
||||||
|
//
|
||||||
|
// It's recommended that one call this function several times with small
|
||||||
|
// durations and use a median value as a compromise between call duration
|
||||||
|
// and entropy. It is common to get values separated by orders of magnitude
|
||||||
|
// depending on whether there was a signal present during the test.
|
||||||
|
// See the [RandomRead] method.
|
||||||
|
func (d *DeviceLoRa) EstimateReadFromRSSIPeriod(testDuration time.Duration) (time.Duration, error) {
|
||||||
|
err := d.clearIRQ(0xff)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
err = d.SetOpMode(OpRx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
start := time.Now()
|
||||||
|
val, _ := d.read8(regRSSI_WIDEBAND)
|
||||||
|
lastBit := val&1 != 0
|
||||||
|
maxBitHoldTime := time.Duration(0)
|
||||||
|
lastBitChangeTime := start
|
||||||
|
var readTime = time.Now()
|
||||||
|
for readTime.Sub(start) < testDuration {
|
||||||
|
val, err := d.read8(regRSSI_WIDEBAND)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
readTime = time.Now()
|
||||||
|
bit0 := val&1 != 0
|
||||||
|
if bit0 != lastBit {
|
||||||
|
lastBit = bit0
|
||||||
|
elapsedSinceBitChange := readTime.Sub(lastBitChangeTime)
|
||||||
|
lastBitChangeTime = readTime
|
||||||
|
if elapsedSinceBitChange > maxBitHoldTime {
|
||||||
|
maxBitHoldTime = elapsedSinceBitChange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxBitHoldTime, d.SetOpMode(OpSleep)
|
||||||
|
}
|
||||||
|
|
||||||
// setBandwidth sets the bandwidth of the LoRa modulation.
|
// setBandwidth sets the bandwidth of the LoRa modulation.
|
||||||
func (d *DeviceLoRa) setBandwidth(bw lora.Frequency) error {
|
func (d *DeviceLoRa) setBandwidth(bw lora.Frequency) error {
|
||||||
const bwMask = 0b1111 << 4
|
const bwMask = 0b1111 << 4
|
||||||
@@ -311,7 +516,9 @@ func (d *DeviceLoRa) setTxPower(txPow int8) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return d.write8(regOCP, 0) // TODO: Disable OCP?
|
// Set to minimal current.
|
||||||
|
return d.setOCP(45)
|
||||||
|
// return d.write8(regOCP, 0) // TODO: Disable OCP?
|
||||||
}
|
}
|
||||||
|
|
||||||
// setFrequency sets the center radio frequency.
|
// setFrequency sets the center radio frequency.
|
||||||
@@ -365,13 +572,6 @@ func (d *DeviceLoRa) setSyncWord(sync byte) error {
|
|||||||
return d.write8(regSYNC_WORD, sync)
|
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
|
// enableLowDataRateOptimization enables/disables Low Data Rate Optimization, a
|
||||||
// feature which is mandated when symbol length exceeds 16ms.
|
// feature which is mandated when symbol length exceeds 16ms.
|
||||||
func (d *DeviceLoRa) enableLowDataRateOptimization(b bool) error {
|
func (d *DeviceLoRa) enableLowDataRateOptimization(b bool) error {
|
||||||
@@ -453,23 +653,6 @@ func (d *DeviceLoRa) csEnable(b bool) {
|
|||||||
d.cs(!b)
|
d.cs(!b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeviceLoRa) Reset() {
|
|
||||||
d.rst(true)
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
d.rst(false)
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
d.rst(true)
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DeviceLoRa) IsConnected() bool {
|
|
||||||
version, err := d.read8(regVERSION)
|
|
||||||
if version == expectedVersion && err == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func b2u8(b bool) uint8 {
|
func b2u8(b bool) uint8 {
|
||||||
if b {
|
if b {
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
Reference in New Issue
Block a user