diff options
-rw-r--r-- | pyserial/CHANGES.txt | 71 | ||||
-rw-r--r-- | pyserial/MANIFEST | 2 | ||||
-rw-r--r-- | pyserial/README.txt | 59 | ||||
-rw-r--r-- | pyserial/examples/miniterm.py | 3 | ||||
-rw-r--r-- | pyserial/examples/test.py | 25 | ||||
-rw-r--r-- | pyserial/examples/test_advanced.py | 156 | ||||
-rw-r--r-- | pyserial/serial/serialjava.py | 215 | ||||
-rw-r--r-- | pyserial/serial/serialposix.py | 306 | ||||
-rw-r--r-- | pyserial/serial/serialutil.py | 247 | ||||
-rw-r--r-- | pyserial/serial/serialwin32.py | 201 | ||||
-rw-r--r-- | pyserial/setup.py | 2 |
11 files changed, 894 insertions, 393 deletions
diff --git a/pyserial/CHANGES.txt b/pyserial/CHANGES.txt index fcdf51b..7e53cda 100644 --- a/pyserial/CHANGES.txt +++ b/pyserial/CHANGES.txt @@ -40,16 +40,17 @@ Version 1.15 04 Jun 2002 - compatibility to win9x improved Version 1.16 02 Jul 2002 - added implementation of RI and corrected RTS/CTS on Win32 + Added implementation of RI and corrected RTS/CTS on Win32 Version 1.17 03 Jul 2002 - silly mix of two versions in win32 code corrected + Silly mix of two versions in win32 code corrected Version 1.18 06 Dec 2002 Bugfixes (general): - - remove the mapping of flush to the destructive flushOutput as this - is not the expected behaviour - - readline: EOL character for lines can be chosen idea by John Florian + - remove the mapping of flush to the destructive flushOutput as + this is not the expected behaviour. + - readline: EOL character for lines can be chosen idea by + John Florian. Bugfixes (posix): - cygwin port numbering fixed - test each and every constant for it's existence in termios module, @@ -62,20 +63,64 @@ Version 1.18 06 Dec 2002 Version 1.19 19 Mar 2003 Bugfixes (posix): - - removed dgux entry which actualy had a wrong comment and is probably not in use anywhere + - removed dgux entry which actualy had a wrong comment and is + probably not in use anywhere. Bugfixes (win32): - added int() conversion, [Bug 702120] - - remove code to set control lines in close methond of win32 version. [Bug 669625] + - remove code to set control lines in close methond of win32 + version. [Bug 669625] Version 1.20 28 Aug 2003 - added serial.device() for all platforms + Added serial.device() for all platforms Bugfixes (win32): - - don't recreate opverlaped structures and events on each read/write - - don't set unneded event masks - - dont use DOS device names for ports > 9 - - remove send timeout (its not used in the linux impl. anyway) + - don't recreate opverlaped structures and events on each + read/write. + - don't set unneded event masks. + - dont use DOS device names for ports > 9. + - remove send timeout (its not used in the linux impl. anyway). Version 1.21 30 sep 2003 Bugfixes (win32): - - name for COM10 was not built correctly, found by Norm Davis + - name for COM10 was not built correctly, found by Norm Davis. + Bugfixes (examples): + - small change in miniterm.py that should mage it run on cygwin, + [Bug 809904] submitted by Rolf Campbell. + +Version 2.0b1 ... + Transition to the 2.0 series: + - New implementation only supports Python 2.2+, backwards compatibility + should be maintained almost everywhere. + The OS handles (like the hComPort or fd attribute) were prefixed with an + underscore. The different names stay, as anyone that uses one of these + has to write platform specific code anyway. + - Common base class serialutil.SerialBase for all implementations. + - PARITY_NONE, PARITY_EVEN, PARITY_ODD constants changed and all these + constants moved to serialutil.py (still available as serial.PARITY_NONE + etc. and they should be used that way) + - Added serial.PARITY_NAMES (implemented in serialutil.PARITY_NAMES). + This dictionary can be used to convert parity constants to meaningful + strings. + - Each Serial class and instance has a list of supported values: + BAUDRATES, BYTESIZES, PARITIES, STOPBITS + (i.e. serial.Serial.BAUDRATES or s = serial.Serial; s.BAUDRATES) + these values can be used to fill in value sin GUI dialogs etc. + - Creating a Serial() object without port spec returns an unconfigured, + closed port. Useful if a GUI dialog should take a port and configure + it. + - New methods for serial.Serial instances: open(), isOpen() + - A port can be opened and closed as many times as desired. + - Instances of serial.Serial have baudrate, bytesize, timeout etc. + attributes implemented as properties, all can be set while the port is + opened. It will then be reconfigured. + - Improved __doc__'s. + - New test_advanced.py for the property setting/getting testing. + - Small bugfix on posix with get* methods (return value should be true a + boolean). + - added a __repr__ that returns a meaningful string will all the serial + setting, easy for debugging. + - The serialposix module does not throw an exception on unsupported + platforms, the message is still printed. The idea that it may still + work even if the platform itself s not known, it simply tries to do + the posix stuff anyway (It's likely that opening ports by number + fails, but by name it should work). diff --git a/pyserial/MANIFEST b/pyserial/MANIFEST index 99e248c..20ea14a 100644 --- a/pyserial/MANIFEST +++ b/pyserial/MANIFEST @@ -10,4 +10,6 @@ serial\serialutil.py examples\miniterm.py examples\tcp_serial_redirect.py examples\test.py +examples\test_advanced.py examples\scan.py +examples\enhancedserial.py diff --git a/pyserial/README.txt b/pyserial/README.txt index 88aef37..8a582f8 100644 --- a/pyserial/README.txt +++ b/pyserial/README.txt @@ -33,7 +33,7 @@ Features Requirements ------------ -- Python 2.0 or newer (1.5.2 untested) +- Python 2.2 or newer - win32all extensions on Windows - "Java Communications" (JavaComm) extension for Java/Jython @@ -43,8 +43,7 @@ Installation Extract files from the archive, open a shell/console in that directory and let Distutils do the rest: "python setup.py install" -The files get installed in the "Lib/site-packages" directory in newer -Python versions. +The files get installed in the "Lib/site-packages" directory. Serial to USB adapters ---------------------- @@ -80,7 +79,7 @@ Open named port at "19200,8,N,1", 1s timeout >>> ser = serial.Serial('/dev/ttyS1', 19200, timeout=1) >>> x = ser.read() #read one byte >>> s = ser.read(10) #read up to ten bytes (timeout) ->>> line = ser.readline() #read a \n terminated line +>>> line = ser.readline() #read a '\n' terminated line >>> ser.close() Open second port at "38400,8,E,1", non blocking HW handshaking @@ -89,6 +88,20 @@ Open second port at "38400,8,E,1", non blocking HW handshaking >>> s = ser.read(100) #read up to one hunded bytes ... #or as much is in the buffer +Get a Serial instance and configure/open it later +>>> ser = serial.Serial() +>>> ser.baudrate = 19200 +>>> ser.port = 0 +>>> ser +Serial<id=0xa81c10, open=False>(port='COM1', baudrate=19200, bytesize=8, +parity='N', stopbits=1, timeout=None, xonxoff=0, rtscts=0) +>>> ser.open() +>>> ser.isOpen() +True +>>> ser.close() +>>> ser.isOpen() +False + Be carefully when using "readline". Do specify a timeout when opening the serial port otherwise it could block forever if no newline character is received. Also note that "readlines" only @@ -100,10 +113,12 @@ if the port is not opened correctly. Parameters for the Serial class ------------------------------- ser = serial.Serial( - port, #number of device, numbering starts at + port=None, #number of device, numbering starts at #zero. if everything fails, the user #can specify a device string, note #that this isn't portable anymore + #if no port is specified an unconfigured + #an closed serial port object is created baudrate=9600, #baudrate bytesize=EIGHTBITS, #number of databits parity=PARITY_NONE, #enable parity checking @@ -119,15 +134,16 @@ timeout=None #wait forever timeout=0 #non-blocking mode (return immediately on read) timeout=x #set timeout to x seconds (float allowed) -Serial object Methods ---------------------- +Methods of Serial instances +--------------------------- +open() #open port close() #close port immediately setBaudrate(baudrate) #change baudarte on an open port inWaiting() #return the number of chars in the receive buffer read(size=1) #read "size" characters -write(s) #write the string to the port -flushInput() #flush input buffer -flushOutput() #flush output buffer +write(s) #write the string s to the port +flushInput() #flush input buffer, discarding all it's contents +flushOutput() #flush output buffer, abort output sendBreak() #send break condition setRTS(level=1) #set RTS line to specified logic level setDTR(level=1) #set DTR line to specified logic level @@ -136,6 +152,27 @@ getDSR() #return the state of the DSR line getRI() #return the state of the RI line getCD() #return the state of the CD line +Attributes of Serial instances +------------------------------ +Read Only: +portstr #device name +BAUDRATES #list of valid baudrates +BYTESIZES #list of valid byte sizes +PARITIES #list of valid parities +STOPBITS #list of valid stop bit widths + +New values can be assigned to the following attribues, the port +will be reconfigured, even if it's opened at that time: +port #port name/number as set by the user +baudrate #current baudrate setting +bytesize #bytesize in bits +parity #parity setting +stopbits #stop bit with (1,2) +timeout #timeout setting +xonxoff #if XonXoff flow control is enabled +rtscts #if hardware flow control is enabled + + Constants --------- parity: @@ -154,7 +191,7 @@ bytesize: Tips & Tricks ------------- - Some protocols need CR LF ("\r\n") as line terminator, not just LF ("\n"). - Modems are an example of this behaviour. + Telephone modems with the AT command set are an example of this behaviour. - Scanning for available serial ports is possible with more or less sucess on some platforms. Look at the tools from Roger Binns: diff --git a/pyserial/examples/miniterm.py b/pyserial/examples/miniterm.py index 43b4fa8..446748e 100644 --- a/pyserial/examples/miniterm.py +++ b/pyserial/examples/miniterm.py @@ -37,7 +37,8 @@ elif os.name == 'posix': termios.tcsetattr(fd, TERMIOS.TCSANOW, new) s = '' # We'll save the characters typed and add them to the pool. def getkey(): - c = os.read(fd, 1) + #~ c = os.read(fd, 1) + c = sys.stdin.read(1) if echo: sys.stdout.write(c) return c def clenaup_console(): diff --git a/pyserial/examples/test.py b/pyserial/examples/test.py index ec8d13a..d312fe6 100644 --- a/pyserial/examples/test.py +++ b/pyserial/examples/test.py @@ -1,25 +1,30 @@ #!/usr/bin/env python -"""Some Tests for the serial module. -part of pyserial (http://pyserial.sf.net) (C)2002 cliechti@gmx.net +#Python Serial Port Extension for Win32, Linux, BSD, Jython +#see __init__.py +# +#(C) 2001-2003 Chris Liechti <cliechti@gmx.net> +# this is distributed under a free software license, see license.txt -intended to be run on different platforms, to ensure portability of +"""Some tests for the serial module. +Part of pyserial (http://pyserial.sf.net) (C)2002-2003 cliechti@gmx.net + +Intended to be run on different platforms, to ensure portability of the code. -for all these tests a simple hardware is required. +For all these tests a simple hardware is required. Loopback HW adapter: -shortcut these pin pairs: +Shortcut these pin pairs: TX <-> RX RTS <-> CTS DTR <-> DSR -on a 9 pole DSUB these are the pins (2-3) (4-6) (7-8) - +On a 9 pole DSUB these are the pins (2-3) (4-6) (7-8) """ import unittest, threading, time import serial -#of which port should the tests be performed: +#on which port should the tests be performed: PORT=0 @@ -153,7 +158,7 @@ class Test0_DataWires(unittest.TestCase): if __name__ == '__main__': import sys print __doc__ - print "testing port", PORT + print "Testing port", PORT sys.argv.append('-v') - # When this module is executed from the command-line, run all its tests + # When this module is executed from the command-line, it runs all its tests unittest.main() diff --git a/pyserial/examples/test_advanced.py b/pyserial/examples/test_advanced.py new file mode 100644 index 0000000..d9ca93d --- /dev/null +++ b/pyserial/examples/test_advanced.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +#needs at least python 2.2.3 + +#Python Serial Port Extension for Win32, Linux, BSD, Jython +#see __init__.py +# +#(C) 2001-2003 Chris Liechti <cliechti@gmx.net> +# this is distributed under a free software license, see license.txt + +"""Some tests for the serial module. +Part of pyserial (http://pyserial.sf.net) (C)2002 cliechti@gmx.net + +Intended to be run on different platforms, to ensure portability of +the code. + +These tests open a serial port and change all the settings on the fly. +If the port is realy correctly configured cannot be determined - that +would require external hardware or a nullmodem cable and an other +serial port library... Thus it mainly tests that all features are +correctly implemented and that the interface does what it should. +""" + +import unittest +import serial + +#on which port should the tests be performed: +PORT=0 + +class Test_ChangeAttributes(unittest.TestCase): + """Test with timeouts""" + + def setUp(self): + self.s = serial.Serial() #create an closed serial port + + def tearDown(self): + self.s.close() + + def test_PortSetting(self): + self.s.port = PORT + #portstr has to be set + self.failUnlessEqual(self.s.portstr, serial.device(PORT)) + #test internals + self.failUnlessEqual(self.s._port, PORT) + #test on the fly change + self.s.open() + self.failUnless(self.s.isOpen()) + self.s.port = 0 + self.failUnless(self.s.isOpen()) + self.failUnlessEqual(self.s.port, 0) + self.failUnlessEqual(self.s.portstr, serial.device(0)) + try: + self.s.port = 1 + except serial.SerialException: #port not available on system + pass #cant test on this machine... + else: + self.failUnless(self.s.isOpen()) + self.failUnlessEqual(self.s.port, 1) + self.failUnlessEqual(self.s.portstr, serial.device(1)) + + def test_BaudrateSetting(self): + for baudrate in (300, 9600, 19200, 115200): + self.s.baudrate = baudrate + #test get method + self.failUnlessEqual(self.s.baudrate, baudrate) + #test internals + self.failUnlessEqual(self.s._baudrate, baudrate) + #test illegal values + for illegal_value in (-300, -1, 0, 301, 9000, 12345, 'a', None): + self.failUnlessRaises(ValueError, self.s.setBaudrate, illegal_value) + + def test_BytesizeSetting(self): + for bytesize in (5,6,7,8): + self.s.bytesize = bytesize + #test get method + self.failUnlessEqual(self.s.bytesize, bytesize) + #test internals + self.failUnlessEqual(self.s._bytesize, bytesize) + #test illegal values + for illegal_value in (0, 1, 3, 4, 9, 10, 'a', None): + self.failUnlessRaises(ValueError, self.s.setByteSize, illegal_value) + + def test_ParitySetting(self): + for parity in (serial.PARITY_NONE, serial.PARITY_EVEN, serial.PARITY_ODD): + self.s.parity = parity + #test get method + self.failUnlessEqual(self.s.parity, parity) + #test internals + self.failUnlessEqual(self.s._parity, parity) + #test illegal values + for illegal_value in (0, 57, 'a', None): + self.failUnlessRaises(ValueError, self.s.setParity, illegal_value) + + def test_StopbitsSetting(self): + for stopbits in (1, 2): + self.s.stopbits = stopbits + #test get method + self.failUnlessEqual(self.s.stopbits, stopbits) + #test internals + self.failUnlessEqual(self.s._stopbits, stopbits) + #test illegal values + for illegal_value in (0, 3, 1.5, 57, 'a', None): + self.failUnlessRaises(ValueError, self.s.setStopbits, illegal_value) + + def test_TimeoutSetting(self): + for timeout in (None, 0, 1, 3.14159, 10, 1000, 3600): + self.s.timeout = timeout + #test get method + self.failUnlessEqual(self.s.timeout, timeout) + #test internals + self.failUnlessEqual(self.s._timeout, timeout) + #test illegal values + for illegal_value in (-1, 'a'): + self.failUnlessRaises(ValueError, self.s.setTimeout, illegal_value) + + def test_XonXoffSetting(self): + for xonxoff in (True, False): + self.s.xonxoff = xonxoff + #test get method + self.failUnlessEqual(self.s.xonxoff, xonxoff) + #test internals + self.failUnlessEqual(self.s._xonxoff, xonxoff) + #no illegal values here, normal rules for the boolean value of an + #object are used thus all objects have a truth value. + + def test_RtsCtsSetting(self): + for rtscts in (True, False): + self.s.rtscts = rtscts + #test get method + self.failUnlessEqual(self.s.rtscts, rtscts) + #test internals + self.failUnlessEqual(self.s._rtscts, rtscts) + #no illegal values here, normal rules for the boolean value of an + #object are used thus all objects have a truth value. + + def test_UnconfiguredPort(self): + #an unconfigured port cannot be opened + self.failUnlessRaises(serial.SerialException, self.s.open) + + def test_PortOpenClose(self): + self.s.port = PORT + for i in range(3): + #open the port and check flag + self.failUnless(not self.s.isOpen()) + self.s.open() + self.failUnless(self.s.isOpen()) + self.s.close() + self.failUnless(not self.s.isOpen()) + + +if __name__ == '__main__': + import sys + print __doc__ + print "Testing port", PORT + sys.argv.append('-v') + # When this module is executed from the command-line, it runs all its tests + unittest.main() diff --git a/pyserial/serial/serialjava.py b/pyserial/serial/serialjava.py index c199d82..90b0896 100644 --- a/pyserial/serial/serialjava.py +++ b/pyserial/serial/serialjava.py @@ -1,136 +1,134 @@ #!jython +#Python Serial Port Extension for Win32, Linux, BSD, Jython #module for serial IO for Jython and JavaComm #see __init__.py # -#(C) 2002 Chris Liechti <cliechti@gmx.net> +#(C) 2002-2003 Chris Liechti <cliechti@gmx.net> # this is distributed under a free software license, see license.txt -import sys, os, string, javax.comm -import serialutil +import javax.comm +from serialutil import * -VERSION = string.split("$Revision: 1.7 $")[1] #extract CVS version +VERSION = "$Revision: 1.8 $".split()[1] #extract CVS version -PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = (0,1,2,3,4) -STOPBITS_ONE, STOPBITS_TWO, STOPBITS_ONE_HALVE = (1, 2, 3) -FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5,6,7,8) - - -portNotOpenError = ValueError('port not open') def device(portnumber): + """Turn a port number into a device name""" enum = javax.comm.CommPortIdentifier.getPortIdentifiers() ports = [] while enum.hasMoreElements(): el = enum.nextElement() if el.getPortType() == javax.comm.CommPortIdentifier.PORT_SERIAL: ports.append(el) - return ports[portnumber] - -class Serial(serialutil.FileLike): - def __init__(self, - port, #number of device, numbering starts at - #zero. if everything fails, the user - #can specify a device string, note - #that this isn't portable anymore - baudrate=9600, #baudrate - bytesize=EIGHTBITS, #number of databits - parity=PARITY_NONE, #enable parity checking - stopbits=STOPBITS_ONE, #number of stopbits - timeout=None, #set a timeout value, None to wait forever - xonxoff=0, #enable software flow control - rtscts=0, #enable RTS/CTS flow control - ): - - if type(port) == type(''): #strings are taken directly - portId = javax.comm.CommPortIdentifier.getPortIdentifier(port) + return ports[portnumber].getName() + +class Serial(SerialBase): + """Serial port class, implemented with javax.comm and thus usable with + jython and the appropriate java extension.""" + + def open(self): + """Open port with current settings. This may throw a SerialException + if the port cannot be opened.""" + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if type(self._port) == type(''): #strings are taken directly + portId = javax.comm.CommPortIdentifier.getPortIdentifier(self._port) else: - portId = device(port) #numbers are transformed to a comportid obj - self.portstr = portId.getName() + portId = javax.comm.CommPortIdentifier.getPortIdentifier(device(self._port)) #numbers are transformed to a comportid obj try: self.sPort = portId.open("python serial module", 10) except Exception, msg: self.sPort = None - raise serialutil.SerialException, "could not open port: %s" % msg - self.instream = self.sPort.getInputStream() - self.outstream = self.sPort.getOutputStream() + raise SerialException("Could not open port: %s" % msg) + self._reconfigurePort() + self._instream = self.sPort.getInputStream() + self._outstream = self.sPort.getOutputStream() + self._isOpen = True + + def _reconfigurePort(self): + """Set commuication parameters on opened port.""" + if not self.sPort: + raise SerialException("Can only operate on a valid port handle") + self.sPort.enableReceiveTimeout(30) - if bytesize == FIVEBITS: - self.databits = javax.comm.SerialPort.DATABITS_5 - elif bytesize == SIXBITS: - self.databits = javax.comm.SerialPort.DATABITS_6 - elif bytesize == SEVENBITS: - self.databits = javax.comm.SerialPort.DATABITS_7 - elif bytesize == EIGHTBITS: - self.databits = javax.comm.SerialPort.DATABITS_8 + if self._bytesize == FIVEBITS: + jdatabits = javax.comm.SerialPort.DATABITS_5 + elif self._bytesize == SIXBITS: + jdatabits = javax.comm.SerialPort.DATABITS_6 + elif self._bytesize == SEVENBITS: + jdatabits = javax.comm.SerialPort.DATABITS_7 + elif self._bytesize == EIGHTBITS: + jdatabits = javax.comm.SerialPort.DATABITS_8 else: - raise ValueError, "unsupported bytesize" + raise ValueError("unsupported bytesize: %r" % self._bytesize) - if stopbits == STOPBITS_ONE: - self.jstopbits = javax.comm.SerialPort.STOPBITS_1 + if self._stopbits == STOPBITS_ONE: + jstopbits = javax.comm.SerialPort.STOPBITS_1 elif stopbits == STOPBITS_ONE_HALVE: - self.jstopbits = javax.comm.SerialPort.STOPBITS_1_5 - elif stopbits == STOPBITS_TWO: - self.jstopbits = javax.comm.SerialPort.STOPBITS_2 + self._jstopbits = javax.comm.SerialPort.STOPBITS_1_5 + elif self._stopbits == STOPBITS_TWO: + jstopbits = javax.comm.SerialPort.STOPBITS_2 else: - raise ValueError, "unsupported number of stopbits" - - if parity == PARITY_NONE: - self.jparity = javax.comm.SerialPort.PARITY_NONE - elif parity == PARITY_EVEN: - self.jparity = javax.comm.SerialPort.PARITY_EVEN - elif parity == PARITY_ODD: - self.jparity = javax.comm.SerialPort.PARITY_ODD - elif parity == PARITY_MARK: - self.jparity = javax.comm.SerialPort.PARITY_MARK - elif parity == PARITY_SPACE: - self.jparity = javax.comm.SerialPort.PARITY_SPACE + raise ValueError("unsupported number of stopbits: %r" % self._stopbits) + + if self._parity == PARITY_NONE: + jparity = javax.comm.SerialPort.PARITY_NONE + elif self._parity == PARITY_EVEN: + jparity = javax.comm.SerialPort.PARITY_EVEN + elif self._parity == PARITY_ODD: + jparity = javax.comm.SerialPort.PARITY_ODD + #~ elif self._parity == PARITY_MARK: + #~ jparity = javax.comm.SerialPort.PARITY_MARK + #~ elif self._parity == PARITY_SPACE: + #~ jparity = javax.comm.SerialPort.PARITY_SPACE else: - raise ValueError, "unsupported parity type" + raise ValueError("unsupported parity type: %r" % self._parity) jflowin = jflowout = 0 - if rtscts: - jflowin = jflowin | javax.comm.SerialPort.FLOWCONTROL_RTSCTS_IN - jflowout = jflowout | javax.comm.SerialPort.FLOWCONTROL_RTSCTS_OUT - if xonxoff: - jflowin = jflowin | javax.comm.SerialPort.FLOWCONTROL_XONXOFF_IN - jflowout = jflowout | javax.comm.SerialPort.FLOWCONTROL_XONXOFF_OUT + if self._rtscts: + jflowin |= javax.comm.SerialPort.FLOWCONTROL_RTSCTS_IN + jflowout |= javax.comm.SerialPort.FLOWCONTROL_RTSCTS_OUT + if self._xonxoff: + jflowin |= javax.comm.SerialPort.FLOWCONTROL_XONXOFF_IN + jflowout |= javax.comm.SerialPort.FLOWCONTROL_XONXOFF_OUT - self.sPort.setSerialPortParams(baudrate, self.databits, self.jstopbits, self.jparity) + self.sPort.setSerialPortParams(baudrate, jdatabits, jstopbits, jparity) self.sPort.setFlowControlMode(jflowin | jflowout) - self.timeout = timeout - if timeout >= 0: - self.sPort.enableReceiveTimeout(timeout*1000) + if self._timeout >= 0: + self.sPort.enableReceiveTimeout(self._timeout*1000) else: self.sPort.disableReceiveTimeout() def close(self): - if self.sPort: - self.instream.close() - self.outstream.close() - self.sPort.close() - self.sPort = None + """Close port""" + if self._isOpen: + if self.sPort: + self._instream.close() + self._outstream.close() + self.sPort.close() + self.sPort = None + self._isOpen = False - def setBaudrate(self, baudrate): - """change baudrate after port is open""" - if not self.sPort: raise portNotOpenError - self.sPort.setSerialPortParams(baudrate, self.databits, self.jstopbits, self.jparity) + def makeDeviceName(self, port): + return device(port) + # - - - - - - - - - - - - - - - - - - - - - - - - def inWaiting(self): + """Return the number of characters currently in the input buffer.""" if not self.sPort: raise portNotOpenError - return self.instream.available() - - def write(self, data): - if not self.sPort: raise portNotOpenError - self.outstream.write(data) + return self._instream.available() def read(self, size=1): + """Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read.""" if not self.sPort: raise portNotOpenError read = '' if size > 0: while len(read) < size: - x = self.instream.read() + x = self._instream.read() if x == -1: if self.timeout >= 0: break @@ -138,41 +136,58 @@ class Serial(serialutil.FileLike): read = read + chr(x) return read + def write(self, data): + """Output the given string over the serial port.""" + if not self.sPort: raise portNotOpenError + self._outstream.write(data) + def flushInput(self): + """Clear input buffer, discarding all that is in the buffer.""" if not self.sPort: raise portNotOpenError - self.instream.skip(self.instream.available()) + self._instream.skip(self._instream.available()) def flushOutput(self): + """Clear output buffer, aborting the current output and + discarding all that is in the buffer.""" if not self.sPort: raise portNotOpenError - self.outstream.flush() + self._outstream.flush() def sendBreak(self): + """Send break condition.""" if not self.sPort: raise portNotOpenError self.sPort.sendBreak() - def getDSR(self): + def setRTS(self,on=1): + """Set terminal status line: Request To Send""" if not self.sPort: raise portNotOpenError - self.sPort.isDSR() + self.sPort.setRTS(on) + + def setDTR(self,on=1): + """Set terminal status line: Data Terminal Ready""" + if not self.sPort: raise portNotOpenError + self.sPort.setDTR(on) - def getCD(self): + def getCTS(self): + """Read terminal status line: Clear To Send""" if not self.sPort: raise portNotOpenError - self.sPort.isCD() + self.sPort.isCTS() + + def getDSR(self): + """Read terminal status line: Data Set Ready""" + if not self.sPort: raise portNotOpenError + self.sPort.isDSR() def getRI(self): + """Read terminal status line: Ring Indicator""" if not self.sPort: raise portNotOpenError self.sPort.isRI() - def getCTS(self): + def getCD(self): + """Read terminal status line: Carrier Detect""" if not self.sPort: raise portNotOpenError - self.sPort.isCTS() + self.sPort.isCD() - def setDTR(self,on=1): - if not self.sPort: raise portNotOpenError - self.sPort.setDTR(on) - def setRTS(self,on=1): - if not self.sPort: raise portNotOpenError - self.sPort.setRTS(on) if __name__ == '__main__': s = Serial(0, diff --git a/pyserial/serial/serialposix.py b/pyserial/serial/serialposix.py index 855ee30..1d1c85e 100644 --- a/pyserial/serial/serialposix.py +++ b/pyserial/serial/serialposix.py @@ -1,22 +1,19 @@ #!/usr/bin/env python +#Python Serial Port Extension for Win32, Linux, BSD, Jython #module for serial IO for POSIX compatible systems, like Linux #see __init__.py # -#(C) 2001-2002 Chris Liechti <cliechti@gmx.net> +#(C) 2001-2003 Chris Liechti <cliechti@gmx.net> # this is distributed under a free software license, see license.txt # #parts based on code from Grant B. Edwards <grante@visi.com>: # ftp://ftp.visi.com/users/grante/python/PosixSerial.py # references: http://www.easysw.com/~mike/serial/serial.html -import sys, os, fcntl, termios, struct, string, select -import serialutil +import sys, os, fcntl, termios, struct, select +from serialutil import * -VERSION = string.split("$Revision: 1.16 $")[1] #extract CVS version - -PARITY_NONE, PARITY_EVEN, PARITY_ODD = range(3) -STOPBITS_ONE, STOPBITS_TWO = (1, 2) -FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5,6,7,8) +VERSION = "$Revision: 1.17 $".split()[1] #extract CVS version #Do check the Python version as some constants have moved. if (sys.hexversion < 0x020100f0): @@ -30,7 +27,7 @@ else: FCNTL = fcntl #try to detect the os so that a device can be selected... -plat = string.lower(sys.platform) +plat = sys.platform.lower() if plat[:5] == 'linux': #Linux (confirmed) def device(port): @@ -67,7 +64,7 @@ elif plat[:5] == 'sunos': #Solaris®/SunOS® (confirmed) else: #platform detection has failed... info = "sys.platform = %r\nos.name = %r\nserialposix.py version = %s" % (sys.platform, os.name, VERSION) - print """send this information to the author of this module: + print """send this information to the author of the pyserial: %s @@ -76,7 +73,12 @@ counting starts for the first serial port. e.g. 'first serial port: /dev/ttyS0' and with a bit luck you can get this module running... """ - raise Exception, "this module does not run on this platform, sorry." + #no exception, just continue with a brave attempt to build a device name + #even if the device name is not correct for the platform it has chances + #to work using a string with the real device name as port paramter. + def device(portum): + return '/dev/ttyS%d' % portnum + #~ raise Exception, "this module does not run on this platform, sorry." #whats up with "aix", "beos", "sco", .... #they should work, just need to know the device names. @@ -124,166 +126,144 @@ TIOCM_zero_str = struct.pack('I', 0) TIOCM_RTS_str = struct.pack('I', TIOCM_RTS) TIOCM_DTR_str = struct.pack('I', TIOCM_DTR) -portNotOpenError = ValueError('port not open') -class Serial(serialutil.FileLike): - def __init__(self, - port, #number of device, numbering starts at - #zero. if everything fails, the user - #can specify a device string, note - #that this isn't portable anymore - baudrate=9600, #baudrate - bytesize=EIGHTBITS, #number of databits - parity=PARITY_NONE, #enable parity checking - stopbits=STOPBITS_ONE, #number of stopbits - timeout=None, #set a timeout value, None to wait forever - xonxoff=0, #enable software flow control - rtscts=0, #enable RTS/CTS flow control - ): - """init comm port""" +class Serial(SerialBase): + """Serial port class POSIX implementation. Serial port configuration is + done with termios and fcntl. Runs on Linux and many other Un*x like + systems.""" + + def open(self): + """Open port with current settings. This may throw a SerialException + if the port cannot be opened.""" + if self._port is None: + raise SerialException("Port must be configured before it can be used.") self.fd = None - self.timeout = timeout - vmin = vtime = 0 #timeout is done via select #open - if type(port) == type(''): #strings are taken directly - self.portstr = port - else: - self.portstr = device(port) #numbers are transformed to a os dependant string try: self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK) except Exception, msg: self.fd = None - raise serialutil.SerialException, "could not open port: %s" % msg + raise SerialException("Could not open port: %s" % msg) fcntl.fcntl(self.fd, FCNTL.F_SETFL, 0) #set blocking + + self._reconfigurePort() + self._isOpen = True + + + def _reconfigurePort(self): + """Set commuication parameters on opened port.""" + if not self.fd: + raise SerialException("Can only operate on a valid port handle") + + vmin = vtime = 0 #timeout is done via select try: - self.__tcgetattr() #read current settings + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = termios.tcgetattr(self.fd) except termios.error, msg: #if a port is nonexistent but has a /dev file, it'll fail here - raise serialutil.SerialException, "could not open port: %s" % msg + raise SerialException("Could not configure port: %s" % msg) #set up raw mode / no echo / binary - self.cflag = self.cflag | (TERMIOS.CLOCAL|TERMIOS.CREAD) - self.lflag = self.lflag & ~(TERMIOS.ICANON|TERMIOS.ECHO|TERMIOS.ECHOE|TERMIOS.ECHOK|TERMIOS.ECHONL| - TERMIOS.ECHOCTL|TERMIOS.ECHOKE|TERMIOS.ISIG|TERMIOS.IEXTEN) #|TERMIOS.ECHOPRT - self.oflag = self.oflag & ~(TERMIOS.OPOST) + cflag |= (TERMIOS.CLOCAL|TERMIOS.CREAD) + lflag &= ~(TERMIOS.ICANON|TERMIOS.ECHO|TERMIOS.ECHOE|TERMIOS.ECHOK|TERMIOS.ECHONL| + TERMIOS.ECHOCTL|TERMIOS.ECHOKE|TERMIOS.ISIG|TERMIOS.IEXTEN) #|TERMIOS.ECHOPRT + oflag &= ~(TERMIOS.OPOST) if hasattr(TERMIOS, 'IUCLC'): - self.iflag = self.iflag & ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IUCLC|TERMIOS.IGNBRK) + iflag &= ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IUCLC|TERMIOS.IGNBRK) else: - self.iflag = self.iflag & ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IGNBRK) + iflag &= ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IGNBRK) #setup baudrate try: - self.ispeed = self.ospeed = baudIntToEnum[baudrate] + ispeed = ospeed = baudIntToEnum[self._baudrate] except: - raise ValueError,'invalid baud rate: %s' % baudrate + raise ValueError('Invalid baud rate: %r' % self._baudrate) #setup char len - self.cflag = self.cflag & ~TERMIOS.CSIZE - if bytesize == 8: - self.cflag = self.cflag | TERMIOS.CS8 - elif bytesize == 7: - self.cflag = self.cflag | TERMIOS.CS7 - elif bytesize == 6: - self.cflag = self.cflag | TERMIOS.CS6 - elif bytesize == 5: - self.cflag = self.cflag | TERMIOS.CS5 + cflag &= ~TERMIOS.CSIZE + if self._bytesize == 8: + cflag |= TERMIOS.CS8 + elif self._bytesize == 7: + cflag |= TERMIOS.CS7 + elif self._bytesize == 6: + cflag |= TERMIOS.CS6 + elif self._bytesize == 5: + cflag |= TERMIOS.CS5 else: - raise ValueError,'invalid char len: '+str(clen) + raise ValueError('Invalid char len: %r' % self._bytesize) #setup stopbits - if stopbits == STOPBITS_ONE: - self.cflag = self.cflag & ~(TERMIOS.CSTOPB) - elif stopbits == STOPBITS_TWO: - self.cflag = self.cflag | (TERMIOS.CSTOPB) + if self._stopbits == STOPBITS_ONE: + cflag &= ~(TERMIOS.CSTOPB) + elif self._stopbits == STOPBITS_TWO: + cflag |= (TERMIOS.CSTOPB) else: - raise ValueError,'invalid stopit specification:'+str(stopbits) + raise ValueError('Invalid stopit specification: %r' % self._stopbits) #setup parity - self.iflag = self.iflag & ~(TERMIOS.INPCK|TERMIOS.ISTRIP) - if parity == PARITY_NONE: - self.cflag = self.cflag & ~(TERMIOS.PARENB|TERMIOS.PARODD) - elif parity == PARITY_EVEN: - self.cflag = self.cflag & ~(TERMIOS.PARODD) - self.cflag = self.cflag | (TERMIOS.PARENB) - elif parity == PARITY_ODD: - self.cflag = self.cflag | (TERMIOS.PARENB|TERMIOS.PARODD) + iflag &= ~(TERMIOS.INPCK|TERMIOS.ISTRIP) + if self._parity == PARITY_NONE: + cflag &= ~(TERMIOS.PARENB|TERMIOS.PARODD) + elif self._parity == PARITY_EVEN: + cflag &= ~(TERMIOS.PARODD) + cflag |= (TERMIOS.PARENB) + elif self._parity == PARITY_ODD: + cflag |= (TERMIOS.PARENB|TERMIOS.PARODD) else: - raise ValueError,'invalid parity: '+str(par) + raise ValueError('Invalid parity: %r' % self._parity) #setup flow control #xonxoff if hasattr(TERMIOS, 'IXANY'): - if xonxoff: - self.iflag = self.iflag | (TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY) + if self._xonxoff: + iflag |= (TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY) else: - self.iflag = self.iflag & ~(TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY) + iflag &= ~(TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY) else: - if xonxoff: - self.iflag = self.iflag | (TERMIOS.IXON|TERMIOS.IXOFF) + if self._xonxoff: + iflag |= (TERMIOS.IXON|TERMIOS.IXOFF) else: - self.iflag = self.iflag & ~(TERMIOS.IXON|TERMIOS.IXOFF) + iflag &= ~(TERMIOS.IXON|TERMIOS.IXOFF) #rtscts if hasattr(TERMIOS, 'CRTSCTS'): - if rtscts: - self.cflag = self.cflag | (TERMIOS.CRTSCTS) + if self._rtscts: + cflag |= (TERMIOS.CRTSCTS) else: - self.cflag = self.cflag & ~(TERMIOS.CRTSCTS) + cflag &= ~(TERMIOS.CRTSCTS) elif hasattr(TERMIOS, 'CNEW_RTSCTS'): #try it with alternate constant name - if rtscts: - self.cflag = self.cflag | (TERMIOS.CNEW_RTSCTS) + if self._rtscts: + cflag |= (TERMIOS.CNEW_RTSCTS) else: - self.cflag = self.cflag & ~(TERMIOS.CNEW_RTSCTS) + cflag &= ~(TERMIOS.CNEW_RTSCTS) #XXX should there be a warning if setting up rtscts (and xonxoff etc) fails?? #buffer #vmin "minimal number of characters to be read. = for non blocking" - if vmin<0 or vmin>255: - raise ValueError,'invalid vmin: '+str(vmin) - self.cc[TERMIOS.VMIN] = vmin + if vmin < 0 or vmin > 255: + raise ValueError('Invalid vmin: %r ' % vmin) + cc[TERMIOS.VMIN] = vmin #vtime - if vtime<0 or vtime>255: - raise ValueError,'invalid vtime: '+str(vtime) - self.cc[TERMIOS.VTIME] = vtime + if vtime < 0 or vtime > 255: + raise ValueError('Invalid vtime: %r' % vtime) + cc[TERMIOS.VTIME] = vtime #activate settings - self.__tcsetattr() - - def __tcsetattr(self): - """internal function to set port attributes""" - termios.tcsetattr(self.fd, TERMIOS.TCSANOW, [self.iflag,self.oflag,self.cflag,self.lflag,self.ispeed,self.ospeed,self.cc]) - - def __tcgetattr(self): - """internal function to get port attributes""" - self.iflag,self.oflag,self.cflag,self.lflag,self.ispeed,self.ospeed,self.cc = termios.tcgetattr(self.fd) + termios.tcsetattr(self.fd, TERMIOS.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) def close(self): - """close port""" - if self.fd: - os.close(self.fd) - self.fd = None + """Close port""" + if self._isOpen: + if self.fd: + os.close(self.fd) + self.fd = None + self._isOpen = False - def setBaudrate(self, baudrate): - """change baudrate after port is open""" - if not self.fd: raise portNotOpenError - self.__tcgetattr() #read current settings - #setup baudrate - try: - self.ispeed = self.ospeed = baudIntToEnum[baudrate] - except: - raise ValueError,'invalid baud rate: %s' % baudrate - self.__tcsetattr() + def makeDeviceName(self, port): + return device(port) + + # - - - - - - - - - - - - - - - - - - - - - - - - def inWaiting(self): - """how many character are in the input queue""" + """Return the number of characters currently in the input buffer.""" #~ s = fcntl.ioctl(self.fd, TERMIOS.FIONREAD, TIOCM_zero_str) s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str) return struct.unpack('I',s)[0] - def write(self, data): - """write a string to the port""" - if not self.fd: raise portNotOpenError - t = len(data) - d = data - while t>0: - n = os.write(self.fd, d) - d = d[n:] - t = t - n - def read(self, size=1): - """read a number of bytes from the port. - the default is one (unlike files)""" + """Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read.""" if not self.fd: raise portNotOpenError read = '' inp = None @@ -299,24 +279,63 @@ class Serial(serialutil.FileLike): break #early abort on timeout return read + def write(self, data): + """Output the given string over the serial port.""" + if not self.fd: raise portNotOpenError + t = len(data) + d = data + while t>0: + n = os.write(self.fd, d) + d = d[n:] + t = t - n + def flushInput(self): - """clear input queue""" + """Clear input buffer, discarding all that is in the buffer.""" if not self.fd: raise portNotOpenError termios.tcflush(self.fd, TERMIOS.TCIFLUSH) def flushOutput(self): - """flush output""" + """Clear output buffer, aborting the current output and + discarding all that is in the buffer.""" if not self.fd: raise portNotOpenError termios.tcflush(self.fd, TERMIOS.TCOFLUSH) def sendBreak(self): - """send break signal""" + """Send break condition.""" if not self.fd: raise portNotOpenError termios.tcsendbreak(self.fd, 0) + def setRTS(self,on=1): + """Set terminal status line: Request To Send""" + if not self.fd: raise portNotOpenError + if on: + fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str) + else: + fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str) + + def setDTR(self,on=1): + """Set terminal status line: Data Terminal Ready""" + if not self.fd: raise portNotOpenError + if on: + fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str) + else: + fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str) + + def getCTS(self): + """Read terminal status line: Clear To Send""" + if not self.fd: raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I',s)[0] & TIOCM_CTS != 0 + + def getDSR(self): + """Read terminal status line: Data Set Ready""" + if not self.fd: raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I',s)[0] & TIOCM_DSR != 0 + def drainOutput(self): """internal - not portable!""" if not self.fd: raise portNotOpenError @@ -328,45 +347,18 @@ class Serial(serialutil.FileLike): raise portNotOpenError fcntl.fcntl(self.fd, FCNTL.F_SETFL, FCNTL.O_NONBLOCK) - def getDSR(self): - """read terminal status line""" - if not self.fd: raise portNotOpenError - s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) - return struct.unpack('I',s)[0] & TIOCM_DSR - - def getCD(self): - """read terminal status line""" - if not self.fd: raise portNotOpenError - s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) - return struct.unpack('I',s)[0] & TIOCM_CD - def getRI(self): - """read terminal status line""" + """Read terminal status line: Ring Indicator""" if not self.fd: raise portNotOpenError s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) - return struct.unpack('I',s)[0] & TIOCM_RI + return struct.unpack('I',s)[0] & TIOCM_RI != 0 - def getCTS(self): - """read terminal status line""" + def getCD(self): + """Read terminal status line: Carrier Detect""" if not self.fd: raise portNotOpenError s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) - return struct.unpack('I',s)[0] & TIOCM_CTS + return struct.unpack('I',s)[0] & TIOCM_CD != 0 - def setDTR(self,on=1): - """set terminal status line""" - if not self.fd: raise portNotOpenError - if on: - fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str) - else: - fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str) - - def setRTS(self,on=1): - """set terminal status line""" - if not self.fd: raise portNotOpenError - if on: - fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str) - else: - fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str) if __name__ == '__main__': s = Serial(0, diff --git a/pyserial/serial/serialutil.py b/pyserial/serial/serialutil.py index e3a4bdb..78ab99d 100644 --- a/pyserial/serial/serialutil.py +++ b/pyserial/serial/serialutil.py @@ -1,8 +1,33 @@ +#! python +#Python Serial Port Extension for Win32, Linux, BSD, Jython +#see __init__.py +# +#(C) 2001-2003 Chris Liechti <cliechti@gmx.net> +# this is distributed under a free software license, see license.txt + +PARITY_NONE, PARITY_EVEN, PARITY_ODD = 'N', 'E', 'O' +STOPBITS_ONE, STOPBITS_TWO = (1, 2) +FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5,6,7,8) + +PARITY_NAMES = { + PARITY_NONE: 'None', + PARITY_EVEN: 'Even', + PARITY_ODD: 'Odd', +} + +#Python < 2.2.3 compatibility +try: + True +except: + True = 1 + False = not True class SerialException(Exception): - pass + """Base class for serial port related exceptions.""" -class FileLike: +portNotOpenError = SerialException('Port not open') + +class FileLike(object): """An abstract file like class. This class implements readline and readlines based on read and @@ -63,3 +88,221 @@ class FileLike: """flush of file like objects""" pass +class SerialBase(FileLike): + """Serial port base class. Provides __init__ function and properties to + get/set port settings.""" + + #default values, may be overriden in subclasses that do not support all values + BAUDRATES = (50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600, + 19200,38400,57600,115200,230400,460800,500000,576000,921600, + 1000000,1152000,1500000,2000000,2500000,3000000,3500000,4000000) + BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) + PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD) + STOPBITS = (STOPBITS_ONE, STOPBITS_TWO) + + def __init__(self, + port = None, #number of device, numbering starts at + #zero. if everything fails, the user + #can specify a device string, note + #that this isn't portable anymore + #port will be opened if one is specified + baudrate=9600, #baudrate + bytesize=EIGHTBITS, #number of databits + parity=PARITY_NONE, #enable parity checking + stopbits=STOPBITS_ONE, #number of stopbits + timeout=None, #set a timeout value, None to wait forever + xonxoff=0, #enable software flow control + rtscts=0, #enable RTS/CTS flow control + ): + """Initialize comm port object. If a port is given, then the port will be + opened immediately. Otherwise a Serial port object with in clsoed state + is returned.""" + + self._isOpen = False + self._port = None #correct value is assigned below trough properties + self._bytesize = None #correct value is assigned below trough properties + self._parity = None #correct value is assigned below trough properties + self._stopbits = None #correct value is assigned below trough properties + self._timeout = None #correct value is assigned below trough properties + self._xonxoff = None #correct value is assigned below trough properties + self._rtscts = None #correct value is assigned below trough properties + + #assign values using get/set methods using the properties featrure + self.port = port + self.baudrate = baudrate + self.bytesize = bytesize + self.parity = parity + self.stopbits = stopbits + self.timeout = timeout + self.xonxoff = xonxoff + self.rtscts = rtscts + + if port is not None: + self.open() + + def isOpen(self): + """Check if the port is opened.""" + return self._isOpen + + # - - - - - - - - - - - - - - - - - - - - - - - - + + #TODO: these are not realy needed as the is the BAUDRATES etc attribute... + #maybe i remove them before the final release... + + def getSupportedBaudrates(self): + return [(str(b), b) for b in self.BAUDRATES] + + def getSupportedByteSizes(self): + return [(str(b), b) for b in self.BYTESIZES] + + def getSupportedStopbits(self): + return [(str(b), b) for b in self.STOPBITS] + + def getSupportedParities(self): + return [(PARITY_NAMES[b], b) for b in self.PARITIES] + + # - - - - - - - - - - - - - - - - - - - - - - - - + + def setPort(self, port): + """Change the port. The attribute portstr is set to a string that + contains the name of the port.""" + + was_open = self._isOpen + if was_open: self.close() + if port is not None: + if type(port) == type(''): #strings are taken directly + self.portstr = port + else: + self.portstr = self.makeDeviceName(port) + else: + self.portstr = None + self._port = port + if was_open: self.open() + + def getPort(self): + """get the current port setting. The value that was passed on init or using + setPort() is passed back. See also the attribute portstr which contains + the name of the port as a string.""" + return self._port + + port = property(getPort, setPort, "Port setting") + + + def setBaudrate(self, baudrate): + """Change baudrate.""" + if baudrate not in self.BAUDRATES: raise ValueError("Not a valid baudrate: %r" % baudrate) + self._baudrate = baudrate + if self._isOpen: self._reconfigurePort() + + def getBaudrate(self): + """Get the current baudrate setting.""" + return self._baudrate + + baudrate = property(getBaudrate, setBaudrate, "Baudrate setting") + + + def setByteSize(self, bytesize): + """Change byte size.""" + if bytesize not in self.BYTESIZES: raise ValueError("Not a valid byte size: %r" % bytesize) + self._bytesize = bytesize + if self._isOpen: self._reconfigurePort() + + def getByteSize(self): + """Get the current byte size setting.""" + return self._bytesize + + bytesize = property(getByteSize, setByteSize, "Byte size setting") + + + def setParity(self, parity): + """Change parity setting.""" + if parity not in self.PARITIES: raise ValueError("Not a valid parity: %r" % parity) + self._parity = parity + if self._isOpen: self._reconfigurePort() + + def getParity(self): + """Get the current parity setting.""" + return self._parity + + parity = property(getParity, setParity, "Parity setting") + + + def setStopbits(self, stopbits): + """Change stopbits size.""" + if stopbits not in self.STOPBITS: raise ValueError("Not a valid stopbit size: %r" % stopbits) + self._stopbits = stopbits + if self._isOpen: self._reconfigurePort() + + def getStopbits(self): + """Get the current stopbits setting.""" + return self._stopbits + + stopbits = property(getStopbits, setStopbits, "Stopbits setting") + + + def setTimeout(self, timeout): + """Change timeout setting.""" + if timeout is not None: + if timeout < 0: raise ValueError("Not a valid timeout: %r" % timeout) + try: + timeout + 1 #test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: %r" % timeout) + + self._timeout = timeout + if self._isOpen: self._reconfigurePort() + + def getTimeout(self): + """Get the current timeout setting.""" + return self._timeout + + timeout = property(getTimeout, setTimeout, "Timeout setting") + + + def setXonXoff(self, xonxoff): + """Change XonXoff setting.""" + self._xonxoff = xonxoff + if self._isOpen: self._reconfigurePort() + + def getXonXoff(self): + """Get the current XonXoff setting.""" + return self._xonxoff + + xonxoff = property(getXonXoff, setXonXoff, "Xon/Xoff setting") + + def setRtsCts(self, rtscts): + """Change RtsCts setting.""" + self._rtscts = rtscts + if self._isOpen: self._reconfigurePort() + + def getRtsCts(self): + """Get the current RtsCts setting.""" + return self._rtscts + + rtscts = property(getRtsCts, setRtsCts, "RTS/CTS setting") + + # - - - - - - - - - - - - - - - - - - - - - - - - + + def __repr__(self): + """String representation of the current port settings and its state.""" + return "%s<id=0x%x, open=%s>(port=%r, baudrate=%r, bytesize=%r, parity=%r, stopbits=%r, timeout=%r, xonxoff=%r, rtscts=%r)" % ( + self.__class__.__name__, + id(self), + self._isOpen, + self.portstr, + self.baudrate, + self.bytesize, + self.parity, + self.stopbits, + self.timeout, + self.xonxoff, + self.rtscts, + ) + +if __name__ == '__main__': + s = SerialBase() + print s.getSupportedBaudrates() + print s.getSupportedByteSizes() + print s.getSupportedParities() + print s.getSupportedStopbits() + print s diff --git a/pyserial/serial/serialwin32.py b/pyserial/serial/serialwin32.py index e428bb5..0f33d00 100644 --- a/pyserial/serial/serialwin32.py +++ b/pyserial/serial/serialwin32.py @@ -1,23 +1,17 @@ #! python +#Python Serial Port Extension for Win32, Linux, BSD, Jython #serial driver for win32 #see __init__.py # -#(C) 2001-2002 Chris Liechti <cliechti@gmx.net> +#(C) 2001-2003 Chris Liechti <cliechti@gmx.net> # this is distributed under a free software license, see license.txt import win32file # The base COM port and file IO functions. import win32event # We use events and the WaitFor[Single|Multiple]Objects functions. import win32con # constants. -import sys, string -import serialutil +from serialutil import * -VERSION = string.split("$Revision: 1.24 $")[1] #extract CVS version - -PARITY_NONE, PARITY_EVEN, PARITY_ODD = range(3) -STOPBITS_ONE, STOPBITS_TWO = (1, 2) -FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5,6,7,8) - -portNotOpenError = ValueError('port not open') +VERSION = "$Revision: 1.25 $".split()[1] #extract CVS version #from winbase.h. these should realy be in win32con MS_CTS_ON = 16 @@ -26,6 +20,7 @@ MS_RING_ON = 64 MS_RLSD_ON = 128 def device(portnum): + """Turn a port number into a device name""" #the "//./COMx" format is required for devices >= 9 #not all versions of windows seem to support this propperly #so that the first few ports are used with the DOS device name @@ -34,29 +29,16 @@ def device(portnum): else: return r'\\.\COM%d' % (portnum+1) -class Serial(serialutil.FileLike): - def __init__(self, - port, #number of device, numbering starts at - #zero. if everything fails, the user - #can specify a device string, note - #that this isn't portable anymore - baudrate=9600, #baudrate - bytesize=EIGHTBITS, #number of databits - parity=PARITY_NONE, #enable parity checking - stopbits=STOPBITS_ONE, #number of stopbits - timeout=None, #set a timeout value, None to wait forever - xonxoff=0, #enable software flow control - rtscts=0, #enable RTS/CTS flow control - ): - """initialize comm port""" - - self.timeout = timeout - - if type(port) == type(''): #strings are taken directly - self.portstr = port - else: - self.portstr = device(port) +class Serial(SerialBase): + """Serial port implemenation for Win32. This implemenatation requires a + win32all installation.""" + def open(self): + """Open port with current settings. This may throw a SerialException + if the port cannot be opened.""" + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + self.hComPort = None try: self.hComPort = win32file.CreateFile(self.portstr, win32con.GENERIC_READ | win32con.GENERIC_WRITE, @@ -67,120 +49,128 @@ class Serial(serialutil.FileLike): None) except Exception, msg: self.hComPort = None #'cause __del__ is called anyway - raise serialutil.SerialException, "could not open port: %s" % msg + raise SerialException("could not open port: %s" % msg) # Setup a 4k buffer win32file.SetupComm(self.hComPort, 4096, 4096) #Save original timeout values: - self.orgTimeouts = win32file.GetCommTimeouts(self.hComPort) + self._orgTimeouts = win32file.GetCommTimeouts(self.hComPort) + + self._reconfigurePort() + + # Clear buffers: + # Remove anything that was there + win32file.PurgeComm(self.hComPort, + win32file.PURGE_TXCLEAR | win32file.PURGE_TXABORT | + win32file.PURGE_RXCLEAR | win32file.PURGE_RXABORT) + self._overlappedRead = win32file.OVERLAPPED() + self._overlappedRead.hEvent = win32event.CreateEvent(None, 1, 0, None) + self._overlappedWrite = win32file.OVERLAPPED() + self._overlappedWrite.hEvent = win32event.CreateEvent(None, 0, 0, None) + self._isOpen = True + + def _reconfigurePort(self): + """Set commuication parameters on opened port.""" + if not self.hComPort: + raise SerialException("Can only operate on a valid port handle") + #Set Windows timeout values #timeouts is a tuple with the following items: #(ReadIntervalTimeout,ReadTotalTimeoutMultiplier, # ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier, # WriteTotalTimeoutConstant) - if timeout is None: + if self._timeout is None: timeouts = (0, 0, 0, 0, 0) - elif timeout == 0: + elif self._timeout == 0: timeouts = (win32con.MAXDWORD, 0, 0, 0, 0) else: - #timeouts = (0, 0, 0, 0, 0) #timeouts are done with WaitForSingleObject - timeouts = (0, 0, int(timeout*1000), 0, 0) + timeouts = (0, 0, int(self._timeout*1000), 0, 0) win32file.SetCommTimeouts(self.hComPort, timeouts) - #win32file.SetCommMask(self.hComPort, win32file.EV_RXCHAR | win32file.EV_TXEMPTY | - # win32file.EV_RXFLAG | win32file.EV_ERR) - #~ win32file.SetCommMask(self.hComPort, - #~ win32file.EV_RXCHAR | win32file.EV_RXFLAG | win32file.EV_ERR) win32file.SetCommMask(self.hComPort, win32file.EV_ERR) # Setup the connection info. # Get state and modify it: comDCB = win32file.GetCommState(self.hComPort) - comDCB.BaudRate = baudrate + comDCB.BaudRate = self._baudrate - if bytesize == FIVEBITS: + if self._bytesize == FIVEBITS: comDCB.ByteSize = 5 - elif bytesize == SIXBITS: + elif self._bytesize == SIXBITS: comDCB.ByteSize = 6 - elif bytesize == SEVENBITS: + elif self._bytesize == SEVENBITS: comDCB.ByteSize = 7 - elif bytesize == EIGHTBITS: + elif self._bytesize == EIGHTBITS: comDCB.ByteSize = 8 + else: + raise ValueError("Unsupported number of data bits: %r" % self._bytesize) - if parity == PARITY_NONE: + if self._parity == PARITY_NONE: comDCB.Parity = win32file.NOPARITY comDCB.fParity = 0 # Dis/Enable Parity Check - elif parity == PARITY_EVEN: + elif self._parity == PARITY_EVEN: comDCB.Parity = win32file.EVENPARITY comDCB.fParity = 1 # Dis/Enable Parity Check - elif parity == PARITY_ODD: + elif self._parity == PARITY_ODD: comDCB.Parity = win32file.ODDPARITY comDCB.fParity = 1 # Dis/Enable Parity Check + else: + raise ValueError("Unsupported parity mode: %r" % self._parity) - if stopbits == STOPBITS_ONE: + if self._stopbits == STOPBITS_ONE: comDCB.StopBits = win32file.ONESTOPBIT - elif stopbits == STOPBITS_TWO: + elif self._stopbits == STOPBITS_TWO: comDCB.StopBits = win32file.TWOSTOPBITS + else: + raise ValueError("Unsupported number of stop bits: %r" % self._stopbits) + comDCB.fBinary = 1 # Enable Binary Transmission # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE) - if rtscts: + if self._rtscts: comDCB.fRtsControl = win32file.RTS_CONTROL_HANDSHAKE comDCB.fDtrControl = win32file.DTR_CONTROL_HANDSHAKE else: comDCB.fRtsControl = win32file.RTS_CONTROL_ENABLE comDCB.fDtrControl = win32file.DTR_CONTROL_ENABLE - comDCB.fOutxCtsFlow = rtscts - comDCB.fOutxDsrFlow = rtscts - comDCB.fOutX = xonxoff - comDCB.fInX = xonxoff + comDCB.fOutxCtsFlow = self._rtscts + comDCB.fOutxDsrFlow = self._rtscts + comDCB.fOutX = self._xonxoff + comDCB.fInX = self._xonxoff comDCB.fNull = 0 comDCB.fErrorChar = 0 comDCB.fAbortOnError = 0 win32file.SetCommState(self.hComPort, comDCB) - # Clear buffers: - # Remove anything that was there - win32file.PurgeComm(self.hComPort, - win32file.PURGE_TXCLEAR | win32file.PURGE_TXABORT | - win32file.PURGE_RXCLEAR | win32file.PURGE_RXABORT) - - #print win32file.ClearCommError(self.hComPort) #flags, comState = - - self._overlappedRead = win32file.OVERLAPPED() - self._overlappedRead.hEvent = win32event.CreateEvent(None, 1, 0, None) - self._overlappedWrite = win32file.OVERLAPPED() - self._overlappedWrite.hEvent = win32event.CreateEvent(None, 0, 0, None) - - def __del__(self): - self.close() + #~ def __del__(self): + #~ self.close() def close(self): - """close port""" - if self.hComPort: - #Restore original timeout values: - win32file.SetCommTimeouts(self.hComPort, self.orgTimeouts) - #Close COM-Port: - win32file.CloseHandle(self.hComPort) - self.hComPort = None - - def setBaudrate(self, baudrate): - """change baudrate after port is open""" - if not self.hComPort: raise portNotOpenError - # Setup the connection info. - # Get state and modify it: - comDCB = win32file.GetCommState(self.hComPort) - comDCB.BaudRate = baudrate - win32file.SetCommState(self.hComPort, comDCB) - + """Close port""" + if self._isOpen: + if self.hComPort: + #Restore original timeout values: + win32file.SetCommTimeouts(self.hComPort, self._orgTimeouts) + #Close COM-Port: + win32file.CloseHandle(self.hComPort) + self.hComPort = None + self._isOpen = False + + def makeDeviceName(self, port): + return device(port) + + # - - - - - - - - - - - - - - - - - - - - - - - - + def inWaiting(self): - """returns the number of bytes waiting to be read""" + """Return the number of characters currently in the input buffer.""" flags, comstat = win32file.ClearCommError(self.hComPort) return comstat.cbInQue def read(self, size=1): - """read num bytes from serial port""" + """Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read.""" if not self.hComPort: raise portNotOpenError if size > 0: win32event.ResetEvent(self._overlappedRead.hEvent) @@ -202,7 +192,7 @@ class Serial(serialutil.FileLike): return read def write(self, s): - """write string to serial port""" + """Output the given string over the serial port.""" if not self.hComPort: raise portNotOpenError #print repr(s), if s: @@ -212,14 +202,18 @@ class Serial(serialutil.FileLike): win32event.WaitForSingleObject(self._overlappedWrite.hEvent, win32event.INFINITE) def flushInput(self): + """Clear input buffer, discarding all that is in the buffer.""" if not self.hComPort: raise portNotOpenError win32file.PurgeComm(self.hComPort, win32file.PURGE_RXCLEAR | win32file.PURGE_RXABORT) def flushOutput(self): + """Clear output buffer, aborting the current output and + discarding all that is in the buffer.""" if not self.hComPort: raise portNotOpenError win32file.PurgeComm(self.hComPort, win32file.PURGE_TXCLEAR | win32file.PURGE_TXABORT) def sendBreak(self): + """Send break condition.""" if not self.hComPort: raise portNotOpenError import time win32file.SetCommBreak(self.hComPort) @@ -228,7 +222,7 @@ class Serial(serialutil.FileLike): win32file.ClearCommBreak(self.hComPort) def setRTS(self,level=1): - """set terminal status line""" + """Set terminal status line: Request To Send""" if not self.hComPort: raise portNotOpenError if level: win32file.EscapeCommFunction(self.hComPort, win32file.SETRTS) @@ -236,7 +230,7 @@ class Serial(serialutil.FileLike): win32file.EscapeCommFunction(self.hComPort, win32file.CLRRTS) def setDTR(self,level=1): - """set terminal status line""" + """Set terminal status line: Data Terminal Ready""" if not self.hComPort: raise portNotOpenError if level: win32file.EscapeCommFunction(self.hComPort, win32file.SETDTR) @@ -244,27 +238,38 @@ class Serial(serialutil.FileLike): win32file.EscapeCommFunction(self.hComPort, win32file.CLRDTR) def getCTS(self): - """read terminal status line""" + """Read terminal status line: Clear To Send""" if not self.hComPort: raise portNotOpenError return MS_CTS_ON & win32file.GetCommModemStatus(self.hComPort) != 0 def getDSR(self): - """read terminal status line""" + """Read terminal status line: Data Set Ready""" if not self.hComPort: raise portNotOpenError return MS_DSR_ON & win32file.GetCommModemStatus(self.hComPort) != 0 def getRI(self): - """read terminal status line""" + """Read terminal status line: Ring Indicator""" if not self.hComPort: raise portNotOpenError return MS_RING_ON & win32file.GetCommModemStatus(self.hComPort) != 0 def getCD(self): - """read terminal status line""" + """Read terminal status line: Carrier Detect""" if not self.hComPort: raise portNotOpenError return MS_RLSD_ON & win32file.GetCommModemStatus(self.hComPort) != 0 #Nur Testfunktion!! if __name__ == '__main__': print __name__ + s = Serial() + print s + s = Serial(0) + print s + + s.baudrate = 19200 + s.databits = 7 + s.close() + s.port = 3 + s.open() + print s diff --git a/pyserial/setup.py b/pyserial/setup.py index d5a908d..3ce4d54 100644 --- a/pyserial/setup.py +++ b/pyserial/setup.py @@ -7,7 +7,7 @@ from distutils.core import setup setup( name="pyserial", description="Python Serial Port Extension", - version="1.21", + version="2.0b1", author="Chris Liechti", author_email="cliechti@gmx.net", url="http://pyserial.sourceforge.net/", |