- Python 91.7%
- HTML 7.7%
- JavaScript 0.5%
- Shell 0.1%
|
|
||
|---|---|---|
| .github/workflows | ||
| docs | ||
| flash_scripts | ||
| githooks | ||
| scenarios | ||
| scripts | ||
| tests | ||
| tm3web_ui | ||
| uds_local | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| alert_log.py | ||
| bhx.py | ||
| can_decoder.py | ||
| candata_to_dbc.py | ||
| clog.py | ||
| compact_to_dbc.py | ||
| config.py | ||
| decode_bin.py | ||
| dfu.py | ||
| dump_alerts.py | ||
| dump_odin.py | ||
| ecu_bench.py | ||
| firmware_report.py | ||
| ihex.py | ||
| pyproject.toml | ||
| README.md | ||
| requirements.txt | ||
| sim.toml.example | ||
| so_alerts.py | ||
| so_candata.py | ||
| tm3cli.py | ||
| tm3uds.py | ||
| tm3web.py | ||
| unsquash_firmware.py | ||
| uv.lock | ||
| vapi_emu.py | ||
| vapi_layout.py | ||
| vapi_registry.py | ||
tm3diag
Tesla Model 3 diagnostics tools for CAN
Use at your own risk This is unofficial, open-source software with no affiliation to Tesla. Flashing ECU firmware carries real risk — a failed or interrupted flash can leave an ECU in an unrecoverable state, potentially disabling safety-critical vehicle systems. By using these tools you accept full responsibility for any damage to your vehicle, its components, or any third parties. The authors provide no warranty and assume no liability.
Requirements
- Python 3.10 or later
- A CAN interface connected to any of the Tesla ECUs — either a real USB adapter (e.g. PEAK, Kvaser, CANable) or a virtual interface (
vcan) for offline testing - Linux is recommended; SocketCAN is the default interface driver
Setup
1. Clone and install dependencies
git clone https://github.com/outlandnish/tm3diag.git
cd tm3diag
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
The source line activates the virtual environment. You'll need to run it again in each new terminal session before using any of the tools:
source .venv/bin/activate
2. Configure your CAN interface
Copy the example config and open it in a text editor:
cp .env.example .env
Set TM3_VEHICLE_CHANNEL to your CAN interface name and TM3_INTERFACE to your adapter's driver. The defaults work for a standard Linux SocketCAN setup:
TM3_VEHICLE_CHANNEL=can0 # your interface name — check with: ip link show type can
TM3_INTERFACE=socketcan
Bring the interface up before running any tool (replace can0 and 500000 with your interface and bitrate):
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up
To use a virtual interface for testing without hardware:
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set vcan0 up
# then set TM3_VEHICLE_CHANNEL=vcan0 in .env
3. Firmware dump (optional)
Some tools (tm3cli.py, dfu.py, tm3uds.py) can decode signal names and validate routines when pointed at an extracted Tesla firmware squashfs. This is not required for the immobilizer handshake script.
If you have a firmware image, extract it with unsquash_firmware.py, then set TM3_ROOT in .env to the resulting squashfs-root directory:
TM3_ROOT=/path/to/squashfs-root
If your firmware's .compact.json and ODJ files are encrypted .bin files, you'll also need the decryption key — see .env.example for how to extract it.
The ODIN diagnostic graphs ship as a zip that nothing unpacks for you. Unzip it in place, or the ODIN panel reports no ODIN bundle:
cd "$TM3_ROOT/opt/odin" && unzip -q odin_bundle.zip # -> opt/odin/odin_bundle/networks
4. Signal database
With TM3_ROOT set, CAN frames are decoded by running the MCU's own decoder — GUICanCracker::crackMessage out of libQtCarVAPI.so, emulated, with the signal catalog from libQtCarCANData.so naming what it stores. Nothing is modelled, so nothing can be modelled wrong, and there is no build step: point TM3_ROOT at an extraction and every tool has the full database.
default_db() resolves three sources, best first:
| Source | Covers | |
|---|---|---|
| 1 | The firmware's own decoder (vapi_emu) |
the whole catalog, exactly as the car decodes it |
| 2 | A generated DBC (candata_to_dbc.py) |
the whole catalog, from bit layouts recovered out of that same decoder |
| 3 | Model3_ETH.compact.json |
only the subset Tesla ships to the diagnostic tool, and it shrinks every release |
Set TM3_VAPI=0 to force the layout path — the A/B for a suspected layout bug.
Decoding needs no DBC. Encoding does: crackMessage only runs one way, so vehicle_sim.py, ecu_bench.py and the frame builders need bit layouts. Build one once per firmware revision:
python candata_to_dbc.py dbc # writes Model3_ETH.<rev>.dbc, ~1-2 min
The revision is taken from the TM3_ROOT directory name — a trailing .ice, .extracted or .ice.extracted is stripped — and that same name is how config finds the DBC again, so the two cannot drift. Without a DBC, encoding falls back to whatever layouts compact.json carries.
To check the recovered layouts against the firmware itself:
python vapi_emu.py parity # same / different / only-emu / only-dbc
Tools
| Tool | Description |
|---|---|
tm3cli.py |
Interactive diagnostic terminal — read DIDs, run routines, trigger firmware updates |
tm3uds.py |
General-purpose UDS CLI for reading/writing DIDs, routines, and session management |
dfu.py |
Firmware flash CLI — identity discovery, file selection, and ECU-specific flash sequence |
scripts/di/di.py |
Drive Inverter bench emulator — gear/system control + immobilizer responder |
scripts/di/immobilizer_handshake.py |
Pair a KEY/SALT with the Drive Inverter and run the runtime 0x276/0x3D9 responder |
scripts/pcs/pcs.py |
PCS bench emulator — operating modes, precharge, DC-DC and charge control |
bhx.py |
BHX firmware image parser and builder |
ihex.py |
Intel HEX / .hgz parser — decode dual-bank gateway images to canonical Intel HEX |
clog.py |
Gateway cluster-log parser — decode CL/DATA/*.CLH+*.CLB signal logs |
compact_to_dbc.py |
Convert Model3_ETH.compact.json to DBC |
dump_odin.py |
Extract + decompile the odin PyInstaller binary from a firmware squashfs |
dump_alerts.py |
Dump the per-bus alert catalogue a firmware revision exposes (recipe supplied privately) |
unsquash_firmware.py |
Unsquash a firmware image and expand its nested .dirsquashed parts |
tm3web.py |
Local web console — live CAN signals, alerts, DB explorer, raw frames, ODIN/DID |
Reference
- unsquash_firmware.md — How to extract a firmware blob to a
squashfs-rootdirectory - immobilizer_handshake.md — Immobilizer pairing and runtime handshake guide
- FIRMWARE_UPDATE.md — UDS flash protocol, script map, frame-by-frame reference
- ghidra_c28x_loading.md — Prepare a TMS320 firmware image (inverter DIR/PMR, PCS) for Ghidra: pick the
.bhx, extract, byte-swap
Tests
source .venv/bin/activate
pytest tests/ -v