#! /usr/bin/env python """pyduino - A python library to interface with the firmata arduino firmware. Copyright (C) 2007 Joe Turner This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """ __version__ = "0.11_dev" import time import serial # Message command bytes - straight outta Pd_firmware.pde DIGITAL_MESSAGE = 0x90 # send data for a digital pin ANALOG_MESSAGE = 0xE0 # send data for an analog pin (or PWM) # PULSE_MESSAGE = 0xA0 # proposed pulseIn/Out message (SysEx) # SHIFTOUT_MESSAGE = 0xB0 # proposed shiftOut message (SysEx) REPORT_ANALOG_PIN = 0xC0 # enable analog input by pin # REPORT_DIGITAL_PORTS = 0xD0 # enable digital input by port pair START_SYSEX = 0xF0 # start a MIDI SysEx message SET_DIGITAL_PIN_MODE = 0xF4 # set a digital pin to INPUT or OUTPUT END_SYSEX = 0xF7 # end a MIDI SysEx message REPORT_VERSION = 0xF9 # report firmware version SYSTEM_RESET = 0xFF # reset from MIDI # Pin modes DIGITAL_INPUT = 0 DIGITAL_OUTPUT = 1 DIGITAL_PWM = 2 PWM_PINS = (9, 10, 11) class Arduino: """Base class for the arduino board""" def __init__(self, port): self.sp = serial.Serial(port, 57600, timeout=0.02) # Allow 2 secs for Diecimila auto-reset to happen time.sleep(2) self.digital = [] for i in range(14): self.digital.append(Digital(self.sp, i)) self.analog = [] for i in range(6): self.analog.append(Analog(self.sp, i)) #Obtain firmata version self.sp.write(chr(REPORT_VERSION)) self.iterate() def __str__(self): return "Arduino: %s"% self.sp.port def iterate(self): """Read and handle a command byte from Arduino's serial port""" data = self.sp.read() if data != "": self._process_input(ord(data)) def _process_input(self, data): """Process a command byte and any additional information bytes""" if data < 0xF0: #Multibyte message = data & 0xF0 pin = data & 0x0F if message == DIGITAL_MESSAGE: #Digital in lsb = "" msb = "" while lsb == "": lsb = self.sp.read() while msb == "": msb = self.sp.read() lsb = ord(lsb) msb = ord(msb) self._set_digital_mask(lsb + (msb << 7)) elif message == ANALOG_MESSAGE: #Analog in lsb = "" msb = "" while lsb == "": lsb = self.sp.read() while msb == "": msb = self.sp.read() lsb = ord(lsb) msb = ord(msb) self.analog[pin].value = msb << 7 | lsb elif data == REPORT_VERSION: major, minor = self.sp.read(2) self.firmata_version = (ord(major), ord(minor)) def _set_digital_mask(self, input_mask): """ Alter the digital mask to reflect changes in the digital inputs""" for pin in self.digital: value = (input_mask & 1 << pin.pin) > 0 if pin.mode == DIGITAL_INPUT and value != pin.read(): Digital.mask ^= 1 << pin.pin def get_firmata_version(self): """Return a (major, minor) version tuple for the firmata firmware""" return self.firmata_version def exit(self): """Exit the application cleanly""" self.sp.close()