mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-04 23:17:47 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38ae263d5a | |||
| dc18724d17 | |||
| e74b01f973 | |||
| c78aa53820 | |||
| 060a2dc99a | |||
| 72c48d2f59 | |||
| 818764fe6a | |||
| 4e687b29c3 | |||
| d283abdb3a | |||
| 02d808a6fb | |||
| d473b9e1dc | |||
| 7cc32dffed | |||
| 58cb44f3f6 | |||
| 78cc1afe46 | |||
| e6cde8f7ae | |||
| 89f113c0ca | |||
| 3b8a808a52 | |||
| 7506a895a2 | |||
| 4a950a4474 | |||
| f2637a87d2 | |||
| ecb343b3b3 | |||
| 0b62d4fb4a | |||
| 5b461ec2d1 | |||
| b1c9612158 | |||
| 0b6e1d0e78 | |||
| 49e7a6d6c8 | |||
| be644b36fe | |||
| 409723e496 | |||
| 8aa3530c8b | |||
| 390854ee45 | |||
| ce5dd65dad | |||
| 73e9d5bfec | |||
| 462d2b3315 | |||
| 9e70ecbe64 | |||
| d6d06396b4 | |||
| 80b2a4a569 | |||
| 556aa9e6d3 | |||
| 4bc8ecdbdd |
@@ -6,6 +6,7 @@
|
||||
package bmp180 // import "tinygo.org/x/drivers/bmp180"
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
@@ -123,6 +124,21 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
|
||||
return 1000 * (p + ((x1 + x2 + 3791) >> 4)), nil
|
||||
}
|
||||
|
||||
// ReadAltitude returns the current altitude in meters based on the
|
||||
// current barometric pressure and estimated pressure at sea level.
|
||||
// Calculation is based on code from Adafruit BME280 library
|
||||
//
|
||||
// https://github.com/adafruit/Adafruit_BME280_Library
|
||||
func (d *Device) ReadAltitude() (int32, error) {
|
||||
mPa, err := d.ReadPressure()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
atmP := float32(mPa) / 100000
|
||||
|
||||
return int32(44330.0 * (1.0 - math.Pow(float64(atmP/SEALEVEL_PRESSURE), 0.1903))), nil
|
||||
}
|
||||
|
||||
// rawTemp returns the sensor's raw values of the temperature
|
||||
func (d *Device) rawTemp() (int32, error) {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
|
||||
|
||||
@@ -28,3 +28,7 @@ const (
|
||||
// ULTRAHIGHRESOLUTION is the highest oversampling mode of the pressure measurement.
|
||||
ULTRAHIGHRESOLUTION
|
||||
)
|
||||
|
||||
const (
|
||||
SEALEVEL_PRESSURE float32 = 1013.25 // in hPa
|
||||
)
|
||||
|
||||
@@ -27,6 +27,9 @@ func main() {
|
||||
pressure, _ := sensor.ReadPressure()
|
||||
println("Pressure", float32(pressure)/100000, "hPa")
|
||||
|
||||
altitude, _ := sensor.ReadAltitude()
|
||||
println("Altitude", altitude, "meters")
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,3 +39,14 @@ Builds/flashes atcmd console application on Lora-E5 using onboard SX126x.
|
||||
tinygo flash -target lorae5 ./examples/lora/lorawan/atcmd/
|
||||
```
|
||||
|
||||
## Joining a Public Lorawan Network
|
||||
|
||||
```
|
||||
AT+ID=DevEui,0101010101010101
|
||||
AT+ID=AppEui,0123012301230213
|
||||
AT+KEY=APPKEY,AEAEAEAEAEAEAEAAEAEAEAEAEAEAAEAE
|
||||
AT+LW=NET,ON
|
||||
AT+JOIN
|
||||
```
|
||||
|
||||
AT+LW=NET,(ON|OFF) command changes Lora Sync Word to connect on public network(ON) or private networks(OFF)
|
||||
|
||||
@@ -337,6 +337,17 @@ func delay(setting string) error {
|
||||
|
||||
func lw(setting string) error {
|
||||
cmd := "LW"
|
||||
|
||||
param, val, hasComma := strings.Cut(setting, ",")
|
||||
if hasComma {
|
||||
if param == "NET" {
|
||||
if val == "ON" {
|
||||
lorawan.SetPublicNetwork(true)
|
||||
} else {
|
||||
lorawan.SetPublicNetwork(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
writeCommandOutput(cmd, setting)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"tinygo.org/x/drivers/examples/lora/lorawan/common"
|
||||
"tinygo.org/x/drivers/lora"
|
||||
"tinygo.org/x/drivers/lora/lorawan"
|
||||
"tinygo.org/x/drivers/lora/lorawan/region"
|
||||
)
|
||||
|
||||
// change these to test a different UART or pins if available
|
||||
@@ -44,6 +45,8 @@ func main() {
|
||||
otaa = &lorawan.Otaa{}
|
||||
lorawan.UseRadio(radio)
|
||||
|
||||
lorawan.UseRegionSettings(region.EU868())
|
||||
|
||||
for {
|
||||
if uart.Buffered() > 0 {
|
||||
data, _ := uart.ReadByte()
|
||||
|
||||
@@ -33,3 +33,12 @@ tinygo flash -target pybadge -tags featherwing ./examples/lora/lorawan/basic-dem
|
||||
tinygo flash -target lorae5 ./examples/lora/lorawan/basic-demo
|
||||
```
|
||||
|
||||
|
||||
## Enable debugging
|
||||
|
||||
You can also enable some debug logs with ldflags :
|
||||
|
||||
```
|
||||
$ tinygo build -ldflags="-X 'main.debug=true'" -target=lorae5
|
||||
```
|
||||
|
||||
|
||||
@@ -2,10 +2,16 @@
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers/lora/lorawan"
|
||||
)
|
||||
|
||||
// These are sample keys, so the example builds
|
||||
// Either change here, or create a new go file and use customkeys build tag
|
||||
func setLorawanKeys() {
|
||||
otaa.SetAppEUI([]uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
otaa.SetDevEUI([]uint8{0xB3, 0xD5, 0x41, 0x00, 0x0A, 0xF1, 0xA4, 0x45})
|
||||
otaa.SetAppKey([]uint8{0x12, 0x22, 0xA3, 0xFF, 0x0C, 0x7B, 0x76, 0x7B, 0x8F, 0xD3, 0x12, 0x4F, 0xCE, 0x7A, 0x32, 0x16})
|
||||
lorawan.SetPublicNetwork(true)
|
||||
|
||||
}
|
||||
|
||||
@@ -9,8 +9,11 @@ import (
|
||||
"tinygo.org/x/drivers/examples/lora/lorawan/common"
|
||||
"tinygo.org/x/drivers/lora"
|
||||
"tinygo.org/x/drivers/lora/lorawan"
|
||||
"tinygo.org/x/drivers/lora/lorawan/region"
|
||||
)
|
||||
|
||||
var debug string
|
||||
|
||||
const (
|
||||
LORAWAN_JOIN_TIMEOUT_SEC = 180
|
||||
LORAWAN_RECONNECT_DELAY_SEC = 15
|
||||
@@ -62,33 +65,32 @@ func main() {
|
||||
session = &lorawan.Session{}
|
||||
otaa = &lorawan.Otaa{}
|
||||
|
||||
// Initial Lora modulation configuration
|
||||
loraConf := lora.Config{
|
||||
Freq: 868100000,
|
||||
Bw: lora.Bandwidth_125_0,
|
||||
Sf: lora.SpreadingFactor9,
|
||||
Cr: lora.CodingRate4_7,
|
||||
HeaderType: lora.HeaderExplicit,
|
||||
Preamble: 12,
|
||||
Ldr: lora.LowDataRateOptimizeOff,
|
||||
Iq: lora.IQStandard,
|
||||
Crc: lora.CRCOn,
|
||||
SyncWord: lora.SyncPublic,
|
||||
LoraTxPowerDBm: 20,
|
||||
}
|
||||
radio.LoraConfig(loraConf)
|
||||
|
||||
// Connect the lorawan with the Lora Radio device.
|
||||
lorawan.UseRadio(radio)
|
||||
|
||||
// Configure AppEUI, DevEUI, APPKey
|
||||
lorawan.UseRegionSettings(region.EU868())
|
||||
|
||||
// Configure AppEUI, DevEUI, APPKey, and public/private Lorawan Network
|
||||
setLorawanKeys()
|
||||
|
||||
if debug != "" {
|
||||
println("main: Network joined")
|
||||
println("main: DevEui, " + otaa.GetDevEUI())
|
||||
println("main: AppEui, " + otaa.GetAppEUI())
|
||||
println("main: DevAddr, " + otaa.GetAppKey())
|
||||
}
|
||||
|
||||
// Try to connect Lorawan network
|
||||
if err := loraConnect(); err != nil {
|
||||
failMessage(err)
|
||||
}
|
||||
|
||||
if debug != "" {
|
||||
println("main: NetID, " + otaa.GetNetID())
|
||||
println("main: NwkSKey, " + session.GetNwkSKey())
|
||||
println("main: AppSKey, " + session.GetAppSKey())
|
||||
println("main: Done")
|
||||
}
|
||||
// Try to periodicaly send an uplink sample message
|
||||
upCount := 1
|
||||
for {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
//go:build featherwing
|
||||
|
||||
package common
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
// We assume LoRa Featherwing module with sx127x is connected to PyBadge
|
||||
rstPin = machine.D11
|
||||
csPin = machine.D10
|
||||
dio0Pin = machine.D6
|
||||
dio1Pin = machine.D9
|
||||
spi = machine.SPI0
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
//go:build lgt92
|
||||
|
||||
package common
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
rstPin = machine.PB0
|
||||
csPin = machine.PA15
|
||||
dio0Pin = machine.PC13
|
||||
dio1Pin = machine.PB10
|
||||
spi = machine.SPI0
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !featherwing && !stm32wlx && !sx126x
|
||||
//go:build !featherwing && !lgt92 && !stm32wlx && !sx126x
|
||||
|
||||
package common
|
||||
|
||||
|
||||
@@ -37,22 +37,6 @@ func SetupLora() (lora.Radio, error) {
|
||||
return nil, errRadioNotFound
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -41,22 +41,6 @@ func SetupLora() (lora.Radio, error) {
|
||||
return nil, errRadioNotFound
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build featherwing
|
||||
//go:build featherwing || lgt92
|
||||
|
||||
package common
|
||||
|
||||
@@ -18,63 +18,25 @@ const (
|
||||
)
|
||||
|
||||
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() (lora.Radio, 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 = sx127x.New(spi, rstPin)
|
||||
loraRadio.SetRadioController(sx127x.NewRadioControl(csPin, dio0Pin, dio1Pin))
|
||||
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_125_0,
|
||||
Sf: lora.SpreadingFactor9,
|
||||
Cr: lora.CodingRate4_7,
|
||||
HeaderType: lora.HeaderExplicit,
|
||||
Preamble: 12,
|
||||
Iq: lora.IQStandard,
|
||||
Crc: lora.CRCOn,
|
||||
SyncWord: lora.SyncPublic,
|
||||
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))
|
||||
|
||||
@@ -35,8 +35,8 @@ func main() {
|
||||
|
||||
// Prepare for Lora operation
|
||||
loraConf := lora.Config{
|
||||
Freq: FREQ,
|
||||
Bw: lora.Bandwidth_500_0,
|
||||
Freq: lora.MHz_868_1,
|
||||
Bw: lora.Bandwidth_125_0,
|
||||
Sf: lora.SpreadingFactor9,
|
||||
Cr: lora.CodingRate4_7,
|
||||
HeaderType: lora.HeaderExplicit,
|
||||
@@ -45,7 +45,7 @@ func main() {
|
||||
Iq: lora.IQStandard,
|
||||
Crc: lora.CRCOn,
|
||||
SyncWord: lora.SyncPrivate,
|
||||
LoraTxPowerDBm: 14,
|
||||
LoraTxPowerDBm: 20,
|
||||
}
|
||||
loraRadio.LoraConfig(loraConf)
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"tinygo.org/x/drivers/sx126x"
|
||||
)
|
||||
|
||||
const FREQ = 868100000
|
||||
|
||||
const (
|
||||
LORA_DEFAULT_RXTIMEOUT_MS = 1000
|
||||
LORA_DEFAULT_TXTIMEOUT_MS = 5000
|
||||
@@ -44,8 +42,8 @@ func main() {
|
||||
}
|
||||
|
||||
loraConf := lora.Config{
|
||||
Freq: FREQ,
|
||||
Bw: lora.Bandwidth_500_0,
|
||||
Freq: lora.MHz_868_1,
|
||||
Bw: lora.Bandwidth_125_0,
|
||||
Sf: lora.SpreadingFactor9,
|
||||
Cr: lora.CodingRate4_7,
|
||||
HeaderType: lora.HeaderExplicit,
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"tinygo.org/x/drivers/sx127x"
|
||||
)
|
||||
|
||||
const FREQ = 868100000
|
||||
|
||||
const (
|
||||
LORA_DEFAULT_RXTIMEOUT_MS = 1000
|
||||
LORA_DEFAULT_TXTIMEOUT_MS = 5000
|
||||
@@ -40,13 +38,13 @@ func main() {
|
||||
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 = sx127x.New(SX127X_SPI, SX127X_PIN_RST)
|
||||
loraRadio.SetRadioController(sx127x.NewRadioControl(SX127X_PIN_CS, SX127X_PIN_DIO0, SX127X_PIN_DIO1))
|
||||
|
||||
loraRadio.Reset()
|
||||
state := loraRadio.DetectDevice()
|
||||
if !state {
|
||||
@@ -55,20 +53,10 @@ func main() {
|
||||
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,
|
||||
Freq: lora.MHz_868_1,
|
||||
Bw: lora.Bandwidth_125_0,
|
||||
Sf: lora.SpreadingFactor9,
|
||||
Cr: lora.CodingRate4_7,
|
||||
HeaderType: lora.HeaderExplicit,
|
||||
|
||||
+14
-3
@@ -42,6 +42,12 @@ func (ge GPSError) Unwrap() error {
|
||||
return ge.Err
|
||||
}
|
||||
|
||||
const (
|
||||
minimumNMEALength = 7
|
||||
startingDelimiter = '$'
|
||||
checksumDelimiter = '*'
|
||||
)
|
||||
|
||||
// Device wraps a connection to a GPS device.
|
||||
type Device struct {
|
||||
buffer []byte
|
||||
@@ -87,11 +93,11 @@ func (gps *Device) readNextSentence() (sentence string) {
|
||||
gps.sentence.Reset()
|
||||
var b byte = ' '
|
||||
|
||||
for b != '$' {
|
||||
for b != startingDelimiter {
|
||||
b = gps.readNextByte()
|
||||
}
|
||||
|
||||
for b != '*' {
|
||||
for b != checksumDelimiter {
|
||||
gps.sentence.WriteByte(b)
|
||||
b = gps.readNextByte()
|
||||
}
|
||||
@@ -153,8 +159,13 @@ func (gps *Device) WriteBytes(bytes []byte) {
|
||||
}
|
||||
|
||||
// validSentence checks if a sentence has been received uncorrupted
|
||||
// For example, a valid NMEA sentence such as this:
|
||||
// $GPGLL,3751.65,S,14507.36,E*77
|
||||
// It has to start with a '$' character.
|
||||
// It has to have a 5 character long sentence identifier.
|
||||
// It has to end with a '*' character following by a checksum.
|
||||
func validSentence(sentence string) error {
|
||||
if len(sentence) < 7 || sentence[0] != '$' || sentence[len(sentence)-3] != '*' {
|
||||
if len(sentence) < minimumNMEALength || sentence[0] != startingDelimiter || sentence[len(sentence)-3] != checksumDelimiter {
|
||||
return errInvalidNMEASentenceLength
|
||||
}
|
||||
var cs byte = 0
|
||||
|
||||
+19
-19
@@ -60,11 +60,11 @@ func (parser *Parser) Parse(sentence string) (Fix, error) {
|
||||
return fix, errInvalidGGASentence
|
||||
}
|
||||
|
||||
fix.Altitude = findAltitude(fields[9])
|
||||
fix.Satellites = findSatellites(fields[7])
|
||||
fix.Longitude = findLongitude(fields[4], fields[5])
|
||||
fix.Latitude = findLatitude(fields[2], fields[3])
|
||||
fix.Time = findTime(fields[1])
|
||||
fix.Latitude = findLatitude(fields[2], fields[3])
|
||||
fix.Longitude = findLongitude(fields[4], fields[5])
|
||||
fix.Satellites = findSatellites(fields[7])
|
||||
fix.Altitude = findAltitude(fields[9])
|
||||
fix.Valid = (fix.Altitude != -99999) && (fix.Satellites > 0)
|
||||
|
||||
return fix, nil
|
||||
@@ -75,11 +75,11 @@ func (parser *Parser) Parse(sentence string) (Fix, error) {
|
||||
return fix, errInvalidGLLSentence
|
||||
}
|
||||
|
||||
fix.Latitude = findLatitude(fields[2], fields[3])
|
||||
fix.Longitude = findLongitude(fields[4], fields[5])
|
||||
fix.Time = findTime(fields[6])
|
||||
fix.Latitude = findLatitude(fields[1], fields[2])
|
||||
fix.Longitude = findLongitude(fields[3], fields[4])
|
||||
fix.Time = findTime(fields[5])
|
||||
|
||||
fix.Valid = (fields[7] == "A")
|
||||
fix.Valid = (fields[6] == "A")
|
||||
|
||||
return fix, nil
|
||||
case "RMC":
|
||||
@@ -89,12 +89,12 @@ func (parser *Parser) Parse(sentence string) (Fix, error) {
|
||||
return fix, errInvalidRMCSentence
|
||||
}
|
||||
|
||||
fix.Longitude = findLongitude(fields[5], fields[6])
|
||||
fix.Latitude = findLatitude(fields[3], fields[4])
|
||||
fix.Time = findTime(fields[1])
|
||||
fix.Valid = (fields[2] == "A")
|
||||
fix.Latitude = findLatitude(fields[3], fields[4])
|
||||
fix.Longitude = findLongitude(fields[5], fields[6])
|
||||
fix.Speed = findSpeed(fields[7])
|
||||
fix.Heading = findHeading(fields[8])
|
||||
fix.Valid = (len(fields[2]) > 0 && fields[2] == "A")
|
||||
|
||||
return fix, nil
|
||||
}
|
||||
@@ -113,8 +113,8 @@ func findTime(val string) time.Time {
|
||||
m, _ := strconv.ParseInt(val[2:4], 10, 8)
|
||||
s, _ := strconv.ParseInt(val[4:6], 10, 8)
|
||||
ms := int64(0)
|
||||
if len(val) == 10 {
|
||||
ms, _ = strconv.ParseInt(val[7:10], 10, 16)
|
||||
if len(val) > 6 {
|
||||
ms, _ = strconv.ParseInt(val[7:], 10, 16)
|
||||
}
|
||||
t := time.Date(0, 0, 0, int(h), int(m), int(s), int(ms), time.UTC)
|
||||
|
||||
@@ -135,11 +135,11 @@ func findAltitude(val string) int32 {
|
||||
// $--GGA,,ddmm.mmmmm,x,,,,,,,,,,,*hh
|
||||
func findLatitude(val, hemi string) float32 {
|
||||
if len(val) > 8 {
|
||||
var dd = val[0:2]
|
||||
var mm = val[2:]
|
||||
var d, _ = strconv.ParseFloat(dd, 32)
|
||||
var m, _ = strconv.ParseFloat(mm, 32)
|
||||
var v = float32(d + (m / 60))
|
||||
dd := val[0:2]
|
||||
mm := val[2:]
|
||||
d, _ := strconv.ParseFloat(dd, 32)
|
||||
m, _ := strconv.ParseFloat(mm, 32)
|
||||
v := float32(d + (m / 60))
|
||||
if hemi == "S" {
|
||||
v *= -1
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func findLatitude(val, hemi string) float32 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
// findLatitude returns the longitude from an NMEA sentence:
|
||||
// findLongitude returns the longitude from an NMEA sentence:
|
||||
// $--GGA,,,,dddmm.mmmmm,x,,,,,,,,,*hh
|
||||
func findLongitude(val, hemi string) float32 {
|
||||
if len(val) > 8 {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package gps
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestParseUnknownSentence(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
p := NewParser()
|
||||
|
||||
val := "$GPGSV,3,1,09,07,14,317,22,08,31,284,25,10,32,133,39,16,85,232,29*7F"
|
||||
_, err := p.Parse(val)
|
||||
c.Assert(err.Error(), qt.Contains, "unsupported NMEA sentence type")
|
||||
}
|
||||
|
||||
func TestParseGGA(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
p := NewParser()
|
||||
|
||||
val := "$GPGGA,115739.00,4158.8441367,N,09147.4416929,"
|
||||
fix, err := p.Parse(val)
|
||||
if err != errInvalidGGASentence {
|
||||
t.Error("should have errInvalidGGASentence error")
|
||||
}
|
||||
|
||||
val = "$GPGGA,115739.00,4158.8441367,N,09147.4416929,W,4,13,0.9,255.747,M,-32.00,M,01,0000*6E"
|
||||
fix, err = p.Parse(val)
|
||||
if err != nil {
|
||||
t.Error("should have parsed")
|
||||
}
|
||||
c.Assert(fix.Latitude, qt.Equals, float32(41.980735778808594))
|
||||
c.Assert(fix.Longitude, qt.Equals, float32(-91.79069519042969))
|
||||
c.Assert(fix.Altitude, qt.Equals, int32(255))
|
||||
}
|
||||
|
||||
func TestParseGLL(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
p := NewParser()
|
||||
|
||||
val := "$GPGLL,3953.88008971,N,10506.7531891"
|
||||
_, err := p.Parse(val)
|
||||
if err != errInvalidGLLSentence {
|
||||
t.Error("should have errInvalidGLLSentence error")
|
||||
}
|
||||
|
||||
val = "$GPGLL,5109.0262317,N,11401.8407304,W,202725.00,A,D*79"
|
||||
fix, err := p.Parse(val)
|
||||
if err != nil {
|
||||
t.Error("should have parsed")
|
||||
}
|
||||
|
||||
c.Assert(fix.Latitude, qt.Equals, float32(51.15043640136719))
|
||||
c.Assert(fix.Longitude, qt.Equals, float32(-114.03067779541016))
|
||||
}
|
||||
|
||||
func TestParseRMC(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
p := NewParser()
|
||||
|
||||
val := "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,"
|
||||
_, err := p.Parse(val)
|
||||
if err != errInvalidRMCSentence {
|
||||
t.Error("should have errInvalidRMCSentence error")
|
||||
}
|
||||
|
||||
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,130522,0.0,E,D*2B"
|
||||
fix, err := p.Parse(val)
|
||||
if err != nil {
|
||||
t.Error("should have parsed")
|
||||
}
|
||||
|
||||
c.Assert(fix.Latitude, qt.Equals, float32(51.15043640136719))
|
||||
c.Assert(fix.Longitude, qt.Equals, float32(-114.03067779541016))
|
||||
}
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
val := ""
|
||||
tm := findTime(val)
|
||||
c.Assert(tm, qt.Equals, time.Time{})
|
||||
|
||||
val = "225446"
|
||||
tm = findTime(val)
|
||||
c.Assert(tm, qt.Equals, time.Date(0, 0, 0, 22, 54, 46, 0, time.UTC))
|
||||
|
||||
val = "124326.02752"
|
||||
tm = findTime(val)
|
||||
c.Assert(tm, qt.Equals, time.Date(0, 0, 0, 12, 43, 26, 2752, time.UTC))
|
||||
}
|
||||
@@ -76,3 +76,10 @@ const (
|
||||
SyncPublic = iota
|
||||
SyncPrivate
|
||||
)
|
||||
|
||||
const (
|
||||
MHz_868_1 = 868100000
|
||||
MHz_868_5 = 868500000
|
||||
MHz_916_8 = 916800000
|
||||
MHz_923_3 = 923300000
|
||||
)
|
||||
|
||||
+64
-27
@@ -4,32 +4,41 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers/lora"
|
||||
"tinygo.org/x/drivers/lora/lorawan/region"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoJoinAcceptReceived = errors.New("no JoinAccept packet received")
|
||||
ErrNoRadioAttached = errors.New("no LoRa radio attached")
|
||||
ErrInvalidEuiLength = errors.New("invalid EUI length")
|
||||
ErrInvalidAppKeyLength = errors.New("invalid AppKey length")
|
||||
ErrInvalidPacketLength = errors.New("invalid packet length")
|
||||
ErrInvalidDevAddrLength = errors.New("invalid DevAddr length")
|
||||
ErrInvalidMic = errors.New("invalid Mic")
|
||||
ErrFrmPayloadTooLarge = errors.New("FRM payload too large")
|
||||
ErrInvalidNetIDLength = errors.New("invalid NetID length")
|
||||
ErrInvalidNwkSKeyLength = errors.New("invalid NwkSKey length")
|
||||
ErrInvalidAppSKeyLength = errors.New("invalid AppSKey length")
|
||||
ErrNoJoinAcceptReceived = errors.New("no JoinAccept packet received")
|
||||
ErrNoRadioAttached = errors.New("no LoRa radio attached")
|
||||
ErrInvalidEuiLength = errors.New("invalid EUI length")
|
||||
ErrInvalidAppKeyLength = errors.New("invalid AppKey length")
|
||||
ErrInvalidPacketLength = errors.New("invalid packet length")
|
||||
ErrInvalidDevAddrLength = errors.New("invalid DevAddr length")
|
||||
ErrInvalidMic = errors.New("invalid Mic")
|
||||
ErrFrmPayloadTooLarge = errors.New("FRM payload too large")
|
||||
ErrInvalidNetIDLength = errors.New("invalid NetID length")
|
||||
ErrInvalidNwkSKeyLength = errors.New("invalid NwkSKey length")
|
||||
ErrInvalidAppSKeyLength = errors.New("invalid AppSKey length")
|
||||
ErrUndefinedRegionSettings = errors.New("undefined Regionnal Settings ")
|
||||
)
|
||||
|
||||
const (
|
||||
LORA_TX_TIMEOUT = 2000
|
||||
LORA_RX_TIMEOUT = 5000
|
||||
LORA_RX_TIMEOUT = 10000
|
||||
)
|
||||
|
||||
var (
|
||||
ActiveRadio lora.Radio
|
||||
Retries = 15
|
||||
ActiveRadio lora.Radio
|
||||
Retries = 15
|
||||
regionSettings region.RegionSettings
|
||||
)
|
||||
|
||||
// UseRegionSettings sets current Lorawan Regional parameters
|
||||
func UseRegionSettings(rs region.RegionSettings) {
|
||||
regionSettings = rs
|
||||
}
|
||||
|
||||
// UseRadio attaches Lora radio driver to Lorawan
|
||||
func UseRadio(r lora.Radio) {
|
||||
if ActiveRadio != nil {
|
||||
panic("lorawan.ActiveRadio is already set")
|
||||
@@ -37,6 +46,25 @@ func UseRadio(r lora.Radio) {
|
||||
ActiveRadio = r
|
||||
}
|
||||
|
||||
// SetPublicNetwork defines Lora Sync Word according to network type (public/private)
|
||||
func SetPublicNetwork(enabled bool) {
|
||||
ActiveRadio.SetPublicNetwork(enabled)
|
||||
}
|
||||
|
||||
// ApplyChannelConfig sets current Lora modulation according to current regional settings
|
||||
func applyChannelConfig(ch *region.Channel) {
|
||||
ActiveRadio.SetFrequency(ch.Frequency)
|
||||
ActiveRadio.SetBandwidth(ch.Bandwidth)
|
||||
ActiveRadio.SetCodingRate(ch.CodingRate)
|
||||
ActiveRadio.SetSpreadingFactor(ch.SpreadingFactor)
|
||||
ActiveRadio.SetPreambleLength(ch.PreambleLength)
|
||||
ActiveRadio.SetTxPower(ch.TxPowerDBm)
|
||||
// Lorawan defaults to explicit headers
|
||||
ActiveRadio.SetHeaderType(lora.HeaderExplicit)
|
||||
ActiveRadio.SetCrc(true)
|
||||
}
|
||||
|
||||
// Join tries to connect Lorawan Gateway
|
||||
func Join(otaa *Otaa, session *Session) error {
|
||||
var resp []uint8
|
||||
|
||||
@@ -44,6 +72,10 @@ func Join(otaa *Otaa, session *Session) error {
|
||||
return ErrNoRadioAttached
|
||||
}
|
||||
|
||||
if regionSettings == nil {
|
||||
return ErrUndefinedRegionSettings
|
||||
}
|
||||
|
||||
otaa.Init()
|
||||
|
||||
// Send join packet
|
||||
@@ -52,24 +84,22 @@ func Join(otaa *Otaa, session *Session) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ActiveRadio.SetCrc(true)
|
||||
ActiveRadio.SetIqMode(0) // IQ Standard
|
||||
// Prepare radio for Join Tx
|
||||
applyChannelConfig(regionSettings.JoinRequestChannel())
|
||||
ActiveRadio.SetIqMode(lora.IQStandard)
|
||||
ActiveRadio.Tx(payload, LORA_TX_TIMEOUT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for JoinAccept
|
||||
ActiveRadio.SetIqMode(1) // IQ Inverted
|
||||
for i := 0; i < Retries; i++ {
|
||||
resp, err = ActiveRadio.Rx(LORA_RX_TIMEOUT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp != nil {
|
||||
break
|
||||
}
|
||||
applyChannelConfig(regionSettings.JoinAcceptChannel())
|
||||
ActiveRadio.SetIqMode(lora.IQInverted)
|
||||
resp, err = ActiveRadio.Rx(LORA_RX_TIMEOUT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
return ErrNoJoinAcceptReceived
|
||||
}
|
||||
@@ -82,13 +112,20 @@ func Join(otaa *Otaa, session *Session) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendUplink sends Lorawan Uplink message
|
||||
func SendUplink(data []uint8, session *Session) error {
|
||||
|
||||
if regionSettings == nil {
|
||||
return ErrUndefinedRegionSettings
|
||||
}
|
||||
|
||||
payload, err := session.GenMessage(0, []byte(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ActiveRadio.SetCrc(true)
|
||||
ActiveRadio.SetIqMode(0) // IQ Standard
|
||||
|
||||
applyChannelConfig(regionSettings.UplinkChannel())
|
||||
ActiveRadio.SetIqMode(lora.IQStandard)
|
||||
ActiveRadio.Tx(payload, LORA_TX_TIMEOUT)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -20,7 +20,6 @@ type Otaa struct {
|
||||
// Initialize DevNonce
|
||||
func (o *Otaa) Init() {
|
||||
o.buf = make([]uint8, 0)
|
||||
|
||||
o.generateDevNonce()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package region
|
||||
|
||||
import "tinygo.org/x/drivers/lora"
|
||||
|
||||
const (
|
||||
AU915_DEFAULT_PREAMBLE_LEN = 8
|
||||
AU915_DEFAULT_TX_POWER_DBM = 20
|
||||
)
|
||||
|
||||
type RegionSettingsAU915 struct {
|
||||
joinRequestChannel *Channel
|
||||
joinAcceptChannel *Channel
|
||||
uplinkChannel *Channel
|
||||
}
|
||||
|
||||
func AU915() *RegionSettingsAU915 {
|
||||
return &RegionSettingsAU915{
|
||||
joinRequestChannel: &Channel{lora.MHz_916_8,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
AU915_DEFAULT_PREAMBLE_LEN,
|
||||
AU915_DEFAULT_TX_POWER_DBM},
|
||||
joinAcceptChannel: &Channel{lora.MHz_923_3,
|
||||
lora.Bandwidth_500_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
AU915_DEFAULT_PREAMBLE_LEN,
|
||||
AU915_DEFAULT_TX_POWER_DBM},
|
||||
uplinkChannel: &Channel{lora.MHz_868_5,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
AU915_DEFAULT_PREAMBLE_LEN,
|
||||
AU915_DEFAULT_TX_POWER_DBM},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RegionSettingsAU915) JoinRequestChannel() *Channel {
|
||||
return r.joinRequestChannel
|
||||
}
|
||||
|
||||
func (r *RegionSettingsAU915) JoinAcceptChannel() *Channel {
|
||||
return r.joinAcceptChannel
|
||||
}
|
||||
|
||||
func (r *RegionSettingsAU915) UplinkChannel() *Channel {
|
||||
return r.uplinkChannel
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package region
|
||||
|
||||
import "tinygo.org/x/drivers/lora"
|
||||
|
||||
const (
|
||||
EU868_DEFAULT_PREAMBLE_LEN = 8
|
||||
EU868_DEFAULT_TX_POWER_DBM = 20
|
||||
)
|
||||
|
||||
type RegionSettingsEU868 struct {
|
||||
joinRequestChannel *Channel
|
||||
joinAcceptChannel *Channel
|
||||
uplinkChannel *Channel
|
||||
}
|
||||
|
||||
func EU868() *RegionSettingsEU868 {
|
||||
return &RegionSettingsEU868{
|
||||
joinRequestChannel: &Channel{lora.MHz_868_1,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
EU868_DEFAULT_PREAMBLE_LEN,
|
||||
EU868_DEFAULT_TX_POWER_DBM},
|
||||
joinAcceptChannel: &Channel{lora.MHz_868_1,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
EU868_DEFAULT_PREAMBLE_LEN,
|
||||
EU868_DEFAULT_TX_POWER_DBM},
|
||||
uplinkChannel: &Channel{lora.MHz_868_1,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
EU868_DEFAULT_PREAMBLE_LEN,
|
||||
EU868_DEFAULT_TX_POWER_DBM},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RegionSettingsEU868) JoinRequestChannel() *Channel {
|
||||
return r.joinRequestChannel
|
||||
}
|
||||
|
||||
func (r *RegionSettingsEU868) JoinAcceptChannel() *Channel {
|
||||
return r.joinAcceptChannel
|
||||
}
|
||||
|
||||
func (r *RegionSettingsEU868) UplinkChannel() *Channel {
|
||||
return r.uplinkChannel
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package region
|
||||
|
||||
type Channel struct {
|
||||
Frequency uint32
|
||||
Bandwidth uint8
|
||||
SpreadingFactor uint8
|
||||
CodingRate uint8
|
||||
PreambleLength uint16
|
||||
TxPowerDBm int8
|
||||
}
|
||||
|
||||
type RegionSettings interface {
|
||||
JoinRequestChannel() *Channel
|
||||
JoinAcceptChannel() *Channel
|
||||
UplinkChannel() *Channel
|
||||
}
|
||||
@@ -10,5 +10,10 @@ type Radio interface {
|
||||
SetBandwidth(bw uint8)
|
||||
SetCrc(enable bool)
|
||||
SetSpreadingFactor(sf uint8)
|
||||
SetPreambleLength(plen uint16)
|
||||
SetTxPower(txpow int8)
|
||||
SetSyncWord(syncWord uint16)
|
||||
SetPublicNetwork(enable bool)
|
||||
SetHeaderType(headerType uint8)
|
||||
LoraConfig(cnf Config)
|
||||
}
|
||||
|
||||
+16
-1
@@ -32,7 +32,7 @@ func (d Device) Connected() bool {
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
func (d Device) Configure() error {
|
||||
return d.bus.WriteRegister(uint8(d.Address), PWR_MGMT_1, []uint8{0})
|
||||
return d.SetClockSource(CLOCK_INTERNAL)
|
||||
}
|
||||
|
||||
// ReadAcceleration reads the current acceleration from the device and returns
|
||||
@@ -78,3 +78,18 @@ func (d Device) ReadRotation() (x int32, y int32, z int32) {
|
||||
z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / 2048 * 1000
|
||||
return
|
||||
}
|
||||
|
||||
// SetClockSource allows the user to configure the clock source.
|
||||
func (d Device) SetClockSource(source uint8) error {
|
||||
return d.bus.WriteRegister(uint8(d.Address), PWR_MGMT_1, []uint8{source})
|
||||
}
|
||||
|
||||
// SetFullScaleGyroRange allows the user to configure the scale range for the gyroscope.
|
||||
func (d Device) SetFullScaleGyroRange(rng uint8) error {
|
||||
return d.bus.WriteRegister(uint8(d.Address), GYRO_CONFIG, []uint8{rng})
|
||||
}
|
||||
|
||||
// SetFullScaleAccelRange allows the user to configure the scale range for the accelerometer.
|
||||
func (d Device) SetFullScaleAccelRange(rng uint8) error {
|
||||
return d.bus.WriteRegister(uint8(d.Address), ACCEL_CONFIG, []uint8{rng})
|
||||
}
|
||||
|
||||
@@ -98,6 +98,29 @@ const (
|
||||
I2C_PER3_DO = 0x66
|
||||
I2C_MST_DELAY_CT = 0x67
|
||||
|
||||
// Clock settings
|
||||
CLOCK_INTERNAL = 0x00
|
||||
CLOCK_PLL_XGYRO = 0x01
|
||||
CLOCK_PLL_YGYRO = 0x02
|
||||
CLOCK_PLL_ZGYRO = 0x03
|
||||
CLOCK_PLL_EXTERNAL_32_768_KZ = 0x04
|
||||
CLOCK_PLL_EXTERNAL_19_2_MHZ = 0x05
|
||||
CLOCK_RESERVED = 0x06
|
||||
CLOCK_STOP = 0x07
|
||||
|
||||
// Accelerometer settings
|
||||
AFS_RANGE_2G = 0x00
|
||||
AFS_RANGE_4G = 0x01
|
||||
AFS_RANGE_8G = 0x02
|
||||
AFS_RANGE_16G = 0x03
|
||||
|
||||
// Gyroscope settings
|
||||
FS_RANGE_250 = 0x00
|
||||
FS_RANGE_500 = 0x01
|
||||
FS_RANGE_1000 = 0x02
|
||||
FS_RANGE_2000 = 0x03
|
||||
|
||||
// other registers
|
||||
SIGNAL_PATH_RES = 0x68 // Signal path reset
|
||||
USER_CTRL = 0x6A // User control
|
||||
PWR_MGMT_1 = 0x6B // Power Management 1
|
||||
|
||||
+4
-1
@@ -357,7 +357,10 @@ func ParseIP(s string) IP {
|
||||
|
||||
// String returns the string form of the IP address ip.
|
||||
func (ip IP) String() string {
|
||||
return string(ip)
|
||||
if len(ip) < 4 {
|
||||
return ""
|
||||
}
|
||||
return strconv.Itoa(int(ip[0])) + "." + strconv.Itoa(int(ip[1])) + "." + strconv.Itoa(int(ip[2])) + "." + strconv.Itoa(int(ip[3]))
|
||||
}
|
||||
|
||||
// Conn is a generic stream-oriented network connection.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestIPAddressString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
ipaddr := ParseIP("127.0.0.1")
|
||||
|
||||
c.Assert(ipaddr.String(), qt.Equals, "127.0.0.1")
|
||||
}
|
||||
+21
-2
@@ -545,7 +545,7 @@ func (d *Device) SetCodingRate(cr uint8) {
|
||||
// SetBandwidth() sets current Lora Bandwidth
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetBandwidth(bw uint8) {
|
||||
d.loraConf.Cr = bw
|
||||
d.loraConf.Bw = bw
|
||||
}
|
||||
|
||||
// SetCrc() sets current CRC mode (ON/OFF)
|
||||
@@ -558,12 +558,30 @@ func (d *Device) SetCrc(enable bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetSpreadingFactor setc surrent Lora Spreading Factor
|
||||
// SetSpreadingFactor sets current Lora Spreading Factor
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetSpreadingFactor(sf uint8) {
|
||||
d.loraConf.Sf = sf
|
||||
}
|
||||
|
||||
// SetPreambleLength sets current Lora Preamble Length
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetPreambleLength(pl uint16) {
|
||||
d.loraConf.Preamble = pl
|
||||
}
|
||||
|
||||
// SetTxPowerDbm sets current Lora TX Power in DBm
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetTxPower(txpow int8) {
|
||||
d.loraConf.LoraTxPowerDBm = txpow
|
||||
}
|
||||
|
||||
// SetHeaderType sets implicit or explicit header mode
|
||||
// NB: Change will be applied at next RX / TX
|
||||
func (d *Device) SetHeaderType(headerType uint8) {
|
||||
d.loraConf.HeaderType = headerType
|
||||
}
|
||||
|
||||
//
|
||||
// Lora functions
|
||||
//
|
||||
@@ -640,6 +658,7 @@ func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) {
|
||||
irqVal := uint16(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR)
|
||||
d.SetStandby()
|
||||
d.SetBufferBaseAddress(0, 0)
|
||||
d.SetRfFrequency(d.loraConf.Freq)
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package sx127x
|
||||
|
||||
// SX127X radio transceiver has several pins that control NSS,
|
||||
// and that are signalled when RX or TX operations are completed.
|
||||
// This interface allows the creation of types that can control this
|
||||
type RadioController interface {
|
||||
Init() error
|
||||
SetNss(state bool) error
|
||||
SetupInterrupts(handler func()) error
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package sx127x
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
// RadioControl for boards that are connected using normal pins.
|
||||
type RadioControl struct {
|
||||
nssPin, dio0Pin, dio1Pin machine.Pin
|
||||
}
|
||||
|
||||
func NewRadioControl(nssPin, dio0Pin, dio1Pin machine.Pin) *RadioControl {
|
||||
return &RadioControl{
|
||||
nssPin: nssPin,
|
||||
dio0Pin: dio0Pin,
|
||||
dio1Pin: dio1Pin,
|
||||
}
|
||||
}
|
||||
|
||||
// SetNss sets the NSS line aka chip select for SPI.
|
||||
func (rc *RadioControl) SetNss(state bool) error {
|
||||
rc.nssPin.Set(state)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Init() configures whatever needed for sx127x radio control
|
||||
func (rc *RadioControl) Init() error {
|
||||
rc.nssPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
rc.dio0Pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
|
||||
rc.dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
|
||||
return nil
|
||||
}
|
||||
|
||||
// add interrupt handlers for Radio IRQs for pins
|
||||
func (rc *RadioControl) SetupInterrupts(handler func()) error {
|
||||
irqHandler = handler
|
||||
|
||||
// Setup DIO0 interrupt Handling
|
||||
if err := rc.dio0Pin.SetInterrupt(machine.PinRising, handleInterrupt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup DIO1 interrupt Handling
|
||||
if err := rc.dio1Pin.SetInterrupt(machine.PinRising, handleInterrupt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var irqHandler func()
|
||||
|
||||
func handleInterrupt(machine.Pin) {
|
||||
irqHandler()
|
||||
}
|
||||
+57
-25
@@ -21,9 +21,10 @@ const (
|
||||
// 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
|
||||
rstPin machine.Pin // GPIO for reset
|
||||
radioEventChan chan lora.RadioEvent // Channel for Receiving events
|
||||
loraConf lora.Config // Current Lora configuration
|
||||
controller RadioController // to manage interactions with the radio
|
||||
deepSleep bool // Internal Sleep state
|
||||
deviceType int // sx1261,sx1262,sx1268 (defaults sx1261)
|
||||
spiBuffer [SPI_BUFFER_SIZE]uint8
|
||||
@@ -41,16 +42,26 @@ func (d *Device) GetRadioEventChan() chan lora.RadioEvent {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func New(spi machine.SPI, rstPin machine.Pin) *Device {
|
||||
k := Device{
|
||||
spi: spi,
|
||||
csPin: csPin,
|
||||
rstPin: rstPin,
|
||||
radioEventChan: make(chan lora.RadioEvent, 10),
|
||||
}
|
||||
return &k
|
||||
}
|
||||
|
||||
// SetRadioControl let you define the RadioController
|
||||
func (d *Device) SetRadioController(rc RadioController) error {
|
||||
d.controller = rc
|
||||
if err := d.controller.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
d.controller.SetupInterrupts(d.HandleInterrupt)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset re-initialize the sx127x device
|
||||
func (d *Device) Reset() {
|
||||
d.rstPin.Low()
|
||||
@@ -67,21 +78,21 @@ func (d *Device) DetectDevice() bool {
|
||||
|
||||
// ReadRegister reads register value
|
||||
func (d *Device) ReadRegister(reg uint8) uint8 {
|
||||
d.csPin.Low()
|
||||
d.controller.SetNss(false)
|
||||
d.spi.Tx([]byte{reg & 0x7f}, nil)
|
||||
var value [1]byte
|
||||
d.spi.Tx(nil, value[:])
|
||||
d.csPin.High()
|
||||
d.controller.SetNss(true)
|
||||
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.controller.SetNss(false)
|
||||
d.spi.Tx([]byte{reg | 0x80}, nil)
|
||||
d.spi.Tx([]byte{value}, response[:])
|
||||
d.csPin.High()
|
||||
d.controller.SetNss(true)
|
||||
return response[0]
|
||||
}
|
||||
|
||||
@@ -134,8 +145,13 @@ func (d *Device) GetBandwidth() int32 {
|
||||
}
|
||||
*/
|
||||
|
||||
// SetTxPower sets the transmitter output power
|
||||
func (d *Device) SetTxPower(txPower int8, paBoost bool) {
|
||||
// SetTxPower sets the transmitter output (with paBoost ON)
|
||||
func (d *Device) SetTxPower(txPower int8) {
|
||||
d.SetTxPowerWithPaBoost(txPower, true)
|
||||
}
|
||||
|
||||
// SetTxPowerWithPaBoost sets the transmitter output power and may activate paBoost
|
||||
func (d *Device) SetTxPowerWithPaBoost(txPower int8, paBoost bool) {
|
||||
if !paBoost {
|
||||
// RFO
|
||||
if txPower < 0 {
|
||||
@@ -260,8 +276,8 @@ func (d *Device) SetCodingRate(cr uint8) {
|
||||
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) {
|
||||
// SetHeaderType set implicit or explicit mode
|
||||
func (d *Device) SetHeaderType(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)
|
||||
@@ -304,7 +320,7 @@ func (d *Device) SetCrc(enable bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) SetPreamble(pLen uint16) {
|
||||
func (d *Device) SetPreambleLength(pLen uint16) {
|
||||
// Sets preamble length
|
||||
d.WriteRegister(SX127X_REG_PREAMBLE_MSB, uint8((pLen>>8)&0xFF))
|
||||
d.WriteRegister(SX127X_REG_PREAMBLE_LSB, uint8(pLen&0xFF))
|
||||
@@ -331,6 +347,15 @@ func (d *Device) SetIqMode(val uint8) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetPublicNetwork changes Sync Word to match network type
|
||||
func (d *Device) SetPublicNetwork(enabled bool) {
|
||||
if enabled {
|
||||
d.SetSyncWord(SX127X_LORA_MAC_PUBLIC_SYNCWORD)
|
||||
} else {
|
||||
d.SetSyncWord(SX127X_LORA_MAC_PRIVATE_SYNCWORD)
|
||||
}
|
||||
}
|
||||
|
||||
// Tx sends a lora packet, (with timeout)
|
||||
func (d *Device) Tx(pkt []uint8, timeoutMs uint32) error {
|
||||
d.SetOpModeLora()
|
||||
@@ -342,15 +367,15 @@ func (d *Device) Tx(pkt []uint8, timeoutMs uint32) error {
|
||||
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.SetPreambleLength(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.SetTxPower(d.loraConf.LoraTxPowerDBm)
|
||||
d.SetHeaderType(d.loraConf.HeaderType)
|
||||
d.SetAgcAuto(SX127X_AGC_AUTO_ON)
|
||||
|
||||
// set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
|
||||
@@ -398,15 +423,15 @@ func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) {
|
||||
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.SetPreambleLength(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.SetTxPower(d.loraConf.LoraTxPowerDBm)
|
||||
d.SetHeaderType(d.loraConf.HeaderType)
|
||||
d.SetAgcAuto(SX127X_AGC_AUTO_ON)
|
||||
|
||||
// set the IRQ mapping DIO0=RxDone DIO1=RxTimeout DIO2=NOP
|
||||
@@ -415,19 +440,26 @@ func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) {
|
||||
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
|
||||
|
||||
// Get Radio Event Channel
|
||||
radioCh := d.GetRadioEventChan()
|
||||
|
||||
msg := <-radioCh
|
||||
if msg.EventType == lora.RadioEventTimeout {
|
||||
// Single RX mode don't properly handle Timeouts on sx127x, so we use Continuous RX
|
||||
// Go routine is a workaround to stop the Continuous RX and fire a timeout Event
|
||||
d.SetOpMode(SX127X_OPMODE_RX)
|
||||
|
||||
var msg lora.RadioEvent
|
||||
select {
|
||||
case msg = <-radioCh:
|
||||
if msg.EventType != lora.RadioEventRxDone {
|
||||
return nil, errors.New("Unexpected Radio Event while RX " + string(0x30+msg.EventType))
|
||||
}
|
||||
case <-time.After(time.Millisecond * time.Duration(timeoutMs)):
|
||||
d.SetOpMode(SX127X_OPMODE_STANDBY)
|
||||
return nil, nil
|
||||
} else if msg.EventType != lora.RadioEventRxDone {
|
||||
return nil, errors.New("Unexpected Radio Event while RX " + string(0x30+msg.EventType))
|
||||
}
|
||||
|
||||
// Get the received payload
|
||||
d.WriteRegister(SX127X_REG_FIFO_RX_BASE_ADDR, 0)
|
||||
d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, 0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user