diff --git a/detect_esp32.py b/detect_esp32.py index 4b803e8..571cb8d 100755 --- a/detect_esp32.py +++ b/detect_esp32.py @@ -7,40 +7,64 @@ Detects and lists ESP32 devices connected via USB import serial.tools.list_ports import subprocess import sys - +import time +import serial def detect_chip_type(port): """ Try to detect the ESP32 chip type using esptool.py Returns chip type string or 'Unknown' """ + # Try to enter ROM bootloader via DTR/RTS before probing + try_enter_bootloader(port) + + cmd = ['esptool.py', '--port', port, '--chip', 'auto', '--before', 'default_reset', + '--after', 'no_reset', 'chip_id'] + try : + result = subprocess.run(cmd, capture_output=True, text=True, timeout=20) + output = (result.stdout or '') + (result.stderr or '') + + if result.returncode != 0: + last = output.strip().splitlines()[-1] if output.strip().splitlines() else 'esptool failed' + print(f" esptool on {port} failed: {last}") + return 'Unknown' + + # Prefer exact "Chip is ..." line (e.g., ESP32-D0WD-V3) + for line in output.splitlines(): + if line.startswith('Chip is '): + return line.replace('Chip is ', '').split(' (', 1)[0].strip() + + # Fallback loose matches + for name in ('ESP32-S3','ESP32-S2','ESP32-C6','ESP32-C3','ESP32-H2','ESP32'): + if name in output: + return name + + except FileNotFoundError: + print(" esptool.py not found (pip install esptool)") + except subprocess.TimeoutExpired: + print(f" esptool on {port} timed out (check EN/IO0 wiring)") + except Exception as e: + print(f" Error running esptool on {port}: {e}") + + return 'Unknown' + +def try_enter_bootloader(port): + """ + Toggle DTR/RTS to enter download mode. + Assumes RTS->EN (active-low) and DTR->IO0 (active-low on many adapters). + Safe no-op if control lines aren't available. + """ try: - result = subprocess.run( - ['esptool.py', '--port', port, 'chip_id'], - capture_output=True, - text=True, - timeout=10 - ) - - output = result.stdout + result.stderr - - if 'ESP32-S3' in output: - return 'ESP32-S3' - elif 'ESP32-S2' in output: - return 'ESP32-S2' - elif 'ESP32-C3' in output: - return 'ESP32-C3' - elif 'ESP32-C6' in output: - return 'ESP32-C6' - elif 'ESP32-H2' in output: - return 'ESP32-H2' - elif 'ESP32' in output: - return 'ESP32' - + with serial.Serial(port, 115200, timeout=0.1) as ser: + ser.rts = True # EN low + ser.dtr = True # IO0 low + time.sleep(0.05) + ser.rts = False # EN high + time.sleep(0.05) + ser.dtr = False # IO0 high + time.sleep(0.05) except Exception: pass - - return 'Unknown' def guess_chip_type_from_usb(vid, pid): @@ -53,7 +77,7 @@ def guess_chip_type_from_usb(vid, pid): return 'ESP32-S3/C3' # Built-in USB, could be either elif pid == 0x1002: return 'ESP32-S3/C3' - + # For CP210x, CH340, etc., we can't tell from USB alone return None @@ -64,7 +88,7 @@ def detect_esp32_devices(): Returns a list of tuples containing (port, description, hwid) """ esp32_devices = [] - + # Common USB vendor IDs used by ESP32 boards ESP32_VID_PIDS = [ ('10C4', 'EA60'), # Silicon Labs CP210x @@ -75,7 +99,7 @@ def detect_esp32_devices(): ('303A', '1001'), # Espressif USB JTAG/serial debug unit (ESP32-C3, ESP32-S3) ('303A', '1002'), # Espressif USB JTAG/serial debug unit ] - + # Keywords that might appear in ESP32 device descriptions ESP32_KEYWORDS = [ 'CP210', @@ -87,56 +111,56 @@ def detect_esp32_devices(): 'JTAG', 'ESP32', ] - + # Get all available ports ports = serial.tools.list_ports.comports() - + for port in ports: # Check by VID:PID if port.vid is not None and port.pid is not None: vid = f"{port.vid:04X}" pid = f"{port.pid:04X}" - + if any((vid == v and pid == p) for v, p in ESP32_VID_PIDS): esp32_devices.append(port) continue - + # Check by description keywords description_upper = port.description.upper() if any(keyword in description_upper for keyword in ESP32_KEYWORDS): esp32_devices.append(port) - + return esp32_devices def main(): import argparse - + parser = argparse.ArgumentParser(description='ESP32 USB Device Detection') parser.add_argument('--no-probe', action='store_true', help='Skip chip type probing (faster, less accurate)') args = parser.parse_args() - + print("=" * 60) print("ESP32 USB Device Detection") print("=" * 60) print() - + # Detect ESP32 devices esp32_devices = detect_esp32_devices() - + if esp32_devices: print(f"Found {len(esp32_devices)} ESP32 device(s):\n") - + for idx, device in enumerate(esp32_devices, 1): print(f"Device {idx}:") print(f" Port: {device.device}") print(f" Description: {device.description}") print(f" Hardware ID: {device.hwid}") - + if device.vid is not None and device.pid is not None: print(f" VID:PID: {device.vid:04X}:{device.pid:04X}") - + # Chip type detection (probe by default) chip_type = None if not args.no_probe: @@ -151,15 +175,15 @@ def main(): print(f" Chip Guess: {chip_type} (remove --no-probe for exact type)") else: print(f" Chip Type: Unknown (remove --no-probe to detect)") - + if device.manufacturer: print(f" Manufacturer: {device.manufacturer}") - + if device.serial_number: print(f" Serial: {device.serial_number}") - + print() - + print("=" * 60) print(f"Total ESP32 devices detected: {len(esp32_devices)}") if args.no_probe: @@ -170,7 +194,7 @@ def main(): print() print("Available USB serial devices:") all_ports = serial.tools.list_ports.comports() - + if all_ports: for port in all_ports: print(f" - {port.device}: {port.description}")