package sx128x import ( "runtime" "time" "tinygo.org/x/drivers" "tinygo.org/x/drivers/internal/pin" ) type Device struct { spi drivers.SPI nssPin pin.Output resetPin pin.Output busyPin pin.Input spiTxBuf []byte spiRxBuf []byte } func New(spi drivers.SPI, nssPin pin.Output, resetPin pin.Output, busyPin pin.Input) *Device { return &Device{ spi: spi, nssPin: nssPin, resetPin: resetPin, busyPin: busyPin, spiTxBuf: make([]byte, 256), // TODO: optimize buffer size spiRxBuf: make([]byte, 256), } } func (d *Device) Reset() { d.resetPin.Set(false) time.Sleep(10 * time.Millisecond) d.resetPin.Set(true) time.Sleep(10 * time.Millisecond) } func (d *Device) WaitWhileBusy(timeout time.Duration) error { // largest busy period is on boot with around ~400ish this should be more than enough now := time.Now() for d.busyPin.Get() { if time.Since(now) > timeout { return ErrBusyPinTimeout } runtime.Gosched() } return nil } // Get tranceiver status, returns circuit mode and command status func (d *Device) GetStatus() (CircuitMode, CommandStatus, error) { err := d.WaitWhileBusy(time.Second) if err != nil { return 0, 0, err } d.nssPin.Set(false) status, err := d.spi.Transfer(cmdGetStatus) d.nssPin.Set(true) if err != nil { return 0, 0, err } circuitMode := (status & circuitModeMask) >> 5 commandStatus := (status & commandStatusMask) >> 2 return CircuitMode(circuitMode), CommandStatus(commandStatus), nil } func (d *Device) WriteRegister(addr uint16, data []byte) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdWriteRegister, uint8((addr>>8)&0xFF), uint8(addr&0xFF)) d.spiTxBuf = append(d.spiTxBuf, data...) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } func (d *Device) ReadRegister(addr uint16) (uint8, error) { err := d.WaitWhileBusy(time.Second) if err != nil { return 0, err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdReadRegister, uint8((addr&0xFF00)>>8), uint8(addr&0x00FF), 0x00, 0x00) d.spiRxBuf = d.spiRxBuf[:5] err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf) d.nssPin.Set(true) if err != nil { return 0, err } return d.spiRxBuf[4], nil } func (d *Device) WriteBuffer(offset uint8, data []byte) error { if len(data) > 256 { return errDataTooLong } err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdWriteBuffer, offset) d.spiTxBuf = append(d.spiTxBuf, data...) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Read data from the payload buffer starting at the given offset with the given length func (d *Device) ReadBuffer(offset uint8, length uint8) ([]byte, error) { err := d.WaitWhileBusy(time.Second) if err != nil { return nil, err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdReadBuffer, offset, 0x00) for i := uint8(0); i < length; i++ { d.spiTxBuf = append(d.spiTxBuf, 0x00) } d.spiRxBuf = d.spiRxBuf[:len(d.spiTxBuf)] err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf) d.nssPin.Set(true) if err != nil { return nil, err } return d.spiRxBuf[3:], nil } // Set the device into sleep mode with the given configuration: 0 (no retention), 1 (ram retentation), 2 (buffer retention) or 3 (ram and buffer retention) func (d *Device) SetSleep(sleepConfig SleepConfig) error { if sleepConfig > (SLEEP_DATA_BUFFER_RETAIN | SLEEP_DATA_RAM_RETAIN) { return errInvalidSleepConfig } err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetSleep, uint8(sleepConfig)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Put device into standby mode, 0 (RC) or 1 (XOSC) func (d *Device) SetStandby(standbyConfig StandbyConfig) error { if standbyConfig > STANDBY_XOSC { // XOSC is the highest standby config anything higher is invalid return errInvalidStandbyConfig } err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetStandby, uint8(standbyConfig)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Set the device into Frequency Synthesizer mode func (d *Device) SetFs() error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetFS) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } func checkPeriodBase(periodBase PeriodBase) error { if periodBase > PERIOD_BASE_4_MS { // 4ms is the highest period base anything higher is invalid return errInvalidPeriodBase } return nil } // Sets the device in transmit mode, the IRQ status should be cleared before using this command // timout is determined by periodBase * periodBaseCount func (d *Device) SetTx(periodBase PeriodBase, periodBaseCount uint16) error { err := checkPeriodBase(periodBase) if err != nil { return err } err = d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetTx, uint8(periodBase), uint8((periodBaseCount>>8)&0xFF), uint8(periodBaseCount&0xFF)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Sets the device in receive mode, the IRQ status should be cleared before using this command // timeout is determined by periodBase * periodBaseCount func (d *Device) SetRx(periodBase PeriodBase, periodBaseCount uint16) error { err := checkPeriodBase(periodBase) if err != nil { return err } err = d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetRx, uint8(periodBase), uint8((periodBaseCount>>8)&0xFF), uint8(periodBaseCount&0xFF)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Sets the device in a continuous receive mode, it enters receive mode with a timeout of periodBase * rxPeriodBaseCount. // If no packet is received it will enter sleep mode for periodBase * sleepPeriodBaseCount before re-entering receive mode. // The loop is exited when a packet is received or the device is put into standby mode. func (d *Device) SetRxDutyCycle(periodBase PeriodBase, rxPeriodBaseCount uint16, sleepPeriodBaseCount uint16) error { err := checkPeriodBase(periodBase) if err != nil { return err } err = d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetRxDutyCycle, uint8(periodBase), uint8((rxPeriodBaseCount&0xFF00)>>8), uint8(rxPeriodBaseCount&0x00FF)) d.spiTxBuf = append(d.spiTxBuf, uint8((sleepPeriodBaseCount&0xFF00)>>8), uint8(sleepPeriodBaseCount&0x00FF)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Sets the transceiver into Long Preamble mode, and can only be used with either the LoRa mode and GFSK mode func (d *Device) SetLongPreamble(enable bool) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetLongPreamble) if enable { d.spiTxBuf = append(d.spiTxBuf, 1) } else { d.spiTxBuf = append(d.spiTxBuf, 0) } err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Channel activity detection (CAD) is a LoRa specific mode of operation where the device searches for a LoRa signal. // After search has completed, the device returns to STDBY_RC mode. The length of the search is configured via the SetCadParams() command. func (d *Device) SetCAD() error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetCAD) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Test command to generate a Continuous Wave (RF tone) at a selected frequency and output power // The device remains in Tx Continuous Wave until the host sends a mode configuration command. func (d *Device) SetTxContinuousWave() error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetTxContinuousWave) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Test command to generate an infinite sequence of alternating ‘0’s and ‘1’s in // GFSK modulation and symbol 0 in LoRa. The device remains in transmit until the host sends a mode configuration command. func (d *Device) SetTxContinuousPreamble() error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetContinuousPreamble) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // This command allows the transceiver to send a packet at a user programmable time after the end of a packet reception. // This is useful for Bluetooth Low Energy (BLE) compatibility which requires the transceiver to be able to send back a response 150µs after a packet reception. func (d *Device) SetAutoTx(timeUs uint16) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetAutoTx, uint8((timeUs&0xFF00)>>8), uint8(timeUs&0x00FF)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Modifies the chip behavior so that the state following a Rx or Tx operation is FS and not standby. // This allows for faster transitions between Rx and/or Tx. func (d *Device) SetAutoFs(enable bool) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetAutoFS) if enable { d.spiTxBuf = append(d.spiTxBuf, 1) } else { d.spiTxBuf = append(d.spiTxBuf, 0) } err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Choose between GFSK, LoRa, Ranging, FLRC or BLE packet types, this will affect the available configuration parameters and the structure of the packet func (d *Device) SetPacketType(packetType PacketType) error { if packetType > PACKET_TYPE_BLE { // BLE is the highest packet type anything higher is invalid. return errInvalidPacketType } err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetPacketType, uint8(packetType)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Get the currently configured packet type, this will be 0 (GFSK), 1 (LoRa), 2 (Ranging), 3 (FLRC) or 4 (BLE) func (d *Device) GetPacketType() (PacketType, error) { err := d.WaitWhileBusy(time.Second) if err != nil { return 0, err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdGetPacketType, 0x00, 0x00) d.spiRxBuf = d.spiRxBuf[:3] err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf) d.nssPin.Set(true) if err != nil { return 0, err } return PacketType(d.spiRxBuf[2]), nil } // Set the RF frequency in Hz, must be between 2.4 GHz and 2.5 GHz func (d *Device) SetRfFrequency(frequencyHz uint32) error { if frequencyHz < 2400000000 { return errFrequencyTooLow } if frequencyHz > 2500000000 { return errFrequencyTooHigh } err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] rfFrequency := uint32((uint64(frequencyHz) << 18) / 52000000) d.spiTxBuf = append(d.spiTxBuf, cmdSetRFFrequency, uint8((rfFrequency>>16)&0xFF), uint8((rfFrequency>>8)&0xFF), uint8(rfFrequency&0xFF)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Set the output power in dBm, must be between -18 and 13 dBm, and the ramp time func (d *Device) SetTxParams(powerdBm int8, rampTime RadioRampTime) error { if powerdBm < -18 { return errPowerTooLow } if powerdBm > 13 { return errPowerTooHigh } err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] adjustedPower := uint8(powerdBm + 18) d.spiTxBuf = append(d.spiTxBuf, cmdSetTxParams, adjustedPower, uint8(rampTime)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Set the number of symbols used for channel activity detection which determines the sensitivity of the detection. // This is only applicable in LoRa mode. func (d *Device) SetCadParams(cadSymbolNum uint8) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetCADParams, cadSymbolNum) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Set the base address for the internal buffer for Tx and Rx operations. // When transmitting or receiving data is read from or written to the buffer starting at the given offset. func (d *Device) SetBufferBaseAddress(txBase uint8, rxBase uint8) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetBufferBaseAddress, txBase, rxBase) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // The arguments to this function depend on the packet type. It is recommended to use the mode specific functions for a better experience. // BLE & GFSK: BitrateBandwidth, ModulationIndex, ModulationShaping // FLRC: BitrateBandwidth, CodingRate, ModulationShaping // LoRa & Ranging: SpreadingFactor, Bandwidth, CodingRate func (d *Device) SetModulationParams(modParam1, modParam2, modParam3 uint8) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetModulationParams, modParam1, modParam2, modParam3) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } func (d *Device) SetModulationParamsBLE(bitrateBandwidth GFSKBLEBitrateBandwidth, modulationIndex ModulationIndex, modulationShaping ModulationShaping) error { return d.SetModulationParams(uint8(bitrateBandwidth), uint8(modulationIndex), uint8(modulationShaping)) } func (d *Device) SetModulationParamsGFSK(bitrateBandwidth GFSKBLEBitrateBandwidth, modulationIndex ModulationIndex, modulationShaping ModulationShaping) error { return d.SetModulationParams(uint8(bitrateBandwidth), uint8(modulationIndex), uint8(modulationShaping)) } func (d *Device) SetModulationParamsFLRC(bitrateBandwidth FLRCBitrateBandwidth, codingRate FLRCCodingRate, modulationShaping ModulationShaping) error { return d.SetModulationParams(uint8(bitrateBandwidth), uint8(codingRate), uint8(modulationShaping)) } func (d *Device) SetModulationParamsLoRa(spreadingFactor LoRaSpreadingFactor, bandwidth LoRaBandwidth, codingRate LoRaCodingRate) error { return d.SetModulationParams(uint8(spreadingFactor), uint8(bandwidth), uint8(codingRate)) } // The arguments to this function depend on the packet type. It is recommended to use the mode specific functions for a better experience. // GFSK & FLRC: PreambleLength, SyncWordLength, SyncWordMatch, HeaderType, PayloadLength, CrcLength, Whitening // BLE: ConnectionState, CrcLength, BleTestPayload, Whitening // LoRa & Ranging: PreambleLength, HeaderType, PayloadLength, CRC, InvertIQ/chirp invert func (d *Device) SetPacketParams(param1, param2, param3, param4, param5, param6, param7 uint8) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetPacketParams, param1, param2, param3, param4, param5, param6, param7) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Set GFSK related packet parameters, this assumes the packet type is already set to GFSK. // - payloadLength: range of 0-255 func (d *Device) SetPacketParamsGFSK(preambleLength GFSKPreambleLength, syncWordLength GFSKSyncWordLength, syncWordMatch GFSKSyncWordMatch, headerType GFSKHeaderType, payloadLength uint8, crcLength GFSKCrcType, whitening bool) error { var whiteningVal uint8 if whitening { whiteningVal = whiteningEnable } else { whiteningVal = whiteningDisable } return d.SetPacketParams(uint8(preambleLength), uint8(syncWordLength), uint8(syncWordMatch), uint8(headerType), payloadLength, uint8(crcLength), whiteningVal) } // Set FLRC related packet parameters, this assumes the packet type is already set to FLRC. // - payloadLength: range of 6-127 func (d *Device) SetPacketParamsFLRC(preambleLength FLRCPreambleLength, syncWordLength FLRCSyncWordLength, syncWordMatch FLRCSyncWordMatch, headerType FLRCHeaderType, payloadLength uint8, crcLength FLRCCrcType) error { if payloadLength < 6 { return errPayloadLengthTooShort } if payloadLength > 127 { return errPayloadLengthTooLong } return d.SetPacketParams(uint8(preambleLength), uint8(syncWordLength), uint8(syncWordMatch), uint8(headerType), payloadLength, uint8(crcLength), whiteningDisable) } // Set BLE related packet parameters, this assumes the packet type is already set to BLE. func (d *Device) SetPacketParamsBLE(connectionState BLEConnectionState, crcLength BLECrcType, bleTestPayload BLETestPayload, whitening bool) error { var whiteningVal uint8 if whitening { whiteningVal = whiteningEnable } else { whiteningVal = whiteningDisable } return d.SetPacketParams(uint8(connectionState), uint8(crcLength), uint8(bleTestPayload), whiteningVal, 0, 0, 0) } // Set LoRa related packet parameters, this assumes the packet type is already set to LoRa. // - payloadLength: range of 1-255 func (d *Device) SetPacketParamsLoRa(preambleLength uint32, headerType LoRaHeaderType, payloadLength uint8, crcType LoRaCrcType, iqType LoRaIqType) error { if payloadLength == 0 { return errPayloadLengthTooShort } exponent, mantissa := getExponentAndMantissa(preambleLength) return d.SetPacketParams(uint8(exponent<<4)|mantissa, uint8(headerType), payloadLength, uint8(crcType), uint8(iqType), 0, 0) } func getExponentAndMantissa(value uint32) (uint8, uint8) { // pulled from RadioLib https://github.com/jgromes/RadioLib/blob/master/src/modules/SX128x/SX128x.cpp e := uint8(1) m := uint8(1) len := uint32(0) for e = uint8(1); e <= 15; e++ { for m = uint8(1); m <= 15; m++ { len = uint32(m) * (uint32(1 << e)) if len >= value { break } } if len >= value { break } } return e, m } // Get information about the most recent packet received. // Return the payload length, the offset in the buffer where the payload starts. func (d *Device) GetRxBufferStatus() (uint8, uint8, error) { err := d.WaitWhileBusy(time.Second) if err != nil { return 0, 0, err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdGetRxBufferStatus, 0x00, 0x00, 0x00) d.spiRxBuf = d.spiRxBuf[:4] err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf) d.nssPin.Set(true) if err != nil { return 0, 0, err } return d.spiRxBuf[2], d.spiRxBuf[3], nil } // The return type of this function depends on the packet type. Use mode specific function for typed returns. // BLE, GFSK & FLRC: unused, rssiSync, errors, status, sync // LoRa & Ranging: rssiSync, SNR func (d *Device) GetPacketStatus() (uint8, uint8, uint8, uint8, uint8, error) { err := d.WaitWhileBusy(time.Second) if err != nil { return 0, 0, 0, 0, 0, err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdGetPacketStatus, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) d.spiRxBuf = d.spiRxBuf[:7] err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf) d.nssPin.Set(true) if err != nil { return 0, 0, 0, 0, 0, err } return d.spiRxBuf[2], d.spiRxBuf[3], d.spiRxBuf[4], d.spiRxBuf[5], d.spiRxBuf[6], nil } // Get information about the most recent GFSK packet received or transmitted: // - RSSI of last received packet // - packet information (each bit represents a different error or status flag) // - whether the last packet transmission has ended // - the sync word that was used for the last packet reception (0-3) func (d *Device) GetPacketStatusGFSK() (float32, GFSKPacketInfo, bool, uint8, error) { _, rssiSync, packetInfo, status, sync, err := d.GetPacketStatus() if err != nil { return 0, 0, false, 0, err } return float32(int8(rssiSync)) / 2 * -1, GFSKPacketInfo(packetInfo), status != 0, sync, nil } // Get information about the most recent BLE packet received or transmitted: // - RSSI of last received packet // - packet information (each bit represents a different error or status flag) // - whether the last packet transmission has ended // - the sync word that was used for the last packet reception (0-1) func (d *Device) GetPacketStatusBLE() (float32, BLEPacketInfo, bool, uint8, error) { _, rssiSync, packetInfo, status, sync, err := d.GetPacketStatus() if err != nil { return 0, 0, false, 0, err } return float32(int8(rssiSync)) / 2 * -1, BLEPacketInfo(packetInfo), status != 0, sync, nil } // Get information about the most recent BLE packet received or transmitted: // - RSSI of last received packet // - packet information (each bit represents a different error or status flag) // - PID field of the received packet // - NO_ACK field of the received packet // - PID check status of the current packet // - whether the last packet transmission has ended // - the sync word that was used for the last packet reception (0-1) func (d *Device) GetPacketStatusFLRC() (float32, FLRCPacketInfo, uint8, bool, bool, bool, uint8, error) { _, rawRSSI, packetInfo, rxTxInfo, sync, err := d.GetPacketStatus() rxPid := (rxTxInfo & 0b11000000) >> 6 noAck := (rxTxInfo & 0b00100000) != 0 pidCheck := (rxTxInfo & 0b00010000) != 0 txDone := (rxTxInfo & 0b00000001) != 0 if err != nil { return 0, 0, 0, false, false, false, 0, err } return float32(int8(rawRSSI)) / 2 * -1, FLRCPacketInfo(packetInfo), rxPid, noAck, pidCheck, txDone, sync, nil } // Get information about the most recent LoRa packet received: // - RSSI of last received packet // - signal-to-noise ratio (SNR) of last received packet func (d *Device) GetPacketStatusLoRa() (float32, float32, error) { rawRSSI, rawSnr, _, _, _, err := d.GetPacketStatus() if err != nil { return 0, 0, err } return float32(int8(rawRSSI)) / 2 * -1, float32(int8(rawSnr)) / 4, nil } // Get the instantaneous RSSI value during reception of the packet func (d *Device) GetRssiInst() (float32, error) { err := d.WaitWhileBusy(time.Second) if err != nil { return 0, err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdGetRSSIInst, 0x00, 0x00) d.spiRxBuf = d.spiRxBuf[:3] err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf) d.nssPin.Set(true) if err != nil { return 0, err } return float32(int8(d.spiRxBuf[2])) / 2 * -1, nil } // Configure the overall IRQ mask and the mapping of individual IRQs to the DIO1, DIO2 and DIO3 pins func (d *Device) SetDioIrqParams(irqMask IRQMask, dio1Mask IRQMask, dio2Mask IRQMask, dio3Mask IRQMask) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetDIOIRQParams, uint8((irqMask&0xFF00)>>8), uint8(irqMask&0x00FF)) d.spiTxBuf = append(d.spiTxBuf, uint8((dio1Mask&0xFF00)>>8), uint8(dio1Mask&0x00FF)) d.spiTxBuf = append(d.spiTxBuf, uint8((dio2Mask&0xFF00)>>8), uint8(dio2Mask&0x00FF)) d.spiTxBuf = append(d.spiTxBuf, uint8((dio3Mask&0xFF00)>>8), uint8(dio3Mask&0x00FF)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Get the current IRQ status. func (d *Device) GetIrqStatus() (IRQMask, error) { err := d.WaitWhileBusy(time.Second) if err != nil { return 0, err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdGetIRQStatus, 0x00, 0x00, 0x00) d.spiRxBuf = d.spiRxBuf[:4] err = d.spi.Tx(d.spiTxBuf, d.spiRxBuf) d.nssPin.Set(true) if err != nil { return 0, err } return uint16(d.spiRxBuf[2])<<8 | uint16(d.spiRxBuf[3]), err } // Clear the IRQ bits specified in the irqMask. func (d *Device) ClearIrqStatus(irqMask IRQMask) error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdClearIRQStatus, uint8((irqMask&0xFF00)>>8), uint8(irqMask&0x00FF)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Switch between the low-dropout regulator (LDO) and the DC-DC converter for internal power regulation. func (d *Device) SetRegulatorMode(mode RegulatorMode) error { if mode > REGULATOR_DC_DC { // DC-DC is the highest regulator mode anything higher is invalid return errInvalidRegulatorMode } err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetRegulatorMode, uint8(mode)) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err } // Stores the present context of the radio register values to the Data RAM which will be restored when the device wakes up from sleep mode. func (d *Device) SetSaveContext() error { err := d.WaitWhileBusy(time.Second) if err != nil { return err } d.nssPin.Set(false) d.spiTxBuf = d.spiTxBuf[:0] d.spiTxBuf = append(d.spiTxBuf, cmdSetSaveContext) err = d.spi.Tx(d.spiTxBuf, nil) d.nssPin.Set(true) return err }