Files
Joel Wetzell 62663c1832 add SPI driver for semtech sx128x chips (#864)
* add SPI driver for semtech sx128x chips

Co-authored-by: Copilot <copilot@github.com>

* handle busy loop better

* switch to time based busy timeout

Co-authored-by: Copilot <copilot@github.com>

* comment functions

* start on using types for function inputs

* use types where applicable and align with datasheet more

Co-authored-by: Copilot <copilot@github.com>

* work on exporting less constants

* only export "actionable" errors

* combine identical constants

* add crude lora rx and tx examples

* change from type aliases to local types

---------

Co-authored-by: Copilot <copilot@github.com>
2026-05-04 09:42:03 -03:00

769 lines
25 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 0s and 1s 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
}