idftool 1.0.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {idftool-1.0.0 → idftool-1.2.0}/CHANGELOG.md +35 -0
  2. {idftool-1.0.0/idftool.egg-info → idftool-1.2.0}/PKG-INFO +14 -7
  3. {idftool-1.0.0 → idftool-1.2.0}/README.md +12 -6
  4. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/backups-and-releases.md +7 -1
  5. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/choosing-a-device.md +17 -1
  6. idftool-1.2.0/docs/guide/plugins.md +57 -0
  7. {idftool-1.0.0 → idftool-1.2.0}/docs/index.md +1 -1
  8. {idftool-1.0.0 → idftool-1.2.0}/docs/library.md +5 -2
  9. idftool-1.2.0/docs/reference/bundles.md +145 -0
  10. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/discovery.md +4 -2
  11. idftool-1.2.0/docs/reference/espefuse.md +18 -0
  12. idftool-1.2.0/docs/reference/esptool.md +17 -0
  13. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/firmware.md +1 -0
  14. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/global-options.md +2 -2
  15. idftool-1.2.0/docs/reference/idf-py.md +21 -0
  16. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/images.md +5 -3
  17. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/index.md +11 -4
  18. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/monitor.md +6 -5
  19. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/nvs.md +14 -3
  20. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/partition-io.md +3 -1
  21. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/write-options.md +5 -5
  22. idftool-1.2.0/examples/identify-plugin/idftool_chip_description.py +7 -0
  23. idftool-1.2.0/examples/identify-plugin/pyproject.toml +15 -0
  24. {idftool-1.0.0 → idftool-1.2.0}/idftool/__init__.py +2 -1
  25. idftool-1.2.0/idftool/apps.py +201 -0
  26. idftool-1.2.0/idftool/bundle.py +456 -0
  27. {idftool-1.0.0 → idftool-1.2.0}/idftool/cli.py +10 -4
  28. {idftool-1.0.0 → idftool-1.2.0}/idftool/commands/apps.py +8 -2
  29. idftool-1.2.0/idftool/commands/bundles.py +364 -0
  30. {idftool-1.0.0 → idftool-1.2.0}/idftool/commands/firmware.py +45 -27
  31. {idftool-1.0.0 → idftool-1.2.0}/idftool/commands/fs.py +47 -1
  32. {idftool-1.0.0 → idftool-1.2.0}/idftool/commands/images.py +3 -2
  33. idftool-1.2.0/idftool/commands/misc.py +308 -0
  34. {idftool-1.0.0 → idftool-1.2.0}/idftool/commands/nvs.py +143 -51
  35. {idftool-1.0.0 → idftool-1.2.0}/idftool/commands/partition_io.py +6 -1
  36. {idftool-1.0.0 → idftool-1.2.0}/idftool/commands/table.py +2 -1
  37. {idftool-1.0.0 → idftool-1.2.0}/idftool/flash.py +7 -3
  38. idftool-1.2.0/idftool/nvs/crypto.py +109 -0
  39. {idftool-1.0.0 → idftool-1.2.0}/idftool/params.py +15 -0
  40. {idftool-1.0.0 → idftool-1.2.0}/idftool/partitions.py +5 -12
  41. idftool-1.2.0/idftool/plugins.py +46 -0
  42. {idftool-1.0.0 → idftool-1.2.0}/idftool/ports.py +17 -8
  43. {idftool-1.0.0 → idftool-1.2.0}/idftool/state.py +39 -30
  44. {idftool-1.0.0 → idftool-1.2.0}/idftool-onedir.spec +6 -2
  45. {idftool-1.0.0 → idftool-1.2.0/idftool.egg-info}/PKG-INFO +14 -7
  46. {idftool-1.0.0 → idftool-1.2.0}/idftool.egg-info/SOURCES.txt +9 -0
  47. {idftool-1.0.0 → idftool-1.2.0}/idftool.egg-info/requires.txt +1 -0
  48. {idftool-1.0.0 → idftool-1.2.0}/idftool.egg-info/scm_file_list.json +9 -0
  49. idftool-1.2.0/idftool.egg-info/scm_version.json +8 -0
  50. {idftool-1.0.0 → idftool-1.2.0}/idftool.spec +6 -2
  51. {idftool-1.0.0 → idftool-1.2.0}/mkdocs.yml +8 -2
  52. {idftool-1.0.0 → idftool-1.2.0}/pyproject.toml +2 -0
  53. {idftool-1.0.0 → idftool-1.2.0}/test/test_device.py +123 -9
  54. {idftool-1.0.0 → idftool-1.2.0}/test/test_flash.py +3 -3
  55. {idftool-1.0.0 → idftool-1.2.0}/test/test_nvs.py +69 -0
  56. {idftool-1.0.0 → idftool-1.2.0}/test/test_offline.py +133 -0
  57. idftool-1.2.0/test/test_virtual.py +837 -0
  58. idftool-1.0.0/docs/reference/bundles.md +0 -78
  59. idftool-1.0.0/idftool/apps.py +0 -66
  60. idftool-1.0.0/idftool/commands/bundles.py +0 -178
  61. idftool-1.0.0/idftool/commands/misc.py +0 -151
  62. idftool-1.0.0/idftool.egg-info/scm_version.json +0 -8
  63. idftool-1.0.0/test/test_virtual.py +0 -404
  64. {idftool-1.0.0 → idftool-1.2.0}/.github/workflows/build.yml +0 -0
  65. {idftool-1.0.0 → idftool-1.2.0}/.github/workflows/ci.yml +0 -0
  66. {idftool-1.0.0 → idftool-1.2.0}/.github/workflows/docs.yml +0 -0
  67. {idftool-1.0.0 → idftool-1.2.0}/.github/workflows/publish.yml +0 -0
  68. {idftool-1.0.0 → idftool-1.2.0}/.gitignore +0 -0
  69. {idftool-1.0.0 → idftool-1.2.0}/CONTRIBUTING.md +0 -0
  70. {idftool-1.0.0 → idftool-1.2.0}/LICENSE.md +0 -0
  71. {idftool-1.0.0 → idftool-1.2.0}/Makefile +0 -0
  72. {idftool-1.0.0 → idftool-1.2.0}/docs/changelog.md +0 -0
  73. {idftool-1.0.0 → idftool-1.2.0}/docs/contributing.md +0 -0
  74. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/flashing-firmware.md +0 -0
  75. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/getting-started.md +0 -0
  76. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/monitoring.md +0 -0
  77. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/partition-addressing.md +0 -0
  78. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/partitions-or-files.md +0 -0
  79. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/provisioning.md +0 -0
  80. {idftool-1.0.0 → idftool-1.2.0}/docs/guide/scripting.md +0 -0
  81. {idftool-1.0.0 → idftool-1.2.0}/docs/installation.md +0 -0
  82. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/filesystems.md +0 -0
  83. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/misc.md +0 -0
  84. {idftool-1.0.0 → idftool-1.2.0}/docs/reference/partition-table.md +0 -0
  85. {idftool-1.0.0 → idftool-1.2.0}/docs/stylesheets/extra.css +0 -0
  86. {idftool-1.0.0 → idftool-1.2.0}/idftool/__main__.py +0 -0
  87. {idftool-1.0.0 → idftool-1.2.0}/idftool/commands/__init__.py +0 -0
  88. {idftool-1.0.0 → idftool-1.2.0}/idftool/display.py +0 -0
  89. {idftool-1.0.0 → idftool-1.2.0}/idftool/fs/__init__.py +0 -0
  90. {idftool-1.0.0 → idftool-1.2.0}/idftool/fs/common.py +0 -0
  91. {idftool-1.0.0 → idftool-1.2.0}/idftool/fs/fatfs.py +0 -0
  92. {idftool-1.0.0 → idftool-1.2.0}/idftool/fs/littlefs.py +0 -0
  93. {idftool-1.0.0 → idftool-1.2.0}/idftool/fs/spiffs.py +0 -0
  94. {idftool-1.0.0 → idftool-1.2.0}/idftool/fs/spiffsgen.py +0 -0
  95. {idftool-1.0.0 → idftool-1.2.0}/idftool/fs/wl.py +0 -0
  96. {idftool-1.0.0 → idftool-1.2.0}/idftool/nvs/__init__.py +0 -0
  97. {idftool-1.0.0 → idftool-1.2.0}/idftool/nvs/common.py +0 -0
  98. {idftool-1.0.0 → idftool-1.2.0}/idftool/nvs/edit.py +0 -0
  99. {idftool-1.0.0 → idftool-1.2.0}/idftool/nvs/parser.py +0 -0
  100. {idftool-1.0.0 → idftool-1.2.0}/idftool/traceback.py +0 -0
  101. {idftool-1.0.0 → idftool-1.2.0}/idftool.egg-info/dependency_links.txt +0 -0
  102. {idftool-1.0.0 → idftool-1.2.0}/idftool.egg-info/entry_points.txt +0 -0
  103. {idftool-1.0.0 → idftool-1.2.0}/idftool.egg-info/top_level.txt +0 -0
  104. {idftool-1.0.0 → idftool-1.2.0}/idftool.py +0 -0
  105. {idftool-1.0.0 → idftool-1.2.0}/setup.cfg +0 -0
  106. {idftool-1.0.0 → idftool-1.2.0}/test/README.md +0 -0
  107. {idftool-1.0.0 → idftool-1.2.0}/test/conftest.py +0 -0
  108. {idftool-1.0.0 → idftool-1.2.0}/test/fixtures/README.md +0 -0
  109. {idftool-1.0.0 → idftool-1.2.0}/test/fixtures/esp32s3/app-v1.bin +0 -0
  110. {idftool-1.0.0 → idftool-1.2.0}/test/fixtures/esp32s3/app-v2.bin +0 -0
  111. {idftool-1.0.0 → idftool-1.2.0}/test/fixtures/esp32s3/bootloader.bin +0 -0
  112. {idftool-1.0.0 → idftool-1.2.0}/test/fixtures/esp32s3/flash-image.bin +0 -0
  113. {idftool-1.0.0 → idftool-1.2.0}/test/fixtures/esp32s3/nvs.csv +0 -0
  114. {idftool-1.0.0 → idftool-1.2.0}/test/fixtures/esp32s3/partition-table.bin +0 -0
  115. {idftool-1.0.0 → idftool-1.2.0}/test/fixtures/esp32s3/partitions.csv +0 -0
  116. {idftool-1.0.0 → idftool-1.2.0}/test/project/.gitignore +0 -0
  117. {idftool-1.0.0 → idftool-1.2.0}/test/project/CMakeLists.txt +0 -0
  118. {idftool-1.0.0 → idftool-1.2.0}/test/project/README.md +0 -0
  119. {idftool-1.0.0 → idftool-1.2.0}/test/project/build_fixtures.sh +0 -0
  120. {idftool-1.0.0 → idftool-1.2.0}/test/project/main/CMakeLists.txt +0 -0
  121. {idftool-1.0.0 → idftool-1.2.0}/test/project/main/idf_component.yml +0 -0
  122. {idftool-1.0.0 → idftool-1.2.0}/test/project/main/main.c +0 -0
  123. {idftool-1.0.0 → idftool-1.2.0}/test/project/nvs.csv +0 -0
  124. {idftool-1.0.0 → idftool-1.2.0}/test/project/partitions.csv +0 -0
  125. {idftool-1.0.0 → idftool-1.2.0}/test/project/sdkconfig.defaults +0 -0
  126. {idftool-1.0.0 → idftool-1.2.0}/test/samples/bootloader-template.csv +0 -0
  127. {idftool-1.0.0 → idftool-1.2.0}/test/samples/nvs.csv +0 -0
  128. {idftool-1.0.0 → idftool-1.2.0}/test/samples/partitions.csv +0 -0
