From cb46530b1a0c7a8d72eea30c698a466d97c0046c Mon Sep 17 00:00:00 2001 From: Filip Vranesevic Date: Sun, 8 Feb 2026 08:32:03 +0100 Subject: [PATCH 1/4] WIO SX126x examples --- examples/fsk4/noradio.go | 2 +- examples/fsk4/wio_sx126x.go | 112 +++++++++++++++++++++++++++++++++++ examples/morse/noradio.go | 2 +- examples/morse/wio_sx126x.go | 112 +++++++++++++++++++++++++++++++++++ examples/wspr/noradio.go | 2 +- examples/wspr/wio_sx126x.go | 112 +++++++++++++++++++++++++++++++++++ fsk4/fsk4.go | 4 +- morse/morse.go | 4 +- morse/radio.go | 2 +- 9 files changed, 344 insertions(+), 8 deletions(-) create mode 100644 examples/fsk4/wio_sx126x.go create mode 100644 examples/morse/wio_sx126x.go create mode 100644 examples/wspr/wio_sx126x.go diff --git a/examples/fsk4/noradio.go b/examples/fsk4/noradio.go index 640183c..879fe56 100644 --- a/examples/fsk4/noradio.go +++ b/examples/fsk4/noradio.go @@ -1,4 +1,4 @@ -//go:build !si5351 && !featherwing +//go:build !si5351 && !featherwing && !wio_sx1262 package main diff --git a/examples/fsk4/wio_sx126x.go b/examples/fsk4/wio_sx126x.go new file mode 100644 index 0000000..e266969 --- /dev/null +++ b/examples/fsk4/wio_sx126x.go @@ -0,0 +1,112 @@ +//go:build nicenano && wio_sx1262 + +// This is an FSK4 modem example using Wio SX1262 module as the radio transmitter. +// Build this example for NiceNano with Wio SX1262 module with command like this: +// tinygo build -target nicenano -tags wio_sx1262 +// For different targets, adjust the pin variables in this file accordingly. + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/sx126x" + "tinygo.org/x/wireless/fsk4" +) + +const freq = 432_300_000 + +var ( + spi = machine.SPI0 + rstPin = machine.D009 + misoPin = machine.D002 + mosiPin = machine.D115 + sckPin = machine.D111 + nssPin = machine.D113 + busyPin = machine.D029 + dio1Pin = machine.D010 + rfSw = machine.D017 +) + +func initRadio() *fsk4.FSK4 { + time.Sleep(3 * time.Second) + + rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + rfSw.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + err := spi.Configure(machine.SPIConfig{ + Frequency: 500_000, + Mode: 0, + SCK: sckPin, + SDO: mosiPin, + SDI: misoPin, + }) + if err != nil { + panic(err) + } + + println("Initializing SX126x...") + dev := sx126x.New(spi, rstPin) + rc := sx126x.NewRadioControlWio(nssPin, busyPin, dio1Pin, rfSw) + dev.SetRadioController(rc) + + println("Resetting device") + dev.Reset() + time.Sleep(100 * time.Millisecond) + + if !dev.DetectDevice() { + for { + println("SX126x device not found!") + time.Sleep(5 * time.Second) + } + } + println("SX126x device detected") + + // Wio specific configuration + dev.SetDio3AsTcxoCtrl(sx126x.SX126X_DIO3_OUTPUT_1_8, 5*time.Millisecond) + dev.SetRegulatorMode(sx126x.SX126X_REGULATOR_DC_DC) + dev.SetDeviceType(sx126x.DEVICE_TYPE_SX1262) + dev.Calibrate(sx126x.SX126X_CALIBRATE_ALL) + + dev.SetPacketType(sx126x.SX126X_PACKET_TYPE_GFSK) + dev.SetTxParams(0, sx126x.SX126X_PA_RAMP_200U) + dev.SetRfFrequency(freq) + dev.CalibrateImage(freq) + + dev.SetStandby() + + radio := &sx126xRadio{device: dev} + f := fsk4.NewFSK4(radio, freq, 270, 10*time.Millisecond) + _ = f.Configure() + return f +} + +type sx126xRadio struct { + device *sx126x.Device + transmitting bool +} + +func (r *sx126xRadio) Transmit(freq uint64) error { + // Put the radio to XOSC mode before first transmission + // Without this, the first tone after standby is not transmitted + if !r.transmitting { + r.device.ExecSetCommand(sx126x.SX126X_CMD_SET_STANDBY, []uint8{sx126x.SX126X_STANDBY_XOSC}) + } + r.transmitting = true + + r.device.SetRfFrequency(uint32(freq)) + r.device.SetTxContinuousWave() + + return nil +} + +func (r *sx126xRadio) Standby() error { + r.transmitting = false + r.device.SetStandby() + return nil +} + +func (r *sx126xRadio) Close() error { + return r.Standby() +} diff --git a/examples/morse/noradio.go b/examples/morse/noradio.go index 1d45deb..5ffdd06 100644 --- a/examples/morse/noradio.go +++ b/examples/morse/noradio.go @@ -1,4 +1,4 @@ -//go:build !si5351 && !featherwing +//go:build !si5351 && !featherwing && !wio_sx1262 package main diff --git a/examples/morse/wio_sx126x.go b/examples/morse/wio_sx126x.go new file mode 100644 index 0000000..2406e75 --- /dev/null +++ b/examples/morse/wio_sx126x.go @@ -0,0 +1,112 @@ +//go:build nicenano && wio_sx1262 + +// This is a Morse code modem example using Wio SX1262 module as the radio transmitter. +// Build this example for NiceNano with Wio SX1262 module with command like this: +// tinygo build -target nicenano -tags wio_sx1262 +// For different targets, adjust the pin variables in this file accordingly. + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/sx126x" + "tinygo.org/x/wireless/morse" +) + +const freq = 432_300_000 + +var ( + spi = machine.SPI0 + rstPin = machine.D009 + misoPin = machine.D002 + mosiPin = machine.D115 + sckPin = machine.D111 + nssPin = machine.D113 + busyPin = machine.D029 + dio1Pin = machine.D010 + rfSw = machine.D017 +) + +func initRadio() *morse.Morse { + time.Sleep(3 * time.Second) + + rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + rfSw.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + err := spi.Configure(machine.SPIConfig{ + Frequency: 500_000, + Mode: 0, + SCK: sckPin, + SDO: mosiPin, + SDI: misoPin, + }) + if err != nil { + panic(err) + } + + println("Initializing SX126x...") + dev := sx126x.New(spi, rstPin) + rc := sx126x.NewRadioControlWio(nssPin, busyPin, dio1Pin, rfSw) + dev.SetRadioController(rc) + + println("Resetting device") + dev.Reset() + time.Sleep(100 * time.Millisecond) + + if !dev.DetectDevice() { + for { + println("SX126x device not found!") + time.Sleep(5 * time.Second) + } + } + println("SX126x device detected") + + // Wio specific configuration + dev.SetDio3AsTcxoCtrl(sx126x.SX126X_DIO3_OUTPUT_1_8, 5*time.Millisecond) + dev.SetRegulatorMode(sx126x.SX126X_REGULATOR_DC_DC) + dev.SetDeviceType(sx126x.DEVICE_TYPE_SX1262) + dev.Calibrate(sx126x.SX126X_CALIBRATE_ALL) + + dev.SetPacketType(sx126x.SX126X_PACKET_TYPE_GFSK) + dev.SetTxParams(0, sx126x.SX126X_PA_RAMP_200U) + dev.SetRfFrequency(freq) + dev.CalibrateImage(freq) + + dev.SetStandby() + + m := morse.NewMorse(&sx126xRadio{device: dev}, freq, 20) + _ = m.Configure() + + return m +} + +type sx126xRadio struct { + device *sx126x.Device + transmitting bool +} + +func (r *sx126xRadio) Transmit(freq uint64) error { + // Put the radio to XOSC mode before first transmission + // Without this, the first tone after standby is not transmitted + if !r.transmitting { + r.device.ExecSetCommand(sx126x.SX126X_CMD_SET_STANDBY, []uint8{sx126x.SX126X_STANDBY_XOSC}) + } + r.transmitting = true + + r.device.SetRfFrequency(uint32(freq)) + r.device.SetTxContinuousWave() + + return nil +} + +func (r *sx126xRadio) Standby() error { + r.transmitting = false + r.device.SetStandby() + return nil +} + +func (r *sx126xRadio) Close() error { + return r.Standby() +} diff --git a/examples/wspr/noradio.go b/examples/wspr/noradio.go index 8505b06..fa5548c 100644 --- a/examples/wspr/noradio.go +++ b/examples/wspr/noradio.go @@ -1,4 +1,4 @@ -//go:build !si5351 && !featherwing +//go:build !si5351 && !featherwing && !wio_sx1262 package main diff --git a/examples/wspr/wio_sx126x.go b/examples/wspr/wio_sx126x.go new file mode 100644 index 0000000..201f689 --- /dev/null +++ b/examples/wspr/wio_sx126x.go @@ -0,0 +1,112 @@ +//go:build nicenano && wio_sx1262 + +// This is an example using Wio SX1262 module as WSPR transmitter. +// Build this example for NiceNano with Wio SX1262 module with command like this: +// tinygo build -target nicenano -tags wio_sx1262 +// For different targets, adjust the pin variables in this file accordingly. + +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/sx126x" + "tinygo.org/x/wireless/fsk4" +) + +const freq = 432_300_000 + +var ( + spi = machine.SPI0 + rstPin = machine.D009 + misoPin = machine.D002 + mosiPin = machine.D115 + sckPin = machine.D111 + nssPin = machine.D113 + busyPin = machine.D029 + dio1Pin = machine.D010 + rfSw = machine.D017 +) + +func initRadio() *fsk4.FSK4 { + time.Sleep(3 * time.Second) + + rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + rfSw.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + err := spi.Configure(machine.SPIConfig{ + Frequency: 500_000, + Mode: 0, + SCK: sckPin, + SDO: mosiPin, + SDI: misoPin, + }) + if err != nil { + panic(err) + } + + println("Initializing SX126x...") + dev := sx126x.New(spi, rstPin) + rc := sx126x.NewRadioControlWio(nssPin, busyPin, dio1Pin, rfSw) + dev.SetRadioController(rc) + + println("Resetting device") + dev.Reset() + time.Sleep(100 * time.Millisecond) + + if !dev.DetectDevice() { + for { + println("SX126x device not found!") + time.Sleep(5 * time.Second) + } + } + println("SX126x device detected") + + // Wio specific configuration + dev.SetDio3AsTcxoCtrl(sx126x.SX126X_DIO3_OUTPUT_1_8, 5*time.Millisecond) + dev.SetRegulatorMode(sx126x.SX126X_REGULATOR_DC_DC) + dev.SetDeviceType(sx126x.DEVICE_TYPE_SX1262) + dev.Calibrate(sx126x.SX126X_CALIBRATE_ALL) + + dev.SetPacketType(sx126x.SX126X_PACKET_TYPE_GFSK) + dev.SetTxParams(0, sx126x.SX126X_PA_RAMP_200U) + dev.SetRfFrequency(freq) + dev.CalibrateImage(freq) + + dev.SetStandby() + + radio := &sx126xRadio{device: dev} + f := fsk4.NewFSK4(radio, freq, 270, 10*time.Millisecond) + _ = f.Configure() + return f +} + +type sx126xRadio struct { + device *sx126x.Device + transmitting bool +} + +func (r *sx126xRadio) Transmit(freq uint64) error { + // Put the radio to XOSC mode before first transmission + // Without this, the first tone after standby is not transmitted + if !r.transmitting { + r.device.ExecSetCommand(sx126x.SX126X_CMD_SET_STANDBY, []uint8{sx126x.SX126X_STANDBY_XOSC}) + } + r.transmitting = true + + r.device.SetRfFrequency(uint32(freq)) + r.device.SetTxContinuousWave() + + return nil +} + +func (r *sx126xRadio) Standby() error { + r.transmitting = false + r.device.SetStandby() + return nil +} + +func (r *sx126xRadio) Close() error { + return r.Standby() +} diff --git a/fsk4/fsk4.go b/fsk4/fsk4.go index b958358..10ded81 100644 --- a/fsk4/fsk4.go +++ b/fsk4/fsk4.go @@ -21,7 +21,7 @@ type FSK4 struct { // NewFSK4 creates a new FSK4 modem instance. // radio: the Radio interface implementation // base: the base frequency in Hz -// shift: the frequency shift in Hz*100 eg. 270 = 2.7 Hz +// shift: the frequency shift in Hz // rate: the send rate func NewFSK4(radio Radio, base uint64, shift uint32, rate time.Duration) *FSK4 { return &FSK4{ @@ -131,7 +131,7 @@ func (r *FSK4) writeByte(data byte) error { func (r *FSK4) tone(symbol byte) error { start := time.Now() - freq := r.base*100 + uint64(r.tones[symbol]) + freq := r.base + uint64(r.tones[symbol]) if err := r.radio.Transmit(freq); err != nil { return err } diff --git a/morse/morse.go b/morse/morse.go index 4a2e641..f8c4257 100644 --- a/morse/morse.go +++ b/morse/morse.go @@ -92,13 +92,13 @@ func (m *Morse) write(b byte) error { for code != guardBit { if (code & Dash) == 1 { // dash - if err := m.radio.Transmit(m.base * 100); err != nil { + if err := m.radio.Transmit(m.base); err != nil { return err } time.Sleep(m.dashLength) } else { // dot - if err := m.radio.Transmit(m.base * 100); err != nil { + if err := m.radio.Transmit(m.base); err != nil { return err } time.Sleep(m.dotLength) diff --git a/morse/radio.go b/morse/radio.go index 232bd6c..f0c65dd 100644 --- a/morse/radio.go +++ b/morse/radio.go @@ -2,7 +2,7 @@ package morse // Radio defines the interface for Morse radio transmitters. type Radio interface { - // Transmit sends a signal at the specified frequency passed in Hz * 100 + // Transmit sends a signal at the specified frequency passed in Hz Transmit(freq uint64) error // Standby puts the radio into standby mode. Standby() error From 825603c70589ffb853cbd651714b06922b1867b0 Mon Sep 17 00:00:00 2001 From: Filip Vranesevic Date: Tue, 17 Feb 2026 08:51:49 +0100 Subject: [PATCH 2/4] Reverting to fractional frequency values --- fsk4/fsk4.go | 4 ++-- morse/morse.go | 4 ++-- morse/radio.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fsk4/fsk4.go b/fsk4/fsk4.go index 10ded81..b958358 100644 --- a/fsk4/fsk4.go +++ b/fsk4/fsk4.go @@ -21,7 +21,7 @@ type FSK4 struct { // NewFSK4 creates a new FSK4 modem instance. // radio: the Radio interface implementation // base: the base frequency in Hz -// shift: the frequency shift in Hz +// shift: the frequency shift in Hz*100 eg. 270 = 2.7 Hz // rate: the send rate func NewFSK4(radio Radio, base uint64, shift uint32, rate time.Duration) *FSK4 { return &FSK4{ @@ -131,7 +131,7 @@ func (r *FSK4) writeByte(data byte) error { func (r *FSK4) tone(symbol byte) error { start := time.Now() - freq := r.base + uint64(r.tones[symbol]) + freq := r.base*100 + uint64(r.tones[symbol]) if err := r.radio.Transmit(freq); err != nil { return err } diff --git a/morse/morse.go b/morse/morse.go index f8c4257..4a2e641 100644 --- a/morse/morse.go +++ b/morse/morse.go @@ -92,13 +92,13 @@ func (m *Morse) write(b byte) error { for code != guardBit { if (code & Dash) == 1 { // dash - if err := m.radio.Transmit(m.base); err != nil { + if err := m.radio.Transmit(m.base * 100); err != nil { return err } time.Sleep(m.dashLength) } else { // dot - if err := m.radio.Transmit(m.base); err != nil { + if err := m.radio.Transmit(m.base * 100); err != nil { return err } time.Sleep(m.dotLength) diff --git a/morse/radio.go b/morse/radio.go index f0c65dd..232bd6c 100644 --- a/morse/radio.go +++ b/morse/radio.go @@ -2,7 +2,7 @@ package morse // Radio defines the interface for Morse radio transmitters. type Radio interface { - // Transmit sends a signal at the specified frequency passed in Hz + // Transmit sends a signal at the specified frequency passed in Hz * 100 Transmit(freq uint64) error // Standby puts the radio into standby mode. Standby() error From a438d26c1b09ecf220fda1092ebde2d0dcc594ab Mon Sep 17 00:00:00 2001 From: Filip Vranesevic Date: Sun, 8 Feb 2026 08:32:03 +0100 Subject: [PATCH 3/4] WIO SX126x examples --- examples/morse/noradio.go | 2 +- fsk4/fsk4.go | 4 ++-- morse/morse.go | 4 ++-- morse/radio.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/morse/noradio.go b/examples/morse/noradio.go index 5ffdd06..9caaf77 100644 --- a/examples/morse/noradio.go +++ b/examples/morse/noradio.go @@ -1,4 +1,4 @@ -//go:build !si5351 && !featherwing && !wio_sx1262 +//go:build !si5351 && !featherwing && !pwm && !wio_sx1262 package main diff --git a/fsk4/fsk4.go b/fsk4/fsk4.go index b958358..10ded81 100644 --- a/fsk4/fsk4.go +++ b/fsk4/fsk4.go @@ -21,7 +21,7 @@ type FSK4 struct { // NewFSK4 creates a new FSK4 modem instance. // radio: the Radio interface implementation // base: the base frequency in Hz -// shift: the frequency shift in Hz*100 eg. 270 = 2.7 Hz +// shift: the frequency shift in Hz // rate: the send rate func NewFSK4(radio Radio, base uint64, shift uint32, rate time.Duration) *FSK4 { return &FSK4{ @@ -131,7 +131,7 @@ func (r *FSK4) writeByte(data byte) error { func (r *FSK4) tone(symbol byte) error { start := time.Now() - freq := r.base*100 + uint64(r.tones[symbol]) + freq := r.base + uint64(r.tones[symbol]) if err := r.radio.Transmit(freq); err != nil { return err } diff --git a/morse/morse.go b/morse/morse.go index 4a2e641..f8c4257 100644 --- a/morse/morse.go +++ b/morse/morse.go @@ -92,13 +92,13 @@ func (m *Morse) write(b byte) error { for code != guardBit { if (code & Dash) == 1 { // dash - if err := m.radio.Transmit(m.base * 100); err != nil { + if err := m.radio.Transmit(m.base); err != nil { return err } time.Sleep(m.dashLength) } else { // dot - if err := m.radio.Transmit(m.base * 100); err != nil { + if err := m.radio.Transmit(m.base); err != nil { return err } time.Sleep(m.dotLength) diff --git a/morse/radio.go b/morse/radio.go index 232bd6c..f0c65dd 100644 --- a/morse/radio.go +++ b/morse/radio.go @@ -2,7 +2,7 @@ package morse // Radio defines the interface for Morse radio transmitters. type Radio interface { - // Transmit sends a signal at the specified frequency passed in Hz * 100 + // Transmit sends a signal at the specified frequency passed in Hz Transmit(freq uint64) error // Standby puts the radio into standby mode. Standby() error From 78353e19010d60a21edcb0ad813c52e37cc77458 Mon Sep 17 00:00:00 2001 From: Filip Vranesevic Date: Fri, 20 Feb 2026 14:05:25 +0100 Subject: [PATCH 4/4] using hundredths of Hz --- examples/fsk4/wio_sx126x.go | 3 ++- examples/morse/noradio.go | 2 +- examples/morse/wio_sx126x.go | 3 ++- examples/wspr/wio_sx126x.go | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/fsk4/wio_sx126x.go b/examples/fsk4/wio_sx126x.go index e266969..f5d1565 100644 --- a/examples/fsk4/wio_sx126x.go +++ b/examples/fsk4/wio_sx126x.go @@ -95,7 +95,8 @@ func (r *sx126xRadio) Transmit(freq uint64) error { } r.transmitting = true - r.device.SetRfFrequency(uint32(freq)) + freqHz := freq / 100 // freq is in hundredths of Hz + r.device.SetRfFrequency(uint32(freqHz)) r.device.SetTxContinuousWave() return nil diff --git a/examples/morse/noradio.go b/examples/morse/noradio.go index 9caaf77..5ffdd06 100644 --- a/examples/morse/noradio.go +++ b/examples/morse/noradio.go @@ -1,4 +1,4 @@ -//go:build !si5351 && !featherwing && !pwm && !wio_sx1262 +//go:build !si5351 && !featherwing && !wio_sx1262 package main diff --git a/examples/morse/wio_sx126x.go b/examples/morse/wio_sx126x.go index 2406e75..1c6a695 100644 --- a/examples/morse/wio_sx126x.go +++ b/examples/morse/wio_sx126x.go @@ -95,7 +95,8 @@ func (r *sx126xRadio) Transmit(freq uint64) error { } r.transmitting = true - r.device.SetRfFrequency(uint32(freq)) + freqHz := freq / 100 // freq is in hundredths of Hz + r.device.SetRfFrequency(uint32(freqHz)) r.device.SetTxContinuousWave() return nil diff --git a/examples/wspr/wio_sx126x.go b/examples/wspr/wio_sx126x.go index 201f689..b279237 100644 --- a/examples/wspr/wio_sx126x.go +++ b/examples/wspr/wio_sx126x.go @@ -95,7 +95,8 @@ func (r *sx126xRadio) Transmit(freq uint64) error { } r.transmitting = true - r.device.SetRfFrequency(uint32(freq)) + freqHz := freq / 100 // freq is in hundredths of Hz + r.device.SetRfFrequency(uint32(freqHz)) r.device.SetTxContinuousWave() return nil