Teensy ⇄ Steervo CAN Link¶
The Teensy and the Steervo talk over a CAN bus. It is the only digital link between them, and it is deliberately kept small and slow for reliability. The authoritative wire format is CAN Protocol; this page explains how it is used.
Datasheet
MCP2562 CAN transceiver (PDF, 0.8 MB) — see all component datasheets on the Datasheets page.
Bus parameters¶
| Property | Value |
|---|---|
| Bitrate | 250 kbps (KART_CAN_BITRATE in firmware/common/kart_common/kart_can.h) |
| IDs | 11-bit standard IDs |
| Nodes | Teensy 4.1 (CAN3, mainboard pins 30/31 via an MCP2562), Steervo ESP32 (transceiver on GPIO16/17) |
| Termination | 120 Ω at exactly the two physical bus ends |
| Traffic | two 50 Hz frame pairs (~13 kbit/s total) |
Why only 250 kbps, and why the Talon isn't on the bus
The Talon SRX is driven by servo PWM, not CAN (see
Talon PWM Control), so the only traffic is the four kart
messages below and the bitrate is ours to choose. A slower bitrate means a
longer bit time, giving reflections from imperfect termination time to settle
before each sample point — valuable noise margin on a real vehicle harness.
Both nodes derive their bit timing from the single KART_CAN_BITRATE macro, so
they can never drift apart. (Older docs and the roadmap say "1 Mbps / CTRE CAN"
— that is retired; 250 kbps is correct.)
The four messages¶
sequenceDiagram
participant T as Teensy (kart-core)
participant S as Steervo (ESP32)
loop every 20 ms (50 Hz)
T->>S: STEER_SET (0x100) — enable, angle, seq
S->>T: STEER_STATUS (0x101) — state, faults, measured, seq echo
end
Note over T,S: on demand (SAFE only)
T->>S: STEER_CAL (0x102) — calibration commands
T->>S: STEER_CFG (0x103) — PID / limit tuning (never in ACTIVE)
| ID | Name | Dir | Rate | Purpose |
|---|---|---|---|---|
0x100 |
STEER_SET | Teensy → Steervo | 50 Hz | ENABLE bit + target angle (centi-degrees) + rolling seq. |
0x101 |
STEER_STATUS | Steervo → Teensy | 50 Hz | state, fault_bits, measured angle, output %, seq echo, raw pot. |
0x102 |
STEER_CAL | Teensy → Steervo | on demand | Guided calibration commands (accepted only in SAFE). |
0x103 |
STEER_CFG | Teensy → Steervo | bench only | Live PID / limit tuning (ignored while ACTIVE). |
Angles are centi-degrees (int16, +100 = 1.00° right of center); full lock
is ±3000 (±30.00°). The wheel's full deflection maps to those same angles, and
the Steervo's calibration assigns them to the captured pot stops — so a wheel
setpoint means the same physical position the pot was calibrated to.
The two safety-critical timeouts¶
Both directions have a 150 ms heartbeat requirement:
STEER_SETstaleness (Steervo side): if the Steervo receives no validSTEER_SETwithENABLE=1 for 150 ms, it de-energizes the motor, sets theSETPOINT_STALEfault bit, and stays in READY until fresh frames resume.STEER_STATUSheartbeat (Teensy side): if the Teensy sees noSTEER_STATUSfor 150 ms — or any frame reportingstate = FAULT— it treats steering as failed. In a full-authority build that triggers a controlled stop; in traction-only bench mode the traction side ignores it.
Link health & self-tests¶
kart-core decodes STEER_STATUS with an interrupt-driven RX FIFO. (A
polled read serviced the hardware FIFO only ~50 % of calls, overflowing it and
silently dropping still-ACKed frames — the cause of an early ~4–6 % status loss
that flapped the link-OK flag. The bitrate was never the problem.)
Two on-bench diagnostics isolate a dead link to one side:
- Teensy
CANTESTruns a FlexCAN internal loopback (controller only):PASSmeans the CAN3 controller is healthy, so a fault is in the transceiver/wiring. - Steervo self-test (
kCanSelfTest = true) puts the ESP32 in NO-ACK self- reception mode and reportsself_rx/other_rx/tx_failcounters, proving the ESP32 controller + GPIO map + transceiver path, and counting the Teensy's frames.
The Teensy STEER command and the Steervo's 10 Hz STAT USB line both expose live
RX/TX counters for diagnosing a marginal link. See
Steering Bring-up Notes for the full procedure.
Transceiver TX/RX labels are reversed on the ESP32 header
The Steervo's transceiver header is silkscreened CTX=GPIO16 / CRX=GPIO17, but
the firmware uses TX = GPIO17, RX = GPIO16 — the only combination that
passes the loopback self-test. Do not "correct" the pins to match the
silkscreen.