Thursday, February 23, 2012

Using Arduino With A "PulseSensor"

I was helping someone with a college project that entailed combining art and technology; they wanted to make a "Social Jacket" that would measure the BPM of the wearer and use sonar sensors to determine how far away people are from them. Then relate the BPM to the closeness of people. I've already posted about sonar modules so i figured it'd be nice to put something up here about the PulseSensor. I didn't write this code, but I used the fact that it used interrupts for determining the BPM to my advantage; the rest of the Social Jacket features could be inserted without much trouble with regards to timing etc.



/*
>> Pulse Sensor Digital Filter <<
This code is the library prototype for Pulse Sensor by Yury Gitman and Joel Murphy
    www.pulsesensor.com
    >>> Pulse Sensor purple wire goes to Analog Pin 0 <<<
Pulse Sensor sample aquisition and processing happens in the background via Timer 1 interrupt. 1mS sample rate.
The following variables are automatically updated:
Pulse :     boolean that is true when a heartbeat is sensed then false in time with pin13 LED going out.
Signal :    int that holds the analog signal data straight from the sensor. updated every 1mS.
HRV  :      int that holds the time between the last two beats. 1mS resolution.
B  :        boolean that is made true whenever HRV is updated. User must reset.
BPM  :      int that holds the heart rate value. derived from averaging HRV every 10 pulses.
QS  :       boolean that is made true whenever BPM is updated. User must reset.
Scale  :    int that works abit like gain. use to change the amplitude of the digital filter output. useful range 12<>20 : high<>low default = 12
FSignal  :  int that holds the output of the digital filter/amplifier. updated every 1mS.

See the README for detailed information and known issues.
Code Version 0.6 by Joel Murphy  December 2011  Happy New Year!
*/


long Hxv[4]; // these arrays are used in the digital filter
long Hyv[4]; // H for highpass, L for lowpass
long Lxv[4];
long Lyv[4];

unsigned long readings; // used to help normalize the signal
unsigned long peakTime; // used to time the start of the heart pulse
unsigned long lastPeakTime = 0;// used to find the time between beats
volatile int Peak;     // used to locate the highest point in positive phase of heart beat waveform
int rate;              // used to help determine pulse rate
volatile int BPM;      // used to hold the pulse rate
int offset = 0;        // used to normalize the raw data
int sampleCounter;     // used to determine pulse timing
int beatCounter = 1;   // used to keep track of pulses
volatile int Signal;   // holds the incoming raw data
int NSignal;           // holds the normalized signal
volatile int FSignal;  // holds result of the bandpass filter
volatile int HRV;      // holds the time between beats
volatile int Scale = 13;  // used to scale the result of the digital filter. range 12<>20 : high<>low amplification
volatile int Fade = 0;

boolean first = true; // reminds us to seed the filter on the first go
volatile boolean Pulse = false;  // becomes true when there is a heart pulse
volatile boolean B = false;     // becomes true when there is a heart pulse
volatile boolean QS = false;      // becomes true when pulse rate is determined. every 20 pulses

int pulsePin = 0;  // pulse sensor purple wire connected to analog pin 0


void setup(){
pinMode(13,OUTPUT);    // pin 13 will blink to your heartbeat!
Serial.begin(115200); // we agree to talk fast!
// this next bit will wind up in the library. it initializes Timer1 to throw an interrupt every 1mS.
TCCR1A = 0x00; // DISABLE OUTPUTS AND BREAK PWM ON DIGITAL PINS 9 & 10
TCCR1B = 0x11; // GO INTO 'PHASE AND FREQUENCY CORRECT' MODE, NO PRESCALER
TCCR1C = 0x00; // DON'T FORCE COMPARE
TIMSK1 = 0x01; // ENABLE OVERFLOW INTERRUPT (TOIE1)
ICR1 = 8000;   // TRIGGER TIMER INTERRUPT EVERY 1mS
sei();         // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED

}



void loop(){
  Serial.print("S");          // S tells processing that the following string is sensor data
  Serial.println(Signal);     // Signal holds the latest raw Pulse Sensor signal
  if (B == true){             //  B is true when arduino finds the heart beat
    Serial.print("B");        // 'B' tells Processing the following string is HRV data (time between beats in mS)
    Serial.println(HRV);      //  HRV holds the time between this pulse and the last pulse in mS
    B = false;                // reseting the QS for next time
  }
  if (QS == true){            //  QS is true when arduino derives the heart rate by averaging HRV over 20 beats
    Serial.print("Q");        //  'QS' tells Processing that the following string is heart rate data
    Serial.println(BPM);      //  BPM holds the heart rate in beats per minute
    QS = false;               //  reset the B for next time
  }
  Fade -= 15;
  Fade = constrain(Fade,0,255);
  analogWrite(11,Fade);
 
delay(20);                    //  take a break

}

// THIS IS THE TIMER 1 INTERRUPT SERVICE ROUTINE. IT WILL BE PUT INTO THE LIBRARY
ISR(TIMER1_OVF_vect){ // triggered every time Timer 1 overflows
// Timer 1 makes sure that we take a reading every milisecond
Signal = analogRead(pulsePin);

// First normailize the waveform around 0
readings += Signal; // take a running total
sampleCounter++;     // we do this every milisecond. this timer is used as a clock
if ((sampleCounter %300) == 0){   // adjust as needed
  offset = readings / 300;        // average the running total
  readings = 0;                   // reset running total
}
NSignal = Signal - offset;        // normalizing here

// IF IT'S THE FIRST TIME THROUGH THE SKETCH, SEED THE FILTER WITH CURRENT DATA
if (first = true){
  for (int i=0; i<4; i++){
    Lxv[i] = Lyv[i] = NSignal <<10;  // seed the lowpass filter
    Hxv[i] = Hyv[i] = NSignal <<10;  // seed the highpass filter
  }
first = false;      // only seed once please
}
// THIS IS THE BANDPAS FILTER. GENERATED AT www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
//  BUTTERWORTH LOWPASS ORDER = 3; SAMPLERATE = 1mS; CORNER = 5Hz
    Lxv[0] = Lxv[1]; Lxv[1] = Lxv[2]; Lxv[2] = Lxv[3];
    Lxv[3] = NSignal<<10;    // insert the normalized data into the lowpass filter
    Lyv[0] = Lyv[1]; Lyv[1] = Lyv[2]; Lyv[2] = Lyv[3];
    Lyv[3] = (Lxv[0] + Lxv[3]) + 3 * (Lxv[1] + Lxv[2])
          + (3846 * Lyv[0]) + (-11781 * Lyv[1]) + (12031 * Lyv[2]);
//  Butterworth; Highpass; Order = 3; Sample Rate = 1mS; Corner = .8Hz
    Hxv[0] = Hxv[1]; Hxv[1] = Hxv[2]; Hxv[2] = Hxv[3];
    Hxv[3] = Lyv[3] / 4116; // insert lowpass result into highpass filter
    Hyv[0] = Hyv[1]; Hyv[1] = Hyv[2]; Hyv[2] = Hyv[3];
    Hyv[3] = (Hxv[3]-Hxv[0]) + 3 * (Hxv[1] - Hxv[2])
          + (8110 * Hyv[0]) + (-12206 * Hyv[1]) + (12031 * Hyv[2]);
FSignal = Hyv[3] >> Scale;  // result of highpass shift-scaled

//PLAY AROUND WITH THE SHIFT VALUE TO SCALE THE OUTPUT ~12 <> ~20 = High <> Low Amplification.

if (FSignal >= Peak && Pulse == false){  // heart beat causes ADC readings to surge down in value.
  Peak = FSignal;                        // finding the moment when the downward pulse starts
  peakTime = sampleCounter;              // recodrd the time to derive HRV.
}
//  NOW IT'S TIME TO LOOK FOR THE HEART BEAT
if ((sampleCounter %20) == 0){// only look for the beat every 20mS. This clears out alot of high frequency noise.
  if (FSignal < 0 && Pulse == false){  // signal surges down in value every time there is a pulse
     Pulse = true;                     // Pulse will stay true as long as pulse signal < 0
     digitalWrite(13,HIGH);            // pin 13 will stay high as long as pulse signal < 0
     Fade = 255;                       // set the fade value to highest for fading LED on pin 11 (optional)  
     HRV = peakTime - lastPeakTime;    // measure time between beats
     lastPeakTime = peakTime;          // keep track of time for next pulse
     B = true;                         // set the Quantified Self flag when HRV gets updated. NOT cleared inside this ISR    
     rate += HRV;                      // add to the running total of HRV used to determine heart rate
     beatCounter++;                     // beatCounter times when to calculate bpm by averaging the beat time values
     if (beatCounter == 10){            // derive heart rate every 10 beats. adjust as needed
       rate /= beatCounter;             // averaging time between beats
       BPM = 60000/rate;                // how many beats can fit into a minute?
       beatCounter = 0;                 // reset counter
       rate = 0;                        // reset running total
       QS = true;                       // set Beat flag when BPM gets updated. NOT cleared inside this ISR
     }
  }
  if (FSignal > 0 && Pulse == true){    // when the values are going up, it's the time between beats
    digitalWrite(13,LOW);               // so turn off the pin 13 LED
    Pulse = false;                      // reset these variables so we can do it again!
    Peak = 0;                           //
  }
}

}// end isr

Saturday, February 18, 2012

Control RC Car Transmitter with Arduino

EDIT: RC Car Update

Now that I can trigger buttons with Arduino (a post or two back), I bought a cheapo RC car from Walmart and almost immediately tore it apart :)

The Victim: New Bright RC Corvette


Transmitter:
It contains a NEW BRIGHT T288-2 0726 chip, which some say is just a re-branded TX2c(very common IC for cheap RC), and i would have to agree as the pin out is almost identical and functions the same. The IC encodes the button status(on/off) to a radio signal that the receiver can decode and act accordingly. The buttons function the same as the older remote i was tinkering with in my previous post, so that makes it easier to interface with a Arduino(just a diode to a digital pin). They are +3v when open, 0v when closed. I found it annoying trying to solder a wire directly on to the button pin, but for some reason their were little solder pads on each trace for each button, so i just connected the wires there. This IC, i believe, also has a turbo function, which is normally used for extra features on the car(lights, sound etc) that are non existent on this cheap model. I think ill still solder a button to the IC pin and make a little hole in the remote case to mount it and to utilize this extra pin. Maybe add some headlights to the car?


