Skip to content

Instantly share code, notes, and snippets.

@briankip
Created February 3, 2015 14:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briankip/df41e1b2fbe86b3761aa to your computer and use it in GitHub Desktop.
Save briankip/df41e1b2fbe86b3761aa to your computer and use it in GitHub Desktop.
Baudrate detection for serial devices
#!/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