#! /usr/bin/env python
"""pyduino - A python library to interface with the firmata arduino firmware.
Copyright (C) 2007 Joe Turner <orphansandoligarchs@gmail.com>
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()