I’m currently busy creating an Arduino steering wheel adapter between my ’05 Pontiac GTO steering wheel controls and the Parrot Asteroid Smart Android-powered head unit. Doing this because my car isn’t supported by Parrot’s Unika steering wheel control interface, and also because it’s a fun project that’s going to cost me almost nothing and allow for better customization.

Resistance Ladder

The steering wheel controls of my Pontiac GTO are set up as a resistance ladder. Pressing each button changes the resistance between two wires going to the stereo. This is actually a very simple circuit. Each button has a resistance value, like so:

Button Function   Resistance (Ohm)
Mute   284
Volume Up   164
Volume Down   82
Mode   1474
Next Track   794
Previous Track   464


And when no button is pressed, the resistance is about 3674 Ohm. You can see where this is going. We need to teach Arduino how to interpret resistance values.

Test Rig

I don’t have a bunch of push buttons that can go on a breadboard, so I made my “steering wheel button simulator” test rig using one push button and a bunch of dip switches. I’ve added six resistors of various values to simulate different buttons (all under 2k Ohm) and a 5.6 KOhm reference resistor. Here’s a picture:

steering-test-rig

steering_bb

Ok so I know it’s a bit more complicated than what it should be, but I’m lacking parts here, so bear with me.

Arduino Code

The circuit you see above is basically a basic voltage divider. You connect the known resistor (5.6K in this case) to the ground and an unknown resistor (one of the six pictured) to power. You connect the two resistors together and measure the voltage between:

G —— R: 5.6K —— To Arduino —— R: unknown —— Vcc (+5V)

None of these values have to be exact. As you can see in the code, everything is configurable. You can run any voltage you want (that the Arduino will support) and any reference resistor. I picked 5.6K because I like that number, it reminds me of strawberries.

Constants

We’re going to need some constants. Here’s what I have set up for my ’05 Pontiac GTO (resistance values currently match the test rig though):

/* !!! Comment out to stop serial prints.  Do this before flashing HID firmware !!! */
#define DEBUG

#define TOTAL_BUTTONS 6          // Total buttons on the steering wheel
#define NONE -1                  // When no button is pressed
#define MUTE 0                   // Index corresponding to the MUTE button resistance
#define VOLUME_UP 1              // Index corresponding to the VOLUME_UP button resistance
#define VOLUME_DOWN 2            // Index corresponding to the VOLUME_DOWN button resistance
#define MODE 3                   // Index corresponding to the MODE button resistance
#define NEXT 4                   // Index corresponding to the NEXT button resistance
#define PREVIOUS 5               // Index corresponding to the PREVIOUS button resistance

#define TOLERANCE_PERCENT 10.f    // Match tolerance
#define RESISTANCE_PIN 0          // Analogue Input on Arduino
#define R_KNOWN 5600.f            // The known resistor
float VOLTS_IN = 5.f;             // Vcc (+5 Volts)

/* Button resistance values and indexes {MUTE, VOLUME_UP, VOLUME_DOWN, MODE, NEXT, PREVIOUS} */
float BUTTONS[TOTAL_BUTTONS] = {390.f, 100.f, 270.f, 820.f, 470.f, 1800.f};

What’s configurable here? Well, you can change your reference resistor value, your voltage (which will probably always be +5V), button resistance value, number of buttons, which buttons represent what function and the tolerance percentage. The last one (tolerance) determines how close the current measurement has to come to a pre-defined steering wheel control resistance value before being considered a button press.

Variables

These will change over time:

int currentButton = NONE;        // Currently selected button
int rawVolts = 0;                // The raw analogue value
float voltsOut = 0.f;            // Voltage at point between resistors
float resistance = 0.f;          // Unknown resistance.

Here we store the currently selected button, current resistance, voltage, etc.

Measuring Resistance

Define a function that will convert the input voltage to resistance (and a function that prints it):

// Calculates the resistance.
void calculateResistance() {
rawVolts = analogRead(RESISTANCE_PIN);            // Read in raw value (0-1023)
voltsOut = (VOLTS_IN/1024.0) * float(rawVolts);    // Convert to voltage
resistance = R_KNOWN*((VOLTS_IN/voltsOut) - 1);    // Calculate the resistance
}

// Prints the resistance (if DEBUG is enabled)
void printResistance() {
#ifdef DEBUG
Serial.print("Voltage: ");
Serial.print(voltsOut);
Serial.print(", Resistance: ");
Serial.println(resistance);
#endif
}

Detecting Button Presses

These two functions will detect which button has been pressed (and print it):

// Detects which button is currently pressed.
void detectCurrentButton() {
for (int i = 0; i < TOTAL_BUTTONS; i++) {
if (buttonPressed(BUTTONS[i])) {
currentButton = i;
break;
} else {
currentButton = NONE;
}
}
}

// Prints the current button, only if DEBUG is enabled.
void printCurrentButton() {
#ifdef DEBUG
Serial.print("Current Button: ");
switch(currentButton) {
case MUTE:
Serial.println("MUTE");
break;
case VOLUME_UP:
Serial.println("VOLUME_UP");
break;
case VOLUME_DOWN:
Serial.println("VOLUME_DOWN");
break;
case MODE:
Serial.println("MODE");
break;
case NEXT:
Serial.println("NEXT");
break;
case PREVIOUS:
Serial.println("PREVIOUS");
break;
default:
Serial.println("NONE");
}
#endif
}

// Determines if the current resistance is within tolerance of a button resistance.
boolean buttonPressed(float buttonResistance) {
float decimalPercent = TOLERANCE_PERCENT / 200.f;
float highRange = buttonResistance * (1.f + decimalPercent);
float lowRange = buttonResistance * (1.f - decimalPercent);
return lowRange <= resistance && resistance <= highRange;
}

Wrapping Up

Now add the following setUp() and loop() functions:

void setup() {
Serial.begin(9600);
}

void loop() {
calculateResistance();
printResistance();
detectCurrentButton();
printCurrentButton();
delay(1000);
}

And try different buttons! The serial monitor should show something like this:

Vol, Resistance: inf
Current Button: NONE
Voltage: 4.68, Resistance: 385.80
Current Button: MUTE
Voltage: 4.91, Resistance: 100.20
Current Button: VOLUME_UP
Voltage: 4.77, Resistance: 275.41
Current Button: VOLUME_DOWN
Voltage: 4.61, Resistance: 474.58
Current Button: NEXT
Voltage: 3.78, Resistance: 1808.79
Current Button: PREVIOUS

Source Code

Download the full sketch here. Don’t forget to change the resistance values to adapt to your particular steering wheel. This will work on any resistance ladder type control, so if your car isn’t a Pontiac GTO you just need to fiddle the values and add/remove buttons to match your car’s controls.