A scavenger hunt uses IoT functionality to connect a series of challenges across the CMU campus. Winner gets beer.

0

Intention

This project establishes an entertaining challenge for the purpose of team building and de-stressing in our community. The motive is to get people to come together to solve this series of challenges, and to introduce more fun into our campus environment. Each challenge corresponds to a hidden box that players must locate and unlock to get to the next stage of the game. The puzzles culminate in a prize... beer!

0

Community

Our audience is the community of students at Carnegie Mellon. With the pervasive culture of academic rigor on campus, our hardworking students may not often feel up for attending social events and activities. This can lead to a sense of isolation and burnout. Our project attends to this issue by creating an activity for students based on their needs: the game is overall relatively short and simple, and students can start and stop it at their leisure and availability (assuming there will be restocking of the beers for multiple groups to be able to play). These clever puzzles allow students to think in a creative way. At their completion of the puzzle, they are rewarded with beer and a perfect opportunity to take a break. This should help energize them, invigorate their sense of community, and let them get back to their studies with a small sense of accomplishment.

0

Inspiration and Context

Escape Room -- we thought this was a great team-building activity. People of any background can come together to use logic to solve a complicated puzzle. At the end, there is a sense of reward as they get to "escape the room". Our project relates in this way, but it will not require a fixed location.

Video games and board games  -- video games can be a great example of a low-commitment break for busy people. Electronic components can make video games more exciting and stimulating than board games. Board games can be more inclusive since they don't require dexterity for using controllers. For both, there are elements of strategy and teamwork, which make them social activities. Our project is different in that it is more active and gives a tangible reward.

Scavenger hunts -- scavenger hunts allow people to actively search an area and possibly complete a linear series of tasks. Our project is similar but incorporates more difficulty and motivation by implementing riddles and a prize at the end. 

0

Process

Starting with a similar concept to a prisoner’s dilemma game, we spent a considerable amount of time figuring out game logistics and balancing the aspects of fun, how challenging it would be, and how well we could implement IoT features. After testing our first prototype and talking to our professor, we found that inventing a captivating game is highly complex and can be unpredictable. Moving forward, we chose a concept structure where players competed against the game and not one another, and this motivated our scavenger hunt game involving riddles, codes, and collaboration within a community.

One concept we worked through during our game design was how the boxes would function to unlock. For example, one iteration of our game had a 9- number keypad where users would enter in a number code. All of these pieces involved setup to connect to the Particle so we reduced the size of the "number pad" and made it binary buttons rather than making each button correspond to a number. We also tested out using potentiometers to dial in number values that could unlock a box. While we made it work, we weighed other options in order to maximize durability and precision of our product. This hardware is sensitive, and if the pieces got shifted or compromised the players would not be able to unlock the box. For future prototypes, we could use another way to enter numbers. 

Another concept we considered was having a linear or parallel structure to the game. Meaning, would opening one riddle box lead to another box? Or, could players unlock boxes in any order and then be able to solve the final puzzle once the others were done? We liked the idea of the former since this meant people would not know what stage of the puzzle they are at, which requires more coordination and effort to determine the scope of the puzzle. 

0

Conceptual Design 

Upon coming across the first box (you can't miss it, it's painted like a candy cane), you must solve the riddle inside of the box. This box will be unlocked. When you have entered the correct answer, the light will change to let you know. Then, you must search for the next box. You will know the box is the right one if the green "activated" light is on, and the box is unlocked. Again, repeat, and answer the riddle by dialing the potentiometers correctly. The light will change to let you know your answer is correct, and that the final box, wherever it is, has been unlocked. For the final box, once you find it, there is one final riddle. You can trigger the beer compartment to unlock by speaking the answer to the riddle. If you reach the final box too early, it will be locked and the voice trigger will not work. 

The related context is that the boxes will be stored in dry places on campus. Each box can remotely be re-locked as long as the user closes the lid after using the box. 

0

Workflow

  1. Open Box 1.
  2. Read and solve riddle in Box 1.
  3. Enter riddle result into Box 1 input.
  4. Box 2 becomes active and thus unlocks.
  5. Open Box 2, read and solve riddle.
  6. Enter riddle 2 result into Box 2 input.
  7. Final Box becomes active and starts to glow.
  8. Read and solve riddle on top of BeerBox.
  9. Trigger Alexa with riddle answer.
  10. Enjoy your beer break!

0

Demo

0
BeerBox | Designing for the Internet of Things 2019
Anya Gipsov - https://youtu.be/uoTeYMGnfI4
0

Prototype and Implementation

Riddle Box #1

