Pastebin is 300% more awesome when you are logged in. Sign Up, it's FREE!
Guest

Joe Turner

By: a guest on Nov 17th, 2008  |  syntax: Python  |  size: 7.71 KB  |  hits: 70  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #! /usr/bin/env python
  2.  
  3. """pyduino - A python library to interface with the firmata arduino firmware.
  4. Copyright (C) 2007 Joe Turner <orphansandoligarchs@gmail.com>
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  19. """
  20.  
  21. __version__ = "0.11_dev"
  22.  
  23. import time
  24. import serial
  25.  
  26. # Message command bytes - straight outta Pd_firmware.pde
  27. DIGITAL_MESSAGE = 0x90 # send data for a digital pin
  28. ANALOG_MESSAGE = 0xE0 # send data for an analog pin (or PWM)
  29.  
  30. # PULSE_MESSAGE = 0xA0 # proposed pulseIn/Out message (SysEx)
  31. # SHIFTOUT_MESSAGE = 0xB0 # proposed shiftOut message (SysEx)
  32.  
  33. REPORT_ANALOG_PIN = 0xC0 # enable analog input by pin #
  34. REPORT_DIGITAL_PORTS = 0xD0 # enable digital input by port pair
  35. START_SYSEX = 0xF0 # start a MIDI SysEx message
  36. SET_DIGITAL_PIN_MODE = 0xF4 # set a digital pin to INPUT or OUTPUT
  37. END_SYSEX = 0xF7 # end a MIDI SysEx message
  38. REPORT_VERSION = 0xF9 # report firmware version
  39. SYSTEM_RESET = 0xFF # reset from MIDI
  40.  
  41. # Pin modes
  42. DIGITAL_INPUT = 0
  43. DIGITAL_OUTPUT = 1
  44. DIGITAL_PWM = 2
  45.  
  46. PWM_PINS = (9, 10, 11)
  47.  
  48. class Arduino:
  49.     """Base class for the arduino board"""
  50.  
  51.     def __init__(self, port):
  52.         self.sp = serial.Serial(port, 57600, timeout=0.02)
  53.         # Allow 2 secs for Diecimila auto-reset to happen
  54.         time.sleep(2)
  55.  
  56.         self.digital = []
  57.         for i in range(14):
  58.             self.digital.append(Digital(self.sp, i))
  59.  
  60.         self.analog = []
  61.         for i in range(6):
  62.             self.analog.append(Analog(self.sp, i))
  63.  
  64.         #Obtain firmata version
  65.         self.sp.write(chr(REPORT_VERSION))
  66.         self.iterate()
  67.  
  68.     def __str__(self):
  69.         return "Arduino: %s"% self.sp.port
  70.  
  71.     def iterate(self):
  72.         """Read and handle a command byte from Arduino's serial port"""
  73.         data = self.sp.read()
  74.         if data != "":
  75.             self._process_input(ord(data))
  76.  
  77.     def _process_input(self, data):
  78.         """Process a command byte and any additional information bytes"""
  79.         if data < 0xF0:
  80.             #Multibyte
  81.             message = data & 0xF0
  82.             pin = data & 0x0F
  83.             if message == DIGITAL_MESSAGE:
  84.                 #Digital in
  85.                 lsb = ""
  86.                 msb = ""
  87.                 while lsb == "":
  88.                     lsb = self.sp.read()
  89.                 while msb == "":
  90.                     msb = self.sp.read()
  91.                 lsb = ord(lsb)
  92.                 msb = ord(msb)
  93.                 self._set_digital_mask(lsb + (msb << 7))
  94.             elif message == ANALOG_MESSAGE:
  95.                 #Analog in
  96.                 lsb = ""
  97.                 msb = ""
  98.                 while lsb == "":
  99.                     lsb = self.sp.read()
  100.                 while msb == "":
  101.                     msb = self.sp.read()
  102.                 lsb = ord(lsb)
  103.                 msb = ord(msb)
  104.                 self.analog[pin].value = msb << 7 | lsb
  105.         elif data == REPORT_VERSION:
  106.             major, minor = self.sp.read(2)
  107.             self.firmata_version = (ord(major), ord(minor))
  108.  
  109.     def _set_digital_mask(self, input_mask):
  110.         """ Alter the digital mask to reflect changes in the digital inputs"""
  111.         for pin in self.digital:
  112.             value = (input_mask & 1 << pin.pin) > 0
  113.             if pin.mode == DIGITAL_INPUT and value != pin.read():
  114.                 Digital.mask ^= 1 << pin.pin
  115.  
  116.     def get_firmata_version(self):
  117.         """Return a (major, minor) version tuple for the firmata firmware"""
  118.         return self.firmata_version
  119.  
  120.     def exit(self):
  121.         """Exit the application cleanly"""
  122.         self.sp.close()
  123.  
  124. class Digital:
  125.     """Digital pin on the arduino board"""
  126.  
  127.     mask = 0
  128.  
  129.     def __init__(self, sp, pin):
  130.         self.sp = sp
  131.         self.pin = pin
  132.         self.is_active = 0
  133.         self.value = 0
  134.         self.mode = DIGITAL_INPUT
  135.  
  136.     def __str__(self):
  137.         return "Digital Port %i"% self.pin
  138.  
  139.     def set_active(self, active):
  140.         """Set the pin to report values"""
  141.         self.is_active = 1
  142.         pin = REPORT_DIGITAL_PORTS + self.pin
  143.         self.sp.write(chr(pin) + chr(active))
  144.  
  145.     def get_active(self):
  146.         """Return whether the pin is reporting values"""
  147.         return self.is_active
  148.  
  149.     def set_mode(self, mode):
  150.         """Set the mode of operation for the pin
  151.        
  152.        Argument:
  153.        mode, takes a value of: - DIGITAL_INPUT
  154.                                - DIGITAL_OUTPUT
  155.                                - DIGITAL_PWM
  156.  
  157.        """
  158.         if mode == DIGITAL_PWM and self.pin not in PWM_PINS:
  159.             error_message = "Digital pin %i does not have PWM capabilities" \
  160.                             % (self.pin)
  161.             raise IOError, error_message
  162.         if self.pin < 2:
  163.             raise IOError, "Cannot set mode for Rx/Tx pins"
  164.         self.mode = mode
  165.         command = chr(SET_DIGITAL_PIN_MODE) + chr(self.pin) + chr(mode)
  166.         self.sp.write(command)
  167.  
  168.     def get_mode(self):
  169.         """Return the pin mode, values explained in set_mode()"""
  170.         return self.mode
  171.  
  172.     def read(self):
  173.         """Return the output value of the pin, values explained in write()"""
  174.         if self.mode == DIGITAL_PWM:
  175.             return self.value
  176.  
  177.     def read(self):
  178.         """Return the output value of the pin, values explained in write()"""
  179.         if self.mode == DIGITAL_PWM:
  180.             return self.value
  181.         else:
  182.             return (self.__class__.mask & 1 << self.pin) > 0
  183.  
  184.     def write(self, value):
  185.         """Output a voltage from the pin
  186.  
  187.        Argument:
  188.        value, takes a boolean if the pin is in output mode, or a value from 0
  189.        to 255 if the pin is in PWM mode
  190.  
  191.        """
  192.         if self.mode == DIGITAL_INPUT:
  193.             error_message = "Digital pin %i is not an output"% self.pin
  194.             raise IOError, error_message
  195.         elif value != self.read():
  196.             if self.mode == DIGITAL_OUTPUT:
  197.                 #Shorter variable dammit!
  198.                 mask = self.__class__.mask
  199.                 mask ^= 1 << self.pin
  200.                 message = chr(DIGITAL_MESSAGE) + chr(mask % 128) \
  201.                           + chr(mask >> 7)
  202.                 self.sp.write(message)
  203.                 #Set the attribute to the new mask
  204.                 self.__class__.mask = mask
  205.             elif self.mode == DIGITAL_PWM:
  206.                 self.value = value
  207.                 pin = ANALOG_MESSAGE + self.pin
  208.                 self.sp.write(chr(pin) + chr(value % 128) + chr(value >> 7))
  209.  
  210.  
  211. class Analog:
  212.     """Analog pin on the arduino board"""
  213.  
  214.     def __init__(self, sp, pin):
  215.         self.sp = sp
  216.         self.pin = pin
  217.         self.active = 0
  218.         self.value = -1
  219.  
  220.     def __str__(self):
  221.         return "Analog Input %i"% self.pin
  222.  
  223.     def set_active(self, active):
  224.         """Set the pin to report values"""
  225.         self.active = active
  226.         pin = REPORT_ANALOG_PIN + self.pin
  227.         self.sp.write(chr(pin) + chr(active))
  228.  
  229.     def get_active(self):
  230.         """Return whether the pin is reporting values"""
  231.         return self.active
  232.  
  233.     def read(self):
  234.         """Return the input in the range 0-1023"""
  235.         return self.value