Compare commits
2 Commits
876bafaa7c
...
5c0c9d0a93
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c0c9d0a93 | |||
| cf5fcf7432 |
@@ -0,0 +1,18 @@
|
|||||||
|
module lilygot3s3
|
||||||
|
|
||||||
|
go 1.26.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jwetzell/osc-go v0.3.0
|
||||||
|
tinygo.org/x/drivers v0.35.0
|
||||||
|
tinygo.org/x/espradio v0.1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
|
github.com/soypat/lneto v0.1.1-0.20260425023453-aa77403a2b32 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace tinygo.org/x/drivers => /Users/jwetzell/Projects/drivers
|
||||||
|
|
||||||
|
replace tinygo.org/x/espradio => /Users/jwetzell/Projects/espradio
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
|
github.com/jwetzell/osc-go v0.3.0 h1:z75TxuQSEmdcmZ56OAepkDa3m88SdZh//3m4nBb/XZI=
|
||||||
|
github.com/jwetzell/osc-go v0.3.0/go.mod h1:kCs329JxY6Qjga08tRQ/Gl0PqhgQzLIMpOhm6uszvIc=
|
||||||
|
github.com/soypat/lneto v0.1.1-0.20260425023453-aa77403a2b32 h1:4fNa4mcNPVTMJ3Kzn+cS/LqmpXhKM9SiZ9Vgu/Ucq3c=
|
||||||
|
github.com/soypat/lneto v0.1.1-0.20260425023453-aa77403a2b32/go.mod h1:Be5PjwoYukvHFiUXxpYi8+ppH2F/gw/vjGBvFdv+Ti8=
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,12 +66,13 @@ func main() {
|
|||||||
timeout.Store(true)
|
timeout.Store(true)
|
||||||
|
|
||||||
dio1Pin.SetInterrupt(machine.PinRising, func(machine.Pin) {
|
dio1Pin.SetInterrupt(machine.PinRising, func(machine.Pin) {
|
||||||
irqStatus := radio.GetIrqStatus()
|
irqStatus, _ := radio.GetIrqStatus()
|
||||||
|
|
||||||
if irqStatus&sx128x.IRQ_TX_DONE_MASK != 0 {
|
if irqStatus&sx128x.IRQ_TX_DONE_MASK != 0 {
|
||||||
txDone.Store(true)
|
txDone.Store(true)
|
||||||
timeout.Store(false)
|
timeout.Store(false)
|
||||||
led.Set(false)
|
led.Set(false)
|
||||||
|
println("tx done")
|
||||||
}
|
}
|
||||||
if irqStatus&sx128x.IRQ_RX_DONE_MASK != 0 {
|
if irqStatus&sx128x.IRQ_RX_DONE_MASK != 0 {
|
||||||
rxDone.Store(true)
|
rxDone.Store(true)
|
||||||
@@ -103,13 +104,8 @@ func main() {
|
|||||||
SetupLora(radio, loraConfig)
|
SetupLora(radio, loraConfig)
|
||||||
|
|
||||||
oscMessage := osc.OSCMessage{
|
oscMessage := osc.OSCMessage{
|
||||||
Address: "/lora/button",
|
Address: "/cue/1/go",
|
||||||
Args: []osc.OSCArg{
|
Args: []osc.OSCArg{},
|
||||||
{
|
|
||||||
Type: "i",
|
|
||||||
Value: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -120,7 +116,6 @@ func main() {
|
|||||||
radio.SetStandby(sx128x.STANDBY_RC)
|
radio.SetStandby(sx128x.STANDBY_RC)
|
||||||
led.Set(true)
|
led.Set(true)
|
||||||
|
|
||||||
oscMessage.Args[0].Value = int32(time.Now().UnixMilli() / 1000)
|
|
||||||
bytes, err := oscMessage.ToBytes()
|
bytes, err := oscMessage.ToBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("failed to serialize OSC message:", err)
|
println("failed to serialize OSC message:", err)
|
||||||
@@ -163,7 +158,7 @@ func main() {
|
|||||||
func SetupLora(radio *sx128x.Device, config lora.Config) {
|
func SetupLora(radio *sx128x.Device, config lora.Config) {
|
||||||
// Switch to standby prior to configuration changes
|
// Switch to standby prior to configuration changes
|
||||||
|
|
||||||
circuitMode, _ := radio.GetStatus()
|
circuitMode, _, _ := radio.GetStatus()
|
||||||
if circuitMode != sx128x.CIRCUIT_MODE_STDBY_RC {
|
if circuitMode != sx128x.CIRCUIT_MODE_STDBY_RC {
|
||||||
radio.SetStandby(sx128x.STANDBY_RC)
|
radio.SetStandby(sx128x.STANDBY_RC)
|
||||||
}
|
}
|
||||||
@@ -184,7 +179,7 @@ func SetupLora(radio *sx128x.Device, config lora.Config) {
|
|||||||
data[0] = 0x32
|
data[0] = 0x32
|
||||||
}
|
}
|
||||||
radio.WriteRegister(0x925, data[:])
|
radio.WriteRegister(0x925, data[:])
|
||||||
existing := radio.ReadRegister(0x93C)
|
existing, _ := radio.ReadRegister(0x93C)
|
||||||
data[0] = existing | 0x01
|
data[0] = existing | 0x01
|
||||||
radio.WriteRegister(0x93C, data[:])
|
radio.WriteRegister(0x93C, data[:])
|
||||||
|
|
||||||
@@ -199,7 +194,7 @@ func SetupLora(radio *sx128x.Device, config lora.Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkStatus(radio *sx128x.Device, operation string) {
|
func checkStatus(radio *sx128x.Device, operation string) {
|
||||||
circuitMode, commandStatus := radio.GetStatus()
|
circuitMode, commandStatus, _ := radio.GetStatus()
|
||||||
if commandStatus != sx128x.COMMAND_STATUS_SUCCESS {
|
if commandStatus != sx128x.COMMAND_STATUS_SUCCESS {
|
||||||
println(operation, "-> failed with status:", commandStatus)
|
println(operation, "-> failed with status:", commandStatus)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user