List of materials 

  1. Foamcore
  2. Particle Argon#1
  3. Neopixel
  4. Red and green LEDs
  5. 4 buttons

Riddle Box #1 features a game with four led lights, each controlled by one button. The light color will change when the player pushes the button. The next box will open when the player gets the right light pattern. 


0
riddle box 1 implementation
// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h> //note this CANNOT be pasted. Must be installed from Library

#define PIXEL_PIN D2
#define PIXEL_COUNT 4
#define PIXEL_TYPE WS2812

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
int b1Pin = D3;
int b2Pin = D4;
int b3Pin = D5;
int b4Pin = D6;
int b1State = 0;
int b2State = 0;
int b3State = 0;
int b4State = 0;
int b1Value;
int b2Value;
int b3Value;
int b4Value;

//Timing variables
unsigned long prevMillis1 = 0;
unsigned long prevMillis2 = 0;
unsigned long prevMillis3 = 0;
unsigned long prevMillis4 = 0;
unsigned long timeElapsed1 = 0;
unsigned long timeElapsed2 = 0;
unsigned long timeElapsed3 = 0;
unsigned long timeElapsed4 = 0;
unsigned long btnInterval = 500;

//establish color codes
uint32_t white = strip.Color(240, 240, 240);
uint32_t red = strip.Color(255, 0, 10);

//Setup the status LED 
int greenLED = A0;
int redLED = A1;
unsigned long lastPublishedAt = 0;
unsigned long elapsed = 0;
int publishAfter = 60000;//listener will re-activated after 60 seconds
bool publish_success = false;

void setup() {
    //set the pin modes
    pinMode (b1Pin, INPUT_PULLUP);
    pinMode (b2Pin, INPUT_PULLUP);
    pinMode (b3Pin, INPUT_PULLUP);
    pinMode (b4Pin, INPUT_PULLUP);
    
    //create the partical variables for debugging purposes
    Particle.variable("button1", b1State);
    Particle.variable("button2", b2State);
    Particle.variable("button3", b3State);
    Particle.variable("button4", b4State);
    
    //turn on the strip
    strip.begin();
    //cycle through pixels, turn each to white
    for (int i = 0; i < 4; i++) {
        strip.setPixelColor(i, white);
    }
    strip.show(); //show the color
    prevMillis1 = millis();
    prevMillis2 = millis();
    prevMillis3 = millis();
    prevMillis4 = millis();
    
    //activate box
    pinMode(redLED, OUTPUT);
    pinMode(greenLED, OUTPUT);
    
    digitalWrite(redLED, HIGH);
    digitalWrite(greenLED, LOW);
}

void loop() {
    //read the buttons
    b1Value = digitalRead(b1Pin);
    b2Value = digitalRead(b2Pin);
    b3Value = digitalRead(b3Pin);
    b4Value = digitalRead(b4Pin);
    //calculate timeElapsed since last button pressed
    timeElapsed1 = millis() - prevMillis1;
    timeElapsed2 = millis() - prevMillis2;
    timeElapsed3 = millis() - prevMillis3;
    timeElapsed4 = millis() - prevMillis4;
    //check if button 1 pressed
    //Only when the time interval between two button press is greater than the threshold, we count it as on press
    if (b1Value == LOW && timeElapsed1 > btnInterval) { 
        prevMillis1 = millis();
        if (b1State < 1) {
            b1State = 1;
            strip.setPixelColor(0, red);
        } else {
            b1State = 0;
            strip.setPixelColor(0, white);
        }
        strip.show();
    }
    //check if button 2 pressed
    if (b2Value == LOW && timeElapsed2 > btnInterval) {
        prevMillis2 = millis();
        if (b2State < 1){
            b2State = 1;
            strip.setPixelColor(1, red);
        } else {
            b2State = 0;
            strip.setPixelColor(1, white);
        }
        strip.show();
    }
    //check if button 3 pressed
    if (b3Value == LOW && timeElapsed3 > btnInterval) {
        prevMillis3 = millis();
        if (b3State < 1 ) {
            b3State = 1;
            strip.setPixelColor(2, red);
        }
        else{
            b3State = 0;
            strip.setPixelColor(2, white);
        }
        strip.show();
    }
    //check if button 4 pressed
    if(b4Value == LOW && timeElapsed4 > btnInterval) {
        prevMillis4 = millis();
        if (b4State < 1) {
            b4State = 1;
            strip.setPixelColor(3, red);
        } else {
            b4State = 0;
            strip.setPixelColor(3, white);
        }
        strip.show();
    }
    //verify the answer, do only when the unlock event is not published
    if (checkAnswer() && !publish_success) {
        puzzleDone();
        digitalWrite(greenLED, HIGH);
    }
    
    elapsed = millis() - lastPublishedAt;
    if( lastPublishedAt > 0 && elapsed > publishAfter && publish_success){
        digitalWrite(greenLED, LOW);
        publish_success = false;
        lastPublishedAt = 0;
    }
}//end the code

