Sunday, February 18, 2018

Groking the Cadillac ELR

Those who know me well know that I have a passion for electric vehicles (EVs). I own a Tesla Model S, have driven it across the US and Canada twice, and am currently designing an electric bicycle with design cues taken from larger EVs and based on lessons learned from my electric skateboard project. I really can not scratch the itch to drive and learn more about EVs.

Last fall I purchased a Cadillac ELR to keep my Tesla company in the garage. I am a huge fan of this car for the striking good looks and the fact that it is a 2-door coupe. I had also never experienced a PHEV (Plug-In Hybrid Electric Vehicle) and really wanted to try out the GM Voltec platform. The prices of the Cadillac ELR are plummeting due to their relatively short-lived time on the market and the fact that they are a little unknown in the eyes of the average consumer.

Cadillac ELR at Cardinale GMC in Seaside, California
I did not buy this car with the intention of merely letting it sit in the garage though. One of the great features of the Tesla Model S is that it lets you "nerd out" with all kinds of stats about your trip. You get total energy consumption in kWh, efficiency in Wh/mile, range estimates based on change in elevation/speed and a whole lot more that makes driving a true delight.

The Cadillac ELR (and Chevy Volt - they share the same powertrain and a similar UI) tend to shield you from these details. I wanted more and without having to do quick mental math based on the limited information available from the built-in infotainment system.

Speed, Distance, Total Energy and Wh/mi Top | State of Charge (SoC) kWh Left | Speed mph Right
I spent about a week decoding traffic on the CAN bus of the ELR in an effort to find a few signals: speed, state of charge (SoC) in kWh, and odometer or a trip of some sort. Once I found these fields, I built a UI with PyQt to render statistics in real-time.

CAN Bus Interface Cable
I packaged it all up into a neatly organized cable and used a Sony Vaio UX to read data from the CAN buses and visualize it on a small display. Continue reading for more details.

Decoding the CAN bus(es)

The first thing I did before spending any time on a fancy UI was make sure that I would be able to read the required data from a CAN bus in the car. This vehicle has two OBD-II-style connectors: one is in the drivers foot well and another in the passenger foot well.

I decided to use the Viewtool Ginkgo USB-CAN interface. Unfortunately, Linux support comes in the form of a binary blob shared object, but I was able to make it work. I wrote a wrapper around this in C to make it easier to call from Python with CFFI. Before ordering any connectors and spending time building a neat cable, I just probed the connectors using spade connectors with one leg removed.

"Half-Spade" Connectors, Inserted into the OBD-II connector. It's a great fit!
I started out with the OBD-II connector in the drivers foot well. I went for a brief drive around my neighborhood, stepping heavily on the accelerator in order to generate some waveforms that I can try to reason with.

I wrote a python script that takes all of the captured data and for each CAN address tries to interpret the packets in a variety of ways. Data is sent on the bus in "network-order", aka big-endian. If a CAN packet contains a multi-byte word, it is sent most-significant byte first. This is also obvious from just inspecting the captured data in a hex editor. The bits most frequently change in the later-received bytes.

The script tries to parse the bytes in a few ways:
  • Each byte individually
  • First byte << 8 | second byte (and third/fourth, fifth/sixth, etc)
  • First byte << 24 | second byte << 16 | third byte << 8 | fourth byte (and fifth/sixth/etc.)
Each of these interpretations are plotted with matplotlib on a separate plot. This generates about 500 images for each trace of CAN data on the bus. It is very easy to open all of them with an image viewer and cull off the obviously useless messages.

Contents of 0x348 plotted
The script outputs a huge number of interesting plots like the one above. This one is clearly speed in units of 0.01mph given that it correlates with my short drive around the block.

There are a lot of less interesting plots to wade through, such as the following:

Contents of 0x589 plotted
I have no idea what this is. It could be a misinterpreted packet or something I just don't understand about the vehicle. In any event, it is easy to say that this is neither speed nor state of charge. It is quite easy to search through a few hundred of these in just a few minutes sorting out the unknown packets from the packets where some further interpretation might yield a useful data point.

What data exists?

The short answer is "a lot". I found curves that vaguely resemble voltage, something that looks like temperature, throttle position and I even managed to decode GPS latitude and longitude which was super interesting.

