From bf676663439caab77db2a8b6750123d4082665b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Setni=C4=8Dka=20Ji=C5=99=C3=AD?= Date: Fri, 16 Dec 2016 11:12:46 +0100 Subject: [PATCH] Pin handling rewrite: Updated tests and default configuration, small bugfixes --- pyfirmata/boards.py | 8 ++--- pyfirmata/pyfirmata.py | 71 +++++++++++++++++++++++++++++++----------- tests.py | 22 ++++++++----- 3 files changed, 71 insertions(+), 30 deletions(-) diff --git a/pyfirmata/boards.py b/pyfirmata/boards.py index ca5adad1..2fee0646 100644 --- a/pyfirmata/boards.py +++ b/pyfirmata/boards.py @@ -1,28 +1,28 @@ BOARDS = { 'arduino': { 'digital': tuple(x for x in range(14)), - 'analog': tuple(x for x in range(6)), + 'analog_real': tuple(14 + x for x in range(6)), 'pwm': (3, 5, 6, 9, 10, 11), 'use_ports': True, 'disabled': (0, 1) # Rx, Tx, Crystal }, 'arduino_mega': { 'digital': tuple(x for x in range(54)), - 'analog': tuple(x for x in range(16)), + 'analog_real': tuple(54 + x for x in range(16)), 'pwm': tuple(x for x in range(2, 14)), 'use_ports': True, 'disabled': (0, 1) # Rx, Tx, Crystal }, 'arduino_due': { 'digital': tuple(x for x in range(54)), - 'analog': tuple(x for x in range(12)), + 'analog_real': tuple(54 + x for x in range(12)), 'pwm': tuple(x for x in range(2, 14)), 'use_ports': True, 'disabled': (0, 1) # Rx, Tx, Crystal }, 'arduino_nano': { 'digital': tuple(x for x in range(14)), - 'analog': tuple(x for x in range(8)), + 'analog_real': tuple(14 + x for x in range(8)), 'pwm': (3, 5, 6, 9, 10, 11), 'use_ports': True, 'disabled': (0, 1) # Rx, Tx, Crystal diff --git a/pyfirmata/pyfirmata.py b/pyfirmata/pyfirmata.py index 8d1f7dd8..c27700b2 100755 --- a/pyfirmata/pyfirmata.py +++ b/pyfirmata/pyfirmata.py @@ -161,12 +161,6 @@ def setup_layout(self, board_layout): # Create pin instances based on board layout self.pins = [] - # Analog: - self.analog = [] - self._setup_pins(board_layout['analog'], ANALOG, append_to=self.analog, default_res=10) - for i, a in enumerate(self.analog): - a.analog_pin_number = i - # Digital input: if 'digital_input' not in board_layout: board_layout['digital_input'] = board_layout['digital'] @@ -181,6 +175,16 @@ def setup_layout(self, board_layout): for p in sorted(set(board_layout['digital_input'] + board_layout['digital_output'])): self.digital.append(self.pins[p]) + # Analog: + self.analog = [] + if 'analog_real' not in board_layout: + board_layout['analog_real'] = [len(self.digital) + x for x in board_layout['analog']] + self._setup_pins( + board_layout['analog_real'], ANALOG, append_to=self.analog, default_res=10 + ) + for i, a in enumerate(self.analog): + a.analog_pin_number = i + # Setup pins into ports self.digital_ports = [] for i in range(0, len(self.digital), 8): @@ -189,13 +193,15 @@ def setup_layout(self, board_layout): if 'pwm' in board_layout: self._setup_pins(board_layout['pwm'], PWM, default_res=8) - if 'servo' in board_layout: - self._setup_pins(board_layout['servo'], SERVO, default_res=14) + + # Is that assumption true? + if 'servo' not in board_layout: + board_layout['servo'] = board_layout['digital_output'] + self._setup_pins(board_layout['servo'], SERVO, default_res=14) # Disable certain ports like Rx/Tx and crystal ports for i in board_layout['disabled']: - if i < len(self.pins): - self.pins[i].mode = UNAVAILABLE + self._get_pin(i).mode = UNAVAILABLE self._set_default_handlers() @@ -422,7 +428,7 @@ def _handle_report_capability_response(self, *data): board_dict = { 'digital_input': [], 'digital_output': [], - 'analog': [], + 'analog_real': [], 'pwm': [], 'servo': [], # 2.2 specs 'i2c': [], # 2.3 specs @@ -447,7 +453,7 @@ def _handle_report_capability_response(self, *data): elif mode == OUTPUT and val == 1: board_dict['digital_output'].append(pin) elif mode == ANALOG: - board_dict['analog'].append((pin, val)) + board_dict['analog_real'].append((pin, val)) elif mode == PWM: board_dict['pwm'].append((pin, val)) elif mode == SERVO: @@ -463,6 +469,7 @@ def _handle_report_capability_response(self, *data): class Port(object): """An 8-bit port on the board.""" + def __init__(self, board, port_number, pins): self.board = board self.port_number = port_number @@ -521,7 +528,7 @@ def __init__(self, board, pin_number, port=None, analog_pin_number=None, support self.pin_number = pin_number self.analog_pin_number = analog_pin_number self.port = port - self.supported_modes = supported_modes + self.supported_modes = supported_modes[:] self._mode = None # Until first usage self.reporting = False self.taken = False @@ -540,7 +547,7 @@ def _set_mode(self, mode): if self.mode is UNAVAILABLE: raise IOError("{0} can not be used through Firmata".format(self)) if mode not in self.supported_modes: - raise IOError("{0} does not support {0} mode".format(modes_names[mode])) + raise IOError("{0} does not support {1} mode".format(self, modes_names[mode])) if mode == SERVO: self.board.servo_config(self.pin_number) return @@ -557,26 +564,54 @@ def _get_mode(self): mode = property(_get_mode, _set_mode) """ Mode of operation for the pin. Can be one of the pin modes: INPUT, OUTPUT, - ANALOG, PWM. or SERVO (or UNAVAILABLE). + ANALOG, PWM or SERVO (or UNAVAILABLE). + """ + + def _get_pwm_capable(self): + return self.is_supported(PWM) + + def _set_pwm_capable(self, pwm): + self.supported_modes.remove(PWM) + if pwm: + self.supported_modes.append(PWM) + + PWM_CAPABLE = property(_get_pwm_capable, _set_pwm_capable) + """ + Deprecated property for backward compatibility. Use + pin.is_supported(PWM) + instead of this. """ + def is_supported(self, mode): + if self.mode == UNAVAILABLE: + return False + return (mode in self.supported_modes) + def enable_reporting(self): """Set an input pin to report values.""" + # Auto set pin mode if pin was not activated yet (analog capable pin as + # ANALOG, INPUT otherwise) + if self.mode is None: + if self.is_supported(ANALOG): + self.mode = ANALOG + elif self.is_supported(INPUT): + self.mode = INPUT + if self.mode == ANALOG: self.reporting = True - msg = bytearray([REPORT_ANALOG + self.pin_number, 1]) + msg = bytearray([REPORT_ANALOG + self.analog_pin_number, 1]) self.board.sp.write(msg) elif self.mode == INPUT: self.port.enable_reporting() # TODO This is not going to work for non-optimized boards like Mega else: - raise IOError("{0} is not an input and can therefore not report".format(self)) + raise IOError("{0} is not set as input and can therefore not report".format(self)) def disable_reporting(self): """Disable the reporting of an input pin.""" if self.mode == ANALOG: self.reporting = False - msg = bytearray([REPORT_ANALOG + self.pin_number, 0]) + msg = bytearray([REPORT_ANALOG + self.analog_pin_number, 0]) self.board.sp.write(msg) else: self.port.disable_reporting() diff --git a/tests.py b/tests.py index 9d3b51ad..d1c9bbd4 100644 --- a/tests.py +++ b/tests.py @@ -132,12 +132,13 @@ def test_handle_capability_response(self): """ test_layout = { - 'digital': (0, 1, 2), - 'analog': (0, 1), - 'pwm': (1, 2), - 'servo': (0, 1, 2), + 'digital_input': [1, 2, 3, 4], + 'digital_output': [1, 2, 3, 4], + 'analog_real': [(3, 10), (4, 10)], + 'pwm': [(1, 8), (2, 8)], + 'servo': [(1, 14), (2, 14)], # 'i2c': (2), # TODO 2.3 specs - 'disabled': (0,), + 'disabled': [0], } # Eg: (127) @@ -153,6 +154,8 @@ def test_handle_capability_response(self): 0x01, 0x03, # PWM 0x08, + 0x04, # SERVO + 0x0E, 0x7F, # END_SYSEX (Pin delimiter) ] @@ -233,6 +236,8 @@ def test_incoming_report_firmware(self): # --------------------------------------------------------------------------- # report analog pin 0xC0 pin # disable/enable(0/1) - n/a - def test_report_analog(self): + self.board.analog[1].mode = pyfirmata.ANALOG + self.assert_serial(0xF4, self.board.analog[1].pin_number, pyfirmata.ANALOG) self.board.analog[1].enable_reporting() self.assert_serial(0xC0 + 1, 1) self.assertTrue(self.board.analog[1].reporting) @@ -353,13 +358,13 @@ class TestBoardLayout(BoardBaseTest): def test_layout_arduino(self): self.assertEqual(len(BOARDS['arduino']['digital']), len(self.board.digital)) - self.assertEqual(len(BOARDS['arduino']['analog']), len(self.board.analog)) + self.assertEqual(len(BOARDS['arduino']['analog_real']), len(self.board.analog)) def test_layout_arduino_mega(self): pyfirmata.pyfirmata.serial.Serial = mockup.MockupSerial mega = pyfirmata.Board('', BOARDS['arduino_mega']) self.assertEqual(len(BOARDS['arduino_mega']['digital']), len(mega.digital)) - self.assertEqual(len(BOARDS['arduino_mega']['analog']), len(mega.analog)) + self.assertEqual(len(BOARDS['arduino_mega']['analog_real']), len(mega.analog)) def test_pwm_layout(self): pins = [] @@ -380,7 +385,8 @@ def test_get_pin_digital(self): def test_get_pin_analog(self): pin = self.board.get_pin('a:5:i') - self.assertEqual(pin.pin_number, 5) + self.assertEqual(pin.analog_pin_number, 5) + self.assertEqual(pin.pin_number, 19) self.assertEqual(pin.reporting, True) self.assertEqual(pin.value, None)