# What Is the Stub Chip Loader and Why Does ESP Flasher Use It for High Baud Rates?

> Discover the stub chip loader, a firmware binary that elevates ESP Flasher's UART baud rates beyond 115200, enabling faster flashing.

- Repository: [Jason2866/esp_flasher](https://github.com/jason2866/esp_flasher)
- Tags: internals
- Published: 2026-03-04

---

**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`](https://github.com/jason2866/esp_flasher/blob/main/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`](https://github.com/jason2866/esp_flasher/blob/main/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_block` command and activated via `run_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, the `ESP32StubLoader` class inherits from `ESP32ROM` but marks itself as a stub implementation in [`esp_flasher/own_esptool.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_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`](https://github.com/jason2866/esp_flasher/blob/main/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:

1. **Chip Detection**: The tool calls `detect_chip()` to identify the specific ESP variant and instantiate the appropriate ROM loader class.

2. **Stub Injection**: Through `chip_run_stub()`, ESP Flasher uploads the `STUB_CODE` binary into RAM and transfers execution from the ROM bootloader to the stub loader.

3. **Baud Rate Negotiation**: If the user specifies `--upload-baud-rate` greater than 115200 (as handled in [`esp_flasher/__main__.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/__main__.py) lines 153-156), the tool invokes `stub_chip.change_baud(args.upload_baud_rate)`【link:/esp_flasher/__main__.py#L153-L156】.

4. **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`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/own_esptool.py), where the `change_baud` method constructs the baudrate command packet and updates the serial port:

```python
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`](https://github.com/jason2866/esp_flasher/blob/main/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:

```bash
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:

```python
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_CODE` in [`esp_flasher/own_esptool.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/own_esptool.py) and loads it via `mem_block` and `run_stub` commands.
- High baud rates (e.g., 921600) require the stub because the ROM bootloader is hardcoded to 115200 baud and lacks the `ESP_CHANGE_BAUDRATE` command.
- The `change_baud` method 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】.