Skip to content

Commit

Permalink
Pin handling rewrite: Updated tests and default configuration, small …
Browse files Browse the repository at this point in the history
…bugfixes
  • Loading branch information
setnicka committed Dec 16, 2016
1 parent 7d55059 commit bf67666
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 30 deletions.
8 changes: 4 additions & 4 deletions pyfirmata/boards.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
71 changes: 53 additions & 18 deletions pyfirmata/pyfirmata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand All @@ -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):
Expand All @@ -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()

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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()
Expand Down
22 changes: 14 additions & 8 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -153,6 +154,8 @@ def test_handle_capability_response(self):
0x01,
0x03, # PWM
0x08,
0x04, # SERVO
0x0E,
0x7F, # END_SYSEX (Pin delimiter)
]

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 = []
Expand All @@ -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)

Expand Down

0 comments on commit bf67666

Please sign in to comment.