User Tools

Site Tools


canbus

CAN bus access

There are two very different ways onto the vehicle bus. Know which is which.

Route A — via the companion MCU (works for control)

The head unit talks to a companion MCU over UART (/dev/ttymxc3 @115200). The MCU bridges to the vehicle CAN bus. The UART is owned by the carmanager CarService; other apps reach it through a system service (AIDL). The MCU frame carries a “send CAN control frame” address and a control-type selector (air-conditioning, charging, dashboard, …).

This is the route that actually reads decoded values and sends control commands. The head unit never sees raw CAN frames here, only decoded register values.

Route B — the OBD2 port (diagnostics, not control)

The OBD2 socket is a query-only diagnostic gateway. Through it you can enumerate all 24 on-board ECUs, read their fault codes and data (including a live battery-measurement block), and clear codes — but you cannot control anything without the manufacturer's Security-Access key. It does not carry the internal broadcasts, so it is no use for sniffing live signals. Full write-up — the ECU map, what is readable, how to decode raw values, clearing codes, and the safety caveats:

→ OBD-II diagnostics.

Safety checklist for tapping the internal (TBox) CAN bus

The internal bus is live and possibly safety-relevant (unlike the isolated OBD2 gateway):

  1. Listen-only (silent) mode first. A normal CAN node still transmits ACK bits, and at the wrong bitrate emits error frames that can disturb real ECUs. Use the adapter's hardware listen-only mode to identify bus/bitrate before ever transmitting.
  2. Measure termination. With the car off, measure CAN-H/CAN-L: ~60 Ω = already terminated both ends (add nothing); ~120 Ω = only one end. Keep the stub short.
  3. Confirm the bitrate (sweep 500k → 250k → 125k in listen-only) before going active.
  4. Non-destructive tap — back-probe or T-tap; never cut or pierce the harness. Tie adapter ground to chassis ground. Never short CAN-H/L to 12 V or to each other.
  5. Parked only. Connect with the car asleep, then wake it to capture. Never probe a bus while the vehicle could move.
  6. Identify the right bus by correlation — trigger a known signal (e.g. AC on, or a lock/unlock) and watch which frames change.

Decoded signal keys

The head unit works with a registry of ~461 action-ID keys — the values it reads (GET) and commands it sends (SET) over the MCU/CAN bridge. These are the CarService.getInt(id) / sendCmd(id,…) keys, not raw CAN arbitration IDs (those sit a layer below in the MCU protocol).

→ Full list, grouped by domain: CAN signal reference. The battery/powertrain subset — the signals most people ask for — is below.

Battery & powertrain

The U5 has a split HV pack (A + B), each reporting its own SoC / voltage / current. Read with getInt(id) while the car is awake.

Signal ID Constant Unit / note
Overall SoC 3335 WARN_BMS_STATE_OF_CHARGE %
Pack A (main) voltage 3455 CARSET_PACK_BMS_VOLTAGE V — live sample ≈ 356 V
Pack A (main) current 3456 CARSET_PACK_BMS_CURRENT A
Pack A SoC 3457 CARSET_PACK_A_SOC %
Pack B voltage 3458 CARSET_PACK_B_VOLTAGE V
Pack B current 3459 CARSET_PACK_B_CURRENT A
Pack B SoC 3460 CARSET_PACK_B_SOC %
Driving range 3461 CARSET_VCU_DRIVING_RANGE km
Energy mode 3462 CARSET_VCU_ENERGY_MODE
BMS warning lamp 3323 WARN_BMS_WARN_LAMP telltale
Thermal-runaway warning 3340 WARN_BMS_THERMALRUNAWAY telltale
Charge time 492 CARSET_CHARGE_TIME SET/est.
Discharge (V2L) request 491 CARSET_DISCHARGE_REQ SET

No State-of-Health (SoH). The firmware decodes SoC but there is no SoH / capacity / cell-voltage key — SoH would have to come from a raw BMS CAN frame or a UDS diagnostic read (see roadmap).

Reading live (adb, car awake): the canbus service logs decoded values under logtag Canbus_SET (e.g. D/Canbus_SET: mPackBMSVoltage: 355.9). The vehicle CAN only broadcasts while the car is awake — values go quiet a short while after it parks/locks.

Probing signals live

The ADC Telemetry app has a built-in probe (it already binds ICanbusService), so you can read or command any key from a shell — handy for checking whether a signal really exists, what it reports, and in which state. Build/install and signing basics are on the development page.

Read — geti = getInt, getf = getFloat, geta = getIntArray:

S=com.example.carmqtt/.BridgeService
adb shell am startservice -n $S --es op geti --ei key 3457    # pack-1 SoC
adb shell am startservice -n $S --es op getf --ei key 3455    # pack-1 voltage
adb shell am startservice -n $S --es op geta --ei key 3464    # doors [FL,FR,RL,RR]
adb logcat -d -s CarMqtt.Probe                                # read the results

Example output:

getf key=3455 -> getFloat=355.5
geti key=3460 -> getInt=0                     (pack-2 SoC — 0: this car is single-pack)
geta key=3464 -> getIntArray=[1, 0, 0, 0]     (driver door open)

Command — send = sendData(action,val). ⚠ this actuates the vehicle — only in a safe state:

adb shell am startservice -n $S --es op send --ei key 103 --ei val 1   # A/C ON

Notes: after (re)installing the app give it ~1 s to rebind the canbus service (a probe fired too early logs canbus service NOT bound); a value of -1 / 0 usually means the signal is at its init default (not populated on this car — as with pack 2).

canbus.txt · Last modified: by 127.0.0.1