Like many microcontrollers, the ESP8266 and ESP32 series contain an in-Silicon bootloader which can be initiated at chip reset by holding a pin low at the moment of reset.
Documentation is not helped by less than common terms (like EXTRSTB for a pin that is really a /RST, and it is labelled EN on the ESP32). The other relevant pin is known as GPIO00, but can be thought of as a /BOOT bin.
Automatic bootloader initiation
The firmware is uploaded using ordinary RS232/TTL, and it is possible to use modem control signals to control the /RST (EXTRSTB) and /BOOT (GPIO00 or IO0) pins, indeed the common convention is to use RS232 signals RTS for resetting the MCU, and DTR for boot selection.
So, it is possible to connect RS232/TTL /RTS to /RST and /DTR to /BOOT, and ESPTOOL will automatically initiate the boot loader when accessing the chip.
Above is a simulation of that type of direct connection. The RTS/DTR scenario is that from ESPTOOL, but it can be seen that even if the RTS transition was delayed somewhat the /BOOT pin is low and when /RST rises the chip will initiate the bootloader. The critical timing is that /BOOT is low when /RST transitions from low to high.
Above is a scope capture of the direct connection. The red trace is /RST and the yellow trace is /BOOT. In this case, /BOOT is pulled low about 250µs before/RST releases and triggers the reset.
The problem is…
That is all good, but if you try to use the same adapter with comms software to read / write the serial port, if the comms software asserts RTS and DTR (as they often do), the low on /RTS will force the MCU into reset (ie it will stop it).
ESPTOOL does not do this, and works just fine without the extra circuitry.
To work around that issue, Esspressif recommended a circuit in their documentation.
Above is the circuit from an Esspressif datasheet. You might wonder what it really achieves. It was a workaround for serial port hardware that asserts DTR and RTS when opened and which would immediately cause a reset of the MCU.
It does this at the expense of making bootloader initiation a little more touchy, lets look at a simulation.
Above is a simulation of the circuit. Note that with the two transistor enhancement, if /DTR and /RTS are both low, /RST is high, so differently to the direct configuration discussed earlier where you can pull /DTR low and some milliseconds later pull /RTS low and release it… this will not work here.
What MUST happen, is that /RTS is low, then released and /DTR is pulled low so quickly that as the /RST signal rises to threshold and the MCU resets, /BOOT is already low. To facilitate this, a capacitor is connected from /RST to ground so that the voltage rises slowly enough that the /BOOT signal is low.
Above is a scope capture of the transistor circuit. The red trace is /RST and the yellow trace is /BOOT. In this case, /BOOT is pulled low about 500µs after /RST releases, but reset is not triggered until a little later when /RST reaches about 1V. This is a Wemos D1 which uses a 0.1µF capacitor on the /RST pin, much smaller and the /RST line would rise too quickly to capture the BOOT condition with this software and driver setup.
To solve a problem, the workaround creates a timing critical behavior that depends on a capacitor (which commonly varies from 0.1 to 1µF in different boards) and response of the serial port to requests to change modem control signals, a response time that is not enshrined in standards. ESP32 WROOM32 – A possible fix for failure of auto-program facility discusses the matter.
The simulation above shows what happens if the /RTS signal is 1ms late, V(/rst) has risen and the chip is reset while V(/boot) is high and the boot loader is not initiated.
If on the other hand, /RTS signal is 1ms early, V(/rst) has risen and the chip is reset while V(/boot) is high and the boot loader is not initiated.
This is the antithesis of good digital design.
ESPTOOL is of interest, it is the most common tool used to invoke the bootloader.
Let’s look at how it does the reset to bootloader.
self._setDTR(False) # IO0=HIGH self._setRTS(True) # EN=LOW, chip in reset time.sleep(0.1) if esp32r0_delay: # Some chips are more likely to trigger the esp32r0 # watchdog reset silicon bug if they're held with EN=LOW # for a longer period time.sleep(1.2) self._setDTR(True) # IO0=LOW self._setRTS(False) # EN=HIGH, chip out of reset if esp32r0_delay: # Sleep longer after reset. # This workaround only works on revision 0 ESP32 chips, # it exploits a silicon bug spurious watchdog reset. time.sleep(0.4) # allow watchdog reset to occur time.sleep(0.05) self._setDTR(False) # IO0=HIGH, done
The timing between making /DTR low and /RTS high is critical and the capacitor mentioned provides for some delay in the rise of /RST to accomodate the serial signals.
Update: improved cheap ESP-01 programmer module
The above module has a CP210x USB chip and contains the auto program circuit (note the two small transistors at the top edge).
ESP_Prog is a new programming tool from Esspressif.
Above, the ESP_Prog wired to an ESP-01S module
Above is capture of the reset into boot loader of the ESP-01S again from ESPTOOL. The slow rise of /RST is due to the 1µF capacitor on the ESP-01S.