Home‎ > ‎

OnStar GPS Hacking

All GM vehicles built since 2007 (and many built before then) are equipped with OnStar modules, which have a built-in GPS receiver.  For a long time, various folks have wanted to use that GPS receiver for their own purposes.  In older OnStar modules, it was possible to take apart the module and access the GPS receiver’s serial port.  This allowed the extraction of GPS information from the module.  The downside was that the GPS would no longer work with OnStar.  However, if you had a vehicle with OnStar but weren’t subscribing to the service, this was a viable option.

The cost of GPS units has plummeted since OnStar was first introduced.  Therefore, hacking the OnStar system to extract GPS information has lost a lot of the appeal.  It’s really not worth the time and effort.  However, it does seem kind of silly to have a GPS on your car and not be able to use it.  So, for those who want to waste their time and effort, read on.

Newer OnStar modules use a much more integrated GPS and inertial navigation chip, so accessing the serial port isn’t practical any more.  However, the OnStar module communicates over an in-vehicle data link known as a Controller Area Network (CAN).  The GM version of CAN is called GMLAN and operates at 33.3kbps using a single wire CAN interface.  Note that there is more than one CAN network in a typical vehicle.  A higher speed network is used to control powertrain functions, and a lower speed CAN or LIN network is used for comfort and convenience functions.  The 33.3 kbps GMLAN bus is the low speed "comfort and convenience" network.  I was curious as to whether the OnStar module broadcast GPS information onto this CAN network.  If so, it would be possible to use a small microcontroller to convert the CAN traffic into a traditional serial data stream in an NMEA of similar data format. This could be done without disabling the GPS for OnStar use.

First, I had to find the low speed GMLAN bus.  I found the OnStar module under my dash and removed it and took it apart.  I found the CAN transceivers on the circuit board (there were several), and found which pins those were attached to.  My monitoring those pins I was able to figure out which ones carried CAN traffic.  Next time I happen to rip my dash apart I'll take some pictures of the module and pinout.

UPDATE:  Turns out the GMLAN bus is available on pin 1 of the OBD-II service connector.  No need to rip your dash apart to access the data!

I did some monitoring of the low speed GMLAN CAN bus while on a road trip.  This can be done in real-time using professional tools from companies like the Dearborn Group, Vector Cantech, or National Instruments.  For hobbyists, you can use CAN to RS-232 converters such as the CAN232 or the OBDLink, but you’ll probably need to log the data and post-process it. 

When I did my analysis I was using a two-wire CAN adapter (remember GMLAN is single wire CAN) and tied the CAN_LO line to chassis ground.  This worked well enough to capture the data, but I did get a lot of error frames on the bus while connected.  Your mileage may vary. 

Below is an example of a data capture from the CAN bus showing a bunch of different messages with unknown content. 

 


I was able to do some real-time analysis, which made things a little easier.  Since I don’t know the content of any of the GMLAN messages, I had to do some detective work.  First, I looked for messages whose data stayed fairly constant while the vehicle wasn’t moving but changed when the vehicle started to move.  Remember, I’m trying to find latitude and longitude information.  However, there are many other parameters such as engine speed or ground speed that may also move when the vehicle moves.  What I needed to find was data that continued to increase or decrease as I drove in a particular direction.  My hunch was that I’d find one parameter that increased as I drove north and decreased as I drove south, and another parameter that increased as I drove west and decreased as I drove east.  Lo and behold, I found an eight-byte message that contained exactly that.  Four bytes for N/S, and four bytes for E/W.

The CAN message ID containing the GPS info was 0x100AA097.  I found that bytes 0-3 corresponded to North/South movement bytes 4-7 corresponded to East/West movement. 

The graph below shows four of these bytes plotted on the same scale.  You can see that the least significant byte decreases very quickly and rolls over often (when it reaches a value of 255 it rolls back to 0).  The next most significant byte decreases more slowly, and so on.  My guess was that if I were to combine these four bytes into a single 32 bit number and compare that number to the respective latitude or longitude, I’d be able to come up with a transfer function to convert the raw numbers into a lat/long position.  Doing this is fairly simple.  Just pick two locations a good distance away from each other and record the raw 32 bit numbers along with the latitude and longitude from another GPS receiver at the locations.  Use some simple math (think y = m*x+b) to find the transfer function.  I recorded my GPS information in decimal degree format for ease of calculation. 

 

Here are the transfer functions I’ve tentatively arrived at:

 

Latitude(in decimal degrees) = 2.7777777778e-007*[32 bit number]

Longitude(in decimal degrees) = 2.7777777778e-007*[32 bit number] -596.52325

I have not validated these transfer functions over a wide area of the country, and who knows how they would function in other hemispheres.  However, it wouldn’t take much effort to improve this transfer function once more data is available.

UPDATE:  A reader of this site was kind enough to provide the actual message definitions for GPS-related info on the GMLAN bus.  This will eliminate a lot of snooping, guessing, and checking.  Here are the message definitions:


