From d1daf8ca3e264259fd2c571f45e7b30d38aaa11d Mon Sep 17 00:00:00 2001 From: GlassOnTin Date: Tue, 31 Mar 2026 14:06:47 +0100 Subject: [PATCH] Add serial reset and bootloader commands, no more BOOT+RST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new debug commands: - 'X' (reset): ESP.restart() for clean reboot - 'Z' (bootloader): sets RTC_CNTL_FORCE_DOWNLOAD_BOOT and restarts, putting the ESP32-S3 directly into download mode for esptool Workflow: screenshot.py z → esptool write_flash (no BOOT+RST) Also adds reset/bootloader subcommands to screenshot.py. --- Gui.h | 20 ++++++++++++++++++++ scripts/screenshot.py | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/Gui.h b/Gui.h index dc1a265..39587fb 100644 --- a/Gui.h +++ b/Gui.h @@ -8,6 +8,7 @@ #if BOARD_MODEL == BOARD_TWATCH_ULT #include +#include "soc/rtc_cntl_reg.h" // Custom fonts: generated with lv_font_conv --no-compress from Montserrat Bold // IMPORTANT: must use --no-compress or set LV_USE_FONT_COMPRESSED=1 in lv_conf.h @@ -1335,6 +1336,25 @@ static void gui_cmd_execute() { Serial.flush(); break; } + case 'X': { // Hard reset + Serial.write(hdr, 4); + Serial.println("{\"reset\":true}"); + Serial.flush(); + delay(100); + ESP.restart(); + break; + } + case 'Z': { // Reboot into download mode (no BOOT+RST needed) + Serial.write(hdr, 4); + Serial.println("{\"bootloader\":true}"); + Serial.flush(); + delay(100); + #if MCU_VARIANT == MCU_ESP32 + REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); + ESP.restart(); + #endif + break; + } } } diff --git a/scripts/screenshot.py b/scripts/screenshot.py index cb4e819..103a47c 100755 --- a/scripts/screenshot.py +++ b/scripts/screenshot.py @@ -241,6 +241,20 @@ def cmd_files(s): print(f"Timeout ({len(buf)} bytes)") +def cmd_simple(s, cmd_char, label): + """Send a command, print response, don't wait long""" + send_cmd(s, ord(cmd_char)) + time.sleep(0.5) + data = s.read(s.in_waiting or 1) + if data: + magic = PREFIX + cmd_char.encode() + idx = data.find(magic) + if idx >= 0: + print(data[idx + 4:].decode('ascii', errors='replace').strip()) + return + print(label) + + def cmd_log(s): send_cmd(s, ord('L')) buf = b"" @@ -295,6 +309,12 @@ def main(): sub.add_parser("files", aliases=["f"], help="List files on SD card") + sub.add_parser("reset", aliases=["x"], + help="Hard reset the device") + + sub.add_parser("bootloader", aliases=["z"], + help="Reboot into download mode (no BOOT+RST needed)") + args = parser.parse_args() if not args.command: parser.print_help() @@ -322,6 +342,10 @@ def main(): cmd_log(s) elif args.command in ("files", "f"): cmd_files(s) + elif args.command in ("reset", "x"): + cmd_simple(s, 'X', "Reset sent") + elif args.command in ("bootloader", "z"): + cmd_simple(s, 'Z', "Rebooting into download mode...") finally: s.close()