mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-14 20:03:40 +00:00
examples: example with LoRa AT command set implementation
Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
@@ -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/
|
||||||
|
```
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
const VERSION = "0.0.1"
|
||||||
|
|
||||||
|
func currentVersion() string {
|
||||||
|
return VERSION
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user