62ac7bd09d
project-yukart becomes the code-free hub: overview, roadmap and cross-cutting design only. Each app or app cluster gets its own repo, indexed from here. - ARCHITECTURE.md: separate services over a message bus, with displays as bus subscribers so the directly-driven screen, Android client and ESP32 panel share one contract. Records CAN scope (listen-only + OBD-II as sole writer) and a decisions table for choices still in flux. - REPOS.md: repo splitting rule, planned repos, naming, and Gitea template-repo policy (scaffolding only — no upstream link after instantiation). - ROADMAP.md: phases 0-5, capture and decode before displays. Bus choice (MQTT) is marked proposed pending a check against real CAN rates; implementation language is still open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
120 lines
4.8 KiB
Markdown
120 lines
4.8 KiB
Markdown
# Architecture
|
|
|
|
## The one rule
|
|
|
|
**Everything is a bus participant.** Acquisition services publish; displays,
|
|
loggers and analysis tools subscribe. No service knows which display exists, and
|
|
no display talks to hardware.
|
|
|
|
This is what lets the three display options coexist without forking the system,
|
|
and what lets a service be restarted or replaced mid-drive without taking the
|
|
rest down.
|
|
|
|
## Shape
|
|
|
|
```
|
|
CAN (can1) ──▶ can-bridge ──┐
|
|
OBD-II ──▶ obd-poller ─┤
|
|
GNSS ──▶ gnss ├──▶ message bus ──┬──▶ recorder (disk)
|
|
camera ──▶ vision* ─┘ ├──▶ hud (local video out)
|
|
└──▶ telemetry-gw ──▶ remote displays
|
|
* later phase, separate task
|
|
```
|
|
|
|
## Services
|
|
|
|
| Service | Responsibility | Transmits on CAN |
|
|
|---|---|---|
|
|
| `can-bridge` | Read SocketCAN frames, timestamp, publish raw | No |
|
|
| `obd-poller` | Active OBD-II PID request/response | **Yes** |
|
|
| `gnss` | gpsd client → position, speed, time fix | No |
|
|
| `decoder` | Raw frames → named signals via DBC | No |
|
|
| `recorder` | Persist raw + decoded sessions to disk | No |
|
|
| `telemetry-gw` | Serve external display clients | No |
|
|
| `vision` | Camera capture and on-device inference (later) | No |
|
|
|
|
Each ships as a systemd unit. The host-level prerequisites they depend on
|
|
(kernel modules, device permissions, gpsd) are the environment repo's job, not
|
|
theirs.
|
|
|
|
## CAN bus scope
|
|
|
|
Passive sniffing **plus** active OBD-II queries. OBD-II is a request/response
|
|
protocol, so it necessarily transmits onto a 2008 car's bus. Constraints that
|
|
follow:
|
|
|
|
- `can-bridge` runs listen-only and must never transmit.
|
|
- `obd-poller` is the **single writer**. No other service opens a TX-capable socket.
|
|
- Transmission is rate-limited and restricted to standard OBD-II diagnostic IDs.
|
|
- TX is a distinct, explicitly enabled mode — not the default state on boot.
|
|
|
|
Reverse-engineering of Z27AG-specific frame IDs proceeds purely from observed
|
|
traffic; discovered signals are recorded as a DBC rather than as code.
|
|
|
|
## Message bus
|
|
|
|
**Proposed: MQTT (mosquitto) on the Jetson.** Rationale:
|
|
|
|
- First-class client libraries on every display target — ESP32 (Arduino
|
|
`PubSubClient` / ESP-IDF) and Android (Paho) — which is the deciding factor.
|
|
- Broker handles fan-out and retained "last known value", so a display that
|
|
connects mid-drive immediately has state to render.
|
|
- Trivially inspectable with `mosquitto_sub` while debugging in a car.
|
|
|
|
Rejected: NNG/ZeroMQ (lower latency, no broker, but a poor ESP32 story and no
|
|
retained values); DDS (capable, disproportionate).
|
|
|
|
### Topics
|
|
|
|
```
|
|
yukart/can/raw high-rate raw frames, not retained
|
|
yukart/vehicle/<signal> decoded signals (rpm, speed, coolant, …), retained
|
|
yukart/gnss/fix position/speed/time, retained
|
|
yukart/system/<service> health and status, retained
|
|
```
|
|
|
|
Payloads: JSON for low-rate signals (readable, debuggable); compact binary or
|
|
CBOR for `can/raw`, since a 500 kbps bus produces far more traffic than JSON
|
|
should carry.
|
|
|
|
Topic names and payload schemas are a **contract shared across repos** — Android
|
|
and ESP32 clients depend on them — so they are versioned in their own repo
|
|
rather than duplicated per client.
|
|
|
|
## Display contract
|
|
|
|
Three display paths, in two classes:
|
|
|
|
**Class A — video out.** A screen driven directly by the Orin over SPI/DP/HDMI.
|
|
The renderer is a local process subscribing to the bus over loopback.
|
|
|
|
**Class B — data only.** The Orin sends no video, only data; the device renders
|
|
itself.
|
|
|
|
| Path | Transport | Notes |
|
|
|---|---|---|
|
|
| Android app | WiFi, or USB (RNDIS) | Full-featured client, richest UI |
|
|
| ESP32 + RLCD/e-ink | WiFi, or USB/BT serial | Low-rate, glanceable subset of signals |
|
|
|
|
Class B devices reaching the bus over IP connect to the broker directly. For
|
|
transports without IP (BT SPP, plain USB serial), `telemetry-gw` bridges
|
|
serial ↔ bus rather than each device inventing its own protocol.
|
|
|
|
Because both classes are bus subscribers, adding, removing or swapping a display
|
|
touches no acquisition code.
|
|
|
|
## Decisions
|
|
|
|
Recorded here so fast-moving choices stay visible. Flip a row rather than
|
|
rewriting the document.
|
|
|
|
| # | Decision | Status | Date |
|
|
|---|---|---|---|
|
|
| 1 | Separate services over a message bus, not one process | Accepted | 2026-08-14 |
|
|
| 2 | Displays are bus subscribers; no display-specific logic upstream | Accepted | 2026-08-14 |
|
|
| 3 | Listen-only sniffing + active OBD-II, single writer | Accepted | 2026-08-14 |
|
|
| 4 | MQTT as the bus | **Proposed** | 2026-08-14 |
|
|
| 5 | Implementation language(s) per service | **Open** | — |
|
|
| 6 | Local logging first; off-board LTE telemetry deferred | Accepted | 2026-08-14 |
|
|
| 7 | Camera/AI inference is a later, separate task | Accepted | 2026-08-14 |
|