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