int checkAnswer( ) {
    //answer is <0 1 1 0>
    if (!b1State && b2State && b3State && !b4State) {
        return 1;
    }
    return 0;
}

void puzzleDone() {
      String eventName = "diot/2019/shrey/unlock/" + System.deviceID();
      publish_success = Particle.publish( eventName );
      lastPublishedAt = millis();
}
Arthur, Shrey, Austin Click to Expand
0

Riddle Box #2

List of materials

  1. Foamcore
  2. Particle Argon#2
  3. Servo Motor
  4. Neopixel
  5. Red and green LEDs
  6. 3 potentiometers

    Riddle Box #2 features three potentiometers as input that maps from 0 to 9. The red LED light indicates the activation status of the box. The green LED light indicates whether the riddle has been solved. The servo controls the lock of the box.


    0
    riddle box 2 implementation
    //Riddle Box 2 Listens to Riddle Box 1
    
    int green = D4;
    int red = D2;
    int buttonpin = D5;
    unsigned long now = 0;
    unsigned long lastPublishedAt; 
    int publishAfter = 60000;
    bool activated = false;
    bool publish_success = false;
    
    //potentiometer interface
    int potPin1 = A2;
    int potPin2 = A3;
    int potPin3 = A4;
    int potReading1 = 0;
    int potReading2 = 0;
    int potReading3 = 0;
    
    int num1 = 0;
    int num2 = 0;
    int num3 = 0;
    
    //Setting up Servo
    Servo myServo;
    int angle = 0;
    int ServoPin = A5;
    String SHREY_DEVICE = "e00fce68f3ec1a95fb53ad4c";
    
    void setup() {
        pinMode ( red, OUTPUT);//Red indicate active state of the device
        pinMode ( green, OUTPUT);//Green indicate if the device has been solved
        pinMode ( buttonpin, INPUT_PULLUP);
        
        digitalWrite(green, LOW);//by default this device is not activated
        digitalWrite(red, LOW);
        
        Particle.variable("num1", num1 );
        Particle.variable("num2", num2 );
        Particle.variable("num3", num3 );
        
        myServo.attach(ServoPin);
        Particle.function("setServoPos", setServoPos);
        Particle.function("actDevice", actDevice);
        Particle.function("resetDev", resetDev);
    
        Particle.subscribe("diot/2019/shrey/unlock/" + SHREY_DEVICE, unlockDevice);
    }
    
    void loop() {
        
        potReading1 = analogRead(potPin1);
        potReading2 = analogRead(potPin2);
        potReading3 = analogRead(potPin3);
        
        num1 = map(potReading1, 0,4095, 0, 10);
        num2 = map(potReading2, 0,4095, 0, 10);
        num3 = map(potReading3, 0,4095, 0, 10);
        
        if (activated) {
            //use button click to simulate puzzle solved
            int buttonState = digitalRead ( buttonpin);
            if (num1 == 9 && num2 == 2 && num3 == 3){//**************************** configure the code here
                digitalWrite ( red, HIGH);
                now = millis();
                puzzleDone();
            }
            unsigned long elapsed = millis() - now;
            
            if( now > 0 && elapsed > 10000 && publish_success){
                digitalWrite(red, LOW);
                publish_success = false;
                now = 0;
                resetDevice();//reset the device after 10 second
            }
        }
    }
    
    // void activateDevice(const char *event, const char *data) {
    //     digitalWrite(green, HIGH); //activate this device
    //     activated = true;
    // }
    
    void puzzleDone()
    {
      if( lastPublishedAt + publishAfter < millis() )
      {
          String eventName = "diot/2019/austin/unlock/" + System.deviceID();
          publish_success = Particle.publish( eventName );
          lastPublishedAt = millis();
          digitalWrite(green, HIGH); //activate this device
      }
    
    }
    
    void resetDevice() {
        digitalWrite(green, LOW);
        activated = false;
        num1 = 0;
        num2 = 0;
        num3 = 0;
    }
    
    int setServoPos(String pos) {
        angle = pos.toInt();
        if (angle < 0) {
            return -1;
        }
        if (angle > 180) {
            return -1;
        }
        
        myServo.write(angle);
        return 0;
    }
    
    void unlockDevice(const char *event, const char *data) {
        setServoPos("10");
        actDevice("");
    }
    
    int actDevice(String arg) {
        activated = true;
        digitalWrite(red, HIGH);
        setServoPos("10");
        return 0;
    }
    
    int resetDev(String arg) {
        activated = false;
        digitalWrite(red, LOW);
        digitalWrite(green, LOW);
        setServoPos("90");
    }
    Arthur, Shrey & Austin Click to Expand
    0

    The Beer Box

    List of materials

    1. Foamcore
    2. Particle Argon#3
    3. Servo Motor
    4. Neopixel
    5. Red and green LEDs
    6. Amazon Echo
    7. Beer(the trophy)

    The Beer Box uses a servo to lock the box. When riddle #2 solved, the Beer Box will be activated. The Neopixel Ring inside the box will glow. The Box is connected with an Amazon Alexa command using IFTTT. And there is an Amazon Echo set onsite. The player can open the box after figuring out the answer and trigger the "unlock" event by saying the answer to Alexa.

    0
    the beer box implementation
    // This #include statement was automatically added by the Particle IDE.
    #include <neopixel.h>
    #define PIXEL_PIN A5
    #define PIXEL_COUNT 24
    #define PIXEL_TYPE WS2812B
    
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
    
    Servo myServo;
    int angle = 0;
    int ServoPin = D3;
    String AUSTIN_DEVICE = "e00fce689c13255696bdc053";
    
    uint32_t gold = strip.Color(255, 215, 0);
    uint32_t off = strip.Color(0, 0, 0);
    
    
    void setup() {
        myServo.attach(ServoPin);
        Particle.function("setServoPos", setServoPos);
        Particle.function("resetDev", resetDev);
        Particle.function("actDevice", actHelp);
        Particle.subscribe("diot/2019/austin/unlock/" + AUSTIN_DEVICE, actDevice);
    }
    
    void loop() {
        
    }
    
    int setServoPos(String pos) {
        angle = pos.toInt();
        if (angle < 0) {
            return -1;
        }
        if (angle > 180) {
            return -1;
        }
        
        myServo.write(angle);
        return 0;
    }
    
    void actDevice(const char *event, const char *data) {
        Particle.unsubscribe();
        Particle.subscribe("diot/2019/user/unlock/" + System.deviceID(), unlockDevice);
        actHelp("");
    }
    
    int actHelp(String arg) {
        for (int i = 0; i < strip.numPixels(); i++) {
            strip.setPixelColor(i, gold);
            strip.show();
        }
        return 0;
    }
    
    void unlockDevice(const char *event, const char *data) {
        setServoPos("10");
        for (int k = 0; k < 3; k ++) {
            for (int i = 0; i < strip.numPixels(); i++) {
                strip.setPixelColor(i, gold);
                strip.show();
            }
            delay(500);
            for (int i = 0; i < strip.numPixels(); i++) {
                strip.setPixelColor(i, off);
                strip.show();
            }
        }
        //TODO: change the light of the ring
    }
    
    int resetDev(String arg) {
        for (int i = 0; i < strip.numPixels(); i++) {
            strip.setPixelColor(i, off);
            strip.show();
        }
        setServoPos("90");
        Particle.unsubscribe();
        Particle.subscribe("diot/2019/austin/unlock/" + AUSTIN_DEVICE, actDevice);
    }
    Arthur, Shrey & Austin Click to Expand
    0

    Next Steps

    One great feature of our project is that new boxes and riddles can be easily introduced to the workflow. We could definitely be able to add more challenging riddle boxes to the game to make sure that it's harder to get the prize. Taking the project forward, we would invest in better hardware; namely, plastic sealed boxes that would not get damaged if they are kept outside or re-used by many players. The potentiometers and buttons could be replaced by more durable pieces. There could also be a sensor utilized to let us know if the box was correctly closed so that we would know if we needed to manually fix one of the riddle boxes as we set the game up for another set of players. From our control panel, we can easily see if the final box gets unlocked, but we could add a sensor to see if the prize gets taken out.

    This game idea was well thought out in that it could actually be used on campus, perhaps just indoors, and with us keeping an eye on the boxes and their contents to make sure they aren't being broken by students. Taking it forward, it would truly be a great addition to our community and give students a well-deserved sense of fun.

    x
    Share this Project

    Courses

    49713 Designing for the Internet of Things

    · 16 members

    A hands-on introductory course exploring the Internet of Things and connected product experiences.


    Skills
    About

    A scavenger hunt uses IoT functionality to connect a series of challenges across the CMU campus. Winner gets beer.

    Created

    December 11th, 2019