251 lines
6.9 KiB
Go
251 lines
6.9 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"machine"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/jwetzell/osc-go"
|
|
"tinygo.org/x/drivers/sx128x"
|
|
)
|
|
|
|
type LoRaConfig struct {
|
|
Frequency uint32
|
|
Power int8
|
|
RadioRamp sx128x.RadioRampTime
|
|
RegulatorMode sx128x.RegulatorMode
|
|
SpreadingFactor sx128x.LoRaSpreadingFactor
|
|
Bandwidth sx128x.LoRaBandwidth
|
|
CodingRate sx128x.LoRaCodingRate
|
|
PreambleLength uint32
|
|
HeaderType sx128x.LoRaHeaderType
|
|
CrcType sx128x.LoRaCrcType
|
|
IqType sx128x.LoRaIqType
|
|
SyncWord uint16
|
|
}
|
|
|
|
func main() {
|
|
time.Sleep(3 * time.Second)
|
|
|
|
led := machine.GPIO37
|
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
|
|
spi := machine.SPI0
|
|
spi.Configure(machine.SPIConfig{
|
|
Mode: 0,
|
|
Frequency: 8 * 1e6,
|
|
SDO: machine.GPIO6,
|
|
SDI: machine.GPIO3,
|
|
SCK: machine.GPIO5,
|
|
})
|
|
|
|
nssPin := machine.GPIO7
|
|
nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
nssPin.Set(true)
|
|
|
|
resetPin := machine.GPIO8
|
|
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
resetPin.Set(true)
|
|
|
|
busyPin := machine.GPIO36
|
|
busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
|
|
|
|
button := machine.GPIO0
|
|
button.Configure(machine.PinConfig{Mode: machine.PinInput})
|
|
buttonPressed := atomic.Bool{}
|
|
buttonPressed.Store(false)
|
|
button.SetInterrupt(machine.PinFalling, func(machine.Pin) {
|
|
println("button interrupt")
|
|
buttonPressed.Store(true)
|
|
})
|
|
|
|
println("initializing radio...")
|
|
|
|
radio := sx128x.New(
|
|
spi,
|
|
nssPin,
|
|
resetPin,
|
|
busyPin,
|
|
)
|
|
|
|
dio1Pin := machine.GPIO9
|
|
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInput})
|
|
|
|
txDone := atomic.Bool{}
|
|
txDone.Store(true)
|
|
rxDone := atomic.Bool{}
|
|
rxDone.Store(true)
|
|
timeout := atomic.Bool{}
|
|
timeout.Store(true)
|
|
|
|
dio1Interrupt := atomic.Bool{}
|
|
dio1Interrupt.Store(false)
|
|
|
|
dio1Pin.SetInterrupt(machine.PinRising, func(machine.Pin) {
|
|
dio1Interrupt.Store(true)
|
|
})
|
|
|
|
loraConfig := LoRaConfig{
|
|
Frequency: 2400000000, // 2.4Ghz
|
|
Power: 13, // dBm
|
|
RadioRamp: sx128x.RADIO_RAMP_02_US, // 2 microsecond ramp time
|
|
RegulatorMode: sx128x.REGULATOR_DC_DC,
|
|
|
|
SpreadingFactor: sx128x.LORA_SF_9,
|
|
Bandwidth: sx128x.LORA_BW_1600,
|
|
CodingRate: sx128x.LORA_CR_4_7,
|
|
PreambleLength: 12,
|
|
HeaderType: sx128x.LORA_HEADER_EXPLICIT,
|
|
CrcType: sx128x.LORA_CRC_DISABLE,
|
|
IqType: sx128x.LORA_IQ_STD,
|
|
SyncWord: 0x1424, // the default private sync word
|
|
}
|
|
|
|
radio.WaitWhileBusy(time.Second)
|
|
SetupLora(radio, loraConfig)
|
|
|
|
oscMessage := osc.OSCMessage{
|
|
Address: "/cue/1/go",
|
|
Args: []osc.OSCArg{},
|
|
}
|
|
|
|
for {
|
|
if dio1Interrupt.Load() {
|
|
irqStatus, _ := radio.GetIrqStatus()
|
|
|
|
if irqStatus&sx128x.IRQ_TX_DONE_MASK != 0 {
|
|
txDone.Store(true)
|
|
timeout.Store(false)
|
|
led.Set(false)
|
|
println("tx done")
|
|
}
|
|
if irqStatus&sx128x.IRQ_RX_DONE_MASK != 0 {
|
|
rxDone.Store(true)
|
|
timeout.Store(false)
|
|
led.Set(true)
|
|
}
|
|
if irqStatus&sx128x.IRQ_RX_TX_TIMEOUT_MASK != 0 {
|
|
timeout.Store(true)
|
|
txDone.Store(true)
|
|
rxDone.Store(true)
|
|
}
|
|
dio1Interrupt.Store(false)
|
|
}
|
|
if txDone.Load() && buttonPressed.Load() {
|
|
txDone.Store(false)
|
|
buttonPressed.Store(false)
|
|
radio.ClearIrqStatus(sx128x.IRQ_ALL_MASK)
|
|
radio.SetStandby(sx128x.STANDBY_RC)
|
|
led.Set(true)
|
|
|
|
bytes, err := oscMessage.ToBytes()
|
|
if err != nil {
|
|
println("failed to serialize OSC message:", err)
|
|
continue
|
|
}
|
|
if len(bytes) > 255 {
|
|
println("OSC message exceeds maximum length of 255 bytes")
|
|
continue
|
|
}
|
|
|
|
err = Tx(radio, loraConfig, bytes)
|
|
if err != nil {
|
|
println("failed to enter transmit:", err)
|
|
}
|
|
println("transmit started")
|
|
}
|
|
|
|
// if rxDone.Load() {
|
|
// rxDone.Store(false)
|
|
// if !timeout.Load() {
|
|
// rxLength, rxPointer := radio.GetRxBufferStatus()
|
|
// rxData := radio.ReadBuffer(rxPointer, rxLength)
|
|
// oscMessage, err := osc.MessageFromBytes(rxData)
|
|
// if err != nil {
|
|
// println("failed to parse OSC message:", err)
|
|
// } else {
|
|
// println("received OSC message with address:", oscMessage.Address)
|
|
// for i, arg := range oscMessage.Args {
|
|
// println("arg", i, "type:", arg.Type, "value:", arg.Value)
|
|
// }
|
|
// }
|
|
|
|
// led.Set(false)
|
|
// }
|
|
// Rx(radio, loraConfig)
|
|
// }
|
|
}
|
|
}
|
|
|
|
func SetupLora(radio *sx128x.Device, config LoRaConfig) {
|
|
// Switch to standby prior to configuration changes
|
|
|
|
circuitMode, _, _ := radio.GetStatus()
|
|
if circuitMode != sx128x.CIRCUIT_MODE_STDBY_RC {
|
|
radio.SetStandby(sx128x.STANDBY_RC)
|
|
}
|
|
// Clear errors, disable radio interrupts for the moment
|
|
radio.SetPacketType(sx128x.PACKET_TYPE_LORA)
|
|
radio.SetCadParams(uint8(sx128x.LORA_CAD_08_SYMBOLS))
|
|
radio.SetRegulatorMode(sx128x.REGULATOR_DC_DC)
|
|
|
|
radio.SetRfFrequency(config.Frequency)
|
|
radio.SetModulationParamsLoRa(config.SpreadingFactor, config.Bandwidth, config.CodingRate)
|
|
|
|
data := [1]uint8{}
|
|
if config.SpreadingFactor == sx128x.LORA_SF_5 || config.SpreadingFactor == sx128x.LORA_SF_6 {
|
|
data[0] = 0x1E
|
|
} else if config.SpreadingFactor == sx128x.LORA_SF_7 || config.SpreadingFactor == sx128x.LORA_SF_8 {
|
|
data[0] = 0x37
|
|
} else {
|
|
data[0] = 0x32
|
|
}
|
|
radio.WriteRegister(0x925, data[:])
|
|
existing, _ := radio.ReadRegister(0x93C)
|
|
data[0] = existing | 0x01
|
|
radio.WriteRegister(0x93C, data[:])
|
|
|
|
radio.SetTxParams(config.Power, sx128x.RADIO_RAMP_02_US)
|
|
radio.SetPacketParamsLoRa(config.PreambleLength, config.HeaderType, 0xFF, config.CrcType, config.IqType)
|
|
var syncWord [2]uint8
|
|
syncWord[0] = uint8(config.SyncWord >> 8)
|
|
syncWord[1] = uint8(config.SyncWord & 0x00FF)
|
|
|
|
radio.WriteRegister(sx128x.REG_LORA_SYNC_WORD_MSB, syncWord[:])
|
|
|
|
}
|
|
|
|
func checkStatus(radio *sx128x.Device, operation string) {
|
|
circuitMode, commandStatus, _ := radio.GetStatus()
|
|
if commandStatus != sx128x.COMMAND_STATUS_SUCCESS {
|
|
println(operation, "-> failed with status:", commandStatus)
|
|
}
|
|
if circuitMode != sx128x.CIRCUIT_MODE_STDBY_RC {
|
|
println(operation, "-> entered circuit mode:", circuitMode)
|
|
}
|
|
}
|
|
|
|
func Tx(radio *sx128x.Device, loraConfig LoRaConfig, data []byte) error {
|
|
if len(data) > 255 {
|
|
return errors.New("data length exceeds maximum of 255 bytes")
|
|
}
|
|
radio.SetStandby(sx128x.STANDBY_RC)
|
|
radio.SetPacketParamsLoRa(loraConfig.PreambleLength, loraConfig.HeaderType, uint8(len(data)&0xFF), loraConfig.CrcType, loraConfig.IqType)
|
|
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)
|
|
return nil
|
|
}
|
|
|
|
func Rx(radio *sx128x.Device, loraConfig LoRaConfig) {
|
|
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.SetRfFrequency(loraConfig.Frequency)
|
|
radio.SetRx(sx128x.PERIOD_BASE_4_MS, 250)
|
|
}
|