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) }