Es gibt zwei völlig verschiedene Wege auf den Fahrzeugbus. Halte sie auseinander.
Die Head-Unit spricht über UART mit einem Begleit-MCU (/dev/ttymxc3 @115200). Der MCU
überbrückt zum Fahrzeug-CAN-Bus. Die UART gehört dem carmanager-CarService; andere Apps erreichen
sie über einen Systemdienst (AIDL). Der MCU-Frame trägt eine Adresse „CAN-Steuerframe senden“ und
einen Steuertyp-Selektor (Klimaanlage, Laden, Kombiinstrument, …).
Das ist die Route, die tatsächlich dekodierte Werte liest und Steuerbefehle sendet. Die Head-Unit sieht hier nie rohe CAN-Frames, nur dekodierte Registerwerte.
Die OBD2-Buchse ist ein rein abfragendes Diagnose-Gateway. Über sie kannst du alle 24 verbauten Steuergeräte auflisten, ihre Fehlercodes und Daten lesen (darunter einen Block mit Live-Batteriemesswerten) und Codes löschen — aber du kannst nichts steuern ohne den Security-Access-Schlüssel des Herstellers. Sie führt die internen Broadcasts nicht, taugt also nicht zum Mitlesen laufender Signale. Vollständige Beschreibung — die Steuergeräte-Übersicht, was lesbar ist, wie man Rohwerte dekodiert, das Löschen von Codes und die Sicherheitshinweise:
Der interne Bus ist aktiv und möglicherweise sicherheitsrelevant (anders als das isolierte OBD2-Gateway):
Die Head-Unit arbeitet mit einer Registry von ~461 Action-ID-Keys
— den Werten, die sie liest (GET), und den Befehlen, die sie sendet (SET), über die MCU/CAN-Brücke. Das sind die
Keys für CarService.getInt(id) / sendCmd(id,…), nicht rohe CAN-Arbitration-IDs (die liegen eine
Schicht tiefer im MCU-Protokoll).
→ Vollständige Liste, nach Bereich gruppiert: CAN-Signalreferenz. Die Untermenge Batterie/Antriebsstrang — die Signale, nach denen die meisten fragen — steht unten.
Der U5 hat ein geteiltes Hochvolt-Batteriepaket (A + B), dessen Hälften jeweils ihren eigenen SoC /
ihre eigene Spannung / ihren eigenen Strom melden. Mit getInt(id) lesen, während das Auto wach ist.
| Signal | ID | Konstante | Einheit / Hinweis |
|---|---|---|---|
| Gesamt-SoC | 3335 | WARN_BMS_STATE_OF_CHARGE | % |
| Spannung Paket A (Haupt) | 3455 | CARSET_PACK_BMS_VOLTAGE | V — Live-Messwert ≈ 356 V |
| Strom Paket A (Haupt) | 3456 | CARSET_PACK_BMS_CURRENT | A |
| SoC Paket A | 3457 | CARSET_PACK_A_SOC | % |
| Spannung Paket B | 3458 | CARSET_PACK_B_VOLTAGE | V |
| Strom Paket B | 3459 | CARSET_PACK_B_CURRENT | A |
| SoC Paket B | 3460 | CARSET_PACK_B_SOC | % |
| Reichweite | 3461 | CARSET_VCU_DRIVING_RANGE | km |
| Energiemodus | 3462 | CARSET_VCU_ENERGY_MODE | |
| BMS-Warnleuchte | 3323 | WARN_BMS_WARN_LAMP | Kontrollleuchte |
| Warnung thermisches Durchgehen | 3340 | WARN_BMS_THERMALRUNAWAY | Kontrollleuchte |
| Ladezeit | 492 | CARSET_CHARGE_TIME | SET/geschätzt |
| Entladeanforderung (V2L) | 491 | CARSET_DISCHARGE_REQ | SET |
Kein State-of-Health (SoH). Die Firmware dekodiert den SoC, aber es gibt keinen Key für SoH / Kapazität / Zellspannung — der SoH müsste aus einem rohen BMS-CAN-Frame oder einem UDS-Diagnoselesevorgang kommen (siehe Roadmap).
Live mitlesen (adb, Auto wach): der canbus-Dienst loggt dekodierte Werte unter dem Logtag
Canbus_SET (z. B. D/Canbus_SET: mPackBMSVoltage: 355.9). Der Fahrzeug-CAN sendet nur, während
das Auto wach ist — die Werte verstummen kurz nachdem es geparkt/verriegelt wurde.
Die App ADC Telemetry hat eine eingebaute Probe (sie bindet ICanbusService schon ein), sodass du
jeden Key aus einer Shell lesen oder senden kannst — praktisch, um zu prüfen, ob ein Signal
wirklich existiert, was es meldet und in welchem Zustand. Grundlagen zu Bauen/Installieren und Signieren stehen auf der
Seite Entwicklung.
Lesen — geti = getInt, getf = getFloat, geta = getIntArray:
S=com.example.carmqtt/.BridgeService adb shell am startservice -n $S --es op geti --ei key 3457 # pack-1 SoC adb shell am startservice -n $S --es op getf --ei key 3455 # pack-1 voltage adb shell am startservice -n $S --es op geta --ei key 3464 # doors [FL,FR,RL,RR] adb logcat -d -s CarMqtt.Probe # read the results
Beispielausgabe:
getf key=3455 -> getFloat=355.5 geti key=3460 -> getInt=0 (pack-2 SoC — 0: this car is single-pack) geta key=3464 -> getIntArray=[1, 0, 0, 0] (driver door open)
Befehl senden — send = sendData(action,val). ⚠ das betätigt das Fahrzeug — nur in einem sicheren Zustand:
adb shell am startservice -n $S --es op send --ei key 103 --ei val 1 # A/C ON
Hinweise: nach dem (Neu-)Installieren der App gib ihr ~1 s, um sich wieder an den canbus-Dienst zu
binden (eine zu früh abgefeuerte Probe loggt canbus service NOT bound); ein Wert von -1 / 0 bedeutet
meist, dass das Signal auf seinem Init-Standardwert steht (auf diesem Auto nicht belegt — wie bei Paket 2).