Since the A/C can be modified via the car media interface (SYNC) we went straight for the MS bus.

But how do we get our computer to be able to read and write CAN packets? The answer is SocketCAN, a set of open source CAN drivers and a networking stack contributed by Volkswagen Research to the Linux kernel.

We can connect 3 wires from the car, GND, MSCANH, MSCANL to a Kvaser Leaf Light HSv2 ($300 on Amazon) or to a CANable ($25 on Tindie) and get a computer with a recent Linux kernel to load the CAN bus as a network device.

modprobe can

modprobe kvaser_usb

ip link set can0 type can bitrate 1250000

ifconfig can0 up

After it is loaded, we can use candump can0 and start looking at the traffic:

can0 33A [8] 00 00 00 00 00 00 00 00 can0 415 [8] 00 00 C4 FB 0F FE 0F FE can0 346 [8] 00 00 00 03 03 00 C0 00 can0 348 [8] 00 00 00 00 00 00 00 00 can0 167 [8] 72 7F FF 10 00 19 F8 00 can0 3E0 [8] 00 00 00 00 80 00 00 00 can0 167 [8] 72 7F FF 10 00 19 F7 00 can0 34E [8] 00 00 00 00 00 00 00 00 can0 358 [8] 00 00 00 00 00 00 00 00 can0 3A4 [8] 00 00 00 00 00 00 00 00 can0 216 [8] 00 00 00 00 82 00 00 00 can0 3AC [8] FF FF FF FF FF FF FF FF can0 415 [8] 00 00 C8 FA 0F FE 0F FE can0 083 [8] 00 00 00 00 00 01 7E F4 can0 2FD [8] D4 00 E3 C1 08 52 00 00 can0 3BC [8] 0C 00 08 96 01 BB 27 00 can0 167 [8] 72 7F FF 10 00 19 F7 00 can0 3BE [8] 00 20 AE EC D2 03 54 00 can0 333 [8] 00 00 00 00 00 00 00 00 can0 42A [8] D6 5B 70 E0 00 00 00 00 can0 42C [8] 05 51 54 00 90 46 A4 00 can0 33B [8] 00 00 00 00 00 00 00 00 can0 42E [8] 93 00 00 E1 78 03 CD 40 can0 42F [8] 7D 04 00 2E 66 04 01 77 can0 167 [8] 72 7F FF 10 00 19 F7 00 can0 3E7 [8] 00 00 00 00 00 00 00 00 can0 216 [8] 00 00 00 00 82 00 00 00 can0 415 [8] 00 00 CC F9 0F FE 0F FE can0 3A5 [8] 00 00 00 00 00 00 00 00 can0 3AD [8] FF FF FF FF FF FF FF FF can0 50B [8] 1E 12 00 00 00 00 00 00

However, this is equivalent to looking at the amplitude of a sound signal, it is very hard to characterize what’s going on and discover patterns. We need the equivalent of frequency analysis for this problem, and thankfully it exists and is called cansniffer. cansniffer shows a list of IDs and helps focus only on what changes in the data section of the CAN frame. As we figure things out about specific IDs, we can use it to filter out what we don’t need and only enable what we think is related to our problem at hand.

Here is an example of cansniffer on the MS bus. We are filtering out everything and only letting CAN id 355 , 356 and 358 show up. Meanwhile, I am pressing the temperature adjust buttons on the car and we can see the 001C00000000 that represents the button push pop up at the very end.

The next step is to connect the A/C functionality to our PC running inside the car. The PC runs the Robot Operating System (ROS), and fortunately there is a module that makes this easy for us since we’re using SocketCAN. socketcan_bridge turns our CAN frame into a message in a ROS topic. Great!

Here is an example of how the decoding is done:

if frame.id == 0x356:

raw_data = unpack('BBBBBBBB', frame.data)

fan_speed = raw_data[1] / 4

driver_temp = parse_temperature(raw_data[2:4])

passenger_temp = parse_temperature(raw_data[4:6])

The resulting data is stored in a CelsiusReport.msg:

bool auto

bool system_on

bool unit_on

bool dual

bool max_cool

bool max_defrost

bool recirculation

bool head_fan

bool feet_fan

bool front_defrost

bool rear_defrost string driver_temp

string passenger_temp

After pressing all the relevant buttons in the car we end up with a list:

CONTROL_CODES = {

'ac_toggle': 0x5C,

'ac_unit_toggle': 0x14,

'max_ac_toggle': 0x38,

'recirculation_toggle': 0x3C,

'dual_temperature_toggle': 0x18,

'passenger_temp_up': 0x24,

'passenger_temp_down': 0x28,

'driver_temp_up': 0x1C,

'driver_temp_down': 0x20,

'auto': 0x34,

'wheel_heat_toggle': 0x78,

'defrost_max_toggle': 0x64,

'defrost_toggle': 0x4C,

'rear_defrost_toggle': 0x58,

'body_fan_toggle': 0x04,

'feet_fan_toggle': 0x0C,

'fan_up': 0x2C,

'fan_down': 0x30,

}

And we can then just send strings to the ROS node and have it translate it to the car’s specific codes: