Tino de Bruijn avatar Tino de Bruijn committed 211ec0b

Fix board layouts. Before there could be more pins than specified. With tests

Comments (0)

Files changed (3)

pyfirmata/boards.py

         'analog' : tuple(x for x in range(6)),
         'pwm' : (3, 5, 6, 9, 10, 11),
         'use_ports' : True,
-        'disabled' : (0, 1, 14, 15) # Rx, Tx, Crystal
+        'disabled' : (0, 1) # Rx, Tx, Crystal
     },
     'arduino_mega' : {
         'digital' : tuple(x for x in range(54)),
         'analog' : tuple(x for x in range(16)),
         'pwm' : tuple(x for x in range(2,14)),
         'use_ports' : True,
-        'disabled' : (0, 1, 14, 15) # Rx, Tx, Crystal
+        'disabled' : (0, 1) # Rx, Tx, Crystal
     }
 }

pyfirmata/pyfirmata.py

         self.analog = []
         for i in board_layout['analog']:
             self.analog.append(Pin(self, i))
-        # Only create digital ports if the Firmata can use them (ie. not on the Mega...)
-        # TODO Why is (TOTAL_FIRMATA_PINS + 7) / 8 used in Firmata?
-        if board_layout['use_ports']:
-            self.digital = []
-            self.digital_ports = []
-            for i in range(len(board_layout['digital']) / 7):
-                self.digital_ports.append(Port(self, i))
-            # Allow to access the Pin instances directly
-            for port in self.digital_ports:
-                self.digital += port.pins
-            for i in board_layout['pwm']:
-                self.digital[i].PWM_CAPABLE = True
-        else:
-            self.digital = []
-            for i in board_layout['digital']:
-                self.digital.append(Pin(self.sp, i, type=DIGITAL))
+
+        self.digital = []
+        self.digital_ports = []
+        for i in xrange(0, len(board_layout['digital']), 8):
+            num_pins = len(board_layout['digital'][i:i+8])
+            port_number = i / 8
+            self.digital_ports.append(Port(self, port_number, num_pins))
+
+        # Allow to access the Pin instances directly
+        for port in self.digital_ports:
+            self.digital += port.pins
+        
+        # Setup PWM pins
+        for i in board_layout['pwm']:
+            self.digital[i].PWM_CAPABLE = True
+
         # Disable certain ports like Rx/Tx and crystal ports
         for i in board_layout['disabled']:
             self.digital[i].mode = UNAVAILABLE
+
         # Create a dictionary of 'taken' pins. Used by the get_pin method
         self.taken = { 'analog' : dict(map(lambda p: (p.pin_number, False), self.analog)),
                        'digital' : dict(map(lambda p: (p.pin_number, False), self.digital)) }
+
         # Setup default handlers for standard incoming commands
         self.add_cmd_handler(ANALOG_MESSAGE, self._handle_analog_message)
         self.add_cmd_handler(DIGITAL_MESSAGE, self._handle_digital_message)
 
 class Port(object):
     """ An 8-bit port on the board """
-    def __init__(self, board, port_number):
+    def __init__(self, board, port_number, num_pins=8):
         self.board = board
         self.port_number = port_number
         self.reporting = False
         
         self.pins = []
-        for i in range(8):
+        for i in range(num_pins):
             pin_nr = i + self.port_number * 8
             self.pins.append(Pin(self.board, pin_nr, type=DIGITAL, port=self))
             
 
 # Messages todo left:
 
-# type                command  channel    first byte            second byte 
+# type                command  channel    first byte            second byte
 # ---------------------------------------------------------------------------
 # set pin mode(I/O)     0xF4              pin # (0-127)         pin state(0=in)
 # system reset          0xFF
         pyfirmata.pyfirmata.serial.Serial = mockup.MockupSerial
         self.board = pyfirmata.Board('', BOARDS['arduino'])
         self.board._stored_data = [] # FIXME How can it be that a fresh instance sometimes still contains data?
-            
+
+
 class TestBoardMessages(BoardBaseTest):
     # TODO Test layout of Board Mega
     def assert_serial(self, *list_of_chrs):
         # max value an analog pin will send and it should result in a value 1
         self.board._handle_analog_message(3, 127, 7)
         self.assertEqual(self.board.analog[3].read(), 1.0)
-        
+
     def test_handle_digital_message(self):
         # A digital message sets the value for a whole port. We will set pin
         # 5 (That is on port 0) to 1 to test if this is working.
         self.assertEqual(self.board.digital[5].read(), None)
         self.board._handle_digital_message(0, mask % 128, mask >> 7)
         self.assertEqual(self.board.digital[5].read(), True)
-        
+
     def test_handle_report_version(self):
         self.assertEqual(self.board.firmata_version, None)
         self.board._handle_report_version(2, 1)
         self.assertEqual(self.board.firmata_version, (2, 1))
-        
+
     def test_handle_report_firmware(self):
         self.assertEqual(self.board.firmware, None)
         data = [2, 1] + str_to_two_byte_iter('Firmware_name')
         self.board._handle_report_firmware(*data)
         self.assertEqual(self.board.firmware, 'Firmware_name')
         self.assertEqual(self.board.firmware_version, (2, 1))
-        
-    # type                command  channel    first byte            second byte 
+
+    # type                command  channel    first byte            second byte
     # ---------------------------------------------------------------------------
     # analog I/O message    0xE0   pin #      LSB(bits 0-6)         MSB(bits 7-13)
     def test_incoming_analog_message(self):
         self.board.iterate()
         self.assertEqual(self.board.analog[4].read(), 1.0)
         self.board._stored_data = []
-    
-    # type                command  channel    first byte            second byte 
+
+    # type                command  channel    first byte            second byte
     # ---------------------------------------------------------------------------
     # digital I/O message   0x90   port       LSB(bits 0-6)         MSB(bits 7-13)
     def test_incoming_digital_message(self):
         self.board.sp.write([chr(pyfirmata.DIGITAL_MESSAGE + 1), chr(mask % 128), chr(mask >> 7)])
         self.board.iterate()
         self.assertEqual(self.board.digital[9].read(), True)
-        
+
     # version report format
     # -------------------------------------------------
     # 0  version report header (0xF9) (MIDI Undefined)
         self.board.sp.write([chr(pyfirmata.REPORT_VERSION), chr(2), chr(1)])
         self.board.iterate()
         self.assertEqual(self.board.firmata_version, (2, 1))
-    
+
     # Receive Firmware Name and Version (after query)
     # 0  START_SYSEX (0xF0)
     # 1  queryFirmware (0x79)
     def test_incoming_report_firmware(self):
         self.assertEqual(self.board.firmware, None)
         self.assertEqual(self.board.firmware_version, None)
