Merge branch 'rp2040-upload-improvements' into integration

This commit is contained in:
J. Nick Koston
2026-03-04 14:25:09 -10:00
3 changed files with 54 additions and 15 deletions
+13
View File
@@ -736,8 +736,21 @@ def upload_using_esptool(
def upload_using_platformio(config: ConfigType, port: str) -> int:
import shutil
from esphome import platformio_api
# RP2040 platform-raspberrypi build recipe expects firmware.bin.signed for
# the upload target, but 'nobuild' skips the build phase that creates it.
# Create it here so the upload doesn't fail.
if CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040:
idedata = platformio_api.get_idedata(config)
build_dir = Path(idedata.firmware_elf_path).parent
firmware_bin = build_dir / "firmware.bin"
signed_bin = build_dir / "firmware.bin.signed"
if firmware_bin.is_file() and not signed_bin.is_file():
shutil.copy2(firmware_bin, signed_bin)
upload_args = ["-t", "upload", "-t", "nobuild"]
if port is not None:
upload_args += ["--upload-port", port]
@@ -18,21 +18,6 @@ def rp2040_copy_ota_bin(source, target, env):
shutil.copyfile(firmware_name, new_file_name)
def rp2040_copy_signed_bin(source, target, env):
"""Create firmware.bin.signed so that 'nobuild' upload target can find it.
The platform-raspberrypi build recipe creates firmware.bin.signed as a build
target, but the 'nobuild' upload flag skips the build phase. Without this
file, the upload fails with 'firmware.bin.signed not found'.
ESPHome does not use signing for RP2040, so this is just a copy.
"""
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
signed_name = env.subst("$BUILD_DIR/${PROGNAME}.bin.signed")
shutil.copyfile(firmware_name, signed_name)
# pylint: disable=E0602
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", rp2040_copy_factory_uf2) # noqa
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", rp2040_copy_ota_bin) # noqa
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", rp2040_copy_signed_bin) # noqa
+41
View File
@@ -40,6 +40,7 @@ from esphome.__main__ import (
show_logs,
upload_program,
upload_using_esptool,
upload_using_platformio,
upload_using_uf2_copy,
)
from esphome.components.esp32 import KEY_ESP32, KEY_VARIANT, VARIANT_ESP32
@@ -1203,6 +1204,46 @@ def test_upload_program_serial_platformio_platforms(
mock_upload_using_platformio.assert_called_once_with(config, device)
def test_upload_using_platformio_creates_signed_bin_for_rp2040(
tmp_path: Path,
) -> None:
"""Test that upload_using_platformio creates firmware.bin.signed for RP2040."""
setup_core(platform=PLATFORM_RP2040)
build_dir = tmp_path / "build"
build_dir.mkdir()
firmware_bin = build_dir / "firmware.bin"
firmware_bin.write_bytes(b"test firmware content")
firmware_elf = build_dir / "firmware.elf"
firmware_elf.write_bytes(b"elf")
mock_idedata = MagicMock()
mock_idedata.firmware_elf_path = str(firmware_elf)
with (
patch("esphome.platformio_api.get_idedata", return_value=mock_idedata),
patch("esphome.platformio_api.run_platformio_cli_run", return_value=0),
):
result = upload_using_platformio({}, "/dev/ttyACM0")
assert result == 0
signed_bin = build_dir / "firmware.bin.signed"
assert signed_bin.is_file()
assert signed_bin.read_bytes() == b"test firmware content"
def test_upload_using_platformio_skips_signed_bin_for_non_rp2040(
tmp_path: Path,
) -> None:
"""Test that upload_using_platformio doesn't create signed bin for non-RP2040."""
setup_core(platform=PLATFORM_ESP32)
with patch("esphome.platformio_api.run_platformio_cli_run", return_value=0):
result = upload_using_platformio({}, "/dev/ttyUSB0")
assert result == 0
def test_upload_program_serial_upload_failed(
mock_upload_using_esptool: Mock,
mock_get_port_type: Mock,