Commits

Tino de Bruijn committed b975de7

CLOSED REPOSITORY. SEE GITHUB.COM/TINO/PYFIRMATA

  • Participants
  • Parent commits 3f54748

Comments (0)

Files changed (13)

LICENSE

-Copyright (c) 2010, Tino de Bruijn
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.

docs/Makefile

-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
-BUILDDIR      = _build
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
-
-help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html      to make standalone HTML files"
-	@echo "  dirhtml   to make HTML files named index.html in directories"
-	@echo "  pickle    to make pickle files"
-	@echo "  json      to make JSON files"
-	@echo "  htmlhelp  to make HTML files and a HTML help project"
-	@echo "  qthelp    to make HTML files and a qthelp project"
-	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  changes   to make an overview of all changed/added/deprecated items"
-	@echo "  linkcheck to check all external links for integrity"
-	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
-
-clean:
-	-rm -rf $(BUILDDIR)/*
-
-html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files."
-
-json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-	@echo
-	@echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-	@echo
-	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyFirmata.qhcp"
-	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyFirmata.qhc"
-
-latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
-	      "run these through (pdf)latex."
-
-changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-	@echo
-	@echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-	@echo "Testing of doctests in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/doctest/output.txt."

docs/conf.py

-# -*- coding: utf-8 -*-
-#
-# pyFirmata documentation build configuration file, created by
-# sphinx-quickstart on Wed Feb 17 18:59:00 2010.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.append(os.path.abspath('.'))
-
-# -- General configuration -----------------------------------------------------
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'pyFirmata'
-copyright = u'2010, Tino de Bruijn'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '0.1'
-# The full version, including alpha/beta/rc tags.
-release = '0.1'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of documents that shouldn't be included in the build.
-#unused_docs = []
-
-# List of directories, relative to source directory, that shouldn't be searched
-# for source files.
-exclude_trees = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  Major themes that come with
-# Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_use_modindex = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = ''
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'pyFirmatadoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
-  ('index', 'pyFirmata.tex', u'pyFirmata Documentation',
-   u'Tino de Bruijn', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_use_modindex = True
-
-import sys, os
-sys.path.append(os.path.abspath('../'))

docs/index.rst

-.. pyFirmata documentation master file, created by
-   sphinx-quickstart on Wed Feb 17 18:59:00 2010.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
-
-Welcome to pyFirmata's documentation!
-=====================================
-
-Contents:
-
-.. toctree::
-   :maxdepth: 2
-   
-   pyfirmata
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-

docs/pyfirmata.rst

-pyFirmata
-=========
-
-.. automodule:: pyfirmata.pyfirmata
-    :members:

pyfirmata/__init__.py

-from pyfirmata import *
-from boards import BOARDS
-
-__version__ = '0.9.4'
-
-# shortcut classes
-
-class Arduino(Board):
-    """
-    A board that wil set itself up as a normal Arduino.
-    """
-    def __init__(self, *args, **kwargs):
-        args = list(args)
-        args.append(BOARDS['arduino'])
-        super(Arduino, self).__init__(*args, **kwargs)
-        
-    def __str__(self):
-        return 'Arduino %s on %s' % (self.name, self.sp.port)
-    
-class ArduinoMega(Board):
-    """
-    A board that wil set itself up as an Arduino Mega.
-    """
-    def __init__(self, *args, **kwargs):
-        args = list(args)
-        args.append(BOARDS['arduino_mega'])
-        super(ArduinoMega, self).__init__(*args, **kwargs)
-    
-    def __str__(self):
-        return 'Arduino Mega %s on %s' % (self.name, self.sp.port)

pyfirmata/boards.py

-BOARDS = {
-    'arduino' : {
-        'digital' : tuple(x for x in range(14)),
-        'analog' : tuple(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)),
-        'pwm' : tuple(x for x in range(2,14)),
-        'use_ports' : True,
-        'disabled' : (0, 1) # Rx, Tx, Crystal
-    }
-}

pyfirmata/mockup.py

-from collections import deque
-import pyfirmata
-
-class MockupSerial(deque):
-    """ 
-    A Mockup object for python's Serial. Functions as a fifo-stack. Push to
-    it with ``write``, read from it with ``read``.
-    
-    >>> s = MockupSerial('someport', 4800)
-    >>> s.read()
-    ''
-    >>> s.write(chr(100))
-    >>> s.write('blaat')
-    >>> s.write(100000)
-    >>> s.read(2)
-    ['d', 'blaat']
-    >>> s.read()
-    100000
-    >>> s.read()
-    ''
-    >>> s.read(2)
-    ['', '']
-    >>> s.close()
-    """
-    def __init__(self, port, baudrate, timeout=0.02):
-        self.port = port or 'somewhere'
-        
-    def read(self, count=1):
-        if count > 1:
-            val = []
-            for i in range(count):
-                try:
-                    val.append(self.popleft())
-                except IndexError:
-                    val.append('')
-        else:
-            try:
-                val = self.popleft()
-            except IndexError:
-                val = ''
-        return val
-            
-    def write(self, value):
-        """
-        Appends items flat to the deque. So iterables will be unpacked.
-        """
-        if hasattr(value, '__iter__'):
-            self.extend(value)
-        else:
-            self.append(value)
-            
-    def close(self):
-        self.clear()
-        
-    def inWaiting(self):
-        return len(self)
-        
-class MockupBoard(pyfirmata.Board):
-
-    def __init__(self, port, layout, values_dict={}):
-        self.sp = MockupSerial(port, 57600)
-        self.setup_layout(layout)
-        self.values_dict = values_dict
-        self.id = 1
-        
-    def reset_taken(self):
-        for key in self.taken['analog']:
-            self.taken['analog'][key] = False
-        for key in self.taken['digital']:
-            self.taken['digital'][key] = False
-        
-    def update_values_dict(self):
-        for port in self.digital_ports:
-            port.values_dict = self.values_dict
-            port.update_values_dict()
-        for pin in self.analog:
-            pin.values_dict = self.values_dict
-        
-class MockupPort(pyfirmata.Port):
-    def __init__(self, board, port_number):
-        self.board = board
-        self.port_number = port_number
-        self.reporting = False
-        
-        self.pins = []
-        for i in range(8):
-            pin_nr = i + self.port_number * 8
-            self.pins.append(MockupPin(self.board, pin_nr, type=pyfirmata.DIGITAL, port=self))
-
-    def update_values_dict(self):
-        for pin in self.pins:
-            pin.values_dict = self.values_dict
-        
-class MockupPin(pyfirmata.Pin):
-    def __init__(self, *args, **kwargs):
-        self.values_dict = kwargs.get('values_dict', {})
-        try:
-            del kwargs['values_dict']
-        except KeyError:
-            pass
-        super(MockupPin, self).__init__(*args, **kwargs)
-    
-    def read(self):
-        if self.value is None:
-            try:
-                type = self.port and 'd' or 'a'
-                return self.values_dict[type][self.pin_number]
-            except KeyError:
-                return None
-        else:
-            return self.value
-            
-    def get_in_output(self):
-        if not self.port and not self.mode: # analog input
-            return 'i'
-        else:
-            return 'o'
-            
-    def set_active(self, active):
-        self.is_active = active
-        
-    def get_active(self):
-        return self.is_active
-        
-    def write(self, value):
-        if self.mode == pyfirmata.UNAVAILABLE:
-            raise IOError, "Cannot read from pin %d" \
-                           % (self.pin_number)
-        if self.mode == pyfirmata.INPUT:
-            raise IOError, "%s pin %d is not an output" \
-                            % (self.port and "Digital" or "Analog", self.get_pin_number())
-        if not self.port:
-            raise AttributeError, "AnalogPin instance has no attribute 'write'"
-        # if value != self.read():
-        self.value = value
-        
-class Iterator(object):
-    def __init__(self, *args, **kwargs):
-        pass
-    def start(self):
-        pass
-    def stop(self):
-        pass
-
-if __name__ == '__main__':
-    import doctest
-    doctest.testmod()

pyfirmata/pyfirmata.py

-import serial
-import inspect
-import time
-import itertools
-from util import two_byte_iter_to_str, to_two_bytes
-
-# Message command bytes - straight from Firmata.h
-DIGITAL_MESSAGE = 0x90      # send data for a digital pin
-ANALOG_MESSAGE = 0xE0       # send data for an analog pin (or PWM)
-DIGITAL_PULSE = 0x91        # SysEx command to send a digital pulse
-
-# PULSE_MESSAGE = 0xA0      # proposed pulseIn/Out msg (SysEx)
-# SHIFTOUT_MESSAGE = 0xB0   # proposed shiftOut msg (SysEx)
-REPORT_ANALOG = 0xC0        # enable analog input by pin #
-REPORT_DIGITAL = 0xD0       # enable digital input by port pair
-START_SYSEX = 0xF0          # start a MIDI SysEx msg
-SET_PIN_MODE = 0xF4         # set a pin to INPUT/OUTPUT/PWM/etc
-END_SYSEX = 0xF7            # end a MIDI SysEx msg
-REPORT_VERSION = 0xF9       # report firmware version
-SYSTEM_RESET = 0xFF         # reset from MIDI
-QUERY_FIRMWARE = 0x79       # query the firmware name
-
-# extended command set using sysex (0-127/0x00-0x7F)
-# 0x00-0x0F reserved for user-defined commands */
-SERVO_CONFIG = 0x70         # set max angle, minPulse, maxPulse, freq
-STRING_DATA = 0x71          # a string message with 14-bits per char
-SHIFT_DATA = 0x75           # a bitstream to/from a shift register
-I2C_REQUEST = 0x76          # send an I2C read/write request
-I2C_REPLY = 0x77            # a reply to an I2C read request
-I2C_CONFIG = 0x78           # config I2C settings such as delay times and power pins
-REPORT_FIRMWARE = 0x79      # report name and version of the firmware
-SAMPLING_INTERVAL = 0x7A    # set the poll rate of the main loop
-SYSEX_NON_REALTIME = 0x7E   # MIDI Reserved for non-realtime messages
-SYSEX_REALTIME = 0x7F       # MIDI Reserved for realtime messages
-
-
-# Pin modes.
-# except from UNAVAILABLE taken from Firmata.h
-UNAVAILABLE = -1 
-INPUT = 0          # as defined in wiring.h
-OUTPUT = 1         # as defined in wiring.h
-ANALOG = 2         # analog pin in analogInput mode
-PWM = 3            # digital pin in PWM output mode
-SERVO = 4          # digital pin in SERVO mode
-
-# Pin types
-DIGITAL = OUTPUT   # same as OUTPUT below
-# ANALOG is already defined above
-
-class PinAlreadyTakenError(Exception):
-    pass
-
-class InvalidPinDefError(Exception):
-    pass
-    
-class NoInputWarning(RuntimeWarning):
-    pass
-    
-class Board(object):
-    """
-    Base class for any board
-    """
-    firmata_version = None
-    firmware = None
-    firmware_version = None
-    _command_handlers = {}
-    _command = None
-    _stored_data = []
-    _parsing_sysex = False
-    
-    def __init__(self, port, layout, baudrate=57600, name=None):
-        self.sp = serial.Serial(port, baudrate)
-        # Allow 5 secs for Arduino's auto-reset to happen
-        # Alas, Firmata blinks it's version before printing it to serial
-        # For 2.3, even 5 seconds might not be enough.
-        # TODO Find a more reliable way to wait until the board is ready
-        self.pass_time(5)
-        self.name = name
-        if not self.name:
-            self.name = port
-        self.setup_layout(layout)
-        # Iterate over the first messages to get firmware data
-        while self.bytes_available():
-            self.iterate()
-        # TODO Test whether we got a firmware name and version, otherwise there 
-        # probably isn't any Firmata installed
-        
-    def __str__(self):
-        return "Board %s on %s" % (self.name, self.sp.port)
-        
-    def __del__(self):
-        ''' 
-        The connection with the a board can get messed up when a script is
-        closed without calling board.exit() (which closes the serial
-        connection). Therefore also do it here and hope it helps.
-        '''
-        self.exit()
-        
-    def send_as_two_bytes(self, val):
-        self.sp.write(chr(val % 128) + chr(val >> 7))
-
-    def setup_layout(self, board_layout):
-        """
-        Setup the Pin instances based on the given board-layout. Maybe it will
-        be possible to do this automatically in the future, by polling the
-        board for its type.
-        """
-        # Create pin instances based on board layout
-        self.analog = []
-        for i in board_layout['analog']:
-            self.analog.append(Pin(self, i))
-
-        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)
-        self.add_cmd_handler(REPORT_VERSION, self._handle_report_version)
-        self.add_cmd_handler(REPORT_FIRMWARE, self._handle_report_firmware)
-    
-    def add_cmd_handler(self, cmd, func):
-        """ 
-        Adds a command handler for a command.
-        """
-        len_args = len(inspect.getargspec(func)[0])
-        def add_meta(f):
-            def decorator(*args, **kwargs):
-                f(*args, **kwargs)
-            decorator.bytes_needed = len_args - 1 # exclude self
-            decorator.__name__ = f.__name__
-            return decorator
-        func = add_meta(func)
-        self._command_handlers[cmd] = func
-        
-    def get_pin(self, pin_def):
-        """
-        Returns the activated pin given by the pin definition.
-        May raise an ``InvalidPinDefError`` or a ``PinAlreadyTakenError``.
-        
-        :arg pin_def: Pin definition as described in TODO,
-            but without the arduino name. So for example ``a:1:i``.
-        
-        """
-        if type(pin_def) == list:
-            bits = pin_def
-        else:
-            bits = pin_def.split(':')
-        a_d = bits[0] == 'a' and 'analog' or 'digital'
-        part = getattr(self, a_d)
-        pin_nr = int(bits[1])
-        if pin_nr >= len(part):
-            raise InvalidPinDefError('Invalid pin definition: %s at position 3 on %s' % (pin_def, self.name))
-        if getattr(part[pin_nr], 'mode', None)  == UNAVAILABLE:
-            raise InvalidPinDefError('Invalid pin definition: UNAVAILABLE pin %s at position on %s' % (pin_def, self.name))
-        if self.taken[a_d][pin_nr]:
-            raise PinAlreadyTakenError('%s pin %s is already taken on %s' % (a_d, bits[1], self.name))
-        # ok, should be available
-        pin = part[pin_nr]
-        self.taken[a_d][pin_nr] = True
-        if pin.type is DIGITAL:
-            if bits[2] == 'p':
-                pin.mode = PWM
-            elif bits[2] == 's':
-                pin.mode = SERVO
-            elif bits[2] is not 'o':
-                pin.mode = INPUT
-        else:
-            pin.enable_reporting()
-        return pin
-        
-    def pass_time(self, t):
-        """ 
-        Non-blocking time-out for ``t`` seconds.
-        """
-        cont = time.time() + t
-        while time.time() < cont:
-            time.sleep(0)
-            
-    def send_sysex(self, sysex_cmd, data=[]):
-        """
-        Sends a SysEx msg.
-        
-        :arg sysex_cmd: A sysex command byte
-        :arg data: A list of 7-bit bytes of arbitrary data (bytes may be 
-            already converted to chr's)
-        """
-        self.sp.write(chr(START_SYSEX))
-        self.sp.write(chr(sysex_cmd))
-        for byte in data:
-            try:
-                byte = chr(byte)
-            except TypeError:
-                pass # byte is already a chr
-            except ValueError:
-                raise ValueError('Sysex data can be 7-bit bytes only. '
-                    'Consider using utils.to_two_bytes for bigger bytes.')
-            self.sp.write(byte)
-        self.sp.write(chr(END_SYSEX))
-        
-    def bytes_available(self):
-        return self.sp.inWaiting()
-
-    def iterate(self):
-        """ 
-        Reads and handles data from the microcontroller over the serial port.
-        This method should be called in a main loop, or in an
-        :class:`Iterator` instance to keep this boards pin values up to date
-        """
-        byte = self.sp.read()
-        if not byte:
-            return
-        data = ord(byte)
-        received_data = []
-        handler = None
-        if data < START_SYSEX:
-            # These commands can have 'channel data' like a pin nummber appended.
-            try:
-                handler = self._command_handlers[data & 0xF0]
-            except KeyError:
-                return
-            received_data.append(data & 0x0F)
-            while len(received_data) < handler.bytes_needed:
-                received_data.append(ord(self.sp.read()))
-        elif data == START_SYSEX:
-            data = ord(self.sp.read())
-            handler = self._command_handlers.get(data)
-            if not handler:
-                return
-            data = ord(self.sp.read())
-            while data != END_SYSEX:
-                received_data.append(data)
-                data = ord(self.sp.read())
-        else:
-            try:
-                handler = self._command_handlers[data]
-            except KeyError:
-                return
-            while len(received_data) < handler.bytes_needed:
-                received_data.append(ord(self.sp.read()))
-        # Handle the data
-        try:
-            handler(*received_data)
-        except ValueError:
-            pass
-            
-    def get_firmata_version(self):
-        """
-        Returns a version tuple (major, mino) for the firmata firmware on the
-        board.
-        """
-        return self.firmata_version
-        
-    def servo_config(self, pin, min_pulse=544, max_pulse=2400, angle=0):
-        """
-        Configure a pin as servo with min_pulse, max_pulse and first angle.
-        ``min_pulse`` and ``max_pulse`` default to the arduino defaults.
-        """
-        if pin > len(self.digital) or self.digital[pin].mode == UNAVAILABLE:
-            raise IOError("Pin %s is not a valid servo pin")
-        data = itertools.chain([pin], to_two_bytes(min_pulse),
-                                        to_two_bytes(max_pulse))
-        self.send_sysex(SERVO_CONFIG, data)
-        
-        # set pin._mode to SERVO so that it sends analog messages
-        # don't set pin.mode as that calls this method
-        self.digital[pin]._mode = SERVO
-        self.digital[pin].write(angle)
-        
-    def exit(self):
-        """ Call this to exit cleanly. """
-        # First detach all servo's, otherwise it somehow doesn't want to close...
-        # FIXME
-        for pin in self.digital:
-            if pin.mode == SERVO:
-                pin.mode = OUTPUT
-        if hasattr(self, 'sp'):
-            self.sp.close()
-        
-    # Command handlers
-    def _handle_analog_message(self, pin_nr, lsb, msb):
-        value = round(float((msb << 7) + lsb) / 1023, 4)
-        # Only set the value if we are actually reporting
-        try:
-            if self.analog[pin_nr].reporting:
-                self.analog[pin_nr].value = value
-        except IndexError:
-            raise ValueError
-
-    def _handle_digital_message(self, port_nr, lsb, msb):
-        """
-        Digital messages always go by the whole port. This means we have a
-        bitmask wich we update the port.
-        """
-        mask = (msb << 7) + lsb
-        try:
-            self.digital_ports[port_nr]._update(mask)
-        except IndexError:
-            raise ValueError
-
-    def _handle_report_version(self, major, minor):
-        self.firmata_version = (major, minor)
-        
-    def _handle_report_firmware(self, *data):
-        major = data[0]
-        minor = data[1]
-        self.firmware_version = (major, minor)
-        self.firmware = two_byte_iter_to_str(data[2:])
-
-class Port(object):
-    """ An 8-bit port on the board """
-    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(num_pins):
-            pin_nr = i + self.port_number * 8
-            self.pins.append(Pin(self.board, pin_nr, type=DIGITAL, port=self))
-            
-    def __str__(self):
-        return "Digital Port %i on %s" % (self.port_number, self.board)
-        
-    def enable_reporting(self):
-        """ Enable reporting of values for the whole port """
-        self.reporting = True
-        msg = chr(REPORT_DIGITAL + self.port_number)
-        msg += chr(1)
-        self.board.sp.write(msg)
-        for pin in self.pins:
-            if pin.mode == INPUT:
-                pin.reporting = True # TODO Shouldn't this happen at the pin?
-        
-    def disable_reporting(self):
-        """ Disable the reporting of the port """
-        self.reporting = False
-        msg = chr(REPORT_DIGITAL + self.port_number)
-        msg += chr(0)
-        self.board.sp.write(msg)
-                
-    def write(self):
-        """Set the output pins of the port to the correct state"""
-        mask = 0
-        for pin in self.pins:
-            if pin.mode == OUTPUT:
-                if pin.value == 1:
-                    pin_nr = pin.pin_number - self.port_number * 8
-                    mask |= 1 << pin_nr
-        msg = chr(DIGITAL_MESSAGE + self.port_number)
-        msg += chr(mask % 128)
-        msg += chr(mask >> 7)
-        self.board.sp.write(msg)
-        
-    def _update(self, mask):
-        """
-        Update the values for the pins marked as input with the mask.
-        """
-        if self.reporting:
-            for pin in self.pins:
-                if pin.mode is INPUT:
-                    pin_nr = pin.pin_number - self.port_number * 8
-                    pin.value = (mask & (1 << pin_nr)) > 0
-
-class Pin(object):
-    """ A Pin representation """
-    def __init__(self, board, pin_number, type=ANALOG, port=None):
-        self.board = board
-        self.pin_number = pin_number
-        self.type = type
-        self.port = port
-        self.PWM_CAPABLE = False
-        self._mode = (type == DIGITAL and OUTPUT or INPUT)
-        self.reporting = False
-        self.value = None
-        
-    def __str__(self):
-        type = {ANALOG : 'Analog', DIGITAL : 'Digital'}[self.type]
-        return "%s pin %d" % (type, self.pin_number)
-
-    def _set_mode(self, mode):
-        if mode is UNAVAILABLE:
-            self._mode = UNAVAILABLE
-            return
-        if self._mode is UNAVAILABLE:
-            raise IOError("%s can not be used through Firmata" % self)
-        if mode is PWM and not self.PWM_CAPABLE:
-            raise IOError("%s does not have PWM capabilities" % self)
-        if mode == SERVO:
-            if self.type != DIGITAL:
-                raise IOError("Only digital pins can drive servos! %s is not"
-                    "digital" % self)
-            self._mode = SERVO
-            self.board.servo_config(self.pin_number)
-            return
-        
-        # Set mode with SET_PIN_MODE message
-        self._mode = mode
-        command = chr(SET_PIN_MODE)
-        command += chr(self.pin_number)
-        command += chr(mode)
-        self.board.sp.write(command)
-        if mode == INPUT:
-            self.enable_reporting()
-        
-    def _get_mode(self):
-        return self._mode
-        
-    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)
-    """
-    
-    def enable_reporting(self):
-        """ Set an input pin to report values """
-        if self.mode is not INPUT:
-            raise IOError, "%s is not an input and can therefore not report" % self
-        if self.type == ANALOG:
-            self.reporting = True
-            msg = chr(REPORT_ANALOG + self.pin_number)
-            msg += chr(1)
-            self.board.sp.write(msg)
-        else:
-            self.port.enable_reporting() # TODO This is not going to work for non-optimized boards like Mega
-        
-    def disable_reporting(self):
-        """ Disable the reporting of an input pin """
-        if self.type == ANALOG:
-            self.reporting = False
-            msg = chr(REPORT_ANALOG + self.pin_number)
-            msg += chr(0)
-            self.board.sp.write(msg)
-        else:
-            self.port.disable_reporting() # TODO This is not going to work for non-optimized boards like Mega
-    
-    def read(self):
-        """
-        Returns the output value of the pin. This value is updated by the
-        boards :meth:`Board.iterate` method. Value is alway in the range 0.0 - 1.0
-        """
-        if self.mode == UNAVAILABLE:
-            raise IOError, "Cannot read pin %s"% self.__str__()
-        return self.value
-        
-    def write(self, value):
-        """
-        Output a voltage from the pin
-
-        :arg value: Uses value as a boolean if the pin is in output mode, or
-            expects a float from 0 to 1 if the pin is in PWM mode. If the pin 
-            is in SERVO the value should be in degrees.
-        
-        """
-        if self.mode is UNAVAILABLE:
-            raise IOError, "%s can not be used through Firmata" % self
-        if self.mode is INPUT:
-            raise IOError, "%s is set up as an INPUT and can therefore not be written to" % self
-        if value is not self.value:
-            self.value = value
-            if self.mode is OUTPUT:
-                if self.port:
-                    self.port.write()
-                else:
-                    msg = chr(DIGITAL_MESSAGE)
-                    msg += chr(self.pin_number)
-                    msg += chr(value)
-                    self.board.sp.write(msg)
-            elif self.mode is PWM:
-                value = int(round(value * 255))
-                msg = chr(ANALOG_MESSAGE + self.pin_number)
-                msg += chr(value % 128)
-                msg += chr(value >> 7)
-                self.board.sp.write(msg)
-            elif self.mode is SERVO:
-                value = int(value)
-                msg = chr(ANALOG_MESSAGE + self.pin_number)
-                msg += chr(value % 128)
-                msg += chr(value >> 7)
-                self.board.sp.write(msg)

pyfirmata/util.py

-import threading
-import serial
-import time
-import os
-import pyfirmata
-from boards import BOARDS
-
-def get_the_board(layout=BOARDS['arduino'], base_dir='/dev/', identifier='tty.usbserial',):
-    """
-    Helper function to get the one and only board connected to the computer
-    running this. It assumes a normal arduino layout, but this can be
-    overriden by passing a different layout dict as the ``layout`` parameter.
-    ``base_dir`` and ``identifier`` are overridable as well. It will raise an
-    IOError if it can't find a board, on a serial, or if it finds more than
-    one.
-    """
-    boards = []
-    for device in os.listdir(base_dir):
-        if device.startswith(identifier):
-            try:
-                board = pyfirmata.Board(os.path.join(base_dir, device), layout)
-            except serial.SerialException:
-                pass
-            else:
-                boards.append(board)
-    if len(boards) == 0:
-        raise IOError, "No boards found in %s with identifier %s" % (base_dir, identifier)
-    elif len(boards) > 1:
-        raise IOError, "More than one board found!"
-    return boards[0]
-
-class Iterator(threading.Thread):
-    def __init__(self, board):
-        super(Iterator, self).__init__()
-        self.board = board
-        
-    def run(self):
-        while 1:
-            try:
-                while self.board.bytes_available():
-                    self.board.iterate()
-                time.sleep(0.001)
-            except (AttributeError, serial.SerialException, OSError), e:
-                # this way we can kill the thread by setting the board object
-                # to None, or when the serial port is closed by board.exit()
-                break
-            except Exception, e:
-                # catch 'error: Bad file descriptor'
-                # iterate may be called while the serial port is being closed,
-                # causing an "error: (9, 'Bad file descriptor')"
-                if getattr(e, 'errno', None) == 9:
-                    break
-                try:
-                    if e[0] == 9:
-                        break
-                except (TypeError, IndexError):
-                    pass
-                raise
-                
-def to_two_bytes(integer):
-    """
-    Breaks an integer into two 7 bit bytes.
-    
-    >>> for i in range(32768):
-    ...     val = to_two_bytes(i)
-    ...     assert len(val) == 2
-    ...
-    >>> to_two_bytes(32767)
-    ('\\x7f', '\\xff')
-    >>> to_two_bytes(32768)
-    Traceback (most recent call last):
-        ...
-    ValueError: Can't handle values bigger than 32767 (max for 2 bits)
-    
-    """
-    if integer > 32767:
-        raise ValueError, "Can't handle values bigger than 32767 (max for 2 bits)"
-    return chr(integer % 128), chr(integer >> 7)
-    
-def from_two_bytes(bytes):
-    """
-    Return an integer from two 7 bit bytes.
-    
-    >>> for i in range(32766, 32768):
-    ...     val = to_two_bytes(i)
-    ...     ret = from_two_bytes(val)
-    ...     assert ret == i
-    ...
-    >>> from_two_bytes(('\\xff', '\\xff'))
-    32767
-    >>> from_two_bytes(('\\x7f', '\\xff'))
-    32767
-    """
-    lsb, msb = bytes
-    try:
-        # Usually bytes have been converted to integers with ord already
-        return msb << 7 | lsb
-    except TypeError:
-        # But add this for easy testing
-        # One of them can be a string, or both
-        try:
-            lsb = ord(lsb)
-        except TypeError:
-            pass
-        try:
-            msb = ord(msb)
-        except TypeError:
-            pass
-        return msb << 7 | lsb
-    
-def two_byte_iter_to_str(bytes):
-    """
-    Return a string made from a list of two byte chars.
-    
-    >>> string, s = 'StandardFirmata', []
-    >>> for i in string:
-    ...   s.append(i)
-    ...   s.append('\\x00')
-    >>> two_byte_iter_to_str(s)
-    'StandardFirmata'
-    
-    >>> string, s = 'StandardFirmata', []
-    >>> for i in string:
-    ...   s.append(ord(i))
-    ...   s.append(ord('\\x00'))
-    >>> two_byte_iter_to_str(s)
-    'StandardFirmata'
-    """
-    bytes = list(bytes)
-    chars = []
-    while bytes:
-        lsb = bytes.pop(0)
-        try:
-            msb = bytes.pop(0)
-        except IndexError:
-            msb = 0x00
-        chars.append(chr(from_two_bytes((lsb, msb))))
-    return ''.join(chars)
-    
-def str_to_two_byte_iter(string):
-    """
-    Return a iter consisting of two byte chars from a string.
-    
-    >>> string, iter = 'StandardFirmata', []
-    >>> for i in string:
-    ...   iter.append(i)
-    ...   iter.append('\\x00')
-    >>> assert iter == str_to_two_byte_iter(string)
-     """
-    bytes = []
-    for char in string:
-        bytes += list(to_two_bytes(ord(char)))
-    return bytes
-
-def break_to_bytes(value):
-    """
-    Breaks a value into values of less than 255 that form value when multiplied.
-    (Or almost do so with primes)
-    Returns a tuple
-    
-    >>> break_to_bytes(200)
-    (200,)
-    >>> break_to_bytes(800)
-    (200, 4)
-    >>> break_to_bytes(802)
-    (2, 2, 200)
-    """
-    if value < 256:
-        return (value,)
-    c = 256
-    least = (0, 255)
-    for i in range(254):
-        c -= 1
-        rest = value % c
-        if rest == 0 and value / c < 256:
-            return (c, value / c)
-        elif rest == 0 and value / c > 255:
-            parts = list(break_to_bytes(value / c))
-            parts.insert(0, c)
-            return tuple(parts)
-        else:
-            if rest < least[1]:
-                least = (c, rest)
-    return (c, value / c)
-
-if __name__ == '__main__':
-    import doctest
-    doctest.testmod()

requirements.txt

-pyserial

setup.py

-#!/usr/bin/env python
- 
-from distutils.core import setup
-
-import pyfirmata
-
-setup(
-    name='pyFirmata',
-    version=pyfirmata.__version__,
-    description="A Python interface for the Firmata procotol",
-    author='Tino de Bruijn',
-    author_email='tinodb@gmail.com',
-    packages=['pyfirmata'],
-    include_package_data=True,
-    zip_safe=False,
-)

tests.py

-import unittest
-import doctest
-import serial
-from itertools import chain
-import pyfirmata
-from pyfirmata import mockup
-from pyfirmata.boards import BOARDS
-from pyfirmata.util import str_to_two_byte_iter, to_two_bytes
-
-# Messages todo left:
-
-# type                command  channel    first byte            second byte
-# ---------------------------------------------------------------------------
-# set pin mode(I/O)     0xF4              pin # (0-127)         pin state(0=in)
-# system reset          0xFF
-
-class BoardBaseTest(unittest.TestCase):
-    def setUp(self):
-        # Test with the MockupSerial so no real connection is needed
-        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):
-        res = self.board.sp.read()
-        serial_msg = res
-        while res:
-            res = self.board.sp.read()
-            serial_msg += res
-        self.assertEqual(''.join(list(list_of_chrs)), serial_msg)
-
-    # First test the handlers
-    def test_handle_analog_message(self):
-        self.board.analog[3].reporting = True
-        self.assertEqual(self.board.analog[3].read(), None)
-        # This sould set it correctly. 1023 (127, 7 in to 7 bit bytes) is the
-        # 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.board.digital_ports[0].reporting = True
-        self.board.digital[5]._mode = 0 # Set it to input
-        # Create the mask
-        mask = 0
-        mask |= 1 << 5 # set the bit for pin 5 to to 1
-        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
-    # ---------------------------------------------------------------------------
-    # analog I/O message    0xE0   pin #      LSB(bits 0-6)         MSB(bits 7-13)
-    def test_incoming_analog_message(self):
-        self.assertEqual(self.board.analog[4].read(), None)
-        self.assertEqual(self.board.analog[4].reporting, False)
-        # Should do nothing as the pin isn't set to report
-        self.board.sp.write([chr(pyfirmata.ANALOG_MESSAGE + 4), chr(127), chr(7)])
-        self.board.iterate()
-        self.assertEqual(self.board.analog[4].read(), None)
-        self.board.analog[4].enable_reporting()
-        self.board.sp.clear()
-        # This should set analog port 4 to 1
-        self.board.sp.write([chr(pyfirmata.ANALOG_MESSAGE + 4), chr(127), chr(7)])
-        self.board.iterate()
-        self.assertEqual(self.board.analog[4].read(), 1.0)
-        self.board._stored_data = []
-
-    # 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):
-        # A digital message sets the value for a whole port. We will set pin
-        # 9 (on port 1) to 1 to test if this is working.
-        self.board.digital[9].mode = pyfirmata.INPUT
-        self.board.sp.clear() # clear mode sent over the wire.
-        # Create the mask
-        mask = 0
-        mask |= 1 << (9 - 8) # set the bit for pin 9 to to 1
-        self.assertEqual(self.board.digital[9].read(), None)
-        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)
-    # 1  major version (0-127)
-    # 2  minor version (0-127)
-    def test_incoming_report_version(self):
-        self.assertEqual(self.board.firmata_version, None)
-        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)
-    # 2  major version (0-127)
-    # 3  minor version (0-127)
-    # 4  first 7-bits of firmware name
-    # 5  second 7-bits of firmware name
-    # x  ...for as many bytes as it needs)
-    # 6  END_SYSEX (0xF7)
-    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),
-               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
-    # ---------------------------------------------------------------------------
-    # report analog pin     0xC0   pin #      disable/enable(0/1)   - n/a -
-    def test_report_analog(self):
-        self.board.analog[1].enable_reporting()
-        self.assert_serial(chr(0xC0 + 1), chr(1))
-        self.assertTrue(self.board.analog[1].reporting)
-        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
-    # ---------------------------------------------------------------------------
-    # report digital port   0xD0   port       disable/enable(0/1)   - n/a -
-    def test_report_digital(self):
-        # This should enable reporting of whole port 1
-        self.board.digital[8]._mode = pyfirmata.INPUT # Outputs can't report
-        self.board.digital[8].enable_reporting()
-        self.assert_serial(chr(0xD0 + 1), chr(1))
-        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)
-    # x     between 0 and MAX_DATA_BYTES 7-bit bytes of arbitrary data
-    # last  END_SYSEX (0xF7)
-    def test_send_sysex_message(self):
-        # 0x79 is queryFirmware, but that doesn't matter for now
-        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',
-            '\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.
-        """
-        self.board.analog[4].enable_reporting()
-        self.board.sp.clear()
-        # Crap
-        self.board.sp.write([chr(i) for i in range(10)])
-        # This should set analog port 4 to 1
-        self.board.sp.write([chr(pyfirmata.ANALOG_MESSAGE + 4), chr(127), chr(7)])
-        # Crap
-        self.board.sp.write([chr(10-i) for i in range(10)])
-        while len(self.board.sp):
-            self.board.iterate()
-        self.assertEqual(self.board.analog[4].read(), 1.0)
-
-    # Servo config
-    # --------------------
-    # 0  START_SYSEX (0xF0)
-    # 1  SERVO_CONFIG (0x70)
-    # 2  pin number (0-127)
-    # 3  minPulse LSB (0-6)
-    # 4  minPulse MSB (7-13)
-    # 5  maxPulse LSB (0-6)
-    # 6  maxPulse MSB (7-13)
-    # 7  END_SYSEX (0xF7)
-    #
-    # then sets angle
-    # 8  analog I/O message (0xE0)
-    # 9  angle LSB
-    # 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),
-            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),
-            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),
-            to_two_bytes(2000), chr(0xF7))
-        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_servo_config_invalid_pin(self):
-        self.assertRaises(IOError, self.board.servo_config, 1)
-
-    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),
-            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:
-            if pin.PWM_CAPABLE:
-                pins.append(self.board.get_pin('d:%d:p' % pin.pin_number))
-        for pin in pins:
-            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
-    TestBoardMessages as it should pass the same tests, but with the
-    MockupBoard.
-    """
-    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
-        it to be high.
-        """
-        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,
-            mask % 128, mask >> 7)
-        self.assertEqual(pin.value, True)
-
-    def test_handle_digital_inputs(self):
-        """
-        Test if digital inputs are correctly updated.
-        """
-        for i in range(8, 16): # pins of port 1
-            if not bool(i%2) and i != 14: # all even pins
-                self.board.digital[i].mode = pyfirmata.INPUT
-                self.assertEqual(self.board.digital[i].value, None)
-        mask = 0
-        # Set the mask high for the first 4 pins
-        for i in range(4):
-            mask |= 1 << i
-        self.board._handle_digital_message(1, mask % 128, mask >> 7)
-        self.assertEqual(self.board.digital[8].value, True)
-        self.assertEqual(self.board.digital[9].value, None)
-        self.assertEqual(self.board.digital[10].value, True)
-        self.assertEqual(self.board.digital[11].value, None)
-        self.assertEqual(self.board.digital[12].value, False)
-        self.assertEqual(self.board.digital[13].value, None)
-
-
-board_messages = unittest.TestLoader().loadTestsFromTestCase(TestBoardMessages)
-board_layout = unittest.TestLoader().loadTestsFromTestCase(TestBoardLayout)
-regression = unittest.TestLoader().loadTestsFromTestCase(RegressionTests)
-default = unittest.TestSuite([board_messages, board_layout, regression])
-mockup_suite = unittest.TestLoader().loadTestsFromTestCase(TestMockupBoardLayout)
-
-if __name__ == '__main__':
-    from optparse import OptionParser
-    parser = OptionParser()
-    parser.add_option("-m", "--mockup", dest="mockup", action="store_true",
-        help="Also run the mockup tests")
-    options, args = parser.parse_args()
-    if not options.mockup:
-        print "Running normal suite. Also consider running the mockup (-m, --mockup) suite"
-        unittest.TextTestRunner(verbosity=3).run(default)
-        from pyfirmata import util
-        print "Running doctests for pyfirmata.util. (No output = No errors)"
-        doctest.testmod(util)
-        print "Done running doctests"
-    if options.mockup:
-        print "Running the mockup test suite"
-        unittest.TextTestRunner(verbosity=2).run(mockup_suite)