-        msg = [chr(pyfirmata.START_SYSEX), 
-               chr(pyfirmata.REPORT_FIRMWARE), 
-               chr(2), 
+        msg = [chr(pyfirmata.START_SYSEX),
+               chr(pyfirmata.REPORT_FIRMWARE),
+               chr(2),
                chr(1)] + str_to_two_byte_iter('Firmware_name') + \
               [chr(pyfirmata.END_SYSEX)]
         self.board.sp.write(msg)
         self.board.iterate()
         self.assertEqual(self.board.firmware, 'Firmware_name')
         self.assertEqual(self.board.firmware_version, (2, 1))
-        
-    # type                command  channel    first byte            second byte 
+
+    # type                command  channel    first byte            second byte
     # ---------------------------------------------------------------------------
     # report analog pin     0xC0   pin #      disable/enable(0/1)   - n/a -
     def test_report_analog(self):
         self.board.analog[1].disable_reporting()
         self.assert_serial(chr(0xC0 + 1), chr(0))
         self.assertFalse(self.board.analog[1].reporting)
-        
-    # type                command  channel    first byte            second byte 
+
+    # type                command  channel    first byte            second byte
     # ---------------------------------------------------------------------------
     # report digital port   0xD0   port       disable/enable(0/1)   - n/a -
     def test_report_digital(self):
         self.assertTrue(self.board.digital_ports[1].reporting)
         self.board.digital[8].disable_reporting()
         self.assert_serial(chr(0xD0 + 1), chr(0))
-        
+
     # Generic Sysex Message
     # 0     START_SYSEX (0xF0)
     # 1     sysex command (0x00-0x7F)
         self.board.send_sysex(0x79, [1, 2, 3])
         sysex = (chr(0xF0), chr(0x79), chr(1), chr(2), chr(3), chr(0xF7))
         self.assert_serial(*sysex)
-        
+
     def test_send_sysex_to_big_data(self):
         self.assertRaises(ValueError, self.board.send_sysex, 0x79, [256, 1])
-        
+
     def test_receive_sysex_message(self):
-        sysex = (chr(0xF0), chr(0x79), chr(2), chr(1), 'a', '\x00', 'b', 
+        sysex = (chr(0xF0), chr(0x79), chr(2), chr(1), 'a', '\x00', 'b',
             '\x00', 'c', '\x00', chr(0xF7))
         self.board.sp.write(sysex)
         while len(self.board.sp):
             self.board.iterate()
         self.assertEqual(self.board.firmware_version, (2, 1))
         self.assertEqual(self.board.firmware, 'abc')
-        
+
     def test_too_much_data(self):
-        """ 
+        """
         When we send random bytes, before or after a command, they should be
         ignored to prevent cascading errors when missing a byte.
         """
         while len(self.board.sp):
             self.board.iterate()
         self.assertEqual(self.board.analog[4].read(), 1.0)
-    
+
     # Servo config
     # --------------------
     # 0  START_SYSEX (0xF0)
     # 10 angle MSB
     def test_servo_config(self):
         self.board.servo_config(2)
-        data = chain([chr(0xF0), chr(0x70), chr(2)], to_two_bytes(544), 
+        data = chain([chr(0xF0), chr(0x70), chr(2)], to_two_bytes(544),
             to_two_bytes(2400), chr(0xF7), chr(0xE0 + 2), chr(0), chr(0))
         self.assert_serial(*data)
 
     def test_servo_config_min_max_pulse(self):
         self.board.servo_config(2, 600, 2000)
-        data = chain([chr(0xF0), chr(0x70), chr(2)], to_two_bytes(600), 
+        data = chain([chr(0xF0), chr(0x70), chr(2)], to_two_bytes(600),
             to_two_bytes(2000), chr(0xF7), chr(0xE0 + 2), chr(0), chr(0))
         self.assert_serial(*data)
 
     def test_servo_config_min_max_pulse_angle(self):
         self.board.servo_config(2, 600, 2000, angle=90)
-        data = chain([chr(0xF0), chr(0x70), chr(2)], to_two_bytes(600), 
+        data = chain([chr(0xF0), chr(0x70), chr(2)], to_two_bytes(600),
             to_two_bytes(2000), chr(0xF7))
-        angle_set = [chr(0xE0 + 2), chr(90 % 128), 
+        angle_set = [chr(0xE0 + 2), chr(90 % 128),
             chr(90 >> 7)] # Angle set happens through analog message
         data = list(data) + angle_set
         self.assert_serial(*data)
     def test_set_mode_servo(self):
         p = self.board.digital[2]
         p.mode = pyfirmata.SERVO
-        data = chain([chr(0xF0), chr(0x70), chr(2)], to_two_bytes(544), 
+        data = chain([chr(0xF0), chr(0x70), chr(2)], to_two_bytes(544),
             to_two_bytes(2400), chr(0xF7), chr(0xE0 + 2), chr(0), chr(0))
         self.assert_serial(*data)
 
 
 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))
+        
+    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))        
+
     def test_pwm_layout(self):
         pins = []
         for pin in self.board.digital:
             self.assertEqual(pin.mode, pyfirmata.PWM)
             self.assertTrue(pin.pin_number in BOARDS['arduino']['pwm'])
         self.assertTrue(len(pins) == len(BOARDS['arduino']['pwm']))
-        
+
     def test_get_pin_digital(self):
         pin = self.board.get_pin('d:13:o')
         self.assertEqual(pin.pin_number, 13)
         self.assertEqual(pin.mode, pyfirmata.OUTPUT)
         self.assertEqual(pin.port.port_number, 1)
         self.assertEqual(pin.port.reporting, False)
-        
+
     def test_get_pin_analog(self):
         pin = self.board.get_pin('a:5:i')
         self.assertEqual(pin.pin_number, 5)
         self.assertEqual(pin.reporting, True)
         self.assertEqual(pin.value, None)
-        
+
     def tearDown(self):
         self.board.exit()
         pyfirmata.serial.Serial = serial.Serial
-        
+
+
 class TestMockupBoardLayout(TestBoardLayout, TestBoardMessages):
     """
     TestMockupBoardLayout is subclassed from TestBoardLayout and
     """
     def setUp(self):
         self.board = mockup.MockupBoard('test', BOARDS['arduino'])
-        
+
+
 class RegressionTests(BoardBaseTest):
-    
+
     def test_correct_digital_input_first_pin_issue_9(self):
         """
         The first pin on the port would always be low, even if the mask said
         pin = self.board.get_pin('d:8:i')
         mask = 0
         mask |= 1 << 0 # set pin 0 high
-        self.board._handle_digital_message(pin.port.port_number, 
+        self.board._handle_digital_message(pin.port.port_number,
             mask % 128, mask >> 7)
         self.assertEqual(pin.value, True)
-        
+
     def test_handle_digital_inputs(self):
         """
         Test if digital inputs are correctly updated.
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.