aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorWheresAlice <[email protected]>2024-04-08 19:27:53 +0100
committerWheresAlice <[email protected]>2024-04-08 19:27:53 +0100
commitb6d925cca7d9bb8b9ca264d85fa86da938d4c30d (patch)
treed348d191a54bcee8798608051d9afcc56d5222b7 /examples
parent212cf4831ca61c57e07df5635f41ea75354cdbea (diff)
provide mqtt example and document radio example
Diffstat (limited to 'examples')
-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
4 files changed, 190 insertions, 0 deletions
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")
+}