http://fluidwall.blogspot.com
Physical Computing
Austin's Adventures in Making Digital Physical
Tuesday, May 17, 2011
FluidWall Moved to it's own blog!
http://fluidwall.blogspot.com
Project 2: Rock Paper Scissors with Arduino
Here's the code I used, written for the Arduino platform. Uses the Bounce library, available here.
/*
Rock Paper Scissors
Author: Austin Hines
VIZA 622 - Physical Computing
This is a serial communication game. It communicates with remote arduinos accross Serial1 utilizing a protocol.
The default starting color is red.
*/
#include <Bounce.h>
const int potpin = 1;
const int sendpin = A3;
const int cheatpin = 3;
const int rpin = 9;
const int gpin = 10;
const int bpin = 11;
const int losepin = 7;
const int winpin = 8;
int potVal = 0;
char currentColor = 'r';
//state
bool buttonIsPressed = 0;
bool localMode = 1; //writes and communicates with computer serial port
Bounce buttonState = Bounce(sendpin,5);
//method declarations
void processPotChange();
char exchangeValues(char localVal);
void doError();
char validateColor(char inChar);
char didLocalWin(char localVal, char remoteVal);
void waitForRemoteReset();
void doWin();
void doLose();
void doTie();
void setup() {
pinMode(potpin, INPUT);
pinMode(sendpin, INPUT);
pinMode(cheatpin, INPUT);
pinMode(rpin, OUTPUT); //r
pinMode(gpin, OUTPUT); //g
pinMode(bpin, OUTPUT); //b
pinMode(winpin, OUTPUT); // green led
pinMode(losepin, OUTPUT); // red led
Serial.begin(9600);
Serial1.begin(9600);
}
//MAIN LOOP
void loop()
{
//COLOR SELECTION PHASE
processPotChange(); //listen for pot changes
buttonState.update();
if(buttonState.read() == HIGH)
{
buttonIsPressed = true; //button is pressed down
} else if(buttonState.read() == LOW && buttonIsPressed)
{
//button state has changed from high, so it has just been released
//COMMUNICATION PHASE
Serial.println("Button Pressed");
char localVal = currentColor;
char remoteVal = exchangeValues(localVal);
if(remoteVal == 'e')
{
doError();
}
else
{
//EVALUATION PHASE
displayColor(remoteVal);
char result = didLocalWin(localVal, remoteVal);
if(result == 'Y')
doWin();
else if (result == 'N')
doLose();
else if (result == 'T')
doTie();
//waitForRemoteReset();
}
//reset button pressed state
buttonIsPressed = false;
}
}
/* processPotChange()
Changes user's color selection if the pot value has changed.
*/
void processPotChange()
{
int newPotVal = analogRead(potpin);
//only run logic if pot value has changed
if(newPotVal != potVal)
{
if(newPotVal < 342)
currentColor = 'r';
else if(newPotVal > 683)
currentColor = 'b';
else
currentColor = 'g';
potVal = newPotVal;
displayColor(currentColor);
}
}
/* exchangeValues
Exchanges user values with a remote host using a predetermined protocol transmitted accross
the serial port.
@return A valid color character from the remote client, or E, indicating the remote client did not
follow protocol.
*/
char exchangeValues(char localVal)
{
char serialIn;
//handle case where users send reset method first
waitForRemoteReset();
if(sAvailable()) //RESPONDER MODE
{
serialIn = sRead();
if(serialIn == 'o') //check for 'O' character
{
sWrite(localVal); //send our color
while(!sAvailable()) //wait for remote response
delay(30);
return validateColor(sRead()); //validate response
}
else
{
Serial.println("ERROR: In responder mode, an o is expected.");
return 'e';
}
}
else //INITIATOR MODE
{
sWrite('o'); //Send open character
Serial.println("Waiting for remote value... ");
while(!sAvailable()) //wait for color
delay(30);
sWrite(localVal); //send over our color
return validateColor(sRead()); //validate response
//TODO: insert cheating code here
}
}
void displayColor(char color)
{
if(color == 'r')
{
digitalWrite(rpin, HIGH);
digitalWrite(gpin, LOW);
digitalWrite(bpin, LOW);
} else if (color == 'g')
{
digitalWrite(rpin, LOW);
digitalWrite(gpin, HIGH);
digitalWrite(bpin, LOW);
} else if (color == 'b')
{
digitalWrite(rpin, LOW);
digitalWrite(gpin, LOW);
digitalWrite(bpin, HIGH);
}
}
/* didLocalWin
Evaluates game result based on the rules of rock, paper, scissors, using each as RGB analogues.
R = Rock, G = Paper, B = Scissors.
R beats B, G beats R, B beats G
@return 'Y' if the local value won, 'N' if local value lost, 'T' if local values is the same as remote.
*/
char didLocalWin(char localVal, char remoteVal)
{
Serial.println("Evaluating Result");
if(localVal == remoteVal)
return 'T';
else if((localVal == 'r' && remoteVal == 'b') ||
(localVal == 'g' && remoteVal == 'r') ||
(localVal == 'b' && remoteVal == 'g'))
return 'Y';
else
return 'N';
}
/* result methods
separeted out to allow for more elaborate effects, like notes with a peizo speaker.
*/
void doWin()
{
digitalWrite(winpin, HIGH);
delay(3000);
digitalWrite(winpin, LOW);
}
void doLose()
{
digitalWrite(losepin, HIGH);
delay(3000);
digitalWrite(losepin, LOW);
}
void doTie()
{
digitalWrite(winpin, HIGH);
digitalWrite(losepin, HIGH);
delay(3000);
digitalWrite(winpin, LOW);
digitalWrite(losepin, LOW);
}
void doError()
{
Serial.println("ERROR");
for(int i = 0; i < 3; i++)
{
digitalWrite(losepin, HIGH);
delay(500);
digitalWrite(losepin, LOW);
}
}
/* waitForRemoteReset
Sends the waiting string "zzz" according to protocol. Waits for three characters in response.
*/
void waitForRemoteReset()
{
sWrite("zzz");
Serial.println("Waiting for remote reset code");
while(sAvailable() != 3)
delay(100);
sRead();
sRead();
sRead();
Serial.println("Resetting!");
}
/* sWrite
Writes to a Serial port depending on what the current mode is.
Precondition: boolean localMode has been defined.
*/
void sWrite(String writeString)
{
Serial.print("/n Sending:");
//if(localMode)
Serial.print(writeString);
//else
Serial1.print(writeString);
}
/* sRead
reads a byte from a serial port depending on what the current mode is.
Precondition: boolean localMode has been defined.
*/
byte sRead()
{
byte readValue;
Serial.print("/n Recieving: ");
if(localMode)
readValue = Serial.read();
else
readValue = Serial1.read();
Serial.print((char)readValue);
return readValue;
}
/* sAvailable
returns the current number of bytes available in the current serial port.
*/
int sAvailable()
{
if(localMode)
return Serial.available();
else
return Serial1.available();
}
/* sPeek
returns the contents of the current serial port without removing it.
*/
byte sPeek()
{
if(localMode)
return Serial.peek();
else
return Serial1.peek();
}
/* validateColor
Ensures remote client sends over a character conforming to the protocol, returns 'E' otherwise.
*/
char validateColor(char inChar)
{
Serial.println(inChar);
if((inChar == 'r') || (inChar == 'g') || (inChar == 'b'))
return inChar;
else
{
Serial.print("ERROR: Color value not valid");
return 'e';
}
}
Tuesday, April 12, 2011
Saturday, April 2, 2011
Fluid Sim Operational!
- Jos Stam's Paper, Real-Time Fluid Dynamics for Games
- Download Stam's Source Code
This still needs a bit of work to make sure that no density ever gets added to areas with boundaries.
Next step: convert this puppy to c++!
Thursday, March 31, 2011
Final Project Schedule
Dates:
- Determine which language we're using - try to implement kinect and Jos Stam's stable fluids in C++.
- Naureen: Determine how to output silhouette boundaries from figures from kinect as 640x480 RGBA images. Determine a protocol to pass information between objects.
- Austin: Write fluid simulation to accept collision objects
- Austin: Accept image information from kinect stream object
- Naureen: Examine how to determine velocity of feet to pass to simulation
- If fluid processing turns out to be processor restrictive, change kinect code into another project (detect hand prints on wall).
- Reserve projector
- Fine tune distance needed for kinect
- Fine tune fluid simulation behaviors
- Optimize speed
- Done just in time!
Tuesday, March 22, 2011
Final Project Proposal: Co-dependant Flowers
Final Project Proposal: Not a Bang but a Ripple
[Image coming soon]
Aesthetic Goals
Users step up to a body-sized projected image on the wall and stomp their feet to introduce a swirl of blue fluid onto the screen. The fluid flows upward, bouncing off of the sides of the screen, as well as off of silhouettes of the user’s body. The system can accommodate multiple people as well. With every step, more fluid is emitted into the volume. As the user moves their body, they will be able to trap the fluid against the wall, and introduce further velocities.
This will hopefully create a sense of playful serenity and mystery. With the absence of detected movement, color fades over time to leave the wall empty and peaceful.
The faster the fluid moves, the lighter in color it becomes.
Conceptual
The user’s feet sets off ripples that reveal that their own silhouettes are part of the pond. The turbulence created by the user’s silhouettes is designed to help users contemplate the complex chain of events that people set off with each step they take, every move in life. With two or more people, the display becomes a complex intermingling of cause and effect between the actions of two people.
Technical
This is technically nontrivial. It will likely require a Kinect for object recognition and depth perception, as well as a projector with a ceiling or rear projection orientation, depending on installation space. A fluid simulation engine complete with object collision detection will need to be designed and implemented. Object collision will need to be able to be defined based on input images from the Kinect. Everything must also run in real time.
Skills required
- Kinect
o What kind of information does the Kinect driver return for you? Velocity of point movement?
- Determine depth plane
- Determine user proximity to presentation screen
o Handled with depth channel of connect
o Requires an array of proximity sensors otherwise
- Determine user outline.
o Kinect: use depth plane
o Use a difference matte of original image vs image with viewer close to it.
o Determine velocity using optical flow.
- Fluid simulation engine
o Probably best to use Eulerian fluid sim methods. Will need to build/find such an engine for processing.
Kinect?
Sillhouette Mirror: http://www.youtube.com/watch?v=hCBUKC9wc_0
Art Possibilities: http://www.youtube.com/watch?v=Z7ZJpwOBl_A
Kinect/iPad: http://www.youtube.com/watch?v=ocoiov2aot4