Skip to content

Commit 2b7666e

Browse files
committed
Skip the 60s bootloader delay on the CC2531
1 parent 8e14c39 commit 2b7666e

File tree

6 files changed

+54
-3
lines changed

6 files changed

+54
-3
lines changed

tests/test_application.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,41 @@ def on_endpoint_deletion(req):
304304
return app, znp_server
305305

306306

307+
@pytest_mark_asyncio_timeout(seconds=5)
308+
async def test_application_startup_skip_bootloader(application, mocker):
309+
app, znp_server = application
310+
311+
first_uart_byte = None
312+
313+
def create_patched_write(original_write):
314+
def patched_write(data):
315+
nonlocal first_uart_byte
316+
317+
# Intercept the first byte if it's destined for the bootloader
318+
is_for_bootloader = data[0] in c.ubl.BootloaderRunMode._value2member_map_
319+
320+
if first_uart_byte is None and is_for_bootloader:
321+
first_uart_byte = data[0]
322+
data = data[1:]
323+
324+
return original_write(data)
325+
326+
return patched_write
327+
328+
async def patched_uart_connect(config, api):
329+
protocol = await uart_connect(config, api)
330+
protocol.transport.write = create_patched_write(protocol.transport.write)
331+
332+
return protocol
333+
334+
mocker.patch("zigpy_znp.uart.connect", side_effect=patched_uart_connect)
335+
336+
app.update_config({conf.CONF_ZNP_CONFIG: {conf.CONF_SKIP_BOOTLOADER: True}})
337+
await app.startup(auto_form=False)
338+
339+
assert first_uart_byte == c.ubl.BootloaderRunMode.FORCE_RUN
340+
341+
307342
@pytest_mark_asyncio_timeout(seconds=5)
308343
async def test_application_startup_nib(application):
309344
app, znp_server = application

zigpy_znp/api.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,16 @@ def set_application(self, app):
155155
def _port_path(self) -> str:
156156
return self._config[conf.CONF_DEVICE][conf.CONF_DEVICE_PATH]
157157

158-
async def connect(self, *, test_port=True) -> None:
158+
async def connect(self, *, test_port=True, skip_bootloader=False) -> None:
159159
assert self._uart is None
160160

161161
try:
162162
self._uart = await uart.connect(self._config[conf.CONF_DEVICE], self)
163163

164+
if skip_bootloader:
165+
LOGGER.debug("Sending first UART byte to the bootloader")
166+
self._uart._transport_write(bytes([c.ubl.BootloaderRunMode.FORCE_RUN]))
167+
164168
if test_port:
165169
LOGGER.debug(
166170
"Testing connection to %s", self._uart.transport.serial.name

zigpy_znp/commands/ubl.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ class BootloaderDeviceType(t.enum_uint8):
2929
CC2530 = 2
3030

3131

32+
class BootloaderRunMode(t.enum_uint8):
33+
# Read the code, not the spec
34+
FORCE_BOOT = 0x10
35+
FORCE_RUN = FORCE_BOOT ^ 0xFF
36+
37+
3238
class UBL(t.CommandsBase, subsystem=t.Subsystem.UBL_FUNC):
3339
WriteReq = t.CommandDef(
3440
t.CommandType.AREQ,

zigpy_znp/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
CONF_TX_POWER = "tx_power"
4141
CONF_SREQ_TIMEOUT = "sync_request_timeout"
4242
CONF_AUTO_RECONNECT_RETRY_DELAY = "auto_reconnect_retry_delay"
43+
CONF_SKIP_BOOTLOADER = "skip_bootloader"
4344

4445
CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
4546
{
@@ -53,6 +54,7 @@
5354
vol.Optional(
5455
CONF_AUTO_RECONNECT_RETRY_DELAY, default=5
5556
): VolPositiveNumber,
57+
vol.Optional(CONF_SKIP_BOOTLOADER, default=True): cv_boolean,
5658
}
5759
),
5860
}

zigpy_znp/uart.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ def data_received(self, data: bytes) -> None:
7777

7878
def send(self, payload: frames.GeneralFrame) -> None:
7979
"""Sends data taking care of framing."""
80-
data = frames.TransportFrame(payload).serialize()
80+
self._transport_write(frames.TransportFrame(payload).serialize())
81+
82+
def _transport_write(self, data: bytes) -> None:
8183
LOGGER.trace("Sending data: %s", Bytes.__repr__(data))
8284
self.transport.write(data)
8385

zigpy_znp/zigbee/application.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,9 @@ async def startup(self, auto_form=False):
303303
znp = ZNP(self.config)
304304
znp.set_application(self)
305305
self._bind_callbacks(znp)
306-
await znp.connect()
306+
await znp.connect(
307+
skip_bootloader=self.config[conf.CONF_ZNP_CONFIG][conf.CONF_SKIP_BOOTLOADER]
308+
)
307309

308310
self._znp = znp
309311

0 commit comments

Comments
 (0)