forked from nikwl/pyduino-lite
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprotocol.py
More file actions
205 lines (181 loc) · 6.94 KB
/
Copy pathprotocol.py
File metadata and controls
205 lines (181 loc) · 6.94 KB
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
from enum import Enum
import struct
class Order(Enum):
'''
Order: Use this to name the orders, or operations, that the
arduino can perform.
'''
HELLO = 0
ALREADY_CONNECTED = 1
ERROR = 2
SUCCESS = 3
POSITION_MOVE = 4
VELOCITY_MOVE = 5
QUERY_DEVICE = 6
class Error(Enum):
'''
Error: Use this to name the possible errors that the arduino
can return.
'''
CORRUPTION = 0
INVALID_COMMAND = 1
INVALID_ORDER = 2
INVALID_DEVICE = 3
class CommandIndexer(Enum):
'''
CommandIndexer: Use this to name the indices of outgoing commands.
For example:
[INC_ORDER, INC_INDEX, INC_VEL, INC_POS, INC_CHECKSUM] = response
'''
CMD_ORDER = 0
CMD_INDEX = 1
CMD_VEL = 2
CMD_POS = 3
CMD_CHECKSUM = 4
class ResponseIndexer(Enum):
'''
ResponseIndexer: Use this to name the indices of incoming responses.
For example:
[OUT_ORDER, OUT_DATA, OUT_CHECKSUM] = response
'''
RESP_ORDER = 0
RESP_DATA = 1
RESP_CHECKSUM = 2
def write_command(serial_file, command):
'''
write_command: Called by PyDuino when sending a command. Must
correspond to the read_command function on the arduino. Change this
and the arduino read_command function to update the outgoing
protocol. For example, the current protocol parses commands of the
form:
[Order (int8),
Device Index (int8),
Motor Velocity (int16),
Motor Position (int16),
Checksum (int16)].
Notes:
1) The command will be exactly the same as whatever is passed
to interface.send() EXCEPT it will have a checksum appended.
For example:
PyDuino.send([1, 2, 3, 4])
would result in the command array:
1, 2, 3, 4, -11 = command
^
checksum
2) PyDuino doesn't care how many elements are in the array,
so long as the first element is the Order and the second
element is the device index. Any command of the form:
[Order, Device Index, ...]
is valid, and can be parsed here.
3) Data types in this case are important. The following are
available to choose from:
int8, int16, int32
and have corresponding read and write functions defined
below.
4) Assuming that you are not manually handling checksums on the
arduino side, the checksum MUST be passed last.
Inputs:
serial_file: serial object
The arduino serial connection.
command: [int]
A list of integers to send to the arduino.
'''
# Here's a bit of parsing that lets commands of the form [Order, Device Index] slide
if (len(command) == 3):
order, device_index, checksum = command
# Note: you can ONLY pad with zeros to maintain integrity of checksum
motor_velocity, motor_position = 0, 0
else:
order, device_index, motor_velocity, motor_position, checksum = command
write_i8(serial_file, order)
write_i8(serial_file, device_index)
write_i16(serial_file, motor_velocity)
write_i16(serial_file, motor_position)
write_i16(serial_file, checksum)
def read_response(serial_file):
'''
read_response: Called by PyDuino when getting a response from the
arduino. Must correspond to the write_command function on the
arduino. Change this and the arduino write_command function to
update the incoming protocol. For example the current protocol
parses responses of the form:
[Order (int8),
Data (int16),
Checksum (int16)]
Notes:
1) PyDuino doesn't care how many elements are in the array,
that is returned so long as it has a length of at least 3
(This is required to detect response corruption.) The
response, minus the checksum, will be stored in the
corresponding device object. For example if the response
had a length of three:
1, 2, -4 = [order, value, checksum]
Reading from that device would return:
PyDuino.read_data(device=0)
>>> [1, 2]
2) Assuming that you are not manually handling checksums on the
python side, the checksum MUST be passed last.
3) Though can return almost any list of values from the
arduino, DO NOT return a list with the first value equal to
Order.Error. This is a special value used to detect message
corruption:
if (...
resp[OutCommandIndexer.OUT_ORDER.value] == Order.ERROR.value and
resp[OutCommandIndexer.OUT_DATA.value] == Error.CORRUPTION.value)):
# Message has been corrupted on arduino side
Inputs:
serial_file: serial object
The arduino serial connection.
'''
order = read_i8(serial_file)
value = read_i16(serial_file)
checksum = read_i16(serial_file)
return [order, value, checksum]
def read_order(ser):
'''
:param ser: file handler or serial file
:return: (Order Enum Object)
'''
return Order(read_i8(ser))
def read_i8(ser):
'''
:param ser: file handler or serial file
:return: (int8_t)
'''
return struct.unpack('<b', bytearray(ser.read(1)))[0]
def read_i16(ser):
'''
:param ser: file handler or serial file
:return: (int16_t)
'''
return struct.unpack('<h', bytearray(ser.read(2)))[0]
def read_i32(ser):
'''
:param ser: file handler or serial file
:return: (int32_t)
'''
return struct.unpack('<l', bytearray(ser.read(4)))[0]
def write_i8(ser, value):
'''
:param ser: file handler or serial file
:param value: (int8_t)
'''
ser.write(struct.pack('<b', value))
def write_order(ser, order):
'''
:param ser: file handler or serial file
:param order: (Order Enum Object)
'''
write_i8(ser, order.value)
def write_i16(ser, value):
'''
:param ser: file handler or serial file
:param value: (int16_t)
'''
ser.write(struct.pack('<h', value))
def write_i32(ser, value):
'''
:param ser: file handler or serial file
:param value: (int32_t)
'''
ser.write(struct.pack('<l', value))