Skip to content

klexical/openRS_

Repository files navigation

openRS_
Open-source real-time telemetry dashboard for the Ford Focus RS MK3

🔷 Sapphire Dashboard🚀 Live EmulatorScreenshotsFeaturesHardwareQuick StartArchitectureRoadmap

Version Platform Kotlin Compose License CAN Bus

"Built for the car Ford should have given us an app for."


What is openRS_?

openRS_ is a native Android app that turns your phone into a full telemetry dashboard for the Ford Focus RS MK3. It connects to a MeatPi USB (C3) or MeatPi Pro (S3) adapter — over Wi-Fi or Bluetooth Low Energy — and passively monitors the full CAN bus at ~2100 fps, decoding every parameter the car broadcasts in real time.

Unlike generic OBD apps, openRS_ is purpose-built for the Focus RS. It understands the GKN Twinster AWD system, polls TPMS tire pressures and temperatures from the BCM, decodes Ford-specific parameters across HS-CAN and MS-CAN, and presents everything in a dark, glanceable interface tuned for track days. A bundled FORScan PID catalog covers 1,149 PIDs across 8 ECU modules (PCM, OBDII, BCM, ABS, AWD, HVAC, IPC, PSCM) — and new parameters can be added from the catalog via JSON without touching code.

Try it now: klexical.github.io/openRS_/emulator — live browser emulator with animated demo data, no hardware required.

Analyse your data: klexical.github.io/openRS_Sapphire, the post-session analytics dashboard. Drop an export ZIP to explore charts, CAN data, and diagnostics in your browser.


Screenshots

DASH tab POWER tab CHASSIS tab TEMPS tab DIAG tab MORE tab

DASH  ·  POWER  ·  CHASSIS  ·  TEMPS  ·  MAP  ·  DIAG  ·  MORE


Features

7 Tabs

Screen Description
DASH Hero boost/RPM/speed gauges (with session peak values), 0-60 / 0-100 performance timer, real-time fuel economy (idle L/hr, instant/average MPG or L/100km, DTE), throttle/brake/clutch/fuel/battery, Temps Quick (oil, coolant, intake, oil life), G-forces (lat, lon, torque), animated AWD split bar, IPC warning lamp banner (CEL, ABS, BRK, CHRG, OIL, TEMP)
POWER AFR hero cards (actual/desired/lambda), commanded AFR (CMD AFR), ETC actual/desired, WGDC, TIP, HP fuel rail PSI, low-pressure fuel rail (LP FUEL), timing, engine load, OAR, per-cylinder knock correction (KR C1–C4, colour-coded), VCT intake/exhaust, STFT/LTFT short/long-term fuel trims
CHASSIS AWD detail (4 wheel speeds, wheel rotation counts, torque bar, F/R delta, L/R delta, rear bias, clutch temps L/R, clutch actuator current L/R, hydraulic pressure L/R, trans oil temp), G-force + yaw + steering with peak reset, TPMS Focus RS wireframe (pressure + temp, 3-tier colour-coded Low/Warn/High thresholds), pressure spread warning (⚠ PRESSURE IMBALANCE when spread ≥ 4 PSI), temperature range legend
TEMPS Animated Ready-to-Race banner, 14 temperature cards each with a colour indicator bar (oil, coolant, intake, ambient, RDU, PTU, charge air, manifold charge, catalytic, cabin, battery, clutch L, clutch R, trans oil)
MAP Live drive tracking with Google Maps — 6 colour modes (speed, drive mode, boost, throttle, lateral G, temp), start/finish markers, peak event markers (RPM/boost/lat-G/speed), pause markers, route stats HUD, weather card, custom recenter / zoom controls. Drive history grouped by date with status badges, swipe-to-delete, per-drive export (GPX/CSV/summary).
DIAG DTC Scanner (full-module scan including GFM 0x7D2, count badges, freeze-frame, clear), DID Prober (scan any ECU for valid Mode 22 DIDs), PID Browser (1,149 FORScan PIDs across 8 modules, searchable), frame inventory with per-ID change tracking, SLCAN raw log, live button input feedback (drive mode toggle, suspension, ASS, ESC defeat), battery row with charge/discharge current, one-tap ZIP export (SavvyCAN/Kayak compatible)
MORE Drive mode (N/S/T/D, tap-to-change with openrs-fw firmware), ESC status, firmware version display, firmware-gated features (Launch Control, Auto S/S Kill), Module Status (RDU/PDC/FENG live OBD), passive VIN decode (CAN 0x40A), connection & snapshot, in-app update checker (stable + beta channels)

A frosted-glass bottom nav bar with vector icons + spring-animated indicator persists across all tabs. Quick Mode Dock drops down from the status bar's MODE pill from any tab for one-tap drive mode changes.

Ready-to-Race Thresholds

The TEMPS tab shows a warming-up / race-ready banner based on oil, coolant, RDU, PTU, and clutch temperatures. Thresholds are preset-based — configurable via Street / Track / Race in Settings:

Sensor Street Track Race
Engine Oil ≥ 70 °C ≥ 80 °C ≥ 85 °C
Coolant ≥ 70 °C ≥ 75 °C ≥ 80 °C
RDU ≥ 30 °C ≥ 30 °C ≥ 30 °C
PTU ≥ 40 °C ≥ 40 °C ≥ 40 °C

The banner shows which sensors are still below threshold with live °C values. A value of −99 °C (not yet received) is treated as passing to avoid blocking warm cars on reconnect. Clutch temperatures are also factored into the race-ready check (cold threshold: < 25 °C).

Settings

Setting Options Default
Speed unit MPH / KPH MPH
Temperature °F / °C °F
Boost pressure PSI / BAR / kPa PSI
Tire pressure PSI / BAR PSI
Low tire threshold PSI (any value) 30 PSI
Threshold preset Street / Track / Race Street
Theme RS paint colours (Nitrous Blue, Frozen White, etc.) Nitrous Blue
Keep screen on on / off on
Auto-reconnect on / off on
Reconnect interval seconds 10 s
Adapter MeatPi USB (C3) / MeatPi Pro (S3) MeatPi USB
Connection method Wi-Fi / Bluetooth Low Energy Wi-Fi
BLE device scan + pick from in-app dialog
Update channel Stable / Beta Stable
Auto-record drives on / off off
Max saved drives count 50
Visibility Night / Day / Sun preset + brightness slider (0.0–1.0) Night
MicroSD logging reminder on / off off (MeatPi Pro only)
Max saved ZIP exports count 5
Odometer display Miles / km Miles
Edge shift light on / off off
Shift light colour Accent / White / Progressive Accent
Shift light intensity Low / Medium / High High
Shift light RPM RPM threshold 6800
Tire warn threshold PSI 32 PSI
Tire high threshold PSI 40 PSI
Live CAN Parameters — WebSocket SLCAN (passive at full bus speed)

All data is received passively from the CAN bus via WebSocket SLCAN at ~2100 fps. No OBD polling windows or header switching required for primary gauges.

CAN ID Parameters Source
0x010 Steering wheel angle (°) with direction sign RS_HS.dbc SASMmsg01
0x070 Torque at transmission (Nm) RS_HS.dbc
0x076 Throttle % RS_HS.dbc
0x080 Accelerator pedal %, brake pedal, reverse RS_HS.dbc
0x090 RPM, barometric pressure RS_HS.dbc
0x0C8 Gauge brightness, e-brake, ignition status RS_HS.dbc
0x0F8 Engine oil temp, boost pressure (gauge + baro), PTU temp RS_HS.dbc PCMmsg07
0x130 Vehicle speed kph RS_HS.dbc
0x138 Clutch pedal position (%) — 10-bit Motorola × 0.1% RS_HS.dbc PCMmsg10
0x160 Longitudinal G-force RS_HS.dbc
0x180 Lateral G-force, yaw rate, vertical G RS_HS.dbc ABSmsg02
0x1E0 Wheel rotation counts (4 × 8-bit rolling FL/FR/RL/RR) + average front wheel speed RS_HS.dbc ABSmsg06
0x190 4-corner wheel speeds (15-bit Motorola × 0.011343 km/h) RS_HS.dbc ABSmsg03
0x1A4 Ambient temperature (MS-CAN bridged) community research
0x1B0 Drive mode (Normal/Sport+Track/Drift) — byte 6 upper nibble, steady-state frames only (byte 4 == 0). Combined with 0x420 to resolve Sport vs Track. RS_HS.dbc AWDmsg01
0x1C0 ESC mode status (On/Off/Sport/Launch) RS_HS.dbc
0x420 Track mode indicator, launch control status — byte 6: 0x10 = Normal/Sport, 0x11 = Track; bit 50 = LC active (~600 ms) RS_HS.dbc + empirical
0x252 Brake pressure (0–100% normalised, raw 0–4095 ADC counts) RS_HS.dbc ABSmsg10
0x2C0 AWD L/R rear torque (Nm) RS_HS.dbc
0x2F0 Coolant temp, Intake Air Temp (IAT) RS_HS.dbc PCMmsg16
0x340 Ambient temperature only (byte 7 signed × 0.25 °C) — not TPMS RS_HS.dbc PCMmsg17
0x360 Odometer, engine status — odo: bytes [3:5] BE, 24-bit, 1 km/bit (~5 Hz); engine: byte 0 (0=Idle, 2=Off, 183=Running, 186=Kill, 191=RecentStart, 196=Warmup) RS_HS.dbc + community #102
0x380 Fuel level % (FuelLevelFiltered — Motorola 10-bit, factor 0.4 %) RS_HS.dbc PCMmsg30
0x40A Multiplexed VIN decode (3 pages × 6 bytes → 17-char VIN) + odometer community (@adamsouthern)

Note: 0x230 (gear position) and 0x3C0 (battery voltage) do not broadcast on this vehicle. Battery voltage is polled via OBD. Gear display has been removed.

Polled via OBD Mode 22 (periodic, low-frequency):

