Embedding into Custom Scripts

esptool.py can be easily integrated into Python applications or called from other Python scripts.

Using Esptool as a Python Module

The esptool module provides a comprehensive Python API for interacting with ESP chips programmatically. By leveraging the API, developers can automate tasks such as flashing firmware, reading device information, managing flash memory, or preparing and analyzing binary images. The API supports both high-level abstractions and low-level control.

Using the Command-Line Interface

The most straightforward and basic integration option is to pass arguments to esptool.main(). This workaround allows you to pass exactly the same arguments as you would on the CLI:

import esptool

command = ['--baud', '460800', 'read-flash', '0', '0x200000', 'flash_contents.bin']
print("Using command ", " ".join(command))
esptool.main(command)

Public API Reference

For more control and custom integration, esptool exposes a public API - a set of high-level functions that encapsulate common operations and simplify the interaction with the ESP chip. These functions are designed to be user-friendly and provide an intuitive way to work with the chip. The public API is the recommended way to interact with the chip programmatically.

Basic Workflow:

  1. Detect and Connect: Use detect_chip() to automatically identify the connected ESP chip and establish a connection, or manually create and instantiate a specific ESPLoader object (e.g. ESP32ROM) and establish a connection in two steps.

  2. Run Stub Flasher (Optional): Upload and execute the stub flasher which provides enhanced functionality and speed.

  3. Perform Operations: Utilize the chip object’s methods or public API command functions to interact with the device.

  4. Reset and Cleanup: Ensure proper reset and resource cleanup using context managers.


This example demonstrates writing two binary files using high-level commands:

from esptool.cmds import detect_chip, attach_flash, reset_chip, run_stub, write_flash

PORT = "/dev/ttyACM0"
BOOTLOADER = "bootloader.bin"
FIRMWARE = "firmware.bin"

with detect_chip(PORT) as esp:
    esp = run_stub(esp)  # Skip this line to avoid running the stub flasher
    attach_flash(esp)  # Attach the flash memory chip, required for flash operations
    with open(BOOTLOADER, "rb") as bl_file, open(FIRMWARE, "rb") as fw_file:
        write_flash(esp, [(0, bl_file), (0x1000, fw_file)])  # Write the binary files
    reset_chip(esp, "hard_reset")  # Reset the chip
  • The esp object has to be replaced with the stub flasher object returned by run_stub(esp) when the stub flasher is activated. This step can be skipped if the stub flasher is not needed.

  • Running attach_flash(esp) is required for any flash-memory-related operations to work.

  • Using the esp object in a context manager ensures the port gets closed properly after the block is executed.


The following example demonstrates running a series of flash memory operations in one go:

from esptool.cmds import (
erase_flash,
attach_flash,
flash_id,
read_flash,
reset_chip,
run_stub,
verify_flash,
write_flash,
)
from esptool.targets import ESP32ROM  # Import the target class, e.g. ESP8266ROM, ESP32S3ROM, etc.

PORT = "/dev/ttyACM0"
BOOTLOADER = "bootloader.bin"
FIRMWARE = "firmware.bin"

with ESP32ROM(PORT) as esp:
    esp.connect()  # Connect to the ESP chip, needed when ESP32ROM is instantiated directly
    esp = run_stub(esp)  # Run the stub loader (optional)
    attach_flash(esp)  # Attach the flash memory chip, required for flash operations
    flash_id(esp)  # Print information about the flash chip
    erase_flash(esp)  # Erase the flash memory first
    with open(BOOTLOADER, "rb") as bl_file, open(FIRMWARE, "rb") as fw_file:
        write_flash(esp, [(0, bl_file), (0x1000, fw_file)])  # Write the binary files
        verify_flash(esp, [(0, bl_file), (0x1000, fw_file)])  # Verify the written data
    read_flash(esp, 0x0, 0x2400, "output.bin")  # Read the flash memory into a file
    reset_chip(esp, "hard_reset")  # Reset the chip
  • This example doesn’t use detect_chip(), but instantiates a ESP32ROM class directly. This is useful when you know the target chip in advance. In this scenario esp.connect() is required to establish a connection with the device.

  • Multiple operations can be chained together in a single context manager block.


The Public API implements a custom ImageSource input type, which expands to str | bytes | IO[bytes] - a path to the firmware image file, an opened file-like object, or the image data as bytes.

As output, the API returns a bytes object representing the binary image or writes the image to a file if the output parameter is provided.

The following example converts an ELF file to a flashable binary, prints the image information, and flashes the image. The example demonstrates three different ways to achieve the same result, showcasing the flexibility of the API:

ELF = "firmware.elf"

# var 1 - Loading ELF from a file, not writing binary to a file
bin_file = elf2image(ELF, "esp32c3")
image_info(bin_file)
with detect_chip(PORT) as esp:
    attach_flash(esp)
    write_flash(esp, [(0, bin_file)])

# var 2 - Loading ELF from an opened file object, not writing binary to a file
with open(ELF, "rb") as elf_file, detect_chip(PORT) as esp:
    bin_file = elf2image(elf_file, "esp32c3")
    image_info(bin_file)
    attach_flash(esp)
    write_flash(esp, [(0, bin_file)])

# var 3 - Loading ELF from a file, writing binary to a file
elf2image(ELF, "esp32c3", "image.bin")
image_info("image.bin")
with detect_chip(PORT) as esp:
    attach_flash(esp)
    write_flash(esp, [(0, "image.bin")])

The following section provides a detailed reference for the public API functions.

Chip Control Operations

esptool.cmds.detect_chip(port: str = '/dev/ttyUSB0', baud: int = 115200, connect_mode: str = 'default_reset', trace_enabled: bool = False, connect_attempts: int = 7) ESPLoader

Detect the type of ESP device connected via serial, connect to it, and return an active ESPLoader object.

Parameters
  • port – The serial port to use for communication.

  • baud – The baud rate for serial communication.

  • connect_mode – The chip reset method to perform when connecting to the ESP device ("default_reset", "usb_reset", "no_reset", "no_reset_no_sync")

  • trace_enabled – Enables or disables tracing for debugging purposes.

  • connect_attempts – Number of connection attempts before failing.

Returns

An initialized instance of the detected chip class ready for use.

esptool.cmds.run_stub(esp: ESPLoader) ESPLoader

Load and execute the stub loader on the ESP device. If stub loading is not supported or is explicitly disabled, warnings are logged.

Parameters

esp – Initiated esp object connected to a real device.

Returns

The esp instance, either as a stub child class in a state where the stub has been executed, or in its original state if the stub loader is disabled or unsupported.

esptool.cmds.load_ram(esp: ESPLoader, input: Union[str, bytes, IO[bytes]]) None

Load a firmware image into RAM and execute it on the ESP device.

Parameters
  • esp – Initiated esp object connected to a real device.

  • input – Path to the firmware image file, opened file-like object, or the image data as bytes.

esptool.cmds.run(esp: ESPLoader) None

Execute the firmware loaded on the ESP device.

Parameters

esp – Initiated esp object connected to a real device.

esptool.cmds.reset_chip(esp: ESPLoader, reset_mode: str = 'hard_reset') None

Reset the ESP device.

Parameters
  • esp – Initiated esp object connected to a real device.

  • reset_mode – Reset mode to use ( "hard_reset": perform a hard reset using the RTS control line, "soft_reset": perform a soft reset, "no_reset": stay in bootloader, "no_reset_stub": stay in flasher stub, "watchdog_reset": perform a hard reset utilizing a software watchdog. )


Chip Information Operations

esptool.cmds.chip_id(esp: ESPLoader) None

Read and display the Chip ID of the ESP device if available, otherwise fall back to displaying the MAC address.

Parameters

esp – Initiated esp object connected to a real device.

esptool.cmds.get_security_info(esp: ESPLoader) None

Read and display security-related information about the ESP device.

Parameters

esp – Initiated esp object connected to a real device.

