serialwin32.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. #! python
  2. # Python Serial Port Extension for Win32, Linux, BSD, Jython
  3. # serial driver for win32
  4. # see __init__.py
  5. #
  6. # (C) 2001-2011 Chris Liechti <cliechti@gmx.net>
  7. # this is distributed under a free software license, see license.txt
  8. #
  9. # Initial patch to use ctypes by Giovanni Bajo <rasky@develer.com>
  10. import ctypes
  11. from serial import win32
  12. from serial.serialutil import *
  13. def device(portnum):
  14. """Turn a port number into a device name"""
  15. return 'COM%d' % (portnum+1) # numbers are transformed to a string
  16. class Win32Serial(SerialBase):
  17. """Serial port implementation for Win32 based on ctypes."""
  18. BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
  19. 9600, 19200, 38400, 57600, 115200)
  20. def __init__(self, *args, **kwargs):
  21. self.hComPort = None
  22. self._overlappedRead = None
  23. self._overlappedWrite = None
  24. self._rtsToggle = False
  25. self._rtsState = win32.RTS_CONTROL_ENABLE
  26. self._dtrState = win32.DTR_CONTROL_ENABLE
  27. SerialBase.__init__(self, *args, **kwargs)
  28. def open(self):
  29. """Open port with current settings. This may throw a SerialException
  30. if the port cannot be opened."""
  31. if self._port is None:
  32. raise SerialException("Port must be configured before it can be used.")
  33. if self._isOpen:
  34. raise SerialException("Port is already open.")
  35. # the "\\.\COMx" format is required for devices other than COM1-COM8
  36. # not all versions of windows seem to support this properly
  37. # so that the first few ports are used with the DOS device name
  38. port = self.portstr
  39. try:
  40. if port.upper().startswith('COM') and int(port[3:]) > 8:
  41. port = '\\\\.\\' + port
  42. except ValueError:
  43. # for like COMnotanumber
  44. pass
  45. self.hComPort = win32.CreateFile(port,
  46. win32.GENERIC_READ | win32.GENERIC_WRITE,
  47. 0, # exclusive access
  48. None, # no security
  49. win32.OPEN_EXISTING,
  50. win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED,
  51. 0)
  52. if self.hComPort == win32.INVALID_HANDLE_VALUE:
  53. self.hComPort = None # 'cause __del__ is called anyway
  54. raise SerialException("could not open port %r: %r" % (self.portstr, ctypes.WinError()))
  55. try:
  56. self._overlappedRead = win32.OVERLAPPED()
  57. self._overlappedRead.hEvent = win32.CreateEvent(None, 1, 0, None)
  58. self._overlappedWrite = win32.OVERLAPPED()
  59. #~ self._overlappedWrite.hEvent = win32.CreateEvent(None, 1, 0, None)
  60. self._overlappedWrite.hEvent = win32.CreateEvent(None, 0, 0, None)
  61. # Setup a 4k buffer
  62. win32.SetupComm(self.hComPort, 4096, 4096)
  63. # Save original timeout values:
  64. self._orgTimeouts = win32.COMMTIMEOUTS()
  65. win32.GetCommTimeouts(self.hComPort, ctypes.byref(self._orgTimeouts))
  66. self._reconfigurePort()
  67. # Clear buffers:
  68. # Remove anything that was there
  69. win32.PurgeComm(self.hComPort,
  70. win32.PURGE_TXCLEAR | win32.PURGE_TXABORT |
  71. win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
  72. except:
  73. try:
  74. self._close()
  75. except:
  76. # ignore any exception when closing the port
  77. # also to keep original exception that happened when setting up
  78. pass
  79. self.hComPort = None
  80. raise
  81. else:
  82. self._isOpen = True
  83. def _reconfigurePort(self):
  84. """Set communication parameters on opened port."""
  85. if not self.hComPort:
  86. raise SerialException("Can only operate on a valid port handle")
  87. # Set Windows timeout values
  88. # timeouts is a tuple with the following items:
  89. # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier,
  90. # ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier,
  91. # WriteTotalTimeoutConstant)
  92. if self._timeout is None:
  93. timeouts = (0, 0, 0, 0, 0)
  94. elif self._timeout == 0:
  95. timeouts = (win32.MAXDWORD, 0, 0, 0, 0)
  96. else:
  97. timeouts = (0, 0, int(self._timeout*1000), 0, 0)
  98. if self._timeout != 0 and self._interCharTimeout is not None:
  99. timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:]
  100. if self._writeTimeout is None:
  101. pass
  102. elif self._writeTimeout == 0:
  103. timeouts = timeouts[:-2] + (0, win32.MAXDWORD)
  104. else:
  105. timeouts = timeouts[:-2] + (0, int(self._writeTimeout*1000))
  106. win32.SetCommTimeouts(self.hComPort, ctypes.byref(win32.COMMTIMEOUTS(*timeouts)))
  107. win32.SetCommMask(self.hComPort, win32.EV_ERR)
  108. # Setup the connection info.
  109. # Get state and modify it:
  110. comDCB = win32.DCB()
  111. win32.GetCommState(self.hComPort, ctypes.byref(comDCB))
  112. comDCB.BaudRate = self._baudrate
  113. if self._bytesize == FIVEBITS:
  114. comDCB.ByteSize = 5
  115. elif self._bytesize == SIXBITS:
  116. comDCB.ByteSize = 6
  117. elif self._bytesize == SEVENBITS:
  118. comDCB.ByteSize = 7
  119. elif self._bytesize == EIGHTBITS:
  120. comDCB.ByteSize = 8
  121. else:
  122. raise ValueError("Unsupported number of data bits: %r" % self._bytesize)
  123. if self._parity == PARITY_NONE:
  124. comDCB.Parity = win32.NOPARITY
  125. comDCB.fParity = 0 # Disable Parity Check
  126. elif self._parity == PARITY_EVEN:
  127. comDCB.Parity = win32.EVENPARITY
  128. comDCB.fParity = 1 # Enable Parity Check
  129. elif self._parity == PARITY_ODD:
  130. comDCB.Parity = win32.ODDPARITY
  131. comDCB.fParity = 1 # Enable Parity Check
  132. elif self._parity == PARITY_MARK:
  133. comDCB.Parity = win32.MARKPARITY
  134. comDCB.fParity = 1 # Enable Parity Check
  135. elif self._parity == PARITY_SPACE:
  136. comDCB.Parity = win32.SPACEPARITY
  137. comDCB.fParity = 1 # Enable Parity Check
  138. else:
  139. raise ValueError("Unsupported parity mode: %r" % self._parity)
  140. if self._stopbits == STOPBITS_ONE:
  141. comDCB.StopBits = win32.ONESTOPBIT
  142. elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
  143. comDCB.StopBits = win32.ONE5STOPBITS
  144. elif self._stopbits == STOPBITS_TWO:
  145. comDCB.StopBits = win32.TWOSTOPBITS
  146. else:
  147. raise ValueError("Unsupported number of stop bits: %r" % self._stopbits)
  148. comDCB.fBinary = 1 # Enable Binary Transmission
  149. # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE)
  150. if self._rtscts:
  151. comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE
  152. elif self._rtsToggle:
  153. comDCB.fRtsControl = win32.RTS_CONTROL_TOGGLE
  154. else:
  155. comDCB.fRtsControl = self._rtsState
  156. if self._dsrdtr:
  157. comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE
  158. else:
  159. comDCB.fDtrControl = self._dtrState
  160. if self._rtsToggle:
  161. comDCB.fOutxCtsFlow = 0
  162. else:
  163. comDCB.fOutxCtsFlow = self._rtscts
  164. comDCB.fOutxDsrFlow = self._dsrdtr
  165. comDCB.fOutX = self._xonxoff
  166. comDCB.fInX = self._xonxoff
  167. comDCB.fNull = 0
  168. comDCB.fErrorChar = 0
  169. comDCB.fAbortOnError = 0
  170. comDCB.XonChar = XON
  171. comDCB.XoffChar = XOFF
  172. if not win32.SetCommState(self.hComPort, ctypes.byref(comDCB)):
  173. raise ValueError("Cannot configure port, some setting was wrong. Original message: %r" % ctypes.WinError())
  174. #~ def __del__(self):
  175. #~ self.close()
  176. def _close(self):
  177. """internal close port helper"""
  178. if self.hComPort:
  179. # Restore original timeout values:
  180. win32.SetCommTimeouts(self.hComPort, self._orgTimeouts)
  181. # Close COM-Port:
  182. win32.CloseHandle(self.hComPort)
  183. if self._overlappedRead is not None:
  184. win32.CloseHandle(self._overlappedRead.hEvent)
  185. self._overlappedRead = None
  186. if self._overlappedWrite is not None:
  187. win32.CloseHandle(self._overlappedWrite.hEvent)
  188. self._overlappedWrite = None
  189. self.hComPort = None
  190. def close(self):
  191. """Close port"""
  192. if self._isOpen:
  193. self._close()
  194. self._isOpen = False
  195. def makeDeviceName(self, port):
  196. return device(port)
  197. # - - - - - - - - - - - - - - - - - - - - - - - -
  198. def inWaiting(self):
  199. """Return the number of characters currently in the input buffer."""
  200. flags = win32.DWORD()
  201. comstat = win32.COMSTAT()
  202. if not win32.ClearCommError(self.hComPort, ctypes.byref(flags), ctypes.byref(comstat)):
  203. raise SerialException('call to ClearCommError failed')
  204. return comstat.cbInQue
  205. def read(self, size=1):
  206. """Read size bytes from the serial port. If a timeout is set it may
  207. return less characters as requested. With no timeout it will block
  208. until the requested number of bytes is read."""
  209. if not self.hComPort: raise portNotOpenError
  210. if size > 0:
  211. win32.ResetEvent(self._overlappedRead.hEvent)
  212. flags = win32.DWORD()
  213. comstat = win32.COMSTAT()
  214. if not win32.ClearCommError(self.hComPort, ctypes.byref(flags), ctypes.byref(comstat)):
  215. raise SerialException('call to ClearCommError failed')
  216. if self.timeout == 0:
  217. n = min(comstat.cbInQue, size)
  218. if n > 0:
  219. buf = ctypes.create_string_buffer(n)
  220. rc = win32.DWORD()
  221. err = win32.ReadFile(self.hComPort, buf, n, ctypes.byref(rc), ctypes.byref(self._overlappedRead))
  222. if not err and win32.GetLastError() != win32.ERROR_IO_PENDING:
  223. raise SerialException("ReadFile failed (%r)" % ctypes.WinError())
  224. err = win32.WaitForSingleObject(self._overlappedRead.hEvent, win32.INFINITE)
  225. read = buf.raw[:rc.value]
  226. else:
  227. read = bytes()
  228. else:
  229. buf = ctypes.create_string_buffer(size)
  230. rc = win32.DWORD()
  231. err = win32.ReadFile(self.hComPort, buf, size, ctypes.byref(rc), ctypes.byref(self._overlappedRead))
  232. if not err and win32.GetLastError() != win32.ERROR_IO_PENDING:
  233. raise SerialException("ReadFile failed (%r)" % ctypes.WinError())
  234. err = win32.GetOverlappedResult(self.hComPort, ctypes.byref(self._overlappedRead), ctypes.byref(rc), True)
  235. read = buf.raw[:rc.value]
  236. else:
  237. read = bytes()
  238. return bytes(read)
  239. def write(self, data):
  240. """Output the given string over the serial port."""
  241. if not self.hComPort: raise portNotOpenError
  242. #~ if not isinstance(data, (bytes, bytearray)):
  243. #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
  244. # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview
  245. data = to_bytes(data)
  246. if data:
  247. #~ win32event.ResetEvent(self._overlappedWrite.hEvent)
  248. n = win32.DWORD()
  249. err = win32.WriteFile(self.hComPort, data, len(data), ctypes.byref(n), self._overlappedWrite)
  250. if not err and win32.GetLastError() != win32.ERROR_IO_PENDING:
  251. raise SerialException("WriteFile failed (%r)" % ctypes.WinError())
  252. if self._writeTimeout != 0: # if blocking (None) or w/ write timeout (>0)
  253. # Wait for the write to complete.
  254. #~ win32.WaitForSingleObject(self._overlappedWrite.hEvent, win32.INFINITE)
  255. err = win32.GetOverlappedResult(self.hComPort, self._overlappedWrite, ctypes.byref(n), True)
  256. if n.value != len(data):
  257. raise writeTimeoutError
  258. return n.value
  259. else:
  260. return 0
  261. def flush(self):
  262. """Flush of file like objects. In this case, wait until all data
  263. is written."""
  264. while self.outWaiting():
  265. time.sleep(0.05)
  266. # XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would
  267. # require overlapped IO and its also only possible to set a single mask
  268. # on the port---
  269. def flushInput(self):
  270. """Clear input buffer, discarding all that is in the buffer."""
  271. if not self.hComPort: raise portNotOpenError
  272. win32.PurgeComm(self.hComPort, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
  273. def flushOutput(self):
  274. """Clear output buffer, aborting the current output and
  275. discarding all that is in the buffer."""
  276. if not self.hComPort: raise portNotOpenError
  277. win32.PurgeComm(self.hComPort, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT)
  278. def sendBreak(self, duration=0.25):
  279. """Send break condition. Timed, returns to idle state after given duration."""
  280. if not self.hComPort: raise portNotOpenError
  281. import time
  282. win32.SetCommBreak(self.hComPort)
  283. time.sleep(duration)
  284. win32.ClearCommBreak(self.hComPort)
  285. def setBreak(self, level=1):
  286. """Set break: Controls TXD. When active, to transmitting is possible."""
  287. if not self.hComPort: raise portNotOpenError
  288. if level:
  289. win32.SetCommBreak(self.hComPort)
  290. else:
  291. win32.ClearCommBreak(self.hComPort)
  292. def setRTS(self, level=1):
  293. """Set terminal status line: Request To Send"""
  294. # remember level for reconfigure
  295. if level:
  296. self._rtsState = win32.RTS_CONTROL_ENABLE
  297. else:
  298. self._rtsState = win32.RTS_CONTROL_DISABLE
  299. # also apply now if port is open
  300. if self.hComPort:
  301. if level:
  302. win32.EscapeCommFunction(self.hComPort, win32.SETRTS)
  303. else:
  304. win32.EscapeCommFunction(self.hComPort, win32.CLRRTS)
  305. def setDTR(self, level=1):
  306. """Set terminal status line: Data Terminal Ready"""
  307. # remember level for reconfigure
  308. if level:
  309. self._dtrState = win32.DTR_CONTROL_ENABLE
  310. else:
  311. self._dtrState = win32.DTR_CONTROL_DISABLE
  312. # also apply now if port is open
  313. if self.hComPort:
  314. if level:
  315. win32.EscapeCommFunction(self.hComPort, win32.SETDTR)
  316. else:
  317. win32.EscapeCommFunction(self.hComPort, win32.CLRDTR)
  318. def _GetCommModemStatus(self):
  319. stat = win32.DWORD()
  320. win32.GetCommModemStatus(self.hComPort, ctypes.byref(stat))
  321. return stat.value
  322. def getCTS(self):
  323. """Read terminal status line: Clear To Send"""
  324. if not self.hComPort: raise portNotOpenError
  325. return win32.MS_CTS_ON & self._GetCommModemStatus() != 0
  326. def getDSR(self):
  327. """Read terminal status line: Data Set Ready"""
  328. if not self.hComPort: raise portNotOpenError
  329. return win32.MS_DSR_ON & self._GetCommModemStatus() != 0
  330. def getRI(self):
  331. """Read terminal status line: Ring Indicator"""
  332. if not self.hComPort: raise portNotOpenError
  333. return win32.MS_RING_ON & self._GetCommModemStatus() != 0
  334. def getCD(self):
  335. """Read terminal status line: Carrier Detect"""
  336. if not self.hComPort: raise portNotOpenError
  337. return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0
  338. # - - platform specific - - - -
  339. def setBufferSize(self, rx_size=4096, tx_size=None):
  340. """\
  341. Recommend a buffer size to the driver (device driver can ignore this
  342. vlaue). Must be called before the port is opended.
  343. """
  344. if tx_size is None: tx_size = rx_size
  345. win32.SetupComm(self.hComPort, rx_size, tx_size)
  346. def setXON(self, level=True):
  347. """\
  348. Manually control flow - when software flow control is enabled.
  349. This will send XON (true) and XOFF (false) to the other device.
  350. WARNING: this function is not portable to different platforms!
  351. """
  352. if not self.hComPort: raise portNotOpenError
  353. if level:
  354. win32.EscapeCommFunction(self.hComPort, win32.SETXON)
  355. else:
  356. win32.EscapeCommFunction(self.hComPort, win32.SETXOFF)
  357. def outWaiting(self):
  358. """return how many characters the in the outgoing buffer"""
  359. flags = win32.DWORD()
  360. comstat = win32.COMSTAT()
  361. if not win32.ClearCommError(self.hComPort, ctypes.byref(flags), ctypes.byref(comstat)):
  362. raise SerialException('call to ClearCommError failed')
  363. return comstat.cbOutQue
  364. # functions useful for RS-485 adapters
  365. def setRtsToggle(self, rtsToggle):
  366. """Change RTS toggle control setting."""
  367. self._rtsToggle = rtsToggle
  368. if self._isOpen: self._reconfigurePort()
  369. def getRtsToggle(self):
  370. """Get the current RTS toggle control setting."""
  371. return self._rtsToggle
  372. rtsToggle = property(getRtsToggle, setRtsToggle, doc="RTS toggle control setting")
  373. # assemble Serial class with the platform specific implementation and the base
  374. # for file-like behavior. for Python 2.6 and newer, that provide the new I/O
  375. # library, derive from io.RawIOBase
  376. try:
  377. import io
  378. except ImportError:
  379. # classic version with our own file-like emulation
  380. class Serial(Win32Serial, FileLike):
  381. pass
  382. else:
  383. # io library present
  384. class Serial(Win32Serial, io.RawIOBase):
  385. pass
  386. # Nur Testfunktion!!
  387. if __name__ == '__main__':
  388. s = Serial(0)
  389. sys.stdout.write("%s\n" % s)
  390. s = Serial()
  391. sys.stdout.write("%s\n" % s)
  392. s.baudrate = 19200
  393. s.databits = 7
  394. s.close()
  395. s.port = 0
  396. s.open()
  397. sys.stdout.write("%s\n" % s)