Steering Wheel Keys to Car Stereo Adapter (CAN Bus -> Key1)
by MrNickBarker in Circuits > Arduino
6208 Views, 11 Favorites, 0 Comments
Steering Wheel Keys to Car Stereo Adapter (CAN Bus -> Key1)
A few days after buying a used car, I discovered that I can't play music from my phone through the car stereo. Even more frustrating was that the car had bluetooth, but only allowed voice calls, not music. It also had a Windows Phone USB port, but it would not work with an iPhone without a 60$ dongle.
Having replaced stereos on my previous cars, without much thought or research, I ordered a 40$ replacement stereo from a well known "cheap" website. The stereo came with a reversing camera, Car Play and a bunch of extra features, which seemed a much better value than the more expensive dongle which only does one thing.
After buying and painting a new faceplate, 3D printing a mount, and a whole lot of extra work (which could be an instructable by itself), I came to an unpleasant discovery. The steering wheel key commands were sent through CAN bus, but the stereo only had a Key1 input. Not being one to give up half way, I ordered a 60£ adapter, which turned out not to work. At this point I decided to make an adapter myself.
I am not an electrical engineer, I only have rudimentary electronics knowledge and this was a learning and exploration project for me. My advice would be to first check your vehicle specs and order a compatible radio, but if you are already stuck (as I was) follow along the instructable at your own risk.
Supplies
The adapter (approx. 15$)
- Arduino Pro Mini 5V (or a compatible board)
- MCP2515 CAN bus module
- 60x80mm perfboard
- X9C104 digital potentiometer 100K Ohm (depends on your stereo)
- DC-DC Step down regulator LM2596S 3-40V 3A
- Cable fuse holder + fuse (100-200 Ohm)
- Project box or 3D printer to print it
- Car stereo jacks (male + female)
- Soldering supplies, wires, etc.
Test helpers (not strictly needed but will make testing much easier)
- Arduino (any 5V board will do)
- MCP2515 CAN bus module
- Breadboard + jumpers
CAN Bus Sniffing
Instead of having a bunch of wires run around the inside of your car interconnecting a bunch of systems, some modern vehicles have pairs of wires running to each component. Information is sent as digital data packets through these wires, and all systems can read all messages. This is the CAN bus network (there can be multiple networks in your car, so all info might not be visible).
What we want to do, is to connect to the CAN bus network and "sniff" the data traffic. This way we can "see" when a steering wheel key is pressed. Each packet has an ID, which represents the vehicle subsystem that sent the packet, and the data which represents the system state. In this case we are trying to find the ID of the subsystem that sends the steering wheel key messages, and the data representation of each key.
If you are lucky you might find the values for your car somewhere online, and can skip this step.
This process is a bit involved and has already been explained in other places, so I'll just summarize it:
- Find the correct values for the CAN bus communication on your vehicle. For my car (a 2009 Fiat Idea) it was a 50KBPS baud rate, and an 8MHz clock speed.
- Connect to the CAN bus network using the CAN bus module and an Arduino in a "sniffer" configuration.
- Read the CAN bus values on your laptop using a tool such as https://github.com/alexandreblin/python-can-monito.... It will be very difficult to do so without it, as a lot of messages are sent even when the car is not doing anything.
- Press the steering wheel button and note the value changes. This can be a bit tricky as a lot of messages are sent and it might be hard to figure out which is which.
Here are two great article explaining the process in-depth:
- https://medium.com/@alexandreblin/can-bus-reverse-...
- https://www.instructables.com/id/CAN-Bus-Sniffing-...
In the end you should have the subsystem ID which we will use to listen for only the steering wheel CAN bus messages, and a list of hexadecimal values for the key commands. In my case the data looked like this:
ID | ID Hex | Byte 0 | Byte 1 | Button --------------------------------------------- 964 | 3C4 | 00 | 00 | No buttons 964 | 3C4 | 04 | 00 | SRC 964 | 3C4 | 10 | 00 | >> 964 | 3C4 | 08 | 00 | << 964 | 3C4 | 00 | 80 | Phone 964 | 3C4 | 00 | 08 | ESC 964 | 3C4 | 80 | 00 | + 964 | 3C4 | 40 | 00 | - 964 | 3C4 | 00 | 40 | Win 964 | 3C4 | 00 | 02 | Up 964 | 3C4 | 00 | 01 | Down 964 | 3C4 | 00 | 04 | OK
The subsystem ID is 3C4 (in this case), which is a hexadecimal number so we should write it like 0x3C4 in the Arduino sketches. We are also interested in bytes 0 and 1 (in your case there might be more bytes). These are also hexadecimal values, so they should also be written with a leading 0x.
If you convert the values to binary, you will notice that the bits don't overlap (for example + 0b10000000 and - 0b01000000) this is so multiple keys can be pressed at the same time.
I suggest building the sniffer with the materials listed in the "test helper" section, so you can reuse it later to simulate your car. This would save you from having to sit in your car the whole time while you are building and testing the adapter. You can use the provided sketch to act as the simulator. Modify "subsystemId", "data0", and "data1" with the values you sniffed out.
Downloads
Sending Commands to the Stereo
Before starting to build the adapter, it is best to first test if the stereo can receive commands.
I had a spare car battery, so I connected the stereo directly to it. If you have a 12V bench top power source, even better. Unfortunately I couldn't find much info online about the Key1 input on my unit, so I resorted to experimentation. I was not overly worried about burning out the stereo at this point, since it is relatively cheap, and this was my last ditch attempt to get it working with my car.
The stereo has a command learning screen, where it is possible to select one of two resistance values (1K and 3.3K) and to see the "voltage" value (0-255). "Voltage" is quoted because it is misleading. I spent a lot of time applying different voltages to Key1 with no luck. I also tried using different resistors to apply the voltage with no luck.
The breakthrough came when I tried touching the Key1 wire to the battery ground, which resulted in "voltage" dropping to 0. This in combination with different resistors would produce consistent "voltage" values on the learning screen.
Now that I knew how to send inputs to the stereo, I needed a way to send them from an Arduino. At this point I haven't heard of multiplexers, which along with some resistors, might have been a faster and more reliable solution (I'm still not sure if this is feasible), so I used a digital potentiometer. At first I had issues getting the digital pot to work, until I figured out that I need to wire it up as a rheostat to act as a variable resistor instead of a voltage divider. Basically I had to connect the RH and RW terminals.
Besides the resistance, timing was crucial. If the resistance drop is too short, the command will not be registered. If it's too long it might get registered multiple times. A 240ms drop, followed by a 240ms delay until the next command worked pretty reliable for my stereo. While that seems like very little time, it means that we can send a maximum of 2 commands per second, which is noticeable if you are trying to rapidly turn the volume up or down. I tried playing around with different timings and patterns, which did increase the speed but was not very reliable. If you have any ideas on how to improve this please leave them in the comments.
Before proceeding any further, I suggest building a prototype to check if your stereo accepts the same kind of input. Even if it accepts different voltages the adapter should work with slight alterations to the wiring and the Arduino sketch.
Building the Adapter
After testing all of the components separately, and trying them together on a breadboard, it was time to give them a more permanent home. This took a few hours of laying out components and soldering.
In the top left is the step down regulator, which transforms 12V from the car battery, to 5V which can be used by the other components.
In the bottom left is the CAN bus module, which reads values from the car's CAN bus network and forwards them to the Arduino.
In the top right is the digital potentiometer (wired as a rheostat) that acts as a variable resistor between the ground and the stereo's Key1 input.
In the bottom right is the Arduino, which acts as the brains of the adapter, transforming CAN bus messages into resistances which are read by the stereo.
On the 12V input is a 150mA fuse, that most likely won't protect the circuit, but is there to prevent a fire in case of a short.
The Software
After downloading, put all three .ino files in a single folder. That way will all be a part of the same sketch and get deployed to the Arudino together.
You also need to add the required libraries to the Arduino IDE. To do this, download the following files:
https://github.com/autowp/arduino-mcp2515/archive/...
https://github.com/philbowles/Arduino-X9C/archive/...
then add both of them by going to Sketch > Include Library > Add .Zip Library...
CanBusStereoAdapter.ino
Basic setup is performed in this file.
Key command CAN bus values are defined at the top. Unless you have the same car as me, you'll most likely have to put in your own values. You can use the hexadecimal values from the sniffer, I used binary so it's easier to see that there are no accidental overlaps in the bits.
All cars don't have the same steering wheel commands, so feel free to remove, add, or edit the defined values.
Don't forget to replace your subsystem id in "STEERING_ID".
CanBus.ino
This file sets up the CAN bus listener, interprets the packets, and puts the resistance values into a circular buffer.
Adjust the CAN bus configuration in the "setupCanBus" function to suit your car.
We use a circular buffer because, as mentioned earlier, the steering wheel command input is much faster than the stereo input. This way we don't miss any commands while the digital potentiometer is doing its thing. If we input too many commands the oldest ones will get discarded first, since they are the least important. This also allows us to handle the case when multiple buttons are pressed, since the stereo input only accepts a single value at a time.
If you've changed any of the command definitions in "CanBusStereoAdapter.ino" you'll also need to update them in the "handleMessageData" function. "handleMessageData" checks if the provided CAN bus data frames contain any of the known commands by using a bitwise AND operation.
For example, if I have pressed >> and + at the same time which will give us a data frame with a value of 0b10010000. >> (for my car) is 0b00010000 in binary, and + is 0b10000000.
--------------- >> -------------- + ------------- << ----- data0 | 0b10010000 | 0b10010000 | 0b10010000 command | AND 0b00010000 | AND 0b10000000 | AND 0b00001000 result | = 0b00010000 | = 0b10000000 | = 0b00000000
Here we can see that the result of the AND operation will be greater than 0 if the command is present in the data frame. So all we have to do is to check for {data frame} & {command value} > 0, for each command we defined.
Keep in mind that each data frame contains different commands, so it's ok if the command values are the same, as we are checking them against their own frames. In my example both << and ESC both have the same value 0b00001000 (0x08), but << is in data0 and ESC in data1.
After we have determined that a command is present in a frame we add a digital pot value to the circular buffer. The values range from 0 to 99, but I have noticed that the "voltage" read by the stereo is not linear so test out the values for yourself.
DigitalPot.ino
This file pops values out of the circular buffer and sends them to the digital pot to execute. In my case "pot.setPotMin(false);" will increase the resistance to maximum, which the stereo will read as maximum "voltage". Your stereo might require you to set the digital pot to minimum, so test it out.
The Project Enclosure
I have a 3D printer so I decided to print a two part enclosure for my adapter. I have included a Fusion 360 file which you can edit, and gcode files that will fit a 60x80mm perfboard.
If you don't have access to a 3D printer, you can use a ready-made project enclosure or a sturdy container.
Final Thoughts
I initially planned on the adapter being connected to constant power and waking up on certain CAN bus messages, since my car doesn't have an ignition wire in the stereo compartment. I later decided against it as I didn't want to risk draining the battery and worrying about the adapter while I'm away from the car. I used a car fuse box splitter to run an ignition wire and not have to complicate the adapter further.
From my tests the power consumption is 20-30 mA. I got it down to 10 mA in sleep mode, and could go even lower by removing the LEDs from the components, but I decided not to bother with it since it will only run while the car is running.
I am pretty pleased with the end result. The response time is reasonable, and it rarely misses commands.
Even though my time investment was far greater than the cost of the commercially available adapter (which didn't work), the knowledge I gained is invaluable.