esptool.cmds.read_mac(esp: ESPLoader) None

Read and display the MAC address of the ESP device.

Parameters

esp – Initiated esp object connected to a real device.


Flash Memory Manipulation Operations

esptool.cmds.attach_flash(esp: ESPLoader, spi_connection: tuple[int, int, int, int, int] | str | None = None) None

Configure and attach a SPI flash memory chip to the ESP device, verify the connection. All following flash operations will be performed on the attached flash chip.

Parameters
  • esp – Initiated esp object connected to a real device.

  • spi_connection – Custom SPI connection configuration. This can either be a tuple containing five pin numbers (CLK, Q, D, HD, CS) for manual configuration or a string ("SPI" or "HSPI") representing a pre-defined config. If not provided, the default flash connection is used.

esptool.cmds.flash_id(esp: ESPLoader) None

Read and display the SPI flash memory chip identification and configuration details, such as the manufacturer ID, device ID, detected flash size, type, and voltage.

Parameters

esp – Initiated esp object connected to a real device.

esptool.cmds.read_flash(esp: ESPLoader, address: int, size: int, output: str | None = None, flash_size: str = 'keep', no_progress: bool = False) bytes | None

Read a specified region of SPI flash memory of an ESP device and optionally save it to a file.

Parameters
  • esp – Initiated esp object connected to a real device.

  • address – The starting address in flash memory to read from.

  • size – The number of bytes to read.

  • output – The name of the file to save the read data. If None, the function returns the data.

  • flash_size – Flash size setting, needs to be set only when the stub flasher is disabled. Options: "detect": auto-detect flash size with fallback to 4MB, "keep": auto-detect but skip setting parameters in SDM, Explicit size: use the specified flash size.

  • no_progress – Disable printing progress.

Returns

The read flash data as bytes if output is None; otherwise, returns None after writing to file.

esptool.cmds.write_flash(esp: ESPLoader, addr_data: list[tuple[int, Union[str, bytes, IO[bytes]]]], flash_freq: str = 'keep', flash_mode: str = 'keep', flash_size: str = 'keep', **kwargs) None

Write firmware or data to the SPI flash memory of an ESP device.

Parameters
  • esp – Initiated esp object connected to a real device.

  • addr_data – List of (address, data) tuples specifying where to write each file or data in flash memory. The data can be a file path (str), bytes, or a file-like object.

  • flash_freq – Flash frequency to set in the bootloader image header ("keep" to retain current).

  • flash_mode – Flash mode to set in the bootloader image header ("keep" to retain current).

  • flash_size – Flash size to set in the bootloader image header ("keep" to retain current).

Keyword Arguments
  • erase_all (bool) – Erase the entire flash before writing.

  • encrypt (bool) – Encrypt all files during flashing.

  • encrypt_files (list[tuple[int, ImageSource]] | None) – List of (address, data) tuples for files to encrypt individually.

  • compress (bool) – Compress data before flashing.

  • no_compress (bool) – Don’t compress data before flashing.

  • force (bool) – Ignore safety checks (e.g., overwriting bootloader, flash size).

  • ignore_flash_enc_efuse (bool) – Ignore flash encryption eFuse settings.

  • no_progress (bool) – Disable progress updates.

esptool.cmds.erase_flash(esp: ESPLoader, force: bool = False) None

Erase the SPI flash memory of the ESP device.

Parameters
  • esp – Initiated esp object connected to a real device.

  • force – Bypass the security checks for flash encryption and secure boot.

esptool.cmds.erase_region(esp: ESPLoader, address: int, size: int, force: bool = False) None

Erase a specific region of the SPI flash memory of the ESP device.

Parameters
  • esp – Initiated esp object connected to a real device.

  • address – The starting address from which to begin erasing.

  • size – The total number of bytes to erase.

  • force – Bypass the security checks for flash encryption and secure boot.

esptool.cmds.verify_flash(esp: ESPLoader, addr_data: list[tuple[int, Union[str, bytes, IO[bytes]]]], flash_freq: str = 'keep', flash_mode: str = 'keep', flash_size: str = 'keep', diff: bool = False) None

