Files
mg4/README.md
T
nilsandClaude Opus 5.5 4b858e3c35 README for Nils' MG4
Rewritten for this fork: the car server, InfluxDB and Grafana setup,
pairing by QR code, the app's tabs, voice-key opening and full screen,
installing the platform-signed car build, the config file under the new
package ID, building with Nix, and credits. Adds a current screenshot
(2.8.2 at 1920x720, emulator).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-18 00:08:14 +02:00

215 lines
8.8 KiB
Markdown

# Nils' MG4
An Android Automotive app for the **MG4 (SAIC eh32)** head unit, plus a small server: the car
sends its telemetry to my own server at car.k8s.stinnesbeck.com, which stores it in InfluxDB 2,
forwards live data to [A Better Route Planner](https://abetterrouteplanner.com) and feeds the
Grafana dashboards.
No OBD dongle and no phone in the loop: the app runs on the head unit and reads the car's own
APIs.
- Repository: https://git.stinnesbeck.com/nils/mg4
- Fork of [fatihdonmezdev/FatihsMG4](https://github.com/fatihdonmezdev/FatihsMG4), itself built on malys' EVABRPUploader and Leon Kernan's `ABRP_Uploader`.
- What changed and what is still to do: [`SPECS.md`](SPECS.md).
> ⚠️ **No warranty, no liability.** This software is provided "as is" and runs on a
> **vehicle**. Installing it is your decision and your risk; see
> [`DISCLAIMER.md`](DISCLAIMER.md). Not affiliated with SAIC, MG Motor or Iternio.
> MG and MG4 are third-party marks used only to identify compatibility.
> **Licence.** The original `ABRP_Uploader` carries no licence, which limits what may legally
> be published. Read [`LICENSE.md`](LICENSE.md) before redistributing anything.
---
## Contents
- [Screenshot](#screenshot)
- [How it fits together](#how-it-fits-together)
- [The app](#the-app)
- [Install on the car](#install-on-the-car)
- [Setup](#setup)
- [Building](#building)
- [Server and dashboards](#server-and-dashboards)
- [Project layout](#project-layout)
- [Project documents](#project-documents)
- [Credits](#credits)
## Screenshot
Version 2.8.2 on the head unit's 1920x720 screen, in full screen (Android 9 emulator; no
vehicle there, so every value shows as a dash).
<p align="center">
<img src="screenshots/nilsmg4-2.8.2-overview.png" width="900" alt="Overview tab: battery, range, range estimates, power and speed tiles above the energy and tyre-pressure cards">
</p>
## How it fits together
```
car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go) ──▶ ABRP
──daily chunks─────▶ │
──charging sessions▶ ▼
InfluxDB 2 ◀── Grafana
```
The ABRP credentials live only on the server, never in the APK. The car authenticates with a
token of its own, obtained by pairing with a phone (below).
## The app
| Tab | What it shows |
|---|---|
| **Overview** | Battery, range, range estimates at current power and at the 5-minute average, power, speed; odometer, climate setpoint and tyre pressures |
| **Consumption** | Daily and period consumption, efficiency |
| **Charging** | Charging sessions and curves, cost in euro |
| **Upload** | Live telemetry on/off, full screen, voice key, upload cadence |
| **Server** | Server URL, pairing by QR code, ingest token, installation ID, config import |
| **Wi-Fi** | Joins a network the head unit already knows (e.g. the phone hotspot) |
| **Logs** | Upload log and the last steering-wheel key reports |
- **Opening the app:** the MG4 launcher does not list third-party apps. **Double-press the
voice key** on the steering wheel and the app comes to the front (switch on the Upload tab,
on by default).
- **Full screen:** hides SAIC's top and bottom bars while the app is open; a swipe from the
edge shows them briefly (Upload tab, on by default).
- **Live telemetry:** every 15 s while driving or charging, much less often while parked. A
value the car cannot read is left out, never sent as zero, so ABRP is never told the battery
is empty because a read failed.
- **Read-only:** the app never writes a vehicle setting.
Signals the MG4 does not publish (battery, cabin and outside temperature, pack energy and
capacity) are listed in [`SPECS.md`](SPECS.md#signals-the-car-does-not-provide).
## Install on the car
The car build (`NilsMG4-unstable-<version>.apk`) runs with the system UID and is signed with the
AOSP test platform key that the MG4 head unit uses. It installs next to the old FatihsMG4 app
(package `com.stinnesbeck.nilsmg4.unstable`), not over it.
Without adb, through the head unit's hidden file manager:
1. Copy the APK to a USB stick and plug it in.
2. Open **Bluetooth Settings** and select the car name.
3. Long-press the **comma** key until the keyboard settings appear.
4. **Android Keyboard Settings → Languages**, then search for `file` in the box at the top.
5. That opens the Files app: open the USB stick, tap the APK and install.
With adb:
```sh
adb push build/car/NilsMG4-unstable-<version>.apk /data/local/tmp/update.apk
adb shell "pm install -r -f -d -t /data/local/tmp/update.apk"
```
> ⚠️ Do this **parked**, and only with an APK you trust. See [DISCLAIMER.md](DISCLAIMER.md).
Self-updating (OTA) is switched off: the updater points at an unresolvable host until it is
pointed at this repository's releases (see `SPECS.md`).
## Setup
1. Open the app (voice key double press, or "Open" right after installing).
2. On the **Server** tab the URL is already `https://car.k8s.stinnesbeck.com`.
3. Tap **Pair with phone**. Scan the QR code with a phone (or open the address shown and enter
the code), and approve with the server's approval password. The car receives its token.
4. On the **Upload** tab, turn live telemetry on. The service starts with the car from then on.
### Config file
Instead of typing, put the settings in a text file and tap **Import file** on the Server tab.
The head unit has no file picker, so copy the file to
```
Android/data/com.stinnesbeck.nilsmg4.unstable/files/
```
on the USB stick or the internal storage. The app scans that folder on every mounted volume and
takes the first file that parses. Format, one `key = value` per line, `#` for comments; keys left
out keep their current value:
```
server_url = https://car.k8s.stinnesbeck.com
token = the server's INGEST_TOKEN (not needed after pairing)
installation_id = keep the old one when reinstalling, so history stays together
interval_sec = 60 # 15 / 30 / 60 / 120 / 300
boost_low_soc = true
low_soc_percent = 20
```
The file is read once and not kept; credentials live only in the app's encrypted store.
### Power
The app holds **no wake lock** and schedules **no alarms**, so it cannot wake a sleeping head
unit and costs nothing while the car is off.
## Building
With Nix:
```sh
nix develop # JDK 21, Android SDK, Go
./gradlew assembleStableDebug # debug APK for a phone or tablet
build-car # car APK, signed with the AOSP test platform key
```
`build-car` writes `build/car/NilsMG4-unstable-<version>.apk` and a `.sha256` next to it, and
checks the signing certificate before it finishes.
Tests and lint:
```sh
./gradlew testStableDebugUnitTest testUnstableDebugUnitTest lintStableDebug
```
[`mise`](https://mise.jdx.dev) tasks from upstream (`mise run check`, `mise run build`) still
work for the plain Gradle builds.
## Server and dashboards
- **Server** ([`server/`](server/README.md)): Go, API only. Live telemetry, daily consumption
chunks, charging sessions, pairing; forwards live data to ABRP. Image at
`git.stinnesbeck.com/nils/mg4-server`.
- **Kubernetes** ([`k8s/`](k8s/README.md)): plain manifests for the server, InfluxDB 2 and
Grafana, applied by hand with `kubectl apply -k k8s/`. Secrets go in the gitignored
`k8s/secrets/secrets.yaml`, from the template next to it.
- **Grafana dashboards** (`k8s/grafana/dashboards/`): Live, Consumption and Charging, provisioned
with the InfluxDB datasource.
## Project layout
```
app/src/main app: upload service, server client, pairing, UI
app/src/stable no-op update hook (no updater in the APK)
app/src/unstable car build: system UID, updater (currently pointed nowhere)
app/src/debug VHAL probe tools, absent from release builds
app/src/test JVM unit tests
EVHardware shared vehicle reads per firmware generation
server/ Go server
k8s/ Kubernetes manifests and Grafana provisioning
flake.nix dev shell and build-car
```
## Project documents
| Document | What it covers |
|---|---|
| [SPECS.md](SPECS.md) | Journal of this fork: architecture, status, to-dos, done log |
| [FIRMWARE.md](FIRMWARE.md) | MG4 firmware generations and how the app reads each |
| [AGENTS.md](AGENTS.md) | Context for AI agents working in this repository |
| [DESIGN.md](DESIGN.md) | The EVSuite design system (the app overrides its palette) |
| [SECURITY.md](SECURITY.md) | Threat model |
| [DISCLAIMER.md](DISCLAIMER.md) | Vehicle-safety disclaimer; read before installing |
| [CHANGELOG.md](CHANGELOG.md) | Upstream release history |
| [LICENSE.md](LICENSE.md) | Licence status |
## Credits
- **fatihdonmezdev**: [FatihsMG4](https://github.com/fatihdonmezdev/FatihsMG4), the app this fork
starts from.
- **malys**: EVABRPUploader.
- **Leon Kernan**: the original `ABRP_Uploader`, the car-API approach and the first working
uploader.
- ABRP telemetry API: [Iternio](https://documenter.getpostman.com/view/7396339/SWTK5a8w).