idftool 0.2.1__tar.gz → 0.3.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.
- idftool-0.3.0/CHANGELOG.md +94 -0
- {idftool-0.2.1 → idftool-0.3.0}/PKG-INFO +92 -14
- {idftool-0.2.1 → idftool-0.3.0}/README.md +90 -12
- {idftool-0.2.1 → idftool-0.3.0}/idftool/__main__.py +426 -34
- {idftool-0.2.1 → idftool-0.3.0}/idftool.egg-info/PKG-INFO +92 -14
- {idftool-0.2.1 → idftool-0.3.0}/idftool.egg-info/SOURCES.txt +3 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool.egg-info/requires.txt +1 -1
- idftool-0.3.0/idftool.egg-info/scm_file_list.json +19 -0
- idftool-0.3.0/idftool.egg-info/scm_version.json +8 -0
- {idftool-0.2.1 → idftool-0.3.0}/pyproject.toml +1 -1
- {idftool-0.2.1 → idftool-0.3.0}/.github/workflows/build.yml +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/.github/workflows/publish.yml +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/.gitignore +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/CONTRIBUTING.md +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/LICENSE.md +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/Makefile +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool/__init__.py +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool/traceback.py +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool-onedir.spec +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool.egg-info/dependency_links.txt +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool.egg-info/entry_points.txt +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool.egg-info/top_level.txt +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool.py +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/idftool.spec +0 -0
- {idftool-0.2.1 → idftool-0.3.0}/setup.cfg +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is
|
|
4
|
+
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
5
|
+
this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [v0.3.0] — 2026-07-15
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Partition table file commands that make the table itself the subject
|
|
11
|
+
(a positional file), keeping the global `--partition-table-file`
|
|
12
|
+
option for its existing role of overriding the layout of other
|
|
13
|
+
commands:
|
|
14
|
+
- `convert-table <in> <out>` — convert a partition table between CSV
|
|
15
|
+
and binary, offline. Format is auto-detected on input and inferred
|
|
16
|
+
from the output extension (`.csv`/`.bin`) or `--format`.
|
|
17
|
+
- `print-table [file]` — pretty-print a partition table from a CSV or
|
|
18
|
+
binary file (offline), or from the device when no file is given.
|
|
19
|
+
- `dump-table [output]` — read the partition table from the device and
|
|
20
|
+
save it as CSV or binary (default filename
|
|
21
|
+
`{chip}-{mac}-{timestamp}-partition-table.csv`).
|
|
22
|
+
- `write-table <file>` — flash a partition table from a CSV or binary
|
|
23
|
+
file to the device. Requires an explicit file (so it can never read
|
|
24
|
+
the device's current table and write it back to itself), verifies
|
|
25
|
+
the table before flashing (override with `--force`), and warns that
|
|
26
|
+
only the map is replaced — existing partition data is not moved or
|
|
27
|
+
erased.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- `list` is now an alias of `print-table` (both print the device's
|
|
31
|
+
partition table identically). Its `--partition-type`,
|
|
32
|
+
`--partition-subtype`, and `--partition-name` flags — which were never
|
|
33
|
+
implemented — have been removed.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- Working with empty or truncated input files now reports a clear error
|
|
37
|
+
instead of a confusing parser failure (e.g. "Partition table is
|
|
38
|
+
missing an end-of-table marker" for a zero-byte image). Empty images,
|
|
39
|
+
app binaries, and bundle ZIPs are rejected up front, as are images too
|
|
40
|
+
small to reach the partition table offset and corrupt (non-ZIP)
|
|
41
|
+
bundles.
|
|
42
|
+
- A parsed-but-empty partition table (all-`0xFF` binary region,
|
|
43
|
+
comment-only CSV, etc.) is now rejected across all parse paths rather
|
|
44
|
+
than silently yielding a table with no partitions.
|
|
45
|
+
- Loading a partition table CSV that contains bootloader rows (such as one
|
|
46
|
+
produced by `dump-table`) no longer requires re-specifying
|
|
47
|
+
`--primary-bootloader-offset`: the offset embedded in the CSV is
|
|
48
|
+
recovered from the file, so dumped tables round-trip through
|
|
49
|
+
`print-table`, `convert-table`, `write-table`, and `--partition-table-file`
|
|
50
|
+
without extra arguments. The flag still works as an explicit override.
|
|
51
|
+
|
|
52
|
+
## [v0.2.2] — 2026-06-15
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
- `print-image` — inspect a flash image file offline: prints the
|
|
56
|
+
partition table and, for each app partition that contains a valid
|
|
57
|
+
app, the project name, version, IDF version, compile time, ELF
|
|
58
|
+
SHA256, and target chip.
|
|
59
|
+
- `print-bundle` — same output for a partition bundle ZIP, driven by
|
|
60
|
+
the embedded `partition_table.csv` and the per-partition `.bin`
|
|
61
|
+
entries.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
- Bumped `esp-idf-defs` to `~=0.1.3`. `print_partition_table` now takes
|
|
65
|
+
a `(offset, length) -> bytes` accessor instead of an `ESPLoader`
|
|
66
|
+
instance, so the same printing path serves device, image, and bundle
|
|
67
|
+
inspection.
|
|
68
|
+
|
|
69
|
+
## [v0.2.1] — 2026-06-10
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
- Boot partition state now uses `OtaImageState` for clearer reporting.
|
|
73
|
+
|
|
74
|
+
## [v0.2.0] — 2026-05-26
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
- `create-nvs` and `write-nvs` commands for generating NVS partition
|
|
78
|
+
images from CSV.
|
|
79
|
+
- Rich-style boxed traceback formatter for friendlier error output.
|
|
80
|
+
|
|
81
|
+
### Documentation
|
|
82
|
+
- README installation section and command index table.
|
|
83
|
+
- Recommend `pipx` over the standalone binary for installation.
|
|
84
|
+
|
|
85
|
+
## [v0.1.0] — 2026-05-25
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
- Initial release.
|
|
89
|
+
|
|
90
|
+
[v0.3.0]: https://github.com/nebkat/idftool/releases/tag/v0.3.0
|
|
91
|
+
[v0.2.2]: https://github.com/nebkat/idftool/releases/tag/v0.2.2
|
|
92
|
+
[v0.2.1]: https://github.com/nebkat/idftool/releases/tag/v0.2.1
|
|
93
|
+
[v0.2.0]: https://github.com/nebkat/idftool/releases/tag/v0.2.0
|
|
94
|
+
[v0.1.0]: https://github.com/nebkat/idftool/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: idftool
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Utility for flashing, provisioning, and interacting with Espressif SOCs running ESP-IDF
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Source, https://github.com/nebkat/idftool
|
|
@@ -8,7 +8,7 @@ Requires-Python: >=3.10
|
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-File: LICENSE.md
|
|
10
10
|
Requires-Dist: esptool~=5.2.0
|
|
11
|
-
Requires-Dist: esp-idf-defs~=0.1.
|
|
11
|
+
Requires-Dist: esp-idf-defs~=0.1.3
|
|
12
12
|
Requires-Dist: esp-idf-nvs-partition-gen~=0.2.0
|
|
13
13
|
Dynamic: license-file
|
|
14
14
|
|
|
@@ -103,7 +103,7 @@ Setting boot partition to 'ota_1'...
|
|
|
103
103
|
|---------|-------------|
|
|
104
104
|
| **Discovery** | |
|
|
105
105
|
| [`devices`](#devices) | List serial ports with hardware IDs |
|
|
106
|
-
| [`list`](#
|
|
106
|
+
| [`list`](#print-table) | Print the partition table (alias of `print-table`) |
|
|
107
107
|
| **Partition I/O** | |
|
|
108
108
|
| [`read`](#read) | Read a partition (or slice) into a file |
|
|
109
109
|
| [`write`](#write) | Write one or more files to named partitions |
|
|
@@ -120,10 +120,17 @@ Setting boot partition to 'ota_1'...
|
|
|
120
120
|
| [`create-image`](#create-image) | Merge partition binaries into a single flash image |
|
|
121
121
|
| [`dump-image`](#dump-image) | Dump the entire flash to an image file |
|
|
122
122
|
| [`write-image`](#write-image-alias-reflash) | Write a full flash image to the device |
|
|
123
|
+
| [`print-image`](#print-image) | Print partition table and app info from a flash image file |
|
|
123
124
|
| **Bundles** | |
|
|
124
125
|
| [`create-bundle`](#create-bundle) | Pack partition images into a ZIP bundle |
|
|
125
126
|
| [`dump-bundle`](#dump-bundle) | Pack every partition from the device into a ZIP |
|
|
126
127
|
| [`write-bundle`](#write-bundle) | Flash every binary in a bundle ZIP |
|
|
128
|
+
| [`print-bundle`](#print-bundle) | Print partition table and app info from a bundle ZIP |
|
|
129
|
+
| **Partition table** | |
|
|
130
|
+
| [`convert-table`](#convert-table) | Convert a partition table file between CSV and binary |
|
|
131
|
+
| [`print-table`](#print-table) | Print a partition table from a CSV or binary file, or the device |
|
|
132
|
+
| [`dump-table`](#dump-table) | Read the partition table from the device into a file |
|
|
133
|
+
| [`write-table`](#write-table) | Flash a partition table from a CSV or binary file |
|
|
127
134
|
| **NVS** | |
|
|
128
135
|
| [`create-nvs`](#create-nvs) | Generate an NVS partition image from a CSV file |
|
|
129
136
|
| [`write-nvs`](#write-nvs) | Generate an NVS image from CSV and flash it |
|
|
@@ -139,17 +146,6 @@ hardware IDs.
|
|
|
139
146
|
idftool devices
|
|
140
147
|
```
|
|
141
148
|
|
|
142
|
-
#### `list`
|
|
143
|
-
Print the device's partition table. With a connected ESP, idftool also
|
|
144
|
-
reads the application descriptor of every app partition (project name,
|
|
145
|
-
version) and marks the currently-active OTA slot with a trailing `*`. The
|
|
146
|
-
selected otadata copy is shown next to the otadata partition's subtype
|
|
147
|
-
(`ota (A)`, `ota (B)`, or `ota (invalid)` when otadata is erased).
|
|
148
|
-
```text
|
|
149
|
-
idftool list
|
|
150
|
-
idftool --partition-table-file partitions.csv list # offline
|
|
151
|
-
```
|
|
152
|
-
|
|
153
149
|
### Partition I/O
|
|
154
150
|
|
|
155
151
|
#### `read`
|
|
@@ -259,6 +255,15 @@ counterpart of `dump-image`.
|
|
|
259
255
|
idftool write-image build/full-flash.img
|
|
260
256
|
```
|
|
261
257
|
|
|
258
|
+
#### `print-image`
|
|
259
|
+
Inspect a flash image file without touching a device: prints the
|
|
260
|
+
partition table embedded in the image and, for each app partition that
|
|
261
|
+
contains a valid app, the project name, version, IDF version, compile
|
|
262
|
+
time, ELF SHA256, and target chip.
|
|
263
|
+
```text
|
|
264
|
+
idftool print-image build/full-flash.img
|
|
265
|
+
```
|
|
266
|
+
|
|
262
267
|
### Bundles
|
|
263
268
|
|
|
264
269
|
A bundle is a plain ZIP file containing one `*.bin` per partition (named
|
|
@@ -292,6 +297,79 @@ to match) instead of reading the table from flash.
|
|
|
292
297
|
idftool write-bundle release.zip
|
|
293
298
|
```
|
|
294
299
|
|
|
300
|
+
#### `print-bundle`
|
|
301
|
+
Inspect a bundle ZIP without touching a device: prints the partition
|
|
302
|
+
table from the embedded `partition_table.csv` and, for each app
|
|
303
|
+
partition whose `.bin` is present in the bundle, the project name,
|
|
304
|
+
version, IDF version, compile time, ELF SHA256, and target chip.
|
|
305
|
+
```text
|
|
306
|
+
idftool print-bundle release.zip
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Partition table
|
|
310
|
+
|
|
311
|
+
These commands work directly on a partition table as a file — the table
|
|
312
|
+
is the subject, passed as a positional argument. This is distinct from the
|
|
313
|
+
global `--partition-table-file` option, which overrides the layout *other*
|
|
314
|
+
commands use to address partitions by name. Input format (CSV or binary)
|
|
315
|
+
is auto-detected; output format is inferred from the file extension
|
|
316
|
+
(`.csv`/`.bin`) or set with `--format`.
|
|
317
|
+
|
|
318
|
+
When the input is a CSV that includes a bootloader row, pass
|
|
319
|
+
`--primary-bootloader-offset` (an offset or a chip name, e.g. `esp32s3`)
|
|
320
|
+
so the offset can be resolved.
|
|
321
|
+
|
|
322
|
+
#### `convert-table`
|
|
323
|
+
Convert a partition table between CSV and binary, offline. Handles both
|
|
324
|
+
directions; the binary output includes the MD5 checksum and padding, so it
|
|
325
|
+
is ready to flash or embed in an image.
|
|
326
|
+
```text
|
|
327
|
+
idftool convert-table partitions.csv partitions.bin
|
|
328
|
+
idftool convert-table partitions.bin partitions.csv
|
|
329
|
+
idftool --primary-bootloader-offset esp32s3 convert-table partitions.csv partitions.bin
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### `print-table`
|
|
333
|
+
Pretty-print a partition table. Given a CSV or binary file it runs offline;
|
|
334
|
+
with no file it reads the table from the device (or from
|
|
335
|
+
`--partition-table-file`). With a connected ESP, idftool also reads the
|
|
336
|
+
application descriptor of every app partition (project name, version) and
|
|
337
|
+
marks the currently-active OTA slot with a trailing `*`; the selected otadata
|
|
338
|
+
copy is shown next to the otadata partition's subtype (`ota (A)`, `ota (B)`,
|
|
339
|
+
or `ota (invalid)` when otadata is erased).
|
|
340
|
+
|
|
341
|
+
Aliased as `list`.
|
|
342
|
+
```text
|
|
343
|
+
idftool print-table partitions.bin # from a file, offline
|
|
344
|
+
idftool print-table # from the device
|
|
345
|
+
idftool list # same thing
|
|
346
|
+
idftool --partition-table-file partitions.csv print-table
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
#### `dump-table`
|
|
350
|
+
Read the partition table from the connected device and save it. Defaults to
|
|
351
|
+
CSV with an auto-generated filename; pass an output file or `--format` to
|
|
352
|
+
choose otherwise.
|
|
353
|
+
```text
|
|
354
|
+
idftool dump-table # auto-named .csv
|
|
355
|
+
idftool dump-table backup.bin
|
|
356
|
+
idftool dump-table backup.csv --format csv
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
#### `write-table`
|
|
360
|
+
Flash a partition table from a CSV or binary file to the device. The file is
|
|
361
|
+
required, so this never reads the device's current table and writes it back
|
|
362
|
+
to itself — use `dump-table` to pull the current table. The table is verified
|
|
363
|
+
before flashing (override with `--force`).
|
|
364
|
+
|
|
365
|
+
Only the partition map (at the partition table offset) is replaced; existing
|
|
366
|
+
partition **data** on flash is not moved, resized, or erased, so a table that
|
|
367
|
+
no longer matches the flash contents can make the device unbootable.
|
|
368
|
+
```text
|
|
369
|
+
idftool write-table partitions.csv
|
|
370
|
+
idftool write-table partitions.bin --force
|
|
371
|
+
```
|
|
372
|
+
|
|
295
373
|
### NVS
|
|
296
374
|
|
|
297
375
|
idftool can generate a binary NVS (Non-Volatile Storage) partition image
|
|
@@ -89,7 +89,7 @@ Setting boot partition to 'ota_1'...
|
|
|
89
89
|
|---------|-------------|
|
|
90
90
|
| **Discovery** | |
|
|
91
91
|
| [`devices`](#devices) | List serial ports with hardware IDs |
|
|
92
|
-
| [`list`](#
|
|
92
|
+
| [`list`](#print-table) | Print the partition table (alias of `print-table`) |
|
|
93
93
|
| **Partition I/O** | |
|
|
94
94
|
| [`read`](#read) | Read a partition (or slice) into a file |
|
|
95
95
|
| [`write`](#write) | Write one or more files to named partitions |
|
|
@@ -106,10 +106,17 @@ Setting boot partition to 'ota_1'...
|
|
|
106
106
|
| [`create-image`](#create-image) | Merge partition binaries into a single flash image |
|
|
107
107
|
| [`dump-image`](#dump-image) | Dump the entire flash to an image file |
|
|
108
108
|
| [`write-image`](#write-image-alias-reflash) | Write a full flash image to the device |
|
|
109
|
+
| [`print-image`](#print-image) | Print partition table and app info from a flash image file |
|
|
109
110
|
| **Bundles** | |
|
|
110
111
|
| [`create-bundle`](#create-bundle) | Pack partition images into a ZIP bundle |
|
|
111
112
|
| [`dump-bundle`](#dump-bundle) | Pack every partition from the device into a ZIP |
|
|
112
113
|
| [`write-bundle`](#write-bundle) | Flash every binary in a bundle ZIP |
|
|
114
|
+
| [`print-bundle`](#print-bundle) | Print partition table and app info from a bundle ZIP |
|
|
115
|
+
| **Partition table** | |
|
|
116
|
+
| [`convert-table`](#convert-table) | Convert a partition table file between CSV and binary |
|
|
117
|
+
| [`print-table`](#print-table) | Print a partition table from a CSV or binary file, or the device |
|
|
118
|
+
| [`dump-table`](#dump-table) | Read the partition table from the device into a file |
|
|
119
|
+
| [`write-table`](#write-table) | Flash a partition table from a CSV or binary file |
|
|
113
120
|
| **NVS** | |
|
|
114
121
|
| [`create-nvs`](#create-nvs) | Generate an NVS partition image from a CSV file |
|
|
115
122
|
| [`write-nvs`](#write-nvs) | Generate an NVS image from CSV and flash it |
|
|
@@ -125,17 +132,6 @@ hardware IDs.
|
|
|
125
132
|
idftool devices
|
|
126
133
|
```
|
|
127
134
|
|
|
128
|
-
#### `list`
|
|
129
|
-
Print the device's partition table. With a connected ESP, idftool also
|
|
130
|
-
reads the application descriptor of every app partition (project name,
|
|
131
|
-
version) and marks the currently-active OTA slot with a trailing `*`. The
|
|
132
|
-
selected otadata copy is shown next to the otadata partition's subtype
|
|
133
|
-
(`ota (A)`, `ota (B)`, or `ota (invalid)` when otadata is erased).
|
|
134
|
-
```text
|
|
135
|
-
idftool list
|
|
136
|
-
idftool --partition-table-file partitions.csv list # offline
|
|
137
|
-
```
|
|
138
|
-
|
|
139
135
|
### Partition I/O
|
|
140
136
|
|
|
141
137
|
#### `read`
|
|
@@ -245,6 +241,15 @@ counterpart of `dump-image`.
|
|
|
245
241
|
idftool write-image build/full-flash.img
|
|
246
242
|
```
|
|
247
243
|
|
|
244
|
+
#### `print-image`
|
|
245
|
+
Inspect a flash image file without touching a device: prints the
|
|
246
|
+
partition table embedded in the image and, for each app partition that
|
|
247
|
+
contains a valid app, the project name, version, IDF version, compile
|
|
248
|
+
time, ELF SHA256, and target chip.
|
|
249
|
+
```text
|
|
250
|
+
idftool print-image build/full-flash.img
|
|
251
|
+
```
|
|
252
|
+
|
|
248
253
|
### Bundles
|
|
249
254
|
|
|
250
255
|
A bundle is a plain ZIP file containing one `*.bin` per partition (named
|
|
@@ -278,6 +283,79 @@ to match) instead of reading the table from flash.
|
|
|
278
283
|
idftool write-bundle release.zip
|
|
279
284
|
```
|
|
280
285
|
|
|
286
|
+
#### `print-bundle`
|
|
287
|
+
Inspect a bundle ZIP without touching a device: prints the partition
|
|
288
|
+
table from the embedded `partition_table.csv` and, for each app
|
|
289
|
+
partition whose `.bin` is present in the bundle, the project name,
|
|
290
|
+
version, IDF version, compile time, ELF SHA256, and target chip.
|
|
291
|
+
```text
|
|
292
|
+
idftool print-bundle release.zip
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Partition table
|
|
296
|
+
|
|
297
|
+
These commands work directly on a partition table as a file — the table
|
|
298
|
+
is the subject, passed as a positional argument. This is distinct from the
|
|
299
|
+
global `--partition-table-file` option, which overrides the layout *other*
|
|
300
|
+
commands use to address partitions by name. Input format (CSV or binary)
|
|
301
|
+
is auto-detected; output format is inferred from the file extension
|
|
302
|
+
(`.csv`/`.bin`) or set with `--format`.
|
|
303
|
+
|
|
304
|
+
When the input is a CSV that includes a bootloader row, pass
|
|
305
|
+
`--primary-bootloader-offset` (an offset or a chip name, e.g. `esp32s3`)
|
|
306
|
+
so the offset can be resolved.
|
|
307
|
+
|
|
308
|
+
#### `convert-table`
|
|
309
|
+
Convert a partition table between CSV and binary, offline. Handles both
|
|
310
|
+
directions; the binary output includes the MD5 checksum and padding, so it
|
|
311
|
+
is ready to flash or embed in an image.
|
|
312
|
+
```text
|
|
313
|
+
idftool convert-table partitions.csv partitions.bin
|
|
314
|
+
idftool convert-table partitions.bin partitions.csv
|
|
315
|
+
idftool --primary-bootloader-offset esp32s3 convert-table partitions.csv partitions.bin
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
#### `print-table`
|
|
319
|
+
Pretty-print a partition table. Given a CSV or binary file it runs offline;
|
|
320
|
+
with no file it reads the table from the device (or from
|
|
321
|
+
`--partition-table-file`). With a connected ESP, idftool also reads the
|
|
322
|
+
application descriptor of every app partition (project name, version) and
|
|
323
|
+
marks the currently-active OTA slot with a trailing `*`; the selected otadata
|
|
324
|
+
copy is shown next to the otadata partition's subtype (`ota (A)`, `ota (B)`,
|
|
325
|
+
or `ota (invalid)` when otadata is erased).
|
|
326
|
+
|
|
327
|
+
Aliased as `list`.
|
|
328
|
+
```text
|
|
329
|
+
idftool print-table partitions.bin # from a file, offline
|
|
330
|
+
idftool print-table # from the device
|
|
331
|
+
idftool list # same thing
|
|
332
|
+
idftool --partition-table-file partitions.csv print-table
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### `dump-table`
|
|
336
|
+
Read the partition table from the connected device and save it. Defaults to
|
|
337
|
+
CSV with an auto-generated filename; pass an output file or `--format` to
|
|
338
|
+
choose otherwise.
|
|
339
|
+
```text
|
|
340
|
+
idftool dump-table # auto-named .csv
|
|
341
|
+
idftool dump-table backup.bin
|
|
342
|
+
idftool dump-table backup.csv --format csv
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### `write-table`
|
|
346
|
+
Flash a partition table from a CSV or binary file to the device. The file is
|
|
347
|
+
required, so this never reads the device's current table and writes it back
|
|
348
|
+
to itself — use `dump-table` to pull the current table. The table is verified
|
|
349
|
+
before flashing (override with `--force`).
|
|
350
|
+
|
|
351
|
+
Only the partition map (at the partition table offset) is replaced; existing
|
|
352
|
+
partition **data** on flash is not moved, resized, or erased, so a table that
|
|
353
|
+
no longer matches the flash contents can make the device unbootable.
|
|
354
|
+
```text
|
|
355
|
+
idftool write-table partitions.csv
|
|
356
|
+
idftool write-table partitions.bin --force
|
|
357
|
+
```
|
|
358
|
+
|
|
281
359
|
### NVS
|
|
282
360
|
|
|
283
361
|
idftool can generate a binary NVS (Non-Volatile Storage) partition image
|
|
@@ -3,8 +3,8 @@ import os.path
|
|
|
3
3
|
import re
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
|
-
from typing import Optional, Literal
|
|
7
|
-
from zipfile import ZipFile
|
|
6
|
+
from typing import Callable, Optional, Literal
|
|
7
|
+
from zipfile import BadZipFile, ZipFile
|
|
8
8
|
|
|
9
9
|
from esptool import ESPLoader, CHIP_DEFS, flash_size_bytes
|
|
10
10
|
from serial import SerialException
|
|
@@ -17,7 +17,7 @@ from esp_idf_defs import ImageMetadata, ChipId
|
|
|
17
17
|
from esp_idf_defs.otadata import OtaDataParameters, OtaDataSelectEntry, OtaImageState
|
|
18
18
|
from esp_idf_defs.partitions import PARTITION_TABLE_SIZE, PARTITION_TABLE_OFFSET, PartitionTable, print_partition_table, \
|
|
19
19
|
PartitionDefinition, BOOTLOADER_TYPE, SUBTYPES, PARTITION_TABLE_TYPE, APP_TYPE, NUM_PARTITION_SUBTYPE_APP_OTA, \
|
|
20
|
-
DATA_TYPE
|
|
20
|
+
DATA_TYPE, get_encoding
|
|
21
21
|
from esptool.cmds import detect_chip, read_flash, write_flash, erase_region, merge_bin, erase_flash, detect_flash_size
|
|
22
22
|
|
|
23
23
|
PARTITION_SLICE_REGEX = re.compile(r"^(?P<partition>.+?)(?:\[(?:(?P<start>[-+]?(?:0x)?[0-9A-Fa-f]+)?(?::(?P<stop>[-+]?(?:0x)?[0-9A-Fa-f]+)?)?)?])?$")
|
|
@@ -239,6 +239,99 @@ def write_otadata(esp: ESPLoader, partition: PartitionDefinition, otadata: OtaDa
|
|
|
239
239
|
esp.flash_block(data=otadata.otadata.to_bytes(), seq=0)
|
|
240
240
|
esp.flash_finish()
|
|
241
241
|
|
|
242
|
+
def check_image_file(path: str, partition_table_offset: int) -> int:
|
|
243
|
+
"""Validate an image file is non-empty and large enough to contain a partition table.
|
|
244
|
+
|
|
245
|
+
Returns the image size in bytes.
|
|
246
|
+
"""
|
|
247
|
+
image_size = os.path.getsize(path)
|
|
248
|
+
if image_size == 0:
|
|
249
|
+
raise RuntimeError(f"Image file '{path}' is empty")
|
|
250
|
+
if image_size <= partition_table_offset:
|
|
251
|
+
raise RuntimeError(
|
|
252
|
+
f"Image file '{path}' ({image_size:#x} bytes) is smaller than the partition table "
|
|
253
|
+
f"offset {partition_table_offset:#x}; it does not appear to contain a partition table")
|
|
254
|
+
return image_size
|
|
255
|
+
|
|
256
|
+
def require_partitions(partition_table: PartitionTable, source: str) -> PartitionTable:
|
|
257
|
+
"""Raise a clear error if a parsed partition table contains no partitions."""
|
|
258
|
+
if len(partition_table) == 0:
|
|
259
|
+
raise RuntimeError(f"Partition table from {source} is empty (no partitions defined)")
|
|
260
|
+
return partition_table
|
|
261
|
+
|
|
262
|
+
def load_partition_table_file(
|
|
263
|
+
path: str,
|
|
264
|
+
partition_table_offset: int,
|
|
265
|
+
primary_bootloader_offset: Optional[int],
|
|
266
|
+
recovery_bootloader_offset: Optional[int],
|
|
267
|
+
) -> PartitionTable:
|
|
268
|
+
"""Load a partition table from a CSV or binary file (format auto-detected)."""
|
|
269
|
+
if os.path.getsize(path) == 0:
|
|
270
|
+
raise RuntimeError(f"Partition table file '{path}' is empty")
|
|
271
|
+
with open(path, 'rb') as f:
|
|
272
|
+
data = f.read()
|
|
273
|
+
|
|
274
|
+
if data[:2] == PartitionDefinition.MAGIC_BYTES:
|
|
275
|
+
partition_table = PartitionTable.from_binary(data)
|
|
276
|
+
else:
|
|
277
|
+
csv_text = data.decode(get_encoding(data))
|
|
278
|
+
# A CSV with bootloader rows can't be parsed without the bootloader offsets. idftool writes
|
|
279
|
+
# those offsets into the CSV as literal bootloader rows, so recover them from the file itself
|
|
280
|
+
# when the user didn't pass them — this lets a dumped table round-trip without re-specifying
|
|
281
|
+
# --primary-bootloader-offset. (print-bundle does the same for bundle CSVs.)
|
|
282
|
+
if primary_bootloader_offset is None or recovery_bootloader_offset is None:
|
|
283
|
+
csv_primary, csv_recovery = _extract_csv_bootloader_offsets(csv_text)
|
|
284
|
+
if primary_bootloader_offset is None:
|
|
285
|
+
primary_bootloader_offset = csv_primary
|
|
286
|
+
if recovery_bootloader_offset is None:
|
|
287
|
+
recovery_bootloader_offset = csv_recovery
|
|
288
|
+
try:
|
|
289
|
+
partition_table = PartitionTable.from_csv(
|
|
290
|
+
csv_text,
|
|
291
|
+
partition_table_offset=partition_table_offset,
|
|
292
|
+
primary_bootloader_offset=primary_bootloader_offset,
|
|
293
|
+
recovery_bootloader_offset=recovery_bootloader_offset,
|
|
294
|
+
)
|
|
295
|
+
except RuntimeError as e:
|
|
296
|
+
msg = str(e)
|
|
297
|
+
if 'bootloader offset is not provided' in msg.lower():
|
|
298
|
+
which = 'recovery' if 'recovery' in msg.lower() else 'primary'
|
|
299
|
+
flag = ('--primary-bootloader-offset (an address or a chip name, e.g. esp32s3)'
|
|
300
|
+
if which == 'primary' else '--recovery-bootloader-offset')
|
|
301
|
+
line = re.search(r'line (\d+)', msg)
|
|
302
|
+
where = f" (line {line.group(1)})" if line else ""
|
|
303
|
+
raise RuntimeError(
|
|
304
|
+
f"The {which} bootloader entry in '{path}'{where} has no offset. "
|
|
305
|
+
f"Add the offset to the CSV, or pass {flag}."
|
|
306
|
+
) from None
|
|
307
|
+
raise
|
|
308
|
+
return require_partitions(partition_table, f"file '{path}'")
|
|
309
|
+
|
|
310
|
+
def resolve_partition_table_format(output_file: Optional[str], explicit: Optional[str]) -> Literal['csv', 'bin']:
|
|
311
|
+
"""Pick the output format from an explicit flag, else the output file extension (default csv)."""
|
|
312
|
+
if explicit:
|
|
313
|
+
return explicit
|
|
314
|
+
if output_file:
|
|
315
|
+
ext = os.path.splitext(output_file)[1].lower()
|
|
316
|
+
if ext == '.csv':
|
|
317
|
+
return 'csv'
|
|
318
|
+
if ext in ('.bin', '.img'):
|
|
319
|
+
return 'bin'
|
|
320
|
+
raise RuntimeError(
|
|
321
|
+
f"Cannot infer partition table format from '{output_file}'; "
|
|
322
|
+
f"pass --format csv|bin or use a .csv/.bin extension")
|
|
323
|
+
return 'csv'
|
|
324
|
+
|
|
325
|
+
def write_partition_table_file(partition_table: PartitionTable, output_file: str, output_format: Literal['csv', 'bin']):
|
|
326
|
+
"""Serialise a partition table to CSV or binary and write it to output_file."""
|
|
327
|
+
if output_format == 'csv':
|
|
328
|
+
data = partition_table.to_csv().encode('utf-8')
|
|
329
|
+
else:
|
|
330
|
+
data = partition_table.to_binary()
|
|
331
|
+
with open(output_file, 'wb') as f:
|
|
332
|
+
f.write(data)
|
|
333
|
+
print(f"Wrote {output_format} partition table ({len(data):#x} bytes) to {output_file}")
|
|
334
|
+
|
|
242
335
|
def validate_app_binary(esp: ESPLoader, app_binary: bytes) -> tuple[bytes, ImageMetadata]:
|
|
243
336
|
try:
|
|
244
337
|
image_metadata = ImageMetadata.from_bytes(app_binary, app_required=True)
|
|
@@ -256,6 +349,8 @@ def validate_app_binary(esp: ESPLoader, app_binary: bytes) -> tuple[bytes, Image
|
|
|
256
349
|
|
|
257
350
|
def load_app_binary(esp: ESPLoader, app_binary_file: str, partition: PartitionDefinition) -> tuple[bytes, ImageMetadata]:
|
|
258
351
|
app_binary_size = os.path.getsize(app_binary_file)
|
|
352
|
+
if app_binary_size == 0:
|
|
353
|
+
raise RuntimeError(f"Application binary '{app_binary_file}' is empty")
|
|
259
354
|
if app_binary_size > partition.size:
|
|
260
355
|
raise RuntimeError(
|
|
261
356
|
f"Application binary size '{app_binary_size}' is greater than partition size {partition.size}")
|
|
@@ -404,7 +499,13 @@ def command_create_bundle(
|
|
|
404
499
|
print(f"Adding partition table CSV to bundle")
|
|
405
500
|
|
|
406
501
|
def check_write_bundle_has_partition_table(file: str) -> bool:
|
|
407
|
-
|
|
502
|
+
if os.path.getsize(file) == 0:
|
|
503
|
+
raise RuntimeError(f"Bundle '{file}' is empty")
|
|
504
|
+
try:
|
|
505
|
+
bundle_zip = ZipFile(file, 'r')
|
|
506
|
+
except BadZipFile as e:
|
|
507
|
+
raise RuntimeError(f"Bundle '{file}' is not a valid ZIP archive") from e
|
|
508
|
+
with bundle_zip as zf:
|
|
408
509
|
return any(m == 'partition_table.csv' for m in zf.namelist())
|
|
409
510
|
|
|
410
511
|
def command_write_bundle(
|
|
@@ -569,27 +670,19 @@ def command_write_nvs(
|
|
|
569
670
|
write_flash(esp=esp, addr_data=[(partition.offset, image)], flash_size='detect')
|
|
570
671
|
|
|
571
672
|
def command_app_info(app_binary_file: str):
|
|
673
|
+
if os.path.getsize(app_binary_file) == 0:
|
|
674
|
+
raise RuntimeError(f"Application binary '{app_binary_file}' is empty")
|
|
572
675
|
app_binary = open(app_binary_file, 'rb').read()
|
|
573
676
|
try:
|
|
574
677
|
image_metadata = ImageMetadata.from_bytes(app_binary, app_required=True)
|
|
575
678
|
except ValueError as e:
|
|
576
679
|
raise RuntimeError(f"Invalid application binary: {e}")
|
|
577
680
|
|
|
578
|
-
|
|
579
|
-
header = image_metadata.header
|
|
580
|
-
|
|
581
|
-
print(f"Project name: {desc.project_name}")
|
|
582
|
-
print(f"Version: {desc.version}")
|
|
583
|
-
print(f"IDF version: {desc.idf_version}")
|
|
584
|
-
print(f"Secure version: {desc.secure_version}")
|
|
585
|
-
if desc.compiled:
|
|
586
|
-
print(f"Compiled: {desc.compiled}")
|
|
587
|
-
print(f"ELF SHA256: {desc.elf_sha256.hex()}")
|
|
588
|
-
max_rev = header.max_chip_rev_full if header.max_chip_rev_full != 0xFFFF else None
|
|
589
|
-
rev_range = f"rev {header.min_chip_rev_full} to {max_rev}" if max_rev else f"rev {header.min_chip_rev_full}+"
|
|
590
|
-
print(f"Chip: {header.chip_id.name} ({rev_range})")
|
|
681
|
+
print_app_info(image_metadata.app_description, image_metadata.header)
|
|
591
682
|
|
|
592
683
|
def command_write_image(esp: ESPLoader, bootloader_entry: PartitionDefinition, image_file_path: str):
|
|
684
|
+
if os.path.getsize(image_file_path) == 0:
|
|
685
|
+
raise RuntimeError(f"Image file '{image_file_path}' is empty")
|
|
593
686
|
image_file = open(image_file_path, 'rb')
|
|
594
687
|
image_file.seek(bootloader_entry.offset)
|
|
595
688
|
bootloader_binary = image_file.read(bootloader_entry.size)
|
|
@@ -619,6 +712,214 @@ def command_write_image(esp: ESPLoader, bootloader_entry: PartitionDefinition, i
|
|
|
619
712
|
flash_size='detect',
|
|
620
713
|
)
|
|
621
714
|
|
|
715
|
+
def print_app_info(desc, header, indent: str = ""):
|
|
716
|
+
print(f"{indent}Project name: {desc.project_name}")
|
|
717
|
+
print(f"{indent}Version: {desc.version}")
|
|
718
|
+
print(f"{indent}IDF version: {desc.idf_version}")
|
|
719
|
+
print(f"{indent}Secure version: {desc.secure_version}")
|
|
720
|
+
if desc.compiled:
|
|
721
|
+
print(f"{indent}Compiled: {desc.compiled}")
|
|
722
|
+
print(f"{indent}ELF SHA256: {desc.elf_sha256.hex()}")
|
|
723
|
+
max_rev = header.max_chip_rev_full if header.max_chip_rev_full != 0xFFFF else None
|
|
724
|
+
rev_range = f"rev {header.min_chip_rev_full} to {max_rev}" if max_rev else f"rev {header.min_chip_rev_full}+"
|
|
725
|
+
print(f"{indent}Chip: {header.chip_id.name} ({rev_range})")
|
|
726
|
+
|
|
727
|
+
def print_partition_table_and_apps(
|
|
728
|
+
partition_table: PartitionTable,
|
|
729
|
+
read: Callable[[int, int], bytes],
|
|
730
|
+
):
|
|
731
|
+
print_partition_table(partition_table, read)
|
|
732
|
+
|
|
733
|
+
for part in partition_table:
|
|
734
|
+
if part.type != APP_TYPE:
|
|
735
|
+
continue
|
|
736
|
+
app_binary = read(part.offset, part.size)
|
|
737
|
+
try:
|
|
738
|
+
image_metadata = ImageMetadata.from_bytes(app_binary, app_required=True)
|
|
739
|
+
except (RuntimeError, ValueError):
|
|
740
|
+
continue
|
|
741
|
+
print()
|
|
742
|
+
print(f"Partition '{part.name}' (offset={part.offset:#x}):")
|
|
743
|
+
print_app_info(image_metadata.app_description, image_metadata.header, indent=" ")
|
|
744
|
+
|
|
745
|
+
def command_print_image(image_file_path: str, partition_table_offset: int, partition_table_size: int):
|
|
746
|
+
image_size = check_image_file(image_file_path, partition_table_offset)
|
|
747
|
+
with open(image_file_path, 'rb') as f:
|
|
748
|
+
f.seek(partition_table_offset)
|
|
749
|
+
partition_table_binary = f.read(partition_table_size)
|
|
750
|
+
try:
|
|
751
|
+
partition_table = PartitionTable.from_binary(partition_table_binary)
|
|
752
|
+
except (RuntimeError, ValueError) as e:
|
|
753
|
+
raise RuntimeError(f"Could not parse partition table at offset {partition_table_offset:#x}") from e
|
|
754
|
+
require_partitions(partition_table, f"image at offset {partition_table_offset:#x}")
|
|
755
|
+
|
|
756
|
+
def read(offset: int, length: int) -> bytes:
|
|
757
|
+
if offset >= image_size:
|
|
758
|
+
return b"\xff" * length
|
|
759
|
+
f.seek(offset)
|
|
760
|
+
data = f.read(length)
|
|
761
|
+
if len(data) < length:
|
|
762
|
+
data += b"\xff" * (length - len(data))
|
|
763
|
+
return data
|
|
764
|
+
|
|
765
|
+
print(f"Image: {image_file_path} ({image_size:#x} bytes)")
|
|
766
|
+
print()
|
|
767
|
+
print_partition_table_and_apps(partition_table, read)
|
|
768
|
+
|
|
769
|
+
def _extract_csv_bootloader_offsets(csv_text: str) -> tuple[Optional[int], Optional[int]]:
|
|
770
|
+
primary = recovery = None
|
|
771
|
+
for line in csv_text.splitlines():
|
|
772
|
+
line = line.strip()
|
|
773
|
+
if not line or line.startswith('#'):
|
|
774
|
+
continue
|
|
775
|
+
parts = [p.strip() for p in line.split(',')]
|
|
776
|
+
if len(parts) < 4 or parts[1] != 'bootloader':
|
|
777
|
+
continue
|
|
778
|
+
try:
|
|
779
|
+
offset = int(parts[3], 0)
|
|
780
|
+
except ValueError:
|
|
781
|
+
continue
|
|
782
|
+
if parts[2] == 'primary' and primary is None:
|
|
783
|
+
primary = offset
|
|
784
|
+
elif parts[2] == 'recovery' and recovery is None:
|
|
785
|
+
recovery = offset
|
|
786
|
+
return primary, recovery
|
|
787
|
+
|
|
788
|
+
def command_print_bundle(
|
|
789
|
+
bundle_file_path: str,
|
|
790
|
+
partition_table_offset: int,
|
|
791
|
+
primary_bootloader_offset: Optional[int],
|
|
792
|
+
recovery_bootloader_offset: Optional[int],
|
|
793
|
+
):
|
|
794
|
+
bundle_size = os.path.getsize(bundle_file_path)
|
|
795
|
+
if bundle_size == 0:
|
|
796
|
+
raise RuntimeError(f"Bundle '{bundle_file_path}' is empty")
|
|
797
|
+
try:
|
|
798
|
+
bundle_zip = ZipFile(bundle_file_path, 'r')
|
|
799
|
+
except BadZipFile as e:
|
|
800
|
+
raise RuntimeError(f"Bundle '{bundle_file_path}' is not a valid ZIP archive") from e
|
|
801
|
+
with bundle_zip as zf:
|
|
802
|
+
names = zf.namelist()
|
|
803
|
+
if 'partition_table.csv' not in names:
|
|
804
|
+
raise RuntimeError(f"Bundle {bundle_file_path} does not contain a partition_table.csv")
|
|
805
|
+
csv_text = zf.read('partition_table.csv').decode('utf-8')
|
|
806
|
+
if not csv_text.strip():
|
|
807
|
+
raise RuntimeError(f"Bundle '{bundle_file_path}' contains an empty partition_table.csv")
|
|
808
|
+
|
|
809
|
+
# idftool-generated bundles encode bootloader offsets in the CSV; PartitionTable.from_csv
|
|
810
|
+
# ignores the literal for bootloader rows and requires an explicit offset, so fall back to
|
|
811
|
+
# what the CSV says when the user didn't pass one.
|
|
812
|
+
if primary_bootloader_offset is None or recovery_bootloader_offset is None:
|
|
813
|
+
csv_primary, csv_recovery = _extract_csv_bootloader_offsets(csv_text)
|
|
814
|
+
if primary_bootloader_offset is None:
|
|
815
|
+
primary_bootloader_offset = csv_primary
|
|
816
|
+
if recovery_bootloader_offset is None:
|
|
817
|
+
recovery_bootloader_offset = csv_recovery
|
|
818
|
+
|
|
819
|
+
partition_table = PartitionTable.from_csv(
|
|
820
|
+
csv_text,
|
|
821
|
+
partition_table_offset=partition_table_offset,
|
|
822
|
+
primary_bootloader_offset=primary_bootloader_offset,
|
|
823
|
+
recovery_bootloader_offset=recovery_bootloader_offset,
|
|
824
|
+
)
|
|
825
|
+
require_partitions(partition_table, f"bundle '{bundle_file_path}'")
|
|
826
|
+
|
|
827
|
+
partition_data = {
|
|
828
|
+
name[:-4]: zf.read(name)
|
|
829
|
+
for name in names if name.endswith('.bin')
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
def read(offset: int, length: int) -> bytes:
|
|
833
|
+
for part in partition_table:
|
|
834
|
+
if part.offset <= offset < part.offset + part.size:
|
|
835
|
+
local = offset - part.offset
|
|
836
|
+
data = partition_data.get(part.name, b"")
|
|
837
|
+
chunk = data[local:local + length]
|
|
838
|
+
if len(chunk) < length:
|
|
839
|
+
chunk += b"\xff" * (length - len(chunk))
|
|
840
|
+
return chunk
|
|
841
|
+
return b"\xff" * length
|
|
842
|
+
|
|
843
|
+
included = sorted(partition_data.keys())
|
|
844
|
+
print(f"Bundle: {bundle_file_path} ({bundle_size:#x} bytes)")
|
|
845
|
+
print(f"Partitions included: {', '.join(included) if included else '(none)'}")
|
|
846
|
+
print()
|
|
847
|
+
print_partition_table_and_apps(partition_table, read)
|
|
848
|
+
|
|
849
|
+
def command_print_table(
|
|
850
|
+
table_file_path: str,
|
|
851
|
+
partition_table_offset: int,
|
|
852
|
+
primary_bootloader_offset: Optional[int],
|
|
853
|
+
recovery_bootloader_offset: Optional[int],
|
|
854
|
+
):
|
|
855
|
+
partition_table = load_partition_table_file(
|
|
856
|
+
table_file_path, partition_table_offset, primary_bootloader_offset, recovery_bootloader_offset)
|
|
857
|
+
print(f"Partition table: {table_file_path}")
|
|
858
|
+
print()
|
|
859
|
+
print_partition_table(partition_table)
|
|
860
|
+
|
|
861
|
+
def command_convert_table(
|
|
862
|
+
input_file: str,
|
|
863
|
+
output_file: str,
|
|
864
|
+
output_format: Optional[str],
|
|
865
|
+
partition_table_offset: int,
|
|
866
|
+
primary_bootloader_offset: Optional[int],
|
|
867
|
+
recovery_bootloader_offset: Optional[int],
|
|
868
|
+
):
|
|
869
|
+
partition_table = load_partition_table_file(
|
|
870
|
+
input_file, partition_table_offset, primary_bootloader_offset, recovery_bootloader_offset)
|
|
871
|
+
output_format = resolve_partition_table_format(output_file, output_format)
|
|
872
|
+
write_partition_table_file(partition_table, output_file, output_format)
|
|
873
|
+
|
|
874
|
+
def command_dump_table(
|
|
875
|
+
esp: ESPLoader,
|
|
876
|
+
partition_table: PartitionTable,
|
|
877
|
+
output_file: Optional[str],
|
|
878
|
+
output_format: Optional[str],
|
|
879
|
+
):
|
|
880
|
+
if not output_file:
|
|
881
|
+
chip = esp.CHIP_NAME.lower().replace(' ', '-')
|
|
882
|
+
try:
|
|
883
|
+
mac = esp.read_mac("BASE_MAC")
|
|
884
|
+
serial = ''.join(f"{b:02x}" for b in mac)
|
|
885
|
+
except Exception:
|
|
886
|
+
serial = "unknown"
|
|
887
|
+
timestamp = time.strftime("%Y%m%d-%H%M%S")
|
|
888
|
+
extension = 'bin' if output_format == 'bin' else 'csv'
|
|
889
|
+
output_file = f"{chip}-{serial}-{timestamp}-partition-table.{extension}"
|
|
890
|
+
output_format = resolve_partition_table_format(output_file, output_format)
|
|
891
|
+
write_partition_table_file(partition_table, output_file, output_format)
|
|
892
|
+
|
|
893
|
+
def command_write_table(
|
|
894
|
+
esp: ESPLoader,
|
|
895
|
+
table_file_path: str,
|
|
896
|
+
partition_table_offset: int,
|
|
897
|
+
primary_bootloader_offset: Optional[int],
|
|
898
|
+
recovery_bootloader_offset: Optional[int],
|
|
899
|
+
force: bool = False,
|
|
900
|
+
):
|
|
901
|
+
partition_table = load_partition_table_file(
|
|
902
|
+
table_file_path, partition_table_offset, primary_bootloader_offset, recovery_bootloader_offset)
|
|
903
|
+
|
|
904
|
+
try:
|
|
905
|
+
partition_table.verify(partition_table_offset=partition_table_offset)
|
|
906
|
+
except RuntimeError as e:
|
|
907
|
+
if not force:
|
|
908
|
+
raise RuntimeError(
|
|
909
|
+
f"Partition table failed verification: {e}. Re-run with --force to flash it anyway.") from e
|
|
910
|
+
print(f"Warning: partition table failed verification: {e} (continuing due to --force)")
|
|
911
|
+
|
|
912
|
+
partition_table.verify_size_fits(flash_size_bytes(detect_flash_size(esp)))
|
|
913
|
+
|
|
914
|
+
binary = partition_table.to_binary()
|
|
915
|
+
print_partition_table(partition_table)
|
|
916
|
+
print()
|
|
917
|
+
print(f"Writing partition table ({len(binary):#x} bytes) to offset {partition_table_offset:#x}...")
|
|
918
|
+
print("Note: this replaces only the partition map; existing partition data on flash is not moved, "
|
|
919
|
+
"resized, or erased. A table that no longer matches the flash contents can make the device unbootable.")
|
|
920
|
+
write_flash(esp=esp, addr_data=[(partition_table_offset, binary)], flash_size='detect')
|
|
921
|
+
print("Partition table written")
|
|
922
|
+
|
|
622
923
|
def command_dump_image(esp: ESPLoader, output_file: Optional[str]):
|
|
623
924
|
flash_size = flash_size_bytes(detect_flash_size(esp))
|
|
624
925
|
|
|
@@ -690,30 +991,87 @@ def main(args):
|
|
|
690
991
|
command_create_nvs(args.csv_file, args.size, args.output)
|
|
691
992
|
return
|
|
692
993
|
|
|
994
|
+
# print-image inspects a local image file and needs neither device nor external partition table
|
|
995
|
+
if args.command == 'print-image':
|
|
996
|
+
command_print_image(
|
|
997
|
+
image_file_path=args.image_file,
|
|
998
|
+
partition_table_offset=args.partition_table_offset,
|
|
999
|
+
partition_table_size=args.partition_table_size,
|
|
1000
|
+
)
|
|
1001
|
+
return
|
|
1002
|
+
|
|
1003
|
+
# print-bundle inspects a local bundle ZIP and needs neither device nor external partition table
|
|
1004
|
+
if args.command == 'print-bundle':
|
|
1005
|
+
command_print_bundle(
|
|
1006
|
+
bundle_file_path=args.bundle_file,
|
|
1007
|
+
partition_table_offset=args.partition_table_offset,
|
|
1008
|
+
primary_bootloader_offset=args.primary_bootloader_offset,
|
|
1009
|
+
recovery_bootloader_offset=args.recovery_bootloader_offset,
|
|
1010
|
+
)
|
|
1011
|
+
return
|
|
1012
|
+
|
|
1013
|
+
# print-table and convert-table operate purely on a local partition table file. print-table
|
|
1014
|
+
# with no file falls through to the shared loader, printing the device's table (or the one
|
|
1015
|
+
# given via --partition-table-file).
|
|
1016
|
+
if args.command == 'print-table' and args.table_file:
|
|
1017
|
+
command_print_table(
|
|
1018
|
+
table_file_path=args.table_file,
|
|
1019
|
+
partition_table_offset=args.partition_table_offset,
|
|
1020
|
+
primary_bootloader_offset=args.primary_bootloader_offset,
|
|
1021
|
+
recovery_bootloader_offset=args.recovery_bootloader_offset,
|
|
1022
|
+
)
|
|
1023
|
+
return
|
|
1024
|
+
|
|
1025
|
+
if args.command == 'convert-table':
|
|
1026
|
+
command_convert_table(
|
|
1027
|
+
input_file=args.input_file,
|
|
1028
|
+
output_file=args.output_file,
|
|
1029
|
+
output_format=args.format,
|
|
1030
|
+
partition_table_offset=args.partition_table_offset,
|
|
1031
|
+
primary_bootloader_offset=args.primary_bootloader_offset,
|
|
1032
|
+
recovery_bootloader_offset=args.recovery_bootloader_offset,
|
|
1033
|
+
)
|
|
1034
|
+
return
|
|
1035
|
+
|
|
693
1036
|
# Connect to ESP device if required
|
|
694
|
-
requires_esp = args.command not in ['
|
|
1037
|
+
requires_esp = args.command not in ['create-image', 'create-bundle', 'create-nvs', 'print-table'] or not args.partition_table_file
|
|
695
1038
|
esp = get_esp(port=args.port, baud=args.baud) if requires_esp else None
|
|
696
|
-
if
|
|
1039
|
+
if args.primary_bootloader_offset is None and esp:
|
|
697
1040
|
args.primary_bootloader_offset = esp.BOOTLOADER_FLASH_OFFSET
|
|
698
1041
|
|
|
1042
|
+
# write-table flashes a partition table from its own positional file, bypassing the shared
|
|
1043
|
+
# loader so it never reads the device's current table and writes it straight back to itself.
|
|
1044
|
+
if args.command == 'write-table':
|
|
1045
|
+
command_write_table(
|
|
1046
|
+
esp=esp,
|
|
1047
|
+
table_file_path=args.table_file,
|
|
1048
|
+
partition_table_offset=args.partition_table_offset,
|
|
1049
|
+
primary_bootloader_offset=args.primary_bootloader_offset,
|
|
1050
|
+
recovery_bootloader_offset=args.recovery_bootloader_offset,
|
|
1051
|
+
force=args.force,
|
|
1052
|
+
)
|
|
1053
|
+
if esp and not args.no_reset:
|
|
1054
|
+
esp.hard_reset()
|
|
1055
|
+
return
|
|
1056
|
+
|
|
699
1057
|
if args.command == 'dump-image':
|
|
700
1058
|
command_dump_image(esp=esp, output_file=args.output_file)
|
|
701
1059
|
return
|
|
702
1060
|
|
|
703
1061
|
# Load partition table
|
|
704
1062
|
if args.command == 'write-image':
|
|
1063
|
+
check_image_file(args.image_file, args.partition_table_offset)
|
|
705
1064
|
with open(args.image_file, 'rb') as f:
|
|
706
1065
|
f.seek(args.partition_table_offset)
|
|
707
1066
|
partition_table_binary = f.read(args.partition_table_size)
|
|
708
1067
|
partition_table = PartitionTable.from_binary(partition_table_binary)
|
|
709
1068
|
elif args.partition_table_file:
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
)[0]
|
|
1069
|
+
partition_table = load_partition_table_file(
|
|
1070
|
+
args.partition_table_file,
|
|
1071
|
+
partition_table_offset=args.partition_table_offset,
|
|
1072
|
+
primary_bootloader_offset=args.primary_bootloader_offset,
|
|
1073
|
+
recovery_bootloader_offset=args.recovery_bootloader_offset,
|
|
1074
|
+
)
|
|
717
1075
|
elif args.command == 'write-bundle' and check_write_bundle_has_partition_table(args.input_file):
|
|
718
1076
|
with ZipFile(args.input_file, 'r') as tar:
|
|
719
1077
|
partition_table_file = tar.read('partition_table.csv')
|
|
@@ -732,6 +1090,8 @@ def main(args):
|
|
|
732
1090
|
except RuntimeError as e:
|
|
733
1091
|
raise RuntimeError("Partition table could not be loaded") from e
|
|
734
1092
|
|
|
1093
|
+
require_partitions(partition_table, "the loaded source")
|
|
1094
|
+
|
|
735
1095
|
# Read otadata so the printed table can mark the active app partition
|
|
736
1096
|
otadata_params: OtaDataParameters | None = None
|
|
737
1097
|
if esp:
|
|
@@ -741,7 +1101,7 @@ def main(args):
|
|
|
741
1101
|
pass
|
|
742
1102
|
|
|
743
1103
|
# Print partition table
|
|
744
|
-
print_partition_table(partition_table, esp, otadata=otadata_params)
|
|
1104
|
+
print_partition_table(partition_table, esp.read_flash if esp else None, otadata=otadata_params)
|
|
745
1105
|
|
|
746
1106
|
if esp:
|
|
747
1107
|
flash_size_str = detect_flash_size(esp)
|
|
@@ -766,7 +1126,8 @@ def main(args):
|
|
|
766
1126
|
) if args.primary_bootloader_offset is not None else None
|
|
767
1127
|
)
|
|
768
1128
|
|
|
769
|
-
if args.command == '
|
|
1129
|
+
if args.command == 'print-table':
|
|
1130
|
+
# The shared path above already loaded and printed the table.
|
|
770
1131
|
pass
|
|
771
1132
|
elif args.command == 'read':
|
|
772
1133
|
command_read(
|
|
@@ -824,6 +1185,13 @@ def main(args):
|
|
|
824
1185
|
)
|
|
825
1186
|
elif args.command == 'dump-bundle':
|
|
826
1187
|
command_dump_bundle(esp=esp, partition_table=partition_table, output_file=args.output_file)
|
|
1188
|
+
elif args.command == 'dump-table':
|
|
1189
|
+
command_dump_table(
|
|
1190
|
+
esp=esp,
|
|
1191
|
+
partition_table=partition_table,
|
|
1192
|
+
output_file=args.output_file,
|
|
1193
|
+
output_format=args.format,
|
|
1194
|
+
)
|
|
827
1195
|
elif args.command == 'write-bundle':
|
|
828
1196
|
command_write_bundle(
|
|
829
1197
|
esp=esp,
|
|
@@ -881,12 +1249,6 @@ def _main():
|
|
|
881
1249
|
# Devices subcommand
|
|
882
1250
|
devices_parser = subparsers.add_parser('devices', help='Device list')
|
|
883
1251
|
|
|
884
|
-
# List subcommand
|
|
885
|
-
list_parser = subparsers.add_parser('list', help='List partitions')
|
|
886
|
-
list_parser.add_argument('--partition-type', help='Filter by partition type')
|
|
887
|
-
list_parser.add_argument('--partition-subtype', help='Filter by partition subtype')
|
|
888
|
-
list_parser.add_argument('--partition-name', help='Filter by partition name')
|
|
889
|
-
|
|
890
1252
|
# Read subcommand
|
|
891
1253
|
read_parser = subparsers.add_parser('read', help='Read partition')
|
|
892
1254
|
read_parser.add_argument('partition', help='Name of the partition to read')
|
|
@@ -932,6 +1294,10 @@ def _main():
|
|
|
932
1294
|
write_image_parser = subparsers.add_parser('write-image', aliases=['reflash'], help='Write a full flash image to the device')
|
|
933
1295
|
write_image_parser.add_argument('image_file', help='Input file containing flash image')
|
|
934
1296
|
|
|
1297
|
+
# Print Image subcommand
|
|
1298
|
+
print_image_parser = subparsers.add_parser('print-image', help='Print partition table and app info from a flash image file')
|
|
1299
|
+
print_image_parser.add_argument('image_file', help='Input file containing flash image')
|
|
1300
|
+
|
|
935
1301
|
# Create bundle subcommand
|
|
936
1302
|
create_bundle_parser = subparsers.add_parser('create-bundle', help='Create a ZIP bundle of partition binaries')
|
|
937
1303
|
create_bundle_parser.add_argument('-o', '--output', help='Output ZIP filename', dest='output_file', required=True)
|
|
@@ -951,6 +1317,30 @@ def _main():
|
|
|
951
1317
|
write_bundle_parser = subparsers.add_parser('write-bundle', help='Write a ZIP bundle of partition binaries')
|
|
952
1318
|
write_bundle_parser.add_argument('input_file', help='Input ZIP filename')
|
|
953
1319
|
|
|
1320
|
+
# Print Bundle subcommand
|
|
1321
|
+
print_bundle_parser = subparsers.add_parser('print-bundle', help='Print partition table and app info from a partition bundle ZIP')
|
|
1322
|
+
print_bundle_parser.add_argument('bundle_file', help='Input ZIP bundle file')
|
|
1323
|
+
|
|
1324
|
+
# Print Table subcommand
|
|
1325
|
+
print_table_parser = subparsers.add_parser('print-table', aliases=['list'], help='Print a partition table from a CSV or binary file, or from the device')
|
|
1326
|
+
print_table_parser.add_argument('table_file', nargs='?', help='Partition table CSV or binary file (default: read from the device, or --partition-table-file)')
|
|
1327
|
+
|
|
1328
|
+
# Convert Table subcommand
|
|
1329
|
+
convert_table_parser = subparsers.add_parser('convert-table', help='Convert a partition table file between CSV and binary')
|
|
1330
|
+
convert_table_parser.add_argument('input_file', help='Input partition table CSV or binary file')
|
|
1331
|
+
convert_table_parser.add_argument('output_file', help='Output partition table file (.csv or .bin)')
|
|
1332
|
+
convert_table_parser.add_argument('-f', '--format', choices=['csv', 'bin'], help='Output format (default: inferred from output extension)')
|
|
1333
|
+
|
|
1334
|
+
# Dump Table subcommand
|
|
1335
|
+
dump_table_parser = subparsers.add_parser('dump-table', help='Dump the partition table from the device to a file')
|
|
1336
|
+
dump_table_parser.add_argument('output_file', nargs='?', help='Output file (default: {chip}-{mac}-{timestamp}-partition-table.csv)')
|
|
1337
|
+
dump_table_parser.add_argument('-f', '--format', choices=['csv', 'bin'], help='Output format (default: inferred from output extension, else csv)')
|
|
1338
|
+
|
|
1339
|
+
# Write Table subcommand
|
|
1340
|
+
write_table_parser = subparsers.add_parser('write-table', help='Write a partition table from a CSV or binary file to the device')
|
|
1341
|
+
write_table_parser.add_argument('table_file', help='Partition table CSV or binary file to flash')
|
|
1342
|
+
write_table_parser.add_argument('--force', action='store_true', help='Flash even if the partition table fails verification')
|
|
1343
|
+
|
|
954
1344
|
# Create NVS subcommand
|
|
955
1345
|
create_nvs_parser = subparsers.add_parser('create-nvs', help='Generate NVS partition image from CSV')
|
|
956
1346
|
create_nvs_parser.add_argument('csv_file', help='Input NVS CSV file')
|
|
@@ -988,6 +1378,8 @@ def _main():
|
|
|
988
1378
|
args = parser.parse_args()
|
|
989
1379
|
if args.command == 'reflash':
|
|
990
1380
|
args.command = 'write-image'
|
|
1381
|
+
if args.command == 'list':
|
|
1382
|
+
args.command = 'print-table'
|
|
991
1383
|
if args.command == 'enter-bootloader' and not args.port:
|
|
992
1384
|
parser.error("enter-bootloader requires -p/--port")
|
|
993
1385
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: idftool
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Utility for flashing, provisioning, and interacting with Espressif SOCs running ESP-IDF
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Source, https://github.com/nebkat/idftool
|
|
@@ -8,7 +8,7 @@ Requires-Python: >=3.10
|
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-File: LICENSE.md
|
|
10
10
|
Requires-Dist: esptool~=5.2.0
|
|
11
|
-
Requires-Dist: esp-idf-defs~=0.1.
|
|
11
|
+
Requires-Dist: esp-idf-defs~=0.1.3
|
|
12
12
|
Requires-Dist: esp-idf-nvs-partition-gen~=0.2.0
|
|
13
13
|
Dynamic: license-file
|
|
14
14
|
|
|
@@ -103,7 +103,7 @@ Setting boot partition to 'ota_1'...
|
|
|
103
103
|
|---------|-------------|
|
|
104
104
|
| **Discovery** | |
|
|
105
105
|
| [`devices`](#devices) | List serial ports with hardware IDs |
|
|
106
|
-
| [`list`](#
|
|
106
|
+
| [`list`](#print-table) | Print the partition table (alias of `print-table`) |
|
|
107
107
|
| **Partition I/O** | |
|
|
108
108
|
| [`read`](#read) | Read a partition (or slice) into a file |
|
|
109
109
|
| [`write`](#write) | Write one or more files to named partitions |
|
|
@@ -120,10 +120,17 @@ Setting boot partition to 'ota_1'...
|
|
|
120
120
|
| [`create-image`](#create-image) | Merge partition binaries into a single flash image |
|
|
121
121
|
| [`dump-image`](#dump-image) | Dump the entire flash to an image file |
|
|
122
122
|
| [`write-image`](#write-image-alias-reflash) | Write a full flash image to the device |
|
|
123
|
+
| [`print-image`](#print-image) | Print partition table and app info from a flash image file |
|
|
123
124
|
| **Bundles** | |
|
|
124
125
|
| [`create-bundle`](#create-bundle) | Pack partition images into a ZIP bundle |
|
|
125
126
|
| [`dump-bundle`](#dump-bundle) | Pack every partition from the device into a ZIP |
|
|
126
127
|
| [`write-bundle`](#write-bundle) | Flash every binary in a bundle ZIP |
|
|
128
|
+
| [`print-bundle`](#print-bundle) | Print partition table and app info from a bundle ZIP |
|
|
129
|
+
| **Partition table** | |
|
|
130
|
+
| [`convert-table`](#convert-table) | Convert a partition table file between CSV and binary |
|
|
131
|
+
| [`print-table`](#print-table) | Print a partition table from a CSV or binary file, or the device |
|
|
132
|
+
| [`dump-table`](#dump-table) | Read the partition table from the device into a file |
|
|
133
|
+
| [`write-table`](#write-table) | Flash a partition table from a CSV or binary file |
|
|
127
134
|
| **NVS** | |
|
|
128
135
|
| [`create-nvs`](#create-nvs) | Generate an NVS partition image from a CSV file |
|
|
129
136
|
| [`write-nvs`](#write-nvs) | Generate an NVS image from CSV and flash it |
|
|
@@ -139,17 +146,6 @@ hardware IDs.
|
|
|
139
146
|
idftool devices
|
|
140
147
|
```
|
|
141
148
|
|
|
142
|
-
#### `list`
|
|
143
|
-
Print the device's partition table. With a connected ESP, idftool also
|
|
144
|
-
reads the application descriptor of every app partition (project name,
|
|
145
|
-
version) and marks the currently-active OTA slot with a trailing `*`. The
|
|
146
|
-
selected otadata copy is shown next to the otadata partition's subtype
|
|
147
|
-
(`ota (A)`, `ota (B)`, or `ota (invalid)` when otadata is erased).
|
|
148
|
-
```text
|
|
149
|
-
idftool list
|
|
150
|
-
idftool --partition-table-file partitions.csv list # offline
|
|
151
|
-
```
|
|
152
|
-
|
|
153
149
|
### Partition I/O
|
|
154
150
|
|
|
155
151
|
#### `read`
|
|
@@ -259,6 +255,15 @@ counterpart of `dump-image`.
|
|
|
259
255
|
idftool write-image build/full-flash.img
|
|
260
256
|
```
|
|
261
257
|
|
|
258
|
+
#### `print-image`
|
|
259
|
+
Inspect a flash image file without touching a device: prints the
|
|
260
|
+
partition table embedded in the image and, for each app partition that
|
|
261
|
+
contains a valid app, the project name, version, IDF version, compile
|
|
262
|
+
time, ELF SHA256, and target chip.
|
|
263
|
+
```text
|
|
264
|
+
idftool print-image build/full-flash.img
|
|
265
|
+
```
|
|
266
|
+
|
|
262
267
|
### Bundles
|
|
263
268
|
|
|
264
269
|
A bundle is a plain ZIP file containing one `*.bin` per partition (named
|
|
@@ -292,6 +297,79 @@ to match) instead of reading the table from flash.
|
|
|
292
297
|
idftool write-bundle release.zip
|
|
293
298
|
```
|
|
294
299
|
|
|
300
|
+
#### `print-bundle`
|
|
301
|
+
Inspect a bundle ZIP without touching a device: prints the partition
|
|
302
|
+
table from the embedded `partition_table.csv` and, for each app
|
|
303
|
+
partition whose `.bin` is present in the bundle, the project name,
|
|
304
|
+
version, IDF version, compile time, ELF SHA256, and target chip.
|
|
305
|
+
```text
|
|
306
|
+
idftool print-bundle release.zip
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Partition table
|
|
310
|
+
|
|
311
|
+
These commands work directly on a partition table as a file — the table
|
|
312
|
+
is the subject, passed as a positional argument. This is distinct from the
|
|
313
|
+
global `--partition-table-file` option, which overrides the layout *other*
|
|
314
|
+
commands use to address partitions by name. Input format (CSV or binary)
|
|
315
|
+
is auto-detected; output format is inferred from the file extension
|
|
316
|
+
(`.csv`/`.bin`) or set with `--format`.
|
|
317
|
+
|
|
318
|
+
When the input is a CSV that includes a bootloader row, pass
|
|
319
|
+
`--primary-bootloader-offset` (an offset or a chip name, e.g. `esp32s3`)
|
|
320
|
+
so the offset can be resolved.
|
|
321
|
+
|
|
322
|
+
#### `convert-table`
|
|
323
|
+
Convert a partition table between CSV and binary, offline. Handles both
|
|
324
|
+
directions; the binary output includes the MD5 checksum and padding, so it
|
|
325
|
+
is ready to flash or embed in an image.
|
|
326
|
+
```text
|
|
327
|
+
idftool convert-table partitions.csv partitions.bin
|
|
328
|
+
idftool convert-table partitions.bin partitions.csv
|
|
329
|
+
idftool --primary-bootloader-offset esp32s3 convert-table partitions.csv partitions.bin
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### `print-table`
|
|
333
|
+
Pretty-print a partition table. Given a CSV or binary file it runs offline;
|
|
334
|
+
with no file it reads the table from the device (or from
|
|
335
|
+
`--partition-table-file`). With a connected ESP, idftool also reads the
|
|
336
|
+
application descriptor of every app partition (project name, version) and
|
|
337
|
+
marks the currently-active OTA slot with a trailing `*`; the selected otadata
|
|
338
|
+
copy is shown next to the otadata partition's subtype (`ota (A)`, `ota (B)`,
|
|
339
|
+
or `ota (invalid)` when otadata is erased).
|
|
340
|
+
|
|
341
|
+
Aliased as `list`.
|
|
342
|
+
```text
|
|
343
|
+
idftool print-table partitions.bin # from a file, offline
|
|
344
|
+
idftool print-table # from the device
|
|
345
|
+
idftool list # same thing
|
|
346
|
+
idftool --partition-table-file partitions.csv print-table
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
#### `dump-table`
|
|
350
|
+
Read the partition table from the connected device and save it. Defaults to
|
|
351
|
+
CSV with an auto-generated filename; pass an output file or `--format` to
|
|
352
|
+
choose otherwise.
|
|
353
|
+
```text
|
|
354
|
+
idftool dump-table # auto-named .csv
|
|
355
|
+
idftool dump-table backup.bin
|
|
356
|
+
idftool dump-table backup.csv --format csv
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
#### `write-table`
|
|
360
|
+
Flash a partition table from a CSV or binary file to the device. The file is
|
|
361
|
+
required, so this never reads the device's current table and writes it back
|
|
362
|
+
to itself — use `dump-table` to pull the current table. The table is verified
|
|
363
|
+
before flashing (override with `--force`).
|
|
364
|
+
|
|
365
|
+
Only the partition map (at the partition table offset) is replaced; existing
|
|
366
|
+
partition **data** on flash is not moved, resized, or erased, so a table that
|
|
367
|
+
no longer matches the flash contents can make the device unbootable.
|
|
368
|
+
```text
|
|
369
|
+
idftool write-table partitions.csv
|
|
370
|
+
idftool write-table partitions.bin --force
|
|
371
|
+
```
|
|
372
|
+
|
|
295
373
|
### NVS
|
|
296
374
|
|
|
297
375
|
idftool can generate a binary NVS (Non-Volatile Storage) partition image
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
.gitignore
|
|
2
|
+
CHANGELOG.md
|
|
2
3
|
CONTRIBUTING.md
|
|
3
4
|
LICENSE.md
|
|
4
5
|
Makefile
|
|
@@ -17,4 +18,6 @@ idftool.egg-info/SOURCES.txt
|
|
|
17
18
|
idftool.egg-info/dependency_links.txt
|
|
18
19
|
idftool.egg-info/entry_points.txt
|
|
19
20
|
idftool.egg-info/requires.txt
|
|
21
|
+
idftool.egg-info/scm_file_list.json
|
|
22
|
+
idftool.egg-info/scm_version.json
|
|
20
23
|
idftool.egg-info/top_level.txt
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
"README.md",
|
|
4
|
+
"Makefile",
|
|
5
|
+
"idftool.spec",
|
|
6
|
+
"pyproject.toml",
|
|
7
|
+
"CHANGELOG.md",
|
|
8
|
+
"CONTRIBUTING.md",
|
|
9
|
+
"LICENSE.md",
|
|
10
|
+
"idftool-onedir.spec",
|
|
11
|
+
".gitignore",
|
|
12
|
+
"idftool.py",
|
|
13
|
+
"idftool/__init__.py",
|
|
14
|
+
"idftool/traceback.py",
|
|
15
|
+
"idftool/__main__.py",
|
|
16
|
+
".github/workflows/build.yml",
|
|
17
|
+
".github/workflows/publish.yml"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|