In this second picture you can see the wires to a header accessible to the outside for the Arduino and the new TURBO button for the car headlights. I put the diodes for triggering the button inside the case for easier interface with the Arduino. I brought out VCC to the header too so that i can power the remote from the Arduino, if necessary. Also, i cut a little hole in the case for mounting the TURBO button.
EDIT: I have found that the turbo button isnt just an extra "channel" it messes with the movement of the car too. With turbo on, the car goes forward and left when i press the left button, same for right too. Forward/Back don't work anymore.




Receiver(car):
It contains a NEW BRIGHT R288-2 0802 chip(again, probably a RX2c clone). not much to explain here... The IC receives the signals, decodes them, then turns on/off the mosfets(L,R,B,F) depending on what data it has received. The wheels use a normal DC motor for turning, not a servo. 5AA batteries at 1.5v, so about 7.5v total, but the IC runs at 3v. Now that i found the TURBO pin on the remote and added a button for it, i can put in another NPN transistor on the car and put some headlights in :)




TX2c_RX2c.pdf is a datasheet for the TX2c and RX2c; the only difference i can find between the NEW BRIGHT IC and the TX/RX2c is that the Left/Right pins are switched, and the Forward/Back pins are switched.

Friday, February 17, 2012

2 Liter Bottle Water Rockets

With five days off of school what better is there to do than make 2 liter pop bottle rockets? Not much, i think. I have a few designs of the rocket made up in CAD and the launcher too. But, i still need to go buy some of the fittings for attaching bottles together and for the launcher before i can build. I had a female-female fitting that works perfectly with two liter bottles, so that was much easier(attaching two bottles together at the "mouth"). I think ill use some nylon couplers similar to these for connecting two ends of the bottles together:




Thursday, February 16, 2012

1.8mm Audio Jack Switch, 3 inputs/2 ouputs

I have quite a few audio devices on my desk, and thought up this little audio switcher to easily switch between them. Currently, I have a computer, FM stereo, and Auxiliary as inputs to the switcher, and headphones or an amp as outputs from the switch.


Possible Designs:
I can either use a DP3T and DPDT design like this:


or a 3x DPST and a DPDT design. If i were to want it Arduino controlled, i would have to use this one as a DP3T relay seem non-existent.



Different Switch Types
SPST: A single pole(SP), meaning opening or closing a single connection. Single throw(ST), meaning one physical slider etc to move the pole between open and closed. simple ON-OFF.


DPST: A double pole(DP), meaning opening or closing two connections. Single throw(ST), meaning one physical slider(or toggle etc) to move both poles together between open and closed. Like two SPST in one.


SPDT: Switches one pole between two output poles using one toggle. aka ON-ON


SPCO/SPTT: Switches one pole between two output poles using one toggle with a center position off. Like SPDT but has a off position. aka ON-OFF-ON.

DPDT: Switches two poles between two outputs for each. Controlled by one toggle or slider etc.  Like two SPDT in one.


xPxT: where x is a number. DP3T: One of three different "throws" (ON-ON-ON), each with two poles, is selected and connected and connected two output poles. very confusing, look at pic:

Trigger Buttons with Arduino

