Embedding into Custom Scripts
Similar to esptool, espefuse
can be easily integrated into Python applications or called from other Python scripts.
For details on redirecting the output, see esptool logging section.
Using Espefuse as a Python Module
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.
The API also provides the benefit of being able to chain commands with esptool
commands and create a custom script. With this approach, you can e.g. flash firmware and set eFuse values in one go.
Using the Command-Line Interface
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:
import espefuse
command = ["--port", "/dev/ttyACM0", "summary"]
print("Using command ", " ".join(command))
espefuse.main(command)
Public API Reference
Basic Workflow:
Detect and Connect: Connect to the chip and load the available eFuse commands for the given chip.
Execute Commands: Execute the commands you need, e.g. read the current eFuse values.
Reset and Cleanup: Reset the chip if needed. Context manager will take care of closing the port.
This example demonstrates a basic workflow using the espefuse API to read the current eFuse values:
from espefuse import init_commands
PORT = "/dev/ttyACM0"
# Autodetect and connect to the chip and load the eFuse commands for the given chip
with init_commands(port=PORT) as espefuse:
espefuse.summary() # Print the current eFuse values
# Get the value of single eFuse
custom_mac = espefuse.efuses["CUSTOM_MAC"].get()
print(f"CUSTOM_MAC: {custom_mac}")
Note
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.
For more information, refer to init_commands
docstring or take a look at tests in test/test_espefuse.py
.
This API can be also used to chain commands with esptool commands.
from espefuse import init_commands
from esptool import attach_flash, flash_id, reset_chip
PORT = "/dev/ttyACM0"
with init_commands(port=PORT) as espefuse:
espefuse.summary() # Get the current eFuse values
# Esptool commands
attach_flash(espefuse.esp) # Attach the flash memory chip, required for flash operations
flash_id(espefuse.esp) # Get the flash information
reset_chip(espefuse.esp, "hard-reset") # Reset the chip
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 esptool and then pass it to the espefuse API.
from espefuse import init_commands
from esptool import detect_chip, run_stub, attach_flash, flash_id, reset_chip
PORT = "/dev/ttyACM0"
# Get the ESP object from esptool
with detect_chip(PORT) as esp:
# Prepare the ESP object; run stub and attach flash
esp = run_stub(esp)
attach_flash(esp)
# Pass the ESP object to the espefuse API
with init_commands(esp=esp) as espefuse:
espefuse.summary() # Get the current eFuse values
# External ESP object was passed, so port won't be closed here
# Here you can continue with esptool commands if needed
flash_id(esp) # Get the flash information
reset_chip(esp, "hard-reset") # Reset the chip
Batch Mode
For burning eFuses, it is possible to use batch mode. This allows to queue multiple eFuse operations and execute them all at once. Please note that nesting batch mode is also supported.
Batch mode is enabled by passing the batch_mode=True
argument to the function init_commands
.
Or can be enabled later by calling the use_batch_mode
method.
The burn_all
method will execute all queued eFuse operations and decrement the batch mode counter.
Here is an example of how to use a batch mode on ESP32:
from espefuse import init_commands
PORT = "/dev/ttyACM0"
# Connect to chip and enable batch mode
with init_commands(port=PORT, batch_mode=True) as espefuse:
# Queue multiple eFuse operations
with open("flash_encryption_key.bin", "rb") as f:
espefuse.burn_key(["flash_encryption"], [f], no_protect_key=True)
espefuse.burn_efuse({"FLASH_CRYPT_CNT": 0x7})
espefuse.burn_efuse({"DISABLE_DL_ENCRYPT": 1})
espefuse.burn_efuse({"JTAG_DISABLE": 1})
# Execute all queued eFuse operations
espefuse.burn_all()
# Check that all eFuses are set properly
espefuse.summary()
# Checks written eFuses
if espefuse.efuses["FLASH_CRYPT_CNT"].get() != 0x7:
raise esptool.FatalError("FLASH_CRYPT_CNT was not set")
if espefuse.efuses["DISABLE_DL_ENCRYPT"].get() != 1:
raise esptool.FatalError("DISABLE_DL_ENCRYPT was not set")
if espefuse.efuses["JTAG_DISABLE"].is_readable() or espefuse.efuses["JTAG_DISABLE"].is_writeable():
raise esptool.FatalError("JTAG_DISABLE should be read and write protected")
Note
Please note that provided example is written for ESP32. For other chips, the names of eFuses might be different and signature of the burn_key
function might also be different.
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.
Upon completion, the new eFuses will be read back, and checks will be performed on the written eFuses by espefuse
. In production, you might need to check that all written eFuses are set properly.
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.
The following section provides a detailed reference for the public API functions.
Init Commands
- espefuse.init_commands(port: str | None = None, baud: int = 115200, before: str = 'default-reset', chip: str = 'auto', esp: esptool.loader.ESPLoader | espefuse.efuse.emulate_efuse_controller_base.EmulateEfuseControllerBase | None = None, **kwargs: Any) BaseCommands
Get the ESP eFuse commands class for the given chip This function will establish a connection to the chip and return the ESP eFuse commands class with initialized chip and eFuse values.
Either esp or port should be provided. If both are provided, esp will be used. If neither is provided, the function will create a mock ESPLoader object for tests.
- Parameters
port – The port to connect to the chip
baud – The baud rate to connect to the chip
before – The reset mode to use before connecting to the chip
chip – The chip to use.
esp – Optional ESPLoader object to use. If provided, the port, baud, before, and chip arguments will be ignored. If provided, user has to take care of closing the port.
- Keyword Arguments
skip_connect (bool) – Whether to skip connecting to the chip. Default is False.
virt (bool) – Whether to use virtual mode. Default is False.
debug (bool) – Whether to enable debug mode. Default is False.
virt_efuse_file (str) – The file to save the eFuse values to. Default is None.
do_not_confirm (bool) – Whether to skip confirmation before burning eFuse. Default is False.
extend_efuse_table (str) – The file to extend the eFuse table from. Default is None.
batch_mode (bool) – Whether to enable batch mode. Default is False.
- Returns
The ESP eFuse commands class
- espefuse.get_esp(port: str | None = None, baud: int = 115200, before: str = 'default-reset', chip: str = 'auto', skip_connect: bool = False, virt: bool = False, debug: bool = False, virt_efuse_file: str | None = None) esptool.loader.ESPLoader | espefuse.efuse.emulate_efuse_controller_base.EmulateEfuseControllerBase
Get the ESPLoader object for the given chip. Uses
esptool.cmds.detect_chip()
function.- Parameters
port – The port to connect to the chip
baud – The baud rate to connect to the chip
before – The reset mode to use before connecting to the chip Supported values are: “default-reset”, “usb-reset”, “no-reset”, “no-reset-no-sync”
chip – The chip to use
skip_connect – Whether to skip connecting to the chip
virt – Whether to use virtual mode
debug – Whether to enable debug mode
virt_efuse_file – The file to save the eFuse values to
- Returns
The ESPLoader object or EmulateEfuseController object
Batch Mode Helpers
- espefuse.BaseCommands.use_batch_mode(self)
Enable batch mode for eFuse operations.
This method increments the batch mode counter, allowing multiple eFuse operations to be queued before burning. All queued operations will be executed together when
burn_all()
is called.This method can be called multiple times to nest batch operations. Each call should be paired with a corresponding call to
burn_all()
.
- espefuse.BaseCommands.burn_all(self, check_batch_mode=False)
Execute all queued eFuse operations and decrement batch mode counter.
This method decrements the batch mode counter, then burns all eFuses that have been queued during batch mode. When the counter reaches zero, batch mode is fully exited.
This method should be called after
use_batch_mode()
to execute the queued operations.- Parameters
check_batch_mode – If True, only execute the burn operation if this is the final burn_all call (i.e., when batch_mode_cnt reaches zero in nested operations).
- Returns
True if the burn operation was successful.
- Return type
bool
Common Read Commands
- espefuse.BaseCommands.summary(self, efuses_to_show: list[str] = [], format: str = 'summary', file: ~typing.TextIO = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>)
Print a human-readable or json summary of eFuse contents.
- Parameters
efuses_to_show – List of eFuse names to show.
format – Format to use for the summary.
file – File to write the summary to.
- espefuse.BaseCommands.dump(self, format: str = 'default', file_name: str | None = None)
Dump raw eFuse data registers.
- Parameters
format – Format to use for the dump. Available options are: - “default”: Print the dump to the console. - “split”: Dump each eFuse block to a separate file. - “joint”: Dump all eFuse blocks to a single file.
file_name – File to write the dump to. If not provided, the dump will be printed to the console.
- espefuse.BaseCommands.get_custom_mac(self)
Get the Custom MAC Address.
- espefuse.BaseCommands.adc_info(self)
Display information about ADC calibration data stored in eFuse.
- espefuse.BaseCommands.check_error(self, recovery: bool = False, do_not_confirm: bool = False)
Check for errors in the eFuse blocks.
- Parameters
recovery – Recovery of BLOCKs after encoding errors.
do_not_confirm – If True, the confirmation will be skipped.
Common Write Commands
- espefuse.BaseCommands.burn_efuse(self, name_value_pairs: dict[str, str], force: bool = False)
Burn eFuses.
- Parameters
name_value_pairs – Dictionary of eFuse names and values to burn.
force – If True, the burn will be performed even if the eFuse settings are incompatible.
- espefuse.BaseCommands.read_protect_efuse(self, efuse_names: list[str])
Disable readback for the eFuse with the specified name.
- Parameters
efuse_names – List of eFuse names to read-protect.
- espefuse.BaseCommands.write_protect_efuse(self, efuse_names: list[str])
Disable writing to the eFuse with the specified name.
- Parameters
efuse_names – List of eFuse names to write-protect.
- espefuse.BaseCommands.burn_block_data(self, block_names: list[str], datafiles: list[BinaryIO], offset: int = 0) None
Burn non-key data to EFUSE blocks.
Don’t use this command to burn key data for Flash Encryption or ESP32 Secure Boot V1, as the byte order of keys is swapped (use burn-key).
- Parameters
block_names – List of eFuse block names to burn data to.
datafiles – List of files to read data from.
offset – Byte offset in the eFuse block to start writing data at.
- espefuse.BaseCommands.burn_bit(self, block: str, bit_number: list[int])
Burn a single bit to the eFuse with the specified name.
- Parameters
block – Name of the eFuse block to burn the bit to.
bit_number – List of bit numbers to burn.
- espefuse.BaseCommands.burn_custom_mac(self, mac: str | bytes)
Burn a 48-bit Custom MAC Address.
- Parameters
mac (str | bytes) – Custom MAC Address to burn. e.g. “aa:cd:ef:11:22:33” or b’ªÍï”3’
- espefuse.BaseCommands.set_flash_voltage(self, voltage: str)
Set the Flash Voltage. Available only for selected chips.
- Parameters
voltage – Voltage to set. Available options are: “1.8V”, “3.3V”, “OFF”
Chip-Specific Commands
- espefuse.efuse.esp32c2.commands.burn_key(self, blocks: list[str], keyfiles: list[BinaryIO], keypurposes: list[str], no_write_protect: bool = False, no_read_protect: bool = False, show_sensitive_info: bool = False, digest: list[bytes] | None = None)
Burn the key block with the specified name. Arguments are groups of block name, key file (containing 128/256 bits of binary key data) and key purpose.
- Parameters
blocks – List of eFuse block names to burn keys to.
keyfiles – List of open files to read key data from.
keypurposes – List of key purposes to burn.
no_write_protect – If True, the write protection will NOT be enabled.
no_read_protect – If True, the read protection will NOT be enabled.
show_sensitive_info – If True, the sensitive information will be shown.
digest – List of digests to burn.
- espefuse.efuse.esp32c2.commands.burn_key_digest(self, keyfile: BinaryIO, no_write_protect: bool = False, no_read_protect: bool = False, show_sensitive_info: bool = False)
Parse a RSA public key and burn the digest to key eFuse block.
- Parameters
keyfile – Open file to read key data from.
no_write_protect – If True, the write protection will NOT be enabled.
no_read_protect – If True, the read protection will NOT be enabled.
show_sensitive_info – If True, the sensitive information will be shown.
eFuse Operations
- espefuse.efuse.base_fields.EfuseFieldBase.get(self, from_read: bool = True) str | int | bytearray
Get a formatted version of the eFuse value, suitable for display
type: int or bool -> int type: bytes -> string “01 02 03 04 05 06 07 08 … “. Byte order [0] … [N]. dump regs: 0x04030201 0x08070605 …
- Parameters
from_read – If True, read the eFuse value from the device. If False, use the cached value.
- Returns
The formatted version of the eFuse value
- Return type
str | int | bytearray
- espefuse.efuse.base_fields.EfuseFieldBase.is_readable(self, blk_part: int | None = None) bool
Check if the eFuse is readable by software
- Parameters
blk_part – The part of the block to check. If None, check all parts.
- Returns
True if the eFuse is readable by software
- Return type
bool
- espefuse.efuse.base_fields.EfuseFieldBase.is_writeable(self) bool
Check if the eFuse is writeable by software
- Returns
True if the eFuse is writeable by software
- Return type
bool
- espefuse.efuse.base_fields.EfuseFieldBase.get_meaning(self, from_read: bool = True) str | int | bytearray
Get the meaning of eFuse from dict if possible, suitable for display
- Parameters
from_read – If True, read the eFuse value from the device. If False, use the cached value.
- Returns
The meaning of the eFuse
- Return type
str | int | bytearray