Tuesday, May 17, 2011

FluidWall Moved to it's own blog!

I've moved the final release information of Fluid Wall to its own blog, which we will continue to update as we develop. You can find it here:

http://fluidwall.blogspot.com

Project 2: Rock Paper Scissors with Arduino

Here's some documentation of my second project: Rock Paper Scissors with Arduino. In this project, I use serial communication to play Rock Paper Scissors with another Arduino. Color selection is displayed using an RGB LED, and input is done through a potentiometer.

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

Visualization of Final Project

Now you can see what's in my head!


Here's an overview of the entire installation.











Here's a breakdown of components with labels










Here's a detail view on how the fluid simulation will work with the silhouette acting as a collision object

Saturday, April 2, 2011

Fluid Sim Operational!

Yay! I adapted Jos Stam's C-based fluid simulation to include mouse-definable boundary conditions!

Velocity field with boundary conditions:


Density field, revealing the spookiest smiley face ever!



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

Splash Wall:

Dates:
March
31
  • Determine which language we're using - try to implement kinect and Jos Stam's stable fluids in C++.
April
Tue. 5, Thur. 7,
  • 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
Tue. 12, Thr. 14
  • 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).
Tue. 19, Thur, 21,
  • Reserve projector
  • Fine tune distance needed for kinect
Tue. 26, Thur, 28
  • Fine tune fluid simulation behaviors
  • Optimize speed
May 3rd
  • Done just in time!

Tuesday, March 22, 2011

Final Project Proposal: Co-dependant Flowers

Since the other idea is so complicated, I thought I would come up with a back-up idea quickly. So here you go:
I remember some kind of plant at viz-a-gogo last year that reacted to user's presence, but I couldn't recall if it used the same kind of thought process. If it did, oh well.

Here's something similar using a new thing called muscle wire:



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