Monday, February 11, 2019

Hacking my Volt with a Raspberry Pi - Part 2: History

Before the Volt

The project actually began in April 2017 (for my previous car, a 2013 Ford Fusion Energi), when I received my Macchina M2 beta unit. It started with just the Macchina, with a Bluetooth module (a Microchip RN42XVP) to connect to my phone. I added code to my all-encompassing Android app to display data gathered from sniffing the CAN bus and OBD2 queries. It also logs this data so I can review it later, and I can even display it along with my dashcam videos. I used one of the 12 volt I/O drivers on the back of the Macchina to power my dashcam - that way, I could not only turn the camera on and off from my phone app, I could record in the log the exact time that power was applied, and use that timestamp to synchronize the recorded data with the video. Over the next year I made a few minor improvements to the firmware, but didn't add any additional hardware.

Of course, when I got my Volt, I had to redo a lot of the reverse-engineering that I had done, because other than the standard OBD2 queries, everything going over the CAN bus was completely different. Eventually, I was able to pull all the same data that I could pull from my old car, and more.

Dashcam video auto-copy

Shortly after I got the Volt, I had an idea - what if I could automatically upload videos from my dashcam to my server when I get home instead of having to swap out the microSD card all the time? I had a plan. My dashcam will power on and start recording if it doesn't detect that it is plugged into a computer, otherwise it will go into mass storage mode, allowing the computer to access the microSD card.

I had a C.H.I.P. single-board computer that I got in December 2015, but had never found a use for, and it had a USB port, built-in WiFi, and a 3.3v TTL serial port on the GPIO pin block that I could use to communicate with the Macchina. All I had to do was figure out a way to connect and disconnect the data lines to control whether the camera would record or copy.

Well, it turned out that the C.H.I.P. is a piece of C.R.A.P. that wouldn't even power on after sitting in a box for 2 years. So, I decided to get a Raspberry Pi Zero W instead. This turned out to be a good thing in the end, because the Raspberry Pi is much better supported and documented.

In order to control the power to the camera, I just need a circuit that could control the +5v line of USB power. I built a circuit that connected a USB B port to a USB A port, with power going through a P-channel MOSFET controlled by a GPIO pin. To trick the camera into thinking that it wasn't plugged into a computer, I just had to effectively disable the USB port on the Pi by switching it into USB Device mode instead of Host mode. This required a change to the kernel module, since it wasn't designed to switch modes without changing the boot configuration.

Since the Raspberry Pi had built-in Bluetooth, I eventually realized I no longer needed the buggy Bluetooth module on the Macchina, which tended to drop characters and sometimes fail to power on. I could just have the Pi connect directly to my phone, using the same protocol I had developed.

The original power control board for the dashcam.

Climate controls

One thing that always bugged me about the Volt, and a lot of other cars in general, is that there are no physical buttons for the climate controls. Everything is controlled through the touchscreen, or flat-labeled touch-sensitive buttons on a panel. I had an idea - there are plenty of GPIO pins on the Raspberry Pi, and they even have built-in pull-up resistors, so I can easily add a few buttons that I can program to do whatever I want. All I had to do was watch the CAN bus and see what frames get sent when I press the virtual buttons, and I have full control over the heating and cooling system.

I came up with a design using 5 of these buttons, and a Rotary encoder (which also can be pressed as a 6th button). I managed to connect the entire board using only 5 GPIO pins, plus ground. The two pins of the rotary encoder are connected to dedicated GPIO pins, and the 6 buttons are connected in a tetrahedral pattern to the other 3 GPIO pins and ground. Everything is soldered to a solderable breadboard, which is cut down to size. I originally mounted it right below the center stack, beneath the volume control, but I found it was a lot easier to use if I mounted it horizontally just to the left of the gear selector (covering the PRNDL indicator - but the same indicator is visible on the main screen).

I assigned the blue button to switch between "Fan Only" and "Comfort/ECO" mode, like I used to be able to easily do in my old 2007 Ford Fusion. The green button would toggle an internal flag selecting between "ECO" and "Comfort" mode. The dial was multi-purpose - by default, it would control the radio volume, but I could press it once to switch it to controlling fan speed, or twice to make it control temperature.

Of course, there's no reason to stop at just controlling stuff on the car. Since the Raspberry Pi was connected was connected to my phone via Bluetooth, I could also have it control that, as well. The black button pauses and plays music on my phone, and I could switch the dial into a mode that would scroll through my music tracks. The red button would insert a marker into the timeline of the recorded data (in case I saw something interesting that I wanted to note later), and holding it down would cause the app to focus itself and bring the data view to the foreground. Holding the white button would cause the Pi to connect to my phone even when the car was off (for diagnostic purposes).

