Skip to content

Instantly share code, notes, and snippets.

@wheresalice
Last active April 16, 2024 19:39
Show Gist options
  • Save wheresalice/26b0902f407bec1bd47f5db17cc65824 to your computer and use it in GitHub Desktop.
Save wheresalice/26b0902f407bec1bd47f5db17cc65824 to your computer and use it in GitHub Desktop.
Decode meshtastic messages from mqtt using meshtastic-go
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)
}
@renanbastos93
Copy link

Do you have example to reply msg receive in this handle?

@wheresalice
Copy link
Author

No I don't. This could probably be made easier by adding some helper functions to the library, especially for text messages.

But essentially, you'll want to:

  1. move client out to a separate var (var client *mqtt.Client) so that it's available outside of main()
  2. do all the serializing of protobufs to byte[] arrays that generating an mqtt.Message requires
  3. use client.Publish() to publish the mqtt.Message

@renanbastos93
Copy link

Ok, but in Meshtastic MQTT does not work that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment