Skip to main content

elm327obd: OBD2 car diagnostics in your terminal

CI License: GPL v2+ PyPI Python 3.11+ macOS | Linux

A free, open-source OBD-II scanner for your terminal. Plug a cheap ELM327 WiFi adapter into your car, join its Wi-Fi, and get a live dashboard, check-engine-light trouble codes explained in plain English, readiness monitors, freeze frame, VIN/calibration info, manufacturer (Mode 22) PIDs, CSV logging and a raw command console. Runs on macOS and Linux.

Website: jeffcaldwell.ca/elm327-obd, with an online trouble-code lookup.

Live dashboard: RPM, speed, coolant, load, throttle, MAP, fuel trims and voltage with sparklines

Features

  • Live dashboard of big-digit gauges with sparklines and min/max, refreshing at 10+ Hz over Wi-Fi
  • Trouble codes (DTCs) from every module: stored, pending and permanent, sorted by severity, with likely causes, what to check and the sensors to watch
  • Offline code lookup with no car connected: obd explain P0420
  • Readiness monitors, freeze frame, VIN, calibration IDs/CVNs: handy before an emissions inspection
  • Mode 22 / UDS manufacturer PIDs with profiles for Ford, GM and Hyundai/Kia, plus a read-only DID scanner
  • CSV data logging and a raw AT/OBD console that decodes replies and asks before risky commands
  • Demo mode (obd --demo) and a built-in ELM327 emulator for trying it or testing other tools without a car

Trouble codes tab with severity, likely causes and checks for the selected code

Install

With pipx (or uv tool install elm327obd):

pipx install elm327obd
obd --demo            # try it without a car

Or from a clone, which also sets up the test suite:

git clone https://github.com/jeffcaldwellca/elm327-obd.git
cd elm327-obd
./setup.sh            # virtualenv + dependencies, links ~/.local/bin/obd
./setup.sh --test     # same, and runs the test suite
./setup.sh --no-link  # don't create the ~/.local/bin/obd symlink

Needs Python 3.11+ and internet access for the first install (it downloads Textual). setup.sh is safe to re-run; it reuses .venv.

Platform Status
macOS Tested (Python 3.14). brew install python if needed.
Linux Tested in Debian 12 / Python 3.11 containers. Stock Debian/Ubuntu Python needs sudo apt install python3-venv; Ubuntu 22.04 ships 3.10, so install 3.11+ first (deadsnakes PPA or uv). The script tells you which case applies.
Windows Not supported by setup.sh. WSL may work but is untested.

Linux notes: the Wi-Fi name isn't shown on the connect screen (macOS-only lookup), and adapter scanning uses ip route to find the router. Manual equivalent: python3 -m venv .venv && .venv/bin/pip install -e ".[dev]", then run .venv/bin/obd.

Connect to the adapter

  1. Plug the adapter into the OBD port (under the dash, driver side). Turn the ignition ON — engine running or not.
  2. On your computer, join the adapter's Wi-Fi network (commonly WiFi_OBDII, V-LINK, OBDII, password none or 12345678). Your computer will have no internet while joined — that's normal.
  3. Run obd. It scans the usual addresses (192.168.0.10:35000 is most common) and connects automatically when it finds exactly one adapter. The last good address is remembered.

No adapter handy? obd --demo runs the whole app against a built-in simulated vehicle.

macOS Local Network permission

On recent macOS, the terminal app needs Local Network access to reach the adapter. If connection fails with "No route to host" / timeouts while you're definitely on the adapter's Wi-Fi: System Settings → Privacy & Security → Local Network → enable Terminal / iTerm / Ghostty (whichever you use), then restart it.

Commands

Command What it does
obd Full-screen TUI (scan → connect)
obd -a 192.168.0.10:35000 TUI, connect directly
obd --demo TUI against the built-in emulator
obd --imperial Start in °F / mph / psi (toggle anytime with u)
obd scan List adapters found on the current network
obd info [-a HOST:PORT] Print VIN, modules, calibration IDs, readiness (no TUI)
obd codes [--clear] Print trouble codes with severity; optionally clear (asks first)
obd explain P0171 P0420 Explain codes offline: severity, likely causes, what to check
obd emulator [--port 35000] [--ignition-off] Run a fake ELM327 for other tools / testing

