idftool 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {idftool-0.1.0 → idftool-0.2.0}/PKG-INFO +131 -44
- {idftool-0.1.0 → idftool-0.2.0}/README.md +129 -43
- {idftool-0.1.0 → idftool-0.2.0}/idftool/__main__.py +83 -2
- idftool-0.2.0/idftool/traceback.py +114 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool.egg-info/PKG-INFO +131 -44
- {idftool-0.1.0 → idftool-0.2.0}/idftool.egg-info/SOURCES.txt +1 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool.egg-info/requires.txt +1 -0
- {idftool-0.1.0 → idftool-0.2.0}/pyproject.toml +1 -0
- {idftool-0.1.0 → idftool-0.2.0}/.github/workflows/build.yml +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/.github/workflows/publish.yml +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/.gitignore +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/CONTRIBUTING.md +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/LICENSE.md +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/Makefile +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool/__init__.py +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool-onedir.spec +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool.egg-info/dependency_links.txt +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool.egg-info/entry_points.txt +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool.egg-info/top_level.txt +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool.py +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/idftool.spec +0 -0
- {idftool-0.1.0 → idftool-0.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: idftool
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.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
|
|
@@ -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,6 +11,8 @@ 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
17
|
from esp_idf_defs.otadata import OtaDataParameters, OtaDataSelectEntry
|
|
16
18
|
from esp_idf_defs.partitions import PARTITION_TABLE_SIZE, PARTITION_TABLE_OFFSET, PartitionTable, print_partition_table, \
|
|
@@ -523,6 +525,48 @@ def command_factory(esp: ESPLoader, partition_table: PartitionTable, app_binary_
|
|
|
523
525
|
print("Erasing 'otadata' partition...")
|
|
524
526
|
esp.erase_region(offset=otadata_partition.offset, size=otadata_partition.size)
|
|
525
527
|
|
|
528
|
+
def generate_nvs_image(csv_file: str, size: int) -> bytes:
|
|
529
|
+
import tempfile
|
|
530
|
+
from esp_idf_nvs_partition_gen import nvs_partition_gen
|
|
531
|
+
|
|
532
|
+
with tempfile.NamedTemporaryFile(suffix='.bin', delete=False) as tmp:
|
|
533
|
+
tmp_path = tmp.name
|
|
534
|
+
try:
|
|
535
|
+
nvs_args = argparse.Namespace(
|
|
536
|
+
input=csv_file,
|
|
537
|
+
output=os.path.basename(tmp_path),
|
|
538
|
+
outdir=os.path.dirname(tmp_path),
|
|
539
|
+
size=f"{size:#x}",
|
|
540
|
+
version=2,
|
|
541
|
+
)
|
|
542
|
+
nvs_partition_gen.generate(nvs_args)
|
|
543
|
+
with open(tmp_path, 'rb') as f:
|
|
544
|
+
return f.read()
|
|
545
|
+
finally:
|
|
546
|
+
if os.path.exists(tmp_path):
|
|
547
|
+
os.unlink(tmp_path)
|
|
548
|
+
|
|
549
|
+
def command_create_nvs(csv_file: str, size: int, output_file: str):
|
|
550
|
+
print(f"Generating NVS image from '{csv_file}' (size={size:#x})...")
|
|
551
|
+
image = generate_nvs_image(csv_file, size)
|
|
552
|
+
with open(output_file, 'wb') as f:
|
|
553
|
+
f.write(image)
|
|
554
|
+
print(f"Wrote {len(image):#x} bytes to '{output_file}'")
|
|
555
|
+
|
|
556
|
+
def command_write_nvs(
|
|
557
|
+
esp: ESPLoader,
|
|
558
|
+
partition_table: PartitionTable,
|
|
559
|
+
partition_table_entry: PartitionDefinition,
|
|
560
|
+
bootloader_entry: Optional[PartitionDefinition],
|
|
561
|
+
csv_file: str,
|
|
562
|
+
partition_name: str
|
|
563
|
+
):
|
|
564
|
+
partition = get_partition(partition_table, partition_table_entry, bootloader_entry, partition_name)
|
|
565
|
+
print(f"Generating NVS image from '{csv_file}' for partition '{partition.name}' (size={partition.size:#x})...")
|
|
566
|
+
image = generate_nvs_image(csv_file, partition.size)
|
|
567
|
+
print(f"Writing NVS image to partition '{partition.name}' (offset={partition.offset:#x}, size={partition.size:#x})")
|
|
568
|
+
write_flash(esp=esp, addr_data=[(partition.offset, image)], flash_size='detect')
|
|
569
|
+
|
|
526
570
|
def command_app_info(app_binary_file: str):
|
|
527
571
|
app_binary = open(app_binary_file, 'rb').read()
|
|
528
572
|
try:
|
|
@@ -640,8 +684,13 @@ def main(args):
|
|
|
640
684
|
command_enter_bootloader(port=args.port, baud=args.baud)
|
|
641
685
|
return
|
|
642
686
|
|
|
687
|
+
# create-nvs with explicit --size needs neither device nor partition table
|
|
688
|
+
if args.command == 'create-nvs' and args.size is not None:
|
|
689
|
+
command_create_nvs(args.csv_file, args.size, args.output)
|
|
690
|
+
return
|
|
691
|
+
|
|
643
692
|
# Connect to ESP device if required
|
|
644
|
-
requires_esp = args.command not in ['list', 'create-image', 'create-bundle'] or not args.partition_table_file
|
|
693
|
+
requires_esp = args.command not in ['list', 'create-image', 'create-bundle', 'create-nvs'] or not args.partition_table_file
|
|
645
694
|
esp = get_esp(port=args.port, baud=args.baud) if requires_esp else None
|
|
646
695
|
if not args.primary_bootloader_offset and esp:
|
|
647
696
|
args.primary_bootloader_offset = esp.BOOTLOADER_FLASH_OFFSET
|
|
@@ -794,12 +843,25 @@ def main(args):
|
|
|
794
843
|
command_factory(esp=esp, partition_table=partition_table, app_binary_file=args.app_binary_file)
|
|
795
844
|
elif args.command == 'write-image':
|
|
796
845
|
command_write_image(esp=esp, bootloader_entry=bootloader_entry, image_file_path=args.image_file)
|
|
846
|
+
elif args.command == 'create-nvs':
|
|
847
|
+
partition = get_partition(partition_table, partition_table_entry, bootloader_entry, args.partition)
|
|
848
|
+
command_create_nvs(args.csv_file, partition.size, args.output)
|
|
849
|
+
elif args.command == 'write-nvs':
|
|
850
|
+
command_write_nvs(
|
|
851
|
+
esp=esp,
|
|
852
|
+
partition_table=partition_table,
|
|
853
|
+
partition_table_entry=partition_table_entry,
|
|
854
|
+
bootloader_entry=bootloader_entry,
|
|
855
|
+
csv_file=args.csv_file,
|
|
856
|
+
partition_name=args.partition
|
|
857
|
+
)
|
|
797
858
|
else:
|
|
798
859
|
print(f"Unknown command: {args.command}", file=sys.stderr)
|
|
799
860
|
|
|
800
861
|
if esp and not args.no_reset: esp.hard_reset()
|
|
801
862
|
|
|
802
863
|
def _main():
|
|
864
|
+
install_excepthook()
|
|
803
865
|
parser = argparse.ArgumentParser()
|
|
804
866
|
|
|
805
867
|
parser.add_argument('-p', '--port', help='Serial port device')
|
|
@@ -888,6 +950,19 @@ def _main():
|
|
|
888
950
|
write_bundle_parser = subparsers.add_parser('write-bundle', help='Write a ZIP bundle of partition binaries')
|
|
889
951
|
write_bundle_parser.add_argument('input_file', help='Input ZIP filename')
|
|
890
952
|
|
|
953
|
+
# Create NVS subcommand
|
|
954
|
+
create_nvs_parser = subparsers.add_parser('create-nvs', help='Generate NVS partition image from CSV')
|
|
955
|
+
create_nvs_parser.add_argument('csv_file', help='Input NVS CSV file')
|
|
956
|
+
create_nvs_parser.add_argument('-o', '--output', required=True, help='Output binary filename (.bin)')
|
|
957
|
+
create_nvs_size_group = create_nvs_parser.add_mutually_exclusive_group(required=True)
|
|
958
|
+
create_nvs_size_group.add_argument('--size', type=auto_int, help='Partition size in bytes (e.g. 0x6000)')
|
|
959
|
+
create_nvs_size_group.add_argument('--partition', help='Partition name to read size from partition table')
|
|
960
|
+
|
|
961
|
+
# Write NVS subcommand
|
|
962
|
+
write_nvs_parser = subparsers.add_parser('write-nvs', help='Generate NVS partition image from CSV and write to device')
|
|
963
|
+
write_nvs_parser.add_argument('partition', help='NVS partition name')
|
|
964
|
+
write_nvs_parser.add_argument('csv_file', help='Input NVS CSV file')
|
|
965
|
+
|
|
891
966
|
# Factory subcommand
|
|
892
967
|
factory_parser = subparsers.add_parser('factory', help='Perform factory flash and clear boot partition')
|
|
893
968
|
factory_parser.add_argument('app_binary_file', help='Input file containing app binary')
|
|
@@ -914,7 +989,13 @@ def _main():
|
|
|
914
989
|
args.command = 'write-image'
|
|
915
990
|
if args.command == 'enter-bootloader' and not args.port:
|
|
916
991
|
parser.error("enter-bootloader requires -p/--port")
|
|
917
|
-
|
|
992
|
+
try:
|
|
993
|
+
main(args)
|
|
994
|
+
except KeyboardInterrupt:
|
|
995
|
+
sys.exit(130)
|
|
996
|
+
except BaseException as e:
|
|
997
|
+
print_exception(e)
|
|
998
|
+
sys.exit(1)
|
|
918
999
|
|
|
919
1000
|
if __name__ == "__main__":
|
|
920
1001
|
_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.
|
|
3
|
+
Version: 0.2.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
|
|
@@ -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
|