August 2026: One Way In
Every actuator command now enters through one door with a guard on it: three bb releases, a migration for every servo driver, drivers that declare what they speak and finally stop when told to. Plus teleop, on someone else's arms.
Last month the robot learned to show its working. This month it learned to take instructions only through the front door.
July's big theme is less glamorous than June's and considerably more important: there is now exactly one way for a command to reach an actuator, and there's a guard on it. Three releases of bb, a migration for every servo driver, and a couple of genuine bugs shaken out along the way.
The Framework Owns the Command Path Now
Commanding an actuator worked, but it worked because every driver subscribed itself to its own command topic — BB.Actuator.Server didn't do it for them. Get the path right and everything behaves; get it slightly wrong and you're publishing into the void with nothing to tell you. That's a lot of ceremony to ask of a driver author, repeated once per transport, for something the framework knows how to do.
So it does it now. Proposal 0021, and a chunk of breaking change:
bb0.23.0 — all three transports (pubsub, cast, call) now converge on a single requiredBB.Actuator.handle_command/2(#204). Drivers had been implementing the same logic once per transport — about eighteen clauses across five drivers, differing only in how the framework chose to deliver the message.bb0.24.0 — actuators can be addressed by name or by full path, everywhere (#205). Previously the pubsub functions took a path and the direct ones took a name, sitting side by side in the docs inviting exactly the generalisation that crashed. Now all eighteen take either, and naming an actuator that doesn't exist raises with a list of the ones that do.bb0.25.0 — an actuator can declare the command payloads it accepts via an optionalcommand_payloads/1(#209). Widen it for a bespoke command struct, narrow it for a port that only speaks one thing.
The bit that actually matters
Owning that one path let the framework start enforcing arm state centrally. BB.Safety.armed?/1 was previously each driver's job, and three of five drivers didn't do it — so set_position!/4 on a disarmed robot could still write PWM to real PCA9685 and pigpio hardware. That's now checked in one place, before the driver ever sees the message, for every command with no exemptions.
I did briefly exempt Stop on the grounds that stopping is the fail-safe direction, and then took it back out. A disarmed robot shouldn't be accepting motion commands at all, and "except this one" is precisely the sort of carve-out you end up regretting in a safety path. Making hardware safe is disarm/1's job, not a command's.
The declared-payloads work exists for the same reason. The server's subscription filters to a set of command payloads, and a payload outside that set isn't rejected — it just isn't delivered, leaving a driver with a bespoke command vocabulary no option but to subscribe to the raw topic itself and bypass the gate. Letting a driver declare its payloads means those commands route through the safety check rather than around it.
Every servo driver has moved across: bb_servo_feetech 0.4.0, bb_servo_pca9685 0.8.0, bb_servo_pigpio 0.8.0 and bb_servo_robotis 0.5.0. If you're writing an actuator driver, the migration is short and the compiler tells you exactly which modules need it.
Stop Meaning Stop
A real bug shaken out by the same work: bb_servo_pca9685 and bb_servo_pigpio both accepted a Stop command and then dropped it on the floor. handle_command/2 matched Position and let everything else fall through a catch-all that returned success. So a stop reported success while the servo kept being driven — silent, and indistinguishable from working.
Both now cut the pulse train for that actuator's channel and mean it (pca9685 #89, pigpio #83). The test asserts the pulse actually goes to zero, rather than merely that the call doesn't crash — which was the whole bug.
Worth being clear on the distinction, since it's the same one that led me briefly astray above: Stop is a motion command meaning "cease travelling and go passive", not an emergency stop. It even has a :decelerate mode.
Teleop, on Someone Else's Arms
Enough plumbing. Damir Batinović printed and assembled two SO-101 arms of his own, wired them up with Beam Bots, and posted a video to the Discord of teleoperation working — one arm following the other, live.
I've been talking about teleop as a thing I'd like to try since I ordered parts for my second arm. Damir just went and did it — two arms to my one, and got there first while I was busy rearranging how commands reach a servo. Watching hardware I didn't build, running software I did, doing something I hadn't done yet: deeply weird, highly recommended.
(My own second SO-101 is now printed and assembled too, so I can stop being jealous and start recording demonstrations — which is exactly the sort of data bb_policy is going to want to eat.)
Robots for Robots
A theme I didn't expect to be writing up: every package in the ecosystem now ships a usage-rules.md, the convention usage_rules uses to hand LLM coding assistants a package's actual conventions instead of letting them guess. Sixteen repos, one PR each.
There's a decent reason to bother. A wrong example in one shared file gets copied, faithfully and at speed, into fifteen packages — I've watched it happen. Teaching the agents from a canonical source seemed like the highest-leverage fix available. Fix the source, fix the copies.
In the same spirit, bb now runs a third-party CI workflow (#163) that builds external community packages against the bb ref under test. bb_tui and bb_mcuhub are in the matrix. They're allowed to fail without blocking — indicative, not required — but it means I find out that a breaking change broke somebody before they do. It earned its keep immediately: 0.23.0 broke bb_mcuhub, CI told me, and I sent Edgar the migration the same day.
Community Corner
- Andrew Houllette ran a full audit of
bb_jidoagainst the actual Jido 2.3.2 sources and landed the results (#53) — the robot plugin's cached safety state never actually updated (a silent no-op, twice over), state transitions produced routing errors,WaitForStatehad a check-then-subscribe race and treated its timeout as a per-message reset rather than a deadline. All fixed, with tests, in dependency-ordered commits. This is the kind of review that only comes from someone reading the dependency's source rather than its README. - Edgar's
bb_mcuhubgained a direction-aware router — a downlink arrival ascends, and the route table serves only descents. It was also the package that motivatedcommand_payloads/1, since its value-types name their own command struct. - Mauricio's
ex_ratatui0.11.2 shipped, keeping the terminal-3D story ticking along.
Housekeeping
bb0.22.2 added semantic math inspection (#191) —BB.Math.Vec3andBB.Math.Quaternionnow inspect in IEx as reconstructable constructor calls instead of raw tensors, which makes debugging kinematics feel much less like reading a hex dump.bb0.22.3 supports runtime robot parameter defaults (#199), so generated config can reference runtime expressions rather than baking values in at compile time.bb_servo_feetech0.3.4 andbb_so1010.2.3 load servo device config at runtime, withFEETECH_DEVICEoverriding the fallback — no recompile to move a robot to a different serial port.set_effort/4andset_velocity/4raised whenever you omitted the duration, despite the docs saying nil means "until stopped". Non-nullable schema fields, always populated. Nothing caught it because no driver implements effort or velocity yet, so nothing had ever called them. Fixed in 0.25.0.
On the Bench
Gus Workman has shipped the prototype workshop boards — they're in the post as I write this. Once they land I'll be building the Goatmire workshop on the real target instead of my breadboard impression of it. Talk prep continues, still mostly under wraps.
Every Goatmire Workshop Has a Channel Now
Lars has added a Discord channel for each individual workshop at Goatmire this year, and ours is #achieving-balance. If you're coming to the balance bot workshop — or you're just curious what you'd be signing up for — hop into the Goatmire Discord and come say hello. I'll be posting build notes, parts lists and the occasional photo of a robot lying on its side as we get closer.
Between that, the Beam Bots Discord and the forum, there is now no shortage of places to tell me your robot fell over.
Links
bb0.25.0 · 0.24.0 · 0.23.0- Proposal 0021 — Actuator Command Pipeline
bb_servo_feetech·bb_servo_pca9685·bb_servo_pigpio·bb_servo_robotisbb_jido·bb_mcuhub·bb_tuibbon GitHub- Goatmire 2026 — per-workshop Discord channels are up; ours is
#achieving-balance - Beam Bots on the Elixir Forum · Discord