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')