Skip to content

Commit ba36933

Browse files
committed
feat(cmds): Rework the public API to work as a Python module
BREAKING CHANGE Closes #208
1 parent 0671d35 commit ba36933

23 files changed

Lines changed: 1741 additions & 1014 deletions

.gitlab-ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ build_docs:
518518
- changes:
519519
- "docs/**/*"
520520
- "CONTRIBUTING.rst"
521+
- "esptool/cmds.py"
521522
needs: []
522523
artifacts:
523524
when: always
@@ -526,6 +527,7 @@ build_docs:
526527
- docs/_build/*/*/html/*
527528
expire_in: 4 days
528529
script:
530+
- pip install . # esptool is needed for the automatic API documentation generation
529531
- cd docs
530532
- pip install -r requirements.txt --prefer-binary
531533
- build-docs -l en -t {esp8266,esp32,esp32s2,esp32c3,esp32s3,esp32c2,esp32c6,esp32h2,esp32p4,esp32c5,esp32c61}
@@ -553,6 +555,7 @@ deploy_docs_preview:
553555
- changes:
554556
- "docs/**/*"
555557
- "CONTRIBUTING.rst"
558+
- "esptool/cmds.py"
556559
variables:
557560
TYPE: "preview"
558561
DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
@@ -570,6 +573,7 @@ deploy_docs_production:
570573
changes:
571574
- "docs/**/*"
572575
- "CONTRIBUTING.rst"
576+
- "esptool/cmds.py"
573577
variables:
574578
TYPE: "production"
575579
DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"

CONTRIBUTING.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ Please report bugs in ``esptool.py`` if you find them. However, before reporting
4444

4545
If you don’t find anything, please `open a new issue <https://github.com/espressif/esptool/issues/new/choose>`_.
4646

47+
.. _feature-requests:
48+
4749
Sending Feature Requests
4850
------------------------
4951

docs/conf_common.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
html_static_path = ["../_static"]
2626

2727
# Conditional content
28-
extensions += ["esp_docs.esp_extensions.dummy_build_system"]
28+
extensions += [
29+
"esp_docs.esp_extensions.dummy_build_system",
30+
"sphinx.ext.autodoc",
31+
"sphinx.ext.napoleon",
32+
]
2933

3034
ESP8266_DOCS = []
3135

docs/en/advanced-topics/serial-protocol.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{IDF_TARGET_SECURITY_INFO:default="32 bits ``flags``, 1 byte ``flash_crypt_cnt``, 7x1 byte ``key_purposes``, 32-bit word ``chip_id``, 32-bit word ``eco_version``", esp32s2="32 bits ``flags``, 1 byte ``flash_crypt_cnt``, 7x1 byte ``key_purposes`` "}
22

3+
.. _serial-protocol:
4+
35
Serial Protocol
46
===============
57

docs/en/esptool/advanced-commands.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ The ``write_flash`` command always verifies the MD5 hash of data which is writte
2020

2121
::
2222

23-
esptool.py verify_flash --diff yes 0x40000 my_app.elf-0x40000.bin
23+
esptool.py verify_flash --diff 0x40000 my_app.elf-0x40000.bin
2424

2525

26-
The ``--diff yes`` option specifies that if the files are different, the details should be printed to the console.
26+
The ``--diff`` option specifies that if the files are different, the details should be printed to the console.
2727

2828
.. note::
2929

docs/en/esptool/scripting.rst

Lines changed: 188 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,17 @@
33
Embedding into Custom Scripts
44
=============================
55

6-
``esptool.py``, ``espefuse.py``, and ``espsecure.py`` can easily be integrated into Python applications or called from other Python scripts.
6+
``esptool.py`` can be easily integrated into Python applications or called from other Python scripts.
77

8-
While it currently does have a poor Python API, something which `#208 <https://github.com/espressif/esptool/issues/208>`_ will address, it allows for passing CLI arguments to ``esptool.main()``. This workaround makes integration very straightforward as you can pass exactly the same arguments as you would on the CLI:
8+
Using Esptool as a Python Module
9+
--------------------------------
10+
11+
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.
12+
13+
Using the Command-Line Interface
14+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
16+
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:
917

