Skip to content

First Flash

This tutorial walks through the complete flash cycle: detect the chip, back up existing firmware, write new firmware, verify the result, and read serial output. Each step uses an mcesptool MCP tool that you invoke through your LLM client.

These same tools work identically with QEMU virtual devices. Wherever you see a /dev/ttyUSB0 port, a socket://localhost:5555 URI works the same way.

  1. Detect the chip

    Start by confirming the device is connected and identifying what you are working with:

    esp_detect_chip(port="/dev/ttyUSB0", detailed=True)

    The response includes chip type, MAC address, flash size, and crystal frequency. Note the flash size — you will need it if you ever restore the backup.

    {
    "success": true,
    "port": "/dev/ttyUSB0",
    "baud_rate": 115200,
    "connection_time_seconds": 0.91,
    "chip_info": {
    "chip_type": "ESP32",
    "mac_address": "24:6f:28:xx:xx:xx",
    "flash_size": "4MB",
    "crystal_frequency": "40MHz",
    "features": ["WiFi", "BT", "Dual Core", "240MHz"]
    }
    }
  2. Back up existing flash

    esp_flash_backup(
    backup_path="/tmp/esp32-backup.bin",
    port="/dev/ttyUSB0"
    )

    The tool reads the full flash (starting from address 0x0 by default, including the bootloader) and writes it to the specified path. You can restore this backup later with esp_flash_firmware at address 0x0.

  3. Flash new firmware

    Write your compiled firmware binary to the device:

    esp_flash_firmware(
    firmware_path="/path/to/my_app.bin",
    port="/dev/ttyUSB0",
    address="0x10000",
    verify=True
    )

    The address parameter controls where in flash the binary lands. Common addresses:

    AddressContents
    0x0Full merged image
    0x1000Bootloader (ESP32)
    0x8000Partition table
    0x10000Application firmware

    With verify=True (the default), esptool reads back the written data and compares hashes after the write completes.

  4. Verify the flash

    If you want an independent verification pass (separate from the write-time check), use esp_verify_flash:

    esp_verify_flash(
    firmware_path="/path/to/my_app.bin",
    port="/dev/ttyUSB0",
    address="0x10000"
    )

    This reads the flash region back and compares it byte-for-byte against your local file. The response indicates whether the contents match or reports the mismatch location.

  5. Monitor serial output

    After flashing, observe the device boot and application output:

    esp_serial_monitor(
    port="/dev/ttyUSB0",
    baud_rate=115200,
    duration_seconds=10.0,
    reset_on_connect=True
    )

    With reset_on_connect=True, the device is hardware-reset before capturing begins, so you see the full boot sequence from the ROM bootloader through your application startup.

    The tool captures output for the specified duration (up to 30 seconds) and returns all lines as text:

    {
    "success": true,
    "port": "/dev/ttyUSB0",
    "baud_rate": 115200,
    "duration_seconds": 10.03,
    "reset_performed": true,
    "line_count": 47,
    "output": "ets Jul 29 2019 12:21:46\nrst:0x1 (POWERON_RESET)..."
    }

If you need to roll back, flash the backup file to address 0x0:

esp_flash_firmware(
firmware_path="/tmp/esp32-backup.bin",
port="/dev/ttyUSB0",
address="0x0"
)

This overwrites the entire flash with the previously saved image, restoring the device to its original state.