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 ) 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) dio1Pin.SetInterrupt(machine.PinRising, func(machine.Pin) { 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") } }) 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) println("radio initialized, waiting for messages...") for { runtime.Gosched() 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") } 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 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 Rx(radio *sx128x.Device, loraConfig lora.Config) { 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) } } 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 } }