From a44ff2e41d579c2dc9079e6820ab8b618889021d Mon Sep 17 00:00:00 2001 From: Tristan Hearn Date: Thu, 9 Aug 2012 20:33:54 -0400 Subject: [PATCH] first commit --- MIT license.txt | 19 +++ README.md | 84 +++++++++++++ arduino/__init__.py | 5 + arduino/arduino.py | 265 ++++++++++++++++++++++++++++++++++++++++ arduino/examples.py | 76 ++++++++++++ prototype/prototype.ino | 178 +++++++++++++++++++++++++++ 6 files changed, 627 insertions(+) create mode 100644 MIT license.txt create mode 100644 README.md create mode 100644 arduino/__init__.py create mode 100644 arduino/arduino.py create mode 100644 arduino/examples.py create mode 100644 prototype/prototype.ino diff --git a/MIT license.txt b/MIT license.txt new file mode 100644 index 0000000..0e1f20e --- /dev/null +++ b/MIT license.txt @@ -0,0 +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..76f0bc9 --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# Python Arduino Command API + +> © 2012-2013 Tristan A. Hearn +> under the MIT License + +Based on the Python Arduino Prototyping API by Akash Manohar (https://github.com/HashNuke/Python-Arduino-Prototyping-API/). + +The Python Arduino Command API is a light-weight Python package for communicating with Arduino microcontroller boards. It is written +using a custom protocol, similair to Firmata (http://firmata.org/wiki/Main_Page). This allows a user to quickly protoype programs +for Arduino or to simply read and control harware connected to an Arduino from a host computer, without having to reload sketches onto an Arduino board. + +Method names within the Python Arduino Command API are designed to be as close as possible to their Arduino programming language counterparts. + +## Usage example + #!/usr/bin/env python + #Example 'Blink' program + + from arduino import Arduino + import time + + board = Arduino('9600') + + while True: + board.digitalWrite(13, "LOW") + time.sleep(1) + board.digitalWrite(13, "HIGH") + time.sleep(1) +Python-Arduino-Command-API +For more examples, see arduino/examples.py. This file contains methods which replicate +the functionality of many Arduino demo sketches. + +#### Requirements: +* Python 2.3 or higher (Python 3.x not yet tested) +* PySerial +* Arduino compatible microcontroller with at least 14KB of memory + +#### To-do list: +* Add simple reset functionality that zeros out all pin values +* Add I2C / TWI function support (Arduino Wire.h commands) +* Add Servo support (Arduino Servo.h commands) +* Add tone() / noTone() squarewave generator support for piezo type speakers +* Make a program which generates 'prototype.ino' with selected Arduino function support, to help reduce memory requirements. +* Multi-serial support for Arduino mega (Serial1.read(), etc) + +#### Setup: + +1. Load the sketch prototype.ino onto your Arduino board. +2. Import the arduino library into your python script. + +## Methods + +*Arduino(baud)* - Set up communication with currently connected and powered Arduino. + +The device name / COM port will be auto-detected. If there are more than one Arduino boards connected, +the desired COM port can be also be passed: +*Arduino(baud, port = "COM3")* +A time-out for reading from the Arduino can also be specified: +*Arduino(baud, timeout = 2)* + +**Digital I/O** + +* *Arduino.digitalWrite(pin_number, state)* - turn digital pin on/off +* *Arduino.digitalRead(pin_number)* - read state of a digital pin +* *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 + +**Analog I/O** + +* *Arduino.analogRead(pin_number)* - returns the analog value +* *Arduino.analogWrite(pin_number, value)* - sets the analog value + +**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 + +**Misc** + +* *Arduino.close()* - closes serial connection to the Arduino. \ No newline at end of file diff --git a/arduino/__init__.py b/arduino/__init__.py new file mode 100644 index 0000000..aa0476b --- /dev/null +++ b/arduino/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python + + +from arduino import Arduino + diff --git a/arduino/arduino.py b/arduino/arduino.py new file mode 100644 index 0000000..9964394 --- /dev/null +++ b/arduino/arduino.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python +from serial.tools import list_ports +import serial, time + +class SoftwareSerial(object): + """ + Class for Arduino software serial functionality + """ + def __init__(self,board): + self.board=board + 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),"$!"]) + self.board.sr.write(cmd_str) + self.board.sr.flush() + response= self.board.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),"$!"]) + self.board.sr.write(cmd_str) + self.board.sr.flush() + response= self.board.sr.readline().replace("\r\n","") + if response == "ss OK": + return True + else: + return False + + def read(self): + """ + returns first bit read from + existing software serial instance + """ + if self.connected: + cmd_str=''.join(["@sr%$!"]) + self.board.sr.write(cmd_str) + self.board.sr.flush() + response= self.board.sr.readline().replace("\r\n","") + if response: + return response + else: + return False + +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 + self.SoftwareSerial = SoftwareSerial(self) + if port == "": + self.findPort() + self.sr = serial.Serial(self.port, self.baud,timeout =self.timeout) + time.sleep(2) + + def findPort(self): + """ + Returns 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 + + +if __name__=="__main__": + board=Arduino(9600) + while True: + time.sleep(0.01) + val=board.analogRead(5)/4 + board.analogWrite(11,val) + \ No newline at end of file diff --git a/arduino/examples.py b/arduino/examples.py new file mode 100644 index 0000000..6945175 --- /dev/null +++ b/arduino/examples.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +from arduino import Arduino +import time + +def Blink(led_pin,baud): + """ + Blinks an LED in 1 sec intervals + """ + board = Arduino(baud) + 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): + """ + Fades an LED off and on, using + Arduino's analogWrite (PWM) function + """ + board=Arduino(baud) + 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): + """ + Adjusts brightness of an LED using a + potentiometer + """ + board=Arduino(baud) + while True: + time.sleep(0.01) + val=board.analogRead(pot_pin)/4 + print val + board.analogWrite(led_pin,val) + + +def PingSonar(pw_pin,baud): + """ + Gets distance measurement from Ping))) + ultrasonic rangefinder connected to pw_pin + """ + board = Arduino(baud) + 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): + """ + Prints to two-line LCD connected to + pin tx + """ + board = Arduino(baud) + board.SoftwareSerial.begin(0,tx,ssbaud) + while True: + board.SoftwareSerial.write(" test ") + + + + +if __name__=="__main__": + #LCD(5,9600,9600," test ") + adjustBrightness(5,11,9600) + #softBlink(11,9600) \ No newline at end of file diff --git a/prototype/prototype.ino b/prototype/prototype.ino new file mode 100644 index 0000000..ff381f4 --- /dev/null +++ b/prototype/prototype.ino @@ -0,0 +1,178 @@ +#include +#include +#include + +SoftwareSerial *sserial = NULL; + +boolean connected = false; + +int Str2int (String Str_value) +{ + char buffer[10]; //max length is three units + Str_value.toCharArray(buffer, 10); + int int_value = atoi(buffer); + return int_value; +} + +void split(String results[], int len, String input, char spChar) { + String temp = input; + for (int i=0; ibegin(baud_); + Serial.println("ss OK"); +} + +void SS_write(String data) { + int len = data.length()+1; + char buffer[len]; + data.toCharArray(buffer,len); + Serial.println("ss OK"); + sserial->write(buffer); +} +void SS_read(String data) { + char c = sserial->read(); + Serial.println(c); +} + +void pulseInHandler(String data){ + int pin = Str2int(data); + long duration; + if(pin <=0){ + pinMode(-pin, INPUT); + duration = pulseIn(-pin, LOW); + }else{ + pinMode(pin, INPUT); + duration = pulseIn(pin, HIGH); + } + Serial.println(duration); +} + +void pulseInSHandler(String data){ + int pin = Str2int(data); + long duration; + if(pin <=0){ + pinMode(-pin, OUTPUT); + digitalWrite(-pin, HIGH); + delayMicroseconds(2); + digitalWrite(-pin, LOW); + delayMicroseconds(5); + digitalWrite(-pin, HIGH); + pinMode(-pin, INPUT); + duration = pulseIn(-pin, LOW); + }else{ + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + delayMicroseconds(2); + digitalWrite(pin, HIGH); + delayMicroseconds(5); + digitalWrite(pin, LOW); + pinMode(pin, INPUT); + duration = pulseIn(pin, HIGH); + } + Serial.println(duration); +} + +void SerialParser(void) { + char readChar[64]; + Serial.readBytesUntil(33,readChar,64); + String read_ = String(readChar); + //Serial.println(readChar); + int idx1 = read_.indexOf('%'); + int idx2 = read_.indexOf('$'); + // separate command from associated data + String cmd = read_.substring(1,idx1); + String data = read_.substring(idx1+1,idx2); + + // determine command sent + if (cmd == "dw") { + DigitalHandler(1, data); + } + else if (cmd == "dr") { + DigitalHandler(0, data); + } + else if (cmd == "aw") { + AnalogHandler(1, data); + } + else if (cmd == "ar") { + AnalogHandler(0, data); + } + else if (cmd == "pm") { + ConfigurePinHandler(data); + } + else if (cmd == "ps") { + pulseInSHandler(data); + } + else if (cmd == "pi") { + pulseInHandler(data); + } + else if (cmd == "ss") { + SS_set(data); + } + else if (cmd == "sw") { + SS_write(data); + } + else if (cmd == "sr") { + SS_read(data); + } +} + + +void setup() { + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for Leonardo only + } +} + +void loop() { + SerialParser(); + }