The GPS lat/long are encoded as a 31-bit signed milliarcsecond values. I had to extend the sign to the 32nd bit in order to get the value to match to my current location. Super unusual, but very interesting. The GPS is of particular interest because it may be quite precise if it is performing dead reckoning. It would allow the location to be very accurate even in locations with poor GPS signal strength such as urban canyons or long tunnels.

Enough about GPS though, I was able to find all of the data that I wanted between the two OBD-II ports so here's a quick breakdown:
  • Primary, 0x3E9, bytes 0 - 1 Speed in 1/100mph
  • Primary, 0x1A1, byte 6, Throttle Position Percent, 0-254
  • Primary, 0x32A, bytes 0 - 3 GPS Latitude, 31-bit signed milliarcseconds
  • Primary, 0x32A, bytes 4 - 7 GPS Longitude, 31-bit signed milliarcseconds
  • Primary, 0x120, bytes 0 - 3, Odometer in 1/100mi
  • Alt, 0x20A, byte 7, State of Charge Percent, 0-254

Lots of data analysis :]

Improving the Hardware

Once I had proven that the necessary data was available, I ordered two OBD-II connectors and wired them up. The primary OBD-II connector is just the standard ISO pinout:

  • Pin 4 - Ground
  • Pin 6 - Can High
  • Pin 14 - Can Low

The secondary connector is proprietary and I found some details on another webpage: - Chevy Volt OBD2 CAN Data. The alternate connector uses the following pinout:

  • Pin 3 - Can High
  • Pin 4 - Ground
  • Pin 11 - Can Low

Incomplete Alternate OBD-II Connector
I made liberal use of heat shrink tubing and nylon cable sheathing to keep things neat and professional looking. I also printed out some labels for keeping track of which connector each cable mates with.

Neatly Labelled Connectors :]
I left some slack in the cables to allow positioning the CAN to USB adapter on the floor. The last missing component is on the way: a RAM Sony Vaio UX windshield mount.

CAN Bus Monitor Connected

Displaying the Data

I opted to use PyQt in order to build a simple interface for rendering the UI. I started with a basic setup like the following:

Speed Plot and Non-Functional Quit Button
I needed just a simple plot in order to test the real-time plotting implementation. I want this application to run full screen so a quit button of sorts is necessary.

After some iteration, I arrived at the more refined UI below. I use white text on a black background to keep the brightness down for night-time driving. The high-contrast is highly readable. The small EV symbol in the top left can be tapped to quit.

More Refined UI
At this time, state of charge and speed are plotted with other numeric statistics shown above: current speed, distance traveled, energy used and Wh/mi. I would like to continue iterating on the design and add a histogram of speed, which is handy for hypermiling. It also needs a reset button and labels for the displayed statistics.

I can also build a simple range estimator based on current efficiency and rated state of charge. Different algorithms can be used to try to make a more accurate estimate. Another interesting UI feature would be a warning for accelerations that are too fast. This would coach me into driving more efficiently.

Does it work?

Of course it does! Here is a screenshot from a recent hypermiling run from Mountain View, CA to the Golden Gate Bridge.

Hypermiling from Mountain View to Golden Gate Bridge
The Cadillac ELR has a rated range of 35 miles but I was able to push it to 44 in this case with some energy to spare. It is not an incredible run, but cool nonetheless.

Sony Vaio UX Resting on the Dash
It was quite cold with single-digit Celsius outdoor temperatures. The tires could use a little more air too. They are at 35-36psi now, but could stand to be increased to 40psi for further efficiency improvements.
Cadillac ELR (the car that is more door than car), Charging up for another adventure
Overall, the ELR is a really fun car to drive. These stats scratch my itch to see more metrics about my driving. Maybe I will take another pass at the data and find some more useful data points to render.

In the meantime, I look forward to the arrival of my windshield mount and more adventures/projects with this car. Thanks for reading!


  1. i love this car!!! truly the first beautiful electric car unlike the Leaf or even Prius.
    first off, some questions...what year, how much mileage, and amount after tax you paid for it?

    like you, im fascinated with EV. got an electric skateboard to have fun with the boys. got an electric stand up scooter for daily commute. i think the scooter is ideal because the wheels can go over any terrain. with the handlebar, anyone can feel confident not falling or taking a nose dive. it can fold down easily to be tucked away when not in use.

  2. Now can you change the software to get the extra performance of the 2016 model? ;)

  3. I'd really like to have a display showing the state of the 2 traction motors, and the clutches that control them. Plus overall KW used, KW for climate control, battery SOC, etc.