Trimpin’s Teeter-totter Moving Speakers (Arduino and ipod)

Trimpin designed a pair of these speakers for the Gurs Zyklus, and when used in performance they were receiving wireless audio. Setting them up this way, we experienced some tiny drops in signal (clicks) even when the wireless transmitters where sitting right next to the units. Since the performance, I’ve been working on embedding an ipod inside them (since the serial communication for ipods is well-documented) and controlling their playback with an arduino and an accelerometer. I didn’t ever have any luck getting an ipod to accept mode 2 commands (which contain controls for volume) so I used mode 4 commands for playback, and a digital potentiometer to fade the volume. The code below is designed for installation, such that some other circuit would trigger the teeter-totters to move, and this separate circuit inside the speakers would play audio only when the speaker is moving, then if the speaker stops, it will fade out, pause the audio, and skip to the next track.

When you are looking at the connections from the Arduino, the serial communication is bright green (only the TX is being used), the SPI communication is orange, and the accelerometer is yellow. The resistors above the accelerometer are converting +5v to +3.3v(10k from +5v, and 15k to ground). The USB communication is on the right (+5v(pin23), D-(pin25), D+(pin27), Ground(pin 15)) and in some pictures there +5v on pin 23 and a 100kohmn resistor from pin 25 to +5v and from pin 27 to ground—this is so the ipod charges off the circuit. In order for the ipod to show up in itunes, these resistors on the data pins must be removed. The first picture is the pinout for the ipodBreakout (from p.405 iPhone Hacks by David Jurick, Adam & Damien Stolarz):

(updated 6/22/11)


/*
  ipod_controller_main
  
  This code controls an ipod embedded in a moving installation, such that sound only plays with the ipod is moving
  TX and RX pins on the Arduino are connected to pins 12 and 13 of PodBreakout
  
  Ipod serial communication only has volume control on mode 2, but since lots of ipods don't accept this mode, playback
  is done through mode 4, and fading volume is handled by a digital potentiometer (DS1801).
  +5v is connected to pin 14
  Pins 1, 4, 7, 10, 11 are connected to ground
  pin 6 LeftIn
  pin 5 LeftOut
  pin 9 RightIn
  pin 8 RightOut
  
  The SPI circuit looks like this:
  Arduino 10--- (SS) DS1801 pin 3
  Arduino 11--- (RX) DS1801 pin 12
  Arduino 13--- (CLK) DS1801 pin 13


  
  the X,Y,Z pins of an accelerometer are connected to analog 3, 4, 5
  
  
  Jeff Aaron Bryant, June 2011
*/


//serial commands for ipod 
byte wake [] ={0xFF, 0x55, 0x03, 0x02, 0x00, 0x04, 0xF7}; //wakeup
byte mode_4 [] = {0xFF, 0x55, 0x03, 0x00, 0x01, 0x04, 0xF8}; //switch to mode 4
byte playlist [] = {0xFF, 0x55, 0x07, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0xCD}; //startlist
byte play_pause [] = {0xFF, 0x55, 0x04, 0x04, 0x00, 0x29, 0x01, 0xCE}; //play_pause
byte skip [] = {0xFF, 0x55, 0x04, 0x04, 0x00, 0x29, 0x03, 0xCC}; //skip
byte back [] = {0xFF, 0x55, 0x04, 0x04, 0x00, 0x29, 0x04, 0xCB}; //back


//sizes
int wakeLength = sizeof(wake);
int mode_4Length = sizeof(mode_4);
int playlistLength =sizeof(playlist);
int play_pauseLength = sizeof(play_pause);
int skipLength = sizeof(skip);
int backLength = sizeof(back);

// include the SPI library
#include <SPI.h>