ECU Request Response PIDs / Function Interval
PCM 0x7E0 0x7E8 ETC actual (0x093C), ETC desired (0x091A), WGDC (0x0462), KR cyl 1–4 (0x03EC–0x03EF), OAR (0x03E8), Charge Air Temp (0x0461), Catalyst Temp (0xF43C), AFR actual (0xF434), AFR desired (0xF444), TIP actual (0x033E), TIP desired (0x0466), VCT intake (0x0318), VCT exhaust (0x0319), Oil Life (0x054B), HP Fuel Rail (0xF422), Fuel Level (0xF42F), Battery Voltage (0x0304) 30 s
BCM 0x726 0x72E Battery SOC (0x4028), Battery temp (0x4029), Cabin temp (0xDD04), TPMS pressure LF/RF/LR/RR (0x2813–0x2816) (((256*A)+B)/3 + 22/3) * 0.145 PSI, TPMS last sensor (0x280B) temp = raw - 40 °C, pressure = (A*256+B)/20 PSI (multi-frame ISO-TP, matched by sensor ID) 30 s
BCM (once) 0x726 0x72E Odometer (0xDD01) — extended session, TPMS sensor IDs (0x280F LF, 0x2810 RF, 0x2811 RR, 0x2812 LR) — 4-byte IDs for 0x280B matching once
AWD module 0x703 0x70B RDU oil temp (0x1E8A), clutch temp L/R (0x1E8B/0x1E8C), trans oil temp (0x1E80), req torque L/R (0x1E90/0x1E91), demanded pressure (0x1E92), pump current (0x1E93) — all A − 40 °C or raw 60 s
HVAC (candidate) 0x733 0x73B Blower %, interior temp, discharge air temp, blend doors L/R, defrost door — response handler wired, DIDs unconfirmed (use DID prober)
IPC (candidate) 0x720 0x728 Warning lamps (MIL/CEL, ABS, brake, charge, oil pressure, high temp) — response handler wired, DIDs unconfirmed (use DID prober)

FORScan PID Catalog — data-driven decode: A bundled JSON catalog covers 1,149 PIDs across 8 ECU modules (PCM, OBDII, BCM, ABS, AWD, HVAC, IPC, PSCM). PidRegistry evaluates formula expressions from the catalog at runtime — new PIDs can be added via JSON without modifying Kotlin code. Decoded values accumulate in VehicleState.genericValues.


Hardware

Required (one of)

Component Details
MeatPi USB (C3) Mouser — ESP32-C3, Wi-Fi + BLE, WebSocket SLCAN at ws://192.168.80.1:80/ws
MeatPi Pro (S3) MeatPi — ESP32-S3, Wi-Fi + BLE + GPS + MicroSD, raw TCP SLCAN at tcp://192.168.0.10:35000
Ford Focus RS MK3 2016–2018 (LZ platform, EcoBoost 2.3L)
Android phone Android 9+ (API 28) with Wi-Fi or Bluetooth Low Energy

Setup

  1. Plug the adapter into the OBD-II port (under the steering column)
  2. Choose your transport in Settings → Adapter and Settings → Connection method:

Wi-Fi — connect your phone to the adapter's Wi-Fi network:

Adapter / Firmware SSID Password
Stock MeatPi USB WiCAN_XXXXXX @meatpi#
openrs-fw USB openRS_XXXXXX openrs_2026
Stock MeatPi Pro MeatPi_PRO_XXXXXX @meatpi#
openrs-fw Pro openRS_PRO_XXXXXX openrs_2026

Bluetooth Low Energy — pair the adapter via the in-app BLE picker (BLUETOOTH_SCAN + CONNECT permissions requested at scan time, RSSI bars, filtered by service UUID 0xFFE0).

  1. Install openRS_ and tap the connection dot in the header.

Stock firmware works out of the box for passive monitoring. For full functionality (drive mode write, Launch Control, Auto S/S Kill, ESC control), flash openrs-fw to the adapter — v1.61 (USB) or v1.2 (Pro). See the firmware update guide.

MeatPi Pro users: The Pro defaults to ELM327 mode. You must open the Pro's web UI (http://192.168.0.10), set the protocol to SLCAN, CAN speed to 500 kbps, and TCP port to 35000, then reboot. See the hardware setup guide for full instructions.

BLE vs Wi-Fi: BLE leaves your phone's Wi-Fi free for internet (weather, maps, in-app updates). Wi-Fi delivers slightly higher CAN throughput. The app supports both transports for both adapters.


Quick Start

Prerequisites

  • Android Studio Ladybug (2024.2) or newer
  • JDK 17+
  • Android SDK 35

Build

git clone https://github.com/klexical/openRS_.git
cd openRS_/android
./gradlew assembleRelease
# Output: app/build/outputs/apk/release/openRS_v2.2.6.apk
# (Requires keystore — see android/docs/signing-setup.md)

Browser Emulator (no hardware required)

Open android/browser-emulator/index.html in any browser, or visit the live version:

klexical.github.io/openRS_/emulator

  • All tabs animate with simulated Focus RS data (RPM, boost, speed, AWD, temps, TPMS)
  • MORE tab shows drive mode, ESC, features, diagnostics
  • ⚙ Settings button demonstrates the settings dialog

Sapphire — Post-Session Analytics

Drop an export ZIP from the app into the web dashboard to explore your session data:

klexical.github.io/openRS_

  • Dashboard KPIs, session peaks, drive mode breakdown
  • 8 time-series charts (RPM, boost, speed, temps, G-force, fuel, wheel speeds, AWD torque)
  • CAN frame inventory, session events, DID probe results, decode trace
  • Persistent session library in your browser (IndexedDB)

Architecture

System diagram
┌──────────────────────────────────────────────────────────────────────┐
│  Phone UI — Jetpack Compose + Material 3 + Haze (frosted glass)      │
│  ┌──────┬───────┬─────────┬───────┬─────┬──────┬──────┐             │
│  │ DASH │ POWER │ CHASSIS │ TEMPS │ MAP │ DIAG │ MORE │             │
│  └──────┴───────┴─────────┴───────┴─────┴──────┴──────┘             │
│  HorizontalPager swipe · BottomNavBar overlay · Quick Mode Dock      │
│  Compact status bar: MODE pill · ESC · conn dot · ⚙                  │
│  Visibility: Night/Day/Sun brightness lerp (live recompose)          │
├──────────────────────────────────────────────────────────────────────┤
│  UserPrefsStore (StateFlow) — units, thresholds, brightness, BLE    │
├──────────────────────────────────────────────────────────────────────┤
│                  VehicleState (StateFlow)                            │
│    Immutable data class · ~110 fields · genericValues map · RTR      │
│    + DriveState (live recording, peaks, fuel economy)                │
├──────────────────────────────────────────────────────────────────────┤
│              CanDataService (Foreground Service)                     │
│   Decodes CAN → VehicleState → notifies UI                           │
│   Auto-reconnect (Wi-Fi + BLE) · DriveRecorder · CrashReporter       │
│   Auto-record on connect (if enabled)                                │
├──────────────────────┬───────────────────────────────────────────────┤
│  CanDecoder          │  DiagnosticLogger / Exporter / DtcScanner     │
│  22+ HS-CAN decoders │  DriveExportBuilder · DiagnosticReportBuilder │
│  has420Arrived gate  │  Per-ID first/last/Δ tracking + JSON export   │
│  RS_HS.dbc-verified  │  GFM (0x7D2) DTC scan support                 │
├──────────────────────┼───────────────────────────────────────────────┤
│  DriveRecorder       │  FirmwareApi (Wi-Fi REST + BLE AT+FRS)        │
│  Room DB · 1 Hz GPS  │  DriveCommand: settle → poll → auto-correct   │
│  Peak tracking       │  Network.socketFactory pins Wi-Fi route       │
├──────────────────────┴───────────────────────────────────────────────┤
│  PidRegistry — data-driven decode from FORScan catalog (JSON)        │
│  1,149 PIDs · 8 modules · formula evaluator · genericValues output   │
├──────────────────────────────────────────────────────────────────────┤
│  ObdConstants / ObdResponseParser / SlcanParser / IsoTpBuffer        │
│  PCM/BCM/AWD/PSCM/FENG/RSProt response parsers                       │
├──────────────────────────────────────────────────────────────────────┤
│  SlcanConnection — shared connection lifecycle                       │
│  Constructor: transportFactory: () -> SlcanTransport                 │
│  3s SLCAN handshake · firmware probe · ISO-TP reassembly             │
├─────────────────┬─────────────────────┬──────────────────────────────┤
│ WebSocketSlcan  │  TcpSlcanTransport  │  BleSlcanTransport           │
│ Transport       │  raw TCP            │  BLE GATT (FFE0/FFE1/FFE2)   │
│ ws://.80.1/ws   │  tcp://.0.10:35000  │  MTU 247 · auto-reconnect    │
│ MeatPi USB Wi-Fi│  MeatPi Pro Wi-Fi   │  Both adapters               │
├─────────────────┴─────────────────────┴──────────────────────────────┤
│  PCM (0x7E0/30s) · BCM (0x726/30s) · AWD (0x703/60s)                │
│  STFT/LTFT · AWD clutch hydraulics · battery current · spark adv     │
│  GFM (0x7D2) DTC scan · IPC/HVAC scaffolded                          │
├──────────────────────┬───────────────────────────────────────────────┤
│  MeatPi USB (C3)     │  MeatPi Pro (S3)                              │
│  ESP32-C3 · Wi-Fi+BLE│  ESP32-S3 · Wi-Fi+BLE+GPS+MicroSD             │
│  openrs-fw v1.61     │  openrs-fw v1.2                               │
├──────────────────────┴───────────────────────────────────────────────┤
│  HS-CAN 500k         │  MS-CAN 125k (bridged via GWM)                │
│  0x010–0x420 frames  │  Ambient 0x340/0x1A4 (GWM bridged)            │
└──────────────────────┴───────────────────────────────────────────────┘
Key design decisions

Why WebSocket SLCAN instead of ELM327 TCP? ELM327's ATMA command is not fully implemented in WiCAN firmware. WebSocket SLCAN bypasses ELM327 entirely — the app does a manual HTTP 101 Upgrade handshake, sends C / S6 / O (close/500kbps/open), and receives raw SLCAN frames. This delivers the full HS-CAN bus at ~2100 fps vs ~12 fps with polled OBD.

How does TPMS work? Tire pressures are polled from the BCM via Mode 22 (PIDs 0x2813–0x2816) every 30 seconds using the formula (((256*A)+B)/3 + 22/3) * 0.145 PSI. Tire temperatures use a two-step approach: on connect, the app polls sensor IDs from PIDs 0x280F–0x2812 (4-byte ID per tire position). Then every 30s it polls PID 0x280B ("last received TPMS sensor") — a 12-byte multi-frame ISO-TP response containing sensor ID, pressure ((A*256+B)/20 PSI), and temperature (raw − 40 °C). The sensor ID is matched against the stored map to assign the temperature to the correct tire. Per-tire temperature PIDs 0x2823–0x2826 were confirmed unsupported by the BCM (7F 22 31).

How does firmware detection work? After SLCAN initialisation, the app sends OPENRS?\r. openRS_ firmware responds with OPENRS:<version>; stock WiCAN ignores it. Every incoming CAN frame for the first 3 seconds is scanned for the probe response — this time-based window ensures the probe reply is not missed even on high-throughput buses (~1700 fps). After 3 seconds without a response, firmware latches as "WiCAN stock" for the session. MORE tab feature buttons unlock when openRS_ firmware is confirmed.

How does the FORScan PID catalog work? PidRegistry lazy-loads forscan_modules.json at first use and indexes 1,149 PIDs by (ecuResponseId, did) tuple. Each PID entry carries a formula expression string (e.g. "signed(A*256+B)/-512.0"). A custom recursive-descent parser evaluates the formula at runtime using bytes from the OBD response. All four OBD parsers (PCM, AWD, HVAC, IPC) fall through to PidRegistry for unrecognised DIDs, storing results in VehicleState.genericValues. New PIDs can be onboarded via JSON without modifying Kotlin code.

How does the DID prober work? DidProberSection (DIAG tab) sends Mode 22 UDS queries across module-specific DID ranges using sendRawQuery() — a new infrastructure method on both WiCanConnection and MeatPiConnection. Responses are classified as FOUND (0x62 positive), NRC (0x7F negative, NRC code shown), or TIMEOUT. A live progress bar, counters, and expandable result list with raw hex data make it easy to discover new ECU parameters.

How does the diagnostic system work? DiagnosticLogger (singleton) accumulates three layers throughout the session: (1) a per-ID frame inventory with firstRawHex, lastRawHex, a hasChanged flag, and up to 10 periodic raw-hex snapshots per ID sampled every 30 s; (2) a rolling 10 000-entry decode trace; (3) a real-time SLCAN log in standard candump format. On export, DiagnosticExporter bundles all three artefacts into a ZIP via FileProvider, compatible with SavvyCAN, Kayak, and python-can.


Technical Reference

Project structure
web/                                         # Sapphire — post-session analytics dashboard
├── src/
│   ├── components/
│   │   ├── layout/                          # Shell, NavRail, Header
│   │   ├── panels/                          # Dashboard, Trip, Diagnostics, Sessions, Import
│   │   ├── charts/                          # TimeSeriesChart (Recharts)
│   │   └── ui/                              # MetricCard, SectionLabel, DataCell, EmptyState
│   ├── store/                               # Zustand state (sessions, panels, UI)
│   ├── lib/                                 # ZIP import, IndexedDB, formatting
│   ├── styles/                              # Design tokens (openRS_ palette)
│   └── types/                               # Session, trip, diagnostic types
├── package.json                             # Vite + React 19 + Tailwind 4 + Recharts + Zustand
└── vite.config.ts                           # base: '/openRS_/'

