Compare commits
2 Commits
73eff1c5d6
...
88c926c58a
| Author | SHA1 | Date | |
|---|---|---|---|
| 88c926c58a | |||
| b55ccba268 |
@@ -18,6 +18,7 @@ definitions from here.
|
|||||||
| [`schema/topics.json`](schema/topics.json) | Topic namespace: payload kind, retention, rate, publisher |
|
| [`schema/topics.json`](schema/topics.json) | Topic namespace: payload kind, retention, rate, publisher |
|
||||||
| [`schema/envelope.json`](schema/envelope.json) | JSON Schema for the decoded-signal envelope |
|
| [`schema/envelope.json`](schema/envelope.json) | JSON Schema for the decoded-signal envelope |
|
||||||
| [`schema/signals.json`](schema/signals.json) | Signal registry — the single source for name, unit, type and range |
|
| [`schema/signals.json`](schema/signals.json) | Signal registry — the single source for name, unit, type and range |
|
||||||
|
| [`docs/BOUNDARY.md`](docs/BOUNDARY.md) | Display boundary protocol: what crosses between `svc-gw` and the app |
|
||||||
|
|
||||||
The Z27AG DBC lands here too, once Phase 2 produces one.
|
The Z27AG DBC lands here too, once Phase 2 produces one.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
type: reference
|
||||||
|
---
|
||||||
|
# Display boundary protocol — v0
|
||||||
|
|
||||||
|
Between a display client (`app-flutter`) and [svc-gw](gitea:yukart/svc-gw). The
|
||||||
|
bus topics in `schema/` stay inside the Orin; this is what crosses to the tablet.
|
||||||
|
|
||||||
|
## Transport
|
||||||
|
|
||||||
|
One WebSocket, `ws://<orin>:7700/`, JSON text messages, each with a `"t"` type.
|
||||||
|
The tablet reaches the car Orin at `192.168.55.1` over USB NCM; the app binds to
|
||||||
|
that network itself (see `app-flutter`'s initial plan).
|
||||||
|
|
||||||
|
The client always connects and owns reconnection. svc-gw refuses peers outside
|
||||||
|
loopback, RFC 1918 and the tailnet's 100.64.0.0/10.
|
||||||
|
|
||||||
|
The two channels from decision 24 share the socket but keep different guarantees:
|
||||||
|
|
||||||
|
| | Down (state) | Up (commands) |
|
||||||
|
|---|---|---|
|
||||||
|
| Loss | tolerated; svc-gw drops the oldest when a client lags (64-message queue) | not tolerated; retry until acked |
|
||||||
|
| On reconnect | `hello`, then current state on the next tick | resend anything unacked, same `id` |
|
||||||
|
|
||||||
|
## Down: svc-gw → client
|
||||||
|
|
||||||
|
| `t` | When | Fields |
|
||||||
|
|---|---|---|
|
||||||
|
| `hello` | first message | `proto` (0), `server`, `host`, `source` (`mock` \| `none`), `mock`, `hb_ms`, `timeout_ms` |
|
||||||
|
| `hb` | every `hb_ms` | `ts` (Orin epoch ms), `clients` |
|
||||||
|
| `state` | 5 Hz, when there is a source | `seq`, `mock`, `signals`: name → envelope (`schema/envelope.json`) |
|
||||||
|
| `ack` | reply to a `cmd` | `id`, `ok`, `detail`, `dup` (true when answered from the cache) |
|
||||||
|
|
||||||
|
`signals` holds only what is currently known. A missing signal is unknown, a
|
||||||
|
`null` value is known-without-value, `quality: "stale"` is an old value. All three
|
||||||
|
are normal and a display must render each distinctly.
|
||||||
|
|
||||||
|
⚠ **`mock: true` must be visible on the display surface** whenever it appears in
|
||||||
|
`hello` or in any envelope. Not in a settings page, not in a log.
|
||||||
|
|
||||||
|
## Up: client → svc-gw
|
||||||
|
|
||||||
|
| `t` | When | Fields |
|
||||||
|
|---|---|---|
|
||||||
|
| `hb` | every `hb_ms` | — |
|
||||||
|
| `cmd` | on user action | `id` (unique, the idempotency key), `cmd`, `args` |
|
||||||
|
|
||||||
|
svc-gw drops a client silent for `timeout_ms` (5 s). The client should treat
|
||||||
|
`timeout_ms` without any downstream message the same way and reconnect: an asleep
|
||||||
|
tablet, or an Orin behind a replugged hub, looks connected at the link layer.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
| `cmd` | `args` | Effect |
|
||||||
|
|---|---|---|
|
||||||
|
| `ping` | — | `ack` with `detail: "pong"` |
|
||||||
|
| `mark` | `text`, `delay_ms` (optional: how long the command waited before this send) | stamped into the open recording; fails with no session open |
|
||||||
|
|
||||||
|
A repeated `id` returns the original `ack` (last 256 kept, shared across
|
||||||
|
connections) and never re-runs the command.
|
||||||
|
|
||||||
|
## Not yet
|
||||||
|
|
||||||
|
- TTS utterances (id, chunk sequence, terminator, expiry) — when there is a TTS.
|
||||||
|
- `gnss` and decoded vehicle state from the bus — when the bus exists.
|
||||||
|
- Any version beyond `proto: 0`. A breaking change bumps it; the client checks it.
|
||||||
@@ -20,20 +20,10 @@ observed traffic. They arrive as DBC entries plus registry rows with
|
|||||||
`system/<service>` have their fields named in `topics.json` but no JSON Schema
|
`system/<service>` have their fields named in `topics.json` but no JSON Schema
|
||||||
yet. They get one when a publisher exists to constrain.
|
yet. They get one when a publisher exists to constrain.
|
||||||
|
|
||||||
**The app ↔ `svc-gw` boundary protocol.** Bus topics are defined here, but the
|
**The app ↔ `svc-gw` boundary protocol** is at v0 in [BOUNDARY.md](../../BOUNDARY.md):
|
||||||
two channels across the boundary (latest-wins state down, acknowledged commands
|
heartbeat both ways, idempotency key on every command, mock flagged per message.
|
||||||
up) have no wire format yet. Project-yukart decision 24 (proposed) already
|
Still missing from it: the TTS utterance shape (id, chunk sequence, terminator,
|
||||||
fixes what it must carry, because the USB link drops for ~3 s at every power
|
expiry), defined when a TTS exists to send one.
|
||||||
change and a sleeping tablet looks alive at the link layer:
|
|
||||||
|
|
||||||
- a **heartbeat** in both directions, with an agreed interval and timeout, so the
|
|
||||||
Orin reaps a silent client instead of trusting TCP or link state;
|
|
||||||
- an **idempotency key** on every upstream command, so retries across a
|
|
||||||
reconnect never act twice;
|
|
||||||
- for TTS, **utterance id + chunk sequence + terminator + an expiry**, so a
|
|
||||||
reconnect can resume or drop an utterance rather than replay it blindly.
|
|
||||||
|
|
||||||
Defined when `svc-gw` and `app-flutter` first talk, not before.
|
|
||||||
|
|
||||||
## Order
|
## Order
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
type: report
|
||||||
|
date: 2026-10-09
|
||||||
|
---
|
||||||
|
# Z27AG CAN: prior art and a first look
|
||||||
|
|
||||||
|
What exists publicly for decoding a Mitsubishi of this age, and what the first
|
||||||
|
10 s capture already shows. The input to Phase 2: not inventing what someone has
|
||||||
|
already published.
|
||||||
|
|
||||||
|
## Short version
|
||||||
|
|
||||||
|
- **Nothing public decodes the Z27 Colt's broadcast frames.** No DBC, no ID list,
|
||||||
|
for the Colt, Colt Ralliart, CZT or Version-R. The broadcast IDs have to be
|
||||||
|
reverse-engineered against our own recordings.
|
||||||
|
- **Diagnostic requests are well covered.** Mitsubishi's MUT-III protocol is
|
||||||
|
KWP2000 over ISO 15765 (`0x21 <local id>` "mode 21" requests), and OBDb publishes
|
||||||
|
open, machine-readable definitions for many Mitsubishi ECUs. Those are the fast
|
||||||
|
path to named signals, and the ground truth for decoding the broadcast ones.
|
||||||
|
- **Standard OBD-II mode 01** (RPM 0x0C, speed 0x0D, coolant 0x05, …) works on any
|
||||||
|
car of this age and is what `obd-poller` should start with — all nine signals in
|
||||||
|
`schema/signals.json` are mode 01 PIDs.
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
### Broadcast frames
|
||||||
|
|
||||||
|
| Source | What it has | Use to us |
|
||||||
|
|---|---|---|
|
||||||
|
| [ECUMASTER ADU application note, Evo X](https://www.ecumaster.cz/download/evoX.pdf) | Evo X OBD port at 500 kbps, pins 6/14, no terminator; a channel list (rpm, speed, tps, clt, wheel speeds, indicators, lights, gear) but the IDs only inside their `.canx` file | Same bus conventions as ours; no IDs |
|
||||||
|
| [EvolutionM "Evo X real CAN packets"](https://www.evolutionm.net/forums/evo-x-engine-management-tuning-forums/552550-evo-x-real-can-packets.html) (forum, blocks automated fetch) | Evo X: RPM in `0x308` bytes 1–2; throttle in `0x210` bytes 0–1 | `0x210` exists on our bus too, but with a **different layout** (6 bytes; the RPM-like value is in bytes 4–5). Evo X IDs do not transfer |
|
||||||
|
| [awesome-automotive-can-id](https://github.com/iDoka/awesome-automotive-can-id) | Index of public CAN ID collections; Mitsubishi: Lancer 2008 ([habr, RU](https://habr.com/ru/post/448658/)), i-MiEV | Lancer article covers diagnostics, not broadcast (below) |
|
||||||
|
|
||||||
|
The Z27 Colt is a 2002 platform shared with the smart forfour (W454). If any
|
||||||
|
broadcast matrix carries over it is more likely from that side than from the Evo
|
||||||
|
X — a direction to look in, not a finding. No forfour ID list turned up either.
|
||||||
|
|
||||||
|
### Diagnostic requests
|
||||||
|
|
||||||
|
| Source | What it has |
|
||||||
|
|---|---|
|
||||||
|
| [OBDb/Mitsubishi](https://github.com/OBDb/Mitsubishi) (CC BY-SA 4.0) | Make-level signal set: request header, response header, request, bit position, scaling. E.g. combination meter `6A0`→`514`: `21 A3` fuel level (byte 2, −16, %), `21 AD` odometer (24 bit, km), `21 AF` dashboard voltage (÷10, V). A/C `688`→`511`: `21 13` engine RPM and vehicle speed. TPMS `600`/`773` |
|
||||||
|
| [OBDb/Mitsubishi-Lancer](https://github.com/OBDb/Mitsubishi-Lancer) and others | Per-model overrides; the Lancer one is empty. No Colt repo |
|
||||||
|
| [habr 448658](https://habr.com/ru/post/448658/), Lancer X 2008 | MUT-III over CAN: ESP `21 02` → steering angle, bytes 2–3, int16 × 0.04375 °; security access `27 01` / `27 02` gates writes, not reads |
|
||||||
|
| [EvoScan](https://evoscan.com/) | Commercial logger with MUT-III support for the EU Colt CZT / Ralliart (4G15T). Its parameter definitions are the most complete source for engine mode-21 requests on our exact engine; not open |
|
||||||
|
| [EcuTek Colt suite](https://www.tpwengineering.com/products/ecutek-vehicle-tuning-mitsubishi-colt) | Confirms the 2005+ EU Colt CZT / Ralliart ECU speaks CAN for flashing; nothing published |
|
||||||
|
|
||||||
|
OBDb's definitions are from newer Mitsubishis (roughly 2008 on). Whether the Z27's
|
||||||
|
meter and A/C units answer at the same addresses is a question one listen-only
|
||||||
|
look at the bus during a MUT-III session — or one careful request — would settle.
|
||||||
|
|
||||||
|
## What the first capture shows
|
||||||
|
|
||||||
|
`z27ag-20261009-163736-ign.log`: engine idling, parked, a light touch of
|
||||||
|
throttle around t ≈ 4–5 s. 27 periodic IDs, ~893 frames/s. Every observation
|
||||||
|
below is a candidate until checked against OBD-II mode 01 in the same recording.
|
||||||
|
|
||||||
|
| ID | Period | Candidate |
|
||||||
|
|---|---|---|
|
||||||
|
| `210` | 20 ms | **Engine speed**: bytes 4–5, big-endian, 1 rpm/bit — ~845 at idle, rising to ~900 with the throttle blip and back |
|
||||||
|
| `13D` | 20 ms | **Engine speed again**: bytes 0–1, little-endian, ≈ 2× `210`'s value (0.5 rpm/bit?) — moves in lockstep with it |
|
||||||
|
| `312` | 20 ms | Four 16-bit big-endian words near 0x07B0–0x0940, all noisy, two follow the blip. Sensor values; load or pressure, not wheel speed (parked) |
|
||||||
|
| `449` | 200 ms | Bytes 0, 1, 3 = 0x6F, 0x5F, 0x71: with the usual −40 offset, 71, 55, 73 °C — temperatures of a warm engine |
|
||||||
|
| `0C0` | 10 ms | Byte 4 low nibble a 4-bit rolling counter; high nibble looks like a checksum |
|
||||||
|
| `119`, `149`, `156` | 20 ms | Byte 6 low nibble a rolling counter, byte 7 a checksum (neither plain sum nor XOR) |
|
||||||
|
| `149` | 20 ms | Bytes 0 and 4 noisy around a centre: an inertial sensor (yaw / lateral g) is the usual owner of such a frame |
|
||||||
|
| `710`–`713` | 200 ms | Slow, mostly constant: body/meter status |
|
||||||
|
|
||||||
|
Counters and checksums matter only for transmitting, which is out of scope. The
|
||||||
|
decoder ignores those bits.
|
||||||
|
|
||||||
|
## What would move Phase 2 fastest
|
||||||
|
|
||||||
|
1. **Record mode 01 alongside the broadcast.** One `obd-poller` session polling
|
||||||
|
RPM, speed, coolant and throttle at ~2 Hz confirms or kills the `210` / `13D` /
|
||||||
|
`449` candidates in an afternoon.
|
||||||
|
2. **Try OBDb's meter and A/C requests** (`6A0`, `688`) once. If the Z27 answers,
|
||||||
|
fuel level and odometer come for free — neither is easy to find in broadcast.
|
||||||
|
3. **A drive with GNSS and marks.** Speed against GNSS speed finds the wheel-speed
|
||||||
|
frame; marked indicators, lights and brake find the body frames.
|
||||||
|
|
||||||
|
Steps 1 and 2 transmit, so they wait for the deliberate move of `can_pcan` out of
|
||||||
|
listen-only (svc-core's initial plan). Step 3 needs only the recorder, which is
|
||||||
|
running on the car unit.
|
||||||
Reference in New Issue
Block a user