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 |
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 |
CAN Bus Interface Cable |
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 |
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: EVtools.info - 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.
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!
No comments :
Post a Comment
Note: Only a member of this blog may post a comment.