Hori Wheel & Pedals¶
The primary in-seat driver input is a Hori Racing Wheel Overdrive (a USB HID
racing wheel with a throttle and brake pedal set). It plugs into the mainboard's
USB-A port, which routes to the Teensy USB host header. The Teensy enumerates
it with the USBHost_t36 library and reads the axes and buttons directly — the
wheel is on the motion-authority computer, not the Pi.
- Vendor / product:
0f0d:0152(Hori). Enumerates throughUSBHost_t36as an Xbox-One controller (joystickType 3). - On connect, the Teensy prints
INFO WHEEL_CONNECTED vid=… pid=… type=…to both its USB serial and the Pi UART.
Two different input maps — use the Teensy one
The Linux js/evdev map in the hardware note
Hori Wheel (hardware) is not the
same as the map the Teensy sees through USBHost_t36. The triggers are
0…1023 unsigned (not ±32767) and the indices are shifted. The Teensy map
below is authoritative for firmware behavior; the js map only applies to
the (legacy) Pi-USB fallback path. Always re-confirm with the WHEELRAW
command if the wheel firmware or library version changes.
Axis map (Teensy USBHost_t36)¶
| Axis | Control | Range | Used for |
|---|---|---|---|
| 0 | Steering wheel | -32768 (full left) … +32767 (full right), centered at 0 |
Steering setpoint |
| 3 | Brake pedal | 0 released … 1023 pressed |
Brake |
| 4 | Throttle pedal | 0 released … 1023 pressed |
Throttle |
The steering axis rarely reaches the raw extremes (mechanical lock is inside the
electrical range), so the usable range is treated as a calibration. The pedal
calibration (released = 0, pressed = 1023) plus a 3 % deadband and a
plausibility margin are configured in firmware/kart-core/src/config.h and can be
adjusted live with the CFG command.
Button map (Teensy USBHost_t36)¶
The Hori reports its face buttons on consecutive bits and its paddles on bits 12/13:
| Bit | Physical control | Function |
|---|---|---|
| 12 | Left shift paddle | Downshift one gear |
| 13 | Right shift paddle | Upshift one gear |
| 6 | X face button | Backup: in DRIVE → disarm; in FAULT → clear (DRIVE entry is automatic, so this is not needed to drive) |
| 5 | B face button | Disarm / turn off (controlled stop → SAFE) |
| 4 / 7 | A / Y face buttons | (unused) |
How the pedals become motion¶
flowchart LR
thr["Throttle axis (0..1023)"] --> map["PedalMap:<br/>deadband + scale → 0..100%"]
map --> gate{"DRIVE & not Park<br/>& not braking?"}
gate -->|yes| dac["MCP4725 DAC → ESC"]
gate -->|no| zero["0 (idle voltage)"]
brk["Brake axis (0..1023)"] --> bmap["PedalMap → 0..100%"]
bmap --> bthr{"> 20% ?"}
bthr -->|yes| brake["Assert ESC regen brake<br/>(overrides throttle)"]
Details for each path:
- Throttle is deadbanded and scaled to 0–100 %, then written to the throttle DAC — but only while the state machine is in DRIVE, the gear is not Park, and the brake is not engaged. See Throttle.
- Brake is treated as a switch: past 20 % pedal travel the ESC's regen brake line is held, and brake always overrides throttle. See Brake.
- Steering maps the wheel axis to an angle setpoint sent to the Steervo over CAN. See Steering Overview.
- Gears are changed with the paddles. See Reverse & Gearshifting.
Plausibility¶
Each pedal reading is range-checked against its calibration plus a margin. A reading that stays implausible (both extremes / out of range) for longer than a debounce window (250 ms) raises a pedal-implausible fault and triggers a controlled stop. This catches a disconnected or shorted pedal signal.
Arming with the wheel (the ARM chord)¶
In a normal (in-seat) build, the driver arms the kart with a deliberate chord rather than a single button, so it can't happen by accident:
ARM chord
Hold both shift paddles (bits 12 + 13) and press the brake pedal, with the throttle released, for 1 second, while the kart is in SAFE and stopped. This transitions SAFE → ARMED, which begins the precharge → contactor sequence.
DRIVE then follows automatically once the bus is charged and the throttle DAC is alive — there is no separate "go" button. The gear ladder starts in Park (throttle inhibited) so the driver must deliberately upshift into a drive gear before any throttle is delivered.
To turn off: press B, or send DISARM from the dashboard/serial — either
performs a controlled stop back to SAFE.
Diagnostics¶
WHEELRAW(serial) dumps the live axis values, button mask, and joystick type — use it to confirm the map and pedal ranges.WHEEL?reports host connection state and button state.
USB host re-enumeration after a reflash
A Teensy reboot leaves an already-connected wheel dark. After flashing,
hot-plug the wheel (or power-cycle the board) so the USB host re-enumerates it.
Confirm with WHEELRAW showing enum=1.
Legacy Pi-USB fallback¶
The wheel can alternately be plugged into a Raspberry Pi USB port, with
pi/wheel_bridge.py forwarding button events to the Teensy over the UART
(WHEEL_BTN commands). This path uses the Linux js map and is a fallback for
when the Teensy USB host cannot bind a device. The primary and normal path is
USB-host-direct to the Teensy.