This post is a sort of preliminary to the Arduino RC car im working on. I need some way to control the RC car and i figured it would be easier to just trigger the buttons on the remote(as they aren't true analog joytsticks, just joy sticks with springy buttons). Here i will explain the two types of button you will most likely find in a circuit. I am interfacing with a 3v circuit in this case.


Button pulled down to GND
This method is much easier to toggle and is what my RC remote has. One side of the so called "button" is connected to GND, the other is connected to the IC of the TX. The pin of RC transmitter IC that the button is connected to is:  +3v when the button is open and 0v when closed. so that means the button is "pulled" down to ground when closed. To trigger this button with a Arduino, it is quite easy; all that is needed for this type of switch is a diode from the RC transmitter IC pin to a Arduino digital pin. Then the following code. I believe the pin only needs to be set LOW once (the first line of code) but im not sure.

digitalWrite(RCtxBtn, LOW); // RCtxBtn is the number of the digital pin
  pinMode(RCtxBtn, OUTPUT);  // Pull the signal low to activate button
  delay(500);  // Wait half a second
  pinMode(RCtxBtn, INPUT);  // Release the button.
  delay(500);  // Wait half a second


Button pulled up to +3v
This method, on the other hand, is a little harder to implement, but still easily done. This method is used when the IC side of the switch when open is GND and the other side is +3v. Then when the button is pressed, both sides become  +3v  So that means the button is "pulled" up to  +3v  when closed. to trigger a button like this with a Arduino, a resistor and PNP transistor are needed. the PNP has thee pins; Emitter, Base, and Collector. Emitter is the "input", Collector is the "output", and the Base triggers the flow of current from the Emitter to the Collector. Emitter is connected to  +3v , Collector to the IC side of the switch, and Base is connected to a 10k resistor and then Arduino Digital pin.

pinMode(RCtxBtn, OUTPUT);
  digitalWrite(RCtxBtn, LOW); //trigger the button, yes LOW is "on"
  delay(5000); //on for 5 sec
  digitalWrite(RCtxBtn, HIGH); //release button
  delay(5000); //off for 5 sec


Visual Explanation:


Monday, February 13, 2012

Cheap TV O-Scope

I've had a 5in black n' white portable TV sitting around for a while with the goal of becoming a simple O-Scope. I finally got around to it a found it was really simple to do.

A few notes: Each coil (vertical and Horizontal) has two wires, they are kinda like speaker coils, so in my case their were just four wires soldered on to a little board mounted on the CRT gun. these coils have A LOT of voltage going through then when on(about 15kV) so DON'T touch them while the TV is on. :) The vertical coil runs a 60Hz instead of ~30Hz for the horizontal, so the vertical coil can display higher frequencies and music better. Older TVs will have H/Vsync pots on the back; tinker with these to make the picture look better.

1. I unsoldered one wire which would cause one of the coils to not work. the test goes like this: if you unsoldered the horizontal coil, you will see a vertical line, if you unsoldered the vertical coil, you will see a horizontal line.
2. So then when you figure out which is which coil(and which two wires are for each coil), move the wires that were soldered to the vertical coil to the horizontal coil. You don't need the original horizontal coil wires, so tape them up /cut them off/unsolder them etc
3. Now you can solder on two more wires(the external signal wires, like a MP3 player) on to the vertical coil.


Thursday, February 9, 2012

SoundCipher Processing Library and TouchOSC

The SoundCipher library the playNote function; it plays a not according to user input for pitch, volume and duration. It has a few other functions but i havent messed with them yet(my iPod is a little small for a full piano :))

I created a TouchOSC layout with three faders: Pitch, Volume, and Duration and a "Play Note" button. The faders, obviously, change the value they are called.Pitch is varied from 0 to 120(60 is middle C), volume varies from 0 to 100%, and duration is in seconds from 0 to 2 seconds. and the note that has been created using the faders is played when the button is pressed on the iPod.

OSC layout noteOSCtest2.touchosc.

Processing Code:

import oscP5.*;        //  Load OSC P5 library
import netP5.*;        //  Load net P5 library
import arb.soundcipher.*; //http://explodingart.com/soundcipher/tutorials.html

OscP5 oscP5;            //  Set oscP5 as OSC connection

SoundCipher sc = new SoundCipher(this);

float [] OSCdata = new float[4]; //pitch fader, volume fader, duration Btn

void setup()
{
  size(100, 100);        // Processing screen size

  oscP5 = new OscP5(this, 8000);  // Start oscP5, listening for incoming messages at port 8000
}

void draw()
{
  background(50);

  if (OSCdata[2] == 1)
  {
    fill(0,255,0);
    ellipse(50,50,25,25); //green status button pressed ellipse
 
    sc.playNote(OSCdata[0], OSCdata[1], OSCdata[3]);
    delay(int(OSCdata[3]*1000)); //so it plays the enitre current note before next note
  }
  else
  {
    fill(255,0,0); //RED status ellipse
    ellipse(50,50,25,25);
  }
}

void oscEvent(OscMessage theOscMessage) //  This runs whenever there is a new OSC message
{
  String addr = theOscMessage.addrPattern();  //  Creates a string out of the OSC message
  float val = theOscMessage.get(0).floatValue();  //get the value

  if (addr.equals("/1/pitch"))
  {
    OSCdata[0] = val;
  }  
  if (addr.equals("/1/volume"))
  {
    OSCdata[1] = val;
  }
  if (addr.equals("/1/push1"))
  {
    OSCdata[2] = val;
  }
  if (addr.equals("/1/duration"))
  {
    OSCdata[3] = val;
  }
}


Sunday, February 5, 2012

TouchOSC with Processing, iPhone and Arduino.


I've seen alot of projects that use iPhones or similair devices to interface with processing, and sometimes then control the arduino using the format sketch. This protocol is called OSC; a popular iOS/android app that implements this is called TouchOSC. It turned out that I couldn't fnd much documentation on how to use TouchOSC with processing, so it took a little fidling to successfuly read the values from the iPod Touch.

TouchOSC has a computer based layout creator which lays out the position, address, and value range of the objects in the layout. Some of the objects are push and toggle buttons, rotarys, encoders, XY pads and a few others. The TouchOSC program on the iPod then sends the OSC messages to a certain IP address(a computer with processing running). The Processing code then reads the OSC messages and can do whatever depending on what the value is.Processing needs the oscP5 library and the arduino library if you want to send commands to a Arduino from processing. MyFullExOSC.touchosc is the layout file for TouchOSC.

The following Processing program reads from device:
-toggle buttons
-push buttons
-XY pad
-Faders
-Rotaries
-Encoders
-multipush button matrixes
-multitoggle button matrixes
-multiXY pad (up to 5 touch points)
-multifader
-accelerometer
Also, the program sends back the highest and lowest accelerometer XYZ values that it has received from the iPod and displays them in labels. All of these readings have some sort of graphical representation in processing to test for:)





