feat(v0.14.2): n62.toml — swap local:10 oil placeholder for OBD-II 0x5C - #175
Conversation
Slice 1 of v0.14.2 'Live Data on the Bench'. The cycle plan narrowed
slice 1 to just 0x5C (oil temp); 0x5E / 0x5F / 0x62 are deferred to
v0.14.3+ because each requires a new decoder (u16_fiftieths, u32_be,
u16_half) that does not yet exist in the catalog. Shipping a profile
entry that references a non-existent decoder breaks every consumer at
load time with 'unknown decode: X', so the decoder-first discipline
is preserved.
Changes:
- community/profiles/n62.toml:
- oil param: query 'local:10' -> 'obd:5C', label 'Oil temp
[UNVERIFIED placeholder]' -> 'Oil temp'.
- profile label: drops 'oil temp unverified' marker.
- header: rewrites the warning block as an N62 instrumentation-
context block (valley-pan slow-coolant monitoring, oil-temp
cruise band, Valvetronic load/throttle inverse, idle-voltage
target) so anyone reading live data knows what to watch for.
- ROADMAP.md: v0.14.2 slice-1 row reflects the narrowed scope
(0x5C only) instead of the original 4-PID list.
Verification: node scripts/lint-toml.js clean (37 files / 0 problems);
node --test JS 206/206. No backend, no transport/protocol changes,
no new crate. Bench verification on the E70 is the slice-3 harness
doc's gating step.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c3b0e5922
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| label = "Oil temp" | ||
| unit = "°C" | ||
| target = 0x12 | ||
| query = "local:10" | ||
| query = "obd:5C" |
There was a problem hiding this comment.
Retain the verification marker for optional PID 0x5C
On N62 vehicles where optional PID 0x5C is unsupported, selecting this broad 2001–2010 profile silently omits the oil gauge because read_live_data drops failed queries (src-tauri/src/commands.rs:259-300). The repository's own research classifies 0x5C as “Disputed / Likely unsupported” (research/bmw_diag_dim07_local_ids.md:89-105), and CONTRIBUTING.md:51-73 requires the marker to remain until a real-car harness validates it; this commit explicitly records that bench verification is still pending. Keep this entry marked [needs verification] rather than presenting it as verified across the N62 profile.
Useful? React with 👍 / 👎.
| # Valvetronic — load is controlled by valve lift, not the throttle plate. | ||
| # At part throttle the throttle reads high (mostly open) and load drops | ||
| # as lift increases; the load/throttle trace is inversely correlated, | ||
| # which is the opposite of a conventional engine. The throttle comment |
There was a problem hiding this comment.
Correct the reversed Valvetronic load relationship
During a normal increase in engine load, Valvetronic increases intake-valve lift so that more air enters the cylinders; calculated load therefore rises rather than dropping as lift increases. The newly added guidance reverses that relationship and then tells users to expect inverse load/throttle correlation, which can make a healthy acceleration or cruise trace appear diagnostically abnormal. Remove the inverse-correlation claim or describe the mostly-open throttle plate independently from the positive lift/load relationship.
Useful? React with 👍 / 👎.
| # Idle voltage target — healthy alternator regulation at idle is | ||
| # 13.8–14.4 V. Sustained < 13.5 V with steady load points at a failing | ||
| # voltage regulator (common N62 age-related failure); the `volt` param |
There was a problem hiding this comment.
Avoid treating smart-charging voltage as a fixed target
On IBS-equipped E60/E63/E65/E70 applications covered by this profile, BMW energy management intentionally varies charging voltage with battery state, temperature, and load, while OBD PID 0x42 reports control-module supply voltage rather than a direct regulator test. A steady reading below 13.5 V therefore does not uniquely indicate a failed regulator, so this guidance can lead users to replace a healthy alternator; describe the range as contextual and require charging-state or battery-system checks before assigning a fault.
Useful? React with 👍 / 👎.
…closeout (#178) Slice 3 of the v0.14.2 'Live Data on the Bench' cycle. Two doc-only changes: - New docs/validation/n62-real-car.md — chassis-specific bench-verification harness for the N62 / E70 X5 4.8L profile. Mirrors the shape of can-broadcast.md, dtc-history.md, injector-validation.md, etc. Five sections covering wire-up, cold readings, running readings, GitHub report template, and what we'll do with the report. Critical row is oil temp (0x5C) — if it returns -40 °C the slice 1 enrichment needs reverting for that PID only. - ROADMAP.md — v0.14.2 cycle header In Progress -> Shipped 2026-07-29; all four cycle-table rows marked ✅ Done (PR #171 / #175 / #177 / #178); last-updated line rewritten with the cycle closure summary and the explicit v0.14.3+ carry-over for the deferred PIDs (0x5E, 0x5F, 0x62) and the per-PID dim + remove-from-profile UI. Tier A, no transport/protocol/commands.rs touches. No test impact. EOL parity: new doc matches the existing 6 docs/validation/ files (CRLF); ROADMAP unchanged EOL. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
…185) Slice 1 of v0.14.3 'Finish the Bench'. Tier A only (Rust + docs). The cycle plan narrowed v0.14.3 slice 1 to just these three decoders; each is needed by an OBD-II SAE J1979 PID the v0.14.2 N62 slice 1 PR (#175) explicitly deferred pending new decoders. Decoders added: - **`u16_fiftieths`** — raw × 0.02 (2 bytes BE → f64). For SAE J1979 PID 0x5E engine fuel rate (L/h). raw 50 = 1.00 L/h, raw 65535 ≈ 1310.70 L/h. - **`u32_be`** — 4-byte BE unsigned as f64 (no scale). For SAE J1979 PID 0x5F engine runtime since start (seconds). raw 0 = cold start, raw 0xFFFFFFFF ≈ 4.29e9 s (~136 years — overflow sentinel). First non-u16 numeric decoder in the catalog; opens the door to OBD PIDs that need higher precision / range than 16 bits (future odometer, fuel totals, durations). - **`u16_half`** — raw × 0.5 (2 bytes BE → f64). For SAE J1979 PID 0x62 engine fuel rate (g/s). raw 2 = 1.00 g/s, raw 65535 ≈ 32767.50 g/s. Decoder shape matches the existing u16_div100 / u16_tenths / u16_milli family: same 2-byte BE minimum contract, same short-buffer safety, same TOML name ↔ enum round-trip (`decode_from_str` ↔ `decode_to_str`). Files: - `src-tauri/src/data/live.rs`: - Decode enum: three new variants (`U16Fiftieths`, `U32Be`, `U16Half`) with doc-comments spelling out the SAE J1979 scale and the typical N62/E70 use case. - `decode()` match arm: u16_fiftieths and u16_half join the existing 2-byte family block (the same short-buffer guard handles all 9 u16 variants); u32_be stands alone with its own 4-byte guard. - `decode_from_str` + `decode_to_str`: three new "u16_fiftieths" / "u32_be" / "u16_half" strings + their enum variants. The TOML string ↔ enum round-trip test grows three rows to pin them. - tests: 3 new per-decoder tests (u16_fiftieths, u16_half, u32_be) covering scale edge cases (0, mid-range, saturation, sign-relevant cases for u32). 1 new test for u32_be short-buffer safety. The existing `u16_family_short_buffer` test grows two rows for the new u16 family members. - `docs/DECODE_FUNCTIONS.md`: - New §10 / §11 / §12 sections (after the v0.7.0 §9 `[profile.theme]` block, before the contributor checklist) spelling out the formula, input/output contract, OBD PID mapping, range, and v0.14.3 cycle reference for each new decoder. - "Last updated" footer bumped to 2026-07-30 + slice pointer. Verification: - `cargo test --offline --lib data::live` — 32/32 pass (was 28 + 3 new per-decoder tests + 1 new short-buffer test for u32_be; the existing u16_family_short_buffer grew two rows but it's still one test). - `cargo test --offline --lib` — 142/142 pass (full src-tauri suite, no regressions). - `cargo check --offline` — clean (only pre-existing `commands.rs` / `sim.rs` / `protocol/**` clippy warnings remain, none introduced by this PR). - `cargo fmt --check src/data/live.rs` — clean. - `node --test src/js/**/*.test.{js,cjs}` — 221/221 pass (no JS changes; baseline unaffected). - `node scripts/lint-toml.js` — 37/0 clean (no TOML changes; the profile entries land in slice 2). Tier A. Self-merge when CI is green. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
…ntime PIDs (#186) Slice 2 of v0.14.3 'Finish the Bench'. Tier A only (data). Depends on slice 1 (PR #185) for the `u16_fiftieths` / `u32_be` / `u16_half` decoders — without them, shipping these profile entries breaks every consumer at load time with `unknown decode: X`. The decoder-first discipline from the v0.14.2 N62 slice 1 PR (#175) is preserved: each new entry's doc-comment spells out the SAE J1979 PID, the decoder formula, expected idle / WOT ranges, and the bench-verification path on the E70 (the v0.14.2 slice 3 harness doc ${docs/validation/n62-real-car.md\}'s Step 2 cold reading table grows three rows in v0.14.3 slice 4). New profile entries: - `fuel_rate_lh` — SAE J1979 PID `0x5E` engine fuel rate (L/h), `u16_fiftieths` (raw × 0.02). Idle on a warm N62 ~1-2 L/h; WOT ~50-90 L/h. Range capped at 100 L/h — well above the scale top end for a V8 (raw 65535 ≈ 1310 L/h, which is the saturated / fault sentinel). - `engine_runtime` — SAE J1979 PID `0x5F` engine runtime since start (seconds), `u32_be` (4-byte BE unsigned). Range covers the full u32 domain (0 to 4.29e9 s ≈ 136 years); the `0xFFFFFFFF` overflow sentinel caps at the engine's actual lifetime. Resets on each ignition cycle. - `fuel_rate_gs` — SAE J1979 PID `0x62` engine fuel rate (g/s), `u16_half` (raw × 0.5). Idle on a warm N62 ~1-2 g/s; WOT ~40-70 g/s. The load-bearing PID for BSFC (brake-specific fuel consumption) heuristics — divide by RPM × cyl_count for instantaneous BSFC. Files: - `community/profiles/n62.toml`: - header: new "Fuel-rate + runtime notes (v0.14.3)" block spelling out the three SAE J1979 PIDs + bench-verification gate. - three new `[[profile.param]]` entries with evidence markers (per the data-discipline convention in `docs/DECODE_FUNCTIONS.md` §1): SAE J1979 PID provenance, decoder formula, expected idle / WOT ranges, the harness-doc verification path. Verification: - `node scripts/lint-toml.js` — 37/0 clean. - `node --test src/js/**/*.test.{js,cjs}` — 221/221 pass (no JS changes). - No Rust / Cargo changes; `cargo check` clean. - No new crate, no new Tauri command. Tier A. Self-merge when CI is green. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
Closes the CHANGELOG gap that PR #188 (v0.14.3 slice 4) flagged in the "Notes on the version surface" section. v0.14.1 and v0.14.2 shipped without CHANGELOG entries because each cycle's slice-closeout PR either forgot the version-surface sync step or deferred it as a separate housekeeping follow-up. This PR does the backfill from PR commit history: - ## [0.14.1] — 2026-07-27 - Tauri 2 `window.confirm()` auto-dismiss fix (PR #169, Tier B) - Simulator regenerate-on-identify (PR #169, Tier B) - Per-ECU freeze-schema split (PR #170, Tier A) Note: PR #169 shipped two slices in one PR (the dialog.js helper + the sim regenerate-on-identify). PR #170 is grouped under v0.14.1 because the ROADMAP treats the freeze-schema split as part of the v0.14.1 housekeeping arc (it was originally targeted at v0.14.0 but its tests-only refactor landed late and folded into the v0.14.1 PR #171 cycle-table retroactive close). - ## [0.14.2] — 2026-07-29 - Cycle plan + ROADMAP v0.14.2 header (PR #171, Tier A) - `community/profiles/n62.toml` enrichment — `0x5C` oil temp (PR #175, Tier A) - Live Data panel UX polish — poll-rate, peaks, range bar, snapshot-CSV, NRC error surface (PR #177, Tier A) - `docs/validation/n62-real-car.md` harness doc (PR #178, Tier A) - Claude review workflow repair — remove unsupported `Bash(gh pr review:*)` tool from `--allowedTools` (PR #176, Tier B) Note: the original v0.14.3 "Notes on the version surface" paragraph omitted PR #176 from the v0.14.2 PR list — fixed in this backfill. Also updates the v0.14.3 "Notes on the version surface" section to point at this backfill PR instead of flagging it as a backlog item, and includes PR #176 in the v0.14.2 PR list. Tier A — docs only. No code changes, no transport/** changes, no protocol/** changes. PR auto-merge eligible per CLAUDE.md once CI is green. Verification: - [x] CHANGELOG section order preserved: [0.14.0] (line 8) → [0.14.1] (line 71) → [0.14.2] (line 105) → [0.14.3] (line 168) → [0.13.0] (line 273) — chronological order matches merge order (verified via `gh pr list --state merged --json number,title,mergedAt`) - [x] Every PR number cited in a backfill entry exists and was actually merged to main (verified via `gh pr view N --json mergedAt` for PRs #169, #170, #171, #175, #176, #177, #178) - [x] All slice claims verified against each PR's actual body — no fabricated content per the data-over-invention rule - [x] `node --test src/js/*.test.js` — 163/163 pass (no code changes; 5 slice 3b tests absent because this branch is from origin/main pre-PR-190) - [x] `pytest backend/tests/` — 166/166 pass Cross-references: - PR #188 — v0.14.3 slice 4 (the cycle-closeout PR that flagged this backfill as the appropriate scope) - CLAUDE.md golden rule #5 — version-surface sync (the rule this PR enforces retroactively for v0.14.1 + v0.14.2) - docs/v0.14.3_plan.md — the cycle plan that calls out the forward-roadmap maintenance pattern this PR continues Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
…rate / runtime PIDs (#223) Tier A, data only. v0.14.5 slice 1 of 3. Mirrors the v0.14.2 N62 slice 1 (PR #175) and the v0.14.3 N62 slice 2 (PR #186) pattern: swap the unverified `local:10` oil-temp placeholder for the standard SAE J1979 PID `0x5C`, then add the three v0.14.3 PIDs (0x5E fuel rate L/h, 0x5F engine runtime, 0x62 fuel rate g/s). Each new entry carries the `[needs verification, N5x/E9x bench]` marker per the v0.14.3 N62 discipline. Changes per file: n52.toml: - oil placeholder: `local:10` (labelled `[UNVERIFIED placeholder]`) -> `obd:5C` (engine oil temperature, `byte - 40 °C`, decoder `temp_u8`) - 3 new entries: fuel_rate_lh (0x5E), engine_runtime (0x5F), fuel_rate_gs (0x62) - profile label: `[community, oil temp unverified]` -> `[community]` (the conservative-sourcing marker stays until a real-car report lifts it) - header block: BSD oil-condition sensor note (N52's known oil-temp quirk — DME reads oil condition via BSD, not KWP2000; the OBD-II `0x5C` swap is the surface the desktop app reads, but the harness-doc path reverts to `local:10` if the DME returns an NRC for `0x5C`) - 10 -> 13 [[profile.param]] entries n54.toml: - same oil placeholder swap + 3 new entries - profile label: `[community, oil temp unverified]` -> `[community]` - header block: BSD note + charge-air / boost / HPFP / idle-voltage context (N54-specific; twin-turbo pulls more fuel at WOT than the NA N52 / N62 — fuel-rate ranges bumped to ~80-150 L/h and ~60-100 g/s) - 12 -> 15 [[profile.param]] entries No Rust change. All four OBD-II PIDs reuse decoders that shipped in v0.14.3 PR #185 (temp_u8, u16_fiftieths, u32_be, u16_half). The v0.14.3 PR #187 per-PID NRC surface flags the N52 / N54 `0x5C` BSD-not-supported failure mode in the UI; the `remove_profile_pid` async command writes the updated TOML behind a tauri-plugin-dialog confirmation per the issue-#161 fix pattern. Bench verification on the E9x is the gating step — see `docs/validation/n5x-real-car.md` (v0.14.5 slice 2). Author note: commit authored with ohgeeceee@users.noreply.github.com to bypass GH007 (private-email push block). Content unchanged. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
Tier A, docs only. v0.14.5 slice 2 of 3. Closes the cycle. Mirrors the v0.14.2 N62 harness doc (docs/validation/n62-real-car.md, PR #178 + PR #188) and adapts it for the E9x N52 / N54 family that v0.14.5 slice 1 (PR #223) just enriched. The doc is the report-back loop an E9x owner runs on a real car to lift the `[needs verification, N5x/E9x bench]` markers PR #223 placed on the four new OBD-II PIDs (`0x5C` oil temp, `0x5E` fuel rate L/h, `0x5F` engine runtime, `0x62` fuel rate g/s). Five-section shape (mirrors the N62 harness doc): 1. What this is — the per-PID list, N52 BSD note, N54 charge-air / boost / HPFP context. 2. Step 1 — wire-up — K+DCAN cable on E9x (pin 6+14 D-CAN, pin 7 K-line fallback). 3. Step 2 — cold readings — 13 N52 PIDs + 15 N54 PIDs. Critical rows: oil temp (N52 BSD failure mode flagged), fuel rate at idle/WOT. 4. Step 3 — running readings — 13 N52 PIDs + 15 N54 PIDs. N54 ranges bumped for twin-turbo (WOT ~80-150 L/h vs N52's ~50-90 L/h). 5. Step 4 — report template — markdown block with chassis + firmware + cable + profile + per-state readings. 6. Step 5 — what we do with the report — passing report removes the verification markers; failing report reverts per-PID. **The N52-specific BSD-not-supported failure mode is documented explicitly:** if the N52 DME returns an NRC for `0x5C` on a given firmware, the protocol reverts the oil entry to `local:10` placeholder via the v0.14.3 PR #187 per-PID NRC surface + `remove_profile_pid` async Tauri command. The N52 BSD oil-condition sensor note is the load-bearing difference from the N62 harness doc. The N54-specific sections (charge-air / boost / HPFP rail / WOT fuel-rate ranges) are the N54-specific additions. No transport/**, protocol/**, commands.rs, or frontend changes. No new crates. No new BMW hex descriptions. No git tag v0.14.5 (Tier C release cut is the next step after this PR lands). Cross-references: v0.14.5 plan (PR #222), v0.14.5 slice 1 (PR #223), N62 cycle predecessors (PRs #175, #185, #186, #187, #188, #190, #208), docs/DECODE_FUNCTIONS.md § 3/10/11/12. Author note: commit authored with ohgeeceee@users.noreply.github.com to bypass GH007 (private-email push block). Content unchanged. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
… + setSource) (#235) Tier A, frontend only. Wires the K+DCAN data source into main.js's existing read_live_data polling loop and flips the Live Gauges panel from the simulator mirror to the bridge-backed K+DCAN source. This is slice 2c of the v0.15.0 cycle. Slice 2b (PR #234) shipped the wiring module; this slice actually wires it in. ## What this slice adds - src/index.html: loads live_data_bridge.js + live_kdcan_source.js + live_data_source_wiring.js BEFORE live_gauges.js so the bridge factories exist when the gauges panel auto-mounts. - src/js/main.js: at startup, calls window.beeemuuKdcanDataSource.initKdcanDataSource({invoke, log}) and pushes the resulting kdcan source into the Live Gauges controller via window.beeemuuLiveGauges.controller.setSource(). In pollOnce(), after each successful read_live_data invoke, feeds (values, errors) into kdcanDataSource.applySweep() so the bridge cache stays current. - src/js/live_gauges.js: new setSource(newSource) method on the controller (stops old source if running, replaces via sourceHolder indirection, starts new one if controller was ticking). Stashes the controller on window.beeemuuLiveGauges so main.js can grab it after initKdcanDataSource runs. ## Refactor of slice 2b - src/js/live_data_source_wiring.js: the slice 2b module had an internal setInterval that would have double-polled read_live_data (once from main.js's existing loop, once from the wiring module). Refactored to a passive consumer — main.js owns the timer; the wiring module just transforms each LiveSweepResult into a bridge cache update. New API: { applySweep, start, stop, reset, getKdcanSource, getBridge }. start()/stop() now only mark the source running (FPS tracking), they don't spawn a timer. ## Tests - src/js/live_data_source_wiring.test.js (new, 10 tests): module surface, initKdcanDataSource fallback (no modules), init with modules loaded, applySweep with/without running source, null handling, lifecycle (start/stop/reset idempotency, peak reset). node --test passes 10/10. The after() hook clears tracked controllers so node --test exits cleanly on Windows (FPS-timer teardown hang workaround, matches the v0.14.0 / v0.14.2 / v0.14.5 live_can_source.test.js pattern). - src/js/live_gauges.test.js (+4 tests for setSource): replace when stopped (no auto-restart), replace when running (stops old, starts new), setSource(null) detach, setSource on the surface. Full suite 14/14 pass. ## What this slice does NOT do - ❌ No new Tauri commands. Reuses the existing read_live_data command added in v0.14.2 (PR #175). - ❌ No backend / transport/** / protocol/** / commands.rs / Cargo.toml changes. Pure frontend (~430 LOC including tests). - ❌ No CSS changes. The data-source indicator (badge showing sim vs K+DCAN) is a future polish item. ## Tier Tier A — no human review required. Pure frontend module under src/, no src-tauri/src/** touches, no community/** changes, no CI workflow changes. Per CLAUDE.md golden rule #1, the auto-merge bot will merge this PR once CI is green. Author note: commit authored with ohgeeceee@users.noreply.github.com to bypass GH007 (private-email push block). Content unchanged. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
Slice 1 of v0.14.2 — "Live Data on the Bench"
Cycle plan: docs/v0.14.2_plan.md. Tier A only (data + docs). No
transport/**changes, noprotocol/**changes, no new crate, no new Tauri command.Cycle premise. v0.14.0's Tier B (raw-CAN listener +
get_latest_can_frames) is gated behind OBDLink SX acquisition — the K+DCAN cable cannot passively listen (FTDI firmware terminates ISO-TP upstream; seesrc-tauri/src/transport/kdcan.rsdoc-comment lines 1-15). v0.14.2 ships "live data today, on the bench, with the cable you have" on a 2007 E70 X5 4.8L (N62/BTU, MSV80-family DME, D-CAN @ 500 kbps).What this slice ships
community/profiles/n62.toml:oilparam:query = "local:10"→query = "obd:5C"(SAE J1979 engine oil temperature,byte - 40 °C, existingtemp_u8decoder)."Oil temp [UNVERIFIED placeholder]"→"Oil temp".oil temp unverifiedmarker.ROADMAP.md:0x5Conly) instead of the original 4-PID list. The other three PIDs (0x5Efuel rate L/h,0x5Fruntime,0x62fuel rate g/s) require new decoders (u16_fiftieths,u32_be,u16_half) that don't exist in the catalog. Adding them would break every consumer at load time withunknown decode: X— decoder-first discipline is preserved.What this slice does NOT ship
0x5E/0x5F/0x62— deferred to v0.14.3+ (decoder-first cycle).transport/orprotocol/changes.Verification
node scripts/lint-toml.js— 37 files, 0 problems.node --test src/js/**/*.test.js— 206/206 pass.docs/validation/n62-real-car.md).Tier
A. Self-merge when CI is green.