Name CAN ID Start Byte Start Bit Length Signed/Unsigned Range Conversion
Year $100A60xx 0 7 8 bits Unsigned 2000 - 2255 Year = X * 1 + 2000
Month $100A60xx 1 3 4 bits Unsigned N/A Month = X
Day $100A60xx 2 4 5 bits Unsigned 0-31 Day = X
Hour $100A60xx 3 5 5 bits Unsigned 0 - 31 h Hour = X
Hours Valid? $100A60xx 3 0 1 bit Unsigned N/A Valid = 0
Minutes $100A60xx 4 6 6 bits Unsigned 0 - 63 min Minutes = X
Minutes Valid? $100A60xx 4 0 1 bit Unsigned N/A Valid = 0
Seconds $100A60xx 5 6 6 bits Unsigned 0 - 63 s Seconds = X
Seconds Valid? $100A60xx 5 0 1 bit Unsigned N/A Valid = 0
Latitude Valid? $100AA0xx 0 6 1 bit Unsigned N/A Valid = 0
Latitude $100AA0xx 0 5 30 bits Signed -536870912 to 536870911 milliarcsecond 1 milliarcsecond = 1 bit
Longitude Valid? $100AA0xx 4 7 1 bit Unsigned N/A Valid = 0
Longitude $100AA0xx 4 6 31 bits Signed -1073741824 - 1073741823 millinarcsecond 1 milliarcsecond = 1 bit
Heading $100AC0xx 0 3 12 bits Unsigned 0 - 409.5 deg Heading (degrees) = X * .1
Speed Valid? $100AC0xx 2 4 1 bit Unsigned N/A Valid = 0
Heading Valid? $100AC0xx 2 3 1 bit Unsigned N/A Valid = 0
Elevation Valid? $100AC0xx 2 2 1 bit Unsigned N/A Valid = 0
Dilution of Precision Rating $100AC0xx 2 1 10 bits Unsigned 0 - 102.3  DOP = X * .1
Speed $100AC0xx 4 7 8 bits Unsigned 0 - 255 km / h Speed (km/h) = X
Elevation $100AC0xx 5 4 21 bits Unsigned -100000 - 1997151 cm Elevation(cm) = X - 100000

So, now that we have a tentative transfer function to extract the raw information from the CAN bus and convert it to lat/long, the next step is to build a converter to read the information off of the CAN bus and convert it to RS-232 in a format (such as NMEA) that can be read by PC mapping software. 

I started to do this with a small and inexpensive SBC28PC-IR2 board from Modtronix that uses a PIC16F2685 and a CAN transceiver because I already happened to have one.  However, the CAN transceiver I had was a two-tire transceiver, where the GMLAN is a single wire CAN bus.  I’ve had some success in bench testing, but haven’t yet tried it on a vehicle.  I also haven’t yet finished the code to the point where it will compute the checksum for the NMEA data sentence.

However, this as far as the project is likely going to go for me, so I thought I’d at least post it here for others to build upon if they want. 

Here is the “guts” of the C code I wrote for converting the CAN data into an NMEA data sentence.  This code is will not run “as-is” on any microcontroller.  You’ll need to write the code to actually get the CAN messages off the bus, etc.  This code starts once you have that accomplished. 


latint = Message.Data[0];
latint = latint<<24;
temp = Message.Data[1];
temp = temp<<16;
latint = latint | temp;
temp = Message.Data[2];
temp = temp<<8;
latint = latint | temp;
latint = latint | Message.Data[3];
gmlat = latint;
lat = gmlat/3600000;
latdeg = lat;
latdegfloat = latdeg;
latmin = (lat-latdegfloat)*60;
latminchar = latmin;
latmins = (latmin-latminchar)*1000;
latminsint = latmins;
longint = Message.Data[4];
longint = longint<<24;
temp = Message.Data[5];
temp = temp<<16;
longint = longint | temp;
temp = Message.Data[6];
temp = temp << 8;
longint = longint | temp;
longint = longint | Message.Data[7];
gmlong = longint;
longitude = gmlong/3600000-596.52325;
longdeg = fabs(longitude);
longdegfloat = longdeg;
longmin = (fabs(longitude)-longdegfloat)*60;
longminchar = longmin;
longmins = (longmin-longminchar)*1000;
longminsint = longmins;

printf("$GPRMC,,A,%02u%02u.%u,N,%03u%02u.%u,W,,,,*CS\n\r",latdeg,latminchar,latminsint,longdeg,longminchar,longminsint);

//still need to determine N or S, E or W

/*
//compute NMEA checksum
sprintf(nmea,"GPRMC,,A,%02u%02u.",latdeg,latminchar);
printf("$%s",nmea);
for(k=0;k<8;k++)
{
    checksum = checksum ^ nmea[k];
} //end for
sprintf(nmea,"%u,N,%03u%02u.",latminsint,longdeg,longminchar);
printf("%s",nmea);
for(k=0;k<8;k++)
{
    checksum = checksum ^ nmea[k];
} //end for
sprintf(nmea,"%u,W,,,,",longminsint);
printf("%s",nmea);
for(k=0;k<8;k++)
{
    checksum = checksum ^ nmea[k];
} //end for
printf("*%s\n\r",checksum);
*/





Comments