From 5b3662c529686b8805a8a6619cc78e6754b64ab6 Mon Sep 17 00:00:00 2001 From: pgdalmeida Date: Thu, 17 Sep 2026 17:58:46 +0200 Subject: [PATCH] Document the drivetrain keys, the derived degree scale and the homing findings --- docs/configuration.md | 47 +++++++++++++++++++++++++++++++++++ docs/hardware-porting-plan.md | 28 +++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index e075a93..2e98376 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -69,6 +69,10 @@ Parsed and validated by `ConfigLoader` ([src/core/Config.cpp](../src/core/Config | `Motor` | `yaw_heading_at_center` | float | `0` | True bearing (0 = north) at that centre (**calibrate**) | | `Motor` | `pitch_angle_at_center` | float | `0` | Elevation above level at that centre (**calibrate**; not 0 on a real rig) | | `Motor` | `yaw_soft_margin_counts` / `pitch_soft_margin_counts` | int | `5000` | Clearance kept from each hard endstop; sent to the firmware, which enforces it | +| `Motor` | `homing_timeout_s` | int | `65` | How long a full two-end homing may take before the host calls it failed. A property of the drivetrain — a high gear ratio searching gently takes minutes | +| `Motor.` | `r_sense_ohm` | float | — | Sense resistor on that axis's TMC5160 board. **hardware.ini.** Makes the milliamp figures below mean something | +| `Motor.` | `run_current_ma` / `homing_current_ma` | float | — | Coil current (mA RMS) for normal motion, and while hunting an endstop. **hardware.ini** | +| `Motor.` | `travel_deg` | float | — | Mechanical angle between the hard endstops. **hardware.ini.** Homing divides the measured travel by it to derive `_counts_per_deg` | | `Scan` | `grid_file` | string | — | CSV of `yaw_deg,pitch_deg` waypoints; empty → generate | | `Scan` | `yaw_intervals` | int | `56` | Generated yaw positions across `[yaw_min_deg, yaw_max_deg]` | | `Scan` | `yaw_min_deg`/`yaw_max_deg` | float | `-90`/`90` | Generated yaw arc | @@ -588,10 +592,53 @@ The firmware speaks **full-word, newline-terminated** commands in absolute **enc | `ENABLE Y` / `ENABLE P` | init | energize the axis coils | | `HOME` | init | home all axes (firmware runs it non-blocking; watch `ST` state `R→H→A`) | | `SPEED Y\|P ` | init | set the production move speed (VMAX, counts/s) | +| `CURRENT Y\|P ` | init, before `HOME` | motor current as TMC register fields, converted by the host from `[Motor.]` milliamps | +| `CENTER Y\|P ` | init, before `HOME` | the count homing anchors the travel midpoint to | +| `MARGIN Y\|P ` | init, before `HOME` | clearance kept from each hard endstop | | `MOVE ,` | each capture point | drive both axes to absolute counts (combined form) | | `MOVE Y\|P ` | — | single-axis absolute move (`set motorctl`) | | `STOP Y\|P\|ALL` | — | ramp to a controlled stop | +### Motor current, and why the host computes it + +The TMC5160 expresses coil current as two dimensionless registers, and what a given pair +delivers depends on the sense resistor soldered to the board. Only `hardware.ini` knows that +resistor, so the **host** converts milliamps to registers and sends the result; the firmware +keeps compile-time defaults so a bench board still behaves. + +`homing_current_ma` exists for one reason: **an endstop the motor can out-push is not an +endstop.** Homing detects a stop by driving into it until the shaft falls behind the +commanded position. On the IOTA tower, whose gear ratio was raised for wind stability, the +motor simply sheared one screw-head stop past the other — it ran 1.8 turns past the stop with +the encoder never falling behind. At 189 mA it homes against the same screw cleanly. + +The startup log names what the registers **actually** deliver, which is not always what was +asked for: + +``` +yaw motor current: run 756 mA (GLOBALSCALER 80, IRUN 31), homing 189 mA (IRUN 7) + -- R_sense 0.075 ohm from hardware.ini [Motor.yaw] +``` + +Because both registers are write-only on the chip, the firmware echoes them in `DUMP` and the +host checks them **before** homing. If an axis declares a reduced homing current and the +firmware has not adopted it, the host **refuses to home** rather than driving into a stop at +full torque — the fire it is there to prevent. + +### Deriving `counts_per_deg` from the homed travel + +`travel_deg` is a nameplate fact: the mechanical angle between the stops. Homing measures the +same span in counts, so dividing gives the degree scale. On a successful home the host +compares the two and, when they disagree by more than a factor of two, adopts the derived +value and offers it at the usual `y`/`n` prompt, saving to `config.ini` with a `;prev` line. + +Within a factor of two it only logs the comparison. That guard matters: a `gimbal heading +setup` landmark fit is a better measurement than this one, and a routine re-home must not +quietly overwrite it. + +**The derived scale is only as good as `travel_deg`**, which is usually a rough figure — a 1 % +error in the angle is a 1 % error in every bearing. `gimbal heading setup` supersedes it. + Capture is a **move → settle → trigger** cycle: the scheduler issues a `MOVE`, waits until both axes report standstill at the target, then triggers the cameras. **ControlCode 0** walks the scan grid (front to back, repeating); **ControlCode 1** drives yaw to `target_HDG` (pitch held). Telemetry arrives as diff --git a/docs/hardware-porting-plan.md b/docs/hardware-porting-plan.md index 23aec66..6ad6e6e 100644 --- a/docs/hardware-porting-plan.md +++ b/docs/hardware-porting-plan.md @@ -297,6 +297,34 @@ commit `aab1925`. |---|---|---| | 0 | Remote-flash question researched — two viable paths, design rule derived (§2) | **done, and now measured on the board: neither works** (§2). First flash needs the MicroPython REPL or BOOTSEL; remote *update* is unsolved pending a `BOOTSEL` command | | 2 | **Phase 2 gate: RP2040 + TMC5160-BOB at 3.3 V** | **PASSED** on tower 121, 2026-09-15: `IOIN Y=0x3000001E version=0x30`, encoder reading (`X_ENC`), one axis, `speed=3000` | +| 3 | Yaw homing on the real drivetrain | **BLOCKED — no endstop found.** See below | + +### Phase 3, first attempt — 2026-09-16 + +Three homing runs at VMAX 120, 1200 and 6000. What came out of them: + +| Established | Value | +|---|---| +| Speed model | **~0.73 counts/s per unit of VMAX** (87 c/s at 120, 880 at 1200, 4400 at 6000 — predictions held to within 0.5 %) | +| Geometry | **~3,400 counts/degree**, so ~1.23 M counts per turn | +| `steps_per_rev` | The inherited Delta value of 177,000 is **wrong by ~7x** for this drivetrain. It is not a search limit (`motor.cpp` uses it only as a minimum-travel check once both limits are found), so it slowed nothing — but it made the axis look broken, and the check is meaningless until corrected | + +**The axis turned ~2,157,000 counts — about 630 degrees, 1.8 full turns — in one +direction without the encoder deviation ever exceeding ~100 counts** against the +1500 that marks a jam. The motor met no resistance at any speed. + +Homing detects an endstop by the shaft falling behind the commanded position, so +it cannot work if nothing stops the shaft. Two candidates, indistinguishable +over the wire because the encoder sits on the motor side: + +1. **No hard stop on this yaw** — it rotates continuously, and stall-based homing + is the wrong strategy for this rig. +2. **The output is not following the motor** — a slipping coupling or a loose + grub screw. The encoder would track the motor perfectly either way. + +**Resolving it needs a physical observation**: did the gimbal itself rotate ~1.8 +turns, or did the motor spin while the platform stayed put? Check any cabling +through that joint before moving the axis further. | 2 | `src/platform.h` — reset cause and free RAM behind one interface per MCU family | **done** | | 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** on tower 121, 2026-09-15: 49452 B flash (0.6 % of 8 MB), 9068 B RAM. `leonardo` still builds (19254 B) and the 22 native tests still pass |