- README: rewrite Install (from source, submodules, deps, Pi/aarch64); new 'Flashing firmware (pm3flash)' section — CLI usage, --build, image resolution, cross-compile + PLATFORM note, Raspberry Pi bring-up, programmatic API; point Firmware Compatibility at it; add a License section (MIT). - CLAUDE.md: flash.py/_firmware.py/flash_cli.py in the structure; a 'Firmware flashing' section with the maintainer gotchas (OLD frame, fullimage-only, page-merge, platform != is_rdv4, the pin). - roadmap: new 2026-07-05-firmware-flasher plan doc + a Firmware-flashing row. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2.8 KiB
2.8 KiB
Pure-Python firmware flasher + pm3flash
Status: ✅ Landed (hardware-validated on a PM3 Easy) · Date: 2026-07-05 Related: firmware-fork DT-Gitea migration
Goal
Let pm3py get the fork firmware onto a device by itself — no C pm3-flash/DFU/JTAG — so
pip install pm3py + one command flashes the build this pm3py corresponds to. Supports the
project "in the meantime", before tagged firmware releases exist on the DT Gitea.
What landed
Flasher(pm3py/core/flash.py) over the legacy 544-byte OLD frame (bootloader protocol, distinct from the NG frames the rest of the client speaks; OLD-frame plumbing incore/transport.py). Automatic OS→bootloader handover (CMD_START_FLASH, no button),CMD_FINISH_WRITEblock loop with per-block ACK/NACK, reset, reopen-and-verify.- Safety: fullimage-only by default; the bootrom region is refused unless
allow_bootrom=True. A bad OS write is recoverable (proven — recovered a corrupted-OS device); a bad bootrom write bricks to JTAG. - Firmware pin (
pm3py/_firmware.py):FIRMWARE_PIN= the fork build pm3py corresponds to (currently the submodule SHA).pm3.firmware.matches_pin/.expected_firmwareon connect. pm3flashCLI (pm3py/flash_cli.py): detect → resolve/build image → flash → verify.--image(prebuilt),--build PLATFORM(makefrom the submodule; platform explicit — never inferred fromis_rdv4, which reports the running firmware, not the board),--force,--allow-bootrom.
Lessons (found on hardware / by adversarial review)
- Merge segments sharing a flash page. The SAM7 erase-programs whole pages; two adjacent PT_LOAD segments in one page must be written once with both segments' data, else 0xFF padding clobbers real bytes (bug: the flashed OS booted but had invalid version info).
--buildmust flash what it built, not gate on the pin — a dirty rebuild keeps the same base SHA, somatches_pincan't distinguish it (--buildtherefore implies--force).- No CWD-relative firmware discovery — running
makeon a stray./firmwareis arbitrary code execution; only an explicit arg /$PM3PY_FIRMWARE_SRC/ the package repo root.
Open follow-ups
- Auto-download of the pinned release asset (toolchain-free install) — needs a tagged
firmware release to exist; rides on the DT-Gitea migration. Drops into
resolve_image. - Read-back verify via
CMD_READ_MEM_DOWNLOAD(v1 trusts per-block ACK/NACK, like the C flasher).
Provenance
Built + hardware-validated in one session; the --build layer was adversarially reviewed by
a multi-agent workflow (correctness / subprocess-safety / CLI-UX lenses with a verify pass),
which surfaced the pin-skip, make-missing, and CWD-Makefile issues fixed above.