summaryrefslogtreecommitdiff
path: root/protocal/proto.go
blob: 05a2d90a154d9969a0aed666b3a6e19abfe10a02 (plain)
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// +build !windows

/* SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2020 jet tsang zeon-git. All Rights Reserved.
 */
package protocal

import (
	"bytes"
	"fmt"
	"io"
)

const (
	FrameHeader byte = 0x55
	// Send an instruction to all servos
	BroadcastIdent int = 0xFE // 254
)

type Proto struct {
	Network io.ReadWriter
}

func New(network io.ReadWriter) *Proto {
	return &Proto{
		Network: network,
	}
}

func (p *Proto) writeInstruction(ident int, instruction byte, params []byte) error {
	buf := new(bytes.Buffer)
	id := byte(ident & 0xFF)
	pLen := len(params) + 3

	// +------+------+----+---------------+-------------+--------+-----+--------+----------+
	// | 0x55 | 0x55 | ID |      LEN      |     INST    | Param1 | ... | ParamN |    CKS   |
	// +------+------+----+---------------+-------------+--------+-----+--------+----------+
	// |    Header   | ID | Packet Length | Instruction |       Parameter       | Checksum |
	// +-------------+----+---------------+-------------+-----------------------+----------+

	buf.Write([]byte{
		FrameHeader, // Header
		FrameHeader, // Header
		id,          // target ID
		byte(pLen),  // Length
		instruction, // instruction type (see const section)
	})

	// append n params
	buf.Write(params)

	// calculate checksum
	b := CheckSum(buf.Bytes()[2:]) // Checksum = ~ (ID + Length + INST + Prm1 + ... PrmN)
	buf.WriteByte(byte(b))

	// write to port
	_, err := buf.WriteTo(p.Network)
	if err != nil {
		return err
	}

	return nil
}

func (p *Proto) readPacket(expID int) ([]byte, error) {

	// +------+------+----+---------------+-------------+--------+-----+--------+----------+
	// | 0x55 | 0x55 | ID |      LEN      |     INST    | Param1 | ... | ParamN |    CKS   |
	// +------+------+----+---------------+-------------+--------+-----+--------+----------+
	// |    Header   | ID | Packet Length | Instruction |       Parameter       | Checksum |
	// +-------------+----+---------------+-------------+-----------------------+----------+
	// Read the first nine bytes (up to Error), which should always be present.

	buf := make([]byte, 5)
	n, err := p.Network.Read(buf)
	if err != nil {
		return nil, fmt.Errorf("reading packet header: %s", err)
	}
	if n != 9 {
		return nil, fmt.Errorf("reading packet header: expected %d bytes, got %d", 9, n)
	}

	if buf[0] != FrameHeader || buf[1] != FrameHeader {
		return nil, fmt.Errorf("bad status packet header: 0x%02X 0x%02X ", buf[0], buf[1])
	}

	actID := int(buf[2])
	plen := int(buf[4]) - 3

	// Now read the params, if there are any. We must do this before checking
	// for errors, to avoid leaving junk in the buffer.

	var pbuf []byte
	if plen > 0 {
		pbuf = make([]byte, plen)
		_, err = p.Network.Read(pbuf)
		if err != nil {
			return nil, fmt.Errorf("reading %d params: %s", plen, err)
		}
	}

	// Read the checksum, which is always two bytes.

	buf = make([]byte, 1)
	n, err = p.Network.Read(buf)
	if err != nil {
		return nil, fmt.Errorf("reading checksum: %s", err)
	}
	if n != 2 {
		return nil, fmt.Errorf("reading checksum: expected %d bytes, got %d", 2, n)
	}

	if actID != expID {
		return nil, fmt.Errorf("expected status packet for %v, but got %v", expID, actID)
	}

	return pbuf, nil
}

// ReadData reads a slice of n bytes from the control table of the given servo
// ID. Use the bytesToInt function to convert the output to something more
// useful.
func (p *Proto) ReadData(ident int, instruction byte) ([]byte, error) {

	err := p.writeInstruction(ident, instruction, nil)
	if err != nil {
		return []byte{}, err
	}

	buf, err := p.readPacket(ident)
	if err != nil {
		return buf, err
	}

	return buf, nil
}

func (p *Proto) WriteData(ident int, instruction byte, data []byte) error {
	return p.writeInstruction(ident, instruction, data)
}