Skip to content

Commit 4f6ffb3

Browse files
committed
agent-guide: an elevated device-node restart turns the S3 wedge into a click, and the whole flash cycle needs no hands
1 parent cc4265a commit 4f6ffb3

2 files changed

Lines changed: 69 additions & 5 deletions

File tree

docs/agent-guide.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,33 @@ failed as "busy or locked", and no process held the handle.
384384
asking for the same replug you were trying to avoid, having also lost the
385385
program that was running.
386386

387-
So: **on a native-USB S3, never try to reach download mode in software.** Ask
388-
for the physical BOOT-and-plug. The network is the useful discriminator if you
389-
do end up guessing — a board that answers HTTP is running your firmware, and a
390-
board that answers nothing while its COM port refuses to open is in ROM mode
391-
behind a stale USB node.
387+
The stale node is repairable, and that changes the whole procedure. Restart
388+
the device node with elevation and Windows enumerates what the chip is
389+
actually presenting:
390+
391+
```bash
392+
powershell.exe -NoProfile -Command "Start-Process pnputil -Verb RunAs \
393+
-ArgumentList '/restart-device','USB\VID_303A&PID_4001\<serial>' -Wait"
394+
```
395+
396+
That pops one UAC prompt, grants nothing that outlives it, and needs a human
397+
to click Yes — but it is a click, not a walk to the bench. On the T-Embed
398+
(2026-09-16, 17:34) the board went straight from the stale `303A:4001` node to
399+
`303A:1001` on a new COM number, esptool flashed it there, and it rebooted
400+
into the new firmware **without a button press** — the whole flash cycle with
401+
no hands on the hardware. `tools/windows/restart-esp-usb.ps1` in this repo
402+
does the same for whatever Espressif board is attached.
403+
404+
So the order to try is: `machine.bootloader()` over `exec`, then the elevated
405+
device-node restart, then flash the `1001` port. Fall back to the physical
406+
BOOT-and-plug only if nobody is there to click. The network is the
407+
discriminator while you are guessing — a board that answers HTTP is running
408+
your firmware, and a board that answers nothing while its COM port refuses to
409+
open is in ROM mode behind a stale USB node.
410+
411+
To lose the prompt as well, an administrator can register a scheduled task
412+
that runs the restart with SYSTEM privileges and let ordinary accounts start
413+
it; an agent must not create that task itself.
392414

393415
### Ctrl-C is not an interrupt inside `atexit`
394416

tools/windows/restart-esp-usb.ps1

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Restart the USB device node of any attached Espressif board (VID 303A).
2+
#
3+
# This is the repair for the ESP32-S3 native-USB wedge described in
4+
# docs/agent-guide.md: after `mpftp bootloader` or a direct
5+
# `machine.bootloader()`, Windows goes on reporting a healthy device while
6+
# every attempt to open its COM port fails as "busy or locked", with no
7+
# process holding the handle. Restarting the device node makes Windows
8+
# enumerate what the chip is really presenting -- a board sitting in ROM
9+
# download mode then appears as 303A:1001 on a new COM number.
10+
#
11+
# Needs elevation. From an ordinary shell (WSL included), this pops one UAC
12+
# prompt and grants nothing that outlives it:
13+
#
14+
# powershell.exe -NoProfile -Command "Start-Process powershell -Verb RunAs `
15+
# -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File','<this file>'"
16+
#
17+
[CmdletBinding()]
18+
param(
19+
# Espressif's vendor ID. Narrow it (e.g. 'USB\VID_303A&PID_4001*') when two
20+
# boards are attached and only one should be restarted.
21+
[string]$Match = 'USB\VID_303A*'
22+
)
23+
24+
$devices = Get-PnpDevice -PresentOnly | Where-Object { $_.InstanceId -like $Match }
25+
if (-not $devices) {
26+
Write-Output "no device matching $Match is attached"
27+
exit 1
28+
}
29+
foreach ($d in $devices) {
30+
Write-Output ("restarting {0} [{1}]" -f $d.InstanceId, $d.Status)
31+
try {
32+
Restart-PnpDevice -InstanceId $d.InstanceId -Confirm:$false -ErrorAction Stop
33+
Write-Output " ok"
34+
} catch {
35+
Write-Output (" failed: {0}" -f $_.Exception.Message)
36+
}
37+
}
38+
Start-Sleep -Seconds 3
39+
Get-PnpDevice -PresentOnly |
40+
Where-Object { $_.InstanceId -like 'USB\VID_303A*' } |
41+
Select-Object Status, InstanceId |
42+
Format-Table -AutoSize | Out-String | Write-Output

0 commit comments

Comments
 (0)