import oscP5.*;        //  Load OSC P5 library
import netP5.*;        //  Load net P5 library
import processing.serial.*;    // Load serial library
import cc.arduino.*;  //load arduino interface library

OscP5 oscP5;            //  Set oscP5 as OSC connection
NetAddress iPod; //holds address for sending data to iPod
Arduino arduino; // create new arudino device

PFont f; //initailize a font

//determine tab pos
boolean [] TabStat = new boolean [5];

//tab1
float [] ToggleStat = new float [3]; //Toggle: btn1, btn2, btn3
float [] PushStat = new float [3]; //Push: btn1, btn2, btn3
float [] XYpad = new float [2]; //x, y
//tab2
float [] TabII = new float [5]; //fader, rotary, encoder, Enc Previous value, Enc Pos(software based increase/decrease based on encoder)
//tab3
float [] MultiPush = new float [9]; //3x3 multi push button matrix
float [] MultiToggle = new float [9]; //3x3 multi toggle button matrix
//tab4
float [] MultiXYpad = new float [10]; //up to 5 xy coordinate pairs
float [] MultiFader = new float [5]; //multi fader with 5 faders
//sine wave
float WaveWidth = 500+16;              // Width of entire wave
float theta = 0.0;  // Start angle at 0
float dx;  // Value for incrementing X, a function of period and xspacing
float[] yvalues = new float[1];  // Using an array to store height values for the wave
//tab5
float [] Accel = new float [13]; //x, y, z, Center press button, x center, y center, z center, final x/y/z, prev final x/y/z
float [] AccelMM = new float [6]; //stores min/max values for xyz

void setup()
{
  //println(Serial.list()); //shows list of avaliable serial ports, put number for you port in Arduino.list()[X]

  size(500, 500);        // Processing screen size
  frameRate(60);
  smooth();

  f = loadFont("ComicSansMS-48.vlw"); //Load Font
  textFont(f, 25);                 //Specify font type

  oscP5 = new OscP5(this, 8000);  // Start oscP5, listening for incoming messages at port 8000
  iPod = new NetAddress("192.168.1.4", 9000); //address for sending messages to iPod(also the iPod sending messages to this computer), not needed if iPod is only sending
  arduino = new Arduino(this, Arduino.list()[0], 115200); //arduino connected, use the standard firmata example sketch
}

