Tuning parameters
The tuning parameters are the live-adjustable knobs of the autopilot's foiling control system: the roll and pitch attitude loops, the ride-height loop, the rear-foil schedule, coordinated-turn banking, and the operating-mode switches. Every one of them can be read and written over the CAN bus while the boat is running, from the helm keyboard, the datalogger, or any other node — which is what makes on-water tuning possible without a laptop, a cable, or a MAVLink link.
The autopilot side of this is foil_tune.lua, a small dedicated script on the flight controller that bridges CAN to the parameter system. It is deliberately separate from the control script (hydrofoils.lua): a bug in the tuning bridge can never take down the control loop, and simply not uploading the file removes the tuning surface entirely. The two never talk to each other — parameters are the shared state. The keyboard tool (tools/foil_tune.py) and the display's foiling screen mirror the same map; the hotkeys and cursor grid are documented in FOILING_PARAMETERS.md of the eoi-can repository.
At a glance
| Property | Value |
|---|---|
| Protocol version | 11 (both sides check it at connect; index 0xFE returns it)
|
| CAN identifiers | 0x260 set · 0x261 value/ack · 0x262 request — 11-bit standard IDs
|
| Value encoding | IEEE754 float32, little-endian |
| Parameters | 49 tunable entries (indices 1–59 with gaps; 37/38/54/58 retired, 59 off the grid) |
| Persistence | All writes volatile by default — a power-cycle reverts a bad tune; persisting to flash is an explicit flag |
| Safety | FC-side whitelist with min/max clamps; envelope and mode entries locked while foiling |
| Boot behaviour | One unsolicited full dump ~5 s after every FC boot, so passive listeners repopulate |
| Pacing | Dumps and bulk restores stream at ≤ 8 frames per 50 ms; senders pace to ~40 frames/s |
| Bus priority | The 0x2xx tuning IDs lose CAN arbitration to every safety frame (0x010–0x012) — tuning can only use idle bus time
|
| Source of truth | The PT table in foil_tune.lua; mirrored in tools/foil_tune.py, which generates this page (tools/export_wiki.py)
|
How a parameter is read and written
Three frame types carry everything. A set (0x260) names a parameter by its index, carries the new value as a float32, and one flag bit: persist-to-flash, which is normally left off so that everything written during a tuning session evaporates on the next power-cycle unless deliberately saved. A request (0x262) asks for one index, or for the whole table (0xFF), or for the protocol version (0xFE). Every set and every request is answered by a value frame (0x261) that carries a status byte and — crucially — the value read back from the flight controller after clamping, locking and type-casting. The reply never echoes what was merely requested: it reports what the boat is actually using. A display that simply listens to 0x261 therefore mirrors every tuning change made by anyone, in real time, without sending a single frame.
| Code | Name | Meaning |
|---|---|---|
| 0 | ok | Accepted (or read) as-is |
| 1 | unknown | No such index (includes the retired indices 37 and 38) |
| 2 | clamped | The value hit the whitelist's min/max; the reply carries the clamped value in force |
| 3 | failed | The parameter system refused the write (should not happen in practice) |
| 4 | unavailable | The parameter does not exist yet — HYD_* before hydrofoils.lua has booted, or TRN_* when foil_turn.lua is not uploaded at all. For the turn parameters this doubles as the "is the feature installed?" probe
|
| 5 | LOCKED | Refused: the entry is envelope/mode-class and the boat is under autopilot control (see below). The reply carries the unchanged value in force |
Three bus behaviours are worth knowing when building anything that listens:
- Boot dump. About five seconds after every flight-controller boot, the bridge transmits one unsolicited full dump — one
0x261per entry, then an end marker (index0xFF, value = entry count). A passive listener is repopulated after every power-cycle without asking, and can always re-request with0x262/0xFF. - Cursor convention. When the helm keyboard's cursor settles on a cell, the tool re-requests that cell's parameter(s). The resulting
0x261is read-only — no value changes — but tells the display where the cursor is: the latest solo value frame is "cursor here". During active tuning the acks serve the same purpose. - Congestion is a non-issue by construction. CAN arbitration gives numerically lower IDs priority, so the tuning traffic (
0x260–0x262) and the throttle beeper (0x338) always yield to the safety frames — rear foil0x010, height sensors0x011/0x012— bit-for-bit, without delaying them. A full 49-parameter restore plus its acks costs about 13 ms of wire time at 1 Mbit/s, and is paced anyway to protect the FC's receive buffer.
Safety model
Two independent mechanisms protect the boat from the tuning surface itself, because the bridge is reachable from any node on a shared bus and — since the helm keyboard exists — from a single keystroke:
- Whitelist and clamps (wrong values). Only the entries in the table below exist; everything else is refused. Every accepted value is clamped FC-side to the listed min/max, so no UI bug can write an out-of-range gain. A few floors are deliberately non-zero: the rate-loop P gains cannot go below 0.02, because
P = 0means "inner loop off", which is not a tune and must not be one key-repeat away on a manned foiler. (HYD_RKP, the rear pitch term, was floored at 0.15 on the same reasoning until 2026-08-24, then 0, and was retired with the term in 2026-09.) - In-flight locks (wrong moments). Some entries are refused outright (status 5) whenever the enable pin says the autopilot is controlling the boat. These are the entries that are not tuning at all: they change what the system is doing rather than how well it does it. A wrong gain degrades gradually; these act instantly. The attitude envelope (
ROLL_LIMIT_DEG,PTCH_LIM_*) defines what the boat is ever allowed to do — its whole legal range contains capsize territory, so changing it is an ashore decision. The mode knobsSCR_USER1(flips to roll-test mode, dropping the height loop on the very next 10 Hz iteration) andSCR_USER4(open-loop rear-foil jog, a bench calibration function) are flight-mode changes, not parameter tweaks.TRN_REVflips the steering sense — mid-turn that would drive the bank to the opposite stop. To change any of them: enable off, write, re-enable — a deliberate two-hand action.
The lock's arbiter is the enable pin, and its ambiguity fails safe: if the button configuration is not verifiably in place, the bridge assumes the boat is under control and locks — the safe default for "may I change this?" is the opposite of the safe default for "should I control the boat?". Note that TRN_ENABLE is deliberately not locked: it is the in-flight off-switch for the banking feature, and switching it off ramps the bank out at TRN_RATE rather than snapping.
Recovery from a bad (volatile) tune is always the same: enable off, power-cycle — everything not explicitly persisted reverts to the values in flash.
How the parameters reach the foils
The boat has three foils, and they are driven in two quite different ways. Knowing which parameter reaches which foil, and in which direction, is most of what is needed on the water.
Front foils (the two elevons)
Each front wing's angle is the sum of three parts, recomputed ten times a second. Angles are the foil's angle to the hull; in the water add the hull's pitch.
| Part | Set by | Typical size |
|---|---|---|
| Trim | SERVO_LIMITS in hydrofoils.lua: the 8 m/s cruise angle, the same on both wings |
+2.8° |
| Feed-forward | HYD_FRNTFF × (rear schedule angle − its 9 m/s angle), so HYD_RSCHED and HYD_RSCALE reach the fronts through it. The same on both wings; it moves with speed only, by shifting the trim |
+3.8° in the run-up, +2.0° at 28 km/h, 0 at 9 m/s (at the seeded 0.944) |
| FBWA output | Pitch (common mode, both wings the same way) ± roll (differential, opposite ways) from the rate loops, × SCALING_SPEED / speed |
From the trim up to the +9.5° stop, or down to the −7.5° stop |
Both wings stop at −7.5° and +9.5°, matched in degrees since 2026-10-18, so at the +2.8° trim FBWA has 6.7° of up travel and 10.3° of down. The feed-forward moves the trim itself, and ArduPlane scales the FBWA output between that moved trim and the stops: a larger feed-forward leaves less up travel for FBWA, and the sum can never pass a stop.
What the FBWA output is asked for comes down a chain: ride-height error → HYD_KP / HYD_KI / HYD_KD, clamped to HYD_CMDMIN … HYD_CMDMAX → pitch target (within PTCH_LIM_*) → ÷ PTCH2SRV_TCONST → pitch-rate target → PTCH_RATE_FF / P / I / D → both wings. Roll runs the same chain from the roll target: level, a bank from foil_turn.lua, or a roll step.
While the hull is in the water it holds the pitch, so the chain never closes. The wings sit at a steady deflection set by the pitch demand (HYD_KP × the height error, clamped at HYD_CMDMAX), PTCH2SRV_TCONST, the pitch P and FF gains and SCALING_SPEED. Meanwhile the script holds both integrators off, the height loop's and FBWA's pitch-rate integrator, so nothing winds up against the hull. On 2026-10-18 that left the fronts at about 4° at 27 km/h: the 2.8° trim plus about 1.3° of elevon from a ~2° demand, with HYD_FRNTFF at 0. The live knobs that add front angle in the run-up are HYD_FRNTFF (by far the largest), PTCH2SRV_TCONST and SCALING_SPEED.
Rear foil (the stepper)
The rear angle comes from the speed schedule alone: −4.5° + HYD_RSCHED / speed², held between −2° and the +5.6° run-up cap, × HYD_RSCALE and capped again. A slow follow-up (HYD_RTKI, off the grid) adds whatever standing demand the height integrator is carrying. No FBWA gain and no height-loop gain moves the rear directly.
| Parameters | Front foils | Rear foil |
|---|---|---|
RLL_*, RLL2SRV_*, ROLL_LIMIT_DEG, TRN_*, SCR_USER3 |
Differential (roll) | — |
PTCH_*, PTCH2SRV_*, HYD_KP … HYD_CMDMIN, HYD_TARGET, SCR_USER2 |
Common mode (pitch) | — |
SCALING_SPEED |
Scales both, roll and pitch | — |
HYD_RSCHED, HYD_RSCALE |
Common mode, through HYD_FRNTFF |
Yes: the schedule |
HYD_FRNTFF |
Common mode (the feed-forward) | No |
SCR_USER4 |
— | Bench jog, only while disabled |
Parameter map
Indices are the wire contract: they are only ever appended, never renumbered, and a change bumps the protocol version. Hotkey is the helm-display key that jumps to the cell (case matters: P is the rate loop's P, p the height loop's). Fine and coarse are the increment sizes used by the keyboard's volume keys / +− and <> respectively — conventions of the tools, not of the protocol, which always carries absolute values.
Roll inner loop
The ArduPlane FBWA rate-plus-angle controller, driving the two front foils differentially: one wing's angle goes up while the other's goes down (elevon mixing), which rolls the boat. A right-roll demand raises the left foil and lowers the right. Roll is the high-authority axis, about 16 °/s of roll rate per degree of differential foil at 8 m/s, so its gains are numerically small.
| Idx | Hotkey | Parameter | Min | Max | Fine | Coarse | Locked | Description |
|---|---|---|---|---|---|---|---|---|
| 1 | P |
RLL_RATE_P |
0.02 | 2 | 0.005 | 0.02 | Differential front-foil angle per °/s of roll-rate error: how hard the two wings work against each other to stop an unwanted roll. Higher = a firmer, quicker correction; too high and the wings flap against each other in a fast roll wobble. The floor is non-zero on purpose: 0 would switch the roll loop off | |
| 2 | I |
RLL_RATE_I |
0 | 2 | 0.005 | 0.02 | Builds up a standing differential angle that holds the boat level against a steady roll moment: a leaning driver, uneven load, a side current. Too low and the boat flies with a standing heel; too high and it wallows slowly from side to side. ArduPlane freezes this integrator whenever the speed estimate is at or below AIRSPEED_MIN (5 m/s), so below that a lean is held by P and FF alone
| |
| 3 | D |
RLL_RATE_D |
0 | 0.5 | 0.001 | 0.005 | Differential angle against roll acceleration: damps fast roll motion, but feeds sensor noise straight into wing chatter. Seeded 0 | |
| 4 | F |
RLL_RATE_FF |
0 | 3 | 0.01 | 0.05 | Differential angle in direct proportion to the roll rate being asked for. It moves the wings the moment a bank or roll step is demanded, before any error builds up: ArduPlane's workhorse term. The seed is the model plant inverse (0.33), not a fitted value | |
| 5 | M |
RLL_RATE_IMAX |
0 | 30 | 0.1 | 0.5 | Cap on how much standing differential the roll integrator may hold. The units are the PID's internal radians: full differential travel is IMAX 0.785, so the seeded 10 is no cap at all (only the anti-windup at full surface applies). A 0.1 m driver lean at unstick speed needs about 0.37 | |
| 6 | C |
RLL2SRV_TCONST |
0.1 | 2 | 0.05 | 0.1 | Angle→rate time constant (s): the roll-rate demand is the roll error divided by this. Smaller = the wings are driven harder per degree of heel and the boat levels faster; too small overshoots | |
| 7 | R |
RLL2SRV_RMAX |
0 | 180 | 5 | 15 | Cap on the roll rate the angle loop may ask for (°/s), and so on how fast the wings swing apart after a big disturbance. 0 = no cap | |
| 8 | L |
ROLL_LIMIT_DEG |
5 | 20 | 1 | 5 | yes | Largest roll target the controller will ever ask for (°). It bounds turn banking and roll steps, and so the differential foil angle used to reach them. 45° of roll on a foiling boat is a capsize, not a limit. Locked in flight: it is the airframe envelope, not a gain |
| 9 | T |
RLL_RATE_FLTT |
0 | 100 | 1 | 5 | Low-pass filter on the roll-rate target (Hz). Lower = smoother wing motion but more lag, which costs stability margin | |
| 10 | E |
RLL_RATE_FLTE |
0 | 100 | 1 | 5 | Low-pass filter on the roll-rate error (Hz); same trade as FLTT | |
| 11 | G |
RLL_RATE_FLTD |
0 | 100 | 1 | 5 | Low-pass filter on the D term (Hz), the first knob against high-frequency wing chatter | |
| 12 | S |
RLL_RATE_SMAX |
0 | 200 | 5 | 20 | Slew guard: when the roll output moves faster than this, ArduPlane automatically lowers P and D until it calms down. An anti-oscillation safety net, not a tuning gain. 0 = off |
Pitch inner loop
The same controller structure for pitch, driving both front foils together (common mode): nose-up raises both wings' angle, adding lift at the bow. The rear foil is not on this loop: it follows its speed schedule only (below). Pitch is the low-authority axis, about 1.8 °/s of pitch rate per degree of common-mode foil at 8 m/s, nine times weaker than roll, so its gains run several times higher. While the hull is in the water the pitch is pinned, so these gains set a steady wing deflection there rather than a pitch response.
| Idx | Hotkey | Parameter | Min | Max | Fine | Coarse | Locked | Description |
|---|---|---|---|---|---|---|---|---|
| 16 | P |
PTCH_RATE_P |
0.02 | 8 | 0.02 | 0.1 | Common-mode front-foil angle (both wings together) per °/s of pitch-rate error. Higher = the bow is held more firmly; too high oscillates in pitch. In the run-up the hull pins the pitch, so the error never closes and P and FF hold a steady deflection: they are what deflects the wings there. Non-zero floor, as roll | |
| 17 | I |
PTCH_RATE_I |
0 | 8 | 0.02 | 0.1 | Builds up the standing common-mode front angle that trims the boat at its current speed and load: the cruise trim. Too low and the boat rides off its pitch target; too high and it porpoises slowly. While the hull is in the water and the boat has not foiled since it was last slow, hydrofoils.lua holds this integrator at 0 (see PTCH_RATE_IMAX) so it cannot wind up against the hull
| |
| 18 | D |
PTCH_RATE_D |
0 | 0.5 | 0.001 | 0.005 | Common-mode angle against pitch acceleration: damps fast bow motion but feeds noise to the wings. Seeded 0 | |
| 19 | F |
PTCH_RATE_FF |
0 | 4 | 0.01 | 0.05 | Common-mode angle in direct proportion to the pitch rate being asked for: moves both wings the instant a pitch change is demanded. Seeded from the model plant inverse | |
| 20 | M |
PTCH_RATE_IMAX |
0 | 40 | 0.1 | 0.5 | Cap on the standing common-mode angle the pitch integrator may hold. Radian units, as roll: full travel = IMAX 0.785, so the seeded 17.5 is no cap. The script overrides it in RAM: hydrofoils.lua sets it to 0 while the hull is down, then restores this value at unstick. That gate, with the 2° HYD_CMDMAX clamp, is why the fronts reached only about 4° hull-down on 2026-10-18: trim + P + FF, with no integrator
| |
| 21 | C |
PTCH2SRV_TCONST |
0.1 | 2 | 0.05 | 0.1 | Angle→rate time constant (s): the pitch-rate demand is the pitch error divided by this, so it sets how much the wings deflect per degree of pitch error. With the hull pinned in the run-up, a smaller value means more front angle for the same demand. Too small overshoots in flight | |
| 22 | R |
PTCH2SRV_RMAX_UP |
0 | 180 | 5 | 15 | Cap on the nose-up pitch rate (°/s) the angle loop may ask for. On the display this is one cell with RMAX_DN: the two are edited together and kept equal
| |
| 23 | R |
PTCH2SRV_RMAX_DN |
0 | 180 | 5 | 15 | Cap on the nose-down pitch rate (°/s); paired with RMAX_UP as above
| |
| 24 | X |
PTCH2SRV_RLL |
0 | 1.5 | 0.01 | 0.05 | Extra nose-up asked for in a bank (aircraft turn compensation), which would move both front wings up in every turn. Seeded 0: a boat does not pitch in a bank the way an aircraft does, which is why the floor is 0 instead of ArduPlane's customary 0.7 | |
| 25 | L |
PTCH_LIM_MAX_DEG |
1 | 10 | 1 | 5 | yes | Largest nose-up pitch the boat may ever be asked for (°). The boat lifts off by pitching up, so this sets the lowest foiling speed: the model unsticks at about 6.2 m/s with 5° and 5.75 m/s with 7°, but each degree costs about 1° of front-foil stall margin at that moment. One display cell with LIM_MIN, mirrored: + widens the envelope. Locked in flight
|
| 26 | L |
PTCH_LIM_MIN_DEG |
−10 | −1 | 1 | 5 | yes | Largest nose-down pitch (°). This bounds the top foiling speed: past the speed where the trim attitude reaches it (about 10.3 m/s at −3°), the boat cannot shed lift by pitching and climbs until the foils ventilate. Mirrored pair of LIM_MAX
|
| 27 | T |
PTCH_RATE_FLTT |
0 | 100 | 1 | 5 | Low-pass filter on the pitch-rate target (Hz): smoother wing motion for more lag | |
| 28 | E |
PTCH_RATE_FLTE |
0 | 100 | 1 | 5 | Low-pass filter on the pitch-rate error (Hz) | |
| 29 | G |
PTCH_RATE_FLTD |
0 | 100 | 1 | 5 | Low-pass filter on the pitch D term (Hz) | |
| 30 | S |
PTCH_RATE_SMAX |
0 | 200 | 5 | 20 | Slew guard for pitch, as roll: lowers P and D automatically when the wings start moving faster than this. 0 = off |
Speed scaling
One parameter stands apart because it silently retunes everything above: ArduPlane multiplies every surface output on both axes by SCALING_SPEED / airspeed (clamped to roughly [0.5, 2]). So it changes how far both front wings move for a given error, at every speed. It must be set to the boat's real foiling cruise speed, or none of the seeded gains mean what they say: a boat doing 8 m/s with the firmware default of 15 would run nearly double gains.
| Idx | Hotkey | Parameter | Min | Max | Fine | Coarse | Locked | Description |
|---|---|---|---|---|---|---|---|---|
| 31 | Q |
SCALING_SPEED |
4 | 15 | 0.5 | 1 | Multiplies every FBWA output, roll and pitch, on both front wings by SCALING_SPEED / speed. Lower it and the wings move less for the same error at every speed; raise it and they move more. Seeded 8. On 2026-10-18 it was lowered to 7, about 12% less elevon at 27 km/h. Listed in the Pitch group, but it is not a pitch gain, and it never touches the rear foil
|
Height outer loop
The ride-height controller in hydrofoils.lua: it turns the EKF's height-above-water estimate into a pitch demand for the inner loop, so it acts on the foils only through the front wings' common mode. Too low = nose-up = more front-foil angle. These parameters are created when that script boots, so they answer unavailable (status 4) for the first seconds after power-on.
| Idx | Hotkey | Parameter | Min | Max | Fine | Coarse | Locked | Description |
|---|---|---|---|---|---|---|---|---|
| 32 | p |
HYD_KP |
0 | 2000 | 10 | 50 | Turns a height error into a nose-up pitch demand, and through FBWA into common-mode front-foil angle. At 960, 0.1 m below target asks for about 1° of nose-up. Higher = the boat chases its target harder; too high porpoises | |
| 33 | k |
HYD_KI |
0 | 500 | 5 | 20 | Integrates the remaining height error into a standing pitch demand, which trims out steady offsets such as load or a wrong schedule. It only integrates while foiling (front-foil height above 0.18 m) and after the takeoff hold, and decays while the hull is down, so it cannot wind up in the run-up | |
| 34 | d |
HYD_KD |
0 | 2000 | 10 | 50 | Damps on the climb rate: nose-down while climbing, nose-up while sinking. At 1800, climbing at 0.1 m/s asks for about 1.8° of nose-down. This is what stops the fly-up. It uses the EKF's fused climb rate, so it is phase-consistent with the P term and blind to wave-surface motion by design | |
| 35 | h |
HYD_IMAX |
0 | 500 | 10 | 50 | Cap on the height integrator, in hundredths of a degree of pitch demand: 150 = 1.5° | |
| 36 | t |
HYD_TARGET |
0 | 1 | 0.01 | 0.05 | Ride-height setpoint (m) at the front foils. Higher = the foils run shallower, closer to ventilating; the strut breaches at about 0.56 m. Lower = the hull starts touching in waves. The most-used control on the water | |
| 37 | — | retired | Was HYD_HSRC (height-source select, removed when control went EKF-exclusive). Answers unknown; the index is reserved forever
| |||||
| 38 | — | retired | Was HYD_HDIV (EKF-vs-geometric divergence gate, removed — EKF3's own innovation gating covers it). Reserved forever
| |||||
| 39 | b |
HYD_ARM |
0 | 3.8 | 0.05 | 0.2 | Ride-height reference point: metres from the sensors back to the point whose height is controlled (2.4 = the front foils, 1.4 m forward of the FC). Vehicle geometry, not a gain: only change it if the foil station is re-measured. The ceiling is the true 3.8 m sensor arm | |
| 52 | g |
HYD_CMDMAX |
0.5 | 5 | 0.1 | 0.5 | Most nose-up pitch the height loop may ask for (°): its own authority, separate from the airframe envelope PTCH_LIM_*, which stays locked. It only bites when HYD_KP × height error (+ D) asks for more. Hull down on 2026-10-18 the loop asked for about 2.1° against the 2.0 clamp, so raising it alone adds little front angle there: the demand, not the clamp, is what is small. Higher = more nose-up authority in flight, so a bigger possible fly-up. One display cell with CMDMIN, mirrored
| |
| 53 | g |
HYD_CMDMIN |
−8 | −0.5 | 0.1 | 0.5 | Most nose-down pitch the height loop may ask for (°): how hard it can push the bow down, and so the fly-up overshoot. The first knob to reach for after watching a fly-up |
Rear-foil schedule
The rear foil's speed schedule, also in hydrofoils.lua. The rear is trim only: a stepper commanded over CAN to the angle that carries its share of the boat at the current speed, with no part in attitude or height control. Two of the three knobs here also move the front foils, through the front feed-forward, and HYD_FRNTFF moves only the fronts. See How the parameters reach the foils.
| Idx | Hotkey | Parameter | Min | Max | Fine | Coarse | Locked | Description |
|---|---|---|---|---|---|---|---|---|
| 54 | — | retired | Was HYD_RKP (the rear foil's pitch-error term: 0 from tune rev 22, removed with the term at rev 23 / protocol v10 — the rear is trim only). Reserved forever
| |||||
| 55 | W |
HYD_RSCALE |
0.5 | 1.2 | 0.02 | 0.1 | Multiplies the rear foil's scheduled angle at every speed. The result is capped at the 5.6° run-up angle again, so above 1 it cannot raise the run-up angle, only keep the rear at the cap to a higher speed. It also moves the front trims, through HYD_FRNTFF. In steady foiling a change does not alter any foil's flying angle: it tilts the boat's trim attitude instead. In the run-up, with the hull holding the attitude, it changes the rear lift directly. 1.0 since rev 27
| |
| 56 | Y |
HYD_RSCHED |
0 | 1200 | 5 | 25 | Strength of the rear foil's speed schedule (deg·m²/s²): rear angle = −4.5° + RSCHED / speed², held between −2° and the 5.6° run-up cap. Higher = more rear angle at every speed, and the rear stays at the cap to a higher speed: at 493 it leaves the cap at 25 km/h and sits at 3.6° at 28 km/h. It moves the front trims too, through HYD_FRNTFF. Lowering it takes lift away in the run-up
| |
| 57 | V |
HYD_FRNTFF |
0 | 1.2 | 0.01 | 0.05 | Moves the front foils only, despite sitting in the Rear group. Both front trims move by this fraction of the rear schedule's change from its 9 m/s (cruise) angle: front trim += FRNTFF × (rear angle now − rear angle at 9 m/s). At 0.944 (moment balance, rev 27) that is +3.8° on both fronts in the run-up, +2.0° at 28 km/h, 0 at 9 m/s and negative above it. At 0 the fronts sit at their +2.8° trim at every speed (2026-10-18 run 21). It never changes the rear
| |
| 58 | — | retired | Was HYD_IGATE (integrator-gating bitmask, added to measure what the two integrator gates were worth and removed once measured). Reserved forever
| |||||
| 59 | — | off the grid | Is HYD_RTKI, since tune rev 22 the rate at which the height integrator's standing pitch demand is moved onto the rear foil (1/s; seeded 0.05 = a 20 s hand-over, 0 = off). It keeps the height loop centred when the speed schedule is wrong — a heavier driver, a current, a wrong RSCHED. On no cell of the grid on purpose: a rate to leave alone, not a knob for the water. Before rev 22 the same index was the abandoned front-deflection follow-up gain. The index is spoken for and must never be reused
|
Coordinated-turn banking
Served by a third script, foil_turn.lua, which banks the boat into turns from the steering input. If that script is not uploaded, all six answer unavailable — which is itself the "is the feature installed?" check.
| Idx | Hotkey | Parameter | Min | Max | Fine | Coarse | Locked | Description |
|---|---|---|---|---|---|---|---|---|
| 40 | N |
TRN_ENABLE |
0 | 1 | 1 | 1 | Feature on/off. Deliberately not locked: this is the in-flight kill switch, and disabling ramps the bank out at TRN_RATE instead of snapping
| |
| 41 | U |
TRN_ON |
5 | 60 | 1 | 5 | Steering input (%) at which banking starts. Below it the roll target stays level and the wings get no turn differential | |
| 42 | A |
TRN_FULL |
10 | 100 | 1 | 5 | Steering input (%) giving full bank (TRN_MAX)
| |
| 43 | Z |
TRN_MAX |
0 | 20 | 0.5 | 2 | Largest bank angle (°) the steering may ask for; 0 = never bank. The bank is a roll target, so it reaches the foils as front-wing differential. The height target drops with the bank so the outside foil keeps its depth, and the bank is limited to what the current ride height can pay for | |
| 44 | H |
TRN_RATE |
1 | 20 | 0.5 | 2 | How fast the bank may change (°/s): into a turn, out of it, and on signal loss | |
| 45 | J |
TRN_REV |
0 | 1 | 1 | 1 | yes | Steering-sense reverse. Locked in flight: flipping it mid-turn would drive the bank to the opposite stop |
Mode and test knobs
The scripting user parameters that hydrofoils.lua reads live. Two of them are flight-mode changes and therefore locked while foiling; the two roll-test demands stay live because sweeping them is the test.
| Idx | Hotkey | Parameter | Min | Max | Fine | Coarse | Locked | Description |
|---|---|---|---|---|---|---|---|---|
| 48 | y |
SCR_USER1 |
0 | 1 | 1 | 1 | yes | Operating mode. 0 = height control (full cascade). 1 = roll test / wheelie: the height loop is off, FBWA holds a fixed nose-up pitch (SCR_USER2) so the front wings lift the bow while the stern stays in the water, and the rear foil is parked at its low-lift setpoint. Takes effect on the next 10 Hz iteration: a mode change, not a tweak, hence the lock
|
| 49 | q |
SCR_USER2 |
−10 | 10 | 0.5 | 1 | Wheelie-mode pitch target (°); 0 uses the built-in 3°. Higher = more front-foil angle and a higher bow | |
| 50 | f |
SCR_USER3 |
−20 | 20 | 1 | 5 | Roll-step demand (°) for step-response tuning; 0 = level. Reaches the foils as front-wing differential. Watch the achieved attitude come back on telemetry 0x250
| |
| 51 | B |
SCR_USER4 |
0 | 2100 | 10 | 50 | yes | Rear-foil jog: a raw stepper setpoint, clamped to 1000–2000 (0.012° of mount per unit; 0 = off), driven open-loop for bench calibration. Only honoured while the boat is disabled, and locked while foiling for the same reason |
Special indices
| Index | Meaning |
|---|---|
0xFE |
Protocol-version query: the reply's value is the version (currently 11). Any tool should check this before trusting its own copy of this map |
0xFF |
As a request: dump the whole table (one 0x261 per entry, paced). As a reply: the end-of-dump marker, value = number of entries sent
|
| 13–15, 46–47 | Unused, free for future entries in their groups |
| 37, 38 | Retired (HYD_HSRC, HYD_HDIV) — reserved forever, never reuse
|
Config slots, undo and factory reset
The helm keyboard adds a layer on top of the protocol; none of this exists on the wire beyond the ordinary set/ack frames it generates. Digit keys 1–9 are configuration slots: tap to restore a slot (every stored parameter is re-sent over CAN, volatile, so the acks repopulate every listener), hold ≈1.2 s to store the current acked values into it. Slots are deliberately volatile — held in the tool's memory, wiped on restart, never written to flash — so a slot is a scratchpad for A/B comparisons on the water, not a configuration store; the flash write remains its own explicit action. ~ undoes the last change (one step back per press — a slot restore or factory reset counts as a single step), and 0 held down performs a factory reset to the values read at connect. Restoring while foiling is safe by construction: the locked entries simply refuse (status 5) and everything else lands.
Keeping this page true
This page is GENERATED by tools/export_wiki.py in the autopilot repository, from the same tables the keyboard tuner runs on; regenerate and re-paste it after any parameter change. The authoritative copy of the map is the PT whitelist in foil_tune.lua on the flight controller. Any change to indices, ranges or locks bumps PROTO_VERSION on both sides of the wire — if the version on this page and the one the boat reports at index 0xFE disagree, trust the boat and update this page.