android/
├── app/src/main/
│   ├── assets/pids/
│   │   ├── forscan_modules.json          # Bundled FORScan catalog (1,149 PIDs, 8 modules)
│   │   └── combined_catalog.json         # Merged PID catalog
│   ├── java/com/openrs/dash/
│   │   ├── OpenRSDashApp.kt              # App singleton + driveDb/Recorder/State
│   │   ├── can/
│   │   │   ├── AdapterState.kt           # Disconnected/Connecting/Connected/Idle/Error
│   │   │   ├── BleDeviceScanner.kt       # BLE discovery filtered to 0xFFE0
│   │   │   ├── BleSlcanTransport.kt      # BLE GATT transport (FFE0/FFE1/FFE2 + MTU 247)
│   │   │   ├── CanDecoder.kt             # 22+ CAN decoders + has420Arrived gate
│   │   │   ├── DriveCommand.kt           # Shared drive mode command flow (settle/poll/auto-correct)
│   │   │   ├── FirmwareApi.kt            # WiFiFirmwareApi (REST) + BleFirmwareApi (AT+FRS)
│   │   │   ├── IsoTpBuffer.kt            # ISO-TP SF/FF/CF reassembly
│   │   │   ├── ObdConstants.kt           # Shared OBD query strings + CAN IDs + timing
│   │   │   ├── ObdResponseParser.kt      # Mode 22 dispatch (PCM/BCM/AWD/PSCM/FENG/RSProt)
│   │   │   ├── PidRegistry.kt            # Data-driven catalog decoder (formula evaluator)
│   │   │   ├── SlcanConnection.kt        # Shared connection lifecycle (transportFactory)
│   │   │   ├── SlcanParser.kt            # Shared SLCAN frame parser
│   │   │   ├── SlcanTransport.kt         # Transport interface (open/read/write/close)
│   │   │   ├── TcpSlcanTransport.kt      # MeatPi Pro raw TCP transport
│   │   │   └── WebSocketSlcanTransport.kt # MeatPi USB WebSocket transport
│   │   ├── data/
│   │   │   ├── DashLayout.kt             # Custom dashboard cell grid
│   │   │   ├── DriveDatabase.kt          # Room DB v3: drives + drive points
│   │   │   ├── DriveState.kt             # Live drive state (recording/peaks/fuel)
│   │   │   ├── DtcModuleSpec.kt          # ECU descriptor for DTC scan
│   │   │   ├── DtcResult.kt              # DTC + status enum
│   │   │   ├── ForscanCatalog.kt         # JSON catalog data classes
│   │   │   ├── FuelEconomy.kt            # Real-time fuel economy (60s rolling window)
│   │   │   ├── PerformanceTimer.kt       # 0-60 / 0-100 timer state machine
│   │   │   ├── SessionDatabase.kt        # Room DB for session snapshots
│   │   │   ├── TripState.kt              # Peak event types
│   │   │   ├── VehicleState.kt           # Immutable state (~110 fields, genericValues)
│   │   │   └── WeatherData.kt            # OpenWeatherMap snapshot
│   │   ├── diagnostics/
│   │   │   ├── CrashReporter.kt          # UncaughtExceptionHandler + persistence
│   │   │   ├── CrashTelemetryBuffer.kt   # 100-snapshot VehicleState ring buffer
│   │   │   ├── DiagnosticExporter.kt     # ZIP orchestrator + share intents
│   │   │   ├── DiagnosticLogger.kt       # Session-scoped collector + SLCAN log
│   │   │   ├── DiagnosticReportBuilder.kt # Summary text + JSON detail (JSONObject)
│   │   │   ├── DriveExportBuilder.kt     # GPX / CSV / drive summary builders
│   │   │   ├── DtcDatabase.kt            # Bundled 873-code Ford DTC lookup
│   │   │   └── DtcScanner.kt             # DTC scan/clear (PCM/BCM/ABS/AWD/PSCM/GFM)
│   │   ├── service/
│   │   │   ├── CanDataService.kt         # Foreground service + auto-reconnect (Wi-Fi+BLE)
│   │   │   ├── DriveRecorder.kt          # Room-backed 1 Hz GPS + telemetry recorder
│   │   │   ├── HudOverlayService.kt      # Floating HUD overlay
│   │   │   └── WeatherRepository.kt      # OpenWeatherMap integration
│   │   ├── update/
│   │   │   ├── AppVersion.kt             # Semver + RC parsing (incl. APK filename)
│   │   │   ├── UpdateChecker.kt          # GitHub Releases API client
│   │   │   ├── UpdateManager.kt          # Check/download/install orchestrator
│   │   │   └── UpdateState.kt            # Sealed state class
│   │   └── ui/
│   │       ├── MainActivity.kt           # 7-tab Compose host (HorizontalPager + BottomNavBar)
│   │       ├── DashPage.kt               # DASH tab
│   │       ├── PowerPage.kt              # POWER tab (incl. STFT/LTFT)
│   │       ├── ChassisPage.kt            # CHASSIS tab (incl. clutch hydraulics)
│   │       ├── TempsPage.kt              # TEMPS tab
│   │       ├── DiagPage.kt               # DIAG tab
│   │       ├── MorePage.kt               # MORE tab
│   │       ├── CustomDashPage.kt         # Custom dashboard
│   │       ├── BottomNavBar.kt           # Frosted-glass nav (Haze + spring indicator)
│   │       ├── DriveModeDock.kt          # Quick Mode Dock dropdown
│   │       ├── PidBrowserSection.kt      # PID browser composable
│   │       ├── DidProberSection.kt       # DID prober composable
│   │       ├── BleDevicePickerDialog.kt  # BLE device picker (RSSI + permissions)
│   │       ├── SettingsSheet.kt          # Settings drawer (units/visibility/adapter)
│   │       ├── WhatsNewDialog.kt         # Version changelog dialog
│   │       ├── Theme.kt                  # Brightness-scaled color tokens
│   │       ├── DesignTokens.kt           # Spacing/shape tokens
│   │       ├── Components.kt             # HeroCard/DataCell/BarCard/TireCard/AfrCard
│   │       ├── AppSettings.kt            # SharedPreferences wrapper
│   │       ├── UserPrefs.kt              # Observable prefs + UserPrefsStore
│   │       ├── trip/
│   │       │   ├── DrivePage.kt          # MAP tab (live + history)
│   │       │   └── DriveMap.kt           # Google Maps Compose wrapper (6 color modes)
│   │       └── anim/
│   │           ├── CarDiagram.kt         # Focus RS wireframe (TPMS)
│   │           ├── EdgeShiftLight.kt     # Peripheral shift light overlay
│   │           ├── GForcePlot.kt         # 2D scatter w/ trail + peak labels
│   │           ├── GlowModifiers.kt      # cardGlow + bloomGlow modifiers
│   │           ├── InteractionModifiers.kt # pressClick (clickable + scale)
│   │           ├── RingBuffer.kt         # Generic fixed-capacity ring buffer
│   │           ├── ShiftLightBar.kt      # Horizontal shift LED indicator
│   │           ├── Sparkline.kt          # Inline trend chart
│   │           └── StaggeredEntrance.kt  # Staggered fade+slide-up
│   └── res/
│       ├── font/                          # Embedded fonts
│       │   ├── orbitron_regular.ttf      # Hero gauge values
│       │   ├── orbitron_bold.ttf
│       │   ├── jetbrains_mono_regular.ttf # Secondary numeric readouts
│       │   ├── jetbrains_mono_bold.ttf
│       │   ├── share_tech_mono.ttf       # Raw data / diagnostics
│       │   ├── barlow_condensed_regular.ttf # UI labels
│       │   ├── barlow_condensed_medium.ttf
│       │   ├── barlow_condensed_semibold.ttf
│       │   └── barlow_condensed_bold.ttf
│       ├── raw/dtc_database.json          # Bundled 873-code Ford DTC lookup
│       ├── values/strings.xml
│       ├── values/themes.xml
│       ├── xml/file_paths.xml            # FileProvider path config
│       └── mipmap-*/ic_launcher*.png     # App icon (all densities)
├── browser-emulator/
│   └── index.html                        # Standalone browser emulator
├── docs/
│   ├── images/focus-rs-a4.png             # Focus RS wireframe source (TPMS UI)
│   ├── hardware-setup.md
│   ├── firmware-update.md
│   ├── pid-reference.md
│   └── signing-setup.md
├── scripts/
│   └── gen_forscan_catalog.py            # Parses FORScan CSV export → forscan_modules.json
└── README.md
Brand tokens
Token Hex Usage
Nitrous Blue #0091EA Accent colour — gauges, highlights, active states, "RS" in logo
Frost White #E8F4FF Primary text — labels, readouts, "open" and "_" in logo
Bg #05070A Background
Surface #0A0D12 Cards, tab bar
Surface 2 #0F141C Inset cards, hero RPM gauge
Surface 3 #141B26 Elevated surfaces
Dim #547A96 Muted text (WCAG AA ≥ 4.5:1)
Mid #7A9AB8 Medium emphasis text
Border #162030 Card/section borders
Ok #00FF88 Neon green — good/ready
Warn #FFCC00 Gold — attention/warm
Orange #FF4D00 Orange-red — hot/aggressive

