BAP reverse engineering (volkswagen PQ)

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).

The control unit displaying the data is called “ASG” (Anzeigesteuergerät ~ display control unti). In PQ this is either the cluster’s display (called MFA) or the radio-navigation unit (only for certain clima-related data).

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.

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).

BAP short frame + example

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.

BAP long frame
example of a long BAP-message

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-messageCAN-ID (hex)comment
BAP_ACC674
BAP_Anzeige676
BAP_VZA69b
BAP_LDW673
BAP_ASG_016c8
BAP_RDK6dd
BAP_OPS6daFSG: OPS => ASG: RNS, Radio, KBI
BAP_ASG_026c9
BAP_ASG_0767c
BAP_AUDIO66cFSG: RNS, Radio => ASG: KBI
BAP_Compass66d
BAP_NAVI67d
BAP_SHZ66e
BAP_Telefon_0466f
BAP_Charisma667
BAP_Debug_Cmd6d6
BAP_Anzeige_0267AhASG: RNS, Radio => FSG: OPS, HYB, Dämpfer
BAP_ASG_036C7hASG: RNS, Radio => FSG: Tel, TV, DSP
BAP_ASG_046CAhASG: RNS, Radio => FSG: RSE, Rearview
BAP_ASG_056CBhASG: RNS, Radio => FSG: Clima
BAP_ASG_066C6hASG: RNS, Radio => FSG: Tel., MDI
BAP_Clima6DBhFSG: Clima => ASG: RNS, Radio
BAP_DS6D8hFSG: AMP => ASG: RNS, Radio
BAP_MDI63ChFSG: MDI => ASG: RNS, Radio
BAP_RV6DChFSG: Rearview => ASG: RNS, Radio
BAP_Telefon_016D2hFSG: Tel. => ASG: RNS, Radio
BAP_Telefon_026D5hFSG: Tel. => ASG: RNS, Radio
BAP_Telefon_0363BhFSG: Tel. => ASG: RNS, Radio
BAP_SWA, BAP_ASG_04, BAP_RV, BAP_DSP, BAP_ASG_08, BAP_TV_Tuner?
list of known BAP-channels with corresponding CAN-ID

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.

devicecommentsubdevicelsg_id
BAP_Charisma(*1)23 (0x17)
34 (0x22)
BAP_NAVI8 (0x8)
21 (0x15)
25 (0x19)
28 (0x1c)
35 (0x23)
50 (0x32)
BAP_AUDIOcluster shows RDS-information only, no menu ?49 (0x31)
BAP_SHZ(*1)3 (0x3)
BAP_Telefon_048 (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_OPS0 (0x0)
3 (0x3)
10 (0xa)
BAP_VZArecognized traffic signs in BAP-traffic33 (0x21)
BAP_LDW(*1)25 (0x19)
BAP_SWA(*1)26 (0x1a)
observed lsg_ids

*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_idnameparameterdata
0x00
0x01GetAllget all propertiesused 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)
0x02BAP-ConfigBAP_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_minorinformation for BAP-version and LSG-version

op_codes: Get (empty), Status (data), Reset (data), HeartbeatStatus (data),
Error (errorCode)
0x03Function-ListFctList = 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)
0x04HeartBeatHeartBeatTime (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)
0x0EFSG-SetupSetup
0x0FFSG-OperationStateOP_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_codedescription
Get
SetGet
Status
HeartbeatStatus
Error0x00=noError, 0x01..0x3f=stdBAPerror, 0x40..0xff=reserved
GetArray
StatusArray
ChangedArray

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. All named above are (hopfully) 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
decbutton
0x0A10menu up / MFA arrow right
0x099menu down / MFA arrow left
0x2234MFA up
0x2335MFA down
0x2840MFA OK / Reset (long press)
0x2941MFA back
0x1A26telephone
0x022skip+ / right
0x033skip- / left
0x066volume +
0x077volume –
0x2A42PTT / 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
decbutton
0x022menuRight
0x033menuLeft
0x044menuUp
0x055menuDown
0x077OK
0x1016volume +
0x1117volume –
0x1521skip right +
0x1622skip left –
0x1C28telephone
0x2032mute
0x1925voicecontrol

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.