mirror of
https://github.com/espressif/esptool.git
synced 2025-10-21 23:30:34 +08:00
feat(bootloader reset): Allow custom reset strategy setting with a config file
This commit is contained in:
@@ -107,3 +107,42 @@ Complete list configurable options:
|
|||||||
+------------------------------+-----------------------------------------------------------+----------+
|
+------------------------------+-----------------------------------------------------------+----------+
|
||||||
| reset_delay | Time to wait before the boot pin is released after reset | 0.05 s |
|
| reset_delay | Time to wait before the boot pin is released after reset | 0.05 s |
|
||||||
+------------------------------+-----------------------------------------------------------+----------+
|
+------------------------------+-----------------------------------------------------------+----------+
|
||||||
|
| custom_reset_sequence | Custom reset sequence for resetting into the bootloader | |
|
||||||
|
+------------------------------+-----------------------------------------------------------+----------+
|
||||||
|
|
||||||
|
Custom Reset Sequence
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The ``custom_reset_sequence`` configuration option allows you to define a reset sequence which will get
|
||||||
|
used when an :ref:`automatic reset into the serial bootloader <automatic-bootloader>` is performed.
|
||||||
|
|
||||||
|
The sequence is defined with a string in the following format:
|
||||||
|
|
||||||
|
- Consists of individual commands divided by ``|`` (e.g. ``R0|D1|W0.5``).
|
||||||
|
- Commands (e.g. ``R0``) are defined by a code (``R``) and an argument (``0``).
|
||||||
|
|
||||||
|
+------+-----------------------------------------------------------+-----------------+
|
||||||
|
| Code | Action | Argument |
|
||||||
|
+======+===========================================================+=================+
|
||||||
|
| D | Set DTR control line | ``1``/``0`` |
|
||||||
|
+------+-----------------------------------------------------------+-----------------+
|
||||||
|
| R | Set RTS control line | ``1``/``0`` |
|
||||||
|
+------+-----------------------------------------------------------+-----------------+
|
||||||
|
| U | Set DTR and RTS control lines at the same time | ``0,0``/``0,1`` |
|
||||||
|
| | (Unix-like systems only) | ``1,0``/``1,1`` |
|
||||||
|
+------+-----------------------------------------------------------+-----------------+
|
||||||
|
| W | Wait for ``N`` seconds (where ``N`` is a float) | ``N`` |
|
||||||
|
+------+-----------------------------------------------------------+-----------------+
|
||||||
|
|
||||||
|
|
||||||
|
For example: ``D0|R1|W0.1|D1|R0|W0.5|D0`` represents the following classic reset sequence:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
_setDTR(False) # IO0=HIGH
|
||||||
|
_setRTS(True) # EN=LOW, chip in reset
|
||||||
|
time.sleep(0.1)
|
||||||
|
_setDTR(True) # IO0=LOW
|
||||||
|
_setRTS(False) # EN=HIGH, chip out of reset
|
||||||
|
time.sleep(0.05)
|
||||||
|
_setDTR(False) # IO0=HIGH, done
|
||||||
|
@@ -19,6 +19,7 @@ CONFIG_OPTIONS = [
|
|||||||
"connect_attempts",
|
"connect_attempts",
|
||||||
"write_block_attempts",
|
"write_block_attempts",
|
||||||
"reset_delay",
|
"reset_delay",
|
||||||
|
"custom_reset_sequence",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ import time
|
|||||||
from .config import load_config_file
|
from .config import load_config_file
|
||||||
from .reset import (
|
from .reset import (
|
||||||
ClassicReset,
|
ClassicReset,
|
||||||
|
CustomReset,
|
||||||
DEFAULT_RESET_DELAY,
|
DEFAULT_RESET_DELAY,
|
||||||
HardReset,
|
HardReset,
|
||||||
USBJTAGSerialReset,
|
USBJTAGSerialReset,
|
||||||
@@ -570,7 +571,9 @@ class ESPLoader(object):
|
|||||||
used ESP chip, external settings, and environment variables.
|
used ESP chip, external settings, and environment variables.
|
||||||
Returns a tuple of one or more reset strategies to be tried sequentially.
|
Returns a tuple of one or more reset strategies to be tried sequentially.
|
||||||
"""
|
"""
|
||||||
# TODO: If config file defines custom reset sequence, parse it and return it
|
cfg_custom_reset_sequence = cfg.get("custom_reset_sequence")
|
||||||
|
if cfg_custom_reset_sequence is not None:
|
||||||
|
return (CustomReset(self._port, cfg_custom_reset_sequence),)
|
||||||
|
|
||||||
cfg_reset_delay = cfg.getfloat("reset_delay")
|
cfg_reset_delay = cfg.getfloat("reset_delay")
|
||||||
if cfg_reset_delay is not None:
|
if cfg_reset_delay is not None:
|
||||||
|
@@ -7,6 +7,8 @@ import os
|
|||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from .util import FatalError
|
||||||
|
|
||||||
# Used for resetting into bootloader on Unix-like systems
|
# Used for resetting into bootloader on Unix-like systems
|
||||||
if os.name != "nt":
|
if os.name != "nt":
|
||||||
import fcntl
|
import fcntl
|
||||||
@@ -130,3 +132,47 @@ class HardReset(ResetStrategy):
|
|||||||
else:
|
else:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
self._setRTS(False)
|
self._setRTS(False)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomReset(ResetStrategy):
|
||||||
|
"""
|
||||||
|
Custom reset strategy defined with a string.
|
||||||
|
|
||||||
|
CustomReset object is created as "rst = CustomReset(port, seq_str)"
|
||||||
|
and can be later executed simply with "rst()"
|
||||||
|
|
||||||
|
The seq_str input string consists of individual commands divided by "|".
|
||||||
|
Commands (e.g. R0) are defined by a code (R) and an argument (0).
|
||||||
|
|
||||||
|
The commands are:
|
||||||
|
D: setDTR - 1=True / 0=False
|
||||||
|
R: setRTS - 1=True / 0=False
|
||||||
|
U: setDTRandRTS (Unix-only) - 0,0 / 0,1 / 1,0 / or 1,1
|
||||||
|
W: Wait (time delay) - positive float number
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
"D0|R1|W0.1|D1|R0|W0.05|D0" represents the ClassicReset strategy
|
||||||
|
"U1,1|U0,1|W0.1|U1,0|W0.05|U0,0" represents the UnixTightReset strategy
|
||||||
|
"""
|
||||||
|
|
||||||
|
format_dict = {
|
||||||
|
"D": "self.port.setDTR({})",
|
||||||
|
"R": "self.port.setRTS({})",
|
||||||
|
"W": "time.sleep({})",
|
||||||
|
"U": "self._setDTRandRTS({})",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
exec(self.constructed_strategy)
|
||||||
|
|
||||||
|
def __init__(self, port, seq_str):
|
||||||
|
super().__init__(port)
|
||||||
|
self.constructed_strategy = self._parse_string_to_seq(seq_str)
|
||||||
|
|
||||||
|
def _parse_string_to_seq(self, seq_str):
|
||||||
|
try:
|
||||||
|
cmds = seq_str.split("|")
|
||||||
|
fn_calls_list = [self.format_dict[cmd[0]].format(cmd[1:]) for cmd in cmds]
|
||||||
|
except Exception as e:
|
||||||
|
raise FatalError(f'Invalid "custom_reset_sequence" option format: {e}')
|
||||||
|
return "\n".join(fn_calls_list)
|
||||||
|
@@ -1124,3 +1124,28 @@ class TestConfigFile(EsptoolTestCase):
|
|||||||
os.environ["ESPTOOL_CFGFILE"] = tmp
|
os.environ["ESPTOOL_CFGFILE"] = tmp
|
||||||
else:
|
else:
|
||||||
os.environ.pop("ESPTOOL_CFGFILE", None)
|
os.environ.pop("ESPTOOL_CFGFILE", None)
|
||||||
|
|
||||||
|
def test_custom_reset_sequence(self):
|
||||||
|
# This reset sequence will fail to reset the chip to bootloader,
|
||||||
|
# the flash_id operation should therefore fail.
|
||||||
|
# Also tests the number of connection attempts.
|
||||||
|
reset_seq_config = (
|
||||||
|
"[esptool]\n"
|
||||||
|
"custom_reset_sequence = D0|W0.1|R1|R0|W0.1|R1|R0\n"
|
||||||
|
"connect_attempts = 1\n"
|
||||||
|
)
|
||||||
|
config_file_path = os.path.join(os.getcwd(), "esptool.cfg")
|
||||||
|
with self.ConfigFile(config_file_path, reset_seq_config):
|
||||||
|
output = self.run_esptool_error("flash_id")
|
||||||
|
assert f"Loaded custom configuration from {config_file_path}" in output
|
||||||
|
assert "A fatal error occurred: Failed to connect to" in output
|
||||||
|
# Connection attempts are represented with dots,
|
||||||
|
# there are enough dots for two attempts here, but only one is executed
|
||||||
|
assert "Connecting............." not in output
|
||||||
|
|
||||||
|
# Test invalid custom_reset_sequence format is not accepted
|
||||||
|
invalid_reset_seq_config = "[esptool]\n" "custom_reset_sequence = F0|R1|C0|A5\n"
|
||||||
|
with self.ConfigFile(config_file_path, invalid_reset_seq_config):
|
||||||
|
output = self.run_esptool_error("flash_id")
|
||||||
|
assert f"Loaded custom configuration from {config_file_path}" in output
|
||||||
|
assert 'Invalid "custom_reset_sequence" option format:' in output
|
||||||
|
Reference in New Issue
Block a user