Android Auto and Carplay Cluster Integration for Porsche mh2p/PCM5

Bridge Android Auto and Carplay navigation from Porsche PCM5 / VW / Audi MH2P to instrument cluster displays

Android Auto

Overview

Displays Android Auto and Carplay navigation instructions from Porsche PCM5 / VW / Audi MH2P to instrument cluster displays to instrument cluster displays. Translates Android Auto DSI events into BAP (Bedien-und Anzeigeprotokoll) messages for real-time turn-by-turn navigation on your cluster.

βœ… Status: v0034_beta2_candidate_90d0b76

Why this project?

Native navigation monopolizes the instrument cluster, this code will change that!. This integration:

Features

πŸ—ΊοΈ

Full Navigation Support

Turn arrows, distance, road names, roundabouts, highway exits - everything from Android Auto displayed on your cluster.

⚑

Real-Time Updates

60 FPS updates with automatic heartbeat to override native navigation. Seamless transitions and no flickering.

🌍

Intelligent Unit Detection

Automatic km/mi and LHD/RHD detection via DSI services with JSON country fallback. Supports 60+ countries.

πŸ’Ύ

Automatic Storage Mounting

Auto-remounts SD/USB storage as writable via ASI services for external logging. No manual intervention needed.

πŸ”§

JSON Configuration

Runtime-configurable logging, heartbeat, units, and more via embedded JSON config. No recompilation required.

πŸ“Š

Comprehensive Logging

Detailed file logging with configurable size limits, external storage support, and optional message hashing for privacy.

Download

v1_main - Beta candidate LATEST

Testing compatiblity across Porsche Cayenne, Macan, Panamera and 911

Installation

Based on MH2p Mod Kit

⚠️ Warning: This modifies java files on you PCM5/Mh2p unit. Only proceed if you understand the risks.

Prerequisites

Installation

Uninstall

Build

Edit build script with paths in your environment and run: 
recompile.sh
or
recompile.bat

Deploy and test

The easiest way is to just scp it to the target if you enbaled ssh access

Architecture

ClusterIntegration is a single Java class (Java 1.4) compatible that bridges the Android Auto DSI and Carplay RGI event stream to the Porsche instrument cluster BAP navigation protocol.

Data Flow

Android Auto (Phone)
        ↓
    DSI Events (Navigation, Media)
        ↓
AndroidAutoClusterIntegration
    β”œβ”€ navFocusRequestNotification   β†’ start/stop heartbeat, set RGType
    β”œβ”€ updateNavigationNextTurnEvent β†’ convertToBAPManeuver β†’ updateManeuverDescriptor
    β”œβ”€ updateNavigationNextTurnDistance β†’ rate-limit β†’ unit convert β†’ updateDistanceToNextManeuver
    └─ Heartbeat Timer (every 2s)   β†’ resend cached maneuver+distance
        ↓
CombiBAPServiceNavi (OSGi)
        ↓
Instrument Cluster

Service Injection

All platform services are injected via setters after construction:

ServicePurpose
CombiBAPServiceNaviBAP cluster output β€” maneuver, distance, RGStatus, destination
ISysServicesUnit system detection, car clock, car type/variant detection
ICarCoreServicesDrive-side detection via exteriorLight().leftHandTraffic()
StorageMountHandlerRemounts USB/SD R/W for external log writing
ICarStatisticsServiceInjected but not currently used in active code

AA Event β†’ BAP Maneuver Translation

Maps Android Auto eventCode (1–19) + turnSide + angle + num to a BAP maneuver descriptor.

Direction is a 0–255 byte (0=straight, 64=left/90Β°, 128=back/180Β°, 192=right/270Β°).

Event(s)Translation
event=3–5 (turns)TURN, direction from angle or fallback to turnSide
event=6 (U-turn)UTURN
event=7 (on-ramp)TURN_ON_MAINROAD, direction from turnSide
event=8/10 (off-ramp/merge)EXIT_LEFT or EXIT_RIGHT based on turnSide; side=0 β†’ FOLLOW_STREET
event=9 (fork)FORK_2
event=14 (STRAIGHT/KEEP)num==1 = Waze lane-keep β†’ FOLLOW_STREET; num==0 or num>1 = motorway exit β†’ EXIT_LEFT/RIGHT
event=11–13 (roundabout)ROUNDABOUT_TRS_LEFT/RIGHT.
RHD angle correction: (540βˆ’angle)%360;
LHD: (angle+180)%360.
T-junction override: 180Β° β†’ 0Β° when exit ≀ 2
event=16/17 (ferry)FERRY
event=19 (destination preview)Sets flag, falls through as FOLLOW_STREET. DESTINATION symbol shown only on event=0 valid=2

