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