-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdetect_all.py
More file actions
118 lines (92 loc) · 3.34 KB
/
Copy pathdetect_all.py
File metadata and controls
118 lines (92 loc) · 3.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/env python3
"""Detect ESP32 devkits attached to /dev/ttyACM* and /dev/ttyUSB* ports.
Prints one "device=port" line per detected device to stdout, e.g.:
esp32c6=/dev/ttyACM1
esp32s31=/dev/ttyUSB0
Device names are normalized the same way as detect.py (lowercased, dashes
stripped), matching the DEVICE values used by this project's Makefiles.
Intended to be consumed by shell scripts, e.g.:
while IFS='=' read -r device port; do ...; done < <(python detect_all.py)
"""
import glob
import os
import re
import shutil
import subprocess
import sys
PORT_GLOBS = ("/dev/ttyACM*", "/dev/ttyUSB*")
ESPTOOL = "esptool"
PROBE_TIMEOUT = 30
# esptool emits ANSI color/cursor-control codes whenever the environment's
# TERM looks like a color terminal, even when stdout is a pipe. NO_COLOR
# disables that. ANSI_RE strips any escape codes that slip through anyway.
ESPTOOL_ENV = dict(os.environ, NO_COLOR="1")
ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]")
CONNECTED_RE = re.compile(r"^Connected to (\S+) on (\S+):")
CHIP_TYPE_LINE_RE = re.compile(r"^Chip type:\s+(.+?)\s+\(revision v\d+\.\d+\)\s*$")
REVISION_RE = re.compile(r"revision (v\d+\.\d+)")
def find_ports():
ports = []
for pattern in PORT_GLOBS:
ports.extend(glob.glob(pattern))
return sorted(ports)
def normalize(name):
return name.strip().lower().replace("-", "")
def detect_chip(port):
try:
result = subprocess.run(
[ESPTOOL, "--port", port, "chip-id"],
capture_output=True,
text=True,
timeout=PROBE_TIMEOUT,
env=ESPTOOL_ENV,
)
except subprocess.TimeoutExpired:
print(f"{port}: timed out probing for a chip", file=sys.stderr)
return None
stdout = ANSI_RE.sub("", result.stdout)
chip_type = None
chip_description = None
revision = None
for line in stdout.splitlines():
match = CONNECTED_RE.match(line)
if match:
chip_type = match.group(1)
continue
match = CHIP_TYPE_LINE_RE.match(line)
if match:
chip_description = match.group(1)
match = REVISION_RE.search(line)
if match:
revision = match.group(1)
if chip_type is None or revision is None:
print(f"{port}: no chip detected", file=sys.stderr)
detail = result.stderr.strip() or stdout.strip()
if detail:
print(f"{port}: esptool output:\n{detail}", file=sys.stderr)
return None
return chip_type, chip_description or chip_type, revision
def main():
if shutil.which(ESPTOOL) is None:
print(f"error: '{ESPTOOL}' not found on PATH", file=sys.stderr)
sys.exit(1)
devices = {}
for port in find_ports():
detected = detect_chip(port)
if detected is None:
continue
chip_type, chip_description, revision = detected
print(f"{port}: detected {chip_description} revision {revision}", file=sys.stderr)
device = normalize(chip_type)
if device in devices:
print(
f"{port}: duplicate {chip_description} found, "
f"already using {devices[device]}",
file=sys.stderr,
)
continue
devices[device] = port
for device in sorted(devices):
print(f"{device}={devices[device]}")
if __name__ == "__main__":
main()