Add ability to select a device for DFU flashing

This commit is contained in:
Roland Dobai 2020-05-26 17:35:40 +02:00
parent e1ec6c86e6
commit 0ff8ec66e4
7 changed files with 88 additions and 11 deletions

View File

@ -64,6 +64,22 @@ which relies on `dfu-util <http://dfu-util.sourceforge.net/>`_. 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:

View File

@ -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()

View File

@ -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()

View File

@ -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."
}
],
},
}
}

View File

@ -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"<I"
@ -126,8 +124,9 @@ def pad_bytes(b, multiple, padding=b"\x00"): # type: (bytes, int, bytes) -> 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 <address> <file> 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

Binary file not shown.

View File

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