dotbot: push float32 LH2 calibrations with a site gate, reframe, and capture from the button - #296
Merged
Merged
Conversation
Breaking change: the calibration messages and the bare-metal homography payload carry float32 matrices, and the swarmit message grows to 84 bytes with valid_mm, the site name and the calibration id. Only the float32 firmware of the same swarmit and DotBot-libs wave reads them. AI-assisted: Claude Opus 5
Breaking change: flash-swarmit-sandbox takes --lh2-calibration (no -l, no --calibration). The sandbox page is the 632-byte float32 swarmit_config_t under swarmit's new config magic, which must match the netcore's; the gateway keeps mari's magic and its 12-byte page. AI-assisted: Claude Opus 5
A push, from the CLI or the console, now reads device info first and refuses robots on device info below v2 (int32 firmware), robots that did not answer, and robots reporting another site unless --site-changed. Needs swarmit's DeviceInfo with lh2_site_name and lh2_calibration_id. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
…s the bot AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
The chunk layout mirrors DotBot-firmware's apps-sandbox/calibrate app, [0xCB][press:5 | chunk:3][up to 13 nine-byte records] with a short last chunk; the two change together. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
This was referenced Sep 21, 2026
Merged
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #296 +/- ##
==========================================
+ Coverage 82.77% 83.72% +0.94%
==========================================
Files 197 201 +4
Lines 23548 24369 +821
Branches 1628 1683 +55
==========================================
+ Hits 19493 20404 +911
+ Misses 4048 3958 -90
Partials 7 7
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
… fleet AI-assisted: Claude Opus 5
A pushed robot resets, and its rejoin moves the device-info generation that swarmit already refreshes by one broadcast, so the worklist reads status and sends nothing. A push checked for named robots stays unicast to exactly those. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
…console AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
…ture AI-assisted: Claude Opus 5
swarmit's tests/lh2_wire_fixture.py carries the same defaults fixture, so a file without [site] or [validity] packs to the same bytes from either repo. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
… module AI-assisted: Claude Opus 5
…tion AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
swarmit's in-process controller drops every log event from a device outside its settings' devices, so a client built for one robot never saw a button capture from another. Captures name their robot, and every call that targets robots passes the addresses explicitly. AI-assisted: Claude Opus 5
collect --push sends to the robots whose captures built the calibration (and --device) rather than to the fleet, because a push resets every robot that takes it. AI-assisted: Claude Opus 5
A console push with no robot chosen no longer goes to the whole swarm; like collect --push, it goes to the capturing robots and the chosen one. AI-assisted: Claude Opus 5
… the swarm AI-assisted: Claude Opus 5
…erver AI-assisted: Claude Opus 5
…mware AI-assisted: Claude Opus 5
…stale AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Fable 5.1
AI-assisted: Claude Fable 5.1
notify_clients drops every None, so a complete session reached the console without `outstanding` and the step card read it as a step number. The session is now sent with its nulls, as the REST routes do. AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
AI-assisted: Claude Opus 5.5
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.
The PyDotBot piece of the LH2 calibration rework: the host sends calibrations as float32 with their site and id, checks what each robot reports before pushing, can re-express a calibration in another site's frame without recapturing, and can take calibration points from the robot's own button.
Changes since review. The default validity rectangle, for a calibration that declares none, goes from 4 x 4.5 m to 10 x 10 m, with the shared wire fixture, matching swarmit. The console's calibration step card now follows captures that arrive from a robot's button: it reads the outstanding point off the session's points, as the map does, adopts the pressing robot as the capturing device, and moves to save and push after the last point instead of showing "Step NaN", which came from the session notification dropping
outstanding: null.What changes
valid_mm, a 16-byte site name, an 8-byte id) and the bare-metal homography payload carry float32 matrices. The int32 x 1000 path is removed.dotbot/tests/lh2_wire_fixture.pypins the bytes, and swarmit's packer test carries the same fixture, so the two packers cannot drift apart silently. Breaking: only the float32 firmware of the companion swarmit, DotBot-libs and DotBot-firmware PRs reads these messages.dotbot device flash-swarmit-sandboxwrites the 632-byte page under swarmit's new config magic; the gateway keeps mari's magic and its 12-byte page, so the magic is chosen per role. Breaking: the option is--lh2-calibration(no-l, no--calibration), matching the controller's option name.--site-changedis given. Nothing tries to push to older firmware anyway.calibrate-lh2 pushsends unicast to exactly the robots the gate checked, andcollect --pushto the robots whose captures built the calibration, plus--device. From the console a push goes to the selected robots, or to the whole swarm when none is selected, as flash and start/stop do; the push route takes an optionaldeviceslist. After a push it waits up to 30 s for each pushed robot to reset, rejoin and report, and lists those still on the old calibration id, or not heard at all, as the worklist. This replaces the controller'sstale_devices, which could only see the per-station bitmask and not which calibration a robot holds.dotbot swarm calibrate-lh2 reframere-expresses a saved calibration in another site's frame without a capture:--site <name> --shift x,y [--rotate deg]turns every placement's points about the first placement's first point and shifts them (the--shifthelp now says so), re-solves every station from the samples stored in the file, and writes a new file with a new id. This is how a set of calibrations moves from a per-arena frame to a whole-floor frame, and how a roughly measured offset between the two is refined later.calibrate-lh2 collect. While collecting, a press on any robot running thecalibratesandbox app arrives as log events ([0xCB][press:5 | chunk:3][up to 13 records], each capture sent three times). The host deduplicates by (device, press, chunk), assembles each press into 25 reads per station, reports a gap in the press counter, stores the result as the pending point, and gives up a press still incomplete after 20 s, above the paced app's spread for four stations (12.7 s on huge at the earlier half-budget pacing, about half that on the uplink interval). A copy is recognised by its content, so a chunk unlike the one stored starts a new press instead of merging into a stale one, and the late copies of a press given up on stay quiet. The console listens for presses from the moment a session starts, without a capture request first, and stores them under the session lock. A capture missing a station an earlier point saw is stored, as an Enter capture is.collecttakes button captures with or without--device: its swarmit client covers the whole swarm, since swarmit drops log events from devices outside a client's own list. The Enter-key path is unchanged and stays bound to--device.calibrateis added to theswarm flashcatalogue.Merge order
The push gate reads swarmit's device-info v2 fields (
lh2_site_name,lh2_calibration_id), so this needs a swarmit release that carries the swarmit PR; theswarmit >= 0.9.0floor inpyproject.tomlshould move to that release when it exists. No other gate: these can merge when reviewed.Still to do in this PR after review: the docs sweep (the
calibrate-lh2CLI page forreframeand--site-changed, and the flash option rename).Validation
pytest dotbot/tests: 759 passed, 6 skipped; one test fails only because the port it binds was held by a controller running on the same machine.pre-commit run --all-filesclean. Indotbot/console-web: typecheck, lint and 418 tests pass.On one DotBot v3 with one lighthouse, on the companion firmware: 12 cable flashes with
--lh2-calibrationeach gave a live position within 1-3 s; device info reported v2 with the file's site name and id; a push of a file naming another site was refused and landed with--site-changed; the worklist printed; a solve outsidevalid_mmwas dropped.collectreceived button presses at the four corners of a 906 x 963 mm rectangle and saved a calibration with 25 reads per point, a spread of at most 5 counts and a 4-point residual of 2e-5 mm, even when only one of each chunk's three copies arrived. A button capture matched a READY-path capture at the same spot to under 1 count.Re-run on 2026-09-22 after the review fixes: a console session opened with no robot and no capture request stored a simulated button press from
calibrateas its first point and the next three presses as points 1-3, then dropped a fifth press with every point captured.Re-run after the final review round, 2026-09-22 13:55-14:19, on the same tethered robot and gateway (schedule tiny): two cable flashes of the Debug bootloader and network core with a calibration each reported a fresh boot 2-3 s after the flash tool returned, device info v2 carrying the file's site and id. A
dotbotbuild with a receive counter got 400 of 400move_rawpackets exactly once (100 at 4/s, then 300 at about 20/s), with STATUS flowing and no mari rejoin. A 15-minute window ondotbothad no reset and no NODE_LEFT, swarm status had updated within the last second at each of 58 samples, and 99.6% of the 2/s adverts arrived (largest gap 1.27 s). Two calibration pushes, one to the whole swarm and one to the robot by name, each left an empty worklist within 6 s. The pushes ran through the sameSessionDriver.pushthe console route calls, withdevicesempty and then naming the robot.pytest dotbot/tests: 778 passed, the port-bound simulator test failing only because a controller held its port; console-web 422 tests, build and lint pass. On the final head, console-web typecheck, lint and 438 tests pass.Final firmware run, 2026-09-22 17:38-18:19, on the same bench: the cable flash with
--lh2-calibrationfrom this branch wrote the float32 config page and the robot reported device info v2 with the file's site and id, andswarm status,swarm info,swarm flashandswarm start/stopfrom this branch drove the stress,calibrateanddotbotapps on the swarmit PR's final bootloader and network core. The firmware results are in the swarmit PR.Not covered on hardware:
reframe(tests only), more than one station, and a push to a robot still on the old firmware, which is exercised in tests against a faked device-info reply.