blob: f2df972bc8fd115b65bd802d134077151552eee7 [file] [log] [blame]
cliechti89b4af12002-02-12 23:24:41 +00001#!/usr/bin/env python
2#module for serial IO for POSIX compatible systems, like Linux
cliechtie8f75f92002-02-14 01:33:33 +00003#see __init__.py
cliechti89b4af12002-02-12 23:24:41 +00004#
cliechtie418ee12002-05-01 15:22:51 +00005#(C) 2001-2002 Chris Liechti <cliechti@gmx.net>
cliechti89b4af12002-02-12 23:24:41 +00006# this is distributed under a free software license, see license.txt
7#
8#parts based on code from Grant B. Edwards <grante@visi.com>:
9# ftp://ftp.visi.com/users/grante/python/PosixSerial.py
10# references: http://www.easysw.com/~mike/serial/serial.html
11
12import sys, os, fcntl, termios, struct, string, select
cliechtie8f75f92002-02-14 01:33:33 +000013import serialutil
cliechti89b4af12002-02-12 23:24:41 +000014
cliechti6ce7ab12002-11-07 02:15:00 +000015VERSION = string.split("$Revision: 1.12 $")[1] #extract CVS version
cliechti89b4af12002-02-12 23:24:41 +000016
17PARITY_NONE, PARITY_EVEN, PARITY_ODD = range(3)
18STOPBITS_ONE, STOPBITS_TWO = (1, 2)
19FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5,6,7,8)
20
cliechtie8f75f92002-02-14 01:33:33 +000021#Do check the Python version as some constants have moved.
cliechti89b4af12002-02-12 23:24:41 +000022if (sys.hexversion < 0x020100f0):
23 import TERMIOS
24else:
25 TERMIOS = termios
26
27if (sys.hexversion < 0x020200f0):
28 import FCNTL
29else:
30 FCNTL = fcntl
31
32#try to detect the os so that a device can be selected...
33plat = string.lower(sys.platform)
34
35if plat[:5] == 'linux': #Linux (confirmed)
36 def device(port):
37 return '/dev/ttyS%d' % port
38
cliechtif281fde2002-06-07 21:53:40 +000039elif plat == 'cygwin': #cywin/win32 (confirmed)
40 def device(port):
41 return '/dev/com%d' % port
42
cliechti89b4af12002-02-12 23:24:41 +000043elif plat == 'openbsd3': #BSD (confirmed)
44 def device(port):
45 return '/dev/ttyp%d' % port
46
47elif plat[:3] == 'bsd' or \
48 plat[:6] == 'netbsd' or \
49 plat[:7] == 'freebsd' or \
50 plat[:7] == 'openbsd' or \
51 plat[:6] == 'darwin': #BSD (confirmed for freebsd4: cuaa%d)
52 def device(port):
53 return '/dev/cuaa%d' % port
54
55elif plat[:4] == 'irix': #IRIX® (not tested)
56 def device(port):
57 return '/dev/ttyf%d' % port
58
59elif plat[:2] == 'hp': #HP-UX (not tested)
60 def device(port):
61 return '/dev/tty%dp0' % (port+1)
62
cliechtie418ee12002-05-01 15:22:51 +000063elif plat[:5] == 'sunos': #Solaris®/SunOS® (confirmed)
cliechti89b4af12002-02-12 23:24:41 +000064 def device(port):
65 return '/dev/tty%c' % (ord('a')+port)
66
67elif plat[:3] == 'dgux': #Digital UNIX® (not tested)
68 def device(port):
69 return '/dev/tty0%d' % (port+1)
70
71else:
72 #platform detection has failed...
73 info = "sys.platform = %r\nos.name = %r\nserialposix.py version = %s" % (sys.platform, os.name, VERSION)
74 print """send this information to the author of this module:
75
76%s
77
78also add the device name of the serial port and where the
79counting starts for the first serial port.
80e.g. 'first serial port: /dev/ttyS0'
81and with a bit luck you can get this module running...
82"""
83 raise Exception, "this module does not run on this platform, sorry."
84
85#whats up with "aix", "beos", "sco", ....
86#they should work, just need to know the device names.
87#"cygwin" has a POSIX emulation but does not seem to have a /dev/ttyxx structure?
88
89
90# construct dictionaries for baud rate lookups
91baudEnumToInt = {}
92baudIntToEnum = {}
93for rate in (0,50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600,
94 19200,38400,57600,115200,230400,460800,500000,576000,921600,
95 1000000,1152000,1500000,2000000,2500000,3000000,3500000,4000000
96 ):
97 try:
98 i = eval('TERMIOS.B'+str(rate))
99 baudEnumToInt[i]=rate
100 baudIntToEnum[rate] = i
101 except:
102 pass
103
cliechtie8f75f92002-02-14 01:33:33 +0000104#load some constants for late use
105if hasattr(TERMIOS, 'TIOCMGET'): #if this const is here the others will be to (hopefully)
cliechti89b4af12002-02-12 23:24:41 +0000106 TIOCMGET = TERMIOS.TIOCMGET
107 TIOCMBIS = TERMIOS.TIOCMBIS
108 TIOCMBIC = TERMIOS.TIOCMBIC
109 TIOCMSET = TERMIOS.TIOCMSET
110
cliechtia3ca5412002-02-14 18:44:01 +0000111# TIOCM_LE = TERMIOS.TIOCM_LE
cliechti89b4af12002-02-12 23:24:41 +0000112 TIOCM_DTR = TERMIOS.TIOCM_DTR
113 TIOCM_RTS = TERMIOS.TIOCM_RTS
cliechtia3ca5412002-02-14 18:44:01 +0000114# TIOCM_ST = TERMIOS.TIOCM_ST
115# TIOCM_SR = TERMIOS.TIOCM_SR
cliechti89b4af12002-02-12 23:24:41 +0000116 TIOCM_CTS = TERMIOS.TIOCM_CTS
117 TIOCM_CAR = TERMIOS.TIOCM_CAR
118 TIOCM_RNG = TERMIOS.TIOCM_RNG
119 TIOCM_DSR = TERMIOS.TIOCM_DSR
120 TIOCM_CD = TERMIOS.TIOCM_CD
121 TIOCM_RI = TERMIOS.TIOCM_RI
cliechtia3ca5412002-02-14 18:44:01 +0000122# TIOCM_OUT1 = TERMIOS.TIOCM_OUT1
123# TIOCM_OUT2 = TERMIOS.TIOCM_OUT2
cliechti89b4af12002-02-12 23:24:41 +0000124else: #workaround for older python versions
cliechtie8f75f92002-02-14 01:33:33 +0000125 TIOCMGET = 0x5415
126 TIOCMBIS = 0x5416
127 TIOCMBIC = 0x5417
128 TIOCMSET = 0x5418
cliechti89b4af12002-02-12 23:24:41 +0000129
cliechtia3ca5412002-02-14 18:44:01 +0000130# TIOCM_LE = 0x001
cliechti89b4af12002-02-12 23:24:41 +0000131 TIOCM_DTR = 0x002
132 TIOCM_RTS = 0x004
cliechtia3ca5412002-02-14 18:44:01 +0000133# TIOCM_ST = 0x008
134# TIOCM_SR = 0x010
cliechti89b4af12002-02-12 23:24:41 +0000135 TIOCM_CTS = 0x020
136 TIOCM_CAR = 0x040
137 TIOCM_RNG = 0x080
138 TIOCM_DSR = 0x100
139 TIOCM_CD = TIOCM_CAR
140 TIOCM_RI = TIOCM_RNG
cliechtia3ca5412002-02-14 18:44:01 +0000141# TIOCM_OUT1 = 0x2000
142# TIOCM_OUT2 = 0x4000
cliechti89b4af12002-02-12 23:24:41 +0000143
144TIOCM_zero_str = struct.pack('I', 0)
145TIOCM_RTS_str = struct.pack('I', TIOCM_RTS)
146TIOCM_DTR_str = struct.pack('I', TIOCM_DTR)
147
148portNotOpenError = ValueError('port not open')
149
cliechtie8f75f92002-02-14 01:33:33 +0000150class Serial(serialutil.FileLike):
cliechti89b4af12002-02-12 23:24:41 +0000151 def __init__(self,
152 port, #number of device, numbering starts at
153 #zero. if everything fails, the user
154 #can specify a device string, note
155 #that this isn't portable anymore
156 baudrate=9600, #baudrate
157 bytesize=EIGHTBITS, #number of databits
158 parity=PARITY_NONE, #enable parity checking
159 stopbits=STOPBITS_ONE, #number of stopbits
160 timeout=None, #set a timeout value, None for waiting forever
161 xonxoff=0, #enable software flow control
162 rtscts=0, #enable RTS/CTS flow control
163 ):
cliechtie8f75f92002-02-14 01:33:33 +0000164 """init comm port"""
cliechti89b4af12002-02-12 23:24:41 +0000165 self.fd = None
166 self.timeout = timeout
167 vmin = vtime = 0 #timeout is done via select
168 #open
169 if type(port) == type(''): #strings are taken directly
170 self.portstr = port
171 else:
cliechti6ce7ab12002-11-07 02:15:00 +0000172 self.portstr = device(port) #numbers are transformed to a os dependant string
cliechti4616bf12002-04-08 23:13:14 +0000173 try:
174 self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK)
175 except Exception, msg:
176 self.fd = None
177 raise serialutil.SerialException, "could not open port: %s" % msg
cliechti89b4af12002-02-12 23:24:41 +0000178 fcntl.fcntl(self.fd, FCNTL.F_SETFL, 0) #set blocking
cliechti6ce7ab12002-11-07 02:15:00 +0000179 try:
180 self.__tcgetattr() #read current settings
181 except termios.error, msg: #if a port is nonexistent but has a /dev file, it'll fail here
182 raise serialutil.SerialException, "could not open port: %s" % msg
cliechti89b4af12002-02-12 23:24:41 +0000183 #set up raw mode / no echo / binary
184 self.cflag = self.cflag | (TERMIOS.CLOCAL|TERMIOS.CREAD)
185 self.lflag = self.lflag & ~(TERMIOS.ICANON|TERMIOS.ECHO|TERMIOS.ECHOE|TERMIOS.ECHOK|TERMIOS.ECHONL|
cliechtif281fde2002-06-07 21:53:40 +0000186 TERMIOS.ECHOCTL|TERMIOS.ECHOKE|TERMIOS.ISIG|TERMIOS.IEXTEN) #|TERMIOS.ECHOPRT
cliechti89b4af12002-02-12 23:24:41 +0000187 self.oflag = self.oflag & ~(TERMIOS.OPOST)
188 if hasattr(TERMIOS, 'IUCLC'):
189 self.iflag = self.iflag & ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IUCLC|TERMIOS.IGNBRK)
190 else:
191 self.iflag = self.iflag & ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IGNBRK)
192 #setup baudrate
193 try:
194 self.ispeed = self.ospeed = baudIntToEnum[baudrate]
195 except:
196 raise ValueError,'invalid baud rate: %s' % baudrate
197 #setup char len
198 self.cflag = self.cflag & ~TERMIOS.CSIZE
199 if bytesize == 8:
200 self.cflag = self.cflag | TERMIOS.CS8
201 elif bytesize == 7:
202 self.cflag = self.cflag | TERMIOS.CS7
203 elif bytesize == 6:
204 self.cflag = self.cflag | TERMIOS.CS6
205 elif bytesize == 5:
206 self.cflag = self.cflag | TERMIOS.CS5
207 else:
208 raise ValueError,'invalid char len: '+str(clen)
209 #setup stopbits
210 if stopbits == STOPBITS_ONE:
211 self.cflag = self.cflag & ~(TERMIOS.CSTOPB)
212 elif stopbits == STOPBITS_TWO:
213 self.cflag = self.cflag | (TERMIOS.CSTOPB)
214 else:
215 raise ValueError,'invalid stopit specification:'+str(stopbits)
216 #setup parity
217 self.iflag = self.iflag & ~(TERMIOS.INPCK|TERMIOS.ISTRIP)
218 if parity == PARITY_NONE:
219 self.cflag = self.cflag & ~(TERMIOS.PARENB|TERMIOS.PARODD)
220 elif parity == PARITY_EVEN:
221 self.cflag = self.cflag & ~(TERMIOS.PARODD)
222 self.cflag = self.cflag | (TERMIOS.PARENB)
223 elif parity == PARITY_ODD:
224 self.cflag = self.cflag | (TERMIOS.PARENB|TERMIOS.PARODD)
225 else:
226 raise ValueError,'invalid parity: '+str(par)
227 #setup flow control
228 #xonxoff
229 if hasattr(TERMIOS, 'IXANY'):
230 if xonxoff:
231 self.iflag = self.iflag | (TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY)
232 else:
233 self.iflag = self.iflag & ~(TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY)
234 else:
235 if xonxoff:
236 self.iflag = self.iflag | (TERMIOS.IXON|TERMIOS.IXOFF)
237 else:
238 self.iflag = self.iflag & ~(TERMIOS.IXON|TERMIOS.IXOFF)
239 #rtscts
240 if hasattr(TERMIOS, 'CRTSCTS'):
241 if rtscts:
242 self.cflag = self.cflag | (TERMIOS.CRTSCTS)
243 else:
244 self.cflag = self.cflag & ~(TERMIOS.CRTSCTS)
cliechtid4743692002-04-08 22:39:53 +0000245 elif hasattr(TERMIOS, 'CNEW_RTSCTS'): #try it with alternate constant name
cliechti09cadc62002-04-08 23:31:01 +0000246 if rtscts:
cliechtid4743692002-04-08 22:39:53 +0000247 self.cflag = self.cflag | (TERMIOS.CNEW_RTSCTS)
248 else:
249 self.cflag = self.cflag & ~(TERMIOS.CNEW_RTSCTS)
250 #XXX should there be a warning if setting up rtscts (and xonxoff etc) fails??
251
cliechti89b4af12002-02-12 23:24:41 +0000252 #buffer
253 #vmin "minimal number of characters to be read. = for non blocking"
254 if vmin<0 or vmin>255:
255 raise ValueError,'invalid vmin: '+str(vmin)
256 self.cc[TERMIOS.VMIN] = vmin
257 #vtime
258 if vtime<0 or vtime>255:
259 raise ValueError,'invalid vtime: '+str(vtime)
260 self.cc[TERMIOS.VTIME] = vtime
261 #activate settings
262 self.__tcsetattr()
263
264 def __tcsetattr(self):
cliechtie8f75f92002-02-14 01:33:33 +0000265 """internal function to set port attributes"""
cliechti89b4af12002-02-12 23:24:41 +0000266 termios.tcsetattr(self.fd, TERMIOS.TCSANOW, [self.iflag,self.oflag,self.cflag,self.lflag,self.ispeed,self.ospeed,self.cc])
267
268 def __tcgetattr(self):
cliechtie8f75f92002-02-14 01:33:33 +0000269 """internal function to get port attributes"""
cliechti89b4af12002-02-12 23:24:41 +0000270 self.iflag,self.oflag,self.cflag,self.lflag,self.ispeed,self.ospeed,self.cc = termios.tcgetattr(self.fd)
271
272 def close(self):
cliechtie8f75f92002-02-14 01:33:33 +0000273 """close port"""
cliechti89b4af12002-02-12 23:24:41 +0000274 if self.fd:
275 os.close(self.fd)
276 self.fd = None
277
cliechti95c62212002-03-04 22:17:53 +0000278 def setBaudrate(self, baudrate):
279 """change baudrate after port is open"""
280 if not self.fd: raise portNotOpenError
281 self.__tcgetattr() #read current settings
282 #setup baudrate
283 try:
284 self.ispeed = self.ospeed = baudIntToEnum[baudrate]
285 except:
286 raise ValueError,'invalid baud rate: %s' % baudrate
287 self.__tcsetattr()
288
cliechti89b4af12002-02-12 23:24:41 +0000289 def inWaiting(self):
cliechtie8f75f92002-02-14 01:33:33 +0000290 """how many character are in the input queue"""
cliechti89b4af12002-02-12 23:24:41 +0000291 s = fcntl.ioctl(self.fd, TERMIOS.FIONREAD, TIOCM_zero_str)
292 return struct.unpack('I',s)[0]
293
294 def write(self, data):
cliechtie8f75f92002-02-14 01:33:33 +0000295 """write a string to the port"""
cliechti89b4af12002-02-12 23:24:41 +0000296 if not self.fd: raise portNotOpenError
297 t = len(data)
298 d = data
299 while t>0:
300 n = os.write(self.fd, d)
301 d = d[n:]
302 t = t - n
303
304 def read(self, size=1):
cliechtie8f75f92002-02-14 01:33:33 +0000305 """read a number of bytes from the port.
306 the default is one (unlike files)"""
cliechti89b4af12002-02-12 23:24:41 +0000307 if not self.fd: raise portNotOpenError
308 read = ''
cliechti8d5dbe22002-04-24 20:44:13 +0000309 inp = None
cliechti89b4af12002-02-12 23:24:41 +0000310 if size > 0:
311 while len(read) < size:
cliechtia9e4e952002-05-26 01:20:22 +0000312 #print "\tread(): size",size, "have", len(read) #debug
313 ready,_,_ = select.select([self.fd],[],[], self.timeout)
314 if not ready:
315 break #timeout
316 buf = os.read(self.fd, size-len(read))
317 read = read + buf
318 if self.timeout >= 0 and not buf:
319 break #early abort on timeout
cliechti89b4af12002-02-12 23:24:41 +0000320 return read
321
322 def flushInput(self):
cliechtie8f75f92002-02-14 01:33:33 +0000323 """clear input queue"""
cliechti89b4af12002-02-12 23:24:41 +0000324 if not self.fd:
325 raise portNotOpenError
326 termios.tcflush(self.fd, TERMIOS.TCIFLUSH)
327
328 def flushOutput(self):
cliechtie8f75f92002-02-14 01:33:33 +0000329 """flush output"""
cliechti89b4af12002-02-12 23:24:41 +0000330 if not self.fd:
331 raise portNotOpenError
332 termios.tcflush(self.fd, TERMIOS.TCOFLUSH)
333
334 def sendBreak(self):
cliechtie8f75f92002-02-14 01:33:33 +0000335 """send break signal"""
cliechti89b4af12002-02-12 23:24:41 +0000336 if not self.fd:
337 raise portNotOpenError
338 termios.tcsendbreak(self.fd, 0)
339
340 def drainOutput(self):
cliechtie8f75f92002-02-14 01:33:33 +0000341 """internal - not portable!"""
cliechti89b4af12002-02-12 23:24:41 +0000342 if not self.fd: raise portNotOpenError
343 termios.tcdrain(self.fd)
344
345 def nonblocking(self):
cliechtie8f75f92002-02-14 01:33:33 +0000346 """internal - not portable!"""
cliechti89b4af12002-02-12 23:24:41 +0000347 if not self.fd:
348 raise portNotOpenError
349 fcntl.fcntl(self.fd, FCNTL.F_SETFL, FCNTL.O_NONBLOCK)
350
351 def getDSR(self):
cliechtie8f75f92002-02-14 01:33:33 +0000352 """read terminal status line"""
cliechti89b4af12002-02-12 23:24:41 +0000353 if not self.fd: raise portNotOpenError
354 s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
355 return struct.unpack('I',s)[0] & TIOCM_DSR
356
357 def getCD(self):
cliechtie8f75f92002-02-14 01:33:33 +0000358 """read terminal status line"""
cliechti89b4af12002-02-12 23:24:41 +0000359 if not self.fd: raise portNotOpenError
360 s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
361 return struct.unpack('I',s)[0] & TIOCM_CD
362
363 def getRI(self):
cliechtie8f75f92002-02-14 01:33:33 +0000364 """read terminal status line"""
cliechti89b4af12002-02-12 23:24:41 +0000365 if not self.fd: raise portNotOpenError
366 s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
367 return struct.unpack('I',s)[0] & TIOCM_RI
368
369 def getCTS(self):
cliechtie8f75f92002-02-14 01:33:33 +0000370 """read terminal status line"""
cliechti89b4af12002-02-12 23:24:41 +0000371 if not self.fd: raise portNotOpenError
372 s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
373 return struct.unpack('I',s)[0] & TIOCM_CTS
374
375 def setDTR(self,on=1):
cliechtie8f75f92002-02-14 01:33:33 +0000376 """set terminal status line"""
cliechti89b4af12002-02-12 23:24:41 +0000377 if not self.fd: raise portNotOpenError
378 if on:
379 fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str)
380 else:
381 fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str)
382
383 def setRTS(self,on=1):
cliechtie8f75f92002-02-14 01:33:33 +0000384 """set terminal status line"""
cliechti89b4af12002-02-12 23:24:41 +0000385 if not self.fd: raise portNotOpenError
386 if on:
387 fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str)
388 else:
389 fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str)
390
391if __name__ == '__main__':
392 s = Serial(0,
393 baudrate=19200, #baudrate
394 bytesize=EIGHTBITS, #number of databits
395 parity=PARITY_EVEN, #enable parity checking
396 stopbits=STOPBITS_ONE, #number of stopbits
397 timeout=3, #set a timeout value, None for waiting forever
398 xonxoff=0, #enable software flow control
399 rtscts=0, #enable RTS/CTS flow control
400 )
401 s.setRTS(1)
402 s.setDTR(1)
403 s.flushInput()
404 s.flushOutput()
405 s.write('hello')
406 print repr(s.read(5))
407 print s.inWaiting()
408 del s