The button board before soldering and cutting to size
The button panel in its original location

A dedicated screen

One day, I was in an auto parts store and I happened to see a product intended to be used to add a heads-up display to a car. It's a cheap plastic unit that you can place a phone into, and it has a pop-up half-silvered mirror to project information directly in front of the driver. This got my attention - the mount for my phone in this car was in a spot that was too low and hard to see. I had an idea - if I could find a cheap LCD screen, I could plug it into the HDMI port on the Pi Zero, and write a program to display the data directly instead of going through my phone. I purchased one of these screens, and tried to use it with the cheap-o HUD adapter.

Unfortunately, it was a disaster. The screen had a poor vertical viewing angle, with light leakage causing another, brighter image of the screen to be visible in the windshield, making it difficult to see at night. In addition, during the day, it was extremely difficult to see at all. Rather than just give up entirely, I decided to compromise and just mount the screen directly between the steering wheel and the infotainment screen, where my phone mount had been on my old car. This way, the extra data would still be easily visible, even if I did have to glance over at it.

Now that I had an actual display, I could expand the functionality of the controls. I made the white button toggle an on-screen menu, allowing me to activate shortcuts that aren't important enough to have dedicated buttons, and pressing the red button now brings up a virtual keyboard that I can use to make a quick note. In keyboard mode, the dial selects a column on the virtual keyboard, and the white, black, dial, and blue buttons select a character from the current row. The green button accepts the current entry and closes the keyboard, while pressing the red button temporarily closes the keyboard while keeping the current text entry. I also added a beeper from an old multimeter, connected to a GPIO port, which could give me some feedback without having to look at the screen.

The improvised mount for the custom display. I re-used a plastic piece from the HUD adapter, since I had already drilled it and couldn't return it. The mount itself is a modified VoltPhone 3.

Dual dashcams

One thing I had been thinking about for a while was adding a rear-facing dashcam. This would complicate my setup, though - would the dashcams still record if I plugged them into a USB hub? Would the Macchina 5 volt output be able to power both of them? I decided to build a completely new power/interface board. Instead of hacking the USB driver kernel module, I would build a board with 3 data passthrough / power control circuits. I would plug the Pi into the B port on first one, then connect the A port to a USB hub. Then, two of the ports on the hub would be connected to the B ports on the other two, and each dashcam would be plugged into one of the A ports. That way, if I wanted to record, I can just cut power to the hub before I power on the cameras, and when I want to access the files, I just turn the hub on.

To power the dashcams, I bought a 3 amp 12v-5v converter, and connected it to a fuse tapper that I bought at an auto-parts store. I also added one more USB power-control circuit (without data) which I could use to turn the screen on and off. I added a manual override switch to this one, so I could turn the screen on to watch the Pi boot up.

Soon after, I realized that with all the extra stuff I was adding, the Pi Zero wasn't quite powerful enough. I bought a full-size Raspberry Pi Model 3 B+, and a case with a cooling fan. I also bought another dedicated 3-amp 12v-5v converter with a micro-USB plug to power it. It turns out my WiFi upload speeds were being hindered by the underpowered Pi Zero, limiting it to 2MB/s, but the Pi 3 B+ could upload at 8MB/s.

Current power control board
The power board with everything plugged in
Close-up of the Raspberry Pi 3 B+ in the case with everything plugged in

Remote control

Around June, my 3-month trial of OnStar expired. I was told that to access the Vehicle Status page on my phone, I would have to pay $20 per month for their cheapest plan. Well, I wouldn't stand for that! All I needed was a 3G/LTE modem, and a sim card for another line on my cell phone plan, which would cost... $20 per month. Well, shoot. Luckily, I found a cheaper option - Hologram Maker Edition. With as little data as I send, I pay next to nothing. I can use their API to send packets directly to the Pi, even though it does not have a public IP address. I ended up not using their standard SDK - I wrote my own monitor program to connect using PPPD, then set up the routing tables so that it doesn't attempt to send normal traffic over the very limited connection.

I had managed to sniff all of the commands sent by the OnStar module, so I could replicate all of its functionality, and more. I set it up to send a series of queries every 10 minutes while the car is off, then it sends a single binary packet to a listening server, which logs the data in a location where I can view it, and even sends me alerts when certain things happen (e.g. charging complete).

Up Next: Demonstration

3 comments:

SSinSD said...

Would you be willing to post and or share the CAN IDs you were able to reverse engineer?

Unknown said...

Do you have software and code to open the software? MI email es iocantera86@gmail.com. Thanks a lot.

Roman Sand said...

Do you plan to install instead of LCD for PI gvif box ?