docs(uno-q): actuator run-book (bring-up, calibration, restart rule)

ACTUATOR.md: hardware (TB6600 PUL=D4/DIR=D5), the stepper/stepper_status agent
tools + relay commands, the calibrated 0..9635 envelope, the safe daily bring-up,
the operating rules (park+zero before restart — a restart resets the MCU zero
while the envelope survives), mismatch recovery, recalibration, and the zigbuild
rebuild recipe. Linked from ONBOARDING.md.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-27 17:07:47 +02:00
co-authored by Claude Opus 4.8
parent b0f47ec271
commit 139f2b25e5
2 changed files with 117 additions and 1 deletions
+114
View File
@@ -0,0 +1,114 @@
# Linear actuator — bring-up & run-book
The workshop board can drive a **linear actuator** (stepper on a lead screw/belt) as the
**"Adapt"** half of the Sense→Forecast→Adapt loop: the ADXL355 senses, the agent decides, the
actuator moves. This is open-loop motion with a **calibrated software safety envelope** so it can
never overrun its ends.
> **Read this before powering an actuator-equipped board.** The one rule that bites: a restart
> resets the position zero (see [Operating rules](#operating-rules)).
---
## Hardware
| Part | Detail |
|---|---|
| Driver | **TB6600 / PB6600** (PUL / DIR / ENA, optically isolated). No feedback, no stall detection. |
| Wiring | **PUL → digital pin 4**, **DIR → digital pin 5** (common-cathode: signal `-` pins to GND). ENA left free (driver enabled). |
| Motion | `dir=1` = **into the rail** (away from the zero end) · `dir=0` = **back toward zero**. |
| Speed | ~830 steps/s (600 µs half-period), moderate — safe for most drivers without missed steps. |
| Sensor (same board) | ADXL355 @ `0x1d` behind a **PCA9548A mux** (`0x70`) on channel 0. Unrelated bus (I²C on SDA/SCL); doesn't compete with D4/D5. |
The **TB6600 has no way to sense position or the ends** — that's why travel is bounded in
firmware, not hardware. If you ever add physical limit switches, that becomes the robust upgrade;
until then the envelope below is the guard.
---
## How it's controlled
The resident MCU sketch (`firmware/zeroclaw-node/sketch/sketch.ino`) runs the pulse train and
enforces the limits. Two ways to reach it:
**Agent tools** (cloud brain, within limits):
- `stepper(steps, dir)` — move `steps` (1–4000) in direction `dir` (1 into rail / 0 toward zero).
- `stepper_status()` — read position + limit without moving.
**Relay commands** (`:9999`, for setup/calibration from the host — `nc`/socket):
- `step <count> <dir>` · `zero` · `pos` · `setmax <n>`
Every move is **clamped to `[0, stepMax]`** and the reply reports position, e.g. `pos=1234
max=9635`, ending in `LIMIT` if it hit the soft limit.
---
## Calibrated envelope (this actuator)
```
0 ─────────────────────────────── 9635 ····· 9685
zero (right end) armed safe max hard end
↑ 50-step margin ↑
```
`stepMax = 9635` is **baked into the sketch** (armed on every boot). Hard end measured at ~9685
steps; armed 50 short so a move never reaches the physical stop.
---
## Daily bring-up (safe sequence)
1. **Wire the actuator first, then start the app.** (Touching the bus on a running board resets the
MCU and crashes the app container — wire cold.)
2. Bring the app up (`arduino-app-cli app start …`); confirm `pos` reports `max=9635`.
3. **Home it:** manually park the carriage at the **right end**, then send **`zero`**.
- Now `pos=0` matches reality; the envelope is already armed. Ready.
That's it — the agent can now drive it safely.
---
## Operating rules
- **Park at the right end and `zero` BEFORE any restart.** A container `app restart` **resets the
MCU position to 0** while the carriage stays where it is. If it was parked anywhere but the right
end, firmware `pos` and reality now disagree — and a `dir=1` move would drive into the far stop.
The **envelope (max) survives** a restart; the **zero does not**.
- **Don't stall it.** Open-loop means a stall against a stop **loses steps**, so the zero drifts.
The soft limit exists precisely to avoid this — keep it armed.
- **One session = one home.** Re-`zero` at the start of each session (there's no home switch).
### Recovery — firmware/reality mismatch
If a restart left `pos=0` but the carriage isn't at the right end:
1. `setmax -1` — disarm the clamp temporarily.
2. Jog **`dir=0`** in bursts back to the **right end** (watch it; stop at the end).
3. `zero`, then `setmax 9635` to re-arm.
---
## Calibrating a *different* actuator
If the rail, motor, or TB6600 microstep DIP changes, re-measure:
1. Park at the right end → `zero`.
2. Jog `dir=1` toward the far end — coarse (`step 200 1`) then fine (`step 10 1`) as it nears —
watching. The firmware sums position for you; read it with `pos`.
3. Stop a hair short of the hard stop. Take that `pos`, subtract a ~50-step margin → that's the max.
4. Bake it: set `long stepMax = <value>;` in the sketch and reflash (push sketch → `app restart`,
~50s = a real recompile+flash).
---
## Rebuilding the binary (when the agent tools change)
The `stepper` / `stepper_status` tools live in the ZeroClaw binary
(`crates/zeroclaw-hardware/src/peripherals/uno_q_bridge.rs`). Cross-build for the board:
```sh
cd <zeroclaw>
cargo zigbuild --target aarch64-unknown-linux-gnu --profile release-fast --features hardware --bin zeroclaw
```
`cargo-zigbuild` + zig is the working cross path on macOS (the `aarch64-linux-gnu-gcc` linker isn't
installed). `--features hardware` is **required** or the peripheral tools are stripped. Then push
the binary to `…/apess-onboard/bin/zeroclaw` and `app restart`, or repackage the distributable app
with `package-onboard-app.sh`.
+3 -1
View File
@@ -2,7 +2,9 @@
> **The setup runbook.** For the participant *journey* (each screen's job, the > **The setup runbook.** For the participant *journey* (each screen's job, the
> module→ADD-layer map, open design questions), see > module→ADD-layer map, open design questions), see
> [`WORKSHOP-FLOW.md`](./WORKSHOP-FLOW.md). > [`WORKSHOP-FLOW.md`](./WORKSHOP-FLOW.md). For a board with the **linear
> actuator** (stepper bring-up, calibration, and the park-and-`zero`-before-restart
> rule), see [`ACTUATOR.md`](./ACTUATOR.md).
A team needs two things running: the **APESS stack on their laptop** and the A team needs two things running: the **APESS stack on their laptop** and the
**ZeroClaw node on their Uno Q**. Both are containers. Nothing installs to a host. **ZeroClaw node on their Uno Q**. Both are containers. Nothing installs to a host.