Eavesdropping on the Single Wire CAN Bus – Part 4: GMLAN (Holden VE Commodore SWCAN).

“Several transmissions were beamed to this ship by rebel spies.” – Darth Vader.

In my most recent post, I tested the Bluetooth module for compatibility to the iPhone – which was successful. I then moved on to the more complex part of the project which was to see if I could capture GMLAN messages on the low speed (33.3Kbps) single wire CAN bus (SWCAN) of the car (Holden VE International). If you recall, the purpose of this was to enable me to listen for steering wheel track change messages, and then to feed those commands to my Bluetooth audio controller causing a Bluetooth song/track change. It took a bit longer to achieve a successful test than I was hoping for as there was very little documentation available and many forum posts with incorrect, misleading or incomplete information.

As such, in this post I’m going to be very detailed in the hope that it helps someone else achieve a similar goal. This post has four key sections – background information, CAN Bus Shield, ODB-II pin-out, and sample code. This post contains all of the information you will need to connect an Arduino with a standard CAN Bus Shield to a Holden VE Commodore International.

Background Information

For reference, the Holden VE Commodore uses a high speed (500 Kbps) and a low speed (33.3 Kbps) bus for connecting the internal systems. The high speed bus is used by the primary vehicle control systems (devoted to the operation of the car), and the low speed bus is used for the peripherals (like the stereo and steering wheel controls). I was able to gather this information from a useful article here. I then used the pin-out guide from here (second table), which indicated that the low speed SWCAN bus is actually on pin 1 of the car’s ODB-II socket – which is a pin not commonly used in ODB-II connectors. That information also lined up with the article mentioned above, so I was reasonably confident it would work. As such, I needed to build a plug with connectivity to pin 1 and pin 5 (signal ground) – and ignore all of the other pins.

CAN Bus Shield

Here’s the key component – the SparkFun CAN-Bus Shield. Even though this shield is designed for high speed CAN, we can drop the baud rate down to 33.3 Kbps and still operate.

CAN Bus Shield

You can ignore the DB-9 connector altogether – the SparkFun ODB-II to DB9 connector doesn’t have the ODB-II pin 1 connection so it doesn’t suit our purposes. We basically need to connect the CAN-H (High) and CAN-L (Low) lines on the shield to the appropriate pins on the car. Luckily, CAN-H and CAN-L are broken out on the shield – so we don’t need to try and connect to the pins on the DB-9. We need to connect CAN-H on the shield to pin 1 (SWCAN) on the car (via the ODB-II plug). We then need to connect CAN-L to ground on the shield and to pin 5 (signal ground) on the car (via the ODB-II plug). So we basically have 2 wires connecting the shield to the car as shown below…

CAN Bus Shield (Wiring)

ODB-II Connector

I also bought (a while ago), an ELM327 USB scanner from eBay. I disassembled it and extracted the ODB-II plug which I then desoldered ready for interfacing to my circuit. I connected a couple of leads to pin 1 (SWCAN) and pin 5 (signal ground). That ‘melted plastic’ look was actually like that when I removed it from the ELM327 – honest. White is pin 1, and orange is pin 5.

ODB-II Plug

Here they are side by side. Note, I’m just using a standard Arduino Uno R3 as the base board.
ODB-II Plug and Shield

Sample Code

As most of the libraries for the CAN Bus shield do not cater for a 33Kbps baud rate, I ended up finding an alternative library here. I also used this spreadsheet for a reference to confirm that I had identified the correct signals for the steering wheel controls. I opened one of the samples that came with the library, and then tweaked it slightly for the proof of concept. Note in the setup() function, the baud rate is specified as CAN_33K3BPS (33.3 Kbps).

#include <mcp_can.h>
#include <SPI.h>

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128];

#define CAN0_INT 2 // Set Interrupt to pin 2
MCP_CAN CAN0(10); // Set CS to pin 10

void setup()
{
  Serial.begin(9600);

  if(CAN0.begin(MCP_ANY, CAN_33K3BPS, MCP_16MHZ) == CAN_OK)
    Serial.println("MCP2515 Initialized Successfully!");
  else
    Serial.println("Error Initializing MCP2515...");

  CAN0.setMode(MCP_NORMAL);

  pinMode(CAN0_INT, INPUT);
}

void loop()
{
  if(!digitalRead(CAN0_INT))
  {
    CAN0.readMsgBuf(&rxId, &len, rxBuf);

    // 0x100d0060 = Wheel Controls
    if ((rxId & 0x1FFFFFFF) == 0x100d0060)
    {
      sprintf(msgString, "0x%.8lX,%1d,", (rxId & 0x1FFFFFFF), len);
      Serial.print(msgString);

      if((rxId & 0x40000000) != 0x40000000)
      {
        for(byte i = 0; i < len; i++)
        {
          sprintf(msgString, " 0x%.2X", rxBuf[i]);
          Serial.print(msgString);
        }
      }
    } 

    Serial.println();
  }
}

I connected it all together, turned the ignition on (for the stereo) and started using the track up/down buttons. As you can see in the screen below those messages were identified and displayed. The code itself is pretty messy as its one of the library example files tweaked slightly to ignore any messages not from the steering wheel (messages with an ID of 0x100D0060). It looks like the shield (the controller on the shield specifically) can be configured with its own filter to help me capture just the messages I’m looking for, but I’ll worry about that in a later build. For now, I’m just ecstatic that I can see the messages coming in.

For reference, and this is described in the spreadsheet, I’m receiving 4 byte messages (4 bytes of data anyway) like this: 0x00 0x00 0x01 0x00. The 0x01 indicates the track change wheel has been clicked up once. If I clicked it up a few times in quick succession, that 0x01 would be something like 0x03 (for 3 clicks). Likewise, a 0x1F indicates a single downward click, and a 0x1E indicates two downward clicks (counting down from hexadecimal 1F). A message of 0x01 0x00 0x00 0x00 would indicate the track change wheel had been pressed (its also a button). I’m going to use that for play/pause.

With that sample code running, any other button press on the steering wheel should display a message on the screen as well.

CAN Bus Shield Connected

With the Bluetooth and the CAN Bus now operational, I can put the complete prototype together and integrate it with the car. If the prototype is successful, I’m going to rebuild the device and mount it inside the stereo itself. The stereo has a lot of empty space inside with access to power, the CAN-H line (as the stereo is connected to the low speed CAN bus as well), and the AUX audio input. As such, the entire device will be self-contained in the stereo with no external cables.

Exciting times. On to Part 5…

~ Mike

9 thoughts on “Eavesdropping on the Single Wire CAN Bus – Part 4: GMLAN (Holden VE Commodore SWCAN).

  1. I really hope that you’ve saved me a little bit of googling, previously i ordered the same canbus board you have as shown above.. Now to find it again (most likely still in my glove box)…

    But seeing a you got yours working, you’ve given me some motivation to maybe tackle this again on the weekend…
    Lets hope my 2004 WL V8 Caprice does truly have the same gmlan..

    — A random thank you for blogging this.

  2. Hey, well it turns out that im running J1850 VPW… I remembered that after i assembled my obdII adapter (i got the one with no legs,,, that was wasted time unsoldering them from a dead board)..

    I have a elm327 IC floating around in my shed that i originally purchased, so i think it is time i find that and try it again.. The pinout did match, but yea i’ve got a 41kpbs speed or similar (cant remember at the moment). But either way your post still reminded me to get back onto it.. My next project is trying to customise the DIC interface.

  3. Hi Mike, could you please explain which function do you use to read a particular steering wheel control command? In particular, with the if ((rxId & 0x1FFFFFFF) == 0x100d0060) you filter all the incoming messages from the steering wheel controls but how do you select a particular action like, for instance, the volume up? Please help me.

    Thanks for your time.

    1. Howdy, in the example above, rxId is used to determine where the message originated (in this case – the steering wheel controls 0x100d0060). rxBuf then contains the data of the message itself. I then look at the data to determine if its a track up, track down, track enter, volume up, volume down command etc.

  4. Mike, first off, outstanding job writing all of this up! I was locked in from the beginning. I had already found several of the resources you linked, such as the GMLAN bible and a couple others. That is going to save a lot of time when it comes time for me o do my project.

    Here is my idea… (I’m not a good writer so bare with me) I will be replacing my stock stereo (2008 Silverado) with a Raspberry Pi with XBMC on it for a headunit. I haven’t decided which way to go next…a secondary RPi or Arduino for a GMLAN control board or build the functionality onto the XBMC RPi (it’s going to be dependant on CPU load an such after some testing). With the XBMC RPi I will be building a program to read engine codes, as well as an app for HVAC. Essentially the app will just send commands to the GMLAN either directly or the controller board (whichever route I go). The plan is to remove the Stereo and HVAC hardware controls completely and move it all to touch screen. I would also have reverse cam and various other features. What would be cool is if I can also bring in some OnStar functionality for free, such as remote start or door unlock from Internet. The RPi will be connected via a cellular shield so that I can also have Navigation via Navit package.

    Do you have any thoughts about using a single RPi setup vs a dual RPi or Arduino combo for the GMLAN communications, as well as any knowledge of wiring up a plug to do both GMLAN and diagnostic codes. I’d also love to talk offline if you’d have some time for me to pick your brain about this a little more. Not sure if you can see the email I’m posting from here, let me know.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s