Skip to content

Commit d7da0f8

Browse files
committed
feat(espefuse): Add public API for espefuse
1 parent 0b56f85 commit d7da0f8

34 files changed

+1619
-964
lines changed

docs/en/espefuse/index.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ For more details about Espressif chips eFuse features, see the `{IDF_TARGET_NAME
1313

1414
``espefuse.py`` is installed alongside ``esptool.py``, so if ``esptool.py`` (v2.0 or newer) is available on the PATH then ``espefuse.py`` should be as well.
1515

16-
Initial State of Efuses
16+
Initial State of eFuses
1717
-----------------------
1818

1919
On relatively new chip, most eFuses are unburned (value 0). Some eFuses are already burned at the factory stage:
@@ -166,6 +166,17 @@ For convenience, the espefuse summary command includes the used bit range of the
166166

167167
For more details on the structure and usage of the CSV file, refer to the `eFuse Manager <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/efuse.html#description-csv-file>`_ chapter in the ESP-IDF documentation.
168168

169+
Scripting
170+
---------
171+
172+
Espefuse can be used as a Python library. See :ref:`espefuse.py Scripting <espefuse-scripting>` for more details.
173+
174+
.. toctree::
175+
:maxdepth: 1
176+
:hidden:
177+
178+
scripting
179+
169180
Recommendations
170181
---------------
171182

