nx_obd2_shim.nx
buildroot/runtime/shims/nx_obd2_shim.nx
about
nx_obd2_shim.nx -- OBD-II protocol shim.
WHEELER-IMPLEMENTATION-OF-CANONICAL-SPEC.
Wire-spec sources (re-implemented clean-room from published specs;
no external code imported):
- ISO 15765-2:2016 -- ISO-TP network layer over CAN
- ISO 14229-1:2020 -- Unified Diagnostic Services (UDS)
- ISO 15031-5 -- Emissions-related diagnostic services
- SAE J1979 -- PID definitions for Mode 01-09
- ISO 15031-6 -- DTC numbering / Mode 03 format
First implementation of the INTEROPERABILITY_CHARTER.md
(nishi-silicon, commit 534e11d) shim layer pattern. Per the
charter ยง6 NxProtocolShim API surface contract.
Status: SEED v0.1.0. 2026-05-26.
WINNER-TIER: BASELINE-C provisional (no measured incumbent
comparison yet; provisional pending paired-bench run
vs python-obd / obdlib).
INCUMBENTS: python-obd v0.7.x, obdlib v0.10.x, ELM327-compatible
AT-command tools, Vector CANalyzer (commercial)
PLAN: M-next: paired bench (mode 01 PID round-trip latency
+ DTC read throughput) on CAN logger hardware vs the
named incumbents above. Re-rate after measurement.
GAP TODAY: unmeasured; provisional rating per BENCH_WINNER_AUDIT pattern.
V1 SCOPE:
- ISO-TP single-frame parse (data length <= 7 bytes; the common
case for mode 01 + mode 03)
- Mode 01 (current data) for the 12 PIDs the operator's
predictive-maintenance use case needs (RPM, speed, temps,
fuel-trim, MAP, intake-temp, MAF, throttle, fuel-level,
voltage)
- Mode 03 (read stored DTCs); 5-byte DTC format
- Bring-the-most-out-of-it value-add hook: emits one
NxProtocolEvent per parsed PID so the substrate's predictive-
maintenance consumer (future commit) can do cross-PID
anomaly aggregation
V2+ SCOPE (TODO):
dependencies 1 imports · 1 importers
imports: nx_syscalls.nx
imported by: _obd_pipeline_smoke.nx
structs
| 121 | struct NxObd2Shim |
consts
| 54 | const NX_SHIM_OK: i64 = 0 |
| 55 | const NX_SHIM_BAD_FRAME: i64 = 1 |
| 56 | const NX_SHIM_PROTOCOL_VIOLATION: i64 = 2 |
| 57 | const NX_SHIM_TIMEOUT: i64 = 3 |
| 58 | const NX_SHIM_BACKPRESSURE: i64 = 4 |
| 59 | const NX_SHIM_UNSUPPORTED_PID: i64 = 5 |
| 60 | const NX_SHIM_NO_WIRE: i64 = 6 |
| 63 | const NX_OBD2_VERDICT_BASE: i64 = 100 |
| 64 | const NX_OBD2_BAD_ISOTP_TYPE: i64 = 100 // bits 7-4 of byte0 not in {0,1,2,3} |
| 65 | const NX_OBD2_MULTIFRAME_NOT_IMPL: i64 = 101 // V1 single-frame only |
| 66 | const NX_OBD2_BAD_MODE: i64 = 102 // mode byte not in 0x01-0x0A (or +0x40 response) |
| 67 | const NX_OBD2_NEGATIVE_RESP: i64 = 103 // 0x7F + mode + NRC; common case ECU rejection |
| 68 | const NX_OBD2_PID_DATA_TRUNCATED: i64 = 104 // expected N data bytes for PID; got < N |
| 71 | const NX_OBD2_MODE_01: i64 = 0x01 // current data |
| 72 | const NX_OBD2_MODE_02: i64 = 0x02 // freeze frame |
| 73 | const NX_OBD2_MODE_03: i64 = 0x03 // read stored DTCs |
| 74 | const NX_OBD2_MODE_04: i64 = 0x04 // clear DTCs |
| 75 | const NX_OBD2_MODE_05: i64 = 0x05 // O2 sensor monitoring (legacy) |
| 76 | const NX_OBD2_MODE_06: i64 = 0x06 // on-board test results |
| 77 | const NX_OBD2_MODE_07: i64 = 0x07 // pending DTCs |
| 78 | const NX_OBD2_MODE_08: i64 = 0x08 // control operation of on-board systems |
| 79 | const NX_OBD2_MODE_09: i64 = 0x09 // vehicle info |
| 80 | const NX_OBD2_MODE_0A: i64 = 0x0A // permanent DTCs |
| 81 | const NX_OBD2_RESP_OFFSET: i64 = 0x40 // ECU response = request mode + 0x40 |
| 84 | const NX_OBD2_PID_MONITOR_STATUS: i64 = 0x01 // bytes: monitor status since DTCs cleared |
| 85 | const NX_OBD2_PID_FUEL_SYSTEM_STAT: i64 = 0x03 // fuel system status (2 bytes) |
| 86 | const NX_OBD2_PID_ENGINE_LOAD: i64 = 0x04 // calculated engine load (1 byte; %) |
| 87 | const NX_OBD2_PID_COOLANT_TEMP: i64 = 0x05 // coolant temp (1 byte; signed -40..215 C) |
| 88 | const NX_OBD2_PID_FUEL_TRIM_S1: i64 = 0x06 // short-term fuel trim bank 1 (%) |
| 89 | const NX_OBD2_PID_FUEL_TRIM_L1: i64 = 0x07 // long-term fuel trim bank 1 (%) |
| 90 | const NX_OBD2_PID_FUEL_PRESSURE: i64 = 0x0A // fuel pressure (1 byte; *3 kPa) |
| 91 | const NX_OBD2_PID_MAP: i64 = 0x0B // manifold absolute pressure (1 byte; kPa) |
| 92 | const NX_OBD2_PID_RPM: i64 = 0x0C // engine RPM (2 bytes; /4) |
| 93 | const NX_OBD2_PID_VEHICLE_SPEED: i64 = 0x0D // vehicle speed (1 byte; km/h) |
| 94 | const NX_OBD2_PID_INTAKE_AIR_TEMP: i64 = 0x0F // intake air temp (1 byte; signed -40..215 C) |
| 95 | const NX_OBD2_PID_MAF: i64 = 0x10 // mass air flow (2 bytes; /100 g/s) |
| 96 | const NX_OBD2_PID_THROTTLE_POS: i64 = 0x11 // throttle position (1 byte; %) |
| 97 | const NX_OBD2_PID_FUEL_LEVEL: i64 = 0x2F // fuel tank level (1 byte; %) |
| 98 | const NX_OBD2_PID_CONTROL_VOLTAGE: i64 = 0x42 // ECU control voltage (2 bytes; /1000 V) |
| 104 | const NX_OBD2_EVT_RPM: i64 = 200 // payload: i64 RPM |
| 105 | const NX_OBD2_EVT_VEHICLE_SPEED: i64 = 201 // payload: i64 km/h |
| 106 | const NX_OBD2_EVT_COOLANT_TEMP: i64 = 202 // payload: i64 deg C |
| 107 | const NX_OBD2_EVT_ENGINE_LOAD: i64 = 203 // payload: i64 % * 100 |
| 108 | const NX_OBD2_EVT_FUEL_TRIM_S1: i64 = 204 // payload: i64 % * 100 (signed; -100% to +99.2%) |
| 109 | const NX_OBD2_EVT_FUEL_TRIM_L1: i64 = 205 |
| 110 | const NX_OBD2_EVT_MAP: i64 = 206 // payload: i64 kPa |
| 111 | const NX_OBD2_EVT_INTAKE_AIR_TEMP: i64 = 207 // payload: i64 deg C |
| 112 | const NX_OBD2_EVT_MAF: i64 = 208 // payload: i64 g/s * 100 |
| 113 | const NX_OBD2_EVT_THROTTLE_POS: i64 = 209 // payload: i64 % * 100 |
| 114 | const NX_OBD2_EVT_FUEL_LEVEL: i64 = 210 // payload: i64 % |
| 115 | const NX_OBD2_EVT_CONTROL_VOLTAGE: i64 = 211 // payload: i64 mV |
| 116 | const NX_OBD2_EVT_FUEL_PRESSURE: i64 = 212 // payload: i64 kPa |
| 117 | const NX_OBD2_EVT_DTC: i64 = 220 // payload: i64 encoded DTC (P0117 etc) |
functions
| 131 | func nx_obd2_shim_init(s: *NxObd2Shim) -> i64 called by 1: main |
| 155 | func nx_obd2_isotp_parse_sf(frame: *u8, frame_len: i64) -> i64 called by 1: nx_obd2_rx_fn |
| 181 | func nx_obd2_pid_to_event(pid: i64, data: *u8, data_len: i64, called by 1: nx_obd2_rx_fn |
| 298 | func nx_obd2_mode03_decode_dtc(byte0: i64, byte1: i64) -> i64 called by 1: nx_obd2_rx_fn |
| 318 | func nx_obd2_rx_fn(s: *NxObd2Shim, frame: *u8, frame_len: i64, |
| 392 | func nx_obd2_tx_fn_mode01_pid(s: *NxObd2Shim, pid: i64, |
| 416 | func nx_obd2_diag_fn(s: *NxObd2Shim, out_vec: *i64) -> i64 |