examples/OBD-II/README.md
This project reads live engine data from any OBD-II compliant vehicle and shows it on a Serial Studio dashboard. A built-in control loop polls an ELM327-compatible adapter (such as the OBDLink EX) over a serial connection, so there is no companion program to run. Engine RPM, vehicle speed, coolant temperature, calculated load, intake air temperature, throttle position and adapter battery voltage appear on live gauges.
Real-time car telemetry with nothing but Serial Studio and a cheap OBD-II adapter.
Some Serial Studio features need a paid license. See serial-studio.com for details.
OBD-II data is read from an ELM327-compatible adapter plugged into the vehicle's diagnostic port and connected to the computer as a serial (UART) device. The project's control loop drives the whole exchange:
setup() initializes the adapter once: reset, echo/linefeed/space off, and automatic protocol detection (ATSP0) so the same script works on CAN, ISO and PWM vehicles.loop() polls six Mode-01 PIDs in round-robin and decodes each reply inline, and periodically reads the adapter's own battery-voltage measurement with the AT RV command (which has no PID header and is parsed separately).These are all standard SAE J1979 Mode-01 PIDs, so no vehicle-specific configuration is required. The OBD-II standard does not require a car to support every PID, though: it only guarantees the protocol handshake and the bitmask (PID 00) that lists which PIDs the car implements. In practice the core three (RPM, speed, coolant) are answered by almost every OBD-II car (US 1996+, EU petrol 2001+, EU diesel 2004+), while load, intake air temperature and throttle position are widely but not universally supported (EVs/hybrids and some diesels may omit them). AT RV always works because the adapter measures it directly and never asks the vehicle.
| PID | Quantity | Formula | Availability |
|---|---|---|---|
010C | Engine RPM | ((A * 256) + B) / 4 | Core, near-universal |
010D | Vehicle speed | A (km/h) | Core, near-universal |
0105 | Coolant temperature | A - 40 (degrees C) | Core, near-universal |
0104 | Calculated load | A * 100 / 255 (%) | Common, not universal |
010F | Intake air temp | A - 40 (degrees C) | Common; some EVs/diesels omit it |
0111 | Throttle position | A * 100 / 255 (%) | Common, not universal |
AT RV | Battery voltage | adapter reading (volts) | Always (measured by the adapter) |
Any PID the car does not support comes back as NO DATA; the script skips that write and the gauge reads "No data" rather than showing a wrong value, so an unsupported metric never breaks the others.
All requests are plain ASCII (the hex PID digits, or the letters AT for adapter commands) terminated by a carriage return, and every reply ends with the ELM327 > prompt. The full command set and reply formats are in the ELM327 datasheet.
This example shows the request/response pattern that OBD-II needs, which differs from a normal telemetry stream where the device pushes data on its own. Three Serial Studio features carry it:
deviceWriteAndWait(data, timeout, until, source?) sends a command and blocks the control-loop worker (never the UI) until the adapter's > prompt arrives or the timeout elapses, then returns the reply. This turns the fire-and-forget serial link into a clean request/response call. It mirrors deviceWrite's data-first shape; source is optional and defaults to 0.OBD data table holds the decoded values. The control loop writes it with tableSet("OBD", "rpm", value) after each reply; the registers are declared as computed so they are writable.tableGet("OBD", "rpm")) and returns the value, so nothing needs a frame parser.After writing the table, the script calls dashboardTick(), which forces the dashboard to re-run the dataset transforms and render the fresh values, even though the adapter only speaks when polled. The project's frame parser is a stub that returns an empty frame.
OBD-II.ssproj: the Serial Studio project; it embeds the source, data table, gauges, the OBD-II poller control loop, and a stub frame parser.doc/screenshot.png: dashboard screenshot.