diff --git a/docs/deployment.md b/docs/deployment.md index e1b69aa..5c10ab7 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -276,6 +276,24 @@ If `CMakeLists.txt` or options changed, re-run the `cmake -B build ...` configur [section 2](#2-build-full-with-cameras--mqtt) first. To roll back, keep the previous binary (`fire_gimbal_control.prev`) and swap it back. +> **One-time: `git pull` can delete your `.deploy.env`.** It was tracked by +> mistake until `b0127db`, which stopped tracking it and added it to +> `.gitignore`. Pulling *across* that commit therefore does what git does to any +> file a commit removes — deletes the working copy, credentials and all. It is a +> checkout, not a merge conflict, so there is no warning and nothing to resolve. +> +> Only checkouts that predate `b0127db` are exposed, and only once. Copy it aside +> first: +> +> ```bash +> cp .deploy.env ../deploy.env.bak 2>/dev/null # then pull, then copy back +> ``` +> +> After that pull the file is ignored and stays put. `deploy.sh` already excludes +> it from the rsync, so deploying never overwrites the tower's own copy — this is +> purely about the machine you deploy *from*. To recover one already lost: +> `git show b0127db^:.deploy.env`. + ## 10. Where the log is The program writes a persistent log alongside the self-test and diagnostics diff --git a/docs/hardware-config-plan.md b/docs/hardware-config-plan.md index b057939..d1e1747 100644 --- a/docs/hardware-config-plan.md +++ b/docs/hardware-config-plan.md @@ -425,8 +425,10 @@ Phase 1 is deployable on its own. Phase 2 requires it. ## Progress -Branch `worktree-hardware-config` (worktree at `.claude/worktrees/hardware-config`). -298 tests pass, up from a 204 baseline. +**Complete.** Merged to `main`; the branch `worktree-hardware-config` is kept only +as a marker and has nothing `main` lacks. 327 tests pass, up from a 204 baseline. +Every row below is delivered, and §6 is validated on tower 121 — see the note +after the table. | § | Item | State | |----|------|-------| @@ -439,11 +441,28 @@ Branch `worktree-hardware-config` (worktree at `.claude/worktrees/hardware-confi | 10 | Presence reconciliation (declared vs found) | **done**, 13 tests. `HardwarePresence.h` compares `[Hardware]` against what came up; warn-only by default, refusing under `[Features] strict_presence` with `--degraded` as the field-triage escape (decision 4) | | 11 | `test hardware` | **done**, 11 tests. Five leaves (`axes` / `motor` / `imu` / `env` / `camera`) under a new `hardware` subsystem; included in a bare `test`, and deliberately not gated on a homed gimbal. Nothing declared reports SKIP, never PASS | | 5 | Delete the `Application.cpp` speed literals | **done**, 13 tests. Decision 2 answered: speed is declared as `[Motor.] scan_speed_deg_s` (default **8.0 deg/s**) and converted per tower through `counts_per_deg` (`AxisSpeed.h`). The literals sent 36.4 deg/s against an intended 21.8 | -| 6 | Config handshake — **host half** | **done**, 13 tests. `FirmwareConfig.h` parses `NEED` and plans the `SET` lines; unanswered keys and unknown keys are reported separately. `[Features] mock_needcfg` makes the simulated controller run the handshake, so the host half is verified with no firmware. **The firmware half is not written** | -| 6 | Config handshake — **firmware half** | **done and built** on both targets 2026-09-18 (iota_rp2040 and leonardo, `./deploy.sh --compile-only`); behaviour still unproven on a running rig. `NEEDCFG` state (`ST` code `C`), `NEED` emitted with the banner, `SET `, `USE DEFAULTS`, and the init block split so the residual-motion stop still runs at boot. 33 native tests pass under `pio test -e native`. **Behaviour on a running rig is the one check still outstanding** | +| 6 | Config handshake — **host half** | **done**, 13 tests. `FirmwareConfig.h` parses `NEED` and plans the `SET` lines; unanswered keys and unknown keys are reported separately. `[Features] mock_needcfg` makes the simulated controller run the handshake, so the host half is verified with no firmware. Later extended: the host also re-sends the whole declared block unprompted on every connect, because the firmware asks only once, at boot | +| 6 | Config handshake — **firmware half** | **done, and validated on tower 121.** `NEEDCFG` state (`ST` code `C`), `NEED` emitted with the banner, `SET `, `USE DEFAULTS`, and the init block split so the residual-motion stop still runs at boot. 33 native tests pass under `pio test -e native`. On the rig: the handshake was watched live, `HOME` refused with `ERR need config`, `RESET` did not escape the state, and `USE DEFAULTS` released it | | 6 | Adoption check generalised | **done**, 9 tests. `DUMP` echoes every host-owned value plus a `cfg_rx` bitmask; `test hardware motor` checks all of them, and reports an absent field as unconfirmable rather than as a mismatch against -1. Round trip NEED -> SET -> DUMP -> PASS verified against `mock_needcfg` | +| 14 | Drivetrain out of `MOTOR_CONFIG` | **done.** The struct keeps pins and `homing_kind` only; `platform_iota.h` keeps its measurement record but no longer defines this tower's values. `USE DEFAULTS` now loads a shared **limp-home** set (`LIMP_*` in `config.h`) that is deliberately nobody's real values — slow speeds, generous margins, the gentle search current — so a tower still running them fails `test hardware motor` instead of looking configured. `SW_MODE` is derived from `homing_kind` rather than shared, because disabling the hardware stops on an axis that homes on real switches would remove its last protection. Flash cost, not saved: see the porting plan. Found a real bug on the way — `DIAG` read `normal_speed` / `center_speed` from the compile-time table, so it ran at compiled speeds rather than the tower's declared ones | | 6 | `NEEDCFG` understood host-side | **done**, 5 tests. `AxisState::NeedConfig` parsed from `ST` `C`, named in `DUMP` and the dashboard, and counted as telemetry arriving rather than as a dead link. The firmware enum APPENDS the state (5) rather than inserting it — inserting renumbered every later state and made a READY axis read back as ERROR | +**Validated on tower 121.** The handshake was watched live on the rig, not inferred: +the firmware emitted `NEED`, the host answered, and `DUMP` echoed every value back +with a full `cfg_rx` mask. The refusal path was exercised too — `HOME` returned +`ERR need config`, `RESET` did not escape `NEEDCFG`, and only `USE DEFAULTS` +released it. Homing repeatability then held at **0.26 % across nine runs**, +spanning two firmware generations, a centre change, a doubled search speed and a +tightened stall threshold. + +One gap only showed up on the rig: the firmware asks **once**, at boot, and only +while in `NEEDCFG`, so a configured board says nothing and there was nothing for +the host to answer. Editing `hardware.ini` and restarting the host silently +changed nothing for every key that had no command of its own. The host now +re-sends the whole declared block on connect — confirmed by moving +`enc_deviation` 1000 -> 1200 -> 1000 with host restarts alone, the board never +rebooting. + Presence reconciliation (§10) changes nothing on a tower without a `hardware.ini`: absent is silent and `hardware.declared` stays false. Verified by running the binary both ways. diff --git a/docs/hardware-porting-plan.md b/docs/hardware-porting-plan.md index 5d6d5b6..8a5c1ae 100644 --- a/docs/hardware-porting-plan.md +++ b/docs/hardware-porting-plan.md @@ -301,9 +301,9 @@ commit `aab1925`. | 2 | `Serial.begin()` moved ahead of all hardware init, so a wedged image stays flashable | **done** | | 2 | `[env:iota_rp2040]` PlatformIO target (arduino-pico core, 8 MB flash) | **done and built.** Latest, 2026-09-18, with the config handshake: **iota_rp2040** 51716 B flash (0.6 % of 8 MB), 9108 B RAM (3.5 %); **leonardo** 22646 B flash (**79 % of 28 KB**), 939 B RAM (36.7 %). Both from `./deploy.sh --compile-only` against towers 121 and 111; 33 native tests pass (`pio test -e native`) | | 2 | Per-board pin map + `NUM_AXES` in `platform_leonardo.h` / `platform_iota.h`, as-built wiring | **done** — compile-time, not yet generated from `hardware.ini` | -| 2 | Pins + `NUM_AXES` generated from `hardware.ini` | **not started** — needs the config plan's phase 2 first | +| 2 | Pins + `NUM_AXES` generated from `hardware.ini` | **not started, no longer blocked** — the config plan it waited on is complete. The last open phase-2 item | | 3 | Yaw homing on the real drivetrain | **done** — the endstop was being sheared through at full torque; homing at 189 mA holds. See below | -| 1 | Everything else | blocked on [hardware-config-plan.md](hardware-config-plan.md) | +| 1 | Phase 1 in full — `hardware.ini`, the config handshake, presence reconciliation, `test hardware` | **done.** [hardware-config-plan.md](hardware-config-plan.md) is complete and validated on tower 121; the gate ("`test hardware` green on every existing tower") is met on 121, and still wants running against the Delta fleet | > **Where the firmware gets built.** PlatformIO is not installed on the dev box; the firmware is built > *remotely on a tower* by `firmware/deploy.sh --compile-only`. Locally, the Arduino-free sources @@ -313,11 +313,18 @@ commit `aab1925`. > That catches typos and signatures; it says nothing about size or behaviour, so a remote build is > still the gate. > -> **Flash headroom is now the constraint on the Delta fleet.** The ATmega32U4 build sits at **79 % of -> its 28 KB** (6026 B left), up from 19254 B at the phase 2 gate — that growth is everything since: -> `BOOTSEL`, `CURRENT`, the reduced homing current, `STOP` ending a homing, and the config handshake. -> RP2040 flash is a non-issue at 0.6 % of 8 MB. Deleting the drivetrain fields from `MOTOR_CONFIG` -> (config plan §14) now buys space as well as removing a duplicate definition. +> **Flash headroom is now the constraint on the Delta fleet.** The ATmega32U4 build sits at **79.6 % of +> its 28 KB** (5838 B left), up from 19254 B at the phase 2 gate — that growth is everything since: +> `BOOTSEL`, `CURRENT`, the reduced homing current, `STOP` ending a homing, the config handshake and +> §14. RP2040 flash is a non-issue at 0.6 % of 8 MB. +> +> **§14 does not relieve it — measured, not assumed.** Deleting the drivetrain fields from +> `MOTOR_CONFIG` *cost* 222 B of flash on the AVR (22646 → 22868) while saving 68 B of RAM +> (939 → 871). The table it removed was only ~35 B per axis, and the limp-home constants, the +> `SW_MODE` derivation, the new accessors and the limp-home flag and warning more than spend +> that. An earlier version of this note claimed §14 "buys space"; it does not. It was worth +> doing for the duplicate definition it removes, not for the headroom — so the flash constraint +> still wants a real answer if the Delta fleet is to gain features. > > To actually build it: `cd firmware && ./deploy.sh --compile-only` compiles on the device without > flashing. That rsyncs over the tower's firmware source tree, so it is a deliberate step, not one to