Conversation
`firmware flash` already copied a .uf2 onto a bootloader drive for rp2 and
samd, but it reported success whenever shutil.copyfile did not raise. That
is not evidence of anything. A UF2 flash has two failure modes that look
identical to success from the host side:
- a copy that reports fine and writes nothing (PowerShell's Copy-Item
fails non-terminatingly, exit 0, no file), and
- a bootloader that silently skips every block whose family ID it does
not own, leaving a perfectly successful copy sitting on the drive.
So the copy is no longer the proof. The proof is that the volume unmounts:
a UF2 bootloader reboots into the new firmware once it has accepted a
complete image, and the mount goes with it. That signal comes from the
board rather than from the host filesystem. A volume still mounted after
--uf2-timeout (default 30s) is now a failure that names the image's family,
since a family mismatch is the usual cause.
Before copying, the new uf2 module validates the file: block magic, payload
bounds, and the block count against the header -- a file that disagrees
with its own numBlocks makes a bootloader wait forever rather than flash.
Family IDs are reported, not enforced: the upstream registry and
CircuitPython's espressif Makefile disagree about 0x540ddf62 (ESP32-C6 vs
esp32p4), so a hard check would reject working images.
--uf2 forces this path for any port, which is what reaches a board whose
UF2 bootloader is not implied by its MicroPython port -- an ESP32-S3
carrying tinyuf2 being the case that matters.
Volume discovery moves to INFO_UF2.TXT rather than labels, which differ per
family, and asks Windows for drive letters so it works under WSL, where a
removable drive is usually not mounted under /mnt at all. Two mounted
volumes now stop the command instead of picking one, because guessing
overwrites the firmware on a board the caller did not name.
test:python needed PYTHONPATH to import the engine at all; three test files
were erroring out before this.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
firmware flashalready copied a.uf2onto a bootloader drive forrp2andsamd, but it reported success whenevershutil.copyfiledid not raise. That is not evidence of anything. A UF2 flash has two failure modes that look identical to success from the host side:Copy-Itemfails non-terminatingly (exit 0, no file), andFor a flash operation, "succeeded" and "wrote nothing" being indistinguishable is the worst possible ambiguity.
Approach
The copy is no longer the proof. The proof is that the volume unmounts. A UF2 bootloader reboots into the new firmware once it has accepted a complete image, and the mount goes with it — a signal that comes from the board rather than from the host filesystem. A volume still mounted after
--uf2-timeout(default 30s) is now a failure that names the image's family, since a family mismatch is the usual cause.The one assumption is a bootloader that accepts an image without rebooting. None of the bootloaders in scope (RP2040/RP2350, SAMD, nRF, tinyuf2) behave that way, and it is documented at
wait_for_volume_gonesince a false negative there would report failure on a flash that worked.Changes
New
python/uf2.py— file validation, volume discovery, copy/verify. Validates block magic, payload bounds, and the block count against the header: a file that disagrees with its ownnumBlocksmakes a bootloader wait forever rather than flash.Family IDs are reported, not enforced. The upstream registry and CircuitPython's espressif Makefile disagree about
0x540ddf62(ESP32-C6 vs esp32p4), so a hard check would reject working images.--uf2forces the path for any port, which is what reaches a board whose UF2 bootloader is not implied by its MicroPython port — an ESP32-S3 carrying tinyuf2 being the case that matters:Discovery moves to
INFO_UF2.TXTrather than volume labels, which differ per family (RPI-RP2,FTHRS3BOOT, …), and asks Windows for drive letters so it works under WSL, where a removable drive is usually not mounted under/mntat all. Two mounted volumes now stop the command instead of picking one, because guessing overwrites the firmware on a board the caller did not name.test:pythonneededPYTHONPATHto import the engine at all — three test files were erroring out before this.Verification
parse_uf2checked against four real artifacts across four families: SAMD21, MIMXRT10XX, ESP32S3, RP2040.mypyclean on the new module; the 4 remaining errors are pre-existing infirmware_download.py.Not yet verified against real hardware — that needs a board in bootloader mode, which interrupts whatever is running on it.
🤖 Generated with Claude Code