Part two begins with a 76MB data log from a 10 minute drive around Circuit Of The Americas to lunch. There is an incredible amount of data zipping around your cars network every second. With the laptop connected to USB2CAN serial port and SavvyCAN on the laptop, constant logging was set. From this starting point I could begin to reverse engineer and test each of the PID’s I discovered. If you missed how I did that, checkout part one.
- Over at the OpenDBC project, there is a Volkswagen dbc (vw_mqb_2010.dbc) that turned out to be quite helpful! The next steps were:
- Decoding German to English – Example: “Laengsbeschleunigung” == Longitudinal acceleration
- Getting very excited when I found “Lenkradwinkel” == Steering Wheel Angle
- Looking closely at units and looking for data by searching for “KiloMeters” and “Unit_Bar”
- Graphing the data out to make sure it was correct.
- Not finding the data in the same location as the VW, but closely watching the data in Flow View for signs.
- Example: PID 262 has all the wheel speeds for the VW. When looking at PID 259 on the Porsche; I found the 4 rows of 12 bit data.
- Test, and repeat!
The “Flow View” in SavvyCAN is a good place to start to analyze the data packet. It gives you a basic idea of how the data is moving, and which bits are changing. An important note about the way SavvyCAN saves logs; It saves them with a negative time in microseconds (It’s UNIX time) but it’s “subtracted”. This was really causing SavvyCAN to struggle for me when I loaded logs. None of the graphs were working correctly, and the playback wasn’t graphing the data. I wrote a quick Python script to subtract the time (minus a minus equals a plus) so that it was in order of microseconds. I’m using “SavvyCAN-Windows_x64” compiled for windows version 213, so you might not have this issue.
import pandas as pd df = pd.read_csv("YOUR_718_Drive.csv") #Show data so that you know it's working print(df.head(3)) #prints 3 heading rows print(df['Time Stamp'].dtype) #Grab the first timestamp from your file and add it here: df['Time Stamp'] += 1723750632962509 df.to_csv("YOUR_718_Drive_timestamp.csv", index=False)
The to-do list was all of the data that the AiM SOLO and MoTeC kids get for their Porsche GT4 Clubsport cars: Accelerator Pedal, Air Temp, Boost Pressure, Brake Pressure, Clutch Position, Coolant Temp, Engine Speed, Vehicle Speed, Lateral Acceleration, Longitudinal Acceleration, Intake Air Temp, Odometer, Oil Pressure, Oil Temp, Steering Angle, Steering Speed, Wheel Speed (FL, FR, RL, RR), Yaw Rate.
Next up was getting that data to a track day logger. I was super excited to discover that a product I already owned had the ability to capture CAN BUS data to my phone. During my discovery for this, I came across a project for the Toyota GR86 / Subaru BRZ that tapped into the CAN BUS like I did, and uses a OBDLink LX connected to a feature of RaceChrono to log CAN BUS messages on your phone. I wired up an OBD2 splitter that I could inject the CAN data into my OBDLink in a similar fashion.
The last step was adding and converting the units + math for each of the PID’s. RaceChrono has a guide to handle all of the units here.
Checkout how BTR Justin and the BTR Garage got this all working on their Toyota GR86!
Another drive and I confirmed I had access to everything. From Oil Pressure to Individual Wheel Speeds. This is NOT data from my laptop, this is data from the OBDLink LX Bluetooth sending CAN messages to RaceChrono Pro on my phone, and then later exported as a CSV. Viewing the data with MegaLogViewer HD here – because it can just take whatever CSV you can throw at it!
Here’s about 40 hours of work reverse engineering the German CAN BUS of the 718. If you use this DBC – please tip your hat to planetkris.com! – Kris
BU_: 718 BO_ 134 Steering: 8 718 SG_ LWI_Steering_angle : 16|13@1+ (0.1,0) [0|800] "Unit_DegreOfArc" 718 SG_ LWI_Steering_Speed : 31|9@1+ (5,0) [0|2500] "Unit_DegreOfArcPerSecon" 718 BO_ 257 ESP_02: 8 718 SG_ ESP_Yaw_Rate : 40|14@1+ (0.01,0) [0|163.82] "Unit_DegreOfArcPerSecon" 718 SG_ ESP_Lateral_accel : 16|8@1+ (0.01,-1.27) [-1.27|1.27] "Unit_ForceOfGravi" 718 SG_ ESP_Longitudinal_adj : 24|10@1+ (0.03125,-16) [-16|15.90625] "Unit_MeterPerSeconSquar" 718 BO_ 259 ABS: 8 718 SG_ Wheel_Speed_FL : 16|12@1+ (0.103,0) [0|325.12] "Unit_KiloMeterPerHou" 718 SG_ Wheel_Speed_FR : 28|12@1+ (0.103,0) [0|325.12] "Unit_KiloMeterPerHou" 718 SG_ Wheel_Speed_RL : 40|12@1+ (0.103,0) [0|325.12] "Unit_KiloMeterPerHou" 718 SG_ Wheel_Speed_RR : 52|12@1+ (0.103,0) [0|325.12] "Unit_KiloMeterPerHou" 718 BO_ 260 Transmission: 8 718 SG_ EPB_Clutch_Pedal : 32|8@1+ (0.4,-10) [8|92] "Unit_PerCent" 718 BO_ 261 Throttle: 8 718 SG_ Throttle_Position : 48|8@1+ (0.4,0) [0|1] "Unit_PerCent" 718 SG_ Throttle_RPM : 16|16@1+ (0.25,0) [0|10000] "RPM" 718 BO_ 262 Brakes: 8 718 SG_ Brake_PressureSig352 : 16|10@1+ (0.3,-30) [-30|276.6] "Unit_Bar" 718 SG_ Brake_Light : 26|1@1+ (1,1) [0|1] "On Off" 718 BO_ 263 Motor: 8 718 SG_ MO_Oil_Pressure : 16|8@1+ (0.04,0) [0|10] "Unit_Bar" 718 SG_ MO_Motor_Speed : 24|12@1+ (3,0) [0|12282] "Unit_MinutInver" 718 SG_ MO_Boost_Pressure : 39|9@1+ (0.01,0) [0|5.1] "Unit_Bar" 718 BO_ 779 Speed: 8 718 SG_ KBI_displayed_speed : 48|10@1+ (0.32,0) [0|325.12] "Unit_KiloMeterPerHour" 718 BO_ 1600 Temps: 8 718 SG_ Oil : 16|8@1+ (1,-60) [0|1] "Degrees C" 718 SG_ Coolant : 24|8@1+ (1,-75) [0|1] "Degrees C" 718 SG_ Intake_Temp : 8|8@1+ (0.75,-48) [-48|141.75] "Unit_DegreCelsi" 718 BO_ 1714 Diagnose_01: 8 718 SG_ KBI_Odometer_K : 8|20@1+ (1,0) [0|1048573] "Unit_KiloMeter" 718 SG_ UH_Year : 28|7@1+ (1,2000) [2000|2127] "Unit_Year" 718 SG_ UH_Month : 35|4@1+ (1,0) [1|12] "Unit_Month" 718 SG_ UH_Day : 39|5@1+ (1,0) [1|31] "Unit_Day" 718 SG_ UH_Hour : 44|5@1+ (1,0) [0|23] "Unit_Hours" 718 SG_ UH_Minute : 49|6@1+ (1,0) [0|59] "Unit_Minut" 718 SG_ UH_Second : 55|6@1+ (1,0) [0|59] "Unit_Secon" 718 BO_ 1719 Fuel: 8 718 SG_ KBI_Contents_Tank : 40|7@1+ (1,0) [0|125] "Unit_Liter" 718 SG_ KBI_Outside_Temp : 56|8@1+ (0.5,-50) [-50|75] "Unit_DegreCelsi" 718
Fun Fact: The odometer is only stored as a 20 bit number. This maxes out at 1,048,574 kilometers or ~651,553.7 miles. I expect the data would roll over, just like a physical odometer hitting 999,999.9 on a mechanical tens counter. Your mileage may vary!
- Updated Codes for RaceChrono Pro
- Here are the PID’s I use and their equations
- Brake Pressure (Reported in Bar needs kPa)
- PID 262 (bitsToUintLe(raw,16,10)*0.3-30)*100
- Clutch Position (0-100%)
- PID 260 (bitsToUintLe(raw,32,8)*0.4)-10
- Coolant Temp (Reported in °C)
- PID 1600 bitsToUintLe(raw,24,8)-75
- Engine RPM
- PID 261 bitsToUintLe(raw,16,16)*0.25
- Engine Oil Pressure (Reported in Bar needs kPa)
- PID 263 (bitsToUintLe(raw,16,8)*0.04)*100
- Engine Oil Temp (Reported in °C)
- PID 1600 bitsToUintLe(raw,16,8)-60
- Fuel Level (Reported in liters)
- PID 1719 bitsToUintLe(raw,40,7)
- Intake Temp (Reported in °C)
- PID 1600 (bitsToUintLe(raw,8,8)*0.75)-48
- Manifold Pressure (Reported in Bar needs kPa)
- PID 263 (bitsToUintLe(raw,39,9)*0.01)*100
- Speed (Reported in KpH needs m/s)
- PID 779 (bitsToUintLe(raw,48,10)*0.32)/3.6
- Steering Angle (Degrees)
- PID 134 bitsToUintLe(raw,16,13)*0.1
- Temperature (Reported in °C)
- PID 1719 (bitsToUintLe(raw,56,8)*0.5)-50
- Throttle Position (0-100%)
- PID 261 bitsToUintLe(raw,48,8)*0.4
- Wheel Speeds (Reported in KpH needs m/s)
- PID 259
- FL (bitsToUintLe(raw,16,12)*0.103)/3.6
- FR (bitsToUintLe(raw,28,12)*0.103)/3.6
- RL (bitsToUintLe(raw,40,12)*0.103)/3.6
- RR (bitsToUintLe(raw,52,12)*0.103)/3.6