11 import sys, os, serial, threading
14 MENUCHARACTER =
'\x14'
18 """generate a readable description for a key"""
19 ascii_code = ord(character)
21 return 'Ctrl+%c' % (ord(
'@') + ascii_code)
23 return repr(character)
30 --- pySerial (%(version)s) - miniterm - help
32 --- %(exit)-8s Exit program
33 --- %(menu)-8s Menu escape key, followed by:
35 --- %(itself)-7s Send the menu character itself to remote
36 --- %(exchar)-7s Send the exit character itself to remote
37 --- %(info)-7s Show info
38 --- %(upload)-7s Upload file (prompt will be shown)
40 --- %(rts)-7s RTS %(echo)-7s local echo
41 --- %(dtr)-7s DTR %(break)-7s BREAK
42 --- %(lfm)-7s line feed %(repr)-7s Cycle repr mode
44 --- Port settings (%(menu)s followed by the following):
47 --- n e o s m change parity (None, Even, Odd, Space, Mark)
48 --- 1 2 3 set stop bits (1, 2, 1.5)
49 --- b change baud rate
50 --- x X disable/enable software flow control
51 --- r R disable/enable hardware flow control
53 'version': getattr(serial,
'VERSION',
'unknown version'),
68 if sys.version_info >= (3, 0):
70 return b.decode(
'latin1')
93 if z ==
'\0' or z ==
'\xe0':
102 elif os.name ==
'posix':
103 import termios, sys, os
106 self.
fd = sys.stdin.fileno()
109 self.
old = termios.tcgetattr(self.
fd)
110 new = termios.tcgetattr(self.
fd)
111 new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG
112 new[6][termios.VMIN] = 1
113 new[6][termios.VTIME] = 0
114 termios.tcsetattr(self.
fd, termios.TCSANOW, new)
117 c = os.read(self.
fd, 1)
121 termios.tcsetattr(self.
fd, termios.TCSAFLUSH, self.
old)
129 sys.exitfunc = cleanup_console
132 raise NotImplementedError(
"Sorry no implementation for your platform (%s) available." % sys.platform)
138 NEWLINE_CONVERISON_MAP = (
'\n',
'\r',
'\r\n')
139 LF_MODES = (
'LF',
'CR',
'CR/LF')
141 REPR_MODES = (
'raw',
'some control',
'all control',
'hex')
144 def __init__(self, port, baudrate, parity, rtscts, xonxoff, echo=False, convert_outgoing=CONVERT_CRLF, repr_mode=0):
146 self.
serial = serial.serial_for_url(port, baudrate, parity=parity, rtscts=rtscts, xonxoff=xonxoff, timeout=1)
147 except AttributeError:
150 self.
serial = serial.Serial(port, baudrate, parity=parity, rtscts=rtscts, xonxoff=xonxoff, timeout=1)
160 """Start reader thread"""
164 self.receiver_thread.setDaemon(
True)
165 self.receiver_thread.start()
168 """Stop reader thread only, wait for clean exit of thread"""
170 self.receiver_thread.join()
178 self.transmitter_thread.setDaemon(
True)
179 self.transmitter_thread.start()
184 def join(self, transmit_only=False):
185 self.transmitter_thread.join()
186 if not transmit_only:
187 self.receiver_thread.join()
190 sys.stderr.write(
"\n--- Settings: %s %s,%s,%s,%s\n" % (
192 self.serial.baudrate,
193 self.serial.bytesize,
195 self.serial.stopbits))
196 sys.stderr.write(
'--- RTS: %-8s DTR: %-8s BREAK: %-8s\n' % (
197 (self.
rts_state and 'active' or 'inactive'),
198 (self.
dtr_state and 'active' or 'inactive'),
201 sys.stderr.write(
'--- CTS: %-8s DSR: %-8s RI: %-8s CD: %-8s\n' % (
202 (self.serial.getCTS()
and 'active' or 'inactive'),
203 (self.serial.getDSR()
and 'active' or 'inactive'),
204 (self.serial.getRI()
and 'active' or 'inactive'),
205 (self.serial.getCD()
and 'active' or 'inactive')))
206 except serial.SerialException:
210 sys.stderr.write(
'--- software flow control: %s\n' % (self.serial.xonxoff
and 'active' or 'inactive'))
211 sys.stderr.write(
'--- hardware flow control: %s\n' % (self.serial.rtscts
and 'active' or 'inactive'))
212 sys.stderr.write(
'--- data escaping: %s linefeed: %s\n' % (
217 """loop and copy serial->console"""
225 sys.stdout.write(
'\n')
227 sys.stdout.write(data)
232 sys.stdout.write(
'\n')
236 sys.stdout.write(
'\n')
238 sys.stdout.write(
'\n')
240 sys.stdout.write(repr(data)[1:-1])
243 sys.stdout.write(repr(data)[1:-1])
247 sys.stdout.write(
"%s " % c.encode(
'hex'))
249 except serial.SerialException, e:
258 Loop and copy console->serial until EXITCHARCTER character is
259 found. When MENUCHARACTER is found, interpret the next key
267 except KeyboardInterrupt:
268 b = serial.to_bytes([3])
271 if c == MENUCHARACTER
or c == EXITCHARCTER:
276 sys.stderr.write(
'\n--- File to upload: ')
279 filename = sys.stdin.readline().rstrip(
'\r\n')
282 file = open(filename,
'r')
283 sys.stderr.write('--- Sending file %s ---\n' % filename)
285 line = file.readline().rstrip(
'\r\n')
288 self.serial.write(line)
289 self.serial.write(
'\r\n')
292 sys.stderr.write(
'.')
293 sys.stderr.write(
'\n--- File %s sent ---\n' % filename)
295 sys.stderr.write(
'--- ERROR opening file %s: %s ---\n' % (filename, e))
302 sys.stderr.write(
'--- RTS %s ---\n' % (self.
rts_state and 'active' or 'inactive'))
306 sys.stderr.write(
'--- DTR %s ---\n' % (self.
dtr_state and 'active' or 'inactive'))
310 sys.stderr.write(
'--- BREAK %s ---\n' % (self.
break_state and 'active' or 'inactive'))
313 sys.stderr.write(
'--- local echo %s ---\n' % (self.
echo and 'active' or 'inactive'))
320 sys.stderr.write(
'--- escape data: %s ---\n' % (
328 sys.stderr.write(
'--- line feed %s ---\n' % (
332 sys.stderr.write(
'\n--- Enter port name: ')
336 port = sys.stdin.readline().strip()
337 except KeyboardInterrupt:
340 if port
and port != self.serial.port:
344 settings = self.serial.getSettingsDict()
347 new_serial = serial.serial_for_url(port, do_not_open=
True)
348 except AttributeError:
351 new_serial = serial.Serial()
352 new_serial.port = port
354 new_serial.applySettingsDict(settings)
360 sys.stderr.write(
'--- ERROR opening new port: %s ---\n' % (e,))
365 sys.stderr.write(
'--- Port changed to: %s ---\n' % (self.serial.port,))
369 sys.stderr.write(
'\n--- Baudrate: ')
372 backup = self.serial.baudrate
374 self.serial.baudrate = int(sys.stdin.readline().strip())
375 except ValueError, e:
376 sys.stderr.write(
'--- ERROR setting baudrate: %s ---\n' % (e,))
377 self.serial.baudrate = backup
382 self.serial.bytesize = serial.EIGHTBITS
385 self.serial.bytesize = serial.SEVENBITS
388 self.serial.parity = serial.PARITY_EVEN
391 self.serial.parity = serial.PARITY_ODD
394 self.serial.parity = serial.PARITY_MARK
397 self.serial.parity = serial.PARITY_SPACE
400 self.serial.parity = serial.PARITY_NONE
403 self.serial.stopbits = serial.STOPBITS_ONE
406 self.serial.stopbits = serial.STOPBITS_TWO
409 self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE
412 self.serial.xonxoff = (c ==
'X')
415 self.serial.rtscts = (c ==
'R')
418 sys.stderr.write(
'--- unknown menu character %s --\n' %
key_description(c))
420 elif c == MENUCHARACTER:
422 elif c == EXITCHARCTER:
426 self.serial.write(self.
newline)
442 parser = optparse.OptionParser(
443 usage =
"%prog [options] [port [baudrate]]",
444 description =
"Miniterm - A simple terminal program for the serial port."
447 parser.add_option(
"-p",
"--port",
449 help =
"port, a number or a device name. (deprecated option, use parameter instead)",
453 parser.add_option(
"-b",
"--baud",
457 help =
"set baud rate, default %default",
461 parser.add_option(
"--parity",
464 help =
"set parity, one of [N, E, O, S, M], default=N",
468 parser.add_option(
"-e",
"--echo",
470 action =
"store_true",
471 help =
"enable local echo (default off)",
475 parser.add_option(
"--rtscts",
477 action =
"store_true",
478 help =
"enable RTS/CTS flow control (default off)",
482 parser.add_option(
"--xonxoff",
484 action =
"store_true",
485 help =
"enable software flow control (default off)",
489 parser.add_option(
"--cr",
491 action =
"store_true",
492 help =
"do not send CR+LF, send CR only",
496 parser.add_option(
"--lf",
498 action =
"store_true",
499 help =
"do not send CR+LF, send LF only",
503 parser.add_option(
"-D",
"--debug",
506 help =
"""debug received data (escape non-printable chars)
507 --debug can be given multiple times:
508 0: just print what is received
509 1: escape non-printable characters, do newlines as unusual
510 2: escape non-printable characters, newlines too
511 3: hex dump everything""",
515 parser.add_option(
"--rts",
519 help =
"set initial RTS line state (possible values: 0, 1)",
523 parser.add_option(
"--dtr",
527 help =
"set initial DTR line state (possible values: 0, 1)",
531 parser.add_option(
"-q",
"--quiet",
533 action =
"store_true",
534 help =
"suppress non error messages",
538 parser.add_option(
"--exit-char",
542 help =
"ASCII code of special character that is used to exit the application",
546 parser.add_option(
"--menu-char",
550 help =
"ASCII code of special character that is used to control miniterm (menu)",
554 (options, args) = parser.parse_args()
556 options.parity = options.parity.upper()
557 if options.parity
not in 'NEOSM':
558 parser.error(
"invalid parity")
560 if options.cr
and options.lf:
561 parser.error(
"only one of --cr or --lf can be specified")
563 if options.menu_char == options.exit_char:
564 parser.error(
'--exit-char can not be the same as --menu-char')
566 global EXITCHARCTER, MENUCHARACTER
567 EXITCHARCTER = chr(options.exit_char)
568 MENUCHARACTER = chr(options.menu_char)
571 baudrate = options.baudrate
573 if options.port
is not None:
574 parser.error(
"no arguments are allowed, options only when --port is given")
578 baudrate = int(args[0])
580 parser.error(
"baud rate must be a number, not %r" % args[0])
583 parser.error(
"too many arguments")
585 if port
is None: port = 0
587 convert_outgoing = CONVERT_CRLF
589 convert_outgoing = CONVERT_CR
591 convert_outgoing = CONVERT_LF
598 rtscts=options.rtscts,
599 xonxoff=options.xonxoff,
601 convert_outgoing=convert_outgoing,
602 repr_mode=options.repr_mode,
604 except serial.SerialException, e:
605 sys.stderr.write(
"could not open port %r: %s\n" % (port, e))
608 if not options.quiet:
609 sys.stderr.write(
'--- Miniterm on %s: %d,%s,%s,%s ---\n' % (
610 miniterm.serial.portstr,
611 miniterm.serial.baudrate,
612 miniterm.serial.bytesize,
613 miniterm.serial.parity,
614 miniterm.serial.stopbits,
616 sys.stderr.write(
'--- Quit: %s | Menu: %s | Help: %s followed by %s ---\n' % (
623 if options.dtr_state
is not None:
624 if not options.quiet:
625 sys.stderr.write(
'--- forcing DTR %s\n' % (options.dtr_state
and 'active' or 'inactive'))
626 miniterm.serial.setDTR(options.dtr_state)
627 miniterm.dtr_state = options.dtr_state
628 if options.rts_state
is not None:
629 if not options.quiet:
630 sys.stderr.write(
'--- forcing RTS %s\n' % (options.rts_state
and 'active' or 'inactive'))
631 miniterm.serial.setRTS(options.rts_state)
632 miniterm.rts_state = options.rts_state
637 except KeyboardInterrupt:
639 if not options.quiet:
640 sys.stderr.write(
"\n--- exit ---\n")
644 if __name__ ==
'__main__':