Maxwell Terry

Interfacing Python and Arduino

Arduino is a physical computing platform, supported by an assortment of microcontroller boards, such as the Diecimila. Since it's an open specification, one could make their own board by hand if desired. Let's look at scripting an Arduino board with Python.

Python is a general-purpose high-level programming language, drawing from the object-oriented, imperative, and functional paradigms, and featuring strong duck typing. It provides built-in data structures like lists, tuples, and dictionaries, a classical object system, and very clean syntax. While functions are first-class citizens, they lack the flexibility of function literals in Lisp, JavaScript, and Ruby (lambdas can only include a single line, or else must be named). It's a very fine language, and particularly noteworthy for its extensive standard library.

Python can easily talk to an Arduino board over a serial interface. While serial devices can be read from and written to like files on Unix-like systems, the pySerial wrapper makes this easier across operating systems.

Let's try reading some data:

>>> import serial
>>> arduino = serial.Serial('/dev/tty.usbserial', 9600)
>>> while 1:
... arduino.readline()
'1 Hello world!\r\n'
'2 Hello world!\r\n'
'3 Hello world!\r\n'

And writing:

>>> ser.write('5')

Let's write an Arduino-specific class.

#!/usr/bin/env python

# Arduino
# Maxwell Terry
# MIT license

import sys, os, time, serial

class Arduino:

connection = None

 def __init__(self, path):
   self.connection = serial.Serial(path, 9600)
   pass

 def __del__(self):
   self.connection.close()
   pass

 def isOpen(self):
   return self.connection.isOpen()

 def readLine(self):
   if self.isOpen():
     if self.connection.inWaiting() > 0:
       return self.connection.readLine()
     else:
       print "---"
   else:
     return None
   pass

 def writeLine(self, line):
   if self.isOpen():
     for i in range(0, len(line)):
       self.connection.write(line[i])
       time.sleep(0.1)

 def flush(self):
   self.connection.flushInput()
   self.connection.flushOutput()

 def readPin(self, pin, line):
   self.flush()
   self.writeLine(line + str(pin))
   result = self.readLine()
   result = result[result.find("Value: ")+7:result.find("Value: ")+10]
   if result[2] == '-':
     result = result[:1]
   return result

 def setPin(self, pin, value):
   self.writeLine("S1" + str(pin) + str(value))
   return True

If you're already familiar with Python, this should be pretty straightforward. Feedback and comments welcome. I'm going to elaborate on the implementation, to serve as a general introduction to Python (assuming one has reasonable experience with other high-level languages).

First notice that with Python, nesting is denoted with indentation rather than braces (C-style languages) or parentheses (Lisp). We start out with the connection instance variable, which is defaulted to None. [1] Python requires one to explicitly pass around the current object, done with the self parameter. [2] __init__ and __del__ are special methods, called on initialization and deletion, allowing one to add polymorphism to built-in operators. [3] Here the methods open and close the serial connection.

isOpen tests whether a connection is open, which is used by readLine and writeLine to determine if the board can be read from or written to, and does so if possible. flush clears the input and output, while readPin and setPin respectively read and write to a given pin.

Obviously, this is only a minimal wrapper over pySerial. We don't need it, but these convenience methods make actions more clear and easier both to debug and extend.

 

1. Python's None is equivalent to JavaScript's null or Arc's nil.

2. Along with significant whitespace and crippled lambdas, this is a common criticism of Python. I personally support the sentiment, but not execution. It's a bit of a hack to just pass state around as the first parameter; that kind of thing seems deserving of syntactic support. I prefer the way JavaScript does it:

example = function() {

this.method = function(x) {
return x
}

this.property = 1

return this

}

Note that JavaScript's this isn't identical to Python's self; the value of JS's depends on the object it's called within (for instance, inside of this.method, this would refer to the context of that function, not example).

3. This kind of functionality debatably offsets Python's general lack of metaprogramming capabilities (i.e. macros), but only provides extensibility to a point.