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:
Detect and Connect: Use
detect_chip()
to automatically identify the connected ESP chip and establish a connection, or manually create and instantiate a specificESPLoader
object (e.g.ESP32ROM
) and establish a connection in two steps.Run Stub Flasher (Optional): Upload and execute the stub flasher which provides enhanced functionality and speed.
Perform Operations: Utilize the chip object’s methods or public API command functions to interact with the device.
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 byrun_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 aESP32ROM
class directly. This is useful when you know the target chip in advance. In this scenarioesp.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.