1018
.. code-block:: python
1119
@@ -15,16 +23,182 @@ While it currently does have a poor Python API, something which `#208 <https://g
1523
print("Using command ", " ".join(command))
1624
esptool.main(command)
1725
26+
Public API Reference
27+
^^^^^^^^^^^^^^^^^^^^
1828

19-
Using Esptool as a Python Module
20-
--------------------------------
29+
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.
2130

22-
The following is an example on how to use esptool as a Python module and leverage its Python API to flash the {IDF_TARGET_NAME}:
31+
Basic Workflow:
2332

24-
.. note::
33+
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.
34+
2. **Run Stub Flasher (Optional)**: Upload and execute the :ref:`stub flasher <stub>` which provides enhanced functionality and speed.
35+
3. **Perform Operations**: Utilize the chip object's methods or public API command functions to interact with the device.
36+
4. **Reset and Cleanup**: Ensure proper reset and resource cleanup using context managers.
37+
38+
------------
39+
40+
This example demonstrates writing two binary files using high-level commands:
41+
42+
.. code-block:: python
43+
44+
from esptool.cmds import detect_chip, attach_flash, reset_chip, write_flash
45+
46+
PORT = "/dev/ttyACM0"
47+
BOOTLOADER = "bootloader.bin"
48+
FIRMWARE = "firmware.bin"
49+
50+
with detect_chip(PORT) as esp:
51+
esp = esp.run_stub() # Skip this line to avoid running the stub flasher
52+
attach_flash(esp) # Attach the flash memory chip, required for flash operations
53+
with open(BOOTLOADER, "rb") as bl_file, open(FIRMWARE, "rb") as fw_file:
54+
write_flash(esp, [(0, bl_file), (0x1000, fw_file)]) # Write the binary files
55+
reset_chip(esp, "hard_reset") # Reset the chip
56+
57+
- The ``esp`` object has to be replaced with the stub flasher object returned by ``esp.run_stub()`` when the stub flasher is activated. This step can be skipped if the stub flasher is not needed.
58+
- Running ``attach_flash(esp)`` is required for any flash-memory-related operations to work.
59+
- Using the ``esp`` object in a context manager ensures the port gets closed properly after the block is executed.
60+
61+
------------
62+
63+
The following example demonstrates running a series of flash memory operations in one go:
64+
65+
.. code-block:: python
66+
67+
from esptool.cmds import (
68+
erase_flash,
69+
attach_flash,
70+
flash_id,
71+
read_flash,
72+
reset_chip,
73+
verify_flash,
74+
write_flash,
75+
)
76+
from esptool.targets import ESP32ROM # Import the target class, e.g. ESP8266ROM, ESP32S3ROM, etc.
77+
78+
PORT = "/dev/ttyACM0"
79+
BOOTLOADER = "bootloader.bin"
80+
FIRMWARE = "firmware.bin"
81+
82+
with ESP32ROM(PORT) as esp:
83+
esp.connect() # Connect to the ESP chip, needed when ESP32ROM is instantiated directly
84+
esp = esp.run_stub() # Run the stub loader (optional)
85+
attach_flash(esp) # Attach the flash memory chip, required for flash operations
86+
flash_id(esp) # Print information about the flash chip
87+
erase_flash(esp) # Erase the flash memory first
88+
with open(BOOTLOADER, "rb") as bl_file, open(FIRMWARE, "rb") as fw_file:
89+
write_flash(esp, [(0, bl_file), (0x1000, fw_file)]) # Write the binary files
90+
verify_flash(esp, [(0, bl_file), (0x1000, fw_file)]) # Verify the written data
91+
read_flash(esp, 0x0, 0x2400, "output.bin") # Read the flash memory into a file
92+
reset_chip(esp, "hard_reset") # Reset the chip
93+
94+
- 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.
95+
- Multiple operations can be chained together in a single context manager block.
96+
97+
------------
98+
99+
**The following section provides a detailed reference for the public API functions.**
100+
101+
Chip Control Operations
102+
"""""""""""""""""""""""
103+
104+
.. autofunction:: esptool.cmds.detect_chip
105+
106+
.. autofunction:: esptool.cmds.load_ram
107+
108+
.. autofunction:: esptool.cmds.run
109+
110+
.. autofunction:: esptool.cmds.reset_chip
111+
112+
------------
113+
114+
Chip Information Operations
115+
"""""""""""""""""""""""""""
116+
117+
.. autofunction:: esptool.cmds.chip_id
118+
119+
.. autofunction:: esptool.cmds.get_security_info
120+
121+
.. autofunction:: esptool.cmds.read_mac
25122

26-
This example code functionally equivalent to ``esptool.py -p /dev/ttyACM0 write_flash 0x10000 firmware.bin``
123+
------------
27124

125+
Flash Memory Manipulation Operations
126+
""""""""""""""""""""""""""""""""""""
127+
128+
.. autofunction:: esptool.cmds.attach_flash
129+
130+
.. autofunction:: esptool.cmds.flash_id
131+
132+
.. autofunction:: esptool.cmds.read_flash
133+
134+
.. autofunction:: esptool.cmds.write_flash
135+
136+
.. autofunction:: esptool.cmds.erase_flash
137+
138+
.. autofunction:: esptool.cmds.erase_region
139+
140+
.. autofunction:: esptool.cmds.verify_flash
141+
142+
.. autofunction:: esptool.cmds.read_flash_status
143+
144+
.. autofunction:: esptool.cmds.write_flash_status
145+
146+
.. autofunction:: esptool.cmds.read_flash_sfdp
147+
148+
------------
149+
150+
Memory Operations
151+
"""""""""""""""""
152+
153+
.. autofunction:: esptool.cmds.read_mem
154+
155+
.. autofunction:: esptool.cmds.write_mem
156+
157+
.. autofunction:: esptool.cmds.dump_mem
158+
159+
------------
160+
161+
Binary Image Manipulation Operations
162+
""""""""""""""""""""""""""""""""""""
163+
164+
The following commands can run without the need for a connected chip:
165+
166+
.. autofunction:: esptool.cmds.elf2image
167+
168+
.. autofunction:: esptool.cmds.merge_bin
169+
170+
.. autofunction:: esptool.cmds.image_info
171+
172+
.. autofunction:: esptool.cmds.make_image
173+
174+
------------
175+
176+
Utility Functions
177+
"""""""""""""""""
178+
179+
.. autofunction:: esptool.cmds.version
180+
181+
------------
182+
183+
For more information, refer to the command implementations in `esptool/cmds.py <https://github.com/espressif/esptool/blob/master/esptool/cmds.py>`_.
184+
185+
186+
Low-Level API Reference
187+
^^^^^^^^^^^^^^^^^^^^^^^
188+
189+
.. warning::
190+
191+
The low-level API provides more control but requires a deeper understanding of the ESP chip, the esptool internals, and the :ref:`serial protocol <serial-protocol>`. It is recommended to use the public API functions for most use cases.
192+
193+
Also, the low-level internals are not a part of the public API, so they may change in between releases.
194+
195+
Please submit a :ref:`feature request <feature-requests>` if you are missing something from the officially supported API.
196+
197+
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 {IDF_TARGET_NAME}:
198+
199+
.. note::
200+
201+
This example code is a very basic implementation of ``esptool.py -p /dev/ttyACM0 write_flash 0x10000 firmware.bin``
28202

