idf_monitor: Small fixes (baud rate, EOL, /dev/tty.X on macOS, Ctrl-T on failure)

* "make monitor" not passed the configured baud rate
  Closes #436 https://github.com/espressif/esp-idf/issues/436
* Pass toolchain prefix from sdkconfig into monitor tool
* Allow setting EOL in idf_monitor.py, use CRLF by default
* Detect if /dev/tty.X is used on macOS, warn and replace with /dev/cu.X
* If a build fails or gdb exits, ignore Ctrl-T (allowing Ctrl-T Ctrl-A/F to be same key sequence everywhere)
* Add a note about winpty on Windows
  Ref 02fdf8271d (commitcomment-21369196)
This commit is contained in:
Angus Gratton 2017-03-17 18:41:18 +08:00
parent 57486a1f61
commit e477ce93e9
3 changed files with 39 additions and 7 deletions

View file

@ -83,12 +83,14 @@ endif
simple_monitor: $(call prereq_if_explicit,%flash) simple_monitor: $(call prereq_if_explicit,%flash)
$(MONITOR_PYTHON) -m serial.tools.miniterm --rts 0 --dtr 0 --raw $(ESPPORT) $(MONITORBAUD) $(MONITOR_PYTHON) -m serial.tools.miniterm --rts 0 --dtr 0 --raw $(ESPPORT) $(MONITORBAUD)
MONITOR_OPTS := --baud $(MONITORBAUD) --port $(ESPPORT) --toolchain-prefix $(CONFIG_TOOLPREFIX) --make "$(MAKE)"
monitor: $(call prereq_if_explicit,%flash) monitor: $(call prereq_if_explicit,%flash)
$(summary) MONITOR $(summary) MONITOR
[ -f $(APP_ELF) ] || echo "*** 'make monitor' target requires an app to be compiled and flashed first." [ -f $(APP_ELF) ] || echo "*** 'make monitor' target requires an app to be compiled and flashed first."
[ -f $(APP_ELF) ] || echo "*** Run 'make flash monitor' to build, flash and monitor" [ -f $(APP_ELF) ] || echo "*** Run 'make flash monitor' to build, flash and monitor"
[ -f $(APP_ELF) ] || echo "*** Or alternatively 'make simple_monitor' to view the serial port as-is." [ -f $(APP_ELF) ] || echo "*** Or alternatively 'make simple_monitor' to view the serial port as-is."
[ -f $(APP_ELF) ] || exit 1 [ -f $(APP_ELF) ] || exit 1
$(MONITOR_PYTHON) $(IDF_PATH)/tools/idf_monitor.py --port $(ESPPORT) --make "$(MAKE)" $(APP_ELF) $(MONITOR_PYTHON) $(IDF_PATH)/tools/idf_monitor.py $(MONITOR_OPTS) $(APP_ELF)
.PHONY: erase_flash .PHONY: erase_flash

View file

@ -102,6 +102,7 @@ Known Issues with idf_monitor
Issues Observed on Windows Issues Observed on Windows
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
- If you are using the supported Windows environment and receive the error "winpty: command not found" then run ``pacman -S winpty`` to fix.
- Arrow keys and some other special keys in gdb don't work, due to Windows Console limitations. - Arrow keys and some other special keys in gdb don't work, due to Windows Console limitations.
- Occasionally when "make" exits, it may stall for up to 30 seconds before idf_monitor resumes. - Occasionally when "make" exits, it may stall for up to 30 seconds before idf_monitor resumes.
- Occasionally when "gdb" is run, it may stall for a short time before it begins communicating with the gdbstub. - Occasionally when "gdb" is run, it may stall for a short time before it begins communicating with the gdbstub.

View file

@ -70,6 +70,8 @@ TAG_SERIAL = 1
# regex matches an potential PC value (0x4xxxxxxx) # regex matches an potential PC value (0x4xxxxxxx)
MATCH_PCADDR = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE) MATCH_PCADDR = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE)
DEFAULT_TOOLCHAIN_PREFIX = "xtensa-esp32-elf-"
class StoppableThread(object): class StoppableThread(object):
""" """
Provide a Thread-like class which can be 'cancelled' via a subclass-provided Provide a Thread-like class which can be 'cancelled' via a subclass-provided
@ -193,7 +195,7 @@ class Monitor(object):
Main difference is that all event processing happens in the main thread, not the worker threads. Main difference is that all event processing happens in the main thread, not the worker threads.
""" """
def __init__(self, serial_instance, elf_file, make="make"): def __init__(self, serial_instance, elf_file, make="make", toolchain_prefix=DEFAULT_TOOLCHAIN_PREFIX, eol="CRLF"):
super(Monitor, self).__init__() super(Monitor, self).__init__()
self.event_queue = queue.Queue() self.event_queue = queue.Queue()
self.console = miniterm.Console() self.console = miniterm.Console()
@ -207,9 +209,16 @@ class Monitor(object):
self.serial_reader = SerialReader(self.serial, self.event_queue) self.serial_reader = SerialReader(self.serial, self.event_queue)
self.elf_file = elf_file self.elf_file = elf_file
self.make = make self.make = make
self.toolchain_prefix = DEFAULT_TOOLCHAIN_PREFIX
self.menu_key = CTRL_T self.menu_key = CTRL_T
self.exit_key = CTRL_RBRACKET self.exit_key = CTRL_RBRACKET
self.translate_eol = {
"CRLF": lambda c: c.replace(b"\n", b"\r\n"),
"CR": lambda c: c.replace(b"\n", b"\r"),
"LF": lambda c: c.replace(b"\r", b"\n"),
}[eol]
# internal state # internal state
self._pressed_menu_key = False self._pressed_menu_key = False
self._read_line = b"" self._read_line = b""
@ -246,6 +255,7 @@ class Monitor(object):
self.serial_reader.stop() self.serial_reader.stop()
else: else:
try: try:
key = self.translate_eol(key)
self.serial.write(codecs.encode(key)) self.serial.write(codecs.encode(key))
except serial.SerialException: except serial.SerialException:
pass # this shouldn't happen, but sometimes port has closed in serial thread pass # this shouldn't happen, but sometimes port has closed in serial thread
@ -327,6 +337,8 @@ class Monitor(object):
.format(key_description(CTRL_A))) .format(key_description(CTRL_A)))
sys.stderr.write("--- Press any other key to resume monitor (resets target).\n") sys.stderr.write("--- Press any other key to resume monitor (resets target).\n")
sys.stderr.write(ANSI_NORMAL) sys.stderr.write(ANSI_NORMAL)
k = CTRL_T # ignore CTRL-T here, so people can muscle-memory Ctrl-T Ctrl-F, etc.
while k == CTRL_T:
k = self.console.getkey() k = self.console.getkey()
finally: finally:
self.console.cleanup() self.console.cleanup()
@ -350,8 +362,8 @@ class Monitor(object):
def lookup_pc_address(self, pc_addr): def lookup_pc_address(self, pc_addr):
translation = subprocess.check_output( translation = subprocess.check_output(
["xtensa-esp32-elf-addr2line", "-pfia", ["%saddr2line" % self.toolchain_prefix,
"-e", self.elf_file, pc_addr], "-pfia", "-e", self.elf_file, pc_addr],
cwd=".") cwd=".")
if not "?? ??:0" in translation: if not "?? ??:0" in translation:
sys.stderr.write(ANSI_YELLOW + translation + ANSI_NORMAL) sys.stderr.write(ANSI_YELLOW + translation + ANSI_NORMAL)
@ -375,7 +387,7 @@ class Monitor(object):
with self: # disable console control with self: # disable console control
sys.stderr.write(ANSI_NORMAL) sys.stderr.write(ANSI_NORMAL)
try: try:
subprocess.call(["xtensa-esp32-elf-gdb", subprocess.call(["%sgdb" % self.toolchain_prefix,
"-ex", "set serial baud %d" % self.serial.baudrate, "-ex", "set serial baud %d" % self.serial.baudrate,
"-ex", "target remote %s" % self.serial.port, "-ex", "target remote %s" % self.serial.port,
"-ex", "interrupt", # monitor has already parsed the first 'reason' command, need a second "-ex", "interrupt", # monitor has already parsed the first 'reason' command, need a second
@ -404,12 +416,29 @@ def main():
help='Command to run make', help='Command to run make',
type=str, default='make') type=str, default='make')
parser.add_argument(
'--toolchain-prefix',
help="Triplet prefix to add before cross-toolchain names",
default=DEFAULT_TOOLCHAIN_PREFIX)
parser.add_argument(
"--eol",
choices=['CR', 'LF', 'CRLF'],
type=lambda c: c.upper(),
help="End of line to use when sending to the serial port",
default='CRLF')
parser.add_argument( parser.add_argument(
'elf_file', help='ELF file of application', 'elf_file', help='ELF file of application',
type=argparse.FileType('r')) type=argparse.FileType('r'))
args = parser.parse_args() args = parser.parse_args()
if args.port.startswith("/dev/tty."):
args.port = args.port.replace("/dev/tty.", "/dev/cu.")
sys.stderr.write("WARNING: Serial ports accessed as /dev/tty.* will hang gdb if launched. ")
sys.stderr.write("Using %s instead...\n" % args.port)
serial_instance = serial.serial_for_url(args.port, args.baud, serial_instance = serial.serial_for_url(args.port, args.baud,
do_not_open=True) do_not_open=True)
serial_instance.dtr = False serial_instance.dtr = False
@ -428,7 +457,7 @@ def main():
except KeyError: except KeyError:
pass # not running a make jobserver pass # not running a make jobserver
monitor = Monitor(serial_instance, args.elf_file.name, args.make) monitor = Monitor(serial_instance, args.elf_file.name, args.make, args.eol)
sys.stderr.write('--- idf_monitor on {p.name} {p.baudrate} ---\n'.format( sys.stderr.write('--- idf_monitor on {p.name} {p.baudrate} ---\n'.format(
p=serial_instance)) p=serial_instance))