Skip to content

Commit 2596f74

Browse files
committed
Added support to average and bulk reads in protocol
Issue #22
1 parent 924bc32 commit 2596f74

File tree

2 files changed

+84
-38
lines changed

2 files changed

+84
-38
lines changed

MLC/arduino/protocol.py

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
_PROTOCOL_CMDS = { "ANALOG_PRECISION": '\x01\x01%s',
3-
"SET_INPUT" : '\x02\x01%s',
4-
"SET_OUTPUT" : '\x03\x01%s',
3+
"SET_INPUT" : '\x02\x01%s',
4+
"SET_OUTPUT" : '\x03\x01%s',
55
"PIN_MODE" : '\x04\x02%s%s',
66
"REPORT_MODE" : '\x05\x03%s%s%s',
77
"ACK" : '\xFF\x00',
@@ -21,11 +21,11 @@ def __init__(self, connection, board):
2121
self._digital_outputs = []
2222
self._anlg_precition = 10 #Default Arduino analog precision
2323
self._report_mode = "AVERAGE"
24-
self._read_count = 1 #Default number of inputs read
24+
self._read_count = 0 #Default number of inputs read
2525
self._read_delay = 0
2626
self._board = board
2727

28-
def set_precition(self, bits):
28+
def set_precision(self, bits):
2929
if bits > 32 or bits < 1:
3030
raise Exception("Precision bits must be between 1 and 32!")
3131
self._connection.send(_PROTOCOL_CMDS["ANALOG_PRECISION"] % chr(bits))
@@ -37,53 +37,58 @@ def __set_pin_mode(self, port, mode):
3737

3838
self._connection.send(_PROTOCOL_CMDS["PIN_MODE"] % (chr(port), chr(self.PIN_MOD[mode])))
3939

40-
def set_report_mode(self, mode, read_count=0, read_delay=0):
40+
def set_report_mode(self, mode, read_count=1, read_delay=0):
4141
if mode not in self.REPORT_MOD.keys():
4242
raise Exception("Report mode error. Unknown value: %s" % mode)
4343

44+
if read_count <= 0:
45+
raise Exception("Read count value must be >= 0")
46+
4447
self._report_mode = mode
45-
self._read_count = read_count + 1
48+
self._read_count = read_count - 1
4649
self._read_delay = read_delay
4750

48-
self._connection.send(_PROTOCOL_CMDS["REPORT_MODE"] % (chr(self.REPORT_MOD[mode]), chr(read_count), chr(read_delay)))
51+
self._connection.send(_PROTOCOL_CMDS["REPORT_MODE"] % (chr(self.REPORT_MOD[mode]), chr(self._read_count), chr(self._read_delay)))
4952

5053

5154
def add_input(self, port):
5255
if port in self._anlg_outputs or port in self._digital_outputs:
5356
raise Exception("Pin %s is configured as output!" % port)
5457

5558
self.__validate_pin(port)
56-
self._connection.send(_PROTOCOL_CMDS["SET_INPUT"] % chr(port))
57-
self.__set_pin_mode(port, "INPUT")
5859

5960
# Determines if we are setting as input an analog port
6061
if port not in self._anlg_inputs and port in self._board["ANALOG_PINS"]:
6162
self._anlg_inputs.append(port)
63+
self._connection.send(_PROTOCOL_CMDS["SET_INPUT"] % chr(port))
64+
self.__set_pin_mode(port, "INPUT")
6265

6366
# Determines if we are setting as input a Digital port
6467
if port not in self._digital_inputs and port in self._board["DIGITAL_PINS"]:
6568
self._digital_inputs.append(port)
66-
69+
self._connection.send(_PROTOCOL_CMDS["SET_INPUT"] % chr(port))
70+
self.__set_pin_mode(port, "INPUT")
6771

6872
def add_output(self, port):
6973
if port in self._anlg_inputs or port in self._digital_inputs:
7074
raise Exception("Port %s is configured as input!" % port)
7175

7276
self.__validate_pin(port)
73-
self._connection.send(_PROTOCOL_CMDS["SET_OUTPUT"] % chr(port))
74-
self.__set_pin_mode(port, "OUTPUT")
7577

7678
if port not in self._anlg_outputs and port in self._board["ANALOG_PINS"]:
7779
self._anlg_outputs.append(port)
80+
self._connection.send(_PROTOCOL_CMDS["SET_OUTPUT"] % chr(port))
81+
self.__set_pin_mode(port, "OUTPUT")
7882

7983
# Determines if we are setting as input a Digital port
8084
if port not in self._digital_outputs and port in self._board["DIGITAL_PINS"]:
8185
self._digital_outputs.append(port)
86+
self._connection.send(_PROTOCOL_CMDS["SET_OUTPUT"] % chr(port))
87+
self.__set_pin_mode(port, "OUTPUT")
8288

8389
def __validate_pin(self, pin):
8490
if pin not in self._board["DIGITAL_PINS"] and pin not in self._board["ANALOG_PINS"]:
8591
raise Exception("Invalid pin %s for board %s" % (pin, self._board["NAME"]))
86-
return
8792

8893
def reset(self):
8994
self._connection.send(_PROTOCOL_CMDS["RESET"])
@@ -124,16 +129,27 @@ def actuate(self, data):
124129
data = self._connection.recv(length)
125130

126131
pos = 0
127-
results = []
132+
results = {x: [] for x in self._anlg_inputs + self._digital_inputs} # One dictionary to save all ports results
133+
128134
while pos < length:
129-
if ord(data[pos]) in self._anlg_inputs:
130-
results.append((data[pos], (ord(data[pos+1]) << 8) + ord(data[pos+2])))
131-
pos = pos + 3
132-
else:
133-
if ord(data[pos]) in self._digital_inputs:
134-
for i in range(0, self._read_count):
135-
results.append((data[pos], bool(ord(data[pos+1]))))
135+
pin = ord(data[pos])
136+
if pin in self._anlg_inputs:
137+
for i in range(0, self._read_count + 1):
138+
results[pin].append((ord(data[pos+1]) << 8) + ord(data[pos+2]))
136139
pos = pos + 2
140+
pos = pos + 1
141+
142+
if self._report_mode == "AVERAGE":
143+
results[pin] = [sum(results[pin]) / (self._read_count + 1)]
144+
else:
145+
if pin in self._digital_inputs:
146+
for i in range(0, self._read_count + 1):
147+
results[pin].append(bool(ord(data[pos + 1 + i/8]) & (0x80 >> (i % 8))))
148+
pos = pos + 1 + self._read_count/8 + 1
149+
150+
if self._report_mode == "AVERAGE":
151+
results[pin] = [ (sum(results[pin]) * 2) > (self._read_count + 1) ]
152+
137153
else:
138154
raise Exception("Unknown port in response. Restart Arduino board, your software and pray")
139155

tests/mlc/arduino_protocol/test_protocol.py

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
ACK = "\xFF\x00"
77
NACK = "\xFF\x01"
8-
REPORT = "\xF1\x05\x10\x01\x3D\x05\x20"
8+
REPORT = "\xF1\x05\x10\x80\x3D\x05\x20"
9+
REPORT_B = "\xF1\x1A\x10\xF1\xA0\x3D\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00"
910

1011

1112
class TestArduinoInterface(unittest.TestCase):
@@ -15,33 +16,28 @@ def setUp(self):
1516
self._interface = ArduinoInterface(self._connection, boards.Due)
1617

1718
def test_set_precition(self):
18-
self._interface.set_precition(12)
19+
self._interface.set_precision(12)
1920
data = self._connection.pop_data()
2021
self.assertEqual("\x01\x01\x0C", data)
2122
with self.assertRaises(Exception):
2223
self._interface.set_precition(33)
2324

24-
def test_set_pin_mode(self):
25-
self._interface.set_pin_mode(1, "INPUT")
26-
data = self._connection.pop_data()
27-
self.assertEqual("\x04\x02\x01\x00", data)
28-
with self.assertRaises(Exception):
29-
self._interface.set_pin_mode(1, "SOMETHING")
30-
3125
def test_report_mode(self):
3226
self._interface.set_report_mode("AVERAGE")
3327
data = self._connection.pop_data()
34-
self.assertEqual("\x05\x03\x00\x01\x00", data)
28+
self.assertEqual("\x05\x03\x00\x00\x00", data)
3529
self._interface.set_report_mode("AVERAGE", read_count=10, read_delay=5)
3630
data = self._connection.pop_data()
37-
self.assertEqual("\x05\x03\x00\x0A\x05", data)
31+
self.assertEqual("\x05\x03\x00\x09\x05", data)
3832
with self.assertRaises(Exception):
3933
self._interface.set_report_mode("SOMETHING")
4034

4135
def test_add_input(self):
4236
self._interface.add_input(60)
4337
data = self._connection.pop_data()
44-
self.assertEqual("\x02\x01\x06", data) # 6 = 60 - min(boards.Due["ANALOG_PINS"])
38+
self.assertEqual("\x02\x01\x3C", data)
39+
data = self._connection.pop_data()
40+
self.assertEqual("\x04\x02\x3C\x00", data)
4541
self._interface.add_input(60)
4642
with self.assertRaises(Exception): # Checks that no data has been sent
4743
data = self._connection.pop_data()
@@ -52,6 +48,8 @@ def test_add_output(self):
5248
self._interface.add_output(60)
5349
data = self._connection.pop_data()
5450
self.assertEqual("\x03\x01\x3C", data)
51+
data = self._connection.pop_data()
52+
self.assertEqual("\x04\x02\x3C\x01", data)
5553
self._interface.add_output(60)
5654
with self.assertRaises(Exception): # Checks that no data has been sent
5755
data = self._connection.pop_data()
@@ -69,16 +67,48 @@ def test_actuate_error_response(self):
6967
with self.assertRaises(Exception):
7068
self._interface.actuate([(60, 128)])
7169

72-
def test_actuate(self):
70+
def test_actuate_with_one_read(self):
7371
self._connection = MockConnection(REPORT)
7472
self._interface = ArduinoInterface(self._connection, boards.Due)
7573
self._interface.add_output(60)
74+
self._interface.add_input(16)
75+
self._interface.add_input(61)
76+
response = self._interface.actuate([(60, 128)])
77+
self.assertEqual(1, len(response[0x10]))
78+
self.assertTrue(response[0x10][0])
79+
self.assertEqual(1, len(response[0x3D]))
80+
self.assertEqual(0x0520,response[0x3D][0])
81+
82+
def test_actuate_with_many_readings(self):
83+
self._connection = MockConnection(REPORT_B)
84+
self._interface = ArduinoInterface(self._connection, boards.Due)
85+
self._interface.set_report_mode("BULK", read_count=11, read_delay=5)
86+
self._interface.add_output(60)
87+
self._interface.add_input(61)
88+
self._interface.add_input(16)
89+
response = self._interface.actuate([(60, 128)])
90+
self.assertEqual(2, len(response))
91+
self.assertTrue(response[0x10][0])
92+
self.assertFalse(response[0x10][6])
93+
self.assertTrue(response[0x10][7])
94+
self.assertTrue(response[0x10][8])
95+
self.assertFalse(response[0x10][9])
96+
self.assertTrue(response[0x10][10])
97+
for i in range(0, 11):
98+
self.assertEqual(0x0100, response[0x3D][i])
99+
100+
101+
def test_average(self):
102+
self._connection = MockConnection(REPORT_B)
103+
self._interface = ArduinoInterface(self._connection, boards.Due)
104+
self._interface.set_report_mode("AVERAGE", read_count=11, read_delay=5)
105+
self._interface.add_output(60)
76106
self._interface.add_input(61)
107+
self._interface.add_input(16)
77108
response = self._interface.actuate([(60, 128)])
78109
self.assertEqual(2, len(response))
79-
self.assertEqual("\x10",response[0][0])
80-
self.assertTrue(response[0][1])
81-
self.assertEqual("\x3D",response[1][0])
82-
self.assertEqual(0x0520,response[1][1])
110+
self.assertEqual(1, len(response[0x10]))
111+
self.assertTrue(response[0x10][0])
112+
self.assertEqual(0x0100, response[0x3D][0])
83113

84114

0 commit comments

Comments
 (0)