Skip to content

jbirby/obdlink-sx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OBD-II Dashboard

A self-hosted web dashboard for the OBDLink SX and any other ELM327-compatible OBD-II adapter. Python backend, browser frontend, no installer, runs entirely on 127.0.0.1.

Python Flask License Status


Why

I have an OBDLink SX scanner and no software that talks to it the way I want. This is a small, hackable utility I can plug into the car, see live engine data, read and clear codes, log a drive to CSV, and extend without fighting a GUI framework. It targets the SX first but works with virtually any ELM327-compatible adapter (cheap blue Bluetooth dongles, OBDLink MX/MX+, Veepeak, Vgate, etc.) because they all share the same ELM327 ASCII command set.

Features

  • Connect to the adapter over USB at 115200 baud (configurable)
  • Diagnostic Trouble Codes — read stored (Mode 03), pending (07), and permanent (0A); clear with Mode 04
  • Freeze frame data (Mode 02) — sensor snapshot at the moment a DTC set
  • I/M readiness monitors (Mode 01 PID 01) — useful before an emissions inspection
  • Live PID streaming — Server-Sent Events feeding gauges and a Chart.js live chart at up to 10 Hz
  • Vehicle info — VIN, OBD protocol, battery voltage, every PID the ECU reports as supported
  • Mode 06 walk — on-board test results, all MIDs and TIDs
  • CSV logging — pick any PIDs, any interval, log a drive to a timestamped CSV
  • Threshold alarms — flash gauges and pop a banner when a streamed PID crosses a min/max bound
  • Simulator mode — drop-in synthetic ECU so you can develop or demo the whole app without a vehicle

Quick start

git clone <this repo>
cd "OBDLink SX"
pip install -r requirements.txt
python run.py

The dashboard opens at http://127.0.0.1:5050. CLI flags:

Flag Effect
--port 8000 listen on a different port
--host 0.0.0.0 listen on the LAN (e.g. for a phone in the car)
--no-browser don't auto-open a browser tab
--debug verbose logging + Flask debug

Try it without a car

Tick Simulator in the connect bar, click Connect. The backend swaps in a synthetic ECU that drifts realistic values for RPM, speed, coolant, throttle, etc., reports two pre-populated DTCs (P0301 and P0420), and implements every endpoint.

Connecting to a real adapter

  1. Plug the OBDLink SX into the OBD-II port and into the laptop's USB.
  2. Turn the ignition to ON (engine running is fine; engine off / key on works too, but some ECUs sleep after a minute).
  3. In the dashboard, click ↻ next to Port to refresh the list.
  4. Pick the SX's COM/tty entry, leave Baud at 115200, click Connect.

If the port doesn't appear:

  • Windows — confirm in Device Manager that the SX shows up under Ports (COM & LPT). If it's listed under Other devices, install the FTDI VCP driver from https://ftdichip.com.
  • macOSls /dev/tty.usbserial-* should list it. If not, install the FTDI VCP driver from https://ftdichip.com.
  • Linux — confirm with dmesg | tail after plugging in. Add yourself to the dialout group: sudo usermod -a -G dialout $USER, then log out / in.

Architecture

┌──────────────────────────┐         ┌────────────────────────┐
│  Browser dashboard       │  HTTP   │  Flask backend         │
│  - HTML/CSS/JS           │ ◄──────►│  - REST + SSE          │
│  - Chart.js gauges       │         │  - obd_session         │
│  - Alarms, CSV controls  │         │  - csv_logger thread   │
└──────────────────────────┘         └──────────┬─────────────┘
                                                │ pyserial @ 115200
                                                ▼
                                     ┌────────────────────────┐
                                     │  OBDLink SX (STN1110)  │
                                     │  ELM327 AT command set │
                                     └──────────┬─────────────┘
                                                │ OBD-II (CAN/ISO/J1850)
                                                ▼
                                     ┌────────────────────────┐
                                     │  Vehicle ECU(s)        │
                                     └────────────────────────┘

The backend never depends on the frontend — obdlib/ is a regular Python library you can import in scripts or a notebook without Flask.

obdlib/
├── elm327.py          serial driver + adapter init (ATZ/ATE0/ATSP0/…)
├── obd_session.py     high-level service: DTCs, PIDs, freeze, readiness, M06
├── pids.py            PID dictionary and per-PID decoders
├── dtc.py             DTC byte-pair parser + description database
├── simulator.py       drop-in fake ECU implementing the same interface
└── csv_logger.py      background thread that writes timestamped samples

webapp/
├── app.py             Flask routes + SSE stream
├── templates/index.html
└── static/style.css, app.js

REST API

All endpoints return JSON. The SSE stream is the one exception.

Method Path Purpose
GET /api/ports list available serial ports
POST /api/connect {port, baud, simulator} — open and init the adapter
POST /api/disconnect close the adapter
GET /api/status are we connected?
GET /api/info VIN, protocol, voltage, supported PIDs
GET /api/dtcs {stored, pending, permanent}
POST /api/dtcs/clear Mode 04 — clear codes
GET /api/freeze freeze frame data
GET /api/readiness I/M readiness monitor states
GET /api/mode06 on-board test results, all MIDs
GET /api/pids PID dictionary the app understands
GET /api/stream?pids=0C,0D&rate=4 SSE stream of selected PIDs
POST /api/log/start {pids, filename, interval} start CSV logger
POST /api/log/stop stop CSV logger
GET /api/log/status logger state

Extending

Add a PID: append one PIDDef(...) row in obdlib/pids.py. You need to know how many bytes the ECU returns, the units, the formula for converting bytes to a useful number, and a min/max for gauge bar scaling. The dashboard picks the new PID up automatically.

