What Is the Stub Chip Loader and Why Does ESP Flasher Use It for High Baud Rates?
The stub chip loader is a small firmware binary that ESP Flasher uploads into RAM to replace the ROM bootloader, enabling UART baud rates up to 921600 and beyond while overcoming the fixed 115200 limitation of the built-in bootloader.
The jason2866/esp_flasher project relies on this technique to dramatically reduce firmware upload times on ESP32 and other ESP chips. By temporarily replacing the chip's stock bootloader with a lightweight stub, the tool gains access to advanced commands like ESP_CHANGE_BAUDRATE that the ROM bootloader does not support.
What Is the Stub Chip Loader?
The stub chip loader is a minimal firmware image originally generated from stub_loader.c in the upstream esptool project. In ESP Flasher, this binary is embedded as the STUB_CODE constant inside esp_flasher/own_esptool.py and copied into the chip's RAM at runtime.
Key characteristics of the stub loader include:
- RAM Execution: Unlike the ROM bootloader stored in read-only memory, the stub is loaded into volatile RAM using the
mem_blockcommand and activated viarun_stub. - Extended Command Set: The stub implements its own protocol commands, including
ESP_CHANGE_BAUDRATE, accelerated flash-write routines, and flash-erase helpers. - Identity Flag: When ESP Flasher instantiates a stub loader, it sets the class attribute
IS_STUB = True. For example, theESP32StubLoaderclass inherits fromESP32ROMbut marks itself as a stub implementation inesp_flasher/own_esptool.py【link:/esp_flasher/own_esptool.py#L1557-L1562】.
Why ESP Flasher Uses the Stub Chip Loader for Higher Baud Rates
The ROM bootloader burned into every ESP chip operates at a fixed 115200 baud and cannot negotiate higher speeds. This limitation becomes a bottleneck when flashing large firmware images, as the upload process takes significantly longer at lower baud rates.
ESP Flasher overcomes this by leveraging the stub's change_baud method, which sends the ESP_CHANGE_BAUDRATE command to the running stub and synchronizes the host-side UART port. As implemented in esp_flasher/own_esptool.py (lines 1064-1071), this method updates both the chip and the host to the new rate simultaneously【link:/esp_flasher/own_esptool.py#L1064-L1071】.
Once the stub acknowledges the new baud rate—commonly 921600 for modern ESP32 devices—all subsequent flash operations execute at this higher speed, reducing upload time by a factor of eight compared to the ROM bootloader's fixed rate.
How the Stub Loading Process Works in ESP Flasher
The ESP Flasher workflow follows a strict sequence to safely transition from the ROM bootloader to high-speed stub operation:
-
Chip Detection: The tool calls
detect_chip()to identify the specific ESP variant and instantiate the appropriate ROM loader class. -
Stub Injection: Through
chip_run_stub(), ESP Flasher uploads theSTUB_CODEbinary into RAM and transfers execution from the ROM bootloader to the stub loader. -
Baud Rate Negotiation: If the user specifies
--upload-baud-rategreater than 115200 (as handled inesp_flasher/__main__.pylines 153-156), the tool invokesstub_chip.change_baud(args.upload_baud_rate)【link:/esp_flasher/main.py#L153-L156】. -
Fallback Handling: If the stub rejects the requested baud rate or the command fails, ESP Flasher catches the exception and falls back to the default 115200 baud, ensuring the flash operation continues reliably rather than failing silently.
Implementation Details in the Source Code
The stub functionality is concentrated in esp_flasher/own_esptool.py, where the change_baud method constructs the baudrate command packet and updates the serial port:
def change_baud(self, baud):
# Send ESP_CHANGE_BAUDRATE command to the stub
self.command(self.ESP_CHANGE_BAUDRATE, struct.pack('<I', baud))
# Synchronize host-side port to match
self._set_port_baudrate(baud)
The CLI layer in esp_flasher/__main__.py determines when to activate high-speed mode based on the --upload-baud-rate argument, ensuring the stub is running before attempting to exceed the ROM's 115200 limit.
Practical Usage Examples
To flash firmware at 921600 baud using the stub loader from the command line:
esp_flasher \
--port /dev/ttyUSB0 \
--upload-baud-rate 921600 \
--chip esp32 \
factory_image.bin
When using ESP Flasher as a Python library, the sequence appears as follows:
import esp_flasher.own_esptool as esptool
# Initialize connection at default ROM baud rate
chip = esptool.ESPLoader.detect_chip(port='/dev/ttyUSB0', baud=115200)
# Load and run the stub loader
stub = chip.run_stub()
# Attempt high-speed upload
try:
stub.change_baud(921600)
except esptool.FatalError:
# Revert to safe speed if negotiation fails
stub._port.baudrate = 115200
# Proceed with flashing at the selected baud rate
Summary
- The stub chip loader is a small RAM-resident firmware that replaces the ROM bootloader to provide extended functionality.
- ESP Flasher embeds this stub as
STUB_CODEinesp_flasher/own_esptool.pyand loads it viamem_blockandrun_stubcommands. - High baud rates (e.g., 921600) require the stub because the ROM bootloader is hardcoded to 115200 baud and lacks the
ESP_CHANGE_BAUDRATEcommand. - The
change_baudmethod in the stub class updates both the ESP chip and the host PC's serial port to maintain synchronization. - If baud rate negotiation fails, ESP Flasher automatically falls back to 115200 baud to ensure reliable flashing.
Frequently Asked Questions
What is the maximum baud rate supported by the stub chip loader?
Most ESP32 devices support 921600 baud reliably through the stub loader, with some chips and USB-to-serial adapters capable of reaching 1500000 baud or higher. The actual limit depends on the specific ESP chip variant, cable quality, and USB bridge hardware. ESP Flasher attempts the requested rate and falls back to 115200 if the stub reports an error.
Can I flash ESP devices without using the stub chip loader?
Yes, ESP Flasher can communicate directly with the ROM bootloader at 115200 baud without loading the stub. However, you will be limited to the ROM's basic command set and fixed baud rate, resulting in significantly slower upload speeds. The stub is only required when you need baud rates above 115200 or advanced features like compressed flashing.
Why does the stub chip loader run from RAM instead of flash?
The stub runs from RAM because the flash memory may need to be erased or rewritten during the flashing process. By executing from RAM, the stub avoids any interference with the flash chip's contents and ensures the code remains accessible even when the flash is being modified. This also allows the stub to be small and fast, loading in milliseconds without permanent modification to the chip.
What happens if the baud rate change fails during stub execution?
If the stub rejects the baud rate change—either because the rate is unsupported or the serial link becomes unstable—ESP Flasher catches the FatalError exception and reverts both the host and the chip back to 115200 baud. As implemented in the CLI logic, this fallback ensures the flashing process continues safely rather than leaving the connection in an undefined state【link:/esp_flasher/main.py#L153-L156】.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →