15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#!/usr/bin/env python
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Very simple serial terminal
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# (C)2002-2011 Chris Liechti <cliechti@gmx.net>
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Input characters are sent directly (only LF -> CR/LF/CRLF translation is
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# done), received characters are displayed as is (or escaped trough pythons
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# repr, useful for debug purposes)
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import sys, os, serial, threading
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)try:
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    from serial.tools.list_ports import comports
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)except ImportError:
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    comports = None
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)EXITCHARCTER = serial.to_bytes([0x1d])   # GS/CTRL+]
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MENUCHARACTER = serial.to_bytes([0x14])  # Menu: CTRL+T
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DEFAULT_PORT = None
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DEFAULT_BAUDRATE = 9600
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DEFAULT_RTS = None
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DEFAULT_DTR = None
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def key_description(character):
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """generate a readable description for a key"""
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ascii_code = ord(character)
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if ascii_code < 32:
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return 'Ctrl+%c' % (ord('@') + ascii_code)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else:
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return repr(character)
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# help text, starts with blank line! it's a function so that the current values
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# for the shortcut keys is used and not the value at program start
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def get_help_text():
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return """
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)--- pySerial (%(version)s) - miniterm - help
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)--- %(exit)-8s Exit program
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)--- %(menu)-8s Menu escape key, followed by:
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)--- Menu keys:
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    %(itself)-7s Send the menu character itself to remote
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    %(exchar)-7s Send the exit character itself to remote
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    %(info)-7s Show info
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    %(upload)-7s Upload file (prompt will be shown)
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)--- Toggles:
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    %(rts)-7s RTS          %(echo)-7s local echo
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    %(dtr)-7s DTR          %(break)-7s BREAK
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    %(lfm)-7s line feed    %(repr)-7s Cycle repr mode
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)--- Port settings (%(menu)s followed by the following):
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    p          change port
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    7 8        set data bits
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    n e o s m  change parity (None, Even, Odd, Space, Mark)
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    1 2 3      set stop bits (1, 2, 1.5)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    b          change baud rate
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    x X        disable/enable software flow control
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)---    r R        disable/enable hardware flow control
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)""" % {
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'version': getattr(serial, 'VERSION', 'unknown version'),
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'exit': key_description(EXITCHARCTER),
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'menu': key_description(MENUCHARACTER),
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'rts': key_description('\x12'),
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'repr': key_description('\x01'),
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'dtr': key_description('\x04'),
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'lfm': key_description('\x0c'),
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'break': key_description('\x02'),
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'echo': key_description('\x05'),
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'info': key_description('\x09'),
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'upload': key_description('\x15'),
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'itself': key_description(MENUCHARACTER),
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    'exchar': key_description(EXITCHARCTER),
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)if sys.version_info >= (3, 0):
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def character(b):
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return b.decode('latin1')
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)else:
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def character(b):
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return b
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)LF = serial.to_bytes([10])
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)CR = serial.to_bytes([13])
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)CRLF = serial.to_bytes([13, 10])
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)X00 = serial.to_bytes([0])
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)X0E = serial.to_bytes([0x0e])
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# first choose a platform dependant way to read single characters from the console
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)global console
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)if os.name == 'nt':
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    import msvcrt
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    class Console(object):
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        def __init__(self):
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            pass
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        def setup(self):
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            pass    # Do nothing for 'nt'
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        def cleanup(self):
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            pass    # Do nothing for 'nt'
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        def getkey(self):
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            while True:
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                z = msvcrt.getch()
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if z == X00 or z == X0E:    # functions keys, ignore
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    msvcrt.getch()
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                else:
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if z == CR:
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        return LF
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    return z
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    console = Console()
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)elif os.name == 'posix':
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    import termios, sys, os
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    class Console(object):
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        def __init__(self):
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.fd = sys.stdin.fileno()
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.old = None
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        def setup(self):
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.old = termios.tcgetattr(self.fd)
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            new = termios.tcgetattr(self.fd)
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            new[6][termios.VMIN] = 1
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            new[6][termios.VTIME] = 0
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            termios.tcsetattr(self.fd, termios.TCSANOW, new)
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        def getkey(self):
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            c = os.read(self.fd, 1)
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return c
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        def cleanup(self):
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if self.old is not None:
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old)
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    console = Console()
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def cleanup_console():
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        console.cleanup()
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sys.exitfunc = cleanup_console      # terminal modes have to be restored on exit...
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)else:
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError("Sorry no implementation for your platform (%s) available." % sys.platform)
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def dump_port_list():
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if comports:
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write('\n--- Available ports:\n')
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for port, desc, hwid in sorted(comports()):
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            #~ sys.stderr.write('--- %-20s %s [%s]\n' % (port, desc, hwid))
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            sys.stderr.write('--- %-20s %s\n' % (port, desc))
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)CONVERT_CRLF = 2
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)CONVERT_CR   = 1
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)CONVERT_LF   = 0
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)NEWLINE_CONVERISON_MAP = (LF, CR, CRLF)
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)LF_MODES = ('LF', 'CR', 'CR/LF')
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)REPR_MODES = ('raw', 'some control', 'all control', 'hex')
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class Miniterm(object):
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def __init__(self, port, baudrate, parity, rtscts, xonxoff, echo=False, convert_outgoing=CONVERT_CRLF, repr_mode=0):
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        try:
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.serial = serial.serial_for_url(port, baudrate, parity=parity, rtscts=rtscts, xonxoff=xonxoff, timeout=1)
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        except AttributeError:
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # happens when the installed pyserial is older than 2.5. use the
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # Serial class directly then.
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.serial = serial.Serial(port, baudrate, parity=parity, rtscts=rtscts, xonxoff=xonxoff, timeout=1)
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.echo = echo
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.repr_mode = repr_mode
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.convert_outgoing = convert_outgoing
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.newline = NEWLINE_CONVERISON_MAP[self.convert_outgoing]
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.dtr_state = True
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.rts_state = True
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.break_state = False
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def _start_reader(self):
1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Start reader thread"""
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self._reader_alive = True
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # start serial->console thread
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.receiver_thread = threading.Thread(target=self.reader)
1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.receiver_thread.setDaemon(True)
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.receiver_thread.start()
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def _stop_reader(self):
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """Stop reader thread only, wait for clean exit of thread"""
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self._reader_alive = False
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.receiver_thread.join()
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def start(self):
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.alive = True
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self._start_reader()
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # enter console->serial loop
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.transmitter_thread = threading.Thread(target=self.writer)
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.transmitter_thread.setDaemon(True)
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.transmitter_thread.start()
2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def stop(self):
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.alive = False
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def join(self, transmit_only=False):
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self.transmitter_thread.join()
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not transmit_only:
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.receiver_thread.join()
2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def dump_port_settings(self):
2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write("\n--- Settings: %s  %s,%s,%s,%s\n" % (
2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                self.serial.portstr,
2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                self.serial.baudrate,
2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                self.serial.bytesize,
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                self.serial.parity,
2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                self.serial.stopbits))
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write('--- RTS: %-8s  DTR: %-8s  BREAK: %-8s\n' % (
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                (self.rts_state and 'active' or 'inactive'),
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                (self.dtr_state and 'active' or 'inactive'),
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                (self.break_state and 'active' or 'inactive')))
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        try:
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            sys.stderr.write('--- CTS: %-8s  DSR: %-8s  RI: %-8s  CD: %-8s\n' % (
2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    (self.serial.getCTS() and 'active' or 'inactive'),
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    (self.serial.getDSR() and 'active' or 'inactive'),
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    (self.serial.getRI() and 'active' or 'inactive'),
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    (self.serial.getCD() and 'active' or 'inactive')))
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        except serial.SerialException:
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # on RFC 2217 ports it can happen to no modem state notification was
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # yet received. ignore this error.
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            pass
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write('--- software flow control: %s\n' % (self.serial.xonxoff and 'active' or 'inactive'))
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write('--- hardware flow control: %s\n' % (self.serial.rtscts and 'active' or 'inactive'))
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write('--- data escaping: %s  linefeed: %s\n' % (
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                REPR_MODES[self.repr_mode],
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                LF_MODES[self.convert_outgoing]))
2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def reader(self):
2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """loop and copy serial->console"""
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        try:
2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            while self.alive and self._reader_alive:
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                data = character(self.serial.read(1))
2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if self.repr_mode == 0:
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    # direct output, just have to care about newline setting
2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if data == '\r' and self.convert_outgoing == CONVERT_CR:
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.write('\n')
2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    else:
2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.write(data)
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                elif self.repr_mode == 1:
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    # escape non-printable, let pass newlines
2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if self.convert_outgoing == CONVERT_CRLF and data in '\r\n':
2565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        if data == '\n':
2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            sys.stdout.write('\n')
2585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        elif data == '\r':
2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            pass
2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif data == '\n' and self.convert_outgoing == CONVERT_LF:
2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.write('\n')
2625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif data == '\r' and self.convert_outgoing == CONVERT_CR:
2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.write('\n')
2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    else:
2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.write(repr(data)[1:-1])
2665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                elif self.repr_mode == 2:
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    # escape all non-printable, including newline
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    sys.stdout.write(repr(data)[1:-1])
2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                elif self.repr_mode == 3:
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    # escape everything (hexdump)
2715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    for c in data:
2725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.write("%s " % c.encode('hex'))
2735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                sys.stdout.flush()
2745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        except serial.SerialException, e:
2755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.alive = False
2765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # would be nice if the console reader could be interruptted at this
2775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            # point...
2785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            raise
2795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def writer(self):
2825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """\
2835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        Loop and copy console->serial until EXITCHARCTER character is
2845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        found. When MENUCHARACTER is found, interpret the next key
2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        locally.
2865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """
2875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        menu_active = False
2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        try:
2895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            while self.alive:
2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                try:
2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    b = console.getkey()
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                except KeyboardInterrupt:
2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    b = serial.to_bytes([3])
2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                c = character(b)
2955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if menu_active:
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if c == MENUCHARACTER or c == EXITCHARCTER: # Menu character again/exit char -> send itself
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.write(b)                    # send character
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        if self.echo:
2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            sys.stdout.write(c)
3005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '\x15':                       # CTRL+U -> upload file
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('\n--- File to upload: ')
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.flush()
3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        console.cleanup()
3045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        filename = sys.stdin.readline().rstrip('\r\n')
3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        if filename:
3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            try:
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                file = open(filename, 'r')
3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                sys.stderr.write('--- Sending file %s ---\n' % filename)
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                while True:
3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    line = file.readline().rstrip('\r\n')
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    if not line:
3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                        break
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    self.serial.write(line)
3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    self.serial.write('\r\n')
3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    # Wait for output buffer to drain.
3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    self.serial.flush()
3175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    sys.stderr.write('.')   # Progress indicator.
3185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                sys.stderr.write('\n--- File %s sent ---\n' % filename)
3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            except IOError, e:
3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                sys.stderr.write('--- ERROR opening file %s: %s ---\n' % (filename, e))
3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        console.setup()
3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in '\x08hH?':                    # CTRL+H, h, H, ? -> Show help
3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write(get_help_text())
3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '\x12':                       # CTRL+R -> Toggle RTS
3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.rts_state = not self.rts_state
3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.setRTS(self.rts_state)
3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('--- RTS %s ---\n' % (self.rts_state and 'active' or 'inactive'))
3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '\x04':                       # CTRL+D -> Toggle DTR
3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dtr_state = not self.dtr_state
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.setDTR(self.dtr_state)
3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('--- DTR %s ---\n' % (self.dtr_state and 'active' or 'inactive'))
3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '\x02':                       # CTRL+B -> toggle BREAK condition
3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.break_state = not self.break_state
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.setBreak(self.break_state)
3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('--- BREAK %s ---\n' % (self.break_state and 'active' or 'inactive'))
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '\x05':                       # CTRL+E -> toggle local echo
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.echo = not self.echo
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('--- local echo %s ---\n' % (self.echo and 'active' or 'inactive'))
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '\x09':                       # CTRL+I -> info
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '\x01':                       # CTRL+A -> cycle escape mode
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.repr_mode += 1
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        if self.repr_mode > 3:
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            self.repr_mode = 0
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('--- escape data: %s ---\n' % (
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            REPR_MODES[self.repr_mode],
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        ))
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '\x0c':                       # CTRL+L -> cycle linefeed mode
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.convert_outgoing += 1
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        if self.convert_outgoing > 2:
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            self.convert_outgoing = 0
3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.newline = NEWLINE_CONVERISON_MAP[self.convert_outgoing]
3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('--- line feed %s ---\n' % (
3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            LF_MODES[self.convert_outgoing],
3555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        ))
3565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'pP':                         # P -> change port
3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        dump_port_list()
3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('--- Enter port name: ')
3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.flush()
3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        console.cleanup()
3615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        try:
3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            port = sys.stdin.readline().strip()
3635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        except KeyboardInterrupt:
3645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            port = None
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        console.setup()
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        if port and port != self.serial.port:
3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            # reader thread needs to be shut down
3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            self._stop_reader()
3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            # save settings
3705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            settings = self.serial.getSettingsDict()
3715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            try:
3725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                try:
3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    new_serial = serial.serial_for_url(port, do_not_open=True)
3745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                except AttributeError:
3755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    # happens when the installed pyserial is older than 2.5. use the
3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    # Serial class directly then.
3775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    new_serial = serial.Serial()
3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    new_serial.port = port
3795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                # restore settings and open
3805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                new_serial.applySettingsDict(settings)
3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                new_serial.open()
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                new_serial.setRTS(self.rts_state)
3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                new_serial.setDTR(self.dtr_state)
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                new_serial.setBreak(self.break_state)
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            except Exception, e:
3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                sys.stderr.write('--- ERROR opening new port: %s ---\n' % (e,))
3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                new_serial.close()
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            else:
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                self.serial.close()
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                self.serial = new_serial
3915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                sys.stderr.write('--- Port changed to: %s ---\n' % (self.serial.port,))
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            # and restart the reader thread
3935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            self._start_reader()
3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'bB':                         # B -> change baudrate
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('\n--- Baudrate: ')
3965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.flush()
3975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        console.cleanup()
3985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        backup = self.serial.baudrate
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        try:
4005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            self.serial.baudrate = int(sys.stdin.readline().strip())
4015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        except ValueError, e:
4025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            sys.stderr.write('--- ERROR setting baudrate: %s ---\n' % (e,))
4035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            self.serial.baudrate = backup
4045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        else:
4055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            self.dump_port_settings()
4065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        console.setup()
4075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '8':                          # 8 -> change to 8 bits
4085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.bytesize = serial.EIGHTBITS
4095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '7':                          # 7 -> change to 8 bits
4115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.bytesize = serial.SEVENBITS
4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'eE':                         # E -> change to even parity
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.parity = serial.PARITY_EVEN
4155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'oO':                         # O -> change to odd parity
4175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.parity = serial.PARITY_ODD
4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'mM':                         # M -> change to mark parity
4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.parity = serial.PARITY_MARK
4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'sS':                         # S -> change to space parity
4235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.parity = serial.PARITY_SPACE
4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'nN':                         # N -> change to no parity
4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.parity = serial.PARITY_NONE
4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '1':                          # 1 -> change to 1 stop bits
4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.stopbits = serial.STOPBITS_ONE
4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '2':                          # 2 -> change to 2 stop bits
4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.stopbits = serial.STOPBITS_TWO
4335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c == '3':                          # 3 -> change to 1.5 stop bits
4355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE
4365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'xX':                         # X -> change software flow control
4385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.xonxoff = (c == 'X')
4395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    elif c in 'rR':                         # R -> change hardware flow control
4415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.serial.rtscts = (c == 'R')
4425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        self.dump_port_settings()
4435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    else:
4445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stderr.write('--- unknown menu character %s --\n' % key_description(c))
4455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    menu_active = False
4465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                elif c == MENUCHARACTER: # next char will be for menu
4475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    menu_active = True
4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                elif c == EXITCHARCTER:
4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    self.stop()
4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    break                                   # exit app
4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                elif c == '\n':
4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    self.serial.write(self.newline)         # send newline character(s)
4535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if self.echo:
4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.write(c)                 # local echo is a real newline in any case
4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.flush()
4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                else:
4575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    self.serial.write(b)                    # send byte
4585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    if self.echo:
4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.write(c)
4605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sys.stdout.flush()
4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        except:
4625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self.alive = False
4635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            raise
4645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def main():
4665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    import optparse
4675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    parser = optparse.OptionParser(
4695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        usage = "%prog [options] [port [baudrate]]",
4705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        description = "Miniterm - A simple terminal program for the serial port."
4715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
4725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group = optparse.OptionGroup(parser, "Port settings")
4745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("-p", "--port",
4765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "port",
4775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "port, a number or a device name. (deprecated option, use parameter instead)",
4785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = DEFAULT_PORT
4795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
4805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("-b", "--baud",
4825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "baudrate",
4835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store",
4845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        type = 'int',
4855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "set baud rate, default %default",
4865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = DEFAULT_BAUDRATE
4875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
4885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--parity",
4905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "parity",
4915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store",
4925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "set parity, one of [N, E, O, S, M], default=N",
4935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = 'N'
4945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
4955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--rtscts",
4975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "rtscts",
4985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store_true",
4995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "enable RTS/CTS flow control (default off)",
5005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = False
5015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--xonxoff",
5045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "xonxoff",
5055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store_true",
5065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "enable software flow control (default off)",
5075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = False
5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--rts",
5115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "rts_state",
5125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store",
5135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        type = 'int',
5145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "set initial RTS line state (possible values: 0, 1)",
5155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = DEFAULT_RTS
5165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--dtr",
5195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "dtr_state",
5205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store",
5215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        type = 'int',
5225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "set initial DTR line state (possible values: 0, 1)",
5235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = DEFAULT_DTR
5245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    parser.add_option_group(group)
5275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group = optparse.OptionGroup(parser, "Data handling")
5295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("-e", "--echo",
5315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "echo",
5325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store_true",
5335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "enable local echo (default off)",
5345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = False
5355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--cr",
5385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "cr",
5395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store_true",
5405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "do not send CR+LF, send CR only",
5415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = False
5425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--lf",
5455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "lf",
5465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store_true",
5475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "do not send CR+LF, send LF only",
5485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = False
5495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("-D", "--debug",
5525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "repr_mode",
5535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "count",
5545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = """debug received data (escape non-printable chars)
5555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)--debug can be given multiple times:
5565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)0: just print what is received
5575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)1: escape non-printable characters, do newlines as unusual
5585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)2: escape non-printable characters, newlines too
5595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)3: hex dump everything""",
5605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = 0
5615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    parser.add_option_group(group)
5645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group = optparse.OptionGroup(parser, "Hotkeys")
5675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--exit-char",
5695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "exit_char",
5705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store",
5715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        type = 'int',
5725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "ASCII code of special character that is used to exit the application",
5735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = 0x1d
5745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("--menu-char",
5775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "menu_char",
5785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store",
5795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        type = 'int',
5805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "ASCII code of special character that is used to control miniterm (menu)",
5815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = 0x14
5825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    parser.add_option_group(group)
5855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group = optparse.OptionGroup(parser, "Diagnostics")
5875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    group.add_option("-q", "--quiet",
5895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        dest = "quiet",
5905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        action = "store_true",
5915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        help = "suppress non-error messages",
5925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        default = False
5935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    )
5945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    parser.add_option_group(group)
5965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    (options, args) = parser.parse_args()
5995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    options.parity = options.parity.upper()
6015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if options.parity not in 'NEOSM':
6025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        parser.error("invalid parity")
6035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if options.cr and options.lf:
6055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        parser.error("only one of --cr or --lf can be specified")
6065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if options.menu_char == options.exit_char:
6085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        parser.error('--exit-char can not be the same as --menu-char')
6095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    global EXITCHARCTER, MENUCHARACTER
6115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    EXITCHARCTER = chr(options.exit_char)
6125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    MENUCHARACTER = chr(options.menu_char)
6135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    port = options.port
6155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    baudrate = options.baudrate
6165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if args:
6175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if options.port is not None:
6185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            parser.error("no arguments are allowed, options only when --port is given")
6195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        port = args.pop(0)
6205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if args:
6215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            try:
6225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                baudrate = int(args[0])
6235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            except ValueError:
6245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                parser.error("baud rate must be a number, not %r" % args[0])
6255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            args.pop(0)
6265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if args:
6275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            parser.error("too many arguments")
6285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else:
6295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # noport given on command line -> ask user now
6305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if port is None:
6315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            dump_port_list()
6325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port = raw_input('Enter port name:')
6335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    convert_outgoing = CONVERT_CRLF
6355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if options.cr:
6365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        convert_outgoing = CONVERT_CR
6375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    elif options.lf:
6385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        convert_outgoing = CONVERT_LF
6395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    try:
6415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        miniterm = Miniterm(
6425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            port,
6435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            baudrate,
6445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            options.parity,
6455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            rtscts=options.rtscts,
6465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            xonxoff=options.xonxoff,
6475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            echo=options.echo,
6485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            convert_outgoing=convert_outgoing,
6495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            repr_mode=options.repr_mode,
6505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        )
6515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    except serial.SerialException, e:
6525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write("could not open port %r: %s\n" % (port, e))
6535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.exit(1)
6545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not options.quiet:
6565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write('--- Miniterm on %s: %d,%s,%s,%s ---\n' % (
6575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            miniterm.serial.portstr,
6585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            miniterm.serial.baudrate,
6595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            miniterm.serial.bytesize,
6605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            miniterm.serial.parity,
6615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            miniterm.serial.stopbits,
6625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ))
6635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write('--- Quit: %s  |  Menu: %s | Help: %s followed by %s ---\n' % (
6645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            key_description(EXITCHARCTER),
6655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            key_description(MENUCHARACTER),
6665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            key_description(MENUCHARACTER),
6675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            key_description('\x08'),
6685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ))
6695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if options.dtr_state is not None:
6715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not options.quiet:
6725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            sys.stderr.write('--- forcing DTR %s\n' % (options.dtr_state and 'active' or 'inactive'))
6735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        miniterm.serial.setDTR(options.dtr_state)
6745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        miniterm.dtr_state = options.dtr_state
6755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if options.rts_state is not None:
6765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if not options.quiet:
6775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            sys.stderr.write('--- forcing RTS %s\n' % (options.rts_state and 'active' or 'inactive'))
6785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        miniterm.serial.setRTS(options.rts_state)
6795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        miniterm.rts_state = options.rts_state
6805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    console.setup()
6825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    miniterm.start()
6835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    try:
6845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        miniterm.join(True)
6855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    except KeyboardInterrupt:
6865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        pass
6875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not options.quiet:
6885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sys.stderr.write("\n--- exit ---\n")
6895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    miniterm.join()
6905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    #~ console.cleanup()
6915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)if __name__ == '__main__':
6945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    main()
695