// Package sx126x provides a driver for SX126x LoRa transceivers. // Inspired from https://github.com/Lora-net/sx126x_driver/ package sx126x import ( "errors" "time" "machine" "tinygo.org/x/drivers" "tinygo.org/x/drivers/lora" ) var ( errWaitWhileBusyTimeout = errors.New("WaitWhileBusy Timeout") errLowPowerTxNotSupported = errors.New("RFSWITCH_TX_LP not supported") errRadioNotFound = errors.New("LoRa radio not found") errUnexpectedRxRadioEvent = errors.New("Unexpected Radio Event during RX") errUnexpectedTxRadioEvent = errors.New("Unexpected Radio Event during TX") ) const ( DEVICE_TYPE_SX1261 = iota DEVICE_TYPE_SX1262 = iota DEVICE_TYPE_SX1268 = iota ) const ( RFSWITCH_RX = iota RFSWITCH_TX_LP = iota RFSWITCH_TX_HP = iota ) const ( PERIOD_PER_SEC = (uint32)(1000000 / 15.625) // SX1261 DS 13.1.4 SPI_BUFFER_SIZE = 256 RADIOEVENTCHAN_SIZE = 1 ) // Device wraps an SPI connection to a SX126x device. type Device struct { spi drivers.SPI // SPI bus for module communication rstPin machine.Pin // GPIO for reset pin radioEventChan chan lora.RadioEvent // Channel for Receiving events loraConf lora.Config // Current Lora configuration controller RadioController // to manage interactions with the radio deepSleep bool // Internal Sleep state deviceType int // sx1261,sx1262,sx1268 (defaults sx1261) spiTxBuf []byte // global Tx buffer to avoid heap allocations in interrupt spiRxBuf []byte // global Rx buffer to avoid heap allocations in interrupt } // New creates a new SX126x connection. func New(spi drivers.SPI) *Device { return &Device{ spi: spi, radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE), spiTxBuf: make([]byte, SPI_BUFFER_SIZE), spiRxBuf: make([]byte, SPI_BUFFER_SIZE), } } const ( SX126X_RTC_FREQ_IN_HZ uint32 = 64000 ) // -------------------------------------------------- // Helper functions // -------------------------------------------------- // timeoutMsToRtcSteps converts Timeout (in ms) to RTC Steps func timeoutMsToRtcSteps(timeoutMs uint32) uint32 { r := uint32(timeoutMs * (SX126X_RTC_FREQ_IN_HZ / 1000)) return r } // -------------------------------------------------- // // Channel and events // // -------------------------------------------------- // Get the RadioEvent channel of the device func (d *Device) GetRadioEventChan() chan lora.RadioEvent { return d.radioEventChan } // Specify device type (SX1261/2/8) func (d *Device) SetDeviceType(devType int) { d.deviceType = devType } // SetRadioControl let you define the RadioController func (d *Device) SetRadioController(rc RadioController) error { d.controller = rc if err := d.controller.Init(); err != nil { return err } d.controller.SetupInterrupts(d.HandleInterrupt) return nil } // -------------------------------------------------- // Operational modes functions // -------------------------------------------------- func (d *Device) Reset() { d.rstPin.Low() time.Sleep(100 * time.Millisecond) d.rstPin.High() time.Sleep(100 * time.Millisecond) } // DetectDevice() tries to detect the radio module by changing SyncWord value func (d *Device) DetectDevice() bool { bak := d.GetSyncWord() d.SetSyncWord(0xBEEF) tmp := d.GetSyncWord() if tmp != 0xBEEF { return false } else { d.SetSyncWord(bak) return true } } // SetSleep sets the device in SLEEP mode with the lowest current consumption possible. func (d *Device) SetSleep() { d.ExecSetCommand(SX126X_CMD_SET_SLEEP, []uint8{SX126X_SLEEP_START_WARM | SX126X_SLEEP_RTC_OFF}) } // SetStandby sets the device in a configuration mode which is at an intermediate level of consumption func (d *Device) SetStandby() { d.ExecSetCommand(SX126X_CMD_SET_STANDBY, []uint8{SX126X_STANDBY_RC}) } // SetFs sets the device in frequency synthesis mode where the PLL is locked to the carrier frequency. func (d *Device) SetFs() { d.ExecSetCommand(SX126X_CMD_SET_FS, []uint8{}) } // SetTxContinuousWave set device in test mode to generate a continuous wave (RF tone) func (d *Device) SetTxContinuousWave() { if d.controller != nil { d.controller.SetRfSwitchMode(RFSWITCH_TX_HP) } d.ExecSetCommand(SX126X_CMD_SET_TX_CONTINUOUS_WAVE, []uint8{}) } // SetTxContinuousPreamble set device in test mode to constantly modulate LoRa preamble symbols. // Take care to initialize all Lora settings like it's done in Tx before calling this function // If you don't init properly all the settings, it'll fail func (d *Device) SetTxContinuousPreamble() { if d.controller != nil { d.controller.SetRfSwitchMode(RFSWITCH_TX_HP) } d.ExecSetCommand(SX126X_CMD_SET_TX_INFINITE_PREAMBLE, []uint8{}) } // SetTx() sets the device in TX mode // timeout is expressed in RTC Step unit (15uS) // The device will stay in Tx until countdown or packet transmitted // Value of 0x000000 will disable timer and device will stay TX func (d *Device) SetTx(timeoutRtcStep uint32) { var p [3]uint8 p[0] = uint8((timeoutRtcStep >> 16) & 0xFF) p[1] = uint8((timeoutRtcStep >> 8) & 0xFF) p[2] = uint8((timeoutRtcStep >> 0) & 0xFF) d.ExecSetCommand(SX126X_CMD_SET_TX, p[:]) } // SetRx() sets the device in RX mode // timeout is expressed in RTC Step unit (15uS) // Value of 0x000000 => No timeout. Rx Single mode. // Value of 0xffffff => Rx Continuous mode // Other values => Timeout active. The device remains in RX until countdown or packet received func (d *Device) SetRx(timeoutRtcStep uint32) { var p [3]uint8 p[0] = uint8(((timeoutRtcStep >> 16) & 0xFF)) p[1] = uint8(((timeoutRtcStep >> 8) & 0xFF)) p[2] = uint8(((timeoutRtcStep >> 0) & 0xFF)) d.ExecSetCommand(SX126X_CMD_SET_RX, p[:]) } // StopTimerOnPreamble allows the user to select if the timer is stopped upon preamble detection of SyncWord / header detection. func (d *Device) StopTimerOnPreamble(enable bool) { var p [1]uint8 if enable { p[0] = 1 } else { p[0] = 0 } d.ExecSetCommand(SX126X_CMD_STOP_TIMER_ON_PREAMBLE, p[:]) } // SetRegulatorMode sets the regulator more (depends on hardware implementation) func (d *Device) SetRegulatorMode(mode uint8) { p := []uint8{mode} d.ExecSetCommand(SX126X_CMD_SET_REGULATOR_MODE, p[:]) } // Calibrate starts the calibration of a block defined by calibParam func (d *Device) Calibrate(calibParam uint8) { p := []uint8{calibParam} d.ExecSetCommand(SX126X_CMD_CALIBRATE, p[:]) } // CalibrateImage calibrates the image rejection of the device for the device operating func (d *Device) CalibrateImage(freq uint32) { var calFreq [2]uint8 if freq > 900000000 { calFreq[0] = 0xE1 calFreq[1] = 0xE9 } else if freq > 850000000 { calFreq[0] = 0xD7 calFreq[1] = 0xD8 } else if freq > 770000000 { calFreq[0] = 0xC1 calFreq[1] = 0xC5 } else if freq > 460000000 { calFreq[0] = 0x75 calFreq[1] = 0x81 } else if freq > 425000000 { calFreq[0] = 0x6B calFreq[1] = 0x6F } d.ExecSetCommand(SX126X_CMD_CALIBRATE_IMAGE, calFreq[:]) } // SetPaConfig sets the Power Amplifier configuration // deviceSel: 0 for SX1262, 1 for SX1261 func (d *Device) SetPaConfig(paDutyCycle, hpMax, deviceSel, paLut uint8) { var p [4]uint8 p[0] = paDutyCycle p[1] = hpMax p[2] = deviceSel p[3] = paLut d.ExecSetCommand(SX126X_CMD_SET_PA_CONFIG, p[:]) } // SetRxTxFallbackMode defines into which mode the chip goes after a successful transmission or after a packet reception. func (d *Device) SetRxTxFallbackMode(fallbackMode uint8) { d.ExecSetCommand(SX126X_CMD_SET_RX_TX_FALLBACK_MODE, []uint8{fallbackMode}) } // -------------------------------------------------- // Registers and Buffers // -------------------------------------------------- // ReadRegister reads register value func (d *Device) ReadRegister(addr, size uint16) ([]uint8, error) { d.CheckDeviceReady() d.controller.SetNss(false) // Send command d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, SX126X_CMD_READ_REGISTER, uint8((addr&0xFF00)>>8), uint8(addr&0x00FF), 0x00) d.spi.Tx(d.spiTxBuf, nil) // Read registers d.spiRxBuf = d.spiRxBuf[0:size] d.spi.Tx(nil, d.spiRxBuf) d.controller.SetNss(true) d.controller.WaitWhileBusy() return d.spiRxBuf, nil } // WriteRegister writes value to register func (d *Device) WriteRegister(addr uint16, data []uint8) { d.CheckDeviceReady() d.controller.SetNss(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, SX126X_CMD_WRITE_REGISTER, uint8((addr&0xFF00)>>8), uint8(addr&0x00FF)) d.spiTxBuf = append(d.spiTxBuf, data...) d.spi.Tx(d.spiTxBuf, nil) d.controller.SetNss(true) d.controller.WaitWhileBusy() } // WriteBuffer write data from current buffer position func (d *Device) WriteBuffer(data []uint8) { p := []uint8{0} p = append(p, data...) d.ExecSetCommand(SX126X_CMD_WRITE_BUFFER, p) } // ReadBuffer Reads size bytes from current buffer position func (d *Device) ReadBuffer(size uint8) []uint8 { ret := d.ExecGetCommand(SX126X_CMD_READ_BUFFER, size) return ret } // -------------------------------------------------- // DIO and IRQ // -------------------------------------------------- // SetDioIrqParams configures DIO Irq func (d *Device) SetDioIrqParams(irqMask, dio1Mask, dio2Mask, dio3Mask uint16) { var p [8]uint8 p[0] = uint8((irqMask >> 8) & 0xFF) p[1] = uint8(irqMask & 0xFF) p[2] = uint8((dio1Mask >> 8) & 0xFF) p[3] = uint8(dio1Mask & 0xFF) p[4] = uint8((dio2Mask >> 8) & 0xFF) p[5] = uint8(dio2Mask & 0xFF) p[6] = uint8((dio3Mask >> 8) & 0xFF) p[7] = uint8(dio3Mask & 0xFF) d.ExecSetCommand(SX126X_CMD_SET_DIO_IRQ_PARAMS, p[:]) } // GetIrqStatus returns IRQ status func (d *Device) GetIrqStatus() (irqStatus uint16) { r := d.ExecGetCommand(SX126X_CMD_GET_IRQ_STATUS, 2) ret := (uint16(r[0]) << 8) | uint16(r[1]) return ret } // ClearIrqStatus clears IRQ flags func (d *Device) ClearIrqStatus(clearIrqParams uint16) { var p [2]uint8 p[0] = uint8((clearIrqParams >> 8) & 0xFF) p[1] = uint8(clearIrqParams & 0xFF) d.ExecSetCommand(SX126X_CMD_CLEAR_IRQ_STATUS, p[:]) } // -------------------------------------------------- // Communication Status Information // -------------------------------------------------- // GetStatus returns radio status(13.5.1) func (d *Device) GetStatus() (radioStatus uint8) { r := d.ExecGetCommand(SX126X_CMD_GET_STATUS, 1) return r[0] } // GetRxBufferStatus returns the length of the last received packet (PayloadLengthRx) // and the address of the first byte received (RxStartBufferPointer). (13.5.2) func (d *Device) GetRxBufferStatus() (payloadLengthRx uint8, rxStartBufferPointer uint8) { r := d.ExecGetCommand(SX126X_CMD_GET_RX_BUFFER_STATUS, 2) return r[0], r[1] } // GetPackeType returns current Packet Type (13.4.3) func (d *Device) GetPacketType() (packetType uint8) { r := d.ExecGetCommand(SX126X_CMD_GET_PACKET_TYPE, 1) return r[0] } // GetDeviceErrors returns current Device Errors func (d *Device) GetDeviceErrors() uint16 { r := d.ExecGetCommand(SX126X_CMD_GET_DEVICE_ERRORS, 2) ret := uint16(r[0]<<8 + r[1]) return ret } // ClearDeviceErrors clears device Errors func (d *Device) ClearDeviceErrors() { p := [2]uint8{0x00, 0x00} d.ExecSetCommand(SX126X_CMD_CLEAR_DEVICE_ERRORS, p[:]) } // GetStats returns the number of informations received on a few last packets // Lora: NbPktReceived, NbPktCrcError, NbPktHeaderErr func (d *Device) GetLoraStats() (nbPktReceived, nbPktCrcError, nbPktHeaderErr uint16) { r := d.ExecGetCommand(SX126X_CMD_GET_STATS, 6) return uint16(r[0]<<8 | r[1]), uint16(r[2]<<8 | r[3]), uint16(r[4]<<8 | r[5]) } // --------------------------------------- // PACKET / RADIO / PROTOCOL CONFIGURATION // --------------------------------------- // SetPacketType sets the packet type func (d *Device) SetPacketType(packetType uint8) { var p [1]uint8 p[0] = packetType d.ExecSetCommand(SX126X_CMD_SET_PACKET_TYPE, p[:]) } // SetSyncWord defines the Sync Word to yse func (d *Device) SetSyncWord(sw uint16) { var p [2]uint8 d.loraConf.SyncWord = sw p[0] = uint8((d.loraConf.SyncWord >> 8) & 0xFF) p[1] = uint8((d.loraConf.SyncWord >> 0) & 0xFF) d.WriteRegister(SX126X_REG_LORA_SYNC_WORD_MSB, p[:]) } // GetSyncWord gets the Sync Word to use func (d *Device) GetSyncWord() uint16 { p, _ := d.ReadRegister(SX126X_REG_LORA_SYNC_WORD_MSB, 2) r := uint16(p[0])<<8 + uint16(p[1]) return r } // SetPublicNetwork sets Sync Word to 0x3444 (Public) or 0x1424 (Private) func (d *Device) SetPublicNetwork(enable bool) { if enable { d.SetSyncWord(SX126X_LORA_MAC_PUBLIC_SYNCWORD) } else { d.SetSyncWord(SX126X_LORA_MAC_PRIVATE_SYNCWORD) } } // SetPacketParam sets various packet-related params func (d *Device) SetPacketParam(preambleLength uint16, headerType, crcType, payloadLength, invertIQ uint8) { var p [6]uint8 p[0] = uint8((preambleLength >> 8) & 0xFF) p[1] = uint8(preambleLength & 0xFF) p[2] = headerType p[3] = payloadLength p[4] = crcType p[5] = invertIQ d.ExecSetCommand(SX126X_CMD_SET_PACKET_PARAMS, p[:]) } // SetBufferBaseAddress sets base address for buffer func (d *Device) SetBufferBaseAddress(txBaseAddress, rxBaseAddress uint8) { var p [2]uint8 p[0] = txBaseAddress p[1] = rxBaseAddress d.ExecSetCommand(SX126X_CMD_SET_BUFFER_BASE_ADDRESS, p[:]) } // SetRfFrequency sets the radio frequency func (d *Device) SetRfFrequency(frequency uint32) { var p [4]uint8 freq := uint32((uint64(frequency) << 25) / 32000000) p[0] = uint8((freq >> 24) & 0xFF) p[1] = uint8((freq >> 16) & 0xFF) p[2] = uint8((freq >> 8) & 0xFF) p[3] = uint8((freq >> 0) & 0xFF) d.ExecSetCommand(SX126X_CMD_SET_RF_FREQUENCY, p[:]) } // SetCurrentLimit sets max current in the module func (d *Device) SetCurrentLimit(limit uint8) { if limit > 140 { limit = 140 } rawLimit := uint8(float32(limit) / 2.5) p := []uint8{rawLimit} d.WriteRegister(SX126X_REG_OCP_CONFIGURATION, p[:]) } // SetTxConfig sets power and rampup time func (d *Device) SetTxParams(power int8, rampTime uint8) { var p [2]uint8 if d.deviceType == DEVICE_TYPE_SX1261 { if power == 15 { d.SetPaConfig(0x06, 0x00, 0x01, 0x01) } else { d.SetPaConfig(0x04, 0x00, 0x01, 0x01) } if power > 14 { power = 14 } else if power < -3 { power = -3 } d.SetCurrentLimit(80) // Set max current limit to 80mA } else { // sx1262 and sx1268 d.SetPaConfig(0x04, 0x07, 0x00, 0x01) if power > 22 { power = 22 } else if power < -3 { power = -3 } d.SetCurrentLimit(140) // Set max current limit to 140 mA } p[0] = uint8(power) p[1] = rampTime d.ExecSetCommand(SX126X_CMD_SET_TX_PARAMS, p[:]) } // SetModulationParams sets the Lora modulation frequency func (d *Device) SetModulationParams(spreadingFactor, bandwidth, codingRate, lowDataRateOptimize uint8) { var p [4]uint8 p[0] = spreadingFactor p[1] = bandwidth p[2] = codingRate p[3] = lowDataRateOptimize d.ExecSetCommand(SX126X_CMD_SET_MODULATION_PARAMS, p[:]) } // CheckDeviceReady sleep until all busy flags clears func (d *Device) CheckDeviceReady() error { if d.deepSleep == true { d.controller.SetNss(false) time.Sleep(time.Millisecond) d.controller.SetNss(true) d.deepSleep = false } return d.controller.WaitWhileBusy() } // ExecSetCommand send a command to configure the peripheral func (d *Device) ExecSetCommand(cmd uint8, buf []uint8) { d.CheckDeviceReady() if cmd == SX126X_CMD_SET_SLEEP { d.deepSleep = true } else { d.deepSleep = false } d.controller.SetNss(false) // Send command and params d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmd) d.spiTxBuf = append(d.spiTxBuf, buf...) d.spi.Tx(d.spiTxBuf, nil) d.controller.SetNss(true) if cmd != SX126X_CMD_SET_SLEEP { d.controller.WaitWhileBusy() } } // ExecGetCommand queries the peripheral the peripheral func (d *Device) ExecGetCommand(cmd uint8, size uint8) []uint8 { d.CheckDeviceReady() d.controller.SetNss(false) // Send the command and flush first status byte (as not used) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmd, 0x00) d.spi.Tx(d.spiTxBuf, nil) // Read resp d.spiRxBuf = d.spiRxBuf[:size] d.spi.Tx(nil, d.spiRxBuf) d.controller.SetNss(true) d.controller.WaitWhileBusy() return d.spiRxBuf } // // Configuration // // SetFrequency() Sets current Lora Frequency // NB: Change will be applied at next RX / TX func (d *Device) SetFrequency(freq uint32) { d.loraConf.Freq = freq } // SetIqMode() defines the current IQ Mode (Standard/Inverted) // NB: Change will be applied at next RX / TX func (d *Device) SetIqMode(mode uint8) { if mode == 0 { d.loraConf.Iq = lora.IQStandard } else { d.loraConf.Iq = lora.IQInverted } } // SetCodingRate() sets current Lora Coding Rate // NB: Change will be applied at next RX / TX func (d *Device) SetCodingRate(cr uint8) { d.loraConf.Cr = cr } // SetBandwidth() sets current Lora Bandwidth // NB: Change will be applied at next RX / TX func (d *Device) SetBandwidth(bw uint8) { d.loraConf.Bw = bw } // SetCrc() sets current CRC mode (ON/OFF) // NB: Change will be applied at next RX / TX func (d *Device) SetCrc(enable bool) { if enable { d.loraConf.Crc = lora.CRCOn } else { d.loraConf.Crc = lora.CRCOn } } // SetSpreadingFactor sets current Lora Spreading Factor // NB: Change will be applied at next RX / TX func (d *Device) SetSpreadingFactor(sf uint8) { d.loraConf.Sf = sf } // SetPreambleLength sets current Lora Preamble Length // NB: Change will be applied at next RX / TX func (d *Device) SetPreambleLength(pl uint16) { d.loraConf.Preamble = pl } // SetTxPowerDbm sets current Lora TX Power in DBm // NB: Change will be applied at next RX / TX func (d *Device) SetTxPower(txpow int8) { d.loraConf.LoraTxPowerDBm = txpow } // SetHeaderType sets implicit or explicit header mode // NB: Change will be applied at next RX / TX func (d *Device) SetHeaderType(headerType uint8) { d.loraConf.HeaderType = headerType } // // Lora functions // // // LoraConfig() defines Lora configuration for next Lora operations func (d *Device) LoraConfig(cnf lora.Config) { // Save given configuration d.loraConf = cnf d.loraConf.SyncWord = syncword(int(cnf.SyncWord)) // Switch to standby prior to configuration changes d.SetStandby() // Clear errors, disable radio interrupts for the moment d.ClearDeviceErrors() d.ClearIrqStatus(SX126X_IRQ_ALL) d.SetDioIrqParams(0x00, 0x00, 0x00, 0x00) // Define radio operation mode d.SetPacketType(SX126X_PACKET_TYPE_LORA) d.SetRfFrequency(d.loraConf.Freq) d.SetModulationParams(d.loraConf.Sf, bandwidth(d.loraConf.Bw), d.loraConf.Cr, d.loraConf.Ldr) d.SetTxParams(d.loraConf.LoraTxPowerDBm, SX126X_PA_RAMP_200U) d.SetSyncWord(d.loraConf.SyncWord) d.SetBufferBaseAddress(0, 0) } // Tx sends a lora packet, (with timeout) func (d *Device) Tx(pkt []uint8, timeoutMs uint32) error { if d.loraConf.Freq == 0 { return lora.ErrUndefinedLoraConf } if d.controller != nil { err := d.controller.SetRfSwitchMode(RFSWITCH_TX_HP) if err != nil { return err } } d.ClearIrqStatus(SX126X_IRQ_ALL) irqVal := uint16(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR) d.SetStandby() d.SetPacketType(SX126X_PACKET_TYPE_LORA) d.SetRfFrequency(d.loraConf.Freq) d.SetTxParams(d.loraConf.LoraTxPowerDBm, SX126X_PA_RAMP_200U) d.SetBufferBaseAddress(0, 0) d.WriteBuffer(pkt) d.SetModulationParams(d.loraConf.Sf, bandwidth(d.loraConf.Bw), d.loraConf.Cr, d.loraConf.Ldr) d.SetPacketParam(d.loraConf.Preamble, d.loraConf.HeaderType, d.loraConf.Crc, uint8(len(pkt)), d.loraConf.Iq) d.SetDioIrqParams(irqVal, irqVal, SX126X_IRQ_NONE, SX126X_IRQ_NONE) d.SetSyncWord(d.loraConf.SyncWord) d.SetTx(timeoutMsToRtcSteps(timeoutMs)) msg := <-d.GetRadioEventChan() if msg.EventType != lora.RadioEventTxDone { return errUnexpectedTxRadioEvent } return nil } // LoraRx tries to receive a Lora packet (with timeout in milliseconds) func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) { if d.loraConf.Freq == 0 { return nil, lora.ErrUndefinedLoraConf } if d.controller != nil { err := d.controller.SetRfSwitchMode(RFSWITCH_RX) if err != nil { return nil, err } } d.ClearIrqStatus(SX126X_IRQ_ALL) irqVal := uint16(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR) d.SetStandby() d.SetBufferBaseAddress(0, 0) d.SetRfFrequency(d.loraConf.Freq) d.SetModulationParams(d.loraConf.Sf, bandwidth(d.loraConf.Bw), d.loraConf.Cr, d.loraConf.Ldr) d.SetPacketParam(d.loraConf.Preamble, d.loraConf.HeaderType, d.loraConf.Crc, 0xFF, d.loraConf.Iq) d.SetDioIrqParams(irqVal, irqVal, SX126X_IRQ_NONE, SX126X_IRQ_NONE) d.SetRx(timeoutMsToRtcSteps(timeoutMs)) msg := <-d.GetRadioEventChan() if msg.EventType == lora.RadioEventTimeout { return nil, nil } else if msg.EventType != lora.RadioEventRxDone { return nil, errUnexpectedRxRadioEvent } pLen, pStart := d.GetRxBufferStatus() d.SetBufferBaseAddress(0, pStart+1) pkt := d.ReadBuffer(pLen + 1) pkt = pkt[1:] return pkt, nil } // HandleInterrupt must be called by main code on DIO state change. func (d *Device) HandleInterrupt() { st := d.GetIrqStatus() d.ClearIrqStatus(SX126X_IRQ_ALL) if (st & SX126X_IRQ_RX_DONE) > 0 { select { case d.radioEventChan <- lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil}: default: } } if (st & SX126X_IRQ_TX_DONE) > 0 { select { case d.radioEventChan <- lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil}: default: } } if (st & SX126X_IRQ_TIMEOUT) > 0 { select { case d.radioEventChan <- lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil}: default: } } if (st & SX126X_IRQ_CRC_ERR) > 0 { select { case d.radioEventChan <- lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil}: default: } } } func bandwidth(bw uint8) uint8 { switch bw { case lora.Bandwidth_7_8: return SX126X_LORA_BW_7_8 case lora.Bandwidth_10_4: return SX126X_LORA_BW_10_4 case lora.Bandwidth_15_6: return SX126X_LORA_BW_15_6 case lora.Bandwidth_20_8: return SX126X_LORA_BW_20_8 case lora.Bandwidth_31_25: return SX126X_LORA_BW_31_25 case lora.Bandwidth_41_7: return SX126X_LORA_BW_41_7 case lora.Bandwidth_62_5: return SX126X_LORA_BW_62_5 case lora.Bandwidth_125_0: return SX126X_LORA_BW_125_0 case lora.Bandwidth_250_0: return SX126X_LORA_BW_250_0 case lora.Bandwidth_500_0: return SX126X_LORA_BW_500_0 default: return 0 } } func syncword(sw int) uint16 { if sw == lora.SyncPublic { return SX126X_LORA_MAC_PUBLIC_SYNCWORD } return SX126X_LORA_MAC_PRIVATE_SYNCWORD }