Tuesday, May 17, 2011

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




No comments:

Post a Comment