Cross-platform replacement for the legacy Axon Robotics Servo Programming Software: a released CLI, a live browser app, and a desktop side experiment built with Electrobun to prove UI/runtime fidelity against the CLI and web app, and to explore a macOS app packaging path, all built from scratch by reverse-engineering a closed HW/SW system with Claude Code, a Saleae Logic 8, and Ghidra.
This started as a research experiment — "how far can a single developer get with a coding agent on a reverse-engineering project against a closed system?" — and ended with a working CLI. The full story is in docs/the-adventure.md.
Warning
This is an experimental reverse-engineered project. Use it on your servos at your own risk.
Axon Robotics sells smart servos (Mini, Max, Micro) used by a lot of
FTC robotics teams. The original programmer adapter clips onto the
servo's signal wire and exposes the servo's settings through a
Windows-only .exe. This repo targets that legacy adapter/servo stack
today: bootloader v1.3 hardware and the archived .sfw firmware files
published for it.
axon is a cross-platform replacement: a TypeScript CLI on
Bun that talks to the same USB HID adapter, reads and
writes the same 95-byte config block, and flashes user-supplied vendor
.sfw firmware files. Same legacy hardware, no Windows VM.
The current Axon MK2 programmer/servo family uses bootloader v1.4 and is not implemented yet. The HID/protocol layers are intentionally kept separate so this project can be adapted if hardware becomes available.
Live WebHID app:
Notes:
- Requires a Chromium-family browser with WebHID support
- Requires HTTPS or localhost; GitHub Pages satisfies that
- Best suited to desktops/laptops; this is not intended for phone/tablet use
Install the latest released CLI with the release-hosted installer:
curl -fsSL https://github.com/caryden/servo-programmer/releases/latest/download/install.sh | bashFor a user-local install without sudo:
curl -fsSL https://github.com/caryden/servo-programmer/releases/latest/download/install.sh | \
AXON_INSTALL_DIR="$HOME/.local/bin" bashDownload the latest Windows binary from the latest release:
axon-windows-x64.exe
Rename it to axon.exe, put it on %PATH%, then run:
axon --versionOn macOS/Linux:
axon --version
axon statusOn Windows:
axon --version
axon statusThe desktop app was a side experiment with Electrobun. We pushed it far enough to prove fidelity with the CLI/web stack and to explore a macOS packaging path. The CLI remains the primary supported install surface; desktop packaging is still experimental. Tagged releases are intended to publish a macOS DMG alongside the CLI assets.
Download the latest experimental desktop app from the latest release:
Axon-Servo-Programmer-macos-arm64.dmg
Install path:
- download the DMG from the latest release
- open it and drag
Axon Servo Programmer.appintoApplications - launch it from
Applications
Notes:
- this app is still experimental
- current packaged desktop release is macOS Apple Silicon only
- because the app is not yet signed/notarized, macOS may require
right-click -> Openthe first time - desktop assets are published by the tagged release workflow; merging
to
mainalone does not create a downloadable DMG
Current source supports:
- Presence and diagnostics —
axon status(one-shot),axon monitor(live 300 ms polling), andaxon doctorfor a non-destructive runtime/catalog/USB/HID/servo diagnostic report. - Config round-trip —
axon read(human /--json/--svo/--hex) andaxon write --from cfg.svowith diff, confirm, model-id checks, and read-back verify. Vendor.svofiles work unmodified. - Named parameters with unit conversion —
axon get <param>andaxon set <param> <value>accept user-facing values such as microseconds, percentages, enum names, steps, and raw bytes, then validate against per-model limits.axon set defaultresets to catalog defaults. - Firmware mode flashing —
axon mode set servo,axon mode set cr, oraxon mode set --file custom.sfw. Recovery flashing is available with--recoverwhen a servo is stuck in the bootloader and cannot be identified normally. - External firmware files — vendor
.sfwfiles are not embedded or redistributed.axonfinds user-supplied files in configured search paths, verifies their SHA-256 when known, and decrypts them internally. --jsonsupport — command output and top-level errors expose machine-readable fields, including stable errorcategoryvalues for scripting and coding-agent use.- Cross-platform, no sudo — Mac (Intel + Apple Silicon), Linux (x64 + ARM64), Windows, via node-hid on top of the OS HID framework.
- Embedded servo catalog — per-model metadata, known defaults,
parameter metadata, firmware filenames, and SHA-256s live in
data/servo_catalog.jsonand ship inside the binary.
Full command surface: docs/CLI_DESIGN.md.
# 1. Install Bun if you don't have it
curl -fsSL https://bun.sh/install | bash
# 2. Clone, install dependencies, run from source
git clone https://github.com/caryden/servo-programmer.git
cd servo-programmer
bun install
cd apps/cli
bun run src/cli.ts statusWith the dongle plugged in and an Axon Mini connected, axon status
prints a compact status bar plus the model docs link. For scripts and
agents, use JSON:
cd apps/cli
bun run src/cli.ts --json status{"adapter":"connected","servo":"present","category":"servo_present","mode_byte":"0x03","mode_label":"Servo Mode","model":{"id":"SA33****","name":"Axon Mini","known":true,"docs_url":"https://docs.axon-robotics.com/servos/mini"}}No sudo required on any platform.
Full end-user CLI install notes are in docs/INSTALL.md.
| Model ID | Name | Catalog status |
|---|---|---|
SA33**** |
Axon Mini | Baseline supported model; defaults and firmware hashes are cataloged, fresh GUI/runtime validation is still tracked separately |
SA20BHS* |
Axon Micro | Primary development hardware; same-mode apply, mode switching, discard, and recovery are confirmed in CLI/web/desktop, with servo-mode catalog completion still pending |
SA81BHMW |
Axon Max | Model ID and firmware hashes are known from .sfw headers; physical config/default capture still needed |
Got an Axon Max, another Micro capture, or an unknown model? Please
help fill out the catalog. Plug it in, run
cd apps/cli && bun run src/cli.ts read --svo > my-model.svo, and
open an issue
with the file attached and the model ID string from axon status.
I'll add the model/defaults to
data/servo_catalog.json.
The repo is now organized as a Bun workspace:
apps/cli/— productionaxonCLIapps/web/— browser WebHID appapps/desktop/— Electrobun desktop PoC / side experimentpackages/core/— shared transport-agnostic protocol/catalog logicpackages/ui/— shared probe UI used by the browser and desktop appspackages/transport-nodehid/— Node/Bun HID transportpackages/transport-webhid/— browser WebHID transport
This repo does not redistribute Axon .sfw firmware files. Download
the legacy programmer files from
Axon's archive page
and either pass them explicitly:
axon mode set --file ~/Downloads/Axon_Mini_Servo_Mode.sfwor place them in a firmware search directory:
$AXON_FIRMWARE_PATH- User firmware cache:
- macOS:
~/Library/Application Support/axon/firmware - Linux:
$XDG_DATA_HOME/axon/firmwareor~/.local/share/axon/firmware - Windows:
%LOCALAPPDATA%\Axon\firmware
- macOS:
- Repo-root
downloads/when running from source
Then catalog mode changes can find and hash-check the files:
axon mode set servo
axon mode set crIf a failed flash leaves a servo in bootloader mode, recovery flashing
skips normal identity/config reads and uses either the .sfw header or
an explicit catalog target:
axon mode set --file "$HOME/Downloads/Axon Micro Servo Mode.sfw" --recover
axon mode set servo --recover microThe whole thing is a research experiment: a single developer plus
coding agents, pointed at a closed vendor stack, to see how far that
pairing could get. The tools that actually mattered were Ghidra,
Saleae wire capture, hidapi/node-hid, WebHID, and some ETW captures in
a Parallels Windows VM. libusb was part of the investigation, but it
turned out to be a dead end rather than part of the final system.
The path:
- Static analysis in Ghidra recovered the
.sfwfirmware format: a Brian Gladman AES-128 reference implementation with a hardcoded 16-byte key of"TTTTTTTTTTTTTTTT", found by walking backwards from theError 1030: Firmware is incorrect.string. - A Saleae Logic 8 on the servo signal wire recovered the on-wire
framing: 9600-baud 8N1 Dynamixel v1, with the standard
FF FF <id> <len> ...header and a one-byte checksum. - Dual capture (libusb and the Saleae sampling simultaneously) proved the dongle is a transparent HID-to-wire proxy — every byte written to HID interface 1 shows up unchanged on the serial line.
- A libusb detour chasing what looked like a transport bug turned out to be a misdiagnosed protocol bug. The final shipping clients use hidapi/node-hid and WebHID instead.
- A clean CLI on Bun + node-hid, with the catalog embedded at
build time and externally supplied
.sfwfiles decrypted internally.
Most of the hands-on work — Ghidra scripting, protocol decoding, TypeScript scaffolding, tests — was driven by the agent. The full first-person write-up, including every dead end, is in docs/the-adventure.md.
- docs/the-adventure.md — first-person story of how this was built (long-form)
- docs/CLI_DESIGN.md — the v1 command surface
- docs/INSTALL.md — install and build-from-source notes
- docs/wire-protocol.md — USB HID and on-wire protocol reference
- docs/BYTE_MAPPING.md — byte offset → parameter mapping
- docs/logic-analyzers.md — low-cost logic analyzer options and Saleae notes
- docs/licenses.md — dependency license audit
- RELEASE.md — maintainer release process and lifecycle
- research/ — captures, decompiled exe output, Python test scripts used during reverse engineering
- vendor/ — vendor-format samples and licensing notes
- CHANGELOG.md — release notes
axon commands expose JSON output where useful, and top-level
AxonError failures include a stable category field in --json
mode. Agents and scripts should branch on that category rather than
screen-scraping human text. Start with
docs/CLI_DESIGN.md for the command surface and
exit codes, and
docs/wire-protocol.md if you need transport
details. When setup or USB ownership is unclear, start with
axon --json doctor; it reports stable per-check IDs and categories.
The current direction is to keep the CLI self-explanatory enough for
both humans and agents: good --help, stable JSON, and direct error
messages with concrete suggestions rather than a repo-specific skill.
See CONTRIBUTING.md. The most useful contribution
right now is physical hardware testing on Axon Max and additional
Micro/unknown servos to fill in catalog gaps (see above). Bug reports
and protocol-edge-case .sal captures are also welcome.
- Axon Robotics for making genuinely cool hardware. This project is a workaround for a Windows-only exe, not a complaint about the servos.
- The FTC robotics community for the use case.
- Saleae for a logic analyzer with an automation API that made the dual-capture experiment cheap enough to try.
- Ghidra for making the static-analysis phase possible at all.
- The libusb, hidapi, node-hid, and Bun maintainers for the open infrastructure this sits on top of.
- Anthropic and Claude Code for the early reverse-engineering push that got the project off the ground.
- OpenAI and Codex for the later hardening, shared UI, browser/desktop integration, recovery flows, releases, and overall productization push that turned the research repo into a usable tool.
MIT.
The Axon Robotics vendor binaries (.sfw firmware files, the Windows
.exe) are not redistributed with this project. They're
downloadable from
docs.axon-robotics.com
— you obtain them yourself and place them where the CLI searches, or
pass them with --file. See vendor/README.md for
licensing notes.