docs/en/espefuse/scripting.rst

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
.. _espefuse-scripting:
2+
3+
Embedding into Custom Scripts
4+
=============================
5+
6+
Similar to :ref:`esptool.py <scripting>`, ``espefuse.py`` can be easily integrated into Python applications or called from other Python scripts.
7+
8+
For details on redirecting the output, see :ref:`esptool.py logging section <logging>`.
9+
10+
Using Espefuse as a Python Module
11+
---------------------------------
12+
13+
The espefuse module provides a comprehensive Python API for interacting with ESP32 chips programmatically. By leveraging the API, developers can automate tasks such as reading and writing eFuse values, managing secure boot, and more.
14+
15+
The API also provides the benefit of being able to chain commands with ``esptool.py`` commands and create a custom script. With this approach, you can e.g. flash firmware and set eFuse values in one go.
16+
17+
Using the Command-Line Interface
18+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
20+
The most straightforward and basic integration option is to pass arguments to ``espefuse.main()``. This workaround allows you to pass exactly the same arguments as you would on the CLI:
21+
22+
.. code-block:: python
23+
24+
import espefuse
25+
26+
command = ["--port", "/dev/ttyACM0", "summary"]
27+
print("Using command ", " ".join(command))
28+
espefuse.main(command)
29+
30+
31+
Public API Reference
32+
^^^^^^^^^^^^^^^^^^^^
33+
34+
Basic Workflow:
35+
36+
1. **Detect and Connect**: Connect to the chip and load the available eFuse commands for the given chip.
37+
2. **Execute Commands**: Execute the commands you need, e.g. read the current eFuse values.
38+
3. **Reset and Cleanup**: Reset the chip if needed. Context manager will take care of closing the port.
39+
40+
This example demonstrates a basic workflow using the espefuse API to read the current eFuse values:
41+
42+
.. code-block:: python
43+
44+
from espefuse import init_commands
45+
46+
PORT = "/dev/ttyACM0"
47+
48+
# Autodetect and connect to the chip and load the eFuse commands for the given chip
49+
with init_commands(port=PORT) as espefuse:
50+
espefuse.summary() # Print the current eFuse values
51+
52+
# Get the value of single eFuse
53+
custom_mac = espefuse.efuses["CUSTOM_MAC"].get()
54+
print(f"CUSTOM_MAC: {custom_mac}")
55+
56+
.. note::
57+
58+
It is also possible to operate in virtual mode, which allows to read and write eFuse values without connecting to the chip for testing purposes.
59+
For more information, refer to :func:`init_commands <espefuse.efuse_interface.init_commands>` docstring or take a look at tests in :file:`test/test_espefuse.py`.
60+
61+
------------
62+
63+
This API can be also used to chain commands with esptool.py commands.
64+
65+
.. code-block:: python
66+
67+
from espefuse import init_commands
68+
from esptool import attach_flash, flash_id, reset_chip
69+
70+
PORT = "/dev/ttyACM0"
71+
72+
with init_commands(port=PORT) as espefuse:
73+
espefuse.summary() # Get the current eFuse values
74+
# Esptool commands
75+
attach_flash(espefuse.esp) # Attach the flash memory chip, required for flash operations
76+
flash_id(espefuse.esp) # Get the flash information
77+
reset_chip(espefuse.esp, "hard-reset") # Reset the chip
78+
79+
------------
80+
81+
If you would like to have a better control over the ESP object from esptool, you can first get the ESP object from esptool as described in :ref:`esptool.py <scripting>` and then pass it to the espefuse API.
82+
83+
.. code-block:: python
84+
85+
from espefuse import init_commands
86+
from esptool import detect_chip, run_stub, attach_flash, flash_id, reset_chip
87+
88+
PORT = "/dev/ttyACM0"
89+
90+
# Get the ESP object from esptool
91+
with detect_chip(PORT) as esp:
92+
# Prepare the ESP object; run stub and attach flash
93+
esp = run_stub(esp)
94+
attach_flash(esp)
95+
96+
# Pass the ESP object to the espefuse API
97+
with init_commands(esp=esp) as espefuse:
98+
espefuse.summary() # Get the current eFuse values
99+
# External ESP object was passed, so port won't be closed here
100+
101+
# Here you can continue with esptool commands if needed
102+
flash_id(esp) # Get the flash information
103+
104+
reset_chip(esp, "hard-reset") # Reset the chip
105+
106+
------------
107+
108+
Batch Mode
109+
^^^^^^^^^^
110+
111+
For burning eFuses, it is possible to use batch mode. This allows to queue multiple eFuse operations and execute them all at once.
112+
Please note that nesting batch mode is also supported.
113+
114+
Batch mode is enabled by passing the ``batch_mode=True`` argument to the function :func:`init_commands <espefuse.init_commands>`.
115+
Or can be enabled later by calling the :func:`use_batch_mode <espefuse.BaseCommands.use_batch_mode>` method.
116+
117+
The :func:`burn_all <espefuse.BaseCommands.burn_all>` method will execute all queued eFuse operations and decrement the batch mode counter.
118+
119+
Here is an example of how to use a batch mode on ESP32:
120+
121+
.. code-block:: python
122+
123+
from espefuse import init_commands
124+
125+
PORT = "/dev/ttyACM0"
126+
127+
# Connect to chip and enable batch mode
128+
with init_commands(port=PORT, batch_mode=True) as espefuse:
129+
# Queue multiple eFuse operations
130+
with open("flash_encryption_key.bin", "rb") as f:
131+
espefuse.burn_key(["flash_encryption"], [f], no_protect_key=True)
132+
espefuse.burn_efuse({"FLASH_CRYPT_CNT": 0x7})
133+
espefuse.burn_efuse({"DISABLE_DL_ENCRYPT": 1})
134+
espefuse.burn_efuse({"JTAG_DISABLE": 1})
135+
136+
# Execute all queued eFuse operations
137+
espefuse.burn_all()
138+
139+
# Check that all eFuses are set properly
140+
espefuse.summary()
141+
142+
# Checks written eFuses
143+
if espefuse.efuses["FLASH_CRYPT_CNT"].get() != 0x7:
144+
raise esptool.FatalError("FLASH_CRYPT_CNT was not set")
145+
if espefuse.efuses["DISABLE_DL_ENCRYPT"].get() != 1:
146+
raise esptool.FatalError("DISABLE_DL_ENCRYPT was not set")
147+
if espefuse.efuses["JTAG_DISABLE"].is_readable() or espefuse.efuses["JTAG_DISABLE"].is_writeable():
148+
raise esptool.FatalError("JTAG_DISABLE should be read and write protected")
149+
150+
.. note::
151+
152+
Please note that provided example is written for ESP32. For other chips, the names of eFuses might be different and signature of the :func:`burn_key <espefuse.BaseCommands.burn_key>` function might also be different.
153+
154+
After ``espefuse.burn_all()``, all needed eFuses will be burnt to chip in order ``BLK_MAX`` to ``BLK_0``. This order prevents cases when protection is set before the value goes to a block. Please note this while developing your scripts.
155+
Upon completion, the new eFuses will be read back, and checks will be performed on the written eFuses by ``espefuse.py``. In production, you might need to check that all written eFuses are set properly.
156+
In the example above, we check that ``FLASH_CRYPT_CNT`` and ``DISABLE_DL_ENCRYPT`` are set properly. Also, we check that ``JTAG_DISABLE`` is read and write protected.
157+
158+
------------
159+
160+
**The following section provides a detailed reference for the public API functions.**
161+
162+
Init Commands
163+
^^^^^^^^^^^^^
164+
165+
.. autofunction:: espefuse.init_commands
166+
167+
.. autofunction:: espefuse.get_esp
168+
169+
------------
170+
171+
Batch Mode Helpers
172+
^^^^^^^^^^^^^^^^^^
173+
174+
.. autofunction:: espefuse.BaseCommands.use_batch_mode
175+
176+
.. autofunction:: espefuse.BaseCommands.burn_all
177+
178+
------------
179+
180+
Common Read Commands
181+
^^^^^^^^^^^^^^^^^^^^
182+
183+
.. autofunction:: espefuse.BaseCommands.summary
184+
185+
.. autofunction:: espefuse.BaseCommands.dump
186+
187+
.. autofunction:: espefuse.BaseCommands.get_custom_mac
188+
189+
.. autofunction:: espefuse.BaseCommands.adc_info
190+
191+
.. autofunction:: espefuse.BaseCommands.check_error
192+
193+
------------
194+
195+
Common Write Commands
196+
^^^^^^^^^^^^^^^^^^^^^
197+
198+
.. autofunction:: espefuse.BaseCommands.burn_efuse
199+
200+
.. autofunction:: espefuse.BaseCommands.read_protect_efuse
201+
202+
.. autofunction:: espefuse.BaseCommands.write_protect_efuse
203+
204+
.. autofunction:: espefuse.BaseCommands.burn_block_data
205+
206+
.. autofunction:: espefuse.BaseCommands.burn_bit
207+
208+
.. autofunction:: espefuse.BaseCommands.burn_custom_mac
209+
210+
.. autofunction:: espefuse.BaseCommands.set_flash_voltage
211+
212+
------------
213+
214+
Chip-Specific Commands
215+
^^^^^^^^^^^^^^^^^^^^^^
216+
217+
.. autofunction:: espefuse.efuse.{IDF_TARGET_PATH_NAME}.commands.burn_key
218+
219+
.. autofunction:: espefuse.efuse.{IDF_TARGET_PATH_NAME}.commands.burn_key_digest
220+
221+
222+
eFuse Operations
223+
^^^^^^^^^^^^^^^^
224+
225+
.. autofunction:: espefuse.efuse.base_fields.EfuseFieldBase.get
226+
227+
.. autofunction:: espefuse.efuse.base_fields.EfuseFieldBase.is_readable
228+
229+
.. autofunction:: espefuse.efuse.base_fields.EfuseFieldBase.is_writeable
230+
231+
.. autofunction:: espefuse.efuse.base_fields.EfuseFieldBase.get_meaning

0 commit comments

Comments
 (0)