Changelog¶
All notable changes to this project are documented here. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
v1.3.1 — 2026-10-07¶
Changed¶
- A process holding a port is named by what it runs (
esp-rfc2217-relay,bench,idf_monitor), not asPython.
v1.3.0 — 2026-10-02¶
Added¶
set-nvs --csvsets every key in a CSV without touching the rest of the partition. Bundleset-nvsops take a CSV asfile.
Changed¶
- The progress bar for sector-by-sector writes matches esptool's: sizes in kB/MB and elapsed time.
v1.2.0 — 2026-10-01¶
Added¶
IDFTOOL_PROBE=1probes by default;--no-probeskips it.dump-bundleincludes the bootloader.writeandwrite-bundlecheck that apps and bootloaders are the right kind of image for the chip.--forcewrites them anyway.print-image,print-bundleandapp-infoshow which chip the bootloader is for.- Bundles take
@factory.binand@ota.bin, which flash likefactoryandota. - Bundles take an optional
manifest.json: a name, the target chip, and ops that set NVS keys, edit files, erase partitions and set the boot slot. --hmac-keyreads and writes NVS encrypted with the HMAC key protection scheme.write-bundlechecks every partition name before writing, and writes the partition table only if it differs. A manifest can ask first, or refuse a device whose layout differs.
Changed¶
- Partition names can't start with
@. - Every write skips what's already in flash and rewrites only the sectors that
changed, not just
otaandfactory.write-imageis unchanged.
v1.1.0 — 2026-09-30¶
Added¶
idf.py,esptoolandespefuserun Espressif's tools on the selected device.- Plugins define how devices are named, from custom probing (e.g. eFuse, NVS).
Changed¶
monitorandidf.pyuse-bwhen it's given.
v1.0.0 — 2026-09-29¶
Added¶
- Documentation site.
monitorrunsesp-idf-monitoron the selected device.-m/--macselects a device by MAC address,--usb-serialby USB serial number.otaandfactoryskip writes already in flash, and rewrite only the sectors that changed.--skip-flashedand--diffdo the same for the other write commands.devices --probeadds each port's chip and MAC.portsis an alias.
Changed¶
- The device picker no longer resets boards to list them.
pidentifies the highlighted port's chip;--probeidentifies every port. devicesshows the adapter type, USB serial number and location.- Tables are boxed and coloured on a terminal, and plain Markdown when piped.
- After picking a device, the re-run hint also offers the
-mor--usb-serialform. - Requires
esptool5.4 or later. Supportsesp-idf-nvs-partition-gen0.3.
Fixed¶
write-bundleaccepts a bundle with an empty OTA slot, asdump-bundlesaves one.make installno longer breaks anidftoolthat's already running.
v0.10.0 — 2026-09-10¶
Added¶
app-info(aliasprint-app) prints the app description of a bare application binary.- Without
-p,idftoolasks which device to use.-yskips the prompt. kin the device picker kills a process holding the port (macOS/Linux).idftool.ports.select_device(), with anidentifyhook.
v0.9.0 — 2026-08-27¶
Changed¶
- Breaking.
get_port_listis no longer importable fromidftool.ports. Serial port discovery now comes fromesp-pylib, which publishes both shapes directly:
from esp_pylib.serial_ports import get_port_names # ["/dev/ttyUSB0", ...]
from esp_pylib.serial_ports import get_port_list # ListPortInfo objects
idftool.ports.get_port_list returned names, so get_port_names is the
drop-in replacement. It was removed instead of quietly re-pointed because
esp-pylib's get_port_list returns objects, not names — had the old name been
kept, code would have carried on importing it and failed somewhere further
along, holding a ListPortInfo where it expected a string. prompt_for_port
is unchanged.
-
The likely Espressif device is now offered first wherever ports are listed or guessed:
devicesprints it at the top, the picker highlights it, and connecting without-ptries it before anything else. It used to be sorted to the bottom and tried last, so a machine with other serial devices attached would work through those first. -
Port discovery — which devices are hidden, how they are ranked, and the vid/pid/name/serial filters — is now whatever esp-pylib does, shared with
esptool, rather than a copy maintained here.
Added¶
esp-pylibis now a dependency (>=1.1.4,<2).
v0.8.1 — 2026-08-27¶
Fixed¶
- Dependency pins no longer fight the ESP-IDF Python environment.
idftoolis installed into that env, which pinsesptoolandesp-idf-nvs-partition-genitself — IDF 6.0 and 6.1 both shipesptool5.3.1 and nvs-partition-gen 0.1.9, so the compatible-release pins (~=5.2.0,~=0.2.0) were reported as conflicts on every pip operation there, and IDF wins the tie. Both are now ranges wide enough to hold whatever IDF installs (esptool>=5.2,<6,esp-idf-nvs-partition-gen>=0.1.9,<0.3); the APIidftoolactually uses is present across them. nvs-partition-gen 0.1.9 and 0.2.0 differ only by an error message, and the ceiling stops short of 0.3.0, which replaces the argparse entry pointgenerate(args)is reached through.
v0.8.0 — 2026-08-21¶
Changed¶
- Breaking. Commands that can read either a partition or a file now take
the partition as their first positional argument and the file behind
-f/--file, matchingread/write/erase/view. Commands whose subject can only be a file take-fas well, so there is no per-command exception to remember. Files that are payloads (write-table,factory,ota) or outputs (-o) are unaffected.
| Was | Now |
|---|---|
print-fs image.bin |
print-fs -f image.bin |
print-fs --partition storage |
print-fs storage |
extract-fs image.bin DEST |
extract-fs -f image.bin DEST |
print-image flash.bin |
print-image -f flash.bin |
print-bundle release.zip |
print-bundle -f release.zip |
print-table partitions.csv |
print-table -f partitions.csv |
print-nvs/get-nvs/set-nvs |
PARTITION, or -f FILE |
Passing a file where a partition now belongs is caught before connecting, so
it fails with a usage error naming the -f form rather than a serial
timeout.
- Breaking. convert-table is now create-table, matching the create-X
builder in every other family (create-image, create-bundle, create-nvs,
create-fs). convert-table remains as a command alias; the library function
is idftool.create_table. Its --format option lost its -f short form,
which now means --file everywhere else.
Added¶
- NVS partitions can now be read and edited, not just generated. ESP-IDF's
nvs_partition_gencan only build an image from scratch, soidftoolhas its own NVS parser and entry encoder inidftool.nvs; the encoder is pinned to the generator's output byte for byte by the test suite, blob chunking included. print-nvs(aliaslist-nvs) lists the key/value pairs in a partition on the device, or in an image file with-f.--pagesalso shows the page map.extract-nvs -f image.bin out.csvdumps the contents back to annvs_partition_genCSV, which feeds straight back intocreate-nvs;read-nvs PARTITION out.csvdoes the same from the device.get-nvsprints the value of one or more keys, bare and one per line, for use in scripts.--rawwrites a blob's bytes to stdout.set-nvssets or deletes keys in a partition or image that already exists. The type of an existing key is taken from the entry being replaced, so only a new key needs one spelled out. Changes are appended the way the firmware writes them — the replaced entry is marked erased rather than overwritten — so the rest of the partition is untouched and a device write only re-flashes the 4 KiB pages that changed. With no room left to append, the image is compacted instead, which--rewritealso asks for directly.
Encrypted NVS partitions are not supported yet.
- Every command that writes to flash — write, write-image, write-nvs,
write-fs, write-bundle, factory, ota, set-nvs — now passes esptool's
write_flash options through, as flags (--skip-flashed,
--compress/--no-compress, --encrypt, --force,
--ignore-flash-enc-efuse, --no-progress) and as keyword arguments on the
matching library function, which also reach the ones that take images rather
than a yes/no (diff_with, no_diff_verify, encrypt_files, erase_all).
--skip-flashed is the interesting one: esptool compares the MD5 of what is
already in flash against the data about to be written and skips the writes
that would change nothing, so re-flashing an up-to-date partition costs a
checksum instead of a write. The check is per file, not per sector, so it is
all-or-nothing for each one.
An option esptool does not know is rejected rather than ignored. It reads its
keyword arguments with kwargs.get, so a misspelled one would otherwise be a
write that quietly did something else.
- dump-image learned --size, which reads only the first N bytes instead of the
whole chip. Most of a large flash is erased — everything past the last
partition — and pulling it all back as one serial read is slow and gives a
long transfer more chance to time out.
- write-image learned --no-erase, which writes only what the image contains
instead of erasing the whole chip first. The erase stays on by default —
that is what writing a whole-flash image has always meant here — but it is
mutually exclusive with --skip-flashed, since nothing can already match a
chip that was just wiped. Asking for both is an error rather than a silent
no-op.
Fixed¶
- The
rich-clickrequirement is now>=1.9, which is the version that actually added native command aliases. The declared>=1.8floor allowed 1.8.9 to be installed, where thealiases=kwarg on a command raisesTypeError: Command.__init__() got an unexpected keyword argument 'aliases'at import time, so everyidftoolinvocation failed.
v0.7.0 — 2026-08-18¶
Added¶
- Filesystem partitions can now be built, flashed, listed, and extracted, for all three filesystems ESP-IDF mounts from a data partition — FAT, littlefs, and SPIFFS:
create-fsbuilds an image from a directory, offline. Size and filesystem come from--size/--type, or from--partitionand its subtype.write-fsbuilds an image and flashes it to a named partition. A prebuilt image is passed through instead of rebuilt, the waywrite-nvshandles a prebuilt NVS binary.read-fsreads a partition off the device and extracts it to a directory;extract-fsdoes the same for a local image file.print-fs(aliaslist-fs) lists the contents of an image file or of a partition on the device.
The filesystem is taken from --type if given, else from the partition's
subtype (fat, littlefs, spiffs), else detected from the image, so most
invocations need neither. Per-filesystem options (--fat-sector-size,
--littlefs-name-max, --spiffs-page-size, …) default to ESP-IDF's own
Kconfig defaults and only need setting when the device's sdkconfig differs.
- FAT images are wrapped in ESP-IDF's wear levelling container by default —
how esp_vfs_fat_spiflash_mount_rw_wl expects a fat partition in SPI flash
to look — and unwrapped transparently when read back, including images the
device has since written to, where the filesystem has been shifted and
rotated by the wear levelling layer. --no-fat-wear-levelling produces a
bare image.
- New dependencies: pyfatfs (used through its low-level PyFat API, so its
PyFilesystem2 dependency is never imported) and littlefs-python (the same
library esp_littlefs generates images with). SPIFFS needs no dependency:
ESP-IDF's spiffsgen is vendored, and idftool adds the reader ESP-IDF does
not ship. The backends are imported lazily, so no other command pays for
them at startup.
Changed¶
- The command layer is split out of
idftool/__main__.py, which had grown to ~1,500 lines holding all 25 commands plus every shared helper. Commands now live one family per module underidftool/commands/, with the pieces they share inidftool/cli.py(the command group and global options),idftool/state.py(State/Loaded),idftool/partitions.py,idftool/apps.py,idftool/nvs.py,idftool/ports.py, andidftool/params.py.__main__.pyis now just the entry point.
No behaviour change: every command's --help output is byte-identical, and
the library API (from idftool import State, write_image, …) is unchanged —
it now resolves each name from the module that defines it. Code reaching into
idftool.__main__ for internals (e.g. idftool.__main__.State) must import
from the defining module instead.
v0.6.1 — 2026-08-10¶
Fixed¶
- A flash read that fails while printing the partition table no longer aborts
the command before it starts. The otadata read (used only to mark the active
app partition) now reports a warning and carries on, and unreadable app
slots render as
<READ ERROR>in the table instead of raisingesptool'sFatalErrorout of the printer — requiresesp-idf-defsv0.1.5.
v0.6.0 — 2026-08-02¶
Added¶
idftoolis now importable as a library, esptool-style: every command's logic is exposed as a plain function (write_image,factory,ota,read_partition,write_partitions,dump_bundle, …) that takes aState— which owns the serial connection — plus the same arguments as the CLI command.from idftool import State, write_image, factory, otaworks with no import side effects, letting callers drive several operations over a single connection. The CLI is unchanged: each command is now a thin wrapper around its function.enter-bootloaderwith no-p/--portnow shows an interactive picker of the visible serial ports (best-guess Espressif device first) with a "type manually" escape hatch, since the target may not have appeared yet. Falls back to the usual usage error when stdin isn't a TTY.
Fixed¶
write-bundleno longer crashes with an internalStopIterationwhen flashing a bundle that includes the partition table; it now uses the resolved partition-table offset directly.print-table/listmoved from the Discovery help panel into the Partition table panel.
v0.5.1 — 2026-07-18¶
Fixed¶
- Addressing a partition by a numeric offset that matches no partition now
reports a clear error instead of crashing with an internal
StopIteration(affectsread,write,erase,view,create-image,create-bundle, andcreate-nvs/write-nvs).
v0.5.0 — 2026-07-17¶
Changed¶
- The CLI is rebuilt on click and
rich-click (like
esptool):--help, usage, and errors now render in boxed panels, and commands are grouped into labelled sections. Command names, arguments, options, aliases (reflash,list), and behaviour are unchanged — only the help/usage and error formatting differ.
Added¶
create-tableas an alias forconvert-table.create-nvsandwrite-nvsnow accept a pre-built NVS binary image as input (auto-detected), not only a CSV — it is validated against the partition size and padded to it, instead of failing when parsed as CSV.- A test suite: an offline suite (no hardware) plus a real-device suite that
cycles every command against a connected ESP, with fixtures built from a
small ESP-IDF app (
make test-offline/make test-device).
Fixed¶
write-bundlenow reports a clear error when a bundle entry is larger than its target partition, instead of crashing with anAttributeError.
v0.3.0 — 2026-07-15¶
Added¶
- Partition table file commands that make the table itself the subject
(a positional file), keeping the global
--partition-table-fileoption for its existing role of overriding the layout of other commands: convert-table <in> <out>— convert a partition table between CSV and binary, offline. Format is auto-detected on input and inferred from the output extension (.csv/.bin) or--format.print-table [file]— pretty-print a partition table from a CSV or binary file (offline), or from the device when no file is given.dump-table [output]— read the partition table from the device and save it as CSV or binary (default filename{chip}-{mac}-{timestamp}-partition-table.csv).write-table <file>— flash a partition table from a CSV or binary file to the device. Requires an explicit file (so it can never read the device's current table and write it back to itself), verifies the table before flashing (override with--force), and warns that only the map is replaced — existing partition data is not moved or erased.
Changed¶
listis now an alias ofprint-table(both print the device's partition table identically). Its--partition-type,--partition-subtype, and--partition-nameflags — which were never implemented — have been removed.
Fixed¶
- Working with empty or truncated input files now reports a clear error instead of a confusing parser failure (e.g. "Partition table is missing an end-of-table marker" for a zero-byte image). Empty images, app binaries, and bundle ZIPs are rejected up front, as are images too small to reach the partition table offset and corrupt (non-ZIP) bundles.
- A parsed-but-empty partition table (all-
0xFFbinary region, comment-only CSV, etc.) is now rejected across all parse paths rather than silently yielding a table with no partitions. - Loading a partition table CSV that contains bootloader rows (such as one
produced by
dump-table) no longer requires re-specifying--primary-bootloader-offset: the offset embedded in the CSV is recovered from the file, so dumped tables round-trip throughprint-table,convert-table,write-table, and--partition-table-filewithout extra arguments. The flag still works as an explicit override.
v0.2.2 — 2026-06-15¶
Added¶
print-image— inspect a flash image file offline: prints the partition table and, for each app partition that contains a valid app, the project name, version, IDF version, compile time, ELF SHA256, and target chip.print-bundle— same output for a partition bundle ZIP, driven by the embeddedpartition_table.csvand the per-partition.binentries.
Changed¶
- Bumped
esp-idf-defsto~=0.1.3.print_partition_tablenow takes a(offset, length) -> bytesaccessor instead of anESPLoaderinstance, so the same printing path serves device, image, and bundle inspection.
v0.2.1 — 2026-06-10¶
Changed¶
- Boot partition state now uses
OtaImageStatefor clearer reporting.
v0.2.0 — 2026-05-26¶
Added¶
create-nvsandwrite-nvscommands for generating NVS partition images from CSV.- Rich-style boxed traceback formatter for friendlier error output.
Documentation¶
- README installation section and command index table.
- Recommend
pipxover the standalone binary for installation.
v0.1.0 — 2026-05-25¶
Added¶
- Initial release.