idftool 0.1.0__tar.gz → 0.2.1__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.1.0 → idftool-0.2.1}/PKG-INFO +131 -44
- {idftool-0.1.0 → idftool-0.2.1}/README.md +129 -43
- {idftool-0.1.0 → idftool-0.2.1}/idftool/__main__.py +86 -4
- idftool-0.2.1/idftool/traceback.py +114 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool.egg-info/PKG-INFO +131 -44
- {idftool-0.1.0 → idftool-0.2.1}/idftool.egg-info/SOURCES.txt +1 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool.egg-info/requires.txt +1 -0
- {idftool-0.1.0 → idftool-0.2.1}/pyproject.toml +1 -0
- {idftool-0.1.0 → idftool-0.2.1}/.github/workflows/build.yml +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/.github/workflows/publish.yml +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/.gitignore +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/CONTRIBUTING.md +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/LICENSE.md +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/Makefile +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool/__init__.py +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool-onedir.spec +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool.egg-info/dependency_links.txt +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool.egg-info/entry_points.txt +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool.egg-info/top_level.txt +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool.py +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/idftool.spec +0 -0
- {idftool-0.1.0 → idftool-0.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: idftool
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
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
|
|
@@ -9,6 +9,7 @@ Description-Content-Type: text/markdown
|
|
|
9
9
|
License-File: LICENSE.md
|
|
10
10
|
Requires-Dist: esptool~=5.2.0
|
|
11
11
|
Requires-Dist: esp-idf-defs~=0.1.0
|
|
12
|
+
Requires-Dist: esp-idf-nvs-partition-gen~=0.2.0
|
|
12
13
|
Dynamic: license-file
|
|
13
14
|
|
|
14
15
|
# idftool
|
|
@@ -39,9 +40,31 @@ especially when dealing with multiple devices/partition tables.
|
|
|
39
40
|
partition binaries into a single ZIP; optionally flashing a partition
|
|
40
41
|
table. `write-bundle` reflashes the lot in one command.
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
### pipx (recommended)
|
|
46
|
+
|
|
47
|
+
[pipx](https://pipx.pypa.io) installs idftool into an isolated environment and
|
|
48
|
+
puts it on your `PATH` automatically:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pipx install idftool
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Install pipx first if you don't have it:
|
|
55
|
+
|
|
56
|
+
| Platform | Command |
|
|
57
|
+
|----------|---------|
|
|
58
|
+
| macOS | `brew install pipx` |
|
|
59
|
+
| Windows | `winget install python.pipx` |
|
|
60
|
+
| Linux / other | `pip install pipx` |
|
|
61
|
+
|
|
62
|
+
### Binary
|
|
63
|
+
|
|
64
|
+
If you don't have Python, download the pre-built binary from the
|
|
65
|
+
[Releases](https://github.com/nebkat/idftool/releases) page. Prefer the `-dir`
|
|
66
|
+
archive over the single-file download — it starts instantly rather than extracting
|
|
67
|
+
itself on each run.
|
|
45
68
|
|
|
46
69
|
## At a glance
|
|
47
70
|
|
|
@@ -72,50 +95,41 @@ $ idftool set-boot ota_1
|
|
|
72
95
|
Setting boot partition to 'ota_1'...
|
|
73
96
|
```
|
|
74
97
|
|
|
75
|
-
## Global options
|
|
76
|
-
|
|
77
|
-
These flags apply to every subcommand and go **before** the command name:
|
|
78
|
-
|
|
79
|
-
| Flag | Purpose |
|
|
80
|
-
|------|---------|
|
|
81
|
-
| `-p`, `--port PATH` | Serial port device. If omitted, idftool auto-picks one. |
|
|
82
|
-
| `-b`, `--baud N` | Serial baud rate (defaults to esptool's ROM baud, 115200). |
|
|
83
|
-
| `--no-reset` | Skip the hard reset that normally happens after a command. |
|
|
84
|
-
| `--partition-table-file PATH` | Use a CSV or binary partition table from disk instead of reading it off the device. |
|
|
85
|
-
| `--partition-table-offset OFFSET` | Where to expect the partition table in flash (default `0x8000`). |
|
|
86
|
-
| `--partition-table-size SIZE` | Size of the partition table region (default `0x1000`). |
|
|
87
|
-
| `--primary-bootloader-offset OFFSET` | Primary bootloader offset, or a chip name like `esp32s3` to pick a default. Only needed when addressing the `bootloader` partition by name in **offline** mode (`--partition-table-file` with no device); auto-detected from a connected chip otherwise. |
|
|
88
|
-
| `--recovery-bootloader-offset OFFSET` | Recovery bootloader offset; same scope as `--primary-bootloader-offset`. |
|
|
89
|
-
|
|
90
|
-
The commands `list`, `merge-bin`, and `create-bundle` will work **without**
|
|
91
|
-
a device when you supply `--partition-table-file`; everything else needs a
|
|
92
|
-
connected ESP.
|
|
93
|
-
|
|
94
|
-
## Partition addressing
|
|
95
|
-
|
|
96
|
-
Wherever a command takes a `partition` argument you can pass:
|
|
97
|
-
|
|
98
|
-
- A **name** from the partition table (`nvs`, `ota_0`, `storage`, …).
|
|
99
|
-
- A **numeric address** that matches an existing partition's start
|
|
100
|
-
offset exactly — equivalent to looking the partition up by name, just
|
|
101
|
-
keyed on its address.
|
|
102
|
-
- An **offset into a partition**: `name[offset]`. Negative values count
|
|
103
|
-
from the end. Sets the starting point for the operation. Accepted by
|
|
104
|
-
`write` and `merge-bin`.
|
|
105
|
-
- A **slice of a partition**: `name[start:stop]`. Negative values count
|
|
106
|
-
from the end, and a `+N` stop is a length relative to `start`.
|
|
107
|
-
Accepted by `read`, `erase`, and `view`. Examples:
|
|
108
|
-
- `nvs[0:0x100]` — first 256 bytes of `nvs`
|
|
109
|
-
- `storage[-0x1000:]` — last 4 KiB of `storage`
|
|
110
|
-
- `ota_0[0x1000:+0x800]` — 2 KiB starting 4 KiB into `ota_0`
|
|
111
|
-
|
|
112
|
-
All numeric values in addresses, offsets, and sizes accept either
|
|
113
|
-
decimal (`4096`) or hex (`0x1000`).
|
|
114
|
-
|
|
115
98
|
---
|
|
116
99
|
|
|
117
100
|
## Command reference
|
|
118
101
|
|
|
102
|
+
| Command | Description |
|
|
103
|
+
|---------|-------------|
|
|
104
|
+
| **Discovery** | |
|
|
105
|
+
| [`devices`](#devices) | List serial ports with hardware IDs |
|
|
106
|
+
| [`list`](#list) | Print the partition table |
|
|
107
|
+
| **Partition I/O** | |
|
|
108
|
+
| [`read`](#read) | Read a partition (or slice) into a file |
|
|
109
|
+
| [`write`](#write) | Write one or more files to named partitions |
|
|
110
|
+
| [`erase`](#erase) | Erase a partition (or slice) |
|
|
111
|
+
| [`view`](#view) | Pretty-print a partition's contents |
|
|
112
|
+
| **Firmware** | |
|
|
113
|
+
| [`ota`](#ota) | Push an app to the next OTA slot and switch to it |
|
|
114
|
+
| [`factory`](#factory) | Flash an app to the factory partition |
|
|
115
|
+
| **Boot selection** | |
|
|
116
|
+
| [`get-boot`](#get-boot) | Show the currently-active OTA slot |
|
|
117
|
+
| [`set-boot`](#set-boot) | Force the next boot to a specific OTA partition |
|
|
118
|
+
| [`clear-boot`](#clear-boot) | Erase otadata and let the bootloader fall back |
|
|
119
|
+
| **Images** | |
|
|
120
|
+
| [`create-image`](#create-image) | Merge partition binaries into a single flash image |
|
|
121
|
+
| [`dump-image`](#dump-image) | Dump the entire flash to an image file |
|
|
122
|
+
| [`write-image`](#write-image-alias-reflash) | Write a full flash image to the device |
|
|
123
|
+
| **Bundles** | |
|
|
124
|
+
| [`create-bundle`](#create-bundle) | Pack partition images into a ZIP bundle |
|
|
125
|
+
| [`dump-bundle`](#dump-bundle) | Pack every partition from the device into a ZIP |
|
|
126
|
+
| [`write-bundle`](#write-bundle) | Flash every binary in a bundle ZIP |
|
|
127
|
+
| **NVS** | |
|
|
128
|
+
| [`create-nvs`](#create-nvs) | Generate an NVS partition image from a CSV file |
|
|
129
|
+
| [`write-nvs`](#write-nvs) | Generate an NVS image from CSV and flash it |
|
|
130
|
+
| **Misc** | |
|
|
131
|
+
| [`enter-bootloader`](#enter-bootloader) | Drop the chip into ROM bootloader mode |
|
|
132
|
+
|
|
119
133
|
### Discovery
|
|
120
134
|
|
|
121
135
|
#### `devices`
|
|
@@ -278,6 +292,37 @@ to match) instead of reading the table from flash.
|
|
|
278
292
|
idftool write-bundle release.zip
|
|
279
293
|
```
|
|
280
294
|
|
|
295
|
+
### NVS
|
|
296
|
+
|
|
297
|
+
idftool can generate a binary NVS (Non-Volatile Storage) partition image
|
|
298
|
+
from a CSV file, using the same format as ESP-IDF's
|
|
299
|
+
[`nvs_partition_gen.py`](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_partition_gen.html).
|
|
300
|
+
|
|
301
|
+
Example CSV:
|
|
302
|
+
```csv
|
|
303
|
+
key,type,encoding,value
|
|
304
|
+
storage,namespace,,
|
|
305
|
+
device_name,data,string,My Device
|
|
306
|
+
device_id,data,u32,12345
|
|
307
|
+
api_key,data,string,abc123def456
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### `create-nvs`
|
|
311
|
+
Generate an NVS partition image from a CSV file, offline. Requires either
|
|
312
|
+
`--size` (explicit partition size) or `--partition` (look up the size from
|
|
313
|
+
the partition table — needs `--partition-table-file` or a device).
|
|
314
|
+
```text
|
|
315
|
+
idftool create-nvs nvs.csv -o nvs.bin --size 0x6000
|
|
316
|
+
idftool --partition-table-file partitions.csv create-nvs nvs.csv -o nvs.bin --partition nvs
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### `write-nvs`
|
|
320
|
+
Generate an NVS partition image from a CSV file and flash it to the named
|
|
321
|
+
partition on the device.
|
|
322
|
+
```text
|
|
323
|
+
idftool write-nvs nvs nvs.csv
|
|
324
|
+
```
|
|
325
|
+
|
|
281
326
|
### Misc
|
|
282
327
|
|
|
283
328
|
#### `enter-bootloader`
|
|
@@ -291,3 +336,45 @@ isn't fully settled) are retried silently.
|
|
|
291
336
|
idftool -p /dev/cu.usbmodem1101 enter-bootloader
|
|
292
337
|
```
|
|
293
338
|
Requires `-p`/`--port`.
|
|
339
|
+
|
|
340
|
+
--
|
|
341
|
+
|
|
342
|
+
## Global options
|
|
343
|
+
|
|
344
|
+
These flags apply to every subcommand and go **before** the command name:
|
|
345
|
+
|
|
346
|
+
| Flag | Purpose |
|
|
347
|
+
|------|---------|
|
|
348
|
+
| `-p`, `--port PATH` | Serial port device. If omitted, idftool auto-picks one. |
|
|
349
|
+
| `-b`, `--baud N` | Serial baud rate (defaults to esptool's ROM baud, 115200). |
|
|
350
|
+
| `--no-reset` | Skip the hard reset that normally happens after a command. |
|
|
351
|
+
| `--partition-table-file PATH` | Use a CSV or binary partition table from disk instead of reading it off the device. |
|
|
352
|
+
| `--partition-table-offset OFFSET` | Where to expect the partition table in flash (default `0x8000`). |
|
|
353
|
+
| `--partition-table-size SIZE` | Size of the partition table region (default `0x1000`). |
|
|
354
|
+
| `--primary-bootloader-offset OFFSET` | Primary bootloader offset, or a chip name like `esp32s3` to pick a default. Only needed when addressing the `bootloader` partition by name in **offline** mode (`--partition-table-file` with no device); auto-detected from a connected chip otherwise. |
|
|
355
|
+
| `--recovery-bootloader-offset OFFSET` | Recovery bootloader offset; same scope as `--primary-bootloader-offset`. |
|
|
356
|
+
|
|
357
|
+
The commands `list`, `merge-bin`, and `create-bundle` will work **without**
|
|
358
|
+
a device when you supply `--partition-table-file`; everything else needs a
|
|
359
|
+
connected ESP.
|
|
360
|
+
|
|
361
|
+
## Partition addressing
|
|
362
|
+
|
|
363
|
+
Wherever a command takes a `partition` argument you can pass:
|
|
364
|
+
|
|
365
|
+
- A **name** from the partition table (`nvs`, `ota_0`, `storage`, …).
|
|
366
|
+
- A **numeric address** that matches an existing partition's start
|
|
367
|
+
offset exactly — equivalent to looking the partition up by name, just
|
|
368
|
+
keyed on its address.
|
|
369
|
+
- An **offset into a partition**: `name[offset]`. Negative values count
|
|
370
|
+
from the end. Sets the starting point for the operation. Accepted by
|
|
371
|
+
`write` and `merge-bin`.
|
|
372
|
+
- A **slice of a partition**: `name[start:stop]`. Negative values count
|
|
373
|
+
from the end, and a `+N` stop is a length relative to `start`.
|
|
374
|
+
Accepted by `read`, `erase`, and `view`. Examples:
|
|
375
|
+
- `nvs[0:0x100]` — first 256 bytes of `nvs`
|
|
376
|
+
- `storage[-0x1000:]` — last 4 KiB of `storage`
|
|
377
|
+
- `ota_0[0x1000:+0x800]` — 2 KiB starting 4 KiB into `ota_0`
|
|
378
|
+
|
|
379
|
+
All numeric values in addresses, offsets, and sizes accept either
|
|
380
|
+
decimal (`4096`) or hex (`0x1000`).
|
|
@@ -26,9 +26,31 @@ especially when dealing with multiple devices/partition tables.
|
|
|
26
26
|
partition binaries into a single ZIP; optionally flashing a partition
|
|
27
27
|
table. `write-bundle` reflashes the lot in one command.
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
### pipx (recommended)
|
|
32
|
+
|
|
33
|
+
[pipx](https://pipx.pypa.io) installs idftool into an isolated environment and
|
|
34
|
+
puts it on your `PATH` automatically:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pipx install idftool
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Install pipx first if you don't have it:
|
|
41
|
+
|
|
42
|
+
| Platform | Command |
|
|
43
|
+
|----------|---------|
|
|
44
|
+
| macOS | `brew install pipx` |
|
|
45
|
+
| Windows | `winget install python.pipx` |
|
|
46
|
+
| Linux / other | `pip install pipx` |
|
|
47
|
+
|
|
48
|
+
### Binary
|
|
49
|
+
|
|
50
|
+
If you don't have Python, download the pre-built binary from the
|
|
51
|
+
[Releases](https://github.com/nebkat/idftool/releases) page. Prefer the `-dir`
|
|
52
|
+
archive over the single-file download — it starts instantly rather than extracting
|
|
53
|
+
itself on each run.
|
|
32
54
|
|
|
33
55
|
## At a glance
|
|
34
56
|
|
|
@@ -59,50 +81,41 @@ $ idftool set-boot ota_1
|
|
|
59
81
|
Setting boot partition to 'ota_1'...
|
|
60
82
|
```
|
|
61
83
|
|
|
62
|
-
## Global options
|
|
63
|
-
|
|
64
|
-
These flags apply to every subcommand and go **before** the command name:
|
|
65
|
-
|
|
66
|
-
| Flag | Purpose |
|
|
67
|
-
|------|---------|
|
|
68
|
-
| `-p`, `--port PATH` | Serial port device. If omitted, idftool auto-picks one. |
|
|
69
|
-
| `-b`, `--baud N` | Serial baud rate (defaults to esptool's ROM baud, 115200). |
|
|
70
|
-
| `--no-reset` | Skip the hard reset that normally happens after a command. |
|
|
71
|
-
| `--partition-table-file PATH` | Use a CSV or binary partition table from disk instead of reading it off the device. |
|
|
72
|
-
| `--partition-table-offset OFFSET` | Where to expect the partition table in flash (default `0x8000`). |
|
|
73
|
-
| `--partition-table-size SIZE` | Size of the partition table region (default `0x1000`). |
|
|
74
|
-
| `--primary-bootloader-offset OFFSET` | Primary bootloader offset, or a chip name like `esp32s3` to pick a default. Only needed when addressing the `bootloader` partition by name in **offline** mode (`--partition-table-file` with no device); auto-detected from a connected chip otherwise. |
|
|
75
|
-
| `--recovery-bootloader-offset OFFSET` | Recovery bootloader offset; same scope as `--primary-bootloader-offset`. |
|
|
76
|
-
|
|
77
|
-
The commands `list`, `merge-bin`, and `create-bundle` will work **without**
|
|
78
|
-
a device when you supply `--partition-table-file`; everything else needs a
|
|
79
|
-
connected ESP.
|
|
80
|
-
|
|
81
|
-
## Partition addressing
|
|
82
|
-
|
|
83
|
-
Wherever a command takes a `partition` argument you can pass:
|
|
84
|
-
|
|
85
|
-
- A **name** from the partition table (`nvs`, `ota_0`, `storage`, …).
|
|
86
|
-
- A **numeric address** that matches an existing partition's start
|
|
87
|
-
offset exactly — equivalent to looking the partition up by name, just
|
|
88
|
-
keyed on its address.
|
|
89
|
-
- An **offset into a partition**: `name[offset]`. Negative values count
|
|
90
|
-
from the end. Sets the starting point for the operation. Accepted by
|
|
91
|
-
`write` and `merge-bin`.
|
|
92
|
-
- A **slice of a partition**: `name[start:stop]`. Negative values count
|
|
93
|
-
from the end, and a `+N` stop is a length relative to `start`.
|
|
94
|
-
Accepted by `read`, `erase`, and `view`. Examples:
|
|
95
|
-
- `nvs[0:0x100]` — first 256 bytes of `nvs`
|
|
96
|
-
- `storage[-0x1000:]` — last 4 KiB of `storage`
|
|
97
|
-
- `ota_0[0x1000:+0x800]` — 2 KiB starting 4 KiB into `ota_0`
|
|
98
|
-
|
|
99
|
-
All numeric values in addresses, offsets, and sizes accept either
|
|
100
|
-
decimal (`4096`) or hex (`0x1000`).
|
|
101
|
-
|
|
102
84
|
---
|
|
103
85
|
|
|
104
86
|
## Command reference
|
|
105
87
|
|
|
88
|
+
| Command | Description |
|
|
89
|
+
|---------|-------------|
|
|
90
|
+
| **Discovery** | |
|
|
91
|
+
| [`devices`](#devices) | List serial ports with hardware IDs |
|
|
92
|
+
| [`list`](#list) | Print the partition table |
|
|
93
|
+
| **Partition I/O** | |
|
|
94
|
+
| [`read`](#read) | Read a partition (or slice) into a file |
|
|
95
|
+
| [`write`](#write) | Write one or more files to named partitions |
|
|
96
|
+
| [`erase`](#erase) | Erase a partition (or slice) |
|
|
97
|
+
| [`view`](#view) | Pretty-print a partition's contents |
|
|
98
|
+
| **Firmware** | |
|
|
99
|
+
| [`ota`](#ota) | Push an app to the next OTA slot and switch to it |
|
|
100
|
+
| [`factory`](#factory) | Flash an app to the factory partition |
|
|
101
|
+
| **Boot selection** | |
|
|
102
|
+
| [`get-boot`](#get-boot) | Show the currently-active OTA slot |
|
|
103
|
+
| [`set-boot`](#set-boot) | Force the next boot to a specific OTA partition |
|
|
104
|
+
| [`clear-boot`](#clear-boot) | Erase otadata and let the bootloader fall back |
|
|
105
|
+
| **Images** | |
|
|
106
|
+
| [`create-image`](#create-image) | Merge partition binaries into a single flash image |
|
|
107
|
+
| [`dump-image`](#dump-image) | Dump the entire flash to an image file |
|
|
108
|
+
| [`write-image`](#write-image-alias-reflash) | Write a full flash image to the device |
|
|
109
|
+
| **Bundles** | |
|
|
110
|
+
| [`create-bundle`](#create-bundle) | Pack partition images into a ZIP bundle |
|
|
111
|
+
| [`dump-bundle`](#dump-bundle) | Pack every partition from the device into a ZIP |
|
|
112
|
+
| [`write-bundle`](#write-bundle) | Flash every binary in a bundle ZIP |
|
|
113
|
+
| **NVS** | |
|
|
114
|
+
| [`create-nvs`](#create-nvs) | Generate an NVS partition image from a CSV file |
|
|
115
|
+
| [`write-nvs`](#write-nvs) | Generate an NVS image from CSV and flash it |
|
|
116
|
+
| **Misc** | |
|
|
117
|
+
| [`enter-bootloader`](#enter-bootloader) | Drop the chip into ROM bootloader mode |
|
|
118
|
+
|
|
106
119
|
### Discovery
|
|
107
120
|
|
|
108
121
|
#### `devices`
|
|
@@ -265,6 +278,37 @@ to match) instead of reading the table from flash.
|
|
|
265
278
|
idftool write-bundle release.zip
|
|
266
279
|
```
|
|
267
280
|
|
|
281
|
+
### NVS
|
|
282
|
+
|
|
283
|
+
idftool can generate a binary NVS (Non-Volatile Storage) partition image
|
|
284
|
+
from a CSV file, using the same format as ESP-IDF's
|
|
285
|
+
[`nvs_partition_gen.py`](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_partition_gen.html).
|
|
286
|
+
|
|
287
|
+
Example CSV:
|
|
288
|
+
```csv
|
|
289
|
+
key,type,encoding,value
|
|
290
|
+
storage,namespace,,
|
|
291
|
+
device_name,data,string,My Device
|
|
292
|
+
device_id,data,u32,12345
|
|
293
|
+
api_key,data,string,abc123def456
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### `create-nvs`
|
|
297
|
+
Generate an NVS partition image from a CSV file, offline. Requires either
|
|
298
|
+
`--size` (explicit partition size) or `--partition` (look up the size from
|
|
299
|
+
the partition table — needs `--partition-table-file` or a device).
|
|
300
|
+
```text
|
|
301
|
+
idftool create-nvs nvs.csv -o nvs.bin --size 0x6000
|
|
302
|
+
idftool --partition-table-file partitions.csv create-nvs nvs.csv -o nvs.bin --partition nvs
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### `write-nvs`
|
|
306
|
+
Generate an NVS partition image from a CSV file and flash it to the named
|
|
307
|
+
partition on the device.
|
|
308
|
+
```text
|
|
309
|
+
idftool write-nvs nvs nvs.csv
|
|
310
|
+
```
|
|
311
|
+
|
|
268
312
|
### Misc
|
|
269
313
|
|
|
270
314
|
#### `enter-bootloader`
|
|
@@ -278,3 +322,45 @@ isn't fully settled) are retried silently.
|
|
|
278
322
|
idftool -p /dev/cu.usbmodem1101 enter-bootloader
|
|
279
323
|
```
|
|
280
324
|
Requires `-p`/`--port`.
|
|
325
|
+
|
|
326
|
+
--
|
|
327
|
+
|
|
328
|
+
## Global options
|
|
329
|
+
|
|
330
|
+
These flags apply to every subcommand and go **before** the command name:
|
|
331
|
+
|
|
332
|
+
| Flag | Purpose |
|
|
333
|
+
|------|---------|
|
|
334
|
+
| `-p`, `--port PATH` | Serial port device. If omitted, idftool auto-picks one. |
|
|
335
|
+
| `-b`, `--baud N` | Serial baud rate (defaults to esptool's ROM baud, 115200). |
|
|
336
|
+
| `--no-reset` | Skip the hard reset that normally happens after a command. |
|
|
337
|
+
| `--partition-table-file PATH` | Use a CSV or binary partition table from disk instead of reading it off the device. |
|
|
338
|
+
| `--partition-table-offset OFFSET` | Where to expect the partition table in flash (default `0x8000`). |
|
|
339
|
+
| `--partition-table-size SIZE` | Size of the partition table region (default `0x1000`). |
|
|
340
|
+
| `--primary-bootloader-offset OFFSET` | Primary bootloader offset, or a chip name like `esp32s3` to pick a default. Only needed when addressing the `bootloader` partition by name in **offline** mode (`--partition-table-file` with no device); auto-detected from a connected chip otherwise. |
|
|
341
|
+
| `--recovery-bootloader-offset OFFSET` | Recovery bootloader offset; same scope as `--primary-bootloader-offset`. |
|
|
342
|
+
|
|
343
|
+
The commands `list`, `merge-bin`, and `create-bundle` will work **without**
|
|
344
|
+
a device when you supply `--partition-table-file`; everything else needs a
|
|
345
|
+
connected ESP.
|
|
346
|
+
|
|
347
|
+
## Partition addressing
|
|
348
|
+
|
|
349
|
+
Wherever a command takes a `partition` argument you can pass:
|
|
350
|
+
|
|
351
|
+
- A **name** from the partition table (`nvs`, `ota_0`, `storage`, …).
|
|
352
|
+
- A **numeric address** that matches an existing partition's start
|
|
353
|
+
offset exactly — equivalent to looking the partition up by name, just
|
|
354
|
+
keyed on its address.
|
|
355
|
+
- An **offset into a partition**: `name[offset]`. Negative values count
|
|
356
|
+
from the end. Sets the starting point for the operation. Accepted by
|
|
357
|
+
`write` and `merge-bin`.
|
|
358
|
+
- A **slice of a partition**: `name[start:stop]`. Negative values count
|
|
359
|
+
from the end, and a `+N` stop is a length relative to `start`.
|
|
360
|
+
Accepted by `read`, `erase`, and `view`. Examples:
|
|
361
|
+
- `nvs[0:0x100]` — first 256 bytes of `nvs`
|
|
362
|
+
- `storage[-0x1000:]` — last 4 KiB of `storage`
|
|
363
|
+
- `ota_0[0x1000:+0x800]` — 2 KiB starting 4 KiB into `ota_0`
|
|
364
|
+
|
|
365
|
+
All numeric values in addresses, offsets, and sizes accept either
|
|
366
|
+
decimal (`4096`) or hex (`0x1000`).
|
|
@@ -11,8 +11,10 @@ from serial import SerialException
|
|
|
11
11
|
from serial.tools import list_ports
|
|
12
12
|
from serial.tools.list_ports_common import ListPortInfo
|
|
13
13
|
|
|
14
|
+
from idftool.traceback import install as install_excepthook, print_exception
|
|
15
|
+
|
|
14
16
|
from esp_idf_defs import ImageMetadata, ChipId
|
|
15
|
-
from esp_idf_defs.otadata import OtaDataParameters, OtaDataSelectEntry
|
|
17
|
+
from esp_idf_defs.otadata import OtaDataParameters, OtaDataSelectEntry, OtaImageState
|
|
16
18
|
from esp_idf_defs.partitions import PARTITION_TABLE_SIZE, PARTITION_TABLE_OFFSET, PartitionTable, print_partition_table, \
|
|
17
19
|
PartitionDefinition, BOOTLOADER_TYPE, SUBTYPES, PARTITION_TABLE_TYPE, APP_TYPE, NUM_PARTITION_SUBTYPE_APP_OTA, \
|
|
18
20
|
DATA_TYPE
|
|
@@ -449,7 +451,7 @@ def command_get_boot(esp: ESPLoader, partition_table: PartitionTable):
|
|
|
449
451
|
if otadata.slot is None:
|
|
450
452
|
print("OTA slot not set")
|
|
451
453
|
else:
|
|
452
|
-
print(f"OTA slot 'ota_{otadata.slot}' (seq={otadata.otadata.seq}, state={otadata.otadata.ota_state})")
|
|
454
|
+
print(f"OTA slot 'ota_{otadata.slot}' (seq={otadata.otadata.seq}, state={otadata.otadata.ota_state.name})")
|
|
453
455
|
|
|
454
456
|
def command_set_boot(esp: ESPLoader, partition_table: PartitionTable, label: str):
|
|
455
457
|
otadata_partition, otadata = read_otadata(esp, partition_table)
|
|
@@ -463,6 +465,7 @@ def command_set_boot(esp: ESPLoader, partition_table: PartitionTable, label: str
|
|
|
463
465
|
|
|
464
466
|
print(f"Setting boot partition to '{partition.name}'...")
|
|
465
467
|
otadata = otadata.incremented_and_swapped(ota_slot)
|
|
468
|
+
otadata.otadata.ota_state = OtaImageState.VALID
|
|
466
469
|
write_otadata(esp, otadata_partition, otadata)
|
|
467
470
|
|
|
468
471
|
def command_clear_boot(esp: ESPLoader, partition_table: PartitionTable):
|
|
@@ -523,6 +526,48 @@ def command_factory(esp: ESPLoader, partition_table: PartitionTable, app_binary_
|
|
|
523
526
|
print("Erasing 'otadata' partition...")
|
|
524
527
|
esp.erase_region(offset=otadata_partition.offset, size=otadata_partition.size)
|
|
525
528
|
|
|
529
|
+
def generate_nvs_image(csv_file: str, size: int) -> bytes:
|
|
530
|
+
import tempfile
|
|
531
|
+
from esp_idf_nvs_partition_gen import nvs_partition_gen
|
|
532
|
+
|
|
533
|
+
with tempfile.NamedTemporaryFile(suffix='.bin', delete=False) as tmp:
|
|
534
|
+
tmp_path = tmp.name
|
|
535
|
+
try:
|
|
536
|
+
nvs_args = argparse.Namespace(
|
|
537
|
+
input=csv_file,
|
|
538
|
+
output=os.path.basename(tmp_path),
|
|
539
|
+
outdir=os.path.dirname(tmp_path),
|
|
540
|
+
size=f"{size:#x}",
|
|
541
|
+
version=2,
|
|
542
|
+
)
|
|
543
|
+
nvs_partition_gen.generate(nvs_args)
|
|
544
|
+
with open(tmp_path, 'rb') as f:
|
|
545
|
+
return f.read()
|
|
546
|
+
finally:
|
|
547
|
+
if os.path.exists(tmp_path):
|
|
548
|
+
os.unlink(tmp_path)
|
|
549
|
+
|
|
550
|
+
def command_create_nvs(csv_file: str, size: int, output_file: str):
|
|
551
|
+
print(f"Generating NVS image from '{csv_file}' (size={size:#x})...")
|
|
552
|
+
image = generate_nvs_image(csv_file, size)
|
|
553
|
+
with open(output_file, 'wb') as f:
|
|
554
|
+
f.write(image)
|
|
555
|
+
print(f"Wrote {len(image):#x} bytes to '{output_file}'")
|
|
556
|
+
|
|
557
|
+
def command_write_nvs(
|
|
558
|
+
esp: ESPLoader,
|
|
559
|
+
partition_table: PartitionTable,
|
|
560
|
+
partition_table_entry: PartitionDefinition,
|
|
561
|
+
bootloader_entry: Optional[PartitionDefinition],
|
|
562
|
+
csv_file: str,
|
|
563
|
+
partition_name: str
|
|
564
|
+
):
|
|
565
|
+
partition = get_partition(partition_table, partition_table_entry, bootloader_entry, partition_name)
|
|
566
|
+
print(f"Generating NVS image from '{csv_file}' for partition '{partition.name}' (size={partition.size:#x})...")
|
|
567
|
+
image = generate_nvs_image(csv_file, partition.size)
|
|
568
|
+
print(f"Writing NVS image to partition '{partition.name}' (offset={partition.offset:#x}, size={partition.size:#x})")
|
|
569
|
+
write_flash(esp=esp, addr_data=[(partition.offset, image)], flash_size='detect')
|
|
570
|
+
|
|
526
571
|
def command_app_info(app_binary_file: str):
|
|
527
572
|
app_binary = open(app_binary_file, 'rb').read()
|
|
528
573
|
try:
|
|
@@ -640,8 +685,13 @@ def main(args):
|
|
|
640
685
|
command_enter_bootloader(port=args.port, baud=args.baud)
|
|
641
686
|
return
|
|
642
687
|
|
|
688
|
+
# create-nvs with explicit --size needs neither device nor partition table
|
|
689
|
+
if args.command == 'create-nvs' and args.size is not None:
|
|
690
|
+
command_create_nvs(args.csv_file, args.size, args.output)
|
|
691
|
+
return
|
|
692
|
+
|
|
643
693
|
# Connect to ESP device if required
|
|
644
|
-
requires_esp = args.command not in ['list', 'create-image', 'create-bundle'] or not args.partition_table_file
|
|
694
|
+
requires_esp = args.command not in ['list', 'create-image', 'create-bundle', 'create-nvs'] or not args.partition_table_file
|
|
645
695
|
esp = get_esp(port=args.port, baud=args.baud) if requires_esp else None
|
|
646
696
|
if not args.primary_bootloader_offset and esp:
|
|
647
697
|
args.primary_bootloader_offset = esp.BOOTLOADER_FLASH_OFFSET
|
|
@@ -794,12 +844,25 @@ def main(args):
|
|
|
794
844
|
command_factory(esp=esp, partition_table=partition_table, app_binary_file=args.app_binary_file)
|
|
795
845
|
elif args.command == 'write-image':
|
|
796
846
|
command_write_image(esp=esp, bootloader_entry=bootloader_entry, image_file_path=args.image_file)
|
|
847
|
+
elif args.command == 'create-nvs':
|
|
848
|
+
partition = get_partition(partition_table, partition_table_entry, bootloader_entry, args.partition)
|
|
849
|
+
command_create_nvs(args.csv_file, partition.size, args.output)
|
|
850
|
+
elif args.command == 'write-nvs':
|
|
851
|
+
command_write_nvs(
|
|
852
|
+
esp=esp,
|
|
853
|
+
partition_table=partition_table,
|
|
854
|
+
partition_table_entry=partition_table_entry,
|
|
855
|
+
bootloader_entry=bootloader_entry,
|
|
856
|
+
csv_file=args.csv_file,
|
|
857
|
+
partition_name=args.partition
|
|
858
|
+
)
|
|
797
859
|
else:
|
|
798
860
|
print(f"Unknown command: {args.command}", file=sys.stderr)
|
|
799
861
|
|
|
800
862
|
if esp and not args.no_reset: esp.hard_reset()
|
|
801
863
|
|
|
802
864
|
def _main():
|
|
865
|
+
install_excepthook()
|
|
803
866
|
parser = argparse.ArgumentParser()
|
|
804
867
|
|
|
805
868
|
parser.add_argument('-p', '--port', help='Serial port device')
|
|
@@ -888,6 +951,19 @@ def _main():
|
|
|
888
951
|
write_bundle_parser = subparsers.add_parser('write-bundle', help='Write a ZIP bundle of partition binaries')
|
|
889
952
|
write_bundle_parser.add_argument('input_file', help='Input ZIP filename')
|
|
890
953
|
|
|
954
|
+
# Create NVS subcommand
|
|
955
|
+
create_nvs_parser = subparsers.add_parser('create-nvs', help='Generate NVS partition image from CSV')
|
|
956
|
+
create_nvs_parser.add_argument('csv_file', help='Input NVS CSV file')
|
|
957
|
+
create_nvs_parser.add_argument('-o', '--output', required=True, help='Output binary filename (.bin)')
|
|
958
|
+
create_nvs_size_group = create_nvs_parser.add_mutually_exclusive_group(required=True)
|
|
959
|
+
create_nvs_size_group.add_argument('--size', type=auto_int, help='Partition size in bytes (e.g. 0x6000)')
|
|
960
|
+
create_nvs_size_group.add_argument('--partition', help='Partition name to read size from partition table')
|
|
961
|
+
|
|
962
|
+
# Write NVS subcommand
|
|
963
|
+
write_nvs_parser = subparsers.add_parser('write-nvs', help='Generate NVS partition image from CSV and write to device')
|
|
964
|
+
write_nvs_parser.add_argument('partition', help='NVS partition name')
|
|
965
|
+
write_nvs_parser.add_argument('csv_file', help='Input NVS CSV file')
|
|
966
|
+
|
|
891
967
|
# Factory subcommand
|
|
892
968
|
factory_parser = subparsers.add_parser('factory', help='Perform factory flash and clear boot partition')
|
|
893
969
|
factory_parser.add_argument('app_binary_file', help='Input file containing app binary')
|
|
@@ -914,7 +990,13 @@ def _main():
|
|
|
914
990
|
args.command = 'write-image'
|
|
915
991
|
if args.command == 'enter-bootloader' and not args.port:
|
|
916
992
|
parser.error("enter-bootloader requires -p/--port")
|
|
917
|
-
|
|
993
|
+
try:
|
|
994
|
+
main(args)
|
|
995
|
+
except KeyboardInterrupt:
|
|
996
|
+
sys.exit(130)
|
|
997
|
+
except BaseException as e:
|
|
998
|
+
print_exception(e)
|
|
999
|
+
sys.exit(1)
|
|
918
1000
|
|
|
919
1001
|
if __name__ == "__main__":
|
|
920
1002
|
_main()
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import traceback as _tb
|
|
4
|
+
from types import TracebackType
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _term_width(default: int = 80) -> int:
|
|
9
|
+
try:
|
|
10
|
+
return os.get_terminal_size().columns
|
|
11
|
+
except OSError:
|
|
12
|
+
return default
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _box(lines: list[str], title: str, width: int) -> list[str]:
|
|
16
|
+
inner = max(width - 4, 4)
|
|
17
|
+
|
|
18
|
+
title_str = f"─ {title} "
|
|
19
|
+
if len(title_str) + 3 <= width:
|
|
20
|
+
top = '╭' + title_str + '─' * (width - 2 - len(title_str)) + '╮'
|
|
21
|
+
else:
|
|
22
|
+
top = '╭' + '─' * (width - 2) + '╮'
|
|
23
|
+
|
|
24
|
+
body = []
|
|
25
|
+
for line in lines:
|
|
26
|
+
line = line.expandtabs(4)
|
|
27
|
+
if len(line) > inner:
|
|
28
|
+
line = line[:inner - 1] + '…'
|
|
29
|
+
body.append('│ ' + line.ljust(inner) + ' │')
|
|
30
|
+
|
|
31
|
+
bottom = '╰' + '─' * (width - 2) + '╯'
|
|
32
|
+
|
|
33
|
+
return [top] + body + [bottom]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _walk_chain(exc: BaseException) -> list[tuple[BaseException, Optional[str]]]:
|
|
37
|
+
"""Walk __cause__ / __context__ chain, return (exception, separator_after) oldest-first."""
|
|
38
|
+
chain: list[tuple[BaseException, Optional[str]]] = []
|
|
39
|
+
e: Optional[BaseException] = exc
|
|
40
|
+
# First build a list of (exc, link_type_to_previous) from newest to oldest
|
|
41
|
+
newest_to_oldest: list[tuple[BaseException, Optional[str]]] = []
|
|
42
|
+
while e is not None:
|
|
43
|
+
if e.__cause__ is not None:
|
|
44
|
+
link = "cause"
|
|
45
|
+
prev: Optional[BaseException] = e.__cause__
|
|
46
|
+
elif not e.__suppress_context__ and e.__context__ is not None:
|
|
47
|
+
link = "context"
|
|
48
|
+
prev = e.__context__
|
|
49
|
+
else:
|
|
50
|
+
link = None
|
|
51
|
+
prev = None
|
|
52
|
+
newest_to_oldest.append((e, link))
|
|
53
|
+
e = prev
|
|
54
|
+
|
|
55
|
+
# Reverse to oldest-first. The link recorded with each exception describes
|
|
56
|
+
# how the NEXT (newer) exception relates to this one — i.e. the separator
|
|
57
|
+
# we should print AFTER this exception.
|
|
58
|
+
oldest_to_newest = list(reversed(newest_to_oldest))
|
|
59
|
+
result: list[tuple[BaseException, Optional[str]]] = []
|
|
60
|
+
for i, (exc_i, _) in enumerate(oldest_to_newest):
|
|
61
|
+
if i + 1 < len(oldest_to_newest):
|
|
62
|
+
next_link = oldest_to_newest[i + 1][1]
|
|
63
|
+
if next_link == "cause":
|
|
64
|
+
sep: Optional[str] = "The above exception was the direct cause of the following exception:"
|
|
65
|
+
else:
|
|
66
|
+
sep = "During handling of the above exception, another exception occurred:"
|
|
67
|
+
else:
|
|
68
|
+
sep = None
|
|
69
|
+
result.append((exc_i, sep))
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _format_one(exc: BaseException, *, color: bool, width: int) -> list[str]:
|
|
74
|
+
frames = _tb.extract_tb(exc.__traceback__)
|
|
75
|
+
frame_lines = [f"in {f.name}:{f.lineno} ({f.filename})" for f in frames]
|
|
76
|
+
if not frame_lines:
|
|
77
|
+
frame_lines = ["<no traceback available>"]
|
|
78
|
+
|
|
79
|
+
box_lines = _box(frame_lines, "Traceback (most recent call last)", width)
|
|
80
|
+
err_line = f"{type(exc).__name__}: {exc}"
|
|
81
|
+
|
|
82
|
+
if color:
|
|
83
|
+
box_lines = [f"\033[2m{l}\033[0m" for l in box_lines]
|
|
84
|
+
err_line = f"\033[1;31m{err_line}\033[0m"
|
|
85
|
+
|
|
86
|
+
return box_lines + [err_line]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def print_exception(exc: BaseException) -> None:
|
|
90
|
+
"""Print an exception (and its chain) in rich-style boxed format to stderr."""
|
|
91
|
+
color = sys.stderr.isatty()
|
|
92
|
+
width = min(_term_width(), 120)
|
|
93
|
+
|
|
94
|
+
chain = _walk_chain(exc)
|
|
95
|
+
for i, (e, sep) in enumerate(chain):
|
|
96
|
+
if i > 0:
|
|
97
|
+
print(file=sys.stderr)
|
|
98
|
+
for line in _format_one(e, color=color, width=width):
|
|
99
|
+
print(line, file=sys.stderr)
|
|
100
|
+
if sep is not None:
|
|
101
|
+
print(file=sys.stderr)
|
|
102
|
+
print(sep, file=sys.stderr)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _excepthook(exc_type: type, exc_value: BaseException, exc_tb: Optional[TracebackType]) -> None:
|
|
106
|
+
if issubclass(exc_type, KeyboardInterrupt):
|
|
107
|
+
sys.__excepthook__(exc_type, exc_value, exc_tb)
|
|
108
|
+
return
|
|
109
|
+
print_exception(exc_value)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def install() -> None:
|
|
113
|
+
"""Replace sys.excepthook with a rich-style boxed formatter."""
|
|
114
|
+
sys.excepthook = _excepthook
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: idftool
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
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
|
|
@@ -9,6 +9,7 @@ Description-Content-Type: text/markdown
|
|
|
9
9
|
License-File: LICENSE.md
|
|
10
10
|
Requires-Dist: esptool~=5.2.0
|
|
11
11
|
Requires-Dist: esp-idf-defs~=0.1.0
|
|
12
|
+
Requires-Dist: esp-idf-nvs-partition-gen~=0.2.0
|
|
12
13
|
Dynamic: license-file
|
|
13
14
|
|
|
14
15
|
# idftool
|
|
@@ -39,9 +40,31 @@ especially when dealing with multiple devices/partition tables.
|
|
|
39
40
|
partition binaries into a single ZIP; optionally flashing a partition
|
|
40
41
|
table. `write-bundle` reflashes the lot in one command.
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
### pipx (recommended)
|
|
46
|
+
|
|
47
|
+
[pipx](https://pipx.pypa.io) installs idftool into an isolated environment and
|
|
48
|
+
puts it on your `PATH` automatically:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pipx install idftool
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Install pipx first if you don't have it:
|
|
55
|
+
|
|
56
|
+
| Platform | Command |
|
|
57
|
+
|----------|---------|
|
|
58
|
+
| macOS | `brew install pipx` |
|
|
59
|
+
| Windows | `winget install python.pipx` |
|
|
60
|
+
| Linux / other | `pip install pipx` |
|
|
61
|
+
|
|
62
|
+
### Binary
|
|
63
|
+
|
|
64
|
+
If you don't have Python, download the pre-built binary from the
|
|
65
|
+
[Releases](https://github.com/nebkat/idftool/releases) page. Prefer the `-dir`
|
|
66
|
+
archive over the single-file download — it starts instantly rather than extracting
|
|
67
|
+
itself on each run.
|
|
45
68
|
|
|
46
69
|
## At a glance
|
|
47
70
|
|
|
@@ -72,50 +95,41 @@ $ idftool set-boot ota_1
|
|
|
72
95
|
Setting boot partition to 'ota_1'...
|
|
73
96
|
```
|
|
74
97
|
|
|
75
|
-
## Global options
|
|
76
|
-
|
|
77
|
-
These flags apply to every subcommand and go **before** the command name:
|
|
78
|
-
|
|
79
|
-
| Flag | Purpose |
|
|
80
|
-
|------|---------|
|
|
81
|
-
| `-p`, `--port PATH` | Serial port device. If omitted, idftool auto-picks one. |
|
|
82
|
-
| `-b`, `--baud N` | Serial baud rate (defaults to esptool's ROM baud, 115200). |
|
|
83
|
-
| `--no-reset` | Skip the hard reset that normally happens after a command. |
|
|
84
|
-
| `--partition-table-file PATH` | Use a CSV or binary partition table from disk instead of reading it off the device. |
|
|
85
|
-
| `--partition-table-offset OFFSET` | Where to expect the partition table in flash (default `0x8000`). |
|
|
86
|
-
| `--partition-table-size SIZE` | Size of the partition table region (default `0x1000`). |
|
|
87
|
-
| `--primary-bootloader-offset OFFSET` | Primary bootloader offset, or a chip name like `esp32s3` to pick a default. Only needed when addressing the `bootloader` partition by name in **offline** mode (`--partition-table-file` with no device); auto-detected from a connected chip otherwise. |
|
|
88
|
-
| `--recovery-bootloader-offset OFFSET` | Recovery bootloader offset; same scope as `--primary-bootloader-offset`. |
|
|
89
|
-
|
|
90
|
-
The commands `list`, `merge-bin`, and `create-bundle` will work **without**
|
|
91
|
-
a device when you supply `--partition-table-file`; everything else needs a
|
|
92
|
-
connected ESP.
|
|
93
|
-
|
|
94
|
-
## Partition addressing
|
|
95
|
-
|
|
96
|
-
Wherever a command takes a `partition` argument you can pass:
|
|
97
|
-
|
|
98
|
-
- A **name** from the partition table (`nvs`, `ota_0`, `storage`, …).
|
|
99
|
-
- A **numeric address** that matches an existing partition's start
|
|
100
|
-
offset exactly — equivalent to looking the partition up by name, just
|
|
101
|
-
keyed on its address.
|
|
102
|
-
- An **offset into a partition**: `name[offset]`. Negative values count
|
|
103
|
-
from the end. Sets the starting point for the operation. Accepted by
|
|
104
|
-
`write` and `merge-bin`.
|
|
105
|
-
- A **slice of a partition**: `name[start:stop]`. Negative values count
|
|
106
|
-
from the end, and a `+N` stop is a length relative to `start`.
|
|
107
|
-
Accepted by `read`, `erase`, and `view`. Examples:
|
|
108
|
-
- `nvs[0:0x100]` — first 256 bytes of `nvs`
|
|
109
|
-
- `storage[-0x1000:]` — last 4 KiB of `storage`
|
|
110
|
-
- `ota_0[0x1000:+0x800]` — 2 KiB starting 4 KiB into `ota_0`
|
|
111
|
-
|
|
112
|
-
All numeric values in addresses, offsets, and sizes accept either
|
|
113
|
-
decimal (`4096`) or hex (`0x1000`).
|
|
114
|
-
|
|
115
98
|
---
|
|
116
99
|
|
|
117
100
|
## Command reference
|
|
118
101
|
|
|
102
|
+
| Command | Description |
|
|
103
|
+
|---------|-------------|
|
|
104
|
+
| **Discovery** | |
|
|
105
|
+
| [`devices`](#devices) | List serial ports with hardware IDs |
|
|
106
|
+
| [`list`](#list) | Print the partition table |
|
|
107
|
+
| **Partition I/O** | |
|
|
108
|
+
| [`read`](#read) | Read a partition (or slice) into a file |
|
|
109
|
+
| [`write`](#write) | Write one or more files to named partitions |
|
|
110
|
+
| [`erase`](#erase) | Erase a partition (or slice) |
|
|
111
|
+
| [`view`](#view) | Pretty-print a partition's contents |
|
|
112
|
+
| **Firmware** | |
|
|
113
|
+
| [`ota`](#ota) | Push an app to the next OTA slot and switch to it |
|
|
114
|
+
| [`factory`](#factory) | Flash an app to the factory partition |
|
|
115
|
+
| **Boot selection** | |
|
|
116
|
+
| [`get-boot`](#get-boot) | Show the currently-active OTA slot |
|
|
117
|
+
| [`set-boot`](#set-boot) | Force the next boot to a specific OTA partition |
|
|
118
|
+
| [`clear-boot`](#clear-boot) | Erase otadata and let the bootloader fall back |
|
|
119
|
+
| **Images** | |
|
|
120
|
+
| [`create-image`](#create-image) | Merge partition binaries into a single flash image |
|
|
121
|
+
| [`dump-image`](#dump-image) | Dump the entire flash to an image file |
|
|
122
|
+
| [`write-image`](#write-image-alias-reflash) | Write a full flash image to the device |
|
|
123
|
+
| **Bundles** | |
|
|
124
|
+
| [`create-bundle`](#create-bundle) | Pack partition images into a ZIP bundle |
|
|
125
|
+
| [`dump-bundle`](#dump-bundle) | Pack every partition from the device into a ZIP |
|
|
126
|
+
| [`write-bundle`](#write-bundle) | Flash every binary in a bundle ZIP |
|
|
127
|
+
| **NVS** | |
|
|
128
|
+
| [`create-nvs`](#create-nvs) | Generate an NVS partition image from a CSV file |
|
|
129
|
+
| [`write-nvs`](#write-nvs) | Generate an NVS image from CSV and flash it |
|
|
130
|
+
| **Misc** | |
|
|
131
|
+
| [`enter-bootloader`](#enter-bootloader) | Drop the chip into ROM bootloader mode |
|
|
132
|
+
|
|
119
133
|
### Discovery
|
|
120
134
|
|
|
121
135
|
#### `devices`
|
|
@@ -278,6 +292,37 @@ to match) instead of reading the table from flash.
|
|
|
278
292
|
idftool write-bundle release.zip
|
|
279
293
|
```
|
|
280
294
|
|
|
295
|
+
### NVS
|
|
296
|
+
|
|
297
|
+
idftool can generate a binary NVS (Non-Volatile Storage) partition image
|
|
298
|
+
from a CSV file, using the same format as ESP-IDF's
|
|
299
|
+
[`nvs_partition_gen.py`](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_partition_gen.html).
|
|
300
|
+
|
|
301
|
+
Example CSV:
|
|
302
|
+
```csv
|
|
303
|
+
key,type,encoding,value
|
|
304
|
+
storage,namespace,,
|
|
305
|
+
device_name,data,string,My Device
|
|
306
|
+
device_id,data,u32,12345
|
|
307
|
+
api_key,data,string,abc123def456
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### `create-nvs`
|
|
311
|
+
Generate an NVS partition image from a CSV file, offline. Requires either
|
|
312
|
+
`--size` (explicit partition size) or `--partition` (look up the size from
|
|
313
|
+
the partition table — needs `--partition-table-file` or a device).
|
|
314
|
+
```text
|
|
315
|
+
idftool create-nvs nvs.csv -o nvs.bin --size 0x6000
|
|
316
|
+
idftool --partition-table-file partitions.csv create-nvs nvs.csv -o nvs.bin --partition nvs
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### `write-nvs`
|
|
320
|
+
Generate an NVS partition image from a CSV file and flash it to the named
|
|
321
|
+
partition on the device.
|
|
322
|
+
```text
|
|
323
|
+
idftool write-nvs nvs nvs.csv
|
|
324
|
+
```
|
|
325
|
+
|
|
281
326
|
### Misc
|
|
282
327
|
|
|
283
328
|
#### `enter-bootloader`
|
|
@@ -291,3 +336,45 @@ isn't fully settled) are retried silently.
|
|
|
291
336
|
idftool -p /dev/cu.usbmodem1101 enter-bootloader
|
|
292
337
|
```
|
|
293
338
|
Requires `-p`/`--port`.
|
|
339
|
+
|
|
340
|
+
--
|
|
341
|
+
|
|
342
|
+
## Global options
|
|
343
|
+
|
|
344
|
+
These flags apply to every subcommand and go **before** the command name:
|
|
345
|
+
|
|
346
|
+
| Flag | Purpose |
|
|
347
|
+
|------|---------|
|
|
348
|
+
| `-p`, `--port PATH` | Serial port device. If omitted, idftool auto-picks one. |
|
|
349
|
+
| `-b`, `--baud N` | Serial baud rate (defaults to esptool's ROM baud, 115200). |
|
|
350
|
+
| `--no-reset` | Skip the hard reset that normally happens after a command. |
|
|
351
|
+
| `--partition-table-file PATH` | Use a CSV or binary partition table from disk instead of reading it off the device. |
|
|
352
|
+
| `--partition-table-offset OFFSET` | Where to expect the partition table in flash (default `0x8000`). |
|
|
353
|
+
| `--partition-table-size SIZE` | Size of the partition table region (default `0x1000`). |
|
|
354
|
+
| `--primary-bootloader-offset OFFSET` | Primary bootloader offset, or a chip name like `esp32s3` to pick a default. Only needed when addressing the `bootloader` partition by name in **offline** mode (`--partition-table-file` with no device); auto-detected from a connected chip otherwise. |
|
|
355
|
+
| `--recovery-bootloader-offset OFFSET` | Recovery bootloader offset; same scope as `--primary-bootloader-offset`. |
|
|
356
|
+
|
|
357
|
+
The commands `list`, `merge-bin`, and `create-bundle` will work **without**
|
|
358
|
+
a device when you supply `--partition-table-file`; everything else needs a
|
|
359
|
+
connected ESP.
|
|
360
|
+
|
|
361
|
+
## Partition addressing
|
|
362
|
+
|
|
363
|
+
Wherever a command takes a `partition` argument you can pass:
|
|
364
|
+
|
|
365
|
+
- A **name** from the partition table (`nvs`, `ota_0`, `storage`, …).
|
|
366
|
+
- A **numeric address** that matches an existing partition's start
|
|
367
|
+
offset exactly — equivalent to looking the partition up by name, just
|
|
368
|
+
keyed on its address.
|
|
369
|
+
- An **offset into a partition**: `name[offset]`. Negative values count
|
|
370
|
+
from the end. Sets the starting point for the operation. Accepted by
|
|
371
|
+
`write` and `merge-bin`.
|
|
372
|
+
- A **slice of a partition**: `name[start:stop]`. Negative values count
|
|
373
|
+
from the end, and a `+N` stop is a length relative to `start`.
|
|
374
|
+
Accepted by `read`, `erase`, and `view`. Examples:
|
|
375
|
+
- `nvs[0:0x100]` — first 256 bytes of `nvs`
|
|
376
|
+
- `storage[-0x1000:]` — last 4 KiB of `storage`
|
|
377
|
+
- `ota_0[0x1000:+0x800]` — 2 KiB starting 4 KiB into `ota_0`
|
|
378
|
+
|
|
379
|
+
All numeric values in addresses, offsets, and sizes accept either
|
|
380
|
+
decimal (`4096`) or hex (`0x1000`).
|
|
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
|