diff --git a/docs/en/api-guides/dfu.rst b/docs/en/api-guides/dfu.rst
index 3d77a94d9..3a322c712 100644
--- a/docs/en/api-guides/dfu.rst
+++ b/docs/en/api-guides/dfu.rst
@@ -64,6 +64,22 @@ which relies on `dfu-util `_. Please see :ref:
installing ``dfu-util``. ``dfu-util`` needs additional setup for :ref:`api_guide_dfu_flash_win` or setting up an
:ref:`api_guide_dfu_flash_udev`. Mac OS users should be able to use ``dfu-util`` without further setup.
+If there are more boards with the same chip connected then ``idf.py dfu-list`` can be used to list the available
+devices, for example::
+
+ Found Runtime: [303a:0002] ver=0723, devnum=4, cfg=1, intf=2, path="1-10", alt=0, name="UNKNOWN", serial="0"
+ Found Runtime: [303a:0002] ver=0723, devnum=6, cfg=1, intf=2, path="1-2", alt=0, name="UNKNOWN", serial="0"
+
+Consequently, the desired device can be selected for flashing by the ``--path`` argument. For example, the devices
+listed above can be flashed individually by the following commands::
+
+ idf.py dfu-flash --path 1-10
+ idf.py dfu-flash --path 1-2
+
+.. note::
+ The vendor and product identificators are set based on the selected chip target by the ``idf.py set-target``
+ command and it is not selectable during the ``idf.py dfu-flash`` call.
+
See :ref:`api_guide_dfu_flash_errors` and their solutions.
.. _api_guide_dfu_flash_udev:
diff --git a/tools/cmake/dfu.cmake b/tools/cmake/dfu.cmake
index f29de4b58..7b68ff2b2 100644
--- a/tools/cmake/dfu.cmake
+++ b/tools/cmake/dfu.cmake
@@ -3,8 +3,12 @@
function(__add_dfu_targets)
idf_build_get_property(target IDF_TARGET)
- if(NOT "${target}" STREQUAL "esp32s2")
+ if("${target}" STREQUAL "esp32")
return()
+ elseif("${target}" STREQUAL "esp32s2")
+ set(dfu_pid "2")
+ else()
+ message(FATAL_ERROR "DFU PID unknown for ${target}")
endif()
idf_build_get_property(python PYTHON)
@@ -14,13 +18,21 @@ function(__add_dfu_targets)
COMMAND ${python} ${idf_path}/tools/mkdfu.py write
-o "${CMAKE_CURRENT_BINARY_DIR}/dfu.bin"
--json "${CMAKE_CURRENT_BINARY_DIR}/flasher_args.json"
+ --pid "${dfu_pid}"
DEPENDS gen_project_binary bootloader
VERBATIM
USES_TERMINAL)
+ add_custom_target(dfu-list
+ COMMAND ${CMAKE_COMMAND}
+ -D ESP_DFU_LIST="1"
+ -P ${idf_path}/tools/cmake/run_dfu_util.cmake
+ USES_TERMINAL)
+
add_custom_target(dfu-flash
- COMMAND dfu-util
- -D "${CMAKE_CURRENT_BINARY_DIR}/dfu.bin"
- VERBATIM
+ COMMAND ${CMAKE_COMMAND}
+ -D ESP_DFU_BIN="${CMAKE_CURRENT_BINARY_DIR}/dfu.bin"
+ -D ESP_DFU_PID="${dfu_pid}"
+ -P ${idf_path}/tools/cmake/run_dfu_util.cmake
USES_TERMINAL)
endfunction()
diff --git a/tools/cmake/run_dfu_util.cmake b/tools/cmake/run_dfu_util.cmake
new file mode 100644
index 000000000..1c7841170
--- /dev/null
+++ b/tools/cmake/run_dfu_util.cmake
@@ -0,0 +1,28 @@
+# A CMake script to run dfu-util from within ninja or make
+# or another cmake-based build runner
+#
+# It is recommended to NOT USE this CMake script directly
+
+cmake_minimum_required(VERSION 3.5)
+
+set(TOOL "dfu-util")
+set(CMD "${TOOL}")
+
+if(${ESP_DFU_LIST})
+ list(APPEND CMD "--list")
+else()
+ # The following works even when ESP_DFU_PID is not defined.
+ list(APPEND CMD "-d" "303a:${ESP_DFU_PID}")
+
+ if(NOT $ENV{ESP_DFU_PATH} STREQUAL "")
+ list(APPEND CMD "--path" $ENV{ESP_DFU_PATH})
+ endif()
+ list(APPEND CMD "-D" ${ESP_DFU_BIN})
+endif()
+
+message("Command list: ${CMD}")
+execute_process(COMMAND ${CMD} RESULT_VARIABLE result)
+
+if(${result})
+ message(FATAL_ERROR "${TOOL} failed")
+endif()
diff --git a/tools/idf_py_actions/dfu_ext.py b/tools/idf_py_actions/dfu_ext.py
index af8d2d7fa..6d70057be 100644
--- a/tools/idf_py_actions/dfu_ext.py
+++ b/tools/idf_py_actions/dfu_ext.py
@@ -10,11 +10,11 @@ def action_extensions(base_actions, project_path):
ensure_build_directory(args, ctx.info_name)
run_target(target_name, args)
- def dfu_flash_target(target_name, ctx, args):
+ def dfu_flash_target(target_name, ctx, args, path):
ensure_build_directory(args, ctx.info_name)
try:
- run_target(target_name, args)
+ run_target(target_name, args, {"ESP_DFU_PATH": path})
except FatalError:
# Cannot capture the error from dfu-util here so the best advise is:
print('Please have a look at the "Device Firmware Upgrade through USB" chapter in API Guides of the '
@@ -28,10 +28,24 @@ def action_extensions(base_actions, project_path):
"short_help": "Build the DFU binary",
"dependencies": ["all"],
},
+ "dfu-list": {
+ "callback": dfu_target,
+ "short_help": "List DFU capable devices",
+ "dependencies": [],
+ },
"dfu-flash": {
"callback": dfu_flash_target,
"short_help": "Flash the DFU binary",
"order_dependencies": ["dfu"],
+ "options": [
+ {
+ "names": ["--path"],
+ "default": "",
+ "help": "Specify path to DFU device. The default empty path works if there is just one "
+ "ESP device with the same product identificator. See the device list for paths "
+ "of available devices."
+ }
+ ],
},
}
}
diff --git a/tools/mkdfu.py b/tools/mkdfu.py
index d2e562c71..54109979f 100755
--- a/tools/mkdfu.py
+++ b/tools/mkdfu.py
@@ -107,8 +107,6 @@ DFUSuffix = namedtuple(
"DFUSuffix", ["bcd_device", "pid", "vid", "bcd_dfu", "sig", "len"]
)
ESPRESSIF_VID = 12346
-# TODO: set PID based on the chip type (add a command line argument)
-DFUSUFFIX_DEFAULT = DFUSuffix(0xFFFF, 0xFFFF, ESPRESSIF_VID, 0x0100, b"UFD", 16)
# This CRC32 gets added after DFUSUFFIX_STRUCT
DFUCRC_STRUCT = b" byt
class EspDfuWriter(object):
- def __init__(self, dest_file): # type: (typing.BinaryIO) -> None
+ def __init__(self, dest_file, pid): # type: (typing.BinaryIO) -> None
self.dest = dest_file
+ self.pid = pid
self.entries = [] # type: typing.List[bytes]
self.index = [] # type: typing.List[DFUInfo]
@@ -151,7 +150,8 @@ class EspDfuWriter(object):
out_data = pad_bytes(out_data, cpio_block_size)
# Add DFU suffix and CRC
- out_data += struct.pack(DFUSUFFIX_STRUCT, *DFUSUFFIX_DEFAULT)
+ dfu_suffix = DFUSuffix(0xFFFF, self.pid, ESPRESSIF_VID, 0x0100, b"UFD", 16)
+ out_data += struct.pack(DFUSUFFIX_STRUCT, *dfu_suffix)
out_data += struct.pack(DFUCRC_STRUCT, dfu_crc(out_data))
# Finally write the entire binary
@@ -187,7 +187,7 @@ class EspDfuWriter(object):
def action_write(args):
- writer = EspDfuWriter(args['output_file'])
+ writer = EspDfuWriter(args['output_file'], args['pid'])
for addr, f in args['files']:
print('Adding {} at {:#x}'.format(f, addr))
writer.add_file(addr, f)
@@ -205,6 +205,10 @@ def main():
help='Filename for storing the output DFU image',
required=True,
type=argparse.FileType("wb"))
+ write_parser.add_argument("--pid",
+ required=True,
+ type=lambda h: int(h, 16),
+ help='Hexa-decimal product indentificator')
write_parser.add_argument("--json",
help='Optional file for loading "flash_files" dictionary with items')
write_parser.add_argument("files",
@@ -241,6 +245,7 @@ def main():
cmd_args = {'output_file': args.output_file,
'files': files,
+ 'pid': args.pid,
}
{'write': action_write
diff --git a/tools/test_mkdfu/1/dfu.bin b/tools/test_mkdfu/1/dfu.bin
index 74f9fef52..cc28754f3 100644
Binary files a/tools/test_mkdfu/1/dfu.bin and b/tools/test_mkdfu/1/dfu.bin differ
diff --git a/tools/test_mkdfu/test_mkdfu.py b/tools/test_mkdfu/test_mkdfu.py
index 1ce60c227..130844171 100755
--- a/tools/test_mkdfu/test_mkdfu.py
+++ b/tools/test_mkdfu/test_mkdfu.py
@@ -35,6 +35,7 @@ class TestHelloWorldExample(unittest.TestCase):
self.addCleanup(os.unlink, f.name)
cmd = ' '.join([sys.executable, mkdfu_path, 'write',
'-o', f.name,
+ '--pid', '2',
add_args])
p = pexpect.spawn(cmd, timeout=10)
self.addCleanup(p.terminate, force=True)
@@ -81,6 +82,7 @@ class TestHelloWorldExample(unittest.TestCase):
cmd = ' '.join([sys.executable, mkdfu_path, 'write',
'-o', output,
+ '--pid', '2',
' '.join(['0x1000', bootloader,
'0x8000', os.path.join(current_dir, '1', '2.bin'),
'0x10000', os.path.join(current_dir, '1', '3.bin')