Files
orin-nano-env-setup/docs/impl/plans/CAN-BITRATE.md
T

5.0 KiB
Raw Blame History

type, status, updated
type status updated
plan done 2026-10-09

CAN Bitrate for the Z27AG: Measure in the Car

Result (2026-10-09, in the car, ignition on, PCAN terminator off)

500 kbps. can-bitrate-probe: 2673 frames in 3 s at 500k with zero RX errors, 0 frames at every other rate. The OBD port is not gatewayed: broadcast traffic is visible passively.

10 s listen-only capture (~/can-captures/z27ag-20261009-163736-ign.log, candump -L format): 27 IDs, ~893 frames/s (≈22% bus load), all periodic at 5 / 10 / 25 / 50 / 100 Hz. IDs: 0C0 (100 Hz), 119 139 13D 149 14D 156 210 231 235 300 312 443 (50 Hz), 239 423 42F 447 570 573 (25 Hz), 2F1 (10 Hz), 159 449 710 711 712 713 (5 Hz), 152 (2 Hz).

Persisted (boot and hot-plug):

  • /etc/udev/rules.d/71-can-up.rules: on add of can_pcan, wants can-up@can_pcan.service
  • /etc/systemd/system/can-up@.service: oneshot; reads /etc/default/can-<iface>, sets the bitrate and options, brings the interface up, and stops with the device
  • /etc/default/can-can_pcan: BITRATE=500000, CAN_OPTS="restart-ms 100"

Listen-only was on during the sniffing phase, so the adapter never ACKed on the car's bus. Dropped 2026-10-12 for obd-poller (svc-core), the single writer: the adapter now acknowledges frames like any scan tool. To go back, set CAN_OPTS="listen-only on restart-ms 100" there and replug the PEAK (or restart can-up@can_pcan).

Goal

Find the bitrate of the bus reachable at the OBD port of the Mitsubishi Colt (Z27AG), then make can_pcan come up with it automatically. Not set yet on purpose: no persistent bitrate until it's measured.

What is known (desk research, 2026-10-09)

  • The Colt's OBD port carries CAN on pin 6 (CAN-H) / pin 14 (CAN-L) (AiM Colt 2004–2008 guide).
  • Mitsubishi diagnostics of this era use KWP2000 on CAN (MUT-III).
  • 500 kbps is the likely answer (era standard for powertrain and OBD CAN), but nothing found confirms it for the Z27AG, and body networks (ETACS etc.) on cars of this era often run slower. Hence: measure.

Procedure (in the car, ignition ON)

  1. PCAN-USB on the OBD port (pins 6/14), adapter on the Orin. It appears as can_pcan.

  2. sudo can-bitrate-probe can_pcan 3

    It cycles 500k, 250k, 125k, 1M, 100k, 83.3k, 50k and 33.3k in listen-only mode (no ACKs, no error frames: a wrong rate can't disturb the bus). For each rate it prints frames received, the RX error counter, and controller state. The right rate has frames > 0 and RX errors ≈ 0. Wrong rates show 0 frames and/or climbing RX errors.

  3. Confirm by watching traffic at that rate, still listen-only: sudo ip link set can_pcan up type can bitrate <rate> listen-only on && candump -c can_pcan, and cansniffer can_pcan for changing IDs.

If every rate shows 0 frames: the OBD port is probably behind a gateway that stays silent unless queried. Then the bitrate has to be found by sending one standard OBD-II request (ISO 15765-4: try 500k, then 250k), e.g. mode 01 PID 00 to 0x7DF. That's transmitting, so it belongs to the single CAN writer (obd-poller, project-yukart decision 3). Do it deliberately, not as part of a sniff.

After the rate is known

  • Persist it for can_pcan (systemd-networkd .network with [CAN] BitRate= and ListenOnly=yes for the sniffing role, or a udev/systemd unit that runs ip link set ... up on add). It must survive hot-plug, not just boot.
  • Record the result in DEVICE-NAMING / BOM, and in project-yukart's README (can_pcan, 500 kbps is currently an assumption).
  • can_onboard (MTTCAN) can't go on the bus as is. The Orin's CAN pins are logic level and need an external CAN transceiver (e.g. SN65HVD230 class).

Probe script

Installed at /usr/local/sbin/can-bitrate-probe on the car unit. Source:

#!/bin/bash
# can-bitrate-probe: find a CAN bus's bitrate without disturbing it.
# Listen-only mode: the adapter sends no ACKs and no error frames, so a wrong bitrate cannot affect the bus.
# Usage: sudo can-bitrate-probe [iface] [seconds-per-rate]
IF=${1:-can_pcan}; T=${2:-3}
RATES="500000 250000 125000 1000000 100000 83333 50000 33333"
stat() { ip -s -d link show "$IF" | awk '/RX:/{getline; print $2}'; }           # rx packets
berr() { ip -d link show "$IF" | grep -oE 'berr-counter tx [0-9]+ rx [0-9]+' | awk '{print $5}'; }
state() { ip -d link show "$IF" | grep -oE 'state (ERROR-[A-Z]+|BUS-OFF|STOPPED|SLEEPING)' | awk '{print $2}'; }
printf "%-9s %7s %8s  %s\n" bitrate frames rx-errs state
for br in $RATES; do
  ip link set "$IF" down
  ip link set "$IF" type can bitrate "$br" listen-only on restart-ms 100 || { echo "$br: unsupported"; continue; }
  ip link set "$IF" up
  n=$(timeout "$T" candump -n 100000 "$IF" 2>/dev/null | wc -l)
  printf "%-9s %7s %8s  %s\n" "$br" "$n" "$(berr)" "$(state)"
done
ip link set "$IF" down
echo "Pick the rate with frames > 0 and rx-errs ~0. Interface left DOWN (listen-only still set)."