Saturday, May 4, 2013

R/C Themed Car Media Controls

The goal of this project is to interface some sort of media player with my radio with media controls easily accessible. After washing my ipod and the cellphone battery not fixing it (soemthing was drawing excessive current and killing the battery within a day of not being used (a new ipod battery didn't fix it either). So i set off with another idea: use my netbook.

Why?
-has my entire music collection
-can be interfaced with an arduino through processing.
-arduino will handle all the control interface reading.
-runs linux which is awesome
-Amarok media player
-small


Overview

I first had to come up with what controls to use with the arduino. As you might have noticed from my recent posts, i'm pretty obsessed with RC. The first RC that i got was a Firebird II ST and had only throttle and roll: aka totally shit. Long story short, i was flying it in a circle and went out of sight (couldn't turn tight enough) and crashed somewhere: never found it. I gutted it's control sticks for my media interface. pretty nerdy. Throttle is used as volume, and the roll stick is used for changing tracks). The arduino reads the analog values and sends them to processing which sets the volume /play/pause/track skip.

The Build

Arduino

First thing i had to do was check out the pots and see how they would behave on the arduino. i wrote a little arduino program below that will find the current value, the min value, and the max value of the pot. so if you move the to from one extreme to the other, it will show what the min and max was. One pot was acting weird o i replaced it with a common 5k. both original pots had weird analog read values that weren't 0 to 1023 (0v to 5v) like usual, so that was the biggest reason for this code.

#define analogPin A5

int potVal , potValMin , potValMax = 400;

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

void loop() {
  // read the analog in value:
  potVal = analogRead(analogPin);      

  //Get the senor range value automatically
  if(potVal > potValMax) //if current value is greater than the
  {                            //previously known max, set max to current
    potValMax = potVal;
  }
  else if(potVal < potValMin) //if current value is less than the
  {                             //previously known min, set min to current
    potValMin = potVal;
  }

  Serial.print(potVal);   //print raw val
  Serial.print("   ");
  Serial.print(potValMin); //print min
  Serial.print("   ");
  Serial.println(potValMin); // print max

  //delay so its easier to read
  delay(5);                  
}

Now that i had the ranges of the pots, i could start the actual interfacing. The arduino code:
1. reads the pots and button
2. maps the values to a useful range
3. sends them in a comma seperated string out the serial port


/////////////////////////POT CONFIG//////////////////////////
#define volInPin A5
#define nxtInPin A4
////////////////////////////////////////////////////////////

//////////////////////////BTN STUFF////////////////////////////////////////////////////////////////
#define buttonPin 3

boolean currentState;           // the current reading from the input pin
boolean previousState;    // the previous reading from the input pin

// the follow variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long time = 0;         // the last time the output pin was toggled
long debounce = 200;   // the debounce time, increase if the output flickers
///////////////////////////////////////////////////////////////////////////////////////////////////

long volVal;
int nxtVal;
int btnVal;

void setup()
{
  Serial.begin(115200);

  pinMode(buttonPin, INPUT); //set button to an input.
  previousState = digitalRead(buttonPin);

  //initiallize default ppm values
  volVal = 16384;  // or quarter volume
  nxtVal = 50; //centerd, 50
  btnVal = 0;  //0, no mute
}

void loop() {
  delay(5);//better values.
  // read the analog in value:
  volVal = analogRead(volInPin);

  delay(5);//better values.
  // read the analog in value:
  nxtVal = analogRead(nxtInPin);

  CheckPPButton(); //check the play/pause button;)

  //raw values pot val. uncomment these lines to see these values
  /*
  Serial.print(volVal);
   Serial.print(",");
   Serial.print(nxtVal);
   Serial.print(",");
   */

  volVal = map(volVal,450,720,0,65536); //vol
  nxtVal = map(nxtVal,640,1023,0,100); //nxt

  //mapped pot values . uncomment these lines to see these values
  /*
  Serial.print("         ");    
   Serial.print(volVal);
   Serial.print(",");    
   Serial.println(nxtVal);    
   */

  SerialWrite(); //makes serial string and sends it at the end of each loop.
}

void SerialWrite()
{
  while (!Serial.available());             // wait for processing to say something
  if(Serial.read() == 100) // check if we need to send data if processing sends "100"
  {
    //volume level, skip track, mute
    Serial.print(volVal); Serial.print(","); Serial.print(nxtVal); Serial.print(","); Serial.println(btnVal);
  }
}

void CheckPPButton()
{
  currentState = digitalRead(buttonPin); //get current state

  //if has passed debounce time and the is different
  if (((millis() - time) > debounce) && (currentState != previousState))
  {
    if(currentState == false)
    {
      btnVal = 0; //Playing
    }
    else // = high, closed to 5v
    {
      btnVal = 1; //Paused
    }

    previousState = currentState;

    time = millis();  
  }
}

Processing

Now for  the processing side which:
1. Reads the serial data
2. Runs the script to change volume and the mute status
3. checks the Skip track pot value to see if it needs to change the Amarok track


import processing.serial.*;
Serial port;  // The serial port object

byte needData 100; //sent when data done procssing.

boolean gotData = false;

long volVal;
int nxtVal;
int btnVal;

void setup()
{
  size(300, 200);
  // In case you want to see the list of available ports
  // println(Serial.list());

  // Using the first available port (might be different on your computer)
  port = new Serial(this, Serial.list()[0], 115200);
  // Request values right off the bat
  port.write(needData); //need data...
}

void draw()
{
  if (gotData)
  {
    exec("ChngVol.sh", "volVal", "btnVal"); //runs the volume / mute script. this needs to be in /usr/bin
 
    gotData = false;
    // When finished ask for values again
    port.write(needData);
  }
  background(0);
}// Called whenever there is something available to read


void serialEvent(Serial port) {
  // Data from the Serial port is read in serialEvent() using the readStringUntil() function with * as the end character.
  String input = port.readStringUntil(',');

  if (input != null) {
    // Print message received
    println( "Receiving:" + input);

    // The data is split into an array of Strings with a comma or asterisk as a delimiter and converted into an array of integers.
    long[] vals = int(splitTokens(input, ","));

    // Fill variables
    volVal = vals[0];
    nxtVal = (int)vals[1];
    btnVal = (int)vals[2];

    gotData = true; //now the main loop will do stuff
  }
}

This project is not yet complete, and may never be as by brother gave me his old 1g iPod Touch because his girlfriends brother gave him his because he got a new iPhone. phew.

1 comment: