From e477ce93e9257d5700f25df6366308682f00bb5a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 17 Mar 2017 18:41:18 +0800 Subject: [PATCH] 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 https://github.com/espressif/esp-idf/commit/02fdf8271de3a6db2ab9d4d6f023c160c84ebdbe#commitcomment-21369196 --- components/esptool_py/Makefile.projbuild | 4 ++- docs/idf-monitor.rst | 1 + tools/idf_monitor.py | 41 ++++++++++++++++++++---- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 860e0383830..b35dc111262 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -83,12 +83,14 @@ endif simple_monitor: $(call prereq_if_explicit,%flash) $(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) $(summary) MONITOR [ -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 "*** Or alternatively 'make simple_monitor' to view the serial port as-is." [ -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 diff --git a/docs/idf-monitor.rst b/docs/idf-monitor.rst index a0a2c5cba69..61dfd0afd31 100644 --- a/docs/idf-monitor.rst +++ b/docs/idf-monitor.rst @@ -102,6 +102,7 @@ Known Issues with idf_monitor 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. - 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. diff --git a/tools/idf_monitor.py b/tools/idf_monitor.py index 0bd5f3368e3..a8ddf77e796 100644 --- a/tools/idf_monitor.py +++ b/tools/idf_monitor.py @@ -70,6 +70,8 @@ # regex matches an potential PC value (0x4xxxxxxx) MATCH_PCADDR = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE) +DEFAULT_TOOLCHAIN_PREFIX = "xtensa-esp32-elf-" + class StoppableThread(object): """ 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. """ - 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__() self.event_queue = queue.Queue() self.console = miniterm.Console() @@ -207,9 +209,16 @@ def __init__(self, serial_instance, elf_file, make="make"): self.serial_reader = SerialReader(self.serial, self.event_queue) self.elf_file = elf_file self.make = make + self.toolchain_prefix = DEFAULT_TOOLCHAIN_PREFIX self.menu_key = CTRL_T 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 self._pressed_menu_key = False self._read_line = b"" @@ -246,6 +255,7 @@ def handle_key(self, key): self.serial_reader.stop() else: try: + key = self.translate_eol(key) self.serial.write(codecs.encode(key)) except serial.SerialException: pass # this shouldn't happen, but sometimes port has closed in serial thread @@ -327,7 +337,9 @@ def prompt_next_action(self, reason): .format(key_description(CTRL_A))) sys.stderr.write("--- Press any other key to resume monitor (resets target).\n") sys.stderr.write(ANSI_NORMAL) - k = self.console.getkey() + 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() finally: self.console.cleanup() if k == self.exit_key: @@ -350,8 +362,8 @@ def run_make(self, target): def lookup_pc_address(self, pc_addr): translation = subprocess.check_output( - ["xtensa-esp32-elf-addr2line", "-pfia", - "-e", self.elf_file, pc_addr], + ["%saddr2line" % self.toolchain_prefix, + "-pfia", "-e", self.elf_file, pc_addr], cwd=".") if not "?? ??:0" in translation: sys.stderr.write(ANSI_YELLOW + translation + ANSI_NORMAL) @@ -375,7 +387,7 @@ def run_gdb(self): with self: # disable console control sys.stderr.write(ANSI_NORMAL) try: - subprocess.call(["xtensa-esp32-elf-gdb", + subprocess.call(["%sgdb" % self.toolchain_prefix, "-ex", "set serial baud %d" % self.serial.baudrate, "-ex", "target remote %s" % self.serial.port, "-ex", "interrupt", # monitor has already parsed the first 'reason' command, need a second @@ -404,12 +416,29 @@ def main(): help='Command to run 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( 'elf_file', help='ELF file of application', type=argparse.FileType('r')) 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, do_not_open=True) serial_instance.dtr = False @@ -428,7 +457,7 @@ def main(): except KeyError: 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( p=serial_instance))