package main import ( "errors" "machine" "sync/atomic" "time" "github.com/jwetzell/osc-go" "tinygo.org/x/drivers/lora" "tinygo.org/x/drivers/sx128x" ) 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) dio1Pin.SetInterrupt(machine.PinRising, func(machine.Pin) { 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) } }) loraConfig := lora.Config{ Freq: 2400000000, Bw: lora.Bandwidth_1625_0, Sf: lora.SpreadingFactor9, Cr: lora.CodingRate4_7, HeaderType: sx128x.LORA_EXPLICIT_HEADER, Preamble: 12, Ldr: lora.LowDataRateOptimizeOff, Iq: sx128x.LORA_IQ_STD, Crc: sx128x.LORA_CRC_DISABLE, SyncWord: 0x1424, LoraTxPowerDBm: 13, } radio.WaitWhileBusy() SetupLora(radio, loraConfig) oscMessage := osc.OSCMessage{ Address: "/cue/1/go", Args: []osc.OSCArg{}, } for { 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 lora.Config) { // 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(sx128x.LORA_CAD_08_SYMBOLS) radio.SetRegulatorMode(sx128x.REGULATOR_DC_DC) radio.SetRfFrequency(config.Freq) radio.SetModulationParams(spreadingFactor(config.Sf), bandwidth(config.Bw), codingRate(config.Cr)) data := [1]uint8{} if config.Sf == lora.SpreadingFactor5 || config.Sf == lora.SpreadingFactor6 { data[0] = 0x1E } else if config.Sf == lora.SpreadingFactor7 || config.Sf == lora.SpreadingFactor8 { 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.LoraTxPowerDBm, sx128x.RADIO_RAMP_02_US) radio.SetPacketParamsLoRa(uint32(config.Preamble), config.HeaderType, 0xFF, config.Crc, config.Iq) 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 lora.Config, data []byte) error { if len(data) > 255 { return errors.New("data length exceeds maximum of 255 bytes") } radio.SetStandby(sx128x.STANDBY_RC) radio.SetPacketParamsLoRa(uint32(loraConfig.Preamble), loraConfig.HeaderType, uint8(len(data)&0xFF), loraConfig.Crc, loraConfig.Iq) 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 lora.Config) { 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.Freq) radio.SetRx(sx128x.PERIOD_BASE_4_MS, 250) } func codingRate(cr uint8) uint8 { switch cr { case lora.CodingRate4_5: return sx128x.LORA_CR_4_5 case lora.CodingRate4_6: return sx128x.LORA_CR_4_6 case lora.CodingRate4_7: return sx128x.LORA_CR_4_7 case lora.CodingRate4_8: return sx128x.LORA_CR_4_8 default: return 0 } } func spreadingFactor(sf uint8) uint8 { switch sf { case lora.SpreadingFactor5: return sx128x.LORA_SF_5 case lora.SpreadingFactor6: return sx128x.LORA_SF_6 case lora.SpreadingFactor7: return sx128x.LORA_SF_7 case lora.SpreadingFactor8: return sx128x.LORA_SF_8 case lora.SpreadingFactor9: return sx128x.LORA_SF_9 case lora.SpreadingFactor10: return sx128x.LORA_SF_10 case lora.SpreadingFactor11: return sx128x.LORA_SF_11 case lora.SpreadingFactor12: return sx128x.LORA_SF_12 default: return 0 } } func bandwidth(bw uint8) uint8 { switch bw { case lora.Bandwidth_1625_0: return sx128x.LORA_BW_1600 case lora.Bandwidth_812_5: return sx128x.LORA_BW_800 case lora.Bandwidth_406_25: return sx128x.LORA_BW_400 case lora.Bandwidth_203_125: return sx128x.LORA_BW_200 default: return 0 } }