diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/Arduino/__init__.py b/Arduino/__init__.py index e440668..2942e63 100644 --- a/Arduino/__init__.py +++ b/Arduino/__init__.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python - - -from arduino import Arduino - +#!/usr/bin/env python + + +from arduino import Arduino,Shrimp + diff --git a/Arduino/arduino.py b/Arduino/arduino.py index 973444e..c1cd67b 100644 --- a/Arduino/arduino.py +++ b/Arduino/arduino.py @@ -1,355 +1,502 @@ -#!/usr/bin/env python -from serial.tools import list_ports -import serial, time - -class Arduino(object): - def __init__(self,baud,port="",timeout=2): - """ - Initializes serial communication with Arduino. - Attempts to self-select COM port, if not specified. - """ - self.baud = baud - self.timeout = timeout - self.ss_connected=False - if port == "": - self.findPort() - self.sr = serial.Serial(self.port, self.baud,timeout =self.timeout) - time.sleep(2) - self.SoftwareSerial = SoftwareSerial(self) - self.Servos = Servos(self) - - def findPort(self): - """ - Sets port to the first Arduino found - in system's device list - """ - for pt in list_ports.comports(): - if ("FTDIBUS" in pt[-1]) or ("usbserial" in pt[-1]): - self.port = pt[0] - return - - def digitalWrite(self,pin,val): - """ - Sends digitalWrite command - to digital pin on Arduino - ------------- - inputs: - pin : digital pin number - val : either "HIGH" or "LOW" - """ - if val=="LOW": - pin_ = -pin - else: - pin_ = pin - cmd_str=''.join(["@dw%",str(pin_),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - - def analogWrite(self,pin,val): - """ - Sends analogWrite pwm command - to pin on Arduino - ------------- - inputs: - pin : pin number - val : integer 0 (off) to 255 (always on) - """ - if val>255: - val=255 - elif val<0: - val=0 - cmd_str=''.join(["@aw%",str(pin),"%",str(val),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - - def analogRead(self,pin): - """ - Returns the value of a specified - analog pin. - inputs: - pin : analog pin number for measurement - returns: - value: integer from 1 to 1023 - """ - cmd_str=''.join(["@ar%",str(pin),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - rd = self.sr.readline().replace("\r\n","") - try: - return int(rd) - except: - return 0 - - - def pinMode(self,pin,val): - """ - Sets I/O mode of pin - inputs: - pin: pin number to toggle - val: "INPUT" or "OUTPUT" - """ - if val=="INPUT": - pin_ = -pin - else: - pin_ = pin - cmd_str=''.join(["@pm%",str(pin_),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - - def pulseIn(self,pin,val): - """ - Reads a pulse from a pin - - inputs: - pin: pin number for pulse measurement - returns: - duration : pulse length measurement - - """ - if val=="LOW": - pin_ = -pin - else: - pin_ = pin - cmd_str=''.join(["@pi%",str(pin_),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - rd = self.sr.readline().replace("\r\n","") - try: - return float(rd) - except: - return -1 - - def pulseIn_set(self,pin,val): - """ - Sets a digital pin value, then reads the response - as a pulse width. - Useful for some ultrasonic rangefinders, etc. - - inputs: - pin: pin number for pulse measurement - val: "HIGH" or "LOW". Pulse is measured - when this state is detected - returns: - duration : pulse length measurement - - This method will automatically toggle - I/O modes on the pin and precondition the - measurment with a clean LOW/HIGH pulse. - Arduino.pulseIn_set(pin,"HIGH") is - equivalent to the Arduino sketch code: - - pinMode(pin, OUTPUT); - digitalWrite(pin, LOW); - delayMicroseconds(2); - digitalWrite(pin, HIGH); - delayMicroseconds(5); - digitalWrite(pin, LOW); - pinMode(pin, INPUT); - long duration = pulseIn(pin, HIGH); - """ - if val=="LOW": - pin_ = -pin - else: - pin_ = pin - cmd_str=''.join(["@ps%",str(pin_),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - rd = self.sr.readline().replace("\r\n","") - try: - return float(rd) - except: - return -1 - - def close(self): - self.sr.close() - - def digitalRead(self,pin): - """ - Returns the value of a specified - digital pin. - inputs: - pin : digital pin number for measurement - returns: - value: 0 for "LOW", 1 for "HIGH" - """ - cmd_str=''.join(["@dr%",str(pin),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - rd = self.sr.readline().replace("\r\n","") - try: - return 1 - int(rd) - except: - return 0 - -class Wires(object): - """ - Class for Arduino wire (i2c) support - """ - def __init__(self, board): - self.board = board - self.sr = board.sr - -class Servos(object): - """ - Class for Arduino servo support - 0.03 second delay noted - """ - def __init__(self, board): - self.board = board - self.sr = board.sr - self.servo_pos = {} - - def attach(self,pin,min = 544, max = 2400): - cmd_str=''.join(["@sva%",str(pin),"%",str(min),"%",str(max),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - rd = self.sr.readline().replace("\r\n","") - try: - position = int(rd) - self.servo_pos[pin] = position - return 1 - except: - return 0 - - def detach(self,pin): - cmd_str=''.join(["@svd%",str(position),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - del self.servo_pos[pin] - - def write(self,pin,angle): - position = self.servo_pos[pin] - cmd_str=''.join(["@svw%",str(position),"%",str(angle),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - - def writeMicroseconds(self,pin,uS): - cmd_str=''.join(["@svw%",str(position),"%",str(uS),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - - def read(self,pin): - if pin not in self.servo_pos.keys(): - self.attach(pin) - position = self.servo_pos[pin] - cmd_str=''.join(["@svr%",str(position),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - rd = self.sr.readline().replace("\r\n","") - try: - angle = int(rd) - return angle - except: - return None - -class SoftwareSerial(object): - """ - Class for Arduino software serial functionality - """ - def __init__(self,board): - self.board=board - self.sr = board.sr - self.connected = False - - def begin(self,p1,p2,baud): - """ - Create software serial instance on - specified tx,rx pins, at specified baud - """ - cmd_str=''.join(["@ss%",str(p1),"%",str(p2),"%",str(baud),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - response= self.sr.readline().replace("\r\n","") - if response == "ss OK": - self.connected = True - return True - else: - self.connected = False - return False - - def write(self,data): - """ - sends data to existing software serial instance - using Arduino's 'write' function - """ - if self.connected: - cmd_str=''.join(["@sw%",str(data),"$!"]) - try: - self.sr.write(cmd_str) - self.sr.flush() - except: - pass - response= self.sr.readline().replace("\r\n","") - if response == "ss OK": - return True - else: - return False - - def read(self): - """ - returns first character read from - existing software serial instance - """ - if self.connected: - cmd_str=''.join(["@sr%$!"]) - self.sr.write(cmd_str) - self.sr.flush() - response= self.sr.readline().replace("\r\n","") - if response: - return response - else: - return False - -if __name__=="__main__": - # quick test - board=Arduino(9600) - board.Servos.attach(9) - board.Servos.write(9,90) - time.sleep(1) - print board.Servos.read(9) - t=time.time() - board.Servos.write(9,1) - while True: - a=board.Servos.read(9) - if a == 1: - print "time",time.time() - t - break \ No newline at end of file +#!/usr/bin/env python +from serial.tools import list_ports +import serial, time +import platform +if platform.system() == 'Windows': + import _winreg as winreg +else: + import glob +import itertools + + +def enumerate_serial_ports(): + """ Uses the Win32 registry to return a iterator of serial + (COM) ports existing on this computer. + """ + path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM' + try: + key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path) + except WindowsError: + raise IterationError + + for i in itertools.count(): + try: + val = winreg.EnumValue(key, i) + yield (str(val[1]))#, str(val[0])) + except EnvironmentError: + break + + +class Arduino(object): + def __init__(self,baud=9600,port="",timeout=2): + """ + Initializes serial communication with Arduino. + Attempts to self-select COM port, if not specified. + """ + self.baud = baud + self.timeout = timeout + self.ss_connected=False + self.port = port + if self.port == "": + self.findPort() + self.SoftwareSerial = SoftwareSerial(self) + self.Servos = Servos(self) + self.sr.flush() + + + def version(self): + cmd_str=''.join(["@version%$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + version = self.sr.readline().replace("\r\n","") + return version + + + def findPort(self): + """ + Sets port to the first Arduino found + in system's device list + """ + if platform.system() == 'Windows': + ports = enumerate_serial_ports() + else: + ports = glob.glob("/dev/ttyUSB*") + for p in ports: + print 'Found ', p + version = None + try: + print 'Testing ', p + self.sr = serial.Serial(p, self.baud,timeout=self.timeout) + time.sleep(2) + version = self.version() + if version != 'version': + raise Exception('This is not a Shrimp/Arduino!') + self.port = p + print p, 'passed' + break + except Exception, e: + print "Exception: ", e + pass + + + def digitalWrite(self,pin,val): + """ + Sends digitalWrite command + to digital pin on Arduino + ------------- + inputs: + pin : digital pin number + val : either "HIGH" or "LOW" + """ + if val=="LOW": + pin_ = -pin + else: + pin_ = pin + cmd_str=''.join(["@dw%",str(pin_),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + + + def analogWrite(self,pin,val): + """ + Sends analogWrite pwm command + to pin on Arduino + ------------- + inputs: + pin : pin number + val : integer 0 (off) to 255 (always on) + """ + if val>255: + val=255 + elif val<0: + val=0 + cmd_str=''.join(["@aw%",str(pin),"%",str(val),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + + + def analogRead(self,pin): + """ + Returns the value of a specified + analog pin. + inputs: + pin : analog pin number for measurement + returns: + value: integer from 1 to 1023 + """ + cmd_str=''.join(["@ar%",str(pin),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + rd = self.sr.readline().replace("\r\n","") + try: + return int(rd) + except: + return 0 + + + def pinMode(self,pin,val): + """ + Sets I/O mode of pin + inputs: + pin: pin number to toggle + val: "INPUT" or "OUTPUT" + """ + if val=="INPUT": + pin_ = -pin + else: + pin_ = pin + cmd_str=''.join(["@pm%",str(pin_),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + + + def pulseIn(self,pin,val): + """ + Reads a pulse from a pin + + inputs: + pin: pin number for pulse measurement + returns: + duration : pulse length measurement + """ + if val=="LOW": + pin_ = -pin + else: + pin_ = pin + cmd_str=''.join(["@pi%",str(pin_),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + rd = self.sr.readline().replace("\r\n","") + try: + return float(rd) + except: + return -1 + + + def pulseIn_set(self,pin,val,numTrials=5): + """ + Sets a digital pin value, then reads the response + as a pulse width. + Useful for some ultrasonic rangefinders, etc. + + inputs: + pin: pin number for pulse measurement + val: "HIGH" or "LOW". Pulse is measured + when this state is detected + numTrials: number of trials (for an average) + returns: + duration : an average of pulse length measurements + + This method will automatically toggle + I/O modes on the pin and precondition the + measurment with a clean LOW/HIGH pulse. + Arduino.pulseIn_set(pin,"HIGH") is + equivalent to the Arduino sketch code: + + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + delayMicroseconds(2); + digitalWrite(pin, HIGH); + delayMicroseconds(5); + digitalWrite(pin, LOW); + pinMode(pin, INPUT); + long duration = pulseIn(pin, HIGH); + """ + if val=="LOW": + pin_ = -pin + else: + pin_ = pin + cmd_str=''.join(["@ps%",str(pin_),"$!"]) + durations = [] + for s in range(numTrials): + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + rd = self.sr.readline().replace("\r\n","") + if rd.isdigit() == True: + if (int(rd) > 1) == True: + durations.append(int(rd)) + if len(durations) > 0: + duration = int(sum(durations)) / int(len(durations)) + else: + duration = None + + try: + return float(duration) + except: + return -1 + + + def close(self): + self.sr.flush() + self.sr.close() + + + def digitalRead(self,pin): + """ + Returns the value of a specified + digital pin. + inputs: + pin : digital pin number for measurement + returns: + value: 0 for "LOW", 1 for "HIGH" + """ + cmd_str=''.join(["@dr%",str(pin),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + rd = self.sr.readline().replace("\r\n","") + try: + return 1 - int(rd) + except: + return 0 + + + def Melody(self, pin, melody, durations): + """ + Plays a melody. + inputs: + pin: digital pin number for playback + melody: list of tones + durations: list of duration (4=quarter note, 8=eighth note, etc.) + length of melody should be of same + length as length of duration + + Melodies of the following lenght, can cause trouble + when playing it multiple times. + board.Melody(9,["C4","G3","G3","A3","G3",0,"B3","C4"],[4,8,8,4,4,4,4,4]) + Playing short melodies (1 or 2 tones) didn't cause trouble during testing + """ + NOTES = dict(B0=31,C1=33,CS1=35,D1=37,DS1=39,E1=41,F1=44,FS1=46,G1=49\ + ,GS1=52,A1=55,AS1=58,B1=62,C2=65,CS2=69,D2=73,DS2=78,E2=82\ + ,F2=87,FS2=93,G2=98,GS2=104,A2=110,AS2=117,B2=123,C3=131\ + ,CS3=139,D3=147,DS3=156,E3=165,F3=175,FS3=185,G3=196,GS3=208\ + ,A3=220,AS3=233,B3=247,C4=262,CS4=277,D4=294,DS4=311,E4=330\ + ,F4=349,FS4=370,G4=392,GS4=415,A4=440,AS4=466,B4=494,C5=523\ + ,CS5=554,D5=587,DS5=622,E5=659,F5=698,FS5=740,G5=784,GS5=831\ + ,A5=880,AS5=932,B5=988,C6=1047,CS6=1109,D6=1175,DS6=1245,E6=1319\ + ,F6=1397,FS6=1480,G6=1568,GS6=1661,A6=1760,AS6=1865,B6=1976,C7=2093\ + ,CS7=2217,D7=2349,DS7=2489,E7=2637,F7=2794,FS7=2960,G7=3136\ + ,GS7=3322,A7=3520,AS7=3729,B7=3951,C8=4186,CS8=4435,D8=4699,DS8=4978) + if (type(melody) == list) and (type(durations) == list): + length = len(melody) + cmd_str = "@to%"+str(length)+"%"+str(pin)+"%" + d = "" + if length == len(durations): + for note in range(length): + n = NOTES.get(melody[note]) + cmd_str = cmd_str+str(n)+"%" + for duration in range(len(durations)): + d = str(durations[duration]) + cmd_str = cmd_str+d+"%" + cmd_str = cmd_str[:-1]+"$!" + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + cmd_str=''.join(["@nto%",str(pin),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + else: + return -1 + else: + return -1 + + + def capacitivePin(self, pin): + ''' + Input: + pin (int): pin to use as capacitive sensor + + Use it in a loop! + DO NOT CONNECT ANY ACTIVE DRIVER TO THE USED PIN ! + + the pin is toggled to output mode to discharge the port, + and if connected to a voltage source, + will short circuit the pin, potentially damaging + the Arduino/Shrimp and any hardware attached to the pin. + ''' + cmd_str="@cap%"+str(pin)+"$!" + self.sr.write(cmd_str) + rd = self.sr.readline().replace("\r\n","") + if rd.isdigit() == True: + return int(rd) + + +class Shrimp(Arduino): + def __init__(self): + Arduino.__init__(self) + + +class Wires(object): + """ + Class for Arduino wire (i2c) support + """ + def __init__(self, board): + self.board = board + self.sr = board.sr + + +class Servos(object): + """ + Class for Arduino servo support + 0.03 second delay noted + """ + def __init__(self, board): + self.board = board + self.sr = board.sr + self.servo_pos = {} + + + def attach(self,pin,min = 544, max = 2400): + cmd_str=''.join(["@sva%",str(pin),"%",str(min),"%",str(max),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + rd = self.sr.readline().replace("\r\n","") + try: + position = int(rd) + self.servo_pos[pin] = position + return 1 + except: + return 0 + + + def detach(self,pin): + cmd_str=''.join(["@svd%",str(position),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + del self.servo_pos[pin] + + + def write(self,pin,angle): + position = self.servo_pos[pin] + cmd_str=''.join(["@svw%",str(position),"%",str(angle),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + + + def writeMicroseconds(self,pin,uS): + cmd_str=''.join(["@svw%",str(position),"%",str(uS),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + + + def read(self,pin): + if pin not in self.servo_pos.keys(): + self.attach(pin) + position = self.servo_pos[pin] + cmd_str=''.join(["@svr%",str(position),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + rd = self.sr.readline().replace("\r\n","") + try: + angle = int(rd) + return angle + except: + return None + + +class SoftwareSerial(object): + """ + Class for Arduino software serial functionality + """ + def __init__(self,board): + self.board=board + self.sr = board.sr + self.connected = False + + + def begin(self,p1,p2,baud): + """ + Create software serial instance on + specified tx,rx pins, at specified baud + """ + cmd_str=''.join(["@ss%",str(p1),"%",str(p2),"%",str(baud),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + response= self.sr.readline().replace("\r\n","") + if response == "ss OK": + self.connected = True + return True + else: + self.connected = False + return False + + + def write(self,data): + """ + sends data to existing software serial instance + using Arduino's 'write' function + """ + if self.connected: + cmd_str=''.join(["@sw%",str(data),"$!"]) + try: + self.sr.write(cmd_str) + self.sr.flush() + except: + pass + response= self.sr.readline().replace("\r\n","") + if response == "ss OK": + return True + else: + return False + + + def read(self): + """ + returns first character read from + existing software serial instance + """ + if self.connected: + cmd_str=''.join(["@sr%$!"]) + self.sr.write(cmd_str) + self.sr.flush() + response= self.sr.readline().replace("\r\n","") + if response: + return response + else: + return False diff --git a/MIT license.txt b/MIT license.txt index 366ef45..0e1f20e 100644 --- a/MIT license.txt +++ b/MIT license.txt @@ -1,19 +1,19 @@ -Copyright (c) 2012-2013 Tristan A. Hearn - -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. +Copyright (c) 2012-2013 Tristan A. Hearn + +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. diff --git a/README.md b/README.md index c7b537c..58abba1 100644 --- a/README.md +++ b/README.md @@ -1,163 +1,166 @@ -# Python Arduino Command API - -The Python Arduino Command API is a light-weight Python library for -communicating with [Arduino microcontroller boards](http://www.arduino.cc/) from a connected computer using -standard serial IO, either physically -or wirelessly. It is written using a custom protocol, similar to [Firmata](http://firmata.org/wiki/Main_Page). - -This allows a user to quickly protoype programs for Arduino using Python code, or to -simply read/control/troubleshoot/experiment -with harware connected to an Arduino board without ever having to recompile and reload sketches to the board itself. - -Method names within the Python Arduino Command API are designed to be as close -as possible to their Arduino programming language counterparts. - -Checkout the 'dev' branch for recent experimental support of some features, such as `tone()`. - -## Simple usage example (LED blink) -```python -#!/usr/bin/env python -""" - Blinks an LED on digital pin 13 - in 1 second intervals -""" - -from Arduino import Arduino -import time - -board = Arduino('9600') #plugged in via USB, serial com at rate 9600 - -while True: - board.digitalWrite(13, "LOW") - time.sleep(1) - board.digitalWrite(13, "HIGH") - time.sleep(1) -``` - -## Requirements: -- [Python](http://python.org/) 2.3 or higher (Python 3.x not yet tested, but would probably work) -- [pyserial](http://pyserial.sourceforge.net/) 2.6 or higher -- Any [Arduino compatible microcontroller](https://www.sparkfun.com/) with at least 14KB of flash memory - -## Setup: -1. Run `setup.py build install` to install the library -2. Verify that your Arduino board communicates at the baud rate specified in the -`setup()` function (line 243) in `prototype.ino`. Change it there if necessary. -3. Load the `prototype.ino` sketch onto your Arduino board, using the Arduino IDE. -4. Set up some kind of serial I/O communication between the Arduino board and your computer (via physical USB cable, -bluetooth, xbee, etc + associated drivers) -5. Add `from Arduino import Arduino` into your python script to communicate with your Arduino - -For a collection of examples, see `examples.py`. This file contains methods which replicate -the functionality of many Arduino demo sketches. - -## Classes -- `Arduino(baud)` - Set up communication with currently connected and powered -Arduino. - -```python -board = Arduino("9600") #Example -``` - -The device name / COM port of the connected Arduino will be auto-detected. -If there are more than one Arduino boards connected, -the desired COM port can be also be passed as an optional argument: - -```python -board = Arduino("9600", port = "COM3") #Windows example -``` -```python -board = Arduino("9600", port = "/dev/tty.usbmodemfa141") #OSX example -``` - -A time-out for reading from the Arduino can also be specified as an optional -argument: - -```python -board = Arduino("9600", timeout = 2) #Serial reading functions will -#wait for no more than 2 seconds -``` - -## Methods - -**Digital I/O** - -- `Arduino.digitalWrite(pin_number, state)` turn digital pin on/off -- `Arduino.digitalRead(pin_number)` read state of a digital pin - -```python -#Digital read / write example -board.digitalWrite(13, "HIGH") #Set digital pin 13 voltage -state_1 = board.digitalRead(13) #Will return integer 1 -board.digitalWrite(13, "LOW") #Set digital pin 13 voltage -state_2 = board.digitalRead(13) #Will return integer 0 -``` - -- `Arduino.pinMode(pin_number, io_mode)` set pin I/O mode -- `Arduino.pulseIn(pin_number, state)` measures a pulse -- `Arduino.pulseIn_set(pin_number, state)` measures a pulse, with preconditioning - -```python -#Digital mode / pulse example -board.pinMode(7, "INPUT") #Set digital pin 7 mode to INPUT -duration = board.pulseIn(7, "HIGH") #Return pulse width measurement on pin 7 -``` - -**Analog I/O** - -- `Arduino.analogRead(pin_number)` returns the analog value -- `Arduino.analogWrite(pin_number, value)` sets the analog value - -```python -#Analog I/O examples -val=board.analogRead(5) #Read value on analog pin 5 (integer 0 to 1023) -val = val / 4 # scale to 0 - 255 -board.analogWrite(11) #Set analog value (PWM) based on analog measurement -``` - -**Servo Library Functionality** -Support is included for up to 8 servos. - -- `Arduino.Servo.attach(pin, min = 544, max = 2400)` Create servo instance. Only 8 servos can be used at one time. -- `Arduino.Servo.read(pin)` Returns the angle of the servo attached to the specified pin -- `Arduino.Servo.write(pin, angle)` Move an attached servo on a pin to a specified angle -- `Arduino.Servo.writeMicroseconds(pin, uS)` Write a value in microseconds to the servo on a specified pin -- `Arduino.Servo.detach(pin)` Detaches the servo on the specified pin - -```python -#Servo example -board.Servo.attach(9) #declare servo on pin 9 -board.Servo.write(9, 0) #move servo on pin 9 to 0 degrees -print board.Servo.read(9) # should be 0 -board.Servo.detach(9) #free pin 9 -``` - -**Software Serial Functionality** - -- `Arduino.SoftwareSerial.begin(ss_rxPin,ss_txPin,ss_device_baud)` initialize software serial device on -specified pins. -Only one sofware serial device can be used at a time. Existing software serial instance will -be be overwritten by calling this method, both in Python and on the arduino board. -- `Arduino.SoftwareSerial.write(data)` send data using the arduino 'write' function to the existing software -serial connection. -- `Arduino.SoftwareSerial.read()` returns one byte from the existing software serial connection - -```python -#Software serial example -board.SoftwareSerial.begin(0,7,"19200") # Start software serial for transmit only (tx on pin 7) -board.SoftwareSerial.write(" test ") #Send some data -response_char = board.SoftwareSerial.read() #read response character -``` - -**Misc** - -- `Arduino.close()` closes serial connection to the Arduino. - -## To-do list: -- Expand software serial functionality (`print()` and `println()`) -- Add simple reset functionality that zeros out all pin values -- Add I2C / TWI function support (Arduino `Wire.h` commands) -- Add `tone()` / `noTone()` squarewave generator support for piezo type speakers currently testing code for this (thanks to Sjoerd Dirk Meijer.) -- Include a wizard which generates 'prototype.ino' with selected serial baud rate and Arduino function support -(to help reduce memory requirements). -- Multi-serial support for Arduino mega (`Serial1.read()`, etc) +# Python Arduino Command API + +The Python Arduino Command API is a light-weight Python library for +communicating with [Arduino microcontroller boards](http://www.arduino.cc/) from a connected computer using +standard serial IO, either physically +or wirelessly. It is written using a custom protocol, similar to [Firmata](http://firmata.org/wiki/Main_Page). + +This allows a user to quickly protoype programs for Arduino using Python code, or to +simply read/control/troubleshoot/experiment +with harware connected to an Arduino board without ever having to recompile and reload sketches to the board itself. + +Method names within the Python Arduino Command API are designed to be as close +as possible to their Arduino programming language counterparts. + +Checkout the 'dev' branch for recent experimental support of some features, such as `tone()`. + +## Simple usage example (LED blink) +```python +#!/usr/bin/env python +""" + Blinks an LED on digital pin 13 + in 1 second intervals +""" + +from Arduino import Arduino +import time + +board = Arduino('9600') #plugged in via USB, serial com at rate 9600 + +while True: + board.digitalWrite(13, "LOW") + time.sleep(1) + board.digitalWrite(13, "HIGH") + time.sleep(1) +``` + +## Requirements: +- [Python](http://python.org/) 2.3 or higher (Python 3.x not yet tested, but would probably work) +- [pyserial](http://pyserial.sourceforge.net/) 2.6 or higher +- Any [Arduino compatible microcontroller](https://www.sparkfun.com/) with at least 14KB of flash memory + +## Setup: +1. Run `setup.py build install` to install the library +2. Verify that your Arduino board communicates at the baud rate specified in the +`setup()` function (line 243) in `prototype.ino`. Change it there if necessary. +3. Load the `prototype.ino` sketch onto your Arduino board, using the Arduino IDE. +4. Set up some kind of serial I/O communication between the Arduino board and your computer (via physical USB cable, +bluetooth, xbee, etc + associated drivers) +5. Add `from Arduino import Arduino` into your python script to communicate with your Arduino + +For a collection of examples, see `examples.py`. This file contains methods which replicate +the functionality of many Arduino demo sketches. + +## Classes +- `Arduino(baud)` - Set up communication with currently connected and powered +Arduino. + +```python +board = Arduino("9600") #Example +``` + +The device name / COM port of the connected Arduino will be auto-detected. +If there are more than one Arduino boards connected, +the desired COM port can be also be passed as an optional argument: + +```python +board = Arduino("9600", port = "COM3") #Windows example +``` +```python +board = Arduino("9600", port = "/dev/tty.usbmodemfa141") #OSX example +``` + +A time-out for reading from the Arduino can also be specified as an optional +argument: + +```python +board = Arduino("9600", timeout = 2) #Serial reading functions will +#wait for no more than 2 seconds +``` + +## Methods + +**Digital I/O** + +- `Arduino.digitalWrite(pin_number, state)` turn digital pin on/off +- `Arduino.digitalRead(pin_number)` read state of a digital pin + +```python +#Digital read / write example +board.digitalWrite(13, "HIGH") #Set digital pin 13 voltage +state_1 = board.digitalRead(13) #Will return integer 1 +board.digitalWrite(13, "LOW") #Set digital pin 13 voltage +state_2 = board.digitalRead(13) #Will return integer 0 +``` + +- `Arduino.pinMode(pin_number, io_mode)` set pin I/O mode +- `Arduino.pulseIn(pin_number, state)` measures a pulse +- `Arduino.pulseIn_set(pin_number, state)` measures a pulse, with preconditioning + +```python +#Digital mode / pulse example +board.pinMode(7, "INPUT") #Set digital pin 7 mode to INPUT +duration = board.pulseIn(7, "HIGH") #Return pulse width measurement on pin 7 +``` + +**Analog I/O** + +- `Arduino.analogRead(pin_number)` returns the analog value +- `Arduino.analogWrite(pin_number, value)` sets the analog value + +```python +#Analog I/O examples +val=board.analogRead(5) #Read value on analog pin 5 (integer 0 to 1023) +val = val / 4 # scale to 0 - 255 +board.analogWrite(11) #Set analog value (PWM) based on analog measurement +``` + +**Servo Library Functionality** +Support is included for up to 8 servos. + +- `Arduino.Servo.attach(pin, min = 544, max = 2400)` Create servo instance. Only 8 servos can be used at one time. +- `Arduino.Servo.read(pin)` Returns the angle of the servo attached to the specified pin +- `Arduino.Servo.write(pin, angle)` Move an attached servo on a pin to a specified angle +- `Arduino.Servo.writeMicroseconds(pin, uS)` Write a value in microseconds to the servo on a specified pin +- `Arduino.Servo.detach(pin)` Detaches the servo on the specified pin + +```python +#Servo example +board.Servo.attach(9) #declare servo on pin 9 +board.Servo.write(9, 0) #move servo on pin 9 to 0 degrees +print board.Servo.read(9) # should be 0 +board.Servo.detach(9) #free pin 9 +``` + +**Software Serial Functionality** + +- `Arduino.SoftwareSerial.begin(ss_rxPin,ss_txPin,ss_device_baud)` initialize software serial device on +specified pins. +Only one sofware serial device can be used at a time. Existing software serial instance will +be be overwritten by calling this method, both in Python and on the arduino board. +- `Arduino.SoftwareSerial.write(data)` send data using the arduino 'write' function to the existing software +serial connection. +- `Arduino.SoftwareSerial.read()` returns one byte from the existing software serial connection + +```python +#Software serial example +board.SoftwareSerial.begin(0,7,"19200") # Start software serial for transmit only (tx on pin 7) +board.SoftwareSerial.write(" test ") #Send some data +response_char = board.SoftwareSerial.read() #read response character +``` + +**Misc** + +- `Arduino.close()` closes serial connection to the Arduino. + +## To-do list: +- Expand software serial functionality (`print()` and `println()`) +- Add simple reset functionality that zeros out all pin values +- Add I2C / TWI function support (Arduino `Wire.h` commands) +- Add `tone()` / `noTone()` squarewave generator support for piezo type speakers currently testing code for this (thanks to Sjoerd Dirk Meijer.) +-(sdmeijer) Add Python-code for Tone/NoTone. +- Include a wizard which generates 'prototype.ino' with selected serial baud rate and Arduino function support +(to help reduce memory requirements). +- Multi-serial support for Arduino mega (`Serial1.read()`, etc) +- (sdmeijer) Add capacitive sensors (http://playground.arduino.cc/Code/CapacitiveSensor) + diff --git a/examples.py b/examples.py index b2c033f..8051b3c 100644 --- a/examples.py +++ b/examples.py @@ -1,77 +1,77 @@ -#!/usr/bin/env python -from Arduino import Arduino -import time - -def Blink(led_pin,baud, port = ""): - """ - Blinks an LED in 1 sec intervals - """ - board = Arduino(baud, port=port) - while True: - board.digitalWrite(led_pin,"LOW") - print board.digitalRead(led_pin) #confirm LOW (0) - time.sleep(1) - board.digitalWrite(led_pin,"HIGH") - print board.digitalRead(led_pin) #confirm HIGH (1) - time.sleep(1) - -def softBlink(led_pin,baud, port = ""): - """ - Fades an LED off and on, using - Arduino's analogWrite (PWM) function - """ - board=Arduino(baud, port=port) - i=0 - while True: - i+=1 - k=i%510 - if k%5==0: - if k>255: - k=510-k - board.analogWrite(led_pin,k) - -def adjustBrightness(pot_pin,led_pin,baud, port=""): - """ - Adjusts brightness of an LED using a - potentiometer - """ - board=Arduino(baud, port=port) - while True: - time.sleep(0.01) - val=board.analogRead(pot_pin)/4 - print val - board.analogWrite(led_pin,val) - - -def PingSonar(pw_pin,baud, port=""): - """ - Gets distance measurement from Ping))) - ultrasonic rangefinder connected to pw_pin - """ - board = Arduino(baud, port=port) - pingPin=pw_pin - while True: - duration = board.pulseIn(pingPin, "HIGH") - inches = duration/72./2. - cent = duration/29./2. - print inches,"inches" - time.sleep(0.1) - -def LCD(tx,baud,ssbaud,message, port=""): - """ - Prints to two-line LCD connected to - pin tx - """ - board = Arduino(baud, port=port) - board.SoftwareSerial.begin(0,tx,ssbaud) - while True: - board.SoftwareSerial.write(" test ") - - - - -if __name__=="__main__": - Blink(13,9600) - #LCD(5,9600,9600," test ") - #adjustBrightness(5,11,9600) +#!/usr/bin/env python +from Arduino import Arduino +import time + +def Blink(led_pin,baud, port = ""): + """ + Blinks an LED in 1 sec intervals + """ + board = Arduino(baud, port=port) + while True: + board.digitalWrite(led_pin,"LOW") + print board.digitalRead(led_pin) #confirm LOW (0) + time.sleep(1) + board.digitalWrite(led_pin,"HIGH") + print board.digitalRead(led_pin) #confirm HIGH (1) + time.sleep(1) + +def softBlink(led_pin,baud, port = ""): + """ + Fades an LED off and on, using + Arduino's analogWrite (PWM) function + """ + board=Arduino(baud, port=port) + i=0 + while True: + i+=1 + k=i%510 + if k%5==0: + if k>255: + k=510-k + board.analogWrite(led_pin,k) + +def adjustBrightness(pot_pin,led_pin,baud, port=""): + """ + Adjusts brightness of an LED using a + potentiometer + """ + board=Arduino(baud, port=port) + while True: + time.sleep(0.01) + val=board.analogRead(pot_pin)/4 + print val + board.analogWrite(led_pin,val) + + +def PingSonar(pw_pin,baud, port=""): + """ + Gets distance measurement from Ping))) + ultrasonic rangefinder connected to pw_pin + """ + board = Arduino(baud, port=port) + pingPin=pw_pin + while True: + duration = board.pulseIn(pingPin, "HIGH") + inches = duration/72./2. + cent = duration/29./2. + print inches,"inches" + time.sleep(0.1) + +def LCD(tx,baud,ssbaud,message, port=""): + """ + Prints to two-line LCD connected to + pin tx + """ + board = Arduino(baud, port=port) + board.SoftwareSerial.begin(0,tx,ssbaud) + while True: + board.SoftwareSerial.write(" test ") + + + + +if __name__=="__main__": + Blink(13,9600) + #LCD(5,9600,9600," test ") + #adjustBrightness(5,11,9600) #softBlink(11,9600) \ No newline at end of file diff --git a/sketches/prototype/prototype.ino b/sketches/prototype/prototype.ino index 10a3066..a245952 100644 --- a/sketches/prototype/prototype.ino +++ b/sketches/prototype/prototype.ino @@ -24,6 +24,100 @@ void split(String results[], int len, String input, char spChar) { } } +void Version(){ + Serial.println("version"); +} + +uint8_t readCapacitivePin(String data) { + int pinToMeasure = Str2int(data); + // readCapacitivePin + // Input: Arduino pin number + // Output: A number, from 0 to 17 expressing + // how much capacitance is on the pin + // When you touch the pin, or whatever you have + // attached to it, the number will get higher + // http://playground.arduino.cc/Code/CapacitiveSensor + // + // Variables used to translate from Arduino to AVR pin naming + volatile uint8_t* port; + volatile uint8_t* ddr; + volatile uint8_t* pin; + // Here we translate the input pin number from + // Arduino pin number to the AVR PORT, PIN, DDR, + // and which bit of those registers we care about. + byte bitmask; + port = portOutputRegister(digitalPinToPort(pinToMeasure)); + ddr = portModeRegister(digitalPinToPort(pinToMeasure)); + bitmask = digitalPinToBitMask(pinToMeasure); + pin = portInputRegister(digitalPinToPort(pinToMeasure)); + // Discharge the pin first by setting it low and output + *port &= ~(bitmask); + *ddr |= bitmask; + delay(1); + // Make the pin an input with the internal pull-up on + *ddr &= ~(bitmask); + *port |= bitmask; + + // Now see how long the pin to get pulled up. This manual unrolling of the loop + // decreases the number of hardware cycles between each read of the pin, + // thus increasing sensitivity. + uint8_t cycles = 17; + if (*pin & bitmask) { cycles = 0;} + else if (*pin & bitmask) { cycles = 1;} + else if (*pin & bitmask) { cycles = 2;} + else if (*pin & bitmask) { cycles = 3;} + else if (*pin & bitmask) { cycles = 4;} + else if (*pin & bitmask) { cycles = 5;} + else if (*pin & bitmask) { cycles = 6;} + else if (*pin & bitmask) { cycles = 7;} + else if (*pin & bitmask) { cycles = 8;} + else if (*pin & bitmask) { cycles = 9;} + else if (*pin & bitmask) { cycles = 10;} + else if (*pin & bitmask) { cycles = 11;} + else if (*pin & bitmask) { cycles = 12;} + else if (*pin & bitmask) { cycles = 13;} + else if (*pin & bitmask) { cycles = 14;} + else if (*pin & bitmask) { cycles = 15;} + else if (*pin & bitmask) { cycles = 16;} + + // Discharge the pin again by setting it low and output + // It's important to leave the pins low if you want to + // be able to touch more than 1 sensor at a time - if + // the sensor is left pulled high, when you touch + // two sensors, your body will transfer the charge between + // sensors. + *port &= ~(bitmask); + *ddr |= bitmask; + + //return cycles; + Serial.println(cycles); +} + +void Tone(String data){ + int idx = data.indexOf('%'); + int len = Str2int(data.substring(0,idx)); + String data2 = data.substring(idx+1); + int idx2 = data2.indexOf('%'); + int pin = Str2int(data2.substring(0,idx2)); + String data3 = data2.substring(idx2+1); + String melody[len*2]; + split(melody,len*2,data3,'%'); + + for (int thisNote = 0; thisNote < len; thisNote++) { + int noteDuration = 1000/Str2int(melody[thisNote+len]); + int note = Str2int(melody[thisNote]); + tone(pin, note, noteDuration); + int pause = noteDuration * 1.30; + delay(pause); + noTone(pin); + } +} + +void ToneNo(String data){ + int pin = Str2int(data); + noTone(pin); +} + void DigitalHandler(int mode, String data){ int pin = Str2int(data); if(mode<=0){ //read @@ -235,10 +329,21 @@ void SerialParser(void) { } else if (cmd == "svd") { SV_remove(data); - } + } + else if (cmd == "version") { + Version(); + } + else if (cmd == "to") { + Tone(data); + } + else if (cmd == "nto") { + ToneNo(data); + } + else if (cmd == "cap") { + readCapacitivePin(data); + } } - void setup() { Serial.begin(9600); while (!Serial) {