Distance Update Pipeline

  1. Throttle check β€” minimum ms between sends per proximity zone (veryFar=2000ms down to now=100ms). In "always" bargraph mode, bargraph-only updates bypass the throttle.
  2. Maneuver change detection β€” key = road|eventCode|turnSide. New maneuver resets baseline distance and bypasses throttle for first update.
  3. Distance threshold β€” minimum change required per zone (100m at veryFar down to 5m at now) before text updates. Bargraph always updates for smooth fill.
  4. Unit conversion β€” metric: m below 100m, tenths of km to 19.9km. Imperial: yards below 161m, tenths of miles to 9.9mi.
  5. Bargraph β€” (1 βˆ’ distance/maneuverInitialDistance) Γ— 100, clamped 0–100.
  6. Roundabout traversal β€” force distance/bargraph to 0 during CONTINUE events to suppress erratic display inside roundabout.

Heartbeat

A daemon timer fires every heartbeatInterval ms (default 2000ms) while navigation is active.
It resends the last cached maneuver, distance, bargraph, and destination data to continuously override the cluster's native navigation display.

Unit & Drive-Side Detection (3-tier each)

PriorityUnitsDrive side
1 (highest)forceImperial config flagforceRHD config flag
2ISysServices.units().distance().setting() (1=km, 2=mi)ICarCoreServices.exteriorLight().leftHandTraffic()
3JSON countries table via locale/system propertiesJSON countries table rhd field
4 (default)MetricLHD

Bargraph Auto Mode β€” Car Variant Detection

When bargraphMode="auto", the car type is queried at runtime via sysServices.config().carType():

CarcarClassgenerationResolved mode
Cayenne E353"always"
911 99268"always"
Panamera G262"always"
Macan41"distance"
Unknownanyany"always" (safe default)

Explicit JSON values always override auto-detection.

Roundabout Latching

Android Auto sends EXIT (event=13) first, then CONTINUE (event=12) Γ— N through the roundabout.
Latching caches the exit direction on event=11/13 and reuses it for all subsequent event=12 updates, preventing the arrow from flickering.
Cleared on any non-roundabout event. Configurable via enableRoundaboutLatching.

Logging

Two parallel log files: plain (full road names) and hashed (road names, music metadata replaced with [HASH_nnn] for privacy).
Each entry has a dual timestamp: QNX boot time + real car clock from ISysServices.clock().localTime().
When enableExternalLogging=true, logs are written to USB/SD if writable, with automatic fallback to /tmp.

Configuration

The config file androidauto_cluster_config.json is embedded in the JAR.
To override without reflashing, place the file on a USB stick or SD card β€” the app checks these paths first:

/fs/usb0_0/androidauto_cluster_config.json
/fs/usb1_0/androidauto_cluster_config.json
/fs/sda0/androidauto_cluster_config.json
/fs/sdb0/androidauto_cluster_config.json

Logging

KeyTypeDefaultDescription
enableFileLoggingbooltrueMaster switch for all file logging.
enableFileHashingboolfalseWrite a second log with road names/music hashed for privacy. Requires enableFileLogging.
enableExternalLoggingboolfalseWrite logs to USB/SD if present; falls back to logFilePath.
logFilePathstring/tmp/androidauto_cluster.logPlain log file path.
hashedLogFilePathstring/tmp/androidauto_cluster_hashed.logHashed log file path.
logFileSizeint (MB)50Max file size before deletion and restart. Range: 1–100.

Features

KeyTypeDefaultDescription
enableRoundaboutLatchingbooltrueCache and hold roundabout exit direction across ENTER→CONTINUE→EXIT events to prevent arrow flickering.
enableHeartbeatbooltruePeriodically resend last maneuver+distance to prevent native nav from overwriting AA data.
heartbeatIntervalint (ms)2000Heartbeat period. Range: 100–10000.
maneuverStateMaskint0Bitmask for updateManeuverState calls.
0 = disabled.
bit0=state1 FOLLOW (>500m),
bit1=state2 PREPARE (200–500m),
bit2=state3 DISTANCE (50–200m),
bit3=state4 CALL_FOR_ACTION (<50m). Falls back to nearest lower enabled state.
Example: 15 (0b1111) = all states.

Units and Display

KeyTypeDefaultDescription
forceImperialboolfalseAlways use miles/yards, ignoring car settings. For testing on metric testbenches.
forceRHDboolfalseAlways treat vehicle as right-hand drive, overriding all detection.
metricUnitThresholdint (m)100Below this, display switches from km to m. Range: 50–5000.
imperialUnitThresholdint (m)161Below this (~0.1 mi), display switches from miles to yards. Range: 50–5000.
destinationDisplayDurationint (ms)5000How long to hold the arrival maneuver on screen before clearing. Range: 0–10000.

Bargraph

KeyTypeDefaultDescription
bargraphModestring"auto""auto" β€” detect car variant at runtime
(Cayenne E3 / 911 992 / Panamera G2 β†’ "always", Macan β†’ "distance").
"always" β€” send both text and bargraph.
"distance" β€” text only.
"dynamic" β€” text when far, bargraph only within dynamicBargraphDistance.
Explicit JSON values override auto-detection.
dynamicBargraphDistanceint (m)100In "dynamic" mode: distance below which bargraph replaces text. Range: 10–500.
dynamicBargraphPercentint (%)50In "dynamic" mode: for short maneuvers (<2Γ— threshold), switch point as % of initial distance. Range: 10–90.

Dynamic Thresholds

Controls how aggressively distance updates are sent per proximity zone. Each zone has a boundary (upper edge in metres), distance threshold (minimum change to trigger a send), and rateLimit (minimum ms between sends). now has no boundary β€” catch-all below veryClose.boundary.

ZoneDefault boundaryDefault distanceDefault rateLimit
veryFar5000 m100 m2000 ms
far1000 m50 m500 ms
approaching500 m25 m250 ms
near200 m15 m200 ms
close100 m15 m150 ms
veryClose50 m10 m120 ms
nowβ€”5 m100 ms
"dynamicThresholds": {
  "veryFar":    { "boundary": 5000, "distance": 100, "rateLimit": 2000 },
  "far":        { "boundary": 1000, "distance": 50,  "rateLimit": 500  },
  "approaching":{ "boundary": 500,  "distance": 25,  "rateLimit": 250  },
  "near":       { "boundary": 200,  "distance": 15,  "rateLimit": 200  },
  "close":      { "boundary": 100,  "distance": 15,  "rateLimit": 150  },
  "veryClose":  { "boundary": 50,   "distance": 10,  "rateLimit": 120  },
  "now":        {                    "distance": 5,   "rateLimit": 100  }
}

Countries

List of country overrides for when locale cannot be detected from platform services. Each entry determines drive side (RHD) and unit system. Countries not listed default to LHD metric. forceImperial and forceRHD override this table entirely.

{ "code": "GB", "name": "United Kingdom", "rhd": true, "imperial": true }
{ "code": "US", "name": "United States",  "rhd": false, "imperial": true }
{ "code": "DE", "name": "Germany",        "rhd": false, "imperial": false }
FieldTypeDescription
codestringISO 3166-1 alpha-2 country code
namestringHuman-readable name (informational only)
rhdbooltrue if vehicles drive on the left (right-hand drive countries)
imperialbooltrue if distances should be shown in miles/yards

Contributing

This project is the result of extensive reverse-engineering and testing on mh2p. Contributions welcome!

Troubleshooting / bug reports


Format a FAT32 SD card
Put SD in PCM before turning the ignition on
Drive β†’ afterwards when you turn the ignition off β†’ pull SD card β†’ look in the log for event that was wrong β†’ match that event to the same one in the hashed log (time stamps can be useful)
Share the hashed logs + expected vs actual cluster behaviour

Known limitations


RHD and metric/imperial still requires testing
Roundabouts limited (Waze lacks exit angles so they can be wrong)
data granularity and slow update rate limit accuracy
Some glitches are inherent to Android Auto

Credits

LukaDev: Managing screens in VW/Mib2, Carplay RGI and GAL hacking, https://github.com/luka-dev/mib2q-carplay-rgi/

One1Blt: Android Auto/VNC to Mib2 rendering https://github.com/OneB1t/VcMOSTRenderMqb

adi961: Turn-by-turn to VC integration https://github.com/adi961/mib2-android-auto-vc

LawPaul: Mh2p Modkit https://lawpaul.github.io/MH2p_SD_ModKit_Site

litdreams10: general platform knowledge, Android Auto testing