Home | History | Annotate | Download | only in serial
      1 #! python
      2 # Python Serial Port Extension for Win32, Linux, BSD, Jython
      3 # see __init__.py
      4 #
      5 # (C) 2001-2010 Chris Liechti <cliechti (at] gmx.net>
      6 # this is distributed under a free software license, see license.txt
      7 
      8 # compatibility for older Python < 2.6
      9 try:
     10     bytes
     11     bytearray
     12 except (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.
     54 try:
     55     memoryview
     56 except (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
     65 def 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
     80 XON  = to_bytes([17])
     81 XOFF = to_bytes([19])
     82 
     83 CR = to_bytes([13])
     84 LF = to_bytes([10])
     85 
     86 
     87 PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S'
     88 STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2)
     89 FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8)
     90 
     91 PARITY_NAMES = {
     92     PARITY_NONE:  'None',
     93     PARITY_EVEN:  'Even',
     94     PARITY_ODD:   'Odd',
     95     PARITY_MARK:  'Mark',
     96     PARITY_SPACE: 'Space',
     97 }
     98 
     99 
    100 class SerialException(IOError):
    101     """Base class for serial port related exceptions."""
    102 
    103 
    104 class SerialTimeoutException(SerialException):
    105     """Write timeouts give an exception"""
    106 
    107 
    108 writeTimeoutError = SerialTimeoutException('Write timeout')
    109 portNotOpenError = SerialException('Attempting to use a port that is not open')
    110 
    111 
    112 class 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 
    221 class 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 
    543 if __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