2 # Copyright (c) 2020 Project CHIP Authors
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 """Flash an ESP32 device.
17 This is layered so that a caller can perform individual operations
18 through an `Flasher` instance, or operations according to a command line.
19 For `Flasher`, see the class documentation. For the parse_command()
20 interface or standalone execution:
22 usage: esp32_firmware_utils.py [-h] [--verbose] [--erase] [--application FILE]
23 [--verify_application] [--reset] [--skip_reset]
24 [--esptool FILE] [--parttool FILE]
25 [--sdkconfig FILE] [--chip CHIP] [--port PORT]
26 [--baud BAUD] [--before ACTION]
27 [--after ACTION] [--flash_mode MODE]
28 [--flash_freq FREQ] [--flash_size SIZE]
29 [--compress] [--bootloader FILE]
30 [--bootloader_offset OFFSET] [--partition FILE]
31 [--partition_offset OFFSET]
32 [--application_offset OFFSET]
37 -h, --help show this help message and exit
40 --verbose, -v Report more verbosely
41 --esptool FILE File name of the esptool executable
42 --parttool FILE File name of the parttool executable
43 --sdkconfig FILE File containing option defaults
44 --chip CHIP Target chip type
45 --port PORT Serial port device
46 --baud BAUD Serial port baud rate
47 --before ACTION What to do before connecting
48 --after ACTION What to do when finished
49 --flash_mode MODE, --flash-mode MODE
51 --flash_freq FREQ, --flash-freq FREQ
53 --flash_size SIZE, --flash-size SIZE
55 --compress, -z Compress data in transfer
56 --bootloader FILE Bootloader image
57 --bootloader_offset OFFSET, --bootloader-offset OFFSET
59 --partition FILE Partition table image
60 --partition_offset OFFSET, --partition-offset OFFSET
61 Partition table offset
62 --application_offset OFFSET, --application-offset OFFSET
67 --application FILE Flash an image
68 --verify_application, --verify-application
69 Verify the image after flashing
70 --reset Reset device after flashing
71 --skip_reset, --skip-reset
72 Do not reset device after flashing
80 # Additional options that can be use to configure an `Flasher`
81 # object (as dictionary keys) and/or passed as command line options.
83 # Configuration options define properties used in flashing operations.
85 # Tool configuration options.
87 'help': 'File name of the esptool executable',
93 {'option': 'esptool'},
96 {'optional': 'before'},
97 {'optional': 'after'},
100 'verify': ['{esptool}', 'version'],
103 Unable to execute {esptool}.
105 Please ensure that this tool is installed and
106 that $IDF_PATH is set. See the ESP32 example
107 README for installation instructions.
111 'help': 'File name of the parttool executable',
117 {'option': 'parttool'},
118 {'optional': 'port'},
119 {'optional': 'baud'},
121 'option': 'partition',
122 'result': ['--partition-table-file', '{partition}'],
127 'verify': ['{parttool}', '--quiet'],
130 Unable to execute {parttool}.
132 Please ensure that this tool is installed and
133 that $IDF_PATH is set. See the ESP32 example
134 README for installation instructions.
138 'help': 'File containing option defaults',
145 # Device configuration options.
147 'help': 'Target chip type',
152 'sdkconfig': 'CONFIG_IDF_TARGET',
155 'help': 'Serial port device',
160 'sdkconfig': 'CONFIG_ESPTOOLPY_PORT',
163 'help': 'Serial port baud rate',
168 'sdkconfig': 'CONFIG_ESPTOOLPY_BAUD',
171 'help': 'What to do before connecting',
176 'sdkconfig': 'CONFIG_ESPTOOLPY_BEFORE',
179 'help': 'What to do when finished',
184 'sdkconfig': 'CONFIG_ESPTOOLPY_AFTER',
187 'help': 'Flash mode',
192 'sdkconfig': 'CONFIG_ESPTOOLPY_FLASHMODE',
195 'help': 'Flash frequency',
200 'sdkconfig': 'CONFIG_ESPTOOLPY_FLASHFREQ',
203 'help': 'Flash size',
208 'sdkconfig': 'CONFIG_ESPTOOLPY_FLASHSIZE',
211 'help': 'Compress data in transfer',
215 'action': 'store_true'
217 'sdkconfig': 'CONFIG_ESPTOOLPY_COMPRESSED',
222 'help': 'Bootloader image',
228 'bootloader_offset': {
229 'help': 'Bootloader offset',
236 'help': 'Partition table image',
242 'partition_offset': {
243 'help': 'Partition table offset',
248 'sdkconfig': 'CONFIG_PARTITION_TABLE_OFFSET',
250 'application_offset': {
251 'help': 'Application offset',
261 def namespace_defaults(dst, src):
262 for key, value in src.items():
263 if key not in dst or getattr(dst, key) is None:
264 setattr(dst, key, value)
267 class Flasher(firmware_utils.Flasher):
268 """Manage esp32 flashing."""
270 def __init__(self, **options):
271 super().__init__(platform='ESP32', module=__name__, **options)
272 self.define_options(ESP32_OPTIONS)
274 def _postprocess_argv(self):
275 if self.option.sdkconfig:
276 namespace_defaults(self.option,
277 self.read_sdkconfig(self.option.sdkconfig))
278 # idf = os.environ.get('IDF_PATH')
280 # if self.option.esptool is None:
281 # self.option.esptool = os.path.join(
283 # 'components/esptool_py/esptool/esptool.py')
284 # if self.option.parttool is None:
285 # self.option.parttool = os.path.join(
287 # 'components/partition_table/parttool.py')
289 def read_sdkconfig(self, filename):
290 """Given an ESP32 sdkconfig file, read it for values of options
294 for key, info in vars(self.info).items():
295 config_key = info.get('sdkconfig')
297 config_map[config_key] = key
299 with open(filename) as f:
301 k, eq, v = line.strip().partition('=')
302 if eq == '=' and k in config_map:
303 result[config_map[k]] = v.strip('"')
307 'esptool': 'components/esptool_py/esptool/esptool.py',
308 'parttool': 'components/partition_table/parttool.py',
311 def locate_tool(self, tool):
312 if tool in self.IDF_PATH_TOOLS:
313 idf_path = os.environ.get('IDF_PATH')
315 return os.path.join(idf_path, self.IDF_PATH_TOOLS[tool])
316 return super().locate_tool(tool)
318 # Common command line arguments for esptool flashing subcommands.
320 {'optional': 'flash_mode'},
321 {'optional': 'flash_freq'},
322 {'optional': 'flash_size'},
324 'match': '{compress}',
325 'test': [(True, '-z'), ('y', '-z')],
331 """Perform `commander device masserase`."""
332 return self.run_tool('esptool', ['erase_flash'], {}, 'Erase device')
334 def verify(self, image, address=0):
335 """Verify image(s)."""
336 if not isinstance(image, list):
337 image = [address, image]
338 return self.run_tool(
340 ['verify_flash', self.FLASH_ARGUMENTS, image],
342 pass_message='Verified',
343 fail_message='Not verified',
346 def flash(self, image, address=0):
347 """Flash image(s)."""
348 if not isinstance(image, list):
349 image = [address, image]
350 return self.run_tool(
352 ['write_flash', self.FLASH_ARGUMENTS, image],
356 'phy': ['--partition-type', 'data', '--partition-subtype', 'phy'],
357 'application': ['--partition-boot-default'],
358 'ota': ['--partition-type', 'data', '--partition-subtype', 'ota'],
360 '--partition-type', 'app', '--partition-subtype', 'factory'
364 def get_partition_info(self, item, info, options=None):
365 """Run parttool to get partition information."""
366 return self.run_tool(
368 ['get_partition_info', self.PARTITION_INFO[item], '--info', info],
370 capture_output=True).stdout.strip()
372 def make_wrapper(self, argv):
373 self.parser.add_argument(
376 help='partition tool to configure flashing script')
377 self.parser.add_argument(
378 '--use-partition-file',
380 help='partition file to configure flashing script')
381 self.parser.add_argument(
384 help='sdkconfig to configure flashing script')
385 super().make_wrapper(argv)
387 def _platform_wrapper_args(self, args):
388 if args.use_sdkconfig:
389 # Include values from sdkconfig so that it isn't needed at
391 namespace_defaults(args, self.read_sdkconfig(args.use_sdkconfig))
392 parttool = args.use_parttool or args.parttool
393 partfile = args.use_partition_file or args.partition
394 if parttool and partfile:
395 # Get unspecified offsets from the partition file now,
396 # so that parttool isn't needed at flashing time.
397 if args.application and args.application_offset is None:
398 args.application_offset = self.get_partition_info(
399 'application', 'offset',
400 {'parttool': parttool, 'partition': partfile})
403 """Perform actions on the device according to self.option."""
404 self.log(3, 'Options:', self.option)
406 if self.option.erase:
410 bootloader = self.optional_file(self.option.bootloader)
411 application = self.optional_file(self.option.application)
412 partition = self.optional_file(self.option.partition)
414 if bootloader or application or partition:
415 # Collect the flashable items.
418 flash += [self.option.bootloader_offset, bootloader]
420 offset = self.option.application_offset
422 offset = self.get_partition_info('application', 'offset')
423 flash += [offset, application]
425 flash += [self.option.partition_offset, partition]
427 # esptool.py doesn't have an independent reset command, so we add
428 # an `--after` option to the final operation, which may be either
430 if self.option.after is None:
431 if self.option.reset or self.option.reset is None:
432 self.option.after = 'hard_reset'
434 self.option.after = 'no_reset'
435 if self.option.verify_application:
436 verify_after = self.option.after
437 self.option.after = 'no_reset'
439 if self.flash(flash).err:
441 if self.option.verify_application:
442 self.option.after = verify_after
443 if self.verify(flash).err:
448 ### Mobly integration
450 def __init__(self, flasher_args):
451 self.flasher = Flasher(**flasher_args)
454 self.flasher.flash_command([os.getcwd()])
456 def verify_platform_args(platform_args):
471 'application_offset',
473 difference = set(required_args) - set(platform_args)
475 raise ValueError("Required arguments missing: %s" % difference)
477 def create_platform(platform_args):
478 verify_platform_args(platform_args[0])
479 return ESP32Platform(platform_args[0])
481 ### End of Mobly integration
483 if __name__ == '__main__':
484 sys.exit(Flasher().flash_command(sys.argv))