Verify the contents of the SPI flash memory against the provided binary files or byte data.

Parameters
  • esp – Initiated esp object connected to a real device.

  • addr_data – List of (address, data) tuples specifying what parts of flash memory to verify. The data can be a file path (str), bytes, or a file-like object.

  • flash_freq – Flash frequency setting ("keep" to retain current).

  • flash_mode – Flash mode setting ("keep" to retain current).

  • flash_size – Flash size setting ("keep" to retain current).

  • diff – If True, perform a byte-by-byte comparison on failure.

esptool.cmds.read_flash_status(esp: ESPLoader, bytes: int = 2) None

Read and print the status register value of the SPI flash memory.

Parameters
  • esp – Initiated esp object connected to a real device.

  • bytes – Number of bytes to read.

esptool.cmds.write_flash_status(esp: ESPLoader, value: int, bytes: int = 2, non_volatile: bool = False) None

Write a new value to the SPI flash memory status register and verify the update.

Parameters
  • esp – Initiated esp object connected to a real device.

  • value – The new status register value to write.

  • bytes – Number of bytes to write.

  • non_volatile – If True, allows non-volatile status register bits to be written.

esptool.cmds.read_flash_sfdp(esp: ESPLoader, address: int, bytes: int = 1) None

Read and display the Serial Flash Discoverable Parameters (SFDP) from the flash memory.

Parameters
  • esp – Initiated esp object connected to a real device.

  • address – Starting address in the SFDP region to read from.

  • bytes – Number of bytes to read (1-4).


Memory Operations

esptool.cmds.read_mem(esp: ESPLoader, address: int) None

Read and display a 32-bit value from a memory address on the ESP device.

Parameters
  • esp – Initiated esp object connected to a real device.

  • address – Memory address to read from (32-bit aligned).

esptool.cmds.write_mem(esp: ESPLoader, address: int, value: int, mask: int = 4294967295) None

Write a 32-bit value to a memory address on the ESP device with optional bitmask.

Parameters
  • esp – Initiated esp object connected to a real device.

  • address – Memory address to write to (32-bit aligned).

  • value – 32-bit value to write.

  • mask – Bitmask specifying which bits to modify (default: all bits).

esptool.cmds.dump_mem(esp: ESPLoader, address: int, size: int, output: str | None = None) bytes | None

Dump a block of memory from the ESP device.

Parameters
  • esp – Initiated esp object connected to a real device.

  • address – Starting memory address to dump from.

  • size – Number of bytes to dump.

  • output – Path to output file for binary data. If None, returns the data.

Returns

Memory dump as bytes if output is None; otherwise, returns None after writing to file.


Binary Image Manipulation Operations

The following commands can run without the need for a connected chip:

esptool.cmds.elf2image(input: Union[str, bytes, IO[bytes]], chip: str, output: str | None = None, flash_freq: str | None = None, flash_mode: str = 'qio', flash_size: str = '1MB', **kwargs) bytes | tuple[bytes | None, bytes] | None

Convert ELF data into a firmware image suitable for flashing onto an ESP device.

Parameters
  • input – Path to the ELF file to convert, opened file-like object, or the ELF data as bytes.

  • chip – Target ESP device type.

  • output – Path to save the generated firmware image. If “auto”, a default pre-defined path is used. If None, the image is not written to a file, but returned as bytes.

  • flash_freq – Flash frequency to set in the image header.

  • flash_mode – Flash mode to set in the image header.

  • flash_size – Flash size to set in the image header.

