Drive State Machine¶
The drive state machine is the heart of the motion authority. It is a small,
host-tested piece of pure C++ (firmware/kart-core/lib/kartcore/drive_state.*)
that decides, every 100 Hz tick, whether the kart is allowed to move and what its
outputs may do. Everything that can produce motion is gated behind it.
The five states¶
stateDiagram-v2
[*] --> SAFE
SAFE --> ARMED: ARM confirmed<br/>(stopped, throttle 0,<br/>healthy, steer ready)
ARMED --> DRIVE: automatic<br/>(bus ready, DAC alive,<br/>throttle 0)
ARMED --> STOPPING: disarm / health fault
DRIVE --> STOPPING: disarm / health fault /<br/>DAC lost while on throttle
STOPPING --> SAFE: stopped, no pending fault
STOPPING --> FAULT: stopped, fault pending
FAULT --> SAFE: explicit clear<br/>(stopped, cause resolved)
| State | Meaning | Outputs |
|---|---|---|
| SAFE (0) | Boot state. Everything de-energized, contactor open, throttle at idle. | all off |
| ARMED (1) | Traction bus live (precharge done, contactor closed). Waiting to advance to DRIVE. | contactor closed, steering enabled |
| DRIVE (2) | Driving. Pedals/stick may command throttle (through the gear ladder). | contactor closed, steering enabled, throttle allowed |
| STOPPING (3) | A controlled stop is in progress: throttle cut, brake asserted, contactor still closed until the kart is stopped. | contactor closed, steering enabled, brake asserted |
| FAULT (4) | A fault has latched. Requires an explicit clear once the cause is gone and the kart is stopped. | all off |
How you move through the states¶
SAFE → ARMED (arming)¶
Arming requires all of:
- a deliberate arm request — the wheel ARM chord or the RC SA switch;
- every subsystem healthy (see the fault table below);
- steering ready — calibrated Steervo, or traction-only bench mode where steering is declared absent;
- the kart stopped (speed = 0);
- the throttle released.
On entry, the precharge → contactor sequence begins.
ARMED → DRIVE (automatic)¶
There is no separate "go" button. ARMED advances to DRIVE on its own as soon as:
- the traction bus is ready (precharge done, contactor closed, settle elapsed);
- the throttle DAC is alive (the operator has turned the ESC key, which powers the DAC); and
- the throttle is released.
DRIVE only permits throttle — the gear ladder still holds the driver in Park (throttle inhibited) until they deliberately select a drive gear. ARMED has no inactivity timeout: it holds the contactor closed indefinitely while the operator keys the ESC and prepares, because a surprise contactor drop mid-setup was worse than an indefinite hold on stands (the e-stop and disarm are always available).
Any state → STOPPING → SAFE/FAULT (controlled stop)¶
A disarm request or any health fault begins a controlled stop: throttle → 0, brake asserted, contactor kept closed so the ESC can brake, until the kart is stopped (or a hard 3-second cap elapses). Then:
- if a fault was pending → FAULT (latched, with the fault code);
- otherwise → SAFE.
FAULT → SAFE (clearing)¶
A latched fault clears only with an explicit request (FAULT_CLEAR, the wheel
X button, or the RC path), and the kart at rest, and the original cause —
plus everything else — healthy again.
Fault codes¶
| Code | Name | Cause |
|---|---|---|
| 0 | NONE |
— |
| 1 | WHEEL_LOST |
No driver input source present (wheel unplugged and RC link down). |
| 2 | STEER_TIMEOUT |
No Steervo heartbeat for 150 ms. (suppressed in bench mode) |
| 3 | STEER_FAULT |
Steervo reports its own FAULT state. (suppressed in bench mode) |
| 4 | PEDAL_IMPLAUSIBLE |
A pedal reading was out of range past the debounce window. |
| 5 | DAC_ERROR |
Throttle DAC I2C failed continuously while throttle was being commanded. |
| 6 | ARMED_TIMEOUT |
Informational only — never latched (ARMED has no timeout). |
| 7 | INTERNAL_WDT |
Hardware watchdog reset. |
| 8 | CONTACTOR_FAULT |
Precharge/contactor sequencer reported a fault. |
Why the DAC is not a general health gate
The throttle DAC (MCP4725) is powered from the ESC's ACC+ (key) rail, which
only comes up after the contactor closes and the operator keys the ESC. So
the DAC is necessarily dead during SAFE and ARMED — requiring it to arm would
deadlock. Instead, dac_ok is a DRIVE-entry gate and a DRIVE-only fault,
and only when throttle is actually being commanded (keying off at a stop is
graceful, not a fault).
Traction-only bench mode¶
kart-core ships with a compile flag KART_TRACTION_ONLY_BENCH = 1 in
src/config.h. It is a stands-only configuration that:
- removes only the "steering healthy and calibrated" DRIVE-entry condition, and
- suppresses the two Steervo watchdog faults (
STEER_TIMEOUT,STEER_FAULT).
Everything else — pedal plausibility, precharge sequencing, throttle handling, stopped-to-enter-DRIVE, brake override, controlled stop on every other fault — is unchanged. There is no steering authority in this mode.
This build must never touch the ground
Traction-only bench mode is valid only with the driven wheels lifted. The
dashboard and LED strip signal it loudly (SAFE shows magenta instead of white).
Set KART_TRACTION_ONLY_BENCH = 0 and reflash for any ground-driving build, so
steering health gates DRIVE.