Compare commits

...

17 Commits

Author SHA1 Message Date
deadprogram b6b5668945 examples: correct missing const for lora atcmd
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 19:13:18 +01:00
deadprogram 24b6f0f296 examples: use shorter names for Lora radio functions
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 18:51:03 +01:00
deadprogram 38ba7772e2 sx126x, sx127x: remove extra Lora in functions to avoid stuttering
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 17:17:05 +01:00
deadprogram 6df6bf4880 examples: add SEND/RECV to lora at-cmd for LoRa P2P/P2MP (not LoRaWAN) testing
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 14:31:25 +01:00
deadprogram 8cfe5ebc4a examples: lora atcmd minimal rx and tx
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 14:31:25 +01:00
deadprogram f098853465 examples: add minimal README to at-cmd
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 14:31:25 +01:00
deadprogram dc953b09b6 examples: lora atcmd compiled for simulator, sx126x, and sx127x
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 14:31:25 +01:00
deadprogram 59ff8a4585 examples: further work on at-lora
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 14:31:25 +01:00
deadprogram 125d8b6530 examples: WIP on adding LoRa AT command set implementation
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 14:31:25 +01:00
deadprogram 1832e40f4d sx127x: correct header type implicit/explcit as they were reversed
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-08 14:30:55 +01:00
deadprogram 9af4640788 sx126x: add Reset() and needed pin
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-06 14:15:38 +01:00
deadprogram 65677a3836 sx127x: lora rx/tx example working, now assumes pybadge+featherwing
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-12-30 10:09:47 +01:00
deadprogram f67130b52d lora: created shared RadioEvent
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-12-30 10:09:47 +01:00
deadprogram d94003bd3a lora: move shared config for sx126x/sx127x to single package
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-12-30 10:09:47 +01:00
Olivier Fauchon a421318a4d sx127x: Ajout du support d'interruptions RXTimeout (DIO1) 2022-12-23 11:07:55 +01:00
Olivier Fauchon deb22c4a57 sx127x: Major cleanup/rewrite, and tests pass with lorawan stack tests 2022-12-23 11:07:55 +01:00
Olivier Fauchon 67d2af5d45 sx127x: Driver for Semtech sx127x radio modules
This first version of the driver has been tested with BB-FRM9x module
2022-12-23 11:07:55 +01:00
20 changed files with 1894 additions and 173 deletions
+29
View File
@@ -0,0 +1,29 @@
# AT-CMD implementation of at-lora command set
```
$ tinygo monitor
Connected to /dev/ttyACM0. Press Ctrl-C to exit.
+AT: OK
+VER: 0.0.1 (sx127x v18)
```
# Building
## Simulator
```
tinygo flash -target pico ./examples/lora/atcmd/
```
## PyBadge with LoRa Featherwing
```
tinygo flash -target pybadge -tags featherwing ./examples/lora/atcmd/
```
## LoRa-E5
```
tinygo flash -target lorae5 ./examples/lora/atcmd/
```
+410
View File
@@ -0,0 +1,410 @@
package main
import (
"encoding/hex"
"strings"
)
// Use to test if connection to module is OK.
func quicktest() {
writeCommandOutput("AT", "OK")
}
// Check firmware version.
func version() {
writeCommandOutput("VER", currentVersion()+" ("+firmwareVersion()+")")
}
// Use to check the ID of the LoRaWAN module, or change the ID.
func id(args string) error {
cmd := "ID"
// look for comma in args
param, val, hasComma := strings.Cut(args, ",")
if hasComma {
// set
switch param {
case "DevAddr":
writeCommandOutput(cmd, "DevAddr, "+val)
case "DevEui":
writeCommandOutput(cmd, "DevEui, "+val)
case "AppEui":
writeCommandOutput(cmd, "AppEui, "+val)
default:
return errInvalidCommand
}
return nil
}
// get
switch param {
case "DevAddr":
writeCommandOutput(cmd, "DevAddr, xx:xx:xx:xx")
case "DevEui":
writeCommandOutput(cmd, "DevEui, xx:xx:xx:xx:xx:xx:xx:xx")
case "AppEui":
writeCommandOutput(cmd, "AppEui, xx:xx:xx:xx:xx:xx:xx:xx")
default:
writeCommandOutput(cmd, "DevAddr, xx:xx:xx:xx")
writeCommandOutput(cmd, "DevEui, xx:xx:xx:xx:xx:xx:xx:xx")
writeCommandOutput(cmd, "AppEui, xx:xx:xx:xx:xx:xx:xx:xx")
}
return nil
}
// Use to reset the module. If module returns error, then reset function is invalid.
func reset() error {
radio.Reset()
writeCommandOutput("RESET", "OK")
return nil
}
// Use to send string format frame which is no need to be confirmed by the server.
func msg(data string) error {
cmd := "MSG"
writeCommandOutput(cmd, "Start")
if err := radio.Tx([]byte(data), defaultTimeout); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
writeCommandOutput(cmd, "Done")
return nil
}
// Use to send string format frame which must be confirmed by the server
func cmsg(data string) error {
cmd := "CMSG"
writeCommandOutput(cmd, "Start")
if err := radio.Tx([]byte(data), defaultTimeout); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
// TODO: confirmation
writeCommandOutput(cmd, "Done")
return nil
}
// Use to send hex format frame which is no need to be confirmed by the server
func msghex(data string) error {
cmd := "MSGHEX"
writeCommandOutput(cmd, "Start")
writeCommandOutput(cmd, "Done")
return nil
}
// Use to send hex format frame which must be confirmed by the server.
func cmsghex(data string) error {
cmd := "CMSGHEX"
writeCommandOutput(cmd, "Start")
writeCommandOutput(cmd, "Done")
return nil
}
// Use to send string format LoRaWAN proprietary frames
func pmsg(data string) error {
cmd := "PMSG"
writeCommandOutput(cmd, "Start")
writeCommandOutput(cmd, "Done")
return nil
}
// Use to send hex format LoRaWAN proprietary frames.
func pmsghex(data string) error {
cmd := "PMSGHEX"
writeCommandOutput(cmd, "Start")
writeCommandOutput(cmd, "Done")
return nil
}
// Set PORT number which will be used by MSG/CMSG/MSGHEX/CMSGHEX command to send
// message, port number should range from 1 to 255. User should refer to LoRaWAN
// specification to choose port.
func port(p string) error {
cmd := "PMSG"
writeCommandOutput(cmd, p)
return nil
}
// Set ADR function of LoRaWAN module
func adr(state string) error {
cmd := "ADR"
writeCommandOutput(cmd, state)
return nil
}
// Use LoRaWAN defined DRx to set datarate of LoRaWAN AT modem.
func dr(rate string) error {
cmd := "DR"
writeCommandOutput(cmd, rate)
return nil
}
// Channel Configuration
func ch(channel string) error {
cmd := "CH"
writeCommandOutput(cmd, channel)
return nil
}
// Set and Check Power
func power(setting string) error {
cmd := "POWER"
writeCommandOutput(cmd, setting)
return nil
}
// Unconfirmed message repeats times.
func rept(setting string) error {
cmd := "REPT"
writeCommandOutput(cmd, setting)
return nil
}
// Confirmed message retry times. Valid range 0~254,
// if retry times is less than 2, only one message will
// be sent. Random delay 3 - 10s between each retry
// (band duty cycle limitation has the priority)
func retry(setting string) error {
cmd := "RETRY"
writeCommandOutput(cmd, setting)
return nil
}
func rxwin2(setting string) error {
cmd := "RXWIN2"
writeCommandOutput(cmd, setting)
return nil
}
func rxwin1(setting string) error {
cmd := "RXWIN1"
writeCommandOutput(cmd, setting)
return nil
}
func key(setting string) error {
cmd := "KEY"
writeCommandOutput(cmd, setting)
return nil
}
func fdefault(setting string) error {
cmd := "FDEFAULT"
writeCommandOutput(cmd, "OK")
return nil
}
func mode(setting string) error {
cmd := "MODE"
writeCommandOutput(cmd, setting)
return nil
}
func join(setting string) error {
cmd := "JOIN"
writeCommandOutput(cmd, "Starting")
writeCommandOutput(cmd, "Done")
return nil
}
func beacon(setting string) error {
cmd := "BEACON"
writeCommandOutput(cmd, "Starting")
writeCommandOutput(cmd, "Done")
return nil
}
func class(setting string) error {
cmd := "CLASS"
writeCommandOutput(cmd, "Starting")
writeCommandOutput(cmd, "Done")
return nil
}
func delay(setting string) error {
cmd := "DELAY"
writeCommandOutput(cmd, setting)
return nil
}
func lw(setting string) error {
cmd := "LW"
writeCommandOutput(cmd, setting)
return nil
}
func wdt(setting string) error {
cmd := "WDT"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func lowpower(setting string) error {
cmd := "LOWPOWER"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func vdd(setting string) error {
cmd := "VDD"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func temp(setting string) error {
cmd := "TEMP"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func rtc(setting string) error {
cmd := "RTC"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func eeprom(setting string) error {
cmd := "EEPROM"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func uartcmd(setting string) error {
cmd := "UART"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func test(setting string) error {
cmd := "TEST"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func log(setting string) error {
cmd := "LOG"
writeCommandOutput(cmd, "Not implemented")
return nil
}
func send(data string) error {
cmd := "SEND"
writeCommandOutput(cmd, "Start")
// remove leading/trailing quotes
data = strings.Trim(data, "\"'")
if err := radio.Tx([]byte(data), defaultTimeout); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
writeCommandOutput(cmd, "Done")
return nil
}
func sendhex(data string) error {
cmd := "SENDHEX"
writeCommandOutput(cmd, "Start")
// remove leading/trailing quotes
data = strings.Trim(data, "\"'")
// convert data from hex formatted string
data = strings.ReplaceAll(data, " ", "")
payload, err := hex.DecodeString(data)
if err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
if err := radio.Tx(payload, defaultTimeout); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
writeCommandOutput(cmd, "Done")
return nil
}
func recv(setting string) error {
cmd := "RECV"
data, err := lorarx()
if err != nil {
writeCommandOutput(cmd, "ERROR "+err.Error())
return err
}
writeCommandOutput(cmd, string(data))
return nil
}
func recvhex(setting string) error {
cmd := "RECVHEX"
data, err := lorarx()
if err != nil {
writeCommandOutput(cmd, "ERROR "+err.Error())
return err
}
writeCommandOutput(cmd, string(data))
return nil
}
func crlf() {
uart.Write([]byte("\r\n"))
}
func writeCommandOutput(cmd, data string) {
uart.Write([]byte("+" + cmd + ": "))
uart.Write([]byte(data))
crlf()
}
+65
View File
@@ -0,0 +1,65 @@
// AT command set console running on the device UART to communicate with
// an attached LoRa device.
//
// Computer <-> UART <-> MCU <-> SPI <-> SX126x/SX127x
//
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
// For details on the AT command set, see:
// https://files.seeedstudio.com/products/317990687/res/LoRa-E5%20AT%20Command%20Specification_V1.0%20.pdf
package main
import (
"machine"
"time"
)
// change these to test a different UART or pins if available
var (
uart = machine.Serial
tx = machine.UART_TX_PIN
rx = machine.UART_RX_PIN
input = make([]byte, 0, 64)
radio LoraRadio
defaultTimeout uint32 = 1000
)
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
var err error
radio, err = setupLora()
if err != nil {
fail(err.Error())
}
for {
if uart.Buffered() > 0 {
data, _ := uart.ReadByte()
switch data {
case 13:
// return key
if err := parse(input); err != nil {
uart.Write([]byte("ERROR: "))
uart.Write([]byte(err.Error()))
crlf()
}
input = input[:0]
default:
// just capture the character
input = append(input, data)
}
}
time.Sleep(10 * time.Millisecond)
}
}
func fail(msg string) {
for {
uart.Write([]byte(msg))
crlf()
time.Sleep(time.Minute)
}
}
+115
View File
@@ -0,0 +1,115 @@
package main
import (
"errors"
"strings"
)
var (
errInvalidCommand = errors.New("Invalid command")
)
func parse(data []byte) error {
switch {
case len(data) < 2, string(data[0:2]) != "AT":
return errInvalidCommand
case len(data) == 2:
// just the AT command by itself
quicktest()
case len(data) < 6 || data[2] != '+':
return errInvalidCommand
default:
// parse the rest of the command
cmd, args, _ := strings.Cut(string(data[3:]), "=")
return parseCommand(cmd, args)
}
return nil
}
func parseCommand(cmd, args string) error {
switch cmd {
case "VER":
version()
case "ID":
id(args)
case "RESET":
reset()
case "MSG":
msg(args)
case "CMSG":
cmsg(args)
case "MSGHEX":
msghex(args)
case "CMSGHEX":
cmsghex(args)
case "PMSG":
pmsg(args)
case "PMSGHEX":
pmsg(args)
case "PORT":
port(args)
case "ADR":
adr(args)
case "DR":
dr(args)
case "CH":
ch(args)
case "POWER":
power(args)
case "REPT":
rept(args)
case "RETRY":
retry(args)
case "RXWIN2":
rxwin2(args)
case "RXWIN1":
rxwin1(args)
case "KEY":
key(args)
case "FDEFAULT":
fdefault(args)
case "MODE":
mode(args)
case "JOIN":
join(args)
case "BEACON":
join(args)
case "CLASS":
class(args)
case "DELAY":
delay(args)
case "LW":
lw(args)
case "WDT":
wdt(args)
case "LOWPOWER":
lowpower(args)
case "VDD":
vdd(args)
case "TEMP":
temp(args)
case "RTC":
rtc(args)
case "EEPROM":
eeprom(args)
case "UART":
uartcmd(args)
case "TEST":
test(args)
case "LOG":
log(args)
case "RECV":
recv(args)
case "RECVHEX":
recvhex(args)
case "SEND":
send(args)
case "SENDHEX":
sendhex(args)
default:
return errInvalidCommand
}
return nil
}
+22
View File
@@ -0,0 +1,22 @@
package main
import (
"errors"
)
var (
errRadioNotFound = errors.New("radio not found")
errRxTimeout = errors.New("radio RX timeout")
)
type LoraRadio interface {
Reset()
Tx(pkt []uint8, timeoutMs uint32) error
Rx(timeoutMs uint32) ([]uint8, error)
SetFrequency(freq uint32)
SetIqMode(mode uint8)
SetCodingRate(cr uint8)
SetBandwidth(bw uint8)
SetCrc(enable bool)
SetSpreadingFactor(sf uint8)
}
+37
View File
@@ -0,0 +1,37 @@
//go:build !featherwing && !gnse && !lorae5 && !nucleowl55jc
package main
// do simulator setup here
func setupLora() (LoraRadio, error) {
return &SimLoraRadio{}, nil
}
type SimLoraRadio struct {
}
func (sr *SimLoraRadio) Reset() {
}
func (sr *SimLoraRadio) Tx(pkt []uint8, timeoutMs uint32) error {
return nil
}
func (sr *SimLoraRadio) Rx(timeoutMs uint32) ([]uint8, error) {
return nil, nil
}
func (sr *SimLoraRadio) SetFrequency(freq uint32) {}
func (sr *SimLoraRadio) SetIqMode(mode uint8) {}
func (sr *SimLoraRadio) SetCodingRate(cr uint8) {}
func (sr *SimLoraRadio) SetBandwidth(bw uint8) {}
func (sr *SimLoraRadio) SetCrc(enable bool) {}
func (sr *SimLoraRadio) SetSpreadingFactor(sf uint8) {}
func firmwareVersion() string {
return "simulator " + currentVersion()
}
func lorarx() ([]byte, error) {
return nil, nil
}
+73
View File
@@ -0,0 +1,73 @@
//go:build gnse || lorae5 || nucleowl55jc
package main
import (
"device/stm32"
"machine"
"runtime/interrupt"
rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx126x"
)
const (
FREQ = 868100000
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
loraRadio *sx126x.Device
)
// do sx126x setup here
func setupLora() (LoraRadio, error) {
loraRadio = sx126x.New(machine.SPI3)
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
// Create RF Switch
var radioSwitch rfswitch.CustomSwitch
loraRadio.SetRfSwitch(radioSwitch)
if state := loraRadio.DetectDevice(); !state {
return nil, errRadioNotFound
}
// Add interrupt handler for Radio IRQs
intr := interrupt.New(stm32.IRQ_Radio_IRQ_Busy, radioIntHandler)
intr.Enable()
loraConf := lora.Config{
Freq: FREQ,
Bw: lora.Bandwidth_500_0,
Sf: lora.SpreadingFactor9,
Cr: lora.CodingRate4_7,
HeaderType: lora.HeaderExplicit,
Preamble: 12,
Ldr: lora.LowDataRateOptimizeOff,
Iq: lora.IQStandard,
Crc: lora.CRCOn,
SyncWord: lora.SyncPrivate,
LoraTxPowerDBm: 20,
}
loraRadio.LoraConfig(loraConf)
return loraRadio, nil
}
// radioIntHandler will take care of radio interrupts
func radioIntHandler(intr interrupt.Interrupt) {
loraRadio.HandleInterrupt()
}
func firmwareVersion() string {
return "sx126x"
}
func lorarx() ([]byte, error) {
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
}
+85
View File
@@ -0,0 +1,85 @@
//go:build featherwing
package main
import (
"strconv"
"machine"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx127x"
)
const (
FREQ = 868100000
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
// We assume LoRa Featherwing module is connected to PyBadge:
rstPin = machine.D11
csPin = machine.D10
dio0Pin = machine.D6
dio1Pin = machine.D9
spi = machine.SPI0
loraRadio *sx127x.Device
)
// do sx127x setup here
func setupLora() (LoraRadio, error) {
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dio0Pin.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
spi.Configure(machine.SPIConfig{Frequency: 500000, Mode: 0})
loraRadio = sx127x.New(spi, csPin, rstPin)
loraRadio.Reset()
if state := loraRadio.DetectDevice(); !state {
return nil, errRadioNotFound
}
// Setup DIO0 interrupt Handling
if err := dio0Pin.SetInterrupt(machine.PinRising, dioIrqHandler); err != nil {
println("could not configure DIO0 pin interrupt:", err.Error())
}
// Setup DIO1 interrupt Handling
if err := dio1Pin.SetInterrupt(machine.PinRising, dioIrqHandler); err != nil {
println("could not configure DIO1 pin interrupt:", err.Error())
}
// Prepare for Lora Operation
loraConf := lora.Config{
Freq: FREQ,
Bw: lora.Bandwidth_500_0,
Sf: lora.SpreadingFactor9,
Cr: lora.CodingRate4_7,
HeaderType: lora.HeaderExplicit,
Preamble: 12,
Iq: lora.IQStandard,
Crc: lora.CRCOn,
SyncWord: lora.SyncPrivate,
LoraTxPowerDBm: 20,
}
loraRadio.LoraConfig(loraConf)
return loraRadio, nil
}
func dioIrqHandler(machine.Pin) {
loraRadio.HandleInterrupt()
}
func firmwareVersion() string {
v := loraRadio.GetVersion()
return "sx127x v" + strconv.Itoa(int(v))
}
func lorarx() ([]byte, error) {
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
}
+7
View File
@@ -0,0 +1,7 @@
package main
const VERSION = "0.0.1"
func currentVersion() string {
return VERSION
}
+54
View File
@@ -0,0 +1,54 @@
//go:build nucleowl55jc
/*
Nucleo WL55JC1
RFSwitch
+-----------+---------+------------+------------+
| | FE_CTRL1 | FE_CTRL2 | FE_CTRL3 |
| | (PC4) | (PC5) | (PC3) |
+-----------+----------+-----------+------------+
| TX_HP | LOW | HIGH | HIGH |
| TX_LP | HIGH | HIGH | HIGH |
| RX | HIGH | LOW | HIGH |
+-----------+----------+-----------+------------+
*/
package main
import (
"machine"
"tinygo.org/x/drivers/sx126x"
)
type CustomSwitch struct {
}
func (s CustomSwitch) InitRFSwitch() {
machine.PC4.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.PC5.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.PC3.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
func (s CustomSwitch) SetRfSwitchMode(mode int) error {
switch mode {
case sx126x.RFSWITCH_TX_HP:
machine.PC4.Set(false)
machine.PC5.Set(true)
machine.PC3.Set(true)
case sx126x.RFSWITCH_TX_LP:
machine.PC4.Set(true)
machine.PC5.Set(true)
machine.PC3.Set(true)
case sx126x.RFSWITCH_RX:
machine.PC4.Set(true)
machine.PC5.Set(false)
machine.PC3.Set(true)
}
return nil
}
@@ -7,6 +7,7 @@ import (
rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx126x"
)
@@ -36,17 +37,17 @@ func main() {
}
// Prepare for Lora operation
loraConf := sx126x.LoraConfig{
loraConf := lora.Config{
Freq: FREQ,
Bw: sx126x.SX126X_LORA_BW_500_0,
Sf: sx126x.SX126X_LORA_SF9,
Cr: sx126x.SX126X_LORA_CR_4_7,
HeaderType: sx126x.SX126X_LORA_HEADER_EXPLICIT,
Bw: lora.Bandwidth_500_0,
Sf: lora.SpreadingFactor9,
Cr: lora.CodingRate4_7,
HeaderType: lora.HeaderExplicit,
Preamble: 12,
Ldr: sx126x.SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF,
Iq: sx126x.SX126X_LORA_IQ_STANDARD,
Crc: sx126x.SX126X_LORA_CRC_ON,
SyncWord: sx126x.SX126X_LORA_MAC_PRIVATE_SYNCWORD,
Ldr: lora.LowDataRateOptimizeOff,
Iq: lora.IQStandard,
Crc: lora.CRCOn,
SyncWord: lora.SyncPrivate,
LoraTxPowerDBm: 14,
}
loraRadio.LoraConfig(loraConf)
+15 -17
View File
@@ -11,6 +11,7 @@ import (
rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx126x"
)
@@ -54,17 +55,17 @@ func main() {
intr := interrupt.New(stm32.IRQ_Radio_IRQ_Busy, radioIntHandler)
intr.Enable()
loraConf := sx126x.LoraConfig{
loraConf := lora.Config{
Freq: FREQ,
Bw: sx126x.SX126X_LORA_BW_500_0,
Sf: sx126x.SX126X_LORA_SF9,
Cr: sx126x.SX126X_LORA_CR_4_7,
HeaderType: sx126x.SX126X_LORA_HEADER_EXPLICIT,
Bw: lora.Bandwidth_500_0,
Sf: lora.SpreadingFactor9,
Cr: lora.CodingRate4_7,
HeaderType: lora.HeaderExplicit,
Preamble: 12,
Ldr: sx126x.SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF,
Iq: sx126x.SX126X_LORA_IQ_STANDARD,
Crc: sx126x.SX126X_LORA_CRC_ON,
SyncWord: sx126x.SX126X_LORA_MAC_PRIVATE_SYNCWORD,
Ldr: lora.LowDataRateOptimizeOff,
Iq: lora.IQStandard,
Crc: lora.CRCOn,
SyncWord: lora.SyncPrivate,
LoraTxPowerDBm: 20,
}
@@ -74,21 +75,18 @@ func main() {
for {
tStart := time.Now()
// Blocking RX for LORA_DEFAULT_RXTIMEOUT_MS
println("Start Lora RX for 10 sec")
println("main: Receiving Lora for 10 seconds")
for int(time.Now().Sub(tStart).Seconds()) < 10 {
buf, err := loraRadio.LoraRx(LORA_DEFAULT_RXTIMEOUT_MS)
buf, err := loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
if err != nil {
println("RX Error: ", err)
} else if buf != nil {
println("Packet Received: len=", len(buf), string(buf))
}
}
println("END Lora RX")
println("LORA TX size=", len(txmsg))
err := loraRadio.LoraTx(txmsg, LORA_DEFAULT_TXTIMEOUT_MS)
println("main: End Lora RX")
println("LORA TX size=", len(txmsg), " -> ", string(txmsg))
err := loraRadio.Tx(txmsg, LORA_DEFAULT_TXTIMEOUT_MS)
if err != nil {
println("TX Error:", err)
}
+106
View File
@@ -0,0 +1,106 @@
package main
// This example code demonstrates Lora RX/TX With SX127x driver
// You need to connect SPI, RST, CS, DIO0 (aka IRQ) and DIO1 to use.
import (
"machine"
"time"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx127x"
)
const FREQ = 868100000
const (
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
loraRadio *sx127x.Device
txmsg = []byte("Hello TinyGO")
// We assume LoRa Featherwing module is connected to PyBadge:
SX127X_PIN_RST = machine.D11
SX127X_PIN_CS = machine.D10
SX127X_PIN_DIO0 = machine.D6
SX127X_PIN_DIO1 = machine.D9
SX127X_SPI = machine.SPI0
)
func dioIrqHandler(machine.Pin) {
loraRadio.HandleInterrupt()
}
func main() {
time.Sleep(5 * time.Second)
println("\n# TinyGo Lora RX/TX test")
println("# ----------------------")
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
SX127X_PIN_RST.Configure(machine.PinConfig{Mode: machine.PinOutput})
SX127X_PIN_CS.Configure(machine.PinConfig{Mode: machine.PinOutput})
SX127X_PIN_DIO0.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
SX127X_PIN_DIO1.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
SX127X_SPI.Configure(machine.SPIConfig{Frequency: 500000, Mode: 0})
println("main: create and start SX127x driver")
loraRadio = sx127x.New(SX127X_SPI, SX127X_PIN_CS, SX127X_PIN_RST)
loraRadio.Reset()
state := loraRadio.DetectDevice()
if !state {
panic("main: sx127x NOT FOUND !!!")
} else {
println("main: sx127x found")
}
// Setup DIO0 interrupt Handling
if err := SX127X_PIN_DIO0.SetInterrupt(machine.PinRising, dioIrqHandler); err != nil {
println("could not configure DIO0 pin interrupt:", err.Error())
}
// Setup DIO1 interrupt Handling
if err := SX127X_PIN_DIO1.SetInterrupt(machine.PinRising, dioIrqHandler); err != nil {
println("could not configure DIO1 pin interrupt:", err.Error())
}
// Prepare for Lora Operation
loraConf := lora.Config{
Freq: FREQ,
Bw: lora.Bandwidth_500_0,
Sf: lora.SpreadingFactor9,
Cr: lora.CodingRate4_7,
HeaderType: lora.HeaderExplicit,
Preamble: 12,
Iq: lora.IQStandard,
Crc: lora.CRCOn,
SyncWord: lora.SyncPrivate,
LoraTxPowerDBm: 20,
}
loraRadio.LoraConfig(loraConf)
var count uint
for {
tStart := time.Now()
println("main: Receiving Lora for 10 seconds")
for time.Since(tStart) < 10*time.Second {
buf, err := loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
if err != nil {
println("RX Error: ", err)
} else if buf != nil {
println("Packet Received: len=", len(buf), string(buf))
}
}
println("main: End Lora RX")
println("LORA TX size=", len(txmsg), " -> ", string(txmsg))
err := loraRadio.Tx(txmsg, LORA_DEFAULT_TXTIMEOUT_MS)
if err != nil {
println("TX Error:", err)
}
count++
}
}
+78
View File
@@ -0,0 +1,78 @@
package lora
import "errors"
// Config holds the LoRa configuration parameters
type Config struct {
Freq uint32 // Frequency
Cr uint8 // Coding Rate
Sf uint8 // Spread Factor
Bw uint8 // Bandwidth
Ldr uint8 // Low Data Rate
Preamble uint16 // PreambleLength
SyncWord uint16 // Sync Word
HeaderType uint8 // Header : Implicit/explicit
Crc uint8 // CRC : Yes/No
Iq uint8 // iq : Standard/inverted
LoraTxPowerDBm int8 // Tx power in Dbm
}
var (
ErrUndefinedLoraConf = errors.New("Undefined Lora configuration")
)
const (
SpreadingFactor5 = 0x05
SpreadingFactor6 = 0x06
SpreadingFactor7 = 0x07
SpreadingFactor8 = 0x08
SpreadingFactor9 = 0x09
SpreadingFactor10 = 0x0A
SpreadingFactor11 = 0x0B
SpreadingFactor12 = 0x0C
)
const (
CodingRate4_5 = 0x01 // 7 0 LoRa coding rate: 4/5
CodingRate4_6 = 0x02 // 7 0 4/6
CodingRate4_7 = 0x03 // 7 0 4/7
CodingRate4_8 = 0x04 // 7 0 4/8
)
const (
HeaderExplicit = 0x00 // 7 0 LoRa header mode: explicit
HeaderImplicit = 0x01 // 7 0 implicit
)
const (
LowDataRateOptimizeOff = 0x00 // 7 0 LoRa low data rate optimization: disabled
LowDataRateOptimizeOn = 0x01 // 7 0 enabled
)
const (
CRCOff = 0x00 // 7 0 LoRa CRC mode: disabled
CRCOn = 0x01 // 7 0 enabled
)
const (
IQStandard = 0x00 // 7 0 LoRa IQ setup: standard
IQInverted = 0x01 // 7 0 inverted
)
const (
Bandwidth_7_8 = iota // 7.8 kHz
Bandwidth_10_4 // 10.4 kHz
Bandwidth_15_6 // 15.6 kHz
Bandwidth_20_8 // 20.8 kHz
Bandwidth_31_25 // 31.25 kHz
Bandwidth_41_7 // 41.7 kHz
Bandwidth_62_5 // 62.5 kHz
Bandwidth_125_0 // 125.0 kHz
Bandwidth_250_0 // 250.0 kHz
Bandwidth_500_0 // 500.0 kHz
)
const (
SyncPublic = iota
SyncPrivate
)
+23
View File
@@ -0,0 +1,23 @@
package lora
const (
RadioEventRxDone = iota
RadioEventTxDone
RadioEventTimeout
RadioEventWatchdog
RadioEventCrcError
RadioEventUnhandled
)
// RadioEvent are used for communicating in the radio Event Channel
type RadioEvent struct {
EventType int
IRQStatus uint16
EventData []byte
}
// NewRadioEvent() returns a new RadioEvent that can be used in the RadioChannel
func NewRadioEvent(eType int, irqStatus uint16, eData []byte) RadioEvent {
r := RadioEvent{EventType: eType, IRQStatus: irqStatus, EventData: eData}
return r
}
+36 -57
View File
@@ -206,48 +206,42 @@ const (
SX126X_PA_RAMP_3400U = 0x07 // 7 0 3400 us
//SX126X_CMD_SET_MODULATION_PARAMS
SX126X_GFSK_FILTER_NONE = 0x00 // 7 0 GFSK filter: none
SX126X_GFSK_FILTER_GAUSS_0_3 = 0x08 // 7 0 Gaussian, BT = 0.3
SX126X_GFSK_FILTER_GAUSS_0_5 = 0x09 // 7 0 Gaussian, BT = 0.5
SX126X_GFSK_FILTER_GAUSS_0_7 = 0x0A // 7 0 Gaussian, BT = 0.7
SX126X_GFSK_FILTER_GAUSS_1 = 0x0B // 7 0 Gaussian, BT = 1
SX126X_GFSK_RX_BW_4_8 = 0x1F // 7 0 GFSK Rx bandwidth: 4.8 kHz
SX126X_GFSK_RX_BW_5_8 = 0x17 // 7 0 5.8 kHz
SX126X_GFSK_RX_BW_7_3 = 0x0F // 7 0 7.3 kHz
SX126X_GFSK_RX_BW_9_7 = 0x1E // 7 0 9.7 kHz
SX126X_GFSK_RX_BW_11_7 = 0x16 // 7 0 11.7 kHz
SX126X_GFSK_RX_BW_14_6 = 0x0E // 7 0 14.6 kHz
SX126X_GFSK_RX_BW_19_5 = 0x1D // 7 0 19.5 kHz
SX126X_GFSK_RX_BW_23_4 = 0x15 // 7 0 23.4 kHz
SX126X_GFSK_RX_BW_29_3 = 0x0D // 7 0 29.3 kHz
SX126X_GFSK_RX_BW_39_0 = 0x1C // 7 0 39.0 kHz
SX126X_GFSK_RX_BW_46_9 = 0x14 // 7 0 46.9 kHz
SX126X_GFSK_RX_BW_58_6 = 0x0C // 7 0 58.6 kHz
SX126X_GFSK_RX_BW_78_2 = 0x1B // 7 0 78.2 kHz
SX126X_GFSK_RX_BW_93_8 = 0x13 // 7 0 93.8 kHz
SX126X_GFSK_RX_BW_117_3 = 0x0B // 7 0 117.3 kHz
SX126X_GFSK_RX_BW_156_2 = 0x1A // 7 0 156.2 kHz
SX126X_GFSK_RX_BW_187_2 = 0x12 // 7 0 187.2 kHz
SX126X_GFSK_RX_BW_234_3 = 0x0A // 7 0 234.3 kHz
SX126X_GFSK_RX_BW_312_0 = 0x19 // 7 0 312.0 kHz
SX126X_GFSK_RX_BW_373_6 = 0x11 // 7 0 373.6 kHz
SX126X_GFSK_RX_BW_467_0 = 0x09 // 7 0 467.0 kHz
SX126X_LORA_BW_7_8 = 0x00 // 7 0 LoRa bandwidth: 7.8 kHz
SX126X_LORA_BW_10_4 = 0x08 // 7 0 10.4 kHz
SX126X_LORA_BW_15_6 = 0x01 // 7 0 15.6 kHz
SX126X_LORA_BW_20_8 = 0x09 // 7 0 20.8 kHz
SX126X_LORA_BW_31_25 = 0x02 // 7 0 31.25 kHz
SX126X_LORA_BW_41_7 = 0x0A // 7 0 41.7 kHz
SX126X_LORA_BW_62_5 = 0x03 // 7 0 62.5 kHz
SX126X_LORA_BW_125_0 = 0x04 // 7 0 125.0 kHz
SX126X_LORA_BW_250_0 = 0x05 // 7 0 250.0 kHz
SX126X_LORA_BW_500_0 = 0x06 // 7 0 500.0 kHz
SX126X_LORA_CR_4_5 = 0x01 // 7 0 LoRa coding rate: 4/5
SX126X_LORA_CR_4_6 = 0x02 // 7 0 4/6
SX126X_LORA_CR_4_7 = 0x03 // 7 0 4/7
SX126X_LORA_CR_4_8 = 0x04 // 7 0 4/8
SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF = 0x00 // 7 0 LoRa low data rate optimization: disabled
SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON = 0x01 // 7 0 enabled
SX126X_GFSK_FILTER_NONE = 0x00 // 7 0 GFSK filter: none
SX126X_GFSK_FILTER_GAUSS_0_3 = 0x08 // 7 0 Gaussian, BT = 0.3
SX126X_GFSK_FILTER_GAUSS_0_5 = 0x09 // 7 0 Gaussian, BT = 0.5
SX126X_GFSK_FILTER_GAUSS_0_7 = 0x0A // 7 0 Gaussian, BT = 0.7
SX126X_GFSK_FILTER_GAUSS_1 = 0x0B // 7 0 Gaussian, BT = 1
SX126X_GFSK_RX_BW_4_8 = 0x1F // 7 0 GFSK Rx bandwidth: 4.8 kHz
SX126X_GFSK_RX_BW_5_8 = 0x17 // 7 0 5.8 kHz
SX126X_GFSK_RX_BW_7_3 = 0x0F // 7 0 7.3 kHz
SX126X_GFSK_RX_BW_9_7 = 0x1E // 7 0 9.7 kHz
SX126X_GFSK_RX_BW_11_7 = 0x16 // 7 0 11.7 kHz
SX126X_GFSK_RX_BW_14_6 = 0x0E // 7 0 14.6 kHz
SX126X_GFSK_RX_BW_19_5 = 0x1D // 7 0 19.5 kHz
SX126X_GFSK_RX_BW_23_4 = 0x15 // 7 0 23.4 kHz
SX126X_GFSK_RX_BW_29_3 = 0x0D // 7 0 29.3 kHz
SX126X_GFSK_RX_BW_39_0 = 0x1C // 7 0 39.0 kHz
SX126X_GFSK_RX_BW_46_9 = 0x14 // 7 0 46.9 kHz
SX126X_GFSK_RX_BW_58_6 = 0x0C // 7 0 58.6 kHz
SX126X_GFSK_RX_BW_78_2 = 0x1B // 7 0 78.2 kHz
SX126X_GFSK_RX_BW_93_8 = 0x13 // 7 0 93.8 kHz
SX126X_GFSK_RX_BW_117_3 = 0x0B // 7 0 117.3 kHz
SX126X_GFSK_RX_BW_156_2 = 0x1A // 7 0 156.2 kHz
SX126X_GFSK_RX_BW_187_2 = 0x12 // 7 0 187.2 kHz
SX126X_GFSK_RX_BW_234_3 = 0x0A // 7 0 234.3 kHz
SX126X_GFSK_RX_BW_312_0 = 0x19 // 7 0 312.0 kHz
SX126X_GFSK_RX_BW_373_6 = 0x11 // 7 0 373.6 kHz
SX126X_GFSK_RX_BW_467_0 = 0x09 // 7 0 467.0 kHz
SX126X_LORA_BW_7_8 = 0x00 // 7 0 LoRa bandwidth: 7.8 kHz
SX126X_LORA_BW_10_4 = 0x08 // 7 0 10.4 kHz
SX126X_LORA_BW_15_6 = 0x01 // 7 0 15.6 kHz
SX126X_LORA_BW_20_8 = 0x09 // 7 0 20.8 kHz
SX126X_LORA_BW_31_25 = 0x02 // 7 0 31.25 kHz
SX126X_LORA_BW_41_7 = 0x0A // 7 0 41.7 kHz
SX126X_LORA_BW_62_5 = 0x03 // 7 0 62.5 kHz
SX126X_LORA_BW_125_0 = 0x04 // 7 0 125.0 kHz
SX126X_LORA_BW_250_0 = 0x05 // 7 0 250.0 kHz
SX126X_LORA_BW_500_0 = 0x06 // 7 0 500.0 kHz
//SX126X_CMD_SET_PACKET_PARAMS
SX126X_GFSK_PREAMBLE_DETECT_OFF = 0x00 // 7 0 GFSK minimum preamble length before reception starts: detector disabled
@@ -267,12 +261,6 @@ const (
SX126X_GFSK_CRC_2_BYTE_INV = 0x06 // 7 0 2 byte, inverted
SX126X_GFSK_WHITENING_OFF = 0x00 // 7 0 GFSK data whitening: disabled
SX126X_GFSK_WHITENING_ON = 0x01 // 7 0 enabled
SX126X_LORA_HEADER_EXPLICIT = 0x00 // 7 0 LoRa header mode: explicit
SX126X_LORA_HEADER_IMPLICIT = 0x01 // 7 0 implicit
SX126X_LORA_CRC_OFF = 0x00 // 7 0 LoRa CRC mode: disabled
SX126X_LORA_CRC_ON = 0x01 // 7 0 enabled
SX126X_LORA_IQ_STANDARD = 0x00 // 7 0 LoRa IQ setup: standard
SX126X_LORA_IQ_INVERTED = 0x01 // 7 0 inverted
//SX126X_CMD_SET_CAD_PARAMS
SX126X_CAD_ON_1_SYMB = 0x00 // 7 0 number of symbols used for CAD: 1
@@ -323,13 +311,4 @@ const (
SX126X_LORA_MAC_PUBLIC_SYNCWORD = 0x3444
SX126X_LORA_MAC_PRIVATE_SYNCWORD = 0x1424
SX126X_LORA_SF5 = 0x05
SX126X_LORA_SF6 = 0x06
SX126X_LORA_SF7 = 0x07
SX126X_LORA_SF8 = 0x08
SX126X_LORA_SF9 = 0x09
SX126X_LORA_SF10 = 0x0A
SX126X_LORA_SF11 = 0x0B
SX126X_LORA_SF12 = 0x0C
)
+2 -1
View File
@@ -7,11 +7,12 @@ import (
"errors"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/lora"
)
// New creates a new SX126x connection.
func New(spi drivers.SPI) *Device {
c := make(chan RadioEvent, 10)
c := make(chan lora.RadioEvent, 10)
d := Device{
spi: spi,
radioEventChan: c,
+96 -89
View File
@@ -7,7 +7,10 @@ import (
"errors"
"time"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/lora"
)
// SX126X radio transceiver RF_IN and RF_OUT may be connected
@@ -30,22 +33,6 @@ const (
RFSWITCH_TX_HP = iota
)
const (
RadioEventRxDone = iota
RadioEventTxDone = iota
RadioEventTimeout = iota
RadioEventWatchdog = iota
RadioEventCrcError = iota
RadioEventUnhandled = iota
)
// RadioEvent are used for communicating in the radio Event Channel
type RadioEvent struct {
EventType int
IRQStatus uint16
EventData []byte
}
const (
PERIOD_PER_SEC = (uint32)(1000000 / 15.625) // SX1261 DS 13.1.4
SPI_BUFFER_SIZE = 256
@@ -53,38 +40,20 @@ const (
// Device wraps an SPI connection to a SX127x device.
type Device struct {
spi drivers.SPI // SPI bus for module communication
radioEventChan chan RadioEvent // Channel for Receiving events
loraConf LoraConfig // Current Lora configuration
rfswitch RFSwitch // RF Switch, if any
deepSleep bool // Internal Sleep state
deviceType int // sx1261,sx1262,sx1268 (defaults sx1261)
spi drivers.SPI // SPI bus for module communication
rstPin, csPin machine.Pin // GPIOs for reset and chip select
radioEventChan chan lora.RadioEvent // Channel for Receiving events
loraConf lora.Config // Current Lora configuration
rfswitch RFSwitch // RF Switch, if any
deepSleep bool // Internal Sleep state
deviceType int // sx1261,sx1262,sx1268 (defaults sx1261)
spiBuffer [SPI_BUFFER_SIZE]uint8
}
// Config holds the LoRa configuration parameters
type LoraConfig struct {
Freq uint32 // Frequency
Cr uint8 // Coding Rate
Sf uint8 // Spread Factor
Bw uint8 // Bandwidth
Ldr uint8 // Low Data Rate
Preamble uint16 // PreambleLength
SyncWord uint16 // Sync Word
HeaderType uint8 // Header : Implicit/explicit
Crc uint8 // CRC : Yes/No
Iq uint8 // iq : Standard/inverted
LoraTxPowerDBm int8 // Tx power in Dbm
}
const (
SX126X_RTC_FREQ_IN_HZ uint32 = 64000
)
var (
errUndefinedLoraConf = errors.New("Undefined Lora configuration")
)
// --------------------------------------------------
// Helper functions
// --------------------------------------------------
@@ -100,14 +69,8 @@ func timeoutMsToRtcSteps(timeoutMs uint32) uint32 {
// Channel and events
//
// --------------------------------------------------
// NewRadioEvent() returns a new RadioEvent that can be used in the RadioChannel
func NewRadioEvent(eType int, irqStatus uint16, eData []byte) RadioEvent {
r := RadioEvent{EventType: eType, IRQStatus: irqStatus, EventData: eData}
return r
}
// Get the RadioEvent channel of the device
func (d *Device) GetRadioEventChan() chan RadioEvent {
func (d *Device) GetRadioEventChan() chan lora.RadioEvent {
return d.radioEventChan
}
@@ -126,6 +89,13 @@ func (d *Device) SetRfSwitch(rfswitch RFSwitch) {
// Operational modes functions
// --------------------------------------------------
func (d *Device) Reset() {
d.rstPin.Low()
time.Sleep(100 * time.Millisecond)
d.rstPin.High()
time.Sleep(100 * time.Millisecond)
}
// DetectDevice() tries to detect the radio module by changing SyncWord value
func (d *Device) DetectDevice() bool {
bak := d.GetSyncWord()
@@ -163,7 +133,7 @@ func (d *Device) SetTxContinuousWave() {
}
// SetTxContinuousPreamble set device in test mode to constantly modulate LoRa preamble symbols.
// Take care to initialize all Lora settings like it's done in LoraTx before calling this function
// Take care to initialize all Lora settings like it's done in Tx before calling this function
// If you don't init properly all the settings, it'll fail
func (d *Device) SetTxContinuousPreamble() {
if d.rfswitch != nil {
@@ -387,11 +357,11 @@ func (d *Device) SetPacketType(packetType uint8) {
}
// SetSyncWord defines the Sync Word to yse
func (d *Device) SetSyncWord(syncword uint16) {
func (d *Device) SetSyncWord(sw uint16) {
var p [2]uint8
d.loraConf.SyncWord = syncword
p[0] = uint8((syncword >> 8) & 0xFF)
p[1] = uint8((syncword >> 0) & 0xFF)
d.loraConf.SyncWord = sw
p[0] = uint8((d.loraConf.SyncWord >> 8) & 0xFF)
p[1] = uint8((d.loraConf.SyncWord >> 0) & 0xFF)
d.WriteRegister(SX126X_REG_LORA_SYNC_WORD_MSB, p[:])
}
@@ -402,8 +372,8 @@ func (d *Device) GetSyncWord() uint16 {
return r
}
// SetLoraPublicNetwork sets Sync Word to 0x3444 (Public) or 0x1424 (Private)
func (d *Device) SetLoraPublicNetwork(enable bool) {
// SetPublicNetwork sets Sync Word to 0x3444 (Public) or 0x1424 (Private)
func (d *Device) SetPublicNetwork(enable bool) {
if enable {
d.SetSyncWord(SX126X_LORA_MAC_PUBLIC_SYNCWORD)
} else {
@@ -537,47 +507,47 @@ func (d *Device) ExecGetCommand(cmd uint8, size uint8) []uint8 {
// Configuration
//
// SetLoraFrequency() Sets current Lora Frequency
// SetFrequency() Sets current Lora Frequency
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraFrequency(freq uint32) {
func (d *Device) SetFrequency(freq uint32) {
d.loraConf.Freq = d.loraConf.Freq
}
// SetLoraIqMode() defines the current IQ Mode (Standard/Inverted)
// SetIqMode() defines the current IQ Mode (Standard/Inverted)
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraIqMode(mode uint8) {
func (d *Device) SetIqMode(mode uint8) {
if mode == 0 {
d.loraConf.Iq = SX126X_LORA_IQ_STANDARD
d.loraConf.Iq = lora.IQStandard
} else {
d.loraConf.Iq = SX126X_LORA_IQ_INVERTED
d.loraConf.Iq = lora.IQInverted
}
}
// SetLoraCodingRate() sets current Lora Coding Rate
// SetCodingRate() sets current Lora Coding Rate
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraCodingRate(cr uint8) {
func (d *Device) SetCodingRate(cr uint8) {
d.loraConf.Cr = cr
}
// SetLoraBandwidth() sets current Lora Bandwidth
// SetBandwidth() sets current Lora Bandwidth
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraBandwidth(bw uint8) {
func (d *Device) SetBandwidth(bw uint8) {
d.loraConf.Cr = bw
}
// SetLoraCrc() sets current CRC mode (ON/OFF)
// SetCrc() sets current CRC mode (ON/OFF)
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraCrc(enable bool) {
func (d *Device) SetCrc(enable bool) {
if enable {
d.loraConf.Crc = SX126X_LORA_CRC_ON
d.loraConf.Crc = lora.CRCOn
} else {
d.loraConf.Crc = SX126X_LORA_CRC_OFF
d.loraConf.Crc = lora.CRCOn
}
}
// SetLoraSpreadingFactor setc surrent Lora Spreading Factor
// SetSpreadingFactor setc surrent Lora Spreading Factor
// NB: Change will be applied at next RX / TX
func (d *Device) SetLoraSpreadingFactor(sf uint8) {
func (d *Device) SetSpreadingFactor(sf uint8) {
d.loraConf.Sf = sf
}
@@ -587,9 +557,10 @@ func (d *Device) SetLoraSpreadingFactor(sf uint8) {
//
// LoraConfig() defines Lora configuration for next Lora operations
func (d *Device) LoraConfig(cnf LoraConfig) {
func (d *Device) LoraConfig(cnf lora.Config) {
// Save given configuration
d.loraConf = cnf
d.loraConf.SyncWord = syncword(int(cnf.SyncWord))
// Switch to standby prior to configuration changes
d.SetStandby()
// Clear errors, disable radio interrupts for the moment
@@ -599,24 +570,25 @@ func (d *Device) LoraConfig(cnf LoraConfig) {
// Define radio operation mode
d.SetPacketType(SX126X_PACKET_TYPE_LORA)
d.SetRfFrequency(d.loraConf.Freq)
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
d.SetModulationParams(d.loraConf.Sf, bandwidth(d.loraConf.Bw), d.loraConf.Cr, d.loraConf.Ldr)
d.SetTxParams(d.loraConf.LoraTxPowerDBm, SX126X_PA_RAMP_200U)
d.SetSyncWord(d.loraConf.SyncWord)
d.SetBufferBaseAddress(0, 0)
}
// LoraTx sends a lora packet, (with timeout)
func (d *Device) LoraTx(pkt []uint8, timeoutMs uint32) error {
// Tx sends a lora packet, (with timeout)
func (d *Device) Tx(pkt []uint8, timeoutMs uint32) error {
if d.loraConf.Freq == 0 {
return errUndefinedLoraConf
return lora.ErrUndefinedLoraConf
}
if d.rfswitch != nil {
err := d.rfswitch.SetRfSwitchMode(RFSWITCH_TX_HP)
if err != nil {
return err
}
}
d.ClearIrqStatus(SX126X_IRQ_ALL)
irqVal := uint16(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR)
d.SetStandby()
@@ -625,45 +597,46 @@ func (d *Device) LoraTx(pkt []uint8, timeoutMs uint32) error {
d.SetTxParams(d.loraConf.LoraTxPowerDBm, SX126X_PA_RAMP_200U)
d.SetBufferBaseAddress(0, 0)
d.WriteBuffer(pkt)
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
d.SetModulationParams(d.loraConf.Sf, bandwidth(d.loraConf.Bw), d.loraConf.Cr, d.loraConf.Ldr)
d.SetPacketParam(d.loraConf.Preamble, d.loraConf.HeaderType, d.loraConf.Crc, uint8(len(pkt)), d.loraConf.Iq)
d.SetDioIrqParams(irqVal, irqVal, SX126X_IRQ_NONE, SX126X_IRQ_NONE)
d.SetSyncWord(d.loraConf.SyncWord)
d.SetTx(timeoutMsToRtcSteps(timeoutMs))
msg := <-d.GetRadioEventChan()
if msg.EventType != RadioEventTxDone {
if msg.EventType != lora.RadioEventTxDone {
return errors.New("Unexpected Radio Event while TX")
}
return nil
}
// LoraRx tries to receive a Lora packet (with timeout in milliseconds)
func (d *Device) LoraRx(timeoutMs uint32) ([]uint8, error) {
func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) {
if d.loraConf.Freq == 0 {
return nil, errUndefinedLoraConf
return nil, lora.ErrUndefinedLoraConf
}
if d.rfswitch != nil {
err := d.rfswitch.SetRfSwitchMode(RFSWITCH_RX)
if err != nil {
return nil, err
}
}
d.ClearIrqStatus(SX126X_IRQ_ALL)
irqVal := uint16(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR)
d.SetStandby()
d.SetBufferBaseAddress(0, 0)
d.SetModulationParams(d.loraConf.Sf, d.loraConf.Bw, d.loraConf.Cr, d.loraConf.Ldr)
d.SetModulationParams(d.loraConf.Sf, bandwidth(d.loraConf.Bw), d.loraConf.Cr, d.loraConf.Ldr)
d.SetPacketParam(d.loraConf.Preamble, d.loraConf.HeaderType, d.loraConf.Crc, 0xFF, d.loraConf.Iq)
d.SetDioIrqParams(irqVal, irqVal, SX126X_IRQ_NONE, SX126X_IRQ_NONE)
d.SetRx(timeoutMsToRtcSteps(timeoutMs))
msg := <-d.GetRadioEventChan()
if msg.EventType == RadioEventTimeout {
if msg.EventType == lora.RadioEventTimeout {
return nil, nil
} else if msg.EventType != RadioEventRxDone {
} else if msg.EventType != lora.RadioEventRxDone {
return nil, errors.New("Unexpected Radio Event while RX")
}
@@ -683,19 +656,53 @@ func (d *Device) HandleInterrupt() {
rChan := d.GetRadioEventChan()
if (st & SX126X_IRQ_RX_DONE) > 0 {
rChan <- NewRadioEvent(RadioEventRxDone, st, nil)
rChan <- lora.NewRadioEvent(lora.RadioEventRxDone, st, nil)
}
if (st & SX126X_IRQ_TX_DONE) > 0 {
rChan <- NewRadioEvent(RadioEventTxDone, st, nil)
rChan <- lora.NewRadioEvent(lora.RadioEventTxDone, st, nil)
}
if (st & SX126X_IRQ_TIMEOUT) > 0 {
rChan <- NewRadioEvent(RadioEventTimeout, st, nil)
rChan <- lora.NewRadioEvent(lora.RadioEventTimeout, st, nil)
}
if (st & SX126X_IRQ_CRC_ERR) > 0 {
rChan <- NewRadioEvent(RadioEventCrcError, st, nil)
rChan <- lora.NewRadioEvent(lora.RadioEventCrcError, st, nil)
}
}
func bandwidth(bw uint8) uint8 {
switch bw {
case lora.Bandwidth_7_8:
return SX126X_LORA_BW_7_8
case lora.Bandwidth_10_4:
return SX126X_LORA_BW_10_4
case lora.Bandwidth_15_6:
return SX126X_LORA_BW_15_6
case lora.Bandwidth_20_8:
return SX126X_LORA_BW_20_8
case lora.Bandwidth_31_25:
return SX126X_LORA_BW_31_25
case lora.Bandwidth_41_7:
return SX126X_LORA_BW_41_7
case lora.Bandwidth_62_5:
return SX126X_LORA_BW_62_5
case lora.Bandwidth_125_0:
return SX126X_LORA_BW_125_0
case lora.Bandwidth_250_0:
return SX126X_LORA_BW_250_0
case lora.Bandwidth_500_0:
return SX126X_LORA_BW_500_0
default:
return 0
}
}
func syncword(sw int) uint16 {
if sw == lora.SyncPublic {
return SX126X_LORA_MAC_PUBLIC_SYNCWORD
}
return SX126X_LORA_MAC_PRIVATE_SYNCWORD
}
+101
View File
@@ -0,0 +1,101 @@
package sx127x
const (
// registers
SX127X_REG_FIFO = 0x00
SX127X_REG_OP_MODE = 0x01
SX127X_REG_FRF_MSB = 0x06
SX127X_REG_FRF_MID = 0x07
SX127X_REG_FRF_LSB = 0x08
SX127X_REG_PA_CONFIG = 0x09
SX127X_REG_PA_RAMP = 0x0a
SX127X_REG_OCP = 0x0b
SX127X_REG_LNA = 0x0c
SX127X_REG_FIFO_ADDR_PTR = 0x0d
SX127X_REG_FIFO_TX_BASE_ADDR = 0x0e
SX127X_REG_FIFO_RX_BASE_ADDR = 0x0f
SX127X_REG_FIFO_RX_CURRENT_ADDR = 0x10
SX127X_REG_IRQ_FLAGS_MASK = 0x11
SX127X_REG_IRQ_FLAGS = 0x12
SX127X_REG_RX_NB_BYTES = 0x13
SX127X_REG_PKT_SNR_VALUE = 0x19
SX127X_REG_PKT_RSSI_VALUE = 0x1a
SX127X_REG_RSSI_VALUE = 0x1b
SX127X_REG_MODEM_CONFIG_1 = 0x1d
SX127X_REG_MODEM_CONFIG_2 = 0x1e
SX127X_REG_SYMB_TIMEOUT_LSB = 0x1f
SX127X_REG_PREAMBLE_MSB = 0x20
SX127X_REG_PREAMBLE_LSB = 0x21
SX127X_REG_PAYLOAD_LENGTH = 0x22
SX127X_REG_MAX_PAYLOAD_LENGTH = 0x23
SX127X_REG_HOP_PERIOD = 0x24
SX127X_REG_MODEM_CONFIG_3 = 0x26
SX127X_REG_FREQ_ERROR_MSB = 0x28
SX127X_REG_FREQ_ERROR_MID = 0x29
SX127X_REG_FREQ_ERROR_LSB = 0x2a
SX127X_REG_RSSI_WIDEBAND = 0x2c
SX127X_REG_DETECTION_OPTIMIZE = 0x31
SX127X_REG_INVERTIQ = 0x33
SX127X_REG_DETECTION_THRESHOLD = 0x37
SX127X_REG_SYNC_WORD = 0x39
SX127X_REG_INVERTIQ2 = 0x3b
SX127X_REG_DIO_MAPPING_1 = 0x40
SX127X_REG_DIO_MAPPING_2 = 0x41
SX127X_REG_VERSION = 0x42
SX127X_REG_PA_DAC = 0x4d
// PA config
SX127X_PA_BOOST = 0x80
// Bits masking the corresponding IRQs from the radio
SX127X_IRQ_LORA_RXTOUT_MASK = uint8(0x80)
SX127X_IRQ_LORA_RXDONE_MASK = uint8(0x40)
SX127X_IRQ_LORA_CRCERR_MASK = uint8(0x20)
SX127X_IRQ_LORA_HEADER_MASK = uint8(0x10)
SX127X_IRQ_LORA_TXDONE_MASK = uint8(0x08)
SX127X_IRQ_LORA_CDDONE_MASK = uint8(0x04)
SX127X_IRQ_LORA_FHSSCH_MASK = uint8(0x02)
SX127X_IRQ_LORA_CDDETD_MASK = uint8(0x01)
// DIO function mappings D0D1D2D3
SX127X_MAP_DIO0_LORA_RXDONE = uint8(0x00) // 00------
SX127X_MAP_DIO0_LORA_TXDONE = uint8(0x40) // 01------
SX127X_MAP_DIO1_LORA_RXTOUT = uint8(0x00) // --00----
SX127X_MAP_DIO1_LORA_NOP = uint8(0x30) // --11----
SX127X_MAP_DIO2_LORA_NOP = uint8(0xC0) // ----11--
SX127X_PAYLOAD_LENGTH = uint8(0x40)
// Low Noise Amp
SX127X_LNA_MAX_GAIN = uint8(0x23)
SX127X_LNA_OFF_GAIN = uint8(0x00)
SX127X_LNA_LOW_GAIN = uint8(0x20)
// Bandwidth
SX127X_LORA_BW_7_8 = uint8(0x00)
SX127X_LORA_BW_10_4 = uint8(0x01)
SX127X_LORA_BW_15_6 = uint8(0x02)
SX127X_LORA_BW_20_8 = uint8(0x03)
SX127X_LORA_BW_31_25 = uint8(0x04)
SX127X_LORA_BW_41_7 = uint8(0x05)
SX127X_LORA_BW_62_5 = uint8(0x06)
SX127X_LORA_BW_125_0 = uint8(0x07)
SX127X_LORA_BW_250_0 = uint8(0x08)
SX127X_LORA_BW_500_0 = uint8(0x09)
// Automatic gain control
SX127X_AGC_AUTO_OFF = uint8(0x00)
SX127X_AGC_AUTO_ON = uint8(0x01)
// Operation modes
SX127X_OPMODE_LORA = uint8(0x80)
SX127X_OPMODE_MASK = uint8(0x07)
SX127X_OPMODE_SLEEP = uint8(0x00)
SX127X_OPMODE_STANDBY = uint8(0x01)
SX127X_OPMODE_FSTX = uint8(0x02)
SX127X_OPMODE_TX = uint8(0x03)
SX127X_OPMODE_FSRX = uint8(0x04)
SX127X_OPMODE_RX = uint8(0x05)
SX127X_OPMODE_RX_SINGLE = uint8(0x06)
SX127X_OPMODE_CAD = uint8(0x07)
SX127X_LORA_MAC_PUBLIC_SYNCWORD = 0x34
SX127X_LORA_MAC_PRIVATE_SYNCWORD = 0x14
)
+530
View File
@@ -0,0 +1,530 @@
// Package sx127x provides a driver for SX127x LoRa transceivers.
// References:
// https://electronics.stackexchange.com/questions/394296/can-t-get-simple-lora-receiver-to-work
// https://www.st.com/resource/en/user_manual/dm00300436-stm32-lora-expansion-package-for-stm32cube-stmicroelectronics.pdf
package sx127x
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/lora"
)
// So we can keep track of the origin of interruption
const (
SPI_BUFFER_SIZE = 256
)
// Device wraps an SPI connection to a SX127x device.
type Device struct {
spi drivers.SPI // SPI bus for module communication
rstPin, csPin machine.Pin // GPIOs for reset and chip select
radioEventChan chan lora.RadioEvent // Channel for Receiving events
loraConf lora.Config // Current Lora configuration
deepSleep bool // Internal Sleep state
deviceType int // sx1261,sx1262,sx1268 (defaults sx1261)
spiBuffer [SPI_BUFFER_SIZE]uint8
packetIndex uint8 // FIXME ... useless ?
}
// --------------------------------------------------
//
// Channel and events
//
// --------------------------------------------------
// Get the RadioEvent channel of the device
func (d *Device) GetRadioEventChan() chan lora.RadioEvent {
return d.radioEventChan
}
// New creates a new SX127x connection. The SPI bus must already be configured.
func New(spi machine.SPI, csPin machine.Pin, rstPin machine.Pin) *Device {
k := Device{
spi: spi,
csPin: csPin,
rstPin: rstPin,
radioEventChan: make(chan lora.RadioEvent, 10),
}
return &k
}
// Reset re-initialize the sx127x device
func (d *Device) Reset() {
d.rstPin.Low()
time.Sleep(100 * time.Millisecond)
d.rstPin.High()
time.Sleep(100 * time.Millisecond)
}
// DetectDevice checks if device responds on the SPI bus
func (d *Device) DetectDevice() bool {
id := d.GetVersion()
return (id == 0x12)
}
// ReadRegister reads register value
func (d *Device) ReadRegister(reg uint8) uint8 {
d.csPin.Low()
d.spi.Tx([]byte{reg & 0x7f}, nil)
var value [1]byte
d.spi.Tx(nil, value[:])
d.csPin.High()
return value[0]
}
// WriteRegister writes value to register
func (d *Device) WriteRegister(reg uint8, value uint8) uint8 {
var response [1]byte
d.csPin.Low()
d.spi.Tx([]byte{reg | 0x80}, nil)
d.spi.Tx([]byte{value}, response[:])
d.csPin.High()
return response[0]
}
// SetOpMode changes the sx1276 mode
func (d *Device) SetOpMode(mode uint8) {
cur := d.ReadRegister(SX127X_REG_OP_MODE)
new := (cur & (^SX127X_OPMODE_MASK)) | mode
d.WriteRegister(SX127X_REG_OP_MODE, new)
}
// SetOpMode changes the sx1276 mode
func (d *Device) SetOpModeLora() {
d.WriteRegister(SX127X_REG_OP_MODE, SX127X_OPMODE_LORA)
}
// GetVersion returns hardware version of sx1276 chipset
func (d *Device) GetVersion() uint8 {
return (d.ReadRegister(SX127X_REG_VERSION))
}
// IsTransmitting tests if a packet transmission is in progress
func (d *Device) IsTransmitting() bool {
return (d.ReadRegister(SX127X_REG_OP_MODE) & SX127X_OPMODE_TX) == SX127X_OPMODE_TX
}
// LastPacketRSSI gives the RSSI of the last packet received
func (d *Device) LastPacketRSSI() uint8 {
// section 5.5.5
var adjustValue uint8 = 157
if d.loraConf.Freq < 868000000 {
adjustValue = 164
}
return d.ReadRegister(SX127X_REG_PKT_RSSI_VALUE) - adjustValue
}
// LastPacketSNR gives the SNR of the last packet received
func (d *Device) LastPacketSNR() uint8 {
return uint8(d.ReadRegister(SX127X_REG_PKT_SNR_VALUE) / 4)
}
// GetRSSI returns current RSSI
func (d *Device) GetRSSI() uint8 {
return d.ReadRegister(SX127X_REG_RSSI_VALUE)
}
/*
// GetBandwidth returns the bandwidth the LoRa module is using
func (d *Device) GetBandwidth() int32 {
return int32(d.loraConf.Bw)
}
*/
// SetTxPower sets the transmitter output power
func (d *Device) SetTxPower(txPower int8, paBoost bool) {
if !paBoost {
// RFO
if txPower < 0 {
txPower = 0
} else if txPower > 14 {
txPower = 14
}
d.WriteRegister(SX127X_REG_PA_CONFIG, uint8(0x70)|uint8(txPower))
} else {
//PA_BOOST
if txPower > 17 {
if txPower > 20 {
txPower = 20
}
txPower -= 3
// High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.)
d.WriteRegister(SX127X_REG_PA_DAC, 0x87)
d.SetOCP(140)
} else {
if txPower < 2 {
txPower = 2
}
d.WriteRegister(SX127X_REG_PA_DAC, 0x84)
d.SetOCP(100)
}
d.WriteRegister(SX127X_REG_PA_CONFIG, uint8(SX127X_PA_BOOST)|uint8(txPower-2))
}
}
// ---------------
// Internal functions
// ---------------
// SetRxTimeout defines RX Timeout expressed as number of symbols
// Default timeout is 64 * Ts
func (d *Device) SetRxTimeout(tmoutSymb uint8) {
d.WriteRegister(SX127X_REG_SYMB_TIMEOUT_LSB, tmoutSymb)
}
// SetOCP defines Overload Current Protection configuration
func (d *Device) SetOCP(mA uint8) {
ocpTrim := uint8(27)
if mA < 45 {
mA = 45
}
if mA <= 120 {
ocpTrim = (mA - 45) / 5
} else if mA <= 240 {
ocpTrim = (mA + 30) / 10
}
d.WriteRegister(SX127X_REG_OCP, 0x20|(0x1F&ocpTrim))
}
// SetAgcAutoOn enables Automatic Gain Control
func (d *Device) SetAgcAuto(val uint8) {
if val == SX127X_AGC_AUTO_ON {
d.WriteRegister(SX127X_REG_MODEM_CONFIG_3, d.ReadRegister(SX127X_REG_MODEM_CONFIG_3)|0x04)
} else {
d.WriteRegister(SX127X_REG_MODEM_CONFIG_3, d.ReadRegister(SX127X_REG_MODEM_CONFIG_3)&0xfb)
}
}
// SetLowDataRateOptimize enables Low Data Rate Optimization
func (d *Device) SetLowDataRateOptim(val uint8) {
if val == lora.LowDataRateOptimizeOn {
d.WriteRegister(SX127X_REG_MODEM_CONFIG_3, d.ReadRegister(SX127X_REG_MODEM_CONFIG_3)|0x08)
} else {
d.WriteRegister(SX127X_REG_MODEM_CONFIG_3, d.ReadRegister(SX127X_REG_MODEM_CONFIG_3)&0xf7)
}
}
// SetLowFrequencyModeOn enables Low Data Rate Optimization
func (d *Device) SetLowFrequencyModeOn(val bool) {
if val {
d.WriteRegister(SX127X_REG_OP_MODE, d.ReadRegister(SX127X_REG_OP_MODE)|0x04)
} else {
d.WriteRegister(SX127X_REG_OP_MODE, d.ReadRegister(SX127X_REG_OP_MODE)&0xfb)
}
}
// SetHopPeriod sets number of symbol periods between frequency hops. (0 = disabled).
func (d *Device) SetHopPeriod(val uint8) {
d.WriteRegister(SX127X_REG_HOP_PERIOD, val)
}
//
// LORA FUNCTIONS
//
// LoraConfig() defines Lora configuration for next Lora operations
func (d *Device) LoraConfig(cnf lora.Config) {
// Save given configuration
d.loraConf = cnf
d.loraConf.SyncWord = syncword(int(cnf.SyncWord))
}
// SetFrequency updates the frequency the LoRa module is using
func (d *Device) SetFrequency(frequency uint32) {
d.loraConf.Freq = frequency
var frf = (uint64(frequency) << 19) / 32000000
d.WriteRegister(SX127X_REG_FRF_MSB, uint8(frf>>16))
d.WriteRegister(SX127X_REG_FRF_MID, uint8(frf>>8))
d.WriteRegister(SX127X_REG_FRF_LSB, uint8(frf>>0))
}
// SetBandwidth updates the bandwidth the LoRa module is using
func (d *Device) SetBandwidth(bw uint8) {
d.loraConf.Bw = bandwidth(bw)
d.WriteRegister(SX127X_REG_MODEM_CONFIG_1, (d.ReadRegister(SX127X_REG_MODEM_CONFIG_1)&0x0f)|(bw<<4))
}
// SetCodingRate updates the coding rate the LoRa module is using
func (d *Device) SetCodingRate(cr uint8) {
d.loraConf.Cr = cr
d.WriteRegister(SX127X_REG_MODEM_CONFIG_1, (d.ReadRegister(SX127X_REG_MODEM_CONFIG_1)&0xf1)|(cr<<1))
}
// SetImplicitHeaderModeOn Enables implicit header mode ***
func (d *Device) SetHeaderMode(headerType uint8) {
d.loraConf.HeaderType = headerType
if headerType == lora.HeaderImplicit {
d.WriteRegister(SX127X_REG_MODEM_CONFIG_1, d.ReadRegister(SX127X_REG_MODEM_CONFIG_1)|0x01)
} else {
d.WriteRegister(SX127X_REG_MODEM_CONFIG_1, d.ReadRegister(SX127X_REG_MODEM_CONFIG_1)&0xfe)
}
}
// SetSpreadingFactor changes spreading factor
func (d *Device) SetSpreadingFactor(sf uint8) {
d.loraConf.Sf = sf
if sf == lora.SpreadingFactor6 {
d.WriteRegister(SX127X_REG_DETECTION_OPTIMIZE, 0xc5)
d.WriteRegister(SX127X_REG_DETECTION_THRESHOLD, 0x0c)
} else {
d.WriteRegister(SX127X_REG_DETECTION_OPTIMIZE, 0xc3)
d.WriteRegister(SX127X_REG_DETECTION_THRESHOLD, 0x0a)
}
var newValue = (d.ReadRegister(SX127X_REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)
d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, newValue)
}
// SetTxContinuousMode enable Continuous Tx mode
func (d *Device) SetTxContinuousMode(val bool) {
if val {
d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, d.ReadRegister(SX127X_REG_MODEM_CONFIG_2)|0x08)
} else {
d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, d.ReadRegister(SX127X_REG_MODEM_CONFIG_2)&0xf7)
}
}
// SetCrc Enable CRC generation and check on payload
func (d *Device) SetCrc(enable bool) {
if enable {
d.loraConf.Crc = lora.CRCOn
d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, d.ReadRegister(SX127X_REG_MODEM_CONFIG_2)|0x04)
} else {
d.loraConf.Crc = lora.CRCOff
d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, d.ReadRegister(SX127X_REG_MODEM_CONFIG_2)&0xfb)
}
}
func (d *Device) SetPreamble(pLen uint16) {
// Sets preamble length
d.WriteRegister(SX127X_REG_PREAMBLE_MSB, uint8((pLen>>8)&0xFF))
d.WriteRegister(SX127X_REG_PREAMBLE_LSB, uint8(pLen&0xFF))
}
// SetSyncWord defines sync word
func (d *Device) SetSyncWord(syncWord uint16) {
d.loraConf.SyncWord = syncWord
sw := uint8(syncWord & 0xFF)
d.WriteRegister(SX127X_REG_SYNC_WORD, sw)
}
// SetIQMode Sets I/Q polarity configuration
func (d *Device) SetIqMode(val uint8) {
d.loraConf.Iq = val
if val == lora.IQStandard {
//Set IQ to normal values
d.WriteRegister(SX127X_REG_INVERTIQ, 0x27)
d.WriteRegister(SX127X_REG_INVERTIQ2, 0x1D)
} else {
//Invert IQ Back
d.WriteRegister(SX127X_REG_INVERTIQ, 0x66)
d.WriteRegister(SX127X_REG_INVERTIQ2, 0x19)
}
}
// Tx sends a lora packet, (with timeout)
func (d *Device) Tx(pkt []uint8, timeoutMs uint32) error {
d.SetOpModeLora()
d.SetOpMode(SX127X_OPMODE_SLEEP)
d.SetHopPeriod(0x00)
d.SetLowFrequencyModeOn(false) // High freq mode
d.WriteRegister(SX127X_REG_PA_RAMP, (d.ReadRegister(SX127X_REG_PA_RAMP)&0xF0)|0x08) // set PA ramp-up time 50 uSec
d.WriteRegister(SX127X_REG_LNA, SX127X_LNA_MAX_GAIN) // Set Low Noise Amplifier to MAX
d.SetFrequency(d.loraConf.Freq)
d.SetPreamble(d.loraConf.Preamble)
d.SetSyncWord(d.loraConf.SyncWord)
d.SetBandwidth(d.loraConf.Bw)
d.SetSpreadingFactor(d.loraConf.Sf)
d.SetIqMode(d.loraConf.Iq)
d.SetCodingRate(d.loraConf.Cr)
d.SetCrc(d.loraConf.Crc == lora.CRCOn)
d.SetTxPower(d.loraConf.LoraTxPowerDBm, true)
d.SetHeaderMode(d.loraConf.HeaderType)
d.SetAgcAuto(SX127X_AGC_AUTO_ON)
// set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
d.WriteRegister(SX127X_REG_DIO_MAPPING_1, SX127X_MAP_DIO0_LORA_TXDONE|SX127X_MAP_DIO1_LORA_NOP|SX127X_MAP_DIO2_LORA_NOP)
// Clear all radio IRQ Flags
d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)
// Mask all but TxDone
d.WriteRegister(SX127X_REG_IRQ_FLAGS_MASK, ^SX127X_IRQ_LORA_TXDONE_MASK)
// initialize the payload size and address pointers
d.WriteRegister(SX127X_REG_PAYLOAD_LENGTH, uint8(len(pkt)))
d.WriteRegister(SX127X_REG_FIFO_TX_BASE_ADDR, 0)
d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, 0)
// FIFO OPs cannot take place in Sleep mode !!!
d.SetOpMode(SX127X_OPMODE_STANDBY)
time.Sleep(time.Millisecond)
// Copy payload to FIFO // TODO: Bulk
for i := 0; i < len(pkt); i++ {
d.WriteRegister(SX127X_REG_FIFO, pkt[i])
}
// Enable TX
d.SetOpMode(SX127X_OPMODE_TX)
msg := <-d.GetRadioEventChan()
if msg.EventType != lora.RadioEventTxDone {
return errors.New("Unexpected Radio Event while TX " + string(0x30+msg.EventType))
}
return nil
}
// Rx tries to receive a Lora packet (with timeout in milliseconds)
func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) {
if d.loraConf.Freq == 0 {
return nil, lora.ErrUndefinedLoraConf
}
d.SetOpModeLora()
d.SetOpMode(SX127X_OPMODE_SLEEP)
d.SetHopPeriod(0x00)
d.SetLowFrequencyModeOn(false) // High freq mode
d.WriteRegister(SX127X_REG_PA_RAMP, (d.ReadRegister(SX127X_REG_PA_RAMP)&0xF0)|0x08) // set PA ramp-up time 50 uSec
d.WriteRegister(SX127X_REG_LNA, SX127X_LNA_MAX_GAIN) // Set Low Noise Amplifier to MAX
d.SetFrequency(d.loraConf.Freq)
d.SetPreamble(d.loraConf.Preamble)
d.SetSyncWord(d.loraConf.SyncWord)
d.SetBandwidth(d.loraConf.Bw)
d.SetSpreadingFactor(d.loraConf.Sf)
d.SetIqMode(d.loraConf.Iq)
d.SetCodingRate(d.loraConf.Cr)
d.SetCrc(d.loraConf.Crc == lora.CRCOn)
d.SetTxPower(d.loraConf.LoraTxPowerDBm, true)
d.SetHeaderMode(d.loraConf.HeaderType)
d.SetAgcAuto(SX127X_AGC_AUTO_ON)
// set the IRQ mapping DIO0=RxDone DIO1=RxTimeout DIO2=NOP
d.WriteRegister(SX127X_REG_DIO_MAPPING_1, SX127X_MAP_DIO0_LORA_RXDONE|SX127X_MAP_DIO1_LORA_RXTOUT|SX127X_MAP_DIO2_LORA_NOP)
// Clear all radio IRQ Flags
d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)
// Mask all but RxDone
d.WriteRegister(SX127X_REG_IRQ_FLAGS_MASK, ^(SX127X_IRQ_LORA_RXDONE_MASK | SX127X_IRQ_LORA_RXTOUT_MASK))
// Switch to RX Mode
d.SetOpMode(SX127X_OPMODE_RX_SINGLE) //
// Wait for Radio Event
radioCh := d.GetRadioEventChan()
msg := <-radioCh
if msg.EventType == lora.RadioEventTimeout {
return nil, nil
} else if msg.EventType != lora.RadioEventRxDone {
return nil, errors.New("Unexpected Radio Event while RX " + string(0x30+msg.EventType))
}
d.WriteRegister(SX127X_REG_FIFO_RX_BASE_ADDR, 0)
d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, 0)
pLen := d.ReadRegister(SX127X_REG_RX_NB_BYTES)
d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, d.ReadRegister(SX127X_REG_FIFO_RX_CURRENT_ADDR))
for i := uint8(0); i < pLen; i++ {
d.spiBuffer[i] = d.ReadRegister(SX127X_REG_FIFO)
}
return d.spiBuffer[:pLen], nil
}
//
// HELPER FUNCTIONS
//
// PrintRegisters outputs the sx127x transceiver registers
func (d *Device) PrintRegisters(compact bool) {
for i := uint8(0); i < 128; i++ {
v := d.ReadRegister(i)
print(v, " ")
}
println()
}
// PrintRegisters outputs the sx127x transceiver registers
func (d *Device) RandomU32() uint32 {
// Disable ALL irqs
d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)
d.SetOpModeLora()
d.SetOpMode(SX127X_OPMODE_SLEEP)
d.SetFrequency(d.loraConf.Freq)
d.SetOpMode(SX127X_OPMODE_RX)
rnd := uint32(0)
for i := 0; i < 32; i++ {
time.Sleep(time.Millisecond * 10)
// Unfiltered RSSI value reading. Only takes the LSB value
rnd |= (uint32(d.ReadRegister(SX127X_REG_RSSI_WIDEBAND)) & 0x01) << i
}
return rnd
}
// HandleInterrupt must be called by main code on DIO state change.
func (d *Device) HandleInterrupt() {
// Get IRQ and clear
st := d.ReadRegister(SX127X_REG_IRQ_FLAGS)
d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)
rChan := d.GetRadioEventChan()
if (st & SX127X_IRQ_LORA_RXDONE_MASK) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventRxDone, uint16(st), nil)
}
if (st & SX127X_IRQ_LORA_TXDONE_MASK) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventTxDone, uint16(st), nil)
}
if (st & SX127X_IRQ_LORA_RXTOUT_MASK) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventTimeout, uint16(st), nil)
}
if (st & SX127X_IRQ_LORA_CRCERR_MASK) > 0 {
rChan <- lora.NewRadioEvent(lora.RadioEventCrcError, uint16(st), nil)
}
}
func bandwidth(bw uint8) uint8 {
switch bw {
case lora.Bandwidth_7_8:
return SX127X_LORA_BW_7_8
case lora.Bandwidth_10_4:
return SX127X_LORA_BW_10_4
case lora.Bandwidth_15_6:
return SX127X_LORA_BW_15_6
case lora.Bandwidth_20_8:
return SX127X_LORA_BW_20_8
case lora.Bandwidth_31_25:
return SX127X_LORA_BW_31_25
case lora.Bandwidth_41_7:
return SX127X_LORA_BW_41_7
case lora.Bandwidth_62_5:
return SX127X_LORA_BW_62_5
case lora.Bandwidth_125_0:
return SX127X_LORA_BW_125_0
case lora.Bandwidth_250_0:
return SX127X_LORA_BW_250_0
case lora.Bandwidth_500_0:
return SX127X_LORA_BW_500_0
default:
return 0
}
}
func syncword(sw int) uint16 {
if sw == lora.SyncPublic {
return SX127X_LORA_MAC_PUBLIC_SYNCWORD
}
return SX127X_LORA_MAC_PRIVATE_SYNCWORD
}