Keyword Arguments
  • version (int) – ESP8266-only, firmware image version.

  • min_rev (int) – Minimum chip revision required in legacy format.

  • min_rev_full (int) – Minimum chip revision required in extended format.

  • max_rev_full (int) – Maximum chip revision allowed in extended format.

  • secure_pad (bool) – ESP32-only, enable secure padding.

  • secure_pad_v2 (bool) – Enable version 2 secure padding.

  • elf_sha256_offset (int) – Offset for storing the ELF file’s SHA-256 hash.

  • append_digest (bool) – Whether to append a digest to the firmware image.

  • use_segments (bool) – Use ELF segments instead of sections.

  • flash_mmu_page_size (str) – MMU page size for flash mapping.

  • pad_to_size (str) – Pad the final image to a specific flash size.

  • ram_only_header (bool) – Include only RAM segments and no SHA-256 hash.

Returns

The firmware image as bytes if output is None; otherwise, returns None after writing to file. When ESP8266 V1 image is generated, returns a tuple of bytes of IROM data and the rest if output is None; otherwise, returns None after writing to two files.

esptool.cmds.merge_bin(addr_data: list[tuple[int, Union[str, bytes, IO[bytes]]]], chip: str, output: str | None = None, flash_freq: str = 'keep', flash_mode: str = 'keep', flash_size: str = 'keep', format: str = 'raw', **kwargs) bytes | None

Merge multiple binary files into a single output file for flashing to an ESP device.

Take multiple binary files along with their flash addresses and merge them into a unified binary in either raw, UF2, or Intel HEX format. Also apply necessary flash parameters and ensure correct alignment for flashing.

Parameters
  • addr_data – List of (address, data) tuples specifying where to write each file or data in flash memory. The data can be a file path (str), bytes, or a file-like object.

  • chip – Target ESP device type (e.g., "esp32").

  • output – Path to the output file where the merged binary will be written. If None, the merged binary will be returned as bytes.

  • flash_freq – Flash frequency to set in the image header ("keep" to retain current).

  • flash_mode – Flash mode to set in the image header ("keep" to retain current).

  • flash_size – Flash size to set in the image header ("keep" to retain current).

  • format – Output format ("raw", "uf2", or "hex").

Keyword Arguments
  • target_offset (int) – Starting offset for the merged output.

  • pad_to_size (str | None) – If specified, pad the output to a specific flash size.

  • chunk_size (int | None) – Chunk size for UF2 format.

  • md5_disable (bool) – If True, disable MD5 checks in UF2 format.

Returns

The merged binary data as bytes if output is None; otherwise, returns None after writing to file.

esptool.cmds.image_info(input: Union[str, bytes, IO[bytes]], chip: str | None = None) None

Display detailed information about an ESP firmware image.

Parameters
  • input – Path to the firmware image file, opened file-like object, or the image data as bytes.

  • chip – Target ESP device type (e.g., "esp32"). If None, the chip type will be automatically detected from the image header.


Utility Functions

esptool.cmds.version() None

Print the current esptool version.


For more information, refer to the command implementations in esptool/cmds.py.

Low-Level API Reference

Warning

The low-level API provides more control but requires a deeper understanding of the ESP chip, the esptool internals, and the serial protocol. It is recommended to use the public API functions for most use cases.

Also, the low-level internals are not a part of the public API, so they may change in between releases.

Please submit a feature request if you are missing something from the officially supported API.

For granular control and more configuration freedom, you can directly access the low-level methods and attributes of the ESPLoader object and create your own routines. The following is an example of a custom routine to flash the ESP8266:

Note

This example code is a very basic implementation of esptool.py -p /dev/ttyACM0 write-flash 0x10000 firmware.bin

from esptool.cmds import detect_chip

# The port of the connected ESP
PORT = "/dev/ttyACM0"
# The binary file
BIN_FILE = "./firmware.bin"
# Flash offset to flash the binary to
FLASH_ADDRESS = 0x10000

def progress_callback(percent):
    print(f"Wrote: {int(percent)}%")

with detect_chip(PORT) as esp:
    description = esp.get_chip_description()
    features = esp.get_chip_features()
    print(f"Detected ESP on port {PORT}: {description}")
    print("Features:", ", ".join(features))

    esp = esp.run_stub()
    with open(BIN_FILE, 'rb') as binary:
        # Load the binary
        binary_data = binary.read()
        total_size = len(binary_data)
        print(f"Binary size: {total_size} bytes")

        # Write binary blocks
        esp.flash_begin(total_size, FLASH_ADDRESS)
        for i in range(0, total_size, esp.FLASH_WRITE_SIZE):
            block = binary_data[i:i + esp.FLASH_WRITE_SIZE]
            # Pad the last block
            block = block + bytes([0xFF]) * (esp.FLASH_WRITE_SIZE - len(block))
            esp.flash_block(block, i + FLASH_ADDRESS)
            progress_callback(i / total_size * 100)
        esp.flash_finish()

        # Reset the chip out of bootloader mode
        esp.hard_reset()

For more information, refer to the methods of the ESPLoader class in esptool/loader.py.

Redirecting Output with a Custom Logger

Esptool allows redirecting output by implementing a custom logger class. This can be useful when integrating esptool with graphical user interfaces or other systems where the default console output is not appropriate. Below is an example demonstrating how to create and use a custom logger:

from esptool.logger import log, TemplateLogger
import sys

class CustomLogger(TemplateLogger):
    log_to_file = True
    log_file = "esptool.log"

    def print(self, message="", *args, **kwargs):
        # Print to console
        print(f"[CustomLogger]: {message}", *args, **kwargs)
        # Optionally log to a file
        if self.log_to_file:
            with open(self.log_file, "a") as log:
                log.write(f"{message}\n")

    def note(self, message):
        self.print(f"NOTE: {message}")

    def warning(self, message):
        self.print(f"WARNING: {message}")

    def error(self, message):
        self.print(message, file=sys.stderr)

    def stage(self, finish=False):
        # Collapsible stages not needed in this example
        pass

    def progress_bar(
        self,
        cur_iter,
        total_iters,
        prefix = "",
        suffix = "",
        bar_length: int = 30,
    ):
        # Progress bars replaced with simple percentage output in this example
        percent = f"{100 * (cur_iter / float(total_iters)):.1f}"
        self.print(f"Finished: {percent}%")

# Replace the default logger with the custom logger
log.set_logger(CustomLogger())

# From now on, all esptool output will be redirected through the custom logger
# Your code here ...

In this example, the CustomLogger class provides additional functionality such as logging messages to a file, which the original EsptoolLogger (imported from esptool.logger as an initiated object log) doesn’t. The EsptoolLogger.set_logger() method is used to replace the default logger with the custom logger.

To ensure compatibility with esptool, the custom logger should re-implement (or inherit) the following methods from the original EsptoolLogger class (see the reference implementation here), this is enforced by the TemplateLogger abstract class:

  • print: Handles plain message logging.

  • note: Logs informational messages.

  • warning: Logs warning messages.

  • error: Logs error messages.

  • stage: Starts or ends a collapsible output stage.

  • progress_bar: Displays a progress bar.

class esptool.logger.EsptoolLogger
print(*args, **kwargs)

Log a plain message. Count newlines if in a collapsing stage.

note(message: str)

Log a Note: message in blue and white.

warning(message: str)

Log a Warning: message in yellow and white.

error(message: str)

Log an error message in red to stderr.

stage(finish: bool = False)

Start or finish a collapsible stage. Any log messages printed between the start and finish will be deleted when the stage is successfully finished. Warnings and notes will be saved and printed at the end of the stage. If terminal doesn’t support ANSI escape codes, no collapsing happens.

progress_bar(cur_iter: int, total_iters: int, prefix: str = '', suffix: str = '', bar_length: int = 30)

Call in a loop to print a progress bar overwriting itself in place. If terminal doesn’t support ANSI escape codes, no overwriting happens.

These methods are essential for maintaining proper integration and behavior with esptool. Additionally, all output printing should be made using log.print() (or the respective method, such as log.info() or log.warning()) instead of the standard print() function to ensure the output is routed through the custom logger. This ensures consistency and allows the custom logger to handle all output appropriately. You can further customize this logger to fit your application’s needs, such as integrating with GUI components or advanced logging frameworks.