@@ -4,6 +4,39 @@ All notable changes to this project are documented here. The format is
4
4
  based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
5
5
  this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [v1.2.0] — 2026-10-01
8
+
9
+ ### Added
10
+ - `IDFTOOL_PROBE=1` probes by default; `--no-probe` skips it.
11
+ - `dump-bundle` includes the bootloader.
12
+ - `write` and `write-bundle` check that apps and bootloaders are the right kind
13
+ of image for the chip. `--force` writes them anyway.
14
+ - `print-image`, `print-bundle` and `app-info` show which chip the bootloader
15
+ is for.
16
+ - Bundles take `@factory.bin` and `@ota.bin`, which flash like `factory` and
17
+ `ota`.
18
+ - Bundles take an optional `manifest.json`: a name, the target chip, and ops
19
+ that set NVS keys, edit files, erase partitions and set the boot slot.
20
+ - `--hmac-key` reads and writes NVS encrypted with the HMAC key protection
21
+ scheme.
22
+ - `write-bundle` checks every partition name before writing, and writes the
23
+ partition table only if it differs. A manifest can ask first, or refuse a
24
+ device whose layout differs.
25
+
26
+ ### Changed
27
+ - Partition names can't start with `@`.
28
+ - Every write skips what's already in flash and rewrites only the sectors that
29
+ changed, not just `ota` and `factory`. `write-image` is unchanged.
30
+
31
+ ## [v1.1.0] — 2026-09-30
32
+
33
+ ### Added
34
+ - `idf.py`, `esptool` and `espefuse` run Espressif's tools on the selected device.
35
+ - Plugins define how devices are named, from custom probing (e.g. eFuse, NVS).
36
+
37
+ ### Changed
38
+ - `monitor` and `idf.py` use `-b` when it's given.
39
+
7
40
  ## [v1.0.0] — 2026-09-29
