Populate design, architecture and repo layout docs
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>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
# 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 |
|
||||
@@ -0,0 +1,86 @@
|
||||
# Repositories
|
||||
|
||||
## Splitting rule
|
||||
|
||||
- **`project-yukart`** — overview, roadmap, cross-cutting design. No code, ever.
|
||||
- **`orin-nano-env-setup`** — the host: kernel modules, drivers, OS packages,
|
||||
device permissions, systemd plumbing. Anything that must be true *before* an
|
||||
application starts.
|
||||
- **One repo per application or application cluster.** A cluster is a set of
|
||||
services that are developed, versioned and deployed together.
|
||||
- **Helper/deployment repos** stand alone when they are reusable outside Yukart.
|
||||
|
||||
Anything cross-repo — topic names, payload schemas, the DBC — belongs in the
|
||||
contracts repo, not copied into each consumer.
|
||||
|
||||
## Current
|
||||
|
||||
| Repository | Role |
|
||||
|---|---|
|
||||
| [project-yukart](gitea:yukart/project-yukart) | Hub: overview, roadmap, architecture |
|
||||
| [orin-nano-env-setup](gitea:yukart/orin-nano-env-setup) | Jetson host bring-up |
|
||||
|
||||
## Planned
|
||||
|
||||
| Repository | Role | Why separate |
|
||||
|---|---|---|
|
||||
| `yukart-contracts` | Topic namespace, payload schemas, Z27AG DBC | Consumed by every other repo, including non-Jetson clients; must not be duplicated |
|
||||
| `yukart-core` | Acquisition + processing cluster: `can-bridge`, `obd-poller`, `gnss`, `decoder`, `recorder` | Developed and deployed as one unit on the Orin |
|
||||
| `yukart-gw` | `telemetry-gw` — serves external display clients | Independent release cycle from acquisition; the network-facing surface |
|
||||
| `yukart-hud` | Local renderer for the directly-driven screen (Class A) | Its own toolchain and graphics stack |
|
||||
| `yukart-android` | Android display client (Class B) | Entirely separate toolchain and release process |
|
||||
| `yukart-esp32-display` | ESP32 RLCD/e-ink display firmware (Class B) | Embedded toolchain, flashed not deployed |
|
||||
| `yukart-analysis` | Offline session analysis, reverse-engineering notebooks | Runs off-car, no runtime coupling |
|
||||
|
||||
Create these lazily — when there is something to put in them, not before.
|
||||
|
||||
## Naming
|
||||
|
||||
- `yukart-*` for project-specific repositories.
|
||||
- **No prefix** for reusable helpers, since the prefix would be a lie the first
|
||||
time one is used in another project.
|
||||
|
||||
## Helper and deployment repos
|
||||
|
||||
Arduino and ESP32 work implies flashing, build and deployment scaffolding that
|
||||
is likely to be reused. Keep those unprefixed and generic — e.g. an ESP32
|
||||
project skeleton with the toolchain, flashing scripts and CI already wired up.
|
||||
|
||||
### Gitea template repositories
|
||||
|
||||
Gitea supports these: repo **Settings → Repository → Template**, after which the
|
||||
repo offers **"Use this template"** to create new repos from it.
|
||||
|
||||
Use a template repo for **scaffolding** — a new ESP32 or Arduino project that
|
||||
should start with the toolchain, directory layout and flashing scripts in place.
|
||||
|
||||
Do **not** use one for code you intend to keep updating in one place. Template
|
||||
instantiation is a fork-at-create: the copy has no link back, so improvements to
|
||||
the template never reach repos already created from it. For genuinely shared
|
||||
code, use a git submodule or publish a package instead.
|
||||
|
||||
Rule of thumb: *template for the starting shape, submodule/package for living
|
||||
code.*
|
||||
|
||||
## Linking convention
|
||||
|
||||
Cross-repo links use the `gitea:owner/repo` scheme, as established in the hub
|
||||
README — e.g. `[Environment setup](gitea:yukart/orin-nano-env-setup)`.
|
||||
|
||||
## Per-repo document layout
|
||||
|
||||
Following the convention already set by `orin-nano-env-setup`:
|
||||
|
||||
```
|
||||
README.md minimal portal, points everywhere else
|
||||
docs/
|
||||
GUIDELINES.md implementation practice for this repo
|
||||
BOM.md hardware, where the repo owns hardware
|
||||
impl/
|
||||
plans/ what is intended
|
||||
reports/ what actually happened, and why
|
||||
```
|
||||
|
||||
The `impl/plans` ÷ `impl/reports` split is worth keeping: plans go stale,
|
||||
reports do not. A report like `PCAN-SETUP.md` stays valuable precisely because
|
||||
it records the investigation and the dead ends, not just the final commands.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Roadmap
|
||||
|
||||
Phases are ordered by dependency, not by date. Each phase should end with
|
||||
something demonstrable in the car.
|
||||
|
||||
## Phase 0 — Host bring-up · *in progress*
|
||||
|
||||
Getting the Jetson to see the hardware at all.
|
||||
|
||||
- [x] PCAN-USB working as SocketCAN `can1` (out-of-tree `pcan.ko`, netdev build)
|
||||
- [x] SIM7600G-H-M.2 specified and documented
|
||||
- [ ] SIM7600 brought up: LTE data path and GNSS via gpsd
|
||||
- [ ] Kernel-update procedure for the out-of-tree PCAN module — it does not
|
||||
survive kernel upgrades and will break silently
|
||||
- [ ] Power and mounting in the car: clean shutdown on ignition off
|
||||
|
||||
Tracked in [orin-nano-env-setup](gitea:yukart/orin-nano-env-setup).
|
||||
|
||||
## Phase 1 — Capture
|
||||
|
||||
Get real Z27AG traffic onto disk before designing anything around it.
|
||||
|
||||
- [ ] `can-bridge` — listen-only, timestamped frames onto the bus
|
||||
- [ ] `recorder` — persist raw sessions
|
||||
- [ ] Capture drive sessions across varied conditions (idle, cold start, moving,
|
||||
lights/indicators/windows actuated) with a written log of what was done when
|
||||
|
||||
The manual event log matters more than it sounds: it is what makes the raw
|
||||
capture decodable later.
|
||||
|
||||
## Phase 2 — Decode
|
||||
|
||||
- [ ] Correlate captures against the event log; identify recurring frame IDs
|
||||
- [ ] Build the Z27AG DBC incrementally in `yukart-contracts`
|
||||
- [ ] `decoder` — raw frames to named signals
|
||||
- [ ] `obd-poller` — active OBD-II PIDs, single writer, rate-limited
|
||||
- [ ] `gnss` — position/speed, useful as ground truth for validating decoded speed
|
||||
|
||||
OBD-II gives standardised signals quickly and is worth doing early; it also
|
||||
provides a cross-check for reverse-engineered ones.
|
||||
|
||||
## Phase 3 — Bus and contracts
|
||||
|
||||
- [ ] Stand up the broker; confirm MQTT holds up at real CAN rates (decision 4
|
||||
in [ARCHITECTURE.md](ARCHITECTURE.md) is still *proposed*)
|
||||
- [ ] Freeze v1 of topic names and payload schemas
|
||||
- [ ] `telemetry-gw` — external client access, including the serial bridge
|
||||
|
||||
## Phase 4 — Displays
|
||||
|
||||
Order chosen by feedback speed, not ambition.
|
||||
|
||||
- [ ] One display end-to-end first, proving the contract — Android client is the
|
||||
quickest to iterate on
|
||||
- [ ] `yukart-hud` — directly-driven screen (Class A)
|
||||
- [ ] `yukart-esp32-display` — RLCD/e-ink glanceable subset (Class B)
|
||||
|
||||
## Phase 5 — Camera and inference · *separate task*
|
||||
|
||||
Deliberately deferred. It is the reason the compute is a Jetson rather than a
|
||||
Pi, but it shares nothing with the CAN work beyond the bus.
|
||||
|
||||
## Deferred
|
||||
|
||||
- Live off-board telemetry over LTE — hardware is present, but local logging
|
||||
covers the current need
|
||||
- Any CAN transmission beyond OBD-II diagnostics
|
||||
Reference in New Issue
Block a user