29203
.. code-block:: python
30204
@@ -66,6 +240,9 @@ The following is an example on how to use esptool as a Python module and leverag
66240
# Reset the chip out of bootloader mode
67241
esp.hard_reset()
68242
243+
------------
244+
245+
For more information, refer to the methods of the ``ESPLoader`` class in `esptool/loader.py <https://github.com/espressif/esptool/blob/master/esptool/loader.py>`_.
69246

70247
.. _logging:
71248

@@ -124,4 +301,8 @@ To ensure compatibility with esptool, the custom logger should re-implement (or
124301
- ``print_overwrite``: Handles message overwriting (can be a simple ``print()`` if overwriting is not needed).
125302
- ``set_progress``: Handles percentage updates of long-running operations - ``write_flash``, ``read_flash``, and ``dump_mem`` (useful for GUI visualisation, e.g. as a progress bar).
126303

304+
.. autoclass:: esptool.logger.EsptoolLogger
305+
:members: print, note, warning, error, print_overwrite, set_progress
306+
:member-order: bysource
307+
127308
These methods are essential for maintaining proper integration and behavior with esptool. Additionally, all calls to the logger 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.

docs/en/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ The flasher stub is a small program included with esptool that replaces the orig
1212
* Prepare binary executable images ready for flashing.
1313
* Analyze, assemble, and merge binary images.
1414

15+
``esptool.py`` can be used both as a command-line tool and as a Python library. The command-line is the most common way to use the tool, and is the primary focus of this documentation. To use it as a library, see the :ref:`scripting <scripting>` section.
16+
1517
This document describes using ``esptool.py`` with the {IDF_TARGET_NAME} SoC. To switch to a different SoC target, choose target from the dropdown in the upper left.
1618

1719
Quick Start

docs/en/migration-guide.rst

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,42 @@ Beta Target Support Removal
7979

8080
Support for the following beta targets has been **removed in v5**:
8181

82-
- ESP32-C5(beta3)
83-
- ESP32-C6(beta)
84-
- ESP32-H2(beta1)
85-
- ESP32-H2(beta2)
86-
- ESP32-S3(beta2)
82+
- ``ESP32-C5(beta3)``
83+
- ``ESP32-C6(beta)``
84+
- ``ESP32-H2(beta1)``
85+
- ``ESP32-H2(beta2)``
86+
- ``ESP32-S3(beta2)``
8787

8888
**Migration Steps:**
8989

9090
1. Update any scripts or workflows not to target these beta chips.
9191
2. Remove any references to these beta targets from CI/CD pipelines or build scripts.
9292

9393
Use esptool ``v4`` for legacy workflows targeting these beta chips.
94+
95+
``verify_flash`` ``--diff`` Argument
96+
*************************************
97+
98+
The format of the ``--diff`` option of the :ref:`verify_flash <verify-flash>` command has **changed in v5**. Previously, ``--diff=yes/no`` had to be specified to enable or disable the diff output. In the new version, the ``--diff`` option is a simple boolean switch without the need of a ``yes`` or ``no`` value.
99+
100+
**Migration Steps:**
101+
102+
1. Rewrite the ``--diff=yes`` argument to a simple ``--diff`` in any existing ``verify_flash`` commands in scripts/CI pipelines. Delete ``--diff=no`` completely if detailed diff output is not required.
103+
104+
Using esptool as a Python Module
105+
********************************
106+
107+
All command functions (e.g., ``verify_flash``, ``write_flash``) have been refactored to remove their dependency on the ``args`` object from the argparse module. Instead, all arguments are now passed explicitly as individual parameters. This change, combined with enhancements to the public API, provides a cleaner, more modular interface for programmatic use of esptool in custom scripts and applications (see :ref:`scripting <scripting>`).
108+
109+
**Key Changes:**
110+
111+
- Refactored Function Signatures: Previously, command functions relied on an ``args`` object (e.g., ``args.addr_filename``, ``args.diff``). Now, they take individual parameters with explicit types and default values, improving clarity and enabling a robust API.
112+
- Public API Expansion: The public API (exposed via ``esptool.cmds``) has been formalized with high-level functions like ``detect_chip()``, ``attach_flash()``, ``write_flash()``, and ``reset_chip()``, designed for ease of use in Python scripts.
113+
114+
**Migration Steps:**
115+
116+
1. Update Function Calls: If you are calling esptool functions programmatically, replace ``args`` object usage with individual parameter passing. Refer to the function signatures in ``esptool.cmds`` for the new parameter names, types, and defaults.
117+
2. Leverage the Public API: Use the new high-level functions in ``esptool.cmds`` for common operations like chip detection, flash attachment, flashing, resetting, or image generation.
118+
3. Test your updated scripts to ensure compatibility with the new API.
119+
120+
For detailed examples and API reference, see the :ref:`scripting <scripting>` section.

0 commit comments

Comments
 (0)