Reverse engineering the MQB Electric steering rack
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Reverse engineering the MQB Electric steering rack
I'm looking for a bit of direction in trying to hack this MQB rack from a VW Transporter 6.1.
It works in fail safe but it'd be nicer if it ran with the speed mapping to the level of assist.
I've got some Canlogs from a MK7 Golf (I think) and played them back. I've identified the differences between the two, and there seems to be only one CAN ID required to wake up the rack... 0x0FD ESP_21
When powered up the unit sends out comms on...
086 - 4th and 5th bits change with torque input on the rack, whilt playing the CAN log back at the rack these bits change moreso, 4th bit indicates direction of turn (9F turned CCW and DF/5F when turned CW)
09F - 6th bit changes with torque applied to the rack, I need to map this out but this appears to be the angle of the steering wheel
7th bit changes with direction also (00 for CW and 80 for CCW)
5FC - is a static message when in failsafe mode (no playback, with playback it responds), I need to look into this further but changes occur on the 1st, 2nd, 3rd and 4th bits
32A - need to check this one more but no changes obseved
6C0 - changes at 7th bit from 19 to 11 when having the log played back.
Any pointers on how to narrow things down further?
0FD in the CAN log appears to contain some speed information but I'm sure someone with more experience will be able to shed some some light.
I've cut the CAN log down into sections where it appears there is no driving and parts with driving, as expected, playing back with driving provides more assistance than playing back parts with driving.
It works in fail safe but it'd be nicer if it ran with the speed mapping to the level of assist.
I've got some Canlogs from a MK7 Golf (I think) and played them back. I've identified the differences between the two, and there seems to be only one CAN ID required to wake up the rack... 0x0FD ESP_21
When powered up the unit sends out comms on...
086 - 4th and 5th bits change with torque input on the rack, whilt playing the CAN log back at the rack these bits change moreso, 4th bit indicates direction of turn (9F turned CCW and DF/5F when turned CW)
09F - 6th bit changes with torque applied to the rack, I need to map this out but this appears to be the angle of the steering wheel
7th bit changes with direction also (00 for CW and 80 for CCW)
5FC - is a static message when in failsafe mode (no playback, with playback it responds), I need to look into this further but changes occur on the 1st, 2nd, 3rd and 4th bits
32A - need to check this one more but no changes obseved
6C0 - changes at 7th bit from 19 to 11 when having the log played back.
Any pointers on how to narrow things down further?
0FD in the CAN log appears to contain some speed information but I'm sure someone with more experience will be able to shed some some light.
I've cut the CAN log down into sections where it appears there is no driving and parts with driving, as expected, playing back with driving provides more assistance than playing back parts with driving.
- Attachments
-
- Driving Log1 7-11-23-shortened with drive.csv
- (20.34 MiB) Downloaded 438 times
-
- Driving Log1 7-11-23-shortened no drive from 4286812-262989021.csv
- (22.73 MiB) Downloaded 472 times
-
- 086-with drive.csv
- (228.33 KiB) Downloaded 464 times
-
- 086-no drive.csv
- (246.52 KiB) Downloaded 463 times
-
- 32a-with drive.csv
- (66.75 KiB) Downloaded 470 times
-
- 32a-no drive.csv
- (57.15 KiB) Downloaded 467 times
-
- 11d-with drive.csv
- (91.4 KiB) Downloaded 455 times
-
- 11d-no drive.csv
- (115.72 KiB) Downloaded 445 times
-
- 09f-with drive.csv
- (171.83 KiB) Downloaded 451 times
-
- 09f-no drive.csv
- (158.46 KiB) Downloaded 462 times
-
- 5fc and 6c0-with drive.csv
- (79.06 KiB) Downloaded 459 times
-
- 5fc and 6c0-no drive.csv
- (58.46 KiB) Downloaded 437 times
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
So I've had a little more time to mess with this.
I'm flying by the seat of my pants as I go here so may be making hard work of what you all would find pretty easy but playing the filtered CAN log back at the rack for 0FD EPS_21 I get assist and speed values showing on ODIS.
I played a log whilst taking a bunch of photos and then laid it out in a graph, removing outlier values of assist (likelihood due to extra torque from steering) and the averageing the results I've got an idea of the levels of assist at different speeds... Following on from this, looking at EPS_21 I can see byte 2 starts at 211 and counts up to 223, so I guess that's a counter then that can be seen in green on this flow graph and the DBC ..
The ESP V Signal is noted in "KiloMeterPerHour" so thats likely my speed, starts at byte 5 (bit 32) and is 16 bits long if I am interpreting the DBC correctly...
But I'm uncertain how the data is then cofigured to the speed shown below.. from the raw data flow graph...
I'm flying by the seat of my pants as I go here so may be making hard work of what you all would find pretty easy but playing the filtered CAN log back at the rack for 0FD EPS_21 I get assist and speed values showing on ODIS.
I played a log whilst taking a bunch of photos and then laid it out in a graph, removing outlier values of assist (likelihood due to extra torque from steering) and the averageing the results I've got an idea of the levels of assist at different speeds... Following on from this, looking at EPS_21 I can see byte 2 starts at 211 and counts up to 223, so I guess that's a counter then that can be seen in green on this flow graph and the DBC ..
Code: Select all
BO_ 253 ESP_21: 8 Gateway_MQB
[b]SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX[/b]
SG_ BR_Eingriffsmoment : 12|10@1+ (1,-509) [-509|509] "" XXX
SG_ ESP_PLA_Bremseingriff : 22|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_Diagnose : 23|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESC_Reku_Freigabe : 24|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESC_v_Signal_Qualifier_High_Low : 25|3@1+ (1.0,0.0) [0.0|7] "" XXX
SG_ ESP_Vorsteuerung : 28|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_AWV3_Brems_aktiv : 29|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ OBD_Schlechtweg : 30|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ OBD_QBit_Schlechtweg : 31|1@1+ (1.0,0.0) [0.0|1] "" XXX
[b] SG_ ESP_v_Signal : 32|16@1+ (0.01,0) [0.00|655.32] "Unit_KiloMeterPerHour" XXX[/b]
SG_ ASR_Tastung_passiv : 48|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_Tastung_passiv : 49|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_Systemstatus : 50|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ASR_Schalteingriff : 51|2@1+ (1.0,0.0) [0.0|3] "" XXX
SG_ ESP_Haltebestaetigung : 53|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_MKB_Abbruch_Geschw : 54|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_QBit_v_Signal : 55|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ABS_Bremsung : 56|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ASR_Anf : 57|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ MSR_Anf : 58|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ EBV_Eingriff : 59|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ EDS_Eingriff : 60|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_Eingriff : 61|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_ASP : 62|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ ESP_Anhaltevorgang_ACC_aktiv : 63|1@1+ (1.0,0.0) [0.0|1] "" XXX
But I'm uncertain how the data is then cofigured to the speed shown below.. from the raw data flow graph...
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
OK, so the vehicle speed is made up from byte 5 and byte 6 and is calculated by (b5 + b6*253)*0.01.
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
So I made a little progress last night after not having done anything on it in a while...
I asked Gemini (googles AI) to help with the checksum for the CAN ID 0X0FD EPS_21...
It managed pretty quickly to crack it and comparing it to the CAN Log I've got appears to be correct which is good news. I've also got it to generate some code (below), but I need to check it over... whilst learning C++
....
Then I'm hoping to merge it into BigPies Canfilter code so I can get it to translate from my VW transporter CAN data to the newer MQB format to satisfy the rack.
I'll bench test it all first but given the rack goes to failsafe mode if it receives invalid data or looses connection I'm confident it should work OK...
I asked Gemini (googles AI) to help with the checksum for the CAN ID 0X0FD EPS_21...
It managed pretty quickly to crack it and comparing it to the CAN Log I've got appears to be correct which is good news. I've also got it to generate some code (below), but I need to check it over... whilst learning C++
Then I'm hoping to merge it into BigPies Canfilter code so I can get it to translate from my VW transporter CAN data to the newer MQB format to satisfy the rack.
I'll bench test it all first but given the rack goes to failsafe mode if it receives invalid data or looses connection I'm confident it should work OK...
Code: Select all
#include <stdint.h>
uint8_t calculateCRC8(const uint8_t *data, size_t length) {
uint8_t crc = 0x00;
uint8_t polynomial = 0x07;
for (size_t i = 0; i < length; i++) {
uint8_t dataByte = data[i];
for (uint8_t j = 0; j < 8; j++) {
uint8_t msb = (crc & 0x80) ? 1 : 0; // Check if MSB of CRC is 1
crc <<= 1; // Shift CRC left by one bit
if (msb ^ ((dataByte >> (7 - j)) & 0x01)) { // If MSB of CRC XOR current data bit is 1
crc ^= polynomial; // XOR CRC with the polynomial
}
crc &= 0xFF; // Ensure CRC remains an 8-bit value
}
}
return crc;
}
// Example usage:
void setup() {
Serial.begin(115200);
Serial.println("CRC-8 Calculation Example");
// Example CAN data (excluding the checksum byte itself)
uint8_t canData[] = {0xD4, 0x1F, 0x80, 0xB4, 0x29, 0x00, 0x00};
size_t dataLength = sizeof(canData) / sizeof(canData[0]);
uint8_t calculatedChecksum = calculateCRC8(canData, dataLength);
Serial.print("Data: ");
for (size_t i = 0; i < dataLength; i++) {
Serial.print(canData[i], HEX);
Serial.print(" ");
}
Serial.println();
Serial.print("Calculated CRC-8 Checksum: 0x");
Serial.println(calculatedChecksum, HEX);
// For the 5th frame in your log, the expected checksum (D1) was 0xCB
Serial.print("Expected Checksum (from log): 0xCB");
}
void loop() {
// In your real-time application, you would read CAN data here
delay(1000);
}- uhi22
- Posts: 1143
- Joined: Mon Mar 14, 2022 3:20 pm
- Location: Ingolstadt/Germany
- Has thanked: 231 times
- Been thanked: 638 times
Re: Reverse engineering the MQB Electric steering rack
The shown code does not include the alive counter (which should increment with each message), and also does not contain the table with magic bytes. So it cannot produce valid E2E protected messages.
Github: http://github.com/uhi22 --- Patreon: https://www.patreon.com/uhi22
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
Thanks for looking over it.
If I'm understanding you, the code only calculates the 1st bit. This is the checksum, the counter is a latter bit (I cannot remember which). I gave the AI some data with the first checksum bit removed and asked it calculate the checksum based on the provided data and the checksum bit matched. But I may be misunderstanding how this all works by the sound of it.
If I'm understanding you, the code only calculates the 1st bit. This is the checksum, the counter is a latter bit (I cannot remember which). I gave the AI some data with the first checksum bit removed and asked it calculate the checksum based on the provided data and the checksum bit matched. But I may be misunderstanding how this all works by the sound of it.
- uhi22
- Posts: 1143
- Joined: Mon Mar 14, 2022 3:20 pm
- Location: Ingolstadt/Germany
- Has thanked: 231 times
- Been thanked: 638 times
Re: Reverse engineering the MQB Electric steering rack
The snippet of the DBC above shows that the checksum is at bit 0 and has 8 bits, and the counter starts at bit 8 (means: in the second byte), and has four bits.
The CRC algorithm may be similar to one of the profiles described in the AUTOSAR E2E specification. I described a similar method which Tesla uses, https://github.com/uhi22/tesla-crc
If we have a lot of trace data of an original car, it is possible to find out the missing pieces: the 16-byte table with magic bytes and the CRC polynom.
The CRC algorithm may be similar to one of the profiles described in the AUTOSAR E2E specification. I described a similar method which Tesla uses, https://github.com/uhi22/tesla-crc
If we have a lot of trace data of an original car, it is possible to find out the missing pieces: the 16-byte table with magic bytes and the CRC polynom.
Github: http://github.com/uhi22 --- Patreon: https://www.patreon.com/uhi22
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
I've got 2 can logs from a VW Mk7 Golf, both are quite long so theres plenty of data there of thats what you mean by trace data.
- uhi22
- Posts: 1143
- Joined: Mon Mar 14, 2022 3:20 pm
- Location: Ingolstadt/Germany
- Has thanked: 231 times
- Been thanked: 638 times
Re: Reverse engineering the MQB Electric steering rack
Ahh, I just see the attached logs above, I will have a look into them in the next days.
If you have longer logs you could extract the lines with the esp_21 message (eg using linux grep command) and provide the extract.
If you have longer logs you could extract the lines with the esp_21 message (eg using linux grep command) and provide the extract.
Github: http://github.com/uhi22 --- Patreon: https://www.patreon.com/uhi22
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
I've filtered the log files for 0x0FD only and attached, thanks again for the help
- Attachments
-
- Driving Log1 7-17-23-0fd.csv
- (2.28 MiB) Downloaded 463 times
-
- Driving Log1 7-11-23-0fd.csv
- (2.69 MiB) Downloaded 445 times
- uhi22
- Posts: 1143
- Joined: Mon Mar 14, 2022 3:20 pm
- Location: Ingolstadt/Germany
- Has thanked: 231 times
- Been thanked: 638 times
Re: Reverse engineering the MQB Electric steering rack
Good news: The CRC algorithm is the same as used in Tesla. Created a program which reads the above two log files, calculates the CRC for each message and compares it with the transmitted CRC. Result: All pass. CRC ok: 91622, CRC fail: 0
Github: https://github.com/uhi22/tesla-crc/tree/main/vag
Github: https://github.com/uhi22/tesla-crc/tree/main/vag
Github: http://github.com/uhi22 --- Patreon: https://www.patreon.com/uhi22
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
Right im revisiting this. So what would be the recommended method for transmitting the checksum in a live translation from my VW T5?
- tom91
- Posts: 2753
- Joined: Fri Mar 01, 2019 9:15 pm
- Location: Bristol
- Has thanked: 264 times
- Been thanked: 717 times
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
ok, so it looks like I've got the Can filter setup and running with the help of BigPies/RStevens CanBridge board and....
ChatGPT/ClaudeAI
...
https://github.com/jamiejones85/ESP32VWMITM
just bench tested it but playing a canlog from my VW T5 back at the Can filter hardware and then listening back on the second can network whilst recording on SavvyCAN shows a close match between the acrtual vehicle speed and the recorded vehicle speed, with the translated speed in blue dashed and the actual speed in black dashed on the graph below. I need to check the checksum calc is correct and then I'll try playing it back at the rack itself and see if it responds as expected. It's importrant to note the points where the vehicle speed doesnt align is where the can interface crashed and I had to restart it. I'll add in an alive CANID on one of the networks (listening of translated) and also maybe add some other safety checks but if the rack doesnt accept the data as valid it should resort to its failsafe mode of 40mph assistance.
https://github.com/jamiejones85/ESP32VWMITM
just bench tested it but playing a canlog from my VW T5 back at the Can filter hardware and then listening back on the second can network whilst recording on SavvyCAN shows a close match between the acrtual vehicle speed and the recorded vehicle speed, with the translated speed in blue dashed and the actual speed in black dashed on the graph below. I need to check the checksum calc is correct and then I'll try playing it back at the rack itself and see if it responds as expected. It's importrant to note the points where the vehicle speed doesnt align is where the can interface crashed and I had to restart it. I'll add in an alive CANID on one of the networks (listening of translated) and also maybe add some other safety checks but if the rack doesnt accept the data as valid it should resort to its failsafe mode of 40mph assistance.
-
Jacobsmess
- Posts: 836
- Joined: Thu Mar 02, 2023 1:30 pm
- Location: Uk
- Has thanked: 480 times
- Been thanked: 137 times
Re: Reverse engineering the MQB Electric steering rack
ok, after a lot of faffing I've managed to get the code to generate the CRC correctly.
I dont have access to my van or Can logs of my van moving at the moment but this translator appears to work to translate VW speed signals transmitted on 0x288 to MQB ESP 21 0x0FD and correctly calculate the checksum. It should run on an ESP32 with a second can interface added. I'll verify tomorrow. If there are any sense checks and/or safety checks I should implement also I'd be interested to know.
I dont have access to my van or Can logs of my van moving at the moment but this translator appears to work to translate VW speed signals transmitted on 0x288 to MQB ESP 21 0x0FD and correctly calculate the checksum. It should run on an ESP32 with a second can interface added. I'll verify tomorrow. If there are any sense checks and/or safety checks I should implement also I'd be interested to know.
Code: Select all
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// ===== VAG CRC8 (POLY = 0x2F, ESP21 magic XOR) =====
// Based on the reference vag-crc-filereader.c implementation
uint8_t vag_crc_esp21(const uint8_t *data, int len, uint8_t counter) {
// Magic bytes for ESP21 (pre-XORed with 0xBA in the reference)
static const uint8_t magicBytes[16] = {
0xA3, 0xA9, 0x66, 0x13, 0x85, 0x1F, 0x0D, 0x37,
0xF0, 0xC2, 0x8E, 0xA1, 0x38, 0xB1, 0x4E, 0xD9
};
// CRITICAL FIX: Initial value must be 0x00, not 0xFF!
uint8_t crc = 0x00;
// Process the payload bytes
for (int i = 0; i < len; ++i) {
crc ^= data[i];
for (int b = 0; b < 8; ++b) {
if (crc & 0x80)
crc = (crc << 1) ^ 0x2F;
else
crc = crc << 1;
}
}
// Apply the magic byte XOR based on the alive counter (lower nibble)
return crc ^ magicBytes[counter & 0x0F];
}
// Build the payload array exactly as the reference code does
void build_payload(uint8_t *payload, const uint8_t *frame) {
// payload[0] = high nibble of D2 (counter byte), keeping lower nibble zero
payload[0] = frame[1] & 0xF0;
// payload[1..6] = D3 through D8
for (int i = 0; i < 6; i++) {
payload[i + 1] = frame[i + 2];
}
// payload[7] = virtual 0x00 byte
payload[7] = 0x00;
}
// Global variables for CSV parsing
double timestamp;
uint32_t id;
char extended_str[8];
char dir_str[4];
uint8_t bus, len;
uint8_t data[8];
// ===== Process one CSV line =====
void processLine(char *line, FILE *out, uint8_t *counter) {
int assigned = sscanf(line, "%lf,%x,%7[^,],%3[^,],%hhu,%hhu,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx",
×tamp, &id, extended_str, dir_str, &bus, &len,
&data[0], &data[1], &data[2], &data[3],
&data[4], &data[5], &data[6], &data[7]);
if (assigned != 14) {
return; // Skip header or malformed lines
}
if (id == 0x288) {
// Extract engine speed from D4 (byte index 3)
uint8_t src_byte3 = data[3];
float speed_kph = (float)src_byte3 * 1.28f;
uint16_t speed_eps = (uint16_t)roundf(speed_kph * 100.0f);
// Build 0x0FD frame
uint8_t frame[8] = {0};
// D2 (index 1) = Counter in lower nibble, high nibble can be data (set to 0xD for now)
frame[1] = 0xD0 | (*counter & 0x0F);
// D3-D8 payload bytes
frame[2] = 0x1F;
frame[3] = 0x82;
frame[4] = (uint8_t)(speed_eps & 0xFF); // Speed Low
frame[5] = (uint8_t)((speed_eps >> 8) & 0xFF); // Speed High
frame[6] = 0x00;
frame[7] = 0x00;
// Build payload array exactly as reference code does
uint8_t payload[8];
build_payload(payload, frame);
// Calculate CRC over the 8-byte payload (includes virtual 0x00)
frame[0] = vag_crc_esp21(payload, 8, *counter);
// Output in SavvyCAN format
fprintf(out, "%.6f,000000FD,false,Tx,0,8,"
"%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X\n",
timestamp,
frame[0], frame[1], frame[2], frame[3],
frame[4], frame[5], frame[6], frame[7]);
// Increment counter (0-15 wraparound)
(*counter)++;
if (*counter > 0x0F) *counter = 0x00;
}
}
// ===== Verification function =====
void verify_log(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("Could not open verification file: %s\n", filename);
return;
}
printf("\n=== Verifying CRC against reference log ===\n");
char line[1024];
int line_num = 0;
int crc_ok = 0;
int crc_fail = 0;
// Skip header
fgets(line, sizeof(line), fp);
while (fgets(line, sizeof(line), fp)) {
line_num++;
double ts;
uint32_t msg_id;
uint8_t msg_data[8];
int assigned = sscanf(line, "%lf,%x,%*[^,],%*[^,],%*hhu,%*hhu,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx",
&ts, &msg_id,
&msg_data[0], &msg_data[1], &msg_data[2], &msg_data[3],
&msg_data[4], &msg_data[5], &msg_data[6], &msg_data[7]);
if (assigned != 10 || msg_id != 0x0FD) continue;
// Extract CRC and counter from message
uint8_t crc_from_can = msg_data[0];
uint8_t counter = msg_data[1] & 0x0F;
// Build payload
uint8_t payload[8];
payload[0] = msg_data[1] & 0xF0;
for (int i = 0; i < 6; i++) {
payload[i + 1] = msg_data[i + 2];
}
payload[7] = 0x00;
// Calculate CRC
uint8_t crc_calculated = vag_crc_esp21(payload, 8, counter);
if (crc_calculated == crc_from_can) {
crc_ok++;
} else {
crc_fail++;
if (crc_fail <= 5) { // Show first 5 failures
printf("Line %d: CRC mismatch - CAN: 0x%02X, Calc: 0x%02X, Counter: 0x%X\n",
line_num, crc_from_can, crc_calculated, counter);
}
}
}
fclose(fp);
printf("Verification complete: %d OK, %d FAIL\n", crc_ok, crc_fail);
if (crc_ok > 0 && crc_fail == 0) {
printf("✓ SUCCESS: All CRCs match!\n");
}
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <input_log.csv> [output_translated.csv] [--verify reference.csv]\n", argv[0]);
printf("\nModes:\n");
printf(" Translate: %s input.csv output.csv\n", argv[0]);
printf(" Verify: %s --verify reference.csv\n", argv[0]);
return 1;
}
// Verification mode
if (argc >= 3 && strcmp(argv[1], "--verify") == 0) {
verify_log(argv[2]);
return 0;
}
// Translation mode
if (argc < 3) {
printf("Error: output filename required for translation\n");
return 1;
}
FILE *in = fopen(argv[1], "r");
if (!in) {
printf("Error: could not open input file %s\n", argv[1]);
return 1;
}
FILE *out = fopen(argv[2], "w");
if (!out) {
printf("Error: could not create output file %s\n", argv[2]);
fclose(in);
return 1;
}
// Write header
fprintf(out, "Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8\n");
printf("Translating 0x288 -> 0x0FD...\n");
uint8_t counter = 0x00; // Start at 0, not 0xD0
char line[1024];
// Skip header
fgets(line, sizeof(line), in);
while (fgets(line, sizeof(line), in)) {
processLine(line, out, &counter);
}
fclose(in);
fclose(out);
printf("Translation complete. Output written to %s\n", argv[2]);
// Auto-verify if verification file provided
if (argc >= 4) {
printf("\nAuto-verifying against: %s\n", argv[3]);
verify_log(argv[3]);
}
return 0;
}