aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example/main.go49
-rw-r--r--examples/mqtt/README.md5
-rw-r--r--examples/mqtt/main.go92
-rw-r--r--examples/radio/README.md6
-rw-r--r--examples/radio/main.go87
-rw-r--r--go.mod8
-rw-r--r--go.sum16
7 files changed, 202 insertions, 61 deletions
diff --git a/example/main.go b/example/main.go
deleted file mode 100644
index f88d52d..0000000
--- a/example/main.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package main
-
-import (
- "context"
- "github.com/charmbracelet/log"
- pb "github.com/meshnet-gophers/meshtastic-go/meshtastic"
- "github.com/meshnet-gophers/meshtastic-go/transport"
- "github.com/meshnet-gophers/meshtastic-go/transport/serial"
- "google.golang.org/protobuf/proto"
- "os"
- "os/signal"
- "time"
-)
-
-func main() {
- ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
- defer cancel()
-
- log.SetLevel(log.DebugLevel)
-
- ports := serial.GetPorts()
- serialConn, err := serial.Connect(ports[0])
- if err != nil {
- panic(err)
- }
- streamConn, err := transport.NewClientStreamConn(serialConn)
- if err != nil {
- panic(err)
- }
- defer func() {
- if err := streamConn.Close(); err != nil {
- panic(err)
- }
- }()
-
- client := transport.NewClient(streamConn, false)
- client.Handle(new(pb.MeshPacket), func(msg proto.Message) {
- pkt := msg.(*pb.MeshPacket)
- log.Info("Received message from radio", "msg", pkt)
- })
- ctxTimeout, cancelTimeout := context.WithTimeout(ctx, 10*time.Second)
- defer cancelTimeout()
- if client.Connect(ctxTimeout) != nil {
- panic("Failed to connect to the radio")
- }
-
- log.Info("Waiting for interrupt signal")
- <-ctx.Done()
-}
diff --git a/examples/mqtt/README.md b/examples/mqtt/README.md
new file mode 100644
index 0000000..1ef2b02
--- /dev/null
+++ b/examples/mqtt/README.md
@@ -0,0 +1,5 @@
+This example connects to the public MQTT server, receives and decodes messages on the EU_868 LongFast topic, and logs them out
+
+```shell
+go run main.go
+```
diff --git a/examples/mqtt/main.go b/examples/mqtt/main.go
new file mode 100644
index 0000000..0ad9c5f
--- /dev/null
+++ b/examples/mqtt/main.go
@@ -0,0 +1,92 @@
+package main
+
+import (
+ "encoding/base64"
+ "encoding/hex"
+ "fmt"
+ "github.com/charmbracelet/log"
+ pb "github.com/meshnet-gophers/meshtastic-go/meshtastic"
+ "github.com/meshnet-gophers/meshtastic-go/mqtt"
+ "github.com/meshnet-gophers/meshtastic-go/radio"
+ "google.golang.org/protobuf/proto"
+ "strings"
+)
+
+func main() {
+ client := mqtt.NewClient("tcp://mqtt.meshtastic.org:1883", "meshdev", "large4cats", "msh")
+ err := client.Connect()
+ if err != nil {
+ log.Fatal(err)
+ }
+ client.Handle("LongFast", channelHandler("LongFast"))
+ log.Info("Started")
+ select {}
+}
+
+func channelHandler(channel string) mqtt.HandlerFunc {
+ return func(m mqtt.Message) {
+ var env pb.ServiceEnvelope
+ err := proto.Unmarshal(m.Payload, &env)
+ if err != nil {
+ log.Fatal("failed unmarshalling to service envelope", "err", err, "payload", hex.EncodeToString(m.Payload))
+ return
+ }
+
+ key, err := generateKey("1PG7OiApB1nwvP+rz05pAQ==")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ decodedMessage, err := radio.XOR(env.Packet.GetEncrypted(), key, env.Packet.Id, env.Packet.From)
+ if err != nil {
+ log.Error(err)
+ }
+ var message pb.Data
+ err = proto.Unmarshal(decodedMessage, &message)
+
+ log.Info(processMessage(message), "topic", m.Topic, "channel", channel, "portnum", message.Portnum.String())
+ }
+}
+
+func processMessage(message pb.Data) string {
+ if message.Portnum == pb.PortNum_NODEINFO_APP {
+ var user = pb.User{}
+ proto.Unmarshal(message.Payload, &user)
+ return user.String()
+ }
+ if message.Portnum == pb.PortNum_POSITION_APP {
+ var pos = pb.Position{}
+ proto.Unmarshal(message.Payload, &pos)
+ return pos.String()
+ }
+ if message.Portnum == pb.PortNum_TELEMETRY_APP {
+ var t = pb.Telemetry{}
+ proto.Unmarshal(message.Payload, &t)
+ return t.String()
+ }
+ if message.Portnum == pb.PortNum_NEIGHBORINFO_APP {
+ var n = pb.NeighborInfo{}
+ proto.Unmarshal(message.Payload, &n)
+ return n.String()
+ }
+ if message.Portnum == pb.PortNum_STORE_FORWARD_APP {
+ var s = pb.StoreAndForward{}
+ proto.Unmarshal(message.Payload, &s)
+ return s.String()
+ }
+
+ return fmt.Sprintf("unknown message type")
+}
+
+func generateKey(key string) ([]byte, error) {
+ // Pad the key with '=' characters to ensure it's a valid base64 string
+ padding := (4 - len(key)%4) % 4
+ paddedKey := key + strings.Repeat("=", padding)
+
+ // Replace '-' with '+' and '_' with '/'
+ replacedKey := strings.ReplaceAll(paddedKey, "-", "+")
+ replacedKey = strings.ReplaceAll(replacedKey, "_", "/")
+
+ // Decode the base64-encoded key
+ return base64.StdEncoding.DecodeString(replacedKey)
+}
diff --git a/examples/radio/README.md b/examples/radio/README.md
new file mode 100644
index 0000000..4a6bec5
--- /dev/null
+++ b/examples/radio/README.md
@@ -0,0 +1,6 @@
+This example connects to a Meshtastic device connected to a serial port, decodes received messages, and logs them out
+
+```shell
+go run main.go # search for first device connected to a serial port
+go run main.go /dev/ttyUSB0 # explicitly provide a port to connect to
+```
diff --git a/examples/radio/main.go b/examples/radio/main.go
new file mode 100644
index 0000000..feda4bd
--- /dev/null
+++ b/examples/radio/main.go
@@ -0,0 +1,87 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "github.com/charmbracelet/log"
+ pb "github.com/meshnet-gophers/meshtastic-go/meshtastic"
+ "github.com/meshnet-gophers/meshtastic-go/transport"
+ "github.com/meshnet-gophers/meshtastic-go/transport/serial"
+ "google.golang.org/protobuf/proto"
+ "os"
+ "os/signal"
+ "time"
+)
+
+var port string
+
+func main() {
+ ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
+ defer cancel()
+
+ log.SetLevel(log.DebugLevel)
+
+ if len(os.Args) > 1 {
+ port = os.Args[1]
+ } else {
+ port = serial.GetPorts()[0]
+ }
+ serialConn, err := serial.Connect(port)
+ if err != nil {
+ panic(err)
+ }
+ streamConn, err := transport.NewClientStreamConn(serialConn)
+ if err != nil {
+ panic(err)
+ }
+ defer func() {
+ if err := streamConn.Close(); err != nil {
+ panic(err)
+ }
+ }()
+
+ client := transport.NewClient(streamConn, false)
+ client.Handle(new(pb.MeshPacket), func(msg proto.Message) {
+ pkt := msg.(*pb.MeshPacket)
+ data := pkt.GetDecoded()
+ log.Info("Received message from radio", "msg", processMessage(*data), "from", pkt.From, "portnum", data.Portnum.String())
+ })
+ ctxTimeout, cancelTimeout := context.WithTimeout(ctx, 10*time.Second)
+ defer cancelTimeout()
+ if client.Connect(ctxTimeout) != nil {
+ panic("Failed to connect to the radio")
+ }
+
+ log.Info("Waiting for interrupt signal")
+ <-ctx.Done()
+}
+
+func processMessage(message pb.Data) string {
+ if message.Portnum == pb.PortNum_NODEINFO_APP {
+ var user = pb.User{}
+ proto.Unmarshal(message.Payload, &user)
+ return user.String()
+ }
+ if message.Portnum == pb.PortNum_POSITION_APP {
+ var pos = pb.Position{}
+ proto.Unmarshal(message.Payload, &pos)
+ return pos.String()
+ }
+ if message.Portnum == pb.PortNum_TELEMETRY_APP {
+ var t = pb.Telemetry{}
+ proto.Unmarshal(message.Payload, &t)
+ return t.String()
+ }
+ if message.Portnum == pb.PortNum_NEIGHBORINFO_APP {
+ var n = pb.NeighborInfo{}
+ proto.Unmarshal(message.Payload, &n)
+ return n.String()
+ }
+ if message.Portnum == pb.PortNum_STORE_FORWARD_APP {
+ var s = pb.StoreAndForward{}
+ proto.Unmarshal(message.Payload, &s)
+ return s.String()
+ }
+
+ return fmt.Sprintf("unknown message type")
+}
diff --git a/go.mod b/go.mod
index ec360e4..f63d124 100644
--- a/go.mod
+++ b/go.mod
@@ -3,18 +3,18 @@ module github.com/meshnet-gophers/meshtastic-go
go 1.21
require (
- github.com/charmbracelet/log v0.3.1
+ github.com/charmbracelet/log v0.4.0
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/planetscale/vtprotobuf v0.6.0
- github.com/stretchr/testify v1.8.4
+ github.com/stretchr/testify v1.9.0
go.bug.st/serial v1.6.2
golang.org/x/sync v0.6.0
- google.golang.org/protobuf v1.32.0
+ google.golang.org/protobuf v1.33.0
)
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
- github.com/charmbracelet/lipgloss v0.9.1 // indirect
+ github.com/charmbracelet/lipgloss v0.10.0 // indirect
github.com/creack/goselect v0.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
diff --git a/go.sum b/go.sum
index 1332046..74384df 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,9 @@
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
-github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
-github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
-github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw=
-github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
+github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
+github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
+github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
+github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -35,8 +35,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8=
go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo=
@@ -48,8 +48,8 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
-google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=