Skip to content
106 changes: 106 additions & 0 deletions examples/sx128x/lora_rx/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package main

import (
"errors"
"machine"
"runtime"
"time"

"tinygo.org/x/drivers/sx128x"
)

var (
// pin mapping specific to the lilygo t3s3, change as needed for your board
sdoPin = machine.GPIO6
sdiPin = machine.GPIO3
sckPin = machine.GPIO5
nssPin = machine.GPIO7
busyPin = machine.GPIO36
resetPin = machine.GPIO8
dio1Pin = machine.GPIO9
)

func setupPins() {
nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
nssPin.Set(true)

resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Set(true)

busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInput})
}

func main() {
setupPins()

spi := machine.SPI0
spi.Configure(machine.SPIConfig{
Mode: 0,
Frequency: 8 * 1e6,
SDO: sdoPin,
SDI: sdiPin,
SCK: sckPin,
})

radio := sx128x.New(
spi,
nssPin,
resetPin,
busyPin,
)

radio.WaitWhileBusy(time.Second)
SetupLora(radio)

for {
data, err := Rx(radio)
if err != nil {
println("failed to receive:", err)
} else {
println("received:", string(data))
}
}
}

func SetupLora(radio *sx128x.Device) {
radio.SetStandby(sx128x.STANDBY_RC)
radio.SetPacketType(sx128x.PACKET_TYPE_LORA)
radio.SetRegulatorMode(sx128x.REGULATOR_DC_DC)

radio.SetRfFrequency(2400000000) // 2.4Ghz
radio.SetModulationParamsLoRa(sx128x.LORA_SF_9, sx128x.LORA_BW_1600, sx128x.LORA_CR_4_7)

// section 14.4.1 shows required register setting for setting up LoRa operations. These depend on the chosen spreading factor.
radio.WriteRegister(0x925, []byte{0x32})
radio.WriteRegister(0x93C, []byte{0x01})

radio.SetTxParams(13, sx128x.RADIO_RAMP_02_US)
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, 0xFF, sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
radio.WriteRegister(sx128x.REG_LORA_SYNC_WORD_MSB, []byte{0x14, 0x24}) // full sync word is 0x1424

}

func Rx(radio *sx128x.Device) ([]byte, error) {
radio.SetStandby(sx128x.STANDBY_RC)
radio.SetDioIrqParams(sx128x.IRQ_RX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, sx128x.IRQ_RX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, 0x00, 0x00)
radio.SetBufferBaseAddress(0, 0)
radio.ClearIrqStatus(sx128x.IRQ_ALL_MASK)
radio.SetRx(sx128x.PERIOD_BASE_4_MS, 250) // 4ms * 250 = 1s
// busy wait for IRQ indication
for dio1Pin.Get() == false {
runtime.Gosched()
}
irqStatus, _ := radio.GetIrqStatus()
if irqStatus&sx128x.IRQ_RX_DONE_MASK != 0 {
payloadLength, bufferOffset, err := radio.GetRxBufferStatus()
if err != nil {
return nil, err
}
data, err := radio.ReadBuffer(bufferOffset, payloadLength)
return data, nil
} else if irqStatus&sx128x.IRQ_RX_TX_TIMEOUT_MASK != 0 {
return nil, errors.New("rx timeout")
}
return nil, errors.New("unexpected IRQ status")
}
96 changes: 96 additions & 0 deletions examples/sx128x/lora_tx/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package main

import (
"errors"
"machine"
"runtime"
"time"

"tinygo.org/x/drivers/sx128x"
)

var (
// pin mapping specific to the lilygo t3s3, change as needed for your board
sdoPin = machine.GPIO6
sdiPin = machine.GPIO3
sckPin = machine.GPIO5
nssPin = machine.GPIO7
busyPin = machine.GPIO36
resetPin = machine.GPIO8
dio1Pin = machine.GPIO9
)

func setupPins() {
nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
nssPin.Set(true)

resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Set(true)

busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInput})
}

func main() {
setupPins()

spi := machine.SPI0
spi.Configure(machine.SPIConfig{
Mode: 0,
Frequency: 8 * 1e6,
SDO: sdoPin,
SDI: sdiPin,
SCK: sckPin,
})

radio := sx128x.New(
spi,
nssPin,
resetPin,
busyPin,
)

radio.WaitWhileBusy(time.Second)
SetupLora(radio)

for {
Tx(radio, []byte("Hello, world!"))
time.Sleep(1 * time.Second)
}
}

