This is kind of a loose data collection on BAP (for the PQ plattform) collected from various data sources and reverse engineering.
VW uses BAP (German: “Bedien- und Anzeigeprotokoll” ~ control- and display protocol) for communication between a control unit and a display unit. BAP is meant to transport data event based instead of the usual broadcast style (e.g. ECU hot-lamp = bit X in message Y, broadcasted all 10ms). Instead data (e.g. data to be displayed or menus to be built in the telephone menu) is transmitted once and updated as necessary. BAP is the successor of DDP (Display Daten Protokoll ~ display data protocol).
Everything in Volkswagen-group terminology is German, so are some of the abbrevations.
The control unit generating the data to be displayed is called “FSG” (Funktionssteuergerät ~ functional control unit). No HMI / display capability.
The control unit displaying the data is called “ASG” (Anzeigesteuergerät ~ display control unit). In PQ this is either the cluster’s display (called MFA) or the radio-navigation unit (only for certain clima-related data). Usually displays data to the user / driver (HMI), able to display data from the FSG.
Communication is always between FSG and ASG. Not ASG to ASG and not FSG to FSG.
In general different control units have different CAN-IDs they emit data from. That is no different in BAP. Certain CAN-IDs are related to BAP-traffic. In vw terms, those messages will be called something like BAP_xxx (e.g. BAP_Clima, BAP_NAVI, BAP_Compass, …) depending on their function. This is the communication channel from FSG (clima control, navigation system, compass) to the ASG (display). The communication backwards (e.g. cluster confirming displaying of data or alive-status) also has its own CAN-IDs / messages (e.g. BAP_ASG_0x, BAP_Anzeige). The FSG sends a ‘request’ to the ASG which answers with a ‘indication’ and vice versa.
Terminology: OSI-Layer1: mostly CAN (could be LIN, FlexRay, MOST, …). OSI-Layer2-4: BCL (BAP communication layer), OSI-Layer5: BPL (BAP protocol layer), OSI-Layer6: BAL (BAP application layer), OSI-Layer7: application layer (BAP functions, e.g. display text, enable symbol, …).
Obviously nearly all data is routed from the different CAN-bus of the car (CAN-powertrain for PDC/PLA & old ACC, CAN-comfort, CAN-infotainment, CAN-extended) to the gateway which finally routes to CAN-cluster.
Despite its alive-functions (called ‘heartbeat’) data is only transmitted event-based or based on a request by FSG or ASG. No “unasked” transmissions take place.
A BAP-message can consist of multiple CAN-messages. Each BAP-message has a BAP-header which specifies lsg_id, fct_id and op_code in addition to the BAP-message’s data.
CAN-messages
There are two types of BAP-messages: short (less or equal 6 BAP-data-bytes) or long (more than 6 BAP-data-bytes). DLC is variable.
- the size of the BAP-header is always 2 bytes
- BAP is big-endian (example: BAP-header is of size 16bit, e.g. using data[0] and data[1] of the CAN-message. Big-endian means that the MSB of the header is in byte0|bit7
BAP-payload-data (BAP-application-data <=6 (less or requal than 6 bytes) -> 1 CAN-message (dlc maximum = 8bytes) is enough to transmit all data. To indicate that it is a short-BAP-frame the MSB of the CAN-data[0] of the CAN-message must be clear (must NOT be set).
If there is more than 6bytes of BAP-data to be transmitted, the BAP-message must be split into multiple CAN-messages. The first CAN-message of this BAP-message is special, since it announces the amount of data to be expected (BAP-preample) as well as the BAP-header. The following CAN-messages have a simple counter (BAP-index) plus the BAP-data. DLC is always according to needs.
derived from: https://github.com/tmbinc/kisim, see his python-class for a excellent python implementation
In the screenhot below you can see a decoded BAP-message which shows the radio station (“ANTENNE”) and the currently played song name (“Habitt” by “Laurell”) in the cluster’s main display, audio tab. Note that the screen is switched back and forth multiple times and only radio-BAP-information is shown in this CAN-trace which covers round about 30 seconds in total. That is why you see multiple occurences of the station-information. It is because data is always transmitted as soon as the audio-tab is opened.
CAN-message | CAN-ID (hex) | comment |
---|---|---|
BAP_ACC | 674 | |
BAP_Anzeige | 676 | |
BAP_VZA | 69b | |
BAP_LDW | 673 | |
BAP_ASG_01 | 6c8 | |
BAP_RDK | 6dd | |
BAP_OPS | 6da | FSG: OPS => ASG: RNS, Radio, KBI |
BAP_ASG_02 | 6c9 | |
BAP_ASG_07 | 67c | |
BAP_AUDIO | 66c | FSG: RNS, Radio => ASG: KBI |
BAP_Compass | 66d | |
BAP_NAVI | 67d | |
BAP_SHZ | 66e | |
BAP_Telefon_04 | 66f | |
BAP_Charisma | 667 | |
BAP_Debug_Cmd | 6d6 | |
BAP_Anzeige_02 | 67Ah | ASG: RNS, Radio => FSG: OPS, HYB, Dämpfer |
BAP_ASG_03 | 6C7h | ASG: RNS, Radio => FSG: Tel, TV, DSP |
BAP_ASG_04 | 6CAh | ASG: RNS, Radio => FSG: RSE, Rearview |
BAP_ASG_05 | 6CBh | ASG: RNS, Radio => FSG: Clima |
BAP_ASG_06 | 6C6h | ASG: RNS, Radio => FSG: Tel., MDI |
BAP_Clima | 6DBh | FSG: Clima => ASG: RNS, Radio |
BAP_DS | 6D8h | FSG: AMP => ASG: RNS, Radio |
BAP_MDI | 63Ch | FSG: MDI => ASG: RNS, Radio |
BAP_RV | 6DCh | FSG: Rearview => ASG: RNS, Radio |
BAP_Telefon_01 | 6D2h | FSG: Tel. => ASG: RNS, Radio |
BAP_Telefon_02 | 6D5h | FSG: Tel. => ASG: RNS, Radio |
BAP_Telefon_03 | 63Bh | FSG: Tel. => ASG: RNS, Radio |
BAP_SWA, BAP_ASG_04, BAP_RV, BAP_DSP, BAP_ASG_08, BAP_TV_Tuner | ? |
BAP communication
Inside the BAP system each control unit has (at least) one so called logical-ID “lsg_id” (logische Steuergeräte ID ~ logical control unit ID) which is partly derived from the CAN-message’s CAN-ID. Note that most control units have multiple lsg_ids, like telephone-menu and telephone-screen have different lsg_ids.
device | comment | subdevice | lsg_id |
---|---|---|---|
BAP_Charisma | (*1) | 23 (0x17) 34 (0x22) | |
BAP_NAVI | 8 (0x8) 21 (0x15) 25 (0x19) 28 (0x1c) 35 (0x23) 50 (0x32) | ||
BAP_AUDIO | cluster shows RDS-information only, menu like structure only for FM radio stations | 49 (0x31) | |
BAP_SHZ | (*1) | 3 (0x3) | |
BAP_Telefon_04 | 8 (0x8) 12 (0xc) 19 (0x13) 20 (0x14) 43 (0x2b) | ||
BAP_RDKS | (*1) | 4 (0x4) 7 (0x7) 8 (0x8) 12 (0xc) | |
BAP_ACC | (*1) | 5 (0x5) 27 (0x1b) | |
BAP_OPS | 0 (0x0) 3 (0x3) 10 (0xa) | ||
BAP_VZA | recognized traffic signs in BAP-traffic | 33 (0x21) | |
BAP_LDW | (*1) | 25 (0x19) | |
BAP_SWA | (*1) | 26 (0x1a) | |
*1 = menu items in cluster, at least no human readable content (like menu-item-textstrings) were observed in BAP-traffic from this unit
For each lsg_id exists a individual list of function-IDs “fct_id” (Funktions-ID ~ functional ID). These function-IDs describe functions that can be applied to the (sub-) control unit. For example: list all functions, screen-data, menu-data, … . For each lsg_id exists a special (device specific) set of fct_ids. However some fct_ids seem to be standardized in the BAP standard an thus often repeat:
fct_id | name | parameter | data |
---|---|---|---|
0x00 | |||
0x01 | GetAll | get all properties | used to synchronize the application-cache in the ASG. FSG delivers all property-values with op_code ‘StatusAll’. Parameter ‘Data’ of op_code ‘StatusAll’ contains all function-property-values in one continously segmented BAP-message op_code: GetAll (noParam), StatusAll (data), Error (errorCode) |
0x02 | BAP-Config | BAP_Version_major (byte), BAP_Version_minor (byte), LSG-Class (The LSG_Class_ID classifies the function catalogue uniquely because a function catalogue can occur several times with different LSG-IDs), LSG-Sub-Class, LSG-Version_major, LSG-Version_minor | information for BAP-version and LSG-version op_codes: Get (empty), Status (data), Reset (data), HeartbeatStatus (data), Error (errorCode) |
0x03 | Function-List | FctList = bitfield, bytestream, | list of supported BAP-functions This bitfield indicates the support of a certain function during runtime. op_codes: Get (noParam), Status (FctList), HeartbeatStatus (FctList), Error (errorCode) |
0x04 | HeartBeat | HeartBeatTime (byte, scale=100, unit=ms, 0..25500ms) | this message contains the time betweent two heartbeat-messages. The parameter ‘HeartBeatTime’ * 100ms = time in unit ‘milliseconds, ms’. If a heart beat error occurs for a BAP-connection, it is reported on function HeartBeat (0x04) op_codes: Get (noParam), Status (HeartBeatTime), HeartbeatStatus (HeartBeatTime), Error (errorCode) |
0x0E | FSG-Setup | Setup | |
0x0F | FSG-OperationState | OP_state byte: 0x00=normalOperation, 0x01=offStandby, 0x02=reserved, 0x03=initializing, 0x04..0x0d=reserved, 0x0e=functionInactive, 0x0f=defective, 0x10..0xff=reserved | with the parameter ‘OP-State’ this function announces the operational state of the FSG op_codes: Get (noParam), Status (OP_state), HeartbeatStatus (OP_state), Error (errorCode) |
The op_code (operation code ID) indicates the type of operation which is to be applied to the “fct_id-lsg_id”-combination. This could be something like set-value, get-value, reset, …
op_codes are grouped in function classes. Each function class has its own values and valid op_codes.
Defined function classes:
- Property: time independant value that can be read or written
- Array: like property but for big amount of data (multiple elements at once, e.g. lists)
- Method: time dependant action
- Cache: synchronize application cache in ASG
op_codes
The “Function List” (VW internal document) defines one function class (either Property or Array or Method or Cache) for each “ID” (most probable “ID” within the “Function List” means function ID = fct_id). As a result, each fct_id only has one function class and thus only one set of valid op_codes!
op_code value | Function class Property | Function class Array | Function class Method | Function class Cache | direction |
0 | Reset / Set | SetArray | Start | – | ASG -> FSG (Start, Set) FSG -> ASG (Reset) |
1 | Get | GetArray | Abort | GetAll | ASG -> FSG |
2 | SetGet | SetGetArray | StartResult | – | ASG -> FSG |
3 | HeartbeatStatus | ChangeArray | Processing | – | FSG -> ASG |
4 | Status | StatusArray | Result | StatusAll | FSG -> ASG |
5 | StatusAck | – | – | – | FSG -> ASG |
6 | Ack | – | – | – | ASG -> FSG |
7 | Error | Error | Error | Error | FSG -> ASG |
Error codes: 0x00=noError, 0x01..0x3f=stdBAPerror, 0x40..0xff=reserved
VW requires:
- All parameters transmitted via BAP shall be stored in non-volatile memory on the FSG to keep information during loss of power.
- Function-IDs 1 to 4 shall not be evaluated by application if not otherwise specified
Note that each lsg_id has its own list of fct_ids and each fct_id has its own set of op_codes. Not all op_codes are valid for all fct_id! All named above are (hopefully) BAP-standardized, not any more device specific or at least the same over all BAP-participants.
Control & buttons
With displaying data, there is a need to control menus, select entries, manipulate values, etc. All this is handled by the FSG (and not via BAP). Button presses from the MFL (Multifunktionslenkrad ~ multifunctional steering wheel) or steering column stalks (up/down + OK/Reset at the wiper control stalk) are passed to the control unit (FSG) which then updates the ASG’s display. It is not entirely clear at the moment if the cluster (as main display device) also handles menus and propagates selected items or adjusted values to other control units (FSGs) (on request?).
For the multifunctional steering wheel CAN-message “mMFL_Tasten” transports the pressed button in byte[0] of CAN-message: CAN-ID 1473 (dec) = 0x5c1 (hex), cycle 100ms, dlc=4
Button codes are as follows for “normal” multifunctional steering wheel:
byte[0] hex | dec | button |
---|---|---|
0x0A | 10 | menu up / MFA arrow right |
0x09 | 9 | menu down / MFA arrow left |
0x22 | 34 | MFA up |
0x23 | 35 | MFA down |
0x28 | 40 | MFA OK / Reset (long press) |
0x29 | 41 | MFA back |
0x1A | 26 | telephone |
0x02 | 2 | skip+ / right |
0x03 | 3 | skip- / left |
0x06 | 6 | volume + |
0x07 | 7 | volume – |
0x2A | 42 | PTT / Microphone / Voicecontrol |
However some more modern PQ cars have a so called “MQB-style” multifunctional steering wheel with a completely different button layout. (These also have the GRA / cruise control switches) in steering-wheel-buttons and not in the turn-indicator-stalk or 3rd stalk). For those steering wheels, the button codes are different:
byte[0] hex | dec | button |
---|---|---|
0x02 | 2 | menuRight |
0x03 | 3 | menuLeft |
0x04 | 4 | menuUp |
0x05 | 5 | menuDown |
0x07 | 7 | OK |
0x10 | 16 | volume + |
0x11 | 17 | volume – |
0x15 | 21 | skip right + |
0x16 | 22 | skip left – |
0x1C | 28 | telephone |
0x20 | 32 | mute |
0x19 | 25 | voicecontrol |
If no multifunctional steering wheel is present, the car has buttons on the right side of the wiper stalk. Those would be “up” / “down” and “OK” ( / Reset if long pressed) which also allow for navigation through the menus. It’s most probable, that those are routed with the message mLSM_1 (CAN-ID: 705 (dec) = 0x2c1(hex) in bit LS1_MFA_Tasten from SMLS into the system.