Steering wheel controlled iPod

Hi all,

Im wondering if someone out there can help me with some code.

Im going to use an Arduino to control my ipod directly from my cars steering wheel audio controls and so far have some code that is working but not entirely as i'd like. Ive taken bits of code from lots of other projects and frankenstein'd them together to make it work.

First of all, can someone please look at the code and add some notes to it relating to the button functions in the main loop. It already has some notes but i find them a bit vague and I'm not really sure whats going on there. I understand the ipod command lines at the end (I put them there) but don't understand how the button is being read. Basically, the 6 buttons on my steering wheel are resistive type and iv'e used them to create a voltage divider and measure the returned voltage on analog pin 5. In this way the Arduino knows which button is being pressed. How is this then being transferred to the individual cases for each command?

I want to know mainly because i don't like having code that works but i don't understand :frowning: but also because of my second question......

How do i modify the code so that i can use a short press, or a long press to give two different commands, ie hold for 1 sec to "play" or hold for 3 sec's to "mute"?

Any help is greatly appreciated and will be rewarded with much praise and knowledge that i will be happily driving down the road and changing songs on my iPod whilst never removing my hands from the steering wheel! Therefore making the roads a safer place for everyone!

#include <SimpleRemote.h>
int j = 1; // integer used in scanning the array designating column number
//2-dimensional array for asigning the buttons and there high and low values
int Button[5][3] = {
  {1, 814, 818  }, //Down
  {2, 905, 909  }, //Up
  {3, 1016, 1020  }, //Menu
  {4, 1002, 1006  }, //OK
  {5, 979, 986  }}; //Play

int analogpin = 5; // analog pin to read the buttons
int label = 0;  // for reporting the button label
int counter = 0; // how many times we have seen new value
long time = 0;  // the last time the output pin was sampled
int debounce_count = 50; // number of millis/samples to consider before declaring a debounced input
int current_state = 0;  // the debounced input value
int ButtonVal;
SimpleRemote simpleRemote;

void setup()
{
  simpleRemote.setup();

}

void loop()
{
  // If we have gone on to the next millisecond
  if (millis() != time)
  {
    // check analog pin for the button value and save it to ButtonVal
    ButtonVal = analogRead(analogpin);
    if(ButtonVal == current_state && counter >0)
    {
      counter--;
    }
    if(ButtonVal != current_state)
    {
      counter++;
    }
    // If ButtonVal has shown the same value for long enough let's switch it
    if (counter >= debounce_count)
    {
      counter = 0;
      current_state = ButtonVal;
      //Checks which button or button combo has been pressed
      if (ButtonVal > 0)
      {
        ButtonCheck();
      }
    }
    time = millis();
  }
}

void ButtonCheck()
{
  // loop for scanning the button array.
  for(int i = 0; i <= 5; i++)
  {
    // checks the ButtonVal against the high and low vales in the array
    if(ButtonVal >= Button[i][j] && ButtonVal <= Button[i][j+1])
    {
      // stores the button number to a variable
      label = Button[i][0];
      Action();      
    }
  }
}

void Action()
{
  if(label == 1) //Down
  {
    simpleRemote.sendScrollDown();
  }
  if(label == 2) //Up
  {
    simpleRemote.sendScrollUp();
  }
  if(label == 3)
  {
    simpleRemote.sendMenuButton();
  }
  if(label == 4)
  {
    simpleRemote.sendOkSelectButton();
  }
  if(label == 5)
  {
    simpleRemote.sendPlay();
  }


  {
    simpleRemote.sendButtonReleased(); 
  }

}

Your Action() function leaves a bit to be desired.

  if(label == 1) //Down
  {
    simpleRemote.sendScrollDown();
  }
  if(label == 2) //Up
  {
    simpleRemote.sendScrollUp();
  }
  if(label == 3)
  {
    simpleRemote.sendMenuButton();
  }

Is there any value for label that causes multiple blocks of code to be executed? No. So, the code should be using if/else if. The switch statement would be even better:

switch(label)
{
   case 1:
      simpleRemote.sendScrollDown();
      break;
   case 2:
      simpleRemote.sendScrollUp();
      break;
   // More cases
}

Curly braces where they are not needed

  {
    simpleRemote.sendButtonReleased(); 
  }

just scream doofus. Get rid of them.

Yes, I know I preach that you should always use curly braces for blocks of code, but that is for statements that can have a block of code, like if, while, for, etc. Not just anywhere.

First of all, can someone please look at the code and add some notes to it relating to the button functions in the main loop.

This is something you should do. Add Serial.print() statements to the code. Run it again and again, adding, changing, and moving the statements around until you KNOW what a line/block of code is doing. Then, add comments that describe what it is doing. Not obvious stuff like "// Set variable to value 3" but a group of comments like

// Test the current button state against the previous state.
// If different, decrement the counter if not already 0. Otherwise, ...

