#!/usr/bin/env python # Very simple serial terminal # (C)2002-2004 Chris Liechti # Input characters are sent directly (only LF -> CR/LF/CRLF translation is # done), received characters are displayed as is (or as trough pythons # repr, useful for debug purposes) # Baudrate and echo configuartion is done through globals import sys, os, serial, threading, getopt EXITCHARCTER = '\x04' #ctrl+D #first choose a platform dependant way to read single characters from the console if os.name == 'nt': import msvcrt def getkey(): while 1: if echo: z = msvcrt.getche() else: z = msvcrt.getch() if z == '\0' or z == '\xe0': #functions keys msvcrt.getch() else: if z == '\r': return '\n' return z elif os.name == 'posix': import termios, sys, os fd = sys.stdin.fileno() old = termios.tcgetattr(fd) new = termios.tcgetattr(fd) new[3] = new[3] & ~termios.ICANON & ~termios.ECHO new[6][termios.VMIN] = 1 new[6][termios.VTIME] = 0 termios.tcsetattr(fd, termios.TCSANOW, new) s = '' # We'll save the characters typed and add them to the pool. def getkey(): c = os.read(fd, 1) #~ c = sys.stdin.read(1) if echo: sys.stdout.write(c); sys.stdout.flush() return c def clenaup_console(): termios.tcsetattr(fd, termios.TCSAFLUSH, old) sys.exitfunc = clenaup_console #terminal modes have to be restored on exit... else: raise "Sorry no implementation for your platform (%s) available." % sys.platform CONVERT_CRLF = 2 CONVERT_CR = 1 CONVERT_LF = 0 def reader(): """loop forever and copy serial->console""" while 1: data = s.read() if repr_mode: sys.stdout.write(repr(data)[1:-1]) else: sys.stdout.write(data) sys.stdout.flush() def writer(): """loop and copy console->serial until EOF character is found""" while 1: c = getkey() if c == EXITCHARCTER: break #exit app elif c == '\n': if convert_outgoing == CONVERT_CRLF: s.write('\r\n') #make it a CR+LF elif convert_outgoing == CONVERT_CR: s.write('\r') #make it a CR elif convert_outgoing == CONVERT_LF: s.write('\n') #make it a LF else: s.write(c) #send character #print a short help message def usage(): sys.stderr.write("""USAGE: %s [options] Miniterm - A simple terminal program for the serial port. options: -p, --port=PORT: port, a number, default = 0 or a device name -b, --baud=BAUD: baudrate, default 9600 -r, --rtscts: enable RTS/CTS flow control (default off) -x, --xonxoff: enable software flow control (default off) -e, --echo: enable local echo (default off) -c, --cr: do not send CR+LF, send CR only -n, --newline: do not send CR+LF, send LF only -D, --debug: debug received data (escape nonprintable chars) """ % (sys.argv[0], )) if __name__ == '__main__': #initialize with defaults port = 0 baudrate = 9600 echo = 0 convert_outgoing = CONVERT_CRLF rtscts = 0 xonxoff = 0 repr_mode = 0 #parse command line options try: opts, args = getopt.getopt(sys.argv[1:], "hp:b:rxecnD", ["help", "port=", "baud=", "rtscts", "xonxoff", "echo", "cr", "newline", "debug"] ) except getopt.GetoptError: # print help information and exit: usage() sys.exit(2) for o, a in opts: if o in ("-h", "--help"): #help text usage() sys.exit() elif o in ("-p", "--port"): #specified port try: port = int(a) except ValueError: port = a elif o in ("-b", "--baud"): #specified baudrate try: baudrate = int(a) except ValueError: raise ValueError, "Baudrate must be a integer number, not %r" % a elif o in ("-r", "--rtscts"): rtscts = 1 elif o in ("-x", "--xonxoff"): xonxoff = 1 elif o in ("-e", "--echo"): echo = 1 elif o in ("-c", "--cr"): convert_outgoing = CONVERT_CR elif o in ("-n", "--newline"): convert_outgoing = CONVERT_LF elif o in ("-D", "--debug"): repr_mode = 1 #open the port try: s = serial.Serial(port, baudrate, rtscts=rtscts, xonxoff=xonxoff) except: sys.stderr.write("Could not open port\n") sys.exit(1) sys.stderr.write("--- Miniterm --- type Ctrl-D to quit\n") #start serial->console thread r = threading.Thread(target=reader) r.setDaemon(1) r.start() #and enter console->serial loop writer() sys.stderr.write("\n--- exit ---\n")