meshtastic: support
This commit is contained in:
159
protocol/meshtastic/packet.go
Normal file
159
protocol/meshtastic/packet.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package meshtastic
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
meshtasticpb "git.maze.io/go/ham/protocol/meshtastic/pb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
minPacketSize = 4 + 4 + 4 + 1 + 1 + 1 + 1
|
||||
maxPayloadSize = 237
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidPacket signals the source buffer does not contain a valid packet.
|
||||
ErrInvalidPacket = errors.New("meshtastic: invalid packet")
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
Destination NodeID `json:"destination"`
|
||||
Source NodeID `json:"source"`
|
||||
ID uint32 `json:"id"`
|
||||
Flags uint8 `json:"flags"`
|
||||
ChannelHash uint8 `json:"channelHash"`
|
||||
NextHop uint8 `json:"nextHop"`
|
||||
RelayNode uint8 `json:"relayNode"`
|
||||
PayloadLength int `json:"-"`
|
||||
Payload [maxPayloadSize]byte `json:"-"`
|
||||
Data *meshtasticpb.Data `json:"data,omitempty"`
|
||||
DecodedPayload proto.Message `json:"decodedPayload,omitempty"`
|
||||
TextPayload string `json:"textPayload,omitempty"`
|
||||
}
|
||||
|
||||
func (packet *Packet) Decode(data []byte) error {
|
||||
if len(data) < minPacketSize {
|
||||
return ErrInvalidPacket
|
||||
}
|
||||
if len(data[16:]) > maxPayloadSize {
|
||||
return ErrInvalidPacket
|
||||
}
|
||||
|
||||
packet.Source = parseNodeID(data[0:])
|
||||
packet.Destination = parseNodeID(data[4:])
|
||||
packet.ID = binary.LittleEndian.Uint32(data[8:])
|
||||
packet.Flags = data[12]
|
||||
packet.ChannelHash = data[13]
|
||||
packet.NextHop = data[14]
|
||||
packet.RelayNode = data[15]
|
||||
packet.PayloadLength = len(data[16:])
|
||||
copy(packet.Payload[:], data[16:])
|
||||
packet.Data = nil
|
||||
packet.DecodedPayload = nil
|
||||
packet.TextPayload = ""
|
||||
|
||||
packet.decodePayloadProtobufs()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (packet *Packet) PayloadBytes() []byte {
|
||||
return packet.Payload[:packet.PayloadLength]
|
||||
}
|
||||
|
||||
func (packet *Packet) HopLimit() int {
|
||||
return 7
|
||||
}
|
||||
|
||||
func parseNodeID(data []byte) NodeID {
|
||||
return NodeID(binary.LittleEndian.Uint32(data))
|
||||
}
|
||||
|
||||
func (packet *Packet) decodePayloadProtobufs() {
|
||||
if packet.PayloadLength == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
decodedData := &meshtasticpb.Data{}
|
||||
if err := proto.Unmarshal(packet.PayloadBytes(), decodedData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
packet.Data = decodedData
|
||||
|
||||
decodedPayload, textPayload := decodeDataPayload(decodedData.GetPortnum(), decodedData.GetPayload())
|
||||
packet.DecodedPayload = decodedPayload
|
||||
packet.TextPayload = textPayload
|
||||
}
|
||||
|
||||
func decodeDataPayload(portNum meshtasticpb.PortNum, payload []byte) (proto.Message, string) {
|
||||
if len(payload) == 0 {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
textPortNum := map[meshtasticpb.PortNum]struct{}{
|
||||
meshtasticpb.PortNum_TEXT_MESSAGE_APP: {},
|
||||
meshtasticpb.PortNum_ALERT_APP: {},
|
||||
meshtasticpb.PortNum_DETECTION_SENSOR_APP: {},
|
||||
meshtasticpb.PortNum_REPLY_APP: {},
|
||||
meshtasticpb.PortNum_RANGE_TEST_APP: {},
|
||||
meshtasticpb.PortNum_TEXT_MESSAGE_COMPRESSED_APP: {},
|
||||
}
|
||||
if _, ok := textPortNum[portNum]; ok {
|
||||
return nil, string(payload)
|
||||
}
|
||||
|
||||
var message proto.Message
|
||||
switch portNum {
|
||||
case meshtasticpb.PortNum_REMOTE_HARDWARE_APP:
|
||||
message = &meshtasticpb.HardwareMessage{}
|
||||
case meshtasticpb.PortNum_POSITION_APP:
|
||||
message = &meshtasticpb.Position{}
|
||||
case meshtasticpb.PortNum_NODEINFO_APP:
|
||||
message = &meshtasticpb.User{}
|
||||
case meshtasticpb.PortNum_ROUTING_APP:
|
||||
message = &meshtasticpb.Routing{}
|
||||
case meshtasticpb.PortNum_ADMIN_APP:
|
||||
message = &meshtasticpb.AdminMessage{}
|
||||
case meshtasticpb.PortNum_WAYPOINT_APP:
|
||||
message = &meshtasticpb.Waypoint{}
|
||||
case meshtasticpb.PortNum_KEY_VERIFICATION_APP:
|
||||
message = &meshtasticpb.KeyVerification{}
|
||||
case meshtasticpb.PortNum_PAXCOUNTER_APP:
|
||||
message = &meshtasticpb.Paxcount{}
|
||||
case meshtasticpb.PortNum_STORE_FORWARD_PLUSPLUS_APP:
|
||||
message = &meshtasticpb.StoreForwardPlusPlus{}
|
||||
case meshtasticpb.PortNum_STORE_FORWARD_APP:
|
||||
message = &meshtasticpb.StoreAndForward{}
|
||||
case meshtasticpb.PortNum_TELEMETRY_APP:
|
||||
message = &meshtasticpb.Telemetry{}
|
||||
case meshtasticpb.PortNum_TRACEROUTE_APP:
|
||||
message = &meshtasticpb.RouteDiscovery{}
|
||||
case meshtasticpb.PortNum_NEIGHBORINFO_APP:
|
||||
message = &meshtasticpb.NeighborInfo{}
|
||||
case meshtasticpb.PortNum_NODE_STATUS_APP:
|
||||
message = &meshtasticpb.StatusMessage{}
|
||||
default:
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
if err := proto.Unmarshal(payload, message); err != nil {
|
||||
return nil, ""
|
||||
}
|
||||
return message, ""
|
||||
}
|
||||
|
||||
// MarshalJSON implements custom JSON marshaling for Packet.
|
||||
func (packet *Packet) MarshalJSON() ([]byte, error) {
|
||||
type Alias Packet
|
||||
return json.Marshal(&struct {
|
||||
*Alias
|
||||
Raw []byte `json:"raw"`
|
||||
}{
|
||||
Alias: (*Alias)(packet),
|
||||
Raw: packet.PayloadBytes(),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user