diff --git a/examples/lora/atcmd/commands.go b/examples/lora/atcmd/commands.go new file mode 100644 index 0000000..3cb4ad9 --- /dev/null +++ b/examples/lora/atcmd/commands.go @@ -0,0 +1,317 @@ +package main + +import ( + "strings" +) + +func crlf() { + uart.Write([]byte("\r\n")) +} + +// Use to test if connection of module is OK. +func quicktest() { + uart.Write([]byte("+AT: OK")) + crlf() +} + +// Check firmware version. +func version() { + uart.Write([]byte("+VER: ")) + uart.Write([]byte(currentVersion())) + crlf() +} + +// Use to check the ID of the LoRaWAN module, or change the ID. +func id(args string) error { + // look for comma in args + param, val, hasComma := strings.Cut(args, ",") + if hasComma { + // set + switch param { + case "DevAddr": + uart.Write([]byte("+ID: DevAddr, ")) + uart.Write([]byte(val)) + crlf() + case "DevEui": + uart.Write([]byte("+ID: DevEui, ")) + uart.Write([]byte(val)) + crlf() + case "AppEui": + uart.Write([]byte("+ID: AppEui, ")) + uart.Write([]byte(val)) + crlf() + default: + return errInvalidCommand + } + + return nil + } + + // get + switch param { + case "DevAddr": + uart.Write([]byte("+ID: DevAddr, xx:xx:xx:xx")) + crlf() + case "DevEui": + uart.Write([]byte("+ID: DevEui, xx:xx:xx:xx:xx:xx:xx:xx")) + crlf() + case "AppEui": + uart.Write([]byte("+ID: AppEui, xx:xx:xx:xx:xx:xx:xx:xx")) + crlf() + default: + uart.Write([]byte("+ID: DevAddr, xx:xx:xx:xx")) + crlf() + uart.Write([]byte("+ID: DevEui, xx:xx:xx:xx:xx:xx:xx:xx")) + crlf() + uart.Write([]byte("+ID: AppEui, xx:xx:xx:xx:xx:xx:xx:xx")) + crlf() + } + + return nil +} + +// Use to reset the module. If module returns error, then reset function is invalid. +func reset() error { + uart.Write([]byte("+RESET: OK")) + crlf() + return nil +} + +// Use to send string format frame which is no need to be confirmed by the server. +func msg(data string) error { + uart.Write([]byte("+MSG: Start")) + crlf() + uart.Write([]byte("+MSG: Done")) + crlf() + return nil +} + +// Use to send string format frame which must be confirmed by the server +func cmsg(data string) error { + uart.Write([]byte("+CMSG: Start")) + crlf() + uart.Write([]byte("+CMSG: Done")) + crlf() + return nil +} + +// Use to send hex format frame which is no need to be confirmed by the server +func msghex(data string) error { + uart.Write([]byte("+MSGHEX: Start")) + crlf() + uart.Write([]byte("+MSGHEX: Done")) + crlf() + return nil +} + +// Use to send hex format frame which must be confirmed by the server. +func cmsghex(data string) error { + uart.Write([]byte("+CMSGHEX: Start")) + crlf() + uart.Write([]byte("+CMSGHEX: Done")) + crlf() + return nil +} + +// Use to send string format LoRaWAN proprietary frames +func pmsg(data string) error { + uart.Write([]byte("+PMSG: Start")) + crlf() + uart.Write([]byte("+PMSG: Done")) + crlf() + return nil +} + +// Use to send hex format LoRaWAN proprietary frames. +func pmsghex(data string) error { + uart.Write([]byte("+PMSGHEX: Start")) + crlf() + uart.Write([]byte("+PMSGHEX: Done")) + crlf() + 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 { + uart.Write([]byte("+PORT: ")) + uart.Write([]byte(p)) + crlf() + return nil +} + +// Set ADR function of LoRaWAN module +func adr(state string) error { + uart.Write([]byte("+ADR: ")) + uart.Write([]byte(state)) + crlf() + return nil +} + +// Use LoRaWAN defined DRx to set datarate of LoRaWAN AT modem. +func dr(rate string) error { + uart.Write([]byte("+ADR: ")) + uart.Write([]byte(rate)) + crlf() + return nil +} + +// Channel Configuration +func ch(channel string) error { + uart.Write([]byte("+CH: ")) + uart.Write([]byte(channel)) + crlf() + return nil +} + +// Set and Check Power +func power(setting string) error { + uart.Write([]byte("+POWER: ")) + uart.Write([]byte(setting)) + crlf() + return nil +} + +// Unconfirmed message repeats times. +func rept(setting string) error { + uart.Write([]byte("+REPT: ")) + uart.Write([]byte(setting)) + crlf() + 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 { + uart.Write([]byte("+RETRY: ")) + uart.Write([]byte(setting)) + crlf() + return nil +} + +func rxwin2(setting string) error { + uart.Write([]byte("+RXWIN2: ")) + uart.Write([]byte(setting)) + crlf() + return nil +} + +func rxwin1(setting string) error { + uart.Write([]byte("+RXWIN1: ")) + uart.Write([]byte(setting)) + crlf() + return nil +} + +func key(setting string) error { + uart.Write([]byte("+RETRY: ")) + uart.Write([]byte(setting)) + crlf() + return nil +} + +func fdefault(setting string) error { + uart.Write([]byte("+FDEFAULT: OK")) + crlf() + return nil +} + +func mode(setting string) error { + uart.Write([]byte("+MODE: ")) + uart.Write([]byte(setting)) + crlf() + return nil +} + +func join(setting string) error { + uart.Write([]byte("+JOIN: Starting")) + crlf() + uart.Write([]byte("+JOIN: Done")) + crlf() + return nil +} + +func beacon(setting string) error { + uart.Write([]byte("+BEACON: Starting")) + crlf() + uart.Write([]byte("+BEACON: Done")) + crlf() + return nil +} + +func class(setting string) error { + uart.Write([]byte("+CLASS: Starting")) + crlf() + uart.Write([]byte("+CLASS: Done")) + crlf() + return nil +} + +func delay(setting string) error { + uart.Write([]byte("+DELAY: Starting")) + crlf() + return nil +} + +func lw(setting string) error { + uart.Write([]byte("+LW: Starting")) + crlf() + return nil +} + +func wdt(setting string) error { + uart.Write([]byte("+WDT: Not implemented")) + crlf() + return nil +} + +func lowpower(setting string) error { + uart.Write([]byte("+LOWPOWER: Not implemented")) + crlf() + return nil +} + +func vdd(setting string) error { + uart.Write([]byte("+VDD: Not implemented")) + crlf() + return nil +} + +func temp(setting string) error { + uart.Write([]byte("+TEMP: Not implemented")) + crlf() + return nil +} + +func rtc(setting string) error { + uart.Write([]byte("+RTC: Not implemented")) + crlf() + return nil +} + +func eeprom(setting string) error { + uart.Write([]byte("+EEPROM: Not implemented")) + crlf() + return nil +} + +func uartcmd(setting string) error { + uart.Write([]byte("+UART: Not implemented")) + crlf() + return nil +} + +func test(setting string) error { + uart.Write([]byte("+TEST: Not implemented")) + crlf() + return nil +} + +func log(setting string) error { + uart.Write([]byte("+LOG: Not implemented")) + crlf() + return nil +} diff --git a/examples/lora/atcmd/main.go b/examples/lora/atcmd/main.go new file mode 100644 index 0000000..f10f9e3 --- /dev/null +++ b/examples/lora/atcmd/main.go @@ -0,0 +1,47 @@ +// 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) +) + +func main() { + uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) + + 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) + } +} diff --git a/examples/lora/atcmd/parser.go b/examples/lora/atcmd/parser.go new file mode 100644 index 0000000..5bee414 --- /dev/null +++ b/examples/lora/atcmd/parser.go @@ -0,0 +1,107 @@ +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) + default: + return errInvalidCommand + } + + return nil +} diff --git a/examples/lora/atcmd/sim.go b/examples/lora/atcmd/sim.go new file mode 100644 index 0000000..d3076b8 --- /dev/null +++ b/examples/lora/atcmd/sim.go @@ -0,0 +1,8 @@ +//go:build !featherwing && !gnse && !lorae5 && !nucleowl55jc + +package main + +// do simulator setup here +func setup() error { + return nil +} diff --git a/examples/lora/atcmd/sx126x.go b/examples/lora/atcmd/sx126x.go new file mode 100644 index 0000000..f3ea872 --- /dev/null +++ b/examples/lora/atcmd/sx126x.go @@ -0,0 +1,8 @@ +//go:build gnse || lorae5 || nucleowl55jc + +package main + +// do sx126x setup here +func setup() error { + return nil +} diff --git a/examples/lora/atcmd/sx127x.go b/examples/lora/atcmd/sx127x.go new file mode 100644 index 0000000..1c83274 --- /dev/null +++ b/examples/lora/atcmd/sx127x.go @@ -0,0 +1,8 @@ +//go:build featherwing + +package main + +// do sx127x setup here +func setup() error { + return nil +} diff --git a/examples/lora/atcmd/version.go b/examples/lora/atcmd/version.go new file mode 100644 index 0000000..d1ee0b9 --- /dev/null +++ b/examples/lora/atcmd/version.go @@ -0,0 +1,7 @@ +package main + +const VERSION = "0.0.1" + +func currentVersion() string { + return VERSION +} \ No newline at end of file