// set pin 10 as the slave select for the digital pot(DS1801)
int SS = 10;
int mute = 64;
int loud = 63;
int soft = 0;


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

    //startup ipod
  for (int i=0; i< wakeLength; i++){               //wakeup
                 Serial.print(wake[i],BYTE);
               }
             
               Serial.println("    WAKE");
               
  for (int i=0; i< mode_4Length; i++){          //mode 4
                 Serial.print(mode_4[i],BYTE);
               }
             
               Serial.println("    MODE_4");               


  // set the slaveSelectPin as an output:
  pinMode (SS, OUTPUT);
  
  // initialize SPI:
  SPI.setBitOrder(LSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV128);
  SPI.begin(); 
  delay(10);
  setVolume(mute);

}
        


void loop(){
  accelerometer();
  ipodCommands();
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//accelerometer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


int x, y, z;
int tilt= 0;  
int lastTilt= 0;
int diff= 0;
int stillness= 8;
int moving= 0;   
int slowing =0;



void accelerometer(){
  if (moving == 1 && slowing ==0){ delay (3000);}          
  if (moving == 1 && slowing ==1){ delay (500);}
  if (moving == 0){ delay (200);}
  
  
  
  x =analogRead(5);
  y =analogRead(4);
  z =analogRead(3);
  Serial.print("   X=");
  Serial.print(x);
  Serial.print(" Y=");
  Serial.print(y);
  Serial.print(" Z=");
  Serial.print(z);
  
  tilt= x + y + z;
  diff= tilt-lastTilt;
  
  
  if (tilt != lastTilt && abs(diff)> 8 && abs(diff)< 400) {  
    Serial.print(" MOVING!!!  ");
    Serial.print(diff);
    moving= 1;
    lastTilt = tilt;
    stillness= 0;
    slowing = 0;
 
    
  }
    else {
      stillness++;
      if (stillness< 7){
      Serial.print(" ...slowing...  ");
      Serial.print(diff);
      slowing = 1;
     
      } else{
          Serial.print(" STOPPED  ");
          Serial.print(diff);
          moving= 0;
          slowing= 0;
          lastTilt=tilt;
        
        }
     
    }

  Serial.println();

}
  
  
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ipodCommands
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int state= 0;
int lastState= 0;
int startup= 0;

void ipodCommands(){
  state=  moving;
 
    
  if (state != lastState){

    switch (state){
      case 1: if (startup ==0){
                  for (int i=0; i< playlistLength; i++){          //start playlist at the beginning
                  Serial.print(playlist[i],BYTE);
               }
             
               Serial.println("    Startlist");  
               for (int i = soft; i <= 10; i++) {            //turn up volume
                   setVolume(i);
                   delay(10);
               }
               for (int i = 10; i <= 63; i++) {            
                   setVolume(i);
                   delay(250);
               }              
               startup++;
               lastState = state;
               break;
                } else{
               
               
               for (int i=0; i< play_pauseLength; i++){      //pause
                   Serial.print(play_pause[i],BYTE);
               }
               Serial.println("    PLAY");
               
               for (int i = soft; i <= 10; i++) {            //turn up volume
                   setVolume(i);
                   delay(10);
               }
               for (int i = 10; i <= 63; i++) {            
                   setVolume(i);
                   delay(250);
               }
               
               lastState= state;
               break;
     
      case 0:  for (int i = loud; i >= 15; i--) {             //turn down volume
                   setVolume(i);
                   delay(10);
               }
               for (int i = 15; i >= 0; i--) {            
                   setVolume(i);
                   delay(50);
               }
   
               
               for (int i=0; i< play_pauseLength; i++){      //pause
                   Serial.print(play_pause[i],BYTE);
               }
               Serial.println("    PAUSE");
               
               
            
               
               delay (1000);
               setVolume(mute);                                  //mute
               
            
               for (int i=0; i< skipLength; i++){             //skip
                   Serial.print(skip[i],BYTE);   
               }
              
               Serial.println("    SKIP");
               
               delay (500);
               lastState= state;
               break;
    }
  }
  } 
  
  
 
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//setVolume
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void setVolume(int level) {
  digitalWrite(SS,HIGH);
  SPI.transfer(level);                                             	    //pot 0
  SPI.transfer(level);                                          	    //pot 1
  digitalWrite(SS,LOW); 
}


  
  



Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s