feat: add interactive erase prompt and 1200 baud bootloader reset option
- Erase flash prompt: asks user before flashing (in addition to --erase flag) - 1200 baud reset: opens port at 1200 baud with DTR toggle to force ESP32-S3 into download mode when device is stuck/unresponsive - Re-scans serial ports after reset since port name may change
This commit is contained in:
62
flash.py
62
flash.py
@@ -284,6 +284,39 @@ def merge_firmware(output_path, esptool_cmd):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def reset_to_bootloader(port):
|
||||||
|
"""Open serial port at 1200 baud to trigger ESP32-S3 USB bootloader reset.
|
||||||
|
|
||||||
|
Many ESP32-S3 boards with native USB will enter download mode when
|
||||||
|
the port is opened at 1200 baud with DTR toggled. This is useful
|
||||||
|
when the device is stuck or unresponsive to normal esptool connection.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import serial
|
||||||
|
except ImportError:
|
||||||
|
print("Error: pyserial is required for 1200 baud reset.")
|
||||||
|
print("Install it with: pip install pyserial")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"Opening {port} at 1200 baud to trigger bootloader...")
|
||||||
|
try:
|
||||||
|
ser = serial.Serial(port, 1200)
|
||||||
|
ser.dtr = False
|
||||||
|
time.sleep(0.1)
|
||||||
|
ser.dtr = True
|
||||||
|
time.sleep(0.1)
|
||||||
|
ser.dtr = False
|
||||||
|
ser.close()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("Waiting for device to re-enumerate in download mode...")
|
||||||
|
time.sleep(3)
|
||||||
|
print("Done. The device should now be in download mode.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def flash_firmware(firmware_path, port, esptool_cmd, baud=BAUD_RATE):
|
def flash_firmware(firmware_path, port, esptool_cmd, baud=BAUD_RATE):
|
||||||
"""Flash firmware to the device."""
|
"""Flash firmware to the device."""
|
||||||
print(f"\nFlashing {firmware_path} to {port}...")
|
print(f"\nFlashing {firmware_path} to {port}...")
|
||||||
@@ -440,7 +473,34 @@ Examples:
|
|||||||
print(f"Firmware: {firmware_path} ({os.path.getsize(firmware_path):,} bytes)")
|
print(f"Firmware: {firmware_path} ({os.path.getsize(firmware_path):,} bytes)")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
confirm = input("Flash firmware? [Y/n] ").strip().lower()
|
# ── Interactive options ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Offer 1200 baud reset if device might be stuck
|
||||||
|
try:
|
||||||
|
reset_choice = input("Reset device to download mode first? (try if device is stuck) [y/N] ").strip().lower()
|
||||||
|
except EOFError:
|
||||||
|
reset_choice = ""
|
||||||
|
if reset_choice == "y":
|
||||||
|
reset_to_bootloader(port)
|
||||||
|
# Port may change after reset — re-scan
|
||||||
|
print("Re-scanning serial ports (port may have changed)...")
|
||||||
|
new_port = args.port or find_serial_port()
|
||||||
|
if new_port:
|
||||||
|
port = new_port
|
||||||
|
print(f"Using port: {port}")
|
||||||
|
else:
|
||||||
|
print(f"Warning: No ports found after reset. Continuing with {port}")
|
||||||
|
|
||||||
|
# Offer erase unless --erase was already passed
|
||||||
|
if not args.erase:
|
||||||
|
try:
|
||||||
|
erase_choice = input("Erase flash before writing? (recommended for recovery) [y/N] ").strip().lower()
|
||||||
|
except EOFError:
|
||||||
|
erase_choice = ""
|
||||||
|
if erase_choice == "y":
|
||||||
|
args.erase = True
|
||||||
|
|
||||||
|
confirm = input("\nFlash firmware? [Y/n] ").strip().lower()
|
||||||
if confirm and confirm != "y":
|
if confirm and confirm != "y":
|
||||||
print("Aborted.")
|
print("Aborted.")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|||||||
Reference in New Issue
Block a user