Fonts (offline-embedded):

  • Orbitron — hero gauge values (RPM, speed, boost)
  • JetBrains Mono — secondary numeric readouts
  • Share Tech Mono — raw data values and diagnostic output
  • Barlow Condensed — all UI labels, section headers, and button text

Full PID Reference

Complete decode formulas, byte-level breakdowns, and all Mode 22 PIDs: android/docs/pid-reference.md


Roadmap

Planned

  • Phase 8.6 — Tier 2 PIDs (Apr 2026 research): MAP/baro/engine load, oil pressure, PTU oil temp, panel illumination, COBB-style WG canister pressure + knock counters (v2.3.x)
  • Phase 9 — Track day intelligence: lap timer with geofence, track map overlay enhancements, drive comparison enhancements (v2.4.x)
  • Phase 10 — Hardware expansion: MeatPi Pro on-board GPS integration, MS-CAN second adapter (v2.5.x)
  • Phase 11 — High-frequency telemetry: UDS Fast Rate Session via DDDI 0x2C (~100 Hz) (v3.x)

Future / Exploratory

  • Android Auto — official AndroidX Car App, unofficial aauto-sdk, or hybrid (see android/docs/android-auto-custom-ui-research.md)
  • Data streaming to external apps (RaceChrono, Harry's LapTimer) via local broadcast or content provider
  • Video overlay export — combine trip data with dashcam footage

Full vision: See Feature Roadmap for 35+ planned features beyond the current release phases.

Completed phases
  • Phase 1 — CAN sniffing + basic OBD (v1.0)
  • Phase 2 — Hybrid ATMA+OBD (v2.0)
  • Phase 2.5 — TPMS+, AFR, ETC/TIP/WGDC, VCT, multi-ECU polling
  • Phase 2.6 — Nitrous Blue/Frost White theme, openRS_ branding, live browser emulator
  • Phase 2.7 — WebSocket SLCAN rewrite (~2100 fps), user settings, diagnostics export, firmware detection (fw-v1.1.0)
  • Phase 2.8 — DBC-verified signal corrections, BCM/AWD polling, IAT, ambient, wheel speeds, SLCAN raw log + per-ID sampling (v1.1.1–v1.1.5)
  • Phase 2.9 — TPMS formula fix, firmware detection timing fix, app version in logs (v1.1.6)
  • Phase 3 — Full UI redesign: 6-tab layout, new fonts, new CAN signals (steering, yaw, brake), PCM Mode 22 polling, updated RTR thresholds (v1.2.0)
  • Phase 4 — Trip recording: GPS trip page with OSM map, weather overlay, peak markers, live HUD, trip summary (v2.1.0)
  • Phase 5 — UI architecture split, per-tab composables, share trip export (v2.2.0)
  • Phase 6 — DTC scanning: 873-code Ford DTC database, full-module scan + clear via UDS 0x19/0x14 (v2.2.1)
  • Phase 7 — Data export + MeatPi Pro: trip ZIP (GPX/CSV/TXT), diagnostics ZIP, raw TCP SLCAN adapter support (v2.2.1)
  • Phase 7.5 — Sensor data + polish: GPS permission fix, Module Status/LC/ASS live OBD, full diagnostic export (~24 new fields), SLCAN OBD frame capture, code review fixes (v2.2.3)
  • Phase 8.0 — Car test fixes + signal expansion: ESC decode fix, throttle fallback, battery voltage OBD, crash telemetry ring buffer, passive odometer (CAN 0x360), tap-to-change drive mode/ESC, RS MK3 theme colour correction, MeatPi default fix, free CAN signal extraction (vertical G, launch control, engine status, ignition status, ESC Launch mode), full repo audit hardening (v2.2.4)
  • Phase 8.1 — FORScan PID catalog (1,149 PIDs, 8 modules), data-driven decode (PidRegistry + formula evaluator), DID prober, PID browser, AWD expansion (clutch temps L/R, trans oil temp, req torques, demanded pressure, pump current), per-cylinder knock correction (KR C1–C4), HVAC/IPC scaffolding, warning lamp banner (v2.2.5)
  • Phase 8.2 — Drive mode reliability (pre-flight logging, auto-correction on overshoot), 0-60/0-100 performance timer, real-time fuel economy (idle/instant/average/DTE), clutch pedal (CAN 0x138), passive VIN decode (CAN 0x40A), wheel rotation counts (CAN 0x1E0), 3-tier TPMS thresholds, TPMS + temps in trip CSV exports, peripheral edge shift light, crash telemetry ring buffer (v2.2.5)
  • Phase 8.3 — MAP tab + drive recorder: Google Maps integration with 6 colour modes (speed/mode/boost/throttle/lat-G/temp), Room-backed DriveRecorder with 1 Hz GPS + telemetry, drive history with date grouping, per-drive GPX/CSV/summary export, weather card, hoisted camera state, peak markers (v2.2.6 rc.1–rc.6)
  • Phase 8.4 — BLE GATT transport: BleSlcanTransport (FFE0/FFE1/FFE2, MTU 247, auto-reconnect), BleDeviceScanner, in-app device picker with RSSI bars, adapter naming refactor (MeatPi USB / Pro × Wi-Fi / BLE), Wi-Fi coexistence banner, BleFirmwareApi (AT+FRS=), Network.socketFactory Wi-Fi route pinning (v2.2.6 rc.7)
  • Phase 8.5 — Drive mode cold-start gate (has420Arrived), corrective auto-loop in MorePage, hybrid scroll-then-wait firmware (USB v1.61 / Pro v1.2 with AT+FRS BLE channel), drive mode reliability under reconnect (v2.2.6 rc.7)
  • Phase 8.6 — Tier 1 PIDs (Apr 2026 research): STFT/LTFT, AWD clutch actuator current L/R, AWD hydraulic pressure L/R, battery current; new CAN decoders for 0x305 mode toggle button, 0x260 ASS/ESC defeat buttons, 0x225 LC engaged, 0x070 suspension button; spark advance + charging voltage desired; GFM (0x7D2) DTC scanning; critical mergeObdState() allowlist fix (v2.2.6 rc.8)
  • Phase 8.7 — UI overhaul: bottom nav bar (Haze frosted glass + spring indicator), compact status bar (78dp → 34dp), Quick Mode Dock, G-Force plot redesign, visibility presets (Night/Day/Sun + brightness slider with live lerp recompose), hybrid glow system (ambient cardGlow + accent glow gates), AggressiveNum typography, 0.5dp card borders (v2.2.6 rc.9)
  • Phase 8.8 — In-app updater: GitHub Releases API client, stable + beta channels, Network.socketFactory to bypass adapter Wi-Fi for download, rolling-RC tag bug fix (UpdateChecker reads version from APK filename, not git tag), CI build provenance attestation via SLSA, lint advisory in CI, 344 unit tests across 11 files, Sapphire V2 web dashboard rewrite (129 vitest tests) (v2.2.6 stable)

Contributing

Pull requests welcome. See CONTRIBUTING.md for guidelines.

If you have a Focus RS and FORScan/OBDLink, we'd love help verifying:

  • Brake pressure bar calibration (raw ADC 0–4095 from 0x252, need known-pressure reference)
  • HVAC / IPC ECU address and DID confirmation (use built-in DID prober)
  • MS-CAN parameters (requires second adapter)

License

MIT — see LICENSE for details.


Acknowledgments

  • FORScan — Ford enhanced PID discovery
  • MeatPi — WiCAN hardware
  • Focus RS community — Testing and feedback