Grounding GPIO0 with a bare wire dragged RESET low and reset the ESP8266 mid-flash
This worked, and here is why.
The problem
Serial-flashing a Tuya TYWE1S (ESP8266) on a Eufy RoboVac carrier with a CH341A at 57600 baud. IO0 was tied straight to GND with a wire for download mode. The bootloader answered, a backup read and a full restore write had both worked, then every write started failing a few blocks in, at a different block each time:
write 0x038000: retry 1 (StopIteration)
write 0x038000: retry 2 (Invalid head of packet (0x46) ...)
write 0x038000: retry 3 (No serial data received.)
The 0x46 and the 00 bytes are not stub replies: they are the ROM boot message, garbled at the wrong baud. The chip had reset under the flasher.
The meter showed why. With IO0 floating, RESET sat at ~2.7 V. With IO0 grounded it fell to 0.2–2 V, and it drifted: 3 V after a reflow, then sliding to ~1 V over a minute or two. Below ~0.75·VDD (~2.4 V) the ESP8266 treats RESET as marginal and resets at random.
It did not behave like a short. The beep test stayed silent, the implied IO0→RESET resistance wandered between ~450 Ω and ~6 kΩ, alcohol cleaning did nothing, cooling did nothing, and removing every part from the RESET pad did not fix it. A 1 kΩ and then a 4 kΩ pull-up only moved the number. A dangling harness on the mainboard connector and an unpopulated cap footprint (C4) next to RESET were ruled out one at a time.
The fix
Ground IO0 through a resistor, not a wire. A few hundred ohms (330–470 Ω) from IO0 to GND. GPIO0 only has to read low (< ~0.8 V) against a weak internal pull-up, so the resistor still selects download mode — but RESET immediately held at 3.2 V with IO0 low, and the full 1 MB write went through.
Why this works is the part worth remembering: whatever couples IO0 to RESET on that board (an active GPIO0 output — the ESP8266 can drive a 26 MHz clock on GPIO0 after reset — or a transistor path on the carrier) only drags RESET down when IO0 is hard-shorted. Limiting the current on IO0 removes the drag without giving up the strap.
The diagnosis that found it, in order:
- Measure RESET with IO0 floating and grounded. If grounding IO0 moves RESET, the problem is coupling, not RESET itself.
- Watch RESET for a minute or two, not one reading. A drifting value is not a solder bridge.
- Read the failure bytes.
00/0x46/boot text arriving where a stub reply should be means the chip reset — stop debugging the serial link.
Keep a proper pull-up on RESET (4–10 kΩ to 3.3 V) as well; a RESET pad with nothing on it picks up noise.
On the software side, make the flasher survive resets anyway: with IO0 held low every reset lands back in the ROM bootloader, so on repeated failures reconnect (ROM sync, re-upload the stub, re-set the baud) and retry the same 16 KB block, then verify the whole flash by read-back at the end.