8
41
 
9
42
  ### Added
@@ -367,6 +400,8 @@ this project adheres to [Semantic Versioning](https://semver.org/).
367
400
  ### Added
368
401
  - Initial release.
369
402
 
403
+ [v1.2.0]: https://github.com/nebkat/idftool/releases/tag/v1.2.0
404
+ [v1.1.0]: https://github.com/nebkat/idftool/releases/tag/v1.1.0
370
405
  [v1.0.0]: https://github.com/nebkat/idftool/releases/tag/v1.0.0
371
406
  [v0.10.0]: https://github.com/nebkat/idftool/releases/tag/v0.10.0
372
407
  [v0.9.0]: https://github.com/nebkat/idftool/releases/tag/v0.9.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: idftool
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: The ultimate CLI tool for interacting with Espressif devices
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Source, https://github.com/nebkat/idftool
@@ -14,6 +14,7 @@ Requires-Dist: esptool<6,>=5.4
14
14
  Requires-Dist: esp-idf-defs~=0.1.5
15
15
  Requires-Dist: esp-pylib<2,>=1.1.4
16
16
  Requires-Dist: esp-idf-nvs-partition-gen<0.4,>=0.1.9
17
+ Requires-Dist: cryptography>=3.1
17
18
  Requires-Dist: esp-idf-monitor<2,>=1.4
18
19
  Requires-Dist: pyfatfs~=1.1
19
20
  Requires-Dist: littlefs-python~=0.19
@@ -62,7 +63,7 @@ No more guessing which device is on which port. Run any command without `-p`
62
63
  and `idftool` lists the connected devices and asks which to use:
63
64
 
64
65
  ```console
65
- $ idftool ota build/app.bin
66
+ $ idftool monitor
66
67
  ? Select device (↑↓ move · ↵ select · p probe · r refresh · k kill · q quit)
67
68
  » /dev/cu.usbmodem11401 ESP USB-Serial/JTAG 7c:2c:67:92:79:c0
68
69
  /dev/cu.usbmodem2101 ESP USB-Serial/JTAG b8:f8:62:49:ea:14
@@ -76,15 +77,21 @@ Pick one, and it tells you how to skip the question next time:
76
77
 
77
78
  ```console
78
79
  Device: /dev/cu.usbmodem11401 — ESP USB-Serial/JTAG · 7c:2c:67:92:79:c0
79
- Re-run with: idftool -p /dev/cu.usbmodem11401 ota build/app.bin
80
- or: idftool -m 7c:2c:67:92:79:c0 ota build/app.bin
81
- ...
82
- Writing 'my-app v1.2.0' to partition 'ota_1'...
83
- Setting boot partition to 'ota_1'...
80
+ Re-run with: idftool -p /dev/cu.usbmodem11401 monitor
81
+ or: idftool -m 7c:2c:67:92:79:c0 monitor
82
+ --- esp-idf-monitor 1.10.0 on /dev/cu.usbmodem11401 115200
83
+ --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
84
84
  ```
85
85
 
86
86
  The `-m` form finds the board by its MAC address, wherever it's plugged in.
87
87
 
88
+ It works with `idf.py` too. Alias it, and every `flash` or `monitor` asks which
89
+ device to use:
90
+
91
+ ```bash
92
+ alias idf.py='idftool idf.py'
93
+ ```
94
+
88
95
  ### Firmware flashing
89
96
 
90
97
  Flash to the default (factory) or next available (OTA) app partition, with no
@@ -33,7 +33,7 @@ No more guessing which device is on which port. Run any command without `-p`
33
33
  and `idftool` lists the connected devices and asks which to use:
34
34
 
35
35
  ```console
36
- $ idftool ota build/app.bin
36
+ $ idftool monitor
37
37
  ? Select device (↑↓ move · ↵ select · p probe · r refresh · k kill · q quit)
38
38
  » /dev/cu.usbmodem11401 ESP USB-Serial/JTAG 7c:2c:67:92:79:c0
39
39
  /dev/cu.usbmodem2101 ESP USB-Serial/JTAG b8:f8:62:49:ea:14
@@ -47,15 +47,21 @@ Pick one, and it tells you how to skip the question next time:
47
47
 
48
48
  ```console
49
49
  Device: /dev/cu.usbmodem11401 — ESP USB-Serial/JTAG · 7c:2c:67:92:79:c0
50
- Re-run with: idftool -p /dev/cu.usbmodem11401 ota build/app.bin
51
- or: idftool -m 7c:2c:67:92:79:c0 ota build/app.bin
52
- ...
53
- Writing 'my-app v1.2.0' to partition 'ota_1'...
54
- Setting boot partition to 'ota_1'...
50
+ Re-run with: idftool -p /dev/cu.usbmodem11401 monitor
51
+ or: idftool -m 7c:2c:67:92:79:c0 monitor
52
+ --- esp-idf-monitor 1.10.0 on /dev/cu.usbmodem11401 115200
53
+ --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
55
54
  ```
56
55
 
57
56
  The `-m` form finds the board by its MAC address, wherever it's plugged in.
58
57
 
58
+ It works with `idf.py` too. Alias it, and every `flash` or `monitor` asks which
59
+ device to use:
60
+
61
+ ```bash
62
+ alias idf.py='idftool idf.py'
63
+ ```
64
+
59
65
  ### Firmware flashing
60
66
 
61
67
  Flash to the default (factory) or next available (OTA) app partition, with no
@@ -34,7 +34,8 @@ This reads the whole chip, which can take a long time on a large flash.
34
34
  idftool write-bundle esp32-s3-7c2c679279c0-20260929-143000.zip
35
35
  ```
36
36
 
37
- This writes every partition and the partition table from the bundle.
37
+ This writes every partition, the bootloader and the partition table from the
38
+ bundle, skipping what's already there.
38
39
 
39
40
  From an image:
40
41
 
@@ -71,6 +72,11 @@ idftool write-bundle release.zip
71
72
  `--flash-partition-table` includes the partition table, so a device with an
72
73
  older layout is updated to match.
73
74
 
75
+ For a field update, name the app `@ota` instead of a partition, so it goes to
76
+ whichever OTA slot is next. A [manifest](../reference/bundles.md#manifest) can
77
+ also set NVS keys, edit files, and control when the partition table may
78
+ change.
79
+
74
80
  ## Clone a device
75
81
 
76
82
  Back up one board and restore the bundle onto another:
@@ -40,7 +40,8 @@ The list comes from USB alone, so no board is reset by looking at it:
40
40
  | ++q++ | Quit |
41
41
 
42
42
  `--probe` connects to every port up front instead, so the chip column is filled
43
- for all of them.
43
+ for all of them. Set `IDFTOOL_PROBE=1` to make that the default, and pass
44
+ `--no-probe` to skip it for one run.
44
45
 
45
46
  After you pick, `idftool` prints how to skip the picker next time:
46
47
 
@@ -87,3 +88,18 @@ On Windows, FTDI's driver adds a channel letter to the serial number
87
88
 
88
89
  For boards on their built-in USB port the serial number is the MAC, so `-m` and
89
90
  `--usb-serial` find the same port.
91
+
92
+ ## With `idf.py`
93
+
94
+ [`idftool idf.py`](../reference/idf-py.md) runs ESP-IDF's `idf.py` and asks for
95
+ a device when the actions need one:
96
+
97
+ ```bash
98
+ idftool idf.py build flash monitor
99
+ ```
100
+
101
+ Alias it to put every `idf.py` call through the device list:
102
+
103
+ ```bash
104
+ alias idf.py='idftool idf.py'
105
+ ```
@@ -0,0 +1,57 @@
1
+ # Plugins
2
+
3
+ A plugin names devices your own way, for example from your eFuse fields or a
4
+ config partition. It's an installed Python package that registers an
5
+ `identify` function:
6
+
7
+ ```toml title="pyproject.toml"
8
+ [project.entry-points."idftool.identify"]
9
+ chip-description = "idftool_chip_description:identify"
10
+ ```
11
+
12
+ ```python title="idftool_chip_description.py"
13
+ def identify(esp):
14
+ return esp.get_chip_description()
15
+ ```
16
+
17
+ This is the full example in
18
+ [`examples/identify-plugin`](https://github.com/nebkat/idftool/tree/main/examples/identify-plugin).
19
+ Install it next to `idftool` and probe:
20
+
21
+ ```console
22
+ $ pip install ./examples/identify-plugin
23
+ $ idftool devices --probe
24
+ Port Device Chip MAC
25
+ /dev/cu.usbmodem11201 ESP32-S3 (QFN56) (revision v0.2) ESP32-S3 90:e5:b1:cd:47:bc
26
+ ```
27
+
28
+ ## When it runs
29
+
30
+ `identify` is called with a connected `ESPLoader` whenever a board is probed:
31
+ `--probe`, or ++p++ in the [picker](choosing-a-device.md#the-picker). It never
32
+ runs otherwise, so installing a plugin resets no extra boards.
33
+
34
+ The loader is esptool's ROM loader. It reads eFuses and registers, but on most
35
+ chips not flash. For that, start the stub first:
36
+
37
+ ```python
38
+ def identify(esp):
39
+ esp = esp if esp.IS_STUB else esp.run_stub()
40
+ data = esp.read_flash(0x9000, 0x1000)
41
+ ...
42
+ ```
43
+
44
+ ## What it returns
45
+
46
+ A string naming the device, shown in place of the chip name. `None` means
47
+ "not one of mine": the next plugin gets a turn, then the chip name is shown.
48
+
49
+ Plugins run in order of entry point name, and the first one that isn't `None`
50
+ wins. A plugin that fails to import or raises is shown as the device's error,
51
+ unless another plugin names it.
52
+
53
+ ## Standalone binaries
54
+
55
+ Plugins load from the Python environment `idftool` is installed in, so they
56
+ don't reach the standalone binaries. Install `idftool` with `pip` or `pipx`
57
+ (`pipx inject idftool ./my-plugin`) to use them.
@@ -102,7 +102,7 @@ with. `idftool` does that for you.
102
102
  Run any command without `-p`, and `idftool` asks which device to use:
103
103
 
104
104
  ```console
105
- $ idftool ota build/my-app.bin
105
+ $ idftool monitor
106
106
  ? Select device (↑↓ move · ↵ select · p probe · r refresh · k kill · q quit)
107
107
  » /dev/cu.usbmodem11401 ESP USB-Serial/JTAG 7c:2c:67:92:79:c0
108
108
  /dev/cu.usbmodem2101 ESP USB-Serial/JTAG b8:f8:62:49:ea:14
@@ -67,8 +67,11 @@ state.port = found["port"]
67
67
  ```
68
68
 
69
69
  `identify` needs a connection, so every port is probed (and reset) when it's
70
- given. If it raises, the device is still listed and usable, with the error
71
- next to it.
70
+ given, unless `probe=False`: then it runs only on ports probed with `p`. If it
71
+ raises, the device is still listed and usable, with the error next to it.
72
+
73
+ `State(identify=...)` uses it in the picker the same way as the CLI uses
74
+ [plugins](guide/plugins.md).
72
75
 
73
76
  ## Available functions
74
77
 
@@ -0,0 +1,145 @@
1
+ # Bundles
2
+
3
+ A bundle is a ZIP whose filenames say what to flash. Use them to hand a build
4
+ from CI to whoever flashes it, or to archive exactly what shipped.
5
+
6
+ | File | Flashing it |
7
+ |------|-------------|
8
+ | `partition_table.csv` or `.bin` | Replaces the partition table, if it differs. Written first. |
9
+ | `bootloader.bin` | Writes the bootloader. |
10
+ | `@factory.bin` | Flashes the app to the factory partition (or `ota_0`) and clears otadata, like [`factory`](firmware.md#factory). |
11
+ | `@ota.bin` | Writes the app to the next OTA slot and boots it, like [`ota`](firmware.md#ota). |
12
+ | `<name>.bin` | Writes the partition called `name`. |
13
+ | `manifest.json` | Optional: a name, the target chip, table rules and extra ops. See [Manifest](#manifest). |
14
+
15
+ ```text
16
+ release.zip
17
+ ├── partition_table.csv
18
+ ├── @ota.bin
19
+ └── storage.bin
20
+ ```
21
+
22
+ They're written in that order. A bundle can't have both `@factory.bin` and
23
+ `@ota.bin`, or name a partition the role file may write. Partition names can't
24
+ start with `@`. Files in subdirectories are only used by manifest ops.
25
+
26
+ Before anything is written, every app and bootloader is checked against the
27
+ chip, and every partition the bundle names must exist in the table it will
28
+ meet: the bundle's own, else the device's.
29
+
30
+ ## Manifest
31
+
32
+ ```json
33
+ {
34
+ "name": "MS5 v0.17.0",
35
+ "description": "Field update: new app, reset the channel",
36
+ "chip": "esp32s3",
37
+ "ops": [
38
+ {"op": "set-nvs", "partition": "nvs_cfg", "set": {"cfg:channel": "string:stable"}},
39
+ {"op": "edit-fs", "partition": "storage", "put": {"/config.json": "files/config.json"}},
40
+ {"op": "clear-boot"}
41
+ ]
42
+ }
43
+ ```
44
+
45
+ All fields are optional. `chip` is checked against the device. `ops` run in
46
+ order after the files:
47
+
48
+ | Op | Fields | Does |
49
+ |----|--------|------|
50
+ | `write` | `partition`, `file` | Write a file to a partition. |
51
+ | `erase` | `partition` | Erase a partition. |
52
+ | `write-fs` | `partition`, `file` | Write a filesystem image. |
53
+ | `edit-fs` | `partition`, `put`, `delete` | Put files (path → file in the bundle) in the filesystem, and delete paths. |
54
+ | `set-nvs` | `partition`, `set`, `delete` | Set keys (`ns:key` → `type:value`, or a bare value for a key that exists) and delete them. Without `partition`, the first NVS partition. |
55
+ | `set-boot` | `partition` | Boot that OTA slot next. |
56
+ | `clear-boot` | | Clear otadata so the factory app boots. |
57
+
58
+ ### Partition table rules
59
+
60
+ A table that already matches the device's isn't written. `table` says what
61
+ happens when it differs:
62
+
63
+ - `update` (default): write it.
64
+ - `ask`: show the differences and ask. `-y` answers yes.
65
+ - `require`: never write it, and refuse the device.
66
+
67
+ `tableMatch` says which differences count: `exact` (default) for any, or `used`
68
+ for only the partitions the bundle writes, erases, edits or boots. With `used`,
69
+ a device that differs elsewhere keeps its own table.
70
+
71
+ ## `create-bundle`
72
+
73
+ Pack partition binaries into a bundle. `@factory` and `@ota` add a role file.
74
+ `--flash-partition-table` includes the partition table CSV so
75
+ [`write-bundle`](#write-bundle) flashes it too. `--manifest` adds a
76
+ `manifest.json` and the files its ops name, relative to it.
77
+
78
+ ```bash
79
+ idftool --partition-table-file partitions.csv create-bundle \
80
+ -o release.zip --flash-partition-table \
81
+ ota_0 build/app.bin storage build/spiffs.bin
82
+ ```
83
+
84
+ ## `dump-bundle`
85
+
86
+ Read every partition off the device into a bundle, always with
87
+ `bootloader.bin` and `partition_table.csv`. Without a filename it's named
88
+ `{chip}-{mac}-{timestamp}.zip`.
89
+
90
+ ```bash
91
+ idftool dump-bundle # auto-named
92
+ idftool dump-bundle my-backup.zip
93
+ ```
94
+
95
+ ## `write-bundle`
96
+
97
+ Flash a bundle. It prints the steps first, then checks everything against the
98
+ device before the first write. `--hmac-key` is the key for an
99
+ [encrypted NVS](nvs.md#encrypted-nvs) partition that a `set-nvs` op edits;
100
+ bundles never carry keys.
101
+
102
+ ```bash
103
+ idftool write-bundle release.zip
104
+ ```
105
+
106
+ Takes the [write options](write-options.md).
107
+
108
+ ## `print-bundle`
109
+
110
+ Inspect a bundle without a device: its manifest, the steps flashing it takes,
111
+ its bootloader's chip, its partition table if it has one, and the app
112
+ descriptions.
113
+
114
+ ```console
115
+ $ idftool print-bundle -f release.zip
116
+ Bundle: release.zip (0x2828d bytes)
117
+ Partition table: partition_table.csv
118
+ Steps:
119
+ 1. Write the partition table (partition_table.csv) if it differs
120
+ 2. Write ota_0.bin to partition 'ota_0'
121
+ Partitions included: ota_0
122
+
123
+ Bootloader: none
124
+
125
+ ╭──────────┬──────┬─────────┬─────────┬──────┬───────────────╮
126
+ │ Name │ Type │ Subtype │ Offset │ Size │ App │
127
+ ├──────────┼──────┼─────────┼─────────┼──────┼───────────────┤
128
+ │ nvs │ data │ nvs │ 0x9000 │ 24K │ │
129
+ │ otadata │ data │ ota │ 0xf000 │ 8K │ │
130
+ │ phy_init │ data │ phy │ 0x11000 │ 4K │ │
131
+ │ factory │ app │ factory │ 0x20000 │ 192K │ empty │
132
+ │ ota_0 │ app │ ota_0 │ 0x50000 │ 192K │ my-app 1.2.0 │
133
+ │ ota_1 │ app │ ota_1 │ 0x80000 │ 192K │ empty │
134
+ │ storage │ data │ spiffs │ 0xb0000 │ 64K │ │
135
+ ╰──────────┴──────┴─────────┴─────────┴──────┴───────────────╯
136
+
137
+ Partition 'ota_0' (offset=0x50000):
138
+ Project name: my-app
139
+ Version: 1.2.0
140
+ IDF version: v6.0.2
141
+ Secure version: 0
142
+ Compiled: Sep 29 2026 14:30:00
143
+ ELF SHA256: 4fd8f31259d82483f0fc34d4422b1aefb6ead9da845395d5ea96a135ed32c78a
144
+ Chip: ESP32S3 (rev 0 to 99)
145
+ ```
@@ -33,10 +33,12 @@ Location
33
33
  Piped, it prints plain space-separated columns instead of a box, for `awk` and
34
34
  friends.
35
35
 
36
- ### `--probe`
36
+ ### `--probe`, `--no-probe`
37
37
 
38
38
  Connect to every port to add each board's chip and MAC, including boards behind
39
- a USB-serial adapter. This resets the boards.
39
+ a USB-serial adapter. This resets the boards. With a
40
+ [plugin](../guide/plugins.md) installed, a Device column shows what it names
41
+ each board. Defaults to the global `--probe`, or `IDFTOOL_PROBE`.
40
42
 
41
43
  ```console
42
44
  $ idftool devices --probe
@@ -0,0 +1,18 @@
1
+ # espefuse
2
+
3
+ ## `espefuse`
4
+
5
+ Run [`espefuse`](https://docs.espressif.com/projects/esptool/en/latest/esp32/espefuse/)
6
+ using `idftool`'s [device selection](../guide/choosing-a-device.md).
7
+
8
+ ```bash
9
+ idftool espefuse summary
10
+ idftool -m 9c:13:9e:1b:d4:6c espefuse summary --format json
11
+ ```
12
+
13
+ Everything after `espefuse` goes to `espefuse`. A `-p` given there is used
14
+ as-is, without the picker. `-b` and `--no-reset`, if given, are passed on as
15
+ `--baud` and `--after no-reset`.
16
+
17
+ `-y` only skips the picker. `espefuse` still asks before burning; pass it
18
+ `--do-not-confirm` to skip that.
@@ -0,0 +1,17 @@
1
+ # esptool
2
+
3
+ ## `esptool`
4
+
5
+ Run [`esptool`](https://docs.espressif.com/projects/esptool/en/latest/esp32/)
6
+ using `idftool`'s [device selection](../guide/choosing-a-device.md).
7
+ Commands that don't need a device, like `merge-bin` or `image-info`, run
8
+ straight away.
9
+
10
+ ```bash
11
+ idftool esptool chip-id
12
+ idftool -m 9c:13:9e:1b:d4:6c esptool read-flash 0 0x1000 boot.bin
13
+ ```
14
+
15
+ Everything after `esptool` goes to `esptool`. A `-p` or `--port-filter` given
16
+ there is used as-is, without the picker. `-b` and `--no-reset`, if given, are
17
+ passed on as `--baud` and `--after no-reset`.
@@ -68,6 +68,7 @@ Print the app description of a bare application binary, without a device:
68
68
  project name, version, IDF version, compile time, ELF SHA-256, and target
69
69
  chip. The same block [`print-image`](images.md#print-image) and
70
70
  [`print-bundle`](bundles.md#print-bundle) print for each app partition.
71
+ Given a bootloader, it prints which chip it's for.
71
72
  Alias: `print-app`.
72
73
 
73
74
  ```console
@@ -11,8 +11,8 @@ idftool -p /dev/cu.usbmodem1101 --no-reset get-boot
11
11
  | `-p`, `--port PATH` | Serial port. Without it, `idftool` asks; see [Choosing a device](../guide/choosing-a-device.md). |
12
12
  | `-m`, `--mac MAC` | Use the device with this MAC address. See [by MAC address](../guide/choosing-a-device.md#by-mac-address). |
13
13
  | `--usb-serial SERIAL` | Use the port with this USB serial number. See [by USB serial number](../guide/choosing-a-device.md#by-usb-serial-number). |
14
- | `-b`, `--baud N` | Serial baud rate for flashing. Default 115200, the ROM bootloader's rate. See [serial connection](https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/serial-connection.html). |
15
- | `--probe` | Connect to every port to identify its board. Resets them. |
14
+ | `-b`, `--baud N` | Serial baud rate for flashing. Default 115200, the ROM bootloader's rate. See [serial connection](https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/serial-connection.html). If given, also passed to [`monitor`](monitor.md#monitor) and [`idf.py`](idf-py.md#idfpy). |
15
+ | `--probe`, `--no-probe` | Connect to every port to identify its board. Resets them. Defaults to `IDFTOOL_PROBE` (`1` to probe). |
16
16
  | `-y`, `--yes` | Don't ask which device to use. |
17
17
  | `--no-reset` | Skip the [hard reset](https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/advanced-options.html#reset-after-operation-after) after a command. Also passed to [`monitor`](monitor.md#monitor). |
18
18
  | `--partition-table-file PATH` | Use a CSV or binary partition table from disk instead of reading the device's. |
@@ -0,0 +1,21 @@
1
+ # idf.py
2
+
3
+ ## `idf.py`
4
+
5
+ Run [`idf.py`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-py.html)
6
+ using `idftool`'s [device selection](../guide/choosing-a-device.md).
7
+ Actions that don't need a device, like `build` or `menuconfig`, run straight
8
+ away.
9
+
10
+ ```bash
11
+ idftool idf.py build flash monitor
12
+ idftool -m 9c:13:9e:1b:d4:6c idf.py app-flash
13
+ ```
14
+
15
+ `-b`, if given, is passed on as `idf.py -b`, the flashing baud rate.
16
+
17
+ Alias it to run every `idf.py` through `idftool`:
18
+
19
+ ```bash
20
+ alias idf.py='idftool idf.py'
21
+ ```
@@ -64,14 +64,16 @@ Takes the [write options](write-options.md).
64
64
 
65
65
  ## `print-image`
66
66
 
67
- Inspect a flash image without a device: the partition table inside it and, for
68
- each app partition holding a valid app, its project name, version, IDF version,
69
- compile time, ELF SHA-256, and target chip.
67
+ Inspect a flash image without a device: which chip its bootloader is for, the
68
+ partition table inside it and, for each app partition holding a valid app, its
69
+ project name, version, IDF version, compile time, ELF SHA-256, and target chip.
70
70
 
71
71
  ```console
72
72
  $ idftool print-image -f build/full-flash.img
73
73
  Image: build/full-flash.img (0xc0000 bytes)
74
74
 
75
+ Bootloader: ESP32S3 (offset=0x0)
76
+
75
77
  ╭──────────┬──────┬─────────┬─────────┬──────┬───────────────╮
76
78
  │ Name │ Type │ Subtype │ Offset │ Size │ App │
77
79
  ├──────────┼──────┼─────────┼─────────┼──────┼───────────────┤
@@ -15,8 +15,6 @@ options. The options shared by every command are under
15
15
  | [`set-boot`](firmware.md#set-boot) | Force the next boot to a specific OTA partition |
16
16
  | [`clear-boot`](firmware.md#clear-boot) | Erase otadata and let the bootloader fall back |
17
17
  | [`app-info`](firmware.md#app-info) | Print the app description from an application binary |
18
- | **[Monitor](monitor.md)** | |
19
- | [`monitor`](monitor.md#monitor) | Open [`esp-idf-monitor`](https://github.com/espressif/esp-idf-monitor) on the selected device |
20
18
  | **[Partition I/O](partition-io.md)** | |
21
19
  | [`read`](partition-io.md#read) | Read a partition (or slice) into a file |
22
20
  | [`write`](partition-io.md#write) | Write one or more files to named partitions |
@@ -30,8 +28,8 @@ options. The options shared by every command are under
30
28
  | **[Bundles](bundles.md)** | |
31
29
  | [`create-bundle`](bundles.md#create-bundle) | Pack partition images into a ZIP bundle |
32
30
  | [`dump-bundle`](bundles.md#dump-bundle) | Pack every partition from the device into a ZIP |
33
- | [`write-bundle`](bundles.md#write-bundle) | Flash every binary in a bundle ZIP |
34
- | [`print-bundle`](bundles.md#print-bundle) | Print partition table and app info from a bundle ZIP |
31
+ | [`write-bundle`](bundles.md#write-bundle) | Flash a bundle ZIP |
32
+ | [`print-bundle`](bundles.md#print-bundle) | Print what a bundle ZIP holds and what flashing it does |
35
33
  | **[Partition table](partition-table.md)** | |
36
34
  | [`print-table`](partition-table.md#print-table) | Print a partition table from a file or the device (alias `list`) |
37
35
  | [`create-table`](partition-table.md#create-table) | Convert a partition table between CSV and binary |
@@ -53,3 +51,12 @@ options. The options shared by every command are under
53
51
  | [`print-fs`](filesystems.md#print-fs) | List the contents of a filesystem partition or image |
54
52
  | **[Misc](misc.md)** | |
55
53
  | [`enter-bootloader`](misc.md#enter-bootloader) | Drop the chip into ROM bootloader mode |
54
+
55
+ ## Tools
56
+
57
+ Espressif's tools, run on the selected device.
58
+
59
+ - [`monitor`](monitor.md#monitor) (aliases `esp-idf-monitor`, `idf-monitor`)
60
+ - [`idf.py`](idf-py.md#idfpy)
61
+ - [`esptool`](esptool.md#esptool)
62
+ - [`espefuse`](espefuse.md#espefuse)
@@ -1,9 +1,10 @@
1
- # Monitor
1
+ # esp-idf-monitor
2
2
 
3
3
  ## `monitor`
4
4
 
5
- Run [`esp-idf-monitor`](https://github.com/espressif/esp-idf-monitor) on the
6
- device picked by `-p`, `-m`, or the [picker](../guide/choosing-a-device.md).
5
+ Run [`esp-idf-monitor`](https://github.com/espressif/esp-idf-monitor)
6
+ using `idftool`'s [device selection](../guide/choosing-a-device.md).
7
+ Aliases: `esp-idf-monitor`, `idf-monitor`.
7
8
 
8
9
  ```bash
9
10
  idftool monitor
@@ -17,7 +18,7 @@ its options. A `-p` given there is used as-is, without the picker.
17
18
  `--no-reset` is passed through, so combined with the picker (which doesn't
18
19
  reset boards) you can attach to a running device without rebooting it.
19
20
 
20
- `idftool`'s `-b` is the flashing baud rate and isn't passed on. Give the
21
- monitor's own after `monitor`: `idftool monitor -b 460800`.
21
+ `-b` sets the monitor's baud rate, the same as giving it after `monitor`:
22
+ `idftool -b 460800 monitor`. Without it, the monitor uses its own default.
22
23
 
23
24
  Quit with ++ctrl+bracket-right++.
@@ -26,9 +26,20 @@ api_key,data,string,abc123def456
26
26
  Don't pad the columns with spaces: `nvs_partition_gen` doesn't accept them in
27
27
  the header.
28
28
 
29
- !!! note
30
- Encrypted NVS isn't supported yet. Every command here works on plaintext
31
- NVS only.
29
+ ## Encrypted NVS
30
+
31
+ For NVS encrypted with the HMAC key protection scheme
32
+ (`CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`), give every command `--hmac-key`: the
33
+ key burned into the HMAC eFuse, as 64 hex digits or a file holding the raw 32
34
+ bytes or the digits. Images are decrypted when read and encrypted when written.
35
+
36
+ ```bash
37
+ idftool write-nvs nvs example.csv --hmac-key hmac_key.bin
38
+ idftool get-nvs nvs storage:device_id --hmac-key hmac_key.bin
39
+ ```
40
+
41
+ Without the key, an encrypted partition is refused rather than read as garbage.
42
+ Flash encryption-based NVS keys aren't supported.
32
43
 
33
44
  ## `create-nvs`
34
45
 
@@ -16,7 +16,9 @@ idftool read 'storage[-0x1000:]' tail.bin
16
16
 
17
17
  Write files to partitions. Arguments come in `PARTITION FILE` pairs; repeat
18
18
  them to flash several partitions in one go. Each file is checked to fit its
19
- partition before anything is written.
19
+ partition before anything is written. A file for an app partition or the
20
+ bootloader must be an app or a bootloader built for this chip; `--force` writes
21
+ it anyway.
20
22
 
21
23
  ```bash
22
24
  idftool write ota_0 build/app.bin storage build/spiffs.bin
@@ -22,9 +22,9 @@ idftool write storage build/spiffs.bin --diff --no-progress
22
22
 
23
23
  ## Skipping what's already there
24
24
 
25
- `ota` and `factory` turn on `--skip-flashed` and `--diff` by default, so
26
- reflashing the same build is nearly instant and a small change rewrites only
27
- the sectors it touched. Other commands leave both off unless asked.
25
+ `--skip-flashed` and `--diff` are on by default for every command except
26
+ `write-image`, so reflashing the same thing is nearly instant and a small
27
+ change rewrites only the sectors it touched.
28
28
 
29
29
  `--skip-flashed`
30
30
  : Hashes the partition on the device and compares it with the file, like
@@ -42,8 +42,8 @@ the sectors it touched. Other commands leave both off unless asked.
42
42
  `--skip-flashed` unless the erase is off: nothing can match a chip that was
43
43
  just wiped. See [`write-image`](images.md#write-image).
44
44
 
45
- `write-table` doesn't take these. Its `--force` already means "flash a table
46
- that failed verification".
45
+ `write-table` doesn't take these, and always skips a table that's already in
46
+ flash. Its `--force` already means "flash a table that failed verification".
47
47
 
48
48
  From Python, the same names work as keyword arguments. See
49
49
  [Python library](../library.md#write-options).