package main import ( "machine" "net" "runtime" "sync/atomic" "time" "tinygo.org/x/drivers/lora" "tinygo.org/x/drivers/netdev" nl "tinygo.org/x/drivers/netlink" "tinygo.org/x/drivers/sx128x" link "tinygo.org/x/espradio/netlink" ) var ( ssid string password string ) 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) link := link.Esplink{} netdev.UseNetdev(&link) println("Connecting to WiFi...") err := link.NetConnect(&nl.ConnectParams{ Ssid: ssid, Passphrase: password, }) if err != nil { panic("connect failed: " + err.Error()) } conn, err := net.Dial("udp", "10.0.0.50:53000") if err != nil { panic("dial failed: " + err.Error()) } defer conn.Close() println("Connected to WiFi.") 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}) 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) println("radio initialized, waiting for messages...") for { runtime.Gosched() if dio1Interrupt.Load() { // println("DIO1 interrupt triggered") irqStatus, _ := radio.GetIrqStatus() println("irq status:", irqStatus) if irqStatus&sx128x.IRQ_TX_DONE_MASK != 0 { txDone.Store(true) timeout.Store(false) led.Set(false) } if irqStatus&sx128x.IRQ_RX_DONE_MASK != 0 { rxDone.Store(true) timeout.Store(false) led.Set(true) println("rx done") } if irqStatus&sx128x.IRQ_RX_TX_TIMEOUT_MASK != 0 { timeout.Store(true) txDone.Store(true) rxDone.Store(true) println("rx/tx timeout") } dio1Interrupt.Store(false) } if rxDone.Load() { rxDone.Store(false) if !timeout.Load() { rxLength, rxPointer, _ := radio.GetRxBufferStatus() rxData, _ := radio.ReadBuffer(rxPointer, rxLength) // num, err := conn.Write(rxData) // if err != nil { // println("failed to send OSC message:", err) // } else { // println("sent", num, "bytes to broker") // } println("received message:", string(rxData)) led.Set(false) } Rx(radio, loraConfig) println("radio set to receive") } else { if dio1Pin.Get() { irqStatus, _ := radio.GetIrqStatus() if irqStatus&sx128x.IRQ_TX_DONE_MASK != 0 { txDone.Store(true) timeout.Store(false) led.Set(false) } 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) } } } } } 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 == lora.SpreadingFactor5 || config.SpreadingFactor == lora.SpreadingFactor6 { data[0] = 0x1E } else if config.SpreadingFactor == lora.SpreadingFactor7 || config.SpreadingFactor == 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.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 Rx(radio *sx128x.Device, loraConfig LoRaConfig) { radio.SetStandby(sx128x.STANDBY_XOSC) radio.SetBufferBaseAddress(0, 0) 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.ClearIrqStatus(sx128x.IRQ_ALL_MASK) err := radio.SetRx(sx128x.PERIOD_BASE_4_MS, 250) if err != nil { println("failed to set RX mode:", err) } }