package main import ( "machine" "sync/atomic" "time" "tinygo.org/x/drivers/lora" "tinygo.org/x/drivers/sx126x" ) 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.GPIO34 busyPin.Configure(machine.PinConfig{Mode: machine.PinInput}) button := machine.GPIO0 button.Configure(machine.PinConfig{Mode: machine.PinInput}) button.SetInterrupt(machine.PinFalling, func(machine.Pin) { println("button interrupt") }) println("initializing radio...") radio := sx126x.New( spi, nssPin, resetPin, busyPin, ) dio1 := machine.GPIO33 var txDone atomic.Bool txDone.Store(true) var rxDone atomic.Bool rxDone.Store(true) var timeout atomic.Bool timeout.Store(true) dio1.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) dio1.SetInterrupt(machine.PinRising, func(machine.Pin) { irqStatus := radio.GetIrqStatus() println("irq status:", irqStatus) if irqStatus&sx126x.IRQ_TX_DONE_MASK != 0 { println("tx done") txDone.Store(true) timeout.Store(false) led.Set(false) } if irqStatus&sx126x.IRQ_RX_DONE_MASK != 0 { println("rx done") rxDone.Store(true) timeout.Store(false) led.Set(true) } if irqStatus&sx126x.IRQ_TIMEOUT_MASK != 0 { println("timeout") txDone.Store(true) rxDone.Store(true) timeout.Store(true) } }) loraConfig := lora.Config{ Freq: lora.MHZ_915_0, Bw: lora.Bandwidth_125_0, Sf: lora.SpreadingFactor9, Cr: lora.CodingRate4_7, HeaderType: lora.HeaderExplicit, Preamble: 8, Ldr: lora.LowDataRateOptimizeOff, Iq: lora.IQStandard, Crc: lora.CRCOff, SyncWord: 0x1424, LoraTxPowerDBm: 2, } radio.WaitWhileBusy() SetupLora(radio, loraConfig) for { // if txDone.Load() { // txDone.Store(false) // println("cleaning up prior to transmit...") // radio.ClearIrqStatus(sx126x.IRQ_ALL_MASK) // radio.SetStandby(sx126x.STANDBY_RC) // println("waiting 1s before transmitting...") // time.Sleep(1000 * time.Millisecond) // led.Set(true) // err := Tx(radio, loraConfig, []byte("hello world")) // if err != nil { // println("failed to enter transmit:", err) // } // println("transmit started") // } if rxDone.Load() { rxDone.Store(false) if !timeout.Load() { println("receive completed without timeout, reading buffer...") rxLength, rxPointer := radio.GetRxBufferStatus() println("rx length:", rxLength, "rx pointer:", rxPointer) rxData := radio.ReadBuffer(rxPointer, rxLength) println("rx data:", string(rxData)) led.Set(false) } else { println("receive timed out") } Rx(radio, loraConfig) println("receive started") } } } func SetupLora(radio *sx126x.Device, config lora.Config) { // Switch to standby prior to configuration changes chipMode, _ := radio.GetStatus() if chipMode != sx126x.CHIP_MODE_STBY_RC { radio.SetStandby(sx126x.STANDBY_RC) } radio.ClearDeviceErrors() // Clear errors, disable radio interrupts for the moment radio.SetPacketType(sx126x.PACKET_TYPE_LORA) radio.SetRfFrequency(config.Freq) radio.SetModulationParamsLoRa(spreadingFactor(config.Sf), bandwidth(config.Bw), codingRate(config.Cr), config.Ldr) radio.SetPaConfig(0x04, 0x07, sx126x.DEVICE_SEL_SX1262) radio.SetTxParams(config.LoraTxPowerDBm, sx126x.RADIO_RAMP_200U) var syncWord [2]uint8 syncWord[0] = uint8(config.SyncWord >> 8) syncWord[1] = uint8(config.SyncWord & 0x00FF) radio.WriteRegister(sx126x.REG_LORA_SYNC_WORD_MSB, syncWord[:]) radio.SetRxTxFallbackMode(sx126x.FALLBACK_MODE_STDBY_RC) radio.SetDIO2AsRfSwitchCtrl(true) radio.SetDIO3AsTCXOCtrl(sx126x.TCXO_VOLTAGE_1_6V, 5000) } func Tx(radio *sx126x.Device, loraConfig lora.Config, data []byte) error { radio.SetStandby(sx126x.STANDBY_RC) radio.SetBufferBaseAddress(0, 0) radio.SetDioIrqParams(sx126x.IRQ_TX_DONE_MASK|sx126x.IRQ_TIMEOUT_MASK, sx126x.IRQ_TX_DONE_MASK|sx126x.IRQ_TIMEOUT_MASK, 0x00, 0x00) if len(data) > 255 { return nil } radio.SetPacketParamsLoRa(loraConfig.Preamble, loraConfig.HeaderType, uint8(len(data)&0xFF), loraConfig.Crc, loraConfig.Iq) radio.WriteBuffer(0, data) radio.ClearIrqStatus(sx126x.IRQ_ALL_MASK) radio.SetTx(1000) return nil } func Rx(radio *sx126x.Device, loraConfig lora.Config) { radio.SetStandby(sx126x.STANDBY_RC) radio.ClearIrqStatus(sx126x.IRQ_ALL_MASK) radio.SetBufferBaseAddress(0, 0) radio.SetRfFrequency(loraConfig.Freq) radio.SetPacketParamsLoRa(loraConfig.Preamble, loraConfig.HeaderType, 0xFF, loraConfig.Crc, loraConfig.Iq) radio.SetDioIrqParams(sx126x.IRQ_RX_DONE_MASK|sx126x.IRQ_TIMEOUT_MASK, sx126x.IRQ_RX_DONE_MASK|sx126x.IRQ_TIMEOUT_MASK, 0x00, 0x00) radio.SetRx(1000) } func codingRate(cr uint8) uint8 { switch cr { case lora.CodingRate4_5: return sx126x.LORA_CR_4_5 case lora.CodingRate4_6: return sx126x.LORA_CR_4_6 case lora.CodingRate4_7: return sx126x.LORA_CR_4_7 case lora.CodingRate4_8: return sx126x.LORA_CR_4_8 default: return 0 } } func spreadingFactor(sf uint8) uint8 { switch sf { case lora.SpreadingFactor5: return sx126x.LORA_SF_5 case lora.SpreadingFactor6: return sx126x.LORA_SF_6 case lora.SpreadingFactor7: return sx126x.LORA_SF_7 case lora.SpreadingFactor8: return sx126x.LORA_SF_8 case lora.SpreadingFactor9: return sx126x.LORA_SF_9 case lora.SpreadingFactor10: return sx126x.LORA_SF_10 case lora.SpreadingFactor11: return sx126x.LORA_SF_11 case lora.SpreadingFactor12: return sx126x.LORA_SF_12 default: return 0 } } func bandwidth(bw uint8) uint8 { switch bw { case lora.Bandwidth_7_8: return sx126x.LORA_BW_7 case lora.Bandwidth_10_4: return sx126x.LORA_BW_10 case lora.Bandwidth_15_6: return sx126x.LORA_BW_15 case lora.Bandwidth_20_8: return sx126x.LORA_BW_20 case lora.Bandwidth_31_25: return sx126x.LORA_BW_31 case lora.Bandwidth_41_7: return sx126x.LORA_BW_41 case lora.Bandwidth_62_5: return sx126x.LORA_BW_62 case lora.Bandwidth_125_0: return sx126x.LORA_BW_125 case lora.Bandwidth_250_0: return sx126x.LORA_BW_250 case lora.Bandwidth_500_0: return sx126x.LORA_BW_500 default: return 0 } } func timeoutMsToRtcSteps(timeoutMs uint32) uint32 { r := uint32(timeoutMs * (64000 / 1000)) return r }