func SetupLora(radio *sx128x.Device) {
radio.SetStandby(sx128x.STANDBY_RC)
radio.SetPacketType(sx128x.PACKET_TYPE_LORA)
radio.SetRegulatorMode(sx128x.REGULATOR_DC_DC)

radio.SetRfFrequency(2400000000) // 2.4Ghz
radio.SetModulationParamsLoRa(sx128x.LORA_SF_9, sx128x.LORA_BW_1600, sx128x.LORA_CR_4_7)

// section 14.4.1 shows required register setting for setting up LoRa operations. These depend on the chosen spreading factor.
radio.WriteRegister(0x925, []byte{0x32})
radio.WriteRegister(0x93C, []byte{0x01})

radio.SetTxParams(13, sx128x.RADIO_RAMP_02_US)
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, 0xFF, sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
radio.WriteRegister(sx128x.REG_LORA_SYNC_WORD_MSB, []byte{0x14, 0x24}) // full sync word is 0x1424

}

func Tx(radio *sx128x.Device, data []byte) error {
if len(data) > 255 {
return errors.New("data length exceeds maximum of 255 bytes")
}
radio.SetStandby(sx128x.STANDBY_RC)
radio.SetPacketParamsLoRa(12, sx128x.LORA_HEADER_EXPLICIT, uint8(len(data)&0xFF), sx128x.LORA_CRC_DISABLE, sx128x.LORA_IQ_STD)
radio.SetBufferBaseAddress(0, 0)
radio.WriteBuffer(0, data)
radio.SetDioIrqParams(sx128x.IRQ_TX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, sx128x.IRQ_TX_DONE_MASK|sx128x.IRQ_RX_TX_TIMEOUT_MASK, 0x00, 0x00)
radio.ClearIrqStatus(sx128x.IRQ_ALL_MASK)
radio.SetTx(sx128x.PERIOD_BASE_4_MS, 250) // 4ms * 250 = 1s
// busy wait for IRQ indication
for dio1Pin.Get() == false {
runtime.Gosched()
}
return nil
}
7 changes: 7 additions & 0 deletions sx128x/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SX128x Radio
Radio from Semtech in the 2.4 GHz band. This driver uses SPI to communicate with the radio instead of the alternative UART interface.

## Supported Chips
- [SX1280](https://www.semtech.com/products/wireless-rf/lora-connect/sx1280)
- [SX1281](https://www.semtech.com/products/wireless-rf/lora-connect/sx1281)

52 changes: 52 additions & 0 deletions sx128x/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package sx128x

const (
// SX128X SPI commands
cmdGetStatus = uint8(0xC0)

// Register Access Operations
cmdWriteRegister = uint8(0x18)
cmdReadRegister = uint8(0x19)

// Data Buffer Operations
cmdWriteBuffer = uint8(0x1A)
cmdReadBuffer = uint8(0x1B)

// Radio Operation Modes
cmdSetSleep = uint8(0x84)
cmdSetStandby = uint8(0x80)
cmdSetFS = uint8(0xC1)
cmdSetTx = uint8(0x83)
cmdSetRx = uint8(0x82)
cmdSetRxDutyCycle = uint8(0x94)
cmdSetLongPreamble = uint8(0x9B)
cmdSetCAD = uint8(0xC5)
cmdSetTxContinuousWave = uint8(0xD1)
cmdSetContinuousPreamble = uint8(0xD2)
cmdSetAutoTx = uint8(0x98)
cmdSetAutoFS = uint8(0x9E)

// Radio Configuration
cmdSetPacketType = uint8(0x8A)
cmdGetPacketType = uint8(0x03)
cmdSetRFFrequency = uint8(0x86)
cmdSetTxParams = uint8(0x8E)
cmdSetCADParams = uint8(0x88)
cmdSetBufferBaseAddress = uint8(0x8F)
cmdSetModulationParams = uint8(0x8B)
cmdSetPacketParams = uint8(0x8C)

// Communication Status Information
cmdGetRxBufferStatus = uint8(0x17)
cmdGetPacketStatus = uint8(0x1D)
cmdGetRSSIInst = uint8(0x1F)

// IRQ Handling
cmdSetDIOIRQParams = uint8(0x8D)
cmdGetIRQStatus = uint8(0x15)
cmdClearIRQStatus = uint8(0x97)

// Miscellaneous
cmdSetRegulatorMode = uint8(0x96)
cmdSetSaveContext = uint8(0xD5)
)
Loading