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

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, 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 (lines 153-166), the tool calls stub_chip.change_baud() with the target rate:

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:

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 (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:

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:

        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 (line 185) provides a convenient wrapper:

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:

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, 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 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, 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. ESP8266 devices typically operate at 115200 baud without the same high-speed switching mechanism, so they follow a simpler detection path.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →