obd-poller: OBD-II mode 01, the single CAN writer; recorder keeps its values

obd-poller asks 0x7DF for the nine obd2 signals, only while the bus is alive,
and publishes envelopes stamped with the reply's kernel time. The recorder
subscribes to yukart/vehicle/* and writes vehicle.ndjson beside the frames.
Tested against a fake ECU on vcan0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
2026-10-12 23:25:03 +09:00
parent b605c27fc0
commit 6a7de03773
13 changed files with 747 additions and 12 deletions
+7 -4
View File
@@ -3,10 +3,13 @@ type: guidelines
---
# Core services guidelines
## 1. Listen-only until `obd-poller` exists
- Nothing here opens a CAN socket for writing. The recorder only reads, and the
host brings `can_pcan` up listen-only.
- `obd-poller` will be the single writer. Until it lands, any transmit is a bug.
## 1. One CAN writer: `obd-poller`
- `obd-poller` is the only thing that transmits on the car's bus (project-yukart
decision 3): OBD-II requests, one at a time, only while the bus is visibly
alive. Anything else that transmits is a bug; the recorder only reads.
- `can_pcan` is no longer listen-only (2026-10-12), so the adapter acknowledges
frames like any scan tool. Going back is one line in orin-nano-env-setup's
`/etc/default/can-can_pcan`.
## 2. Capture never waits
- A frame missed is gone. Producers hand frames to the writer without blocking;
+36 -3
View File
@@ -22,9 +22,10 @@ bus. Below.
## Recorder
Reads SocketCAN and gpsd directly. It is tier 1, and the broker is a single point
of failure that raw capture should not share. When `can-bridge` exists, the
recorder still owns raw capture; it subscribes to the bus only for decoded
signals.
of failure that raw capture should not share (decision 25). When `can-bridge`
exists, the recorder still owns raw capture; it subscribes to the bus only for
`yukart/vehicle/*` (`--broker`, `''` to disable), and a broker that is down costs
those envelopes, never a frame.
### Session triggers
@@ -60,6 +61,9 @@ is the intended second client: `ctl mark crank` for a recovered input loss, and
can-0001.log.zst candump -L, closed chunks (5 min or 64 MB), zstd
can-0002.log the open chunk, plain
gnss.ndjson {"rx": <local receive time>, "msg": <gpsd report>}
vehicle.ndjson {"rx": …, "signal": …, "env": <envelope>} for every message on
yukart/vehicle/* (OBD-II now); "retained": true for the
broker's stored copy, which may predate the session
events.ndjson marks, can_link, gnss_link, clock_step, drops, storage_bound
```
@@ -110,6 +114,35 @@ enough to time individual frames against position.
- **Copy-off.** Recordings stay on the car until the parked-at-home step in the
power plan, or a manual `rsync`.
## obd-poller
OBD-II mode 01 (SAE J1979) on `can_pcan`, the single CAN writer. Tier 2.
- **Requests** go to the functional address `0x7DF`, one at a time; the first
single-frame reply from `0x7E8`–`0x7EF` answers it (kernel filter: nothing else
reaches the process). 100 ms timeout, 10 ms between requests.
- **Discovery** walks the supported-PID bitmasks (`01 00`, `01 20`, …) whenever the
bus wakes; only supported PIDs are polled.
- **Schedule:** rpm, speed, throttle, load, MAP and MAF every round (~10–13 Hz each
with a 4 ms ECU); coolant, intake air and fuel level once a second.
- **Only while the bus is alive:** the interface's received-frame count moved in
the last second. A request into a sleeping bus gets no ACK, and the adapter would
resend it until error-passive.
- **Output:** envelopes on `yukart/vehicle/<signal>`, retained; names, units and the
`obd2` source checked against contracts at build. `timestamp` is the reply's
kernel receive time, identical to its line in the recorder's CAN log.
- **State** on `yukart/system/obd-poller`: `ready` with supported PIDs, achieved
rates and unanswered count every 5 s; `degraded` with the reason (bus idle, no
ECU answers, interface errors); last will `stopping`.
- `--once`: discover, read each supported PID once, print JSON with reply times.
- **Not yet:** multi-frame replies (ISO-TP flow control), so no DTC reading (modes
03/07/0A) and no MUT-III `21 xx` requests. The kernel has no `can-isotp`; flow
control will be done by hand.
Tested 2026-10-12 against a fake ECU on `vcan0`: 7 PIDs discovered and polled,
540 requests and 540 replies in the recorder's log, every envelope's timestamp
equal to its reply frame's.
## orin-status
The Orin's own health as envelopes on `yukart/host/<signal>`, retained, once a