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
2026-10-09 17:31:08 +03:00
2026-09-30 13:46:51 +03:00
2026-10-18 00:08:14 +02:00
2026-10-17 22:58:28 +02:00
2026-10-18 00:08:14 +02:00
2026-09-29 14:32:25 +03:00

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 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.

⚠️ 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. 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 before redistributing anything.


Contents

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).

Overview tab: battery, range, range estimates, power and speed tiles above the energy and tyre-pressure cards

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.

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:

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.

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:

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:

./gradlew testStableDebugUnitTest testUnstableDebugUnitTest lintStableDebug

mise tasks from upstream (mise run check, mise run build) still work for the plain Gradle builds.

Server and dashboards

  • Server (server/): 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/): 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 Journal of this fork: architecture, status, to-dos, done log
FIRMWARE.md MG4 firmware generations and how the app reads each
AGENTS.md Context for AI agents working in this repository
DESIGN.md The EVSuite design system (the app overrides its palette)
SECURITY.md Threat model
DISCLAIMER.md Vehicle-safety disclaimer; read before installing
CHANGELOG.md Upstream release history
LICENSE.md Licence status

Credits

  • 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.
S
Description
No description provided
Readme MIT
4.1 MiB
Languages
Kotlin 64.2%
Java 27.3%
Go 7.6%
Nix 0.8%
Shell 0.1%