-
-
Notifications
You must be signed in to change notification settings - Fork 27
/
serial-wrapper.go
102 lines (79 loc) · 2.09 KB
/
serial-wrapper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package client
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/kjx98/crc16"
"github.com/simpleiot/simpleiot/data"
"github.com/simpleiot/simpleiot/internal/pb"
"google.golang.org/protobuf/proto"
)
// Packet format is:
// - sequence #: 1 byte
// - subject (16 bytes)
// - protobuf (serial) payload
// - crc: 2 bytes (CCITT)
// SerialEncode can be used in a client to encode points sent over a serial link.
func SerialEncode(seq byte, subject string, points data.Points) ([]byte, error) {
var ret bytes.Buffer
ret.WriteByte(seq)
sub := make([]byte, 16)
if len(subject) > 16 {
return []byte{},
fmt.Errorf("SerialEncode Error: length of subject %v is longer than 20 bytes", subject)
}
copy(sub, []byte(subject))
_, err := ret.Write(sub)
if err != nil {
return []byte{}, fmt.Errorf("SerialEncode: error writing to buffer: %v", err)
}
pbPoints := make([]*pb.SerialPoint, len(points))
for i, p := range points {
pPb, err := p.ToSerial()
if err != nil {
return nil, err
}
pbPoints[i] = &pPb
}
pbSerial := &pb.SerialPoints{
Points: pbPoints,
}
pbSerialBytes, err := proto.Marshal(pbSerial)
if err != nil {
return nil, err
}
ret.Write(pbSerialBytes)
crc := crc16.ChecksumCCITT(ret.Bytes())
err = binary.Write(&ret, binary.LittleEndian, crc)
return ret.Bytes(), err
}
// SerialDecode can be used to decode serial data in a client.
// returns seq, subject, payload
func SerialDecode(d []byte) (byte, string, []byte, error) {
l := len(d)
if l < 1 {
return 0, "", nil, errors.New("Not enough data")
}
if l < (1 + 16) {
return d[0], "", nil, errors.New("Not enough data")
}
// try to extract subject
subject := string(bytes.Trim(d[1:17], "\x00"))
end := l
if subject != "log" {
// check CRC
end -= 2
if len(d) < (1 + 2 + 16) {
return d[0], "", nil, errors.New("Not enough data")
}
crc := binary.LittleEndian.Uint16(d[l-2:])
crcCalc := crc16.ChecksumCCITT(d[:l-2])
if crc != crcCalc {
return d[0], "", nil, errors.New("CRC check failed")
}
}
// try to extract protobuf
payload := d[17:end]
return d[0], subject, payload, nil
}