Keys

Key Action
1–6 Switch tabs (press esc first if you're typing in the console)
e Choose dashboard gauges
r / f / x Read codes / freeze frame / clear codes (clear always asks for confirmation)
u Toggle metric ↔ imperial
l Start/stop CSV recording of dashboard PIDs (→ ~/Documents/OBD Logs/)
m Reset min/max markers
ctrl+r Retry vehicle detection / reconnect
ctrl+p Command palette
q Quit

Tabs

  • Dashboard: big-digit gauges with colored level bars, sparklines and session min/max. The grid reflows to the terminal width. Gauges dim when their data goes stale.
  • Live data: every supported Mode 01 PID with value, min/max and an inline trend. Requests are batched (up to 6 PIDs per CAN request), which usually gives 10+ Hz over Wi-Fi.
  • Trouble codes: stored, pending and permanent codes from every module, sorted most urgent first, with the check-engine light (MIL) status and a freeze-frame snapshot. Selecting a code shows its severity, plain-English meaning, likely causes (most likely first), what to check, and live readings of the relevant sensors (e.g. fuel trims for a lean code).
  • Vehicle: VIN, protocol, modules found, calibration IDs/CVNs, odometer (where supported) and the readiness monitors — useful before an emissions inspection.
  • Extended (Mode 22): per-make profiles (Ford, GM, Hyundai/Kia) plus a read-only DID scanner to discover what identifiers a module answers.
  • Console: send raw AT/OBD commands and see decoded replies (010C → "Engine RPM = 797 rpm"). :monitor shows the poller's traffic. Commands that write to, actuate, reset or reflash a module (services 04, 08, 10, 11, 27, 28, 2C, 2E, 2F, 30, 31, 34–37, 3B, 3D, 85, 87) and ATPP need confirmation. Leaving the console restores the adapter's settings (headers, filters, formatting) so the live poller keeps working even after you've experimented.

Manufacturer profiles

Built-ins live in src/elm327obd/profiles/*.toml. Add or override your own in ~/.config/elm327obd/profiles/<name>.toml:

name = "My truck"

[[pid]]
name     = "Transmission fluid temp"
header   = "7E2"        # physical request address (ATSH)
rx       = "7EA"        # optional CAN receive filter (ATCRA)
command  = "221940"     # service 22 + DID
formula  = "A - 40"     # A,B,C… = bytes after the DID echo; b[i]; signed(), s16(), u16(), bit()
unit     = "°C"
min      = -40
max      = 150
verified = true

formula = "ascii" or "hex" shows the raw payload. Entries marked unverified in the built-ins are community-sourced values, so check them against a known reading before you trust them. The standard UDS identifiers (F190 VIN, F188/F195 software numbers) work on most 2010+ modules that support service 22.

Custom DTC descriptions (e.g. manufacturer P1xxx codes) go in ~/.config/elm327obd/dtc.csv (code,description per line).

Trouble-code guidance

Every code gets a severity:

Severity Meaning
■ STOP Stop driving as soon as it's safe: risk of engine damage or unsafe operation (e.g. overheating, low oil pressure)
▲ SOON Get it fixed soon: affects how the car runs or can cause damage (e.g. misfires, lean/rich, transmission slipping)
● MONITOR Fix when convenient; keep an eye on it
○ LOW Mainly emissions/inspection; the car drives fine (e.g. catalyst efficiency, EVAP leaks, O2 heaters)

Where the information comes from:

  1. Hand-written guidance (src/elm327obd/dtc_guide.toml) for the ~200 codes people actually see: summary, likely causes, checks and which live PIDs to watch.
  2. Estimated severity for every other code, from keywords in its name and its category, labelled "estimated" in the UI. Manufacturer-specific codes (P1xxx, B1xxx, U1xxx…) have no public definition, so look those up for your make.
  3. Names: your ~/.config/elm327obd/dtc.csv first, then the built-in list, then the python-OBD list (2,066 generic P0/P2/P3/U0 codes), then a description derived from the code's structure.

You can override or add guidance in ~/.config/elm327obd/dtc_guide.toml (same format as the built-in file, e.g. for a manufacturer code your mechanic has explained). This is general guidance, not a diagnosis for your specific vehicle. A flashing check-engine light always means stop.

Third-party data and licensing

src/elm327obd/data/python_obd_dtc.tsv is derived from python-OBD (GPL-2.0; license in data/LICENSE.python-OBD). It was converted to TSV and 48 OCR typos ("lntake") were fixed; regenerate it with tools/import_python_obd.py. This project is itself GPL-2.0-or-later, so the two licenses are compatible. If you want to reuse this code under other terms, delete the file: names then fall back to the built-in list and code categories.

What an ELM327 can't do: tuning

This tool reads data, reads/clears codes and sends raw diagnostic requests. It does not reflash or tune ECUs, and an ELM327 isn't suited to that job. Flashing needs a reliable high-throughput interface (J2534 / vendor tools), the manufacturer's security-access algorithms, and correct calibration files. A Wi-Fi dropout halfway through a flash can leave the module unusable. For tuning, use a platform-specific tool: HP Tuners (GM/Ford), Cobb/SCT (Ford), or the manufacturer's service software. FORScan (Ford) can also change module configuration with a suitable adapter.

Troubleshooting

  • "Adapter only – vehicle not responding": the ignition is off, or the car's OBD port isn't powered. Turn the key to ON and press ctrl+r.
  • Slow or erratic data: many cheap clones are fake "v1.5"/"v2.1" chips. If batched requests fail, the app falls back to single-PID mode automatically (shown on the Vehicle tab).
  • Mode 22 "no reply": wrong header or receive address for that module. Try the DID scanner on 7E0, 7E1, 7E2, …
  • Other adapter address: obd -a HOST:PORT. In macOS Wi-Fi details, the router field is usually the adapter's IP.

Development

.venv/bin/pytest            # protocol, decoders, client-vs-emulator, TUI + regression tests
.venv/bin/obd emulator      # fake adapter on 127.0.0.1:35000

Layout: transport.py (TCP + prompt handling) → protocol.py (frame / ISO-TP parsing) → elm.py (client: Mode 01/02/03/04/07/09/0A/22) → tui/ (Textual app). pids.py, dtc.py and profiles.py hold the decoders. The GitHub Pages site lives in docs/; after changing the trouble-code guidance or names, regenerate its data with .venv/bin/python tools/build_site_data.py.

Contributing

Bug reports and pull requests are welcome. ELM327 clones vary a lot, so reports of which adapters and vehicles work (or don't) are especially useful: use the adapter / vehicle report issue template.

License

GPL-2.0-or-later. The bundled python-OBD code-name data is GPL-2.0 as well; see Third-party data and licensing.

Metadata

Release files for elm327obd 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for elm327obd 0.1.1
File Size Uploaded
elm327obd-0.1.1.tar.gz 117.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for elm327obd 0.1.1
File Interpreter ABI Platform
elm327obd-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 231.1 kB

Release files / elm327obd-0.1.1.tar.gz

Download URL elm327obd-0.1.1.tar.gz
Size 117.3 kB
Tags Source
SHA-256 checksum
How to use checksums
c518e5c16c1dd0f769541e1c51423288e39fd828d29fe6c57c51acfd0542304f
BLAKE2b-256 checksum
How to use checksums
6850065d945ec46f4bb75b05a569fa4300267151afc163491d017751b37d1e92
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / elm327obd-0.1.1-py3-none-any.whl

Download URL elm327obd-0.1.1-py3-none-any.whl
Size 113.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
131d0202eaa051e88efe75070e72bcbc3f33fa1b58c5bd6fb38b1730421d13d5
BLAKE2b-256 checksum
How to use checksums
6bd3cb758a21d74e7d9736a54176dcf5d08f917ee2609674a4c371fd8496947b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page