apps-sandbox: move to the float32 bootloader, add dotbot-next and a button calibration app - #424
Merged
Merged
Conversation
AI-assisted: Claude Opus 5
The secure side gained swarmit_localization_get_fix(), which reports a position together with the sequence of the solve it came from. Adding an entry moves every veneer address, so all the applications here are rebuilt against this blob whether or not they call the new function, and a bot only runs them once its bootloader has been reflashed over cable from the matching swarmit build. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
The advertisement used to zero the accumulator, so counts belonged to whoever read first and an estimator added later would see nothing. The tick now drains the destructive hardware read into totals that are never cleared, and each consumer takes a delta against its own cursor. Totals are unsigned so the wrap at 2^32 subtracts correctly rather than being undefined. AI-assisted: Claude Opus 5
The LH2 calibration packet now carries the matrix as float32 in millimetres and the calibrated advertisement byte is a full bitmask, so the host's calibration packer has to move with this bump. AI-assisted: Claude Opus 5
The matrix sits one byte into the packet and the driver reads it as floats, which the FPU cannot load from an unaligned address, so it is copied to an aligned local first. AI-assisted: Claude Opus 5
The secure side gained swarmit_localization_get_raw_counts(). Adding an entry moves most veneer addresses, so every application here is rebuilt against this blob and runs only on a bootloader reflashed over cable from the matching swarmit build. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
Its log events, [0xCB][press:5 | chunk:3][up to 13 nine-byte records] with a short last chunk, are decoded by PyDotBot's calibrate-lh2 collect; the two change together. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
This was referenced Sep 21, 2026
Merged
AI-assisted: Claude Opus 5
…dget AI-assisted: Claude Opus 5
…ed ticks AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
…function 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
AI-assisted: Claude Fable 5.1
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Fable 5.1
AI-assisted: Claude Fable 5.1
AI-assisted: Claude Fable 5.1
AI-assisted: Claude Fable 5.1
…lang-format versions agree clang-format 15 (local) and 18 (CI) disagree on how to align _drain[16] against the following aligned-attribute declaration; only the attribute-after-declarator form is stable across both, which is why the CI style check failed while a local check-format passed clean. AI-assisted: Claude Sonnet 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 DotBot-firmware piece of the LH2 calibration rework: the sandbox apps move to swarmit's new import library, a new
calibrateapp captures a calibration point from the robot's own button, and the bare-metaldotbotapp takes the float32 calibration packet.This PR supersedes #422 and carries its commits first (
apps-sandbox/dotbot-next, the control-loop-free app on the fix sequence, its README, and the import library with the fix-sequence entry). #422 sat on the same import library this work regenerates, so it is folded in rather than merged ahead; its description and review stay readable there.Changes since review. The sandbox apps follow the swarmit PR's interface change: the uplink-budget entry point is replaced by
swarmit_get_min_tx_interval_us(), socmse_implib.ais regenerated with that entry and every app relinks against it. Veneer addresses move, so an app built before this change must not run on the new bootloader.calibratepaces its log events on the minimum TX interval instead of on half the budget.What is in it
cmse_implib.aregenerated from the swarmit companion PR, now carrying the fix-sequence, raw-counts and minimum TX interval entries (15 in all). Most veneer addresses move, so every sandbox app here is rebuilt against it, even the ones unchanged in source, and runs only on a bootloader from that same swarmit build. Old app binaries do not run on the new bootloader, and new ones do not run on the old.apps-sandbox/calibrate, a button-triggered LH2 capture. Press the robot's button: three blinks on LED3, then a solid window in which it reads 25 raw-count samples per visible station throughswarmit_localization_get_raw_counts(), then a verdict. The app checks the capture locally (every read valid, and the spread of each station's counts under a stillness threshold, so a finger pushing the chassis is refused with a slow pulse). A clean capture is sent three times overswarmit_log_data()under a 5-bit press counter, as[0xCB][press:5 | chunk:3][up to 13 nine-byte records]with a short last chunk, so the host needs no station count and deduplicates by press and chunk.swarmit_keep_alive()is never called inside the capture window, because it drains the counts the window is reading; the window is bounded at 800 ms, under the ~1 s watchdog. The events are paced at least one minimum TX interval apart (swarmit_get_min_tx_interval_us(), rounded up to the 10 ms tick, never under 20 ms), 1 s apart when not joined, so a burst never outruns mari's 32-entry TX queue: on tiny (30,260 us) four stations' 24 events take about 1 s. The three acknowledgement blinks no longer stretch over the whole send. The host side is PyDotBot'scalibrate-lh2 collect; the two change together.calibrateanddotbot-next. Both used to callswarmit_keep_alive()when the tick number at service time was a multiple of 10, so a backlog of ticks serviced at once could step over the multiple. Ten skips in a row would let the watchdog reset the robot. They now feed it when at least 10 ticks have passed since the last feed. Indotbot-nextthat call is also what runs the position solve, so the position poll moves to the same rule, and so do its command timeout and advertisement.apps/dotbot(bare metal) takes the float32 homography packet. The matrix sits one byte into the packet and the FPU cannot load a float from an unaligned address, so it is copied to an aligned local first, and the calibration index is bounded before the store.dotbot-libsbumped to the float32 store and the decoder and timer fixes.Lines changed
apps-sandbox/applications.emProjectapps-sandbox/calibrate/main.capps-sandbox/dotbot-next/README.mdapps-sandbox/dotbot-next/main.capps/dotbot/main.c,dotbot-libsapps-sandbox/cmse_implib.a(binary, 860 bytes, 716 on main)Merge order
The
dotbot-libssubmodule here points at the DotBot-libs PR's branch tip and is re-pointed at its merge commit once that lands;cmse_implib.ashould be checked against the swarmit build that merges. No other gate: these can merge when reviewed.Validation
SEGGER Embedded Studio 7.22,
sandbox-dotbot-v3Release, zero warnings forcalibrate,dotbot-nextanddotbot;make check-formatclean on every file in this PR. On one DotBot v3 with one lighthouse, running the Debug bootloader and network core from the swarmit companion PR:dotbot-nextanddotbotran 5-minute soaks with no reset at about 10 solves/s, and 10-15 minute windows each in a 4.4 h A/B against main with no unexpected reset and 98-100% STATUS delivery.calibrateran 11 minutes with no reset. Button presses at four corners of a 906 x 963 mm rectangle were each received and assembled by the host, even when only one of the three copies of a chunk arrived; the resulting calibration had 25 reads per point, a spread of at most 5 counts, and a 4-point residual of 2e-5 mm. A button capture matched a READY-path capture at the same spot to under 1 count.dotbot-nextflashed over the air ran 5.5 minutes on one boot with no reset and a fresh position in every 30 s sample, then the rebuiltdotbotapp went back on and came up Running.Re-run after the code-review fixes, 2026-09-22 10:17-11:38, on one tethered DotBot v3 with one lighthouse and the gateway on schedule tiny, Debug bootloader and network core from swarmit #165 cable-flashed with a calibration: three fresh cable flashes each reported a live position 1.5-1.6 s after the flash tool returned, with no button press. In 15-minute windows (bootloader idle,
dotbotidle,dotbotwithmove_rawbursts,dotbot-nextidle,calibratewith simulated presses) there was no reset, no crash report, no absence from swarm status, no NODE_LEFT and no FCS error on the gateway link; STATUS delivery was 98.9-99.7% and adverts 97.7-99.6% of the 2/s the apps send, with no advert gap over 1.13 s; the advertised position matched STATUS to 1 mm and no invalid position was reported.calibratewith five simulated presses delivered all three copies of every chunk, the six events of each press within 0.4 s on tiny (budget 3304, so about 70 ms apart), and the host assembled every press.dotbotreceived 259 of 262move_rawpackets in the fully sampled windows, anddotbot-nextpublished 9.99 fixes per second.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).dotbot-nextstopped its motors 539-720 ms after the last command (a 519 ms timeout checked every 200 ms) and advertised at 2.00/s. All ten sandbox apps and the baredotbotapp build with zero warnings;cmse_implib.ais unchanged (nmidentical to a fresh bootloader build).Final run on this branch's head, 2026-09-22 17:38-18:19, on one tethered DotBot v3 and a gateway DK on schedule tiny, with the Debug bootloader and network core from the swarmit PR's head cable-flashed and every app rebuilt against the committed
cmse_implib.a(nm-identical to that bootloader build's): a stress build ofdotbot-nextsending 20 raw frames every 10 ms ran 5 minutes withmove_rawat 25 Hz and no reset, then stayed up through three unjoined windows of 10 s to 15.8 min with its frame counter running and rejoined by itself each time;calibrateran 2.5 minutes with no reset (its button capture was not exercised, with nobody at the bench to press the button); and thedotbotapp from this head came up Running and joined, with adverts at 2/s. The minimum TX interval read 30,260 us on tiny and 0 while unjoined. The details are in the swarmit PR's validation section.Not observed on hardware:
calibrate's refusal when the chassis is pushed, and a press with no host collecting. The watchdog-feed change was checked by a soak, not by forcing a tick backlog.