""" d5000_serial.py -- A python module for interfacing with a d5000 series serial device Copyright (C) 2005 Eli Fulkerson ------------------------------------ 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. ---------------------------------------------------------------------- Other license terms may be negotiable. Contact the author if you would like a copy that is licensed differently. In particular, the researchers at the Robert Wood Johnson University Hospital are free to use this module however they choose, without being bound by anything GPL. ---------------------------------------------------------------------- Additionally, this script requires the use of "pyserial", which is licensed separately by its author. This library can be found at http://pyserial.sourceforge.net/ Contact information (as well as this script) lives at http://www.elifulkerson.com """ # Python standard libraries from string import * import sys from getopt import getopt, GetoptError from time import sleep, clock # PySerial - http://pyserial.sourceforge.net/ import serial def usage(): "print out some documentation, then exit. This could be prettier." usagestring=""" Usage: sensorname = d5000_sensor("OPTIONS") ... connects to a d5000 series sensor with the specified com port addressing: Serial Options: -p, --port : Specify the desired serial port. (0 for COM1, 1 for COM2 etc) -r, --baudrate : Specify baudrate -s, --bytesize : Specify bytesize -y, --parity : Specify parity options -b, --stopbits : Specify number of stopbits -t, --timeout : Specify timeout -f, --flow : Specify flow-control options ... once thats done, you can use: sensorname.sendcmd("command", "address", "prompt") to query the device. The raw response on the serial port is returned, you have to handle that yourself. Spec says its just a string, so that should be fine. """ print usagestring sys.exit(0) class sensor: "A class to communicate with a DGH D500 series device" def __init__(self,argv=""): "set up the serial port on initialization" # need to split it manually since we are being fed a string, not a real argv anymore argv = argv.split() "Parse our arguments" try: options, args = getopt(argv, "p:r:s:y:b:t:f:h", ["port=", "baudrate=", "bytesize=", "parity=", "stopbits=", "timeout=", "flow=", "help"]) except GetoptError: usage() return "first, loop through and open the right port" got_a_serial_port = False for o,a in options: if o in ("-p", "--port"): a = int(a) try: com = serial.Serial(a) #print "Serial port opened: %s" % (com.portstr) got_a_serial_port = True except: print "Couldn't open serial port: %s" % (a) print "This should be a numerical value. 0 == COM1, 1 == COM2, etc" sys.exit(1) if o in ("-h", "--help"): usage() return if not got_a_serial_port: # we don't have a port. Fine, use the default. try: com = serial.Serial(0) #print "Serial port opened: %s" % (com.portstr) except: print "Couldn't open serial port: %s" % (0) sys.exit(1) # sensible defaults com.baudrate = 9600 com.timeout = 0 com.bytesize = serial.EIGHTBITS com.parity = serial.PARITY_NONE com.stopbits = serial.STOPBITS_ONE com.xonxoff = 0 com.rtscts = 0 # now loop through the other options for o,a in options: if o in ("-r", "--baudrate"): a = int(a) if a in com.BAUDRATES: com.baudrate = a else: print "Valid baudrates are:", com.BAUDRATES sys.exit(1) if o in ("-s", "--bytesize"): a = int(a) if a in com.BYTESIZES: com.bytesize = a else: print "Valid bytesizes are:", com.BYTESIZES sys.exit(1) if o in ("-y", "--parity"): if a in com.PARITIES: com.parity = a else: print "Valid parities are:", com.PARITIES sys.exit(1) if o in ("-b", "--stopbits"): if a in com.STOPBITS: com.stopbits = a else: print "Valid stopbits are:", com.STOPBITS sys.exit(1) if o in ("-t", "--timeout"): a = int(a) if a < 0 or a > 100: print "Valid timesouts are 0-100." sys.exit(1) else: com.timeout = a if o in ("-f", "--flow"): FLOWS = ("xonxoff", "rtscts", "none") if a in FLOWS: if a == "xonxoff": com.xonxoff = True if a == "rtscts": com.rtscts = True else: print "Valid flow-controls are:", FLOWS sys.exit(1) try: com.open() #print "Com port open successfully.." except serial.SerialException: print "Error opening serial port. Is it in use?" print "Exiting..." sys.exit(1) self.com = com # print out com's statistics if 0: print "------------------------" print "Serial Port Information:" print "------------------------" print "port: %s" % com.portstr print "baudrate: %s" % com.baudrate print "bytesize: %s" % com.bytesize print "parity: %s" % com.parity print "stopbits: %s" % com.stopbits print "timeout: %s" % com.timeout print "xonxoff: %s" % com.xonxoff print "rtscts: %s" % com.rtscts print "" def __recv_serial(self): "Recieve some data from the serial port" data = self.com.read(self.com.inWaiting() ) return data def __send_serial(self,data): "Send some data out to the serial port" self.com.write(data) def rawcmd(self, command_string): # before I send my command, I want to "burn off" any old data sitting in the queue... just in case self.__recv_serial() self.__send_serial(command_string + chr(13)) # ok, now lets wait for an answer delinated by a carriage return... response = "" done = False sanitycounter = 0 # so if the device doesn't respond, we don't wait forever while not done: response = response + self.__recv_serial() if len(response) > 1 and response[-1] == chr(13): done = True if sanitycounter == 100: # with the sleep(0.001) this should be no more than a tenth of a second later return "*SERIAL ERROR*" sanitycounter = sanitycounter + 1 sleep(0.001) return response def sendcmd(self, command, address, prompt): "Construct a D5000 command, send it to the device at address 1 on this port, return the response" address = str(address) prompt = str(prompt) command = str(command) if len(address) != 1: print "Error: according to d5000 spec address must only be 1 character." sys.exit(1) if prompt != '$' and prompt != '#': print "Error: according to d5000 spec prompt must be '$' or '#'" sys.exit(1) command_string = prompt + address + command response = self.rawcmd(command_string) return response class module: "A specific module hanging of off the d5000 attached to 'sensor'" def __init__(self, sensor, address): "Set up the module" address = str(address) if len(address) > 1: print "Error: according to d5000 spec address must only be 1 character." sys.exit(1) self.address = address self.sensor = sensor def sendcmd(self, command): "Send a command code to this module, return the response" return self.sensor.sendcmd(command, self.address, '$') def to_float(strval): "convert the wonky ' *+00026.00' format to a floating point number" #this assumes that you can just burn off the three rightmost values... which #might not be the case for all d5000 output strval = str(strval)[3:] num = float(strval) return num