diff options
author | cliechti <cliechti@f19166aa-fa4f-0410-85c2-fa1106f25c8a> | 2009-07-08 02:10:46 +0000 |
---|---|---|
committer | cliechti <cliechti@f19166aa-fa4f-0410-85c2-fa1106f25c8a> | 2009-07-08 02:10:46 +0000 |
commit | 6c8eb2fc41bdad234c6715f7a2fe6545af411848 (patch) | |
tree | 7bb471f368f20eb2777a928bd9f73649875f9c8b | |
parent | 58b481c43a0d6d262f651f56e9e015807d5272fc (diff) | |
download | pyserial-git-6c8eb2fc41bdad234c6715f7a2fe6545af411848.tar.gz |
- remove "upload" character
- introduce "menu" character
- add "menu" for RTS, DTR, BREAK, port settings, upload etc. see CTRL+T,CTRL+H
- menu and exit character must not be the same
- clear alive flag on errors within loop
-rw-r--r-- | pyserial/examples/miniterm.py | 260 |
1 files changed, 201 insertions, 59 deletions
diff --git a/pyserial/examples/miniterm.py b/pyserial/examples/miniterm.py index b0541cd..2691ffd 100644 --- a/pyserial/examples/miniterm.py +++ b/pyserial/examples/miniterm.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # Very simple serial terminal -# (C)2002-2006 Chris Liechti <cliechti@gmx.net> +# (C)2002-2009 Chris Liechti <cliechti@gmx.net> # Input characters are sent directly (only LF -> CR/LF/CRLF translation is # done), received characters are displayed as is (or escaped trough pythons @@ -11,9 +11,54 @@ import sys, os, serial, threading EXITCHARCTER = '\x1d' #GS/ctrl+] -UPLOADCHARACTER = '\x15' # Upload: ctrl+u +MENUCHARACTER = '\x14' # Menu: ctrl+t -#first choose a platform dependant way to read single characters from the console + +def key_description(character): + """generate a readable description for a key""" + ascii_code = ord(character) + if ascii_code < 32: + return 'Ctrl+%c' % (ord('@') + ascii_code) + else: + return repr(character) + +# help text, starts with blank line! it's a function so that the current values +# for the shortcut keys is used and not the value at program start +def get_help_text(): + return """ +> pySerial - miniterm - help +> +> %(exit)-16s Exit program +> %(menu)-16s Menu escape key +> +> %(itself)-16s Send the menu character itself to remote +> %(exchar)-16s Send the exit character to remote +> %(rts)-16s Toggle RTS +> %(dtr)-16s Toggle DTR +> %(break)-16s Toggle BREAK condition +> %(info)-16s Show info +> %(upload)-16s Upload file (prompt will be shown) +> +> Port settings (%(menu)s followed by the follwoing): +> 7 8 set data bits +> e o m s n change parity (Even, Odd, Mark, Space, None) +> 1 2 3 set stop bits (1, 2, 1.5) +> b change baudrate +> x X disable/enable software flow control +> r R disable/enable hardware flow control +""" % { + 'exit': key_description(EXITCHARCTER), + 'menu': key_description(MENUCHARACTER), + 'rts': '%s, %s' % (key_description(MENUCHARACTER), key_description('\x12')), + 'dtr': '%s, %s' % (key_description(MENUCHARACTER), key_description('\x04')), + 'break': '%s, %s' % (key_description(MENUCHARACTER), key_description('\x02')), + 'info': '%s, %s' % (key_description(MENUCHARACTER), key_description('\x09')), + 'upload': '%s, %s' % (key_description(MENUCHARACTER), key_description('\x15')), + 'itself': '%s, %s' % (key_description(MENUCHARACTER), key_description(MENUCHARACTER)), + 'exchar': '%s, %s' % (key_description(MENUCHARACTER), key_description(EXITCHARCTER)), +} + +# first choose a platform dependant way to read single characters from the console global console if os.name == 'nt': @@ -85,6 +130,9 @@ class Miniterm: self.repr_mode = repr_mode self.convert_outgoing = convert_outgoing self.newline = NEWLINE_CONVERISON_MAP[self.convert_outgoing] + self.dtr_state = True + self.rts_state = True + self.break_state = False def start(self): self.alive = True @@ -105,6 +153,20 @@ class Miniterm: if not transmit_only: self.receiver_thread.join() + def dump_port_settings(self): + sys.stderr.write("\n> Settings: %s %s,%s,%s,%s\n" % ( + self.serial.portstr, + self.serial.baudrate, + self.serial.bytesize, + self.serial.parity, + self.serial.stopbits, + )) + sys.stderr.write('> RTS %s\n' % (self.rts_state and 'active' or 'inactive')) + sys.stderr.write('> DTR %s\n' % (self.dtr_state and 'active' or 'inactive')) + sys.stderr.write('> BREAK %s\n' % (self.break_state and 'active' or 'inactive')) + sys.stderr.write('> software flow control %s\n' % (self.serial.xonxoff and 'active' or 'inactive')) + sys.stderr.write('> hardware flow control %s\n' % (self.serial.rtscts and 'active' or 'inactive')) + def reader(self): """loop and copy serial->console""" while self.alive: @@ -140,55 +202,128 @@ class Miniterm: def writer(self): - """loop and copy console->serial until EXITCHARCTER character is found""" - while self.alive: - try: - c = console.getkey() - except KeyboardInterrupt: - c = '\x03' - if c == EXITCHARCTER: - self.stop() - break # exit app - elif c == UPLOADCHARACTER: # upload text file - sys.stderr.write('\nFile to upload: ') - sys.stderr.flush() - console.cleanup() - filename = sys.stdin.readline().rstrip('\r\n') - if filename != '': - try: - file = open(filename, 'r') - sys.stderr.write('Sending file %s ' % filename) - while True: - line = file.readline().rstrip('\r\n') - if not line: - break - self.serial.write(line) - self.serial.write('\r\n') - # Wait for output buffer to drain. - self.serial.flush() - sys.stderr.write('.') # Progress indicator. - sys.stderr.write('\nFile %s sent.\n' % filename) - except IOError: - print 'Error opening file %s' % filename - console.setup() - - elif c == '\n': - self.serial.write(self.newline) # send newline character(s) - if self.echo: - sys.stdout.write(c) # local echo is a real newline in any case - else: - self.serial.write(c) # send character - if self.echo: - sys.stdout.write(c) - -def key_description(character): - """generate a readable description for a key""" - ascii_code = ord(character) - if ascii_code < 32: - return 'Ctrl+%c' % (ord('@') + ascii_code) - else: - return repr(ascii_code) - + """loop and copy console->serial until EXITCHARCTER character is + found. when MENUCHARACTER is found, interpret the next key + locally. + """ + menu_active = False + try: + while self.alive: + try: + c = console.getkey() + except KeyboardInterrupt: + c = '\x03' + if menu_active: + if c == MENUCHARACTER or c == EXITCHARCTER: # Menu character again/exit char -> send itself + self.serial.write(c) # send character + if self.echo: + sys.stdout.write(c) + elif c == '\x15': # CTRL+U -> upload file + sys.stderr.write('\n> File to upload: ') + sys.stderr.flush() + console.cleanup() + filename = sys.stdin.readline().rstrip('\r\n') + if filename: + try: + file = open(filename, 'r') + sys.stderr.write('> Sending file %s ' % filename) + while True: + line = file.readline().rstrip('\r\n') + if not line: + break + self.serial.write(line) + self.serial.write('\r\n') + # Wait for output buffer to drain. + self.serial.flush() + sys.stderr.write('.') # Progress indicator. + sys.stderr.write('\n> File %s sent.\n' % filename) + except IOError, e: + sys.stderr.write('> Error opening file %s: %s\n' % (filename, e)) + console.setup() + elif c in '\x08hH?': # CTRL+H, h, H, ? -> Show help + sys.stderr.write(get_help_text()) + elif c == '\x12': # CTRL+R -> Toggle RTS + self.rts_state = not self.rts_state + self.serial.setRTS(self.rts_state) + sys.stderr.write('<RTS %s>' % (self.rts_state and 'active' or 'inactive')) + elif c == '\x04': # CTRL+D -> Toggle DTR + self.dtr_state = not self.dtr_state + self.serial.setDTR(self.dtr_state) + sys.stderr.write('<DTR %s>' % (self.dtr_state and 'active' or 'inactive')) + elif c == '\x02': # CTRL+B -> toggle BREAK condition + self.break_state = not self.break_state + self.serial.setBreak(self.break_state) + sys.stderr.write('<BREAK %s>' % (self.break_state and 'active' or 'inactive')) + elif c == '\x09': # CTRL+I -> info + self.dump_port_settings() + #~ elif c in 'pP': # P -> change port XXX reader thread would exit + elif c in 'bB': # B -> change baudrate + sys.stderr.write('\n> Baudrate: ') + sys.stderr.flush() + console.cleanup() + backup = self.serial.baudrate + try: + self.serial.baudrate = int(sys.stdin.readline().strip()) + except ValueError, e: + sys.stderr.write('\n> Error setting baudrate: %s' % (e,)) + self.serial.baudrate = backup + console.setup() + self.dump_port_settings() + elif c == '8': # 8 -> change to 8 bits + self.serial.bytesize = serial.EIGHTBITS + self.dump_port_settings() + elif c == '7': # 7 -> change to 8 bits + self.serial.bytesize = serial.SEVENBITS + self.dump_port_settings() + elif c in 'eE': # E -> change to even parity + self.serial.parity = serial.PARITY_EVEN + self.dump_port_settings() + elif c in 'oO': # O -> change to odd parity + self.serial.parity = serial.PARITY_ODD + self.dump_port_settings() + elif c in 'mM': # M -> change to mark parity + self.serial.parity = serial.PARITY_MARK + self.dump_port_settings() + elif c in 'sS': # S -> change to space parity + self.serial.parity = serial.PARITY_SPACE + self.dump_port_settings() + elif c in 'nN': # N -> change to no parity + self.serial.parity = serial.PARITY_NONE + self.dump_port_settings() + elif c == '1': # 1 -> change to 1 stop bits + self.serial.stopbits = serial.STOPBITS_ONE + self.dump_port_settings() + elif c == '2': # 2 -> change to 2 stop bits + self.serial.stopbits = serial.STOPBITS_TWO + self.dump_port_settings() + elif c == '3': # 3 -> change to 1.5 stop bits + self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE + self.dump_port_settings() + elif c in 'xX': # X -> change software flow control + self.serial.xonxoff = (c == 'X') + self.dump_port_settings() + elif c in 'rR': # R -> change hardware flow control + self.serial.rtscts = (c == 'R') + self.dump_port_settings() + else: + sys.stderr.write('<unknown menu character %s>' % key_description(c)) + menu_active = False + elif c == MENUCHARACTER: # next char will be for menu + menu_active = True + elif c == EXITCHARCTER: + self.stop() + break # exit app + elif c == '\n': + self.serial.write(self.newline) # send newline character(s) + if self.echo: + sys.stdout.write(c) # local echo is a real newline in any case + else: + self.serial.write(c) # send character + if self.echo: + sys.stdout.write(c) + except: + self.alive = False + raise def main(): import optparse @@ -297,12 +432,12 @@ def main(): default = 0x1d ) - parser.add_option("--upload-char", - dest = "upload_char", + parser.add_option("--menu-char", + dest = "menu_char", action = "store", type = 'int', help = "ASCII code of special character that is used to send a file", - default = 0x15 + default = 0x14 ) (options, args) = parser.parse_args() @@ -310,9 +445,12 @@ def main(): if options.cr and options.lf: parser.error("only one of --cr or --lf can be specified") - global EXITCHARCTER, UPLOADCHARACTER + if options.dtr_state is not None and options.rts_state is not None and options.dtr_state == options.rts_state: + parser.error('--exit-char can not be the same as --menu-char') + + global EXITCHARCTER, MENUCHARACTER EXITCHARCTER = chr(options.exit_char) - UPLOADCHARACTER = chr(options.upload_char) + MENUCHARACTER = chr(options.menu_char) port = options.port baudrate = options.baudrate @@ -360,18 +498,22 @@ def main(): miniterm.serial.parity, miniterm.serial.stopbits, )) - sys.stderr.write('--- Quit: %s | Upload: %s ---\n' % ( + sys.stderr.write('--- Quit: %s | Menu: %s | Help: %s followed by %s ---\n' % ( key_description(EXITCHARCTER), - key_description(UPLOADCHARACTER) + key_description(MENUCHARACTER), + key_description(MENUCHARACTER), + key_description('\x08'), )) if options.dtr_state is not None: if not options.quiet: sys.stderr.write('--- forcing DTR %s\n' % (options.dtr_state and 'active' or 'inactive')) miniterm.serial.setDTR(options.dtr_state) + miniterm.dtr_state = options.dtr_state if options.rts_state is not None: if not options.quiet: sys.stderr.write('--- forcing RTS %s\n' % (options.rts_state and 'active' or 'inactive')) miniterm.serial.setRTS(options.rts_state) + miniterm.rts_state = options.rts_state miniterm.start() miniterm.join(True) |