# Engine oil pressure (manufacturer-specific PID 0x53, hypothetical)
PIDDef(0x53, "Oil pressure", 1, "kPa", lambda d: d[0] * 4, 0, 1020,
       "Engine oil pressure"),

Add a DTC description: add "P1234": "Description" to DTC_DB in obdlib/dtc.py. The byte-to-code conversion is automatic; this dict only controls the human description shown next to the code.

Use the library directly: every backend feature is callable from Python without Flask:

from obdlib.elm327 import ELM327
from obdlib.obd_session import OBDSession

elm = ELM327("COM5", baudrate=115200)
elm.open(); elm.initialize()
sess = OBDSession(elm)

print("VIN:", sess.get_vin())
print("Voltage:", sess.get_voltage(), "V")
print("DTCs:", sess.get_dtcs())
print("RPM:", sess.query_pid(0x0C))

Reading non-emissions DTCs (TPMS, ABS, SRS, body)

OBD-II Mode 03 only covers emissions ECUs — that's the federal mandate. Body/chassis modules (TPMS, ABS, airbag, body control, etc.) live at separate CAN addresses and use UDS rather than the OBD-II protocol.

The Body / Chassis tab walks a curated list of 11-bit Toyota CAN IDs, sends UDS Service 19 02 FF (Read DTC by Status Mask) to each, and aggregates the responses. ECUs that aren't on the bus simply time out and get marked "no response". Any ECU that does answer reports its DTCs in the 3-byte UDS format with status flags (test failed, confirmed, pending, MIL request, etc.).

Caveats:

  • The default ECU list is Toyota-flavored. Other manufacturers use different addresses; for a Ford/GM/VW vehicle you'd extend obdlib/uds.TOYOTA_ECUS with the right list (or build a separate one).
  • A full scan takes ~30 seconds because each missing ECU has to time out. Drop per_ecu_timeout in scan_body_chassis_dtcs() if you want it faster on a known vehicle.
  • Clearing body/chassis DTCs uses UDS Service 14. Some ECUs require a security access (27 01 / 27 02 with seed-key) before clearing — the dashboard's clear button will fail in that case. Use the Diagnostics tab terminal for those.
  • TPMS-specific note: clearing the code won't help if the underlying tire is actually low. Inflate first, drive a few minutes for the system to re-learn, then check.

Limitations

  • One adapter at a time. Single process, single connection.
  • Mode 06 TID/UAS decoding is manufacturer-flavored. The dashboard prints raw values, min, and max so you can compare runs even without a TID lookup.
  • Pre-CAN protocols (ISO 9141-2, KWP2000, J1850) are supported by the SX but slower; the first connect on those takes a few extra seconds while ATSP0 settles on a protocol.
  • No auth. With --host 0.0.0.0 the dashboard is reachable from your LAN. Don't expose it to the internet.

When auto-protocol won't latch

If Vehicle Info shows the protocol stuck at AUTO with no PIDs and no VIN, the adapter is talking to your laptop but isn't getting a reply from the ECU. The dashboard now ships a Diagnostics tab to deal with this:

  1. Make sure the engine is actually running. Key in RUN isn't always enough — some ECUs don't wake the OBD bus until the engine turns over.
  2. Open the Diagnostics tab and click 0100 (probe ECU). If it returns NO DATA or UNABLE TO CONNECT, the bus isn't replying.
  3. Try forcing a specific protocol from the dropdown. Most cars 2008+ are 6 — ISO 15765-4 CAN (11-bit, 500 kbps). Older Ford = 1, older GM = 2, older Asian/Euro = 3.
  4. Send ATWS (warm reset) or ATD (defaults) from the manual command terminal to clear any sticky configuration the SX may have saved from a previous program.
  5. Use ATRV to confirm the SX is reading the OBD-port battery voltage. ~12.6 V key-on / ~13.8 V engine-running confirms the wiring is fine.

The terminal accepts any AT or OBD command (ATI, ATDP, ATDPN, 0100, 0902, 03, 06 01, etc.) and prints the raw response — the same way you would talk to the SX from a serial terminal program.

Troubleshooting

Symptom Likely cause / fix
could not open port 'COMx' Another program already has the port open. Close TouchScan, ScanXL, etc.
Connect failed: NO DATA on the first PID Ignition not in ON / RUN.
UNABLE TO CONNECT on connect Vehicle's OBD bus isn't responding. Cycle the key, or run the engine.
Stream stops after a while Some adapters drop after long idle. Disconnect → Connect to re-init.
VIN comes back as Some pre-2008 vehicles don't support Mode 09 PID 02.

Contributing

Patches welcome. Suggested ground rules:

  • Keep obdlib/ free of Flask / web dependencies — the library is the important part.
  • Add a unit test (or a short script under scripts/) when you touch a parser. The simulator is enough to drive most tests.
  • Stick to the existing PID / DTC table style; don't introduce a database unless we genuinely need one.

License

MIT. See LICENSE if present, otherwise: do anything you want, no warranty.

Acknowledgments

  • Elm Electronics for the ELM327 command set that became the de-facto industry standard.
  • ScanTool / OBDLink for the STN1110 chip in the SX, which is faster and more capable than a stock ELM327 but stays bidirectionally compatible.
  • SAE J1979 / ISO 15031-5 for the OBD-II protocol the rest is built on.

About

Self-hosted web dashboard for the OBDLink SX and any ELM327-compatible OBD-II adapter. Python + Flask backend, browser frontend.

Topics

Resources

License

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors