Renders Android Auto and CarPlay navigation directly on the instrument cluster of PCM5 / MH2P Porsche vehicles. Turn-by-turn arrows, live map view, seamless handoff back to native Porsche navigation when the phone disconnects.
This software is free software, developed and maintained in my spare time.
A lot of time goes into reverse engineering, development, testing and supporting different Porsche models and firmware versions.
If you find it useful, please consider supporting continued development.
Donations are entirely optional. The software is free and all features are available without payment.
Website: https://fifthbro.github.io/
Support / Discussion: ModKit Discord https://discord.gg/QAEkZmDh9
Status: Stable release — CarPlay cluster rendering now live alongside Android Auto.
When you plug in (or wirelessly connect) your phone, the digital instrument cluster becomes a second navigation display driven by the phone.
| Model | Head unit | Status |
|---|---|---|
| Cayenne (9Y0) | PCM5 / MH2P | ✅ Supported |
| Macan | PCM5 / MH2P | ✅ Supported |
| Panamera | PCM5 / MH2P | ✅ Supported |
| 992.1 (911) | PCM5 / MH2P | ✅ Supported |
| 718 (Cayman / Boxster) | MHI2 / PCM4 | 🚧 In progress — AA and CarPlay working, release next |
Model years: 2019–2021. Firmware range: 9xxx–29xx. Other firmwares can be enabled on request via the installer's OEM/firmware gate.
Uses the MH2P Mod Kit (LawPaul): https://lawpaul.github.io/MH2p_SD_ModKit_Site/
⚠ Warning: This modifies Java files on your PCM5/MH2P unit. Only proceed if you understand the risks.
Prerequisites
Steps
Extract the ZIP to the root of the SD card
The root of the SD card should look like this:

Using a Mac? Additional steps are required:
Start the vehicle
Uninstall
uninstall.txt.template (in the mod folder on the SD card) to uninstall.txtUpdate/uninstall.sh runs automatically and restores originals from backupuninstall.txt from the SD so it doesn't re-triggerAndroid Auto navigation events arrive via the DSI service; CarPlay navigation arrives via the iAP2 RGI event stream. Both are translated to BAP (Bedien-und Anzeigeprotokoll) messages and pushed to the instrument cluster via CombiBAPServiceNavi. A heartbeat timer (default 2 s) continually re-sends the last state so the native cluster nav can't overwrite it.
CarPlay cluster rendering additionally uses a second video stream from the phone that is decoded on the head unit (H.264 via NvMedia) and composited onto the cluster's dedicated video surface. Android Auto uses an equivalent second-video-stream channel.
Maps AA 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; else motorway exit → EXIT_LEFT/RIGHT |
event=11–13 (roundabout) |
ROUNDABOUT_TRS_LEFT/RIGHT. RHD correction: (540−angle)%360; LHD: (angle+180)%360 |
event=16/17 (ferry) |
FERRY |
event=19 (destination preview) |
Sets flag, falls through as FOLLOW_STREET |
road|eventCode|turnSide. New maneuver resets baseline distance and bypasses throttle for first update.(1 − distance/maneuverInitialDistance) × 100, clamped 0–100.When bargraphMode="auto", the car type is queried at runtime:
| 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.
The config file cluster_config.json is installed to /mnt/app/eso/bin/apps/cluster/cluster_config.json. To override without reinstalling, place a file on a USB stick or SD card — the app checks these paths first:
/fs/usb0_0/cluster_config.json
/fs/usb1_0/cluster_config.json
/fs/sda0/cluster_config.json
/fs/sdb0/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. |
enableExternalLogging |
bool | false |
Write logs to USB/SD if present; falls back to logFilePath. |
logFilePath |
string | /tmp/cluster.log |
Plain log file path. |
hashedLogFilePath |
string | /tmp/cluster_hashed.log |
Hashed log file path. |
logFileSize |
int (MB) | 50 |
Max file size before rotation. 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. |
heartbeatInterval |
int (ms) | 2000 |
Heartbeat period. Range: 100–10000. |
maneuverStateMask |
int | 0 |
Bitmask for updateManeuverState calls. 0 = disabled. bit0=FOLLOW, bit1=PREPARE, bit2=DISTANCE, bit3=CALL_FOR_ACTION. Example: 15 (0b1111) = all states. |
| Key | Type | Default | Description |
|---|---|---|---|
forceImperial |
bool | false |
Always use miles/yards, ignoring car settings. |
forceRHD |
bool | false |
Always treat vehicle as right-hand drive. |
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. "always" — send both text and bargraph. "distance" — text only. "dynamic" — bargraph only within dynamicBargraphDistance. |
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.
| 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. 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 drive on the left (right-hand-drive) |
imperial |
bool | true if distances should be shown in miles/yards |
Bug reports
Known limitations
Not affiliated with Porsche, Volkswagen, Audi, or Google. This software is free and not for commercial use.
| 日期 | 榜单 | 排名 | 周期新增 | 总 STAR |
|---|