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