Bridge Android Auto and Carplay navigation from Porsche PCM5 / VW / Audi MH2P to instrument cluster displays
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.
Native navigation monopolizes the instrument cluster, this code will change that!. This integration:
Turn arrows, distance, road names, roundabouts, highway exits - everything from Android Auto displayed on your cluster.
60 FPS updates with automatic heartbeat to override native navigation. Seamless transitions and no flickering.
Automatic km/mi and LHD/RHD detection via DSI services with JSON country fallback. Supports 60+ countries.
Auto-remounts SD/USB storage as writable via ASI services for external logging. No manual intervention needed.
Runtime-configurable logging, heartbeat, units, and more via embedded JSON config. No recompilation required.
Detailed file logging with configurable size limits, external storage support, and optional message hashing for privacy.
Testing compatiblity across Porsche Cayenne, Macan, Panamera and 911
Edit build script with paths in your environment and run:
recompile.sh
or
recompile.bat
The easiest way is to just scp it to the target if you enbaled ssh access
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.
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
All platform services are injected via setters after construction:
| Service | Purpose |
|---|---|
CombiBAPServiceNavi | BAP cluster output β maneuver, distance, RGStatus, destination |
ISysServices | Unit system detection, car clock, car type/variant detection |
ICarCoreServices | Drive-side detection via exteriorLight().leftHandTraffic() |
StorageMountHandler | Remounts USB/SD R/W for external log writing |
ICarStatisticsService | Injected but not currently used in active code |
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 |
road|eventCode|turnSide. New maneuver resets baseline distance and bypasses throttle for first update.(1 β distance/maneuverInitialDistance) Γ 100, clamped 0β100.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.
| Priority | Units | Drive side |
|---|---|---|
| 1 (highest) | forceImperial config flag | forceRHD config flag |
| 2 | ISysServices.units().distance().setting() (1=km, 2=mi) | ICarCoreServices.exteriorLight().leftHandTraffic() |
| 3 | JSON countries table via locale/system properties | JSON countries table rhd field |
| 4 (default) | Metric | LHD |
When bargraphMode="auto", the car type is queried at runtime via sysServices.config().carType():
| Car | carClass | generation | Resolved mode |
|---|---|---|---|
| Cayenne E3 | 5 | 3 | "always" |
| 911 992 | 6 | 8 | "always" |
| Panamera G2 | 6 | 2 | "always" |
| Macan | 4 | 1 | "distance" |
| Unknown | any | any | "always" (safe default) |
Explicit JSON values always override auto-detection.
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.
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.
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
| Key | Type | Default | Description |
|---|---|---|---|
enableFileLogging | bool | true | Master switch for all file logging. |
enableFileHashing | bool | false | Write a second log with road names/music hashed for privacy. Requires enableFileLogging. |
enableExternalLogging | bool | false | Write logs to USB/SD if present; falls back to logFilePath. |
logFilePath | string | /tmp/androidauto_cluster.log | Plain log file path. |
hashedLogFilePath | string | /tmp/androidauto_cluster_hashed.log | Hashed log file path. |
logFileSize | int (MB) | 50 | Max file size before deletion and restart. Range: 1β100. |
| Key | Type | Default | Description |
|---|---|---|---|
enableRoundaboutLatching | bool | true | Cache and hold roundabout exit direction across ENTERβCONTINUEβEXIT events to prevent arrow flickering. |
enableHeartbeat | bool | true | Periodically resend last maneuver+distance to prevent native nav from overwriting AA data. |
heartbeatInterval | int (ms) | 2000 | Heartbeat period. Range: 100β10000. |
maneuverStateMask | int | 0 | Bitmask 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. |
| Key | Type | Default | Description |
|---|---|---|---|
forceImperial | bool | false | Always use miles/yards, ignoring car settings. For testing on metric testbenches. |
forceRHD | bool | false | Always treat vehicle as right-hand drive, overriding all detection. |
metricUnitThreshold | int (m) | 100 | Below this, display switches from km to m. Range: 50β5000. |
imperialUnitThreshold | int (m) | 161 | Below this (~0.1 mi), display switches from miles to yards. Range: 50β5000. |
destinationDisplayDuration | int (ms) | 5000 | How long to hold the arrival maneuver on screen before clearing. Range: 0β10000. |
| Key | Type | Default | Description |
|---|---|---|---|
bargraphMode | string | "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. |
dynamicBargraphDistance | int (m) | 100 | In "dynamic" mode: distance below which bargraph replaces text. Range: 10β500. |
dynamicBargraphPercent | int (%) | 50 | In "dynamic" mode: for short maneuvers (<2Γ threshold), switch point as % of initial distance. Range: 10β90. |
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.
| Zone | Default boundary | Default distance | Default rateLimit |
|---|---|---|---|
veryFar | 5000 m | 100 m | 2000 ms |
far | 1000 m | 50 m | 500 ms |
approaching | 500 m | 25 m | 250 ms |
near | 200 m | 15 m | 200 ms |
close | 100 m | 15 m | 150 ms |
veryClose | 50 m | 10 m | 120 ms |
now | β | 5 m | 100 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 }
}
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 }
| Field | Type | Description |
|---|---|---|
code | string | ISO 3166-1 alpha-2 country code |
name | string | Human-readable name (informational only) |
rhd | bool | true if vehicles drive on the left (right-hand drive countries) |
imperial | bool | true if distances should be shown in miles/yards |
This project is the result of extensive reverse-engineering and testing on mh2p. Contributions welcome!
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