Created
February 3, 2015 14:34
-
-
Save briankip/df41e1b2fbe86b3761aa to your computer and use it in GitHub Desktop.
Baudrate detection for serial devices
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys | |
import time | |
import serial | |
from threading import Thread | |
class RawInput: | |
"""Gets a single character from standard input. Does not echo to the screen.""" | |
def __init__(self): | |
try: | |
self.impl = RawInputWindows() | |
except ImportError: | |
self.impl = RawInputUnix() | |
def __call__(self): return self.impl() | |
class RawInputUnix: | |
def __init__(self): | |
import tty, sys | |
def __call__(self): | |
import sys, tty, termios | |
fd = sys.stdin.fileno() | |
old_settings = termios.tcgetattr(fd) | |
try: | |
tty.setraw(sys.stdin.fileno()) | |
ch = sys.stdin.read(1) | |
finally: | |
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) | |
return ch | |
class RawInputWindows: | |
def __init__(self): | |
import msvcrt | |
def __call__(self): | |
import msvcrt | |
return msvcrt.getch() | |
class Baudrate: | |
VERSION = '1.0' | |
READ_TIMEOUT = 5 | |
BAUDRATES = [ | |
# "1200", | |
# "1800", | |
# "2400", | |
# "4800", | |
"9600", | |
"38400", | |
"19200", | |
"57600", | |
"115200", | |
] | |
UPKEYS = ['u', 'U', 'A'] | |
DOWNKEYS = ['d', 'D', 'B'] | |
MIN_CHAR_COUNT = 25 | |
WHITESPACE = [' ', '\t', '\r', '\n'] | |
PUNCTUATION = ['.', ',', ':', ';', '?', '!'] | |
VOWELS = ['a', 'A', 'e', 'E', 'i', 'I', 'o', 'O', 'u', 'U'] | |
def __init__(self, port=None, threshold=MIN_CHAR_COUNT, timeout=READ_TIMEOUT, name=None, auto=True, verbose=False): | |
self.port = port | |
self.threshold = threshold | |
self.timeout = timeout | |
self.name = name | |
self.auto_detect = auto | |
self.verbose = verbose | |
self.index = len(self.BAUDRATES) - 1 | |
self.valid_characters = [] | |
self.ctlc = False | |
self.thread = None | |
self._gen_char_list() | |
def _gen_char_list(self): | |
c = ' ' | |
while c <= '~': | |
self.valid_characters.append(c) | |
c = chr(ord(c) + 1) | |
for c in self.WHITESPACE: | |
if c not in self.valid_characters: | |
self.valid_characters.append(c) | |
def _print(self, data): | |
if self.verbose: | |
sys.stderr.write(data) | |
def Open(self): | |
self.serial = serial.Serial(self.port, timeout=self.timeout) | |
self.NextBaudrate(0) | |
def NextBaudrate(self, updn): | |
self.index += updn | |
if self.index >= len(self.BAUDRATES): | |
self.index = 0 | |
elif self.index < 0: | |
self.index = len(self.BAUDRATES) - 1 | |
sys.stderr.write('\n\n@@@@@@@@@@@@@@@@@@@@@ Baudrate: %s @@@@@@@@@@@@@@@@@@@@@\n\n' % self.BAUDRATES[self.index]) | |
self.serial.flush() | |
self.serial.baudrate = self.BAUDRATES[self.index] | |
self.serial.flush() | |
def Detect(self): | |
count = 0 | |
whitespace = 0 | |
punctuation = 0 | |
vowels = 0 | |
start_time = 0 | |
timed_out = False | |
clear_counters = False | |
if not self.auto_detect: | |
self.thread = Thread(None, self.HandleKeypress, None, (self, 1)) | |
self.thread.start() | |
while True: | |
if start_time == 0: | |
start_time = time.time() | |
byte = self.serial.read(1) | |
if byte: | |
if self.auto_detect and byte in self.valid_characters: | |
if byte in self.WHITESPACE: | |
whitespace += 1 | |
elif byte in self.PUNCTUATION: | |
punctuation += 1 | |
elif byte in self.VOWELS: | |
vowels += 1 | |
count += 1 | |
else: | |
clear_counters = True | |
self._print(byte) | |
if count >= self.threshold and whitespace > 0 and punctuation > 0 and vowels > 0: | |
break | |
elif (time.time() - start_time) >= self.timeout: | |
timed_out = True | |
else: | |
timed_out = True | |
if timed_out and self.auto_detect: | |
start_time = 0 | |
self.NextBaudrate(-1) | |
clear_counters = True | |
timed_out = False | |
if clear_counters: | |
whitespace = 0 | |
punctuation = 0 | |
vowels = 0 | |
count = 0 | |
clear_counters = False | |
if self.ctlc: | |
break | |
self._print("\n") | |
return self.BAUDRATES[self.index] | |
def HandleKeypress(self, *args): | |
userinput = RawInput() | |
while not self.ctlc: | |
c = userinput() | |
if c in self.UPKEYS: | |
self.NextBaudrate(1) | |
elif c in self.DOWNKEYS: | |
self.NextBaudrate(-1) | |
elif c == '\x03': | |
self.ctlc = True | |
def MinicomConfig(self, name=None): | |
success = True | |
if name is None: | |
name = self.name | |
config = "########################################################################\n" | |
config += "# Minicom configuration file - use \"minicom -s\" to change parameters.\n" | |
config += "pu port %s\n" % self.port | |
config += "pu baudrate %s\n" % self.BAUDRATES[self.index] | |
config += "pu bits 8\n" | |
config += "pu parity N\n" | |
config += "pu stopbits 1\n" | |
config += "pu rtscts No\n" | |
config += "########################################################################\n" | |
if name is not None and name: | |
try: | |
open("/etc/minicom/minirc.%s" % name, "w").write(config) | |
except Exception, e: | |
print "Error saving minicom config file:", str(e) | |
success = False | |
return (success, config) | |
def Close(self): | |
self.ctlc = True | |
self.serial.close() | |
if __name__ == '__main__': | |
import subprocess | |
from getopt import getopt as GetOpt, GetoptError | |
def usage(): | |
baud = Baudrate() | |
print "" | |
print "Baudrate v%s" % baud.VERSION | |
print "Craig Heffner, http://www.devttys0.com" | |
print "" | |
print "Usage: %s [OPTIONS]" % sys.argv[0] | |
print "" | |
print "\t-p <serial port> Specify the serial port to use [/dev/ttyUSB0]" | |
print "\t-t <seconds> Set the timeout period used when switching baudrates in auto detect mode [%d]" % baud.READ_TIMEOUT | |
print "\t-c <num> Set the minimum ASCII character threshold used during auto detect mode [%d]" % baud.MIN_CHAR_COUNT | |
print "\t-n <name> Save the resulting serial configuration as <name> and automatically invoke minicom (implies -a)" | |
print "\t-a Enable auto detect mode" | |
print "\t-b Display supported baud rates and exit" | |
print "\t-q Do not display data read from the serial port" | |
print "\t-h Display help" | |
print "" | |
sys.exit(1) | |
def main(): | |
display = False | |
verbose = True | |
auto = False | |
run = False | |
threshold = 25 | |
timeout = 5 | |
name = None | |
port = '/dev/ttyUSB0' | |
try: | |
(opts, args) = GetOpt(sys.argv[1:], 'p:t:c:n:abqh') | |
except GetoptError, e: | |
print e | |
usage() | |
for opt, arg in opts: | |
if opt == '-t': | |
timeout = int(arg) | |
elif opt == '-c': | |
threshold = int(arg) | |
elif opt == '-p': | |
port = arg | |
elif opt == '-n': | |
name = arg | |
auto = True | |
run = True | |
elif opt == '-a': | |
auto = True | |
elif opt == '-b': | |
display = True | |
elif opt == '-q': | |
verbose = False | |
else: | |
usage() | |
baud = Baudrate(port, threshold=threshold, timeout=timeout, name=name, verbose=verbose, auto=auto) | |
if display: | |
print "" | |
for rate in baud.BAUDRATES: | |
print "\t%s" % rate | |
print "" | |
else: | |
print "" | |
print "Starting baudrate detection on %s, turn on your serial device now." % port | |
print "Press Ctl+C to quit." | |
print "" | |
baud.Open() | |
try: | |
rate = baud.Detect() | |
print "\nDetected baudrate: %s" % rate | |
if name is None: | |
print "\nSave minicom configuration as: ", | |
name = sys.stdin.readline().strip() | |
print "" | |
(ok, config) = baud.MinicomConfig(name) | |
if name and name is not None: | |
if ok: | |
if not run: | |
print "Configuration saved. Run minicom now [n/Y]? ", | |
yn = sys.stdin.readline().strip() | |
print "" | |
if yn == "" or yn.lower().startswith('y'): | |
run = True | |
if run: | |
subprocess.call(["minicom", name]) | |
else: | |
print config | |
else: | |
print config | |
except KeyboardInterrupt: | |
pass | |
baud.Close() | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment