Dev board cheat sheet
Last updated
Commands that only make sense on a dev board. Everything a robot needs day to day is in the robotctl reference.
The dev channel
sudo robotctl update apply --ref <branch> daemon
sudo robotctl update apply --ref main daemon
--version pins an exact release instead. Give one of them unless you genuinely mean “go to stable”.
apply without --ref is a trap
apply daemonwith no--refinstalls the latest stable release, which on a dev board is usually a downgrade.
It is not “install the newest thing”; it is “install what the stable channel offers”.
Right after a branch merges, that stable release is still older than everything you have been testing. Worse: if it predates a daemon that now has a unit file on the board, its ExecStart points at a binary the older release does not contain, the restart fails, and the update rolls back. That is the gate working — but the command that caused it looked like the obvious one.
How the branch tag behaves
The tag daemon-dev-<branch> moves with the branch, so there is no version number to copy. The version inside stays unique per build — 0.1.0-dev.42.c719ec8 — so two builds of the same branch are never confusable.
--ref main is how a board goes back to mainline without leaving the dev channel; a plain apply daemon leaves it, since a prerelease sorts below its release.
A merge does not publish instantly: CI has to build main before --ref main resolves to it. gh run list --branch main.
Release candidates
sudo robotctl update apply --staging daemon
What release.yml published to staging and nobody has promoted — what a canary robot should run before a promotion.
A candidate is signed with the release key like any release. What makes it unreachable without the flag is that it is flagged as a prerelease, and a plain apply skips those so no robot drifts onto a build nobody has validated. --staging is that filter’s only opt-in and leaves nothing switched on afterwards.
After an update — the part that bites
This is the section worth memorising, because what it produces is “the fix is definitely installed and definitely not working”.
Restarts happen in two waves
robotd,configdandpaddrestart during the update.updaterdandbtdrestart five seconds after it replies — the first cannot restart itself mid-update, and the second may be carrying the reply.
So a btd fix is live a few seconds later, with no manual step.
If one of those restarts does not happen
The next updaterd start fixes it — except updaterd itself, which reports the disagreement rather than restarting itself.
For that one, run the apply again: it answers already_current, names the daemon that is not running it, and schedules the restart. sudo systemctl restart updaterd does the same by hand.
Older boards have none of this
A board running an updaterd older than 0.4.0 keeps both on the old binary until you restart them. One update fixes it, and only the update after that behaves.
already_current is no longer inert
robotctl update apply reports already_current and installs nothing if you ask for the version a board already has — but it is no longer inert. It checks which daemons are running that release and restarts the ones that are not, naming them in stale.
So it is the command to reach for when a fix looks absent: either it fixes it, or stale is empty and the fix was never in that release.
Finding which daemon is behind
robotctl health
The units block prints one line per daemon with the release its process was launched from, and a warning naming the restart when that disagrees with what is installed.
build unknown (old) means that daemon predates the release which taught it to say — restart it and it will answer.
Driving with a pad from your laptop
The pad in your hands, the robot on the bench, nothing installed on either: padd is an ordinary client, so it can run from a clone against a forwarded socket.
sudo systemctl stop padd # on the robot
ssh -L /tmp/robotd.sock:/run/robotd.sock [email protected]
cargo run -p padd -- --socket /tmp/robotd.sock # from the clone
This is also where padd’s flags are worth having — --max-linear (m/s), --max-angular (rad/s), --max-head (radians), and --deadzone, which exists because analogue sticks rarely rest at exactly zero and the robot creeps without it.