void draw()
{
  if (TabStat[0]) //if we are on the first tab
  {
    background(50); //color of background

    fill(0);
    text("Toggle:", 218, 25);
    text("Push:", 45, 25);

    for (int x = 0; x<3; x++)
    {
      fill(0); //fill font with black
      text(x+1, 110, 75+(x * 50)); //label buttons
      text(x+1, 310, 75+(x * 50));

      //draw buttons
      if (ToggleStat[x] == 1)  //if x button is pressed
      {
        fill(0, 255, 0); //R,G,B/0,255,0
        ellipse(146, 65+(x * 50), 30, 30); //x,y,width.height
        arduino.digitalWrite(x+2, 1); //turns on(HIGH) digital pin x+2(2->5) on the arduino
      }
      else
      {
        fill(255, 0, 0);
        ellipse(146, 65+(x * 50), 30, 30);
        arduino.digitalWrite(x+2, 0); //turns off(LOW) digital pin x+2(2->5) on the arduino
      }

      if (PushStat[x] == 1)
      {
        fill(0, 255, 0);
        ellipse(342, 65+(x * 50), 30, 30);
      }
      else
      {
        fill(255, 0, 0);
        ellipse(342, 65+(x * 50), 30, 30);
      }
    }

    //draw XY Pad box
    fill(0); //fill with black
    stroke(255, 0, 0); //outline box with red
    rectMode(CORNER); //measure x,y from top left corner
    rect(150, 250, 200, 200);  //x,y,width,height

    //level bar
    fill(0, 255, 0);
    stroke(0); //black outline
    ellipse((150+XYpad[0]), (450-XYpad[1]), 5, 5);  //150(from top of program) + how far from top left corner my finger is on the xy pad, dido for y, dot size
  }

  else if (TabStat[1]) //if we are on the second tab
  {
    rectMode(CORNER);
    background(50);

    //fader box
    fill(0);
    text("Fader", 30, 65); //this text, x ,y
    stroke(255, 0, 0);
    rect(50, 75, 25, 350);    // 50 pixels from left, 75 from top, width 25, height 350
    //fader fill
    fill(255, 0, 0);
    rect(50, 75+350, 25, -TabII[0]); //75 is how far the bars are from the top of the window, then 350 for length to the bottom of bar


    //rotary, ~270deg
    fill(0);
    text("Rotary", 125, 175);
    fill(0, 0, 255);
    stroke(0, 0, 255);
    ellipse(300, 175, TabII[1], TabII[1]); //set size of ellipse based on rotary


    //encoder, free turning, alternates between two values, ex 01010101011111111111,LRLRLRLRLRLRLRRRRRRRRRRR
    //have fun with this below :D
    fill(0);
    text("Encoder", 125, 300);

    if (mouseX >= 200 && mouseX <= 400 && mouseY >= 200 && mouseY <= 400)  //if mouse over encoder circle, stop
    {
      TabII[4]=TabII[3]; //keep circle the same size
    }
    else //continue changing
    {
      if (TabII[2] == 1) //if this way
      {
        TabII[4]+=1; //add one
      }
      else if (TabII[2] == 0)
      {
        TabII[4]-=1;
      }
    }

    fill(200, 0, 100);
    stroke(200, 0, 150);
    ellipse(300, 300, TabII[4], TabII[4]); //draw ellipse based on encoder

    TabII[3] = TabII[4];
  }

  else if (TabStat[2]) //if we are on the third tab
  {
    background(50);
    stroke(0);
    fill(0);
    text("Toggle:", 218, 25);
    text("Push:", 45, 25);

    for (int x = 0; x<9; x++) //test all the buttons for on/off and set them green/red accordingly
    {
      fill(0);
      text(x+1, 107, 55+(x * 50)); //print button numbers
      text(x+1, 305, 55+(x * 50));

      if (MultiPush[x] == 1)  //draw buttons
      {
        fill(0, 255, 0);
        ellipse(146, 45+(x * 50), 30, 30);
      }
      else
      {
        fill(255, 0, 0);
        ellipse(146, 45+(x * 50), 30, 30);
      }

      if (MultiToggle[x] == 1)
      {
        fill(0, 255, 0);
        ellipse(342, 45+(x * 50), 30, 30);
      }
      else
      {
        fill(255, 0, 0);
        ellipse(342, 45+(x * 50), 30, 30);
      }
    }
  }

  else if (TabStat[3]) //if we are on the fourth tab
  {
    background(50);

    //draw multi XY Pad box
    fill(0);
    stroke(255, 0, 0);
    rectMode(CORNER);
    rect(150, 275, 200, 200);

    fill(0, 255, 0);
    stroke(0);
    ellipse((150+MultiXYpad[1]), (475-MultiXYpad[0]), 5, 5); //draw touch 1, 150 is the box x coordinate then + the x coordinate of touch, 475 is box y coordinate plus y length of box - the y value of touch
    ellipse((150+MultiXYpad[3]), (475-MultiXYpad[2]), 5, 5); //touch 2
    ellipse((150+MultiXYpad[5]), (475-MultiXYpad[4]), 5, 5); //...
    ellipse((150+MultiXYpad[7]), (475-MultiXYpad[6]), 5, 5);
    ellipse((150+MultiXYpad[9]), (475-MultiXYpad[8]), 5, 5);


    //sine wave maker based on fader
    theta += MultiFader[1]; //angular velocity
    // For every x value, calculate a y value with sine function
    float x = theta;
    for (int i = 0; i < yvalues.length; i++)
    {
      yvalues[i] = sin(x)* MultiFader[2];
      x+=dx;
    }

    for (int b = 0; b < yvalues.length; b++)
    {
      noStroke();
      fill(255, 0, 0);  //
      rectMode(CENTER);
      rect(b*MultiFader[0], 450/3+yvalues[b], (MultiFader[4]/15), (MultiFader[4]/15)); //x,y, width, height
    }
  }

  else if (TabStat[4]) //if we are on the fith tab
  {
    background(50);
    if (Accel[3]==1) //when zeroing the accel, make font green
    {
      fill(0, 255, 0);
      text("3-Axis Accelerometer", 130, 75);
    }
    else
    {
      fill(255, 0, 0);
      text("3-Axis Accelerometer", 130, 75);
    }

    Accel[7] = (Accel[0] - Accel[4]); //x final val = current val - center val
    Accel[8] = (Accel[1] - Accel[5]); //y
    Accel[9] = (Accel[2] - Accel[6]); //z

    fill(255, 0, 0);
    text("X Axis: ", 175, 125); //label axis in program
    text(Accel[7], 265, 125); //then write values next to label

    text("Y Axis: ", 175, 175);
    text(Accel[8], 265, 175);

    text("Z Axis: ", 175, 225);
    text(Accel[9], 265, 225);

    //all of this is for sending the highest and lowest value so far back to the ipod!
 
    //x
    if (Accel[10] < Accel[7] && (Accel[7] - Accel[10]) > AccelMM[0])  //if prev final val is less than current final val && current max is higher than prev max
    {
      //from here
      AccelMM[0] = Accel[7]; //set new max
      OscMessage Xmax = new OscMessage("/5/Xmax"); //new message called Xmax going to /5/Xmax label
      Xmax.add(Accel[7]); //add value to Xmax message that is going to be sent
      oscP5.send(Xmax, iPod); //oscP5.send(Xmax, iPod); Xmax is name of message, iPod in this line is the address, see top of code
      //to here is how you send info to device
    }
    else if (Accel[10] > Accel[7] && (Accel[10] - Accel[7]) < AccelMM[1]) //if prev final val is greater than current final val && current min is less than prev min
    {
      AccelMM[1] = Accel[10]; //set new min
      OscMessage Xmin = new OscMessage("/5/Xmin");
      Xmin.add(-Accel[10]);
      oscP5.send(Xmin, iPod);
    }

    //y
    if (Accel[11] < Accel[8] && (Accel[8] - Accel[11]) > AccelMM[2])  //if prev final val is less than current final val && current max is higher than prev max
    {
      AccelMM[2] = Accel[8]; //set new max
      OscMessage Ymax = new OscMessage("/5/Ymax");
      Ymax.add(Accel[8]);
      oscP5.send(Ymax, iPod);
    }
    else if (Accel[11] > Accel[8] && (Accel[11] - Accel[8]) < AccelMM[3]) //if prev final val is greater than current final val && current min is less than prev min
    {
      AccelMM[3] = Accel[11]; //set new min
      OscMessage Ymin = new OscMessage("/5/Ymin");
      Ymin.add(-Accel[11]);
      oscP5.send(Ymin, iPod);
    }

    //z
    if (Accel[12] < Accel[9] && (Accel[9] - Accel[12]) > AccelMM[4])  //if prev final val is less than current final val && current max is higher than prev max
    {
      AccelMM[4] = Accel[8]; //set new max
      OscMessage Zmax = new OscMessage("/5/Zmax");
      Zmax.add(Accel[9]);
      oscP5.send(Zmax, iPod);
    }
    else if (Accel[12] > Accel[9] && (Accel[12] - Accel[9]) < AccelMM[5]) //if prev final val is greater than current final val && current min is less than prev min
    {
      AccelMM[5] = Accel[12]; //set new max
      OscMessage Zmin = new OscMessage("/5/Zmin");
      Zmin.add(-Accel[12]);
      oscP5.send(Zmin, iPod);
    }
 
    /*this is also how you turn on a virtual LED on the iPod:
 
      OscMessage LEDmsg = new OscMessage("/1/LED1"); //new message going to the "/1/LED1" led
      LEDmsg.add(0); //add value to message that is going to be sent , 0 for LED off, 1 for LED on
      oscP5.send(LEDmsg, iPod); //send the message
    */

    Accel[10] = Accel[7]; //to be previous val = current final val
    Accel[11] = Accel[8];
    Accel[12] = Accel[9];
  }
}