How is this then being transferred to the individual cases for each command?

With the voltage divider setup you have, each switch connects a different resistor to the analog pin. The voltage through that resistor is measured, and recorded. The recorded value is compared to a table of ranges of values (the Button array). If the actual voltage measured is in the range in row n, the switch pressed was switch n.

How do i modify the code so that i can use a short press, or a long press to give two different commands, ie hold for 1 sec to "play" or hold for 3 sec's to "mute"?

With a series of discrete switches, this would be relatively simple. You observe the state of each switch at the start of loop. At the end of loop, you record that state in another variable.

So, on each pass through loop, you can tell if the current state matches the previous state. If not, a transition has occurred - either from pressed to released or from released to pressed.

Since you know when a transition occurs, you can note the time that the transition occurred.

Record when the switch was pressed. When it is released (the transition from pressed to released occurred), note the time. Of the difference between the two events is less than some value, do one thing. Otherwise, do something else.

It's a little more difficult with your setup, though. Not impossible. You must have the voltage divider wired so that there is a consistent value for no-switch-pressed. This would be button 0 in the table.

On each pass through loop, when ButtonCheck() returns a different number, a transition has occurred.

If the transition is from 0 to n, switch n was pressed. If the transition is from n to 0, switch n was released.

The rest of the process is then the same as for the discrete digital switch scenario.

Hi PaulS,

Thanks so much for your help. I've actually tried a number of different things with the code including a switch/case but was just having no luck. Ive been using some "Getting Started with Arduino" books and this web site to try and learn what to code but having an experienced eye look over what i've done is a huge help.

Although the code i posted worked, i was aware that it was almost certainly not perfect. I've now successfully updated it with the switch/case and although i almost pulled some large chunks of hair out trying to get it to work i finally got it going.

In your post you mention that the wheel needs a resting resistance to measure in order to know when no buttons are being pressed. Fortunately Subaru thought of this when they made the wheel and it has a resting resistance of 4.7k. This, when used with another 4.7k resistor, creates the voltage divider and naturally returns 511 on analog 5 with no buttons pushed.

When i initially changed the if/else for the switch/case i was having problems with the ipod skipping all over the place when buttons were pressed so i've added the resting state as "case 0" in order to send the "button released" command to the ipod.

Now it works just smashingly!

I havent looked at creating the short and long holds yet but will have a go later today. Expect more questions :stuck_out_tongue:

Again, Thanks for your help

Here's the latest

#include <SimpleRemote.h>

int j = 1; // integer used in scanning the array designating column number
//2-dimensional array for asigning the buttons and there high and low values
int Button[6][3] = {
  {0, 508, 513  }, //Button Released
  {1, 814, 818  }, //Down
  {2, 905, 909  }, //Up
  {3, 1016, 1020  }, //Menu
  {4, 1002, 1006  }, //OK
  {5, 979, 986  }}; //Play

int analogpin = 5; // analog pin to read the buttons
int label = 0;  // for reporting the button label
int counter = 0; // how many times we have seen new value
long time = 0;  // the last time the output pin was sampled
int debounce_count = 25; // number of millis/samples to consider before declaring a debounced input
int current_state = 0;  // the debounced input value
int ButtonVal;
SimpleRemote simpleRemote;

void setup()
{
  simpleRemote.setup();

}

void loop()
{
  // If we have gone on to the next millisecond
  if (millis() != time)
  {
    // check analog pin for the button value and save it to ButtonVal
    ButtonVal = analogRead(analogpin);
    if(ButtonVal == current_state && counter >0)
    {
      counter--;
    }
    if(ButtonVal != current_state)
    {
      counter++;
    }
    // If ButtonVal has shown the same value for long enough let's switch it
    if (counter >= debounce_count)
    {
      counter = 0;
      current_state = ButtonVal;
      //Checks which button or button combo has been pressed
      if (ButtonVal > 0)
      {
        ButtonCheck();
      }
    }
    time = millis();
  }
}

void ButtonCheck()
{
  // loop for scanning the button array.
  for(int i = 0; i <= 6; i++)
  {
    // checks the ButtonVal against the high and low vales in the array
    if(ButtonVal >= Button[i][j] && ButtonVal <= Button[i][j+1])
    {
      // stores the button number to a variable
      label = Button[i][0];
      Action();      
    }
  }
}

void Action()
{
  switch (label) 
{
  case 0:
  simpleRemote.sendButtonReleased();
  break;
  
  case 1:
  simpleRemote.sendScrollDown();
  break;
  
  case 2:
  simpleRemote.sendScrollUp();
  break;
  
  case 3:
  simpleRemote.sendMenuButton();
  break;
  
  case 4:
  simpleRemote.sendOkSelectButton();
  break;
  
  case 5:
  simpleRemote.sendPlay();
  break;
  
  }
}