# Flash Size Detection in ESP Flasher: Handling Chips That Don't Support High Baud Rates

> Discover how ESP Flasher handles flash size detection on chips that lack high baud rate support. Learn about the automatic fallback to 115200 baud for reliable operation. jason2866/esp_flasher

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

---

**When the target chip cannot switch to the requested high baud rate, ESP Flasher automatically falls back to the default 115200 baud, recreates the chip connection, and re-runs flash size detection to ensure reliable operation.**

The jason2866/esp_flasher tool manages firmware uploads for ESP32-family devices, automatically handling hardware limitations that prevent baud rate switching. When **flash size detection** fails at high speeds, the tool implements a graceful recovery mechanism that maintains functionality without user intervention.

## The High-Baud Detection Challenge

ESP32 devices typically communicate at the ROM default of 115200 baud. While many chips support higher rates like 921600 baud for faster uploads, some hardware configurations cannot switch speeds. If the tool blindly assumed high-speed support, **flash size detection** would fail silently or throw errors, leaving the flash configuration unknown.

## Automatic Fallback Mechanism

The fallback logic resides in [`esp_flasher/__main__.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/__main__.py), where the tool orchestrates the connection sequence. The implementation follows a three-step recovery process when high-speed communication fails.

### Attempting the Baud Rate Change

After starting the stub loader, the code attempts to switch to the user-requested baud rate. In [`esp_flasher/__main__.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/__main__.py) (lines 153-166), the tool calls `stub_chip.change_baud()` with the target rate:

```python
if (args.upload_baud_rate != 115200) and ("ESP32" in info.family):
    try:
        stub_chip.change_baud(args.upload_baud_rate)
    except esptool.FatalError as err:
        raise Esp_flasherError(f"Error changing ESP upload baud rate: {err}") from err

```

If the ROM rejects the speed change, it raises a `FatalError`, which the tool converts to an `Esp_flasherError`.

### Validating Communication

Immediately after changing baud, the tool validates the connection by calling `detect_flash_size(stub_chip)` (lines 161-168). This serves as a handshake test:

```python
try:
    flash_size = detect_flash_size(stub_chip)
except Esp_flasherError:
    # Chip rejected the high baud – fall back to 115200

    print(f"Chip does not support baud rate {args.upload_baud_rate}, changing to 115200")
    stub_chip._port.close()
    chip = detect_chip(port, args.esp8266, args.esp32)
    stub_chip = chip_run_stub(chip)

```

If detection fails, the code catches the exception, closes the serial port, and prepares for recovery.

### Recovery at Default Speed

The recovery sequence recreates the chip object using `detect_chip()` at the default baud rate, then restarts the stub loader via `chip_run_stub(chip)`. This ensures a clean slate for **flash size detection** at the safe 115200 baud rate.

## The Flash Size Detection Algorithm

Once a stable connection exists (either at high speed or after fallback), the actual detection occurs in [`esp_flasher/own_esptool.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/own_esptool.py) (lines 5408-5419). The `detect_flash_size()` function queries the flash chip's ID register and maps it to a human-readable capacity.

### Reading the Flash ID Register

The function calls `esp.flash_id()` to retrieve a 32-bit identifier. The upper 16 bits encode the manufacturer and capacity information:

```python
def detect_flash_size(esp, args):
    if args.flash_size == 'detect':
        if esp.secure_download_mode:
            raise FatalError("Detecting flash size is not supported in secure download mode.")
        flash_id = esp.flash_id()
        size_id = flash_id >> 16
        args.flash_size = DETECTED_FLASH_SIZES.get(size_id)

```

### Size ID Translation

The `size_id` joins against the `DETECTED_FLASH_SIZES` dictionary. If the ID exists in the map, the tool sets the corresponding size (e.g., "4MB", "8MB"). For unknown IDs, it defaults to **4MB** with a warning:

```python
        if args.flash_size is None:
            print('Warning: Could not auto‑detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4MB' % (flash_id, size_id))
            args.flash_size = '4MB'

```

A helper function in [`esp_flasher/common.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/common.py) (line 185) provides a convenient wrapper:

```python
def detect_flash_size(stub_chip):
    return esptool.DETECTED_FLASH_SIZES.get(flash_id >> 16, "4MB")

```

## Practical Implementation Example

To handle **flash size detection** manually while implementing the same fallback logic, use this pattern:

```python
from esp_flasher.own_esptool import detect_flash_size
from esp_flasher.common import detect_chip, chip_run_stub

chip = detect_chip(port, force_esp32=False, force_esp8266=False)
stub = chip_run_stub(chip)

try:
    # Attempt high-speed detection

    stub.change_baud(921600)
    flash = detect_flash_size(stub, args)
except Exception:
    # Fallback to safe speed

    stub._port.close()
    chip = detect_chip(port, force_esp32=False, force_esp8266=False)
    stub = chip_run_stub(chip)
    flash = detect_flash_size(stub, args)

print("Detected flash size:", flash)

```

This mirrors the internal behavior of [`esp_flasher/__main__.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/__main__.py), ensuring robust detection across different hardware capabilities.

## Summary

- **Flash size detection** in jason2866/esp_flasher uses the `detect_flash_size()` function in [`esp_flasher/own_esptool.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/own_esptool.py) to read the flash ID register and map it to capacity values.
- When high baud rates fail, the tool catches `Esp_flasherError` in [`esp_flasher/__main__.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/__main__.py), closes the connection, and recreates the chip object at 115200 baud.
- The detection algorithm extracts the size ID from the upper 16 bits of the flash ID (`flash_id >> 16`) and looks it up in the `DETECTED_FLASH_SIZES` map.
- Unknown flash chips default to 4MB to ensure the flashing process can continue safely.

## Frequently Asked Questions

### Why does ESP Flasher need to detect flash size before uploading firmware?

The flash size determines partition table offsets and file system boundaries. Without knowing the capacity, the tool cannot verify that the firmware fits within the available storage or configure the ESP32's memory mapping correctly.

### What happens if the flash chip is not in the DETECTED_FLASH_SIZES map?

If the size ID extracted from `flash_id >> 16` does not match any entry in the map, the code defaults to 4MB and prints a warning message. This conservative default prevents flashing failures while alerting the user to the auto-detection limitation.

### Can I force flash size detection to skip the high-baud attempt?

The tool automatically handles unsupported baud rates by falling back to 115200. You can also set `--upload-baud-rate 115200` explicitly to avoid the initial high-speed attempt entirely, eliminating the need for the recovery sequence.

### Does this fallback mechanism work for ESP8266 chips as well?

The specific fallback logic targets ESP32-family chips as indicated by the `"ESP32" in info.family` check in [`esp_flasher/__main__.py`](https://github.com/jason2866/esp_flasher/blob/main/esp_flasher/__main__.py). ESP8266 devices typically operate at 115200 baud without the same high-speed switching mechanism, so they follow a simpler detection path.