diff --git a/examples/transmitter/transmitter.go b/examples/transmitter/transmitter.go index 5b55dff..7dc2a3f 100644 --- a/examples/transmitter/transmitter.go +++ b/examples/transmitter/transmitter.go @@ -51,20 +51,27 @@ func main() { if err != nil { panic(err.Error()) } - 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()) + 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) + if err != nil { + println("got error transmitting:", err.Error()) + } else { + 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])) + } } - fmt.Printf("random bytes: %b\nelapsed:%s\n", rng, time.Since(tstart).String()) } diff --git a/sx127x/sx127x.go b/sx127x/sx127x.go index 967cb01..abac828 100644 --- a/sx127x/sx127x.go +++ b/sx127x/sx127x.go @@ -84,6 +84,9 @@ var ( 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") ) func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) { @@ -250,6 +253,7 @@ func (d *DeviceLoRa) EnableAutoGainControl(b bool) error { return d.writeMasked8(regMODEM_CONFIG_3, agcMask, b2u8(b)<<2) } +// Tx sends packet over LoRa network and blocks until packet is done sending. func (d *DeviceLoRa) Tx(packet []byte) (err error) { if len(packet) > 255 { return errors.New("packet too long") @@ -291,18 +295,19 @@ func (d *DeviceLoRa) Tx(packet []byte) (err error) { 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 - } + var irq uint8 + for irq&irqTXDONE_MASK == 0 { runtime.Gosched() // Yield to scheduler. + irq, err = d.read8(regIRQ_FLAGS) + if err != nil { + d.SetOpMode(OpSleep) // Back to sleep in case of error. + return err + } + } + // Go back to sleep to be able to write to static registers. + err = d.SetOpMode(OpSleep) + if err != nil { + return err } err = d.clearIRQ(irqTXDONE_MASK) if err != nil { @@ -311,6 +316,84 @@ func (d *DeviceLoRa) Tx(packet []byte) (err error) { return nil } +func (d *DeviceLoRa) RxSingle(dst []byte) (uint8, error) { + op, err := d.GetOpMode() + switch { + case err != nil: + // err already set + case len(dst) < 255: + err = io.ErrShortBuffer + case op == OpTx || op == OpRx || op == OpRxSingle: + err = ErrDeviceBusy // Device is currently transmitting or receiving. + } + if err != nil { + return 0, err + } + // Reset packet pointers. + const fifoAddr = 0 + err = d.prepareForRx(fifoAddr) + if err != nil { + return 0, err + } + // Begin looking for packets immediately until first one found (RxSingle mode). + err = d.SetOpMode(OpRxSingle) + if err != nil { + return 0, err + } + defer d.SetOpMode(OpSleep) // Ensure Sleep Mode on exit. + + // Loop until Rx received or timeout IRQ. + var irq uint8 + for irq&(irqRXDONE_MASK|irqRXTOUT_MASK) == 0 { + runtime.Gosched() // Yield to scheduler. + irq, err = d.read8(regIRQ_FLAGS) + if err != nil { + return 0, err + } + } + // Check for payload integrity and timeout interrupt. + if irq&irqCRCERR_MASK != 0 { + return 0, ErrCRC + } else if irq&irqRXTOUT_MASK != 0 { + return 0, ErrRxTimeout + } + // TODO(soypat): This should already be in standby mode according to datasheet. + // remove once confirmed it does not affect behaviour. Needed to read FIFO addr. + err = d.SetOpMode(OpStandby) + if err != nil { + return 0, err + } + // Ready to read packet! Rewrite FifoAddrPtr just in case... + d.write8(regFIFO_ADDR_PTR, fifoAddr) + nbBytes, err := d.read8(regRX_NB_BYTES) + if err != nil { + return 0, err + } + for i := uint8(0); i < nbBytes; i++ { + dst[i], err = d.read8(regFIFO) + if err != nil { + return i, err + } + } + return nbBytes, nil +} + +func (d *DeviceLoRa) prepareForRx(fifoAddr uint8) (err error) { + err = d.SetOpMode(OpStandby) // Standby mode needed so that FIFO can be written/read. + if err != nil { + return err + } + err = d.clearIRQ(irqRXDONE_MASK | irqHEADER_MASK | irqCRCERR_MASK | irqRXTOUT_MASK) + if err != nil { + return err + } + err = d.write8(regFIFO_RX_BASE_ADDR, fifoAddr) + if err != nil { + return err + } + return d.write8(regFIFO_ADDR_PTR, fifoAddr) +} + // clearIRQ clears IRQ bits indicated by toClear: // - bit 0: CAD detected interrupt // - bit 1: FHSS change channel interrupt @@ -325,8 +408,10 @@ func (d *DeviceLoRa) clearIRQ(toClear uint8) error { if err != nil { return err } + time.Sleep(500 * time.Millisecond) reg, _ := d.read8(regIRQ_FLAGS) if reg&toClear != 0 { + println(reg, toClear) return errIRQNotCleared } return nil @@ -347,6 +432,8 @@ func (d *DeviceLoRa) RandomU32() (rnd uint32, err error) { // 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. +// +// See EstimateReadFromRSSIPeriod method. func (d *DeviceLoRa) RandomRead(dst []byte, readFromRSSIPeriod time.Duration) error { // Disable ALL irqs err := d.clearIRQ(0xff) @@ -596,8 +683,8 @@ func (d *DeviceLoRa) enableCRC(b bool) error { // // 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") + if symbTimeout > 0x3FF || symbTimeout < 4 { + return errors.New("symbol timeout must be in range 4..1023") } err = d.writeMasked8(regMODEM_CONFIG_2, 0b11, byte(symbTimeout>>8)) if err != nil {