1#! python
2# Python Serial Port Extension for Win32, Linux, BSD, Jython
3# see __init__.py
4#
5# (C) 2001-2010 Chris Liechti <cliechti@gmx.net>
6# this is distributed under a free software license, see license.txt
7
8# compatibility for older Python < 2.6
9try:
10    bytes
11    bytearray
12except (NameError, AttributeError):
13    # Python older than 2.6 do not have these types. Like for Python 2.6 they
14    # should behave like str. For Python older than 3.0 we want to work with
15    # strings anyway, only later versions have a true bytes type.
16    bytes = str
17    # bytearray is a mutable type that is easily turned into an instance of
18    # bytes
19    class bytearray(list):
20        # for bytes(bytearray()) usage
21        def __str__(self): return ''.join(self)
22        def __repr__(self): return 'bytearray(%r)' % ''.join(self)
23        # append automatically converts integers to characters
24        def append(self, item):
25            if isinstance(item, str):
26                list.append(self, item)
27            else:
28                list.append(self, chr(item))
29        # +=
30        def __iadd__(self, other):
31            for byte in other:
32                self.append(byte)
33            return self
34
35        def __getslice__(self, i, j):
36            return bytearray(list.__getslice__(self, i, j))
37
38        def __getitem__(self, item):
39            if isinstance(item, slice):
40                return bytearray(list.__getitem__(self, item))
41            else:
42                return ord(list.__getitem__(self, item))
43
44        def __eq__(self, other):
45            if isinstance(other, basestring):
46                other = bytearray(other)
47            return list.__eq__(self, other)
48
49# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)``
50# isn't returning the contents (very unfortunate). Therefore we need special
51# cases and test for it. Ensure that there is a ``memoryview`` object for older
52# Python versions. This is easier than making every test dependent on its
53# existence.
54try:
55    memoryview
56except (NameError, AttributeError):
57    # implementation does not matter as we do not realy use it.
58    # it just must not inherit from something else we might care for.
59    class memoryview:
60        pass
61
62
63# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11'
64# so a simple ``bytes(sequence)`` doesn't work for all versions
65def to_bytes(seq):
66    """convert a sequence to a bytes type"""
67    if isinstance(seq, bytes):
68        return seq
69    elif isinstance(seq, bytearray):
70        return bytes(seq)
71    elif isinstance(seq, memoryview):
72        return seq.tobytes()
73    else:
74        b = bytearray()
75        for item in seq:
76            b.append(item)  # this one handles int and str for our emulation and ints for Python 3.x
77        return bytes(b)
78
79# create control bytes
80XON  = to_bytes([17])
81XOFF = to_bytes([19])
82
83CR = to_bytes([13])
84LF = to_bytes([10])
85
86
87PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S'
88STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2)
89FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8)
90
91PARITY_NAMES = {
92    PARITY_NONE:  'None',
93    PARITY_EVEN:  'Even',
94    PARITY_ODD:   'Odd',
95    PARITY_MARK:  'Mark',
96    PARITY_SPACE: 'Space',
97}
98
99
100class SerialException(IOError):
101    """Base class for serial port related exceptions."""
102
103
104class SerialTimeoutException(SerialException):
105    """Write timeouts give an exception"""
106
107
108writeTimeoutError = SerialTimeoutException('Write timeout')
109portNotOpenError = SerialException('Attempting to use a port that is not open')
110
111
112class FileLike(object):
113    """An abstract file like class.
114
115    This class implements readline and readlines based on read and
116    writelines based on write.
117    This class is used to provide the above functions for to Serial
118    port objects.
119
120    Note that when the serial port was opened with _NO_ timeout that
121    readline blocks until it sees a newline (or the specified size is
122    reached) and that readlines would never return and therefore
123    refuses to work (it raises an exception in this case)!
124    """
125
126    def __init__(self):
127        self.closed = True
128
129    def close(self):
130        self.closed = True
131
132    # so that ports are closed when objects are discarded
133    def __del__(self):
134        """Destructor.  Calls close()."""
135        # The try/except block is in case this is called at program
136        # exit time, when it's possible that globals have already been
137        # deleted, and then the close() call might fail.  Since
138        # there's nothing we can do about such failures and they annoy
139        # the end users, we suppress the traceback.
140        try:
141            self.close()
142        except:
143            pass
144
145    def writelines(self, sequence):
146        for line in sequence:
147            self.write(line)
148
149    def flush(self):
150        """flush of file like objects"""
151        pass
152
153    # iterator for e.g. "for line in Serial(0): ..." usage
154    def next(self):
155        line = self.readline()
156        if not line: raise StopIteration
157        return line
158
159    def __iter__(self):
160        return self
161
162    def readline(self, size=None, eol=LF):
163        """read a line which is terminated with end-of-line (eol) character
164        ('\n' by default) or until timeout."""
165        leneol = len(eol)
166        line = bytearray()
167        while True:
168            c = self.read(1)
169            if c:
170                line += c
171                if line[-leneol:] == eol:
172                    break
173                if size is not None and len(line) >= size:
174                    break
175            else:
176                break
177        return bytes(line)
178
179    def readlines(self, sizehint=None, eol=LF):
180        """read a list of lines, until timeout.
181        sizehint is ignored."""
182        if self.timeout is None:
183            raise ValueError("Serial port MUST have enabled timeout for this function!")
184        leneol = len(eol)
185        lines = []
186        while True:
187            line = self.readline(eol=eol)
188            if line:
189                lines.append(line)
190                if line[-leneol:] != eol:    # was the line received with a timeout?
191                    break
192            else:
193                break
194        return lines
195
196    def xreadlines(self, sizehint=None):
197        """Read lines, implemented as generator. It will raise StopIteration on
198        timeout (empty read). sizehint is ignored."""
199        while True:
200            line = self.readline()
201            if not line: break
202            yield line
203
204    # other functions of file-likes - not used by pySerial
205
206    #~ readinto(b)
207
208    def seek(self, pos, whence=0):
209        raise IOError("file is not seekable")
210
211    def tell(self):
212        raise IOError("file is not seekable")
213
214    def truncate(self, n=None):
215        raise IOError("file is not seekable")
216
217    def isatty(self):
218        return False
219
220
221class SerialBase(object):
222    """Serial port base class. Provides __init__ function and properties to
223       get/set port settings."""
224
225    # default values, may be overridden in subclasses that do not support all values
226    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
227                 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000,
228                 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000,
229                 3000000, 3500000, 4000000)
230    BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS)
231    PARITIES  = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE)
232    STOPBITS  = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO)
233
234    def __init__(self,
235                 port = None,           # number of device, numbering starts at
236                                        # zero. if everything fails, the user
237                                        # can specify a device string, note
238                                        # that this isn't portable anymore
239                                        # port will be opened if one is specified
240                 baudrate=9600,         # baud rate
241                 bytesize=EIGHTBITS,    # number of data bits
242                 parity=PARITY_NONE,    # enable parity checking
243                 stopbits=STOPBITS_ONE, # number of stop bits
244                 timeout=None,          # set a timeout value, None to wait forever
245                 xonxoff=False,         # enable software flow control
246                 rtscts=False,          # enable RTS/CTS flow control
247                 writeTimeout=None,     # set a timeout for writes
248                 dsrdtr=False,          # None: use rtscts setting, dsrdtr override if True or False
249                 interCharTimeout=None  # Inter-character timeout, None to disable
250                 ):
251        """Initialize comm port object. If a port is given, then the port will be
252           opened immediately. Otherwise a Serial port object in closed state
253           is returned."""
254
255        self._isOpen   = False
256        self._port     = None           # correct value is assigned below through properties
257        self._baudrate = None           # correct value is assigned below through properties
258        self._bytesize = None           # correct value is assigned below through properties
259        self._parity   = None           # correct value is assigned below through properties
260        self._stopbits = None           # correct value is assigned below through properties
261        self._timeout  = None           # correct value is assigned below through properties
262        self._writeTimeout = None       # correct value is assigned below through properties
263        self._xonxoff  = None           # correct value is assigned below through properties
264        self._rtscts   = None           # correct value is assigned below through properties
265        self._dsrdtr   = None           # correct value is assigned below through properties
266        self._interCharTimeout = None   # correct value is assigned below through properties
267
268        # assign values using get/set methods using the properties feature
269        self.port     = port
270        self.baudrate = baudrate
271        self.bytesize = bytesize
272        self.parity   = parity
273        self.stopbits = stopbits
274        self.timeout  = timeout
275        self.writeTimeout = writeTimeout
276        self.xonxoff  = xonxoff
277        self.rtscts   = rtscts
278        self.dsrdtr   = dsrdtr
279        self.interCharTimeout = interCharTimeout
280
281        if port is not None:
282            self.open()
283
284    def isOpen(self):
285        """Check if the port is opened."""
286        return self._isOpen
287
288    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
289
290    # TODO: these are not really needed as the is the BAUDRATES etc. attribute...
291    # maybe i remove them before the final release...
292
293    def getSupportedBaudrates(self):
294        return [(str(b), b) for b in self.BAUDRATES]
295
296    def getSupportedByteSizes(self):
297        return [(str(b), b) for b in self.BYTESIZES]
298
299    def getSupportedStopbits(self):
300        return [(str(b), b) for b in self.STOPBITS]
301
302    def getSupportedParities(self):
303        return [(PARITY_NAMES[b], b) for b in self.PARITIES]
304
305    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
306
307    def setPort(self, port):
308        """Change the port. The attribute portstr is set to a string that
309           contains the name of the port."""
310
311        was_open = self._isOpen
312        if was_open: self.close()
313        if port is not None:
314            if isinstance(port, basestring):
315                self.portstr = port
316            else:
317                self.portstr = self.makeDeviceName(port)
318        else:
319            self.portstr = None
320        self._port = port
321        self.name = self.portstr
322        if was_open: self.open()
323
324    def getPort(self):
325        """Get the current port setting. The value that was passed on init or using
326           setPort() is passed back. See also the attribute portstr which contains
327           the name of the port as a string."""
328        return self._port
329
330    port = property(getPort, setPort, doc="Port setting")
331
332
333    def setBaudrate(self, baudrate):
334        """Change baud rate. It raises a ValueError if the port is open and the
335        baud rate is not possible. If the port is closed, then the value is
336        accepted and the exception is raised when the port is opened."""
337        try:
338            b = int(baudrate)
339        except TypeError:
340            raise ValueError("Not a valid baudrate: %r" % (baudrate,))
341        else:
342            if b <= 0:
343                raise ValueError("Not a valid baudrate: %r" % (baudrate,))
344            self._baudrate = b
345            if self._isOpen:  self._reconfigurePort()
346
347    def getBaudrate(self):
348        """Get the current baud rate setting."""
349        return self._baudrate
350
351    baudrate = property(getBaudrate, setBaudrate, doc="Baud rate setting")
352
353
354    def setByteSize(self, bytesize):
355        """Change byte size."""
356        if bytesize not in self.BYTESIZES: raise ValueError("Not a valid byte size: %r" % (bytesize,))
357        self._bytesize = bytesize
358        if self._isOpen: self._reconfigurePort()
359
360    def getByteSize(self):
361        """Get the current byte size setting."""
362        return self._bytesize
363
364    bytesize = property(getByteSize, setByteSize, doc="Byte size setting")
365
366
367    def setParity(self, parity):
368        """Change parity setting."""
369        if parity not in self.PARITIES: raise ValueError("Not a valid parity: %r" % (parity,))
370        self._parity = parity
371        if self._isOpen: self._reconfigurePort()
372
373    def getParity(self):
374        """Get the current parity setting."""
375        return self._parity
376
377    parity = property(getParity, setParity, doc="Parity setting")
378
379
380    def setStopbits(self, stopbits):
381        """Change stop bits size."""
382        if stopbits not in self.STOPBITS: raise ValueError("Not a valid stop bit size: %r" % (stopbits,))
383        self._stopbits = stopbits
384        if self._isOpen: self._reconfigurePort()
385
386    def getStopbits(self):
387        """Get the current stop bits setting."""
388        return self._stopbits
389
390    stopbits = property(getStopbits, setStopbits, doc="Stop bits setting")
391
392
393    def setTimeout(self, timeout):
394        """Change timeout setting."""
395        if timeout is not None:
396            try:
397                timeout + 1     # test if it's a number, will throw a TypeError if not...
398            except TypeError:
399                raise ValueError("Not a valid timeout: %r" % (timeout,))
400            if timeout < 0: raise ValueError("Not a valid timeout: %r" % (timeout,))
401        self._timeout = timeout
402        if self._isOpen: self._reconfigurePort()
403
404    def getTimeout(self):
405        """Get the current timeout setting."""
406        return self._timeout
407
408    timeout = property(getTimeout, setTimeout, doc="Timeout setting for read()")
409
410
411    def setWriteTimeout(self, timeout):
412        """Change timeout setting."""
413        if timeout is not None:
414            if timeout < 0: raise ValueError("Not a valid timeout: %r" % (timeout,))
415            try:
416                timeout + 1     #test if it's a number, will throw a TypeError if not...
417            except TypeError:
418                raise ValueError("Not a valid timeout: %r" % timeout)
419
420        self._writeTimeout = timeout
421        if self._isOpen: self._reconfigurePort()
422
423    def getWriteTimeout(self):
424        """Get the current timeout setting."""
425        return self._writeTimeout
426
427    writeTimeout = property(getWriteTimeout, setWriteTimeout, doc="Timeout setting for write()")
428
429
430    def setXonXoff(self, xonxoff):
431        """Change XON/XOFF setting."""
432        self._xonxoff = xonxoff
433        if self._isOpen: self._reconfigurePort()
434
435    def getXonXoff(self):
436        """Get the current XON/XOFF setting."""
437        return self._xonxoff
438
439    xonxoff = property(getXonXoff, setXonXoff, doc="XON/XOFF setting")
440
441    def setRtsCts(self, rtscts):
442        """Change RTS/CTS flow control setting."""
443        self._rtscts = rtscts
444        if self._isOpen: self._reconfigurePort()
445
446    def getRtsCts(self):
447        """Get the current RTS/CTS flow control setting."""
448        return self._rtscts
449
450    rtscts = property(getRtsCts, setRtsCts, doc="RTS/CTS flow control setting")
451
452    def setDsrDtr(self, dsrdtr=None):
453        """Change DsrDtr flow control setting."""
454        if dsrdtr is None:
455            # if not set, keep backwards compatibility and follow rtscts setting
456            self._dsrdtr = self._rtscts
457        else:
458            # if defined independently, follow its value
459            self._dsrdtr = dsrdtr
460        if self._isOpen: self._reconfigurePort()
461
462    def getDsrDtr(self):
463        """Get the current DSR/DTR flow control setting."""
464        return self._dsrdtr
465
466    dsrdtr = property(getDsrDtr, setDsrDtr, "DSR/DTR flow control setting")
467
468    def setInterCharTimeout(self, interCharTimeout):
469        """Change inter-character timeout setting."""
470        if interCharTimeout is not None:
471            if interCharTimeout < 0: raise ValueError("Not a valid timeout: %r" % interCharTimeout)
472            try:
473                interCharTimeout + 1     # test if it's a number, will throw a TypeError if not...
474            except TypeError:
475                raise ValueError("Not a valid timeout: %r" % interCharTimeout)
476
477        self._interCharTimeout = interCharTimeout
478        if self._isOpen: self._reconfigurePort()
479
480    def getInterCharTimeout(self):
481        """Get the current inter-character timeout setting."""
482        return self._interCharTimeout
483
484    interCharTimeout = property(getInterCharTimeout, setInterCharTimeout, doc="Inter-character timeout setting for read()")
485
486    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
487
488    _SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff',
489            'dsrdtr', 'rtscts', 'timeout', 'writeTimeout', 'interCharTimeout')
490
491    def getSettingsDict(self):
492        """Get current port settings as a dictionary. For use with
493        applySettingsDict"""
494        return dict([(key, getattr(self, '_'+key)) for key in self._SETTINGS])
495
496    def applySettingsDict(self, d):
497        """apply stored settings from a dictionary returned from
498        getSettingsDict. it's allowed to delete keys from the dictionary. these
499        values will simply left unchanged."""
500        for key in self._SETTINGS:
501            if d[key] != getattr(self, '_'+key):   # check against internal "_" value
502                setattr(self, key, d[key])          # set non "_" value to use properties write function
503
504    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
505
506    def __repr__(self):
507        """String representation of the current port settings and its state."""
508        return "%s<id=0x%x, open=%s>(port=%r, baudrate=%r, bytesize=%r, parity=%r, stopbits=%r, timeout=%r, xonxoff=%r, rtscts=%r, dsrdtr=%r)" % (
509            self.__class__.__name__,
510            id(self),
511            self._isOpen,
512            self.portstr,
513            self.baudrate,
514            self.bytesize,
515            self.parity,
516            self.stopbits,
517            self.timeout,
518            self.xonxoff,
519            self.rtscts,
520            self.dsrdtr,
521        )
522
523
524    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
525    # compatibility with io library
526
527    def readable(self): return True
528    def writable(self): return True
529    def seekable(self): return False
530    def readinto(self, b):
531        data = self.read(len(b))
532        n = len(data)
533        try:
534            b[:n] = data
535        except TypeError, err:
536            import array
537            if not isinstance(b, array.array):
538                raise err
539            b[:n] = array.array('b', data)
540        return n
541
542
543if __name__ == '__main__':
544    import sys
545    s = SerialBase()
546    sys.stdout.write('port name:  %s\n' % s.portstr)
547    sys.stdout.write('baud rates: %s\n' % s.getSupportedBaudrates())
548    sys.stdout.write('byte sizes: %s\n' % s.getSupportedByteSizes())
549    sys.stdout.write('parities:   %s\n' % s.getSupportedParities())
550    sys.stdout.write('stop bits:  %s\n' % s.getSupportedStopbits())
551    sys.stdout.write('%s\n' % s)
552