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
}
}
}
}
Hi, first thanks alot for your code ! Could you repost the touchosc layout, your link is bad,
ReplyDeletethanks !
done.
ReplyDeleteHi there - i also wanted to thank you for taking the time to post this.
ReplyDeleteI'm trying to do a simple d-pad type control interface in touchOsc to control an arduino robot over bluetooth. I've got all the other pieces into place but i'm having troubles with what i imagine is a simple bit of code in processing. I just want 4 push buttons, and when one is held down, i'll have processing send an ascii code over bluetooth to the robot. I can already send the ascii code over its just the actual touchosc to processing programming i'm struggling with.
Think you could pass along any tips?
thanks!
Oh - and that link still seems broken?
ReplyDeletemoved it to another host, should work now.
ReplyDeletethis is a basic 4 button OSC sketch. label your buttons as /1/push2 or /1/push1 etc. you also need to create a comic sans font
import oscP5.*; // Load OSC P5 library
import netP5.*; // Load net P5 library
import processing.serial.*; // Load serial library
OscP5 oscP5; // Set oscP5 as OSC connection
PFont f; //initailize a font
float [] PushStat = new float [4]; //Push: btn1, btn2, btn3
void setup()
{
//println(Serial.list()); //shows list of avaliable serial ports, put number for you port in Arduino.list()[X]
size(250, 300); // Processing screen size
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
}
void draw()
{
background(50); //color of background
fill(0);
text("Push:", 45, 25);
for (int x = 0; x<4; x++) //draws all the buttons
{
fill(0); //fill font with black
text(x+1, 110, 75+(x * 50));
if (PushStat[x] == 1)
{
fill(0, 255, 0);
ellipse(142, 65+(x * 50), 30, 30);
//put serial code here or below--------------------------------
}
else
{
fill(255, 0, 0);
ellipse(142, 65+(x * 50), 30, 30);
}
}
}
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/push1"))
{
PushStat[0] = val;
//or put your code here------------------------
}
if (addr.equals("/1/push2"))
{
PushStat[1] = val;
}
if (addr.equals("/1/push3"))
{
PushStat[2] = val;
}
if (addr.equals("/1/push4"))
{
PushStat[3] = val;
}
}
thanks a million!
ReplyDeletegreat tutorial that inspire me to write an article about how to develop iOS applications to control robots. the article could be read here http://www.intorobotics.com/tutorials-how-to-build-ios-applications-to-control-a-robot/
ReplyDeleteAwesome! i once tried IOS programming on my OSX86 PC :D
Delete