Connecting and flashing an ESP-01¶
The ESP-01 is an ESP8266 module on an eight-pin header. Getting a serial console and flashing new firmware are both straightforward once you know which adapter you have — most of the pain comes from cheap USB dongles that omit the auto-reset wiring, so every tutorial written for a NodeMCU board quietly fails.
Pinout¶
Held with the antenna at the top, pins facing you:
┌───────────────┐
│ [antenna] │
│ │
GND │ ● ● │ TX
GPIO2 │ ● ● │ CH_PD (EN)
GPIO0 │ ● ● │ RST
RX │ ● ● │ VCC
└───────────────┘
The pairs across the two rows, in order, are fixed by the module: GND/TX, GPIO2/CH_PD, GPIO0/RST, RX/VCC. That ordering never changes, which makes it a reliable way to identify an unlabelled adapter socket: find GND with a continuity meter (it beeps against the USB shell), and everything else follows. VCC is diagonally opposite GND.
Two pins matter beyond power and serial:
- CH_PD (EN) must be pulled high. Floating means the chip sits in permanent reset — powered, silent, and completely unresponsive.
- GPIO0 selects boot mode, sampled only at reset.
The auto-reset problem¶
Plain CH340 dongles for the ESP-01 typically wire only TX, RX, VCC and GND. DTR and RTS go nowhere. Consequences:
esptoolcannot reset the board, so it cannot enter flash mode by itself and--before default-resetalways fails- You can never catch the power-on boot banner, because the ESP boots the instant USB power arrives — long before the kernel creates
/dev/ttyUSB0
Adapters with a PROG/FLASH switch, or a CP2102/FTDI with DTR → GPIO0 and RTS → RST, avoid all of this. Wiring those two lines yourself is the single highest-value modification.
If the tool reports Failed to connect ... No serial data received for every combination of settings, suspect wiring rather than software. A quick check: bridge the adapter's TX to RX with the module removed and confirm bytes echo back. That proves the USB side end to end and halves the search space.
Reading the boot banner¶
The ROM bootloader always prints at 74880 baud — an unusual rate, but the chip's own, and it works regardless of what firmware is installed.
The trick that makes this practical without auto-reset: hold the serial port open, then briefly touch RST to GND. The chip resets while something is already listening, so the banner lands in an open port. Chasing it by replugging is a race you cannot win.
import serial, time
s = serial.Serial('/dev/ttyUSB0', 74880, timeout=0.2)
t0 = time.time()
while time.time() - t0 < 30: # tap RST to GND during this window
d = s.read(4096)
if d:
print(d.decode('utf-8', 'replace'), end='')
A healthy boot looks like:
ets Jan 8 2013,rst cause:2, boot mode:(3,6)
load 0x40100000, len 1856, room 16
...
2nd boot version : 1.5
SPI Flash Size & Map: 8Mbit(512KB+512KB)
jump to run user1 @ 1000
Read it as:
| Field | Meaning |
|---|---|
rst cause:1 | power-on |
rst cause:2 | external reset (your RST tap) |
rst cause:4 | watchdog — often a crash loop |
boot mode:(3,x) | GPIO0 high — normal boot |
boot mode:(1,x) | GPIO0 low — bootloader, waiting for esptool |
jump to run user1 | firmware loaded and started |
RST must be momentary. Holding it at GND keeps the chip halted — silent, with the blue TX LED dark — which looks exactly like a dead board.
Entering flash mode¶
GPIO0 is sampled only as the chip leaves reset, so:
- Hold GPIO0 to GND
- Tap RST to GND, release RST
- Release GPIO0
Shorting GPIO0 on an already-running chip does nothing at all.
Once latched, the bootloader waits indefinitely — there is no timeout to race. Confirm with boot mode:(1,x) and the absence of jump to run user1.
esptool¶
pipx install esptool # or: uv tool install esptool
sudo usermod -aG dialout $USER # then log out and back in
Group membership does not reach already-running sessions. Until you log out, wrap commands in sg dialout -c "...".
With no auto-reset, tell esptool not to try:
Why --no-stub¶
esptool normally uploads a helper program ("stub") into RAM for speed. Over long jumper wires that bulk transfer often fails with Packet content transfer stopped. --no-stub drives the ROM loader directly: slower, far more tolerant.
The catch:
write-flashworks without the stub — the ROM supports flash writesread-flashdoes not. It fails withESP8266 ROM does not support function read_flash_slow. Dumping flash requires the stub, so on a flaky link you may be able to write firmware but never back up what was there first.
Flashing¶
esptool --port /dev/ttyUSB0 --chip esp8266 \
--before no-reset --after no-reset --no-stub \
--baud 57600 --flash-size 1MB --flash-mode qio \
write-flash 0x0 firmware.bin
Drop the baud rate if a write dies partway. At 115200 a ~290 KB write may fail around 10–15%; the same write at 57600 completes reliably. It is roughly twice as slow and worth every second — flash is erased before writing, so a failed write leaves the module unbootable until you retry.
A failed write is never fatal: flash mode is selected by GPIO0 in hardware, independent of flash contents, so the board is always recoverable.
Brownout¶
The ESP-01 draws ~300 mA peaks when the radio starts. Symptom: it boots, prints its banner, begins associating, and resets — over and over.
- Seat the module directly in its adapter rather than running it through jumper wires; the bulk capacitor is then millimetres away instead of at the end of a resistive chain
- Add 470 µF + 100 nF across VCC/GND right at the module
- Better still, use a separate 3.3 V supply rated for the peaks
- In firmware,
WiFi.setOutputPower(12.0)cuts the current spike
Never feed it 5 V — neither VCC nor the GPIOs are 5 V tolerant.
PlatformIO¶
[env:esp01_1m]
platform = espressif8266
board = esp01_1m
framework = arduino
board_build.flash_mode = qio
monitor_speed = 115200
pio run -t upload cannot work on an adapter without auto-reset — build with pio run, then flash manually as above.
Quick diagnosis¶
| Symptom | Likely cause |
|---|---|
| Nothing at any baud, ever | TX not contacting; module not fully seated |
| Red LED on, blue LED never lights | Chip held in reset — RST tied low, or CH_PD floating |
| Banner appears, but commands get no reply | RX leg not contacting |
| Boots, then resets during Wi-Fi | Brownout |
boot mode:(3,x) when you wanted flash mode | GPIO0 was not low at reset |
| Garbled text | Wrong baud — the ROM talks at 74880, not 115200 |