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';
  }
}