void oscEvent(OscMessage theOscMessage) //  This runs whenever there is a new OSC message
{
  /*
  OSC message looks like this: /1/toggle1 or /1/multitoggle/2/1.
   /tab/"object name"   /tab/"object name"/row pos/column pos
 
   multitoggle 3x3 example:
       1  2  3   Y
       __ __ __
X   3  |      |
    2  |      |
    1  |      |
       __ __ __
   /1/multitoggle1/X/Y
 
   */

  String addr = theOscMessage.addrPattern();  //  Creates a string out of the OSC message
  if (addr.equals("/1")) //search string, if we are on the first tab
  {
    TabStat[0] = true; //so we know what part of the draw function to run
    TabStat[1] = false; //and not to run
    TabStat[2] = false; //...
    TabStat[3] = false;
    TabStat[4] = false;
  }
  else if (addr.equals("/2")) //tab 2
  {
    TabStat[0] = false;
    TabStat[1] = true;
    TabStat[2] = false;
    TabStat[3] = false;
    TabStat[4] = false;
  }
  else if (addr.equals("/3")) //tab 3
  {
    TabStat[0] = false;
    TabStat[1] = false;
    TabStat[2] = true;
    TabStat[3] = false;
    TabStat[4] = false;
  }
  else if (addr.equals("/4")) //tab 4
  {
    TabStat[0] = false;
    TabStat[1] = false;
    TabStat[2] = false;
    TabStat[3] = true;
    TabStat[4] = false;
  }
  else if (addr.equals("/5")) //tab 5
  {
    TabStat[0] = false;
    TabStat[1] = false;
    TabStat[2] = false;
    TabStat[3] = false;
    TabStat[4] = true;
  }

  float val = theOscMessage.get(0).floatValue();  //get the value

  if (TabStat[0]) //if we are on the first tab
  {
    if (addr.equals("/1/toggle1")) //if we find this address
    {
      ToggleStat[0] = val; //write the value to it, 0 or 1
    }
    if (addr.equals("/1/toggle2"))
    {
      ToggleStat[1] = val;
    }
    if (addr.equals("/1/toggle3"))
    {
      ToggleStat[2] = val;
    }

    if (addr.equals("/1/push1"))
    {
      PushStat[0] = val;
    }
    if (addr.equals("/1/push2"))
    {
      PushStat[1] = val;
    }  
    if (addr.equals("/1/push3"))
    {
      PushStat[2] = val;
    }

    if (addr.equals("/1/xyPad"))
    {  //xy pad writes to values to one address
      XYpad[1] = val;  //y
      XYpad[0] = theOscMessage.get(1).floatValue(); //buttons dont work if i put this(get(1)) into a float at begining of function...???
    }
  }

  else if (TabStat[1]) //if we are on the second tab
  {
    if (addr.equals("/2/fader"))
    {
      // print("fader recv");  //just checking
      // println(val);
      TabII[0] = val;
    }
    if (addr.equals("/2/rotary"))
    {
      TabII[1] = val;
    }
    if (addr.equals("/2/encoder"))
    {
      TabII[2] = val;
    }
  }

  else if (TabStat[2]) //if we are on the third tab
  {
    //MULTIPUSH-------------------------
    //Row3
    if (addr.equals("/3/multipush/1/1"))  //remember weird row numbering
    {
      MultiPush[6] = val;
    }
    if (addr.equals("/3/multipush/1/2"))
    {
      MultiPush[7] = val;
    }
    if (addr.equals("/3/multipush/1/3"))
    {
      MultiPush[8] = val;
    }
    //Row2
    if (addr.equals("/3/multipush/2/1"))
    {
      MultiPush[3] = val;
    }
    if (addr.equals("/3/multipush/2/2"))
    {
      MultiPush[4] = val;
    }
    if (addr.equals("/3/multipush/2/3"))
    {
      MultiPush[5] = val;
    }
    //Row1
    if (addr.equals("/3/multipush/3/1"))
    {
      MultiPush[0] = val;
    }
    if (addr.equals("/3/multipush/3/2"))
    {
      MultiPush[1] = val;
    }
    if (addr.equals("/3/multipush/3/3"))
    {
      MultiPush[2] = val;
    }


    //MULTITOGGLE-------------------------
    //Row1
    if (addr.equals("/3/multitoggle/1/1"))
    {
      MultiToggle[6] = val;
    }
    if (addr.equals("/3/multitoggle/1/2"))
    {
      MultiToggle[7] = val;
    }
    if (addr.equals("/3/multitoggle/1/3"))
    {
      MultiToggle[8] = val;
    }
    //Row2
    if (addr.equals("/3/multitoggle/2/1"))
    {
      MultiToggle[3] = val;
    }
    if (addr.equals("/3/multitoggle/2/2"))
    {
      MultiToggle[4] = val;
    }
    if (addr.equals("/3/multitoggle/2/3"))
    {
      MultiToggle[5] = val;
    }
    //Row3
    if (addr.equals("/3/multitoggle/3/1"))
    {
      MultiToggle[0] = val;
    }
    if (addr.equals("/3/multitoggle/3/2"))
    {
      MultiToggle[1] = val;
    }
    if (addr.equals("/3/multitoggle/3/3"))
    {
      MultiToggle[2] = val;
    }
  }

  else if (TabStat[3]) //if we are on the fourth tab
  {  
    //Multi touch XY, up to 5 x/y points
    if (addr.equals("/4/multixy/1")) //point 1 x and y values
    {
      MultiXYpad[0] = val; //y
      MultiXYpad[1] = theOscMessage.get(1).floatValue();  //x
    }

    if (addr.equals("/4/multixy/2")) //point 2 x and y values
    {
      MultiXYpad[2] = val;  //y
      MultiXYpad[3] = theOscMessage.get(1).floatValue();  //x
    }

    if (addr.equals("/4/multixy/3")) //point 3 x and y values
    {
      MultiXYpad[4] = val;
      MultiXYpad[5] = theOscMessage.get(1).floatValue();
    }

    if (addr.equals("/4/multixy/4")) //point 4 x and y values
    {
      MultiXYpad[6] = val;
      MultiXYpad[7] = theOscMessage.get(1).floatValue();
    }

    if (addr.equals("/4/multixy/5")) //point 5 x and y values
    {
      MultiXYpad[8] = val;
      MultiXYpad[9] = theOscMessage.get(1).floatValue();
    }


/* what multi faders look like
--------------- fader 4
--------------- 3
--------------- 2
--------------- 1


*/
    if (addr.equals("/4/multifader/1"))  //if multi fader 1 has been moved
    {
      // divide by 40 to get smaller value that fits this application
      MultiFader[0] = (val/40.0)+1; //How far apart should each horizontal location be spaced,
      yvalues = new float[int(WaveWidth/MultiFader[0])]; //height values for the wave
    }
    if (addr.equals("/4/multifader/2"))
    {
      MultiFader[1] = (val/400.0); //angular velocity, theta
    }
    if (addr.equals("/4/multifader/3"))
    {
      MultiFader[2] = val/2.5;  //amplitude -- Height of wave
    }
    if (addr.equals("/4/multifader/4"))
    {
      //MultiFader[3] = val = period -- How many pixels before the wave repeats
      dx = (TWO_PI / val) * MultiFader[0];  //dx Value for incrementing X, a function of period and xspacing
    }
    if (addr.equals("/4/multifader/5"))
    {
      MultiFader[4] = val;   //line thickness
    }
  }
  else if (TabStat[4]) //if we are on the fith tab
  {
    //read Accelerometer
    if (addr.equals("/accxyz"))
    {
      Accel[0] = val;  //x
      Accel[1] = theOscMessage.get(1).floatValue();  //y
      Accel[2] = theOscMessage.get(2).floatValue();  //z
    }

    //Accel Center
    if (addr.equals("/5/AccCenter"))
    {
      Accel[3] = val;
      for (int x = 0; x<3; x++)
      {
        Accel[x+4] = Accel[x]; //save current x, y, z values as center values
      }
    }
  }
}